Merge branch 'rewrite-metrics' into merge-metrics
BUG:22773266
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b25c15b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+*~
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..7c57258
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH := $(my-dir)
+
+include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/CleanSpec.mk b/CleanSpec.mk
new file mode 100644
index 0000000..b3661e4
--- /dev/null
+++ b/CleanSpec.mk
@@ -0,0 +1,58 @@
+# Copyright (C) 2007 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.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list. These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+# $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT_DIR) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list. E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+
+# For example:
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/init.rc)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/init.rc)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/reboot)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/default.prop)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/default.prop)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/lmkd_intermediates/import_includes)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libsysutils_intermediates/import_includes)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/grep $(PRODUCT_OUT)/system/bin/toolbox)
diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_APACHE2
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..152be20
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,324 @@
+ =========================================================================
+ == NOTICE file corresponding to the section 4 d of ==
+ == the Apache License, Version 2.0, ==
+ == in this case for the Android-specific code. ==
+ =========================================================================
+
+Android Code
+Copyright 2005-2008 The Android Open Source Project
+
+This product includes software developed as part of
+The Android Open Source Project (http://source.android.com).
+
+ =========================================================================
+ == NOTICE file corresponding to the section 4 d of ==
+ == the Apache License, Version 2.0, ==
+ == in this case for Apache Commons code. ==
+ =========================================================================
+
+Apache Commons
+Copyright 1999-2006 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+ =========================================================================
+ == NOTICE file corresponding to the section 4 d of ==
+ == the Apache License, Version 2.0, ==
+ == in this case for Jakarta Commons Logging. ==
+ =========================================================================
+
+Jakarta Commons Logging (JCL)
+Copyright 2005,2006 The Apache Software Foundation.
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+ =========================================================================
+ == NOTICE file corresponding to the section 4 d of ==
+ == the Apache License, Version 2.0, ==
+ == in this case for the Nuance code. ==
+ =========================================================================
+
+These files are Copyright 2007 Nuance Communications, but released under
+the Apache2 License.
+
+ =========================================================================
+ == NOTICE file corresponding to the section 4 d of ==
+ == the Apache License, Version 2.0, ==
+ == in this case for the Media Codecs code. ==
+ =========================================================================
+
+Media Codecs
+These files are Copyright 1998 - 2009 PacketVideo, but released under
+the Apache2 License.
+
+ =========================================================================
+ == NOTICE file corresponding to the section 4 d of ==
+ == the Apache License, Version 2.0, ==
+ == in this case for the TagSoup code. ==
+ =========================================================================
+
+This file is part of TagSoup and is Copyright 2002-2008 by John Cowan.
+
+TagSoup is licensed under the Apache License,
+Version 2.0. You may obtain a copy of this license at
+http://www.apache.org/licenses/LICENSE-2.0 . You may also have
+additional legal rights not granted by this license.
+
+TagSoup is distributed in the hope that it will be useful, but
+unless required by applicable law or agreed to in writing, TagSoup
+is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
+OF ANY KIND, either express or implied; not even the implied warranty
+of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ =========================================================================
+ == NOTICE file corresponding to the section 4 d of ==
+ == the Apache License, Version 2.0, ==
+ == in this case for Additional Codecs code. ==
+ =========================================================================
+
+Additional Codecs
+These files are Copyright 2003-2010 VisualOn, but released under
+the Apache2 License.
+
+ =========================================================================
+ == NOTICE file corresponding to the section 4 d of ==
+ == the Apache License, Version 2.0, ==
+ == in this case for the Audio Effects code. ==
+ =========================================================================
+
+Audio Effects
+These files are Copyright (C) 2004-2010 NXP Software and
+Copyright (C) 2010 The Android Open Source Project, but released under
+the Apache2 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
+
+
+
+UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE
+
+Unicode Data Files include all data files under the directories
+http://www.unicode.org/Public/, http://www.unicode.org/reports/,
+and http://www.unicode.org/cldr/data/ . Unicode Software includes any
+source code published in the Unicode Standard or under the directories
+http://www.unicode.org/Public/, http://www.unicode.org/reports/, and
+http://www.unicode.org/cldr/data/.
+
+NOTICE TO USER: Carefully read the following legal agreement. BY
+DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S DATA
+FILES ("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"), YOU UNEQUIVOCALLY
+ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE TERMS AND CONDITIONS OF
+THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY,
+DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE.
+
+COPYRIGHT AND PERMISSION NOTICE
+
+Copyright © 1991-2008 Unicode, Inc. All rights reserved. Distributed
+under the Terms of Use in http://www.unicode.org/copyright.html.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Unicode data files and any associated documentation (the
+"Data Files") or Unicode software and any associated documentation (the
+"Software") to deal in the Data Files or Software without restriction,
+including without limitation the rights to use, copy, modify, merge,
+publish, distribute, and/or sell copies of the Data Files or Software,
+and to permit persons to whom the Data Files or Software are furnished to
+do so, provided that (a) the above copyright notice(s) and this permission
+notice appear with all copies of the Data Files or Software, (b) both the
+above copyright notice(s) and this permission notice appear in associated
+documentation, and (c) there is clear notice in each modified Data File
+or in the Software as well as in the documentation associated with the
+Data File(s) or Software that the data or software has been modified.
+
+THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
+INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
+OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
+OR PERFORMANCE OF THE DATA FILES OR SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder
+shall not be used in advertising or otherwise to promote the sale, use
+or other dealings in these Data Files or Software without prior written
+authorization of the copyright holder.
diff --git a/adb/.clang-format b/adb/.clang-format
new file mode 100644
index 0000000..0395c8e
--- /dev/null
+++ b/adb/.clang-format
@@ -0,0 +1,11 @@
+BasedOnStyle: Google
+AllowShortBlocksOnASingleLine: false
+AllowShortFunctionsOnASingleLine: false
+
+CommentPragmas: NOLINT:.*
+DerivePointerAlignment: false
+IndentWidth: 4
+PointerAlignment: Left
+TabWidth: 4
+UseTab: Never
+PenaltyExcessCharacter: 32
diff --git a/adb/Android.mk b/adb/Android.mk
new file mode 100644
index 0000000..355bb7f
--- /dev/null
+++ b/adb/Android.mk
@@ -0,0 +1,271 @@
+# Copyright 2005 The Android Open Source Project
+#
+# Android.mk for adb
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+ifeq ($(HOST_OS),windows)
+ adb_host_clang := false # libc++ for mingw not ready yet.
+else
+ adb_host_clang := true
+endif
+
+adb_version := $(shell git -C $(LOCAL_PATH) rev-parse --short=12 HEAD 2>/dev/null)-android
+
+ADB_COMMON_CFLAGS := \
+ -Wall -Wextra -Werror \
+ -Wno-unused-parameter \
+ -Wno-missing-field-initializers \
+ -DADB_REVISION='"$(adb_version)"' \
+
+# libadb
+# =========================================================
+
+# Much of adb is duplicated in bootable/recovery/minadb and fastboot. Changes
+# made to adb rarely get ported to the other two, so the trees have diverged a
+# bit. We'd like to stop this because it is a maintenance nightmare, but the
+# divergence makes this difficult to do all at once. For now, we will start
+# small by moving common files into a static library. Hopefully some day we can
+# get enough of adb in here that we no longer need minadb. https://b/17626262
+LIBADB_SRC_FILES := \
+ adb.cpp \
+ adb_auth.cpp \
+ adb_io.cpp \
+ adb_listeners.cpp \
+ adb_utils.cpp \
+ sockets.cpp \
+ transport.cpp \
+ transport_local.cpp \
+ transport_usb.cpp \
+
+LIBADB_TEST_SRCS := \
+ adb_io_test.cpp \
+ adb_utils_test.cpp \
+ transport_test.cpp \
+
+LIBADB_CFLAGS := \
+ $(ADB_COMMON_CFLAGS) \
+ -fvisibility=hidden \
+
+LIBADB_linux_CFLAGS := \
+ -std=c++14 \
+
+LIBADB_CFLAGS += $(LIBADB_$(HOST_OS)_CFLAGS)
+
+LIBADB_darwin_SRC_FILES := \
+ fdevent.cpp \
+ get_my_path_darwin.cpp \
+ usb_osx.cpp \
+
+LIBADB_linux_SRC_FILES := \
+ fdevent.cpp \
+ get_my_path_linux.cpp \
+ usb_linux.cpp \
+
+LIBADB_windows_SRC_FILES := \
+ get_my_path_windows.cpp \
+ sysdeps_win32.cpp \
+ usb_windows.cpp \
+
+include $(CLEAR_VARS)
+LOCAL_CLANG := true
+LOCAL_MODULE := libadbd
+LOCAL_CFLAGS := $(LIBADB_CFLAGS) -DADB_HOST=0
+LOCAL_SRC_FILES := \
+ $(LIBADB_SRC_FILES) \
+ adb_auth_client.cpp \
+ fdevent.cpp \
+ jdwp_service.cpp \
+ qemu_tracing.cpp \
+ usb_linux_client.cpp \
+
+LOCAL_SHARED_LIBRARIES := libbase
+
+# Even though we're building a static library (and thus there's no link step for
+# this to take effect), this adds the includes to our path.
+LOCAL_STATIC_LIBRARIES := libbase
+
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_CLANG := $(adb_host_clang)
+LOCAL_MODULE := libadb
+LOCAL_CFLAGS := $(LIBADB_CFLAGS) -DADB_HOST=1
+LOCAL_SRC_FILES := \
+ $(LIBADB_SRC_FILES) \
+ $(LIBADB_$(HOST_OS)_SRC_FILES) \
+ adb_auth_host.cpp \
+
+LOCAL_SHARED_LIBRARIES := libbase
+
+# Even though we're building a static library (and thus there's no link step for
+# this to take effect), this adds the includes to our path.
+LOCAL_STATIC_LIBRARIES := libcrypto_static libbase
+
+ifeq ($(HOST_OS),windows)
+ LOCAL_C_INCLUDES += development/host/windows/usb/api/
+endif
+
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_CLANG := true
+LOCAL_MODULE := adbd_test
+LOCAL_CFLAGS := -DADB_HOST=0 $(LIBADB_CFLAGS)
+LOCAL_SRC_FILES := $(LIBADB_TEST_SRCS)
+LOCAL_STATIC_LIBRARIES := libadbd
+LOCAL_SHARED_LIBRARIES := liblog libbase libcutils
+include $(BUILD_NATIVE_TEST)
+
+ifneq ($(HOST_OS),windows)
+include $(CLEAR_VARS)
+LOCAL_CLANG := $(adb_host_clang)
+LOCAL_MODULE := adb_test
+LOCAL_CFLAGS := -DADB_HOST=1 $(LIBADB_CFLAGS)
+LOCAL_SRC_FILES := $(LIBADB_TEST_SRCS) services.cpp
+LOCAL_SHARED_LIBRARIES := liblog libbase
+LOCAL_STATIC_LIBRARIES := \
+ libadb \
+ libcrypto_static \
+ libcutils \
+
+ifeq ($(HOST_OS),linux)
+ LOCAL_LDLIBS += -lrt -ldl -lpthread
+endif
+
+ifeq ($(HOST_OS),darwin)
+ LOCAL_LDLIBS += -framework CoreFoundation -framework IOKit
+endif
+
+include $(BUILD_HOST_NATIVE_TEST)
+endif
+
+# adb device tracker (used by ddms) test tool
+# =========================================================
+
+ifeq ($(HOST_OS),linux)
+include $(CLEAR_VARS)
+LOCAL_CLANG := $(adb_host_clang)
+LOCAL_MODULE := adb_device_tracker_test
+LOCAL_CFLAGS := -DADB_HOST=1 $(LIBADB_CFLAGS)
+LOCAL_SRC_FILES := test_track_devices.cpp
+LOCAL_SHARED_LIBRARIES := liblog libbase
+LOCAL_STATIC_LIBRARIES := libadb libcrypto_static libcutils
+LOCAL_LDLIBS += -lrt -ldl -lpthread
+include $(BUILD_HOST_EXECUTABLE)
+endif
+
+# adb host tool
+# =========================================================
+include $(CLEAR_VARS)
+
+ifeq ($(HOST_OS),linux)
+ LOCAL_LDLIBS += -lrt -ldl -lpthread
+ LOCAL_CFLAGS += -DWORKAROUND_BUG6558362
+endif
+
+ifeq ($(HOST_OS),darwin)
+ LOCAL_LDLIBS += -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
+ LOCAL_CFLAGS += -Wno-sizeof-pointer-memaccess -Wno-unused-parameter
+endif
+
+ifeq ($(HOST_OS),windows)
+ LOCAL_LDLIBS += -lws2_32 -lgdi32
+ EXTRA_STATIC_LIBS := AdbWinApi
+endif
+
+LOCAL_CLANG := $(adb_host_clang)
+
+LOCAL_SRC_FILES := \
+ client/main.cpp \
+ console.cpp \
+ commandline.cpp \
+ adb_client.cpp \
+ services.cpp \
+ file_sync_client.cpp \
+
+LOCAL_CFLAGS += \
+ $(ADB_COMMON_CFLAGS) \
+ -D_GNU_SOURCE \
+ -DADB_HOST=1 \
+
+LOCAL_MODULE := adb
+LOCAL_MODULE_TAGS := debug
+
+LOCAL_STATIC_LIBRARIES := \
+ libadb \
+ libbase \
+ libcrypto_static \
+ libcutils \
+ liblog \
+ $(EXTRA_STATIC_LIBS) \
+
+# libc++ not available on windows yet
+ifneq ($(HOST_OS),windows)
+ LOCAL_CXX_STL := libc++_static
+endif
+
+# Don't add anything here, we don't want additional shared dependencies
+# on the host adb tool, and shared libraries that link against libc++
+# will violate ODR
+LOCAL_SHARED_LIBRARIES :=
+
+include $(BUILD_HOST_EXECUTABLE)
+
+$(call dist-for-goals,dist_files sdk,$(LOCAL_BUILT_MODULE))
+
+ifeq ($(HOST_OS),windows)
+$(LOCAL_INSTALLED_MODULE): \
+ $(HOST_OUT_EXECUTABLES)/AdbWinApi.dll \
+ $(HOST_OUT_EXECUTABLES)/AdbWinUsbApi.dll
+endif
+
+
+# adbd device daemon
+# =========================================================
+
+include $(CLEAR_VARS)
+
+LOCAL_CLANG := true
+
+LOCAL_SRC_FILES := \
+ daemon/main.cpp \
+ services.cpp \
+ file_sync_service.cpp \
+ framebuffer_service.cpp \
+ remount_service.cpp \
+ set_verity_enable_state_service.cpp \
+
+LOCAL_CFLAGS := \
+ $(ADB_COMMON_CFLAGS) \
+ -DADB_HOST=0 \
+ -D_GNU_SOURCE \
+ -Wno-deprecated-declarations \
+
+LOCAL_CFLAGS += -DALLOW_ADBD_NO_AUTH=$(if $(filter userdebug eng,$(TARGET_BUILD_VARIANT)),1,0)
+
+ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
+LOCAL_CFLAGS += -DALLOW_ADBD_DISABLE_VERITY=1
+LOCAL_CFLAGS += -DALLOW_ADBD_ROOT=1
+endif
+
+LOCAL_MODULE := adbd
+
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN)
+LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED)
+LOCAL_C_INCLUDES += system/extras/ext4_utils
+
+LOCAL_STATIC_LIBRARIES := \
+ libadbd \
+ libbase \
+ libfs_mgr \
+ liblog \
+ libmincrypt \
+ libselinux \
+ libext4_utils_static \
+ libcutils \
+ libbase \
+
+include $(BUILD_EXECUTABLE)
diff --git a/adb/CPPLINT.cfg b/adb/CPPLINT.cfg
new file mode 100644
index 0000000..f496490
--- /dev/null
+++ b/adb/CPPLINT.cfg
@@ -0,0 +1,2 @@
+set noparent
+filter=-build/header_guard,-build/include,-readability/function,-whitespace/indent
diff --git a/adb/MODULE_LICENSE_APACHE2 b/adb/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/adb/MODULE_LICENSE_APACHE2
diff --git a/adb/NOTICE b/adb/NOTICE
new file mode 100644
index 0000000..9ffcc08
--- /dev/null
+++ b/adb/NOTICE
@@ -0,0 +1,191 @@
+
+ Copyright (c) 2006-2009, The Android Open Source Project
+ Copyright 2006, Brian Swetland <swetland@frotz.net>
+
+ 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/adb/OVERVIEW.TXT b/adb/OVERVIEW.TXT
new file mode 100644
index 0000000..c40695a
--- /dev/null
+++ b/adb/OVERVIEW.TXT
@@ -0,0 +1,139 @@
+Implementation notes regarding ADB.
+
+I. General Overview:
+
+The Android Debug Bridge (ADB) is used to:
+
+- keep track of all Android devices and emulators instances
+ connected to or running on a given host developer machine
+
+- implement various control commands (e.g. "adb shell", "adb pull", etc..)
+ for the benefit of clients (command-line users, or helper programs like
+ DDMS). These commands are what is called a 'service' in ADB.
+
+As a whole, everything works through the following components:
+
+ 1. The ADB server
+
+ This is a background process that runs on the host machine. Its purpose
+ if to sense the USB ports to know when devices are attached/removed,
+ as well as when emulator instances start/stop.
+
+ It thus maintains a list of "connected devices" and assigns a 'state'
+ to each one of them: OFFLINE, BOOTLOADER, RECOVERY or ONLINE (more on
+ this below).
+
+ The ADB server is really one giant multiplexing loop whose purpose is
+ to orchestrate the exchange of data (packets, really) between clients,
+ services and devices.
+
+
+ 2. The ADB daemon (adbd)
+
+ The 'adbd' program runs as a background process within an Android device
+ or emulated system. Its purpose is to connect to the ADB server
+ (through USB for devices, through TCP for emulators) and provide a
+ few services for clients that run on the host.
+
+ The ADB server considers that a device is ONLINE when it has successfully
+ connected to the adbd program within it. Otherwise, the device is OFFLINE,
+ meaning that the ADB server detected a new device/emulator, but could not
+ connect to the adbd daemon.
+
+ the BOOTLOADER and RECOVERY states correspond to alternate states of
+ devices when they are in the bootloader or recovery mode.
+
+ 3. The ADB command-line client
+
+ The 'adb' command-line program is used to run adb commands from a shell
+ or a script. It first tries to locate the ADB server on the host machine,
+ and will start one automatically if none is found.
+
+ then, the client sends its service requests to the ADB server. It doesn't
+ need to know.
+
+ Currently, a single 'adb' binary is used for both the server and client.
+ this makes distribution and starting the server easier.
+
+
+ 4. Services
+
+ There are essentially two kinds of services that a client can talk to.
+
+ Host Services:
+ these services run within the ADB Server and thus do not need to
+ communicate with a device at all. A typical example is "adb devices"
+ which is used to return the list of currently known devices and their
+ state. They are a few couple other services though.
+
+ Local Services:
+ these services either run within the adbd daemon, or are started by
+ it on the device. The ADB server is used to multiplex streams
+ between the client and the service running in adbd. In this case
+ its role is to initiate the connection, then of being a pass-through
+ for the data.
+
+
+II. Protocol details:
+
+ 1. Client <-> Server protocol:
+
+ This details the protocol used between ADB clients and the ADB
+ server itself. The ADB server listens on TCP:localhost:5037.
+
+ A client sends a request using the following format:
+
+ 1. A 4-byte hexadecimal string giving the length of the payload
+ 2. Followed by the payload itself.
+
+ For example, to query the ADB server for its internal version number,
+ the client will do the following:
+
+ 1. Connect to tcp:localhost:5037
+ 2. Send the string "000Chost:version" to the corresponding socket
+
+ The 'host:' prefix is used to indicate that the request is addressed
+ to the server itself (we will talk about other kinds of requests later).
+ The content length is encoded in ASCII for easier debugging.
+
+ The server should answer a request with one of the following:
+
+ 1. For success, the 4-byte "OKAY" string
+
+ 2. For failure, the 4-byte "FAIL" string, followed by a
+ 4-byte hex length, followed by a string giving the reason
+ for failure.
+
+ 3. As a special exception, for 'host:version', a 4-byte
+ hex string corresponding to the server's internal version number
+
+ Note that the connection is still alive after an OKAY, which allows the
+ client to make other requests. But in certain cases, an OKAY will even
+ change the state of the connection.
+
+ For example, the case of the 'host:transport:<serialnumber>' request,
+ where '<serialnumber>' is used to identify a given device/emulator; after
+ the "OKAY" answer, all further requests made by the client will go
+ directly to the corresponding adbd daemon.
+
+ The file SERVICES.TXT lists all services currently implemented by ADB.
+
+
+ 2. Transports:
+
+ An ADB transport models a connection between the ADB server and one device
+ or emulator. There are currently two kinds of transports:
+
+ - USB transports, for physical devices through USB
+
+ - Local transports, for emulators running on the host, connected to
+ the server through TCP
+
+ In theory, it should be possible to write a local transport that proxies
+ a connection between an ADB server and a device/emulator connected to/
+ running on another machine. This hasn't been done yet though.
+
+ Each transport can carry one or more multiplexed streams between clients
+ and the device/emulator they point to. The ADB server must handle
+ unexpected transport disconnections (e.g. when a device is physically
+ unplugged) properly.
diff --git a/adb/SERVICES.TXT b/adb/SERVICES.TXT
new file mode 100644
index 0000000..63000f2
--- /dev/null
+++ b/adb/SERVICES.TXT
@@ -0,0 +1,259 @@
+This file tries to document all requests a client can make
+to the ADB server of an adbd daemon. See the OVERVIEW.TXT document
+to understand what's going on here.
+
+HOST SERVICES:
+
+host:version
+ Ask the ADB server for its internal version number.
+
+ As a special exception, the server will respond with a 4-byte
+ hex string corresponding to its internal version number, without
+ any OKAY or FAIL.
+
+host:kill
+ Ask the ADB server to quit immediately. This is used when the
+ ADB client detects that an obsolete server is running after an
+ upgrade.
+
+host:devices
+host:devices-l
+ Ask to return the list of available Android devices and their
+ state. devices-l includes the device paths in the state.
+ After the OKAY, this is followed by a 4-byte hex len,
+ and a string that will be dumped as-is by the client, then
+ the connection is closed
+
+host:track-devices
+ This is a variant of host:devices which doesn't close the
+ connection. Instead, a new device list description is sent
+ each time a device is added/removed or the state of a given
+ device changes (hex4 + content). This allows tools like DDMS
+ to track the state of connected devices in real-time without
+ polling the server repeatedly.
+
+host:emulator:<port>
+ This is a special query that is sent to the ADB server when a
+ new emulator starts up. <port> is a decimal number corresponding
+ to the emulator's ADB control port, i.e. the TCP port that the
+ emulator will forward automatically to the adbd daemon running
+ in the emulator system.
+
+ This mechanism allows the ADB server to know when new emulator
+ instances start.
+
+host:transport:<serial-number>
+ Ask to switch the connection to the device/emulator identified by
+ <serial-number>. After the OKAY response, every client request will
+ be sent directly to the adbd daemon running on the device.
+ (Used to implement the -s option)
+
+host:transport-usb
+ Ask to switch the connection to one device connected through USB
+ to the host machine. This will fail if there are more than one such
+ devices. (Used to implement the -d convenience option)
+
+host:transport-local
+ Ask to switch the connection to one emulator connected through TCP.
+ This will fail if there is more than one such emulator instance
+ running. (Used to implement the -e convenience option)
+
+host:transport-any
+ Another host:transport variant. Ask to switch the connection to
+ either the device or emulator connect to/running on the host.
+ Will fail if there is more than one such device/emulator available.
+ (Used when neither -s, -d or -e are provided)
+
+host-serial:<serial-number>:<request>
+ This is a special form of query, where the 'host-serial:<serial-number>:'
+ prefix can be used to indicate that the client is asking the ADB server
+ for information related to a specific device. <request> can be in one
+ of the format described below.
+
+host-usb:<request>
+ A variant of host-serial used to target the single USB device connected
+ to the host. This will fail if there is none or more than one.
+
+host-local:<request>
+ A variant of host-serial used to target the single emulator instance
+ running on the host. This will fail if there is none or more than one.
+
+host:<request>
+ When asking for information related to a device, 'host:' can also be
+ interpreted as 'any single device or emulator connected to/running on
+ the host'.
+
+<host-prefix>:get-product
+ XXX
+
+<host-prefix>:get-serialno
+ Returns the serial number of the corresponding device/emulator.
+ Note that emulator serial numbers are of the form "emulator-5554"
+
+<host-prefix>:get-devpath
+ Returns the device path of the corresponding device/emulator.
+
+<host-prefix>:get-state
+ Returns the state of a given device as a string.
+
+<host-prefix>:forward:<local>;<remote>
+ Asks the ADB server to forward local connections from <local>
+ to the <remote> address on a given device.
+
+ There, <host-prefix> can be one of the
+ host-serial/host-usb/host-local/host prefixes as described previously
+ and indicates which device/emulator to target.
+
+ the format of <local> is one of:
+
+ tcp:<port> -> TCP connection on localhost:<port>
+ local:<path> -> Unix local domain socket on <path>
+
+ the format of <remote> is one of:
+
+ tcp:<port> -> TCP localhost:<port> on device
+ local:<path> -> Unix local domain socket on device
+ jdwp:<pid> -> JDWP thread on VM process <pid>
+
+ or even any one of the local services described below.
+
+<host-prefix>:forward:norebind:<local>;<remote>
+ Same as <host-prefix>:forward:<local>;<remote> except that it will
+ fail it there is already a forward connection from <local>.
+
+ Used to implement 'adb forward --no-rebind <local> <remote>'
+
+<host-prefix>:killforward:<local>
+ Remove any existing forward local connection from <local>.
+ This is used to implement 'adb forward --remove <local>'
+
+<host-prefix>:killforward-all
+ Remove all forward network connections.
+ This is used to implement 'adb forward --remove-all'.
+
+<host-prefix>:list-forward
+ List all existing forward connections from this server.
+ This returns something that looks like the following:
+
+ <hex4>: The length of the payload, as 4 hexadecimal chars.
+ <payload>: A series of lines of the following format:
+
+ <serial> " " <local> " " <remote> "\n"
+
+ Where <serial> is a device serial number.
+ <local> is the host-specific endpoint (e.g. tcp:9000).
+ <remote> is the device-specific endpoint.
+
+ Used to implement 'adb forward --list'.
+
+LOCAL SERVICES:
+
+All the queries below assumed that you already switched the transport
+to a real device, or that you have used a query prefix as described
+above.
+
+shell:command arg1 arg2 ...
+ Run 'command arg1 arg2 ...' in a shell on the device, and return
+ its output and error streams. Note that arguments must be separated
+ by spaces. If an argument contains a space, it must be quoted with
+ double-quotes. Arguments cannot contain double quotes or things
+ will go very wrong.
+
+ Note that this is the non-interactive version of "adb shell"
+
+shell:
+ Start an interactive shell session on the device. Redirect
+ stdin/stdout/stderr as appropriate. Note that the ADB server uses
+ this to implement "adb shell", but will also cook the input before
+ sending it to the device (see interactive_shell() in commandline.c)
+
+remount:
+ Ask adbd to remount the device's filesystem in read-write mode,
+ instead of read-only. This is usually necessary before performing
+ an "adb sync" or "adb push" request.
+
+ This request may not succeed on certain builds which do not allow
+ that.
+
+dev:<path>
+ Opens a device file and connects the client directly to it for
+ read/write purposes. Useful for debugging, but may require special
+ privileges and thus may not run on all devices. <path> is a full
+ path from the root of the filesystem.
+
+tcp:<port>
+ Tries to connect to tcp port <port> on localhost.
+
+tcp:<port>:<server-name>
+ Tries to connect to tcp port <port> on machine <server-name> from
+ the device. This can be useful to debug some networking/proxy
+ issues that can only be revealed on the device itself.
+
+local:<path>
+ Tries to connect to a Unix domain socket <path> on the device
+
+localreserved:<path>
+localabstract:<path>
+localfilesystem:<path>
+ Variants of local:<path> that are used to access other Android
+ socket namespaces.
+
+framebuffer:
+ This service is used to send snapshots of the framebuffer to a client.
+ It requires sufficient privileges but works as follow:
+
+ After the OKAY, the service sends 16-byte binary structure
+ containing the following fields (little-endian format):
+
+ depth: uint32_t: framebuffer depth
+ size: uint32_t: framebuffer size in bytes
+ width: uint32_t: framebuffer width in pixels
+ height: uint32_t: framebuffer height in pixels
+
+ With the current implementation, depth is always 16, and
+ size is always width*height*2
+
+ Then, each time the client wants a snapshot, it should send
+ one byte through the channel, which will trigger the service
+ to send it 'size' bytes of framebuffer data.
+
+ If the adbd daemon doesn't have sufficient privileges to open
+ the framebuffer device, the connection is simply closed immediately.
+
+jdwp:<pid>
+ Connects to the JDWP thread running in the VM of process <pid>.
+
+track-jdwp
+ This is used to send the list of JDWP pids periodically to the client.
+ The format of the returned data is the following:
+
+ <hex4>: the length of all content as a 4-char hexadecimal string
+ <content>: a series of ASCII lines of the following format:
+ <pid> "\n"
+
+ This service is used by DDMS to know which debuggable processes are running
+ on the device/emulator.
+
+ Note that there is no single-shot service to retrieve the list only once.
+
+sync:
+ This starts the file synchronisation service, used to implement "adb push"
+ and "adb pull". Since this service is pretty complex, it will be detailed
+ in a companion document named SYNC.TXT
+
+reverse:<forward-command>
+ This implements the 'adb reverse' feature, i.e. the ability to reverse
+ socket connections from a device to the host. <forward-command> is one
+ of the forwarding commands that are described above, as in:
+
+ list-forward
+ forward:<local>;<remote>
+ forward:norebind:<local>;<remote>
+ killforward-all
+ killforward:<local>
+
+ Note that in this case, <local> corresponds to the socket on the device
+ and <remote> corresponds to the socket on the host.
+
+ The output of reverse:list-forward is the same as host:list-forward
+ except that <serial> will be just 'host'.
diff --git a/adb/SYNC.TXT b/adb/SYNC.TXT
new file mode 100644
index 0000000..e74d217
--- /dev/null
+++ b/adb/SYNC.TXT
@@ -0,0 +1,84 @@
+This file tries to document file related requests a client can make
+to the ADB server of an adbd daemon. See the OVERVIEW.TXT document
+to understand what's going on here. See the SERVICES.TXT to learn more
+about the other requests that are possible.
+
+SYNC SERVICES:
+
+
+Requesting the sync service ("sync:") using the protocol as described in
+SERVICES.TXT sets the connection in sync mode. This mode is a binary mode that
+differ from the regular adb protocol. The connection stays in sync mode until
+explicitly terminated (see below).
+
+After the initial "sync:" command is sent the server must respond with either
+"OKAY" or "FAIL" as per usual.
+
+In sync mode both the server and the client will frequently use eight-byte
+packets to communicate in this document called sync request and sync
+responses. The first four bytes is an id and specifies sync request is
+represented by four utf-8 characters. The last four bytes is a Little-Endian
+integer, with various uses. This number will be called "length" below. In fact
+all binary integers are Little-Endian in the sync mode. Sync mode is
+implicitly exited after each sync request, and normal adb communication
+follows as described in SERVICES.TXT.
+
+The following sync requests are accepted:
+LIST - List the files in a folder
+SEND - Send a file to device
+RECV - Retreive a file from device
+
+Not yet documented:
+STAT - Stat a file
+ULNK - Unlink (remove) a file. (Not currently supported)
+
+For all of the sync request above the must be followed by length number of
+bytes containing an utf-8 string with a remote filename.
+
+LIST:
+Lists files in the directory specified by the remote filename. The server will
+respond with zero or more directory entries or "dents".
+
+The directory entries will be returned in the following form
+1. A four-byte sync response id beeing "DENT"
+2. A four-byte integer representing file mode.
+3. A four-byte integer representing file size.
+4. A four-byte integer representing last modified time.
+5. A four-byte integer representing file name length.
+6. length number of bytes containing an utf-8 string representing the file
+ name.
+
+When an sync response "DONE" is received the listing is done.
+
+SEND:
+The remote file name is split into two parts separated by the last
+comma (","). The first part is the actual path, while the second is a decimal
+encoded file mode containing the permissions of the file on device.
+
+Note that some file types will be deleted before the copying starts, and if
+the transfer fails. Some file types will not be deleted, which allows
+ adb push disk_image /some_block_device
+to work.
+
+After this the actual file is sent in chunks. Each chucks has the following
+format.
+A sync request with id "DATA" and length equal to the chunk size. After
+follows chunk size number of bytes. This is repeated until the file is
+transfered. Each chunk must not be larger than 64k.
+
+When the file is tranfered a sync request "DONE" is sent, where length is set
+to the last modified time for the file. The server responds to this last
+request (but not to chuck requests) with an "OKAY" sync response (length can
+be ignored).
+
+
+RECV:
+Retrieves a file from device to a local file. The remote path is the path to
+the file that will be returned. Just as for the SEND sync request the file
+received is split up into chunks. The sync response id is "DATA" and length is
+the chuck size. After follows chunk size number of bytes. This is repeated
+until the file is transfered. Each chuck will not be larger than 64k.
+
+When the file is transfered a sync resopnse "DONE" is retrieved where the
+length can be ignored.
+
diff --git a/adb/__init__.py b/adb/__init__.py
new file mode 100644
index 0000000..6b509c6
--- /dev/null
+++ b/adb/__init__.py
@@ -0,0 +1,17 @@
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+from __future__ import absolute_import
+from .device import * # pylint: disable=wildcard-import
diff --git a/adb/adb.cpp b/adb/adb.cpp
new file mode 100644
index 0000000..97ce125
--- /dev/null
+++ b/adb/adb.cpp
@@ -0,0 +1,959 @@
+/*
+ * Copyright (C) 2007 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 TRACE_TAG TRACE_ADB
+
+#include "sysdeps.h"
+#include "adb.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include <string>
+#include <vector>
+#include <unordered_map>
+
+#include <base/logging.h>
+#include <base/stringprintf.h>
+#include <base/strings.h>
+
+#include "adb_auth.h"
+#include "adb_io.h"
+#include "adb_listeners.h"
+#include "adb_utils.h"
+#include "transport.h"
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+
+#if !ADB_HOST
+#include <cutils/properties.h>
+#include <sys/capability.h>
+#include <sys/mount.h>
+#endif
+
+ADB_MUTEX_DEFINE(D_lock);
+
+int HOST = 0;
+
+#if !ADB_HOST
+const char* adb_device_banner = "device";
+static android::base::LogdLogger gLogdLogger;
+#endif
+
+void AdbLogger(android::base::LogId id, android::base::LogSeverity severity,
+ const char* tag, const char* file, unsigned int line,
+ const char* message) {
+ android::base::StderrLogger(id, severity, tag, file, line, message);
+#if !ADB_HOST
+ gLogdLogger(id, severity, tag, file, line, message);
+#endif
+}
+
+void fatal(const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ fprintf(stderr, "error: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+ exit(-1);
+}
+
+void fatal_errno(const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ fprintf(stderr, "error: %s: ", strerror(errno));
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+ exit(-1);
+}
+
+#if !ADB_HOST
+static std::string get_log_file_name() {
+ struct tm now;
+ time_t t;
+ tzset();
+ time(&t);
+ localtime_r(&t, &now);
+
+ char timestamp[PATH_MAX];
+ strftime(timestamp, sizeof(timestamp), "%Y-%m-%d-%H-%M-%S", &now);
+
+ return android::base::StringPrintf("/data/adb/adb-%s-%d", timestamp,
+ getpid());
+}
+
+void start_device_log(void) {
+ int fd = unix_open(get_log_file_name().c_str(),
+ O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0640);
+ if (fd == -1) {
+ return;
+ }
+
+ // Redirect stdout and stderr to the log file.
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+ fprintf(stderr, "--- adb starting (pid %d) ---\n", getpid());
+ unix_close(fd);
+}
+#endif
+
+int adb_trace_mask;
+
+std::string get_trace_setting_from_env() {
+ const char* setting = getenv("ADB_TRACE");
+ if (setting == nullptr) {
+ setting = "";
+ }
+
+ return std::string(setting);
+}
+
+#if !ADB_HOST
+std::string get_trace_setting_from_prop() {
+ char buf[PROPERTY_VALUE_MAX];
+ property_get("persist.adb.trace_mask", buf, "");
+ return std::string(buf);
+}
+#endif
+
+std::string get_trace_setting() {
+#if ADB_HOST
+ return get_trace_setting_from_env();
+#else
+ return get_trace_setting_from_prop();
+#endif
+}
+
+// Split the space separated list of tags from the trace setting and build the
+// trace mask from it. note that '1' and 'all' are special cases to enable all
+// tracing.
+//
+// adb's trace setting comes from the ADB_TRACE environment variable, whereas
+// adbd's comes from the system property persist.adb.trace_mask.
+static void setup_trace_mask() {
+ const std::string trace_setting = get_trace_setting();
+
+ std::unordered_map<std::string, int> trace_flags = {
+ {"1", 0},
+ {"all", 0},
+ {"adb", TRACE_ADB},
+ {"sockets", TRACE_SOCKETS},
+ {"packets", TRACE_PACKETS},
+ {"rwx", TRACE_RWX},
+ {"usb", TRACE_USB},
+ {"sync", TRACE_SYNC},
+ {"sysdeps", TRACE_SYSDEPS},
+ {"transport", TRACE_TRANSPORT},
+ {"jdwp", TRACE_JDWP},
+ {"services", TRACE_SERVICES},
+ {"auth", TRACE_AUTH}};
+
+ std::vector<std::string> elements = android::base::Split(trace_setting, " ");
+ for (const auto& elem : elements) {
+ const auto& flag = trace_flags.find(elem);
+ if (flag == trace_flags.end()) {
+ D("Unknown trace flag: %s\n", flag->first.c_str());
+ continue;
+ }
+
+ if (flag->second == 0) {
+ // 0 is used for the special values "1" and "all" that enable all
+ // tracing.
+ adb_trace_mask = ~0;
+ return;
+ } else {
+ adb_trace_mask |= 1 << flag->second;
+ }
+ }
+}
+
+void adb_trace_init(char** argv) {
+ // Don't open log file if no tracing, since this will block
+ // the crypto unmount of /data
+ const std::string trace_setting = get_trace_setting();
+ if (trace_setting.empty()) {
+ return;
+ }
+
+#if !ADB_HOST
+ if (isatty(STDOUT_FILENO) == 0) {
+ start_device_log();
+ }
+#endif
+
+ setup_trace_mask();
+ android::base::InitLogging(argv, AdbLogger);
+}
+
+apacket* get_apacket(void)
+{
+ apacket* p = reinterpret_cast<apacket*>(malloc(sizeof(apacket)));
+ if (p == nullptr) {
+ fatal("failed to allocate an apacket");
+ }
+
+ memset(p, 0, sizeof(apacket) - MAX_PAYLOAD);
+ return p;
+}
+
+void put_apacket(apacket *p)
+{
+ free(p);
+}
+
+void handle_online(atransport *t)
+{
+ D("adb: online\n");
+ t->online = 1;
+}
+
+void handle_offline(atransport *t)
+{
+ D("adb: offline\n");
+ //Close the associated usb
+ t->online = 0;
+ run_transport_disconnects(t);
+}
+
+#if DEBUG_PACKETS
+#define DUMPMAX 32
+void print_packet(const char *label, apacket *p)
+{
+ char *tag;
+ char *x;
+ unsigned count;
+
+ switch(p->msg.command){
+ case A_SYNC: tag = "SYNC"; break;
+ case A_CNXN: tag = "CNXN" ; break;
+ case A_OPEN: tag = "OPEN"; break;
+ case A_OKAY: tag = "OKAY"; break;
+ case A_CLSE: tag = "CLSE"; break;
+ case A_WRTE: tag = "WRTE"; break;
+ case A_AUTH: tag = "AUTH"; break;
+ default: tag = "????"; break;
+ }
+
+ fprintf(stderr, "%s: %s %08x %08x %04x \"",
+ label, tag, p->msg.arg0, p->msg.arg1, p->msg.data_length);
+ count = p->msg.data_length;
+ x = (char*) p->data;
+ if(count > DUMPMAX) {
+ count = DUMPMAX;
+ tag = "\n";
+ } else {
+ tag = "\"\n";
+ }
+ while(count-- > 0){
+ if((*x >= ' ') && (*x < 127)) {
+ fputc(*x, stderr);
+ } else {
+ fputc('.', stderr);
+ }
+ x++;
+ }
+ fputs(tag, stderr);
+}
+#endif
+
+static void send_ready(unsigned local, unsigned remote, atransport *t)
+{
+ D("Calling send_ready \n");
+ apacket *p = get_apacket();
+ p->msg.command = A_OKAY;
+ p->msg.arg0 = local;
+ p->msg.arg1 = remote;
+ send_packet(p, t);
+}
+
+static void send_close(unsigned local, unsigned remote, atransport *t)
+{
+ D("Calling send_close \n");
+ apacket *p = get_apacket();
+ p->msg.command = A_CLSE;
+ p->msg.arg0 = local;
+ p->msg.arg1 = remote;
+ send_packet(p, t);
+}
+
+static size_t fill_connect_data(char *buf, size_t bufsize)
+{
+#if ADB_HOST
+ return snprintf(buf, bufsize, "host::") + 1;
+#else
+ static const char *cnxn_props[] = {
+ "ro.product.name",
+ "ro.product.model",
+ "ro.product.device",
+ };
+ static const int num_cnxn_props = ARRAY_SIZE(cnxn_props);
+ int i;
+ size_t remaining = bufsize;
+ size_t len;
+
+ len = snprintf(buf, remaining, "%s::", adb_device_banner);
+ remaining -= len;
+ buf += len;
+ for (i = 0; i < num_cnxn_props; i++) {
+ char value[PROPERTY_VALUE_MAX];
+ property_get(cnxn_props[i], value, "");
+ len = snprintf(buf, remaining, "%s=%s;", cnxn_props[i], value);
+ remaining -= len;
+ buf += len;
+ }
+
+ return bufsize - remaining + 1;
+#endif
+}
+
+void send_connect(atransport *t)
+{
+ D("Calling send_connect \n");
+ apacket *cp = get_apacket();
+ cp->msg.command = A_CNXN;
+ cp->msg.arg0 = t->get_protocol_version();
+ cp->msg.arg1 = t->get_max_payload();
+ cp->msg.data_length = fill_connect_data((char *)cp->data,
+ MAX_PAYLOAD_V1);
+ send_packet(cp, t);
+}
+
+// qual_overwrite is used to overwrite a qualifier string. dst is a
+// pointer to a char pointer. It is assumed that if *dst is non-NULL, it
+// was malloc'ed and needs to freed. *dst will be set to a dup of src.
+// TODO: switch to std::string for these atransport fields instead.
+static void qual_overwrite(char** dst, const std::string& src) {
+ free(*dst);
+ *dst = strdup(src.c_str());
+}
+
+void parse_banner(const char* banner, atransport* t) {
+ D("parse_banner: %s\n", banner);
+
+ // The format is something like:
+ // "device::ro.product.name=x;ro.product.model=y;ro.product.device=z;".
+ std::vector<std::string> pieces = android::base::Split(banner, ":");
+
+ if (pieces.size() > 2) {
+ const std::string& props = pieces[2];
+ for (auto& prop : android::base::Split(props, ";")) {
+ // The list of properties was traditionally ;-terminated rather than ;-separated.
+ if (prop.empty()) continue;
+
+ std::vector<std::string> key_value = android::base::Split(prop, "=");
+ if (key_value.size() != 2) continue;
+
+ const std::string& key = key_value[0];
+ const std::string& value = key_value[1];
+ if (key == "ro.product.name") {
+ qual_overwrite(&t->product, value);
+ } else if (key == "ro.product.model") {
+ qual_overwrite(&t->model, value);
+ } else if (key == "ro.product.device") {
+ qual_overwrite(&t->device, value);
+ }
+ }
+ }
+
+ const std::string& type = pieces[0];
+ if (type == "bootloader") {
+ D("setting connection_state to kCsBootloader\n");
+ t->connection_state = kCsBootloader;
+ update_transports();
+ } else if (type == "device") {
+ D("setting connection_state to kCsDevice\n");
+ t->connection_state = kCsDevice;
+ update_transports();
+ } else if (type == "recovery") {
+ D("setting connection_state to kCsRecovery\n");
+ t->connection_state = kCsRecovery;
+ update_transports();
+ } else if (type == "sideload") {
+ D("setting connection_state to kCsSideload\n");
+ t->connection_state = kCsSideload;
+ update_transports();
+ } else {
+ D("setting connection_state to kCsHost\n");
+ t->connection_state = kCsHost;
+ }
+}
+
+void handle_packet(apacket *p, atransport *t)
+{
+ asocket *s;
+
+ D("handle_packet() %c%c%c%c\n", ((char*) (&(p->msg.command)))[0],
+ ((char*) (&(p->msg.command)))[1],
+ ((char*) (&(p->msg.command)))[2],
+ ((char*) (&(p->msg.command)))[3]);
+ print_packet("recv", p);
+
+ switch(p->msg.command){
+ case A_SYNC:
+ if(p->msg.arg0){
+ send_packet(p, t);
+ if(HOST) send_connect(t);
+ } else {
+ t->connection_state = kCsOffline;
+ handle_offline(t);
+ send_packet(p, t);
+ }
+ return;
+
+ case A_CNXN: /* CONNECT(version, maxdata, "system-id-string") */
+ if(t->connection_state != kCsOffline) {
+ t->connection_state = kCsOffline;
+ handle_offline(t);
+ }
+
+ t->update_version(p->msg.arg0, p->msg.arg1);
+ parse_banner(reinterpret_cast<const char*>(p->data), t);
+
+ if (HOST || !auth_required) {
+ handle_online(t);
+ if (!HOST) send_connect(t);
+ } else {
+ send_auth_request(t);
+ }
+ break;
+
+ case A_AUTH:
+ if (p->msg.arg0 == ADB_AUTH_TOKEN) {
+ t->connection_state = kCsUnauthorized;
+ t->key = adb_auth_nextkey(t->key);
+ if (t->key) {
+ send_auth_response(p->data, p->msg.data_length, t);
+ } else {
+ /* No more private keys to try, send the public key */
+ send_auth_publickey(t);
+ }
+ } else if (p->msg.arg0 == ADB_AUTH_SIGNATURE) {
+ if (adb_auth_verify(t->token, p->data, p->msg.data_length)) {
+ adb_auth_verified(t);
+ t->failed_auth_attempts = 0;
+ } else {
+ if (t->failed_auth_attempts++ > 10)
+ adb_sleep_ms(1000);
+ send_auth_request(t);
+ }
+ } else if (p->msg.arg0 == ADB_AUTH_RSAPUBLICKEY) {
+ adb_auth_confirm_key(p->data, p->msg.data_length, t);
+ }
+ break;
+
+ case A_OPEN: /* OPEN(local-id, 0, "destination") */
+ if (t->online && p->msg.arg0 != 0 && p->msg.arg1 == 0) {
+ char *name = (char*) p->data;
+ name[p->msg.data_length > 0 ? p->msg.data_length - 1 : 0] = 0;
+ s = create_local_service_socket(name);
+ if(s == 0) {
+ send_close(0, p->msg.arg0, t);
+ } else {
+ s->peer = create_remote_socket(p->msg.arg0, t);
+ s->peer->peer = s;
+ send_ready(s->id, s->peer->id, t);
+ s->ready(s);
+ }
+ }
+ break;
+
+ case A_OKAY: /* READY(local-id, remote-id, "") */
+ if (t->online && p->msg.arg0 != 0 && p->msg.arg1 != 0) {
+ if((s = find_local_socket(p->msg.arg1, 0))) {
+ if(s->peer == 0) {
+ /* On first READY message, create the connection. */
+ s->peer = create_remote_socket(p->msg.arg0, t);
+ s->peer->peer = s;
+ s->ready(s);
+ } else if (s->peer->id == p->msg.arg0) {
+ /* Other READY messages must use the same local-id */
+ s->ready(s);
+ } else {
+ D("Invalid A_OKAY(%d,%d), expected A_OKAY(%d,%d) on transport %s\n",
+ p->msg.arg0, p->msg.arg1, s->peer->id, p->msg.arg1, t->serial);
+ }
+ }
+ }
+ break;
+
+ case A_CLSE: /* CLOSE(local-id, remote-id, "") or CLOSE(0, remote-id, "") */
+ if (t->online && p->msg.arg1 != 0) {
+ if((s = find_local_socket(p->msg.arg1, p->msg.arg0))) {
+ /* According to protocol.txt, p->msg.arg0 might be 0 to indicate
+ * a failed OPEN only. However, due to a bug in previous ADB
+ * versions, CLOSE(0, remote-id, "") was also used for normal
+ * CLOSE() operations.
+ *
+ * This is bad because it means a compromised adbd could
+ * send packets to close connections between the host and
+ * other devices. To avoid this, only allow this if the local
+ * socket has a peer on the same transport.
+ */
+ if (p->msg.arg0 == 0 && s->peer && s->peer->transport != t) {
+ D("Invalid A_CLSE(0, %u) from transport %s, expected transport %s\n",
+ p->msg.arg1, t->serial, s->peer->transport->serial);
+ } else {
+ s->close(s);
+ }
+ }
+ }
+ break;
+
+ case A_WRTE: /* WRITE(local-id, remote-id, <data>) */
+ if (t->online && p->msg.arg0 != 0 && p->msg.arg1 != 0) {
+ if((s = find_local_socket(p->msg.arg1, p->msg.arg0))) {
+ unsigned rid = p->msg.arg0;
+ p->len = p->msg.data_length;
+
+ if(s->enqueue(s, p) == 0) {
+ D("Enqueue the socket\n");
+ send_ready(s->id, rid, t);
+ }
+ return;
+ }
+ }
+ break;
+
+ default:
+ printf("handle_packet: what is %08x?!\n", p->msg.command);
+ }
+
+ put_apacket(p);
+}
+
+#if ADB_HOST
+
+int launch_server(int server_port)
+{
+#if defined(_WIN32)
+ /* we need to start the server in the background */
+ /* we create a PIPE that will be used to wait for the server's "OK" */
+ /* message since the pipe handles must be inheritable, we use a */
+ /* security attribute */
+ HANDLE nul_read, nul_write;
+ HANDLE pipe_read, pipe_write;
+ HANDLE stdout_handle, stderr_handle;
+ SECURITY_ATTRIBUTES sa;
+ STARTUPINFO startup;
+ PROCESS_INFORMATION pinfo;
+ char program_path[ MAX_PATH ];
+ int ret;
+
+ sa.nLength = sizeof(sa);
+ sa.lpSecurityDescriptor = NULL;
+ sa.bInheritHandle = TRUE;
+
+ /* Redirect stdin and stderr to Windows /dev/null. If we instead pass our
+ * stdin/stderr handles and they are console handles, when the adb server
+ * starts up, the C Runtime will see console handles for a process that
+ * isn't connected to a console and it will configure stderr to be closed.
+ * At that point, freopen() could be used to reopen stderr, but it would
+ * take more massaging to fixup the file descriptor number that freopen()
+ * uses. It's simplest to avoid all of this complexity by just redirecting
+ * stdin/stderr to `nul' and then the C Runtime acts as expected.
+ */
+ nul_read = CreateFileW(L"nul", GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, &sa,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (nul_read == INVALID_HANDLE_VALUE) {
+ fprintf(stderr, "CreateFileW(nul, GENERIC_READ) failure, error %ld\n",
+ GetLastError());
+ return -1;
+ }
+
+ nul_write = CreateFileW(L"nul", GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, &sa,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (nul_write == INVALID_HANDLE_VALUE) {
+ fprintf(stderr, "CreateFileW(nul, GENERIC_WRITE) failure, error %ld\n",
+ GetLastError());
+ CloseHandle(nul_read);
+ return -1;
+ }
+
+ /* create pipe, and ensure its read handle isn't inheritable */
+ ret = CreatePipe( &pipe_read, &pipe_write, &sa, 0 );
+ if (!ret) {
+ fprintf(stderr, "CreatePipe() failure, error %ld\n", GetLastError() );
+ CloseHandle(nul_read);
+ CloseHandle(nul_write);
+ return -1;
+ }
+
+ SetHandleInformation( pipe_read, HANDLE_FLAG_INHERIT, 0 );
+
+ /* Some programs want to launch an adb command and collect its output by
+ * calling CreateProcess with inheritable stdout/stderr handles, then
+ * using read() to get its output. When this happens, the stdout/stderr
+ * handles passed to the adb client process will also be inheritable.
+ * When starting the adb server here, care must be taken to reset them
+ * to non-inheritable.
+ * Otherwise, something bad happens: even if the adb command completes,
+ * the calling process is stuck while read()-ing from the stdout/stderr
+ * descriptors, because they're connected to corresponding handles in the
+ * adb server process (even if the latter never uses/writes to them).
+ */
+ stdout_handle = GetStdHandle( STD_OUTPUT_HANDLE );
+ stderr_handle = GetStdHandle( STD_ERROR_HANDLE );
+ if (stdout_handle != INVALID_HANDLE_VALUE) {
+ SetHandleInformation( stdout_handle, HANDLE_FLAG_INHERIT, 0 );
+ }
+ if (stderr_handle != INVALID_HANDLE_VALUE) {
+ SetHandleInformation( stderr_handle, HANDLE_FLAG_INHERIT, 0 );
+ }
+
+ ZeroMemory( &startup, sizeof(startup) );
+ startup.cb = sizeof(startup);
+ startup.hStdInput = nul_read;
+ startup.hStdOutput = pipe_write;
+ startup.hStdError = nul_write;
+ startup.dwFlags = STARTF_USESTDHANDLES;
+
+ ZeroMemory( &pinfo, sizeof(pinfo) );
+
+ /* get path of current program */
+ GetModuleFileName( NULL, program_path, sizeof(program_path) );
+ char args[64];
+ snprintf(args, sizeof(args), "adb -P %d fork-server server", server_port);
+ ret = CreateProcess(
+ program_path, /* program path */
+ args,
+ /* the fork-server argument will set the
+ debug = 2 in the child */
+ NULL, /* process handle is not inheritable */
+ NULL, /* thread handle is not inheritable */
+ TRUE, /* yes, inherit some handles */
+ DETACHED_PROCESS, /* the new process doesn't have a console */
+ NULL, /* use parent's environment block */
+ NULL, /* use parent's starting directory */
+ &startup, /* startup info, i.e. std handles */
+ &pinfo );
+
+ CloseHandle( nul_read );
+ CloseHandle( nul_write );
+ CloseHandle( pipe_write );
+
+ if (!ret) {
+ fprintf(stderr, "CreateProcess failure, error %ld\n", GetLastError() );
+ CloseHandle( pipe_read );
+ return -1;
+ }
+
+ CloseHandle( pinfo.hProcess );
+ CloseHandle( pinfo.hThread );
+
+ /* wait for the "OK\n" message */
+ {
+ char temp[3];
+ DWORD count;
+
+ ret = ReadFile( pipe_read, temp, 3, &count, NULL );
+ CloseHandle( pipe_read );
+ if ( !ret ) {
+ fprintf(stderr, "could not read ok from ADB Server, error = %ld\n", GetLastError() );
+ return -1;
+ }
+ if (count != 3 || temp[0] != 'O' || temp[1] != 'K' || temp[2] != '\n') {
+ fprintf(stderr, "ADB server didn't ACK\n" );
+ return -1;
+ }
+ }
+#else /* !defined(_WIN32) */
+ char path[PATH_MAX];
+ int fd[2];
+
+ // set up a pipe so the child can tell us when it is ready.
+ // fd[0] will be parent's end, and fd[1] will get mapped to stderr in the child.
+ if (pipe(fd)) {
+ fprintf(stderr, "pipe failed in launch_server, errno: %d\n", errno);
+ return -1;
+ }
+ get_my_path(path, PATH_MAX);
+ pid_t pid = fork();
+ if(pid < 0) return -1;
+
+ if (pid == 0) {
+ // child side of the fork
+
+ // redirect stderr to the pipe
+ // we use stderr instead of stdout due to stdout's buffering behavior.
+ adb_close(fd[0]);
+ dup2(fd[1], STDERR_FILENO);
+ adb_close(fd[1]);
+
+ char str_port[30];
+ snprintf(str_port, sizeof(str_port), "%d", server_port);
+ // child process
+ int result = execl(path, "adb", "-P", str_port, "fork-server", "server", NULL);
+ // this should not return
+ fprintf(stderr, "OOPS! execl returned %d, errno: %d\n", result, errno);
+ } else {
+ // parent side of the fork
+
+ char temp[3];
+
+ temp[0] = 'A'; temp[1] = 'B'; temp[2] = 'C';
+ // wait for the "OK\n" message
+ adb_close(fd[1]);
+ int ret = adb_read(fd[0], temp, 3);
+ int saved_errno = errno;
+ adb_close(fd[0]);
+ if (ret < 0) {
+ fprintf(stderr, "could not read ok from ADB Server, errno = %d\n", saved_errno);
+ return -1;
+ }
+ if (ret != 3 || temp[0] != 'O' || temp[1] != 'K' || temp[2] != '\n') {
+ fprintf(stderr, "ADB server didn't ACK\n" );
+ return -1;
+ }
+
+ setsid();
+ }
+#endif /* !defined(_WIN32) */
+ return 0;
+}
+#endif /* ADB_HOST */
+
+// Try to handle a network forwarding request.
+// This returns 1 on success, 0 on failure, and -1 to indicate this is not
+// a forwarding-related request.
+int handle_forward_request(const char* service, TransportType type, const char* serial, int reply_fd)
+{
+ if (!strcmp(service, "list-forward")) {
+ // Create the list of forward redirections.
+ std::string listeners = format_listeners();
+#if ADB_HOST
+ SendOkay(reply_fd);
+#endif
+ SendProtocolString(reply_fd, listeners);
+ return 1;
+ }
+
+ if (!strcmp(service, "killforward-all")) {
+ remove_all_listeners();
+#if ADB_HOST
+ /* On the host: 1st OKAY is connect, 2nd OKAY is status */
+ SendOkay(reply_fd);
+#endif
+ SendOkay(reply_fd);
+ return 1;
+ }
+
+ if (!strncmp(service, "forward:", 8) || !strncmp(service, "killforward:", 12)) {
+ // killforward:local
+ // forward:(norebind:)?local;remote
+ bool kill_forward = false;
+ bool no_rebind = false;
+ if (android::base::StartsWith(service, "killforward:")) {
+ kill_forward = true;
+ service += 12;
+ if (android::base::StartsWith(service, "norebind:")) {
+ no_rebind = true;
+ service += 9;
+ }
+ } else {
+ service += 8;
+ }
+
+ std::vector<std::string> pieces = android::base::Split(service, ";");
+
+ if (kill_forward) {
+ // Check killforward: parameter format: '<local>'
+ if (pieces.size() != 1 || pieces[0].empty()) {
+ SendFail(reply_fd, android::base::StringPrintf("bad killforward: %s", service));
+ return 1;
+ }
+ } else {
+ // Check forward: parameter format: '<local>;<remote>'
+ if (pieces.size() != 2 || pieces[0].empty() || pieces[1].empty() || pieces[1][0] == '*') {
+ SendFail(reply_fd, android::base::StringPrintf("bad forward: %s", service));
+ return 1;
+ }
+ }
+
+ std::string error_msg;
+ atransport* transport = acquire_one_transport(kCsAny, type, serial, &error_msg);
+ if (!transport) {
+ SendFail(reply_fd, error_msg);
+ return 1;
+ }
+
+ InstallStatus r;
+ if (kill_forward) {
+ r = remove_listener(pieces[0].c_str(), transport);
+ } else {
+ r = install_listener(pieces[0], pieces[1].c_str(), transport, no_rebind);
+ }
+ if (r == INSTALL_STATUS_OK) {
+#if ADB_HOST
+ /* On the host: 1st OKAY is connect, 2nd OKAY is status */
+ SendOkay(reply_fd);
+#endif
+ SendOkay(reply_fd);
+ return 1;
+ }
+
+ std::string message;
+ switch (r) {
+ case INSTALL_STATUS_OK: message = "success (!)"; break;
+ case INSTALL_STATUS_INTERNAL_ERROR: message = "internal error"; break;
+ case INSTALL_STATUS_CANNOT_BIND:
+ message = android::base::StringPrintf("cannot bind to socket: %s", strerror(errno));
+ break;
+ case INSTALL_STATUS_CANNOT_REBIND:
+ message = android::base::StringPrintf("cannot rebind existing socket: %s", strerror(errno));
+ break;
+ case INSTALL_STATUS_LISTENER_NOT_FOUND:
+ message = android::base::StringPrintf("listener '%s' not found", service);
+ break;
+ }
+ SendFail(reply_fd, message);
+ return 1;
+ }
+ return 0;
+}
+
+#if ADB_HOST
+static int SendOkay(int fd, const std::string& s) {
+ SendOkay(fd);
+ SendProtocolString(fd, s);
+ return 0;
+}
+#endif
+
+int handle_host_request(const char* service, TransportType type,
+ const char* serial, int reply_fd, asocket* s) {
+ if (strcmp(service, "kill") == 0) {
+ fprintf(stderr, "adb server killed by remote request\n");
+ fflush(stdout);
+ SendOkay(reply_fd);
+ exit(0);
+ }
+
+#if ADB_HOST
+ // "transport:" is used for switching transport with a specified serial number
+ // "transport-usb:" is used for switching transport to the only USB transport
+ // "transport-local:" is used for switching transport to the only local transport
+ // "transport-any:" is used for switching transport to the only transport
+ if (!strncmp(service, "transport", strlen("transport"))) {
+ TransportType type = kTransportAny;
+
+ if (!strncmp(service, "transport-usb", strlen("transport-usb"))) {
+ type = kTransportUsb;
+ } else if (!strncmp(service, "transport-local", strlen("transport-local"))) {
+ type = kTransportLocal;
+ } else if (!strncmp(service, "transport-any", strlen("transport-any"))) {
+ type = kTransportAny;
+ } else if (!strncmp(service, "transport:", strlen("transport:"))) {
+ service += strlen("transport:");
+ serial = service;
+ }
+
+ std::string error_msg;
+ atransport* t = acquire_one_transport(kCsAny, type, serial, &error_msg);
+ if (t != nullptr) {
+ s->transport = t;
+ SendOkay(reply_fd);
+ } else {
+ SendFail(reply_fd, error_msg);
+ }
+ return 1;
+ }
+
+ // return a list of all connected devices
+ if (!strncmp(service, "devices", 7)) {
+ bool long_listing = (strcmp(service+7, "-l") == 0);
+ if (long_listing || service[7] == 0) {
+ D("Getting device list...\n");
+ std::string device_list = list_transports(long_listing);
+ D("Sending device list...\n");
+ return SendOkay(reply_fd, device_list);
+ }
+ return 1;
+ }
+
+ // remove TCP transport
+ if (!strncmp(service, "disconnect:", 11)) {
+ const std::string address(service + 11);
+ if (address.empty()) {
+ // disconnect from all TCP devices
+ unregister_all_tcp_transports();
+ return SendOkay(reply_fd, "disconnected everything");
+ }
+
+ std::string serial;
+ std::string host;
+ int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
+ std::string error;
+ if (!parse_host_and_port(address, &serial, &host, &port, &error)) {
+ return SendFail(reply_fd, android::base::StringPrintf("couldn't parse '%s': %s",
+ address.c_str(), error.c_str()));
+ }
+ atransport* t = find_transport(serial.c_str());
+ if (t == nullptr) {
+ return SendFail(reply_fd, android::base::StringPrintf("no such device '%s'",
+ serial.c_str()));
+ }
+ unregister_transport(t);
+ return SendOkay(reply_fd, android::base::StringPrintf("disconnected %s", address.c_str()));
+ }
+
+ // returns our value for ADB_SERVER_VERSION
+ if (!strcmp(service, "version")) {
+ return SendOkay(reply_fd, android::base::StringPrintf("%04x", ADB_SERVER_VERSION));
+ }
+
+ // These always report "unknown" rather than the actual error, for scripts.
+ if (!strcmp(service, "get-serialno")) {
+ std::string ignored;
+ atransport* t = acquire_one_transport(kCsAny, type, serial, &ignored);
+ return SendOkay(reply_fd, (t && t->serial) ? t->serial : "unknown");
+ }
+ if (!strcmp(service, "get-devpath")) {
+ std::string ignored;
+ atransport* t = acquire_one_transport(kCsAny, type, serial, &ignored);
+ return SendOkay(reply_fd, (t && t->devpath) ? t->devpath : "unknown");
+ }
+ if (!strcmp(service, "get-state")) {
+ std::string ignored;
+ atransport* t = acquire_one_transport(kCsAny, type, serial, &ignored);
+ return SendOkay(reply_fd, t ? t->connection_state_name() : "unknown");
+ }
+
+ // indicates a new emulator instance has started
+ if (!strncmp(service, "emulator:", 9)) {
+ int port = atoi(service+9);
+ local_connect(port);
+ /* we don't even need to send a reply */
+ return 0;
+ }
+#endif // ADB_HOST
+
+ int ret = handle_forward_request(service, type, serial, reply_fd);
+ if (ret >= 0)
+ return ret - 1;
+ return -1;
+}
diff --git a/adb/adb.h b/adb/adb.h
new file mode 100644
index 0000000..309b0e9
--- /dev/null
+++ b/adb/adb.h
@@ -0,0 +1,407 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __ADB_H
+#define __ADB_H
+
+#include <limits.h>
+#include <sys/types.h>
+
+#include <base/macros.h>
+
+#include <string>
+
+#include "adb_trace.h"
+#include "fdevent.h"
+
+constexpr size_t MAX_PAYLOAD_V1 = 4 * 1024;
+constexpr size_t MAX_PAYLOAD_V2 = 256 * 1024;
+constexpr size_t MAX_PAYLOAD = MAX_PAYLOAD_V2;
+
+#define A_SYNC 0x434e5953
+#define A_CNXN 0x4e584e43
+#define A_OPEN 0x4e45504f
+#define A_OKAY 0x59414b4f
+#define A_CLSE 0x45534c43
+#define A_WRTE 0x45545257
+#define A_AUTH 0x48545541
+
+// ADB protocol version.
+#define A_VERSION 0x01000000
+
+// Used for help/version information.
+#define ADB_VERSION_MAJOR 1
+#define ADB_VERSION_MINOR 0
+
+// Increment this when we want to force users to start a new adb server.
+#define ADB_SERVER_VERSION 32
+
+class atransport;
+struct usb_handle;
+
+struct amessage {
+ unsigned command; /* command identifier constant */
+ unsigned arg0; /* first argument */
+ unsigned arg1; /* second argument */
+ unsigned data_length; /* length of payload (0 is allowed) */
+ unsigned data_check; /* checksum of data payload */
+ unsigned magic; /* command ^ 0xffffffff */
+};
+
+struct apacket
+{
+ apacket *next;
+
+ unsigned len;
+ unsigned char *ptr;
+
+ amessage msg;
+ unsigned char data[MAX_PAYLOAD];
+};
+
+/* An asocket represents one half of a connection between a local and
+** remote entity. A local asocket is bound to a file descriptor. A
+** remote asocket is bound to the protocol engine.
+*/
+struct asocket {
+ /* chain pointers for the local/remote list of
+ ** asockets that this asocket lives in
+ */
+ asocket *next;
+ asocket *prev;
+
+ /* the unique identifier for this asocket
+ */
+ unsigned id;
+
+ /* flag: set when the socket's peer has closed
+ ** but packets are still queued for delivery
+ */
+ int closing;
+
+ /* flag: quit adbd when both ends close the
+ ** local service socket
+ */
+ int exit_on_close;
+
+ /* the asocket we are connected to
+ */
+
+ asocket *peer;
+
+ /* For local asockets, the fde is used to bind
+ ** us to our fd event system. For remote asockets
+ ** these fields are not used.
+ */
+ fdevent fde;
+ int fd;
+
+ /* queue of apackets waiting to be written
+ */
+ apacket *pkt_first;
+ apacket *pkt_last;
+
+ /* enqueue is called by our peer when it has data
+ ** for us. It should return 0 if we can accept more
+ ** data or 1 if not. If we return 1, we must call
+ ** peer->ready() when we once again are ready to
+ ** receive data.
+ */
+ int (*enqueue)(asocket *s, apacket *pkt);
+
+ /* ready is called by the peer when it is ready for
+ ** us to send data via enqueue again
+ */
+ void (*ready)(asocket *s);
+
+ /* shutdown is called by the peer before it goes away.
+ ** the socket should not do any further calls on its peer.
+ ** Always followed by a call to close. Optional, i.e. can be NULL.
+ */
+ void (*shutdown)(asocket *s);
+
+ /* close is called by the peer when it has gone away.
+ ** we are not allowed to make any further calls on the
+ ** peer once our close method is called.
+ */
+ void (*close)(asocket *s);
+
+ /* A socket is bound to atransport */
+ atransport *transport;
+
+ size_t get_max_payload() const;
+};
+
+
+/* the adisconnect structure is used to record a callback that
+** will be called whenever a transport is disconnected (e.g. by the user)
+** this should be used to cleanup objects that depend on the
+** transport (e.g. remote sockets, listeners, etc...)
+*/
+struct adisconnect
+{
+ void (*func)(void* opaque, atransport* t);
+ void* opaque;
+ adisconnect* next;
+ adisconnect* prev;
+};
+
+
+// A transport object models the connection to a remote device or emulator there
+// is one transport per connected device/emulator. A "local transport" connects
+// through TCP (for the emulator), while a "usb transport" through USB (for real
+// devices).
+//
+// Note that kTransportHost doesn't really correspond to a real transport
+// object, it's a special value used to indicate that a client wants to connect
+// to a service implemented within the ADB server itself.
+enum TransportType {
+ kTransportUsb,
+ kTransportLocal,
+ kTransportAny,
+ kTransportHost,
+};
+
+#define TOKEN_SIZE 20
+
+enum ConnectionState {
+ kCsAny = -1,
+ kCsOffline = 0,
+ kCsBootloader,
+ kCsDevice,
+ kCsHost,
+ kCsRecovery,
+ kCsNoPerm, // Insufficient permissions to communicate with the device.
+ kCsSideload,
+ kCsUnauthorized,
+};
+
+class atransport {
+public:
+ // TODO(danalbert): We expose waaaaaaay too much stuff because this was
+ // historically just a struct, but making the whole thing a more idiomatic
+ // class in one go is a very large change. Given how bad our testing is,
+ // it's better to do this piece by piece.
+
+ atransport() {
+ auth_fde = {};
+ transport_fde = {};
+ protocol_version = A_VERSION;
+ max_payload = MAX_PAYLOAD;
+ }
+
+ virtual ~atransport() {}
+
+ int (*read_from_remote)(apacket* p, atransport* t) = nullptr;
+ int (*write_to_remote)(apacket* p, atransport* t) = nullptr;
+ void (*close)(atransport* t) = nullptr;
+ void (*kick)(atransport* t) = nullptr;
+
+ int fd = -1;
+ int transport_socket = -1;
+ fdevent transport_fde;
+ int ref_count = 0;
+ uint32_t sync_token = 0;
+ ConnectionState connection_state = kCsOffline;
+ bool online = false;
+ TransportType type = kTransportAny;
+
+ // USB handle or socket fd as needed.
+ usb_handle* usb = nullptr;
+ int sfd = -1;
+
+ // Used to identify transports for clients.
+ char* serial = nullptr;
+ char* product = nullptr;
+ char* model = nullptr;
+ char* device = nullptr;
+ char* devpath = nullptr;
+ int adb_port = -1; // Use for emulators (local transport)
+ bool kicked = false;
+
+ // A list of adisconnect callbacks called when the transport is kicked.
+ adisconnect disconnects = {};
+
+ void* key = nullptr;
+ unsigned char token[TOKEN_SIZE] = {};
+ fdevent auth_fde;
+ size_t failed_auth_attempts = 0;
+
+ const char* connection_state_name() const;
+
+ void update_version(int version, size_t payload);
+ int get_protocol_version() const;
+ size_t get_max_payload() const;
+
+private:
+ int protocol_version;
+ size_t max_payload;
+
+ DISALLOW_COPY_AND_ASSIGN(atransport);
+};
+
+
+/* A listener is an entity which binds to a local port
+** and, upon receiving a connection on that port, creates
+** an asocket to connect the new local connection to a
+** specific remote service.
+**
+** TODO: some listeners read from the new connection to
+** determine what exact service to connect to on the far
+** side.
+*/
+struct alistener
+{
+ alistener *next;
+ alistener *prev;
+
+ fdevent fde;
+ int fd;
+
+ char *local_name;
+ char *connect_to;
+ atransport *transport;
+ adisconnect disconnect;
+};
+
+
+void print_packet(const char *label, apacket *p);
+
+asocket *find_local_socket(unsigned local_id, unsigned remote_id);
+void install_local_socket(asocket *s);
+void remove_socket(asocket *s);
+void close_all_sockets(atransport *t);
+
+asocket *create_local_socket(int fd);
+asocket *create_local_service_socket(const char *destination);
+
+asocket *create_remote_socket(unsigned id, atransport *t);
+void connect_to_remote(asocket *s, const char *destination);
+void connect_to_smartsocket(asocket *s);
+
+void fatal(const char *fmt, ...);
+void fatal_errno(const char *fmt, ...);
+
+void handle_packet(apacket *p, atransport *t);
+
+void get_my_path(char *s, size_t maxLen);
+int launch_server(int server_port);
+int adb_main(int is_daemon, int server_port);
+
+/* initialize a transport object's func pointers and state */
+#if ADB_HOST
+int get_available_local_transport_index();
+#endif
+int init_socket_transport(atransport *t, int s, int port, int local);
+void init_usb_transport(atransport *t, usb_handle *usb, ConnectionState state);
+
+#if ADB_HOST
+atransport* find_emulator_transport_by_adb_port(int adb_port);
+#endif
+
+int service_to_fd(const char *name);
+#if ADB_HOST
+asocket *host_service_to_socket(const char* name, const char *serial);
+#endif
+
+#if !ADB_HOST
+int init_jdwp(void);
+asocket* create_jdwp_service_socket();
+asocket* create_jdwp_tracker_service_socket();
+int create_jdwp_connection_fd(int jdwp_pid);
+#endif
+
+int handle_forward_request(const char* service, TransportType type, const char* serial, int reply_fd);
+
+#if !ADB_HOST
+void framebuffer_service(int fd, void *cookie);
+void set_verity_enabled_state_service(int fd, void* cookie);
+#endif
+
+/* packet allocator */
+apacket *get_apacket(void);
+void put_apacket(apacket *p);
+
+// Define it if you want to dump packets.
+#define DEBUG_PACKETS 0
+
+#if !DEBUG_PACKETS
+#define print_packet(tag,p) do {} while (0)
+#endif
+
+#if ADB_HOST_ON_TARGET
+/* adb and adbd are coexisting on the target, so use 5038 for adb
+ * to avoid conflicting with adbd's usage of 5037
+ */
+# define DEFAULT_ADB_PORT 5038
+#else
+# define DEFAULT_ADB_PORT 5037
+#endif
+
+#define DEFAULT_ADB_LOCAL_TRANSPORT_PORT 5555
+
+#define ADB_CLASS 0xff
+#define ADB_SUBCLASS 0x42
+#define ADB_PROTOCOL 0x1
+
+
+void local_init(int port);
+void local_connect(int port);
+int local_connect_arbitrary_ports(int console_port, int adb_port, std::string* error);
+
+/* usb host/client interface */
+void usb_init();
+int usb_write(usb_handle *h, const void *data, int len);
+int usb_read(usb_handle *h, void *data, int len);
+int usb_close(usb_handle *h);
+void usb_kick(usb_handle *h);
+
+/* used for USB device detection */
+#if ADB_HOST
+int is_adb_interface(int vid, int pid, int usb_class, int usb_subclass, int usb_protocol);
+#endif
+
+int adb_commandline(int argc, const char **argv);
+
+ConnectionState connection_state(atransport *t);
+
+extern const char *adb_device_banner;
+extern int HOST;
+#if !ADB_HOST
+extern int SHELL_EXIT_NOTIFY_FD;
+#endif // !ADB_HOST
+
+#define CHUNK_SIZE (64*1024)
+
+#if !ADB_HOST
+#define USB_ADB_PATH "/dev/android_adb"
+
+#define USB_FFS_ADB_PATH "/dev/usb-ffs/adb/"
+#define USB_FFS_ADB_EP(x) USB_FFS_ADB_PATH#x
+
+#define USB_FFS_ADB_EP0 USB_FFS_ADB_EP(ep0)
+#define USB_FFS_ADB_OUT USB_FFS_ADB_EP(ep1)
+#define USB_FFS_ADB_IN USB_FFS_ADB_EP(ep2)
+#endif
+
+int handle_host_request(const char* service, TransportType type, const char* serial, int reply_fd, asocket *s);
+
+void handle_online(atransport *t);
+void handle_offline(atransport *t);
+
+void send_connect(atransport *t);
+
+#endif
diff --git a/adb/adb_auth.cpp b/adb/adb_auth.cpp
new file mode 100644
index 0000000..8a6b156
--- /dev/null
+++ b/adb/adb_auth.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TRACE_TAG TRACE_ADB
+
+#include "sysdeps.h"
+#include "adb_auth.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "adb.h"
+#include "transport.h"
+
+bool auth_required = true;
+
+void send_auth_request(atransport *t)
+{
+ D("Calling send_auth_request\n");
+ apacket *p;
+ int ret;
+
+ ret = adb_auth_generate_token(t->token, sizeof(t->token));
+ if (ret != sizeof(t->token)) {
+ D("Error generating token ret=%d\n", ret);
+ return;
+ }
+
+ p = get_apacket();
+ memcpy(p->data, t->token, ret);
+ p->msg.command = A_AUTH;
+ p->msg.arg0 = ADB_AUTH_TOKEN;
+ p->msg.data_length = ret;
+ send_packet(p, t);
+}
+
+void send_auth_response(uint8_t *token, size_t token_size, atransport *t)
+{
+ D("Calling send_auth_response\n");
+ apacket *p = get_apacket();
+ int ret;
+
+ ret = adb_auth_sign(t->key, token, token_size, p->data);
+ if (!ret) {
+ D("Error signing the token\n");
+ put_apacket(p);
+ return;
+ }
+
+ p->msg.command = A_AUTH;
+ p->msg.arg0 = ADB_AUTH_SIGNATURE;
+ p->msg.data_length = ret;
+ send_packet(p, t);
+}
+
+void send_auth_publickey(atransport *t)
+{
+ D("Calling send_auth_publickey\n");
+ apacket *p = get_apacket();
+ int ret;
+
+ ret = adb_auth_get_userkey(p->data, MAX_PAYLOAD_V1);
+ if (!ret) {
+ D("Failed to get user public key\n");
+ put_apacket(p);
+ return;
+ }
+
+ p->msg.command = A_AUTH;
+ p->msg.arg0 = ADB_AUTH_RSAPUBLICKEY;
+ p->msg.data_length = ret;
+ send_packet(p, t);
+}
+
+void adb_auth_verified(atransport *t)
+{
+ handle_online(t);
+ send_connect(t);
+}
diff --git a/adb/adb_auth.h b/adb/adb_auth.h
new file mode 100644
index 0000000..a13604a
--- /dev/null
+++ b/adb/adb_auth.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __ADB_AUTH_H
+#define __ADB_AUTH_H
+
+#include "adb.h"
+
+extern bool auth_required;
+
+int adb_auth_keygen(const char* filename);
+void adb_auth_verified(atransport *t);
+
+void send_auth_request(atransport *t);
+void send_auth_response(uint8_t *token, size_t token_size, atransport *t);
+void send_auth_publickey(atransport *t);
+
+/* AUTH packets first argument */
+/* Request */
+#define ADB_AUTH_TOKEN 1
+/* Response */
+#define ADB_AUTH_SIGNATURE 2
+#define ADB_AUTH_RSAPUBLICKEY 3
+
+#if ADB_HOST
+
+void adb_auth_init(void);
+int adb_auth_sign(void *key, const unsigned char* token, size_t token_size,
+ unsigned char* sig);
+void *adb_auth_nextkey(void *current);
+int adb_auth_get_userkey(unsigned char *data, size_t len);
+
+static inline int adb_auth_generate_token(void *token, size_t token_size) { return 0; }
+static inline int adb_auth_verify(void *token, void *sig, int siglen) { return 0; }
+static inline void adb_auth_confirm_key(unsigned char *data, size_t len, atransport *t) { }
+
+#else // !ADB_HOST
+
+static inline int adb_auth_sign(void* key, const unsigned char* token,
+ size_t token_size, unsigned char* sig) {
+ return 0;
+}
+static inline void *adb_auth_nextkey(void *current) { return NULL; }
+static inline int adb_auth_get_userkey(unsigned char *data, size_t len) { return 0; }
+
+void adbd_auth_init(void);
+void adbd_cloexec_auth_socket();
+int adb_auth_generate_token(void *token, size_t token_size);
+int adb_auth_verify(uint8_t* token, uint8_t* sig, int siglen);
+void adb_auth_confirm_key(unsigned char *data, size_t len, atransport *t);
+
+#endif // ADB_HOST
+
+#endif // __ADB_AUTH_H
diff --git a/adb/adb_auth_client.cpp b/adb/adb_auth_client.cpp
new file mode 100644
index 0000000..be28202
--- /dev/null
+++ b/adb/adb_auth_client.cpp
@@ -0,0 +1,275 @@
+/*
+ * 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 TRACE_TAG TRACE_AUTH
+
+#include "sysdeps.h"
+#include "adb_auth.h"
+
+#include <resolv.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "cutils/list.h"
+#include "cutils/sockets.h"
+#include "mincrypt/rsa.h"
+#include "mincrypt/sha.h"
+
+#include "adb.h"
+#include "fdevent.h"
+#include "transport.h"
+
+struct adb_public_key {
+ struct listnode node;
+ RSAPublicKey key;
+};
+
+static const char *key_paths[] = {
+ "/adb_keys",
+ "/data/misc/adb/adb_keys",
+ NULL
+};
+
+static fdevent listener_fde;
+static int framework_fd = -1;
+
+static void usb_disconnected(void* unused, atransport* t);
+static struct adisconnect usb_disconnect = { usb_disconnected, 0, 0, 0 };
+static atransport* usb_transport;
+static bool needs_retry = false;
+
+static void read_keys(const char *file, struct listnode *list)
+{
+ FILE *f;
+ char buf[MAX_PAYLOAD_V1];
+ char *sep;
+ int ret;
+
+ f = fopen(file, "re");
+ if (!f) {
+ D("Can't open '%s'\n", file);
+ return;
+ }
+
+ while (fgets(buf, sizeof(buf), f)) {
+ /* Allocate 4 extra bytes to decode the base64 data in-place */
+ auto key = reinterpret_cast<adb_public_key*>(
+ calloc(1, sizeof(adb_public_key) + 4));
+ if (key == nullptr) {
+ D("Can't malloc key\n");
+ break;
+ }
+
+ sep = strpbrk(buf, " \t");
+ if (sep)
+ *sep = '\0';
+
+ ret = __b64_pton(buf, (u_char *)&key->key, sizeof(key->key) + 4);
+ if (ret != sizeof(key->key)) {
+ D("%s: Invalid base64 data ret=%d\n", file, ret);
+ free(key);
+ continue;
+ }
+
+ if (key->key.len != RSANUMWORDS) {
+ D("%s: Invalid key len %d\n", file, key->key.len);
+ free(key);
+ continue;
+ }
+
+ list_add_tail(list, &key->node);
+ }
+
+ fclose(f);
+}
+
+static void free_keys(struct listnode *list)
+{
+ struct listnode *item;
+
+ while (!list_empty(list)) {
+ item = list_head(list);
+ list_remove(item);
+ free(node_to_item(item, struct adb_public_key, node));
+ }
+}
+
+static void load_keys(struct listnode *list)
+{
+ const char* path;
+ const char** paths = key_paths;
+ struct stat buf;
+
+ list_init(list);
+
+ while ((path = *paths++)) {
+ if (!stat(path, &buf)) {
+ D("Loading keys from '%s'\n", path);
+ read_keys(path, list);
+ }
+ }
+}
+
+int adb_auth_generate_token(void *token, size_t token_size)
+{
+ FILE *f;
+ int ret;
+
+ f = fopen("/dev/urandom", "re");
+ if (!f)
+ return 0;
+
+ ret = fread(token, token_size, 1, f);
+
+ fclose(f);
+ return ret * token_size;
+}
+
+int adb_auth_verify(uint8_t* token, uint8_t* sig, int siglen)
+{
+ struct listnode *item;
+ struct listnode key_list;
+ int ret = 0;
+
+ if (siglen != RSANUMBYTES)
+ return 0;
+
+ load_keys(&key_list);
+
+ list_for_each(item, &key_list) {
+ adb_public_key* key = node_to_item(item, struct adb_public_key, node);
+ ret = RSA_verify(&key->key, sig, siglen, token, SHA_DIGEST_SIZE);
+ if (ret)
+ break;
+ }
+
+ free_keys(&key_list);
+
+ return ret;
+}
+
+static void usb_disconnected(void* unused, atransport* t)
+{
+ D("USB disconnect\n");
+ remove_transport_disconnect(usb_transport, &usb_disconnect);
+ usb_transport = NULL;
+ needs_retry = false;
+}
+
+static void adb_auth_event(int fd, unsigned events, void *data)
+{
+ char response[2];
+ int ret;
+
+ if (events & FDE_READ) {
+ ret = unix_read(fd, response, sizeof(response));
+ if (ret <= 0) {
+ D("Framework disconnect\n");
+ if (usb_transport)
+ fdevent_remove(&usb_transport->auth_fde);
+ framework_fd = -1;
+ }
+ else if (ret == 2 && response[0] == 'O' && response[1] == 'K') {
+ if (usb_transport)
+ adb_auth_verified(usb_transport);
+ }
+ }
+}
+
+void adb_auth_confirm_key(unsigned char *key, size_t len, atransport *t)
+{
+ char msg[MAX_PAYLOAD_V1];
+ int ret;
+
+ if (!usb_transport) {
+ usb_transport = t;
+ add_transport_disconnect(t, &usb_disconnect);
+ }
+
+ if (framework_fd < 0) {
+ D("Client not connected\n");
+ needs_retry = true;
+ return;
+ }
+
+ if (key[len - 1] != '\0') {
+ D("Key must be a null-terminated string\n");
+ return;
+ }
+
+ ret = snprintf(msg, sizeof(msg), "PK%s", key);
+ if (ret >= (signed)sizeof(msg)) {
+ D("Key too long. ret=%d\n", ret);
+ return;
+ }
+ D("Sending '%s'\n", msg);
+
+ ret = unix_write(framework_fd, msg, ret);
+ if (ret < 0) {
+ D("Failed to write PK, errno=%d\n", errno);
+ return;
+ }
+
+ fdevent_install(&t->auth_fde, framework_fd, adb_auth_event, t);
+ fdevent_add(&t->auth_fde, FDE_READ);
+}
+
+static void adb_auth_listener(int fd, unsigned events, void *data)
+{
+ struct sockaddr addr;
+ socklen_t alen;
+ int s;
+
+ alen = sizeof(addr);
+
+ s = adb_socket_accept(fd, &addr, &alen);
+ if (s < 0) {
+ D("Failed to accept: errno=%d\n", errno);
+ return;
+ }
+
+ framework_fd = s;
+
+ if (needs_retry) {
+ needs_retry = false;
+ send_auth_request(usb_transport);
+ }
+}
+
+void adbd_cloexec_auth_socket() {
+ int fd = android_get_control_socket("adbd");
+ if (fd == -1) {
+ D("Failed to get adbd socket\n");
+ return;
+ }
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+}
+
+void adbd_auth_init(void) {
+ int fd = android_get_control_socket("adbd");
+ if (fd == -1) {
+ D("Failed to get adbd socket\n");
+ return;
+ }
+
+ if (listen(fd, 4) == -1) {
+ D("Failed to listen on '%d'\n", fd);
+ return;
+ }
+
+ fdevent_install(&listener_fde, fd, adb_auth_listener, NULL);
+ fdevent_add(&listener_fde, FDE_READ);
+}
diff --git a/adb/adb_auth_host.cpp b/adb/adb_auth_host.cpp
new file mode 100644
index 0000000..b6bb00c
--- /dev/null
+++ b/adb/adb_auth_host.cpp
@@ -0,0 +1,467 @@
+/*
+ * 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 TRACE_TAG TRACE_AUTH
+
+#include "sysdeps.h"
+#include "adb_auth.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef _WIN32
+# ifndef WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+# endif
+# include "windows.h"
+# include "shlobj.h"
+#else
+# include <sys/types.h>
+# include <sys/stat.h>
+# include <unistd.h>
+#endif
+
+#include "adb.h"
+
+/* HACK: we need the RSAPublicKey struct
+ * but RSA_verify conflits with openssl */
+#define RSA_verify RSA_verify_mincrypt
+#include "mincrypt/rsa.h"
+#undef RSA_verify
+
+#include <base/strings.h>
+#include <cutils/list.h>
+
+#include <openssl/evp.h>
+#include <openssl/objects.h>
+#include <openssl/pem.h>
+#include <openssl/rsa.h>
+#include <openssl/sha.h>
+
+#if defined(OPENSSL_IS_BORINGSSL)
+#include <openssl/base64.h>
+#endif
+
+#define ANDROID_PATH ".android"
+#define ADB_KEY_FILE "adbkey"
+
+struct adb_private_key {
+ struct listnode node;
+ RSA *rsa;
+};
+
+static struct listnode key_list;
+
+
+/* Convert OpenSSL RSA private key to android pre-computed RSAPublicKey format */
+static int RSA_to_RSAPublicKey(RSA *rsa, RSAPublicKey *pkey)
+{
+ int ret = 1;
+ unsigned int i;
+
+ BN_CTX* ctx = BN_CTX_new();
+ BIGNUM* r32 = BN_new();
+ BIGNUM* rr = BN_new();
+ BIGNUM* r = BN_new();
+ BIGNUM* rem = BN_new();
+ BIGNUM* n = BN_new();
+ BIGNUM* n0inv = BN_new();
+
+ if (RSA_size(rsa) != RSANUMBYTES) {
+ ret = 0;
+ goto out;
+ }
+
+ BN_set_bit(r32, 32);
+ BN_copy(n, rsa->n);
+ BN_set_bit(r, RSANUMWORDS * 32);
+ BN_mod_sqr(rr, r, n, ctx);
+ BN_div(NULL, rem, n, r32, ctx);
+ BN_mod_inverse(n0inv, rem, r32, ctx);
+
+ pkey->len = RSANUMWORDS;
+ pkey->n0inv = 0 - BN_get_word(n0inv);
+ for (i = 0; i < RSANUMWORDS; i++) {
+ BN_div(rr, rem, rr, r32, ctx);
+ pkey->rr[i] = BN_get_word(rem);
+ BN_div(n, rem, n, r32, ctx);
+ pkey->n[i] = BN_get_word(rem);
+ }
+ pkey->exponent = BN_get_word(rsa->e);
+
+out:
+ BN_free(n0inv);
+ BN_free(n);
+ BN_free(rem);
+ BN_free(r);
+ BN_free(rr);
+ BN_free(r32);
+ BN_CTX_free(ctx);
+
+ return ret;
+}
+
+static void get_user_info(char *buf, size_t len)
+{
+ char hostname[1024], username[1024];
+ int ret = -1;
+
+ if (getenv("HOSTNAME") != NULL) {
+ strncpy(hostname, getenv("HOSTNAME"), sizeof(hostname));
+ hostname[sizeof(hostname)-1] = '\0';
+ ret = 0;
+ }
+
+#ifndef _WIN32
+ if (ret < 0)
+ ret = gethostname(hostname, sizeof(hostname));
+#endif
+ if (ret < 0)
+ strcpy(hostname, "unknown");
+
+ ret = -1;
+
+ if (getenv("LOGNAME") != NULL) {
+ strncpy(username, getenv("LOGNAME"), sizeof(username));
+ username[sizeof(username)-1] = '\0';
+ ret = 0;
+ }
+
+#if !defined _WIN32 && !defined ADB_HOST_ON_TARGET
+ if (ret < 0)
+ ret = getlogin_r(username, sizeof(username));
+#endif
+ if (ret < 0)
+ strcpy(username, "unknown");
+
+ ret = snprintf(buf, len, " %s@%s", username, hostname);
+ if (ret >= (signed)len)
+ buf[len - 1] = '\0';
+}
+
+static int write_public_keyfile(RSA *private_key, const char *private_key_path)
+{
+ RSAPublicKey pkey;
+ FILE *outfile = NULL;
+ char path[PATH_MAX], info[MAX_PAYLOAD_V1];
+ uint8_t* encoded = nullptr;
+ size_t encoded_length;
+ int ret = 0;
+
+ if (snprintf(path, sizeof(path), "%s.pub", private_key_path) >=
+ (int)sizeof(path)) {
+ D("Path too long while writing public key\n");
+ return 0;
+ }
+
+ if (!RSA_to_RSAPublicKey(private_key, &pkey)) {
+ D("Failed to convert to publickey\n");
+ return 0;
+ }
+
+ outfile = fopen(path, "w");
+ if (!outfile) {
+ D("Failed to open '%s'\n", path);
+ return 0;
+ }
+
+ D("Writing public key to '%s'\n", path);
+
+#if defined(OPENSSL_IS_BORINGSSL)
+ if (!EVP_EncodedLength(&encoded_length, sizeof(pkey))) {
+ D("Public key too large to base64 encode\n");
+ goto out;
+ }
+#else
+ /* While we switch from OpenSSL to BoringSSL we have to implement
+ * |EVP_EncodedLength| here. */
+ encoded_length = 1 + ((sizeof(pkey) + 2) / 3 * 4);
+#endif
+
+ encoded = new uint8_t[encoded_length];
+ if (encoded == nullptr) {
+ D("Allocation failure\n");
+ goto out;
+ }
+
+ encoded_length = EVP_EncodeBlock(encoded, (uint8_t*) &pkey, sizeof(pkey));
+ get_user_info(info, sizeof(info));
+
+ if (fwrite(encoded, encoded_length, 1, outfile) != 1 ||
+ fwrite(info, strlen(info), 1, outfile) != 1) {
+ D("Write error while writing public key\n");
+ goto out;
+ }
+
+ ret = 1;
+
+ out:
+ if (outfile != NULL) {
+ fclose(outfile);
+ }
+ delete[] encoded;
+ return ret;
+}
+
+static int generate_key(const char *file)
+{
+ EVP_PKEY* pkey = EVP_PKEY_new();
+ BIGNUM* exponent = BN_new();
+ RSA* rsa = RSA_new();
+ mode_t old_mask;
+ FILE *f = NULL;
+ int ret = 0;
+
+ D("generate_key '%s'\n", file);
+
+ if (!pkey || !exponent || !rsa) {
+ D("Failed to allocate key\n");
+ goto out;
+ }
+
+ BN_set_word(exponent, RSA_F4);
+ RSA_generate_key_ex(rsa, 2048, exponent, NULL);
+ EVP_PKEY_set1_RSA(pkey, rsa);
+
+ old_mask = umask(077);
+
+ f = fopen(file, "w");
+ if (!f) {
+ D("Failed to open '%s'\n", file);
+ umask(old_mask);
+ goto out;
+ }
+
+ umask(old_mask);
+
+ if (!PEM_write_PrivateKey(f, pkey, NULL, NULL, 0, NULL, NULL)) {
+ D("Failed to write key\n");
+ goto out;
+ }
+
+ if (!write_public_keyfile(rsa, file)) {
+ D("Failed to write public key\n");
+ goto out;
+ }
+
+ ret = 1;
+
+out:
+ if (f)
+ fclose(f);
+ EVP_PKEY_free(pkey);
+ RSA_free(rsa);
+ BN_free(exponent);
+ return ret;
+}
+
+static int read_key(const char *file, struct listnode *list)
+{
+ D("read_key '%s'\n", file);
+
+ FILE* fp = fopen(file, "r");
+ if (!fp) {
+ D("Failed to open '%s': %s\n", file, strerror(errno));
+ return 0;
+ }
+
+ adb_private_key* key = new adb_private_key;
+ key->rsa = RSA_new();
+
+ if (!PEM_read_RSAPrivateKey(fp, &key->rsa, NULL, NULL)) {
+ D("Failed to read key\n");
+ fclose(fp);
+ RSA_free(key->rsa);
+ delete key;
+ return 0;
+ }
+
+ fclose(fp);
+ list_add_tail(list, &key->node);
+ return 1;
+}
+
+static int get_user_keyfilepath(char *filename, size_t len)
+{
+ const char *format, *home;
+ char android_dir[PATH_MAX];
+ struct stat buf;
+#ifdef _WIN32
+ char path[PATH_MAX];
+ home = getenv("ANDROID_SDK_HOME");
+ if (!home) {
+ SHGetFolderPath(NULL, CSIDL_PROFILE, NULL, 0, path);
+ home = path;
+ }
+ format = "%s\\%s";
+#else
+ home = getenv("HOME");
+ if (!home)
+ return -1;
+ format = "%s/%s";
+#endif
+
+ D("home '%s'\n", home);
+
+ if (snprintf(android_dir, sizeof(android_dir), format, home,
+ ANDROID_PATH) >= (int)sizeof(android_dir))
+ return -1;
+
+ if (stat(android_dir, &buf)) {
+ if (adb_mkdir(android_dir, 0750) < 0) {
+ D("Cannot mkdir '%s'\n", android_dir);
+ return -1;
+ }
+ }
+
+ return snprintf(filename, len, format, android_dir, ADB_KEY_FILE);
+}
+
+static int get_user_key(struct listnode *list)
+{
+ struct stat buf;
+ char path[PATH_MAX];
+ int ret;
+
+ ret = get_user_keyfilepath(path, sizeof(path));
+ if (ret < 0 || ret >= (signed)sizeof(path)) {
+ D("Error getting user key filename\n");
+ return 0;
+ }
+
+ D("user key '%s'\n", path);
+
+ if (stat(path, &buf) == -1) {
+ if (!generate_key(path)) {
+ D("Failed to generate new key\n");
+ return 0;
+ }
+ }
+
+ return read_key(path, list);
+}
+
+static void get_vendor_keys(struct listnode* key_list) {
+ const char* adb_keys_path = getenv("ADB_VENDOR_KEYS");
+ if (adb_keys_path == nullptr) {
+ return;
+ }
+
+ for (auto& path : android::base::Split(adb_keys_path, ENV_PATH_SEPARATOR_STR)) {
+ if (!read_key(path.c_str(), key_list)) {
+ D("Failed to read '%s'\n", path.c_str());
+ }
+ }
+}
+
+int adb_auth_sign(void *node, const unsigned char* token, size_t token_size,
+ unsigned char* sig)
+{
+ unsigned int len;
+ struct adb_private_key *key = node_to_item(node, struct adb_private_key, node);
+
+ if (token_size != TOKEN_SIZE) {
+ D("Unexpected token size %zd\n", token_size);
+ return 0;
+ }
+
+ if (!RSA_sign(NID_sha1, token, token_size, sig, &len, key->rsa)) {
+ return 0;
+ }
+
+ D("adb_auth_sign len=%d\n", len);
+ return (int)len;
+}
+
+void *adb_auth_nextkey(void *current)
+{
+ struct listnode *item;
+
+ if (list_empty(&key_list))
+ return NULL;
+
+ if (!current)
+ return list_head(&key_list);
+
+ list_for_each(item, &key_list) {
+ if (item == current) {
+ /* current is the last item, we tried all the keys */
+ if (item->next == &key_list)
+ return NULL;
+ return item->next;
+ }
+ }
+
+ return NULL;
+}
+
+int adb_auth_get_userkey(unsigned char *data, size_t len)
+{
+ char path[PATH_MAX];
+ int ret = get_user_keyfilepath(path, sizeof(path) - 4);
+ if (ret < 0 || ret >= (signed)(sizeof(path) - 4)) {
+ D("Error getting user key filename\n");
+ return 0;
+ }
+ strcat(path, ".pub");
+
+ // TODO(danalbert): ReadFileToString
+ // Note that on Windows, load_file() does not do CR/LF translation, but
+ // ReadFileToString() uses the C Runtime which uses CR/LF translation by
+ // default (by is overridable with _setmode()).
+ unsigned size;
+ char* file_data = reinterpret_cast<char*>(load_file(path, &size));
+ if (file_data == nullptr) {
+ D("Can't load '%s'\n", path);
+ return 0;
+ }
+
+ if (len < (size_t)(size + 1)) {
+ D("%s: Content too large ret=%d\n", path, size);
+ free(file_data);
+ return 0;
+ }
+
+ memcpy(data, file_data, size);
+ free(file_data);
+ file_data = nullptr;
+ data[size] = '\0';
+
+ return size + 1;
+}
+
+int adb_auth_keygen(const char* filename) {
+ adb_trace_mask |= (1 << TRACE_AUTH);
+ return (generate_key(filename) == 0);
+}
+
+void adb_auth_init(void)
+{
+ int ret;
+
+ D("adb_auth_init\n");
+
+ list_init(&key_list);
+
+ ret = get_user_key(&key_list);
+ if (!ret) {
+ D("Failed to get user key\n");
+ return;
+ }
+
+ get_vendor_keys(&key_list);
+}
diff --git a/adb/adb_client.cpp b/adb/adb_client.cpp
new file mode 100644
index 0000000..418662c
--- /dev/null
+++ b/adb/adb_client.cpp
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TRACE_TAG TRACE_ADB
+
+#include "sysdeps.h"
+#include "adb_client.h"
+
+#include <errno.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <string>
+#include <vector>
+
+#include <base/stringprintf.h>
+#include <base/strings.h>
+#include <cutils/sockets.h>
+
+#include "adb_io.h"
+#include "adb_utils.h"
+
+static TransportType __adb_transport = kTransportAny;
+static const char* __adb_serial = NULL;
+
+static int __adb_server_port = DEFAULT_ADB_PORT;
+static const char* __adb_server_name = NULL;
+
+static std::string perror_str(const char* msg) {
+ return android::base::StringPrintf("%s: %s", msg, strerror(errno));
+}
+
+static bool ReadProtocolString(int fd, std::string* s, std::string* error) {
+ char buf[5];
+ if (!ReadFdExactly(fd, buf, 4)) {
+ *error = perror_str("protocol fault (couldn't read status length)");
+ return false;
+ }
+ buf[4] = 0;
+
+ unsigned long len = strtoul(buf, 0, 16);
+ s->resize(len, '\0');
+ if (!ReadFdExactly(fd, &(*s)[0], len)) {
+ *error = perror_str("protocol fault (couldn't read status message)");
+ return false;
+ }
+
+ return true;
+}
+
+void adb_set_transport(TransportType type, const char* serial)
+{
+ __adb_transport = type;
+ __adb_serial = serial;
+}
+
+void adb_set_tcp_specifics(int server_port)
+{
+ __adb_server_port = server_port;
+}
+
+void adb_set_tcp_name(const char* hostname)
+{
+ __adb_server_name = hostname;
+}
+
+static int switch_socket_transport(int fd, std::string* error) {
+ std::string service;
+ if (__adb_serial) {
+ service += "host:transport:";
+ service += __adb_serial;
+ } else {
+ const char* transport_type = "???";
+ switch (__adb_transport) {
+ case kTransportUsb:
+ transport_type = "transport-usb";
+ break;
+ case kTransportLocal:
+ transport_type = "transport-local";
+ break;
+ case kTransportAny:
+ transport_type = "transport-any";
+ break;
+ case kTransportHost:
+ // no switch necessary
+ return 0;
+ }
+ service += "host:";
+ service += transport_type;
+ }
+
+ if (!SendProtocolString(fd, service)) {
+ *error = perror_str("write failure during connection");
+ adb_close(fd);
+ return -1;
+ }
+ D("Switch transport in progress\n");
+
+ if (!adb_status(fd, error)) {
+ adb_close(fd);
+ D("Switch transport failed: %s\n", error->c_str());
+ return -1;
+ }
+ D("Switch transport success\n");
+ return 0;
+}
+
+bool adb_status(int fd, std::string* error) {
+ char buf[5];
+ if (!ReadFdExactly(fd, buf, 4)) {
+ *error = perror_str("protocol fault (couldn't read status)");
+ return false;
+ }
+
+ if (!memcmp(buf, "OKAY", 4)) {
+ return true;
+ }
+
+ if (memcmp(buf, "FAIL", 4)) {
+ *error = android::base::StringPrintf("protocol fault (status %02x %02x %02x %02x?!)",
+ buf[0], buf[1], buf[2], buf[3]);
+ return false;
+ }
+
+ ReadProtocolString(fd, error, error);
+ return false;
+}
+
+int _adb_connect(const std::string& service, std::string* error) {
+ D("_adb_connect: %s\n", service.c_str());
+ if (service.empty() || service.size() > 1024) {
+ *error = android::base::StringPrintf("bad service name length (%zd)",
+ service.size());
+ return -1;
+ }
+
+ int fd;
+ if (__adb_server_name) {
+ std::string reason;
+ fd = network_connect(__adb_server_name, __adb_server_port, SOCK_STREAM, 0, &reason);
+ if (fd == -1) {
+ *error = android::base::StringPrintf("can't connect to %s:%d: %s",
+ __adb_server_name, __adb_server_port,
+ reason.c_str());
+ return -2;
+ }
+ } else {
+ fd = socket_loopback_client(__adb_server_port, SOCK_STREAM);
+ if (fd == -1) {
+ *error = perror_str("cannot connect to daemon");
+ return -2;
+ }
+ }
+
+ if (memcmp(&service[0],"host",4) != 0 && switch_socket_transport(fd, error)) {
+ return -1;
+ }
+
+ if(!SendProtocolString(fd, service)) {
+ *error = perror_str("write failure during connection");
+ adb_close(fd);
+ return -1;
+ }
+
+ if (!adb_status(fd, error)) {
+ adb_close(fd);
+ return -1;
+ }
+
+ D("_adb_connect: return fd %d\n", fd);
+ return fd;
+}
+
+int adb_connect(const std::string& service, std::string* error) {
+ // first query the adb server's version
+ int fd = _adb_connect("host:version", error);
+
+ D("adb_connect: service %s\n", service.c_str());
+ if (fd == -2 && __adb_server_name) {
+ fprintf(stderr,"** Cannot start server on remote host\n");
+ return fd;
+ } else if (fd == -2) {
+ fprintf(stdout,"* daemon not running. starting it now on port %d *\n",
+ __adb_server_port);
+ start_server:
+ if (launch_server(__adb_server_port)) {
+ fprintf(stderr,"* failed to start daemon *\n");
+ return -1;
+ } else {
+ fprintf(stdout,"* daemon started successfully *\n");
+ }
+ /* give the server some time to start properly and detect devices */
+ adb_sleep_ms(3000);
+ // fall through to _adb_connect
+ } else {
+ // if server was running, check its version to make sure it is not out of date
+ int version = ADB_SERVER_VERSION - 1;
+
+ // if we have a file descriptor, then parse version result
+ if (fd >= 0) {
+ std::string version_string;
+ if (!ReadProtocolString(fd, &version_string, error)) {
+ goto error;
+ }
+
+ adb_close(fd);
+
+ if (sscanf(&version_string[0], "%04x", &version) != 1) {
+ goto error;
+ }
+ } else {
+ // if fd is -1, then check for "unknown host service",
+ // which would indicate a version of adb that does not support the version command
+ if (*error == "unknown host service") {
+ return fd;
+ }
+ }
+
+ if (version != ADB_SERVER_VERSION) {
+ printf("adb server is out of date. killing...\n");
+ fd = _adb_connect("host:kill", error);
+ adb_close(fd);
+
+ /* XXX can we better detect its death? */
+ adb_sleep_ms(2000);
+ goto start_server;
+ }
+ }
+
+ // if the command is start-server, we are done.
+ if (service == "host:start-server") {
+ return 0;
+ }
+
+ fd = _adb_connect(service, error);
+ if (fd == -1) {
+ D("_adb_connect error: %s\n", error->c_str());
+ } else if(fd == -2) {
+ fprintf(stderr,"** daemon still not running\n");
+ }
+ D("adb_connect: return fd %d\n", fd);
+
+ return fd;
+error:
+ adb_close(fd);
+ return -1;
+}
+
+
+bool adb_command(const std::string& service) {
+ std::string error;
+ int fd = adb_connect(service, &error);
+ if (fd < 0) {
+ fprintf(stderr, "error: %s\n", error.c_str());
+ return false;
+ }
+
+ if (!adb_status(fd, &error)) {
+ fprintf(stderr, "error: %s\n", error.c_str());
+ adb_close(fd);
+ return false;
+ }
+
+ return true;
+}
+
+bool adb_query(const std::string& service, std::string* result, std::string* error) {
+ D("adb_query: %s\n", service.c_str());
+ int fd = adb_connect(service, error);
+ if (fd < 0) {
+ return false;
+ }
+
+ result->clear();
+ if (!ReadProtocolString(fd, result, error)) {
+ adb_close(fd);
+ return false;
+ }
+ return true;
+}
diff --git a/adb/adb_client.h b/adb/adb_client.h
new file mode 100644
index 0000000..5de0638
--- /dev/null
+++ b/adb/adb_client.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ADB_CLIENT_H_
+#define _ADB_CLIENT_H_
+
+#include "adb.h"
+
+#include <string>
+
+// Connect to adb, connect to the named service, and return a valid fd for
+// interacting with that service upon success or a negative number on failure.
+int adb_connect(const std::string& service, std::string* error);
+int _adb_connect(const std::string& service, std::string* error);
+
+// Connect to adb, connect to the named service, returns true if the connection
+// succeeded AND the service returned OKAY. Outputs any returned error otherwise.
+bool adb_command(const std::string& service);
+
+// Connects to the named adb service and fills 'result' with the response.
+// Returns true on success; returns false and fills 'error' on failure.
+bool adb_query(const std::string& service, std::string* result, std::string* error);
+
+// Set the preferred transport to connect to.
+void adb_set_transport(TransportType type, const char* serial);
+
+// Set TCP specifics of the transport to use.
+void adb_set_tcp_specifics(int server_port);
+
+// Set TCP Hostname of the transport to use.
+void adb_set_tcp_name(const char* hostname);
+
+// Send commands to the current emulator instance. Will fail if there is not
+// exactly one emulator connected (or if you use -s <serial> with a <serial>
+// that does not designate an emulator).
+int adb_send_emulator_command(int argc, const char** argv, const char* serial);
+
+// Reads a standard adb status response (OKAY|FAIL) and returns true in the
+// event of OKAY, false in the event of FAIL or protocol error.
+bool adb_status(int fd, std::string* error);
+
+#endif
diff --git a/adb/adb_io.cpp b/adb/adb_io.cpp
new file mode 100644
index 0000000..5ae6ec3
--- /dev/null
+++ b/adb/adb_io.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TRACE_TAG TRACE_RWX
+
+#include "adb_io.h"
+
+#include <unistd.h>
+
+#include <base/stringprintf.h>
+
+#include "adb_trace.h"
+#include "adb_utils.h"
+#include "sysdeps.h"
+
+bool SendProtocolString(int fd, const std::string& s) {
+ int length = s.size();
+ if (length > 0xffff) {
+ length = 0xffff;
+ }
+
+ return WriteFdFmt(fd, "%04x", length) && WriteFdExactly(fd, s);
+}
+
+bool SendOkay(int fd) {
+ return WriteFdExactly(fd, "OKAY", 4);
+}
+
+bool SendFail(int fd, const std::string& reason) {
+ return WriteFdExactly(fd, "FAIL", 4) && SendProtocolString(fd, reason);
+}
+
+bool ReadFdExactly(int fd, void* buf, size_t len) {
+ char* p = reinterpret_cast<char*>(buf);
+
+ size_t len0 = len;
+
+ D("readx: fd=%d wanted=%zu\n", fd, len);
+ while (len > 0) {
+ int r = adb_read(fd, p, len);
+ if (r > 0) {
+ len -= r;
+ p += r;
+ } else if (r == -1) {
+ D("readx: fd=%d error %d: %s\n", fd, errno, strerror(errno));
+ return false;
+ } else {
+ D("readx: fd=%d disconnected\n", fd);
+ errno = 0;
+ return false;
+ }
+ }
+
+ D("readx: fd=%d wanted=%zu got=%zu\n", fd, len0, len0 - len);
+ if (ADB_TRACING) {
+ dump_hex(reinterpret_cast<const unsigned char*>(buf), len0);
+ }
+
+ return true;
+}
+
+bool WriteFdExactly(int fd, const void* buf, size_t len) {
+ const char* p = reinterpret_cast<const char*>(buf);
+ int r;
+
+ D("writex: fd=%d len=%d: ", fd, (int)len);
+ if (ADB_TRACING) {
+ dump_hex(reinterpret_cast<const unsigned char*>(buf), len);
+ }
+
+ while (len > 0) {
+ r = adb_write(fd, p, len);
+ if (r == -1) {
+ D("writex: fd=%d error %d: %s\n", fd, errno, strerror(errno));
+ if (errno == EAGAIN) {
+ adb_sleep_ms(1); // just yield some cpu time
+ continue;
+ } else if (errno == EPIPE) {
+ D("writex: fd=%d disconnected\n", fd);
+ errno = 0;
+ return false;
+ } else {
+ return false;
+ }
+ } else {
+ len -= r;
+ p += r;
+ }
+ }
+ return true;
+}
+
+bool WriteFdExactly(int fd, const char* str) {
+ return WriteFdExactly(fd, str, strlen(str));
+}
+
+bool WriteFdExactly(int fd, const std::string& str) {
+ return WriteFdExactly(fd, str.c_str(), str.size());
+}
+
+bool WriteFdFmt(int fd, const char* fmt, ...) {
+ std::string str;
+
+ va_list ap;
+ va_start(ap, fmt);
+ android::base::StringAppendV(&str, fmt, ap);
+ va_end(ap);
+
+ return WriteFdExactly(fd, str);
+}
diff --git a/adb/adb_io.h b/adb/adb_io.h
new file mode 100644
index 0000000..8d50a6d
--- /dev/null
+++ b/adb/adb_io.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ADB_IO_H
+#define ADB_IO_H
+
+#include <sys/types.h>
+
+#include <string>
+
+// Sends the protocol "OKAY" message.
+bool SendOkay(int fd);
+
+// Sends the protocol "FAIL" message, with the given failure reason.
+bool SendFail(int fd, const std::string& reason);
+
+// Writes a protocol-format string; a four hex digit length followed by the string data.
+bool SendProtocolString(int fd, const std::string& s);
+
+/*
+ * Reads exactly len bytes from fd into buf.
+ *
+ * Returns false if there is an error or if EOF was reached before len bytes
+ * were read. If EOF was found, errno will be set to 0.
+ *
+ * If this function fails, the contents of buf are undefined.
+ */
+bool ReadFdExactly(int fd, void *buf, size_t len);
+
+/*
+ * Writes exactly len bytes from buf to fd.
+ *
+ * Returns false if there is an error or if the fd was closed before the write
+ * completed. If the other end of the fd (such as in a socket, pipe, or fifo),
+ * is closed, errno will be set to 0.
+ */
+bool WriteFdExactly(int fd, const void* buf, size_t len);
+
+// Same as above, but for strings.
+bool WriteFdExactly(int fd, const char* s);
+bool WriteFdExactly(int fd, const std::string& s);
+
+// Same as above, but formats the string to send.
+bool WriteFdFmt(int fd, const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3)));
+
+#endif /* ADB_IO_H */
diff --git a/adb/adb_io_test.cpp b/adb/adb_io_test.cpp
new file mode 100644
index 0000000..8fd5cbf
--- /dev/null
+++ b/adb/adb_io_test.cpp
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "adb_io.h"
+
+#include <gtest/gtest.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+
+#include "base/file.h"
+
+class TemporaryFile {
+ public:
+ TemporaryFile() {
+ init("/data/local/tmp");
+ if (fd == -1) {
+ init("/tmp");
+ }
+ }
+
+ ~TemporaryFile() {
+ close(fd);
+ unlink(filename);
+ }
+
+ int fd;
+ char filename[1024];
+
+ private:
+ void init(const char* tmp_dir) {
+ snprintf(filename, sizeof(filename), "%s/TemporaryFile-XXXXXX", tmp_dir);
+ fd = mkstemp(filename);
+ }
+};
+
+TEST(io, ReadFdExactly_whole) {
+ const char expected[] = "Foobar";
+ TemporaryFile tf;
+ ASSERT_NE(-1, tf.fd);
+
+ ASSERT_TRUE(android::base::WriteStringToFd(expected, tf.fd)) << strerror(errno);
+ ASSERT_EQ(0, lseek(tf.fd, SEEK_SET, 0));
+
+ // Test reading the whole file.
+ char buf[sizeof(expected)] = {};
+ ASSERT_TRUE(ReadFdExactly(tf.fd, buf, sizeof(buf) - 1)) << strerror(errno);
+ EXPECT_STREQ(expected, buf);
+}
+
+TEST(io, ReadFdExactly_eof) {
+ const char expected[] = "Foobar";
+ TemporaryFile tf;
+ ASSERT_NE(-1, tf.fd);
+
+ ASSERT_TRUE(android::base::WriteStringToFd(expected, tf.fd)) << strerror(errno);
+ ASSERT_EQ(0, lseek(tf.fd, SEEK_SET, 0));
+
+ // Test that not having enough data will fail.
+ char buf[sizeof(expected) + 1] = {};
+ ASSERT_FALSE(ReadFdExactly(tf.fd, buf, sizeof(buf)));
+ EXPECT_EQ(0, errno) << strerror(errno);
+}
+
+TEST(io, ReadFdExactly_partial) {
+ const char input[] = "Foobar";
+ TemporaryFile tf;
+ ASSERT_NE(-1, tf.fd);
+
+ ASSERT_TRUE(android::base::WriteStringToFd(input, tf.fd)) << strerror(errno);
+ ASSERT_EQ(0, lseek(tf.fd, SEEK_SET, 0));
+
+ // Test reading a partial file.
+ char buf[sizeof(input) - 1] = {};
+ ASSERT_TRUE(ReadFdExactly(tf.fd, buf, sizeof(buf) - 1));
+
+ std::string expected(input);
+ expected.pop_back();
+ EXPECT_STREQ(expected.c_str(), buf);
+}
+
+TEST(io, WriteFdExactly_whole) {
+ const char expected[] = "Foobar";
+ TemporaryFile tf;
+ ASSERT_NE(-1, tf.fd);
+
+ // Test writing the whole string to the file.
+ ASSERT_TRUE(WriteFdExactly(tf.fd, expected, sizeof(expected)))
+ << strerror(errno);
+ ASSERT_EQ(0, lseek(tf.fd, SEEK_SET, 0));
+
+ std::string s;
+ ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s));
+ EXPECT_STREQ(expected, s.c_str());
+}
+
+TEST(io, WriteFdExactly_partial) {
+ const char buf[] = "Foobar";
+ TemporaryFile tf;
+ ASSERT_NE(-1, tf.fd);
+
+ // Test writing a partial string to the file.
+ ASSERT_TRUE(WriteFdExactly(tf.fd, buf, sizeof(buf) - 2)) << strerror(errno);
+ ASSERT_EQ(0, lseek(tf.fd, SEEK_SET, 0));
+
+ std::string expected(buf);
+ expected.pop_back();
+
+ std::string s;
+ ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s));
+ EXPECT_EQ(expected, s);
+}
+
+TEST(io, WriteFdExactly_ENOSPC) {
+ int fd = open("/dev/full", O_WRONLY);
+ ASSERT_NE(-1, fd);
+
+ char buf[] = "foo";
+ ASSERT_FALSE(WriteFdExactly(fd, buf, sizeof(buf)));
+ ASSERT_EQ(ENOSPC, errno);
+}
+
+TEST(io, WriteFdExactly_string) {
+ const char str[] = "Foobar";
+ TemporaryFile tf;
+ ASSERT_NE(-1, tf.fd);
+
+ // Test writing a partial string to the file.
+ ASSERT_TRUE(WriteFdExactly(tf.fd, str)) << strerror(errno);
+ ASSERT_EQ(0, lseek(tf.fd, SEEK_SET, 0));
+
+ std::string s;
+ ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s));
+ EXPECT_STREQ(str, s.c_str());
+}
+
+TEST(io, WriteFdFmt) {
+ TemporaryFile tf;
+ ASSERT_NE(-1, tf.fd);
+
+ // Test writing a partial string to the file.
+ ASSERT_TRUE(WriteFdFmt(tf.fd, "Foo%s%d", "bar", 123)) << strerror(errno);
+ ASSERT_EQ(0, lseek(tf.fd, SEEK_SET, 0));
+
+ std::string s;
+ ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s));
+ EXPECT_STREQ("Foobar123", s.c_str());
+}
diff --git a/adb/adb_listeners.cpp b/adb/adb_listeners.cpp
new file mode 100644
index 0000000..bb45022
--- /dev/null
+++ b/adb/adb_listeners.cpp
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "adb_listeners.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <base/stringprintf.h>
+#include <cutils/sockets.h>
+
+#include "sysdeps.h"
+#include "transport.h"
+
+int gListenAll = 0; /* Not static because it is used in commandline.c. */
+
+static alistener listener_list = {
+ .next = &listener_list,
+ .prev = &listener_list,
+};
+
+static void ss_listener_event_func(int _fd, unsigned ev, void *_l) {
+ asocket *s;
+
+ if(ev & FDE_READ) {
+ struct sockaddr addr;
+ socklen_t alen;
+ int fd;
+
+ alen = sizeof(addr);
+ fd = adb_socket_accept(_fd, &addr, &alen);
+ if(fd < 0) return;
+
+ adb_socket_setbufsize(fd, CHUNK_SIZE);
+
+ s = create_local_socket(fd);
+ if(s) {
+ connect_to_smartsocket(s);
+ return;
+ }
+
+ adb_close(fd);
+ }
+}
+
+static void listener_event_func(int _fd, unsigned ev, void* _l)
+{
+ alistener* listener = reinterpret_cast<alistener*>(_l);
+ asocket *s;
+
+ if (ev & FDE_READ) {
+ struct sockaddr addr;
+ socklen_t alen;
+ int fd;
+
+ alen = sizeof(addr);
+ fd = adb_socket_accept(_fd, &addr, &alen);
+ if (fd < 0) {
+ return;
+ }
+
+ s = create_local_socket(fd);
+ if (s) {
+ s->transport = listener->transport;
+ connect_to_remote(s, listener->connect_to);
+ return;
+ }
+
+ adb_close(fd);
+ }
+}
+
+static void free_listener(alistener* l)
+{
+ if (l->next) {
+ l->next->prev = l->prev;
+ l->prev->next = l->next;
+ l->next = l->prev = l;
+ }
+
+ // closes the corresponding fd
+ fdevent_remove(&l->fde);
+
+ if (l->local_name)
+ free((char*)l->local_name);
+
+ if (l->connect_to)
+ free((char*)l->connect_to);
+
+ if (l->transport) {
+ remove_transport_disconnect(l->transport, &l->disconnect);
+ }
+ free(l);
+}
+
+static void listener_disconnect(void* listener, atransport* t) {
+ free_listener(reinterpret_cast<alistener*>(listener));
+}
+
+static int local_name_to_fd(const char* name) {
+ if (!strncmp("tcp:", name, 4)) {
+ int port = atoi(name + 4);
+ if (gListenAll > 0) {
+ return socket_inaddr_any_server(port, SOCK_STREAM);
+ } else {
+ return socket_loopback_server(port, SOCK_STREAM);
+ }
+ }
+#if !defined(_WIN32) // No Unix-domain sockets on Windows.
+ // It's nonsensical to support the "reserved" space on the adb host side
+ if (!strncmp(name, "local:", 6)) {
+ return socket_local_server(name + 6, ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
+ } else if (!strncmp(name, "localabstract:", 14)) {
+ return socket_local_server(name + 14, ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
+ } else if (!strncmp(name, "localfilesystem:", 16)) {
+ return socket_local_server(name + 16, ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM);
+ }
+
+#endif
+ printf("unknown local portname '%s'\n", name);
+ return -1;
+}
+
+// Write the list of current listeners (network redirections) into a string.
+std::string format_listeners() {
+ std::string result;
+ for (alistener* l = listener_list.next; l != &listener_list; l = l->next) {
+ // Ignore special listeners like those for *smartsocket*
+ if (l->connect_to[0] == '*') {
+ continue;
+ }
+ // <device-serial> " " <local-name> " " <remote-name> "\n"
+ // Entries from "adb reverse" have no serial.
+ android::base::StringAppendF(&result, "%s %s %s\n",
+ l->transport->serial ? l->transport->serial : "(reverse)",
+ l->local_name, l->connect_to);
+ }
+ return result;
+}
+
+InstallStatus remove_listener(const char *local_name, atransport* transport) {
+ alistener *l;
+
+ for (l = listener_list.next; l != &listener_list; l = l->next) {
+ if (!strcmp(local_name, l->local_name)) {
+ listener_disconnect(l, l->transport);
+ return INSTALL_STATUS_OK;
+ }
+ }
+ return INSTALL_STATUS_LISTENER_NOT_FOUND;
+}
+
+void remove_all_listeners(void)
+{
+ alistener *l, *l_next;
+ for (l = listener_list.next; l != &listener_list; l = l_next) {
+ l_next = l->next;
+ // Never remove smart sockets.
+ if (l->connect_to[0] == '*')
+ continue;
+ listener_disconnect(l, l->transport);
+ }
+}
+
+InstallStatus install_listener(const std::string& local_name,
+ const char *connect_to,
+ atransport* transport,
+ int no_rebind)
+{
+ for (alistener* l = listener_list.next; l != &listener_list; l = l->next) {
+ if (local_name == l->local_name) {
+ char* cto;
+
+ /* can't repurpose a smartsocket */
+ if(l->connect_to[0] == '*') {
+ return INSTALL_STATUS_INTERNAL_ERROR;
+ }
+
+ /* can't repurpose a listener if 'no_rebind' is true */
+ if (no_rebind) {
+ return INSTALL_STATUS_CANNOT_REBIND;
+ }
+
+ cto = strdup(connect_to);
+ if(cto == 0) {
+ return INSTALL_STATUS_INTERNAL_ERROR;
+ }
+
+ free((void*) l->connect_to);
+ l->connect_to = cto;
+ if (l->transport != transport) {
+ remove_transport_disconnect(l->transport, &l->disconnect);
+ l->transport = transport;
+ add_transport_disconnect(l->transport, &l->disconnect);
+ }
+ return INSTALL_STATUS_OK;
+ }
+ }
+
+ alistener* listener = reinterpret_cast<alistener*>(
+ calloc(1, sizeof(alistener)));
+ if (listener == nullptr) {
+ goto nomem;
+ }
+
+ listener->local_name = strdup(local_name.c_str());
+ if (listener->local_name == nullptr) {
+ goto nomem;
+ }
+
+ listener->connect_to = strdup(connect_to);
+ if (listener->connect_to == nullptr) {
+ goto nomem;
+ }
+
+ listener->fd = local_name_to_fd(listener->local_name);
+ if (listener->fd < 0) {
+ printf("cannot bind '%s': %s\n", listener->local_name, strerror(errno));
+ free(listener->local_name);
+ free(listener->connect_to);
+ free(listener);
+ return INSTALL_STATUS_CANNOT_BIND;
+ }
+
+ close_on_exec(listener->fd);
+ if (!strcmp(listener->connect_to, "*smartsocket*")) {
+ fdevent_install(&listener->fde, listener->fd, ss_listener_event_func,
+ listener);
+ } else {
+ fdevent_install(&listener->fde, listener->fd, listener_event_func,
+ listener);
+ }
+ fdevent_set(&listener->fde, FDE_READ);
+
+ listener->next = &listener_list;
+ listener->prev = listener_list.prev;
+ listener->next->prev = listener;
+ listener->prev->next = listener;
+ listener->transport = transport;
+
+ if (transport) {
+ listener->disconnect.opaque = listener;
+ listener->disconnect.func = listener_disconnect;
+ add_transport_disconnect(transport, &listener->disconnect);
+ }
+ return INSTALL_STATUS_OK;
+
+nomem:
+ fatal("cannot allocate listener");
+ return INSTALL_STATUS_INTERNAL_ERROR;
+}
diff --git a/adb/adb_listeners.h b/adb/adb_listeners.h
new file mode 100644
index 0000000..67deb21
--- /dev/null
+++ b/adb/adb_listeners.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __ADB_LISTENERS_H
+#define __ADB_LISTENERS_H
+
+#include "adb.h"
+
+#include <string>
+
+// error/status codes for install_listener.
+enum InstallStatus {
+ INSTALL_STATUS_OK = 0,
+ INSTALL_STATUS_INTERNAL_ERROR = -1,
+ INSTALL_STATUS_CANNOT_BIND = -2,
+ INSTALL_STATUS_CANNOT_REBIND = -3,
+ INSTALL_STATUS_LISTENER_NOT_FOUND = -4,
+};
+
+InstallStatus install_listener(const std::string& local_name,
+ const char* connect_to,
+ atransport* transport,
+ int no_rebind);
+
+std::string format_listeners();
+
+InstallStatus remove_listener(const char* local_name, atransport* transport);
+void remove_all_listeners(void);
+
+#endif /* __ADB_LISTENERS_H */
diff --git a/adb/adb_trace.h b/adb/adb_trace.h
new file mode 100644
index 0000000..dbc7ec8
--- /dev/null
+++ b/adb/adb_trace.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __ADB_TRACE_H
+#define __ADB_TRACE_H
+
+#if !ADB_HOST
+#include <android/log.h>
+#else
+#include <stdio.h>
+#endif
+
+/* IMPORTANT: if you change the following list, don't
+ * forget to update the corresponding 'tags' table in
+ * the adb_trace_init() function implemented in adb.c
+ */
+enum AdbTrace {
+ TRACE_ADB = 0, /* 0x001 */
+ TRACE_SOCKETS,
+ TRACE_PACKETS,
+ TRACE_TRANSPORT,
+ TRACE_RWX, /* 0x010 */
+ TRACE_USB,
+ TRACE_SYNC,
+ TRACE_SYSDEPS,
+ TRACE_JDWP, /* 0x100 */
+ TRACE_SERVICES,
+ TRACE_AUTH,
+ TRACE_FDEVENT,
+} ;
+
+#if !ADB_HOST
+/*
+ * When running inside the emulator, guest's adbd can connect to 'adb-debug'
+ * qemud service that can display adb trace messages (on condition that emulator
+ * has been started with '-debug adb' option).
+ */
+
+/* Delivers a trace message to the emulator via QEMU pipe. */
+void adb_qemu_trace(const char* fmt, ...);
+/* Macro to use to send ADB trace messages to the emulator. */
+#define DQ(...) adb_qemu_trace(__VA_ARGS__)
+#else
+#define DQ(...) ((void)0)
+#endif /* !ADB_HOST */
+
+extern int adb_trace_mask;
+extern unsigned char adb_trace_output_count;
+void adb_trace_init(char**);
+
+# define ADB_TRACING ((adb_trace_mask & (1 << TRACE_TAG)) != 0)
+
+/* you must define TRACE_TAG before using this macro */
+#if ADB_HOST
+# define D(...) \
+ do { \
+ if (ADB_TRACING) { \
+ int save_errno = errno; \
+ adb_mutex_lock(&D_lock); \
+ fprintf(stderr, "%16s: %5d:%5lu | ", \
+ __FUNCTION__, \
+ getpid(), adb_thread_id()); \
+ errno = save_errno; \
+ fprintf(stderr, __VA_ARGS__ ); \
+ fflush(stderr); \
+ adb_mutex_unlock(&D_lock); \
+ errno = save_errno; \
+ } \
+ } while (0)
+# define DR(...) \
+ do { \
+ if (ADB_TRACING) { \
+ int save_errno = errno; \
+ adb_mutex_lock(&D_lock); \
+ errno = save_errno; \
+ fprintf(stderr, __VA_ARGS__ ); \
+ fflush(stderr); \
+ adb_mutex_unlock(&D_lock); \
+ errno = save_errno; \
+ } \
+ } while (0)
+#else
+# define D(...) \
+ do { \
+ if (ADB_TRACING) { \
+ __android_log_print( \
+ ANDROID_LOG_INFO, \
+ __FUNCTION__, \
+ __VA_ARGS__ ); \
+ } \
+ } while (0)
+# define DR(...) \
+ do { \
+ if (ADB_TRACING) { \
+ __android_log_print( \
+ ANDROID_LOG_INFO, \
+ __FUNCTION__, \
+ __VA_ARGS__ ); \
+ } \
+ } while (0)
+#endif /* ADB_HOST */
+
+#endif /* __ADB_TRACE_H */
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
new file mode 100644
index 0000000..cd3c7bc
--- /dev/null
+++ b/adb/adb_utils.cpp
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TRACE_TAG TRACE_ADB
+
+#include "adb_utils.h"
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+
+#include <base/logging.h>
+#include <base/stringprintf.h>
+#include <base/strings.h>
+#include <cutils/sockets.h>
+
+#include "adb_trace.h"
+#include "sysdeps.h"
+
+#if defined(_WIN32)
+#include <ws2tcpip.h>
+#else
+#include <netdb.h>
+#endif
+
+bool getcwd(std::string* s) {
+ char* cwd = getcwd(nullptr, 0);
+ if (cwd != nullptr) *s = cwd;
+ free(cwd);
+ return (cwd != nullptr);
+}
+
+bool directory_exists(const std::string& path) {
+ struct stat sb;
+ return lstat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode);
+}
+
+std::string escape_arg(const std::string& s) {
+ std::string result = s;
+
+ // Escape any ' in the string (before we single-quote the whole thing).
+ // The correct way to do this for the shell is to replace ' with '\'' --- that is,
+ // close the existing single-quoted string, escape a single single-quote, and start
+ // a new single-quoted string. Like the C preprocessor, the shell will concatenate
+ // these pieces into one string.
+ for (size_t i = 0; i < s.size(); ++i) {
+ if (s[i] == '\'') {
+ result.insert(i, "'\\'");
+ i += 2;
+ }
+ }
+
+ // Prefix and suffix the whole string with '.
+ result.insert(result.begin(), '\'');
+ result.push_back('\'');
+ return result;
+}
+
+int mkdirs(const char *path)
+{
+ int ret;
+ char *x = (char *)path + 1;
+
+ for(;;) {
+ x = adb_dirstart(x);
+ if(x == 0) return 0;
+ *x = 0;
+ ret = adb_mkdir(path, 0775);
+ *x = OS_PATH_SEPARATOR;
+ if((ret < 0) && (errno != EEXIST)) {
+ return ret;
+ }
+ x++;
+ }
+ return 0;
+}
+
+void dump_hex(const void* data, size_t byte_count) {
+ byte_count = std::min(byte_count, size_t(16));
+
+ const uint8_t* p = reinterpret_cast<const uint8_t*>(data);
+
+ std::string line;
+ for (size_t i = 0; i < byte_count; ++i) {
+ android::base::StringAppendF(&line, "%02x", p[i]);
+ }
+ line.push_back(' ');
+
+ for (size_t i = 0; i < byte_count; ++i) {
+ int c = p[i];
+ if (c < 32 || c > 127) {
+ c = '.';
+ }
+ line.push_back(c);
+ }
+
+ DR("%s\n", line.c_str());
+}
+
+bool parse_host_and_port(const std::string& address,
+ std::string* canonical_address,
+ std::string* host, int* port,
+ std::string* error) {
+ host->clear();
+
+ bool ipv6 = true;
+ bool saw_port = false;
+ size_t colons = std::count(address.begin(), address.end(), ':');
+ size_t dots = std::count(address.begin(), address.end(), '.');
+ std::string port_str;
+ if (address[0] == '[') {
+ // [::1]:123
+ if (address.rfind("]:") == std::string::npos) {
+ *error = android::base::StringPrintf("bad IPv6 address '%s'", address.c_str());
+ return false;
+ }
+ *host = address.substr(1, (address.find("]:") - 1));
+ port_str = address.substr(address.rfind("]:") + 2);
+ saw_port = true;
+ } else if (dots == 0 && colons >= 2 && colons <= 7) {
+ // ::1
+ *host = address;
+ } else if (colons <= 1) {
+ // 1.2.3.4 or some.accidental.domain.com
+ ipv6 = false;
+ std::vector<std::string> pieces = android::base::Split(address, ":");
+ *host = pieces[0];
+ if (pieces.size() > 1) {
+ port_str = pieces[1];
+ saw_port = true;
+ }
+ }
+
+ if (host->empty()) {
+ *error = android::base::StringPrintf("no host in '%s'", address.c_str());
+ return false;
+ }
+
+ if (saw_port) {
+ if (sscanf(port_str.c_str(), "%d", port) != 1 || *port <= 0 || *port > 65535) {
+ *error = android::base::StringPrintf("bad port number '%s' in '%s'",
+ port_str.c_str(), address.c_str());
+ return false;
+ }
+ }
+
+ *canonical_address = android::base::StringPrintf(ipv6 ? "[%s]:%d" : "%s:%d", host->c_str(), *port);
+ LOG(DEBUG) << "parsed " << address << " as " << *host << " and " << *port
+ << " (" << *canonical_address << ")";
+ return true;
+}
+
+int network_connect(const std::string& host, int port, int type, int timeout, std::string* error) {
+ int getaddrinfo_error = 0;
+ int fd = socket_network_client_timeout(host.c_str(), port, type, timeout, &getaddrinfo_error);
+ if (fd != -1) {
+ return fd;
+ }
+ if (getaddrinfo_error != 0) {
+ // TODO: gai_strerror is not thread safe on Win32.
+ *error = gai_strerror(getaddrinfo_error);
+ } else {
+ *error = strerror(errno);
+ }
+ return -1;
+}
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
new file mode 100644
index 0000000..d1a3f5f
--- /dev/null
+++ b/adb/adb_utils.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ADB_UTILS_H_
+#define _ADB_UTILS_H_
+
+#include <string>
+
+bool getcwd(std::string* cwd);
+bool directory_exists(const std::string& path);
+
+int mkdirs(const char *path);
+
+std::string escape_arg(const std::string& s);
+
+void dump_hex(const void* ptr, size_t byte_count);
+
+// Parses 'address' into 'host' and 'port'.
+// If no port is given, takes the default from *port.
+// 'canonical_address' then becomes "host:port" or "[host]:port" as appropriate.
+// Note that no real checking is done that 'host' or 'port' is valid; that's
+// left to getaddrinfo(3).
+// Returns false on failure and sets *error to an appropriate message.
+bool parse_host_and_port(const std::string& address,
+ std::string* canonical_address,
+ std::string* host, int* port,
+ std::string* error);
+
+int network_connect(const std::string& host, int port, int type, int timeout, std::string* error);
+
+#endif
diff --git a/adb/adb_utils_test.cpp b/adb/adb_utils_test.cpp
new file mode 100644
index 0000000..7aa610a
--- /dev/null
+++ b/adb/adb_utils_test.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "adb_utils.h"
+
+#include <gtest/gtest.h>
+
+TEST(adb_utils, directory_exists) {
+ ASSERT_TRUE(directory_exists("/proc"));
+ ASSERT_FALSE(directory_exists("/proc/self")); // Symbolic link.
+ ASSERT_FALSE(directory_exists("/proc/does-not-exist"));
+}
+
+TEST(adb_utils, escape_arg) {
+ ASSERT_EQ(R"('')", escape_arg(""));
+
+ ASSERT_EQ(R"('abc')", escape_arg("abc"));
+
+ ASSERT_EQ(R"(' abc')", escape_arg(" abc"));
+ ASSERT_EQ(R"(''\''abc')", escape_arg("'abc"));
+ ASSERT_EQ(R"('"abc')", escape_arg("\"abc"));
+ ASSERT_EQ(R"('\abc')", escape_arg("\\abc"));
+ ASSERT_EQ(R"('(abc')", escape_arg("(abc"));
+ ASSERT_EQ(R"(')abc')", escape_arg(")abc"));
+
+ ASSERT_EQ(R"('abc abc')", escape_arg("abc abc"));
+ ASSERT_EQ(R"('abc'\''abc')", escape_arg("abc'abc"));
+ ASSERT_EQ(R"('abc"abc')", escape_arg("abc\"abc"));
+ ASSERT_EQ(R"('abc\abc')", escape_arg("abc\\abc"));
+ ASSERT_EQ(R"('abc(abc')", escape_arg("abc(abc"));
+ ASSERT_EQ(R"('abc)abc')", escape_arg("abc)abc"));
+
+ ASSERT_EQ(R"('abc ')", escape_arg("abc "));
+ ASSERT_EQ(R"('abc'\''')", escape_arg("abc'"));
+ ASSERT_EQ(R"('abc"')", escape_arg("abc\""));
+ ASSERT_EQ(R"('abc\')", escape_arg("abc\\"));
+ ASSERT_EQ(R"('abc(')", escape_arg("abc("));
+ ASSERT_EQ(R"('abc)')", escape_arg("abc)"));
+}
+
+TEST(adb_utils, parse_host_and_port) {
+ std::string canonical_address;
+ std::string host;
+ int port;
+ std::string error;
+
+ // Name, default port.
+ port = 123;
+ ASSERT_TRUE(parse_host_and_port("www.google.com", &canonical_address, &host, &port, &error));
+ ASSERT_EQ("www.google.com:123", canonical_address);
+ ASSERT_EQ("www.google.com", host);
+ ASSERT_EQ(123, port);
+
+ // Name, explicit port.
+ ASSERT_TRUE(parse_host_and_port("www.google.com:666", &canonical_address, &host, &port, &error));
+ ASSERT_EQ("www.google.com:666", canonical_address);
+ ASSERT_EQ("www.google.com", host);
+ ASSERT_EQ(666, port);
+
+ // IPv4, default port.
+ port = 123;
+ ASSERT_TRUE(parse_host_and_port("1.2.3.4", &canonical_address, &host, &port, &error));
+ ASSERT_EQ("1.2.3.4:123", canonical_address);
+ ASSERT_EQ("1.2.3.4", host);
+ ASSERT_EQ(123, port);
+
+ // IPv4, explicit port.
+ ASSERT_TRUE(parse_host_and_port("1.2.3.4:666", &canonical_address, &host, &port, &error));
+ ASSERT_EQ("1.2.3.4:666", canonical_address);
+ ASSERT_EQ("1.2.3.4", host);
+ ASSERT_EQ(666, port);
+
+ // Simple IPv6, default port.
+ port = 123;
+ ASSERT_TRUE(parse_host_and_port("::1", &canonical_address, &host, &port, &error));
+ ASSERT_EQ("[::1]:123", canonical_address);
+ ASSERT_EQ("::1", host);
+ ASSERT_EQ(123, port);
+
+ // Simple IPv6, explicit port.
+ ASSERT_TRUE(parse_host_and_port("[::1]:666", &canonical_address, &host, &port, &error));
+ ASSERT_EQ("[::1]:666", canonical_address);
+ ASSERT_EQ("::1", host);
+ ASSERT_EQ(666, port);
+
+ // Hairy IPv6, default port.
+ port = 123;
+ ASSERT_TRUE(parse_host_and_port("fe80::200:5aee:feaa:20a2", &canonical_address, &host, &port, &error));
+ ASSERT_EQ("[fe80::200:5aee:feaa:20a2]:123", canonical_address);
+ ASSERT_EQ("fe80::200:5aee:feaa:20a2", host);
+ ASSERT_EQ(123, port);
+
+ // Simple IPv6, explicit port.
+ ASSERT_TRUE(parse_host_and_port("[fe80::200:5aee:feaa:20a2]:666", &canonical_address, &host, &port, &error));
+ ASSERT_EQ("[fe80::200:5aee:feaa:20a2]:666", canonical_address);
+ ASSERT_EQ("fe80::200:5aee:feaa:20a2", host);
+ ASSERT_EQ(666, port);
+
+ // Invalid IPv4.
+ EXPECT_FALSE(parse_host_and_port("1.2.3.4:", &canonical_address, &host, &port, &error));
+ EXPECT_FALSE(parse_host_and_port("1.2.3.4::", &canonical_address, &host, &port, &error));
+ EXPECT_FALSE(parse_host_and_port("1.2.3.4:hello", &canonical_address, &host, &port, &error));
+ EXPECT_FALSE(parse_host_and_port(":123", &canonical_address, &host, &port, &error));
+
+ // Invalid IPv6.
+ EXPECT_FALSE(parse_host_and_port(":1", &canonical_address, &host, &port, &error));
+ EXPECT_FALSE(parse_host_and_port("::::::::1", &canonical_address, &host, &port, &error));
+ EXPECT_FALSE(parse_host_and_port("[::1", &canonical_address, &host, &port, &error));
+ EXPECT_FALSE(parse_host_and_port("[::1]", &canonical_address, &host, &port, &error));
+ EXPECT_FALSE(parse_host_and_port("[::1]:", &canonical_address, &host, &port, &error));
+ EXPECT_FALSE(parse_host_and_port("[::1]::", &canonical_address, &host, &port, &error));
+ EXPECT_FALSE(parse_host_and_port("[::1]:hello", &canonical_address, &host, &port, &error));
+
+ // Invalid ports.
+ EXPECT_FALSE(parse_host_and_port("[::1]:-1", &canonical_address, &host, &port, &error));
+ EXPECT_FALSE(parse_host_and_port("[::1]:0", &canonical_address, &host, &port, &error));
+ EXPECT_FALSE(parse_host_and_port("[::1]:65536", &canonical_address, &host, &port, &error));
+ EXPECT_FALSE(parse_host_and_port("1.2.3.4:-1", &canonical_address, &host, &port, &error));
+ EXPECT_FALSE(parse_host_and_port("1.2.3.4:0", &canonical_address, &host, &port, &error));
+ EXPECT_FALSE(parse_host_and_port("1.2.3.4:65536", &canonical_address, &host, &port, &error));
+}
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
new file mode 100644
index 0000000..c018b8a
--- /dev/null
+++ b/adb/client/main.cpp
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TRACE_TAG TRACE_ADB
+
+#include "sysdeps.h"
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+// We only build the affinity WAR code for Linux.
+#if defined(__linux__)
+#include <sched.h>
+#endif
+
+#include "base/file.h"
+#include "base/logging.h"
+#include "base/stringprintf.h"
+
+#include "adb.h"
+#include "adb_auth.h"
+#include "adb_listeners.h"
+#include "transport.h"
+
+#if defined(WORKAROUND_BUG6558362) && defined(__linux__)
+static const bool kWorkaroundBug6558362 = true;
+#else
+static const bool kWorkaroundBug6558362 = false;
+#endif
+
+static void adb_workaround_affinity(void) {
+#if defined(__linux__)
+ const char affinity_env[] = "ADB_CPU_AFFINITY_BUG6558362";
+ const char* cpunum_str = getenv(affinity_env);
+ if (cpunum_str == nullptr || *cpunum_str == '\0') {
+ return;
+ }
+
+ char* strtol_res;
+ int cpu_num = strtol(cpunum_str, &strtol_res, 0);
+ if (*strtol_res != '\0') {
+ fatal("bad number (%s) in env var %s. Expecting 0..n.\n", cpunum_str,
+ affinity_env);
+ }
+
+ cpu_set_t cpu_set;
+ sched_getaffinity(0, sizeof(cpu_set), &cpu_set);
+ D("orig cpu_set[0]=0x%08lx\n", cpu_set.__bits[0]);
+
+ CPU_ZERO(&cpu_set);
+ CPU_SET(cpu_num, &cpu_set);
+ sched_setaffinity(0, sizeof(cpu_set), &cpu_set);
+
+ sched_getaffinity(0, sizeof(cpu_set), &cpu_set);
+ D("new cpu_set[0]=0x%08lx\n", cpu_set.__bits[0]);
+#else
+ // No workaround was ever implemented for the other platforms.
+#endif
+}
+
+#if defined(_WIN32)
+static const char kNullFileName[] = "NUL";
+
+static BOOL WINAPI ctrlc_handler(DWORD type) {
+ exit(STATUS_CONTROL_C_EXIT);
+ return TRUE;
+}
+
+static std::string GetLogFilePath() {
+ const char log_name[] = "adb.log";
+ char temp_path[MAX_PATH - sizeof(log_name) + 1];
+
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/aa364992%28v=vs.85%29.aspx
+ DWORD nchars = GetTempPath(sizeof(temp_path), temp_path);
+ CHECK_LE(nchars, sizeof(temp_path));
+ if (nchars == 0) {
+ // TODO(danalbert): Log the error message from FormatError().
+ // Windows unfortunately has two errnos, errno and GetLastError(), so
+ // I'm not sure what to do about PLOG here. Probably better to just
+ // ignore it and add a simplified version of FormatError() for use in
+ // log messages.
+ LOG(ERROR) << "Error creating log file";
+ }
+
+ return std::string(temp_path) + log_name;
+}
+#else
+static const char kNullFileName[] = "/dev/null";
+
+static std::string GetLogFilePath() {
+ return std::string("/tmp/adb.log");
+}
+#endif
+
+static void close_stdin() {
+ int fd = unix_open(kNullFileName, O_RDONLY);
+ CHECK_NE(fd, -1);
+ dup2(fd, STDIN_FILENO);
+ unix_close(fd);
+}
+
+static void setup_daemon_logging(void) {
+ int fd = unix_open(GetLogFilePath().c_str(), O_WRONLY | O_CREAT | O_APPEND,
+ 0640);
+ if (fd == -1) {
+ fd = unix_open(kNullFileName, O_WRONLY);
+ }
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+ unix_close(fd);
+
+#ifdef _WIN32
+ // On Windows, stderr is buffered by default, so switch to non-buffered
+ // to match Linux.
+ setvbuf(stderr, NULL, _IONBF, 0);
+#endif
+ fprintf(stderr, "--- adb starting (pid %d) ---\n", getpid());
+}
+
+int adb_main(int is_daemon, int server_port) {
+ HOST = 1;
+
+#if defined(_WIN32)
+ SetConsoleCtrlHandler(ctrlc_handler, TRUE);
+#else
+ signal(SIGPIPE, SIG_IGN);
+#endif
+
+ init_transport_registration();
+
+ if (kWorkaroundBug6558362 && is_daemon) {
+ adb_workaround_affinity();
+ }
+
+ usb_init();
+ local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT);
+ adb_auth_init();
+
+ std::string local_name = android::base::StringPrintf("tcp:%d", server_port);
+ if (install_listener(local_name, "*smartsocket*", nullptr, 0)) {
+ LOG(FATAL) << "Could not install *smartsocket* listener";
+ }
+
+ if (is_daemon) {
+ // Inform our parent that we are up and running.
+ // TODO(danalbert): Can't use SendOkay because we're sending "OK\n", not
+ // "OKAY".
+ // TODO(danalbert): Why do we use stdout for Windows? There is a
+ // comment in launch_server() that suggests that non-Windows uses
+ // stderr because it is non-buffered. So perhaps the history is that
+ // stdout was preferred for all platforms, but it was discovered that
+ // non-Windows needed a non-buffered fd, so stderr was used there.
+ // Note that using stderr on unix means that if you do
+ // `ADB_TRACE=all adb start-server`, it will say "ADB server didn't ACK"
+ // and "* failed to start daemon *" because the adb server will write
+ // logging to stderr, obscuring the OK\n output that is sent to stderr.
+#if defined(_WIN32)
+ int reply_fd = STDOUT_FILENO;
+ // Change stdout mode to binary so \n => \r\n translation does not
+ // occur. In a moment stdout will be reopened to the daemon log file
+ // anyway.
+ _setmode(reply_fd, _O_BINARY);
+#else
+ int reply_fd = STDERR_FILENO;
+#endif
+ android::base::WriteStringToFd("OK\n", reply_fd);
+ close_stdin();
+ setup_daemon_logging();
+ }
+
+ D("Event loop starting\n");
+ fdevent_loop();
+
+ return 0;
+}
+
+int main(int argc, char** argv) {
+ adb_sysdeps_init();
+ adb_trace_init(argv);
+ D("Handling commandline()\n");
+ return adb_commandline(argc - 1, const_cast<const char**>(argv + 1));
+}
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
new file mode 100644
index 0000000..f190336
--- /dev/null
+++ b/adb/commandline.cpp
@@ -0,0 +1,1660 @@
+/*
+ * Copyright (C) 2007 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 TRACE_TAG TRACE_ADB
+
+#include "sysdeps.h"
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <string>
+
+#include <base/stringprintf.h>
+
+#if !defined(_WIN32)
+#include <termios.h>
+#include <unistd.h>
+#endif
+
+#include "adb.h"
+#include "adb_auth.h"
+#include "adb_client.h"
+#include "adb_io.h"
+#include "adb_utils.h"
+#include "file_sync_service.h"
+
+static int install_app(TransportType t, const char* serial, int argc, const char** argv);
+static int install_multiple_app(TransportType t, const char* serial, int argc, const char** argv);
+static int uninstall_app(TransportType t, const char* serial, int argc, const char** argv);
+
+static std::string gProductOutPath;
+extern int gListenAll;
+
+static std::string product_file(const char *extra) {
+ if (gProductOutPath.empty()) {
+ fprintf(stderr, "adb: Product directory not specified; "
+ "use -p or define ANDROID_PRODUCT_OUT\n");
+ exit(1);
+ }
+
+ return android::base::StringPrintf("%s%s%s",
+ gProductOutPath.c_str(), OS_PATH_SEPARATOR_STR, extra);
+}
+
+static void version(FILE* out) {
+ fprintf(out, "Android Debug Bridge version %d.%d.%d\nRevision %s\n",
+ ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION, ADB_REVISION);
+}
+
+static void help() {
+ version(stderr);
+
+ fprintf(stderr,
+ "\n"
+ " -a - directs adb to listen on all interfaces for a connection\n"
+ " -d - directs command to the only connected USB device\n"
+ " returns an error if more than one USB device is present.\n"
+ " -e - directs command to the only running emulator.\n"
+ " returns an error if more than one emulator is running.\n"
+ " -s <specific device> - directs command to the device or emulator with the given\n"
+ " serial number or qualifier. Overrides ANDROID_SERIAL\n"
+ " environment variable.\n"
+ " -p <product name or path> - simple product name like 'sooner', or\n"
+ " a relative/absolute path to a product\n"
+ " out directory like 'out/target/product/sooner'.\n"
+ " If -p is not specified, the ANDROID_PRODUCT_OUT\n"
+ " environment variable is used, which must\n"
+ " be an absolute path.\n"
+ " -H - Name of adb server host (default: localhost)\n"
+ " -P - Port of adb server (default: 5037)\n"
+ " devices [-l] - list all connected devices\n"
+ " ('-l' will also list device qualifiers)\n"
+ " connect <host>[:<port>] - connect to a device via TCP/IP\n"
+ " Port 5555 is used by default if no port number is specified.\n"
+ " disconnect [<host>[:<port>]] - disconnect from a TCP/IP device.\n"
+ " Port 5555 is used by default if no port number is specified.\n"
+ " Using this command with no additional arguments\n"
+ " will disconnect from all connected TCP/IP devices.\n"
+ "\n"
+ "device commands:\n"
+ " adb push [-p] <local> <remote>\n"
+ " - copy file/dir to device\n"
+ " ('-p' to display the transfer progress)\n"
+ " adb pull [-p] [-a] <remote> [<local>]\n"
+ " - copy file/dir from device\n"
+ " ('-p' to display the transfer progress)\n"
+ " ('-a' means copy timestamp and mode)\n"
+ " adb sync [ <directory> ] - copy host->device only if changed\n"
+ " (-l means list but don't copy)\n"
+ " adb shell - run remote shell interactively\n"
+ " adb shell <command> - run remote shell command\n"
+ " adb emu <command> - run emulator console command\n"
+ " adb logcat [ <filter-spec> ] - View device log\n"
+ " adb forward --list - list all forward socket connections.\n"
+ " the format is a list of lines with the following format:\n"
+ " <serial> \" \" <local> \" \" <remote> \"\\n\"\n"
+ " adb forward <local> <remote> - forward socket connections\n"
+ " forward specs are one of: \n"
+ " tcp:<port>\n"
+ " localabstract:<unix domain socket name>\n"
+ " localreserved:<unix domain socket name>\n"
+ " localfilesystem:<unix domain socket name>\n"
+ " dev:<character device name>\n"
+ " jdwp:<process pid> (remote only)\n"
+ " adb forward --no-rebind <local> <remote>\n"
+ " - same as 'adb forward <local> <remote>' but fails\n"
+ " if <local> is already forwarded\n"
+ " adb forward --remove <local> - remove a specific forward socket connection\n"
+ " adb forward --remove-all - remove all forward socket connections\n"
+ " adb reverse --list - list all reverse socket connections from device\n"
+ " adb reverse <remote> <local> - reverse socket connections\n"
+ " reverse specs are one of:\n"
+ " tcp:<port>\n"
+ " localabstract:<unix domain socket name>\n"
+ " localreserved:<unix domain socket name>\n"
+ " localfilesystem:<unix domain socket name>\n"
+ " adb reverse --norebind <remote> <local>\n"
+ " - same as 'adb reverse <remote> <local>' but fails\n"
+ " if <remote> is already reversed.\n"
+ " adb reverse --remove <remote>\n"
+ " - remove a specific reversed socket connection\n"
+ " adb reverse --remove-all - remove all reversed socket connections from device\n"
+ " adb jdwp - list PIDs of processes hosting a JDWP transport\n"
+ " adb install [-lrtsd] <file>\n"
+ " adb install-multiple [-lrtsdp] <file...>\n"
+ " - push this package file to the device and install it\n"
+ " (-l: forward lock application)\n"
+ " (-r: replace existing application)\n"
+ " (-t: allow test packages)\n"
+ " (-s: install application on sdcard)\n"
+ " (-d: allow version code downgrade)\n"
+ " (-p: partial application install)\n"
+ " adb uninstall [-k] <package> - remove this app package from the device\n"
+ " ('-k' means keep the data and cache directories)\n"
+ " adb bugreport - return all information from the device\n"
+ " that should be included in a bug report.\n"
+ "\n"
+ " adb backup [-f <file>] [-apk|-noapk] [-obb|-noobb] [-shared|-noshared] [-all] [-system|-nosystem] [<packages...>]\n"
+ " - write an archive of the device's data to <file>.\n"
+ " If no -f option is supplied then the data is written\n"
+ " to \"backup.ab\" in the current directory.\n"
+ " (-apk|-noapk enable/disable backup of the .apks themselves\n"
+ " in the archive; the default is noapk.)\n"
+ " (-obb|-noobb enable/disable backup of any installed apk expansion\n"
+ " (aka .obb) files associated with each application; the default\n"
+ " is noobb.)\n"
+ " (-shared|-noshared enable/disable backup of the device's\n"
+ " shared storage / SD card contents; the default is noshared.)\n"
+ " (-all means to back up all installed applications)\n"
+ " (-system|-nosystem toggles whether -all automatically includes\n"
+ " system applications; the default is to include system apps)\n"
+ " (<packages...> is the list of applications to be backed up. If\n"
+ " the -all or -shared flags are passed, then the package\n"
+ " list is optional. Applications explicitly given on the\n"
+ " command line will be included even if -nosystem would\n"
+ " ordinarily cause them to be omitted.)\n"
+ "\n"
+ " adb restore <file> - restore device contents from the <file> backup archive\n"
+ "\n"
+ " adb disable-verity - disable dm-verity checking on USERDEBUG builds\n"
+ " adb enable-verity - re-enable dm-verity checking on USERDEBUG builds\n"
+ " adb keygen <file> - generate adb public/private key. The private key is stored in <file>,\n"
+ " and the public key is stored in <file>.pub. Any existing files\n"
+ " are overwritten.\n"
+ " adb help - show this help message\n"
+ " adb version - show version num\n"
+ "\n"
+ "scripting:\n"
+ " adb wait-for-device - block until device is online\n"
+ " adb start-server - ensure that there is a server running\n"
+ " adb kill-server - kill the server if it is running\n"
+ " adb get-state - prints: offline | bootloader | device\n"
+ " adb get-serialno - prints: <serial-number>\n"
+ " adb get-devpath - prints: <device-path>\n"
+ " adb remount - remounts the /system, /vendor (if present) and /oem (if present) partitions on the device read-write\n"
+ " adb reboot [bootloader|recovery]\n"
+ " - reboots the device, optionally into the bootloader or recovery program.\n"
+ " adb reboot sideload - reboots the device into the sideload mode in recovery program (adb root required).\n"
+ " adb reboot sideload-auto-reboot\n"
+ " - reboots into the sideload mode, then reboots automatically after the sideload regardless of the result.\n"
+ " adb sideload <file> - sideloads the given package\n"
+ " adb root - restarts the adbd daemon with root permissions\n"
+ " adb unroot - restarts the adbd daemon without root permissions\n"
+ " adb usb - restarts the adbd daemon listening on USB\n"
+ " adb tcpip <port> - restarts the adbd daemon listening on TCP on the specified port\n"
+ "\n"
+ "networking:\n"
+ " adb ppp <tty> [parameters] - Run PPP over USB.\n"
+ " Note: you should not automatically start a PPP connection.\n"
+ " <tty> refers to the tty for PPP stream. Eg. dev:/dev/omap_csmi_tty1\n"
+ " [parameters] - Eg. defaultroute debug dump local notty usepeerdns\n"
+ "\n"
+ "adb sync notes: adb sync [ <directory> ]\n"
+ " <localdir> can be interpreted in several ways:\n"
+ "\n"
+ " - If <directory> is not specified, /system, /vendor (if present), /oem (if present) and /data partitions will be updated.\n"
+ "\n"
+ " - If it is \"system\", \"vendor\", \"oem\" or \"data\", only the corresponding partition\n"
+ " is updated.\n"
+ "\n"
+ "environment variables:\n"
+ " ADB_TRACE - Print debug information. A comma separated list of the following values\n"
+ " 1 or all, adb, sockets, packets, rwx, usb, sync, sysdeps, transport, jdwp\n"
+ " ANDROID_SERIAL - The serial number to connect to. -s takes priority over this if given.\n"
+ " ANDROID_LOG_TAGS - When used with the logcat option, only these debug tags are printed.\n"
+ );
+}
+
+static int usage() {
+ help();
+ return 1;
+}
+
+#if defined(_WIN32)
+
+// Implemented in sysdeps_win32.cpp.
+void stdin_raw_init(int fd);
+void stdin_raw_restore(int fd);
+
+#else
+static termios g_saved_terminal_state;
+
+static void stdin_raw_init(int fd) {
+ if (tcgetattr(fd, &g_saved_terminal_state)) return;
+
+ termios tio;
+ if (tcgetattr(fd, &tio)) return;
+
+ cfmakeraw(&tio);
+
+ // No timeout but request at least one character per read.
+ tio.c_cc[VTIME] = 0;
+ tio.c_cc[VMIN] = 1;
+
+ tcsetattr(fd, TCSAFLUSH, &tio);
+}
+
+static void stdin_raw_restore(int fd) {
+ tcsetattr(fd, TCSAFLUSH, &g_saved_terminal_state);
+}
+#endif
+
+static void read_and_dump(int fd) {
+ while (fd >= 0) {
+ D("read_and_dump(): pre adb_read(fd=%d)\n", fd);
+ char buf[BUFSIZ];
+ int len = adb_read(fd, buf, sizeof(buf));
+ D("read_and_dump(): post adb_read(fd=%d): len=%d\n", fd, len);
+ if (len <= 0) {
+ break;
+ }
+
+ fwrite(buf, 1, len, stdout);
+ fflush(stdout);
+ }
+}
+
+static void read_status_line(int fd, char* buf, size_t count)
+{
+ count--;
+ while (count > 0) {
+ int len = adb_read(fd, buf, count);
+ if (len == 0) {
+ break;
+ } else if (len < 0) {
+ if (errno == EINTR) continue;
+ break;
+ }
+
+ buf += len;
+ count -= len;
+ }
+ *buf = '\0';
+}
+
+static void copy_to_file(int inFd, int outFd) {
+ const size_t BUFSIZE = 32 * 1024;
+ char* buf = (char*) malloc(BUFSIZE);
+ if (buf == nullptr) fatal("couldn't allocate buffer for copy_to_file");
+ int len;
+ long total = 0;
+#ifdef _WIN32
+ int old_stdin_mode = -1;
+ int old_stdout_mode = -1;
+#endif
+
+ D("copy_to_file(%d -> %d)\n", inFd, outFd);
+
+ if (inFd == STDIN_FILENO) {
+ stdin_raw_init(STDIN_FILENO);
+#ifdef _WIN32
+ old_stdin_mode = _setmode(STDIN_FILENO, _O_BINARY);
+ if (old_stdin_mode == -1) {
+ fatal_errno("could not set stdin to binary");
+ }
+#endif
+ }
+
+#ifdef _WIN32
+ if (outFd == STDOUT_FILENO) {
+ old_stdout_mode = _setmode(STDOUT_FILENO, _O_BINARY);
+ if (old_stdout_mode == -1) {
+ fatal_errno("could not set stdout to binary");
+ }
+ }
+#endif
+
+ while (true) {
+ if (inFd == STDIN_FILENO) {
+ len = unix_read(inFd, buf, BUFSIZE);
+ } else {
+ len = adb_read(inFd, buf, BUFSIZE);
+ }
+ if (len == 0) {
+ D("copy_to_file() : read 0 bytes; exiting\n");
+ break;
+ }
+ if (len < 0) {
+ if (errno == EINTR) {
+ D("copy_to_file() : EINTR, retrying\n");
+ continue;
+ }
+ D("copy_to_file() : error %d\n", errno);
+ break;
+ }
+ if (outFd == STDOUT_FILENO) {
+ fwrite(buf, 1, len, stdout);
+ fflush(stdout);
+ } else {
+ adb_write(outFd, buf, len);
+ }
+ total += len;
+ }
+
+ if (inFd == STDIN_FILENO) {
+ stdin_raw_restore(STDIN_FILENO);
+#ifdef _WIN32
+ if (_setmode(STDIN_FILENO, old_stdin_mode) == -1) {
+ fatal_errno("could not restore stdin mode");
+ }
+#endif
+ }
+
+#ifdef _WIN32
+ if (outFd == STDOUT_FILENO) {
+ if (_setmode(STDOUT_FILENO, old_stdout_mode) == -1) {
+ fatal_errno("could not restore stdout mode");
+ }
+ }
+#endif
+
+ D("copy_to_file() finished after %lu bytes\n", total);
+ free(buf);
+}
+
+static void *stdin_read_thread(void *x)
+{
+ int fd, fdi;
+ unsigned char buf[1024];
+ int r, n;
+ int state = 0;
+
+ int *fds = (int*) x;
+ fd = fds[0];
+ fdi = fds[1];
+ free(fds);
+
+ for(;;) {
+ /* fdi is really the client's stdin, so use read, not adb_read here */
+ D("stdin_read_thread(): pre unix_read(fdi=%d,...)\n", fdi);
+ r = unix_read(fdi, buf, 1024);
+ D("stdin_read_thread(): post unix_read(fdi=%d,...)\n", fdi);
+ if(r == 0) break;
+ if(r < 0) {
+ if(errno == EINTR) continue;
+ break;
+ }
+ for(n = 0; n < r; n++){
+ switch(buf[n]) {
+ case '\n':
+ state = 1;
+ break;
+ case '\r':
+ state = 1;
+ break;
+ case '~':
+ if(state == 1) state++;
+ break;
+ case '.':
+ if(state == 2) {
+ fprintf(stderr,"\n* disconnect *\n");
+ stdin_raw_restore(fdi);
+ exit(0);
+ }
+ default:
+ state = 0;
+ }
+ }
+ r = adb_write(fd, buf, r);
+ if(r <= 0) {
+ break;
+ }
+ }
+ return 0;
+}
+
+static int interactive_shell() {
+ int fdi;
+
+ std::string error;
+ int fd = adb_connect("shell:", &error);
+ if (fd < 0) {
+ fprintf(stderr,"error: %s\n", error.c_str());
+ return 1;
+ }
+ fdi = 0; //dup(0);
+
+ int* fds = reinterpret_cast<int*>(malloc(sizeof(int) * 2));
+ if (fds == nullptr) {
+ fprintf(stderr, "couldn't allocate fds array: %s\n", strerror(errno));
+ return 1;
+ }
+
+ fds[0] = fd;
+ fds[1] = fdi;
+
+ stdin_raw_init(fdi);
+
+ adb_thread_create(stdin_read_thread, fds);
+ read_and_dump(fd);
+ stdin_raw_restore(fdi);
+ return 0;
+}
+
+
+static std::string format_host_command(const char* command, TransportType type, const char* serial) {
+ if (serial) {
+ return android::base::StringPrintf("host-serial:%s:%s", serial, command);
+ }
+
+ const char* prefix = "host";
+ if (type == kTransportUsb) {
+ prefix = "host-usb";
+ } else if (type == kTransportLocal) {
+ prefix = "host-local";
+ }
+ return android::base::StringPrintf("%s:%s", prefix, command);
+}
+
+static int adb_download_buffer(const char *service, const char *fn, const void* data, unsigned sz,
+ bool show_progress)
+{
+ std::string error;
+ int fd = adb_connect(android::base::StringPrintf("%s:%d", service, sz), &error);
+ if (fd < 0) {
+ fprintf(stderr,"error: %s\n", error.c_str());
+ return -1;
+ }
+
+ int opt = CHUNK_SIZE;
+ opt = adb_setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (const void *) &opt, sizeof(opt));
+
+ unsigned total = sz;
+ const uint8_t* ptr = reinterpret_cast<const uint8_t*>(data);
+
+ if (show_progress) {
+ char *x = strrchr(service, ':');
+ if(x) service = x + 1;
+ }
+
+ while (sz > 0) {
+ unsigned xfer = (sz > CHUNK_SIZE) ? CHUNK_SIZE : sz;
+ if (!WriteFdExactly(fd, ptr, xfer)) {
+ std::string error;
+ adb_status(fd, &error);
+ fprintf(stderr,"* failed to write data '%s' *\n", error.c_str());
+ return -1;
+ }
+ sz -= xfer;
+ ptr += xfer;
+ if (show_progress) {
+ printf("sending: '%s' %4d%% \r", fn, (int)(100LL - ((100LL * sz) / (total))));
+ fflush(stdout);
+ }
+ }
+ if (show_progress) {
+ printf("\n");
+ }
+
+ if (!adb_status(fd, &error)) {
+ fprintf(stderr,"* error response '%s' *\n", error.c_str());
+ return -1;
+ }
+
+ adb_close(fd);
+ return 0;
+}
+
+#define SIDELOAD_HOST_BLOCK_SIZE (CHUNK_SIZE)
+
+/*
+ * The sideload-host protocol serves the data in a file (given on the
+ * command line) to the client, using a simple protocol:
+ *
+ * - The connect message includes the total number of bytes in the
+ * file and a block size chosen by us.
+ *
+ * - The other side sends the desired block number as eight decimal
+ * digits (eg "00000023" for block 23). Blocks are numbered from
+ * zero.
+ *
+ * - We send back the data of the requested block. The last block is
+ * likely to be partial; when the last block is requested we only
+ * send the part of the block that exists, it's not padded up to the
+ * block size.
+ *
+ * - When the other side sends "DONEDONE" instead of a block number,
+ * we hang up.
+ */
+static int adb_sideload_host(const char* fn) {
+ unsigned sz;
+ size_t xfer = 0;
+ int status;
+ int last_percent = -1;
+ int opt = SIDELOAD_HOST_BLOCK_SIZE;
+
+ printf("loading: '%s'", fn);
+ fflush(stdout);
+ uint8_t* data = reinterpret_cast<uint8_t*>(load_file(fn, &sz));
+ if (data == 0) {
+ printf("\n");
+ fprintf(stderr, "* cannot read '%s' *\n", fn);
+ return -1;
+ }
+
+ std::string service =
+ android::base::StringPrintf("sideload-host:%d:%d", sz, SIDELOAD_HOST_BLOCK_SIZE);
+ std::string error;
+ int fd = adb_connect(service, &error);
+ if (fd < 0) {
+ // Try falling back to the older sideload method. Maybe this
+ // is an older device that doesn't support sideload-host.
+ printf("\n");
+ status = adb_download_buffer("sideload", fn, data, sz, true);
+ goto done;
+ }
+
+ opt = adb_setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (const void *) &opt, sizeof(opt));
+
+ while (true) {
+ char buf[9];
+ if (!ReadFdExactly(fd, buf, 8)) {
+ fprintf(stderr, "* failed to read command: %s\n", strerror(errno));
+ status = -1;
+ goto done;
+ }
+ buf[8] = '\0';
+
+ if (strcmp("DONEDONE", buf) == 0) {
+ status = 0;
+ break;
+ }
+
+ int block = strtol(buf, NULL, 10);
+
+ size_t offset = block * SIDELOAD_HOST_BLOCK_SIZE;
+ if (offset >= sz) {
+ fprintf(stderr, "* attempt to read block %d past end\n", block);
+ status = -1;
+ goto done;
+ }
+ uint8_t* start = data + offset;
+ size_t offset_end = offset + SIDELOAD_HOST_BLOCK_SIZE;
+ size_t to_write = SIDELOAD_HOST_BLOCK_SIZE;
+ if (offset_end > sz) {
+ to_write = sz - offset;
+ }
+
+ if(!WriteFdExactly(fd, start, to_write)) {
+ adb_status(fd, &error);
+ fprintf(stderr,"* failed to write data '%s' *\n", error.c_str());
+ status = -1;
+ goto done;
+ }
+ xfer += to_write;
+
+ // For normal OTA packages, we expect to transfer every byte
+ // twice, plus a bit of overhead (one read during
+ // verification, one read of each byte for installation, plus
+ // extra access to things like the zip central directory).
+ // This estimate of the completion becomes 100% when we've
+ // transferred ~2.13 (=100/47) times the package size.
+ int percent = (int)(xfer * 47LL / (sz ? sz : 1));
+ if (percent != last_percent) {
+ printf("\rserving: '%s' (~%d%%) ", fn, percent);
+ fflush(stdout);
+ last_percent = percent;
+ }
+ }
+
+ printf("\rTotal xfer: %.2fx%*s\n", (double)xfer / (sz ? sz : 1), (int)strlen(fn)+10, "");
+
+ done:
+ if (fd >= 0) adb_close(fd);
+ free(data);
+ return status;
+}
+
+/**
+ * Run ppp in "notty" mode against a resource listed as the first parameter
+ * eg:
+ *
+ * ppp dev:/dev/omap_csmi_tty0 <ppp options>
+ *
+ */
+static int ppp(int argc, const char** argv) {
+#if defined(_WIN32)
+ fprintf(stderr, "error: adb %s not implemented on Win32\n", argv[0]);
+ return -1;
+#else
+ if (argc < 2) {
+ fprintf(stderr, "usage: adb %s <adb service name> [ppp opts]\n",
+ argv[0]);
+
+ return 1;
+ }
+
+ const char* adb_service_name = argv[1];
+ std::string error;
+ int fd = adb_connect(adb_service_name, &error);
+ if (fd < 0) {
+ fprintf(stderr,"Error: Could not open adb service: %s. Error: %s\n",
+ adb_service_name, error.c_str());
+ return 1;
+ }
+
+ pid_t pid = fork();
+
+ if (pid < 0) {
+ perror("from fork()");
+ return 1;
+ } else if (pid == 0) {
+ int err;
+ int i;
+ const char **ppp_args;
+
+ // copy args
+ ppp_args = (const char **) alloca(sizeof(char *) * argc + 1);
+ ppp_args[0] = "pppd";
+ for (i = 2 ; i < argc ; i++) {
+ //argv[2] and beyond become ppp_args[1] and beyond
+ ppp_args[i - 1] = argv[i];
+ }
+ ppp_args[i-1] = NULL;
+
+ // child side
+
+ dup2(fd, STDIN_FILENO);
+ dup2(fd, STDOUT_FILENO);
+ adb_close(STDERR_FILENO);
+ adb_close(fd);
+
+ err = execvp("pppd", (char * const *)ppp_args);
+
+ if (err < 0) {
+ perror("execing pppd");
+ }
+ exit(-1);
+ } else {
+ // parent side
+
+ adb_close(fd);
+ return 0;
+ }
+#endif /* !defined(_WIN32) */
+}
+
+static bool wait_for_device(const char* service, TransportType t, const char* serial) {
+ // Was the caller vague about what they'd like us to wait for?
+ // If so, check they weren't more specific in their choice of transport type.
+ if (strcmp(service, "wait-for-device") == 0) {
+ if (t == kTransportUsb) {
+ service = "wait-for-usb";
+ } else if (t == kTransportLocal) {
+ service = "wait-for-local";
+ } else {
+ service = "wait-for-any";
+ }
+ }
+
+ std::string cmd = format_host_command(service, t, serial);
+ return adb_command(cmd);
+}
+
+static int send_shell_command(TransportType transport_type, const char* serial,
+ const std::string& command) {
+ int fd;
+ while (true) {
+ std::string error;
+ fd = adb_connect(command, &error);
+ if (fd >= 0) {
+ break;
+ }
+ fprintf(stderr,"- waiting for device -\n");
+ adb_sleep_ms(1000);
+ wait_for_device("wait-for-device", transport_type, serial);
+ }
+
+ read_and_dump(fd);
+ int rc = adb_close(fd);
+ if (rc) {
+ perror("close");
+ }
+ return rc;
+}
+
+static int logcat(TransportType transport, const char* serial, int argc, const char** argv) {
+ char* log_tags = getenv("ANDROID_LOG_TAGS");
+ std::string quoted = escape_arg(log_tags == nullptr ? "" : log_tags);
+
+ std::string cmd = "shell:export ANDROID_LOG_TAGS=\"" + quoted + "\"; exec logcat";
+
+ if (!strcmp(argv[0], "longcat")) {
+ cmd += " -v long";
+ }
+
+ --argc;
+ ++argv;
+ while (argc-- > 0) {
+ cmd += " " + escape_arg(*argv++);
+ }
+
+ return send_shell_command(transport, serial, cmd);
+}
+
+static int backup(int argc, const char** argv) {
+ const char* filename = "./backup.ab";
+
+ /* find, extract, and use any -f argument */
+ for (int i = 1; i < argc; i++) {
+ if (!strcmp("-f", argv[i])) {
+ if (i == argc-1) {
+ fprintf(stderr, "adb: -f passed with no filename\n");
+ return usage();
+ }
+ filename = argv[i+1];
+ for (int j = i+2; j <= argc; ) {
+ argv[i++] = argv[j++];
+ }
+ argc -= 2;
+ argv[argc] = NULL;
+ }
+ }
+
+ /* bare "adb backup" or "adb backup -f filename" are not valid invocations */
+ if (argc < 2) return usage();
+
+ adb_unlink(filename);
+ mkdirs(filename);
+ int outFd = adb_creat(filename, 0640);
+ if (outFd < 0) {
+ fprintf(stderr, "adb: unable to open file %s\n", filename);
+ return -1;
+ }
+
+ std::string cmd = "backup:";
+ --argc;
+ ++argv;
+ while (argc-- > 0) {
+ cmd += " " + escape_arg(*argv++);
+ }
+
+ D("backup. filename=%s cmd=%s\n", filename, cmd.c_str());
+ std::string error;
+ int fd = adb_connect(cmd, &error);
+ if (fd < 0) {
+ fprintf(stderr, "adb: unable to connect for backup: %s\n", error.c_str());
+ adb_close(outFd);
+ return -1;
+ }
+
+ printf("Now unlock your device and confirm the backup operation.\n");
+ copy_to_file(fd, outFd);
+
+ adb_close(fd);
+ adb_close(outFd);
+ return 0;
+}
+
+static int restore(int argc, const char** argv) {
+ if (argc != 2) return usage();
+
+ const char* filename = argv[1];
+ int tarFd = adb_open(filename, O_RDONLY);
+ if (tarFd < 0) {
+ fprintf(stderr, "adb: unable to open file %s: %s\n", filename, strerror(errno));
+ return -1;
+ }
+
+ std::string error;
+ int fd = adb_connect("restore:", &error);
+ if (fd < 0) {
+ fprintf(stderr, "adb: unable to connect for restore: %s\n", error.c_str());
+ adb_close(tarFd);
+ return -1;
+ }
+
+ printf("Now unlock your device and confirm the restore operation.\n");
+ copy_to_file(tarFd, fd);
+
+ adb_close(fd);
+ adb_close(tarFd);
+ return 0;
+}
+
+/* <hint> may be:
+ * - A simple product name
+ * e.g., "sooner"
+ * - A relative path from the CWD to the ANDROID_PRODUCT_OUT dir
+ * e.g., "out/target/product/sooner"
+ * - An absolute path to the PRODUCT_OUT dir
+ * e.g., "/src/device/out/target/product/sooner"
+ *
+ * Given <hint>, try to construct an absolute path to the
+ * ANDROID_PRODUCT_OUT dir.
+ */
+static std::string find_product_out_path(const char* hint) {
+ if (hint == NULL || hint[0] == '\0') {
+ return "";
+ }
+
+ // If it's already absolute, don't bother doing any work.
+ if (adb_is_absolute_host_path(hint)) {
+ return hint;
+ }
+
+ // If there are any slashes in it, assume it's a relative path;
+ // make it absolute.
+ if (adb_dirstart(hint) != nullptr) {
+ std::string cwd;
+ if (!getcwd(&cwd)) {
+ fprintf(stderr, "adb: getcwd failed: %s\n", strerror(errno));
+ return "";
+ }
+ return android::base::StringPrintf("%s%s%s", cwd.c_str(), OS_PATH_SEPARATOR_STR, hint);
+ }
+
+ // It's a string without any slashes. Try to do something with it.
+ //
+ // Try to find the root of the build tree, and build a PRODUCT_OUT
+ // path from there.
+ char* top = getenv("ANDROID_BUILD_TOP");
+ if (top == nullptr) {
+ fprintf(stderr, "adb: ANDROID_BUILD_TOP not set!\n");
+ return "";
+ }
+
+ std::string path = top;
+ path += OS_PATH_SEPARATOR_STR;
+ path += "out";
+ path += OS_PATH_SEPARATOR_STR;
+ path += "target";
+ path += OS_PATH_SEPARATOR_STR;
+ path += "product";
+ path += OS_PATH_SEPARATOR_STR;
+ path += hint;
+ if (!directory_exists(path)) {
+ fprintf(stderr, "adb: Couldn't find a product dir based on -p %s; "
+ "\"%s\" doesn't exist\n", hint, path.c_str());
+ return "";
+ }
+ return path;
+}
+
+static void parse_push_pull_args(const char **arg, int narg, char const **path1,
+ char const **path2, int *show_progress,
+ int *copy_attrs) {
+ *show_progress = 0;
+ *copy_attrs = 0;
+
+ while (narg > 0) {
+ if (!strcmp(*arg, "-p")) {
+ *show_progress = 1;
+ } else if (!strcmp(*arg, "-a")) {
+ *copy_attrs = 1;
+ } else {
+ break;
+ }
+ ++arg;
+ --narg;
+ }
+
+ if (narg > 0) {
+ *path1 = *arg;
+ ++arg;
+ --narg;
+ }
+
+ if (narg > 0) {
+ *path2 = *arg;
+ }
+}
+
+static int adb_connect_command(const std::string& command) {
+ std::string error;
+ int fd = adb_connect(command, &error);
+ if (fd < 0) {
+ fprintf(stderr, "error: %s\n", error.c_str());
+ return 1;
+ }
+ read_and_dump(fd);
+ adb_close(fd);
+ return 0;
+}
+
+static int adb_query_command(const std::string& command) {
+ std::string result;
+ std::string error;
+ if (!adb_query(command, &result, &error)) {
+ fprintf(stderr, "error: %s\n", error.c_str());
+ return 1;
+ }
+ printf("%s\n", result.c_str());
+ return 0;
+}
+
+int adb_commandline(int argc, const char **argv) {
+ int no_daemon = 0;
+ int is_daemon = 0;
+ int is_server = 0;
+ int r;
+ TransportType transport_type = kTransportAny;
+
+ // If defined, this should be an absolute path to
+ // the directory containing all of the various system images
+ // for a particular product. If not defined, and the adb
+ // command requires this information, then the user must
+ // specify the path using "-p".
+ char* ANDROID_PRODUCT_OUT = getenv("ANDROID_PRODUCT_OUT");
+ if (ANDROID_PRODUCT_OUT != nullptr) {
+ gProductOutPath = ANDROID_PRODUCT_OUT;
+ }
+ // TODO: also try TARGET_PRODUCT/TARGET_DEVICE as a hint
+
+ const char* serial = getenv("ANDROID_SERIAL");
+
+ /* Validate and assign the server port */
+ const char* server_port_str = getenv("ANDROID_ADB_SERVER_PORT");
+ int server_port = DEFAULT_ADB_PORT;
+ if (server_port_str && strlen(server_port_str) > 0) {
+ server_port = (int) strtol(server_port_str, NULL, 0);
+ if (server_port <= 0 || server_port > 65535) {
+ fprintf(stderr,
+ "adb: Env var ANDROID_ADB_SERVER_PORT must be a positive number less than 65535. Got \"%s\"\n",
+ server_port_str);
+ return usage();
+ }
+ }
+
+ /* modifiers and flags */
+ while (argc > 0) {
+ if (!strcmp(argv[0],"server")) {
+ is_server = 1;
+ } else if (!strcmp(argv[0],"nodaemon")) {
+ no_daemon = 1;
+ } else if (!strcmp(argv[0], "fork-server")) {
+ /* this is a special flag used only when the ADB client launches the ADB Server */
+ is_daemon = 1;
+ } else if (!strncmp(argv[0], "-p", 2)) {
+ const char *product = NULL;
+ if (argv[0][2] == '\0') {
+ if (argc < 2) return usage();
+ product = argv[1];
+ argc--;
+ argv++;
+ } else {
+ product = argv[0] + 2;
+ }
+ gProductOutPath = find_product_out_path(product);
+ if (gProductOutPath.empty()) {
+ fprintf(stderr, "adb: could not resolve \"-p %s\"\n", product);
+ return usage();
+ }
+ } else if (argv[0][0]=='-' && argv[0][1]=='s') {
+ if (isdigit(argv[0][2])) {
+ serial = argv[0] + 2;
+ } else {
+ if (argc < 2 || argv[0][2] != '\0') return usage();
+ serial = argv[1];
+ argc--;
+ argv++;
+ }
+ } else if (!strcmp(argv[0],"-d")) {
+ transport_type = kTransportUsb;
+ } else if (!strcmp(argv[0],"-e")) {
+ transport_type = kTransportLocal;
+ } else if (!strcmp(argv[0],"-a")) {
+ gListenAll = 1;
+ } else if (!strncmp(argv[0], "-H", 2)) {
+ const char *hostname = NULL;
+ if (argv[0][2] == '\0') {
+ if (argc < 2) return usage();
+ hostname = argv[1];
+ argc--;
+ argv++;
+ } else {
+ hostname = argv[0] + 2;
+ }
+ adb_set_tcp_name(hostname);
+
+ } else if (!strncmp(argv[0], "-P", 2)) {
+ if (argv[0][2] == '\0') {
+ if (argc < 2) return usage();
+ server_port_str = argv[1];
+ argc--;
+ argv++;
+ } else {
+ server_port_str = argv[0] + 2;
+ }
+ if (strlen(server_port_str) > 0) {
+ server_port = (int) strtol(server_port_str, NULL, 0);
+ if (server_port <= 0 || server_port > 65535) {
+ fprintf(stderr,
+ "adb: port number must be a positive number less than 65536. Got \"%s\"\n",
+ server_port_str);
+ return usage();
+ }
+ } else {
+ fprintf(stderr,
+ "adb: port number must be a positive number less than 65536. Got empty string.\n");
+ return usage();
+ }
+ } else {
+ /* out of recognized modifiers and flags */
+ break;
+ }
+ argc--;
+ argv++;
+ }
+
+ adb_set_transport(transport_type, serial);
+ adb_set_tcp_specifics(server_port);
+
+ if (is_server) {
+ if (no_daemon || is_daemon) {
+ r = adb_main(is_daemon, server_port);
+ } else {
+ r = launch_server(server_port);
+ }
+ if (r) {
+ fprintf(stderr,"* could not start server *\n");
+ }
+ return r;
+ }
+
+ if (argc == 0) {
+ return usage();
+ }
+
+ /* handle wait-for-* prefix */
+ if (!strncmp(argv[0], "wait-for-", strlen("wait-for-"))) {
+ const char* service = argv[0];
+
+ if (!wait_for_device(service, transport_type, serial)) {
+ return 1;
+ }
+
+ // Allow a command to be run after wait-for-device,
+ // e.g. 'adb wait-for-device shell'.
+ if (argc == 1) {
+ return 0;
+ }
+
+ /* Fall through */
+ argc--;
+ argv++;
+ }
+
+ /* adb_connect() commands */
+ if (!strcmp(argv[0], "devices")) {
+ const char *listopt;
+ if (argc < 2) {
+ listopt = "";
+ } else if (argc == 2 && !strcmp(argv[1], "-l")) {
+ listopt = argv[1];
+ } else {
+ fprintf(stderr, "Usage: adb devices [-l]\n");
+ return 1;
+ }
+
+ std::string query = android::base::StringPrintf("host:%s%s", argv[0], listopt);
+ printf("List of devices attached\n");
+ return adb_query_command(query);
+ }
+ else if (!strcmp(argv[0], "connect")) {
+ if (argc != 2) {
+ fprintf(stderr, "Usage: adb connect <host>[:<port>]\n");
+ return 1;
+ }
+
+ std::string query = android::base::StringPrintf("host:connect:%s", argv[1]);
+ return adb_query_command(query);
+ }
+ else if (!strcmp(argv[0], "disconnect")) {
+ if (argc > 2) {
+ fprintf(stderr, "Usage: adb disconnect [<host>[:<port>]]\n");
+ return 1;
+ }
+
+ std::string query = android::base::StringPrintf("host:disconnect:%s",
+ (argc == 2) ? argv[1] : "");
+ return adb_query_command(query);
+ }
+ else if (!strcmp(argv[0], "emu")) {
+ return adb_send_emulator_command(argc, argv, serial);
+ }
+ else if (!strcmp(argv[0], "shell") || !strcmp(argv[0], "hell")) {
+ char h = (argv[0][0] == 'h');
+
+ if (h) {
+ printf("\x1b[41;33m");
+ fflush(stdout);
+ }
+
+ if (argc < 2) {
+ D("starting interactive shell\n");
+ r = interactive_shell();
+ if (h) {
+ printf("\x1b[0m");
+ fflush(stdout);
+ }
+ return r;
+ }
+
+ std::string cmd = "shell:";
+ --argc;
+ ++argv;
+ while (argc-- > 0) {
+ // We don't escape here, just like ssh(1). http://b/20564385.
+ cmd += *argv++;
+ if (*argv) cmd += " ";
+ }
+
+ while (true) {
+ D("interactive shell loop. cmd=%s\n", cmd.c_str());
+ std::string error;
+ int fd = adb_connect(cmd, &error);
+ int r;
+ if (fd >= 0) {
+ D("about to read_and_dump(fd=%d)\n", fd);
+ read_and_dump(fd);
+ D("read_and_dump() done.\n");
+ adb_close(fd);
+ r = 0;
+ } else {
+ fprintf(stderr,"error: %s\n", error.c_str());
+ r = -1;
+ }
+
+ if (h) {
+ printf("\x1b[0m");
+ fflush(stdout);
+ }
+ D("interactive shell loop. return r=%d\n", r);
+ return r;
+ }
+ }
+ else if (!strcmp(argv[0], "exec-in") || !strcmp(argv[0], "exec-out")) {
+ int exec_in = !strcmp(argv[0], "exec-in");
+
+ std::string cmd = "exec:";
+ cmd += argv[1];
+ argc -= 2;
+ argv += 2;
+ while (argc-- > 0) {
+ cmd += " " + escape_arg(*argv++);
+ }
+
+ std::string error;
+ int fd = adb_connect(cmd, &error);
+ if (fd < 0) {
+ fprintf(stderr, "error: %s\n", error.c_str());
+ return -1;
+ }
+
+ if (exec_in) {
+ copy_to_file(STDIN_FILENO, fd);
+ } else {
+ copy_to_file(fd, STDOUT_FILENO);
+ }
+
+ adb_close(fd);
+ return 0;
+ }
+ else if (!strcmp(argv[0], "kill-server")) {
+ std::string error;
+ int fd = _adb_connect("host:kill", &error);
+ if (fd == -1) {
+ fprintf(stderr,"* server not running *\n");
+ return 1;
+ }
+ return 0;
+ }
+ else if (!strcmp(argv[0], "sideload")) {
+ if (argc != 2) return usage();
+ if (adb_sideload_host(argv[1])) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+ else if (!strcmp(argv[0], "tcpip") && argc > 1) {
+ return adb_connect_command(android::base::StringPrintf("tcpip:%s", argv[1]));
+ }
+ else if (!strcmp(argv[0], "remount") ||
+ !strcmp(argv[0], "reboot") ||
+ !strcmp(argv[0], "reboot-bootloader") ||
+ !strcmp(argv[0], "usb") ||
+ !strcmp(argv[0], "root") ||
+ !strcmp(argv[0], "unroot") ||
+ !strcmp(argv[0], "disable-verity") ||
+ !strcmp(argv[0], "enable-verity")) {
+ std::string command;
+ if (!strcmp(argv[0], "reboot-bootloader")) {
+ command = "reboot:bootloader";
+ } else if (argc > 1) {
+ command = android::base::StringPrintf("%s:%s", argv[0], argv[1]);
+ } else {
+ command = android::base::StringPrintf("%s:", argv[0]);
+ }
+ return adb_connect_command(command);
+ }
+ else if (!strcmp(argv[0], "bugreport")) {
+ if (argc != 1) return usage();
+ return send_shell_command(transport_type, serial, "shell:bugreport");
+ }
+ else if (!strcmp(argv[0], "forward") || !strcmp(argv[0], "reverse")) {
+ bool reverse = !strcmp(argv[0], "reverse");
+ ++argv;
+ --argc;
+ if (argc < 1) return usage();
+
+ // Determine the <host-prefix> for this command.
+ std::string host_prefix;
+ if (reverse) {
+ host_prefix = "reverse";
+ } else {
+ if (serial) {
+ host_prefix = android::base::StringPrintf("host-serial:%s", serial);
+ } else if (transport_type == kTransportUsb) {
+ host_prefix = "host-usb";
+ } else if (transport_type == kTransportLocal) {
+ host_prefix = "host-local";
+ } else {
+ host_prefix = "host";
+ }
+ }
+
+ std::string cmd;
+ if (strcmp(argv[0], "--list") == 0) {
+ if (argc != 1) return usage();
+ return adb_query_command(host_prefix + ":list-forward");
+ } else if (strcmp(argv[0], "--remove-all") == 0) {
+ if (argc != 1) return usage();
+ cmd = host_prefix + ":killforward-all";
+ } else if (strcmp(argv[0], "--remove") == 0) {
+ // forward --remove <local>
+ if (argc != 2) return usage();
+ cmd = host_prefix + ":killforward:" + argv[1];
+ } else if (strcmp(argv[0], "--no-rebind") == 0) {
+ // forward --no-rebind <local> <remote>
+ if (argc != 3) return usage();
+ cmd = host_prefix + ":forward:norebind:" + argv[1] + ";" + argv[2];
+ } else {
+ // forward <local> <remote>
+ if (argc != 2) return usage();
+ cmd = host_prefix + ":forward:" + argv[0] + ";" + argv[1];
+ }
+
+ return adb_command(cmd) ? 0 : 1;
+ }
+ /* do_sync_*() commands */
+ else if (!strcmp(argv[0], "ls")) {
+ if (argc != 2) return usage();
+ return do_sync_ls(argv[1]);
+ }
+ else if (!strcmp(argv[0], "push")) {
+ int show_progress = 0;
+ int copy_attrs = 0; // unused
+ const char* lpath = NULL, *rpath = NULL;
+
+ parse_push_pull_args(&argv[1], argc - 1, &lpath, &rpath, &show_progress, ©_attrs);
+
+ if ((lpath != NULL) && (rpath != NULL)) {
+ return do_sync_push(lpath, rpath, show_progress);
+ }
+
+ return usage();
+ }
+ else if (!strcmp(argv[0], "pull")) {
+ int show_progress = 0;
+ int copy_attrs = 0;
+ const char* rpath = NULL, *lpath = ".";
+
+ parse_push_pull_args(&argv[1], argc - 1, &rpath, &lpath, &show_progress, ©_attrs);
+
+ if (rpath != NULL) {
+ return do_sync_pull(rpath, lpath, show_progress, copy_attrs);
+ }
+
+ return usage();
+ }
+ else if (!strcmp(argv[0], "install")) {
+ if (argc < 2) return usage();
+ return install_app(transport_type, serial, argc, argv);
+ }
+ else if (!strcmp(argv[0], "install-multiple")) {
+ if (argc < 2) return usage();
+ return install_multiple_app(transport_type, serial, argc, argv);
+ }
+ else if (!strcmp(argv[0], "uninstall")) {
+ if (argc < 2) return usage();
+ return uninstall_app(transport_type, serial, argc, argv);
+ }
+ else if (!strcmp(argv[0], "sync")) {
+ std::string src;
+ bool list_only = false;
+ if (argc < 2) {
+ // No local path was specified.
+ src = "";
+ } else if (argc >= 2 && strcmp(argv[1], "-l") == 0) {
+ list_only = true;
+ if (argc == 3) {
+ src = argv[2];
+ } else {
+ src = "";
+ }
+ } else if (argc == 2) {
+ // A local path or "android"/"data" arg was specified.
+ src = argv[1];
+ } else {
+ return usage();
+ }
+
+ if (src != "" &&
+ src != "system" && src != "data" && src != "vendor" && src != "oem") {
+ return usage();
+ }
+
+ std::string system_src_path = product_file("system");
+ std::string data_src_path = product_file("data");
+ std::string vendor_src_path = product_file("vendor");
+ std::string oem_src_path = product_file("oem");
+
+ int rc = 0;
+ if (rc == 0 && (src.empty() || src == "system")) {
+ rc = do_sync_sync(system_src_path, "/system", list_only);
+ }
+ if (rc == 0 && (src.empty() || src == "vendor") && directory_exists(vendor_src_path)) {
+ rc = do_sync_sync(vendor_src_path, "/vendor", list_only);
+ }
+ if (rc == 0 && (src.empty() || src == "oem") && directory_exists(oem_src_path)) {
+ rc = do_sync_sync(oem_src_path, "/oem", list_only);
+ }
+ if (rc == 0 && (src.empty() || src == "data")) {
+ rc = do_sync_sync(data_src_path, "/data", list_only);
+ }
+ return rc;
+ }
+ /* passthrough commands */
+ else if (!strcmp(argv[0],"get-state") ||
+ !strcmp(argv[0],"get-serialno") ||
+ !strcmp(argv[0],"get-devpath"))
+ {
+ return adb_query_command(format_host_command(argv[0], transport_type, serial));
+ }
+ /* other commands */
+ else if (!strcmp(argv[0],"logcat") || !strcmp(argv[0],"lolcat") || !strcmp(argv[0],"longcat")) {
+ return logcat(transport_type, serial, argc, argv);
+ }
+ else if (!strcmp(argv[0],"ppp")) {
+ return ppp(argc, argv);
+ }
+ else if (!strcmp(argv[0], "start-server")) {
+ std::string error;
+ return adb_connect("host:start-server", &error);
+ }
+ else if (!strcmp(argv[0], "backup")) {
+ return backup(argc, argv);
+ }
+ else if (!strcmp(argv[0], "restore")) {
+ return restore(argc, argv);
+ }
+ else if (!strcmp(argv[0], "keygen")) {
+ if (argc < 2) return usage();
+ return adb_auth_keygen(argv[1]);
+ }
+ else if (!strcmp(argv[0], "jdwp")) {
+ return adb_connect_command("jdwp");
+ }
+ /* "adb /?" is a common idiom under Windows */
+ else if (!strcmp(argv[0], "help") || !strcmp(argv[0], "/?")) {
+ help();
+ return 0;
+ }
+ else if (!strcmp(argv[0], "version")) {
+ version(stdout);
+ return 0;
+ }
+
+ usage();
+ return 1;
+}
+
+static int pm_command(TransportType transport, const char* serial, int argc, const char** argv) {
+ std::string cmd = "shell:pm";
+
+ while (argc-- > 0) {
+ cmd += " " + escape_arg(*argv++);
+ }
+
+ return send_shell_command(transport, serial, cmd);
+}
+
+static int uninstall_app(TransportType transport, const char* serial, int argc, const char** argv) {
+ /* if the user choose the -k option, we refuse to do it until devices are
+ out with the option to uninstall the remaining data somehow (adb/ui) */
+ if (argc == 3 && strcmp(argv[1], "-k") == 0)
+ {
+ printf(
+ "The -k option uninstalls the application while retaining the data/cache.\n"
+ "At the moment, there is no way to remove the remaining data.\n"
+ "You will have to reinstall the application with the same signature, and fully uninstall it.\n"
+ "If you truly wish to continue, execute 'adb shell pm uninstall -k %s'\n", argv[2]);
+ return -1;
+ }
+
+ /* 'adb uninstall' takes the same arguments as 'pm uninstall' on device */
+ return pm_command(transport, serial, argc, argv);
+}
+
+static int delete_file(TransportType transport, const char* serial, char* filename) {
+ std::string cmd = "shell:rm -f " + escape_arg(filename);
+ return send_shell_command(transport, serial, cmd);
+}
+
+static const char* get_basename(const char* filename)
+{
+ const char* basename = adb_dirstop(filename);
+ if (basename) {
+ basename++;
+ return basename;
+ } else {
+ return filename;
+ }
+}
+
+static int install_app(TransportType transport, const char* serial, int argc, const char** argv) {
+ static const char *const DATA_DEST = "/data/local/tmp/%s";
+ static const char *const SD_DEST = "/sdcard/tmp/%s";
+ const char* where = DATA_DEST;
+ int i;
+ struct stat sb;
+
+ for (i = 1; i < argc; i++) {
+ if (!strcmp(argv[i], "-s")) {
+ where = SD_DEST;
+ }
+ }
+
+ // Find last APK argument.
+ // All other arguments passed through verbatim.
+ int last_apk = -1;
+ for (i = argc - 1; i >= 0; i--) {
+ const char* file = argv[i];
+ char* dot = strrchr(file, '.');
+ if (dot && !strcasecmp(dot, ".apk")) {
+ if (stat(file, &sb) == -1 || !S_ISREG(sb.st_mode)) {
+ fprintf(stderr, "Invalid APK file: %s\n", file);
+ return -1;
+ }
+
+ last_apk = i;
+ break;
+ }
+ }
+
+ if (last_apk == -1) {
+ fprintf(stderr, "Missing APK file\n");
+ return -1;
+ }
+
+ const char* apk_file = argv[last_apk];
+ char apk_dest[PATH_MAX];
+ snprintf(apk_dest, sizeof apk_dest, where, get_basename(apk_file));
+ int err = do_sync_push(apk_file, apk_dest, 0 /* no show progress */);
+ if (err) {
+ goto cleanup_apk;
+ } else {
+ argv[last_apk] = apk_dest; /* destination name, not source location */
+ }
+
+ err = pm_command(transport, serial, argc, argv);
+
+cleanup_apk:
+ delete_file(transport, serial, apk_dest);
+ return err;
+}
+
+static int install_multiple_app(TransportType transport, const char* serial, int argc,
+ const char** argv)
+{
+ int i;
+ struct stat sb;
+ uint64_t total_size = 0;
+
+ // Find all APK arguments starting at end.
+ // All other arguments passed through verbatim.
+ int first_apk = -1;
+ for (i = argc - 1; i >= 0; i--) {
+ const char* file = argv[i];
+ char* dot = strrchr(file, '.');
+ if (dot && !strcasecmp(dot, ".apk")) {
+ if (stat(file, &sb) == -1 || !S_ISREG(sb.st_mode)) {
+ fprintf(stderr, "Invalid APK file: %s\n", file);
+ return -1;
+ }
+
+ total_size += sb.st_size;
+ first_apk = i;
+ } else {
+ break;
+ }
+ }
+
+ if (first_apk == -1) {
+ fprintf(stderr, "Missing APK file\n");
+ return 1;
+ }
+
+ std::string cmd = android::base::StringPrintf("exec:pm install-create -S %" PRIu64, total_size);
+ for (i = 1; i < first_apk; i++) {
+ cmd += " " + escape_arg(argv[i]);
+ }
+
+ // Create install session
+ std::string error;
+ int fd = adb_connect(cmd, &error);
+ if (fd < 0) {
+ fprintf(stderr, "Connect error for create: %s\n", error.c_str());
+ return -1;
+ }
+ char buf[BUFSIZ];
+ read_status_line(fd, buf, sizeof(buf));
+ adb_close(fd);
+
+ int session_id = -1;
+ if (!strncmp("Success", buf, 7)) {
+ char* start = strrchr(buf, '[');
+ char* end = strrchr(buf, ']');
+ if (start && end) {
+ *end = '\0';
+ session_id = strtol(start + 1, NULL, 10);
+ }
+ }
+ if (session_id < 0) {
+ fprintf(stderr, "Failed to create session\n");
+ fputs(buf, stderr);
+ return -1;
+ }
+
+ // Valid session, now stream the APKs
+ int success = 1;
+ for (i = first_apk; i < argc; i++) {
+ const char* file = argv[i];
+ if (stat(file, &sb) == -1) {
+ fprintf(stderr, "Failed to stat %s\n", file);
+ success = 0;
+ goto finalize_session;
+ }
+
+ std::string cmd = android::base::StringPrintf(
+ "exec:pm install-write -S %" PRIu64 " %d %d_%s -",
+ static_cast<uint64_t>(sb.st_size), session_id, i, get_basename(file));
+
+ int localFd = adb_open(file, O_RDONLY);
+ if (localFd < 0) {
+ fprintf(stderr, "Failed to open %s: %s\n", file, strerror(errno));
+ success = 0;
+ goto finalize_session;
+ }
+
+ std::string error;
+ int remoteFd = adb_connect(cmd, &error);
+ if (remoteFd < 0) {
+ fprintf(stderr, "Connect error for write: %s\n", error.c_str());
+ adb_close(localFd);
+ success = 0;
+ goto finalize_session;
+ }
+
+ copy_to_file(localFd, remoteFd);
+ read_status_line(remoteFd, buf, sizeof(buf));
+
+ adb_close(localFd);
+ adb_close(remoteFd);
+
+ if (strncmp("Success", buf, 7)) {
+ fprintf(stderr, "Failed to write %s\n", file);
+ fputs(buf, stderr);
+ success = 0;
+ goto finalize_session;
+ }
+ }
+
+finalize_session:
+ // Commit session if we streamed everything okay; otherwise abandon
+ std::string service =
+ android::base::StringPrintf("exec:pm install-%s %d",
+ success ? "commit" : "abandon", session_id);
+ fd = adb_connect(service, &error);
+ if (fd < 0) {
+ fprintf(stderr, "Connect error for finalize: %s\n", error.c_str());
+ return -1;
+ }
+ read_status_line(fd, buf, sizeof(buf));
+ adb_close(fd);
+
+ if (!strncmp("Success", buf, 7)) {
+ fputs(buf, stderr);
+ return 0;
+ } else {
+ fprintf(stderr, "Failed to finalize session\n");
+ fputs(buf, stderr);
+ return -1;
+ }
+}
diff --git a/adb/console.cpp b/adb/console.cpp
new file mode 100644
index 0000000..b7f5345
--- /dev/null
+++ b/adb/console.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sysdeps.h"
+
+#include <stdio.h>
+
+#include <base/file.h>
+#include <base/logging.h>
+#include <base/strings.h>
+#include <cutils/sockets.h>
+
+#include "adb.h"
+#include "adb_client.h"
+
+// Return the console port of the currently connected emulator (if any) or -1 if
+// there is no emulator, and -2 if there is more than one.
+static int adb_get_emulator_console_port(const char* serial) {
+ if (serial) {
+ // The user specified a serial number; is it an emulator?
+ int port;
+ return (sscanf(serial, "emulator-%d", &port) == 1) ? port : -1;
+ }
+
+ // No specific device was given, so get the list of connected devices and
+ // search for emulators. If there's one, we'll take it. If there are more
+ // than one, that's an error.
+ std::string devices;
+ std::string error;
+ if (!adb_query("host:devices", &devices, &error)) {
+ fprintf(stderr, "error: no emulator connected: %s\n", error.c_str());
+ return -1;
+ }
+
+ int port;
+ size_t emulator_count = 0;
+ for (const auto& device : android::base::Split(devices, "\n")) {
+ if (sscanf(device.c_str(), "emulator-%d", &port) == 1) {
+ if (++emulator_count > 1) {
+ fprintf(
+ stderr, "error: more than one emulator detected; use -s\n");
+ return -1;
+ }
+ }
+ }
+
+ if (emulator_count == 0) {
+ fprintf(stderr, "error: no emulator detected\n");
+ return -1;
+ }
+
+ return port;
+}
+
+static int connect_to_console(const char* serial) {
+ int port = adb_get_emulator_console_port(serial);
+ if (port == -1) {
+ return -1;
+ }
+
+ int fd = socket_loopback_client(port, SOCK_STREAM);
+ if (fd == -1) {
+ fprintf(stderr, "error: could not connect to TCP port %d\n", port);
+ return -1;
+ }
+ return fd;
+}
+
+int adb_send_emulator_command(int argc, const char** argv, const char* serial) {
+ int fd = connect_to_console(serial);
+ if (fd == -1) {
+ return 1;
+ }
+
+ for (int i = 1; i < argc; i++) {
+ adb_write(fd, argv[i], strlen(argv[i]));
+ adb_write(fd, i == argc - 1 ? "\n" : " ", 1);
+ }
+
+ const char disconnect_command[] = "quit\n";
+ if (adb_write(fd, disconnect_command, sizeof(disconnect_command) - 1) == -1) {
+ LOG(FATAL) << "Could not finalize emulator command";
+ }
+
+ // Drain output that the emulator console has sent us to prevent a problem
+ // on Windows where if adb closes the socket without reading all the data,
+ // the emulator's next call to recv() will have an ECONNABORTED error,
+ // preventing the emulator from reading the command that adb has sent.
+ // https://code.google.com/p/android/issues/detail?id=21021
+ int result;
+ do {
+ char buf[BUFSIZ];
+ result = adb_read(fd, buf, sizeof(buf));
+ // Keep reading until zero bytes (EOF) or an error. If 'adb emu kill'
+ // is executed, the emulator calls exit() which causes adb to get
+ // ECONNRESET. Any other emu command is followed by the quit command
+ // that we sent above, and that causes the emulator to close the socket
+ // which should cause zero bytes (EOF) to be returned.
+ } while (result > 0);
+
+ adb_close(fd);
+
+ return 0;
+}
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
new file mode 100644
index 0000000..157c97b
--- /dev/null
+++ b/adb/daemon/main.cpp
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TRACE_TAG TRACE_ADB
+
+#include "sysdeps.h"
+
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <sys/prctl.h>
+
+#include "base/logging.h"
+#include "base/stringprintf.h"
+#include "cutils/properties.h"
+#include "private/android_filesystem_config.h"
+#include "selinux/selinux.h"
+
+#include "adb.h"
+#include "adb_auth.h"
+#include "adb_listeners.h"
+#include "transport.h"
+#include "qemu_tracing.h"
+
+static const char* root_seclabel = nullptr;
+
+static void drop_capabilities_bounding_set_if_needed() {
+#ifdef ALLOW_ADBD_ROOT
+ char value[PROPERTY_VALUE_MAX];
+ property_get("ro.debuggable", value, "");
+ if (strcmp(value, "1") == 0) {
+ return;
+ }
+#endif
+ for (int i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) >= 0; i++) {
+ if (i == CAP_SETUID || i == CAP_SETGID) {
+ // CAP_SETUID CAP_SETGID needed by /system/bin/run-as
+ continue;
+ }
+
+ int err = prctl(PR_CAPBSET_DROP, i, 0, 0, 0);
+
+ // Some kernels don't have file capabilities compiled in, and
+ // prctl(PR_CAPBSET_DROP) returns EINVAL. Don't automatically
+ // die when we see such misconfigured kernels.
+ if ((err < 0) && (errno != EINVAL)) {
+ PLOG(FATAL) << "Could not drop capabilities";
+ }
+ }
+}
+
+static bool should_drop_privileges() {
+#if defined(ALLOW_ADBD_ROOT)
+ char value[PROPERTY_VALUE_MAX];
+
+ // The emulator is never secure, so don't drop privileges there.
+ // TODO: this seems like a bug --- shouldn't the emulator behave like a device?
+ property_get("ro.kernel.qemu", value, "");
+ if (strcmp(value, "1") == 0) {
+ return false;
+ }
+
+ // The properties that affect `adb root` and `adb unroot` are ro.secure and
+ // ro.debuggable. In this context the names don't make the expected behavior
+ // particularly obvious.
+ //
+ // ro.debuggable:
+ // Allowed to become root, but not necessarily the default. Set to 1 on
+ // eng and userdebug builds.
+ //
+ // ro.secure:
+ // Drop privileges by default. Set to 1 on userdebug and user builds.
+ property_get("ro.secure", value, "1");
+ bool ro_secure = (strcmp(value, "1") == 0);
+
+ property_get("ro.debuggable", value, "");
+ bool ro_debuggable = (strcmp(value, "1") == 0);
+
+ // Drop privileges if ro.secure is set...
+ bool drop = ro_secure;
+
+ property_get("service.adb.root", value, "");
+ bool adb_root = (strcmp(value, "1") == 0);
+ bool adb_unroot = (strcmp(value, "0") == 0);
+
+ // ...except "adb root" lets you keep privileges in a debuggable build.
+ if (ro_debuggable && adb_root) {
+ drop = false;
+ }
+
+ // ...and "adb unroot" lets you explicitly drop privileges.
+ if (adb_unroot) {
+ drop = true;
+ }
+
+ return drop;
+#else
+ return true; // "adb root" not allowed, always drop privileges.
+#endif // ALLOW_ADBD_ROOT
+}
+
+int adbd_main(int server_port) {
+ umask(0);
+
+ signal(SIGPIPE, SIG_IGN);
+
+ init_transport_registration();
+
+ // We need to call this even if auth isn't enabled because the file
+ // descriptor will always be open.
+ adbd_cloexec_auth_socket();
+
+ if (ALLOW_ADBD_NO_AUTH && property_get_bool("ro.adb.secure", 0) == 0) {
+ auth_required = false;
+ }
+
+ adbd_auth_init();
+
+ // Our external storage path may be different than apps, since
+ // we aren't able to bind mount after dropping root.
+ const char* adb_external_storage = getenv("ADB_EXTERNAL_STORAGE");
+ if (adb_external_storage != nullptr) {
+ setenv("EXTERNAL_STORAGE", adb_external_storage, 1);
+ } else {
+ D("Warning: ADB_EXTERNAL_STORAGE is not set. Leaving EXTERNAL_STORAGE"
+ " unchanged.\n");
+ }
+
+ // Add extra groups:
+ // AID_ADB to access the USB driver
+ // AID_LOG to read system logs (adb logcat)
+ // AID_INPUT to diagnose input issues (getevent)
+ // AID_INET to diagnose network issues (ping)
+ // AID_NET_BT and AID_NET_BT_ADMIN to diagnose bluetooth (hcidump)
+ // AID_SDCARD_R to allow reading from the SD card
+ // AID_SDCARD_RW to allow writing to the SD card
+ // AID_NET_BW_STATS to read out qtaguid statistics
+ gid_t groups[] = {AID_ADB, AID_LOG, AID_INPUT,
+ AID_INET, AID_NET_BT, AID_NET_BT_ADMIN,
+ AID_SDCARD_R, AID_SDCARD_RW, AID_NET_BW_STATS};
+ if (setgroups(sizeof(groups) / sizeof(groups[0]), groups) != 0) {
+ PLOG(FATAL) << "Could not set supplental groups";
+ }
+
+ /* don't listen on a port (default 5037) if running in secure mode */
+ /* don't run as root if we are running in secure mode */
+ if (should_drop_privileges()) {
+ drop_capabilities_bounding_set_if_needed();
+
+ /* then switch user and group to "shell" */
+ if (setgid(AID_SHELL) != 0) {
+ PLOG(FATAL) << "Could not setgid";
+ }
+ if (setuid(AID_SHELL) != 0) {
+ PLOG(FATAL) << "Could not setuid";
+ }
+
+ D("Local port disabled\n");
+ } else {
+ if (root_seclabel != nullptr) {
+ if (setcon(root_seclabel) < 0) {
+ LOG(FATAL) << "Could not set selinux context";
+ }
+ }
+ std::string local_name =
+ android::base::StringPrintf("tcp:%d", server_port);
+ if (install_listener(local_name, "*smartsocket*", nullptr, 0)) {
+ LOG(FATAL) << "Could not install *smartsocket* listener";
+ }
+ }
+
+ bool is_usb = false;
+ if (access(USB_ADB_PATH, F_OK) == 0 || access(USB_FFS_ADB_EP0, F_OK) == 0) {
+ // Listen on USB.
+ usb_init();
+ is_usb = true;
+ }
+
+ // If one of these properties is set, also listen on that port.
+ // If one of the properties isn't set and we couldn't listen on usb, listen
+ // on the default port.
+ char prop_port[PROPERTY_VALUE_MAX];
+ property_get("service.adb.tcp.port", prop_port, "");
+ if (prop_port[0] == '\0') {
+ property_get("persist.adb.tcp.port", prop_port, "");
+ }
+
+ int port;
+ if (sscanf(prop_port, "%d", &port) == 1 && port > 0) {
+ printf("using port=%d\n", port);
+ // Listen on TCP port specified by service.adb.tcp.port property.
+ local_init(port);
+ } else if (!is_usb) {
+ // Listen on default port.
+ local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT);
+ }
+
+ D("adbd_main(): pre init_jdwp()\n");
+ init_jdwp();
+ D("adbd_main(): post init_jdwp()\n");
+
+ D("Event loop starting\n");
+ fdevent_loop();
+
+ return 0;
+}
+
+static void close_stdin() {
+ int fd = unix_open("/dev/null", O_RDONLY);
+ if (fd == -1) {
+ perror("failed to open /dev/null, stdin will remain open");
+ return;
+ }
+ dup2(fd, STDIN_FILENO);
+ unix_close(fd);
+}
+
+int main(int argc, char** argv) {
+ while (true) {
+ static struct option opts[] = {
+ {"root_seclabel", required_argument, nullptr, 's'},
+ {"device_banner", required_argument, nullptr, 'b'},
+ {"version", no_argument, nullptr, 'v'},
+ };
+
+ int option_index = 0;
+ int c = getopt_long(argc, argv, "", opts, &option_index);
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case 's':
+ root_seclabel = optarg;
+ break;
+ case 'b':
+ adb_device_banner = optarg;
+ break;
+ case 'v':
+ printf("Android Debug Bridge Daemon version %d.%d.%d %s\n",
+ ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION,
+ ADB_REVISION);
+ return 0;
+ default:
+ // getopt already prints "adbd: invalid option -- %c" for us.
+ return 1;
+ }
+ }
+
+ close_stdin();
+
+ adb_trace_init(argv);
+
+ /* If adbd runs inside the emulator this will enable adb tracing via
+ * adb-debug qemud service in the emulator. */
+ adb_qemu_trace_init();
+
+ D("Handling main()\n");
+ return adbd_main(DEFAULT_ADB_PORT);
+}
diff --git a/adb/device.py b/adb/device.py
new file mode 100644
index 0000000..601989b
--- /dev/null
+++ b/adb/device.py
@@ -0,0 +1,233 @@
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+import os
+import re
+import subprocess
+
+
+class FindDeviceError(RuntimeError):
+ pass
+
+
+class DeviceNotFoundError(FindDeviceError):
+ def __init__(self, serial):
+ self.serial = serial
+ super(DeviceNotFoundError, self).__init__(
+ 'No device with serial {}'.format(serial))
+
+
+class NoUniqueDeviceError(FindDeviceError):
+ def __init__(self):
+ super(NoUniqueDeviceError, self).__init__('No unique device')
+
+
+def get_devices():
+ with open(os.devnull, 'wb') as devnull:
+ subprocess.check_call(['adb', 'start-server'], stdout=devnull,
+ stderr=devnull)
+ out = subprocess.check_output(['adb', 'devices']).splitlines()
+
+ # The first line of `adb devices` just says "List of attached devices", so
+ # skip that.
+ devices = []
+ for line in out[1:]:
+ if not line.strip():
+ continue
+ if 'offline' in line:
+ continue
+
+ serial, _ = re.split(r'\s+', line, maxsplit=1)
+ devices.append(serial)
+ return devices
+
+
+def _get_unique_device(product=None):
+ devices = get_devices()
+ if len(devices) != 1:
+ raise NoUniqueDeviceError()
+ return AndroidDevice(devices[0], product)
+
+def _get_device_by_serial(serial, product=None):
+ for device in get_devices():
+ if device == serial:
+ return AndroidDevice(serial, product)
+ raise DeviceNotFoundError(serial)
+
+
+def get_device(serial=None, product=None):
+ """Get a uniquely identified AndroidDevice if one is available.
+
+ Raises:
+ DeviceNotFoundError:
+ The serial specified by `serial` or $ANDROID_SERIAL is not
+ connected.
+
+ NoUniqueDeviceError:
+ Neither `serial` nor $ANDROID_SERIAL was set, and the number of
+ devices connected to the system is not 1. Having 0 connected
+ devices will also result in this error.
+
+ Returns:
+ An AndroidDevice associated with the first non-None identifier in the
+ following order of preference:
+
+ 1) The `serial` argument.
+ 2) The environment variable $ANDROID_SERIAL.
+ 3) The single device connnected to the system.
+ """
+ if serial is not None:
+ return _get_device_by_serial(serial, product)
+
+ android_serial = os.getenv('ANDROID_SERIAL')
+ if android_serial is not None:
+ return _get_device_by_serial(android_serial, product)
+
+ return _get_unique_device(product)
+
+
+class AndroidDevice(object):
+ def __init__(self, serial, product=None):
+ self.serial = serial
+ self.product = product
+ self.adb_cmd = ['adb']
+ if self.serial is not None:
+ self.adb_cmd.extend(['-s', serial])
+ if self.product is not None:
+ self.adb_cmd.extend(['-p', product])
+ self._linesep = None
+ self._shell_result_pattern = None
+
+ @property
+ def linesep(self):
+ if self._linesep is None:
+ self._linesep = subprocess.check_output(['adb', 'shell', 'echo'])
+ return self._linesep
+
+ def _make_shell_cmd(self, user_cmd):
+ # Follow any shell command with `; echo; echo $?` to get the exit
+ # status of a program since this isn't propagated by adb.
+ #
+ # The leading newline is needed because `printf 1; echo $?` would print
+ # "10", and we wouldn't be able to distinguish the exit code.
+ rc_probe = '; echo "\n$?"'
+ return self.adb_cmd + ['shell'] + user_cmd + [rc_probe]
+
+ def _parse_shell_output(self, out): # pylint: disable=no-self-use
+ search_text = out
+ max_result_len = len('{0}255{0}'.format(self.linesep))
+ if len(search_text) > max_result_len:
+ # We don't want to regex match over massive amounts of data when we
+ # know the part we want is right at the end.
+ search_text = search_text[-max_result_len:]
+ if self._shell_result_pattern is None:
+ self._shell_result_pattern = re.compile(
+ r'({0}\d+{0})$'.format(self.linesep), re.MULTILINE)
+ m = self._shell_result_pattern.search(search_text)
+ if m is None:
+ raise RuntimeError('Could not find exit status in shell output.')
+
+ result_text = m.group(1)
+ result = int(result_text.strip())
+ out = out[:-len(result_text)] # Trim the result text from the output.
+ return result, out
+
+ def _simple_call(self, cmd):
+ return subprocess.check_output(
+ self.adb_cmd + cmd, stderr=subprocess.STDOUT)
+
+ def shell(self, cmd):
+ cmd = self._make_shell_cmd(cmd)
+ out = subprocess.check_output(cmd)
+ rc, out = self._parse_shell_output(out)
+ if rc != 0:
+ error = subprocess.CalledProcessError(rc, cmd)
+ error.out = out
+ raise error
+ return out
+
+ def shell_nocheck(self, cmd):
+ cmd = self._make_shell_cmd(cmd)
+ p = subprocess.Popen(
+ cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ out, _ = p.communicate()
+ return self._parse_shell_output(out)
+
+ def install(self, filename):
+ return self._simple_call(['install', filename])
+
+ def push(self, local, remote):
+ return self._simple_call(['push', local, remote])
+
+ def pull(self, remote, local):
+ return self._simple_call(['pull', remote, local])
+
+ def sync(self, directory=None):
+ cmd = ['sync']
+ if directory is not None:
+ cmd.append(directory)
+ return self._simple_call(cmd)
+
+ def forward(self, local, remote):
+ return self._simple_call(['forward', local, remote])
+
+ def tcpip(self, port):
+ return self._simple_call(['tcpip', port])
+
+ def usb(self):
+ return self._simple_call(['usb'])
+
+ def root(self):
+ return self._simple_call(['root'])
+
+ def unroot(self):
+ return self._simple_call(['unroot'])
+
+ def forward_remove(self, local):
+ return self._simple_call(['forward', '--remove', local])
+
+ def forward_remove_all(self):
+ return self._simple_call(['forward', '--remove-all'])
+
+ def connect(self, host):
+ return self._simple_call(['connect', host])
+
+ def disconnect(self, host):
+ return self._simple_call(['disconnect', host])
+
+ def reverse(self, remote, local):
+ return self._simple_call(['reverse', remote, local])
+
+ def reverse_remove_all(self):
+ return self._simple_call(['reverse', '--remove-all'])
+
+ def reverse_remove(self, remote):
+ return self._simple_call(['reverse', '--remove', remote])
+
+ def wait(self):
+ return self._simple_call(['wait-for-device'])
+
+ def get_prop(self, prop_name):
+ output = self.shell(['getprop', prop_name])
+ if len(output) != 1:
+ raise RuntimeError('Too many lines in getprop output:\n' +
+ '\n'.join(output))
+ value = output[0]
+ if not value.strip():
+ return None
+ return value
+
+ def set_prop(self, prop_name, value):
+ self.shell(['setprop', prop_name, value])
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
new file mode 100644
index 0000000..5cd4988
--- /dev/null
+++ b/adb/fdevent.cpp
@@ -0,0 +1,700 @@
+/* http://frotznet.googlecode.com/svn/trunk/utils/fdevent.c
+**
+** Copyright 2006, Brian Swetland <swetland@frotz.net>
+**
+** 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 TRACE_TAG TRACE_FDEVENT
+
+#include "sysdeps.h"
+#include "fdevent.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#include "adb_io.h"
+#include "adb_trace.h"
+
+/* !!! Do not enable DEBUG for the adb that will run as the server:
+** both stdout and stderr are used to communicate between the client
+** and server. Any extra output will cause failures.
+*/
+#define DEBUG 0 /* non-0 will break adb server */
+
+// This socket is used when a subproc shell service exists.
+// It wakes up the fdevent_loop() and cause the correct handling
+// of the shell's pseudo-tty master. I.e. force close it.
+#if !ADB_HOST
+int SHELL_EXIT_NOTIFY_FD = -1;
+#endif // !ADB_HOST
+
+static void fatal(const char *fn, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ fprintf(stderr, "%s:", fn);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ abort();
+}
+
+#define FATAL(x...) fatal(__FUNCTION__, x)
+
+#if DEBUG
+static void dump_fde(fdevent *fde, const char *info)
+{
+ adb_mutex_lock(&D_lock);
+ fprintf(stderr,"FDE #%03d %c%c%c %s\n", fde->fd,
+ fde->state & FDE_READ ? 'R' : ' ',
+ fde->state & FDE_WRITE ? 'W' : ' ',
+ fde->state & FDE_ERROR ? 'E' : ' ',
+ info);
+ adb_mutex_unlock(&D_lock);
+}
+#else
+#define dump_fde(fde, info) do { } while(0)
+#endif
+
+#define FDE_EVENTMASK 0x00ff
+#define FDE_STATEMASK 0xff00
+
+#define FDE_ACTIVE 0x0100
+#define FDE_PENDING 0x0200
+#define FDE_CREATED 0x0400
+
+static void fdevent_plist_enqueue(fdevent *node);
+static void fdevent_plist_remove(fdevent *node);
+static fdevent *fdevent_plist_dequeue(void);
+
+static fdevent list_pending = {
+ .next = &list_pending,
+ .prev = &list_pending,
+ .fd = -1,
+ .force_eof = 0,
+ .state = 0,
+ .events = 0,
+ .func = nullptr,
+ .arg = nullptr,
+};
+
+static fdevent **fd_table = 0;
+static int fd_table_max = 0;
+
+#ifdef CRAPTASTIC
+//HAVE_EPOLL
+
+#include <sys/epoll.h>
+
+static int epoll_fd = -1;
+
+static void fdevent_init()
+{
+ /* XXX: what's a good size for the passed in hint? */
+ epoll_fd = epoll_create(256);
+
+ if(epoll_fd < 0) {
+ perror("epoll_create() failed");
+ exit(1);
+ }
+
+ /* mark for close-on-exec */
+ fcntl(epoll_fd, F_SETFD, FD_CLOEXEC);
+}
+
+static void fdevent_connect(fdevent *fde)
+{
+ struct epoll_event ev;
+
+ memset(&ev, 0, sizeof(ev));
+ ev.events = 0;
+ ev.data.ptr = fde;
+
+#if 0
+ if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fde->fd, &ev)) {
+ perror("epoll_ctl() failed\n");
+ exit(1);
+ }
+#endif
+}
+
+static void fdevent_disconnect(fdevent *fde)
+{
+ struct epoll_event ev;
+
+ memset(&ev, 0, sizeof(ev));
+ ev.events = 0;
+ ev.data.ptr = fde;
+
+ /* technically we only need to delete if we
+ ** were actively monitoring events, but let's
+ ** be aggressive and do it anyway, just in case
+ ** something's out of sync
+ */
+ epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fde->fd, &ev);
+}
+
+static void fdevent_update(fdevent *fde, unsigned events)
+{
+ struct epoll_event ev;
+ int active;
+
+ active = (fde->state & FDE_EVENTMASK) != 0;
+
+ memset(&ev, 0, sizeof(ev));
+ ev.events = 0;
+ ev.data.ptr = fde;
+
+ if(events & FDE_READ) ev.events |= EPOLLIN;
+ if(events & FDE_WRITE) ev.events |= EPOLLOUT;
+ if(events & FDE_ERROR) ev.events |= (EPOLLERR | EPOLLHUP);
+
+ fde->state = (fde->state & FDE_STATEMASK) | events;
+
+ if(active) {
+ /* we're already active. if we're changing to *no*
+ ** events being monitored, we need to delete, otherwise
+ ** we need to just modify
+ */
+ if(ev.events) {
+ if(epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fde->fd, &ev)) {
+ perror("epoll_ctl() failed\n");
+ exit(1);
+ }
+ } else {
+ if(epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fde->fd, &ev)) {
+ perror("epoll_ctl() failed\n");
+ exit(1);
+ }
+ }
+ } else {
+ /* we're not active. if we're watching events, we need
+ ** to add, otherwise we can just do nothing
+ */
+ if(ev.events) {
+ if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fde->fd, &ev)) {
+ perror("epoll_ctl() failed\n");
+ exit(1);
+ }
+ }
+ }
+}
+
+static void fdevent_process()
+{
+ struct epoll_event events[256];
+ fdevent *fde;
+ int i, n;
+
+ n = epoll_wait(epoll_fd, events, 256, -1);
+
+ if(n < 0) {
+ if(errno == EINTR) return;
+ perror("epoll_wait");
+ exit(1);
+ }
+
+ for(i = 0; i < n; i++) {
+ struct epoll_event *ev = events + i;
+ fde = ev->data.ptr;
+
+ if(ev->events & EPOLLIN) {
+ fde->events |= FDE_READ;
+ }
+ if(ev->events & EPOLLOUT) {
+ fde->events |= FDE_WRITE;
+ }
+ if(ev->events & (EPOLLERR | EPOLLHUP)) {
+ fde->events |= FDE_ERROR;
+ }
+ if(fde->events) {
+ if(fde->state & FDE_PENDING) continue;
+ fde->state |= FDE_PENDING;
+ fdevent_plist_enqueue(fde);
+ }
+ }
+}
+
+#else /* USE_SELECT */
+
+#ifdef HAVE_WINSOCK
+#include <winsock2.h>
+#else
+#include <sys/select.h>
+#endif
+
+static fd_set read_fds;
+static fd_set write_fds;
+static fd_set error_fds;
+
+static int select_n = 0;
+
+static void fdevent_init(void)
+{
+ FD_ZERO(&read_fds);
+ FD_ZERO(&write_fds);
+ FD_ZERO(&error_fds);
+}
+
+static void fdevent_connect(fdevent *fde)
+{
+ if(fde->fd >= select_n) {
+ select_n = fde->fd + 1;
+ }
+}
+
+static void fdevent_disconnect(fdevent *fde)
+{
+ int i, n;
+
+ FD_CLR(fde->fd, &read_fds);
+ FD_CLR(fde->fd, &write_fds);
+ FD_CLR(fde->fd, &error_fds);
+
+ for(n = 0, i = 0; i < select_n; i++) {
+ if(fd_table[i] != 0) n = i;
+ }
+ select_n = n + 1;
+}
+
+static void fdevent_update(fdevent *fde, unsigned events)
+{
+ if(events & FDE_READ) {
+ FD_SET(fde->fd, &read_fds);
+ } else {
+ FD_CLR(fde->fd, &read_fds);
+ }
+ if(events & FDE_WRITE) {
+ FD_SET(fde->fd, &write_fds);
+ } else {
+ FD_CLR(fde->fd, &write_fds);
+ }
+ if(events & FDE_ERROR) {
+ FD_SET(fde->fd, &error_fds);
+ } else {
+ FD_CLR(fde->fd, &error_fds);
+ }
+
+ fde->state = (fde->state & FDE_STATEMASK) | events;
+}
+
+/* Looks at fd_table[] for bad FDs and sets bit in fds.
+** Returns the number of bad FDs.
+*/
+static int fdevent_fd_check(fd_set *fds)
+{
+ int i, n = 0;
+ fdevent *fde;
+
+ for(i = 0; i < select_n; i++) {
+ fde = fd_table[i];
+ if(fde == 0) continue;
+ if(fcntl(i, F_GETFL, NULL) < 0) {
+ FD_SET(i, fds);
+ n++;
+ // fde->state |= FDE_DONT_CLOSE;
+
+ }
+ }
+ return n;
+}
+
+#if !DEBUG
+static inline void dump_all_fds(const char* /* extra_msg */) {}
+#else
+static void dump_all_fds(const char *extra_msg)
+{
+int i;
+ fdevent *fde;
+ // per fd: 4 digits (but really: log10(FD_SETSIZE)), 1 staus, 1 blank
+ char msg_buff[FD_SETSIZE*6 + 1], *pb=msg_buff;
+ size_t max_chars = FD_SETSIZE * 6 + 1;
+ int printed_out;
+#define SAFE_SPRINTF(...) \
+ do { \
+ printed_out = snprintf(pb, max_chars, __VA_ARGS__); \
+ if (printed_out <= 0) { \
+ D("... snprintf failed.\n"); \
+ return; \
+ } \
+ if (max_chars < (unsigned int)printed_out) { \
+ D("... snprintf out of space.\n"); \
+ return; \
+ } \
+ pb += printed_out; \
+ max_chars -= printed_out; \
+ } while(0)
+
+ for(i = 0; i < select_n; i++) {
+ fde = fd_table[i];
+ SAFE_SPRINTF("%d", i);
+ if(fde == 0) {
+ SAFE_SPRINTF("? ");
+ continue;
+ }
+ if(fcntl(i, F_GETFL, NULL) < 0) {
+ SAFE_SPRINTF("b");
+ }
+ SAFE_SPRINTF(" ");
+ }
+ D("%s fd_table[]->fd = {%s}\n", extra_msg, msg_buff);
+}
+#endif
+
+static void fdevent_process()
+{
+ int i, n;
+ fdevent *fde;
+ unsigned events;
+ fd_set rfd, wfd, efd;
+
+ memcpy(&rfd, &read_fds, sizeof(fd_set));
+ memcpy(&wfd, &write_fds, sizeof(fd_set));
+ memcpy(&efd, &error_fds, sizeof(fd_set));
+
+ dump_all_fds("pre select()");
+
+ n = select(select_n, &rfd, &wfd, &efd, NULL);
+ int saved_errno = errno;
+ D("select() returned n=%d, errno=%d\n", n, n<0?saved_errno:0);
+
+ dump_all_fds("post select()");
+
+ if(n < 0) {
+ switch(saved_errno) {
+ case EINTR: return;
+ case EBADF:
+ // Can't trust the FD sets after an error.
+ FD_ZERO(&wfd);
+ FD_ZERO(&efd);
+ FD_ZERO(&rfd);
+ break;
+ default:
+ D("Unexpected select() error=%d\n", saved_errno);
+ return;
+ }
+ }
+ if(n <= 0) {
+ // We fake a read, as the rest of the code assumes
+ // that errors will be detected at that point.
+ n = fdevent_fd_check(&rfd);
+ }
+
+ for(i = 0; (i < select_n) && (n > 0); i++) {
+ events = 0;
+ if(FD_ISSET(i, &rfd)) { events |= FDE_READ; n--; }
+ if(FD_ISSET(i, &wfd)) { events |= FDE_WRITE; n--; }
+ if(FD_ISSET(i, &efd)) { events |= FDE_ERROR; n--; }
+
+ if(events) {
+ fde = fd_table[i];
+ if(fde == 0)
+ FATAL("missing fde for fd %d\n", i);
+
+ fde->events |= events;
+
+ D("got events fde->fd=%d events=%04x, state=%04x\n",
+ fde->fd, fde->events, fde->state);
+ if(fde->state & FDE_PENDING) continue;
+ fde->state |= FDE_PENDING;
+ fdevent_plist_enqueue(fde);
+ }
+ }
+}
+
+#endif
+
+static void fdevent_register(fdevent *fde)
+{
+ if(fde->fd < 0) {
+ FATAL("bogus negative fd (%d)\n", fde->fd);
+ }
+
+ if(fde->fd >= fd_table_max) {
+ int oldmax = fd_table_max;
+ if(fde->fd > 32000) {
+ FATAL("bogus huuuuge fd (%d)\n", fde->fd);
+ }
+ if(fd_table_max == 0) {
+ fdevent_init();
+ fd_table_max = 256;
+ }
+ while(fd_table_max <= fde->fd) {
+ fd_table_max *= 2;
+ }
+ fd_table = reinterpret_cast<fdevent**>(
+ realloc(fd_table, sizeof(fdevent*) * fd_table_max));
+ if(fd_table == 0) {
+ FATAL("could not expand fd_table to %d entries\n", fd_table_max);
+ }
+ memset(fd_table + oldmax, 0, sizeof(int) * (fd_table_max - oldmax));
+ }
+
+ fd_table[fde->fd] = fde;
+}
+
+static void fdevent_unregister(fdevent *fde)
+{
+ if((fde->fd < 0) || (fde->fd >= fd_table_max)) {
+ FATAL("fd out of range (%d)\n", fde->fd);
+ }
+
+ if(fd_table[fde->fd] != fde) {
+ FATAL("fd_table out of sync [%d]\n", fde->fd);
+ }
+
+ fd_table[fde->fd] = 0;
+
+ if(!(fde->state & FDE_DONT_CLOSE)) {
+ dump_fde(fde, "close");
+ adb_close(fde->fd);
+ }
+}
+
+static void fdevent_plist_enqueue(fdevent *node)
+{
+ fdevent *list = &list_pending;
+
+ node->next = list;
+ node->prev = list->prev;
+ node->prev->next = node;
+ list->prev = node;
+}
+
+static void fdevent_plist_remove(fdevent *node)
+{
+ node->prev->next = node->next;
+ node->next->prev = node->prev;
+ node->next = 0;
+ node->prev = 0;
+}
+
+static fdevent *fdevent_plist_dequeue(void)
+{
+ fdevent *list = &list_pending;
+ fdevent *node = list->next;
+
+ if(node == list) return 0;
+
+ list->next = node->next;
+ list->next->prev = list;
+ node->next = 0;
+ node->prev = 0;
+
+ return node;
+}
+
+static void fdevent_call_fdfunc(fdevent* fde)
+{
+ unsigned events = fde->events;
+ fde->events = 0;
+ if(!(fde->state & FDE_PENDING)) return;
+ fde->state &= (~FDE_PENDING);
+ dump_fde(fde, "callback");
+ fde->func(fde->fd, events, fde->arg);
+}
+
+#if !ADB_HOST
+static void fdevent_subproc_event_func(int fd, unsigned ev,
+ void* /* userdata */)
+{
+
+ D("subproc handling on fd=%d ev=%04x\n", fd, ev);
+
+ // Hook oneself back into the fde's suitable for select() on read.
+ if((fd < 0) || (fd >= fd_table_max)) {
+ FATAL("fd %d out of range for fd_table \n", fd);
+ }
+ fdevent *fde = fd_table[fd];
+ fdevent_add(fde, FDE_READ);
+
+ if(ev & FDE_READ){
+ int subproc_fd;
+
+ if(!ReadFdExactly(fd, &subproc_fd, sizeof(subproc_fd))) {
+ FATAL("Failed to read the subproc's fd from fd=%d\n", fd);
+ }
+ if((subproc_fd < 0) || (subproc_fd >= fd_table_max)) {
+ D("subproc_fd %d out of range 0, fd_table_max=%d\n",
+ subproc_fd, fd_table_max);
+ return;
+ }
+ fdevent *subproc_fde = fd_table[subproc_fd];
+ if(!subproc_fde) {
+ D("subproc_fd %d cleared from fd_table\n", subproc_fd);
+ return;
+ }
+ if(subproc_fde->fd != subproc_fd) {
+ // Already reallocated?
+ D("subproc_fd %d != fd_table[].fd %d\n", subproc_fd, subproc_fde->fd);
+ return;
+ }
+
+ subproc_fde->force_eof = 1;
+
+ int rcount = 0;
+ ioctl(subproc_fd, FIONREAD, &rcount);
+ D("subproc with fd=%d has rcount=%d err=%d\n",
+ subproc_fd, rcount, errno);
+
+ if(rcount) {
+ // If there is data left, it will show up in the select().
+ // This works because there is no other thread reading that
+ // data when in this fd_func().
+ return;
+ }
+
+ D("subproc_fde.state=%04x\n", subproc_fde->state);
+ subproc_fde->events |= FDE_READ;
+ if(subproc_fde->state & FDE_PENDING) {
+ return;
+ }
+ subproc_fde->state |= FDE_PENDING;
+ fdevent_call_fdfunc(subproc_fde);
+ }
+}
+
+void fdevent_subproc_setup()
+{
+ int s[2];
+
+ if(adb_socketpair(s)) {
+ FATAL("cannot create shell-exit socket-pair\n");
+ }
+ D("socketpair: (%d,%d)\n", s[0], s[1]);
+
+ SHELL_EXIT_NOTIFY_FD = s[0];
+ fdevent *fde;
+ fde = fdevent_create(s[1], fdevent_subproc_event_func, NULL);
+ if(!fde)
+ FATAL("cannot create fdevent for shell-exit handler\n");
+ fdevent_add(fde, FDE_READ);
+}
+#endif // !ADB_HOST
+
+fdevent *fdevent_create(int fd, fd_func func, void *arg)
+{
+ fdevent *fde = (fdevent*) malloc(sizeof(fdevent));
+ if(fde == 0) return 0;
+ fdevent_install(fde, fd, func, arg);
+ fde->state |= FDE_CREATED;
+ return fde;
+}
+
+void fdevent_destroy(fdevent *fde)
+{
+ if(fde == 0) return;
+ if(!(fde->state & FDE_CREATED)) {
+ FATAL("fde %p not created by fdevent_create()\n", fde);
+ }
+ fdevent_remove(fde);
+ free(fde);
+}
+
+void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg)
+{
+ memset(fde, 0, sizeof(fdevent));
+ fde->state = FDE_ACTIVE;
+ fde->fd = fd;
+ fde->force_eof = 0;
+ fde->func = func;
+ fde->arg = arg;
+
+#ifndef HAVE_WINSOCK
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+#endif
+ fdevent_register(fde);
+ dump_fde(fde, "connect");
+ fdevent_connect(fde);
+ fde->state |= FDE_ACTIVE;
+}
+
+void fdevent_remove(fdevent *fde)
+{
+ if(fde->state & FDE_PENDING) {
+ fdevent_plist_remove(fde);
+ }
+
+ if(fde->state & FDE_ACTIVE) {
+ fdevent_disconnect(fde);
+ dump_fde(fde, "disconnect");
+ fdevent_unregister(fde);
+ }
+
+ fde->state = 0;
+ fde->events = 0;
+}
+
+
+void fdevent_set(fdevent *fde, unsigned events)
+{
+ events &= FDE_EVENTMASK;
+
+ if((fde->state & FDE_EVENTMASK) == events) return;
+
+ if(fde->state & FDE_ACTIVE) {
+ fdevent_update(fde, events);
+ dump_fde(fde, "update");
+ }
+
+ fde->state = (fde->state & FDE_STATEMASK) | events;
+
+ if(fde->state & FDE_PENDING) {
+ /* if we're pending, make sure
+ ** we don't signal an event that
+ ** is no longer wanted.
+ */
+ fde->events &= (~events);
+ if(fde->events == 0) {
+ fdevent_plist_remove(fde);
+ fde->state &= (~FDE_PENDING);
+ }
+ }
+}
+
+void fdevent_add(fdevent *fde, unsigned events)
+{
+ fdevent_set(
+ fde, (fde->state & FDE_EVENTMASK) | (events & FDE_EVENTMASK));
+}
+
+void fdevent_del(fdevent *fde, unsigned events)
+{
+ fdevent_set(
+ fde, (fde->state & FDE_EVENTMASK) & (~(events & FDE_EVENTMASK)));
+}
+
+void fdevent_loop()
+{
+ fdevent *fde;
+#if !ADB_HOST
+ fdevent_subproc_setup();
+#endif // !ADB_HOST
+
+ for(;;) {
+ D("--- ---- waiting for events\n");
+
+ fdevent_process();
+
+ while((fde = fdevent_plist_dequeue())) {
+ fdevent_call_fdfunc(fde);
+ }
+ }
+}
diff --git a/adb/fdevent.h b/adb/fdevent.h
new file mode 100644
index 0000000..8d84b29
--- /dev/null
+++ b/adb/fdevent.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __FDEVENT_H
+#define __FDEVENT_H
+
+#include <stdint.h> /* for int64_t */
+
+/* events that may be observed */
+#define FDE_READ 0x0001
+#define FDE_WRITE 0x0002
+#define FDE_ERROR 0x0004
+#define FDE_TIMEOUT 0x0008
+
+/* features that may be set (via the events set/add/del interface) */
+#define FDE_DONT_CLOSE 0x0080
+
+struct fdevent;
+
+typedef void (*fd_func)(int fd, unsigned events, void *userdata);
+
+/* Allocate and initialize a new fdevent object
+ * Note: use FD_TIMER as 'fd' to create a fd-less object
+ * (used to implement timers).
+*/
+fdevent *fdevent_create(int fd, fd_func func, void *arg);
+
+/* Uninitialize and deallocate an fdevent object that was
+** created by fdevent_create()
+*/
+void fdevent_destroy(fdevent *fde);
+
+/* Initialize an fdevent object that was externally allocated
+*/
+void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg);
+
+/* Uninitialize an fdevent object that was initialized by
+** fdevent_install()
+*/
+void fdevent_remove(fdevent *item);
+
+/* Change which events should cause notifications
+*/
+void fdevent_set(fdevent *fde, unsigned events);
+void fdevent_add(fdevent *fde, unsigned events);
+void fdevent_del(fdevent *fde, unsigned events);
+
+void fdevent_set_timeout(fdevent *fde, int64_t timeout_ms);
+
+/* loop forever, handling events.
+*/
+void fdevent_loop();
+
+struct fdevent {
+ fdevent *next;
+ fdevent *prev;
+
+ int fd;
+ int force_eof;
+
+ uint16_t state;
+ uint16_t events;
+
+ fd_func func;
+ void *arg;
+};
+
+#endif
diff --git a/adb/file_sync_client.cpp b/adb/file_sync_client.cpp
new file mode 100644
index 0000000..49d42a3
--- /dev/null
+++ b/adb/file_sync_client.cpp
@@ -0,0 +1,1020 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <time.h>
+#include <utime.h>
+
+#include "sysdeps.h"
+
+#include "adb.h"
+#include "adb_client.h"
+#include "adb_io.h"
+#include "adb_utils.h"
+#include "file_sync_service.h"
+
+static unsigned long long total_bytes;
+static long long start_time;
+
+static long long NOW()
+{
+ struct timeval tv;
+ gettimeofday(&tv, 0);
+ return ((long long) tv.tv_usec) +
+ 1000000LL * ((long long) tv.tv_sec);
+}
+
+static void BEGIN()
+{
+ total_bytes = 0;
+ start_time = NOW();
+}
+
+static void END()
+{
+ long long t = NOW() - start_time;
+ if(total_bytes == 0) return;
+
+ if (t == 0) /* prevent division by 0 :-) */
+ t = 1000000;
+
+ fprintf(stderr,"%lld KB/s (%lld bytes in %lld.%03llds)\n",
+ ((total_bytes * 1000000LL) / t) / 1024LL,
+ total_bytes, (t / 1000000LL), (t % 1000000LL) / 1000LL);
+}
+
+static void print_transfer_progress(uint64_t bytes_current,
+ uint64_t bytes_total) {
+ if (bytes_total == 0) return;
+
+ fprintf(stderr, "\rTransferring: %" PRIu64 "/%" PRIu64 " (%d%%)",
+ bytes_current, bytes_total,
+ (int) (bytes_current * 100 / bytes_total));
+
+ if (bytes_current == bytes_total) {
+ fputc('\n', stderr);
+ }
+
+ fflush(stderr);
+}
+
+static void sync_quit(int fd) {
+ syncmsg msg;
+
+ msg.req.id = ID_QUIT;
+ msg.req.namelen = 0;
+
+ WriteFdExactly(fd, &msg.req, sizeof(msg.req));
+}
+
+typedef void (*sync_ls_cb)(unsigned mode, unsigned size, unsigned time, const char *name, void *cookie);
+
+static int sync_ls(int fd, const char* path, sync_ls_cb func, void* cookie) {
+ syncmsg msg;
+ char buf[257];
+ int len;
+
+ len = strlen(path);
+ if(len > 1024) goto fail;
+
+ msg.req.id = ID_LIST;
+ msg.req.namelen = htoll(len);
+
+ if(!WriteFdExactly(fd, &msg.req, sizeof(msg.req)) ||
+ !WriteFdExactly(fd, path, len)) {
+ goto fail;
+ }
+
+ for(;;) {
+ if(!ReadFdExactly(fd, &msg.dent, sizeof(msg.dent))) break;
+ if(msg.dent.id == ID_DONE) return 0;
+ if(msg.dent.id != ID_DENT) break;
+
+ len = ltohl(msg.dent.namelen);
+ if(len > 256) break;
+
+ if(!ReadFdExactly(fd, buf, len)) break;
+ buf[len] = 0;
+
+ func(ltohl(msg.dent.mode),
+ ltohl(msg.dent.size),
+ ltohl(msg.dent.time),
+ buf, cookie);
+ }
+
+fail:
+ adb_close(fd);
+ return -1;
+}
+
+struct syncsendbuf {
+ unsigned id;
+ unsigned size;
+ char data[SYNC_DATA_MAX];
+};
+
+static syncsendbuf send_buffer;
+
+static int sync_readtime(int fd, const char* path, unsigned int* timestamp, unsigned int* mode) {
+ syncmsg msg;
+ int len = strlen(path);
+
+ msg.req.id = ID_STAT;
+ msg.req.namelen = htoll(len);
+
+ if(!WriteFdExactly(fd, &msg.req, sizeof(msg.req)) ||
+ !WriteFdExactly(fd, path, len)) {
+ return -1;
+ }
+
+ if(!ReadFdExactly(fd, &msg.stat, sizeof(msg.stat))) {
+ return -1;
+ }
+
+ if(msg.stat.id != ID_STAT) {
+ return -1;
+ }
+
+ *timestamp = ltohl(msg.stat.time);
+ *mode = ltohl(msg.stat.mode);
+ return 0;
+}
+
+static int sync_start_readtime(int fd, const char *path)
+{
+ syncmsg msg;
+ int len = strlen(path);
+
+ msg.req.id = ID_STAT;
+ msg.req.namelen = htoll(len);
+
+ if(!WriteFdExactly(fd, &msg.req, sizeof(msg.req)) ||
+ !WriteFdExactly(fd, path, len)) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int sync_finish_readtime(int fd, unsigned int *timestamp,
+ unsigned int *mode, unsigned int *size)
+{
+ syncmsg msg;
+
+ if(!ReadFdExactly(fd, &msg.stat, sizeof(msg.stat)))
+ return -1;
+
+ if(msg.stat.id != ID_STAT)
+ return -1;
+
+ *timestamp = ltohl(msg.stat.time);
+ *mode = ltohl(msg.stat.mode);
+ *size = ltohl(msg.stat.size);
+
+ return 0;
+}
+
+static int sync_readmode(int fd, const char* path, unsigned* mode) {
+ syncmsg msg;
+ int len = strlen(path);
+
+ msg.req.id = ID_STAT;
+ msg.req.namelen = htoll(len);
+
+ if(!WriteFdExactly(fd, &msg.req, sizeof(msg.req)) ||
+ !WriteFdExactly(fd, path, len)) {
+ return -1;
+ }
+
+ if(!ReadFdExactly(fd, &msg.stat, sizeof(msg.stat))) {
+ return -1;
+ }
+
+ if(msg.stat.id != ID_STAT) {
+ return -1;
+ }
+
+ *mode = ltohl(msg.stat.mode);
+ return 0;
+}
+
+static int write_data_file(int fd, const char *path, syncsendbuf *sbuf, int show_progress)
+{
+ int lfd, err = 0;
+ unsigned long long size = 0;
+
+ lfd = adb_open(path, O_RDONLY);
+ if(lfd < 0) {
+ fprintf(stderr,"cannot open '%s': %s\n", path, strerror(errno));
+ return -1;
+ }
+
+ if (show_progress) {
+ // Determine local file size.
+ struct stat st;
+ if (stat(path, &st)) {
+ fprintf(stderr,"cannot stat '%s': %s\n", path, strerror(errno));
+ return -1;
+ }
+
+ size = st.st_size;
+ }
+
+ sbuf->id = ID_DATA;
+ for(;;) {
+ int ret;
+
+ ret = adb_read(lfd, sbuf->data, SYNC_DATA_MAX);
+ if(!ret)
+ break;
+
+ if(ret < 0) {
+ if(errno == EINTR)
+ continue;
+ fprintf(stderr,"cannot read '%s': %s\n", path, strerror(errno));
+ break;
+ }
+
+ sbuf->size = htoll(ret);
+ if(!WriteFdExactly(fd, sbuf, sizeof(unsigned) * 2 + ret)){
+ err = -1;
+ break;
+ }
+ total_bytes += ret;
+
+ if (show_progress) {
+ print_transfer_progress(total_bytes, size);
+ }
+ }
+
+ adb_close(lfd);
+ return err;
+}
+
+static int write_data_buffer(int fd, char* file_buffer, int size, syncsendbuf *sbuf,
+ int show_progress)
+{
+ int err = 0;
+ int total = 0;
+
+ sbuf->id = ID_DATA;
+ while (total < size) {
+ int count = size - total;
+ if (count > SYNC_DATA_MAX) {
+ count = SYNC_DATA_MAX;
+ }
+
+ memcpy(sbuf->data, &file_buffer[total], count);
+ sbuf->size = htoll(count);
+ if(!WriteFdExactly(fd, sbuf, sizeof(unsigned) * 2 + count)){
+ err = -1;
+ break;
+ }
+ total += count;
+ total_bytes += count;
+
+ if (show_progress) {
+ print_transfer_progress(total, size);
+ }
+ }
+
+ return err;
+}
+
+#if defined(_WIN32)
+extern int write_data_link(int fd, const char *path, syncsendbuf *sbuf) __attribute__((error("no symlinks on Windows")));
+#else
+static int write_data_link(int fd, const char *path, syncsendbuf *sbuf)
+{
+ int len, ret;
+
+ len = readlink(path, sbuf->data, SYNC_DATA_MAX-1);
+ if(len < 0) {
+ fprintf(stderr, "error reading link '%s': %s\n", path, strerror(errno));
+ return -1;
+ }
+ sbuf->data[len] = '\0';
+
+ sbuf->size = htoll(len + 1);
+ sbuf->id = ID_DATA;
+
+ ret = !WriteFdExactly(fd, sbuf, sizeof(unsigned) * 2 + len + 1);
+ if(ret)
+ return -1;
+
+ total_bytes += len + 1;
+
+ return 0;
+}
+#endif
+
+static int sync_send(int fd, const char *lpath, const char *rpath,
+ unsigned mtime, mode_t mode, int show_progress)
+{
+ syncmsg msg;
+ int len, r;
+ syncsendbuf *sbuf = &send_buffer;
+ char* file_buffer = NULL;
+ int size = 0;
+ char tmp[64];
+
+ len = strlen(rpath);
+ if(len > 1024) goto fail;
+
+ snprintf(tmp, sizeof(tmp), ",%d", mode);
+ r = strlen(tmp);
+
+ msg.req.id = ID_SEND;
+ msg.req.namelen = htoll(len + r);
+
+ if(!WriteFdExactly(fd, &msg.req, sizeof(msg.req)) ||
+ !WriteFdExactly(fd, rpath, len) || !WriteFdExactly(fd, tmp, r)) {
+ free(file_buffer);
+ goto fail;
+ }
+
+ if (file_buffer) {
+ write_data_buffer(fd, file_buffer, size, sbuf, show_progress);
+ free(file_buffer);
+ } else if (S_ISREG(mode))
+ write_data_file(fd, lpath, sbuf, show_progress);
+ else if (S_ISLNK(mode))
+ write_data_link(fd, lpath, sbuf);
+ else
+ goto fail;
+
+ msg.data.id = ID_DONE;
+ msg.data.size = htoll(mtime);
+ if(!WriteFdExactly(fd, &msg.data, sizeof(msg.data)))
+ goto fail;
+
+ if(!ReadFdExactly(fd, &msg.status, sizeof(msg.status)))
+ return -1;
+
+ if(msg.status.id != ID_OKAY) {
+ if(msg.status.id == ID_FAIL) {
+ len = ltohl(msg.status.msglen);
+ if(len > 256) len = 256;
+ if(!ReadFdExactly(fd, sbuf->data, len)) {
+ return -1;
+ }
+ sbuf->data[len] = 0;
+ } else
+ strcpy(sbuf->data, "unknown reason");
+
+ fprintf(stderr,"failed to copy '%s' to '%s': %s\n", lpath, rpath, sbuf->data);
+ return -1;
+ }
+
+ return 0;
+
+fail:
+ fprintf(stderr,"protocol failure\n");
+ adb_close(fd);
+ return -1;
+}
+
+static int sync_recv(int fd, const char* rpath, const char* lpath, int show_progress) {
+ syncmsg msg;
+ int len;
+ int lfd = -1;
+ char *buffer = send_buffer.data;
+ unsigned id;
+ unsigned long long size = 0;
+
+ len = strlen(rpath);
+ if(len > 1024) return -1;
+
+ if (show_progress) {
+ // Determine remote file size.
+ syncmsg stat_msg;
+ stat_msg.req.id = ID_STAT;
+ stat_msg.req.namelen = htoll(len);
+
+ if (!WriteFdExactly(fd, &stat_msg.req, sizeof(stat_msg.req)) ||
+ !WriteFdExactly(fd, rpath, len)) {
+ return -1;
+ }
+
+ if (!ReadFdExactly(fd, &stat_msg.stat, sizeof(stat_msg.stat))) {
+ return -1;
+ }
+
+ if (stat_msg.stat.id != ID_STAT) return -1;
+
+ size = ltohl(stat_msg.stat.size);
+ }
+
+ msg.req.id = ID_RECV;
+ msg.req.namelen = htoll(len);
+ if(!WriteFdExactly(fd, &msg.req, sizeof(msg.req)) ||
+ !WriteFdExactly(fd, rpath, len)) {
+ return -1;
+ }
+
+ if(!ReadFdExactly(fd, &msg.data, sizeof(msg.data))) {
+ return -1;
+ }
+ id = msg.data.id;
+
+ if((id == ID_DATA) || (id == ID_DONE)) {
+ adb_unlink(lpath);
+ mkdirs(lpath);
+ lfd = adb_creat(lpath, 0644);
+ if(lfd < 0) {
+ fprintf(stderr,"cannot create '%s': %s\n", lpath, strerror(errno));
+ return -1;
+ }
+ goto handle_data;
+ } else {
+ goto remote_error;
+ }
+
+ for(;;) {
+ if(!ReadFdExactly(fd, &msg.data, sizeof(msg.data))) {
+ return -1;
+ }
+ id = msg.data.id;
+
+ handle_data:
+ len = ltohl(msg.data.size);
+ if(id == ID_DONE) break;
+ if(id != ID_DATA) goto remote_error;
+ if(len > SYNC_DATA_MAX) {
+ fprintf(stderr,"data overrun\n");
+ adb_close(lfd);
+ return -1;
+ }
+
+ if(!ReadFdExactly(fd, buffer, len)) {
+ adb_close(lfd);
+ return -1;
+ }
+
+ if(!WriteFdExactly(lfd, buffer, len)) {
+ fprintf(stderr,"cannot write '%s': %s\n", rpath, strerror(errno));
+ adb_close(lfd);
+ return -1;
+ }
+
+ total_bytes += len;
+
+ if (show_progress) {
+ print_transfer_progress(total_bytes, size);
+ }
+ }
+
+ adb_close(lfd);
+ return 0;
+
+remote_error:
+ adb_close(lfd);
+ adb_unlink(lpath);
+
+ if(id == ID_FAIL) {
+ len = ltohl(msg.data.size);
+ if(len > 256) len = 256;
+ if(!ReadFdExactly(fd, buffer, len)) {
+ return -1;
+ }
+ buffer[len] = 0;
+ } else {
+ memcpy(buffer, &id, 4);
+ buffer[4] = 0;
+ }
+ fprintf(stderr,"failed to copy '%s' to '%s': %s\n", rpath, lpath, buffer);
+ return 0;
+}
+
+/* --- */
+static void do_sync_ls_cb(unsigned mode, unsigned size, unsigned time,
+ const char *name, void *cookie)
+{
+ printf("%08x %08x %08x %s\n", mode, size, time, name);
+}
+
+int do_sync_ls(const char* path) {
+ std::string error;
+ int fd = adb_connect("sync:", &error);
+ if (fd < 0) {
+ fprintf(stderr,"error: %s\n", error.c_str());
+ return 1;
+ }
+
+ if(sync_ls(fd, path, do_sync_ls_cb, 0)) {
+ return 1;
+ } else {
+ sync_quit(fd);
+ return 0;
+ }
+}
+
+struct copyinfo
+{
+ copyinfo *next;
+ const char *src;
+ const char *dst;
+ unsigned int time;
+ unsigned int mode;
+ unsigned int size;
+ int flag;
+};
+
+static copyinfo* mkcopyinfo(const char* spath, const char* dpath, const char* name, int isdir) {
+ int slen = strlen(spath);
+ int dlen = strlen(dpath);
+ int nlen = strlen(name);
+ int ssize = slen + nlen + 2;
+ int dsize = dlen + nlen + 2;
+
+ copyinfo *ci = reinterpret_cast<copyinfo*>(malloc(sizeof(copyinfo) + ssize + dsize));
+ if(ci == 0) {
+ fprintf(stderr,"out of memory\n");
+ abort();
+ }
+
+ ci->next = 0;
+ ci->time = 0;
+ ci->mode = 0;
+ ci->size = 0;
+ ci->flag = 0;
+ ci->src = (const char*)(ci + 1);
+ ci->dst = ci->src + ssize;
+ snprintf((char*) ci->src, ssize, isdir ? "%s%s/" : "%s%s", spath, name);
+ snprintf((char*) ci->dst, dsize, isdir ? "%s%s/" : "%s%s", dpath, name);
+
+ return ci;
+}
+
+
+static int local_build_list(copyinfo **filelist,
+ const char *lpath, const char *rpath)
+{
+ DIR *d;
+ struct dirent *de;
+ struct stat st;
+ copyinfo *dirlist = 0;
+ copyinfo *ci, *next;
+
+ d = opendir(lpath);
+ if(d == 0) {
+ fprintf(stderr,"cannot open '%s': %s\n", lpath, strerror(errno));
+ return -1;
+ }
+
+ while((de = readdir(d))) {
+ char stat_path[PATH_MAX];
+ char *name = de->d_name;
+
+ if(name[0] == '.') {
+ if(name[1] == 0) continue;
+ if((name[1] == '.') && (name[2] == 0)) continue;
+ }
+
+ /*
+ * We could use d_type if HAVE_DIRENT_D_TYPE is defined, but reiserfs
+ * always returns DT_UNKNOWN, so we just use stat() for all cases.
+ */
+ if (strlen(lpath) + strlen(de->d_name) + 1 > sizeof(stat_path))
+ continue;
+ strcpy(stat_path, lpath);
+ strcat(stat_path, de->d_name);
+
+ if(!lstat(stat_path, &st)) {
+ if (S_ISDIR(st.st_mode)) {
+ ci = mkcopyinfo(lpath, rpath, name, 1);
+ ci->next = dirlist;
+ dirlist = ci;
+ } else {
+ ci = mkcopyinfo(lpath, rpath, name, 0);
+ if(lstat(ci->src, &st)) {
+ fprintf(stderr,"cannot stat '%s': %s\n", ci->src, strerror(errno));
+ free(ci);
+ closedir(d);
+ return -1;
+ }
+ if(!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
+ fprintf(stderr, "skipping special file '%s'\n", ci->src);
+ free(ci);
+ } else {
+ ci->time = st.st_mtime;
+ ci->mode = st.st_mode;
+ ci->size = st.st_size;
+ ci->next = *filelist;
+ *filelist = ci;
+ }
+ }
+ } else {
+ fprintf(stderr, "cannot lstat '%s': %s\n",stat_path , strerror(errno));
+ }
+ }
+
+ closedir(d);
+
+ for(ci = dirlist; ci != 0; ci = next) {
+ next = ci->next;
+ local_build_list(filelist, ci->src, ci->dst);
+ free(ci);
+ }
+
+ return 0;
+}
+
+
+static int copy_local_dir_remote(int fd, const char *lpath, const char *rpath, int checktimestamps, int listonly)
+{
+ copyinfo *filelist = 0;
+ copyinfo *ci, *next;
+ int pushed = 0;
+ int skipped = 0;
+
+ if((lpath[0] == 0) || (rpath[0] == 0)) return -1;
+ if(lpath[strlen(lpath) - 1] != '/') {
+ int tmplen = strlen(lpath)+2;
+ char *tmp = reinterpret_cast<char*>(malloc(tmplen));
+ if(tmp == 0) return -1;
+ snprintf(tmp, tmplen, "%s/",lpath);
+ lpath = tmp;
+ }
+ if(rpath[strlen(rpath) - 1] != '/') {
+ int tmplen = strlen(rpath)+2;
+ char *tmp = reinterpret_cast<char*>(malloc(tmplen));
+ if(tmp == 0) return -1;
+ snprintf(tmp, tmplen, "%s/",rpath);
+ rpath = tmp;
+ }
+
+ if(local_build_list(&filelist, lpath, rpath)) {
+ return -1;
+ }
+
+ if(checktimestamps){
+ for(ci = filelist; ci != 0; ci = ci->next) {
+ if(sync_start_readtime(fd, ci->dst)) {
+ return 1;
+ }
+ }
+ for(ci = filelist; ci != 0; ci = ci->next) {
+ unsigned int timestamp, mode, size;
+ if(sync_finish_readtime(fd, ×tamp, &mode, &size))
+ return 1;
+ if(size == ci->size) {
+ /* for links, we cannot update the atime/mtime */
+ if((S_ISREG(ci->mode & mode) && timestamp == ci->time) ||
+ (S_ISLNK(ci->mode & mode) && timestamp >= ci->time))
+ ci->flag = 1;
+ }
+ }
+ }
+ for(ci = filelist; ci != 0; ci = next) {
+ next = ci->next;
+ if(ci->flag == 0) {
+ fprintf(stderr,"%spush: %s -> %s\n", listonly ? "would " : "", ci->src, ci->dst);
+ if(!listonly &&
+ sync_send(fd, ci->src, ci->dst, ci->time, ci->mode,
+ 0 /* no show progress */)) {
+ return 1;
+ }
+ pushed++;
+ } else {
+ skipped++;
+ }
+ free(ci);
+ }
+
+ fprintf(stderr,"%d file%s pushed. %d file%s skipped.\n",
+ pushed, (pushed == 1) ? "" : "s",
+ skipped, (skipped == 1) ? "" : "s");
+
+ return 0;
+}
+
+
+int do_sync_push(const char *lpath, const char *rpath, int show_progress)
+{
+ struct stat st;
+ unsigned mode;
+
+ std::string error;
+ int fd = adb_connect("sync:", &error);
+ if (fd < 0) {
+ fprintf(stderr,"error: %s\n", error.c_str());
+ return 1;
+ }
+
+ if(stat(lpath, &st)) {
+ fprintf(stderr,"cannot stat '%s': %s\n", lpath, strerror(errno));
+ sync_quit(fd);
+ return 1;
+ }
+
+ if(S_ISDIR(st.st_mode)) {
+ BEGIN();
+ if(copy_local_dir_remote(fd, lpath, rpath, 0, 0)) {
+ return 1;
+ } else {
+ END();
+ sync_quit(fd);
+ }
+ } else {
+ if(sync_readmode(fd, rpath, &mode)) {
+ return 1;
+ }
+ if((mode != 0) && S_ISDIR(mode)) {
+ /* if we're copying a local file to a remote directory,
+ ** we *really* want to copy to remotedir + "/" + localfilename
+ */
+ const char *name = adb_dirstop(lpath);
+ if(name == 0) {
+ name = lpath;
+ } else {
+ name++;
+ }
+ int tmplen = strlen(name) + strlen(rpath) + 2;
+ char *tmp = reinterpret_cast<char*>(
+ malloc(strlen(name) + strlen(rpath) + 2));
+ if(tmp == 0) return 1;
+ snprintf(tmp, tmplen, "%s/%s", rpath, name);
+ rpath = tmp;
+ }
+ BEGIN();
+ if(sync_send(fd, lpath, rpath, st.st_mtime, st.st_mode, show_progress)) {
+ return 1;
+ } else {
+ END();
+ sync_quit(fd);
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+
+struct sync_ls_build_list_cb_args {
+ copyinfo **filelist;
+ copyinfo **dirlist;
+ const char *rpath;
+ const char *lpath;
+};
+
+static void sync_ls_build_list_cb(unsigned mode, unsigned size, unsigned time,
+ const char* name, void* cookie)
+{
+ sync_ls_build_list_cb_args *args = (sync_ls_build_list_cb_args *)cookie;
+ copyinfo *ci;
+
+ if (S_ISDIR(mode)) {
+ copyinfo **dirlist = args->dirlist;
+
+ /* Don't try recursing down "." or ".." */
+ if (name[0] == '.') {
+ if (name[1] == '\0') return;
+ if ((name[1] == '.') && (name[2] == '\0')) return;
+ }
+
+ ci = mkcopyinfo(args->rpath, args->lpath, name, 1);
+ ci->next = *dirlist;
+ *dirlist = ci;
+ } else if (S_ISREG(mode) || S_ISLNK(mode)) {
+ copyinfo **filelist = args->filelist;
+
+ ci = mkcopyinfo(args->rpath, args->lpath, name, 0);
+ ci->time = time;
+ ci->mode = mode;
+ ci->size = size;
+ ci->next = *filelist;
+ *filelist = ci;
+ } else {
+ fprintf(stderr, "skipping special file '%s'\n", name);
+ }
+}
+
+static int remote_build_list(int syncfd, copyinfo **filelist,
+ const char *rpath, const char *lpath)
+{
+ copyinfo *dirlist = NULL;
+ sync_ls_build_list_cb_args args;
+
+ args.filelist = filelist;
+ args.dirlist = &dirlist;
+ args.rpath = rpath;
+ args.lpath = lpath;
+
+ /* Put the files/dirs in rpath on the lists. */
+ if (sync_ls(syncfd, rpath, sync_ls_build_list_cb, (void *)&args)) {
+ return 1;
+ }
+
+ /* Recurse into each directory we found. */
+ while (dirlist != NULL) {
+ copyinfo *next = dirlist->next;
+ if (remote_build_list(syncfd, filelist, dirlist->src, dirlist->dst)) {
+ return 1;
+ }
+ free(dirlist);
+ dirlist = next;
+ }
+
+ return 0;
+}
+
+static int set_time_and_mode(const char *lpath, time_t time, unsigned int mode)
+{
+ struct utimbuf times = { time, time };
+ int r1 = utime(lpath, ×);
+
+ /* use umask for permissions */
+ mode_t mask=umask(0000);
+ umask(mask);
+ int r2 = chmod(lpath, mode & ~mask);
+
+ return r1 ? : r2;
+}
+
+/* Return a copy of the path string with / appended if needed */
+static char *add_slash_to_path(const char *path)
+{
+ if (path[strlen(path) - 1] != '/') {
+ size_t len = strlen(path) + 2;
+ char *path_with_slash = reinterpret_cast<char*>(malloc(len));
+ if (path_with_slash == NULL)
+ return NULL;
+ snprintf(path_with_slash, len, "%s/", path);
+ return path_with_slash;
+ } else {
+ return strdup(path);
+ }
+}
+
+static int copy_remote_dir_local(int fd, const char *rpath, const char *lpath,
+ int copy_attrs)
+{
+ copyinfo *filelist = 0;
+ copyinfo *ci, *next;
+ int pulled = 0;
+ int skipped = 0;
+ char *rpath_clean = NULL;
+ char *lpath_clean = NULL;
+ int ret = 0;
+
+ if (rpath[0] == '\0' || lpath[0] == '\0') {
+ ret = -1;
+ goto finish;
+ }
+
+ /* Make sure that both directory paths end in a slash. */
+ rpath_clean = add_slash_to_path(rpath);
+ if (!rpath_clean) {
+ ret = -1;
+ goto finish;
+ }
+ lpath_clean = add_slash_to_path(lpath);
+ if (!lpath_clean) {
+ ret = -1;
+ goto finish;
+ }
+
+ /* Recursively build the list of files to copy. */
+ fprintf(stderr, "pull: building file list...\n");
+ if (remote_build_list(fd, &filelist, rpath_clean, lpath_clean)) {
+ ret = -1;
+ goto finish;
+ }
+
+ for (ci = filelist; ci != 0; ci = next) {
+ next = ci->next;
+ if (ci->flag == 0) {
+ fprintf(stderr, "pull: %s -> %s\n", ci->src, ci->dst);
+ if (sync_recv(fd, ci->src, ci->dst, 0 /* no show progress */)) {
+ ret = -1;
+ goto finish;
+ }
+
+ if (copy_attrs && set_time_and_mode(ci->dst, ci->time, ci->mode)) {
+ ret = -1;
+ goto finish;
+ }
+ pulled++;
+ } else {
+ skipped++;
+ }
+ free(ci);
+ }
+
+ fprintf(stderr, "%d file%s pulled. %d file%s skipped.\n",
+ pulled, (pulled == 1) ? "" : "s",
+ skipped, (skipped == 1) ? "" : "s");
+
+finish:
+ free(lpath_clean);
+ free(rpath_clean);
+ return ret;
+}
+
+int do_sync_pull(const char *rpath, const char *lpath, int show_progress, int copy_attrs)
+{
+ unsigned mode, time;
+ struct stat st;
+
+ std::string error;
+ int fd = adb_connect("sync:", &error);
+ if (fd < 0) {
+ fprintf(stderr,"error: %s\n", error.c_str());
+ return 1;
+ }
+
+ if(sync_readtime(fd, rpath, &time, &mode)) {
+ return 1;
+ }
+ if(mode == 0) {
+ fprintf(stderr,"remote object '%s' does not exist\n", rpath);
+ return 1;
+ }
+
+ if(S_ISREG(mode) || S_ISLNK(mode) || S_ISCHR(mode) || S_ISBLK(mode)) {
+ if(stat(lpath, &st) == 0) {
+ if(S_ISDIR(st.st_mode)) {
+ /* if we're copying a remote file to a local directory,
+ ** we *really* want to copy to localdir + "/" + remotefilename
+ */
+ const char *name = adb_dirstop(rpath);
+ if(name == 0) {
+ name = rpath;
+ } else {
+ name++;
+ }
+ int tmplen = strlen(name) + strlen(lpath) + 2;
+ char *tmp = reinterpret_cast<char*>(malloc(tmplen));
+ if(tmp == 0) return 1;
+ snprintf(tmp, tmplen, "%s/%s", lpath, name);
+ lpath = tmp;
+ }
+ }
+ BEGIN();
+ if (sync_recv(fd, rpath, lpath, show_progress)) {
+ return 1;
+ } else {
+ if (copy_attrs && set_time_and_mode(lpath, time, mode))
+ return 1;
+ END();
+ sync_quit(fd);
+ return 0;
+ }
+ } else if(S_ISDIR(mode)) {
+ BEGIN();
+ if (copy_remote_dir_local(fd, rpath, lpath, copy_attrs)) {
+ return 1;
+ } else {
+ END();
+ sync_quit(fd);
+ return 0;
+ }
+ } else {
+ fprintf(stderr,"remote object '%s' not a file or directory\n", rpath);
+ return 1;
+ }
+}
+
+int do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only)
+{
+ fprintf(stderr, "syncing %s...\n", rpath.c_str());
+
+ std::string error;
+ int fd = adb_connect("sync:", &error);
+ if (fd < 0) {
+ fprintf(stderr, "error: %s\n", error.c_str());
+ return 1;
+ }
+
+ BEGIN();
+ if (copy_local_dir_remote(fd, lpath.c_str(), rpath.c_str(), 1, list_only)) {
+ return 1;
+ } else {
+ END();
+ sync_quit(fd);
+ return 0;
+ }
+}
diff --git a/adb/file_sync_service.cpp b/adb/file_sync_service.cpp
new file mode 100644
index 0000000..e8e9a0f
--- /dev/null
+++ b/adb/file_sync_service.cpp
@@ -0,0 +1,469 @@
+/*
+ * Copyright (C) 2007 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 TRACE_TAG TRACE_SYNC
+
+#include "sysdeps.h"
+#include "file_sync_service.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <selinux/android.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <utime.h>
+
+#include "adb.h"
+#include "adb_io.h"
+#include "private/android_filesystem_config.h"
+
+static bool should_use_fs_config(const char* path) {
+ // TODO: use fs_config to configure permissions on /data.
+ return strncmp("/system/", path, strlen("/system/")) == 0 ||
+ strncmp("/vendor/", path, strlen("/vendor/")) == 0 ||
+ strncmp("/oem/", path, strlen("/oem/")) == 0;
+}
+
+static int mkdirs(char *name)
+{
+ int ret;
+ char *x = name + 1;
+ uid_t uid = -1;
+ gid_t gid = -1;
+ unsigned int mode = 0775;
+ uint64_t cap = 0;
+
+ if(name[0] != '/') return -1;
+
+ for(;;) {
+ x = adb_dirstart(x);
+ if(x == 0) return 0;
+ *x = 0;
+ if (should_use_fs_config(name)) {
+ fs_config(name, 1, &uid, &gid, &mode, &cap);
+ }
+ ret = adb_mkdir(name, mode);
+ if((ret < 0) && (errno != EEXIST)) {
+ D("mkdir(\"%s\") -> %s\n", name, strerror(errno));
+ *x = '/';
+ return ret;
+ } else if(ret == 0) {
+ ret = chown(name, uid, gid);
+ if (ret < 0) {
+ *x = '/';
+ return ret;
+ }
+ selinux_android_restorecon(name, 0);
+ }
+ *x++ = '/';
+ }
+ return 0;
+}
+
+static int do_stat(int s, const char *path)
+{
+ syncmsg msg;
+ struct stat st;
+
+ msg.stat.id = ID_STAT;
+
+ if(lstat(path, &st)) {
+ msg.stat.mode = 0;
+ msg.stat.size = 0;
+ msg.stat.time = 0;
+ } else {
+ msg.stat.mode = htoll(st.st_mode);
+ msg.stat.size = htoll(st.st_size);
+ msg.stat.time = htoll(st.st_mtime);
+ }
+
+ return WriteFdExactly(s, &msg.stat, sizeof(msg.stat)) ? 0 : -1;
+}
+
+static int do_list(int s, const char *path)
+{
+ DIR *d;
+ struct dirent *de;
+ struct stat st;
+ syncmsg msg;
+ int len;
+
+ char tmp[1024 + 256 + 1];
+ char *fname;
+
+ len = strlen(path);
+ memcpy(tmp, path, len);
+ tmp[len] = '/';
+ fname = tmp + len + 1;
+
+ msg.dent.id = ID_DENT;
+
+ d = opendir(path);
+ if(d == 0) goto done;
+
+ while((de = readdir(d))) {
+ int len = strlen(de->d_name);
+
+ /* not supposed to be possible, but
+ if it does happen, let's not buffer overrun */
+ if(len > 256) continue;
+
+ strcpy(fname, de->d_name);
+ if(lstat(tmp, &st) == 0) {
+ msg.dent.mode = htoll(st.st_mode);
+ msg.dent.size = htoll(st.st_size);
+ msg.dent.time = htoll(st.st_mtime);
+ msg.dent.namelen = htoll(len);
+
+ if(!WriteFdExactly(s, &msg.dent, sizeof(msg.dent)) ||
+ !WriteFdExactly(s, de->d_name, len)) {
+ closedir(d);
+ return -1;
+ }
+ }
+ }
+
+ closedir(d);
+
+done:
+ msg.dent.id = ID_DONE;
+ msg.dent.mode = 0;
+ msg.dent.size = 0;
+ msg.dent.time = 0;
+ msg.dent.namelen = 0;
+ return WriteFdExactly(s, &msg.dent, sizeof(msg.dent)) ? 0 : -1;
+}
+
+static int fail_message(int s, const char *reason)
+{
+ syncmsg msg;
+ int len = strlen(reason);
+
+ D("sync: failure: %s\n", reason);
+
+ msg.data.id = ID_FAIL;
+ msg.data.size = htoll(len);
+ if(!WriteFdExactly(s, &msg.data, sizeof(msg.data)) ||
+ !WriteFdExactly(s, reason, len)) {
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+static int fail_errno(int s)
+{
+ return fail_message(s, strerror(errno));
+}
+
+static int handle_send_file(int s, char *path, uid_t uid,
+ gid_t gid, mode_t mode, char *buffer, bool do_unlink)
+{
+ syncmsg msg;
+ unsigned int timestamp = 0;
+ int fd;
+
+ fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode);
+ if(fd < 0 && errno == ENOENT) {
+ if(mkdirs(path) != 0) {
+ if(fail_errno(s))
+ return -1;
+ fd = -1;
+ } else {
+ fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode);
+ }
+ }
+ if(fd < 0 && errno == EEXIST) {
+ fd = adb_open_mode(path, O_WRONLY | O_CLOEXEC, mode);
+ }
+ if(fd < 0) {
+ if(fail_errno(s))
+ return -1;
+ fd = -1;
+ } else {
+ if(fchown(fd, uid, gid) != 0) {
+ fail_errno(s);
+ errno = 0;
+ }
+
+ /*
+ * fchown clears the setuid bit - restore it if present.
+ * Ignore the result of calling fchmod. It's not supported
+ * by all filesystems. b/12441485
+ */
+ fchmod(fd, mode);
+ }
+
+ for(;;) {
+ unsigned int len;
+
+ if(!ReadFdExactly(s, &msg.data, sizeof(msg.data)))
+ goto fail;
+
+ if(msg.data.id != ID_DATA) {
+ if(msg.data.id == ID_DONE) {
+ timestamp = ltohl(msg.data.size);
+ break;
+ }
+ fail_message(s, "invalid data message");
+ goto fail;
+ }
+ len = ltohl(msg.data.size);
+ if(len > SYNC_DATA_MAX) {
+ fail_message(s, "oversize data message");
+ goto fail;
+ }
+ if(!ReadFdExactly(s, buffer, len))
+ goto fail;
+
+ if(fd < 0)
+ continue;
+ if(!WriteFdExactly(fd, buffer, len)) {
+ int saved_errno = errno;
+ adb_close(fd);
+ if (do_unlink) adb_unlink(path);
+ fd = -1;
+ errno = saved_errno;
+ if(fail_errno(s)) return -1;
+ }
+ }
+
+ if(fd >= 0) {
+ struct utimbuf u;
+ adb_close(fd);
+ selinux_android_restorecon(path, 0);
+ u.actime = timestamp;
+ u.modtime = timestamp;
+ utime(path, &u);
+
+ msg.status.id = ID_OKAY;
+ msg.status.msglen = 0;
+ if(!WriteFdExactly(s, &msg.status, sizeof(msg.status)))
+ return -1;
+ }
+ return 0;
+
+fail:
+ if(fd >= 0)
+ adb_close(fd);
+ if (do_unlink) adb_unlink(path);
+ return -1;
+}
+
+#if defined(_WIN32)
+extern int handle_send_link(int s, char *path, char *buffer) __attribute__((error("no symlinks on Windows")));
+#else
+static int handle_send_link(int s, char *path, char *buffer)
+{
+ syncmsg msg;
+ unsigned int len;
+ int ret;
+
+ if(!ReadFdExactly(s, &msg.data, sizeof(msg.data)))
+ return -1;
+
+ if(msg.data.id != ID_DATA) {
+ fail_message(s, "invalid data message: expected ID_DATA");
+ return -1;
+ }
+
+ len = ltohl(msg.data.size);
+ if(len > SYNC_DATA_MAX) {
+ fail_message(s, "oversize data message");
+ return -1;
+ }
+ if(!ReadFdExactly(s, buffer, len))
+ return -1;
+
+ ret = symlink(buffer, path);
+ if(ret && errno == ENOENT) {
+ if(mkdirs(path) != 0) {
+ fail_errno(s);
+ return -1;
+ }
+ ret = symlink(buffer, path);
+ }
+ if(ret) {
+ fail_errno(s);
+ return -1;
+ }
+
+ if(!ReadFdExactly(s, &msg.data, sizeof(msg.data)))
+ return -1;
+
+ if(msg.data.id == ID_DONE) {
+ msg.status.id = ID_OKAY;
+ msg.status.msglen = 0;
+ if(!WriteFdExactly(s, &msg.status, sizeof(msg.status)))
+ return -1;
+ } else {
+ fail_message(s, "invalid data message: expected ID_DONE");
+ return -1;
+ }
+
+ return 0;
+}
+#endif
+
+static int do_send(int s, char *path, char *buffer)
+{
+ unsigned int mode;
+ bool is_link = false;
+ bool do_unlink;
+
+ char* tmp = strrchr(path,',');
+ if(tmp) {
+ *tmp = 0;
+ errno = 0;
+ mode = strtoul(tmp + 1, NULL, 0);
+ is_link = S_ISLNK((mode_t) mode);
+ mode &= 0777;
+ }
+ if(!tmp || errno) {
+ mode = 0644;
+ is_link = 0;
+ do_unlink = true;
+ } else {
+ struct stat st;
+ /* Don't delete files before copying if they are not "regular" */
+ do_unlink = lstat(path, &st) || S_ISREG(st.st_mode) || S_ISLNK(st.st_mode);
+ if (do_unlink) {
+ adb_unlink(path);
+ }
+ }
+
+ if (is_link) {
+ return handle_send_link(s, path, buffer);
+ }
+
+ uid_t uid = -1;
+ gid_t gid = -1;
+ uint64_t cap = 0;
+
+ /* copy user permission bits to "group" and "other" permissions */
+ mode |= ((mode >> 3) & 0070);
+ mode |= ((mode >> 3) & 0007);
+
+ tmp = path;
+ if(*tmp == '/') {
+ tmp++;
+ }
+ if (should_use_fs_config(path)) {
+ fs_config(tmp, 0, &uid, &gid, &mode, &cap);
+ }
+ return handle_send_file(s, path, uid, gid, mode, buffer, do_unlink);
+}
+
+static int do_recv(int s, const char *path, char *buffer)
+{
+ syncmsg msg;
+ int fd, r;
+
+ fd = adb_open(path, O_RDONLY | O_CLOEXEC);
+ if(fd < 0) {
+ if(fail_errno(s)) return -1;
+ return 0;
+ }
+
+ msg.data.id = ID_DATA;
+ for(;;) {
+ r = adb_read(fd, buffer, SYNC_DATA_MAX);
+ if(r <= 0) {
+ if(r == 0) break;
+ if(errno == EINTR) continue;
+ r = fail_errno(s);
+ adb_close(fd);
+ return r;
+ }
+ msg.data.size = htoll(r);
+ if(!WriteFdExactly(s, &msg.data, sizeof(msg.data)) ||
+ !WriteFdExactly(s, buffer, r)) {
+ adb_close(fd);
+ return -1;
+ }
+ }
+
+ adb_close(fd);
+
+ msg.data.id = ID_DONE;
+ msg.data.size = 0;
+ if(!WriteFdExactly(s, &msg.data, sizeof(msg.data))) {
+ return -1;
+ }
+
+ return 0;
+}
+
+void file_sync_service(int fd, void *cookie)
+{
+ syncmsg msg;
+ char name[1025];
+ unsigned namelen;
+
+ char *buffer = reinterpret_cast<char*>(malloc(SYNC_DATA_MAX));
+ if(buffer == 0) goto fail;
+
+ for(;;) {
+ D("sync: waiting for command\n");
+
+ if(!ReadFdExactly(fd, &msg.req, sizeof(msg.req))) {
+ fail_message(fd, "command read failure");
+ break;
+ }
+ namelen = ltohl(msg.req.namelen);
+ if(namelen > 1024) {
+ fail_message(fd, "invalid namelen");
+ break;
+ }
+ if(!ReadFdExactly(fd, name, namelen)) {
+ fail_message(fd, "filename read failure");
+ break;
+ }
+ name[namelen] = 0;
+
+ msg.req.namelen = 0;
+ D("sync: '%s' '%s'\n", (char*) &msg.req, name);
+
+ switch(msg.req.id) {
+ case ID_STAT:
+ if(do_stat(fd, name)) goto fail;
+ break;
+ case ID_LIST:
+ if(do_list(fd, name)) goto fail;
+ break;
+ case ID_SEND:
+ if(do_send(fd, name, buffer)) goto fail;
+ break;
+ case ID_RECV:
+ if(do_recv(fd, name, buffer)) goto fail;
+ break;
+ case ID_QUIT:
+ goto fail;
+ default:
+ fail_message(fd, "unknown command");
+ goto fail;
+ }
+ }
+
+fail:
+ if(buffer != 0) free(buffer);
+ D("sync: done\n");
+ adb_close(fd);
+}
diff --git a/adb/file_sync_service.h b/adb/file_sync_service.h
new file mode 100644
index 0000000..344eb98
--- /dev/null
+++ b/adb/file_sync_service.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _FILE_SYNC_SERVICE_H_
+#define _FILE_SYNC_SERVICE_H_
+
+#include <string>
+
+#define htoll(x) (x)
+#define ltohl(x) (x)
+
+#define MKID(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((d) << 24))
+
+#define ID_STAT MKID('S','T','A','T')
+#define ID_LIST MKID('L','I','S','T')
+#define ID_ULNK MKID('U','L','N','K')
+#define ID_SEND MKID('S','E','N','D')
+#define ID_RECV MKID('R','E','C','V')
+#define ID_DENT MKID('D','E','N','T')
+#define ID_DONE MKID('D','O','N','E')
+#define ID_DATA MKID('D','A','T','A')
+#define ID_OKAY MKID('O','K','A','Y')
+#define ID_FAIL MKID('F','A','I','L')
+#define ID_QUIT MKID('Q','U','I','T')
+
+union syncmsg {
+ unsigned id;
+ struct {
+ unsigned id;
+ unsigned namelen;
+ } req;
+ struct {
+ unsigned id;
+ unsigned mode;
+ unsigned size;
+ unsigned time;
+ } stat;
+ struct {
+ unsigned id;
+ unsigned mode;
+ unsigned size;
+ unsigned time;
+ unsigned namelen;
+ } dent;
+ struct {
+ unsigned id;
+ unsigned size;
+ } data;
+ struct {
+ unsigned id;
+ unsigned msglen;
+ } status;
+} ;
+
+
+void file_sync_service(int fd, void *cookie);
+int do_sync_ls(const char *path);
+int do_sync_push(const char *lpath, const char *rpath, int show_progress);
+int do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only);
+int do_sync_pull(const char *rpath, const char *lpath, int show_progress, int pullTime);
+
+#define SYNC_DATA_MAX (64*1024)
+
+#endif
diff --git a/adb/framebuffer_service.cpp b/adb/framebuffer_service.cpp
new file mode 100644
index 0000000..7baad8b
--- /dev/null
+++ b/adb/framebuffer_service.cpp
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2007 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 <linux/fb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "sysdeps.h"
+
+#include "adb.h"
+#include "adb_io.h"
+#include "fdevent.h"
+
+/* TODO:
+** - sync with vsync to avoid tearing
+*/
+/* This version number defines the format of the fbinfo struct.
+ It must match versioning in ddms where this data is consumed. */
+#define DDMS_RAWIMAGE_VERSION 1
+struct fbinfo {
+ unsigned int version;
+ unsigned int bpp;
+ unsigned int size;
+ unsigned int width;
+ unsigned int height;
+ unsigned int red_offset;
+ unsigned int red_length;
+ unsigned int blue_offset;
+ unsigned int blue_length;
+ unsigned int green_offset;
+ unsigned int green_length;
+ unsigned int alpha_offset;
+ unsigned int alpha_length;
+} __attribute__((packed));
+
+void framebuffer_service(int fd, void *cookie)
+{
+ struct fbinfo fbinfo;
+ unsigned int i, bsize;
+ char buf[640];
+ int fd_screencap;
+ int w, h, f;
+ int fds[2];
+ pid_t pid;
+
+ if (pipe2(fds, O_CLOEXEC) < 0) goto pipefail;
+
+ pid = fork();
+ if (pid < 0) goto done;
+
+ if (pid == 0) {
+ dup2(fds[1], STDOUT_FILENO);
+ adb_close(fds[0]);
+ adb_close(fds[1]);
+ const char* command = "screencap";
+ const char *args[2] = {command, NULL};
+ execvp(command, (char**)args);
+ exit(1);
+ }
+
+ adb_close(fds[1]);
+ fd_screencap = fds[0];
+
+ /* read w, h & format */
+ if(!ReadFdExactly(fd_screencap, &w, 4)) goto done;
+ if(!ReadFdExactly(fd_screencap, &h, 4)) goto done;
+ if(!ReadFdExactly(fd_screencap, &f, 4)) goto done;
+
+ fbinfo.version = DDMS_RAWIMAGE_VERSION;
+ /* see hardware/hardware.h */
+ switch (f) {
+ case 1: /* RGBA_8888 */
+ fbinfo.bpp = 32;
+ fbinfo.size = w * h * 4;
+ fbinfo.width = w;
+ fbinfo.height = h;
+ fbinfo.red_offset = 0;
+ fbinfo.red_length = 8;
+ fbinfo.green_offset = 8;
+ fbinfo.green_length = 8;
+ fbinfo.blue_offset = 16;
+ fbinfo.blue_length = 8;
+ fbinfo.alpha_offset = 24;
+ fbinfo.alpha_length = 8;
+ break;
+ case 2: /* RGBX_8888 */
+ fbinfo.bpp = 32;
+ fbinfo.size = w * h * 4;
+ fbinfo.width = w;
+ fbinfo.height = h;
+ fbinfo.red_offset = 0;
+ fbinfo.red_length = 8;
+ fbinfo.green_offset = 8;
+ fbinfo.green_length = 8;
+ fbinfo.blue_offset = 16;
+ fbinfo.blue_length = 8;
+ fbinfo.alpha_offset = 24;
+ fbinfo.alpha_length = 0;
+ break;
+ case 3: /* RGB_888 */
+ fbinfo.bpp = 24;
+ fbinfo.size = w * h * 3;
+ fbinfo.width = w;
+ fbinfo.height = h;
+ fbinfo.red_offset = 0;
+ fbinfo.red_length = 8;
+ fbinfo.green_offset = 8;
+ fbinfo.green_length = 8;
+ fbinfo.blue_offset = 16;
+ fbinfo.blue_length = 8;
+ fbinfo.alpha_offset = 24;
+ fbinfo.alpha_length = 0;
+ break;
+ case 4: /* RGB_565 */
+ fbinfo.bpp = 16;
+ fbinfo.size = w * h * 2;
+ fbinfo.width = w;
+ fbinfo.height = h;
+ fbinfo.red_offset = 11;
+ fbinfo.red_length = 5;
+ fbinfo.green_offset = 5;
+ fbinfo.green_length = 6;
+ fbinfo.blue_offset = 0;
+ fbinfo.blue_length = 5;
+ fbinfo.alpha_offset = 0;
+ fbinfo.alpha_length = 0;
+ break;
+ case 5: /* BGRA_8888 */
+ fbinfo.bpp = 32;
+ fbinfo.size = w * h * 4;
+ fbinfo.width = w;
+ fbinfo.height = h;
+ fbinfo.red_offset = 16;
+ fbinfo.red_length = 8;
+ fbinfo.green_offset = 8;
+ fbinfo.green_length = 8;
+ fbinfo.blue_offset = 0;
+ fbinfo.blue_length = 8;
+ fbinfo.alpha_offset = 24;
+ fbinfo.alpha_length = 8;
+ break;
+ default:
+ goto done;
+ }
+
+ /* write header */
+ if(!WriteFdExactly(fd, &fbinfo, sizeof(fbinfo))) goto done;
+
+ /* write data */
+ for(i = 0; i < fbinfo.size; i += bsize) {
+ bsize = sizeof(buf);
+ if (i + bsize > fbinfo.size)
+ bsize = fbinfo.size - i;
+ if(!ReadFdExactly(fd_screencap, buf, bsize)) goto done;
+ if(!WriteFdExactly(fd, buf, bsize)) goto done;
+ }
+
+done:
+ adb_close(fds[0]);
+
+ TEMP_FAILURE_RETRY(waitpid(pid, NULL, 0));
+pipefail:
+ adb_close(fd);
+}
diff --git a/adb/get_my_path_darwin.cpp b/adb/get_my_path_darwin.cpp
new file mode 100644
index 0000000..b0c962e
--- /dev/null
+++ b/adb/get_my_path_darwin.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2007 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.
+ */
+
+#import <Carbon/Carbon.h>
+#include <unistd.h>
+
+#include "adb.h"
+
+void get_my_path(char *s, size_t maxLen)
+{
+ CFBundleRef mainBundle = CFBundleGetMainBundle();
+ CFURLRef executableURL = CFBundleCopyExecutableURL(mainBundle);
+ CFStringRef executablePathString = CFURLCopyFileSystemPath(executableURL, kCFURLPOSIXPathStyle);
+ CFRelease(executableURL);
+
+ CFStringGetFileSystemRepresentation(executablePathString, s, maxLen);
+ CFRelease(executablePathString);
+}
+
diff --git a/adb/get_my_path_linux.cpp b/adb/get_my_path_linux.cpp
new file mode 100644
index 0000000..11c0b21
--- /dev/null
+++ b/adb/get_my_path_linux.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2007 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 <limits.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "adb.h"
+
+void get_my_path(char *exe, size_t maxLen)
+{
+ char proc[64];
+ snprintf(proc, sizeof proc, "/proc/%d/exe", getpid());
+ int err = readlink(proc, exe, maxLen - 1);
+ if(err > 0) {
+ exe[err] = '\0';
+ } else {
+ exe[0] = '\0';
+ }
+}
+
diff --git a/adb/get_my_path_windows.cpp b/adb/get_my_path_windows.cpp
new file mode 100644
index 0000000..9d23e1c
--- /dev/null
+++ b/adb/get_my_path_windows.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2007 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 <assert.h>
+#include <limits.h>
+#include <windows.h>
+
+#include "adb.h"
+
+void get_my_path(char *exe, size_t maxLen)
+{
+ char *r;
+
+ /* XXX: should be GetModuleFileNameA */
+ if (GetModuleFileName(NULL, exe, maxLen) > 0) {
+ r = strrchr(exe, '\\');
+ if (r != NULL)
+ *r = '\0';
+ } else {
+ exe[0] = '\0';
+ }
+}
+
diff --git a/adb/jdwp_service.cpp b/adb/jdwp_service.cpp
new file mode 100644
index 0000000..06e7780
--- /dev/null
+++ b/adb/jdwp_service.cpp
@@ -0,0 +1,753 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* implement the "debug-ports" and "track-debug-ports" device services */
+
+#define TRACE_TAG TRACE_JDWP
+
+#include "sysdeps.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "adb.h"
+
+/* here's how these things work.
+
+ when adbd starts, it creates a unix server socket
+ named @vm-debug-control (@ is a shortcut for "first byte is zero"
+ to use the private namespace instead of the file system)
+
+ when a new JDWP daemon thread starts in a new VM process, it creates
+ a connection to @vm-debug-control to announce its availability.
+
+
+ JDWP thread @vm-debug-control
+ | |
+ |-------------------------------> |
+ | hello I'm in process <pid> |
+ | |
+ | |
+
+ the connection is kept alive. it will be closed automatically if
+ the JDWP process terminates (this allows adbd to detect dead
+ processes).
+
+ adbd thus maintains a list of "active" JDWP processes. it can send
+ its content to clients through the "device:debug-ports" service,
+ or even updates through the "device:track-debug-ports" service.
+
+ when a debugger wants to connect, it simply runs the command
+ equivalent to "adb forward tcp:<hostport> jdwp:<pid>"
+
+ "jdwp:<pid>" is a new forward destination format used to target
+ a given JDWP process on the device. when sutch a request arrives,
+ adbd does the following:
+
+ - first, it calls socketpair() to create a pair of equivalent
+ sockets.
+
+ - it attaches the first socket in the pair to a local socket
+ which is itself attached to the transport's remote socket:
+
+
+ - it sends the file descriptor of the second socket directly
+ to the JDWP process with the help of sendmsg()
+
+
+ JDWP thread @vm-debug-control
+ | |
+ | <----------------------|
+ | OK, try this file descriptor |
+ | |
+ | |
+
+ then, the JDWP thread uses this new socket descriptor as its
+ pass-through connection to the debugger (and receives the
+ JDWP-Handshake message, answers to it, etc...)
+
+ this gives the following graphics:
+ ____________________________________
+ | |
+ | ADB Server (host) |
+ | |
+ Debugger <---> LocalSocket <----> RemoteSocket |
+ | ^^ |
+ |___________________________||_______|
+ ||
+ Transport ||
+ (TCP for emulator - USB for device) ||
+ ||
+ ___________________________||_______
+ | || |
+ | ADBD (device) || |
+ | VV |
+ JDWP <======> LocalSocket <----> RemoteSocket |
+ | |
+ |____________________________________|
+
+ due to the way adb works, this doesn't need a special socket
+ type or fancy handling of socket termination if either the debugger
+ or the JDWP process closes the connection.
+
+ THIS IS THE SIMPLEST IMPLEMENTATION I COULD FIND, IF YOU HAPPEN
+ TO HAVE A BETTER IDEA, LET ME KNOW - Digit
+
+**********************************************************************/
+
+/** JDWP PID List Support Code
+ ** for each JDWP process, we record its pid and its connected socket
+ **/
+
+#define MAX_OUT_FDS 4
+
+#if !ADB_HOST
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+struct JdwpProcess {
+ JdwpProcess* next;
+ JdwpProcess* prev;
+ int pid;
+ int socket;
+ fdevent* fde;
+
+ char in_buff[4]; /* input character to read PID */
+ int in_len; /* number from JDWP process */
+
+ int out_fds[MAX_OUT_FDS]; /* output array of file descriptors */
+ int out_count; /* to send to the JDWP process */
+};
+
+static JdwpProcess _jdwp_list;
+
+static int
+jdwp_process_list( char* buffer, int bufferlen )
+{
+ char* end = buffer + bufferlen;
+ char* p = buffer;
+ JdwpProcess* proc = _jdwp_list.next;
+
+ for ( ; proc != &_jdwp_list; proc = proc->next ) {
+ int len;
+
+ /* skip transient connections */
+ if (proc->pid < 0)
+ continue;
+
+ len = snprintf(p, end-p, "%d\n", proc->pid);
+ if (p + len >= end)
+ break;
+ p += len;
+ }
+ p[0] = 0;
+ return (p - buffer);
+}
+
+
+static int
+jdwp_process_list_msg( char* buffer, int bufferlen )
+{
+ char head[5];
+ int len = jdwp_process_list( buffer+4, bufferlen-4 );
+ snprintf(head, sizeof head, "%04x", len);
+ memcpy(buffer, head, 4);
+ return len + 4;
+}
+
+
+static void jdwp_process_list_updated(void);
+
+static void
+jdwp_process_free( JdwpProcess* proc )
+{
+ if (proc) {
+ int n;
+
+ proc->prev->next = proc->next;
+ proc->next->prev = proc->prev;
+
+ if (proc->socket >= 0) {
+ adb_shutdown(proc->socket);
+ adb_close(proc->socket);
+ proc->socket = -1;
+ }
+
+ if (proc->fde != NULL) {
+ fdevent_destroy(proc->fde);
+ proc->fde = NULL;
+ }
+ proc->pid = -1;
+
+ for (n = 0; n < proc->out_count; n++) {
+ adb_close(proc->out_fds[n]);
+ }
+ proc->out_count = 0;
+
+ free(proc);
+
+ jdwp_process_list_updated();
+ }
+}
+
+
+static void jdwp_process_event(int, unsigned, void*); /* forward */
+
+
+static JdwpProcess*
+jdwp_process_alloc( int socket )
+{
+ JdwpProcess* proc = reinterpret_cast<JdwpProcess*>(
+ calloc(1, sizeof(*proc)));
+
+ if (proc == NULL) {
+ D("not enough memory to create new JDWP process\n");
+ return NULL;
+ }
+
+ proc->socket = socket;
+ proc->pid = -1;
+ proc->next = proc;
+ proc->prev = proc;
+
+ proc->fde = fdevent_create( socket, jdwp_process_event, proc );
+ if (proc->fde == NULL) {
+ D("could not create fdevent for new JDWP process\n" );
+ free(proc);
+ return NULL;
+ }
+
+ proc->fde->state |= FDE_DONT_CLOSE;
+ proc->in_len = 0;
+ proc->out_count = 0;
+
+ /* append to list */
+ proc->next = &_jdwp_list;
+ proc->prev = proc->next->prev;
+
+ proc->prev->next = proc;
+ proc->next->prev = proc;
+
+ /* start by waiting for the PID */
+ fdevent_add(proc->fde, FDE_READ);
+
+ return proc;
+}
+
+
+static void
+jdwp_process_event( int socket, unsigned events, void* _proc )
+{
+ JdwpProcess* proc = reinterpret_cast<JdwpProcess*>(_proc);
+
+ if (events & FDE_READ) {
+ if (proc->pid < 0) {
+ /* read the PID as a 4-hexchar string */
+ char* p = proc->in_buff + proc->in_len;
+ int size = 4 - proc->in_len;
+ char temp[5];
+ while (size > 0) {
+ int len = recv( socket, p, size, 0 );
+ if (len < 0) {
+ if (errno == EINTR)
+ continue;
+ if (errno == EAGAIN)
+ return;
+ /* this can fail here if the JDWP process crashes very fast */
+ D("weird unknown JDWP process failure: %s\n",
+ strerror(errno));
+
+ goto CloseProcess;
+ }
+ if (len == 0) { /* end of stream ? */
+ D("weird end-of-stream from unknown JDWP process\n");
+ goto CloseProcess;
+ }
+ p += len;
+ proc->in_len += len;
+ size -= len;
+ }
+ /* we have read 4 characters, now decode the pid */
+ memcpy(temp, proc->in_buff, 4);
+ temp[4] = 0;
+
+ if (sscanf( temp, "%04x", &proc->pid ) != 1) {
+ D("could not decode JDWP %p PID number: '%s'\n", proc, temp);
+ goto CloseProcess;
+ }
+
+ /* all is well, keep reading to detect connection closure */
+ D("Adding pid %d to jdwp process list\n", proc->pid);
+ jdwp_process_list_updated();
+ }
+ else
+ {
+ /* the pid was read, if we get there it's probably because the connection
+ * was closed (e.g. the JDWP process exited or crashed) */
+ char buf[32];
+
+ for (;;) {
+ int len = recv(socket, buf, sizeof(buf), 0);
+
+ if (len <= 0) {
+ if (len < 0 && errno == EINTR)
+ continue;
+ if (len < 0 && errno == EAGAIN)
+ return;
+ else {
+ D("terminating JDWP %d connection: %s\n", proc->pid,
+ strerror(errno));
+ break;
+ }
+ }
+ else {
+ D( "ignoring unexpected JDWP %d control socket activity (%d bytes)\n",
+ proc->pid, len );
+ }
+ }
+
+ CloseProcess:
+ if (proc->pid >= 0)
+ D( "remove pid %d to jdwp process list\n", proc->pid );
+ jdwp_process_free(proc);
+ return;
+ }
+ }
+
+ if (events & FDE_WRITE) {
+ D("trying to write to JDWP pid controli (count=%d first=%d) %d\n",
+ proc->pid, proc->out_count, proc->out_fds[0]);
+ if (proc->out_count > 0) {
+ int fd = proc->out_fds[0];
+ int n, ret;
+ struct cmsghdr* cmsg;
+ struct msghdr msg;
+ struct iovec iov;
+ char dummy = '!';
+ char buffer[sizeof(struct cmsghdr) + sizeof(int)];
+ int flags;
+
+ iov.iov_base = &dummy;
+ iov.iov_len = 1;
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_flags = 0;
+ msg.msg_control = buffer;
+ msg.msg_controllen = sizeof(buffer);
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_len = msg.msg_controllen;
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ ((int*)CMSG_DATA(cmsg))[0] = fd;
+
+ flags = fcntl(proc->socket,F_GETFL,0);
+
+ if (flags == -1) {
+ D("failed to get cntl flags for socket %d: %s\n",
+ proc->pid, strerror(errno));
+ goto CloseProcess;
+
+ }
+
+ if (fcntl(proc->socket, F_SETFL, flags & ~O_NONBLOCK) == -1) {
+ D("failed to remove O_NONBLOCK flag for socket %d: %s\n",
+ proc->pid, strerror(errno));
+ goto CloseProcess;
+ }
+
+ for (;;) {
+ ret = sendmsg(proc->socket, &msg, 0);
+ if (ret >= 0) {
+ adb_close(fd);
+ break;
+ }
+ if (errno == EINTR)
+ continue;
+ D("sending new file descriptor to JDWP %d failed: %s\n",
+ proc->pid, strerror(errno));
+ goto CloseProcess;
+ }
+
+ D("sent file descriptor %d to JDWP process %d\n",
+ fd, proc->pid);
+
+ for (n = 1; n < proc->out_count; n++)
+ proc->out_fds[n-1] = proc->out_fds[n];
+
+ if (fcntl(proc->socket, F_SETFL, flags) == -1) {
+ D("failed to set O_NONBLOCK flag for socket %d: %s\n",
+ proc->pid, strerror(errno));
+ goto CloseProcess;
+ }
+
+ if (--proc->out_count == 0)
+ fdevent_del( proc->fde, FDE_WRITE );
+ }
+ }
+}
+
+
+int
+create_jdwp_connection_fd(int pid)
+{
+ JdwpProcess* proc = _jdwp_list.next;
+
+ D("looking for pid %d in JDWP process list\n", pid);
+ for ( ; proc != &_jdwp_list; proc = proc->next ) {
+ if (proc->pid == pid) {
+ goto FoundIt;
+ }
+ }
+ D("search failed !!\n");
+ return -1;
+
+FoundIt:
+ {
+ int fds[2];
+
+ if (proc->out_count >= MAX_OUT_FDS) {
+ D("%s: too many pending JDWP connection for pid %d\n",
+ __FUNCTION__, pid);
+ return -1;
+ }
+
+ if (adb_socketpair(fds) < 0) {
+ D("%s: socket pair creation failed: %s\n",
+ __FUNCTION__, strerror(errno));
+ return -1;
+ }
+ D("socketpair: (%d,%d)\n", fds[0], fds[1]);
+
+ proc->out_fds[ proc->out_count ] = fds[1];
+ if (++proc->out_count == 1)
+ fdevent_add( proc->fde, FDE_WRITE );
+
+ return fds[0];
+ }
+}
+
+/** VM DEBUG CONTROL SOCKET
+ **
+ ** we do implement a custom asocket to receive the data
+ **/
+
+/* name of the debug control Unix socket */
+#define JDWP_CONTROL_NAME "\0jdwp-control"
+#define JDWP_CONTROL_NAME_LEN (sizeof(JDWP_CONTROL_NAME)-1)
+
+struct JdwpControl {
+ int listen_socket;
+ fdevent* fde;
+};
+
+
+static void
+jdwp_control_event(int s, unsigned events, void* user);
+
+
+static int
+jdwp_control_init( JdwpControl* control,
+ const char* sockname,
+ int socknamelen )
+{
+ struct sockaddr_un addr;
+ socklen_t addrlen;
+ int s;
+ int maxpath = sizeof(addr.sun_path);
+ int pathlen = socknamelen;
+
+ if (pathlen >= maxpath) {
+ D( "vm debug control socket name too long (%d extra chars)\n",
+ pathlen+1-maxpath );
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ memcpy(addr.sun_path, sockname, socknamelen);
+
+ s = socket( AF_UNIX, SOCK_STREAM, 0 );
+ if (s < 0) {
+ D( "could not create vm debug control socket. %d: %s\n",
+ errno, strerror(errno));
+ return -1;
+ }
+
+ addrlen = (pathlen + sizeof(addr.sun_family));
+
+ if (bind(s, (struct sockaddr*)&addr, addrlen) < 0) {
+ D( "could not bind vm debug control socket: %d: %s\n",
+ errno, strerror(errno) );
+ adb_close(s);
+ return -1;
+ }
+
+ if ( listen(s, 4) < 0 ) {
+ D("listen failed in jdwp control socket: %d: %s\n",
+ errno, strerror(errno));
+ adb_close(s);
+ return -1;
+ }
+
+ control->listen_socket = s;
+
+ control->fde = fdevent_create(s, jdwp_control_event, control);
+ if (control->fde == NULL) {
+ D( "could not create fdevent for jdwp control socket\n" );
+ adb_close(s);
+ return -1;
+ }
+
+ /* only wait for incoming connections */
+ fdevent_add(control->fde, FDE_READ);
+ close_on_exec(s);
+
+ D("jdwp control socket started (%d)\n", control->listen_socket);
+ return 0;
+}
+
+
+static void
+jdwp_control_event( int s, unsigned events, void* _control )
+{
+ JdwpControl* control = (JdwpControl*) _control;
+
+ if (events & FDE_READ) {
+ struct sockaddr addr;
+ socklen_t addrlen = sizeof(addr);
+ int s = -1;
+ JdwpProcess* proc;
+
+ do {
+ s = adb_socket_accept( control->listen_socket, &addr, &addrlen );
+ if (s < 0) {
+ if (errno == EINTR)
+ continue;
+ if (errno == ECONNABORTED) {
+ /* oops, the JDWP process died really quick */
+ D("oops, the JDWP process died really quick\n");
+ return;
+ }
+ /* the socket is probably closed ? */
+ D( "weird accept() failed on jdwp control socket: %s\n",
+ strerror(errno) );
+ return;
+ }
+ }
+ while (s < 0);
+
+ proc = jdwp_process_alloc( s );
+ if (proc == NULL)
+ return;
+ }
+}
+
+
+static JdwpControl _jdwp_control;
+
+/** "jdwp" local service implementation
+ ** this simply returns the list of known JDWP process pids
+ **/
+
+struct JdwpSocket {
+ asocket socket;
+ int pass;
+};
+
+static void
+jdwp_socket_close( asocket* s )
+{
+ asocket* peer = s->peer;
+
+ remove_socket(s);
+
+ if (peer) {
+ peer->peer = NULL;
+ peer->close(peer);
+ }
+ free(s);
+}
+
+static int
+jdwp_socket_enqueue( asocket* s, apacket* p )
+{
+ /* you can't write to this asocket */
+ put_apacket(p);
+ s->peer->close(s->peer);
+ return -1;
+}
+
+
+static void
+jdwp_socket_ready( asocket* s )
+{
+ JdwpSocket* jdwp = (JdwpSocket*)s;
+ asocket* peer = jdwp->socket.peer;
+
+ /* on the first call, send the list of pids,
+ * on the second one, close the connection
+ */
+ if (jdwp->pass == 0) {
+ apacket* p = get_apacket();
+ p->len = jdwp_process_list((char*)p->data, s->get_max_payload());
+ peer->enqueue(peer, p);
+ jdwp->pass = 1;
+ }
+ else {
+ peer->close(peer);
+ }
+}
+
+asocket*
+create_jdwp_service_socket( void )
+{
+ JdwpSocket* s = reinterpret_cast<JdwpSocket*>(calloc(sizeof(*s), 1));
+
+ if (s == NULL)
+ return NULL;
+
+ install_local_socket(&s->socket);
+
+ s->socket.ready = jdwp_socket_ready;
+ s->socket.enqueue = jdwp_socket_enqueue;
+ s->socket.close = jdwp_socket_close;
+ s->pass = 0;
+
+ return &s->socket;
+}
+
+/** "track-jdwp" local service implementation
+ ** this periodically sends the list of known JDWP process pids
+ ** to the client...
+ **/
+
+struct JdwpTracker {
+ asocket socket;
+ JdwpTracker* next;
+ JdwpTracker* prev;
+ int need_update;
+};
+
+static JdwpTracker _jdwp_trackers_list;
+
+
+static void
+jdwp_process_list_updated(void)
+{
+ char buffer[1024];
+ int len;
+ JdwpTracker* t = _jdwp_trackers_list.next;
+
+ len = jdwp_process_list_msg(buffer, sizeof(buffer));
+
+ for ( ; t != &_jdwp_trackers_list; t = t->next ) {
+ apacket* p = get_apacket();
+ asocket* peer = t->socket.peer;
+ memcpy(p->data, buffer, len);
+ p->len = len;
+ peer->enqueue( peer, p );
+ }
+}
+
+static void
+jdwp_tracker_close( asocket* s )
+{
+ JdwpTracker* tracker = (JdwpTracker*) s;
+ asocket* peer = s->peer;
+
+ if (peer) {
+ peer->peer = NULL;
+ peer->close(peer);
+ }
+
+ remove_socket(s);
+
+ tracker->prev->next = tracker->next;
+ tracker->next->prev = tracker->prev;
+
+ free(s);
+}
+
+static void
+jdwp_tracker_ready( asocket* s )
+{
+ JdwpTracker* t = (JdwpTracker*) s;
+
+ if (t->need_update) {
+ apacket* p = get_apacket();
+ t->need_update = 0;
+ p->len = jdwp_process_list_msg((char*)p->data, s->get_max_payload());
+ s->peer->enqueue(s->peer, p);
+ }
+}
+
+static int
+jdwp_tracker_enqueue( asocket* s, apacket* p )
+{
+ /* you can't write to this socket */
+ put_apacket(p);
+ s->peer->close(s->peer);
+ return -1;
+}
+
+
+asocket*
+create_jdwp_tracker_service_socket( void )
+{
+ JdwpTracker* t = reinterpret_cast<JdwpTracker*>(calloc(sizeof(*t), 1));
+
+ if (t == NULL)
+ return NULL;
+
+ t->next = &_jdwp_trackers_list;
+ t->prev = t->next->prev;
+
+ t->next->prev = t;
+ t->prev->next = t;
+
+ install_local_socket(&t->socket);
+
+ t->socket.ready = jdwp_tracker_ready;
+ t->socket.enqueue = jdwp_tracker_enqueue;
+ t->socket.close = jdwp_tracker_close;
+ t->need_update = 1;
+
+ return &t->socket;
+}
+
+
+int
+init_jdwp(void)
+{
+ _jdwp_list.next = &_jdwp_list;
+ _jdwp_list.prev = &_jdwp_list;
+
+ _jdwp_trackers_list.next = &_jdwp_trackers_list;
+ _jdwp_trackers_list.prev = &_jdwp_trackers_list;
+
+ return jdwp_control_init( &_jdwp_control,
+ JDWP_CONTROL_NAME,
+ JDWP_CONTROL_NAME_LEN );
+}
+
+#endif /* !ADB_HOST */
diff --git a/adb/mutex_list.h b/adb/mutex_list.h
new file mode 100644
index 0000000..ff72751
--- /dev/null
+++ b/adb/mutex_list.h
@@ -0,0 +1,25 @@
+/* the list of mutexes used by adb */
+/* #ifndef __MUTEX_LIST_H
+ * Do not use an include-guard. This file is included once to declare the locks
+ * and once in win32 to actually do the runtime initialization.
+ */
+#ifndef ADB_MUTEX
+#error ADB_MUTEX not defined when including this file
+#endif
+ADB_MUTEX(socket_list_lock)
+ADB_MUTEX(transport_lock)
+#if ADB_HOST
+ADB_MUTEX(local_transports_lock)
+#endif
+ADB_MUTEX(usb_lock)
+
+// Sadly logging to /data/adb/adb-... is not thread safe.
+// After modifying adb.h::D() to count invocations:
+// DEBUG(jpa):0:Handling main()
+// DEBUG(jpa):1:[ usb_init - starting thread ]
+// (Oopsies, no :2:, and matching message is also gone.)
+// DEBUG(jpa):3:[ usb_thread - opening device ]
+// DEBUG(jpa):4:jdwp control socket started (10)
+ADB_MUTEX(D_lock)
+
+#undef ADB_MUTEX
diff --git a/adb/protocol.txt b/adb/protocol.txt
new file mode 100644
index 0000000..5c7c6ba
--- /dev/null
+++ b/adb/protocol.txt
@@ -0,0 +1,274 @@
+
+--- a replacement for aproto -------------------------------------------
+
+When it comes down to it, aproto's primary purpose is to forward
+various streams between the host computer and client device (in either
+direction).
+
+This replacement further simplifies the concept, reducing the protocol
+to an extremely straightforward model optimized to accomplish the
+forwarding of these streams and removing additional state or
+complexity.
+
+The host side becomes a simple comms bridge with no "UI", which will
+be used by either commandline or interactive tools to communicate with
+a device or emulator that is connected to the bridge.
+
+The protocol is designed to be straightforward and well-defined enough
+that if it needs to be reimplemented in another environment (Java
+perhaps), there should not problems ensuring perfect interoperability.
+
+The protocol discards the layering aproto has and should allow the
+implementation to be much more robust.
+
+
+--- protocol overview and basics ---------------------------------------
+
+The transport layer deals in "messages", which consist of a 24 byte
+header followed (optionally) by a payload. The header consists of 6
+32 bit words which are sent across the wire in little endian format.
+
+struct message {
+ unsigned command; /* command identifier constant */
+ unsigned arg0; /* first argument */
+ unsigned arg1; /* second argument */
+ unsigned data_length; /* length of payload (0 is allowed) */
+ unsigned data_crc32; /* crc32 of data payload */
+ unsigned magic; /* command ^ 0xffffffff */
+};
+
+Receipt of an invalid message header, corrupt message payload, or an
+unrecognized command MUST result in the closing of the remote
+connection. The protocol depends on shared state and any break in the
+message stream will result in state getting out of sync.
+
+The following sections describe the six defined message types in
+detail. Their format is COMMAND(arg0, arg1, payload) where the payload
+is represented by a quoted string or an empty string if none should be
+sent.
+
+The identifiers "local-id" and "remote-id" are always relative to the
+*sender* of the message, so for a receiver, the meanings are effectively
+reversed.
+
+
+
+--- CONNECT(version, maxdata, "system-identity-string") ----------------
+
+The CONNECT message establishes the presence of a remote system.
+The version is used to ensure protocol compatibility and maxdata
+declares the maximum message body size that the remote system
+is willing to accept.
+
+Currently, version=0x01000000 and maxdata=256*1024. Older versions of adb
+hard-coded maxdata=4096, so CONNECT and AUTH packets sent to a device must not
+be larger than that because they're sent before the CONNECT from the device
+that tells the adb server what maxdata the device can support.
+
+Both sides send a CONNECT message when the connection between them is
+established. Until a CONNECT message is received no other messages may
+be sent. Any messages received before a CONNECT message MUST be ignored.
+
+If a CONNECT message is received with an unknown version or insufficiently
+large maxdata value, the connection with the other side must be closed.
+
+The system identity string should be "<systemtype>:<serialno>:<banner>"
+where systemtype is "bootloader", "device", or "host", serialno is some
+kind of unique ID (or empty), and banner is a human-readable version
+or identifier string. The banner is used to transmit useful properties.
+
+
+--- AUTH(type, 0, "data") ----------------------------------------------
+
+The AUTH message informs the recipient that authentication is required to
+connect to the sender. If type is TOKEN(1), data is a random token that
+the recipient can sign with a private key. The recipient replies with an
+AUTH packet where type is SIGNATURE(2) and data is the signature. If the
+signature verification succeeds, the sender replies with a CONNECT packet.
+
+If the signature verification fails, the sender replies with a new AUTH
+packet and a new random token, so that the recipient can retry signing
+with a different private key.
+
+Once the recipient has tried all its private keys, it can reply with an
+AUTH packet where type is RSAPUBLICKEY(3) and data is the public key. If
+possible, an on-screen confirmation may be displayed for the user to
+confirm they want to install the public key on the device.
+
+
+--- OPEN(local-id, 0, "destination") -----------------------------------
+
+The OPEN message informs the recipient that the sender has a stream
+identified by local-id that it wishes to connect to the named
+destination in the message payload. The local-id may not be zero.
+
+The OPEN message MUST result in either a READY message indicating that
+the connection has been established (and identifying the other end) or
+a CLOSE message, indicating failure. An OPEN message also implies
+a READY message sent at the same time.
+
+Common destination naming conventions include:
+
+* "tcp:<host>:<port>" - host may be omitted to indicate localhost
+* "udp:<host>:<port>" - host may be omitted to indicate localhost
+* "local-dgram:<identifier>"
+* "local-stream:<identifier>"
+* "shell" - local shell service
+* "upload" - service for pushing files across (like aproto's /sync)
+* "fs-bridge" - FUSE protocol filesystem bridge
+
+
+--- READY(local-id, remote-id, "") -------------------------------------
+
+The READY message informs the recipient that the sender's stream
+identified by local-id is ready for write messages and that it is
+connected to the recipient's stream identified by remote-id.
+
+Neither the local-id nor the remote-id may be zero.
+
+A READY message containing a remote-id which does not map to an open
+stream on the recipient's side is ignored. The stream may have been
+closed while this message was in-flight.
+
+The local-id is ignored on all but the first READY message (where it
+is used to establish the connection). Nonetheless, the local-id MUST
+not change on later READY messages sent to the same stream.
+
+
+
+--- WRITE(local-id, remote-id, "data") ---------------------------------
+
+The WRITE message sends data to the recipient's stream identified by
+remote-id. The payload MUST be <= maxdata in length.
+
+A WRITE message containing a remote-id which does not map to an open
+stream on the recipient's side is ignored. The stream may have been
+closed while this message was in-flight.
+
+A WRITE message may not be sent until a READY message is received.
+Once a WRITE message is sent, an additional WRITE message may not be
+sent until another READY message has been received. Recipients of
+a WRITE message that is in violation of this requirement will CLOSE
+the connection.
+
+
+--- CLOSE(local-id, remote-id, "") -------------------------------------
+
+The CLOSE message informs recipient that the connection between the
+sender's stream (local-id) and the recipient's stream (remote-id) is
+broken. The remote-id MUST not be zero, but the local-id MAY be zero
+if this CLOSE indicates a failed OPEN.
+
+A CLOSE message containing a remote-id which does not map to an open
+stream on the recipient's side is ignored. The stream may have
+already been closed by the recipient while this message was in-flight.
+
+The recipient should not respond to a CLOSE message in any way. The
+recipient should cancel pending WRITEs or CLOSEs, but this is not a
+requirement, since they will be ignored.
+
+
+--- SYNC(online, sequence, "") -----------------------------------------
+
+The SYNC message is used by the io pump to make sure that stale
+outbound messages are discarded when the connection to the remote side
+is broken. It is only used internally to the bridge and never valid
+to send across the wire.
+
+* when the connection to the remote side goes offline, the io pump
+ sends a SYNC(0, 0) and starts discarding all messages
+* when the connection to the remote side is established, the io pump
+ sends a SYNC(1, token) and continues to discard messages
+* when the io pump receives a matching SYNC(1, token), it once again
+ starts accepting messages to forward to the remote side
+
+
+--- message command constants ------------------------------------------
+
+#define A_SYNC 0x434e5953
+#define A_CNXN 0x4e584e43
+#define A_AUTH 0x48545541
+#define A_OPEN 0x4e45504f
+#define A_OKAY 0x59414b4f
+#define A_CLSE 0x45534c43
+#define A_WRTE 0x45545257
+
+
+
+--- implementation details ---------------------------------------------
+
+The core of the bridge program will use three threads. One thread
+will be a select/epoll loop to handle io between various inbound and
+outbound connections and the connection to the remote side.
+
+The remote side connection will be implemented as two threads (one for
+reading, one for writing) and a datagram socketpair to provide the
+channel between the main select/epoll thread and the remote connection
+threadpair. The reason for this is that for usb connections, the
+kernel interface on linux and osx does not allow you to do meaningful
+nonblocking IO.
+
+The endian swapping for the message headers will happen (as needed) in
+the remote connection threadpair and that the rest of the program will
+always treat message header values as native-endian.
+
+The bridge program will be able to have a number of mini-servers
+compiled in. They will be published under known names (examples
+"shell", "fs-bridge", etc) and upon receiving an OPEN() to such a
+service, the bridge program will create a stream socketpair and spawn
+a thread or subprocess to handle the io.
+
+
+--- simplified / embedded implementation -------------------------------
+
+For limited environments, like the bootloader, it is allowable to
+support a smaller, fixed number of channels using pre-assigned channel
+ID numbers such that only one stream may be connected to a bootloader
+endpoint at any given time. The protocol remains unchanged, but the
+"embedded" version of it is less dynamic.
+
+The bootloader will support two streams. A "bootloader:debug" stream,
+which may be opened to get debug messages from the bootloader and a
+"bootloader:control", stream which will support the set of basic
+bootloader commands.
+
+Example command stream dialogues:
+ "flash_kernel,2515049,........\n" "okay\n"
+ "flash_ramdisk,5038,........\n" "fail,flash write error\n"
+ "bogus_command......" <CLOSE>
+
+
+--- future expansion ---------------------------------------------------
+
+I plan on providing either a message or a special control stream so that
+the client device could ask the host computer to setup inbound socket
+translations on the fly on behalf of the client device.
+
+
+The initial design does handshaking to provide flow control, with a
+message flow that looks like:
+
+ >OPEN <READY >WRITE <READY >WRITE <READY >WRITE <CLOSE
+
+The far side may choose to issue the READY message as soon as it receives
+a WRITE or it may defer the READY until the write to the local stream
+succeeds. A future version may want to do some level of windowing where
+multiple WRITEs may be sent without requiring individual READY acks.
+
+------------------------------------------------------------------------
+
+--- smartsockets -------------------------------------------------------
+
+Port 5037 is used for smart sockets which allow a client on the host
+side to request access to a service in the host adb daemon or in the
+remote (device) daemon. The service is requested by ascii name,
+preceeded by a 4 digit hex length. Upon successful connection an
+"OKAY" response is sent, otherwise a "FAIL" message is returned. Once
+connected the client is talking to that (remote or local) service.
+
+client: <hex4> <service-name>
+server: "OKAY"
+
+client: <hex4> <service-name>
+server: "FAIL" <hex4> <reason>
+
diff --git a/adb/qemu_tracing.cpp b/adb/qemu_tracing.cpp
new file mode 100644
index 0000000..f31eae8
--- /dev/null
+++ b/adb/qemu_tracing.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Implements ADB tracing inside the emulator.
+ */
+
+#include <stdarg.h>
+
+#include "sysdeps.h"
+#include "qemu_tracing.h"
+
+/*
+ * Redefine open and write for qemu_pipe.h that contains inlined references
+ * to those routines. We will redifine them back after qemu_pipe.h inclusion.
+ */
+
+#undef open
+#undef write
+#define open adb_open
+#define write adb_write
+#include <hardware/qemu_pipe.h>
+#undef open
+#undef write
+#define open ___xxx_open
+#define write ___xxx_write
+
+/* A handle to adb-debug qemud service in the emulator. */
+int adb_debug_qemu = -1;
+
+/* Initializes connection with the adb-debug qemud service in the emulator. */
+int adb_qemu_trace_init(void)
+{
+ char con_name[32];
+
+ if (adb_debug_qemu >= 0) {
+ return 0;
+ }
+
+ /* adb debugging QEMUD service connection request. */
+ snprintf(con_name, sizeof(con_name), "qemud:adb-debug");
+ adb_debug_qemu = qemu_pipe_open(con_name);
+ return (adb_debug_qemu >= 0) ? 0 : -1;
+}
+
+void adb_qemu_trace(const char* fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ char msg[1024];
+
+ if (adb_debug_qemu >= 0) {
+ vsnprintf(msg, sizeof(msg), fmt, args);
+ adb_write(adb_debug_qemu, msg, strlen(msg));
+ }
+}
diff --git a/adb/qemu_tracing.h b/adb/qemu_tracing.h
new file mode 100644
index 0000000..ff42d4f
--- /dev/null
+++ b/adb/qemu_tracing.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Implements ADB tracing inside the emulator.
+ */
+
+#ifndef __QEMU_TRACING_H
+#define __QEMU_TRACING_H
+
+/* Initializes connection with the adb-debug qemud service in the emulator. */
+int adb_qemu_trace_init(void);
+void adb_qemu_trace(const char* fmt, ...);
+
+#endif /* __QEMU_TRACING_H */
diff --git a/adb/remount_service.cpp b/adb/remount_service.cpp
new file mode 100644
index 0000000..2893263
--- /dev/null
+++ b/adb/remount_service.cpp
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TRACE_TAG TRACE_ADB
+
+#include "sysdeps.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <mntent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <unistd.h>
+
+#include <string>
+
+#include "adb.h"
+#include "adb_io.h"
+#include "adb_utils.h"
+#include "cutils/properties.h"
+#include "fs_mgr.h"
+
+const std::string kFstab_Prefix = "/fstab.";
+
+// Returns the device used to mount a directory in /proc/mounts.
+static std::string find_proc_mount(const char* dir) {
+ std::unique_ptr<FILE, int(*)(FILE*)> fp(setmntent("/proc/mounts", "r"), endmntent);
+ if (!fp) {
+ return "";
+ }
+
+ mntent* e;
+ while ((e = getmntent(fp.get())) != nullptr) {
+ if (strcmp(dir, e->mnt_dir) == 0) {
+ return e->mnt_fsname;
+ }
+ }
+ return "";
+}
+
+// Returns the device used to mount a directory in the fstab.
+static std::string find_fstab_mount(const char* dir) {
+ char propbuf[PROPERTY_VALUE_MAX];
+
+ property_get("ro.hardware", propbuf, "");
+ std::string fstab_filename = kFstab_Prefix + propbuf;
+ struct fstab* fstab = fs_mgr_read_fstab(fstab_filename.c_str());
+ struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab, dir);
+ std::string dev = rec ? std::string(rec->blk_device) : "";
+ fs_mgr_free_fstab(fstab);
+ return dev;
+}
+
+// The proc entry for / is full of lies, so check fstab instead.
+// /proc/mounts lists rootfs and /dev/root, neither of which is what we want.
+static std::string find_mount(const char* dir) {
+ if (strcmp(dir, "/") == 0) {
+ return find_fstab_mount(dir);
+ } else {
+ return find_proc_mount(dir);
+ }
+}
+
+bool make_block_device_writable(const std::string& dev) {
+ int fd = unix_open(dev.c_str(), O_RDONLY | O_CLOEXEC);
+ if (fd == -1) {
+ return false;
+ }
+
+ int OFF = 0;
+ bool result = (ioctl(fd, BLKROSET, &OFF) != -1);
+ unix_close(fd);
+ return result;
+}
+
+static bool remount_partition(int fd, const char* dir) {
+ if (!directory_exists(dir)) {
+ return true;
+ }
+ std::string dev = find_mount(dir);
+ if (dev.empty()) {
+ return true;
+ }
+ if (!make_block_device_writable(dev)) {
+ WriteFdFmt(fd, "remount of %s failed; couldn't make block device %s writable: %s\n",
+ dir, dev.c_str(), strerror(errno));
+ return false;
+ }
+ if (mount(dev.c_str(), dir, "none", MS_REMOUNT, nullptr) == -1) {
+ WriteFdFmt(fd, "remount of %s failed: %s\n", dir, strerror(errno));
+ return false;
+ }
+ return true;
+}
+
+void remount_service(int fd, void* cookie) {
+ if (getuid() != 0) {
+ WriteFdExactly(fd, "Not running as root. Try \"adb root\" first.\n");
+ adb_close(fd);
+ return;
+ }
+
+ char prop_buf[PROPERTY_VALUE_MAX];
+ property_get("partition.system.verified", prop_buf, "");
+ bool system_verified = (strlen(prop_buf) > 0);
+
+ property_get("partition.vendor.verified", prop_buf, "");
+ bool vendor_verified = (strlen(prop_buf) > 0);
+
+ if (system_verified || vendor_verified) {
+ // Allow remount but warn of likely bad effects
+ bool both = system_verified && vendor_verified;
+ WriteFdFmt(fd,
+ "dm_verity is enabled on the %s%s%s partition%s.\n",
+ system_verified ? "system" : "",
+ both ? " and " : "",
+ vendor_verified ? "vendor" : "",
+ both ? "s" : "");
+ WriteFdExactly(fd,
+ "Use \"adb disable-verity\" to disable verity.\n"
+ "If you do not, remount may succeed, however, you will still "
+ "not be able to write to these volumes.\n");
+ }
+
+ bool success = true;
+ property_get("ro.build.system_root_image", prop_buf, "");
+ bool system_root = !strcmp(prop_buf, "true");
+ if (system_root) {
+ success &= remount_partition(fd, "/");
+ } else {
+ success &= remount_partition(fd, "/system");
+ }
+ success &= remount_partition(fd, "/vendor");
+ success &= remount_partition(fd, "/oem");
+
+ WriteFdExactly(fd, success ? "remount succeeded\n" : "remount failed\n");
+
+ adb_close(fd);
+}
diff --git a/adb/remount_service.h b/adb/remount_service.h
new file mode 100644
index 0000000..7bda1be
--- /dev/null
+++ b/adb/remount_service.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _REMOUNT_SERVICE_H_
+#define _REMOUNT_SERVICE_H_
+
+#include <string>
+
+bool make_block_device_writable(const std::string&);
+void remount_service(int, void*);
+
+#endif
diff --git a/adb/services.cpp b/adb/services.cpp
new file mode 100644
index 0000000..82efb1c
--- /dev/null
+++ b/adb/services.cpp
@@ -0,0 +1,689 @@
+/*
+ * Copyright (C) 2007 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 TRACE_TAG TRACE_SERVICES
+
+#include "sysdeps.h"
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if !ADB_HOST
+#include <pty.h>
+#include <termios.h>
+#endif
+
+#ifndef _WIN32
+#include <netdb.h>
+#include <netinet/in.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#endif
+
+#include <base/file.h>
+#include <base/stringprintf.h>
+#include <base/strings.h>
+#include <cutils/sockets.h>
+
+#if !ADB_HOST
+#include "cutils/android_reboot.h"
+#include "cutils/properties.h"
+#endif
+
+#include "adb.h"
+#include "adb_io.h"
+#include "adb_utils.h"
+#include "file_sync_service.h"
+#include "remount_service.h"
+#include "transport.h"
+
+struct stinfo {
+ void (*func)(int fd, void *cookie);
+ int fd;
+ void *cookie;
+};
+
+
+void *service_bootstrap_func(void *x)
+{
+ stinfo* sti = reinterpret_cast<stinfo*>(x);
+ sti->func(sti->fd, sti->cookie);
+ free(sti);
+ return 0;
+}
+
+#if !ADB_HOST
+
+void restart_root_service(int fd, void *cookie) {
+ if (getuid() == 0) {
+ WriteFdExactly(fd, "adbd is already running as root\n");
+ adb_close(fd);
+ } else {
+ char value[PROPERTY_VALUE_MAX];
+ property_get("ro.debuggable", value, "");
+ if (strcmp(value, "1") != 0) {
+ WriteFdExactly(fd, "adbd cannot run as root in production builds\n");
+ adb_close(fd);
+ return;
+ }
+
+ property_set("service.adb.root", "1");
+ WriteFdExactly(fd, "restarting adbd as root\n");
+ adb_close(fd);
+ }
+}
+
+void restart_unroot_service(int fd, void *cookie) {
+ if (getuid() != 0) {
+ WriteFdExactly(fd, "adbd not running as root\n");
+ adb_close(fd);
+ } else {
+ property_set("service.adb.root", "0");
+ WriteFdExactly(fd, "restarting adbd as non root\n");
+ adb_close(fd);
+ }
+}
+
+void restart_tcp_service(int fd, void *cookie) {
+ int port = (int) (uintptr_t) cookie;
+ if (port <= 0) {
+ WriteFdFmt(fd, "invalid port %d\n", port);
+ adb_close(fd);
+ return;
+ }
+
+ char value[PROPERTY_VALUE_MAX];
+ snprintf(value, sizeof(value), "%d", port);
+ property_set("service.adb.tcp.port", value);
+ WriteFdFmt(fd, "restarting in TCP mode port: %d\n", port);
+ adb_close(fd);
+}
+
+void restart_usb_service(int fd, void *cookie) {
+ property_set("service.adb.tcp.port", "0");
+ WriteFdExactly(fd, "restarting in USB mode\n");
+ adb_close(fd);
+}
+
+static bool reboot_service_impl(int fd, const char* arg) {
+ const char* reboot_arg = arg;
+ bool auto_reboot = false;
+
+ if (strcmp(reboot_arg, "sideload-auto-reboot") == 0) {
+ auto_reboot = true;
+ reboot_arg = "sideload";
+ }
+
+ // It reboots into sideload mode by setting "--sideload" or "--sideload_auto_reboot"
+ // in the command file.
+ if (strcmp(reboot_arg, "sideload") == 0) {
+ if (getuid() != 0) {
+ WriteFdExactly(fd, "'adb root' is required for 'adb reboot sideload'.\n");
+ return false;
+ }
+
+ const char* const recovery_dir = "/cache/recovery";
+ const char* const command_file = "/cache/recovery/command";
+ // Ensure /cache/recovery exists.
+ if (adb_mkdir(recovery_dir, 0770) == -1 && errno != EEXIST) {
+ D("Failed to create directory '%s': %s\n", recovery_dir, strerror(errno));
+ return false;
+ }
+
+ bool write_status = android::base::WriteStringToFile(
+ auto_reboot ? "--sideload_auto_reboot" : "--sideload", command_file);
+ if (!write_status) {
+ return false;
+ }
+
+ reboot_arg = "recovery";
+ }
+
+ sync();
+
+ char property_val[PROPERTY_VALUE_MAX];
+ int ret = snprintf(property_val, sizeof(property_val), "reboot,%s", reboot_arg);
+ if (ret >= static_cast<int>(sizeof(property_val))) {
+ WriteFdFmt(fd, "reboot string too long: %d\n", ret);
+ return false;
+ }
+
+ ret = property_set(ANDROID_RB_PROPERTY, property_val);
+ if (ret < 0) {
+ WriteFdFmt(fd, "reboot failed: %d\n", ret);
+ return false;
+ }
+
+ return true;
+}
+
+void reboot_service(int fd, void* arg)
+{
+ if (reboot_service_impl(fd, static_cast<const char*>(arg))) {
+ // Don't return early. Give the reboot command time to take effect
+ // to avoid messing up scripts which do "adb reboot && adb wait-for-device"
+ while (true) {
+ pause();
+ }
+ }
+
+ free(arg);
+ adb_close(fd);
+}
+
+void reverse_service(int fd, void* arg)
+{
+ const char* command = reinterpret_cast<const char*>(arg);
+
+ if (handle_forward_request(command, kTransportAny, NULL, fd) < 0) {
+ SendFail(fd, "not a reverse forwarding command");
+ }
+ free(arg);
+ adb_close(fd);
+}
+
+#endif
+
+static int create_service_thread(void (*func)(int, void *), void *cookie)
+{
+ int s[2];
+ if (adb_socketpair(s)) {
+ printf("cannot create service socket pair\n");
+ return -1;
+ }
+ D("socketpair: (%d,%d)\n", s[0], s[1]);
+
+ stinfo* sti = reinterpret_cast<stinfo*>(malloc(sizeof(stinfo)));
+ if (sti == nullptr) {
+ fatal("cannot allocate stinfo");
+ }
+ sti->func = func;
+ sti->cookie = cookie;
+ sti->fd = s[1];
+
+ if (!adb_thread_create(service_bootstrap_func, sti)) {
+ free(sti);
+ adb_close(s[0]);
+ adb_close(s[1]);
+ printf("cannot create service thread\n");
+ return -1;
+ }
+
+ D("service thread started, %d:%d\n",s[0], s[1]);
+ return s[0];
+}
+
+#if !ADB_HOST
+
+static void init_subproc_child()
+{
+ setsid();
+
+ // Set OOM score adjustment to prevent killing
+ int fd = adb_open("/proc/self/oom_score_adj", O_WRONLY | O_CLOEXEC);
+ if (fd >= 0) {
+ adb_write(fd, "0", 1);
+ adb_close(fd);
+ } else {
+ D("adb: unable to update oom_score_adj\n");
+ }
+}
+
+#if !ADB_HOST
+static int create_subproc_pty(const char* cmd, const char* arg0,
+ const char* arg1, pid_t* pid) {
+ D("create_subproc_pty(cmd=%s, arg0=%s, arg1=%s)\n", cmd, arg0, arg1);
+ char pts_name[PATH_MAX];
+ int ptm;
+ *pid = forkpty(&ptm, pts_name, nullptr, nullptr);
+ if (*pid == -1) {
+ printf("- fork failed: %s -\n", strerror(errno));
+ unix_close(ptm);
+ return -1;
+ }
+
+ if (*pid == 0) {
+ init_subproc_child();
+
+ int pts = unix_open(pts_name, O_RDWR | O_CLOEXEC);
+ if (pts == -1) {
+ fprintf(stderr, "child failed to open pseudo-term slave %s: %s\n",
+ pts_name, strerror(errno));
+ unix_close(ptm);
+ exit(-1);
+ }
+
+ // arg0 is "-c" in batch mode and "-" in interactive mode.
+ if (strcmp(arg0, "-c") == 0) {
+ termios tattr;
+ if (tcgetattr(pts, &tattr) == -1) {
+ fprintf(stderr, "tcgetattr failed: %s\n", strerror(errno));
+ unix_close(pts);
+ unix_close(ptm);
+ exit(-1);
+ }
+
+ cfmakeraw(&tattr);
+ if (tcsetattr(pts, TCSADRAIN, &tattr) == -1) {
+ fprintf(stderr, "tcsetattr failed: %s\n", strerror(errno));
+ unix_close(pts);
+ unix_close(ptm);
+ exit(-1);
+ }
+ }
+
+ dup2(pts, STDIN_FILENO);
+ dup2(pts, STDOUT_FILENO);
+ dup2(pts, STDERR_FILENO);
+
+ unix_close(pts);
+ unix_close(ptm);
+
+ execl(cmd, cmd, arg0, arg1, nullptr);
+ fprintf(stderr, "- exec '%s' failed: %s (%d) -\n",
+ cmd, strerror(errno), errno);
+ exit(-1);
+ } else {
+ return ptm;
+ }
+}
+#endif // !ADB_HOST
+
+static int create_subproc_raw(const char *cmd, const char *arg0, const char *arg1, pid_t *pid)
+{
+ D("create_subproc_raw(cmd=%s, arg0=%s, arg1=%s)\n", cmd, arg0, arg1);
+#if defined(_WIN32)
+ fprintf(stderr, "error: create_subproc_raw not implemented on Win32 (%s %s %s)\n", cmd, arg0, arg1);
+ return -1;
+#else
+
+ // 0 is parent socket, 1 is child socket
+ int sv[2];
+ if (adb_socketpair(sv) < 0) {
+ printf("[ cannot create socket pair - %s ]\n", strerror(errno));
+ return -1;
+ }
+ D("socketpair: (%d,%d)\n", sv[0], sv[1]);
+
+ *pid = fork();
+ if (*pid < 0) {
+ printf("- fork failed: %s -\n", strerror(errno));
+ adb_close(sv[0]);
+ adb_close(sv[1]);
+ return -1;
+ }
+
+ if (*pid == 0) {
+ adb_close(sv[0]);
+ init_subproc_child();
+
+ dup2(sv[1], STDIN_FILENO);
+ dup2(sv[1], STDOUT_FILENO);
+ dup2(sv[1], STDERR_FILENO);
+
+ adb_close(sv[1]);
+
+ execl(cmd, cmd, arg0, arg1, NULL);
+ fprintf(stderr, "- exec '%s' failed: %s (%d) -\n",
+ cmd, strerror(errno), errno);
+ exit(-1);
+ } else {
+ adb_close(sv[1]);
+ return sv[0];
+ }
+#endif /* !defined(_WIN32) */
+}
+#endif /* !ABD_HOST */
+
+#if ADB_HOST
+#define SHELL_COMMAND "/bin/sh"
+#else
+#define SHELL_COMMAND "/system/bin/sh"
+#endif
+
+#if !ADB_HOST
+static void subproc_waiter_service(int fd, void *cookie)
+{
+ pid_t pid = (pid_t) (uintptr_t) cookie;
+
+ D("entered. fd=%d of pid=%d\n", fd, pid);
+ while (true) {
+ int status;
+ pid_t p = waitpid(pid, &status, 0);
+ if (p == pid) {
+ D("fd=%d, post waitpid(pid=%d) status=%04x\n", fd, p, status);
+ if (WIFSIGNALED(status)) {
+ D("*** Killed by signal %d\n", WTERMSIG(status));
+ break;
+ } else if (!WIFEXITED(status)) {
+ D("*** Didn't exit!!. status %d\n", status);
+ break;
+ } else if (WEXITSTATUS(status) >= 0) {
+ D("*** Exit code %d\n", WEXITSTATUS(status));
+ break;
+ }
+ }
+ }
+ D("shell exited fd=%d of pid=%d err=%d\n", fd, pid, errno);
+ if (SHELL_EXIT_NOTIFY_FD >=0) {
+ int res;
+ res = WriteFdExactly(SHELL_EXIT_NOTIFY_FD, &fd, sizeof(fd)) ? 0 : -1;
+ D("notified shell exit via fd=%d for pid=%d res=%d errno=%d\n",
+ SHELL_EXIT_NOTIFY_FD, pid, res, errno);
+ }
+}
+
+static int create_subproc_thread(const char *name, bool pty = false) {
+ const char *arg0, *arg1;
+ if (name == 0 || *name == 0) {
+ arg0 = "-"; arg1 = 0;
+ } else {
+ arg0 = "-c"; arg1 = name;
+ }
+
+ pid_t pid = -1;
+ int ret_fd;
+ if (pty) {
+ ret_fd = create_subproc_pty(SHELL_COMMAND, arg0, arg1, &pid);
+ } else {
+ ret_fd = create_subproc_raw(SHELL_COMMAND, arg0, arg1, &pid);
+ }
+ D("create_subproc ret_fd=%d pid=%d\n", ret_fd, pid);
+
+ stinfo* sti = reinterpret_cast<stinfo*>(malloc(sizeof(stinfo)));
+ if(sti == 0) fatal("cannot allocate stinfo");
+ sti->func = subproc_waiter_service;
+ sti->cookie = (void*) (uintptr_t) pid;
+ sti->fd = ret_fd;
+
+ if (!adb_thread_create(service_bootstrap_func, sti)) {
+ free(sti);
+ adb_close(ret_fd);
+ fprintf(stderr, "cannot create service thread\n");
+ return -1;
+ }
+
+ D("service thread started, fd=%d pid=%d\n", ret_fd, pid);
+ return ret_fd;
+}
+#endif
+
+int service_to_fd(const char *name)
+{
+ int ret = -1;
+
+ if(!strncmp(name, "tcp:", 4)) {
+ int port = atoi(name + 4);
+ name = strchr(name + 4, ':');
+ if(name == 0) {
+ ret = socket_loopback_client(port, SOCK_STREAM);
+ if (ret >= 0)
+ disable_tcp_nagle(ret);
+ } else {
+#if ADB_HOST
+ std::string error;
+ ret = network_connect(name + 1, port, SOCK_STREAM, 0, &error);
+#else
+ return -1;
+#endif
+ }
+#ifndef HAVE_WINSOCK /* winsock doesn't implement unix domain sockets */
+ } else if(!strncmp(name, "local:", 6)) {
+ ret = socket_local_client(name + 6,
+ ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
+ } else if(!strncmp(name, "localreserved:", 14)) {
+ ret = socket_local_client(name + 14,
+ ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
+ } else if(!strncmp(name, "localabstract:", 14)) {
+ ret = socket_local_client(name + 14,
+ ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
+ } else if(!strncmp(name, "localfilesystem:", 16)) {
+ ret = socket_local_client(name + 16,
+ ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM);
+#endif
+#if !ADB_HOST
+ } else if(!strncmp("dev:", name, 4)) {
+ ret = unix_open(name + 4, O_RDWR | O_CLOEXEC);
+ } else if(!strncmp(name, "framebuffer:", 12)) {
+ ret = create_service_thread(framebuffer_service, 0);
+ } else if (!strncmp(name, "jdwp:", 5)) {
+ ret = create_jdwp_connection_fd(atoi(name+5));
+ } else if(!HOST && !strncmp(name, "shell:", 6)) {
+ ret = create_subproc_thread(name + 6, true);
+ } else if(!HOST && !strncmp(name, "exec:", 5)) {
+ ret = create_subproc_thread(name + 5);
+ } else if(!strncmp(name, "sync:", 5)) {
+ ret = create_service_thread(file_sync_service, NULL);
+ } else if(!strncmp(name, "remount:", 8)) {
+ ret = create_service_thread(remount_service, NULL);
+ } else if(!strncmp(name, "reboot:", 7)) {
+ void* arg = strdup(name + 7);
+ if (arg == NULL) return -1;
+ ret = create_service_thread(reboot_service, arg);
+ } else if(!strncmp(name, "root:", 5)) {
+ ret = create_service_thread(restart_root_service, NULL);
+ } else if(!strncmp(name, "unroot:", 7)) {
+ ret = create_service_thread(restart_unroot_service, NULL);
+ } else if(!strncmp(name, "backup:", 7)) {
+ ret = create_subproc_thread(android::base::StringPrintf("/system/bin/bu backup %s",
+ (name + 7)).c_str());
+ } else if(!strncmp(name, "restore:", 8)) {
+ ret = create_subproc_thread("/system/bin/bu restore");
+ } else if(!strncmp(name, "tcpip:", 6)) {
+ int port;
+ if (sscanf(name + 6, "%d", &port) != 1) {
+ return -1;
+ }
+ ret = create_service_thread(restart_tcp_service, (void *) (uintptr_t) port);
+ } else if(!strncmp(name, "usb:", 4)) {
+ ret = create_service_thread(restart_usb_service, NULL);
+ } else if (!strncmp(name, "reverse:", 8)) {
+ char* cookie = strdup(name + 8);
+ if (cookie == NULL) {
+ ret = -1;
+ } else {
+ ret = create_service_thread(reverse_service, cookie);
+ if (ret < 0) {
+ free(cookie);
+ }
+ }
+ } else if(!strncmp(name, "disable-verity:", 15)) {
+ ret = create_service_thread(set_verity_enabled_state_service, (void*)0);
+ } else if(!strncmp(name, "enable-verity:", 15)) {
+ ret = create_service_thread(set_verity_enabled_state_service, (void*)1);
+#endif
+ }
+ if (ret >= 0) {
+ close_on_exec(ret);
+ }
+ return ret;
+}
+
+#if ADB_HOST
+struct state_info {
+ TransportType transport_type;
+ char* serial;
+ ConnectionState state;
+};
+
+static void wait_for_state(int fd, void* cookie)
+{
+ state_info* sinfo = reinterpret_cast<state_info*>(cookie);
+
+ D("wait_for_state %d\n", sinfo->state);
+
+ std::string error_msg = "unknown error";
+ atransport* t = acquire_one_transport(sinfo->state, sinfo->transport_type, sinfo->serial,
+ &error_msg);
+ if (t != nullptr) {
+ SendOkay(fd);
+ } else {
+ SendFail(fd, error_msg);
+ }
+
+ if (sinfo->serial)
+ free(sinfo->serial);
+ free(sinfo);
+ adb_close(fd);
+ D("wait_for_state is done\n");
+}
+
+static void connect_device(const std::string& address, std::string* response) {
+ if (address.empty()) {
+ *response = "empty address";
+ return;
+ }
+
+ std::string serial;
+ std::string host;
+ int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
+ if (!parse_host_and_port(address, &serial, &host, &port, response)) {
+ return;
+ }
+
+ std::string error;
+ int fd = network_connect(host.c_str(), port, SOCK_STREAM, 10, &error);
+ if (fd == -1) {
+ *response = android::base::StringPrintf("unable to connect to %s: %s",
+ serial.c_str(), error.c_str());
+ return;
+ }
+
+ D("client: connected %s remote on fd %d\n", serial.c_str(), fd);
+ close_on_exec(fd);
+ disable_tcp_nagle(fd);
+
+ int ret = register_socket_transport(fd, serial.c_str(), port, 0);
+ if (ret < 0) {
+ adb_close(fd);
+ *response = android::base::StringPrintf("already connected to %s", serial.c_str());
+ } else {
+ *response = android::base::StringPrintf("connected to %s", serial.c_str());
+ }
+}
+
+void connect_emulator(const std::string& port_spec, std::string* response) {
+ std::vector<std::string> pieces = android::base::Split(port_spec, ",");
+ if (pieces.size() != 2) {
+ *response = android::base::StringPrintf("unable to parse '%s' as <console port>,<adb port>",
+ port_spec.c_str());
+ return;
+ }
+
+ int console_port = strtol(pieces[0].c_str(), NULL, 0);
+ int adb_port = strtol(pieces[1].c_str(), NULL, 0);
+ if (console_port <= 0 || adb_port <= 0) {
+ *response = android::base::StringPrintf("Invalid port numbers: %s", port_spec.c_str());
+ return;
+ }
+
+ // Check if the emulator is already known.
+ // Note: There's a small but harmless race condition here: An emulator not
+ // present just yet could be registered by another invocation right
+ // after doing this check here. However, local_connect protects
+ // against double-registration too. From here, a better error message
+ // can be produced. In the case of the race condition, the very specific
+ // error message won't be shown, but the data doesn't get corrupted.
+ atransport* known_emulator = find_emulator_transport_by_adb_port(adb_port);
+ if (known_emulator != nullptr) {
+ *response = android::base::StringPrintf("Emulator already registered on port %d", adb_port);
+ return;
+ }
+
+ // Check if more emulators can be registered. Similar unproblematic
+ // race condition as above.
+ int candidate_slot = get_available_local_transport_index();
+ if (candidate_slot < 0) {
+ *response = "Cannot accept more emulators";
+ return;
+ }
+
+ // Preconditions met, try to connect to the emulator.
+ std::string error;
+ if (!local_connect_arbitrary_ports(console_port, adb_port, &error)) {
+ *response = android::base::StringPrintf("Connected to emulator on ports %d,%d",
+ console_port, adb_port);
+ } else {
+ *response = android::base::StringPrintf("Could not connect to emulator on ports %d,%d: %s",
+ console_port, adb_port, error.c_str());
+ }
+}
+
+static void connect_service(int fd, void* data) {
+ char* host = reinterpret_cast<char*>(data);
+ std::string response;
+ if (!strncmp(host, "emu:", 4)) {
+ connect_emulator(host + 4, &response);
+ } else {
+ connect_device(host, &response);
+ }
+ free(host);
+
+ // Send response for emulator and device
+ SendProtocolString(fd, response);
+ adb_close(fd);
+}
+#endif
+
+#if ADB_HOST
+asocket* host_service_to_socket(const char* name, const char *serial)
+{
+ if (!strcmp(name,"track-devices")) {
+ return create_device_tracker();
+ } else if (!strncmp(name, "wait-for-", strlen("wait-for-"))) {
+ auto sinfo = reinterpret_cast<state_info*>(malloc(sizeof(state_info)));
+ if (sinfo == nullptr) {
+ fprintf(stderr, "couldn't allocate state_info: %s", strerror(errno));
+ return NULL;
+ }
+
+ if (serial)
+ sinfo->serial = strdup(serial);
+ else
+ sinfo->serial = NULL;
+
+ name += strlen("wait-for-");
+
+ if (!strncmp(name, "local", strlen("local"))) {
+ sinfo->transport_type = kTransportLocal;
+ sinfo->state = kCsDevice;
+ } else if (!strncmp(name, "usb", strlen("usb"))) {
+ sinfo->transport_type = kTransportUsb;
+ sinfo->state = kCsDevice;
+ } else if (!strncmp(name, "any", strlen("any"))) {
+ sinfo->transport_type = kTransportAny;
+ sinfo->state = kCsDevice;
+ } else {
+ if (sinfo->serial) {
+ free(sinfo->serial);
+ }
+ free(sinfo);
+ return NULL;
+ }
+
+ int fd = create_service_thread(wait_for_state, sinfo);
+ return create_local_socket(fd);
+ } else if (!strncmp(name, "connect:", 8)) {
+ char* host = strdup(name + 8);
+ int fd = create_service_thread(connect_service, host);
+ return create_local_socket(fd);
+ }
+ return NULL;
+}
+#endif /* ADB_HOST */
diff --git a/adb/set_verity_enable_state_service.cpp b/adb/set_verity_enable_state_service.cpp
new file mode 100644
index 0000000..bae38cf
--- /dev/null
+++ b/adb/set_verity_enable_state_service.cpp
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2014 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 TRACE_TAG TRACE_ADB
+
+#include "sysdeps.h"
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/stat.h>
+
+#include "cutils/properties.h"
+
+#include "adb.h"
+#include "adb_io.h"
+#include "ext4_sb.h"
+#include "fs_mgr.h"
+#include "remount_service.h"
+
+#define FSTAB_PREFIX "/fstab."
+struct fstab *fstab;
+
+#ifdef ALLOW_ADBD_DISABLE_VERITY
+static const bool kAllowDisableVerity = true;
+#else
+static const bool kAllowDisableVerity = false;
+#endif
+
+static int get_target_device_size(int fd, const char *blk_device,
+ uint64_t *device_size)
+{
+ int data_device;
+ struct ext4_super_block sb;
+ struct fs_info info;
+
+ info.len = 0; /* Only len is set to 0 to ask the device for real size. */
+
+ data_device = adb_open(blk_device, O_RDONLY | O_CLOEXEC);
+ if (data_device < 0) {
+ WriteFdFmt(fd, "Error opening block device (%s)\n", strerror(errno));
+ return -1;
+ }
+
+ if (lseek64(data_device, 1024, SEEK_SET) < 0) {
+ WriteFdFmt(fd, "Error seeking to superblock\n");
+ adb_close(data_device);
+ return -1;
+ }
+
+ if (adb_read(data_device, &sb, sizeof(sb)) != sizeof(sb)) {
+ WriteFdFmt(fd, "Error reading superblock\n");
+ adb_close(data_device);
+ return -1;
+ }
+
+ ext4_parse_sb(&sb, &info);
+ *device_size = info.len;
+
+ adb_close(data_device);
+ return 0;
+}
+
+/* Turn verity on/off */
+static int set_verity_enabled_state(int fd, const char *block_device,
+ const char* mount_point, bool enable)
+{
+ uint32_t magic_number;
+ const uint32_t new_magic = enable ? VERITY_METADATA_MAGIC_NUMBER
+ : VERITY_METADATA_MAGIC_DISABLE;
+ uint64_t device_length = 0;
+ int device = -1;
+ int retval = -1;
+
+ if (!make_block_device_writable(block_device)) {
+ WriteFdFmt(fd, "Could not make block device %s writable (%s).\n",
+ block_device, strerror(errno));
+ goto errout;
+ }
+
+ device = adb_open(block_device, O_RDWR | O_CLOEXEC);
+ if (device == -1) {
+ WriteFdFmt(fd, "Could not open block device %s (%s).\n", block_device, strerror(errno));
+ WriteFdFmt(fd, "Maybe run adb remount?\n");
+ goto errout;
+ }
+
+ // find the start of the verity metadata
+ if (get_target_device_size(fd, (char*)block_device, &device_length) < 0) {
+ WriteFdFmt(fd, "Could not get target device size.\n");
+ goto errout;
+ }
+
+ if (lseek64(device, device_length, SEEK_SET) < 0) {
+ WriteFdFmt(fd, "Could not seek to start of verity metadata block.\n");
+ goto errout;
+ }
+
+ // check the magic number
+ if (adb_read(device, &magic_number, sizeof(magic_number)) != sizeof(magic_number)) {
+ WriteFdFmt(fd, "Couldn't read magic number!\n");
+ goto errout;
+ }
+
+ if (!enable && magic_number == VERITY_METADATA_MAGIC_DISABLE) {
+ WriteFdFmt(fd, "Verity already disabled on %s\n", mount_point);
+ goto errout;
+ }
+
+ if (enable && magic_number == VERITY_METADATA_MAGIC_NUMBER) {
+ WriteFdFmt(fd, "Verity already enabled on %s\n", mount_point);
+ goto errout;
+ }
+
+ if (magic_number != VERITY_METADATA_MAGIC_NUMBER
+ && magic_number != VERITY_METADATA_MAGIC_DISABLE) {
+ WriteFdFmt(fd, "Couldn't find verity metadata at offset %" PRIu64 "!\n", device_length);
+ goto errout;
+ }
+
+ if (lseek64(device, device_length, SEEK_SET) < 0) {
+ WriteFdFmt(fd, "Could not seek to start of verity metadata block.\n");
+ goto errout;
+ }
+
+ if (adb_write(device, &new_magic, sizeof(new_magic)) != sizeof(new_magic)) {
+ WriteFdFmt(fd, "Could not set verity %s flag on device %s with error %s\n",
+ enable ? "enabled" : "disabled",
+ block_device, strerror(errno));
+ goto errout;
+ }
+
+ WriteFdFmt(fd, "Verity %s on %s\n", enable ? "enabled" : "disabled", mount_point);
+ retval = 0;
+errout:
+ if (device != -1)
+ adb_close(device);
+ return retval;
+}
+
+void set_verity_enabled_state_service(int fd, void* cookie)
+{
+ bool enable = (cookie != NULL);
+ if (kAllowDisableVerity) {
+ char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)];
+ char propbuf[PROPERTY_VALUE_MAX];
+ int i;
+ bool any_changed = false;
+
+ property_get("ro.secure", propbuf, "0");
+ if (strcmp(propbuf, "1")) {
+ WriteFdFmt(fd, "verity not enabled - ENG build\n");
+ goto errout;
+ }
+
+ property_get("ro.debuggable", propbuf, "0");
+ if (strcmp(propbuf, "1")) {
+ WriteFdFmt(fd, "verity cannot be disabled/enabled - USER build\n");
+ goto errout;
+ }
+
+ property_get("ro.hardware", propbuf, "");
+ snprintf(fstab_filename, sizeof(fstab_filename), FSTAB_PREFIX"%s",
+ propbuf);
+
+ fstab = fs_mgr_read_fstab(fstab_filename);
+ if (!fstab) {
+ WriteFdFmt(fd, "Failed to open %s\nMaybe run adb root?\n", fstab_filename);
+ goto errout;
+ }
+
+ /* Loop through entries looking for ones that vold manages */
+ for (i = 0; i < fstab->num_entries; i++) {
+ if(fs_mgr_is_verified(&fstab->recs[i])) {
+ if (!set_verity_enabled_state(fd, fstab->recs[i].blk_device,
+ fstab->recs[i].mount_point,
+ enable)) {
+ any_changed = true;
+ }
+ }
+ }
+
+ if (any_changed) {
+ WriteFdFmt(fd, "Now reboot your device for settings to take effect\n");
+ }
+ } else {
+ WriteFdFmt(fd, "%s-verity only works for userdebug builds\n",
+ enable ? "enable" : "disable");
+ }
+
+errout:
+ adb_close(fd);
+}
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
new file mode 100644
index 0000000..d8ea2ee
--- /dev/null
+++ b/adb/sockets.cpp
@@ -0,0 +1,915 @@
+/*
+ * Copyright (C) 2007 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 TRACE_TAG TRACE_SOCKETS
+
+#include "sysdeps.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#if !ADB_HOST
+#include "cutils/properties.h"
+#endif
+
+#include "adb.h"
+#include "adb_io.h"
+#include "transport.h"
+
+ADB_MUTEX_DEFINE( socket_list_lock );
+
+static void local_socket_close_locked(asocket *s);
+
+static unsigned local_socket_next_id = 1;
+
+static asocket local_socket_list = {
+ .next = &local_socket_list,
+ .prev = &local_socket_list,
+};
+
+/* the the list of currently closing local sockets.
+** these have no peer anymore, but still packets to
+** write to their fd.
+*/
+static asocket local_socket_closing_list = {
+ .next = &local_socket_closing_list,
+ .prev = &local_socket_closing_list,
+};
+
+// Parse the global list of sockets to find one with id |local_id|.
+// If |peer_id| is not 0, also check that it is connected to a peer
+// with id |peer_id|. Returns an asocket handle on success, NULL on failure.
+asocket *find_local_socket(unsigned local_id, unsigned peer_id)
+{
+ asocket *s;
+ asocket *result = NULL;
+
+ adb_mutex_lock(&socket_list_lock);
+ for (s = local_socket_list.next; s != &local_socket_list; s = s->next) {
+ if (s->id != local_id)
+ continue;
+ if (peer_id == 0 || (s->peer && s->peer->id == peer_id)) {
+ result = s;
+ }
+ break;
+ }
+ adb_mutex_unlock(&socket_list_lock);
+
+ return result;
+}
+
+static void
+insert_local_socket(asocket* s, asocket* list)
+{
+ s->next = list;
+ s->prev = s->next->prev;
+ s->prev->next = s;
+ s->next->prev = s;
+}
+
+
+void install_local_socket(asocket *s)
+{
+ adb_mutex_lock(&socket_list_lock);
+
+ s->id = local_socket_next_id++;
+
+ // Socket ids should never be 0.
+ if (local_socket_next_id == 0)
+ local_socket_next_id = 1;
+
+ insert_local_socket(s, &local_socket_list);
+
+ adb_mutex_unlock(&socket_list_lock);
+}
+
+void remove_socket(asocket *s)
+{
+ // socket_list_lock should already be held
+ if (s->prev && s->next)
+ {
+ s->prev->next = s->next;
+ s->next->prev = s->prev;
+ s->next = 0;
+ s->prev = 0;
+ s->id = 0;
+ }
+}
+
+void close_all_sockets(atransport *t)
+{
+ asocket *s;
+
+ /* this is a little gross, but since s->close() *will* modify
+ ** the list out from under you, your options are limited.
+ */
+ adb_mutex_lock(&socket_list_lock);
+restart:
+ for(s = local_socket_list.next; s != &local_socket_list; s = s->next){
+ if(s->transport == t || (s->peer && s->peer->transport == t)) {
+ local_socket_close_locked(s);
+ goto restart;
+ }
+ }
+ adb_mutex_unlock(&socket_list_lock);
+}
+
+static int local_socket_enqueue(asocket *s, apacket *p)
+{
+ D("LS(%d): enqueue %d\n", s->id, p->len);
+
+ p->ptr = p->data;
+
+ /* if there is already data queue'd, we will receive
+ ** events when it's time to write. just add this to
+ ** the tail
+ */
+ if(s->pkt_first) {
+ goto enqueue;
+ }
+
+ /* write as much as we can, until we
+ ** would block or there is an error/eof
+ */
+ while(p->len > 0) {
+ int r = adb_write(s->fd, p->ptr, p->len);
+ if(r > 0) {
+ p->len -= r;
+ p->ptr += r;
+ continue;
+ }
+ if((r == 0) || (errno != EAGAIN)) {
+ D( "LS(%d): not ready, errno=%d: %s\n", s->id, errno, strerror(errno) );
+ s->close(s);
+ return 1; /* not ready (error) */
+ } else {
+ break;
+ }
+ }
+
+ if(p->len == 0) {
+ put_apacket(p);
+ return 0; /* ready for more data */
+ }
+
+enqueue:
+ p->next = 0;
+ if(s->pkt_first) {
+ s->pkt_last->next = p;
+ } else {
+ s->pkt_first = p;
+ }
+ s->pkt_last = p;
+
+ /* make sure we are notified when we can drain the queue */
+ fdevent_add(&s->fde, FDE_WRITE);
+
+ return 1; /* not ready (backlog) */
+}
+
+static void local_socket_ready(asocket *s)
+{
+ /* far side is ready for data, pay attention to
+ readable events */
+ fdevent_add(&s->fde, FDE_READ);
+}
+
+static void local_socket_close(asocket *s)
+{
+ adb_mutex_lock(&socket_list_lock);
+ local_socket_close_locked(s);
+ adb_mutex_unlock(&socket_list_lock);
+}
+
+// be sure to hold the socket list lock when calling this
+static void local_socket_destroy(asocket *s)
+{
+ apacket *p, *n;
+ int exit_on_close = s->exit_on_close;
+
+ D("LS(%d): destroying fde.fd=%d\n", s->id, s->fde.fd);
+
+ /* IMPORTANT: the remove closes the fd
+ ** that belongs to this socket
+ */
+ fdevent_remove(&s->fde);
+
+ /* dispose of any unwritten data */
+ for(p = s->pkt_first; p; p = n) {
+ D("LS(%d): discarding %d bytes\n", s->id, p->len);
+ n = p->next;
+ put_apacket(p);
+ }
+ remove_socket(s);
+ free(s);
+
+ if (exit_on_close) {
+ D("local_socket_destroy: exiting\n");
+ exit(1);
+ }
+}
+
+
+static void local_socket_close_locked(asocket *s)
+{
+ D("entered local_socket_close_locked. LS(%d) fd=%d\n", s->id, s->fd);
+ if(s->peer) {
+ D("LS(%d): closing peer. peer->id=%d peer->fd=%d\n",
+ s->id, s->peer->id, s->peer->fd);
+ /* Note: it's important to call shutdown before disconnecting from
+ * the peer, this ensures that remote sockets can still get the id
+ * of the local socket they're connected to, to send a CLOSE()
+ * protocol event. */
+ if (s->peer->shutdown)
+ s->peer->shutdown(s->peer);
+ s->peer->peer = 0;
+ // tweak to avoid deadlock
+ if (s->peer->close == local_socket_close) {
+ local_socket_close_locked(s->peer);
+ } else {
+ s->peer->close(s->peer);
+ }
+ s->peer = 0;
+ }
+
+ /* If we are already closing, or if there are no
+ ** pending packets, destroy immediately
+ */
+ if (s->closing || s->pkt_first == NULL) {
+ int id = s->id;
+ local_socket_destroy(s);
+ D("LS(%d): closed\n", id);
+ return;
+ }
+
+ /* otherwise, put on the closing list
+ */
+ D("LS(%d): closing\n", s->id);
+ s->closing = 1;
+ fdevent_del(&s->fde, FDE_READ);
+ remove_socket(s);
+ D("LS(%d): put on socket_closing_list fd=%d\n", s->id, s->fd);
+ insert_local_socket(s, &local_socket_closing_list);
+}
+
+static void local_socket_event_func(int fd, unsigned ev, void* _s)
+{
+ asocket* s = reinterpret_cast<asocket*>(_s);
+ D("LS(%d): event_func(fd=%d(==%d), ev=%04x)\n", s->id, s->fd, fd, ev);
+
+ /* put the FDE_WRITE processing before the FDE_READ
+ ** in order to simplify the code.
+ */
+ if (ev & FDE_WRITE) {
+ apacket* p;
+ while ((p = s->pkt_first) != nullptr) {
+ while (p->len > 0) {
+ int r = adb_write(fd, p->ptr, p->len);
+ if (r == -1) {
+ /* returning here is ok because FDE_READ will
+ ** be processed in the next iteration loop
+ */
+ if (errno == EAGAIN) {
+ return;
+ }
+ } else if (r > 0) {
+ p->ptr += r;
+ p->len -= r;
+ continue;
+ }
+
+ D(" closing after write because r=%d and errno is %d\n", r, errno);
+ s->close(s);
+ return;
+ }
+
+ if (p->len == 0) {
+ s->pkt_first = p->next;
+ if (s->pkt_first == 0) {
+ s->pkt_last = 0;
+ }
+ put_apacket(p);
+ }
+ }
+
+ /* if we sent the last packet of a closing socket,
+ ** we can now destroy it.
+ */
+ if (s->closing) {
+ D(" closing because 'closing' is set after write\n");
+ s->close(s);
+ return;
+ }
+
+ /* no more packets queued, so we can ignore
+ ** writable events again and tell our peer
+ ** to resume writing
+ */
+ fdevent_del(&s->fde, FDE_WRITE);
+ s->peer->ready(s->peer);
+ }
+
+
+ if (ev & FDE_READ) {
+ apacket *p = get_apacket();
+ unsigned char *x = p->data;
+ const size_t max_payload = s->get_max_payload();
+ size_t avail = max_payload;
+ int r = 0;
+ int is_eof = 0;
+
+ while (avail > 0) {
+ r = adb_read(fd, x, avail);
+ D("LS(%d): post adb_read(fd=%d,...) r=%d (errno=%d) avail=%zu\n",
+ s->id, s->fd, r, r < 0 ? errno : 0, avail);
+ if (r == -1) {
+ if (errno == EAGAIN) {
+ break;
+ }
+ } else if (r > 0) {
+ avail -= r;
+ x += r;
+ continue;
+ }
+
+ /* r = 0 or unhandled error */
+ is_eof = 1;
+ break;
+ }
+ D("LS(%d): fd=%d post avail loop. r=%d is_eof=%d forced_eof=%d\n",
+ s->id, s->fd, r, is_eof, s->fde.force_eof);
+ if ((avail == max_payload) || (s->peer == 0)) {
+ put_apacket(p);
+ } else {
+ p->len = max_payload - avail;
+
+ r = s->peer->enqueue(s->peer, p);
+ D("LS(%d): fd=%d post peer->enqueue(). r=%d\n", s->id, s->fd,
+ r);
+
+ if (r < 0) {
+ /* error return means they closed us as a side-effect
+ ** and we must return immediately.
+ **
+ ** note that if we still have buffered packets, the
+ ** socket will be placed on the closing socket list.
+ ** this handler function will be called again
+ ** to process FDE_WRITE events.
+ */
+ return;
+ }
+
+ if (r > 0) {
+ /* if the remote cannot accept further events,
+ ** we disable notification of READs. They'll
+ ** be enabled again when we get a call to ready()
+ */
+ fdevent_del(&s->fde, FDE_READ);
+ }
+ }
+ /* Don't allow a forced eof if data is still there */
+ if ((s->fde.force_eof && !r) || is_eof) {
+ D(" closing because is_eof=%d r=%d s->fde.force_eof=%d\n",
+ is_eof, r, s->fde.force_eof);
+ s->close(s);
+ }
+ }
+
+ if (ev & FDE_ERROR){
+ /* this should be caught be the next read or write
+ ** catching it here means we may skip the last few
+ ** bytes of readable data.
+ */
+ D("LS(%d): FDE_ERROR (fd=%d)\n", s->id, s->fd);
+
+ return;
+ }
+}
+
+asocket *create_local_socket(int fd)
+{
+ asocket *s = reinterpret_cast<asocket*>(calloc(1, sizeof(asocket)));
+ if (s == NULL) fatal("cannot allocate socket");
+ s->fd = fd;
+ s->enqueue = local_socket_enqueue;
+ s->ready = local_socket_ready;
+ s->shutdown = NULL;
+ s->close = local_socket_close;
+ install_local_socket(s);
+
+ fdevent_install(&s->fde, fd, local_socket_event_func, s);
+ D("LS(%d): created (fd=%d)\n", s->id, s->fd);
+ return s;
+}
+
+asocket *create_local_service_socket(const char *name)
+{
+#if !ADB_HOST
+ if (!strcmp(name,"jdwp")) {
+ return create_jdwp_service_socket();
+ }
+ if (!strcmp(name,"track-jdwp")) {
+ return create_jdwp_tracker_service_socket();
+ }
+#endif
+ int fd = service_to_fd(name);
+ if(fd < 0) return 0;
+
+ asocket* s = create_local_socket(fd);
+ D("LS(%d): bound to '%s' via %d\n", s->id, name, fd);
+
+#if !ADB_HOST
+ char debug[PROPERTY_VALUE_MAX];
+ if (!strncmp(name, "root:", 5))
+ property_get("ro.debuggable", debug, "");
+
+ if ((!strncmp(name, "root:", 5) && getuid() != 0 && strcmp(debug, "1") == 0)
+ || (!strncmp(name, "unroot:", 7) && getuid() == 0)
+ || !strncmp(name, "usb:", 4)
+ || !strncmp(name, "tcpip:", 6)) {
+ D("LS(%d): enabling exit_on_close\n", s->id);
+ s->exit_on_close = 1;
+ }
+#endif
+
+ return s;
+}
+
+#if ADB_HOST
+static asocket *create_host_service_socket(const char *name, const char* serial)
+{
+ asocket *s;
+
+ s = host_service_to_socket(name, serial);
+
+ if (s != NULL) {
+ D("LS(%d) bound to '%s'\n", s->id, name);
+ return s;
+ }
+
+ return s;
+}
+#endif /* ADB_HOST */
+
+/* a Remote socket is used to send/receive data to/from a given transport object
+** it needs to be closed when the transport is forcibly destroyed by the user
+*/
+struct aremotesocket {
+ asocket socket;
+ adisconnect disconnect;
+};
+
+static int remote_socket_enqueue(asocket *s, apacket *p)
+{
+ D("entered remote_socket_enqueue RS(%d) WRITE fd=%d peer.fd=%d\n",
+ s->id, s->fd, s->peer->fd);
+ p->msg.command = A_WRTE;
+ p->msg.arg0 = s->peer->id;
+ p->msg.arg1 = s->id;
+ p->msg.data_length = p->len;
+ send_packet(p, s->transport);
+ return 1;
+}
+
+static void remote_socket_ready(asocket *s)
+{
+ D("entered remote_socket_ready RS(%d) OKAY fd=%d peer.fd=%d\n",
+ s->id, s->fd, s->peer->fd);
+ apacket *p = get_apacket();
+ p->msg.command = A_OKAY;
+ p->msg.arg0 = s->peer->id;
+ p->msg.arg1 = s->id;
+ send_packet(p, s->transport);
+}
+
+static void remote_socket_shutdown(asocket *s)
+{
+ D("entered remote_socket_shutdown RS(%d) CLOSE fd=%d peer->fd=%d\n",
+ s->id, s->fd, s->peer?s->peer->fd:-1);
+ apacket *p = get_apacket();
+ p->msg.command = A_CLSE;
+ if(s->peer) {
+ p->msg.arg0 = s->peer->id;
+ }
+ p->msg.arg1 = s->id;
+ send_packet(p, s->transport);
+}
+
+static void remote_socket_close(asocket *s)
+{
+ if (s->peer) {
+ s->peer->peer = 0;
+ D("RS(%d) peer->close()ing peer->id=%d peer->fd=%d\n",
+ s->id, s->peer->id, s->peer->fd);
+ s->peer->close(s->peer);
+ }
+ D("entered remote_socket_close RS(%d) CLOSE fd=%d peer->fd=%d\n",
+ s->id, s->fd, s->peer?s->peer->fd:-1);
+ D("RS(%d): closed\n", s->id);
+ remove_transport_disconnect( s->transport, &((aremotesocket*)s)->disconnect );
+ free(s);
+}
+
+static void remote_socket_disconnect(void* _s, atransport* t)
+{
+ asocket* s = reinterpret_cast<asocket*>(_s);
+ asocket* peer = s->peer;
+
+ D("remote_socket_disconnect RS(%d)\n", s->id);
+ if (peer) {
+ peer->peer = NULL;
+ peer->close(peer);
+ }
+ remove_transport_disconnect( s->transport, &((aremotesocket*)s)->disconnect );
+ free(s);
+}
+
+/* Create an asocket to exchange packets with a remote service through transport
+ |t|. Where |id| is the socket id of the corresponding service on the other
+ side of the transport (it is allocated by the remote side and _cannot_ be 0).
+ Returns a new non-NULL asocket handle. */
+asocket *create_remote_socket(unsigned id, atransport *t)
+{
+ if (id == 0) fatal("invalid remote socket id (0)");
+ asocket* s = reinterpret_cast<asocket*>(calloc(1, sizeof(aremotesocket)));
+ adisconnect* dis = &reinterpret_cast<aremotesocket*>(s)->disconnect;
+
+ if (s == NULL) fatal("cannot allocate socket");
+ s->id = id;
+ s->enqueue = remote_socket_enqueue;
+ s->ready = remote_socket_ready;
+ s->shutdown = remote_socket_shutdown;
+ s->close = remote_socket_close;
+ s->transport = t;
+
+ dis->func = remote_socket_disconnect;
+ dis->opaque = s;
+ add_transport_disconnect( t, dis );
+ D("RS(%d): created\n", s->id);
+ return s;
+}
+
+void connect_to_remote(asocket *s, const char *destination)
+{
+ D("Connect_to_remote call RS(%d) fd=%d\n", s->id, s->fd);
+ apacket *p = get_apacket();
+ size_t len = strlen(destination) + 1;
+
+ if(len > (s->get_max_payload()-1)) {
+ fatal("destination oversized");
+ }
+
+ D("LS(%d): connect('%s')\n", s->id, destination);
+ p->msg.command = A_OPEN;
+ p->msg.arg0 = s->id;
+ p->msg.data_length = len;
+ strcpy((char*) p->data, destination);
+ send_packet(p, s->transport);
+}
+
+
+/* this is used by magic sockets to rig local sockets to
+ send the go-ahead message when they connect */
+static void local_socket_ready_notify(asocket *s)
+{
+ s->ready = local_socket_ready;
+ s->shutdown = NULL;
+ s->close = local_socket_close;
+ SendOkay(s->fd);
+ s->ready(s);
+}
+
+/* this is used by magic sockets to rig local sockets to
+ send the failure message if they are closed before
+ connected (to avoid closing them without a status message) */
+static void local_socket_close_notify(asocket *s)
+{
+ s->ready = local_socket_ready;
+ s->shutdown = NULL;
+ s->close = local_socket_close;
+ SendFail(s->fd, "closed");
+ s->close(s);
+}
+
+static unsigned unhex(unsigned char *s, int len)
+{
+ unsigned n = 0, c;
+
+ while(len-- > 0) {
+ switch((c = *s++)) {
+ case '0': case '1': case '2':
+ case '3': case '4': case '5':
+ case '6': case '7': case '8':
+ case '9':
+ c -= '0';
+ break;
+ case 'a': case 'b': case 'c':
+ case 'd': case 'e': case 'f':
+ c = c - 'a' + 10;
+ break;
+ case 'A': case 'B': case 'C':
+ case 'D': case 'E': case 'F':
+ c = c - 'A' + 10;
+ break;
+ default:
+ return 0xffffffff;
+ }
+
+ n = (n << 4) | c;
+ }
+
+ return n;
+}
+
+#if ADB_HOST
+
+#define PREFIX(str) { str, sizeof(str) - 1 }
+static const struct prefix_struct {
+ const char *str;
+ const size_t len;
+} prefixes[] = {
+ PREFIX("usb:"),
+ PREFIX("product:"),
+ PREFIX("model:"),
+ PREFIX("device:"),
+};
+static const int num_prefixes = (sizeof(prefixes) / sizeof(prefixes[0]));
+
+/* skip_host_serial return the position in a string
+ skipping over the 'serial' parameter in the ADB protocol,
+ where parameter string may be a host:port string containing
+ the protocol delimiter (colon). */
+static char *skip_host_serial(char *service) {
+ char *first_colon, *serial_end;
+ int i;
+
+ for (i = 0; i < num_prefixes; i++) {
+ if (!strncmp(service, prefixes[i].str, prefixes[i].len))
+ return strchr(service + prefixes[i].len, ':');
+ }
+
+ first_colon = strchr(service, ':');
+ if (!first_colon) {
+ /* No colon in service string. */
+ return NULL;
+ }
+ serial_end = first_colon;
+ if (isdigit(serial_end[1])) {
+ serial_end++;
+ while ((*serial_end) && isdigit(*serial_end)) {
+ serial_end++;
+ }
+ if ((*serial_end) != ':') {
+ // Something other than numbers was found, reset the end.
+ serial_end = first_colon;
+ }
+ }
+ return serial_end;
+}
+
+#endif // ADB_HOST
+
+static int smart_socket_enqueue(asocket *s, apacket *p)
+{
+ unsigned len;
+#if ADB_HOST
+ char *service = NULL;
+ char* serial = NULL;
+ TransportType type = kTransportAny;
+#endif
+
+ D("SS(%d): enqueue %d\n", s->id, p->len);
+
+ if(s->pkt_first == 0) {
+ s->pkt_first = p;
+ s->pkt_last = p;
+ } else {
+ if((s->pkt_first->len + p->len) > s->get_max_payload()) {
+ D("SS(%d): overflow\n", s->id);
+ put_apacket(p);
+ goto fail;
+ }
+
+ memcpy(s->pkt_first->data + s->pkt_first->len,
+ p->data, p->len);
+ s->pkt_first->len += p->len;
+ put_apacket(p);
+
+ p = s->pkt_first;
+ }
+
+ /* don't bother if we can't decode the length */
+ if(p->len < 4) return 0;
+
+ len = unhex(p->data, 4);
+ if((len < 1) || (len > 1024)) {
+ D("SS(%d): bad size (%d)\n", s->id, len);
+ goto fail;
+ }
+
+ D("SS(%d): len is %d\n", s->id, len );
+ /* can't do anything until we have the full header */
+ if((len + 4) > p->len) {
+ D("SS(%d): waiting for %d more bytes\n", s->id, len+4 - p->len);
+ return 0;
+ }
+
+ p->data[len + 4] = 0;
+
+ D("SS(%d): '%s'\n", s->id, (char*) (p->data + 4));
+
+#if ADB_HOST
+ service = (char *)p->data + 4;
+ if(!strncmp(service, "host-serial:", strlen("host-serial:"))) {
+ char* serial_end;
+ service += strlen("host-serial:");
+
+ // serial number should follow "host:" and could be a host:port string.
+ serial_end = skip_host_serial(service);
+ if (serial_end) {
+ *serial_end = 0; // terminate string
+ serial = service;
+ service = serial_end + 1;
+ }
+ } else if (!strncmp(service, "host-usb:", strlen("host-usb:"))) {
+ type = kTransportUsb;
+ service += strlen("host-usb:");
+ } else if (!strncmp(service, "host-local:", strlen("host-local:"))) {
+ type = kTransportLocal;
+ service += strlen("host-local:");
+ } else if (!strncmp(service, "host:", strlen("host:"))) {
+ type = kTransportAny;
+ service += strlen("host:");
+ } else {
+ service = NULL;
+ }
+
+ if (service) {
+ asocket *s2;
+
+ /* some requests are handled immediately -- in that
+ ** case the handle_host_request() routine has sent
+ ** the OKAY or FAIL message and all we have to do
+ ** is clean up.
+ */
+ if(handle_host_request(service, type, serial, s->peer->fd, s) == 0) {
+ /* XXX fail message? */
+ D( "SS(%d): handled host service '%s'\n", s->id, service );
+ goto fail;
+ }
+ if (!strncmp(service, "transport", strlen("transport"))) {
+ D( "SS(%d): okay transport\n", s->id );
+ p->len = 0;
+ return 0;
+ }
+
+ /* try to find a local service with this name.
+ ** if no such service exists, we'll fail out
+ ** and tear down here.
+ */
+ s2 = create_host_service_socket(service, serial);
+ if(s2 == 0) {
+ D( "SS(%d): couldn't create host service '%s'\n", s->id, service );
+ SendFail(s->peer->fd, "unknown host service");
+ goto fail;
+ }
+
+ /* we've connected to a local host service,
+ ** so we make our peer back into a regular
+ ** local socket and bind it to the new local
+ ** service socket, acknowledge the successful
+ ** connection, and close this smart socket now
+ ** that its work is done.
+ */
+ SendOkay(s->peer->fd);
+
+ s->peer->ready = local_socket_ready;
+ s->peer->shutdown = NULL;
+ s->peer->close = local_socket_close;
+ s->peer->peer = s2;
+ s2->peer = s->peer;
+ s->peer = 0;
+ D( "SS(%d): okay\n", s->id );
+ s->close(s);
+
+ /* initial state is "ready" */
+ s2->ready(s2);
+ return 0;
+ }
+#else /* !ADB_HOST */
+ if (s->transport == NULL) {
+ std::string error_msg = "unknown failure";
+ s->transport =
+ acquire_one_transport(kCsAny, kTransportAny, NULL, &error_msg);
+
+ if (s->transport == NULL) {
+ SendFail(s->peer->fd, error_msg);
+ goto fail;
+ }
+ }
+#endif
+
+ if(!(s->transport) || (s->transport->connection_state == kCsOffline)) {
+ /* if there's no remote we fail the connection
+ ** right here and terminate it
+ */
+ SendFail(s->peer->fd, "device offline (x)");
+ goto fail;
+ }
+
+
+ /* instrument our peer to pass the success or fail
+ ** message back once it connects or closes, then
+ ** detach from it, request the connection, and
+ ** tear down
+ */
+ s->peer->ready = local_socket_ready_notify;
+ s->peer->shutdown = NULL;
+ s->peer->close = local_socket_close_notify;
+ s->peer->peer = 0;
+ /* give him our transport and upref it */
+ s->peer->transport = s->transport;
+
+ connect_to_remote(s->peer, (char*) (p->data + 4));
+ s->peer = 0;
+ s->close(s);
+ return 1;
+
+fail:
+ /* we're going to close our peer as a side-effect, so
+ ** return -1 to signal that state to the local socket
+ ** who is enqueueing against us
+ */
+ s->close(s);
+ return -1;
+}
+
+static void smart_socket_ready(asocket *s)
+{
+ D("SS(%d): ready\n", s->id);
+}
+
+static void smart_socket_close(asocket *s)
+{
+ D("SS(%d): closed\n", s->id);
+ if(s->pkt_first){
+ put_apacket(s->pkt_first);
+ }
+ if(s->peer) {
+ s->peer->peer = 0;
+ s->peer->close(s->peer);
+ s->peer = 0;
+ }
+ free(s);
+}
+
+static asocket *create_smart_socket(void)
+{
+ D("Creating smart socket \n");
+ asocket *s = reinterpret_cast<asocket*>(calloc(1, sizeof(asocket)));
+ if (s == NULL) fatal("cannot allocate socket");
+ s->enqueue = smart_socket_enqueue;
+ s->ready = smart_socket_ready;
+ s->shutdown = NULL;
+ s->close = smart_socket_close;
+
+ D("SS(%d)\n", s->id);
+ return s;
+}
+
+void connect_to_smartsocket(asocket *s)
+{
+ D("Connecting to smart socket \n");
+ asocket *ss = create_smart_socket();
+ s->peer = ss;
+ ss->peer = s;
+ s->ready(s);
+}
+
+size_t asocket::get_max_payload() const {
+ size_t max_payload = MAX_PAYLOAD;
+ if (transport) {
+ max_payload = std::min(max_payload, transport->get_max_payload());
+ }
+ if (peer && peer->transport) {
+ max_payload = std::min(max_payload, peer->transport->get_max_payload());
+ }
+ return max_payload;
+}
diff --git a/adb/sockets.dia b/adb/sockets.dia
new file mode 100644
index 0000000..c626f20
--- /dev/null
+++ b/adb/sockets.dia
Binary files differ
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
new file mode 100644
index 0000000..6160923
--- /dev/null
+++ b/adb/sysdeps.h
@@ -0,0 +1,546 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* this file contains system-dependent definitions used by ADB
+ * they're related to threads, sockets and file descriptors
+ */
+#ifndef _ADB_SYSDEPS_H
+#define _ADB_SYSDEPS_H
+
+#ifdef __CYGWIN__
+# undef _WIN32
+#endif
+
+#include <errno.h>
+
+/*
+ * TEMP_FAILURE_RETRY is defined by some, but not all, versions of
+ * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's
+ * not already defined, then define it here.
+ */
+#ifndef TEMP_FAILURE_RETRY
+/* Used to retry syscalls that can return EINTR. */
+#define TEMP_FAILURE_RETRY(exp) ({ \
+ typeof (exp) _rc; \
+ do { \
+ _rc = (exp); \
+ } while (_rc == -1 && errno == EINTR); \
+ _rc; })
+#endif
+
+#ifdef _WIN32
+
+#include <ctype.h>
+#include <direct.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <io.h>
+#include <process.h>
+#include <sys/stat.h>
+#include <winsock2.h>
+#include <windows.h>
+#include <ws2tcpip.h>
+
+#include "fdevent.h"
+
+#define OS_PATH_SEPARATOR '\\'
+#define OS_PATH_SEPARATOR_STR "\\"
+#define ENV_PATH_SEPARATOR_STR ";"
+
+typedef CRITICAL_SECTION adb_mutex_t;
+
+#define ADB_MUTEX_DEFINE(x) adb_mutex_t x
+
+/* declare all mutexes */
+/* For win32, adb_sysdeps_init() will do the mutex runtime initialization. */
+#define ADB_MUTEX(x) extern adb_mutex_t x;
+#include "mutex_list.h"
+
+extern void adb_sysdeps_init(void);
+
+static __inline__ void adb_mutex_lock( adb_mutex_t* lock )
+{
+ EnterCriticalSection( lock );
+}
+
+static __inline__ void adb_mutex_unlock( adb_mutex_t* lock )
+{
+ LeaveCriticalSection( lock );
+}
+
+typedef void* (*adb_thread_func_t)(void* arg);
+
+typedef void (*win_thread_func_t)(void* arg);
+
+static __inline__ bool adb_thread_create(adb_thread_func_t func, void* arg) {
+ uintptr_t tid = _beginthread((win_thread_func_t)func, 0, arg);
+ return (tid != static_cast<uintptr_t>(-1L));
+}
+
+static __inline__ unsigned long adb_thread_id()
+{
+ return GetCurrentThreadId();
+}
+
+static __inline__ void close_on_exec(int fd)
+{
+ /* nothing really */
+}
+
+#define lstat stat /* no symlinks on Win32 */
+
+#define S_ISLNK(m) 0 /* no symlinks on Win32 */
+
+static __inline__ int adb_unlink(const char* path)
+{
+ int rc = unlink(path);
+
+ if (rc == -1 && errno == EACCES) {
+ /* unlink returns EACCES when the file is read-only, so we first */
+ /* try to make it writable, then unlink again... */
+ rc = chmod(path, _S_IREAD|_S_IWRITE );
+ if (rc == 0)
+ rc = unlink(path);
+ }
+ return rc;
+}
+#undef unlink
+#define unlink ___xxx_unlink
+
+static __inline__ int adb_mkdir(const char* path, int mode)
+{
+ return _mkdir(path);
+}
+#undef mkdir
+#define mkdir ___xxx_mkdir
+
+// See the comments for the !defined(_WIN32) versions of adb_*().
+extern int adb_open(const char* path, int options);
+extern int adb_creat(const char* path, int mode);
+extern int adb_read(int fd, void* buf, int len);
+extern int adb_write(int fd, const void* buf, int len);
+extern int adb_lseek(int fd, int pos, int where);
+extern int adb_shutdown(int fd);
+extern int adb_close(int fd);
+
+// See the comments for the !defined(_WIN32) version of unix_close().
+static __inline__ int unix_close(int fd)
+{
+ return close(fd);
+}
+#undef close
+#define close ____xxx_close
+
+// See the comments for the !defined(_WIN32) version of unix_read().
+extern int unix_read(int fd, void* buf, size_t len);
+
+#undef read
+#define read ___xxx_read
+
+// See the comments for the !defined(_WIN32) version of unix_write().
+static __inline__ int unix_write(int fd, const void* buf, size_t len)
+{
+ return write(fd, buf, len);
+}
+#undef write
+#define write ___xxx_write
+
+// See the comments for the !defined(_WIN32) version of adb_open_mode().
+static __inline__ int adb_open_mode(const char* path, int options, int mode)
+{
+ return adb_open(path, options);
+}
+
+// See the comments for the !defined(_WIN32) version of unix_open().
+static __inline__ int unix_open(const char* path, int options,...)
+{
+ if ((options & O_CREAT) == 0)
+ {
+ return open(path, options);
+ }
+ else
+ {
+ int mode;
+ va_list args;
+ va_start( args, options );
+ mode = va_arg( args, int );
+ va_end( args );
+ return open(path, options, mode);
+ }
+}
+#define open ___xxx_unix_open
+
+
+/* normally provided by <cutils/misc.h> */
+extern void* load_file(const char* pathname, unsigned* psize);
+
+/* normally provided by "fdevent.h" */
+
+#define FDE_READ 0x0001
+#define FDE_WRITE 0x0002
+#define FDE_ERROR 0x0004
+#define FDE_DONT_CLOSE 0x0080
+
+typedef void (*fd_func)(int fd, unsigned events, void *userdata);
+
+fdevent *fdevent_create(int fd, fd_func func, void *arg);
+void fdevent_destroy(fdevent *fde);
+void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg);
+void fdevent_remove(fdevent *item);
+void fdevent_set(fdevent *fde, unsigned events);
+void fdevent_add(fdevent *fde, unsigned events);
+void fdevent_del(fdevent *fde, unsigned events);
+void fdevent_loop();
+
+static __inline__ void adb_sleep_ms( int mseconds )
+{
+ Sleep( mseconds );
+}
+
+extern int adb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t *addrlen);
+
+#undef accept
+#define accept ___xxx_accept
+
+extern int adb_setsockopt(int fd, int level, int optname, const void* optval, socklen_t optlen);
+
+#undef setsockopt
+#define setsockopt ___xxx_setsockopt
+
+static __inline__ int adb_socket_setbufsize( int fd, int bufsize )
+{
+ int opt = bufsize;
+ return adb_setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (const void*)&opt, sizeof(opt));
+}
+
+static __inline__ void disable_tcp_nagle( int fd )
+{
+ int on = 1;
+ adb_setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (const void*)&on, sizeof(on));
+}
+
+extern int adb_socketpair( int sv[2] );
+
+static __inline__ char* adb_dirstart( const char* path )
+{
+ char* p = strchr(path, '/');
+ char* p2 = strchr(path, '\\');
+
+ if ( !p )
+ p = p2;
+ else if ( p2 && p2 > p )
+ p = p2;
+
+ return p;
+}
+
+static __inline__ char* adb_dirstop( const char* path )
+{
+ char* p = strrchr(path, '/');
+ char* p2 = strrchr(path, '\\');
+
+ if ( !p )
+ p = p2;
+ else if ( p2 && p2 > p )
+ p = p2;
+
+ return p;
+}
+
+static __inline__ int adb_is_absolute_host_path( const char* path )
+{
+ return isalpha(path[0]) && path[1] == ':' && path[2] == '\\';
+}
+
+#else /* !_WIN32 a.k.a. Unix */
+
+#include "fdevent.h"
+#include <cutils/misc.h>
+#include <cutils/threads.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <pthread.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <string.h>
+#include <unistd.h>
+
+#define OS_PATH_SEPARATOR '/'
+#define OS_PATH_SEPARATOR_STR "/"
+#define ENV_PATH_SEPARATOR_STR ":"
+
+typedef pthread_mutex_t adb_mutex_t;
+
+#define ADB_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
+#define adb_mutex_init pthread_mutex_init
+#define adb_mutex_lock pthread_mutex_lock
+#define adb_mutex_unlock pthread_mutex_unlock
+#define adb_mutex_destroy pthread_mutex_destroy
+
+#define ADB_MUTEX_DEFINE(m) adb_mutex_t m = PTHREAD_MUTEX_INITIALIZER
+
+#define adb_cond_t pthread_cond_t
+#define adb_cond_init pthread_cond_init
+#define adb_cond_wait pthread_cond_wait
+#define adb_cond_broadcast pthread_cond_broadcast
+#define adb_cond_signal pthread_cond_signal
+#define adb_cond_destroy pthread_cond_destroy
+
+/* declare all mutexes */
+#define ADB_MUTEX(x) extern adb_mutex_t x;
+#include "mutex_list.h"
+
+static __inline__ void close_on_exec(int fd)
+{
+ fcntl( fd, F_SETFD, FD_CLOEXEC );
+}
+
+// Open a file and return a file descriptor that may be used with unix_read(),
+// unix_write(), unix_close(), but not adb_read(), adb_write(), adb_close().
+//
+// On Unix, this is based on open(), so the file descriptor is a real OS file
+// descriptor, but the Windows implementation (in sysdeps_win32.cpp) returns a
+// file descriptor that can only be used with C Runtime APIs (which are wrapped
+// by unix_read(), unix_write(), unix_close()). Also, the C Runtime has
+// configurable CR/LF translation which defaults to text mode, but is settable
+// with _setmode().
+static __inline__ int unix_open(const char* path, int options,...)
+{
+ if ((options & O_CREAT) == 0)
+ {
+ return TEMP_FAILURE_RETRY( open(path, options) );
+ }
+ else
+ {
+ int mode;
+ va_list args;
+ va_start( args, options );
+ mode = va_arg( args, int );
+ va_end( args );
+ return TEMP_FAILURE_RETRY( open( path, options, mode ) );
+ }
+}
+
+// Similar to the two-argument adb_open(), but takes a mode parameter for file
+// creation. See adb_open() for more info.
+static __inline__ int adb_open_mode( const char* pathname, int options, int mode )
+{
+ return TEMP_FAILURE_RETRY( open( pathname, options, mode ) );
+}
+
+
+// Open a file and return a file descriptor that may be used with adb_read(),
+// adb_write(), adb_close(), but not unix_read(), unix_write(), unix_close().
+//
+// On Unix, this is based on open(), but the Windows implementation (in
+// sysdeps_win32.cpp) uses Windows native file I/O and bypasses the C Runtime
+// and its CR/LF translation. The returned file descriptor should be used with
+// adb_read(), adb_write(), adb_close(), etc.
+static __inline__ int adb_open( const char* pathname, int options )
+{
+ int fd = TEMP_FAILURE_RETRY( open( pathname, options ) );
+ if (fd < 0)
+ return -1;
+ close_on_exec( fd );
+ return fd;
+}
+#undef open
+#define open ___xxx_open
+
+static __inline__ int adb_shutdown(int fd)
+{
+ return shutdown(fd, SHUT_RDWR);
+}
+#undef shutdown
+#define shutdown ____xxx_shutdown
+
+// Closes a file descriptor that came from adb_open() or adb_open_mode(), but
+// not designed to take a file descriptor from unix_open(). See the comments
+// for adb_open() for more info.
+static __inline__ int adb_close(int fd)
+{
+ return close(fd);
+}
+#undef close
+#define close ____xxx_close
+
+
+static __inline__ int adb_read(int fd, void* buf, size_t len)
+{
+ return TEMP_FAILURE_RETRY( read( fd, buf, len ) );
+}
+
+#undef read
+#define read ___xxx_read
+
+static __inline__ int adb_write(int fd, const void* buf, size_t len)
+{
+ return TEMP_FAILURE_RETRY( write( fd, buf, len ) );
+}
+#undef write
+#define write ___xxx_write
+
+static __inline__ int adb_lseek(int fd, int pos, int where)
+{
+ return lseek(fd, pos, where);
+}
+#undef lseek
+#define lseek ___xxx_lseek
+
+static __inline__ int adb_unlink(const char* path)
+{
+ return unlink(path);
+}
+#undef unlink
+#define unlink ___xxx_unlink
+
+static __inline__ int adb_creat(const char* path, int mode)
+{
+ int fd = TEMP_FAILURE_RETRY( creat( path, mode ) );
+
+ if ( fd < 0 )
+ return -1;
+
+ close_on_exec(fd);
+ return fd;
+}
+#undef creat
+#define creat ___xxx_creat
+
+static __inline__ int adb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t *addrlen)
+{
+ int fd;
+
+ fd = TEMP_FAILURE_RETRY( accept( serverfd, addr, addrlen ) );
+ if (fd >= 0)
+ close_on_exec(fd);
+
+ return fd;
+}
+
+#undef accept
+#define accept ___xxx_accept
+
+// Operate on a file descriptor returned from unix_open() or a well-known file
+// descriptor such as STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO.
+//
+// On Unix, unix_read(), unix_write(), unix_close() map to adb_read(),
+// adb_write(), adb_close() (which all map to Unix system calls), but the
+// Windows implementations (in the ifdef above and in sysdeps_win32.cpp) call
+// into the C Runtime and its configurable CR/LF translation (which is settable
+// via _setmode()).
+#define unix_read adb_read
+#define unix_write adb_write
+#define unix_close adb_close
+
+typedef void* (*adb_thread_func_t)( void* arg );
+
+static __inline__ bool adb_thread_create(adb_thread_func_t start, void* arg) {
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+ pthread_t thread;
+ errno = pthread_create(&thread, &attr, start, arg);
+ return (errno == 0);
+}
+
+static __inline__ int adb_socket_setbufsize( int fd, int bufsize )
+{
+ int opt = bufsize;
+ return setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt));
+}
+
+static __inline__ void disable_tcp_nagle(int fd)
+{
+ int on = 1;
+ setsockopt( fd, IPPROTO_TCP, TCP_NODELAY, (void*)&on, sizeof(on) );
+}
+
+static __inline__ int adb_setsockopt( int fd, int level, int optname, const void* optval, socklen_t optlen )
+{
+ return setsockopt( fd, level, optname, optval, optlen );
+}
+
+#undef setsockopt
+#define setsockopt ___xxx_setsockopt
+
+static __inline__ int unix_socketpair( int d, int type, int protocol, int sv[2] )
+{
+ return socketpair( d, type, protocol, sv );
+}
+
+static __inline__ int adb_socketpair( int sv[2] )
+{
+ int rc;
+
+ rc = unix_socketpair( AF_UNIX, SOCK_STREAM, 0, sv );
+ if (rc < 0)
+ return -1;
+
+ close_on_exec( sv[0] );
+ close_on_exec( sv[1] );
+ return 0;
+}
+
+#undef socketpair
+#define socketpair ___xxx_socketpair
+
+static __inline__ void adb_sleep_ms( int mseconds )
+{
+ usleep( mseconds*1000 );
+}
+
+static __inline__ int adb_mkdir(const char* path, int mode)
+{
+ return mkdir(path, mode);
+}
+#undef mkdir
+#define mkdir ___xxx_mkdir
+
+static __inline__ void adb_sysdeps_init(void)
+{
+}
+
+static __inline__ char* adb_dirstart(const char* path)
+{
+ return strchr(path, '/');
+}
+
+static __inline__ char* adb_dirstop(const char* path)
+{
+ return strrchr(path, '/');
+}
+
+static __inline__ int adb_is_absolute_host_path( const char* path )
+{
+ return path[0] == '/';
+}
+
+static __inline__ unsigned long adb_thread_id()
+{
+ return (unsigned long)gettid();
+}
+
+#endif /* !_WIN32 */
+
+#endif /* _ADB_SYSDEPS_H */
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
new file mode 100644
index 0000000..a274892
--- /dev/null
+++ b/adb/sysdeps_win32.cpp
@@ -0,0 +1,3054 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TRACE_TAG TRACE_SYSDEPS
+
+#include "sysdeps.h"
+
+#include <winsock2.h> /* winsock.h *must* be included before windows.h. */
+#include <windows.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <cutils/sockets.h>
+
+#include "adb.h"
+
+extern void fatal(const char *fmt, ...);
+
+/* forward declarations */
+
+typedef const struct FHClassRec_* FHClass;
+typedef struct FHRec_* FH;
+typedef struct EventHookRec_* EventHook;
+
+typedef struct FHClassRec_ {
+ void (*_fh_init)(FH);
+ int (*_fh_close)(FH);
+ int (*_fh_lseek)(FH, int, int);
+ int (*_fh_read)(FH, void*, int);
+ int (*_fh_write)(FH, const void*, int);
+ void (*_fh_hook)(FH, int, EventHook);
+} FHClassRec;
+
+static void _fh_file_init(FH);
+static int _fh_file_close(FH);
+static int _fh_file_lseek(FH, int, int);
+static int _fh_file_read(FH, void*, int);
+static int _fh_file_write(FH, const void*, int);
+static void _fh_file_hook(FH, int, EventHook);
+
+static const FHClassRec _fh_file_class = {
+ _fh_file_init,
+ _fh_file_close,
+ _fh_file_lseek,
+ _fh_file_read,
+ _fh_file_write,
+ _fh_file_hook
+};
+
+static void _fh_socket_init(FH);
+static int _fh_socket_close(FH);
+static int _fh_socket_lseek(FH, int, int);
+static int _fh_socket_read(FH, void*, int);
+static int _fh_socket_write(FH, const void*, int);
+static void _fh_socket_hook(FH, int, EventHook);
+
+static const FHClassRec _fh_socket_class = {
+ _fh_socket_init,
+ _fh_socket_close,
+ _fh_socket_lseek,
+ _fh_socket_read,
+ _fh_socket_write,
+ _fh_socket_hook
+};
+
+#define assert(cond) do { if (!(cond)) fatal( "assertion failed '%s' on %s:%ld\n", #cond, __FILE__, __LINE__ ); } while (0)
+
+/**************************************************************************/
+/**************************************************************************/
+/***** *****/
+/***** replaces libs/cutils/load_file.c *****/
+/***** *****/
+/**************************************************************************/
+/**************************************************************************/
+
+void *load_file(const char *fn, unsigned *_sz)
+{
+ HANDLE file;
+ char *data;
+ DWORD file_size;
+
+ file = CreateFile( fn,
+ GENERIC_READ,
+ FILE_SHARE_READ,
+ NULL,
+ OPEN_EXISTING,
+ 0,
+ NULL );
+
+ if (file == INVALID_HANDLE_VALUE)
+ return NULL;
+
+ file_size = GetFileSize( file, NULL );
+ data = NULL;
+
+ if (file_size > 0) {
+ data = (char*) malloc( file_size + 1 );
+ if (data == NULL) {
+ D("load_file: could not allocate %ld bytes\n", file_size );
+ file_size = 0;
+ } else {
+ DWORD out_bytes;
+
+ if ( !ReadFile( file, data, file_size, &out_bytes, NULL ) ||
+ out_bytes != file_size )
+ {
+ D("load_file: could not read %ld bytes from '%s'\n", file_size, fn);
+ free(data);
+ data = NULL;
+ file_size = 0;
+ }
+ }
+ }
+ CloseHandle( file );
+
+ *_sz = (unsigned) file_size;
+ return data;
+}
+
+/**************************************************************************/
+/**************************************************************************/
+/***** *****/
+/***** common file descriptor handling *****/
+/***** *****/
+/**************************************************************************/
+/**************************************************************************/
+
+/* used to emulate unix-domain socket pairs */
+typedef struct SocketPairRec_* SocketPair;
+
+typedef struct FHRec_
+{
+ FHClass clazz;
+ int used;
+ int eof;
+ union {
+ HANDLE handle;
+ SOCKET socket;
+ SocketPair pair;
+ } u;
+
+ HANDLE event;
+ int mask;
+
+ char name[32];
+
+} FHRec;
+
+#define fh_handle u.handle
+#define fh_socket u.socket
+#define fh_pair u.pair
+
+#define WIN32_FH_BASE 100
+
+#define WIN32_MAX_FHS 128
+
+static adb_mutex_t _win32_lock;
+static FHRec _win32_fhs[ WIN32_MAX_FHS ];
+static int _win32_fh_count;
+
+static FH
+_fh_from_int( int fd, const char* func )
+{
+ FH f;
+
+ fd -= WIN32_FH_BASE;
+
+ if (fd < 0 || fd >= _win32_fh_count) {
+ D( "_fh_from_int: invalid fd %d passed to %s\n", fd + WIN32_FH_BASE,
+ func );
+ errno = EBADF;
+ return NULL;
+ }
+
+ f = &_win32_fhs[fd];
+
+ if (f->used == 0) {
+ D( "_fh_from_int: invalid fd %d passed to %s\n", fd + WIN32_FH_BASE,
+ func );
+ errno = EBADF;
+ return NULL;
+ }
+
+ return f;
+}
+
+
+static int
+_fh_to_int( FH f )
+{
+ if (f && f->used && f >= _win32_fhs && f < _win32_fhs + WIN32_MAX_FHS)
+ return (int)(f - _win32_fhs) + WIN32_FH_BASE;
+
+ return -1;
+}
+
+static FH
+_fh_alloc( FHClass clazz )
+{
+ int nn;
+ FH f = NULL;
+
+ adb_mutex_lock( &_win32_lock );
+
+ if (_win32_fh_count < WIN32_MAX_FHS) {
+ f = &_win32_fhs[ _win32_fh_count++ ];
+ goto Exit;
+ }
+
+ for (nn = 0; nn < WIN32_MAX_FHS; nn++) {
+ if ( _win32_fhs[nn].clazz == NULL) {
+ f = &_win32_fhs[nn];
+ goto Exit;
+ }
+ }
+ D( "_fh_alloc: no more free file descriptors\n" );
+Exit:
+ if (f) {
+ f->clazz = clazz;
+ f->used = 1;
+ f->eof = 0;
+ clazz->_fh_init(f);
+ }
+ adb_mutex_unlock( &_win32_lock );
+ return f;
+}
+
+
+static int
+_fh_close( FH f )
+{
+ if ( f->used ) {
+ f->clazz->_fh_close( f );
+ f->used = 0;
+ f->eof = 0;
+ f->clazz = NULL;
+ }
+ return 0;
+}
+
+/**************************************************************************/
+/**************************************************************************/
+/***** *****/
+/***** file-based descriptor handling *****/
+/***** *****/
+/**************************************************************************/
+/**************************************************************************/
+
+static void _fh_file_init( FH f ) {
+ f->fh_handle = INVALID_HANDLE_VALUE;
+}
+
+static int _fh_file_close( FH f ) {
+ CloseHandle( f->fh_handle );
+ f->fh_handle = INVALID_HANDLE_VALUE;
+ return 0;
+}
+
+static int _fh_file_read( FH f, void* buf, int len ) {
+ DWORD read_bytes;
+
+ if ( !ReadFile( f->fh_handle, buf, (DWORD)len, &read_bytes, NULL ) ) {
+ D( "adb_read: could not read %d bytes from %s\n", len, f->name );
+ errno = EIO;
+ return -1;
+ } else if (read_bytes < (DWORD)len) {
+ f->eof = 1;
+ }
+ return (int)read_bytes;
+}
+
+static int _fh_file_write( FH f, const void* buf, int len ) {
+ DWORD wrote_bytes;
+
+ if ( !WriteFile( f->fh_handle, buf, (DWORD)len, &wrote_bytes, NULL ) ) {
+ D( "adb_file_write: could not write %d bytes from %s\n", len, f->name );
+ errno = EIO;
+ return -1;
+ } else if (wrote_bytes < (DWORD)len) {
+ f->eof = 1;
+ }
+ return (int)wrote_bytes;
+}
+
+static int _fh_file_lseek( FH f, int pos, int origin ) {
+ DWORD method;
+ DWORD result;
+
+ switch (origin)
+ {
+ case SEEK_SET: method = FILE_BEGIN; break;
+ case SEEK_CUR: method = FILE_CURRENT; break;
+ case SEEK_END: method = FILE_END; break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ result = SetFilePointer( f->fh_handle, pos, NULL, method );
+ if (result == INVALID_SET_FILE_POINTER) {
+ errno = EIO;
+ return -1;
+ } else {
+ f->eof = 0;
+ }
+ return (int)result;
+}
+
+
+/**************************************************************************/
+/**************************************************************************/
+/***** *****/
+/***** file-based descriptor handling *****/
+/***** *****/
+/**************************************************************************/
+/**************************************************************************/
+
+int adb_open(const char* path, int options)
+{
+ FH f;
+
+ DWORD desiredAccess = 0;
+ DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
+
+ switch (options) {
+ case O_RDONLY:
+ desiredAccess = GENERIC_READ;
+ break;
+ case O_WRONLY:
+ desiredAccess = GENERIC_WRITE;
+ break;
+ case O_RDWR:
+ desiredAccess = GENERIC_READ | GENERIC_WRITE;
+ break;
+ default:
+ D("adb_open: invalid options (0x%0x)\n", options);
+ errno = EINVAL;
+ return -1;
+ }
+
+ f = _fh_alloc( &_fh_file_class );
+ if ( !f ) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ f->fh_handle = CreateFile( path, desiredAccess, shareMode, NULL, OPEN_EXISTING,
+ 0, NULL );
+
+ if ( f->fh_handle == INVALID_HANDLE_VALUE ) {
+ const DWORD err = GetLastError();
+ _fh_close(f);
+ D( "adb_open: could not open '%s': ", path );
+ switch (err) {
+ case ERROR_FILE_NOT_FOUND:
+ D( "file not found\n" );
+ errno = ENOENT;
+ return -1;
+
+ case ERROR_PATH_NOT_FOUND:
+ D( "path not found\n" );
+ errno = ENOTDIR;
+ return -1;
+
+ default:
+ D( "unknown error: %ld\n", err );
+ errno = ENOENT;
+ return -1;
+ }
+ }
+
+ snprintf( f->name, sizeof(f->name), "%d(%s)", _fh_to_int(f), path );
+ D( "adb_open: '%s' => fd %d\n", path, _fh_to_int(f) );
+ return _fh_to_int(f);
+}
+
+/* ignore mode on Win32 */
+int adb_creat(const char* path, int mode)
+{
+ FH f;
+
+ f = _fh_alloc( &_fh_file_class );
+ if ( !f ) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ f->fh_handle = CreateFile( path, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
+ NULL );
+
+ if ( f->fh_handle == INVALID_HANDLE_VALUE ) {
+ const DWORD err = GetLastError();
+ _fh_close(f);
+ D( "adb_creat: could not open '%s': ", path );
+ switch (err) {
+ case ERROR_FILE_NOT_FOUND:
+ D( "file not found\n" );
+ errno = ENOENT;
+ return -1;
+
+ case ERROR_PATH_NOT_FOUND:
+ D( "path not found\n" );
+ errno = ENOTDIR;
+ return -1;
+
+ default:
+ D( "unknown error: %ld\n", err );
+ errno = ENOENT;
+ return -1;
+ }
+ }
+ snprintf( f->name, sizeof(f->name), "%d(%s)", _fh_to_int(f), path );
+ D( "adb_creat: '%s' => fd %d\n", path, _fh_to_int(f) );
+ return _fh_to_int(f);
+}
+
+
+int adb_read(int fd, void* buf, int len)
+{
+ FH f = _fh_from_int(fd, __func__);
+
+ if (f == NULL) {
+ return -1;
+ }
+
+ return f->clazz->_fh_read( f, buf, len );
+}
+
+
+int adb_write(int fd, const void* buf, int len)
+{
+ FH f = _fh_from_int(fd, __func__);
+
+ if (f == NULL) {
+ return -1;
+ }
+
+ return f->clazz->_fh_write(f, buf, len);
+}
+
+
+int adb_lseek(int fd, int pos, int where)
+{
+ FH f = _fh_from_int(fd, __func__);
+
+ if (!f) {
+ return -1;
+ }
+
+ return f->clazz->_fh_lseek(f, pos, where);
+}
+
+
+int adb_shutdown(int fd)
+{
+ FH f = _fh_from_int(fd, __func__);
+
+ if (!f || f->clazz != &_fh_socket_class) {
+ D("adb_shutdown: invalid fd %d\n", fd);
+ return -1;
+ }
+
+ D( "adb_shutdown: %s\n", f->name);
+ shutdown( f->fh_socket, SD_BOTH );
+ return 0;
+}
+
+
+int adb_close(int fd)
+{
+ FH f = _fh_from_int(fd, __func__);
+
+ if (!f) {
+ return -1;
+ }
+
+ D( "adb_close: %s\n", f->name);
+ _fh_close(f);
+ return 0;
+}
+
+/**************************************************************************/
+/**************************************************************************/
+/***** *****/
+/***** socket-based file descriptors *****/
+/***** *****/
+/**************************************************************************/
+/**************************************************************************/
+
+#undef setsockopt
+
+static void _socket_set_errno( void ) {
+ switch (WSAGetLastError()) {
+ case 0: errno = 0; break;
+ case WSAEWOULDBLOCK: errno = EAGAIN; break;
+ case WSAEINTR: errno = EINTR; break;
+ default:
+ D( "_socket_set_errno: unhandled value %d\n", WSAGetLastError() );
+ errno = EINVAL;
+ }
+}
+
+static void _fh_socket_init( FH f ) {
+ f->fh_socket = INVALID_SOCKET;
+ f->event = WSACreateEvent();
+ f->mask = 0;
+}
+
+static int _fh_socket_close( FH f ) {
+ /* gently tell any peer that we're closing the socket */
+ shutdown( f->fh_socket, SD_BOTH );
+ closesocket( f->fh_socket );
+ f->fh_socket = INVALID_SOCKET;
+ CloseHandle( f->event );
+ f->mask = 0;
+ return 0;
+}
+
+static int _fh_socket_lseek( FH f, int pos, int origin ) {
+ errno = EPIPE;
+ return -1;
+}
+
+static int _fh_socket_read(FH f, void* buf, int len) {
+ int result = recv(f->fh_socket, reinterpret_cast<char*>(buf), len, 0);
+ if (result == SOCKET_ERROR) {
+ _socket_set_errno();
+ result = -1;
+ }
+ return result;
+}
+
+static int _fh_socket_write(FH f, const void* buf, int len) {
+ int result = send(f->fh_socket, reinterpret_cast<const char*>(buf), len, 0);
+ if (result == SOCKET_ERROR) {
+ _socket_set_errno();
+ result = -1;
+ }
+ return result;
+}
+
+/**************************************************************************/
+/**************************************************************************/
+/***** *****/
+/***** replacement for libs/cutils/socket_xxxx.c *****/
+/***** *****/
+/**************************************************************************/
+/**************************************************************************/
+
+#include <winsock2.h>
+
+static int _winsock_init;
+
+static void
+_cleanup_winsock( void )
+{
+ WSACleanup();
+}
+
+static void
+_init_winsock( void )
+{
+ if (!_winsock_init) {
+ WSADATA wsaData;
+ int rc = WSAStartup( MAKEWORD(2,2), &wsaData);
+ if (rc != 0) {
+ fatal( "adb: could not initialize Winsock\n" );
+ }
+ atexit( _cleanup_winsock );
+ _winsock_init = 1;
+ }
+}
+
+int socket_loopback_client(int port, int type)
+{
+ FH f = _fh_alloc( &_fh_socket_class );
+ struct sockaddr_in addr;
+ SOCKET s;
+
+ if (!f)
+ return -1;
+
+ if (!_winsock_init)
+ _init_winsock();
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ s = socket(AF_INET, type, 0);
+ if(s == INVALID_SOCKET) {
+ D("socket_loopback_client: could not create socket\n" );
+ _fh_close(f);
+ return -1;
+ }
+
+ f->fh_socket = s;
+ if(connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ D("socket_loopback_client: could not connect to %s:%d\n", type != SOCK_STREAM ? "udp" : "tcp", port );
+ _fh_close(f);
+ return -1;
+ }
+ snprintf( f->name, sizeof(f->name), "%d(lo-client:%s%d)", _fh_to_int(f), type != SOCK_STREAM ? "udp:" : "", port );
+ D( "socket_loopback_client: port %d type %s => fd %d\n", port, type != SOCK_STREAM ? "udp" : "tcp", _fh_to_int(f) );
+ return _fh_to_int(f);
+}
+
+#define LISTEN_BACKLOG 4
+
+int socket_loopback_server(int port, int type)
+{
+ FH f = _fh_alloc( &_fh_socket_class );
+ struct sockaddr_in addr;
+ SOCKET s;
+ int n;
+
+ if (!f) {
+ return -1;
+ }
+
+ if (!_winsock_init)
+ _init_winsock();
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ s = socket(AF_INET, type, 0);
+ if(s == INVALID_SOCKET) return -1;
+
+ f->fh_socket = s;
+
+ n = 1;
+ setsockopt(s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (const char*)&n, sizeof(n));
+
+ if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ _fh_close(f);
+ return -1;
+ }
+ if (type == SOCK_STREAM) {
+ int ret;
+
+ ret = listen(s, LISTEN_BACKLOG);
+ if (ret < 0) {
+ _fh_close(f);
+ return -1;
+ }
+ }
+ snprintf( f->name, sizeof(f->name), "%d(lo-server:%s%d)", _fh_to_int(f), type != SOCK_STREAM ? "udp:" : "", port );
+ D( "socket_loopback_server: port %d type %s => fd %d\n", port, type != SOCK_STREAM ? "udp" : "tcp", _fh_to_int(f) );
+ return _fh_to_int(f);
+}
+
+
+int socket_network_client_timeout(const char *host, int port, int type, int timeout,
+ int* getaddrinfo_error) {
+ FH f = _fh_alloc( &_fh_socket_class );
+ if (!f) return -1;
+
+ if (!_winsock_init) _init_winsock();
+
+ hostent* hp = gethostbyname(host);
+ if(hp == 0) {
+ _fh_close(f);
+ return -1;
+ }
+
+ sockaddr_in addr;
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = hp->h_addrtype;
+ addr.sin_port = htons(port);
+ memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
+
+ SOCKET s = socket(hp->h_addrtype, type, 0);
+ if(s == INVALID_SOCKET) {
+ _fh_close(f);
+ return -1;
+ }
+ f->fh_socket = s;
+
+ // TODO: implement timeouts for Windows.
+
+ if(connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ _fh_close(f);
+ return -1;
+ }
+
+ snprintf( f->name, sizeof(f->name), "%d(net-client:%s%d)", _fh_to_int(f), type != SOCK_STREAM ? "udp:" : "", port );
+ D( "socket_network_client_timeout: host '%s' port %d type %s => fd %d\n", host, port, type != SOCK_STREAM ? "udp" : "tcp", _fh_to_int(f) );
+ return _fh_to_int(f);
+}
+
+
+int socket_inaddr_any_server(int port, int type)
+{
+ FH f = _fh_alloc( &_fh_socket_class );
+ struct sockaddr_in addr;
+ SOCKET s;
+ int n;
+
+ if (!f)
+ return -1;
+
+ if (!_winsock_init)
+ _init_winsock();
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ s = socket(AF_INET, type, 0);
+ if(s == INVALID_SOCKET) {
+ _fh_close(f);
+ return -1;
+ }
+
+ f->fh_socket = s;
+ n = 1;
+ setsockopt(s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (const char*)&n, sizeof(n));
+
+ if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ _fh_close(f);
+ return -1;
+ }
+
+ if (type == SOCK_STREAM) {
+ int ret;
+
+ ret = listen(s, LISTEN_BACKLOG);
+ if (ret < 0) {
+ _fh_close(f);
+ return -1;
+ }
+ }
+ snprintf( f->name, sizeof(f->name), "%d(any-server:%s%d)", _fh_to_int(f), type != SOCK_STREAM ? "udp:" : "", port );
+ D( "socket_inaddr_server: port %d type %s => fd %d\n", port, type != SOCK_STREAM ? "udp" : "tcp", _fh_to_int(f) );
+ return _fh_to_int(f);
+}
+
+#undef accept
+int adb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t *addrlen)
+{
+ FH serverfh = _fh_from_int(serverfd, __func__);
+ FH fh;
+
+ if ( !serverfh || serverfh->clazz != &_fh_socket_class ) {
+ D( "adb_socket_accept: invalid fd %d\n", serverfd );
+ return -1;
+ }
+
+ fh = _fh_alloc( &_fh_socket_class );
+ if (!fh) {
+ D( "adb_socket_accept: not enough memory to allocate accepted socket descriptor\n" );
+ return -1;
+ }
+
+ fh->fh_socket = accept( serverfh->fh_socket, addr, addrlen );
+ if (fh->fh_socket == INVALID_SOCKET) {
+ const DWORD err = WSAGetLastError();
+ _fh_close( fh );
+ D( "adb_socket_accept: accept on fd %d return error %ld\n", serverfd, err );
+ return -1;
+ }
+
+ snprintf( fh->name, sizeof(fh->name), "%d(accept:%s)", _fh_to_int(fh), serverfh->name );
+ D( "adb_socket_accept on fd %d returns fd %d\n", serverfd, _fh_to_int(fh) );
+ return _fh_to_int(fh);
+}
+
+
+int adb_setsockopt( int fd, int level, int optname, const void* optval, socklen_t optlen )
+{
+ FH fh = _fh_from_int(fd, __func__);
+
+ if ( !fh || fh->clazz != &_fh_socket_class ) {
+ D("adb_setsockopt: invalid fd %d\n", fd);
+ return -1;
+ }
+
+ return setsockopt( fh->fh_socket, level, optname, reinterpret_cast<const char*>(optval), optlen );
+}
+
+/**************************************************************************/
+/**************************************************************************/
+/***** *****/
+/***** emulated socketpairs *****/
+/***** *****/
+/**************************************************************************/
+/**************************************************************************/
+
+/* we implement socketpairs directly in use space for the following reasons:
+ * - it avoids copying data from/to the Nt kernel
+ * - it allows us to implement fdevent hooks easily and cheaply, something
+ * that is not possible with standard Win32 pipes !!
+ *
+ * basically, we use two circular buffers, each one corresponding to a given
+ * direction.
+ *
+ * each buffer is implemented as two regions:
+ *
+ * region A which is (a_start,a_end)
+ * region B which is (0, b_end) with b_end <= a_start
+ *
+ * an empty buffer has: a_start = a_end = b_end = 0
+ *
+ * a_start is the pointer where we start reading data
+ * a_end is the pointer where we start writing data, unless it is BUFFER_SIZE,
+ * then you start writing at b_end
+ *
+ * the buffer is full when b_end == a_start && a_end == BUFFER_SIZE
+ *
+ * there is room when b_end < a_start || a_end < BUFER_SIZE
+ *
+ * when reading, a_start is incremented, it a_start meets a_end, then
+ * we do: a_start = 0, a_end = b_end, b_end = 0, and keep going on..
+ */
+
+#define BIP_BUFFER_SIZE 4096
+
+#if 0
+#include <stdio.h>
+# define BIPD(x) D x
+# define BIPDUMP bip_dump_hex
+
+static void bip_dump_hex( const unsigned char* ptr, size_t len )
+{
+ int nn, len2 = len;
+
+ if (len2 > 8) len2 = 8;
+
+ for (nn = 0; nn < len2; nn++)
+ printf("%02x", ptr[nn]);
+ printf(" ");
+
+ for (nn = 0; nn < len2; nn++) {
+ int c = ptr[nn];
+ if (c < 32 || c > 127)
+ c = '.';
+ printf("%c", c);
+ }
+ printf("\n");
+ fflush(stdout);
+}
+
+#else
+# define BIPD(x) do {} while (0)
+# define BIPDUMP(p,l) BIPD(p)
+#endif
+
+typedef struct BipBufferRec_
+{
+ int a_start;
+ int a_end;
+ int b_end;
+ int fdin;
+ int fdout;
+ int closed;
+ int can_write; /* boolean */
+ HANDLE evt_write; /* event signaled when one can write to a buffer */
+ int can_read; /* boolean */
+ HANDLE evt_read; /* event signaled when one can read from a buffer */
+ CRITICAL_SECTION lock;
+ unsigned char buff[ BIP_BUFFER_SIZE ];
+
+} BipBufferRec, *BipBuffer;
+
+static void
+bip_buffer_init( BipBuffer buffer )
+{
+ D( "bit_buffer_init %p\n", buffer );
+ buffer->a_start = 0;
+ buffer->a_end = 0;
+ buffer->b_end = 0;
+ buffer->can_write = 1;
+ buffer->can_read = 0;
+ buffer->fdin = 0;
+ buffer->fdout = 0;
+ buffer->closed = 0;
+ buffer->evt_write = CreateEvent( NULL, TRUE, TRUE, NULL );
+ buffer->evt_read = CreateEvent( NULL, TRUE, FALSE, NULL );
+ InitializeCriticalSection( &buffer->lock );
+}
+
+static void
+bip_buffer_close( BipBuffer bip )
+{
+ bip->closed = 1;
+
+ if (!bip->can_read) {
+ SetEvent( bip->evt_read );
+ }
+ if (!bip->can_write) {
+ SetEvent( bip->evt_write );
+ }
+}
+
+static void
+bip_buffer_done( BipBuffer bip )
+{
+ BIPD(( "bip_buffer_done: %d->%d\n", bip->fdin, bip->fdout ));
+ CloseHandle( bip->evt_read );
+ CloseHandle( bip->evt_write );
+ DeleteCriticalSection( &bip->lock );
+}
+
+static int
+bip_buffer_write( BipBuffer bip, const void* src, int len )
+{
+ int avail, count = 0;
+
+ if (len <= 0)
+ return 0;
+
+ BIPD(( "bip_buffer_write: enter %d->%d len %d\n", bip->fdin, bip->fdout, len ));
+ BIPDUMP( src, len );
+
+ EnterCriticalSection( &bip->lock );
+
+ while (!bip->can_write) {
+ int ret;
+ LeaveCriticalSection( &bip->lock );
+
+ if (bip->closed) {
+ errno = EPIPE;
+ return -1;
+ }
+ /* spinlocking here is probably unfair, but let's live with it */
+ ret = WaitForSingleObject( bip->evt_write, INFINITE );
+ if (ret != WAIT_OBJECT_0) { /* buffer probably closed */
+ D( "bip_buffer_write: error %d->%d WaitForSingleObject returned %d, error %ld\n", bip->fdin, bip->fdout, ret, GetLastError() );
+ return 0;
+ }
+ if (bip->closed) {
+ errno = EPIPE;
+ return -1;
+ }
+ EnterCriticalSection( &bip->lock );
+ }
+
+ BIPD(( "bip_buffer_write: exec %d->%d len %d\n", bip->fdin, bip->fdout, len ));
+
+ avail = BIP_BUFFER_SIZE - bip->a_end;
+ if (avail > 0)
+ {
+ /* we can append to region A */
+ if (avail > len)
+ avail = len;
+
+ memcpy( bip->buff + bip->a_end, src, avail );
+ src = (const char *)src + avail;
+ count += avail;
+ len -= avail;
+
+ bip->a_end += avail;
+ if (bip->a_end == BIP_BUFFER_SIZE && bip->a_start == 0) {
+ bip->can_write = 0;
+ ResetEvent( bip->evt_write );
+ goto Exit;
+ }
+ }
+
+ if (len == 0)
+ goto Exit;
+
+ avail = bip->a_start - bip->b_end;
+ assert( avail > 0 ); /* since can_write is TRUE */
+
+ if (avail > len)
+ avail = len;
+
+ memcpy( bip->buff + bip->b_end, src, avail );
+ count += avail;
+ bip->b_end += avail;
+
+ if (bip->b_end == bip->a_start) {
+ bip->can_write = 0;
+ ResetEvent( bip->evt_write );
+ }
+
+Exit:
+ assert( count > 0 );
+
+ if ( !bip->can_read ) {
+ bip->can_read = 1;
+ SetEvent( bip->evt_read );
+ }
+
+ BIPD(( "bip_buffer_write: exit %d->%d count %d (as=%d ae=%d be=%d cw=%d cr=%d\n",
+ bip->fdin, bip->fdout, count, bip->a_start, bip->a_end, bip->b_end, bip->can_write, bip->can_read ));
+ LeaveCriticalSection( &bip->lock );
+
+ return count;
+ }
+
+static int
+bip_buffer_read( BipBuffer bip, void* dst, int len )
+{
+ int avail, count = 0;
+
+ if (len <= 0)
+ return 0;
+
+ BIPD(( "bip_buffer_read: enter %d->%d len %d\n", bip->fdin, bip->fdout, len ));
+
+ EnterCriticalSection( &bip->lock );
+ while ( !bip->can_read )
+ {
+#if 0
+ LeaveCriticalSection( &bip->lock );
+ errno = EAGAIN;
+ return -1;
+#else
+ int ret;
+ LeaveCriticalSection( &bip->lock );
+
+ if (bip->closed) {
+ errno = EPIPE;
+ return -1;
+ }
+
+ ret = WaitForSingleObject( bip->evt_read, INFINITE );
+ if (ret != WAIT_OBJECT_0) { /* probably closed buffer */
+ D( "bip_buffer_read: error %d->%d WaitForSingleObject returned %d, error %ld\n", bip->fdin, bip->fdout, ret, GetLastError());
+ return 0;
+ }
+ if (bip->closed) {
+ errno = EPIPE;
+ return -1;
+ }
+ EnterCriticalSection( &bip->lock );
+#endif
+ }
+
+ BIPD(( "bip_buffer_read: exec %d->%d len %d\n", bip->fdin, bip->fdout, len ));
+
+ avail = bip->a_end - bip->a_start;
+ assert( avail > 0 ); /* since can_read is TRUE */
+
+ if (avail > len)
+ avail = len;
+
+ memcpy( dst, bip->buff + bip->a_start, avail );
+ dst = (char *)dst + avail;
+ count += avail;
+ len -= avail;
+
+ bip->a_start += avail;
+ if (bip->a_start < bip->a_end)
+ goto Exit;
+
+ bip->a_start = 0;
+ bip->a_end = bip->b_end;
+ bip->b_end = 0;
+
+ avail = bip->a_end;
+ if (avail > 0) {
+ if (avail > len)
+ avail = len;
+ memcpy( dst, bip->buff, avail );
+ count += avail;
+ bip->a_start += avail;
+
+ if ( bip->a_start < bip->a_end )
+ goto Exit;
+
+ bip->a_start = bip->a_end = 0;
+ }
+
+ bip->can_read = 0;
+ ResetEvent( bip->evt_read );
+
+Exit:
+ assert( count > 0 );
+
+ if (!bip->can_write ) {
+ bip->can_write = 1;
+ SetEvent( bip->evt_write );
+ }
+
+ BIPDUMP( (const unsigned char*)dst - count, count );
+ BIPD(( "bip_buffer_read: exit %d->%d count %d (as=%d ae=%d be=%d cw=%d cr=%d\n",
+ bip->fdin, bip->fdout, count, bip->a_start, bip->a_end, bip->b_end, bip->can_write, bip->can_read ));
+ LeaveCriticalSection( &bip->lock );
+
+ return count;
+}
+
+typedef struct SocketPairRec_
+{
+ BipBufferRec a2b_bip;
+ BipBufferRec b2a_bip;
+ FH a_fd;
+ int used;
+
+} SocketPairRec;
+
+void _fh_socketpair_init( FH f )
+{
+ f->fh_pair = NULL;
+}
+
+static int
+_fh_socketpair_close( FH f )
+{
+ if ( f->fh_pair ) {
+ SocketPair pair = f->fh_pair;
+
+ if ( f == pair->a_fd ) {
+ pair->a_fd = NULL;
+ }
+
+ bip_buffer_close( &pair->b2a_bip );
+ bip_buffer_close( &pair->a2b_bip );
+
+ if ( --pair->used == 0 ) {
+ bip_buffer_done( &pair->b2a_bip );
+ bip_buffer_done( &pair->a2b_bip );
+ free( pair );
+ }
+ f->fh_pair = NULL;
+ }
+ return 0;
+}
+
+static int
+_fh_socketpair_lseek( FH f, int pos, int origin )
+{
+ errno = ESPIPE;
+ return -1;
+}
+
+static int
+_fh_socketpair_read( FH f, void* buf, int len )
+{
+ SocketPair pair = f->fh_pair;
+ BipBuffer bip;
+
+ if (!pair)
+ return -1;
+
+ if ( f == pair->a_fd )
+ bip = &pair->b2a_bip;
+ else
+ bip = &pair->a2b_bip;
+
+ return bip_buffer_read( bip, buf, len );
+}
+
+static int
+_fh_socketpair_write( FH f, const void* buf, int len )
+{
+ SocketPair pair = f->fh_pair;
+ BipBuffer bip;
+
+ if (!pair)
+ return -1;
+
+ if ( f == pair->a_fd )
+ bip = &pair->a2b_bip;
+ else
+ bip = &pair->b2a_bip;
+
+ return bip_buffer_write( bip, buf, len );
+}
+
+
+static void _fh_socketpair_hook( FH f, int event, EventHook hook ); /* forward */
+
+static const FHClassRec _fh_socketpair_class =
+{
+ _fh_socketpair_init,
+ _fh_socketpair_close,
+ _fh_socketpair_lseek,
+ _fh_socketpair_read,
+ _fh_socketpair_write,
+ _fh_socketpair_hook
+};
+
+
+int adb_socketpair(int sv[2]) {
+ SocketPair pair;
+
+ FH fa = _fh_alloc(&_fh_socketpair_class);
+ FH fb = _fh_alloc(&_fh_socketpair_class);
+
+ if (!fa || !fb)
+ goto Fail;
+
+ pair = reinterpret_cast<SocketPair>(malloc(sizeof(*pair)));
+ if (pair == NULL) {
+ D("adb_socketpair: not enough memory to allocate pipes\n" );
+ goto Fail;
+ }
+
+ bip_buffer_init( &pair->a2b_bip );
+ bip_buffer_init( &pair->b2a_bip );
+
+ fa->fh_pair = pair;
+ fb->fh_pair = pair;
+ pair->used = 2;
+ pair->a_fd = fa;
+
+ sv[0] = _fh_to_int(fa);
+ sv[1] = _fh_to_int(fb);
+
+ pair->a2b_bip.fdin = sv[0];
+ pair->a2b_bip.fdout = sv[1];
+ pair->b2a_bip.fdin = sv[1];
+ pair->b2a_bip.fdout = sv[0];
+
+ snprintf( fa->name, sizeof(fa->name), "%d(pair:%d)", sv[0], sv[1] );
+ snprintf( fb->name, sizeof(fb->name), "%d(pair:%d)", sv[1], sv[0] );
+ D( "adb_socketpair: returns (%d, %d)\n", sv[0], sv[1] );
+ return 0;
+
+Fail:
+ _fh_close(fb);
+ _fh_close(fa);
+ return -1;
+}
+
+/**************************************************************************/
+/**************************************************************************/
+/***** *****/
+/***** fdevents emulation *****/
+/***** *****/
+/***** this is a very simple implementation, we rely on the fact *****/
+/***** that ADB doesn't use FDE_ERROR. *****/
+/***** *****/
+/**************************************************************************/
+/**************************************************************************/
+
+#define FATAL(x...) fatal(__FUNCTION__, x)
+
+#if DEBUG
+static void dump_fde(fdevent *fde, const char *info)
+{
+ fprintf(stderr,"FDE #%03d %c%c%c %s\n", fde->fd,
+ fde->state & FDE_READ ? 'R' : ' ',
+ fde->state & FDE_WRITE ? 'W' : ' ',
+ fde->state & FDE_ERROR ? 'E' : ' ',
+ info);
+}
+#else
+#define dump_fde(fde, info) do { } while(0)
+#endif
+
+#define FDE_EVENTMASK 0x00ff
+#define FDE_STATEMASK 0xff00
+
+#define FDE_ACTIVE 0x0100
+#define FDE_PENDING 0x0200
+#define FDE_CREATED 0x0400
+
+static void fdevent_plist_enqueue(fdevent *node);
+static void fdevent_plist_remove(fdevent *node);
+static fdevent *fdevent_plist_dequeue(void);
+
+static fdevent list_pending = {
+ .next = &list_pending,
+ .prev = &list_pending,
+};
+
+static fdevent **fd_table = 0;
+static int fd_table_max = 0;
+
+typedef struct EventLooperRec_* EventLooper;
+
+typedef struct EventHookRec_
+{
+ EventHook next;
+ FH fh;
+ HANDLE h;
+ int wanted; /* wanted event flags */
+ int ready; /* ready event flags */
+ void* aux;
+ void (*prepare)( EventHook hook );
+ int (*start) ( EventHook hook );
+ void (*stop) ( EventHook hook );
+ int (*check) ( EventHook hook );
+ int (*peek) ( EventHook hook );
+} EventHookRec;
+
+static EventHook _free_hooks;
+
+static EventHook
+event_hook_alloc(FH fh) {
+ EventHook hook = _free_hooks;
+ if (hook != NULL) {
+ _free_hooks = hook->next;
+ } else {
+ hook = reinterpret_cast<EventHook>(malloc(sizeof(*hook)));
+ if (hook == NULL)
+ fatal( "could not allocate event hook\n" );
+ }
+ hook->next = NULL;
+ hook->fh = fh;
+ hook->wanted = 0;
+ hook->ready = 0;
+ hook->h = INVALID_HANDLE_VALUE;
+ hook->aux = NULL;
+
+ hook->prepare = NULL;
+ hook->start = NULL;
+ hook->stop = NULL;
+ hook->check = NULL;
+ hook->peek = NULL;
+
+ return hook;
+}
+
+static void
+event_hook_free( EventHook hook )
+{
+ hook->fh = NULL;
+ hook->wanted = 0;
+ hook->ready = 0;
+ hook->next = _free_hooks;
+ _free_hooks = hook;
+}
+
+
+static void
+event_hook_signal( EventHook hook )
+{
+ FH f = hook->fh;
+ int fd = _fh_to_int(f);
+ fdevent* fde = fd_table[ fd - WIN32_FH_BASE ];
+
+ if (fde != NULL && fde->fd == fd) {
+ if ((fde->state & FDE_PENDING) == 0) {
+ fde->state |= FDE_PENDING;
+ fdevent_plist_enqueue( fde );
+ }
+ fde->events |= hook->wanted;
+ }
+}
+
+
+#define MAX_LOOPER_HANDLES WIN32_MAX_FHS
+
+typedef struct EventLooperRec_
+{
+ EventHook hooks;
+ HANDLE htab[ MAX_LOOPER_HANDLES ];
+ int htab_count;
+
+} EventLooperRec;
+
+static EventHook*
+event_looper_find_p( EventLooper looper, FH fh )
+{
+ EventHook *pnode = &looper->hooks;
+ EventHook node = *pnode;
+ for (;;) {
+ if ( node == NULL || node->fh == fh )
+ break;
+ pnode = &node->next;
+ node = *pnode;
+ }
+ return pnode;
+}
+
+static void
+event_looper_hook( EventLooper looper, int fd, int events )
+{
+ FH f = _fh_from_int(fd, __func__);
+ EventHook *pnode;
+ EventHook node;
+
+ if (f == NULL) /* invalid arg */ {
+ D("event_looper_hook: invalid fd=%d\n", fd);
+ return;
+ }
+
+ pnode = event_looper_find_p( looper, f );
+ node = *pnode;
+ if ( node == NULL ) {
+ node = event_hook_alloc( f );
+ node->next = *pnode;
+ *pnode = node;
+ }
+
+ if ( (node->wanted & events) != events ) {
+ /* this should update start/stop/check/peek */
+ D("event_looper_hook: call hook for %d (new=%x, old=%x)\n",
+ fd, node->wanted, events);
+ f->clazz->_fh_hook( f, events & ~node->wanted, node );
+ node->wanted |= events;
+ } else {
+ D("event_looper_hook: ignoring events %x for %d wanted=%x)\n",
+ events, fd, node->wanted);
+ }
+}
+
+static void
+event_looper_unhook( EventLooper looper, int fd, int events )
+{
+ FH fh = _fh_from_int(fd, __func__);
+ EventHook *pnode = event_looper_find_p( looper, fh );
+ EventHook node = *pnode;
+
+ if (node != NULL) {
+ int events2 = events & node->wanted;
+ if ( events2 == 0 ) {
+ D( "event_looper_unhook: events %x not registered for fd %d\n", events, fd );
+ return;
+ }
+ node->wanted &= ~events2;
+ if (!node->wanted) {
+ *pnode = node->next;
+ event_hook_free( node );
+ }
+ }
+}
+
+/*
+ * A fixer for WaitForMultipleObjects on condition that there are more than 64
+ * handles to wait on.
+ *
+ * In cetain cases DDMS may establish more than 64 connections with ADB. For
+ * instance, this may happen if there are more than 64 processes running on a
+ * device, or there are multiple devices connected (including the emulator) with
+ * the combined number of running processes greater than 64. In this case using
+ * WaitForMultipleObjects to wait on connection events simply wouldn't cut,
+ * because of the API limitations (64 handles max). So, we need to provide a way
+ * to scale WaitForMultipleObjects to accept an arbitrary number of handles. The
+ * easiest (and "Microsoft recommended") way to do that would be dividing the
+ * handle array into chunks with the chunk size less than 64, and fire up as many
+ * waiting threads as there are chunks. Then each thread would wait on a chunk of
+ * handles, and will report back to the caller which handle has been set.
+ * Here is the implementation of that algorithm.
+ */
+
+/* Number of handles to wait on in each wating thread. */
+#define WAIT_ALL_CHUNK_SIZE 63
+
+/* Descriptor for a wating thread */
+typedef struct WaitForAllParam {
+ /* A handle to an event to signal when waiting is over. This handle is shared
+ * accross all the waiting threads, so each waiting thread knows when any
+ * other thread has exited, so it can exit too. */
+ HANDLE main_event;
+ /* Upon exit from a waiting thread contains the index of the handle that has
+ * been signaled. The index is an absolute index of the signaled handle in
+ * the original array. This pointer is shared accross all the waiting threads
+ * and it's not guaranteed (due to a race condition) that when all the
+ * waiting threads exit, the value contained here would indicate the first
+ * handle that was signaled. This is fine, because the caller cares only
+ * about any handle being signaled. It doesn't care about the order, nor
+ * about the whole list of handles that were signaled. */
+ LONG volatile *signaled_index;
+ /* Array of handles to wait on in a waiting thread. */
+ HANDLE* handles;
+ /* Number of handles in 'handles' array to wait on. */
+ int handles_count;
+ /* Index inside the main array of the first handle in the 'handles' array. */
+ int first_handle_index;
+ /* Waiting thread handle. */
+ HANDLE thread;
+} WaitForAllParam;
+
+/* Waiting thread routine. */
+static unsigned __stdcall
+_in_waiter_thread(void* arg)
+{
+ HANDLE wait_on[WAIT_ALL_CHUNK_SIZE + 1];
+ int res;
+ WaitForAllParam* const param = (WaitForAllParam*)arg;
+
+ /* We have to wait on the main_event in order to be notified when any of the
+ * sibling threads is exiting. */
+ wait_on[0] = param->main_event;
+ /* The rest of the handles go behind the main event handle. */
+ memcpy(wait_on + 1, param->handles, param->handles_count * sizeof(HANDLE));
+
+ res = WaitForMultipleObjects(param->handles_count + 1, wait_on, FALSE, INFINITE);
+ if (res > 0 && res < (param->handles_count + 1)) {
+ /* One of the original handles got signaled. Save its absolute index into
+ * the output variable. */
+ InterlockedCompareExchange(param->signaled_index,
+ res - 1L + param->first_handle_index, -1L);
+ }
+
+ /* Notify the caller (and the siblings) that the wait is over. */
+ SetEvent(param->main_event);
+
+ _endthreadex(0);
+ return 0;
+}
+
+/* WaitForMultipeObjects fixer routine.
+ * Param:
+ * handles Array of handles to wait on.
+ * handles_count Number of handles in the array.
+ * Return:
+ * (>= 0 && < handles_count) - Index of the signaled handle in the array, or
+ * WAIT_FAILED on an error.
+ */
+static int
+_wait_for_all(HANDLE* handles, int handles_count)
+{
+ WaitForAllParam* threads;
+ HANDLE main_event;
+ int chunks, chunk, remains;
+
+ /* This variable is going to be accessed by several threads at the same time,
+ * this is bound to fail randomly when the core is run on multi-core machines.
+ * To solve this, we need to do the following (1 _and_ 2):
+ * 1. Use the "volatile" qualifier to ensure the compiler doesn't optimize
+ * out the reads/writes in this function unexpectedly.
+ * 2. Ensure correct memory ordering. The "simple" way to do that is to wrap
+ * all accesses inside a critical section. But we can also use
+ * InterlockedCompareExchange() which always provide a full memory barrier
+ * on Win32.
+ */
+ volatile LONG sig_index = -1;
+
+ /* Calculate number of chunks, and allocate thread param array. */
+ chunks = handles_count / WAIT_ALL_CHUNK_SIZE;
+ remains = handles_count % WAIT_ALL_CHUNK_SIZE;
+ threads = (WaitForAllParam*)malloc((chunks + (remains ? 1 : 0)) *
+ sizeof(WaitForAllParam));
+ if (threads == NULL) {
+ D("Unable to allocate thread array for %d handles.\n", handles_count);
+ return (int)WAIT_FAILED;
+ }
+
+ /* Create main event to wait on for all waiting threads. This is a "manualy
+ * reset" event that will remain set once it was set. */
+ main_event = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (main_event == NULL) {
+ D("Unable to create main event. Error: %ld\n", GetLastError());
+ free(threads);
+ return (int)WAIT_FAILED;
+ }
+
+ /*
+ * Initialize waiting thread parameters.
+ */
+
+ for (chunk = 0; chunk < chunks; chunk++) {
+ threads[chunk].main_event = main_event;
+ threads[chunk].signaled_index = &sig_index;
+ threads[chunk].first_handle_index = WAIT_ALL_CHUNK_SIZE * chunk;
+ threads[chunk].handles = handles + threads[chunk].first_handle_index;
+ threads[chunk].handles_count = WAIT_ALL_CHUNK_SIZE;
+ }
+ if (remains) {
+ threads[chunk].main_event = main_event;
+ threads[chunk].signaled_index = &sig_index;
+ threads[chunk].first_handle_index = WAIT_ALL_CHUNK_SIZE * chunk;
+ threads[chunk].handles = handles + threads[chunk].first_handle_index;
+ threads[chunk].handles_count = remains;
+ chunks++;
+ }
+
+ /* Start the waiting threads. */
+ for (chunk = 0; chunk < chunks; chunk++) {
+ /* Note that using adb_thread_create is not appropriate here, since we
+ * need a handle to wait on for thread termination. */
+ threads[chunk].thread = (HANDLE)_beginthreadex(NULL, 0, _in_waiter_thread,
+ &threads[chunk], 0, NULL);
+ if (threads[chunk].thread == NULL) {
+ /* Unable to create a waiter thread. Collapse. */
+ D("Unable to create a waiting thread %d of %d. errno=%d\n",
+ chunk, chunks, errno);
+ chunks = chunk;
+ SetEvent(main_event);
+ break;
+ }
+ }
+
+ /* Wait on any of the threads to get signaled. */
+ WaitForSingleObject(main_event, INFINITE);
+
+ /* Wait on all the waiting threads to exit. */
+ for (chunk = 0; chunk < chunks; chunk++) {
+ WaitForSingleObject(threads[chunk].thread, INFINITE);
+ CloseHandle(threads[chunk].thread);
+ }
+
+ CloseHandle(main_event);
+ free(threads);
+
+
+ const int ret = (int)InterlockedCompareExchange(&sig_index, -1, -1);
+ return (ret >= 0) ? ret : (int)WAIT_FAILED;
+}
+
+static EventLooperRec win32_looper;
+
+static void fdevent_init(void)
+{
+ win32_looper.htab_count = 0;
+ win32_looper.hooks = NULL;
+}
+
+static void fdevent_connect(fdevent *fde)
+{
+ EventLooper looper = &win32_looper;
+ int events = fde->state & FDE_EVENTMASK;
+
+ if (events != 0)
+ event_looper_hook( looper, fde->fd, events );
+}
+
+static void fdevent_disconnect(fdevent *fde)
+{
+ EventLooper looper = &win32_looper;
+ int events = fde->state & FDE_EVENTMASK;
+
+ if (events != 0)
+ event_looper_unhook( looper, fde->fd, events );
+}
+
+static void fdevent_update(fdevent *fde, unsigned events)
+{
+ EventLooper looper = &win32_looper;
+ unsigned events0 = fde->state & FDE_EVENTMASK;
+
+ if (events != events0) {
+ int removes = events0 & ~events;
+ int adds = events & ~events0;
+ if (removes) {
+ D("fdevent_update: remove %x from %d\n", removes, fde->fd);
+ event_looper_unhook( looper, fde->fd, removes );
+ }
+ if (adds) {
+ D("fdevent_update: add %x to %d\n", adds, fde->fd);
+ event_looper_hook ( looper, fde->fd, adds );
+ }
+ }
+}
+
+static void fdevent_process()
+{
+ EventLooper looper = &win32_looper;
+ EventHook hook;
+ int gotone = 0;
+
+ /* if we have at least one ready hook, execute it/them */
+ for (hook = looper->hooks; hook; hook = hook->next) {
+ hook->ready = 0;
+ if (hook->prepare) {
+ hook->prepare(hook);
+ if (hook->ready != 0) {
+ event_hook_signal( hook );
+ gotone = 1;
+ }
+ }
+ }
+
+ /* nothing's ready yet, so wait for something to happen */
+ if (!gotone)
+ {
+ looper->htab_count = 0;
+
+ for (hook = looper->hooks; hook; hook = hook->next)
+ {
+ if (hook->start && !hook->start(hook)) {
+ D( "fdevent_process: error when starting a hook\n" );
+ return;
+ }
+ if (hook->h != INVALID_HANDLE_VALUE) {
+ int nn;
+
+ for (nn = 0; nn < looper->htab_count; nn++)
+ {
+ if ( looper->htab[nn] == hook->h )
+ goto DontAdd;
+ }
+ looper->htab[ looper->htab_count++ ] = hook->h;
+ DontAdd:
+ ;
+ }
+ }
+
+ if (looper->htab_count == 0) {
+ D( "fdevent_process: nothing to wait for !!\n" );
+ return;
+ }
+
+ do
+ {
+ int wait_ret;
+
+ D( "adb_win32: waiting for %d events\n", looper->htab_count );
+ if (looper->htab_count > MAXIMUM_WAIT_OBJECTS) {
+ D("handle count %d exceeds MAXIMUM_WAIT_OBJECTS.\n", looper->htab_count);
+ wait_ret = _wait_for_all(looper->htab, looper->htab_count);
+ } else {
+ wait_ret = WaitForMultipleObjects( looper->htab_count, looper->htab, FALSE, INFINITE );
+ }
+ if (wait_ret == (int)WAIT_FAILED) {
+ D( "adb_win32: wait failed, error %ld\n", GetLastError() );
+ } else {
+ D( "adb_win32: got one (index %d)\n", wait_ret );
+
+ /* according to Cygwin, some objects like consoles wake up on "inappropriate" events
+ * like mouse movements. we need to filter these with the "check" function
+ */
+ if ((unsigned)wait_ret < (unsigned)looper->htab_count)
+ {
+ for (hook = looper->hooks; hook; hook = hook->next)
+ {
+ if ( looper->htab[wait_ret] == hook->h &&
+ (!hook->check || hook->check(hook)) )
+ {
+ D( "adb_win32: signaling %s for %x\n", hook->fh->name, hook->ready );
+ event_hook_signal( hook );
+ gotone = 1;
+ break;
+ }
+ }
+ }
+ }
+ }
+ while (!gotone);
+
+ for (hook = looper->hooks; hook; hook = hook->next) {
+ if (hook->stop)
+ hook->stop( hook );
+ }
+ }
+
+ for (hook = looper->hooks; hook; hook = hook->next) {
+ if (hook->peek && hook->peek(hook))
+ event_hook_signal( hook );
+ }
+}
+
+
+static void fdevent_register(fdevent *fde)
+{
+ int fd = fde->fd - WIN32_FH_BASE;
+
+ if(fd < 0) {
+ FATAL("bogus negative fd (%d)\n", fde->fd);
+ }
+
+ if(fd >= fd_table_max) {
+ int oldmax = fd_table_max;
+ if(fde->fd > 32000) {
+ FATAL("bogus huuuuge fd (%d)\n", fde->fd);
+ }
+ if(fd_table_max == 0) {
+ fdevent_init();
+ fd_table_max = 256;
+ }
+ while(fd_table_max <= fd) {
+ fd_table_max *= 2;
+ }
+ fd_table = reinterpret_cast<fdevent**>(realloc(fd_table, sizeof(fdevent*) * fd_table_max));
+ if(fd_table == 0) {
+ FATAL("could not expand fd_table to %d entries\n", fd_table_max);
+ }
+ memset(fd_table + oldmax, 0, sizeof(int) * (fd_table_max - oldmax));
+ }
+
+ fd_table[fd] = fde;
+}
+
+static void fdevent_unregister(fdevent *fde)
+{
+ int fd = fde->fd - WIN32_FH_BASE;
+
+ if((fd < 0) || (fd >= fd_table_max)) {
+ FATAL("fd out of range (%d)\n", fde->fd);
+ }
+
+ if(fd_table[fd] != fde) {
+ FATAL("fd_table out of sync");
+ }
+
+ fd_table[fd] = 0;
+
+ if(!(fde->state & FDE_DONT_CLOSE)) {
+ dump_fde(fde, "close");
+ adb_close(fde->fd);
+ }
+}
+
+static void fdevent_plist_enqueue(fdevent *node)
+{
+ fdevent *list = &list_pending;
+
+ node->next = list;
+ node->prev = list->prev;
+ node->prev->next = node;
+ list->prev = node;
+}
+
+static void fdevent_plist_remove(fdevent *node)
+{
+ node->prev->next = node->next;
+ node->next->prev = node->prev;
+ node->next = 0;
+ node->prev = 0;
+}
+
+static fdevent *fdevent_plist_dequeue(void)
+{
+ fdevent *list = &list_pending;
+ fdevent *node = list->next;
+
+ if(node == list) return 0;
+
+ list->next = node->next;
+ list->next->prev = list;
+ node->next = 0;
+ node->prev = 0;
+
+ return node;
+}
+
+fdevent *fdevent_create(int fd, fd_func func, void *arg)
+{
+ fdevent *fde = (fdevent*) malloc(sizeof(fdevent));
+ if(fde == 0) return 0;
+ fdevent_install(fde, fd, func, arg);
+ fde->state |= FDE_CREATED;
+ return fde;
+}
+
+void fdevent_destroy(fdevent *fde)
+{
+ if(fde == 0) return;
+ if(!(fde->state & FDE_CREATED)) {
+ FATAL("fde %p not created by fdevent_create()\n", fde);
+ }
+ fdevent_remove(fde);
+}
+
+void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg)
+{
+ memset(fde, 0, sizeof(fdevent));
+ fde->state = FDE_ACTIVE;
+ fde->fd = fd;
+ fde->func = func;
+ fde->arg = arg;
+
+ fdevent_register(fde);
+ dump_fde(fde, "connect");
+ fdevent_connect(fde);
+ fde->state |= FDE_ACTIVE;
+}
+
+void fdevent_remove(fdevent *fde)
+{
+ if(fde->state & FDE_PENDING) {
+ fdevent_plist_remove(fde);
+ }
+
+ if(fde->state & FDE_ACTIVE) {
+ fdevent_disconnect(fde);
+ dump_fde(fde, "disconnect");
+ fdevent_unregister(fde);
+ }
+
+ fde->state = 0;
+ fde->events = 0;
+}
+
+
+void fdevent_set(fdevent *fde, unsigned events)
+{
+ events &= FDE_EVENTMASK;
+
+ if((fde->state & FDE_EVENTMASK) == (int)events) return;
+
+ if(fde->state & FDE_ACTIVE) {
+ fdevent_update(fde, events);
+ dump_fde(fde, "update");
+ }
+
+ fde->state = (fde->state & FDE_STATEMASK) | events;
+
+ if(fde->state & FDE_PENDING) {
+ /* if we're pending, make sure
+ ** we don't signal an event that
+ ** is no longer wanted.
+ */
+ fde->events &= (~events);
+ if(fde->events == 0) {
+ fdevent_plist_remove(fde);
+ fde->state &= (~FDE_PENDING);
+ }
+ }
+}
+
+void fdevent_add(fdevent *fde, unsigned events)
+{
+ fdevent_set(
+ fde, (fde->state & FDE_EVENTMASK) | (events & FDE_EVENTMASK));
+}
+
+void fdevent_del(fdevent *fde, unsigned events)
+{
+ fdevent_set(
+ fde, (fde->state & FDE_EVENTMASK) & (~(events & FDE_EVENTMASK)));
+}
+
+void fdevent_loop()
+{
+ fdevent *fde;
+
+ for(;;) {
+#if DEBUG
+ fprintf(stderr,"--- ---- waiting for events\n");
+#endif
+ fdevent_process();
+
+ while((fde = fdevent_plist_dequeue())) {
+ unsigned events = fde->events;
+ fde->events = 0;
+ fde->state &= (~FDE_PENDING);
+ dump_fde(fde, "callback");
+ fde->func(fde->fd, events, fde->arg);
+ }
+ }
+}
+
+/** FILE EVENT HOOKS
+ **/
+
+static void _event_file_prepare( EventHook hook )
+{
+ if (hook->wanted & (FDE_READ|FDE_WRITE)) {
+ /* we can always read/write */
+ hook->ready |= hook->wanted & (FDE_READ|FDE_WRITE);
+ }
+}
+
+static int _event_file_peek( EventHook hook )
+{
+ return (hook->wanted & (FDE_READ|FDE_WRITE));
+}
+
+static void _fh_file_hook( FH f, int events, EventHook hook )
+{
+ hook->h = f->fh_handle;
+ hook->prepare = _event_file_prepare;
+ hook->peek = _event_file_peek;
+}
+
+/** SOCKET EVENT HOOKS
+ **/
+
+static void _event_socket_verify( EventHook hook, WSANETWORKEVENTS* evts )
+{
+ if ( evts->lNetworkEvents & (FD_READ|FD_ACCEPT|FD_CLOSE) ) {
+ if (hook->wanted & FDE_READ)
+ hook->ready |= FDE_READ;
+ if ((evts->iErrorCode[FD_READ] != 0) && hook->wanted & FDE_ERROR)
+ hook->ready |= FDE_ERROR;
+ }
+ if ( evts->lNetworkEvents & (FD_WRITE|FD_CONNECT|FD_CLOSE) ) {
+ if (hook->wanted & FDE_WRITE)
+ hook->ready |= FDE_WRITE;
+ if ((evts->iErrorCode[FD_WRITE] != 0) && hook->wanted & FDE_ERROR)
+ hook->ready |= FDE_ERROR;
+ }
+ if ( evts->lNetworkEvents & FD_OOB ) {
+ if (hook->wanted & FDE_ERROR)
+ hook->ready |= FDE_ERROR;
+ }
+}
+
+static void _event_socket_prepare( EventHook hook )
+{
+ WSANETWORKEVENTS evts;
+
+ /* look if some of the events we want already happened ? */
+ if (!WSAEnumNetworkEvents( hook->fh->fh_socket, NULL, &evts ))
+ _event_socket_verify( hook, &evts );
+}
+
+static int _socket_wanted_to_flags( int wanted )
+{
+ int flags = 0;
+ if (wanted & FDE_READ)
+ flags |= FD_READ | FD_ACCEPT | FD_CLOSE;
+
+ if (wanted & FDE_WRITE)
+ flags |= FD_WRITE | FD_CONNECT | FD_CLOSE;
+
+ if (wanted & FDE_ERROR)
+ flags |= FD_OOB;
+
+ return flags;
+}
+
+static int _event_socket_start( EventHook hook )
+{
+ /* create an event which we're going to wait for */
+ FH fh = hook->fh;
+ long flags = _socket_wanted_to_flags( hook->wanted );
+
+ hook->h = fh->event;
+ if (hook->h == INVALID_HANDLE_VALUE) {
+ D( "_event_socket_start: no event for %s\n", fh->name );
+ return 0;
+ }
+
+ if ( flags != fh->mask ) {
+ D( "_event_socket_start: hooking %s for %x (flags %ld)\n", hook->fh->name, hook->wanted, flags );
+ if ( WSAEventSelect( fh->fh_socket, hook->h, flags ) ) {
+ D( "_event_socket_start: WSAEventSelect() for %s failed, error %d\n", hook->fh->name, WSAGetLastError() );
+ CloseHandle( hook->h );
+ hook->h = INVALID_HANDLE_VALUE;
+ exit(1);
+ return 0;
+ }
+ fh->mask = flags;
+ }
+ return 1;
+}
+
+static void _event_socket_stop( EventHook hook )
+{
+ hook->h = INVALID_HANDLE_VALUE;
+}
+
+static int _event_socket_check( EventHook hook )
+{
+ int result = 0;
+ FH fh = hook->fh;
+ WSANETWORKEVENTS evts;
+
+ if (!WSAEnumNetworkEvents( fh->fh_socket, hook->h, &evts ) ) {
+ _event_socket_verify( hook, &evts );
+ result = (hook->ready != 0);
+ if (result) {
+ ResetEvent( hook->h );
+ }
+ }
+ D( "_event_socket_check %s returns %d\n", fh->name, result );
+ return result;
+}
+
+static int _event_socket_peek( EventHook hook )
+{
+ WSANETWORKEVENTS evts;
+ FH fh = hook->fh;
+
+ /* look if some of the events we want already happened ? */
+ if (!WSAEnumNetworkEvents( fh->fh_socket, NULL, &evts )) {
+ _event_socket_verify( hook, &evts );
+ if (hook->ready)
+ ResetEvent( hook->h );
+ }
+
+ return hook->ready != 0;
+}
+
+
+
+static void _fh_socket_hook( FH f, int events, EventHook hook )
+{
+ hook->prepare = _event_socket_prepare;
+ hook->start = _event_socket_start;
+ hook->stop = _event_socket_stop;
+ hook->check = _event_socket_check;
+ hook->peek = _event_socket_peek;
+
+ _event_socket_start( hook );
+}
+
+/** SOCKETPAIR EVENT HOOKS
+ **/
+
+static void _event_socketpair_prepare( EventHook hook )
+{
+ FH fh = hook->fh;
+ SocketPair pair = fh->fh_pair;
+ BipBuffer rbip = (pair->a_fd == fh) ? &pair->b2a_bip : &pair->a2b_bip;
+ BipBuffer wbip = (pair->a_fd == fh) ? &pair->a2b_bip : &pair->b2a_bip;
+
+ if (hook->wanted & FDE_READ && rbip->can_read)
+ hook->ready |= FDE_READ;
+
+ if (hook->wanted & FDE_WRITE && wbip->can_write)
+ hook->ready |= FDE_WRITE;
+ }
+
+ static int _event_socketpair_start( EventHook hook )
+ {
+ FH fh = hook->fh;
+ SocketPair pair = fh->fh_pair;
+ BipBuffer rbip = (pair->a_fd == fh) ? &pair->b2a_bip : &pair->a2b_bip;
+ BipBuffer wbip = (pair->a_fd == fh) ? &pair->a2b_bip : &pair->b2a_bip;
+
+ if (hook->wanted == FDE_READ)
+ hook->h = rbip->evt_read;
+
+ else if (hook->wanted == FDE_WRITE)
+ hook->h = wbip->evt_write;
+
+ else {
+ D("_event_socketpair_start: can't handle FDE_READ+FDE_WRITE\n" );
+ return 0;
+ }
+ D( "_event_socketpair_start: hook %s for %x wanted=%x\n",
+ hook->fh->name, _fh_to_int(fh), hook->wanted);
+ return 1;
+}
+
+static int _event_socketpair_peek( EventHook hook )
+{
+ _event_socketpair_prepare( hook );
+ return hook->ready != 0;
+}
+
+static void _fh_socketpair_hook( FH fh, int events, EventHook hook )
+{
+ hook->prepare = _event_socketpair_prepare;
+ hook->start = _event_socketpair_start;
+ hook->peek = _event_socketpair_peek;
+}
+
+
+void
+adb_sysdeps_init( void )
+{
+#define ADB_MUTEX(x) InitializeCriticalSection( & x );
+#include "mutex_list.h"
+ InitializeCriticalSection( &_win32_lock );
+}
+
+/**************************************************************************/
+/**************************************************************************/
+/***** *****/
+/***** Console Window Terminal Emulation *****/
+/***** *****/
+/**************************************************************************/
+/**************************************************************************/
+
+// This reads input from a Win32 console window and translates it into Unix
+// terminal-style sequences. This emulates mostly Gnome Terminal (in Normal
+// mode, not Application mode), which itself emulates xterm. Gnome Terminal
+// is emulated instead of xterm because it is probably more popular than xterm:
+// Ubuntu's default Ctrl-Alt-T shortcut opens Gnome Terminal, Gnome Terminal
+// supports modern fonts, etc. It seems best to emulate the terminal that most
+// Android developers use because they'll fix apps (the shell, etc.) to keep
+// working with that terminal's emulation.
+//
+// The point of this emulation is not to be perfect or to solve all issues with
+// console windows on Windows, but to be better than the original code which
+// just called read() (which called ReadFile(), which called ReadConsoleA())
+// which did not support Ctrl-C, tab completion, shell input line editing
+// keys, server echo, and more.
+//
+// This implementation reconfigures the console with SetConsoleMode(), then
+// calls ReadConsoleInput() to get raw input which it remaps to Unix
+// terminal-style sequences which is returned via unix_read() which is used
+// by the 'adb shell' command.
+//
+// Code organization:
+//
+// * stdin_raw_init() and stdin_raw_restore() reconfigure the console.
+// * unix_read() detects console windows (as opposed to pipes, files, etc.).
+// * _console_read() is the main code of the emulation.
+
+
+// Read an input record from the console; one that should be processed.
+static bool _get_interesting_input_record_uncached(const HANDLE console,
+ INPUT_RECORD* const input_record) {
+ for (;;) {
+ DWORD read_count = 0;
+ memset(input_record, 0, sizeof(*input_record));
+ if (!ReadConsoleInputA(console, input_record, 1, &read_count)) {
+ D("_get_interesting_input_record_uncached: ReadConsoleInputA() "
+ "failure, error %ld\n", GetLastError());
+ errno = EIO;
+ return false;
+ }
+
+ if (read_count == 0) { // should be impossible
+ fatal("ReadConsoleInputA returned 0");
+ }
+
+ if (read_count != 1) { // should be impossible
+ fatal("ReadConsoleInputA did not return one input record");
+ }
+
+ if ((input_record->EventType == KEY_EVENT) &&
+ (input_record->Event.KeyEvent.bKeyDown)) {
+ if (input_record->Event.KeyEvent.wRepeatCount == 0) {
+ fatal("ReadConsoleInputA returned a key event with zero repeat"
+ " count");
+ }
+
+ // Got an interesting INPUT_RECORD, so return
+ return true;
+ }
+ }
+}
+
+// Cached input record (in case _console_read() is passed a buffer that doesn't
+// have enough space to fit wRepeatCount number of key sequences). A non-zero
+// wRepeatCount indicates that a record is cached.
+static INPUT_RECORD _win32_input_record;
+
+// Get the next KEY_EVENT_RECORD that should be processed.
+static KEY_EVENT_RECORD* _get_key_event_record(const HANDLE console) {
+ // If nothing cached, read directly from the console until we get an
+ // interesting record.
+ if (_win32_input_record.Event.KeyEvent.wRepeatCount == 0) {
+ if (!_get_interesting_input_record_uncached(console,
+ &_win32_input_record)) {
+ // There was an error, so make sure wRepeatCount is zero because
+ // that signifies no cached input record.
+ _win32_input_record.Event.KeyEvent.wRepeatCount = 0;
+ return NULL;
+ }
+ }
+
+ return &_win32_input_record.Event.KeyEvent;
+}
+
+static __inline__ bool _is_shift_pressed(const DWORD control_key_state) {
+ return (control_key_state & SHIFT_PRESSED) != 0;
+}
+
+static __inline__ bool _is_ctrl_pressed(const DWORD control_key_state) {
+ return (control_key_state & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) != 0;
+}
+
+static __inline__ bool _is_alt_pressed(const DWORD control_key_state) {
+ return (control_key_state & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) != 0;
+}
+
+static __inline__ bool _is_numlock_on(const DWORD control_key_state) {
+ return (control_key_state & NUMLOCK_ON) != 0;
+}
+
+static __inline__ bool _is_capslock_on(const DWORD control_key_state) {
+ return (control_key_state & CAPSLOCK_ON) != 0;
+}
+
+static __inline__ bool _is_enhanced_key(const DWORD control_key_state) {
+ return (control_key_state & ENHANCED_KEY) != 0;
+}
+
+// Constants from MSDN for ToAscii().
+static const BYTE TOASCII_KEY_OFF = 0x00;
+static const BYTE TOASCII_KEY_DOWN = 0x80;
+static const BYTE TOASCII_KEY_TOGGLED_ON = 0x01; // for CapsLock
+
+// Given a key event, ignore a modifier key and return the character that was
+// entered without the modifier. Writes to *ch and returns the number of bytes
+// written.
+static size_t _get_char_ignoring_modifier(char* const ch,
+ const KEY_EVENT_RECORD* const key_event, const DWORD control_key_state,
+ const WORD modifier) {
+ // If there is no character from Windows, try ignoring the specified
+ // modifier and look for a character. Note that if AltGr is being used,
+ // there will be a character from Windows.
+ if (key_event->uChar.AsciiChar == '\0') {
+ // Note that we read the control key state from the passed in argument
+ // instead of from key_event since the argument has been normalized.
+ if (((modifier == VK_SHIFT) &&
+ _is_shift_pressed(control_key_state)) ||
+ ((modifier == VK_CONTROL) &&
+ _is_ctrl_pressed(control_key_state)) ||
+ ((modifier == VK_MENU) && _is_alt_pressed(control_key_state))) {
+
+ BYTE key_state[256] = {0};
+ key_state[VK_SHIFT] = _is_shift_pressed(control_key_state) ?
+ TOASCII_KEY_DOWN : TOASCII_KEY_OFF;
+ key_state[VK_CONTROL] = _is_ctrl_pressed(control_key_state) ?
+ TOASCII_KEY_DOWN : TOASCII_KEY_OFF;
+ key_state[VK_MENU] = _is_alt_pressed(control_key_state) ?
+ TOASCII_KEY_DOWN : TOASCII_KEY_OFF;
+ key_state[VK_CAPITAL] = _is_capslock_on(control_key_state) ?
+ TOASCII_KEY_TOGGLED_ON : TOASCII_KEY_OFF;
+
+ // cause this modifier to be ignored
+ key_state[modifier] = TOASCII_KEY_OFF;
+
+ WORD translated = 0;
+ if (ToAscii(key_event->wVirtualKeyCode,
+ key_event->wVirtualScanCode, key_state, &translated, 0) == 1) {
+ // Ignoring the modifier, we found a character.
+ *ch = (CHAR)translated;
+ return 1;
+ }
+ }
+ }
+
+ // Just use whatever Windows told us originally.
+ *ch = key_event->uChar.AsciiChar;
+
+ // If the character from Windows is NULL, return a size of zero.
+ return (*ch == '\0') ? 0 : 1;
+}
+
+// If a Ctrl key is pressed, lookup the character, ignoring the Ctrl key,
+// but taking into account the shift key. This is because for a sequence like
+// Ctrl-Alt-0, we want to find the character '0' and for Ctrl-Alt-Shift-0,
+// we want to find the character ')'.
+//
+// Note that Windows doesn't seem to pass bKeyDown for Ctrl-Shift-NoAlt-0
+// because it is the default key-sequence to switch the input language.
+// This is configurable in the Region and Language control panel.
+static __inline__ size_t _get_non_control_char(char* const ch,
+ const KEY_EVENT_RECORD* const key_event, const DWORD control_key_state) {
+ return _get_char_ignoring_modifier(ch, key_event, control_key_state,
+ VK_CONTROL);
+}
+
+// Get without Alt.
+static __inline__ size_t _get_non_alt_char(char* const ch,
+ const KEY_EVENT_RECORD* const key_event, const DWORD control_key_state) {
+ return _get_char_ignoring_modifier(ch, key_event, control_key_state,
+ VK_MENU);
+}
+
+// Ignore the control key, find the character from Windows, and apply any
+// Control key mappings (for example, Ctrl-2 is a NULL character). Writes to
+// *pch and returns number of bytes written.
+static size_t _get_control_character(char* const pch,
+ const KEY_EVENT_RECORD* const key_event, const DWORD control_key_state) {
+ const size_t len = _get_non_control_char(pch, key_event,
+ control_key_state);
+
+ if ((len == 1) && _is_ctrl_pressed(control_key_state)) {
+ char ch = *pch;
+ switch (ch) {
+ case '2':
+ case '@':
+ case '`':
+ ch = '\0';
+ break;
+ case '3':
+ case '[':
+ case '{':
+ ch = '\x1b';
+ break;
+ case '4':
+ case '\\':
+ case '|':
+ ch = '\x1c';
+ break;
+ case '5':
+ case ']':
+ case '}':
+ ch = '\x1d';
+ break;
+ case '6':
+ case '^':
+ case '~':
+ ch = '\x1e';
+ break;
+ case '7':
+ case '-':
+ case '_':
+ ch = '\x1f';
+ break;
+ case '8':
+ ch = '\x7f';
+ break;
+ case '/':
+ if (!_is_alt_pressed(control_key_state)) {
+ ch = '\x1f';
+ }
+ break;
+ case '?':
+ if (!_is_alt_pressed(control_key_state)) {
+ ch = '\x7f';
+ }
+ break;
+ }
+ *pch = ch;
+ }
+
+ return len;
+}
+
+static DWORD _normalize_altgr_control_key_state(
+ const KEY_EVENT_RECORD* const key_event) {
+ DWORD control_key_state = key_event->dwControlKeyState;
+
+ // If we're in an AltGr situation where the AltGr key is down (depending on
+ // the keyboard layout, that might be the physical right alt key which
+ // produces a control_key_state where Right-Alt and Left-Ctrl are down) or
+ // AltGr-equivalent keys are down (any Ctrl key + any Alt key), and we have
+ // a character (which indicates that there was an AltGr mapping), then act
+ // as if alt and control are not really down for the purposes of modifiers.
+ // This makes it so that if the user with, say, a German keyboard layout
+ // presses AltGr-] (which we see as Right-Alt + Left-Ctrl + key), we just
+ // output the key and we don't see the Alt and Ctrl keys.
+ if (_is_ctrl_pressed(control_key_state) &&
+ _is_alt_pressed(control_key_state)
+ && (key_event->uChar.AsciiChar != '\0')) {
+ // Try to remove as few bits as possible to improve our chances of
+ // detecting combinations like Left-Alt + AltGr, Right-Ctrl + AltGr, or
+ // Left-Alt + Right-Ctrl + AltGr.
+ if ((control_key_state & RIGHT_ALT_PRESSED) != 0) {
+ // Remove Right-Alt.
+ control_key_state &= ~RIGHT_ALT_PRESSED;
+ // If uChar is set, a Ctrl key is pressed, and Right-Alt is
+ // pressed, Left-Ctrl is almost always set, except if the user
+ // presses Right-Ctrl, then AltGr (in that specific order) for
+ // whatever reason. At any rate, make sure the bit is not set.
+ control_key_state &= ~LEFT_CTRL_PRESSED;
+ } else if ((control_key_state & LEFT_ALT_PRESSED) != 0) {
+ // Remove Left-Alt.
+ control_key_state &= ~LEFT_ALT_PRESSED;
+ // Whichever Ctrl key is down, remove it from the state. We only
+ // remove one key, to improve our chances of detecting the
+ // corner-case of Left-Ctrl + Left-Alt + Right-Ctrl.
+ if ((control_key_state & LEFT_CTRL_PRESSED) != 0) {
+ // Remove Left-Ctrl.
+ control_key_state &= ~LEFT_CTRL_PRESSED;
+ } else if ((control_key_state & RIGHT_CTRL_PRESSED) != 0) {
+ // Remove Right-Ctrl.
+ control_key_state &= ~RIGHT_CTRL_PRESSED;
+ }
+ }
+
+ // Note that this logic isn't 100% perfect because Windows doesn't
+ // allow us to detect all combinations because a physical AltGr key
+ // press shows up as two bits, plus some combinations are ambiguous
+ // about what is actually physically pressed.
+ }
+
+ return control_key_state;
+}
+
+// If NumLock is on and Shift is pressed, SHIFT_PRESSED is not set in
+// dwControlKeyState for the following keypad keys: period, 0-9. If we detect
+// this scenario, set the SHIFT_PRESSED bit so we can add modifiers
+// appropriately.
+static DWORD _normalize_keypad_control_key_state(const WORD vk,
+ const DWORD control_key_state) {
+ if (!_is_numlock_on(control_key_state)) {
+ return control_key_state;
+ }
+ if (!_is_enhanced_key(control_key_state)) {
+ switch (vk) {
+ case VK_INSERT: // 0
+ case VK_DELETE: // .
+ case VK_END: // 1
+ case VK_DOWN: // 2
+ case VK_NEXT: // 3
+ case VK_LEFT: // 4
+ case VK_CLEAR: // 5
+ case VK_RIGHT: // 6
+ case VK_HOME: // 7
+ case VK_UP: // 8
+ case VK_PRIOR: // 9
+ return control_key_state | SHIFT_PRESSED;
+ }
+ }
+
+ return control_key_state;
+}
+
+static const char* _get_keypad_sequence(const DWORD control_key_state,
+ const char* const normal, const char* const shifted) {
+ if (_is_shift_pressed(control_key_state)) {
+ // Shift is pressed and NumLock is off
+ return shifted;
+ } else {
+ // Shift is not pressed and NumLock is off, or,
+ // Shift is pressed and NumLock is on, in which case we want the
+ // NumLock and Shift to neutralize each other, thus, we want the normal
+ // sequence.
+ return normal;
+ }
+ // If Shift is not pressed and NumLock is on, a different virtual key code
+ // is returned by Windows, which can be taken care of by a different case
+ // statement in _console_read().
+}
+
+// Write sequence to buf and return the number of bytes written.
+static size_t _get_modifier_sequence(char* const buf, const WORD vk,
+ DWORD control_key_state, const char* const normal) {
+ // Copy the base sequence into buf.
+ const size_t len = strlen(normal);
+ memcpy(buf, normal, len);
+
+ int code = 0;
+
+ control_key_state = _normalize_keypad_control_key_state(vk,
+ control_key_state);
+
+ if (_is_shift_pressed(control_key_state)) {
+ code |= 0x1;
+ }
+ if (_is_alt_pressed(control_key_state)) { // any alt key pressed
+ code |= 0x2;
+ }
+ if (_is_ctrl_pressed(control_key_state)) { // any control key pressed
+ code |= 0x4;
+ }
+ // If some modifier was held down, then we need to insert the modifier code
+ if (code != 0) {
+ if (len == 0) {
+ // Should be impossible because caller should pass a string of
+ // non-zero length.
+ return 0;
+ }
+ size_t index = len - 1;
+ const char lastChar = buf[index];
+ if (lastChar != '~') {
+ buf[index++] = '1';
+ }
+ buf[index++] = ';'; // modifier separator
+ // 2 = shift, 3 = alt, 4 = shift & alt, 5 = control,
+ // 6 = shift & control, 7 = alt & control, 8 = shift & alt & control
+ buf[index++] = '1' + code;
+ buf[index++] = lastChar; // move ~ (or other last char) to the end
+ return index;
+ }
+ return len;
+}
+
+// Write sequence to buf and return the number of bytes written.
+static size_t _get_modifier_keypad_sequence(char* const buf, const WORD vk,
+ const DWORD control_key_state, const char* const normal,
+ const char shifted) {
+ if (_is_shift_pressed(control_key_state)) {
+ // Shift is pressed and NumLock is off
+ if (shifted != '\0') {
+ buf[0] = shifted;
+ return sizeof(buf[0]);
+ } else {
+ return 0;
+ }
+ } else {
+ // Shift is not pressed and NumLock is off, or,
+ // Shift is pressed and NumLock is on, in which case we want the
+ // NumLock and Shift to neutralize each other, thus, we want the normal
+ // sequence.
+ return _get_modifier_sequence(buf, vk, control_key_state, normal);
+ }
+ // If Shift is not pressed and NumLock is on, a different virtual key code
+ // is returned by Windows, which can be taken care of by a different case
+ // statement in _console_read().
+}
+
+// The decimal key on the keypad produces a '.' for U.S. English and a ',' for
+// Standard German. Figure this out at runtime so we know what to output for
+// Shift-VK_DELETE.
+static char _get_decimal_char() {
+ return (char)MapVirtualKeyA(VK_DECIMAL, MAPVK_VK_TO_CHAR);
+}
+
+// Prefix the len bytes in buf with the escape character, and then return the
+// new buffer length.
+size_t _escape_prefix(char* const buf, const size_t len) {
+ // If nothing to prefix, don't do anything. We might be called with
+ // len == 0, if alt was held down with a dead key which produced nothing.
+ if (len == 0) {
+ return 0;
+ }
+
+ memmove(&buf[1], buf, len);
+ buf[0] = '\x1b';
+ return len + 1;
+}
+
+// Writes to buffer buf (of length len), returning number of bytes written or
+// -1 on error. Never returns zero because Win32 consoles are never 'closed'
+// (as far as I can tell).
+static int _console_read(const HANDLE console, void* buf, size_t len) {
+ for (;;) {
+ KEY_EVENT_RECORD* const key_event = _get_key_event_record(console);
+ if (key_event == NULL) {
+ return -1;
+ }
+
+ const WORD vk = key_event->wVirtualKeyCode;
+ const CHAR ch = key_event->uChar.AsciiChar;
+ const DWORD control_key_state = _normalize_altgr_control_key_state(
+ key_event);
+
+ // The following emulation code should write the output sequence to
+ // either seqstr or to seqbuf and seqbuflen.
+ const char* seqstr = NULL; // NULL terminated C-string
+ // Enough space for max sequence string below, plus modifiers and/or
+ // escape prefix.
+ char seqbuf[16];
+ size_t seqbuflen = 0; // Space used in seqbuf.
+
+#define MATCH(vk, normal) \
+ case (vk): \
+ { \
+ seqstr = (normal); \
+ } \
+ break;
+
+ // Modifier keys should affect the output sequence.
+#define MATCH_MODIFIER(vk, normal) \
+ case (vk): \
+ { \
+ seqbuflen = _get_modifier_sequence(seqbuf, (vk), \
+ control_key_state, (normal)); \
+ } \
+ break;
+
+ // The shift key should affect the output sequence.
+#define MATCH_KEYPAD(vk, normal, shifted) \
+ case (vk): \
+ { \
+ seqstr = _get_keypad_sequence(control_key_state, (normal), \
+ (shifted)); \
+ } \
+ break;
+
+ // The shift key and other modifier keys should affect the output
+ // sequence.
+#define MATCH_MODIFIER_KEYPAD(vk, normal, shifted) \
+ case (vk): \
+ { \
+ seqbuflen = _get_modifier_keypad_sequence(seqbuf, (vk), \
+ control_key_state, (normal), (shifted)); \
+ } \
+ break;
+
+#define ESC "\x1b"
+#define CSI ESC "["
+#define SS3 ESC "O"
+
+ // Only support normal mode, not application mode.
+
+ // Enhanced keys:
+ // * 6-pack: insert, delete, home, end, page up, page down
+ // * cursor keys: up, down, right, left
+ // * keypad: divide, enter
+ // * Undocumented: VK_PAUSE (Ctrl-NumLock), VK_SNAPSHOT,
+ // VK_CANCEL (Ctrl-Pause/Break), VK_NUMLOCK
+ if (_is_enhanced_key(control_key_state)) {
+ switch (vk) {
+ case VK_RETURN: // Enter key on keypad
+ if (_is_ctrl_pressed(control_key_state)) {
+ seqstr = "\n";
+ } else {
+ seqstr = "\r";
+ }
+ break;
+
+ MATCH_MODIFIER(VK_PRIOR, CSI "5~"); // Page Up
+ MATCH_MODIFIER(VK_NEXT, CSI "6~"); // Page Down
+
+ // gnome-terminal currently sends SS3 "F" and SS3 "H", but that
+ // will be fixed soon to match xterm which sends CSI "F" and
+ // CSI "H". https://bugzilla.redhat.com/show_bug.cgi?id=1119764
+ MATCH(VK_END, CSI "F");
+ MATCH(VK_HOME, CSI "H");
+
+ MATCH_MODIFIER(VK_LEFT, CSI "D");
+ MATCH_MODIFIER(VK_UP, CSI "A");
+ MATCH_MODIFIER(VK_RIGHT, CSI "C");
+ MATCH_MODIFIER(VK_DOWN, CSI "B");
+
+ MATCH_MODIFIER(VK_INSERT, CSI "2~");
+ MATCH_MODIFIER(VK_DELETE, CSI "3~");
+
+ MATCH(VK_DIVIDE, "/");
+ }
+ } else { // Non-enhanced keys:
+ switch (vk) {
+ case VK_BACK: // backspace
+ if (_is_alt_pressed(control_key_state)) {
+ seqstr = ESC "\x7f";
+ } else {
+ seqstr = "\x7f";
+ }
+ break;
+
+ case VK_TAB:
+ if (_is_shift_pressed(control_key_state)) {
+ seqstr = CSI "Z";
+ } else {
+ seqstr = "\t";
+ }
+ break;
+
+ // Number 5 key in keypad when NumLock is off, or if NumLock is
+ // on and Shift is down.
+ MATCH_KEYPAD(VK_CLEAR, CSI "E", "5");
+
+ case VK_RETURN: // Enter key on main keyboard
+ if (_is_alt_pressed(control_key_state)) {
+ seqstr = ESC "\n";
+ } else if (_is_ctrl_pressed(control_key_state)) {
+ seqstr = "\n";
+ } else {
+ seqstr = "\r";
+ }
+ break;
+
+ // VK_ESCAPE: Don't do any special handling. The OS uses many
+ // of the sequences with Escape and many of the remaining
+ // sequences don't produce bKeyDown messages, only !bKeyDown
+ // for whatever reason.
+
+ case VK_SPACE:
+ if (_is_alt_pressed(control_key_state)) {
+ seqstr = ESC " ";
+ } else if (_is_ctrl_pressed(control_key_state)) {
+ seqbuf[0] = '\0'; // NULL char
+ seqbuflen = 1;
+ } else {
+ seqstr = " ";
+ }
+ break;
+
+ MATCH_MODIFIER_KEYPAD(VK_PRIOR, CSI "5~", '9'); // Page Up
+ MATCH_MODIFIER_KEYPAD(VK_NEXT, CSI "6~", '3'); // Page Down
+
+ MATCH_KEYPAD(VK_END, CSI "4~", "1");
+ MATCH_KEYPAD(VK_HOME, CSI "1~", "7");
+
+ MATCH_MODIFIER_KEYPAD(VK_LEFT, CSI "D", '4');
+ MATCH_MODIFIER_KEYPAD(VK_UP, CSI "A", '8');
+ MATCH_MODIFIER_KEYPAD(VK_RIGHT, CSI "C", '6');
+ MATCH_MODIFIER_KEYPAD(VK_DOWN, CSI "B", '2');
+
+ MATCH_MODIFIER_KEYPAD(VK_INSERT, CSI "2~", '0');
+ MATCH_MODIFIER_KEYPAD(VK_DELETE, CSI "3~",
+ _get_decimal_char());
+
+ case 0x30: // 0
+ case 0x31: // 1
+ case 0x39: // 9
+ case VK_OEM_1: // ;:
+ case VK_OEM_PLUS: // =+
+ case VK_OEM_COMMA: // ,<
+ case VK_OEM_PERIOD: // .>
+ case VK_OEM_7: // '"
+ case VK_OEM_102: // depends on keyboard, could be <> or \|
+ case VK_OEM_2: // /?
+ case VK_OEM_3: // `~
+ case VK_OEM_4: // [{
+ case VK_OEM_5: // \|
+ case VK_OEM_6: // ]}
+ {
+ seqbuflen = _get_control_character(seqbuf, key_event,
+ control_key_state);
+
+ if (_is_alt_pressed(control_key_state)) {
+ seqbuflen = _escape_prefix(seqbuf, seqbuflen);
+ }
+ }
+ break;
+
+ case 0x32: // 2
+ case 0x36: // 6
+ case VK_OEM_MINUS: // -_
+ {
+ seqbuflen = _get_control_character(seqbuf, key_event,
+ control_key_state);
+
+ // If Alt is pressed and it isn't Ctrl-Alt-ShiftUp, then
+ // prefix with escape.
+ if (_is_alt_pressed(control_key_state) &&
+ !(_is_ctrl_pressed(control_key_state) &&
+ !_is_shift_pressed(control_key_state))) {
+ seqbuflen = _escape_prefix(seqbuf, seqbuflen);
+ }
+ }
+ break;
+
+ case 0x33: // 3
+ case 0x34: // 4
+ case 0x35: // 5
+ case 0x37: // 7
+ case 0x38: // 8
+ {
+ seqbuflen = _get_control_character(seqbuf, key_event,
+ control_key_state);
+
+ // If Alt is pressed and it isn't Ctrl-Alt-ShiftUp, then
+ // prefix with escape.
+ if (_is_alt_pressed(control_key_state) &&
+ !(_is_ctrl_pressed(control_key_state) &&
+ !_is_shift_pressed(control_key_state))) {
+ seqbuflen = _escape_prefix(seqbuf, seqbuflen);
+ }
+ }
+ break;
+
+ case 0x41: // a
+ case 0x42: // b
+ case 0x43: // c
+ case 0x44: // d
+ case 0x45: // e
+ case 0x46: // f
+ case 0x47: // g
+ case 0x48: // h
+ case 0x49: // i
+ case 0x4a: // j
+ case 0x4b: // k
+ case 0x4c: // l
+ case 0x4d: // m
+ case 0x4e: // n
+ case 0x4f: // o
+ case 0x50: // p
+ case 0x51: // q
+ case 0x52: // r
+ case 0x53: // s
+ case 0x54: // t
+ case 0x55: // u
+ case 0x56: // v
+ case 0x57: // w
+ case 0x58: // x
+ case 0x59: // y
+ case 0x5a: // z
+ {
+ seqbuflen = _get_non_alt_char(seqbuf, key_event,
+ control_key_state);
+
+ // If Alt is pressed, then prefix with escape.
+ if (_is_alt_pressed(control_key_state)) {
+ seqbuflen = _escape_prefix(seqbuf, seqbuflen);
+ }
+ }
+ break;
+
+ // These virtual key codes are generated by the keys on the
+ // keypad *when NumLock is on* and *Shift is up*.
+ MATCH(VK_NUMPAD0, "0");
+ MATCH(VK_NUMPAD1, "1");
+ MATCH(VK_NUMPAD2, "2");
+ MATCH(VK_NUMPAD3, "3");
+ MATCH(VK_NUMPAD4, "4");
+ MATCH(VK_NUMPAD5, "5");
+ MATCH(VK_NUMPAD6, "6");
+ MATCH(VK_NUMPAD7, "7");
+ MATCH(VK_NUMPAD8, "8");
+ MATCH(VK_NUMPAD9, "9");
+
+ MATCH(VK_MULTIPLY, "*");
+ MATCH(VK_ADD, "+");
+ MATCH(VK_SUBTRACT, "-");
+ // VK_DECIMAL is generated by the . key on the keypad *when
+ // NumLock is on* and *Shift is up* and the sequence is not
+ // Ctrl-Alt-NoShift-. (which causes Ctrl-Alt-Del and the
+ // Windows Security screen to come up).
+ case VK_DECIMAL:
+ // U.S. English uses '.', Germany German uses ','.
+ seqbuflen = _get_non_control_char(seqbuf, key_event,
+ control_key_state);
+ break;
+
+ MATCH_MODIFIER(VK_F1, SS3 "P");
+ MATCH_MODIFIER(VK_F2, SS3 "Q");
+ MATCH_MODIFIER(VK_F3, SS3 "R");
+ MATCH_MODIFIER(VK_F4, SS3 "S");
+ MATCH_MODIFIER(VK_F5, CSI "15~");
+ MATCH_MODIFIER(VK_F6, CSI "17~");
+ MATCH_MODIFIER(VK_F7, CSI "18~");
+ MATCH_MODIFIER(VK_F8, CSI "19~");
+ MATCH_MODIFIER(VK_F9, CSI "20~");
+ MATCH_MODIFIER(VK_F10, CSI "21~");
+ MATCH_MODIFIER(VK_F11, CSI "23~");
+ MATCH_MODIFIER(VK_F12, CSI "24~");
+
+ MATCH_MODIFIER(VK_F13, CSI "25~");
+ MATCH_MODIFIER(VK_F14, CSI "26~");
+ MATCH_MODIFIER(VK_F15, CSI "28~");
+ MATCH_MODIFIER(VK_F16, CSI "29~");
+ MATCH_MODIFIER(VK_F17, CSI "31~");
+ MATCH_MODIFIER(VK_F18, CSI "32~");
+ MATCH_MODIFIER(VK_F19, CSI "33~");
+ MATCH_MODIFIER(VK_F20, CSI "34~");
+
+ // MATCH_MODIFIER(VK_F21, ???);
+ // MATCH_MODIFIER(VK_F22, ???);
+ // MATCH_MODIFIER(VK_F23, ???);
+ // MATCH_MODIFIER(VK_F24, ???);
+ }
+ }
+
+#undef MATCH
+#undef MATCH_MODIFIER
+#undef MATCH_KEYPAD
+#undef MATCH_MODIFIER_KEYPAD
+#undef ESC
+#undef CSI
+#undef SS3
+
+ const char* out;
+ size_t outlen;
+
+ // Check for output in any of:
+ // * seqstr is set (and strlen can be used to determine the length).
+ // * seqbuf and seqbuflen are set
+ // Fallback to ch from Windows.
+ if (seqstr != NULL) {
+ out = seqstr;
+ outlen = strlen(seqstr);
+ } else if (seqbuflen > 0) {
+ out = seqbuf;
+ outlen = seqbuflen;
+ } else if (ch != '\0') {
+ // Use whatever Windows told us it is.
+ seqbuf[0] = ch;
+ seqbuflen = 1;
+ out = seqbuf;
+ outlen = seqbuflen;
+ } else {
+ // No special handling for the virtual key code and Windows isn't
+ // telling us a character code, then we don't know how to translate
+ // the key press.
+ //
+ // Consume the input and 'continue' to cause us to get a new key
+ // event.
+ D("_console_read: unknown virtual key code: %d, enhanced: %s\n",
+ vk, _is_enhanced_key(control_key_state) ? "true" : "false");
+ key_event->wRepeatCount = 0;
+ continue;
+ }
+
+ int bytesRead = 0;
+
+ // put output wRepeatCount times into buf/len
+ while (key_event->wRepeatCount > 0) {
+ if (len >= outlen) {
+ // Write to buf/len
+ memcpy(buf, out, outlen);
+ buf = (void*)((char*)buf + outlen);
+ len -= outlen;
+ bytesRead += outlen;
+
+ // consume the input
+ --key_event->wRepeatCount;
+ } else {
+ // Not enough space, so just leave it in _win32_input_record
+ // for a subsequent retrieval.
+ if (bytesRead == 0) {
+ // We didn't write anything because there wasn't enough
+ // space to even write one sequence. This should never
+ // happen if the caller uses sensible buffer sizes
+ // (i.e. >= maximum sequence length which is probably a
+ // few bytes long).
+ D("_console_read: no buffer space to write one sequence; "
+ "buffer: %ld, sequence: %ld\n", (long)len,
+ (long)outlen);
+ errno = ENOMEM;
+ return -1;
+ } else {
+ // Stop trying to write to buf/len, just return whatever
+ // we wrote so far.
+ break;
+ }
+ }
+ }
+
+ return bytesRead;
+ }
+}
+
+static DWORD _old_console_mode; // previous GetConsoleMode() result
+static HANDLE _console_handle; // when set, console mode should be restored
+
+void stdin_raw_init(const int fd) {
+ if (STDIN_FILENO == fd) {
+ const HANDLE in = GetStdHandle(STD_INPUT_HANDLE);
+ if ((in == INVALID_HANDLE_VALUE) || (in == NULL)) {
+ return;
+ }
+
+ if (GetFileType(in) != FILE_TYPE_CHAR) {
+ // stdin might be a file or pipe.
+ return;
+ }
+
+ if (!GetConsoleMode(in, &_old_console_mode)) {
+ // If GetConsoleMode() fails, stdin is probably is not a console.
+ return;
+ }
+
+ // Disable ENABLE_PROCESSED_INPUT so that Ctrl-C is read instead of
+ // calling the process Ctrl-C routine (configured by
+ // SetConsoleCtrlHandler()).
+ // Disable ENABLE_LINE_INPUT so that input is immediately sent.
+ // Disable ENABLE_ECHO_INPUT to disable local echo. Disabling this
+ // flag also seems necessary to have proper line-ending processing.
+ if (!SetConsoleMode(in, _old_console_mode & ~(ENABLE_PROCESSED_INPUT |
+ ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT))) {
+ // This really should not fail.
+ D("stdin_raw_init: SetConsoleMode() failure, error %ld\n",
+ GetLastError());
+ }
+
+ // Once this is set, it means that stdin has been configured for
+ // reading from and that the old console mode should be restored later.
+ _console_handle = in;
+
+ // Note that we don't need to configure C Runtime line-ending
+ // translation because _console_read() does not call the C Runtime to
+ // read from the console.
+ }
+}
+
+void stdin_raw_restore(const int fd) {
+ if (STDIN_FILENO == fd) {
+ if (_console_handle != NULL) {
+ const HANDLE in = _console_handle;
+ _console_handle = NULL; // clear state
+
+ if (!SetConsoleMode(in, _old_console_mode)) {
+ // This really should not fail.
+ D("stdin_raw_restore: SetConsoleMode() failure, error %ld\n",
+ GetLastError());
+ }
+ }
+ }
+}
+
+// Called by 'adb shell' and 'adb exec-in' to read from stdin.
+int unix_read(int fd, void* buf, size_t len) {
+ if ((fd == STDIN_FILENO) && (_console_handle != NULL)) {
+ // If it is a request to read from stdin, and stdin_raw_init() has been
+ // called, and it successfully configured the console, then read from
+ // the console using Win32 console APIs and partially emulate a unix
+ // terminal.
+ return _console_read(_console_handle, buf, len);
+ } else {
+ // Just call into C Runtime which can read from pipes/files and which
+ // can do LF/CR translation (which is overridable with _setmode()).
+ // Undefine the macro that is set in sysdeps.h which bans calls to
+ // plain read() in favor of unix_read() or adb_read().
+#pragma push_macro("read")
+#undef read
+ return read(fd, buf, len);
+#pragma pop_macro("read")
+ }
+}
diff --git a/adb/test_adb.py b/adb/test_adb.py
new file mode 100644
index 0000000..59aa14d
--- /dev/null
+++ b/adb/test_adb.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""Tests for the adb program itself.
+
+This differs from things in test_device.py in that there is no API for these
+things. Most of these tests involve specific error messages or the help text.
+"""
+from __future__ import print_function
+
+import random
+import subprocess
+import unittest
+
+import adb
+
+
+class NonApiTest(unittest.TestCase):
+ """Tests for ADB that aren't a part of the AndroidDevice API."""
+
+ def test_help(self):
+ """Make sure we get _something_ out of help."""
+ out = subprocess.check_output(
+ ['adb', 'help'], stderr=subprocess.STDOUT)
+ self.assertGreater(len(out), 0)
+
+ def test_version(self):
+ """Get a version number out of the output of adb."""
+ lines = subprocess.check_output(['adb', 'version']).splitlines()
+ version_line = lines[0]
+ self.assertRegexpMatches(
+ version_line, r'^Android Debug Bridge version \d+\.\d+\.\d+$')
+ if len(lines) == 2:
+ # Newer versions of ADB have a second line of output for the
+ # version that includes a specific revision (git SHA).
+ revision_line = lines[1]
+ self.assertRegexpMatches(
+ revision_line, r'^Revision [0-9a-f]{12}-android$')
+
+ def test_tcpip_error_messages(self):
+ p = subprocess.Popen(['adb', 'tcpip'], stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
+ out, _ = p.communicate()
+ self.assertEqual(1, p.returncode)
+ self.assertIn('help message', out)
+
+ p = subprocess.Popen(['adb', 'tcpip', 'foo'], stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
+ out, _ = p.communicate()
+ self.assertEqual(1, p.returncode)
+ self.assertIn('error', out)
+
+
+def main():
+ random.seed(0)
+ if len(adb.get_devices()) > 0:
+ suite = unittest.TestLoader().loadTestsFromName(__name__)
+ unittest.TextTestRunner(verbosity=3).run(suite)
+ else:
+ print('Test suite must be run with attached devices')
+
+
+if __name__ == '__main__':
+ main()
diff --git a/adb/test_device.py b/adb/test_device.py
new file mode 100644
index 0000000..6c20b6e
--- /dev/null
+++ b/adb/test_device.py
@@ -0,0 +1,424 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+from __future__ import print_function
+
+import hashlib
+import os
+import posixpath
+import random
+import shlex
+import shutil
+import subprocess
+import tempfile
+import unittest
+
+import mock
+
+import adb
+
+
+class GetDeviceTest(unittest.TestCase):
+ def setUp(self):
+ self.android_serial = os.getenv('ANDROID_SERIAL')
+ del os.environ['ANDROID_SERIAL']
+
+ def tearDown(self):
+ os.environ['ANDROID_SERIAL'] = self.android_serial
+
+ @mock.patch('adb.device.get_devices')
+ def test_explicit(self, mock_get_devices):
+ mock_get_devices.return_value = ['foo', 'bar']
+ device = adb.get_device('foo')
+ self.assertEqual(device.serial, 'foo')
+
+ @mock.patch('adb.device.get_devices')
+ def test_from_env(self, mock_get_devices):
+ mock_get_devices.return_value = ['foo', 'bar']
+ os.environ['ANDROID_SERIAL'] = 'foo'
+ device = adb.get_device()
+ self.assertEqual(device.serial, 'foo')
+
+ @mock.patch('adb.device.get_devices')
+ def test_arg_beats_env(self, mock_get_devices):
+ mock_get_devices.return_value = ['foo', 'bar']
+ os.environ['ANDROID_SERIAL'] = 'bar'
+ device = adb.get_device('foo')
+ self.assertEqual(device.serial, 'foo')
+
+ @mock.patch('adb.device.get_devices')
+ def test_no_such_device(self, mock_get_devices):
+ mock_get_devices.return_value = ['foo', 'bar']
+ self.assertRaises(adb.DeviceNotFoundError, adb.get_device, ['baz'])
+
+ os.environ['ANDROID_SERIAL'] = 'baz'
+ self.assertRaises(adb.DeviceNotFoundError, adb.get_device)
+
+ @mock.patch('adb.device.get_devices')
+ def test_unique_device(self, mock_get_devices):
+ mock_get_devices.return_value = ['foo']
+ device = adb.get_device()
+ self.assertEqual(device.serial, 'foo')
+
+ @mock.patch('adb.device.get_devices')
+ def test_no_unique_device(self, mock_get_devices):
+ mock_get_devices.return_value = ['foo', 'bar']
+ self.assertRaises(adb.NoUniqueDeviceError, adb.get_device)
+
+
+class DeviceTest(unittest.TestCase):
+ def setUp(self):
+ self.device = adb.get_device()
+
+
+class ShellTest(DeviceTest):
+ def test_cat(self):
+ """Check that we can at least cat a file."""
+ out = self.device.shell(['cat', '/proc/uptime']).strip()
+ elements = out.split()
+ self.assertEqual(len(elements), 2)
+
+ uptime, idle = elements
+ self.assertGreater(float(uptime), 0.0)
+ self.assertGreater(float(idle), 0.0)
+
+ def test_throws_on_failure(self):
+ self.assertRaises(subprocess.CalledProcessError,
+ self.device.shell, ['false'])
+
+ def test_output_not_stripped(self):
+ out = self.device.shell(['echo', 'foo'])
+ self.assertEqual(out, 'foo' + self.device.linesep)
+
+ def test_shell_nocheck_failure(self):
+ rc, out = self.device.shell_nocheck(['false'])
+ self.assertNotEqual(rc, 0)
+ self.assertEqual(out, '')
+
+ def test_shell_nocheck_output_not_stripped(self):
+ rc, out = self.device.shell_nocheck(['echo', 'foo'])
+ self.assertEqual(rc, 0)
+ self.assertEqual(out, 'foo' + self.device.linesep)
+
+ def test_can_distinguish_tricky_results(self):
+ # If result checking on ADB shell is naively implemented as
+ # `adb shell <cmd>; echo $?`, we would be unable to distinguish the
+ # output from the result for a cmd of `echo -n 1`.
+ rc, out = self.device.shell_nocheck(['echo', '-n', '1'])
+ self.assertEqual(rc, 0)
+ self.assertEqual(out, '1')
+
+ def test_line_endings(self):
+ """Ensure that line ending translation is not happening in the pty.
+
+ Bug: http://b/19735063
+ """
+ output = self.device.shell(['uname'])
+ self.assertEqual(output, 'Linux' + self.device.linesep)
+
+
+class ArgumentEscapingTest(DeviceTest):
+ def test_shell_escaping(self):
+ """Make sure that argument escaping is somewhat sane."""
+
+ # http://b/19734868
+ # Note that this actually matches ssh(1)'s behavior --- it's
+ # converted to `sh -c echo hello; echo world` which sh interprets
+ # as `sh -c echo` (with an argument to that shell of "hello"),
+ # and then `echo world` back in the first shell.
+ result = self.device.shell(
+ shlex.split("sh -c 'echo hello; echo world'"))
+ result = result.splitlines()
+ self.assertEqual(['', 'world'], result)
+ # If you really wanted "hello" and "world", here's what you'd do:
+ result = self.device.shell(
+ shlex.split(r'echo hello\;echo world')).splitlines()
+ self.assertEqual(['hello', 'world'], result)
+
+ # http://b/15479704
+ result = self.device.shell(shlex.split("'true && echo t'")).strip()
+ self.assertEqual('t', result)
+ result = self.device.shell(
+ shlex.split("sh -c 'true && echo t'")).strip()
+ self.assertEqual('t', result)
+
+ # http://b/20564385
+ result = self.device.shell(shlex.split('FOO=a BAR=b echo t')).strip()
+ self.assertEqual('t', result)
+ result = self.device.shell(shlex.split(r'echo -n 123\;uname')).strip()
+ self.assertEqual('123Linux', result)
+
+ def test_install_argument_escaping(self):
+ """Make sure that install argument escaping works."""
+ # http://b/20323053
+ tf = tempfile.NamedTemporaryFile('wb', suffix='-text;ls;1.apk')
+ self.assertIn("-text;ls;1.apk", self.device.install(tf.name))
+
+ # http://b/3090932
+ tf = tempfile.NamedTemporaryFile('wb', suffix="-Live Hold'em.apk")
+ self.assertIn("-Live Hold'em.apk", self.device.install(tf.name))
+
+
+class RootUnrootTest(DeviceTest):
+ def _test_root(self):
+ message = self.device.root()
+ if 'adbd cannot run as root in production builds' in message:
+ return
+ self.device.wait()
+ self.assertEqual('root', self.device.shell(['id', '-un']).strip())
+
+ def _test_unroot(self):
+ self.device.unroot()
+ self.device.wait()
+ self.assertEqual('shell', self.device.shell(['id', '-un']).strip())
+
+ def test_root_unroot(self):
+ """Make sure that adb root and adb unroot work, using id(1)."""
+ original_user = self.device.shell(['id', '-un']).strip()
+ try:
+ if original_user == 'root':
+ self._test_unroot()
+ self._test_root()
+ elif original_user == 'shell':
+ self._test_root()
+ self._test_unroot()
+ finally:
+ if original_user == 'root':
+ self.device.root()
+ else:
+ self.device.unroot()
+ self.device.wait()
+
+
+class TcpIpTest(DeviceTest):
+ def test_tcpip_failure_raises(self):
+ """adb tcpip requires a port.
+
+ Bug: http://b/22636927
+ """
+ self.assertRaises(
+ subprocess.CalledProcessError, self.device.tcpip, '')
+ self.assertRaises(
+ subprocess.CalledProcessError, self.device.tcpip, 'foo')
+
+
+def compute_md5(string):
+ hsh = hashlib.md5()
+ hsh.update(string)
+ return hsh.hexdigest()
+
+
+def get_md5_prog(device):
+ """Older platforms (pre-L) had the name md5 rather than md5sum."""
+ try:
+ device.shell(['md5sum', '/proc/uptime'])
+ return 'md5sum'
+ except subprocess.CalledProcessError:
+ return 'md5'
+
+
+class HostFile(object):
+ def __init__(self, handle, checksum):
+ self.handle = handle
+ self.checksum = checksum
+ self.full_path = handle.name
+ self.base_name = os.path.basename(self.full_path)
+
+
+class DeviceFile(object):
+ def __init__(self, checksum, full_path):
+ self.checksum = checksum
+ self.full_path = full_path
+ self.base_name = posixpath.basename(self.full_path)
+
+
+def make_random_host_files(in_dir, num_files):
+ min_size = 1 * (1 << 10)
+ max_size = 16 * (1 << 10)
+
+ files = []
+ for _ in xrange(num_files):
+ file_handle = tempfile.NamedTemporaryFile(dir=in_dir, delete=False)
+
+ size = random.randrange(min_size, max_size, 1024)
+ rand_str = os.urandom(size)
+ file_handle.write(rand_str)
+ file_handle.flush()
+ file_handle.close()
+
+ md5 = compute_md5(rand_str)
+ files.append(HostFile(file_handle, md5))
+ return files
+
+
+def make_random_device_files(device, in_dir, num_files):
+ min_size = 1 * (1 << 10)
+ max_size = 16 * (1 << 10)
+
+ files = []
+ for file_num in xrange(num_files):
+ size = random.randrange(min_size, max_size, 1024)
+
+ base_name = 'device_tmpfile' + str(file_num)
+ full_path = os.path.join(in_dir, base_name)
+
+ device.shell(['dd', 'if=/dev/urandom', 'of={}'.format(full_path),
+ 'bs={}'.format(size), 'count=1'])
+ dev_md5, _ = device.shell([get_md5_prog(device), full_path]).split()
+
+ files.append(DeviceFile(dev_md5, full_path))
+ return files
+
+
+class FileOperationsTest(DeviceTest):
+ SCRATCH_DIR = '/data/local/tmp'
+ DEVICE_TEMP_FILE = SCRATCH_DIR + '/adb_test_file'
+ DEVICE_TEMP_DIR = SCRATCH_DIR + '/adb_test_dir'
+
+ def _test_push(self, local_file, checksum):
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_FILE])
+ try:
+ self.device.push(
+ local=local_file, remote=self.DEVICE_TEMP_FILE)
+ dev_md5, _ = self.device.shell(
+ [get_md5_prog(self.device), self.DEVICE_TEMP_FILE]).split()
+ self.assertEqual(checksum, dev_md5)
+ finally:
+ self.device.shell(['rm', '-f', self.DEVICE_TEMP_FILE])
+
+ def test_push(self):
+ """Push a randomly generated file to specified device."""
+ kbytes = 512
+ tmp = tempfile.NamedTemporaryFile(mode='wb', delete=False)
+ try:
+ rand_str = os.urandom(1024 * kbytes)
+ tmp.write(rand_str)
+ tmp.close()
+ self._test_push(tmp.name, compute_md5(rand_str))
+ finally:
+ os.remove(tmp.name)
+
+ # TODO: write push directory test.
+
+ def _test_pull(self, remote_file, checksum):
+ tmp_write = tempfile.NamedTemporaryFile(mode='wb', delete=False)
+ try:
+ tmp_write.close()
+ self.device.pull(remote=remote_file, local=tmp_write.name)
+ with open(tmp_write.name, 'rb') as tmp_read:
+ host_contents = tmp_read.read()
+ host_md5 = compute_md5(host_contents)
+ self.assertEqual(checksum, host_md5)
+ finally:
+ os.remove(tmp_write.name)
+
+ def test_pull(self):
+ """Pull a randomly generated file from specified device."""
+ kbytes = 512
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_FILE])
+ try:
+ cmd = ['dd', 'if=/dev/urandom',
+ 'of={}'.format(self.DEVICE_TEMP_FILE), 'bs=1024',
+ 'count={}'.format(kbytes)]
+ self.device.shell(cmd)
+ dev_md5, _ = self.device.shell(
+ [get_md5_prog(self.device), self.DEVICE_TEMP_FILE]).split()
+ self._test_pull(self.DEVICE_TEMP_FILE, dev_md5)
+ finally:
+ self.device.shell_nocheck(['rm', self.DEVICE_TEMP_FILE])
+
+ def test_pull_dir(self):
+ """Pull a randomly generated directory of files from the device."""
+ host_dir = tempfile.mkdtemp()
+ try:
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
+
+ # Populate device directory with random files.
+ temp_files = make_random_device_files(
+ self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
+
+ self.device.pull(remote=self.DEVICE_TEMP_DIR, local=host_dir)
+
+ for temp_file in temp_files:
+ host_path = os.path.join(host_dir, temp_file.base_name)
+ with open(host_path, 'rb') as host_file:
+ host_md5 = compute_md5(host_file.read())
+ self.assertEqual(host_md5, temp_file.checksum)
+ finally:
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
+
+ def test_sync(self):
+ """Sync a randomly generated directory of files to specified device."""
+ base_dir = tempfile.mkdtemp()
+ try:
+ # Create mirror device directory hierarchy within base_dir.
+ full_dir_path = base_dir + self.DEVICE_TEMP_DIR
+ os.makedirs(full_dir_path)
+
+ # Create 32 random files within the host mirror.
+ temp_files = make_random_host_files(in_dir=full_dir_path,
+ num_files=32)
+
+ # Clean up any trash on the device.
+ device = adb.get_device(product=base_dir)
+ device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+
+ device.sync('data')
+
+ # Confirm that every file on the device mirrors that on the host.
+ for temp_file in temp_files:
+ device_full_path = posixpath.join(
+ self.DEVICE_TEMP_DIR, temp_file.base_name)
+ dev_md5, _ = device.shell(
+ [get_md5_prog(self.device), device_full_path]).split()
+ self.assertEqual(temp_file.checksum, dev_md5)
+ finally:
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ shutil.rmtree(base_dir + self.DEVICE_TEMP_DIR)
+
+
+ def test_unicode_paths(self):
+ """Ensure that we can support non-ASCII paths, even on Windows."""
+ name = u'로보카 폴리'.encode('utf-8')
+
+ ## push.
+ tf = tempfile.NamedTemporaryFile('wb', suffix=name)
+ self.device.push(tf.name, '/data/local/tmp/adb-test-{}'.format(name))
+ self.device.shell(['rm', '-f', '/data/local/tmp/adb-test-*'])
+
+ # pull.
+ cmd = ['touch', '"/data/local/tmp/adb-test-{}"'.format(name)]
+ self.device.shell(cmd)
+
+ tf = tempfile.NamedTemporaryFile('wb', suffix=name)
+ self.device.pull('/data/local/tmp/adb-test-{}'.format(name), tf.name)
+
+
+def main():
+ random.seed(0)
+ if len(adb.get_devices()) > 0:
+ suite = unittest.TestLoader().loadTestsFromName(__name__)
+ unittest.TextTestRunner(verbosity=3).run(suite)
+ else:
+ print('Test suite must be run with attached devices')
+
+
+if __name__ == '__main__':
+ main()
diff --git a/adb/test_track_devices.cpp b/adb/test_track_devices.cpp
new file mode 100644
index 0000000..f78daeb
--- /dev/null
+++ b/adb/test_track_devices.cpp
@@ -0,0 +1,68 @@
+// TODO: replace this with a shell/python script.
+
+/* a simple test program, connects to ADB server, and opens a track-devices session */
+#include <netdb.h>
+#include <sys/socket.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <memory.h>
+
+#include <base/file.h>
+
+static void
+panic( const char* msg )
+{
+ fprintf(stderr, "PANIC: %s: %s\n", msg, strerror(errno));
+ exit(1);
+}
+
+int main(int argc, char* argv[]) {
+ const char* request = "host:track-devices";
+
+ if (argv[1] && strcmp(argv[1], "--jdwp") == 0) {
+ request = "track-jdwp";
+ }
+
+ int ret;
+ struct sockaddr_in server;
+ char buffer[1024];
+
+ memset( &server, 0, sizeof(server) );
+ server.sin_family = AF_INET;
+ server.sin_port = htons(5037);
+ server.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ int s = socket( PF_INET, SOCK_STREAM, 0 );
+ ret = connect( s, (struct sockaddr*) &server, sizeof(server) );
+ if (ret < 0) panic( "could not connect to server" );
+
+ /* send the request */
+ int len = snprintf(buffer, sizeof(buffer), "%04zx%s", strlen(request), request);
+ if (!android::base::WriteFully(s, buffer, len))
+ panic( "could not send request" );
+
+ /* read the OKAY answer */
+ if (!android::base::ReadFully(s, buffer, 4))
+ panic( "could not read request" );
+
+ printf( "server answer: %.*s\n", 4, buffer );
+
+ /* now loop */
+ while (true) {
+ char head[5] = "0000";
+
+ if (!android::base::ReadFully(s, head, 4))
+ panic("could not read length");
+
+ int len;
+ if (sscanf(head, "%04x", &len) != 1 )
+ panic("could not decode length");
+
+ if (!android::base::ReadFully(s, buffer, len))
+ panic("could not read data");
+
+ printf( "received header %.*s (%d bytes):\n%.*s----\n", 4, head, len, len, buffer );
+ }
+ close(s);
+}
diff --git a/adb/transport.cpp b/adb/transport.cpp
new file mode 100644
index 0000000..87aff88
--- /dev/null
+++ b/adb/transport.cpp
@@ -0,0 +1,1069 @@
+/*
+ * Copyright (C) 2007 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 TRACE_TAG TRACE_TRANSPORT
+
+#include "sysdeps.h"
+#include "transport.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <list>
+
+#include <base/stringprintf.h>
+
+#include "adb.h"
+#include "adb_utils.h"
+
+static void transport_unref(atransport *t);
+
+static std::list<atransport*> transport_list;
+static std::list<atransport*> pending_list;
+
+ADB_MUTEX_DEFINE( transport_lock );
+
+void kick_transport(atransport* t)
+{
+ if (t && !t->kicked)
+ {
+ int kicked;
+
+ adb_mutex_lock(&transport_lock);
+ kicked = t->kicked;
+ if (!kicked)
+ t->kicked = 1;
+ adb_mutex_unlock(&transport_lock);
+
+ if (!kicked)
+ t->kick(t);
+ }
+}
+
+// Each atransport contains a list of adisconnects (t->disconnects).
+// An adisconnect contains a link to the next/prev adisconnect, a function
+// pointer to a disconnect callback which takes a void* piece of user data and
+// the atransport, and some user data for the callback (helpfully named
+// "opaque").
+//
+// The list is circular. New items are added to the entry member of the list
+// (t->disconnects) by add_transport_disconnect.
+//
+// run_transport_disconnects invokes each function in the list.
+//
+// Gotchas:
+// * run_transport_disconnects assumes that t->disconnects is non-null, so
+// this can't be run on a zeroed atransport.
+// * The callbacks in this list are not removed when called, and this function
+// is not guarded against running more than once. As such, ensure that this
+// function is not called multiple times on the same atransport.
+// TODO(danalbert): Just fix this so that it is guarded once you have tests.
+void run_transport_disconnects(atransport* t)
+{
+ adisconnect* dis = t->disconnects.next;
+
+ D("%s: run_transport_disconnects\n", t->serial);
+ while (dis != &t->disconnects) {
+ adisconnect* next = dis->next;
+ dis->func( dis->opaque, t );
+ dis = next;
+ }
+}
+
+static void dump_packet(const char* name, const char* func, apacket* p) {
+ unsigned command = p->msg.command;
+ int len = p->msg.data_length;
+ char cmd[9];
+ char arg0[12], arg1[12];
+ int n;
+
+ for (n = 0; n < 4; n++) {
+ int b = (command >> (n*8)) & 255;
+ if (b < 32 || b >= 127)
+ break;
+ cmd[n] = (char)b;
+ }
+ if (n == 4) {
+ cmd[4] = 0;
+ } else {
+ /* There is some non-ASCII name in the command, so dump
+ * the hexadecimal value instead */
+ snprintf(cmd, sizeof cmd, "%08x", command);
+ }
+
+ if (p->msg.arg0 < 256U)
+ snprintf(arg0, sizeof arg0, "%d", p->msg.arg0);
+ else
+ snprintf(arg0, sizeof arg0, "0x%x", p->msg.arg0);
+
+ if (p->msg.arg1 < 256U)
+ snprintf(arg1, sizeof arg1, "%d", p->msg.arg1);
+ else
+ snprintf(arg1, sizeof arg1, "0x%x", p->msg.arg1);
+
+ D("%s: %s: [%s] arg0=%s arg1=%s (len=%d) ",
+ name, func, cmd, arg0, arg1, len);
+ dump_hex(p->data, len);
+}
+
+static int
+read_packet(int fd, const char* name, apacket** ppacket)
+{
+ char *p = (char*)ppacket; /* really read a packet address */
+ int r;
+ int len = sizeof(*ppacket);
+ char buff[8];
+ if (!name) {
+ snprintf(buff, sizeof buff, "fd=%d", fd);
+ name = buff;
+ }
+ while(len > 0) {
+ r = adb_read(fd, p, len);
+ if(r > 0) {
+ len -= r;
+ p += r;
+ } else {
+ D("%s: read_packet (fd=%d), error ret=%d errno=%d: %s\n", name, fd, r, errno, strerror(errno));
+ if((r < 0) && (errno == EINTR)) continue;
+ return -1;
+ }
+ }
+
+ if (ADB_TRACING) {
+ dump_packet(name, "from remote", *ppacket);
+ }
+ return 0;
+}
+
+static int
+write_packet(int fd, const char* name, apacket** ppacket)
+{
+ char *p = (char*) ppacket; /* we really write the packet address */
+ int r, len = sizeof(ppacket);
+ char buff[8];
+ if (!name) {
+ snprintf(buff, sizeof buff, "fd=%d", fd);
+ name = buff;
+ }
+
+ if (ADB_TRACING) {
+ dump_packet(name, "to remote", *ppacket);
+ }
+ len = sizeof(ppacket);
+ while(len > 0) {
+ r = adb_write(fd, p, len);
+ if(r > 0) {
+ len -= r;
+ p += r;
+ } else {
+ D("%s: write_packet (fd=%d) error ret=%d errno=%d: %s\n", name, fd, r, errno, strerror(errno));
+ if((r < 0) && (errno == EINTR)) continue;
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static void transport_socket_events(int fd, unsigned events, void *_t)
+{
+ atransport *t = reinterpret_cast<atransport*>(_t);
+ D("transport_socket_events(fd=%d, events=%04x,...)\n", fd, events);
+ if(events & FDE_READ){
+ apacket *p = 0;
+ if(read_packet(fd, t->serial, &p)){
+ D("%s: failed to read packet from transport socket on fd %d\n", t->serial, fd);
+ } else {
+ handle_packet(p, (atransport *) _t);
+ }
+ }
+}
+
+void send_packet(apacket *p, atransport *t)
+{
+ unsigned char *x;
+ unsigned sum;
+ unsigned count;
+
+ p->msg.magic = p->msg.command ^ 0xffffffff;
+
+ count = p->msg.data_length;
+ x = (unsigned char *) p->data;
+ sum = 0;
+ while(count-- > 0){
+ sum += *x++;
+ }
+ p->msg.data_check = sum;
+
+ print_packet("send", p);
+
+ if (t == NULL) {
+ D("Transport is null \n");
+ // Zap errno because print_packet() and other stuff have errno effect.
+ errno = 0;
+ fatal_errno("Transport is null");
+ }
+
+ if(write_packet(t->transport_socket, t->serial, &p)){
+ fatal_errno("cannot enqueue packet on transport socket");
+ }
+}
+
+/* The transport is opened by transport_register_func before
+** the input and output threads are started.
+**
+** The output thread issues a SYNC(1, token) message to let
+** the input thread know to start things up. In the event
+** of transport IO failure, the output thread will post a
+** SYNC(0,0) message to ensure shutdown.
+**
+** The transport will not actually be closed until both
+** threads exit, but the input thread will kick the transport
+** on its way out to disconnect the underlying device.
+*/
+
+static void *output_thread(void *_t)
+{
+ atransport *t = reinterpret_cast<atransport*>(_t);
+ apacket *p;
+
+ D("%s: starting transport output thread on fd %d, SYNC online (%d)\n",
+ t->serial, t->fd, t->sync_token + 1);
+ p = get_apacket();
+ p->msg.command = A_SYNC;
+ p->msg.arg0 = 1;
+ p->msg.arg1 = ++(t->sync_token);
+ p->msg.magic = A_SYNC ^ 0xffffffff;
+ if(write_packet(t->fd, t->serial, &p)) {
+ put_apacket(p);
+ D("%s: failed to write SYNC packet\n", t->serial);
+ goto oops;
+ }
+
+ D("%s: data pump started\n", t->serial);
+ for(;;) {
+ p = get_apacket();
+
+ if(t->read_from_remote(p, t) == 0){
+ D("%s: received remote packet, sending to transport\n",
+ t->serial);
+ if(write_packet(t->fd, t->serial, &p)){
+ put_apacket(p);
+ D("%s: failed to write apacket to transport\n", t->serial);
+ goto oops;
+ }
+ } else {
+ D("%s: remote read failed for transport\n", t->serial);
+ put_apacket(p);
+ break;
+ }
+ }
+
+ D("%s: SYNC offline for transport\n", t->serial);
+ p = get_apacket();
+ p->msg.command = A_SYNC;
+ p->msg.arg0 = 0;
+ p->msg.arg1 = 0;
+ p->msg.magic = A_SYNC ^ 0xffffffff;
+ if(write_packet(t->fd, t->serial, &p)) {
+ put_apacket(p);
+ D("%s: failed to write SYNC apacket to transport\n", t->serial);
+ }
+
+oops:
+ D("%s: transport output thread is exiting\n", t->serial);
+ kick_transport(t);
+ transport_unref(t);
+ return 0;
+}
+
+static void *input_thread(void *_t)
+{
+ atransport *t = reinterpret_cast<atransport*>(_t);
+ apacket *p;
+ int active = 0;
+
+ D("%s: starting transport input thread, reading from fd %d\n",
+ t->serial, t->fd);
+
+ for(;;){
+ if(read_packet(t->fd, t->serial, &p)) {
+ D("%s: failed to read apacket from transport on fd %d\n",
+ t->serial, t->fd );
+ break;
+ }
+ if(p->msg.command == A_SYNC){
+ if(p->msg.arg0 == 0) {
+ D("%s: transport SYNC offline\n", t->serial);
+ put_apacket(p);
+ break;
+ } else {
+ if(p->msg.arg1 == t->sync_token) {
+ D("%s: transport SYNC online\n", t->serial);
+ active = 1;
+ } else {
+ D("%s: transport ignoring SYNC %d != %d\n",
+ t->serial, p->msg.arg1, t->sync_token);
+ }
+ }
+ } else {
+ if(active) {
+ D("%s: transport got packet, sending to remote\n", t->serial);
+ t->write_to_remote(p, t);
+ } else {
+ D("%s: transport ignoring packet while offline\n", t->serial);
+ }
+ }
+
+ put_apacket(p);
+ }
+
+ // this is necessary to avoid a race condition that occured when a transport closes
+ // while a client socket is still active.
+ close_all_sockets(t);
+
+ D("%s: transport input thread is exiting, fd %d\n", t->serial, t->fd);
+ kick_transport(t);
+ transport_unref(t);
+ return 0;
+}
+
+
+static int transport_registration_send = -1;
+static int transport_registration_recv = -1;
+static fdevent transport_registration_fde;
+
+
+#if ADB_HOST
+
+/* this adds support required by the 'track-devices' service.
+ * this is used to send the content of "list_transport" to any
+ * number of client connections that want it through a single
+ * live TCP connection
+ */
+struct device_tracker {
+ asocket socket;
+ int update_needed;
+ device_tracker* next;
+};
+
+/* linked list of all device trackers */
+static device_tracker* device_tracker_list;
+
+static void
+device_tracker_remove( device_tracker* tracker )
+{
+ device_tracker** pnode = &device_tracker_list;
+ device_tracker* node = *pnode;
+
+ adb_mutex_lock( &transport_lock );
+ while (node) {
+ if (node == tracker) {
+ *pnode = node->next;
+ break;
+ }
+ pnode = &node->next;
+ node = *pnode;
+ }
+ adb_mutex_unlock( &transport_lock );
+}
+
+static void
+device_tracker_close( asocket* socket )
+{
+ device_tracker* tracker = (device_tracker*) socket;
+ asocket* peer = socket->peer;
+
+ D( "device tracker %p removed\n", tracker);
+ if (peer) {
+ peer->peer = NULL;
+ peer->close(peer);
+ }
+ device_tracker_remove(tracker);
+ free(tracker);
+}
+
+static int
+device_tracker_enqueue( asocket* socket, apacket* p )
+{
+ /* you can't read from a device tracker, close immediately */
+ put_apacket(p);
+ device_tracker_close(socket);
+ return -1;
+}
+
+static int device_tracker_send(device_tracker* tracker, const std::string& string) {
+ apacket* p = get_apacket();
+ asocket* peer = tracker->socket.peer;
+
+ snprintf(reinterpret_cast<char*>(p->data), 5, "%04x", static_cast<int>(string.size()));
+ memcpy(&p->data[4], string.data(), string.size());
+ p->len = 4 + string.size();
+ return peer->enqueue(peer, p);
+}
+
+static void device_tracker_ready(asocket* socket) {
+ device_tracker* tracker = reinterpret_cast<device_tracker*>(socket);
+
+ // We want to send the device list when the tracker connects
+ // for the first time, even if no update occurred.
+ if (tracker->update_needed > 0) {
+ tracker->update_needed = 0;
+
+ std::string transports = list_transports(false);
+ device_tracker_send(tracker, transports);
+ }
+}
+
+asocket*
+create_device_tracker(void)
+{
+ device_tracker* tracker = reinterpret_cast<device_tracker*>(calloc(1, sizeof(*tracker)));
+ if (tracker == nullptr) fatal("cannot allocate device tracker");
+
+ D( "device tracker %p created\n", tracker);
+
+ tracker->socket.enqueue = device_tracker_enqueue;
+ tracker->socket.ready = device_tracker_ready;
+ tracker->socket.close = device_tracker_close;
+ tracker->update_needed = 1;
+
+ tracker->next = device_tracker_list;
+ device_tracker_list = tracker;
+
+ return &tracker->socket;
+}
+
+
+// Call this function each time the transport list has changed.
+void update_transports() {
+ std::string transports = list_transports(false);
+
+ device_tracker* tracker = device_tracker_list;
+ while (tracker != nullptr) {
+ device_tracker* next = tracker->next;
+ // This may destroy the tracker if the connection is closed.
+ device_tracker_send(tracker, transports);
+ tracker = next;
+ }
+}
+
+#else
+
+void update_transports() {
+ // Nothing to do on the device side.
+}
+
+#endif // ADB_HOST
+
+struct tmsg
+{
+ atransport *transport;
+ int action;
+};
+
+static int
+transport_read_action(int fd, struct tmsg* m)
+{
+ char *p = (char*)m;
+ int len = sizeof(*m);
+ int r;
+
+ while(len > 0) {
+ r = adb_read(fd, p, len);
+ if(r > 0) {
+ len -= r;
+ p += r;
+ } else {
+ if((r < 0) && (errno == EINTR)) continue;
+ D("transport_read_action: on fd %d, error %d: %s\n",
+ fd, errno, strerror(errno));
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int
+transport_write_action(int fd, struct tmsg* m)
+{
+ char *p = (char*)m;
+ int len = sizeof(*m);
+ int r;
+
+ while(len > 0) {
+ r = adb_write(fd, p, len);
+ if(r > 0) {
+ len -= r;
+ p += r;
+ } else {
+ if((r < 0) && (errno == EINTR)) continue;
+ D("transport_write_action: on fd %d, error %d: %s\n",
+ fd, errno, strerror(errno));
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static void transport_registration_func(int _fd, unsigned ev, void *data)
+{
+ tmsg m;
+ int s[2];
+ atransport *t;
+
+ if(!(ev & FDE_READ)) {
+ return;
+ }
+
+ if(transport_read_action(_fd, &m)) {
+ fatal_errno("cannot read transport registration socket");
+ }
+
+ t = m.transport;
+
+ if(m.action == 0){
+ D("transport: %s removing and free'ing %d\n", t->serial, t->transport_socket);
+
+ /* IMPORTANT: the remove closes one half of the
+ ** socket pair. The close closes the other half.
+ */
+ fdevent_remove(&(t->transport_fde));
+ adb_close(t->fd);
+
+ adb_mutex_lock(&transport_lock);
+ transport_list.remove(t);
+ adb_mutex_unlock(&transport_lock);
+
+ run_transport_disconnects(t);
+
+ if (t->product)
+ free(t->product);
+ if (t->serial)
+ free(t->serial);
+ if (t->model)
+ free(t->model);
+ if (t->device)
+ free(t->device);
+ if (t->devpath)
+ free(t->devpath);
+
+ delete t;
+
+ update_transports();
+ return;
+ }
+
+ /* don't create transport threads for inaccessible devices */
+ if (t->connection_state != kCsNoPerm) {
+ /* initial references are the two threads */
+ t->ref_count = 2;
+
+ if (adb_socketpair(s)) {
+ fatal_errno("cannot open transport socketpair");
+ }
+
+ D("transport: %s socketpair: (%d,%d) starting\n", t->serial, s[0], s[1]);
+
+ t->transport_socket = s[0];
+ t->fd = s[1];
+
+ fdevent_install(&(t->transport_fde),
+ t->transport_socket,
+ transport_socket_events,
+ t);
+
+ fdevent_set(&(t->transport_fde), FDE_READ);
+
+ if (!adb_thread_create(input_thread, t)) {
+ fatal_errno("cannot create input thread");
+ }
+
+ if (!adb_thread_create(output_thread, t)) {
+ fatal_errno("cannot create output thread");
+ }
+ }
+
+ adb_mutex_lock(&transport_lock);
+ pending_list.remove(t);
+ transport_list.push_front(t);
+ adb_mutex_unlock(&transport_lock);
+
+ t->disconnects.next = t->disconnects.prev = &t->disconnects;
+
+ update_transports();
+}
+
+void init_transport_registration(void)
+{
+ int s[2];
+
+ if(adb_socketpair(s)){
+ fatal_errno("cannot open transport registration socketpair");
+ }
+ D("socketpair: (%d,%d)\n", s[0], s[1]);
+
+ transport_registration_send = s[0];
+ transport_registration_recv = s[1];
+
+ fdevent_install(&transport_registration_fde,
+ transport_registration_recv,
+ transport_registration_func,
+ 0);
+
+ fdevent_set(&transport_registration_fde, FDE_READ);
+}
+
+/* the fdevent select pump is single threaded */
+static void register_transport(atransport *transport)
+{
+ tmsg m;
+ m.transport = transport;
+ m.action = 1;
+ D("transport: %s registered\n", transport->serial);
+ if(transport_write_action(transport_registration_send, &m)) {
+ fatal_errno("cannot write transport registration socket\n");
+ }
+}
+
+static void remove_transport(atransport *transport)
+{
+ tmsg m;
+ m.transport = transport;
+ m.action = 0;
+ D("transport: %s removed\n", transport->serial);
+ if(transport_write_action(transport_registration_send, &m)) {
+ fatal_errno("cannot write transport registration socket\n");
+ }
+}
+
+
+static void transport_unref_locked(atransport *t)
+{
+ t->ref_count--;
+ if (t->ref_count == 0) {
+ D("transport: %s unref (kicking and closing)\n", t->serial);
+ if (!t->kicked) {
+ t->kicked = 1;
+ t->kick(t);
+ }
+ t->close(t);
+ remove_transport(t);
+ } else {
+ D("transport: %s unref (count=%d)\n", t->serial, t->ref_count);
+ }
+}
+
+static void transport_unref(atransport *t)
+{
+ if (t) {
+ adb_mutex_lock(&transport_lock);
+ transport_unref_locked(t);
+ adb_mutex_unlock(&transport_lock);
+ }
+}
+
+void add_transport_disconnect(atransport* t, adisconnect* dis)
+{
+ adb_mutex_lock(&transport_lock);
+ dis->next = &t->disconnects;
+ dis->prev = dis->next->prev;
+ dis->prev->next = dis;
+ dis->next->prev = dis;
+ adb_mutex_unlock(&transport_lock);
+}
+
+void remove_transport_disconnect(atransport* t, adisconnect* dis)
+{
+ dis->prev->next = dis->next;
+ dis->next->prev = dis->prev;
+ dis->next = dis->prev = dis;
+}
+
+static int qual_match(const char *to_test,
+ const char *prefix, const char *qual, bool sanitize_qual)
+{
+ if (!to_test || !*to_test)
+ /* Return true if both the qual and to_test are null strings. */
+ return !qual || !*qual;
+
+ if (!qual)
+ return 0;
+
+ if (prefix) {
+ while (*prefix) {
+ if (*prefix++ != *to_test++)
+ return 0;
+ }
+ }
+
+ while (*qual) {
+ char ch = *qual++;
+ if (sanitize_qual && !isalnum(ch))
+ ch = '_';
+ if (ch != *to_test++)
+ return 0;
+ }
+
+ /* Everything matched so far. Return true if *to_test is a NUL. */
+ return !*to_test;
+}
+
+atransport* acquire_one_transport(ConnectionState state, TransportType type,
+ const char* serial, std::string* error_out) {
+ atransport *result = NULL;
+ int ambiguous = 0;
+
+retry:
+ *error_out = serial ? android::base::StringPrintf("device '%s' not found", serial) : "no devices found";
+
+ adb_mutex_lock(&transport_lock);
+ for (auto t : transport_list) {
+ if (t->connection_state == kCsNoPerm) {
+ *error_out = "insufficient permissions for device";
+ continue;
+ }
+
+ /* check for matching serial number */
+ if (serial) {
+ if ((t->serial && !strcmp(serial, t->serial)) ||
+ (t->devpath && !strcmp(serial, t->devpath)) ||
+ qual_match(serial, "product:", t->product, false) ||
+ qual_match(serial, "model:", t->model, true) ||
+ qual_match(serial, "device:", t->device, false)) {
+ if (result) {
+ *error_out = "more than one device";
+ ambiguous = 1;
+ result = NULL;
+ break;
+ }
+ result = t;
+ }
+ } else {
+ if (type == kTransportUsb && t->type == kTransportUsb) {
+ if (result) {
+ *error_out = "more than one device";
+ ambiguous = 1;
+ result = NULL;
+ break;
+ }
+ result = t;
+ } else if (type == kTransportLocal && t->type == kTransportLocal) {
+ if (result) {
+ *error_out = "more than one emulator";
+ ambiguous = 1;
+ result = NULL;
+ break;
+ }
+ result = t;
+ } else if (type == kTransportAny) {
+ if (result) {
+ *error_out = "more than one device/emulator";
+ ambiguous = 1;
+ result = NULL;
+ break;
+ }
+ result = t;
+ }
+ }
+ }
+ adb_mutex_unlock(&transport_lock);
+
+ if (result) {
+ if (result->connection_state == kCsUnauthorized) {
+ *error_out = "device unauthorized.\n";
+ char* ADB_VENDOR_KEYS = getenv("ADB_VENDOR_KEYS");
+ *error_out += "This adb server's $ADB_VENDOR_KEYS is ";
+ *error_out += ADB_VENDOR_KEYS ? ADB_VENDOR_KEYS : "not set";
+ *error_out += "\n";
+ *error_out += "Try 'adb kill-server' if that seems wrong.\n";
+ *error_out += "Otherwise check for a confirmation dialog on your device.";
+ result = NULL;
+ }
+
+ /* offline devices are ignored -- they are either being born or dying */
+ if (result && result->connection_state == kCsOffline) {
+ *error_out = "device offline";
+ result = NULL;
+ }
+
+ /* check for required connection state */
+ if (result && state != kCsAny && result->connection_state != state) {
+ *error_out = "invalid device state";
+ result = NULL;
+ }
+ }
+
+ if (result) {
+ /* found one that we can take */
+ *error_out = "success";
+ } else if (state != kCsAny && (serial || !ambiguous)) {
+ adb_sleep_ms(1000);
+ goto retry;
+ }
+
+ return result;
+}
+
+const char* atransport::connection_state_name() const {
+ switch (connection_state) {
+ case kCsOffline: return "offline";
+ case kCsBootloader: return "bootloader";
+ case kCsDevice: return "device";
+ case kCsHost: return "host";
+ case kCsRecovery: return "recovery";
+ case kCsNoPerm: return "no permissions";
+ case kCsSideload: return "sideload";
+ case kCsUnauthorized: return "unauthorized";
+ default: return "unknown";
+ }
+}
+
+void atransport::update_version(int version, size_t payload) {
+ protocol_version = std::min(version, A_VERSION);
+ max_payload = std::min(payload, MAX_PAYLOAD);
+}
+
+int atransport::get_protocol_version() const {
+ return protocol_version;
+}
+
+size_t atransport::get_max_payload() const {
+ return max_payload;
+}
+
+#if ADB_HOST
+
+static void append_transport_info(std::string* result, const char* key,
+ const char* value, bool sanitize) {
+ if (value == nullptr || *value == '\0') {
+ return;
+ }
+
+ *result += ' ';
+ *result += key;
+
+ for (const char* p = value; *p; ++p) {
+ result->push_back((!sanitize || isalnum(*p)) ? *p : '_');
+ }
+}
+
+static void append_transport(const atransport* t, std::string* result,
+ bool long_listing) {
+ const char* serial = t->serial;
+ if (!serial || !serial[0]) {
+ serial = "(no serial number)";
+ }
+
+ if (!long_listing) {
+ *result += serial;
+ *result += '\t';
+ *result += t->connection_state_name();
+ } else {
+ android::base::StringAppendF(result, "%-22s %s", serial, t->connection_state_name());
+
+ append_transport_info(result, "", t->devpath, false);
+ append_transport_info(result, "product:", t->product, false);
+ append_transport_info(result, "model:", t->model, true);
+ append_transport_info(result, "device:", t->device, false);
+ }
+ *result += '\n';
+}
+
+std::string list_transports(bool long_listing) {
+ std::string result;
+ adb_mutex_lock(&transport_lock);
+ for (const auto t : transport_list) {
+ append_transport(t, &result, long_listing);
+ }
+ adb_mutex_unlock(&transport_lock);
+ return result;
+}
+
+/* hack for osx */
+void close_usb_devices() {
+ adb_mutex_lock(&transport_lock);
+ for (auto t : transport_list) {
+ if (!t->kicked) {
+ t->kicked = 1;
+ t->kick(t);
+ }
+ }
+ adb_mutex_unlock(&transport_lock);
+}
+#endif // ADB_HOST
+
+int register_socket_transport(int s, const char *serial, int port, int local) {
+ atransport* t = new atransport();
+
+ if (!serial) {
+ char buf[32];
+ snprintf(buf, sizeof(buf), "T-%p", t);
+ serial = buf;
+ }
+
+ D("transport: %s init'ing for socket %d, on port %d\n", serial, s, port);
+ if (init_socket_transport(t, s, port, local) < 0) {
+ delete t;
+ return -1;
+ }
+
+ adb_mutex_lock(&transport_lock);
+ for (auto transport : pending_list) {
+ if (transport->serial && strcmp(serial, transport->serial) == 0) {
+ adb_mutex_unlock(&transport_lock);
+ delete t;
+ return -1;
+ }
+ }
+
+ for (auto transport : transport_list) {
+ if (transport->serial && strcmp(serial, transport->serial) == 0) {
+ adb_mutex_unlock(&transport_lock);
+ delete t;
+ return -1;
+ }
+ }
+
+ pending_list.push_front(t);
+ t->serial = strdup(serial);
+ adb_mutex_unlock(&transport_lock);
+
+ register_transport(t);
+ return 0;
+}
+
+#if ADB_HOST
+atransport *find_transport(const char *serial) {
+ atransport* result = nullptr;
+
+ adb_mutex_lock(&transport_lock);
+ for (auto t : transport_list) {
+ if (t->serial && strcmp(serial, t->serial) == 0) {
+ result = t;
+ break;
+ }
+ }
+ adb_mutex_unlock(&transport_lock);
+
+ return result;
+}
+
+void unregister_transport(atransport *t)
+{
+ adb_mutex_lock(&transport_lock);
+ transport_list.remove(t);
+ adb_mutex_unlock(&transport_lock);
+
+ kick_transport(t);
+ transport_unref(t);
+}
+
+// Unregisters all non-emulator TCP transports.
+void unregister_all_tcp_transports() {
+ adb_mutex_lock(&transport_lock);
+ for (auto it = transport_list.begin(); it != transport_list.end(); ) {
+ atransport* t = *it;
+ if (t->type == kTransportLocal && t->adb_port == 0) {
+ // We cannot call kick_transport when holding transport_lock.
+ if (!t->kicked) {
+ t->kicked = 1;
+ t->kick(t);
+ }
+ transport_unref_locked(t);
+
+ it = transport_list.erase(it);
+ } else {
+ ++it;
+ }
+ }
+
+ adb_mutex_unlock(&transport_lock);
+}
+
+#endif
+
+void register_usb_transport(usb_handle* usb, const char* serial,
+ const char* devpath, unsigned writeable) {
+ atransport* t = new atransport();
+
+ D("transport: %p init'ing for usb_handle %p (sn='%s')\n", t, usb,
+ serial ? serial : "");
+ init_usb_transport(t, usb, (writeable ? kCsOffline : kCsNoPerm));
+ if(serial) {
+ t->serial = strdup(serial);
+ }
+
+ if (devpath) {
+ t->devpath = strdup(devpath);
+ }
+
+ adb_mutex_lock(&transport_lock);
+ pending_list.push_front(t);
+ adb_mutex_unlock(&transport_lock);
+
+ register_transport(t);
+}
+
+// This should only be used for transports with connection_state == kCsNoPerm.
+void unregister_usb_transport(usb_handle *usb) {
+ adb_mutex_lock(&transport_lock);
+ transport_list.remove_if([usb](atransport* t) {
+ return t->usb == usb && t->connection_state == kCsNoPerm;
+ });
+ adb_mutex_unlock(&transport_lock);
+}
+
+#undef TRACE_TAG
+#define TRACE_TAG TRACE_RWX
+
+int check_header(apacket *p, atransport *t)
+{
+ if(p->msg.magic != (p->msg.command ^ 0xffffffff)) {
+ D("check_header(): invalid magic\n");
+ return -1;
+ }
+
+ if(p->msg.data_length > t->get_max_payload()) {
+ D("check_header(): %u > atransport::max_payload = %zu\n",
+ p->msg.data_length, t->get_max_payload());
+ return -1;
+ }
+
+ return 0;
+}
+
+int check_data(apacket *p)
+{
+ unsigned count, sum;
+ unsigned char *x;
+
+ count = p->msg.data_length;
+ x = p->data;
+ sum = 0;
+ while(count-- > 0) {
+ sum += *x++;
+ }
+
+ if(sum != p->msg.data_check) {
+ return -1;
+ } else {
+ return 0;
+ }
+}
diff --git a/adb/transport.h b/adb/transport.h
new file mode 100644
index 0000000..edcc99d
--- /dev/null
+++ b/adb/transport.h
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+#ifndef __TRANSPORT_H
+#define __TRANSPORT_H
+
+#include <sys/types.h>
+
+#include <string>
+
+#include "adb.h"
+
+/*
+ * Obtain a transport from the available transports.
+ * If state is != kCsAny, only transports in that state are considered.
+ * If serial is non-NULL then only the device with that serial will be chosen.
+ * If no suitable transport is found, error is set.
+ */
+atransport* acquire_one_transport(ConnectionState state, TransportType type,
+ const char* serial, std::string* error_out);
+void add_transport_disconnect(atransport* t, adisconnect* dis);
+void remove_transport_disconnect(atransport* t, adisconnect* dis);
+void kick_transport(atransport* t);
+void run_transport_disconnects(atransport* t);
+void update_transports(void);
+
+/* transports are ref-counted
+** get_device_transport does an acquire on your behalf before returning
+*/
+void init_transport_registration(void);
+std::string list_transports(bool long_listing);
+atransport* find_transport(const char* serial);
+
+void register_usb_transport(usb_handle* h, const char* serial,
+ const char* devpath, unsigned writeable);
+
+/* cause new transports to be init'd and added to the list */
+int register_socket_transport(int s, const char* serial, int port, int local);
+
+// This should only be used for transports with connection_state == kCsNoPerm.
+void unregister_usb_transport(usb_handle* usb);
+
+/* these should only be used for the "adb disconnect" command */
+void unregister_transport(atransport* t);
+void unregister_all_tcp_transports();
+
+int check_header(apacket* p, atransport* t);
+int check_data(apacket* p);
+
+/* for MacOS X cleanup */
+void close_usb_devices();
+
+void send_packet(apacket* p, atransport* t);
+
+asocket* create_device_tracker(void);
+
+#endif /* __TRANSPORT_H */
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
new file mode 100644
index 0000000..0dc9581
--- /dev/null
+++ b/adb/transport_local.cpp
@@ -0,0 +1,424 @@
+/*
+ * Copyright (C) 2007 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 TRACE_TAG TRACE_TRANSPORT
+
+#include "sysdeps.h"
+#include "transport.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <base/stringprintf.h>
+#include <cutils/sockets.h>
+
+#if !ADB_HOST
+#include "cutils/properties.h"
+#endif
+
+#include "adb.h"
+#include "adb_io.h"
+#include "adb_utils.h"
+
+#if ADB_HOST
+/* we keep a list of opened transports. The atransport struct knows to which
+ * local transport it is connected. The list is used to detect when we're
+ * trying to connect twice to a given local transport.
+ */
+#define ADB_LOCAL_TRANSPORT_MAX 64
+
+ADB_MUTEX_DEFINE( local_transports_lock );
+
+static atransport* local_transports[ ADB_LOCAL_TRANSPORT_MAX ];
+#endif /* ADB_HOST */
+
+static int remote_read(apacket *p, atransport *t)
+{
+ if(!ReadFdExactly(t->sfd, &p->msg, sizeof(amessage))){
+ D("remote local: read terminated (message)\n");
+ return -1;
+ }
+
+ if(check_header(p, t)) {
+ D("bad header: terminated (data)\n");
+ return -1;
+ }
+
+ if(!ReadFdExactly(t->sfd, p->data, p->msg.data_length)){
+ D("remote local: terminated (data)\n");
+ return -1;
+ }
+
+ if(check_data(p)) {
+ D("bad data: terminated (data)\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int remote_write(apacket *p, atransport *t)
+{
+ int length = p->msg.data_length;
+
+ if(!WriteFdExactly(t->sfd, &p->msg, sizeof(amessage) + length)) {
+ D("remote local: write terminated\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+void local_connect(int port) {
+ std::string dummy;
+ local_connect_arbitrary_ports(port-1, port, &dummy);
+}
+
+int local_connect_arbitrary_ports(int console_port, int adb_port, std::string* error) {
+ int fd = -1;
+
+#if ADB_HOST
+ const char *host = getenv("ADBHOST");
+ if (host) {
+ fd = network_connect(host, adb_port, SOCK_STREAM, 0, error);
+ }
+#endif
+ if (fd < 0) {
+ fd = socket_loopback_client(adb_port, SOCK_STREAM);
+ }
+
+ if (fd >= 0) {
+ D("client: connected on remote on fd %d\n", fd);
+ close_on_exec(fd);
+ disable_tcp_nagle(fd);
+ std::string serial = android::base::StringPrintf("emulator-%d", console_port);
+ register_socket_transport(fd, serial.c_str(), adb_port, 1);
+ return 0;
+ }
+ return -1;
+}
+
+
+static void *client_socket_thread(void *x)
+{
+#if ADB_HOST
+ int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
+ int count = ADB_LOCAL_TRANSPORT_MAX;
+
+ D("transport: client_socket_thread() starting\n");
+
+ /* try to connect to any number of running emulator instances */
+ /* this is only done when ADB starts up. later, each new emulator */
+ /* will send a message to ADB to indicate that is is starting up */
+ for ( ; count > 0; count--, port += 2 ) {
+ local_connect(port);
+ }
+#endif
+ return 0;
+}
+
+static void *server_socket_thread(void * arg)
+{
+ int serverfd, fd;
+ struct sockaddr addr;
+ socklen_t alen;
+ int port = (int) (uintptr_t) arg;
+
+ D("transport: server_socket_thread() starting\n");
+ serverfd = -1;
+ for(;;) {
+ if(serverfd == -1) {
+ serverfd = socket_inaddr_any_server(port, SOCK_STREAM);
+ if(serverfd < 0) {
+ D("server: cannot bind socket yet: %s\n", strerror(errno));
+ adb_sleep_ms(1000);
+ continue;
+ }
+ close_on_exec(serverfd);
+ }
+
+ alen = sizeof(addr);
+ D("server: trying to get new connection from %d\n", port);
+ fd = adb_socket_accept(serverfd, &addr, &alen);
+ if(fd >= 0) {
+ D("server: new connection on fd %d\n", fd);
+ close_on_exec(fd);
+ disable_tcp_nagle(fd);
+ register_socket_transport(fd, "host", port, 1);
+ }
+ }
+ D("transport: server_socket_thread() exiting\n");
+ return 0;
+}
+
+/* This is relevant only for ADB daemon running inside the emulator. */
+#if !ADB_HOST
+/*
+ * Redefine open and write for qemu_pipe.h that contains inlined references
+ * to those routines. We will redifine them back after qemu_pipe.h inclusion.
+ */
+#undef open
+#undef write
+#define open adb_open
+#define write adb_write
+#include <hardware/qemu_pipe.h>
+#undef open
+#undef write
+#define open ___xxx_open
+#define write ___xxx_write
+
+/* A worker thread that monitors host connections, and registers a transport for
+ * every new host connection. This thread replaces server_socket_thread on
+ * condition that adbd daemon runs inside the emulator, and emulator uses QEMUD
+ * pipe to communicate with adbd daemon inside the guest. This is done in order
+ * to provide more robust communication channel between ADB host and guest. The
+ * main issue with server_socket_thread approach is that it runs on top of TCP,
+ * and thus is sensitive to network disruptions. For instance, the
+ * ConnectionManager may decide to reset all network connections, in which case
+ * the connection between ADB host and guest will be lost. To make ADB traffic
+ * independent from the network, we use here 'adb' QEMUD service to transfer data
+ * between the host, and the guest. See external/qemu/android/adb-*.* that
+ * implements the emulator's side of the protocol. Another advantage of using
+ * QEMUD approach is that ADB will be up much sooner, since it doesn't depend
+ * anymore on network being set up.
+ * The guest side of the protocol contains the following phases:
+ * - Connect with adb QEMUD service. In this phase a handle to 'adb' QEMUD service
+ * is opened, and it becomes clear whether or not emulator supports that
+ * protocol.
+ * - Wait for the ADB host to create connection with the guest. This is done by
+ * sending an 'accept' request to the adb QEMUD service, and waiting on
+ * response.
+ * - When new ADB host connection is accepted, the connection with adb QEMUD
+ * service is registered as the transport, and a 'start' request is sent to the
+ * adb QEMUD service, indicating that the guest is ready to receive messages.
+ * Note that the guest will ignore messages sent down from the emulator before
+ * the transport registration is completed. That's why we need to send the
+ * 'start' request after the transport is registered.
+ */
+static void *qemu_socket_thread(void * arg)
+{
+/* 'accept' request to the adb QEMUD service. */
+static const char _accept_req[] = "accept";
+/* 'start' request to the adb QEMUD service. */
+static const char _start_req[] = "start";
+/* 'ok' reply from the adb QEMUD service. */
+static const char _ok_resp[] = "ok";
+
+ const int port = (int) (uintptr_t) arg;
+ int res, fd;
+ char tmp[256];
+ char con_name[32];
+
+ D("transport: qemu_socket_thread() starting\n");
+
+ /* adb QEMUD service connection request. */
+ snprintf(con_name, sizeof(con_name), "qemud:adb:%d", port);
+
+ /* Connect to the adb QEMUD service. */
+ fd = qemu_pipe_open(con_name);
+ if (fd < 0) {
+ /* This could be an older version of the emulator, that doesn't
+ * implement adb QEMUD service. Fall back to the old TCP way. */
+ D("adb service is not available. Falling back to TCP socket.\n");
+ adb_thread_create(server_socket_thread, arg);
+ return 0;
+ }
+
+ for(;;) {
+ /*
+ * Wait till the host creates a new connection.
+ */
+
+ /* Send the 'accept' request. */
+ res = adb_write(fd, _accept_req, strlen(_accept_req));
+ if ((size_t)res == strlen(_accept_req)) {
+ /* Wait for the response. In the response we expect 'ok' on success,
+ * or 'ko' on failure. */
+ res = adb_read(fd, tmp, sizeof(tmp));
+ if (res != 2 || memcmp(tmp, _ok_resp, 2)) {
+ D("Accepting ADB host connection has failed.\n");
+ adb_close(fd);
+ } else {
+ /* Host is connected. Register the transport, and start the
+ * exchange. */
+ register_socket_transport(fd, "host", port, 1);
+ adb_write(fd, _start_req, strlen(_start_req));
+ }
+
+ /* Prepare for accepting of the next ADB host connection. */
+ fd = qemu_pipe_open(con_name);
+ if (fd < 0) {
+ D("adb service become unavailable.\n");
+ return 0;
+ }
+ } else {
+ D("Unable to send the '%s' request to ADB service.\n", _accept_req);
+ return 0;
+ }
+ }
+ D("transport: qemu_socket_thread() exiting\n");
+ return 0;
+}
+#endif // !ADB_HOST
+
+void local_init(int port)
+{
+ void* (*func)(void *);
+
+ if(HOST) {
+ func = client_socket_thread;
+ } else {
+#if ADB_HOST
+ func = server_socket_thread;
+#else
+ /* For the adbd daemon in the system image we need to distinguish
+ * between the device, and the emulator. */
+ char is_qemu[PROPERTY_VALUE_MAX];
+ property_get("ro.kernel.qemu", is_qemu, "");
+ if (!strcmp(is_qemu, "1")) {
+ /* Running inside the emulator: use QEMUD pipe as the transport. */
+ func = qemu_socket_thread;
+ } else {
+ /* Running inside the device: use TCP socket as the transport. */
+ func = server_socket_thread;
+ }
+#endif // !ADB_HOST
+ }
+
+ D("transport: local %s init\n", HOST ? "client" : "server");
+
+ if (!adb_thread_create(func, (void *) (uintptr_t) port)) {
+ fatal_errno("cannot create local socket %s thread", HOST ? "client" : "server");
+ }
+}
+
+static void remote_kick(atransport *t)
+{
+ int fd = t->sfd;
+ t->sfd = -1;
+ adb_shutdown(fd);
+ adb_close(fd);
+
+#if ADB_HOST
+ if(HOST) {
+ int nn;
+ adb_mutex_lock( &local_transports_lock );
+ for (nn = 0; nn < ADB_LOCAL_TRANSPORT_MAX; nn++) {
+ if (local_transports[nn] == t) {
+ local_transports[nn] = NULL;
+ break;
+ }
+ }
+ adb_mutex_unlock( &local_transports_lock );
+ }
+#endif
+}
+
+static void remote_close(atransport *t)
+{
+ int fd = t->sfd;
+ if (fd != -1) {
+ t->sfd = -1;
+ adb_close(fd);
+ }
+}
+
+
+#if ADB_HOST
+/* Only call this function if you already hold local_transports_lock. */
+atransport* find_emulator_transport_by_adb_port_locked(int adb_port)
+{
+ int i;
+ for (i = 0; i < ADB_LOCAL_TRANSPORT_MAX; i++) {
+ if (local_transports[i] && local_transports[i]->adb_port == adb_port) {
+ return local_transports[i];
+ }
+ }
+ return NULL;
+}
+
+atransport* find_emulator_transport_by_adb_port(int adb_port)
+{
+ adb_mutex_lock( &local_transports_lock );
+ atransport* result = find_emulator_transport_by_adb_port_locked(adb_port);
+ adb_mutex_unlock( &local_transports_lock );
+ return result;
+}
+
+/* Only call this function if you already hold local_transports_lock. */
+int get_available_local_transport_index_locked()
+{
+ int i;
+ for (i = 0; i < ADB_LOCAL_TRANSPORT_MAX; i++) {
+ if (local_transports[i] == NULL) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+int get_available_local_transport_index()
+{
+ adb_mutex_lock( &local_transports_lock );
+ int result = get_available_local_transport_index_locked();
+ adb_mutex_unlock( &local_transports_lock );
+ return result;
+}
+#endif
+
+int init_socket_transport(atransport *t, int s, int adb_port, int local)
+{
+ int fail = 0;
+
+ t->kick = remote_kick;
+ t->close = remote_close;
+ t->read_from_remote = remote_read;
+ t->write_to_remote = remote_write;
+ t->sfd = s;
+ t->sync_token = 1;
+ t->connection_state = kCsOffline;
+ t->type = kTransportLocal;
+ t->adb_port = 0;
+
+#if ADB_HOST
+ if (HOST && local) {
+ adb_mutex_lock( &local_transports_lock );
+ {
+ t->adb_port = adb_port;
+ atransport* existing_transport =
+ find_emulator_transport_by_adb_port_locked(adb_port);
+ int index = get_available_local_transport_index_locked();
+ if (existing_transport != NULL) {
+ D("local transport for port %d already registered (%p)?\n",
+ adb_port, existing_transport);
+ fail = -1;
+ } else if (index < 0) {
+ // Too many emulators.
+ D("cannot register more emulators. Maximum is %d\n",
+ ADB_LOCAL_TRANSPORT_MAX);
+ fail = -1;
+ } else {
+ local_transports[index] = t;
+ }
+ }
+ adb_mutex_unlock( &local_transports_lock );
+ }
+#endif
+ return fail;
+}
diff --git a/adb/transport_test.cpp b/adb/transport_test.cpp
new file mode 100644
index 0000000..4b74adf
--- /dev/null
+++ b/adb/transport_test.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "transport.h"
+
+#include <gtest/gtest.h>
+
+#include "adb.h"
+
+class TestTransport : public atransport {
+public:
+ bool operator==(const atransport& rhs) const {
+ EXPECT_EQ(read_from_remote, rhs.read_from_remote);
+ EXPECT_EQ(write_to_remote, rhs.write_to_remote);
+ EXPECT_EQ(close, rhs.close);
+ EXPECT_EQ(kick, rhs.kick);
+
+ EXPECT_EQ(fd, rhs.fd);
+ EXPECT_EQ(transport_socket, rhs.transport_socket);
+
+ EXPECT_EQ(
+ 0, memcmp(&transport_fde, &rhs.transport_fde, sizeof(fdevent)));
+
+ EXPECT_EQ(ref_count, rhs.ref_count);
+ EXPECT_EQ(sync_token, rhs.sync_token);
+ EXPECT_EQ(connection_state, rhs.connection_state);
+ EXPECT_EQ(online, rhs.online);
+ EXPECT_EQ(type, rhs.type);
+
+ EXPECT_EQ(usb, rhs.usb);
+ EXPECT_EQ(sfd, rhs.sfd);
+
+ EXPECT_EQ(serial, rhs.serial);
+ EXPECT_EQ(product, rhs.product);
+ EXPECT_EQ(model, rhs.model);
+ EXPECT_EQ(device, rhs.device);
+ EXPECT_EQ(devpath, rhs.devpath);
+ EXPECT_EQ(adb_port, rhs.adb_port);
+ EXPECT_EQ(kicked, rhs.kicked);
+
+ EXPECT_EQ(
+ 0, memcmp(&disconnects, &rhs.disconnects, sizeof(adisconnect)));
+
+ EXPECT_EQ(key, rhs.key);
+ EXPECT_EQ(0, memcmp(token, rhs.token, TOKEN_SIZE));
+ EXPECT_EQ(0, memcmp(&auth_fde, &rhs.auth_fde, sizeof(fdevent)));
+ EXPECT_EQ(failed_auth_attempts, rhs.failed_auth_attempts);
+
+ return true;
+ }
+};
+
+TEST(transport, kick_transport) {
+ TestTransport t;
+
+ // Mutate some member so we can test that the function is run.
+ t.kick = [](atransport* trans) { trans->fd = 42; };
+
+ TestTransport expected;
+ expected.kick = t.kick;
+ expected.fd = 42;
+ expected.kicked = 1;
+
+ kick_transport(&t);
+ ASSERT_EQ(42, t.fd);
+ ASSERT_EQ(1, t.kicked);
+ ASSERT_EQ(expected, t);
+}
+
+TEST(transport, kick_transport_already_kicked) {
+ // Ensure that the transport is not modified if the transport has already been
+ // kicked.
+ TestTransport t;
+ t.kicked = 1;
+ t.kick = [](atransport*) { FAIL() << "Kick should not have been called"; };
+
+ TestTransport expected;
+ expected.kicked = 1;
+ expected.kick = t.kick;
+
+ kick_transport(&t);
+ ASSERT_EQ(expected, t);
+}
+
+// Disabled because the function currently segfaults for a zeroed atransport. I
+// want to make sure I understand how this is working at all before I try fixing
+// that.
+TEST(transport, DISABLED_run_transport_disconnects_zeroed_atransport) {
+ atransport t;
+ run_transport_disconnects(&t);
+}
diff --git a/adb/transport_usb.cpp b/adb/transport_usb.cpp
new file mode 100644
index 0000000..2c975a9
--- /dev/null
+++ b/adb/transport_usb.cpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2007 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 TRACE_TAG TRACE_TRANSPORT
+
+#include "sysdeps.h"
+#include "transport.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "adb.h"
+
+static int remote_read(apacket *p, atransport *t)
+{
+ if(usb_read(t->usb, &p->msg, sizeof(amessage))){
+ D("remote usb: read terminated (message)\n");
+ return -1;
+ }
+
+ if(check_header(p, t)) {
+ D("remote usb: check_header failed\n");
+ return -1;
+ }
+
+ if(p->msg.data_length) {
+ if(usb_read(t->usb, p->data, p->msg.data_length)){
+ D("remote usb: terminated (data)\n");
+ return -1;
+ }
+ }
+
+ if(check_data(p)) {
+ D("remote usb: check_data failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int remote_write(apacket *p, atransport *t)
+{
+ unsigned size = p->msg.data_length;
+
+ if(usb_write(t->usb, &p->msg, sizeof(amessage))) {
+ D("remote usb: 1 - write terminated\n");
+ return -1;
+ }
+ if(p->msg.data_length == 0) return 0;
+ if(usb_write(t->usb, &p->data, size)) {
+ D("remote usb: 2 - write terminated\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void remote_close(atransport *t)
+{
+ usb_close(t->usb);
+ t->usb = 0;
+}
+
+static void remote_kick(atransport *t)
+{
+ usb_kick(t->usb);
+}
+
+void init_usb_transport(atransport *t, usb_handle *h, ConnectionState state)
+{
+ D("transport: usb\n");
+ t->close = remote_close;
+ t->kick = remote_kick;
+ t->read_from_remote = remote_read;
+ t->write_to_remote = remote_write;
+ t->sync_token = 1;
+ t->connection_state = state;
+ t->type = kTransportUsb;
+ t->usb = h;
+
+#if ADB_HOST
+ HOST = 1;
+#else
+ HOST = 0;
+#endif
+}
+
+#if ADB_HOST
+int is_adb_interface(int vid, int pid, int usb_class, int usb_subclass, int usb_protocol)
+{
+ return (usb_class == ADB_CLASS && usb_subclass == ADB_SUBCLASS && usb_protocol == ADB_PROTOCOL);
+}
+#endif
diff --git a/adb/usb_linux.cpp b/adb/usb_linux.cpp
new file mode 100644
index 0000000..e570ef5
--- /dev/null
+++ b/adb/usb_linux.cpp
@@ -0,0 +1,598 @@
+/*
+ * Copyright (C) 2007 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 TRACE_TAG TRACE_USB
+
+#include "sysdeps.h"
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/usb/ch9.h>
+#include <linux/usbdevice_fs.h>
+#include <linux/version.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <chrono>
+#include <condition_variable>
+#include <list>
+#include <mutex>
+#include <string>
+
+#include <base/file.h>
+#include <base/stringprintf.h>
+#include <base/strings.h>
+
+#include "adb.h"
+#include "transport.h"
+
+using namespace std::literals;
+
+/* usb scan debugging is waaaay too verbose */
+#define DBGX(x...)
+
+struct usb_handle {
+ ~usb_handle() {
+ if (fd != -1) unix_close(fd);
+ }
+
+ std::string path;
+ int fd = -1;
+ unsigned char ep_in;
+ unsigned char ep_out;
+
+ unsigned zero_mask;
+ unsigned writeable = 1;
+
+ usbdevfs_urb urb_in;
+ usbdevfs_urb urb_out;
+
+ bool urb_in_busy = false;
+ bool urb_out_busy = false;
+ bool dead = false;
+
+ std::condition_variable cv;
+ std::mutex mutex;
+
+ // for garbage collecting disconnected devices
+ bool mark;
+
+ // ID of thread currently in REAPURB
+ pthread_t reaper_thread = 0;
+};
+
+static std::mutex g_usb_handles_mutex;
+static std::list<usb_handle*> g_usb_handles;
+
+static int is_known_device(const char* dev_name) {
+ std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
+ for (usb_handle* usb : g_usb_handles) {
+ if (usb->path == dev_name) {
+ // set mark flag to indicate this device is still alive
+ usb->mark = true;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void kick_disconnected_devices() {
+ std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
+ // kick any devices in the device list that were not found in the device scan
+ for (usb_handle* usb : g_usb_handles) {
+ if (!usb->mark) {
+ usb_kick(usb);
+ } else {
+ usb->mark = false;
+ }
+ }
+}
+
+static inline bool contains_non_digit(const char* name) {
+ while (*name) {
+ if (!isdigit(*name++)) return true;
+ }
+ return false;
+}
+
+static void find_usb_device(const std::string& base,
+ void (*register_device_callback)
+ (const char*, const char*, unsigned char, unsigned char, int, int, unsigned))
+{
+ std::unique_ptr<DIR, int(*)(DIR*)> bus_dir(opendir(base.c_str()), closedir);
+ if (!bus_dir) return;
+
+ dirent* de;
+ while ((de = readdir(bus_dir.get())) != 0) {
+ if (contains_non_digit(de->d_name)) continue;
+
+ std::string bus_name = base + "/" + de->d_name;
+
+ std::unique_ptr<DIR, int(*)(DIR*)> dev_dir(opendir(bus_name.c_str()), closedir);
+ if (!dev_dir) continue;
+
+ while ((de = readdir(dev_dir.get()))) {
+ unsigned char devdesc[4096];
+ unsigned char* bufptr = devdesc;
+ unsigned char* bufend;
+ struct usb_device_descriptor* device;
+ struct usb_config_descriptor* config;
+ struct usb_interface_descriptor* interface;
+ struct usb_endpoint_descriptor *ep1, *ep2;
+ unsigned zero_mask = 0;
+ unsigned vid, pid;
+
+ if (contains_non_digit(de->d_name)) continue;
+
+ std::string dev_name = bus_name + "/" + de->d_name;
+ if (is_known_device(dev_name.c_str())) {
+ continue;
+ }
+
+ int fd = unix_open(dev_name.c_str(), O_RDONLY | O_CLOEXEC);
+ if (fd == -1) {
+ continue;
+ }
+
+ size_t desclength = unix_read(fd, devdesc, sizeof(devdesc));
+ bufend = bufptr + desclength;
+
+ // should have device and configuration descriptors, and atleast two endpoints
+ if (desclength < USB_DT_DEVICE_SIZE + USB_DT_CONFIG_SIZE) {
+ D("desclength %zu is too small\n", desclength);
+ unix_close(fd);
+ continue;
+ }
+
+ device = (struct usb_device_descriptor*)bufptr;
+ bufptr += USB_DT_DEVICE_SIZE;
+
+ if((device->bLength != USB_DT_DEVICE_SIZE) || (device->bDescriptorType != USB_DT_DEVICE)) {
+ unix_close(fd);
+ continue;
+ }
+
+ vid = device->idVendor;
+ pid = device->idProduct;
+ DBGX("[ %s is V:%04x P:%04x ]\n", dev_name.c_str(), vid, pid);
+
+ // should have config descriptor next
+ config = (struct usb_config_descriptor *)bufptr;
+ bufptr += USB_DT_CONFIG_SIZE;
+ if (config->bLength != USB_DT_CONFIG_SIZE || config->bDescriptorType != USB_DT_CONFIG) {
+ D("usb_config_descriptor not found\n");
+ unix_close(fd);
+ continue;
+ }
+
+ // loop through all the descriptors and look for the ADB interface
+ while (bufptr < bufend) {
+ unsigned char length = bufptr[0];
+ unsigned char type = bufptr[1];
+
+ if (type == USB_DT_INTERFACE) {
+ interface = (struct usb_interface_descriptor *)bufptr;
+ bufptr += length;
+
+ if (length != USB_DT_INTERFACE_SIZE) {
+ D("interface descriptor has wrong size\n");
+ break;
+ }
+
+ DBGX("bInterfaceClass: %d, bInterfaceSubClass: %d,"
+ "bInterfaceProtocol: %d, bNumEndpoints: %d\n",
+ interface->bInterfaceClass, interface->bInterfaceSubClass,
+ interface->bInterfaceProtocol, interface->bNumEndpoints);
+
+ if (interface->bNumEndpoints == 2 &&
+ is_adb_interface(vid, pid, interface->bInterfaceClass,
+ interface->bInterfaceSubClass, interface->bInterfaceProtocol)) {
+
+ struct stat st;
+ char pathbuf[128];
+ char link[256];
+ char *devpath = nullptr;
+
+ DBGX("looking for bulk endpoints\n");
+ // looks like ADB...
+ ep1 = (struct usb_endpoint_descriptor *)bufptr;
+ bufptr += USB_DT_ENDPOINT_SIZE;
+ // For USB 3.0 SuperSpeed devices, skip potential
+ // USB 3.0 SuperSpeed Endpoint Companion descriptor
+ if (bufptr+2 <= devdesc + desclength &&
+ bufptr[0] == USB_DT_SS_EP_COMP_SIZE &&
+ bufptr[1] == USB_DT_SS_ENDPOINT_COMP) {
+ bufptr += USB_DT_SS_EP_COMP_SIZE;
+ }
+ ep2 = (struct usb_endpoint_descriptor *)bufptr;
+ bufptr += USB_DT_ENDPOINT_SIZE;
+ if (bufptr+2 <= devdesc + desclength &&
+ bufptr[0] == USB_DT_SS_EP_COMP_SIZE &&
+ bufptr[1] == USB_DT_SS_ENDPOINT_COMP) {
+ bufptr += USB_DT_SS_EP_COMP_SIZE;
+ }
+
+ if (bufptr > devdesc + desclength ||
+ ep1->bLength != USB_DT_ENDPOINT_SIZE ||
+ ep1->bDescriptorType != USB_DT_ENDPOINT ||
+ ep2->bLength != USB_DT_ENDPOINT_SIZE ||
+ ep2->bDescriptorType != USB_DT_ENDPOINT) {
+ D("endpoints not found\n");
+ break;
+ }
+
+ // both endpoints should be bulk
+ if (ep1->bmAttributes != USB_ENDPOINT_XFER_BULK ||
+ ep2->bmAttributes != USB_ENDPOINT_XFER_BULK) {
+ D("bulk endpoints not found\n");
+ continue;
+ }
+ /* aproto 01 needs 0 termination */
+ if(interface->bInterfaceProtocol == 0x01) {
+ zero_mask = ep1->wMaxPacketSize - 1;
+ }
+
+ // we have a match. now we just need to figure out which is in and which is out.
+ unsigned char local_ep_in, local_ep_out;
+ if (ep1->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
+ local_ep_in = ep1->bEndpointAddress;
+ local_ep_out = ep2->bEndpointAddress;
+ } else {
+ local_ep_in = ep2->bEndpointAddress;
+ local_ep_out = ep1->bEndpointAddress;
+ }
+
+ // Determine the device path
+ if (!fstat(fd, &st) && S_ISCHR(st.st_mode)) {
+ char *slash;
+ ssize_t link_len;
+ snprintf(pathbuf, sizeof(pathbuf), "/sys/dev/char/%d:%d",
+ major(st.st_rdev), minor(st.st_rdev));
+ link_len = readlink(pathbuf, link, sizeof(link) - 1);
+ if (link_len > 0) {
+ link[link_len] = '\0';
+ slash = strrchr(link, '/');
+ if (slash) {
+ snprintf(pathbuf, sizeof(pathbuf),
+ "usb:%s", slash + 1);
+ devpath = pathbuf;
+ }
+ }
+ }
+
+ register_device_callback(dev_name.c_str(), devpath,
+ local_ep_in, local_ep_out,
+ interface->bInterfaceNumber, device->iSerialNumber, zero_mask);
+ break;
+ }
+ } else {
+ bufptr += length;
+ }
+ } // end of while
+
+ unix_close(fd);
+ }
+ }
+}
+
+static int usb_bulk_write(usb_handle* h, const void* data, int len) {
+ std::unique_lock<std::mutex> lock(h->mutex);
+ D("++ usb_bulk_write ++\n");
+
+ usbdevfs_urb* urb = &h->urb_out;
+ memset(urb, 0, sizeof(*urb));
+ urb->type = USBDEVFS_URB_TYPE_BULK;
+ urb->endpoint = h->ep_out;
+ urb->status = -1;
+ urb->buffer = const_cast<void*>(data);
+ urb->buffer_length = len;
+
+ if (h->dead) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (TEMP_FAILURE_RETRY(ioctl(h->fd, USBDEVFS_SUBMITURB, urb)) == -1) {
+ return -1;
+ }
+
+ h->urb_out_busy = true;
+ while (true) {
+ auto now = std::chrono::system_clock::now();
+ if (h->cv.wait_until(lock, now + 5s) == std::cv_status::timeout || h->dead) {
+ // TODO: call USBDEVFS_DISCARDURB?
+ errno = ETIMEDOUT;
+ return -1;
+ }
+ if (!h->urb_out_busy) {
+ if (urb->status != 0) {
+ errno = -urb->status;
+ return -1;
+ }
+ return urb->actual_length;
+ }
+ }
+}
+
+static int usb_bulk_read(usb_handle* h, void* data, int len) {
+ std::unique_lock<std::mutex> lock(h->mutex);
+ D("++ usb_bulk_read ++\n");
+
+ usbdevfs_urb* urb = &h->urb_in;
+ memset(urb, 0, sizeof(*urb));
+ urb->type = USBDEVFS_URB_TYPE_BULK;
+ urb->endpoint = h->ep_in;
+ urb->status = -1;
+ urb->buffer = data;
+ urb->buffer_length = len;
+
+ if (h->dead) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (TEMP_FAILURE_RETRY(ioctl(h->fd, USBDEVFS_SUBMITURB, urb)) == -1) {
+ return -1;
+ }
+
+ h->urb_in_busy = true;
+ while (true) {
+ D("[ reap urb - wait ]\n");
+ h->reaper_thread = pthread_self();
+ int fd = h->fd;
+ lock.unlock();
+
+ // This ioctl must not have TEMP_FAILURE_RETRY because we send SIGALRM to break out.
+ usbdevfs_urb* out = nullptr;
+ int res = ioctl(fd, USBDEVFS_REAPURB, &out);
+ int saved_errno = errno;
+
+ lock.lock();
+ h->reaper_thread = 0;
+ if (h->dead) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (res < 0) {
+ if (saved_errno == EINTR) {
+ continue;
+ }
+ D("[ reap urb - error ]\n");
+ errno = saved_errno;
+ return -1;
+ }
+ D("[ urb @%p status = %d, actual = %d ]\n", out, out->status, out->actual_length);
+
+ if (out == &h->urb_in) {
+ D("[ reap urb - IN complete ]\n");
+ h->urb_in_busy = false;
+ if (urb->status != 0) {
+ errno = -urb->status;
+ return -1;
+ }
+ return urb->actual_length;
+ }
+ if (out == &h->urb_out) {
+ D("[ reap urb - OUT compelete ]\n");
+ h->urb_out_busy = false;
+ h->cv.notify_all();
+ }
+ }
+}
+
+
+int usb_write(usb_handle *h, const void *_data, int len)
+{
+ D("++ usb_write ++\n");
+
+ unsigned char *data = (unsigned char*) _data;
+ int n = usb_bulk_write(h, data, len);
+ if (n != len) {
+ D("ERROR: n = %d, errno = %d (%s)\n", n, errno, strerror(errno));
+ return -1;
+ }
+
+ if (h->zero_mask && !(len & h->zero_mask)) {
+ // If we need 0-markers and our transfer is an even multiple of the packet size,
+ // then send a zero marker.
+ return usb_bulk_write(h, _data, 0);
+ }
+
+ D("-- usb_write --\n");
+ return 0;
+}
+
+int usb_read(usb_handle *h, void *_data, int len)
+{
+ unsigned char *data = (unsigned char*) _data;
+ int n;
+
+ D("++ usb_read ++\n");
+ while(len > 0) {
+ int xfer = len;
+
+ D("[ usb read %d fd = %d], path=%s\n", xfer, h->fd, h->path.c_str());
+ n = usb_bulk_read(h, data, xfer);
+ D("[ usb read %d ] = %d, path=%s\n", xfer, n, h->path.c_str());
+ if(n != xfer) {
+ if((errno == ETIMEDOUT) && (h->fd != -1)) {
+ D("[ timeout ]\n");
+ if(n > 0){
+ data += n;
+ len -= n;
+ }
+ continue;
+ }
+ D("ERROR: n = %d, errno = %d (%s)\n",
+ n, errno, strerror(errno));
+ return -1;
+ }
+
+ len -= xfer;
+ data += xfer;
+ }
+
+ D("-- usb_read --\n");
+ return 0;
+}
+
+void usb_kick(usb_handle* h) {
+ std::lock_guard<std::mutex> lock(h->mutex);
+ D("[ kicking %p (fd = %d) ]\n", h, h->fd);
+ if (!h->dead) {
+ h->dead = true;
+
+ if (h->writeable) {
+ /* HACK ALERT!
+ ** Sometimes we get stuck in ioctl(USBDEVFS_REAPURB).
+ ** This is a workaround for that problem.
+ */
+ if (h->reaper_thread) {
+ pthread_kill(h->reaper_thread, SIGALRM);
+ }
+
+ /* cancel any pending transactions
+ ** these will quietly fail if the txns are not active,
+ ** but this ensures that a reader blocked on REAPURB
+ ** will get unblocked
+ */
+ ioctl(h->fd, USBDEVFS_DISCARDURB, &h->urb_in);
+ ioctl(h->fd, USBDEVFS_DISCARDURB, &h->urb_out);
+ h->urb_in.status = -ENODEV;
+ h->urb_out.status = -ENODEV;
+ h->urb_in_busy = false;
+ h->urb_out_busy = false;
+ h->cv.notify_all();
+ } else {
+ unregister_usb_transport(h);
+ }
+ }
+}
+
+int usb_close(usb_handle* h) {
+ std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
+ g_usb_handles.remove(h);
+
+ D("-- usb close %p (fd = %d) --\n", h, h->fd);
+
+ delete h;
+
+ return 0;
+}
+
+static void register_device(const char* dev_name, const char* dev_path,
+ unsigned char ep_in, unsigned char ep_out,
+ int interface, int serial_index,
+ unsigned zero_mask) {
+ // Since Linux will not reassign the device ID (and dev_name) as long as the
+ // device is open, we can add to the list here once we open it and remove
+ // from the list when we're finally closed and everything will work out
+ // fine.
+ //
+ // If we have a usb_handle on the list of handles with a matching name, we
+ // have no further work to do.
+ {
+ std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
+ for (usb_handle* usb: g_usb_handles) {
+ if (usb->path == dev_name) {
+ return;
+ }
+ }
+ }
+
+ D("[ usb located new device %s (%d/%d/%d) ]\n", dev_name, ep_in, ep_out, interface);
+ std::unique_ptr<usb_handle> usb(new usb_handle);
+ usb->path = dev_name;
+ usb->ep_in = ep_in;
+ usb->ep_out = ep_out;
+ usb->zero_mask = zero_mask;
+
+ // Initialize mark so we don't get garbage collected after the device scan.
+ usb->mark = true;
+
+ usb->fd = unix_open(usb->path.c_str(), O_RDWR | O_CLOEXEC);
+ if (usb->fd == -1) {
+ // Opening RW failed, so see if we have RO access.
+ usb->fd = unix_open(usb->path.c_str(), O_RDONLY | O_CLOEXEC);
+ if (usb->fd == -1) {
+ D("[ usb open %s failed: %s]\n", usb->path.c_str(), strerror(errno));
+ return;
+ }
+ usb->writeable = 0;
+ }
+
+ D("[ usb opened %s%s, fd=%d]\n",
+ usb->path.c_str(), (usb->writeable ? "" : " (read-only)"), usb->fd);
+
+ if (usb->writeable) {
+ if (ioctl(usb->fd, USBDEVFS_CLAIMINTERFACE, &interface) != 0) {
+ D("[ usb ioctl(%d, USBDEVFS_CLAIMINTERFACE) failed: %s]\n", usb->fd, strerror(errno));
+ return;
+ }
+ }
+
+ // Read the device's serial number.
+ std::string serial_path = android::base::StringPrintf(
+ "/sys/bus/usb/devices/%s/serial", dev_path + 4);
+ std::string serial;
+ if (!android::base::ReadFileToString(serial_path, &serial)) {
+ D("[ usb read %s failed: %s ]\n", serial_path.c_str(), strerror(errno));
+ // We don't actually want to treat an unknown serial as an error because
+ // devices aren't able to communicate a serial number in early bringup.
+ // http://b/20883914
+ serial = "";
+ }
+ serial = android::base::Trim(serial);
+
+ // Add to the end of the active handles.
+ usb_handle* done_usb = usb.release();
+ {
+ std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
+ g_usb_handles.push_back(done_usb);
+ }
+ register_usb_transport(done_usb, serial.c_str(), dev_path, done_usb->writeable);
+}
+
+static void* device_poll_thread(void* unused) {
+ D("Created device thread\n");
+ while (true) {
+ // TODO: Use inotify.
+ find_usb_device("/dev/bus/usb", register_device);
+ kick_disconnected_devices();
+ sleep(1);
+ }
+ return nullptr;
+}
+
+void usb_init() {
+ struct sigaction actions;
+ memset(&actions, 0, sizeof(actions));
+ sigemptyset(&actions.sa_mask);
+ actions.sa_flags = 0;
+ actions.sa_handler = [](int) {};
+ sigaction(SIGALRM, &actions, nullptr);
+
+ if (!adb_thread_create(device_poll_thread, nullptr)) {
+ fatal_errno("cannot create input thread");
+ }
+}
diff --git a/adb/usb_linux_client.cpp b/adb/usb_linux_client.cpp
new file mode 100644
index 0000000..b1b3538
--- /dev/null
+++ b/adb/usb_linux_client.cpp
@@ -0,0 +1,521 @@
+/*
+ * Copyright (C) 2007 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 TRACE_TAG TRACE_USB
+
+#include "sysdeps.h"
+
+#include <cutils/properties.h>
+#include <dirent.h>
+#include <errno.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/functionfs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "adb.h"
+#include "transport.h"
+
+#define MAX_PACKET_SIZE_FS 64
+#define MAX_PACKET_SIZE_HS 512
+#define MAX_PACKET_SIZE_SS 1024
+
+#define cpu_to_le16(x) htole16(x)
+#define cpu_to_le32(x) htole32(x)
+
+struct usb_handle
+{
+ adb_cond_t notify;
+ adb_mutex_t lock;
+
+ int (*write)(usb_handle *h, const void *data, int len);
+ int (*read)(usb_handle *h, void *data, int len);
+ void (*kick)(usb_handle *h);
+
+ // Legacy f_adb
+ int fd;
+
+ // FunctionFS
+ int control;
+ int bulk_out; /* "out" from the host's perspective => source for adbd */
+ int bulk_in; /* "in" from the host's perspective => sink for adbd */
+};
+
+struct func_desc {
+ struct usb_interface_descriptor intf;
+ struct usb_endpoint_descriptor_no_audio source;
+ struct usb_endpoint_descriptor_no_audio sink;
+} __attribute__((packed));
+
+struct desc_v1 {
+ struct usb_functionfs_descs_head_v1 {
+ __le32 magic;
+ __le32 length;
+ __le32 fs_count;
+ __le32 hs_count;
+ } __attribute__((packed)) header;
+ struct func_desc fs_descs, hs_descs;
+} __attribute__((packed));
+
+struct desc_v2 {
+ struct usb_functionfs_descs_head_v2 header;
+ // The rest of the structure depends on the flags in the header.
+ __le32 fs_count;
+ __le32 hs_count;
+ struct func_desc fs_descs, hs_descs;
+} __attribute__((packed));
+
+static struct func_desc fs_descriptors = {
+ .intf = {
+ .bLength = sizeof(fs_descriptors.intf),
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = ADB_CLASS,
+ .bInterfaceSubClass = ADB_SUBCLASS,
+ .bInterfaceProtocol = ADB_PROTOCOL,
+ .iInterface = 1, /* first string from the provided table */
+ },
+ .source = {
+ .bLength = sizeof(fs_descriptors.source),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 1 | USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = MAX_PACKET_SIZE_FS,
+ },
+ .sink = {
+ .bLength = sizeof(fs_descriptors.sink),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 2 | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = MAX_PACKET_SIZE_FS,
+ },
+};
+
+static struct func_desc hs_descriptors = {
+ .intf = {
+ .bLength = sizeof(hs_descriptors.intf),
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = ADB_CLASS,
+ .bInterfaceSubClass = ADB_SUBCLASS,
+ .bInterfaceProtocol = ADB_PROTOCOL,
+ .iInterface = 1, /* first string from the provided table */
+ },
+ .source = {
+ .bLength = sizeof(hs_descriptors.source),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 1 | USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = MAX_PACKET_SIZE_HS,
+ },
+ .sink = {
+ .bLength = sizeof(hs_descriptors.sink),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 2 | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = MAX_PACKET_SIZE_HS,
+ },
+};
+
+#define STR_INTERFACE_ "ADB Interface"
+
+static const struct {
+ struct usb_functionfs_strings_head header;
+ struct {
+ __le16 code;
+ const char str1[sizeof(STR_INTERFACE_)];
+ } __attribute__((packed)) lang0;
+} __attribute__((packed)) strings = {
+ .header = {
+ .magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC),
+ .length = cpu_to_le32(sizeof(strings)),
+ .str_count = cpu_to_le32(1),
+ .lang_count = cpu_to_le32(1),
+ },
+ .lang0 = {
+ cpu_to_le16(0x0409), /* en-us */
+ STR_INTERFACE_,
+ },
+};
+
+
+
+static void *usb_adb_open_thread(void *x)
+{
+ struct usb_handle *usb = (struct usb_handle *)x;
+ int fd;
+
+ while (true) {
+ // wait until the USB device needs opening
+ adb_mutex_lock(&usb->lock);
+ while (usb->fd != -1)
+ adb_cond_wait(&usb->notify, &usb->lock);
+ adb_mutex_unlock(&usb->lock);
+
+ D("[ usb_thread - opening device ]\n");
+ do {
+ /* XXX use inotify? */
+ fd = unix_open("/dev/android_adb", O_RDWR);
+ if (fd < 0) {
+ // to support older kernels
+ fd = unix_open("/dev/android", O_RDWR);
+ }
+ if (fd < 0) {
+ adb_sleep_ms(1000);
+ }
+ } while (fd < 0);
+ D("[ opening device succeeded ]\n");
+
+ close_on_exec(fd);
+ usb->fd = fd;
+
+ D("[ usb_thread - registering device ]\n");
+ register_usb_transport(usb, 0, 0, 1);
+ }
+
+ // never gets here
+ return 0;
+}
+
+static int usb_adb_write(usb_handle *h, const void *data, int len)
+{
+ int n;
+
+ D("about to write (fd=%d, len=%d)\n", h->fd, len);
+ n = unix_write(h->fd, data, len);
+ if(n != len) {
+ D("ERROR: fd = %d, n = %d, errno = %d (%s)\n",
+ h->fd, n, errno, strerror(errno));
+ return -1;
+ }
+ D("[ done fd=%d ]\n", h->fd);
+ return 0;
+}
+
+static int usb_adb_read(usb_handle *h, void *data, int len)
+{
+ D("about to read (fd=%d, len=%d)\n", h->fd, len);
+ while (len > 0) {
+ // The kernel implementation of adb_read in f_adb.c doesn't support
+ // reads larger then 4096 bytes. Read the data in 4096 byte chunks to
+ // avoid the issue. (The ffs implementation doesn't have this limit.)
+ int bytes_to_read = len < 4096 ? len : 4096;
+ int n = unix_read(h->fd, data, bytes_to_read);
+ if (n != bytes_to_read) {
+ D("ERROR: fd = %d, n = %d, errno = %d (%s)\n",
+ h->fd, n, errno, strerror(errno));
+ return -1;
+ }
+ len -= n;
+ data = ((char*)data) + n;
+ }
+ D("[ done fd=%d ]\n", h->fd);
+ return 0;
+}
+
+static void usb_adb_kick(usb_handle *h)
+{
+ D("usb_kick\n");
+ adb_mutex_lock(&h->lock);
+ unix_close(h->fd);
+ h->fd = -1;
+
+ // notify usb_adb_open_thread that we are disconnected
+ adb_cond_signal(&h->notify);
+ adb_mutex_unlock(&h->lock);
+}
+
+static void usb_adb_init()
+{
+ usb_handle* h = reinterpret_cast<usb_handle*>(calloc(1, sizeof(usb_handle)));
+ if (h == nullptr) fatal("couldn't allocate usb_handle");
+
+ h->write = usb_adb_write;
+ h->read = usb_adb_read;
+ h->kick = usb_adb_kick;
+ h->fd = -1;
+
+ adb_cond_init(&h->notify, 0);
+ adb_mutex_init(&h->lock, 0);
+
+ // Open the file /dev/android_adb_enable to trigger
+ // the enabling of the adb USB function in the kernel.
+ // We never touch this file again - just leave it open
+ // indefinitely so the kernel will know when we are running
+ // and when we are not.
+ int fd = unix_open("/dev/android_adb_enable", O_RDWR);
+ if (fd < 0) {
+ D("failed to open /dev/android_adb_enable\n");
+ } else {
+ close_on_exec(fd);
+ }
+
+ D("[ usb_init - starting thread ]\n");
+ if (!adb_thread_create(usb_adb_open_thread, h)) {
+ fatal_errno("cannot create usb thread");
+ }
+}
+
+
+static void init_functionfs(struct usb_handle *h)
+{
+ ssize_t ret;
+ struct desc_v1 v1_descriptor;
+ struct desc_v2 v2_descriptor;
+
+ v2_descriptor.header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2);
+ v2_descriptor.header.length = cpu_to_le32(sizeof(v2_descriptor));
+ v2_descriptor.header.flags = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC;
+ v2_descriptor.fs_count = 3;
+ v2_descriptor.hs_count = 3;
+ v2_descriptor.fs_descs = fs_descriptors;
+ v2_descriptor.hs_descs = hs_descriptors;
+
+ if (h->control < 0) { // might have already done this before
+ D("OPENING %s\n", USB_FFS_ADB_EP0);
+ h->control = adb_open(USB_FFS_ADB_EP0, O_RDWR);
+ if (h->control < 0) {
+ D("[ %s: cannot open control endpoint: errno=%d]\n", USB_FFS_ADB_EP0, errno);
+ goto err;
+ }
+
+ ret = adb_write(h->control, &v2_descriptor, sizeof(v2_descriptor));
+ if (ret < 0) {
+ v1_descriptor.header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC);
+ v1_descriptor.header.length = cpu_to_le32(sizeof(v1_descriptor));
+ v1_descriptor.header.fs_count = 3;
+ v1_descriptor.header.hs_count = 3;
+ v1_descriptor.fs_descs = fs_descriptors;
+ v1_descriptor.hs_descs = hs_descriptors;
+ D("[ %s: Switching to V1_descriptor format errno=%d ]\n", USB_FFS_ADB_EP0, errno);
+ ret = adb_write(h->control, &v1_descriptor, sizeof(v1_descriptor));
+ if (ret < 0) {
+ D("[ %s: write descriptors failed: errno=%d ]\n", USB_FFS_ADB_EP0, errno);
+ goto err;
+ }
+ }
+
+ ret = adb_write(h->control, &strings, sizeof(strings));
+ if (ret < 0) {
+ D("[ %s: writing strings failed: errno=%d]\n", USB_FFS_ADB_EP0, errno);
+ goto err;
+ }
+ }
+
+ h->bulk_out = adb_open(USB_FFS_ADB_OUT, O_RDWR);
+ if (h->bulk_out < 0) {
+ D("[ %s: cannot open bulk-out ep: errno=%d ]\n", USB_FFS_ADB_OUT, errno);
+ goto err;
+ }
+
+ h->bulk_in = adb_open(USB_FFS_ADB_IN, O_RDWR);
+ if (h->bulk_in < 0) {
+ D("[ %s: cannot open bulk-in ep: errno=%d ]\n", USB_FFS_ADB_IN, errno);
+ goto err;
+ }
+
+ return;
+
+err:
+ if (h->bulk_in > 0) {
+ adb_close(h->bulk_in);
+ h->bulk_in = -1;
+ }
+ if (h->bulk_out > 0) {
+ adb_close(h->bulk_out);
+ h->bulk_out = -1;
+ }
+ if (h->control > 0) {
+ adb_close(h->control);
+ h->control = -1;
+ }
+ return;
+}
+
+static void *usb_ffs_open_thread(void *x)
+{
+ struct usb_handle *usb = (struct usb_handle *)x;
+
+ while (true) {
+ // wait until the USB device needs opening
+ adb_mutex_lock(&usb->lock);
+ while (usb->control != -1 && usb->bulk_in != -1 && usb->bulk_out != -1)
+ adb_cond_wait(&usb->notify, &usb->lock);
+ adb_mutex_unlock(&usb->lock);
+
+ while (true) {
+ init_functionfs(usb);
+
+ if (usb->control >= 0 && usb->bulk_in >= 0 && usb->bulk_out >= 0)
+ break;
+
+ adb_sleep_ms(1000);
+ }
+ property_set("sys.usb.ffs.ready", "1");
+
+ D("[ usb_thread - registering device ]\n");
+ register_usb_transport(usb, 0, 0, 1);
+ }
+
+ // never gets here
+ return 0;
+}
+
+static int bulk_write(int bulk_in, const uint8_t* buf, size_t length)
+{
+ size_t count = 0;
+ int ret;
+
+ do {
+ ret = adb_write(bulk_in, buf + count, length - count);
+ if (ret < 0) {
+ if (errno != EINTR)
+ return ret;
+ } else {
+ count += ret;
+ }
+ } while (count < length);
+
+ D("[ bulk_write done fd=%d ]\n", bulk_in);
+ return count;
+}
+
+static int usb_ffs_write(usb_handle* h, const void* data, int len)
+{
+ D("about to write (fd=%d, len=%d)\n", h->bulk_in, len);
+ int n = bulk_write(h->bulk_in, reinterpret_cast<const uint8_t*>(data), len);
+ if (n != len) {
+ D("ERROR: fd = %d, n = %d: %s\n", h->bulk_in, n, strerror(errno));
+ return -1;
+ }
+ D("[ done fd=%d ]\n", h->bulk_in);
+ return 0;
+}
+
+static int bulk_read(int bulk_out, uint8_t* buf, size_t length)
+{
+ size_t count = 0;
+ int ret;
+
+ do {
+ ret = adb_read(bulk_out, buf + count, length - count);
+ if (ret < 0) {
+ if (errno != EINTR) {
+ D("[ bulk_read failed fd=%d length=%zu count=%zu ]\n",
+ bulk_out, length, count);
+ return ret;
+ }
+ } else {
+ count += ret;
+ }
+ } while (count < length);
+
+ return count;
+}
+
+static int usb_ffs_read(usb_handle* h, void* data, int len)
+{
+ D("about to read (fd=%d, len=%d)\n", h->bulk_out, len);
+ int n = bulk_read(h->bulk_out, reinterpret_cast<uint8_t*>(data), len);
+ if (n != len) {
+ D("ERROR: fd = %d, n = %d: %s\n", h->bulk_out, n, strerror(errno));
+ return -1;
+ }
+ D("[ done fd=%d ]\n", h->bulk_out);
+ return 0;
+}
+
+static void usb_ffs_kick(usb_handle *h)
+{
+ int err;
+
+ err = ioctl(h->bulk_in, FUNCTIONFS_CLEAR_HALT);
+ if (err < 0)
+ D("[ kick: source (fd=%d) clear halt failed (%d) ]\n", h->bulk_in, errno);
+
+ err = ioctl(h->bulk_out, FUNCTIONFS_CLEAR_HALT);
+ if (err < 0)
+ D("[ kick: sink (fd=%d) clear halt failed (%d) ]\n", h->bulk_out, errno);
+
+ adb_mutex_lock(&h->lock);
+
+ // don't close ep0 here, since we may not need to reinitialize it with
+ // the same descriptors again. if however ep1/ep2 fail to re-open in
+ // init_functionfs, only then would we close and open ep0 again.
+ adb_close(h->bulk_out);
+ adb_close(h->bulk_in);
+ h->bulk_out = h->bulk_in = -1;
+
+ // notify usb_ffs_open_thread that we are disconnected
+ adb_cond_signal(&h->notify);
+ adb_mutex_unlock(&h->lock);
+}
+
+static void usb_ffs_init()
+{
+ D("[ usb_init - using FunctionFS ]\n");
+
+ usb_handle* h = reinterpret_cast<usb_handle*>(calloc(1, sizeof(usb_handle)));
+ if (h == nullptr) fatal("couldn't allocate usb_handle");
+
+ h->write = usb_ffs_write;
+ h->read = usb_ffs_read;
+ h->kick = usb_ffs_kick;
+ h->control = -1;
+ h->bulk_out = -1;
+ h->bulk_out = -1;
+
+ adb_cond_init(&h->notify, 0);
+ adb_mutex_init(&h->lock, 0);
+
+ D("[ usb_init - starting thread ]\n");
+ if (!adb_thread_create(usb_ffs_open_thread, h)) {
+ fatal_errno("[ cannot create usb thread ]\n");
+ }
+}
+
+void usb_init()
+{
+ if (access(USB_FFS_ADB_EP0, F_OK) == 0)
+ usb_ffs_init();
+ else
+ usb_adb_init();
+}
+
+int usb_write(usb_handle *h, const void *data, int len)
+{
+ return h->write(h, data, len);
+}
+
+int usb_read(usb_handle *h, void *data, int len)
+{
+ return h->read(h, data, len);
+}
+int usb_close(usb_handle *h)
+{
+ return 0;
+}
+
+void usb_kick(usb_handle *h)
+{
+ h->kick(h);
+}
diff --git a/adb/usb_osx.cpp b/adb/usb_osx.cpp
new file mode 100644
index 0000000..af65130
--- /dev/null
+++ b/adb/usb_osx.cpp
@@ -0,0 +1,532 @@
+/*
+ * Copyright (C) 2007 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 TRACE_TAG TRACE_USB
+
+#include "sysdeps.h"
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#include <IOKit/IOKitLib.h>
+#include <IOKit/IOCFPlugIn.h>
+#include <IOKit/usb/IOUSBLib.h>
+#include <IOKit/IOMessage.h>
+#include <mach/mach_port.h>
+
+#include <inttypes.h>
+#include <stdio.h>
+
+#include "adb.h"
+#include "transport.h"
+
+#define DBG D
+
+static IONotificationPortRef notificationPort = 0;
+static io_iterator_t notificationIterator;
+
+struct usb_handle
+{
+ UInt8 bulkIn;
+ UInt8 bulkOut;
+ IOUSBInterfaceInterface **interface;
+ io_object_t usbNotification;
+ unsigned int zero_mask;
+};
+
+static CFRunLoopRef currentRunLoop = 0;
+static pthread_mutex_t start_lock;
+static pthread_cond_t start_cond;
+
+
+static void AndroidInterfaceAdded(void *refCon, io_iterator_t iterator);
+static void AndroidInterfaceNotify(void *refCon, io_iterator_t iterator,
+ natural_t messageType,
+ void *messageArgument);
+static usb_handle* CheckInterface(IOUSBInterfaceInterface **iface,
+ UInt16 vendor, UInt16 product);
+
+static int
+InitUSB()
+{
+ CFMutableDictionaryRef matchingDict;
+ CFRunLoopSourceRef runLoopSource;
+
+ //* To set up asynchronous notifications, create a notification port and
+ //* add its run loop event source to the program's run loop
+ notificationPort = IONotificationPortCreate(kIOMasterPortDefault);
+ runLoopSource = IONotificationPortGetRunLoopSource(notificationPort);
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopDefaultMode);
+
+ //* Create our matching dictionary to find the Android device's
+ //* adb interface
+ //* IOServiceAddMatchingNotification consumes the reference, so we do
+ //* not need to release this
+ matchingDict = IOServiceMatching(kIOUSBInterfaceClassName);
+
+ if (!matchingDict) {
+ DBG("ERR: Couldn't create USB matching dictionary.\n");
+ return -1;
+ }
+
+ //* We have to get notifications for all potential candidates and test them
+ //* at connection time because the matching rules don't allow for a
+ //* USB interface class of 0xff for class+subclass+protocol matches
+ //* See https://developer.apple.com/library/mac/qa/qa1076/_index.html
+ IOServiceAddMatchingNotification(
+ notificationPort,
+ kIOFirstMatchNotification,
+ matchingDict,
+ AndroidInterfaceAdded,
+ NULL,
+ ¬ificationIterator);
+
+ //* Iterate over set of matching interfaces to access already-present
+ //* devices and to arm the notification
+ AndroidInterfaceAdded(NULL, notificationIterator);
+
+ return 0;
+}
+
+static void
+AndroidInterfaceAdded(void *refCon, io_iterator_t iterator)
+{
+ kern_return_t kr;
+ io_service_t usbDevice;
+ io_service_t usbInterface;
+ IOCFPlugInInterface **plugInInterface = NULL;
+ IOUSBInterfaceInterface220 **iface = NULL;
+ IOUSBDeviceInterface197 **dev = NULL;
+ HRESULT result;
+ SInt32 score;
+ UInt32 locationId;
+ UInt8 if_class, subclass, protocol;
+ UInt16 vendor;
+ UInt16 product;
+ UInt8 serialIndex;
+ char serial[256];
+ char devpathBuf[64];
+ char *devpath = NULL;
+
+ while ((usbInterface = IOIteratorNext(iterator))) {
+ //* Create an intermediate interface plugin
+ kr = IOCreatePlugInInterfaceForService(usbInterface,
+ kIOUSBInterfaceUserClientTypeID,
+ kIOCFPlugInInterfaceID,
+ &plugInInterface, &score);
+ IOObjectRelease(usbInterface);
+ if ((kIOReturnSuccess != kr) || (!plugInInterface)) {
+ DBG("ERR: Unable to create an interface plug-in (%08x)\n", kr);
+ continue;
+ }
+
+ //* This gets us the interface object
+ result = (*plugInInterface)->QueryInterface(
+ plugInInterface,
+ CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID), (LPVOID*)&iface);
+ //* We only needed the plugin to get the interface, so discard it
+ (*plugInInterface)->Release(plugInInterface);
+ if (result || !iface) {
+ DBG("ERR: Couldn't query the interface (%08x)\n", (int) result);
+ continue;
+ }
+
+ kr = (*iface)->GetInterfaceClass(iface, &if_class);
+ kr = (*iface)->GetInterfaceSubClass(iface, &subclass);
+ kr = (*iface)->GetInterfaceProtocol(iface, &protocol);
+ if(if_class != ADB_CLASS || subclass != ADB_SUBCLASS || protocol != ADB_PROTOCOL) {
+ // Ignore non-ADB devices.
+ DBG("Ignoring interface with incorrect class/subclass/protocol - %d, %d, %d\n", if_class, subclass, protocol);
+ (*iface)->Release(iface);
+ continue;
+ }
+
+ //* this gets us an ioservice, with which we will find the actual
+ //* device; after getting a plugin, and querying the interface, of
+ //* course.
+ //* Gotta love OS X
+ kr = (*iface)->GetDevice(iface, &usbDevice);
+ if (kIOReturnSuccess != kr || !usbDevice) {
+ DBG("ERR: Couldn't grab device from interface (%08x)\n", kr);
+ continue;
+ }
+
+ plugInInterface = NULL;
+ score = 0;
+ //* create an intermediate device plugin
+ kr = IOCreatePlugInInterfaceForService(usbDevice,
+ kIOUSBDeviceUserClientTypeID,
+ kIOCFPlugInInterfaceID,
+ &plugInInterface, &score);
+ //* only needed this to find the plugin
+ (void)IOObjectRelease(usbDevice);
+ if ((kIOReturnSuccess != kr) || (!plugInInterface)) {
+ DBG("ERR: Unable to create a device plug-in (%08x)\n", kr);
+ continue;
+ }
+
+ result = (*plugInInterface)->QueryInterface(plugInInterface,
+ CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID*)&dev);
+ //* only needed this to query the plugin
+ (*plugInInterface)->Release(plugInInterface);
+ if (result || !dev) {
+ DBG("ERR: Couldn't create a device interface (%08x)\n",
+ (int) result);
+ continue;
+ }
+
+ //* Now after all that, we actually have a ref to the device and
+ //* the interface that matched our criteria
+ kr = (*dev)->GetDeviceVendor(dev, &vendor);
+ kr = (*dev)->GetDeviceProduct(dev, &product);
+ kr = (*dev)->GetLocationID(dev, &locationId);
+ if (kr == 0) {
+ snprintf(devpathBuf, sizeof(devpathBuf), "usb:%" PRIu32 "X",
+ (unsigned int)locationId);
+ devpath = devpathBuf;
+ }
+ kr = (*dev)->USBGetSerialNumberStringIndex(dev, &serialIndex);
+
+ if (serialIndex > 0) {
+ IOUSBDevRequest req;
+ UInt16 buffer[256];
+ UInt16 languages[128];
+
+ memset(languages, 0, sizeof(languages));
+
+ req.bmRequestType =
+ USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
+ req.bRequest = kUSBRqGetDescriptor;
+ req.wValue = (kUSBStringDesc << 8) | 0;
+ req.wIndex = 0;
+ req.pData = languages;
+ req.wLength = sizeof(languages);
+ kr = (*dev)->DeviceRequest(dev, &req);
+
+ if (kr == kIOReturnSuccess && req.wLenDone > 0) {
+
+ int langCount = (req.wLenDone - 2) / 2, lang;
+
+ for (lang = 1; lang <= langCount; lang++) {
+
+ memset(buffer, 0, sizeof(buffer));
+ memset(&req, 0, sizeof(req));
+
+ req.bmRequestType =
+ USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
+ req.bRequest = kUSBRqGetDescriptor;
+ req.wValue = (kUSBStringDesc << 8) | serialIndex;
+ req.wIndex = languages[lang];
+ req.pData = buffer;
+ req.wLength = sizeof(buffer);
+ kr = (*dev)->DeviceRequest(dev, &req);
+
+ if (kr == kIOReturnSuccess && req.wLenDone > 0) {
+ int i, count;
+
+ // skip first word, and copy the rest to the serial string,
+ // changing shorts to bytes.
+ count = (req.wLenDone - 1) / 2;
+ for (i = 0; i < count; i++)
+ serial[i] = buffer[i + 1];
+ serial[i] = 0;
+ break;
+ }
+ }
+ }
+ }
+ (*dev)->Release(dev);
+
+ DBG("INFO: Found vid=%04x pid=%04x serial=%s\n", vendor, product,
+ serial);
+
+ usb_handle* handle = CheckInterface((IOUSBInterfaceInterface**)iface,
+ vendor, product);
+ if (handle == NULL) {
+ DBG("ERR: Could not find device interface: %08x\n", kr);
+ (*iface)->Release(iface);
+ continue;
+ }
+
+ DBG("AndroidDeviceAdded calling register_usb_transport\n");
+ register_usb_transport(handle, (serial[0] ? serial : NULL), devpath, 1);
+
+ // Register for an interest notification of this device being removed.
+ // Pass the reference to our private data as the refCon for the
+ // notification.
+ kr = IOServiceAddInterestNotification(notificationPort,
+ usbInterface,
+ kIOGeneralInterest,
+ AndroidInterfaceNotify,
+ handle,
+ &handle->usbNotification);
+
+ if (kIOReturnSuccess != kr) {
+ DBG("ERR: Unable to create interest notification (%08x)\n", kr);
+ }
+ }
+}
+
+static void
+AndroidInterfaceNotify(void *refCon, io_service_t service, natural_t messageType, void *messageArgument)
+{
+ usb_handle *handle = (usb_handle *)refCon;
+
+ if (messageType == kIOMessageServiceIsTerminated) {
+ if (!handle) {
+ DBG("ERR: NULL handle\n");
+ return;
+ }
+ DBG("AndroidInterfaceNotify\n");
+ IOObjectRelease(handle->usbNotification);
+ usb_kick(handle);
+ }
+}
+
+//* TODO: simplify this further since we only register to get ADB interface
+//* subclass+protocol events
+static usb_handle*
+CheckInterface(IOUSBInterfaceInterface **interface, UInt16 vendor, UInt16 product)
+{
+ usb_handle* handle = NULL;
+ IOReturn kr;
+ UInt8 interfaceNumEndpoints, interfaceClass, interfaceSubClass, interfaceProtocol;
+ UInt8 endpoint;
+
+
+ //* Now open the interface. This will cause the pipes associated with
+ //* the endpoints in the interface descriptor to be instantiated
+ kr = (*interface)->USBInterfaceOpen(interface);
+ if (kr != kIOReturnSuccess) {
+ DBG("ERR: Could not open interface: (%08x)\n", kr);
+ return NULL;
+ }
+
+ //* Get the number of endpoints associated with this interface
+ kr = (*interface)->GetNumEndpoints(interface, &interfaceNumEndpoints);
+ if (kr != kIOReturnSuccess) {
+ DBG("ERR: Unable to get number of endpoints: (%08x)\n", kr);
+ goto err_get_num_ep;
+ }
+
+ //* Get interface class, subclass and protocol
+ if ((*interface)->GetInterfaceClass(interface, &interfaceClass) != kIOReturnSuccess ||
+ (*interface)->GetInterfaceSubClass(interface, &interfaceSubClass) != kIOReturnSuccess ||
+ (*interface)->GetInterfaceProtocol(interface, &interfaceProtocol) != kIOReturnSuccess) {
+ DBG("ERR: Unable to get interface class, subclass and protocol\n");
+ goto err_get_interface_class;
+ }
+
+ //* check to make sure interface class, subclass and protocol match ADB
+ //* avoid opening mass storage endpoints
+ if (!is_adb_interface(vendor, product, interfaceClass,
+ interfaceSubClass, interfaceProtocol))
+ goto err_bad_adb_interface;
+
+ handle = reinterpret_cast<usb_handle*>(calloc(1, sizeof(usb_handle)));
+ if (handle == nullptr) goto err_bad_adb_interface;
+
+ //* Iterate over the endpoints for this interface and find the first
+ //* bulk in/out pipes available. These will be our read/write pipes.
+ for (endpoint = 0; endpoint <= interfaceNumEndpoints; endpoint++) {
+ UInt8 transferType;
+ UInt16 maxPacketSize;
+ UInt8 interval;
+ UInt8 number;
+ UInt8 direction;
+
+ kr = (*interface)->GetPipeProperties(interface, endpoint, &direction,
+ &number, &transferType, &maxPacketSize, &interval);
+
+ if (kIOReturnSuccess == kr) {
+ if (kUSBBulk != transferType)
+ continue;
+
+ if (kUSBIn == direction)
+ handle->bulkIn = endpoint;
+
+ if (kUSBOut == direction)
+ handle->bulkOut = endpoint;
+
+ handle->zero_mask = maxPacketSize - 1;
+ } else {
+ DBG("ERR: FindDeviceInterface - could not get pipe properties\n");
+ goto err_get_pipe_props;
+ }
+ }
+
+ handle->interface = interface;
+ return handle;
+
+err_get_pipe_props:
+ free(handle);
+err_bad_adb_interface:
+err_get_interface_class:
+err_get_num_ep:
+ (*interface)->USBInterfaceClose(interface);
+ return NULL;
+}
+
+
+void* RunLoopThread(void* unused)
+{
+ InitUSB();
+
+ currentRunLoop = CFRunLoopGetCurrent();
+
+ // Signal the parent that we are running
+ adb_mutex_lock(&start_lock);
+ adb_cond_signal(&start_cond);
+ adb_mutex_unlock(&start_lock);
+
+ CFRunLoopRun();
+ currentRunLoop = 0;
+
+ IOObjectRelease(notificationIterator);
+ IONotificationPortDestroy(notificationPort);
+
+ DBG("RunLoopThread done\n");
+ return NULL;
+}
+
+static void usb_cleanup() {
+ DBG("usb_cleanup\n");
+ close_usb_devices();
+ if (currentRunLoop)
+ CFRunLoopStop(currentRunLoop);
+}
+
+void usb_init() {
+ static bool initialized = false;
+ if (!initialized) {
+ atexit(usb_cleanup);
+
+ adb_mutex_init(&start_lock, NULL);
+ adb_cond_init(&start_cond, NULL);
+
+ if (!adb_thread_create(RunLoopThread, nullptr)) {
+ fatal_errno("cannot create input thread");
+ }
+
+ // Wait for initialization to finish
+ adb_mutex_lock(&start_lock);
+ adb_cond_wait(&start_cond, &start_lock);
+ adb_mutex_unlock(&start_lock);
+
+ adb_mutex_destroy(&start_lock);
+ adb_cond_destroy(&start_cond);
+
+ initialized = true;
+ }
+}
+
+int usb_write(usb_handle *handle, const void *buf, int len)
+{
+ IOReturn result;
+
+ if (!len)
+ return 0;
+
+ if (!handle)
+ return -1;
+
+ if (NULL == handle->interface) {
+ DBG("ERR: usb_write interface was null\n");
+ return -1;
+ }
+
+ if (0 == handle->bulkOut) {
+ DBG("ERR: bulkOut endpoint not assigned\n");
+ return -1;
+ }
+
+ result =
+ (*handle->interface)->WritePipe(
+ handle->interface, handle->bulkOut, (void *)buf, len);
+
+ if ((result == 0) && (handle->zero_mask)) {
+ /* we need 0-markers and our transfer */
+ if(!(len & handle->zero_mask)) {
+ result =
+ (*handle->interface)->WritePipe(
+ handle->interface, handle->bulkOut, (void *)buf, 0);
+ }
+ }
+
+ if (0 == result)
+ return 0;
+
+ DBG("ERR: usb_write failed with status %d\n", result);
+ return -1;
+}
+
+int usb_read(usb_handle *handle, void *buf, int len)
+{
+ IOReturn result;
+ UInt32 numBytes = len;
+
+ if (!len) {
+ return 0;
+ }
+
+ if (!handle) {
+ return -1;
+ }
+
+ if (NULL == handle->interface) {
+ DBG("ERR: usb_read interface was null\n");
+ return -1;
+ }
+
+ if (0 == handle->bulkIn) {
+ DBG("ERR: bulkIn endpoint not assigned\n");
+ return -1;
+ }
+
+ result = (*handle->interface)->ReadPipe(handle->interface, handle->bulkIn, buf, &numBytes);
+
+ if (kIOUSBPipeStalled == result) {
+ DBG(" Pipe stalled, clearing stall.\n");
+ (*handle->interface)->ClearPipeStall(handle->interface, handle->bulkIn);
+ result = (*handle->interface)->ReadPipe(handle->interface, handle->bulkIn, buf, &numBytes);
+ }
+
+ if (kIOReturnSuccess == result)
+ return 0;
+ else {
+ DBG("ERR: usb_read failed with status %x\n", result);
+ }
+
+ return -1;
+}
+
+int usb_close(usb_handle *handle)
+{
+ return 0;
+}
+
+void usb_kick(usb_handle *handle)
+{
+ /* release the interface */
+ if (!handle)
+ return;
+
+ if (handle->interface)
+ {
+ (*handle->interface)->USBInterfaceClose(handle->interface);
+ (*handle->interface)->Release(handle->interface);
+ handle->interface = 0;
+ }
+}
diff --git a/adb/usb_windows.cpp b/adb/usb_windows.cpp
new file mode 100644
index 0000000..4c9a152
--- /dev/null
+++ b/adb/usb_windows.cpp
@@ -0,0 +1,504 @@
+/*
+ * Copyright (C) 2007 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 TRACE_TAG TRACE_USB
+
+#include "sysdeps.h"
+
+#include <winsock2.h> // winsock.h *must* be included before windows.h.
+#include <adb_api.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <usb100.h>
+#include <windows.h>
+#include <winerror.h>
+
+#include "adb.h"
+#include "transport.h"
+
+/** Structure usb_handle describes our connection to the usb device via
+ AdbWinApi.dll. This structure is returned from usb_open() routine and
+ is expected in each subsequent call that is accessing the device.
+*/
+struct usb_handle {
+ /// Previous entry in the list of opened usb handles
+ usb_handle *prev;
+
+ /// Next entry in the list of opened usb handles
+ usb_handle *next;
+
+ /// Handle to USB interface
+ ADBAPIHANDLE adb_interface;
+
+ /// Handle to USB read pipe (endpoint)
+ ADBAPIHANDLE adb_read_pipe;
+
+ /// Handle to USB write pipe (endpoint)
+ ADBAPIHANDLE adb_write_pipe;
+
+ /// Interface name
+ char* interface_name;
+
+ /// Mask for determining when to use zero length packets
+ unsigned zero_mask;
+};
+
+/// Class ID assigned to the device by androidusb.sys
+static const GUID usb_class_id = ANDROID_USB_CLASS_ID;
+
+/// List of opened usb handles
+static usb_handle handle_list = {
+ .prev = &handle_list,
+ .next = &handle_list,
+};
+
+/// Locker for the list of opened usb handles
+ADB_MUTEX_DEFINE( usb_lock );
+
+/// Checks if there is opened usb handle in handle_list for this device.
+int known_device(const char* dev_name);
+
+/// Checks if there is opened usb handle in handle_list for this device.
+/// usb_lock mutex must be held before calling this routine.
+int known_device_locked(const char* dev_name);
+
+/// Registers opened usb handle (adds it to handle_list).
+int register_new_device(usb_handle* handle);
+
+/// Checks if interface (device) matches certain criteria
+int recognized_device(usb_handle* handle);
+
+/// Enumerates present and available interfaces (devices), opens new ones and
+/// registers usb transport for them.
+void find_devices();
+
+/// Entry point for thread that polls (every second) for new usb interfaces.
+/// This routine calls find_devices in infinite loop.
+void* device_poll_thread(void* unused);
+
+/// Initializes this module
+void usb_init();
+
+/// Opens usb interface (device) by interface (device) name.
+usb_handle* do_usb_open(const wchar_t* interface_name);
+
+/// Writes data to the opened usb handle
+int usb_write(usb_handle* handle, const void* data, int len);
+
+/// Reads data using the opened usb handle
+int usb_read(usb_handle *handle, void* data, int len);
+
+/// Cleans up opened usb handle
+void usb_cleanup_handle(usb_handle* handle);
+
+/// Cleans up (but don't close) opened usb handle
+void usb_kick(usb_handle* handle);
+
+/// Closes opened usb handle
+int usb_close(usb_handle* handle);
+
+/// Gets interface (device) name for an opened usb handle
+const char *usb_name(usb_handle* handle);
+
+int known_device_locked(const char* dev_name) {
+ usb_handle* usb;
+
+ if (NULL != dev_name) {
+ // Iterate through the list looking for the name match.
+ for(usb = handle_list.next; usb != &handle_list; usb = usb->next) {
+ // In Windows names are not case sensetive!
+ if((NULL != usb->interface_name) &&
+ (0 == stricmp(usb->interface_name, dev_name))) {
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int known_device(const char* dev_name) {
+ int ret = 0;
+
+ if (NULL != dev_name) {
+ adb_mutex_lock(&usb_lock);
+ ret = known_device_locked(dev_name);
+ adb_mutex_unlock(&usb_lock);
+ }
+
+ return ret;
+}
+
+int register_new_device(usb_handle* handle) {
+ if (NULL == handle)
+ return 0;
+
+ adb_mutex_lock(&usb_lock);
+
+ // Check if device is already in the list
+ if (known_device_locked(handle->interface_name)) {
+ adb_mutex_unlock(&usb_lock);
+ return 0;
+ }
+
+ // Not in the list. Add this handle to the list.
+ handle->next = &handle_list;
+ handle->prev = handle_list.prev;
+ handle->prev->next = handle;
+ handle->next->prev = handle;
+
+ adb_mutex_unlock(&usb_lock);
+
+ return 1;
+}
+
+void* device_poll_thread(void* unused) {
+ D("Created device thread\n");
+
+ while(1) {
+ find_devices();
+ adb_sleep_ms(1000);
+ }
+
+ return NULL;
+}
+
+void usb_init() {
+ if (!adb_thread_create(device_poll_thread, nullptr)) {
+ fatal_errno("cannot create input thread");
+ }
+}
+
+usb_handle* do_usb_open(const wchar_t* interface_name) {
+ // Allocate our handle
+ usb_handle* ret = (usb_handle*)malloc(sizeof(usb_handle));
+ if (NULL == ret)
+ return NULL;
+
+ // Set linkers back to the handle
+ ret->next = ret;
+ ret->prev = ret;
+
+ // Create interface.
+ ret->adb_interface = AdbCreateInterfaceByName(interface_name);
+
+ if (NULL == ret->adb_interface) {
+ free(ret);
+ errno = GetLastError();
+ return NULL;
+ }
+
+ // Open read pipe (endpoint)
+ ret->adb_read_pipe =
+ AdbOpenDefaultBulkReadEndpoint(ret->adb_interface,
+ AdbOpenAccessTypeReadWrite,
+ AdbOpenSharingModeReadWrite);
+ if (NULL != ret->adb_read_pipe) {
+ // Open write pipe (endpoint)
+ ret->adb_write_pipe =
+ AdbOpenDefaultBulkWriteEndpoint(ret->adb_interface,
+ AdbOpenAccessTypeReadWrite,
+ AdbOpenSharingModeReadWrite);
+ if (NULL != ret->adb_write_pipe) {
+ // Save interface name
+ unsigned long name_len = 0;
+
+ // First get expected name length
+ AdbGetInterfaceName(ret->adb_interface,
+ NULL,
+ &name_len,
+ true);
+ if (0 != name_len) {
+ ret->interface_name = (char*)malloc(name_len);
+
+ if (NULL != ret->interface_name) {
+ // Now save the name
+ if (AdbGetInterfaceName(ret->adb_interface,
+ ret->interface_name,
+ &name_len,
+ true)) {
+ // We're done at this point
+ return ret;
+ }
+ } else {
+ SetLastError(ERROR_OUTOFMEMORY);
+ }
+ }
+ }
+ }
+
+ // Something went wrong.
+ int saved_errno = GetLastError();
+ usb_cleanup_handle(ret);
+ free(ret);
+ SetLastError(saved_errno);
+
+ return NULL;
+}
+
+int usb_write(usb_handle* handle, const void* data, int len) {
+ unsigned long time_out = 5000;
+ unsigned long written = 0;
+ int ret;
+
+ D("usb_write %d\n", len);
+ if (NULL != handle) {
+ // Perform write
+ ret = AdbWriteEndpointSync(handle->adb_write_pipe,
+ (void*)data,
+ (unsigned long)len,
+ &written,
+ time_out);
+ int saved_errno = GetLastError();
+
+ if (ret) {
+ // Make sure that we've written what we were asked to write
+ D("usb_write got: %ld, expected: %d\n", written, len);
+ if (written == (unsigned long)len) {
+ if(handle->zero_mask && (len & handle->zero_mask) == 0) {
+ // Send a zero length packet
+ AdbWriteEndpointSync(handle->adb_write_pipe,
+ (void*)data,
+ 0,
+ &written,
+ time_out);
+ }
+ return 0;
+ }
+ } else {
+ // assume ERROR_INVALID_HANDLE indicates we are disconnected
+ if (saved_errno == ERROR_INVALID_HANDLE)
+ usb_kick(handle);
+ }
+ errno = saved_errno;
+ } else {
+ D("usb_write NULL handle\n");
+ SetLastError(ERROR_INVALID_HANDLE);
+ }
+
+ D("usb_write failed: %d\n", errno);
+
+ return -1;
+}
+
+int usb_read(usb_handle *handle, void* data, int len) {
+ unsigned long time_out = 0;
+ unsigned long read = 0;
+
+ D("usb_read %d\n", len);
+ if (handle != nullptr) {
+ while (len > 0) {
+ int ret = AdbReadEndpointSync(handle->adb_read_pipe, data, len, &read, time_out);
+ int saved_errno = GetLastError();
+ D("usb_write got: %ld, expected: %d, errno: %d\n", read, len, saved_errno);
+ if (ret) {
+ data = (char *)data + read;
+ len -= read;
+
+ if (len == 0)
+ return 0;
+ } else {
+ // assume ERROR_INVALID_HANDLE indicates we are disconnected
+ if (saved_errno == ERROR_INVALID_HANDLE)
+ usb_kick(handle);
+ break;
+ }
+ errno = saved_errno;
+ }
+ } else {
+ D("usb_read NULL handle\n");
+ SetLastError(ERROR_INVALID_HANDLE);
+ }
+
+ D("usb_read failed: %d\n", errno);
+
+ return -1;
+}
+
+void usb_cleanup_handle(usb_handle* handle) {
+ if (NULL != handle) {
+ if (NULL != handle->interface_name)
+ free(handle->interface_name);
+ if (NULL != handle->adb_write_pipe)
+ AdbCloseHandle(handle->adb_write_pipe);
+ if (NULL != handle->adb_read_pipe)
+ AdbCloseHandle(handle->adb_read_pipe);
+ if (NULL != handle->adb_interface)
+ AdbCloseHandle(handle->adb_interface);
+
+ handle->interface_name = NULL;
+ handle->adb_write_pipe = NULL;
+ handle->adb_read_pipe = NULL;
+ handle->adb_interface = NULL;
+ }
+}
+
+void usb_kick(usb_handle* handle) {
+ if (NULL != handle) {
+ adb_mutex_lock(&usb_lock);
+
+ usb_cleanup_handle(handle);
+
+ adb_mutex_unlock(&usb_lock);
+ } else {
+ SetLastError(ERROR_INVALID_HANDLE);
+ errno = ERROR_INVALID_HANDLE;
+ }
+}
+
+int usb_close(usb_handle* handle) {
+ D("usb_close\n");
+
+ if (NULL != handle) {
+ // Remove handle from the list
+ adb_mutex_lock(&usb_lock);
+
+ if ((handle->next != handle) && (handle->prev != handle)) {
+ handle->next->prev = handle->prev;
+ handle->prev->next = handle->next;
+ handle->prev = handle;
+ handle->next = handle;
+ }
+
+ adb_mutex_unlock(&usb_lock);
+
+ // Cleanup handle
+ usb_cleanup_handle(handle);
+ free(handle);
+ }
+
+ return 0;
+}
+
+const char *usb_name(usb_handle* handle) {
+ if (NULL == handle) {
+ SetLastError(ERROR_INVALID_HANDLE);
+ errno = ERROR_INVALID_HANDLE;
+ return NULL;
+ }
+
+ return (const char*)handle->interface_name;
+}
+
+int recognized_device(usb_handle* handle) {
+ if (NULL == handle)
+ return 0;
+
+ // Check vendor and product id first
+ USB_DEVICE_DESCRIPTOR device_desc;
+
+ if (!AdbGetUsbDeviceDescriptor(handle->adb_interface,
+ &device_desc)) {
+ return 0;
+ }
+
+ // Then check interface properties
+ USB_INTERFACE_DESCRIPTOR interf_desc;
+
+ if (!AdbGetUsbInterfaceDescriptor(handle->adb_interface,
+ &interf_desc)) {
+ return 0;
+ }
+
+ // Must have two endpoints
+ if (2 != interf_desc.bNumEndpoints) {
+ return 0;
+ }
+
+ if (is_adb_interface(device_desc.idVendor, device_desc.idProduct,
+ interf_desc.bInterfaceClass, interf_desc.bInterfaceSubClass, interf_desc.bInterfaceProtocol)) {
+
+ if(interf_desc.bInterfaceProtocol == 0x01) {
+ AdbEndpointInformation endpoint_info;
+ // assuming zero is a valid bulk endpoint ID
+ if (AdbGetEndpointInformation(handle->adb_interface, 0, &endpoint_info)) {
+ handle->zero_mask = endpoint_info.max_packet_size - 1;
+ }
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+void find_devices() {
+ usb_handle* handle = NULL;
+ char entry_buffer[2048];
+ char interf_name[2048];
+ AdbInterfaceInfo* next_interface = (AdbInterfaceInfo*)(&entry_buffer[0]);
+ unsigned long entry_buffer_size = sizeof(entry_buffer);
+ char* copy_name;
+
+ // Enumerate all present and active interfaces.
+ ADBAPIHANDLE enum_handle =
+ AdbEnumInterfaces(usb_class_id, true, true, true);
+
+ if (NULL == enum_handle)
+ return;
+
+ while (AdbNextInterface(enum_handle, next_interface, &entry_buffer_size)) {
+ // TODO: FIXME - temp hack converting wchar_t into char.
+ // It would be better to change AdbNextInterface so it will return
+ // interface name as single char string.
+ const wchar_t* wchar_name = next_interface->device_name;
+ for(copy_name = interf_name;
+ L'\0' != *wchar_name;
+ wchar_name++, copy_name++) {
+ *copy_name = (char)(*wchar_name);
+ }
+ *copy_name = '\0';
+
+ // Lets see if we already have this device in the list
+ if (!known_device(interf_name)) {
+ // This seems to be a new device. Open it!
+ handle = do_usb_open(next_interface->device_name);
+ if (NULL != handle) {
+ // Lets see if this interface (device) belongs to us
+ if (recognized_device(handle)) {
+ D("adding a new device %s\n", interf_name);
+ char serial_number[512];
+ unsigned long serial_number_len = sizeof(serial_number);
+ if (AdbGetSerialNumber(handle->adb_interface,
+ serial_number,
+ &serial_number_len,
+ true)) {
+ // Lets make sure that we don't duplicate this device
+ if (register_new_device(handle)) {
+ register_usb_transport(handle, serial_number, NULL, 1);
+ } else {
+ D("register_new_device failed for %s\n", interf_name);
+ usb_cleanup_handle(handle);
+ free(handle);
+ }
+ } else {
+ D("cannot get serial number\n");
+ usb_cleanup_handle(handle);
+ free(handle);
+ }
+ } else {
+ usb_cleanup_handle(handle);
+ free(handle);
+ }
+ }
+ }
+
+ entry_buffer_size = sizeof(entry_buffer);
+ }
+
+ AdbCloseHandle(enum_handle);
+}
diff --git a/adf/Android.mk b/adf/Android.mk
new file mode 100644
index 0000000..64d486e
--- /dev/null
+++ b/adf/Android.mk
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2013 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH := $(my-dir)
+
+include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/adf/libadf/Android.mk b/adf/libadf/Android.mk
new file mode 100644
index 0000000..7df354b
--- /dev/null
+++ b/adf/libadf/Android.mk
@@ -0,0 +1,24 @@
+# Copyright (C) 2013 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := adf.c
+LOCAL_MODULE := libadf
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS += -Werror
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES += $(LOCAL_EXPORT_C_INCLUDE_DIRS)
+include $(BUILD_STATIC_LIBRARY)
diff --git a/adf/libadf/adf.c b/adf/libadf/adf.c
new file mode 100644
index 0000000..c4d6681
--- /dev/null
+++ b/adf/libadf/adf.c
@@ -0,0 +1,811 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <malloc.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <linux/limits.h>
+
+#include <sys/ioctl.h>
+
+#include <adf/adf.h>
+
+#define ADF_BASE_PATH "/dev/"
+
+static ssize_t adf_find_nodes(const char *pattern, adf_id_t **ids)
+{
+ DIR *dir;
+ struct dirent *dirent;
+ size_t n = 0;
+ ssize_t ret;
+ adf_id_t *ids_ret = NULL;
+
+ dir = opendir(ADF_BASE_PATH);
+ if (!dir)
+ return -errno;
+
+ errno = 0;
+ while ((dirent = readdir(dir))) {
+ adf_id_t id;
+ int matched = sscanf(dirent->d_name, pattern, &id);
+
+ if (matched < 0) {
+ ret = -errno;
+ goto done;
+ } else if (matched != 1) {
+ continue;
+ }
+
+ adf_id_t *new_ids = realloc(ids_ret, (n + 1) * sizeof(ids_ret[0]));
+ if (!new_ids) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ ids_ret = new_ids;
+ ids_ret[n] = id;
+ n++;
+ }
+ if (errno)
+ ret = -errno;
+ else
+ ret = n;
+
+done:
+ closedir(dir);
+ if (ret < 0)
+ free(ids_ret);
+ else
+ *ids = ids_ret;
+ return ret;
+}
+
+ssize_t adf_devices(adf_id_t **ids)
+{
+ return adf_find_nodes("adf%u", ids);
+}
+
+int adf_device_open(adf_id_t id, int flags, struct adf_device *dev)
+{
+ char filename[64];
+
+ dev->id = id;
+
+ snprintf(filename, sizeof(filename), ADF_BASE_PATH "adf%u", id);
+ dev->fd = open(filename, flags);
+ if (dev->fd < 0)
+ return -errno;
+
+ return 0;
+}
+
+void adf_device_close(struct adf_device *dev)
+{
+ if (dev->fd >= 0)
+ close(dev->fd);
+}
+
+int adf_get_device_data(struct adf_device *dev, struct adf_device_data *data)
+{
+ int err;
+ int ret = 0;
+
+ memset(data, 0, sizeof(*data));
+
+ err = ioctl(dev->fd, ADF_GET_DEVICE_DATA, data);
+ if (err < 0)
+ return -ENOMEM;
+
+ if (data->n_attachments) {
+ data->attachments = malloc(sizeof(data->attachments[0]) *
+ data->n_attachments);
+ if (!data->attachments)
+ return -ENOMEM;
+ }
+
+ if (data->n_allowed_attachments) {
+ data->allowed_attachments =
+ malloc(sizeof(data->allowed_attachments[0]) *
+ data->n_allowed_attachments);
+ if (!data->allowed_attachments) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ }
+
+ if (data->custom_data_size) {
+ data->custom_data = malloc(data->custom_data_size);
+ if (!data->custom_data) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ }
+
+ err = ioctl(dev->fd, ADF_GET_DEVICE_DATA, data);
+ if (err < 0)
+ ret = -errno;
+
+done:
+ if (ret < 0)
+ adf_free_device_data(data);
+ return ret;
+}
+
+void adf_free_device_data(struct adf_device_data *data)
+{
+ free(data->attachments);
+ free(data->allowed_attachments);
+ free(data->custom_data);
+}
+
+int adf_device_post(struct adf_device *dev,
+ adf_id_t *interfaces, size_t n_interfaces,
+ struct adf_buffer_config *bufs, size_t n_bufs,
+ void *custom_data, size_t custom_data_size)
+{
+ int err;
+ struct adf_post_config data;
+
+ memset(&data, 0, sizeof(data));
+ data.interfaces = interfaces;
+ data.n_interfaces = n_interfaces;
+ data.bufs = bufs;
+ data.n_bufs = n_bufs;
+ data.custom_data = custom_data;
+ data.custom_data_size = custom_data_size;
+
+ err = ioctl(dev->fd, ADF_POST_CONFIG, &data);
+ if (err < 0)
+ return -errno;
+
+ return (int)data.complete_fence;
+}
+
+static int adf_device_attachment(struct adf_device *dev,
+ adf_id_t overlay_engine, adf_id_t interface, bool attach)
+{
+ int err;
+ struct adf_attachment_config data;
+
+ memset(&data, 0, sizeof(data));
+ data.overlay_engine = overlay_engine;
+ data.interface = interface;
+
+ err = ioctl(dev->fd, attach ? ADF_ATTACH : ADF_DETACH, &data);
+ if (err < 0)
+ return -errno;
+
+ return 0;
+}
+
+int adf_device_attach(struct adf_device *dev, adf_id_t overlay_engine,
+ adf_id_t interface)
+{
+ return adf_device_attachment(dev, overlay_engine, interface, true);
+}
+
+int adf_device_detach(struct adf_device *dev, adf_id_t overlay_engine,
+ adf_id_t interface)
+{
+ return adf_device_attachment(dev, overlay_engine, interface, false);
+}
+
+ssize_t adf_interfaces(struct adf_device *dev, adf_id_t **interfaces)
+{
+ char pattern[64];
+
+ snprintf(pattern, sizeof(pattern), "adf-interface%u.%%u", dev->id);
+ return adf_find_nodes(pattern, interfaces);
+}
+
+ssize_t adf_interfaces_for_overlay_engine(struct adf_device *dev,
+ adf_id_t overlay_engine, adf_id_t **interfaces)
+{
+ struct adf_device_data data;
+ ssize_t n = 0;
+ ssize_t ret;
+ adf_id_t *ids_ret = NULL;
+
+ ret = adf_get_device_data(dev, &data);
+ if (ret < 0)
+ return ret;
+
+ size_t i;
+ for (i = 0; i < data.n_allowed_attachments; i++) {
+ if (data.allowed_attachments[i].overlay_engine != overlay_engine)
+ continue;
+
+ adf_id_t *new_ids = realloc(ids_ret, (n + 1) * sizeof(ids_ret[0]));
+ if (!new_ids) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ ids_ret = new_ids;
+ ids_ret[n] = data.allowed_attachments[i].interface;
+ n++;
+ }
+
+ ret = n;
+
+done:
+ adf_free_device_data(&data);
+ if (ret < 0)
+ free(ids_ret);
+ else
+ *interfaces = ids_ret;
+ return ret;
+}
+
+static ssize_t adf_interfaces_filter(struct adf_device *dev,
+ adf_id_t *in, size_t n_in, adf_id_t **out,
+ bool (*filter)(struct adf_interface_data *data, __u32 match),
+ __u32 match)
+{
+ size_t n = 0;
+ ssize_t ret;
+ adf_id_t *ids_ret = NULL;
+
+ size_t i;
+ for (i = 0; i < n_in; i++) {
+ int fd = adf_interface_open(dev, in[i], O_RDONLY);
+ if (fd < 0) {
+ ret = fd;
+ goto done;
+ }
+
+ struct adf_interface_data data;
+ ret = adf_get_interface_data(fd, &data);
+ close(fd);
+ if (ret < 0)
+ goto done;
+
+ if (!filter(&data, match))
+ continue;
+
+ adf_id_t *new_ids = realloc(ids_ret, (n + 1) * sizeof(ids_ret[0]));
+ if (!new_ids) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ ids_ret = new_ids;
+ ids_ret[n] = in[i];
+ n++;
+ }
+
+ ret = n;
+
+done:
+ if (ret < 0)
+ free(ids_ret);
+ else
+ *out = ids_ret;
+ return ret;
+}
+
+static bool adf_interface_type_filter(struct adf_interface_data *data,
+ __u32 type)
+{
+ return data->type == (enum adf_interface_type)type;
+}
+
+ssize_t adf_interfaces_filter_by_type(struct adf_device *dev,
+ enum adf_interface_type type,
+ adf_id_t *in, size_t n_in, adf_id_t **out)
+{
+ return adf_interfaces_filter(dev, in, n_in, out, adf_interface_type_filter,
+ type);
+}
+
+static bool adf_interface_flags_filter(struct adf_interface_data *data,
+ __u32 flag)
+{
+ return !!(data->flags & flag);
+}
+
+ssize_t adf_interfaces_filter_by_flag(struct adf_device *dev, __u32 flag,
+ adf_id_t *in, size_t n_in, adf_id_t **out)
+{
+ return adf_interfaces_filter(dev, in, n_in, out, adf_interface_flags_filter,
+ flag);
+}
+
+int adf_interface_open(struct adf_device *dev, adf_id_t id, int flags)
+{
+ char filename[64];
+
+ snprintf(filename, sizeof(filename), ADF_BASE_PATH "adf-interface%u.%u",
+ dev->id, id);
+
+ int fd = open(filename, flags);
+ if (fd < 0)
+ return -errno;
+ return fd;
+}
+
+int adf_get_interface_data(int fd, struct adf_interface_data *data)
+{
+ int err;
+ int ret = 0;
+
+ memset(data, 0, sizeof(*data));
+
+ err = ioctl(fd, ADF_GET_INTERFACE_DATA, data);
+ if (err < 0)
+ return -errno;
+
+ if (data->n_available_modes) {
+ data->available_modes = malloc(sizeof(data->available_modes[0]) *
+ data->n_available_modes);
+ if (!data->available_modes)
+ return -ENOMEM;
+ }
+
+ if (data->custom_data_size) {
+ data->custom_data = malloc(data->custom_data_size);
+ if (!data->custom_data) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ }
+
+ err = ioctl(fd, ADF_GET_INTERFACE_DATA, data);
+ if (err < 0)
+ ret = -errno;
+
+done:
+ if (ret < 0)
+ adf_free_interface_data(data);
+ return ret;
+}
+
+void adf_free_interface_data(struct adf_interface_data *data)
+{
+ free(data->available_modes);
+ free(data->custom_data);
+}
+
+int adf_interface_blank(int fd, __u8 mode)
+{
+ int err = ioctl(fd, ADF_BLANK, mode);
+ if (err < 0)
+ return -errno;
+ return 0;
+}
+
+int adf_interface_set_mode(int fd, struct drm_mode_modeinfo *mode)
+{
+ int err = ioctl(fd, ADF_SET_MODE, mode);
+ if (err < 0)
+ return -errno;
+ return 0;
+}
+
+int adf_interface_simple_buffer_alloc(int fd, __u32 w, __u32 h,
+ __u32 format, __u32 *offset, __u32 *pitch)
+{
+ int err;
+ struct adf_simple_buffer_alloc data;
+
+ memset(&data, 0, sizeof(data));
+ data.w = w;
+ data.h = h;
+ data.format = format;
+
+ err = ioctl(fd, ADF_SIMPLE_BUFFER_ALLOC, &data);
+ if (err < 0)
+ return -errno;
+
+ *offset = data.offset;
+ *pitch = data.pitch;
+ return (int)data.fd;
+}
+
+int adf_interface_simple_post(int fd, __u32 overlay_engine,
+ __u32 w, __u32 h, __u32 format, int buf_fd, __u32 offset,
+ __u32 pitch, int acquire_fence)
+{
+ int ret;
+ struct adf_simple_post_config data;
+
+ memset(&data, 0, sizeof(data));
+ data.buf.overlay_engine = overlay_engine;
+ data.buf.w = w;
+ data.buf.h = h;
+ data.buf.format = format;
+ data.buf.fd[0] = buf_fd;
+ data.buf.offset[0] = offset;
+ data.buf.pitch[0] = pitch;
+ data.buf.n_planes = 1;
+ data.buf.acquire_fence = acquire_fence;
+
+ ret = ioctl(fd, ADF_SIMPLE_POST_CONFIG, &data);
+ if (ret < 0)
+ return -errno;
+
+ return (int)data.complete_fence;
+}
+
+ssize_t adf_overlay_engines(struct adf_device *dev, adf_id_t **overlay_engines)
+{
+ char pattern[64];
+
+ snprintf(pattern, sizeof(pattern), "adf-overlay-engine%u.%%u", dev->id);
+ return adf_find_nodes(pattern, overlay_engines);
+}
+
+ssize_t adf_overlay_engines_for_interface(struct adf_device *dev,
+ adf_id_t interface, adf_id_t **overlay_engines)
+{
+ struct adf_device_data data;
+ ssize_t n = 0;
+ ssize_t ret;
+ adf_id_t *ids_ret = NULL;
+
+ ret = adf_get_device_data(dev, &data);
+ if (ret < 0)
+ return ret;
+
+ size_t i;
+ for (i = 0; i < data.n_allowed_attachments; i++) {
+ if (data.allowed_attachments[i].interface != interface)
+ continue;
+
+ adf_id_t *new_ids = realloc(ids_ret, (n + 1) * sizeof(ids_ret[0]));
+ if (!new_ids) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ ids_ret = new_ids;
+ ids_ret[n] = data.allowed_attachments[i].overlay_engine;
+ n++;
+ }
+
+ ret = n;
+
+done:
+ adf_free_device_data(&data);
+ if (ret < 0)
+ free(ids_ret);
+ else
+ *overlay_engines = ids_ret;
+ return ret;
+}
+
+static ssize_t adf_overlay_engines_filter(struct adf_device *dev,
+ adf_id_t *in, size_t n_in, adf_id_t **out,
+ bool (*filter)(struct adf_overlay_engine_data *data, void *cookie),
+ void *cookie)
+{
+ size_t n = 0;
+ ssize_t ret;
+ adf_id_t *ids_ret = NULL;
+
+ size_t i;
+ for (i = 0; i < n_in; i++) {
+ int fd = adf_overlay_engine_open(dev, in[i], O_RDONLY);
+ if (fd < 0) {
+ ret = fd;
+ goto done;
+ }
+
+ struct adf_overlay_engine_data data;
+ ret = adf_get_overlay_engine_data(fd, &data);
+ close(fd);
+ if (ret < 0)
+ goto done;
+
+ if (!filter(&data, cookie))
+ continue;
+
+ adf_id_t *new_ids = realloc(ids_ret, (n + 1) * sizeof(ids_ret[0]));
+ if (!new_ids) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ ids_ret = new_ids;
+ ids_ret[n] = in[i];
+ n++;
+ }
+
+ ret = n;
+
+done:
+ if (ret < 0)
+ free(ids_ret);
+ else
+ *out = ids_ret;
+ return ret;
+}
+
+struct format_filter_cookie {
+ const __u32 *formats;
+ size_t n_formats;
+};
+
+static bool adf_overlay_engine_format_filter(
+ struct adf_overlay_engine_data *data, void *cookie)
+{
+ struct format_filter_cookie *c = cookie;
+ size_t i;
+ for (i = 0; i < data->n_supported_formats; i++) {
+ size_t j;
+ for (j = 0; j < c->n_formats; j++)
+ if (data->supported_formats[i] == c->formats[j])
+ return true;
+ }
+ return false;
+}
+
+ssize_t adf_overlay_engines_filter_by_format(struct adf_device *dev,
+ const __u32 *formats, size_t n_formats, adf_id_t *in, size_t n_in,
+ adf_id_t **out)
+{
+ struct format_filter_cookie cookie = { formats, n_formats };
+ return adf_overlay_engines_filter(dev, in, n_in, out,
+ adf_overlay_engine_format_filter, &cookie);
+}
+
+int adf_overlay_engine_open(struct adf_device *dev, adf_id_t id, int flags)
+{
+ char filename[64];
+
+ snprintf(filename, sizeof(filename),
+ ADF_BASE_PATH "adf-overlay-engine%u.%u", dev->id, id);
+
+ int fd = open(filename, flags);
+ if (fd < 0)
+ return -errno;
+ return fd;
+}
+
+int adf_get_overlay_engine_data(int fd, struct adf_overlay_engine_data *data)
+{
+ int err;
+ int ret = 0;
+
+ memset(data, 0, sizeof(*data));
+
+ err = ioctl(fd, ADF_GET_OVERLAY_ENGINE_DATA, data);
+ if (err < 0)
+ return -errno;
+
+ if (data->n_supported_formats) {
+ data->supported_formats = malloc(sizeof(data->supported_formats[0]) *
+ data->n_supported_formats);
+ if (!data->supported_formats)
+ return -ENOMEM;
+ }
+
+ if (data->custom_data_size) {
+ data->custom_data = malloc(data->custom_data_size);
+ if (!data->custom_data) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ }
+
+ err = ioctl(fd, ADF_GET_OVERLAY_ENGINE_DATA, data);
+ if (err < 0)
+ ret = -errno;
+
+done:
+ if (ret < 0)
+ adf_free_overlay_engine_data(data);
+ return ret;
+}
+
+void adf_free_overlay_engine_data(struct adf_overlay_engine_data *data)
+{
+ free(data->supported_formats);
+ free(data->custom_data);
+}
+
+bool adf_overlay_engine_supports_format(int fd, __u32 format)
+{
+ struct adf_overlay_engine_data data;
+ bool ret = false;
+ size_t i;
+
+ int err = adf_get_overlay_engine_data(fd, &data);
+ if (err < 0)
+ return false;
+
+ for (i = 0; i < data.n_supported_formats; i++) {
+ if (data.supported_formats[i] == format) {
+ ret = true;
+ break;
+ }
+ }
+
+ adf_free_overlay_engine_data(&data);
+ return ret;
+}
+
+int adf_set_event(int fd, enum adf_event_type type, bool enabled)
+{
+ struct adf_set_event data;
+
+ data.type = type;
+ data.enabled = enabled;
+
+ int err = ioctl(fd, ADF_SET_EVENT, &data);
+ if (err < 0)
+ return -errno;
+ return 0;
+}
+
+int adf_read_event(int fd, struct adf_event **event)
+{
+ struct adf_event header;
+ struct {
+ struct adf_event base;
+ uint8_t data[0];
+ } *event_ret;
+ size_t data_size;
+ int ret = 0;
+
+ int err = read(fd, &header, sizeof(header));
+ if (err < 0)
+ return -errno;
+ if ((size_t)err < sizeof(header))
+ return -EIO;
+ if (header.length < sizeof(header))
+ return -EIO;
+
+ event_ret = malloc(header.length);
+ if (!event_ret)
+ return -ENOMEM;
+ data_size = header.length - sizeof(header);
+
+ memcpy(event_ret, &header, sizeof(header));
+ ssize_t read_size = read(fd, &event_ret->data, data_size);
+ if (read_size < 0) {
+ ret = -errno;
+ goto done;
+ }
+ if ((size_t)read_size < data_size) {
+ ret = -EIO;
+ goto done;
+ }
+
+ *event = &event_ret->base;
+
+done:
+ if (ret < 0)
+ free(event_ret);
+ return ret;
+}
+
+void adf_format_str(__u32 format, char buf[ADF_FORMAT_STR_SIZE])
+{
+ buf[0] = format & 0xFF;
+ buf[1] = (format >> 8) & 0xFF;
+ buf[2] = (format >> 16) & 0xFF;
+ buf[3] = (format >> 24) & 0xFF;
+ buf[4] = '\0';
+}
+
+static bool adf_find_simple_post_overlay_engine(struct adf_device *dev,
+ const __u32 *formats, size_t n_formats,
+ adf_id_t interface, adf_id_t *overlay_engine)
+{
+ adf_id_t *engs;
+ ssize_t n_engs = adf_overlay_engines_for_interface(dev, interface, &engs);
+
+ if (n_engs <= 0)
+ return false;
+
+ adf_id_t *filtered_engs;
+ ssize_t n_filtered_engs = adf_overlay_engines_filter_by_format(dev,
+ formats, n_formats, engs, n_engs, &filtered_engs);
+ free(engs);
+
+ if (n_filtered_engs <= 0)
+ return false;
+
+ *overlay_engine = filtered_engs[0];
+ free(filtered_engs);
+ return true;
+}
+
+static const __u32 any_rgb_format[] = {
+ DRM_FORMAT_C8,
+ DRM_FORMAT_RGB332,
+ DRM_FORMAT_BGR233,
+ DRM_FORMAT_XRGB1555,
+ DRM_FORMAT_XBGR1555,
+ DRM_FORMAT_RGBX5551,
+ DRM_FORMAT_BGRX5551,
+ DRM_FORMAT_ARGB1555,
+ DRM_FORMAT_ABGR1555,
+ DRM_FORMAT_RGBA5551,
+ DRM_FORMAT_BGRA5551,
+ DRM_FORMAT_RGB565,
+ DRM_FORMAT_BGR565,
+ DRM_FORMAT_RGB888,
+ DRM_FORMAT_BGR888,
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_XBGR8888,
+ DRM_FORMAT_RGBX8888,
+ DRM_FORMAT_BGRX8888,
+ DRM_FORMAT_XRGB2101010,
+ DRM_FORMAT_XBGR2101010,
+ DRM_FORMAT_RGBX1010102,
+ DRM_FORMAT_BGRX1010102,
+ DRM_FORMAT_ARGB2101010,
+ DRM_FORMAT_ABGR2101010,
+ DRM_FORMAT_RGBA1010102,
+ DRM_FORMAT_BGRA1010102,
+ DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_ABGR8888,
+ DRM_FORMAT_RGBA8888,
+ DRM_FORMAT_BGRA8888,
+};
+
+int adf_find_simple_post_configuration(struct adf_device *dev,
+ const __u32 *formats, size_t n_formats,
+ adf_id_t *interface, adf_id_t *overlay_engine)
+{
+ adf_id_t *intfs = NULL;
+ ssize_t n_intfs = adf_interfaces(dev, &intfs);
+
+ if (n_intfs < 0)
+ return n_intfs;
+ else if (!n_intfs)
+ return -ENODEV;
+
+ adf_id_t *primary_intfs;
+ ssize_t n_primary_intfs = adf_interfaces_filter_by_flag(dev,
+ ADF_INTF_FLAG_PRIMARY, intfs, n_intfs, &primary_intfs);
+ free(intfs);
+
+ if (n_primary_intfs < 0)
+ return n_primary_intfs;
+ else if (!n_primary_intfs)
+ return -ENODEV;
+
+ if (!formats) {
+ formats = any_rgb_format;
+ n_formats = sizeof(any_rgb_format) / sizeof(any_rgb_format[0]);
+ }
+
+ bool found = false;
+ ssize_t i = 0;
+ for (i = 0; i < n_primary_intfs; i++) {
+ found = adf_find_simple_post_overlay_engine(dev, formats, n_formats,
+ primary_intfs[i], overlay_engine);
+ if (found) {
+ *interface = primary_intfs[i];
+ break;
+ }
+ }
+ free(primary_intfs);
+
+ if (!found)
+ return -ENODEV;
+
+ return 0;
+}
diff --git a/adf/libadf/include/adf/adf.h b/adf/libadf/include/adf/adf.h
new file mode 100644
index 0000000..b6bda34
--- /dev/null
+++ b/adf/libadf/include/adf/adf.h
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBADF_ADF_H_
+#define _LIBADF_ADF_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <video/adf.h>
+
+typedef __u32 adf_id_t;
+
+struct adf_device {
+ adf_id_t id;
+ int fd;
+};
+
+__BEGIN_DECLS
+
+/**
+ * Enumerates all ADF devices.
+ *
+ * Returns the number of ADF devices, and sets ids to a list of device IDs.
+ * The caller must free() the returned list of device IDs.
+ *
+ * On error, returns -errno.
+ */
+ssize_t adf_devices(adf_id_t **ids);
+
+/**
+ * Opens an ADF device.
+ *
+ * On error, returns -errno.
+ */
+int adf_device_open(adf_id_t id, int flags, struct adf_device *dev);
+/**
+ * Closes an ADF device.
+ */
+void adf_device_close(struct adf_device *dev);
+/**
+ * Reads the ADF device data.
+ *
+ * adf_get_device_data() allocates buffers inside data, which the caller
+ * must free by calling adf_free_device_data(). On error, returns -errno.
+ */
+int adf_get_device_data(struct adf_device *dev, struct adf_device_data *data);
+/**
+ * Frees the device data returned by adf_get_device_data().
+ */
+void adf_free_device_data(struct adf_device_data *data);
+
+/**
+ * Atomically posts a new display configuration to the specified interfaces.
+ *
+ * Returns a sync fence fd that will fire when the configuration is removed
+ * from the screen. On error, returns -errno.
+ */
+int adf_device_post(struct adf_device *dev,
+ adf_id_t *interfaces, size_t n_interfaces,
+ struct adf_buffer_config *bufs, size_t n_bufs,
+ void *custom_data, size_t custom_data_size);
+/**
+ * Attaches the specified interface and overlay engine.
+ */
+int adf_device_attach(struct adf_device *dev, adf_id_t overlay_engine,
+ adf_id_t interface);
+/**
+ * Detaches the specified interface and overlay engine.
+ */
+int adf_device_detach(struct adf_device *dev, adf_id_t overlay_engine,
+ adf_id_t interface);
+
+/**
+ * Enumerates all interfaces belonging to an ADF device.
+ *
+ * The caller must free() the returned list of interface IDs.
+ */
+ssize_t adf_interfaces(struct adf_device *dev, adf_id_t **interfaces);
+
+/**
+ * Enumerates all interfaces which can be attached to the specified overlay
+ * engine.
+ *
+ * The caller must free() the returned list of interface IDs.
+ */
+ssize_t adf_interfaces_for_overlay_engine(struct adf_device *dev,
+ adf_id_t overlay_engine, adf_id_t **interfaces);
+/**
+ * Filters a list of interfaces by type.
+ *
+ * Returns the number of matching interfaces, and sets out to a list of matching
+ * interface IDs. The caller must free() the returned list of interface IDs.
+ *
+ * On error, returns -errno.
+ */
+ssize_t adf_interfaces_filter_by_type(struct adf_device *dev,
+ enum adf_interface_type type,
+ adf_id_t *in, size_t n_in, adf_id_t **out);
+/**
+ * Filters a list of interfaces by flag.
+ *
+ * The caller must free() the returned list of interface IDs.
+ */
+ssize_t adf_interfaces_filter_by_flag(struct adf_device *dev, __u32 flag,
+ adf_id_t *in, size_t n_in, adf_id_t **out);
+
+/**
+ * Opens an ADF interface.
+ *
+ * Returns a file descriptor. The caller must close() the fd when done.
+ * On error, returns -errno.
+ */
+int adf_interface_open(struct adf_device *dev, adf_id_t id, int flags);
+/**
+ * Reads the interface data.
+ *
+ * adf_get_interface_data() allocates buffers inside data, which the caller
+ * must free by calling adf_free_interface_data(). On error, returns -errno.
+ */
+int adf_get_interface_data(int fd, struct adf_interface_data *data);
+/**
+ * Frees the interface data returned by adf_get_interface_data().
+ */
+void adf_free_interface_data(struct adf_interface_data *data);
+
+/**
+ * Sets the interface's DPMS mode.
+ */
+int adf_interface_blank(int fd, __u8 mode);
+/**
+ * Sets the interface's display mode.
+ */
+int adf_interface_set_mode(int fd, struct drm_mode_modeinfo *mode);
+/**
+ * Allocates a single-plane RGB buffer of the specified size and format.
+ *
+ * Returns a dma-buf fd. On error, returns -errno.
+ */
+int adf_interface_simple_buffer_alloc(int fd, __u32 w, __u32 h,
+ __u32 format, __u32 *offset, __u32 *pitch);
+/**
+ * Posts a single-plane RGB buffer to the display using the specified
+ * overlay engine.
+ *
+ * Returns a sync fence fd that will fire when the buffer is removed
+ * from the screen. On error, returns -errno.
+ */
+int adf_interface_simple_post(int fd, adf_id_t overlay_engine,
+ __u32 w, __u32 h, __u32 format, int buf_fd, __u32 offset,
+ __u32 pitch, int acquire_fence);
+
+/**
+ * Enumerates all overlay engines belonging to an ADF device.
+ *
+ * The caller must free() the returned list of overlay engine IDs.
+ */
+ssize_t adf_overlay_engines(struct adf_device *dev, adf_id_t **overlay_engines);
+
+/**
+ * Enumerates all overlay engines which can be attached to the specified
+ * interface.
+ *
+ * The caller must free() the returned list of overlay engine IDs.
+ */
+ssize_t adf_overlay_engines_for_interface(struct adf_device *dev,
+ adf_id_t interface, adf_id_t **overlay_engines);
+/**
+ * Filters a list of overlay engines by supported buffer format.
+ *
+ * Returns the overlay engines which support at least one of the specified
+ * formats. The caller must free() the returned list of overlay engine IDs.
+ */
+ssize_t adf_overlay_engines_filter_by_format(struct adf_device *dev,
+ const __u32 *formats, size_t n_formats, adf_id_t *in, size_t n_in,
+ adf_id_t **out);
+
+/**
+ * Opens an ADF overlay engine.
+ *
+ * Returns a file descriptor. The caller must close() the fd when done.
+ * On error, returns -errno.
+ */
+int adf_overlay_engine_open(struct adf_device *dev, adf_id_t id, int flags);
+/**
+ * Reads the overlay engine data.
+ *
+ * adf_get_overlay_engine_data() allocates buffers inside data, which the caller
+ * must free by calling adf_free_overlay_engine_data(). On error, returns
+ * -errno.
+ */
+int adf_get_overlay_engine_data(int fd, struct adf_overlay_engine_data *data);
+/**
+ * Frees the overlay engine data returned by adf_get_overlay_engine_data().
+ */
+void adf_free_overlay_engine_data(struct adf_overlay_engine_data *data);
+
+/**
+ * Returns whether the overlay engine supports the specified format.
+ */
+bool adf_overlay_engine_supports_format(int fd, __u32 format);
+
+/**
+ * Subscribes or unsubscribes from the specified hardware event.
+ */
+int adf_set_event(int fd, enum adf_event_type type, bool enabled);
+/**
+ * Reads one event from the fd, blocking if needed.
+ *
+ * The caller must free() the returned buffer. On error, returns -errno.
+ */
+int adf_read_event(int fd, struct adf_event **event);
+
+#define ADF_FORMAT_STR_SIZE 5
+/**
+ * Converts an ADF/DRM fourcc format to its string representation.
+ */
+void adf_format_str(__u32 format, char buf[ADF_FORMAT_STR_SIZE]);
+
+/**
+ * Finds an appropriate interface and overlay engine for a simple post.
+ *
+ * Specifically, finds the primary interface, and an overlay engine
+ * that can be attached to the primary interface and supports one of the
+ * specified formats. The caller may pass a NULL formats list, to indicate that
+ * any RGB format is acceptable.
+ *
+ * On error, returns -errno.
+ */
+int adf_find_simple_post_configuration(struct adf_device *dev,
+ const __u32 *formats, size_t n_formats,
+ adf_id_t *interface, adf_id_t *overlay_engine);
+
+__END_DECLS
+
+#endif /* _LIBADF_ADF_H_ */
diff --git a/adf/libadf/tests/Android.mk b/adf/libadf/tests/Android.mk
new file mode 100644
index 0000000..68e5817
--- /dev/null
+++ b/adf/libadf/tests/Android.mk
@@ -0,0 +1,23 @@
+#
+# Copyright (C) 2013 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH := $(my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := adf_test.cpp
+LOCAL_MODULE := adf-unit-tests
+LOCAL_STATIC_LIBRARIES := libadf
+LOCAL_CFLAGS += -Werror
+include $(BUILD_NATIVE_TEST)
diff --git a/adf/libadf/tests/adf_test.cpp b/adf/libadf/tests/adf_test.cpp
new file mode 100644
index 0000000..01b2785
--- /dev/null
+++ b/adf/libadf/tests/adf_test.cpp
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+
+#include <adf/adf.h>
+#include <gtest/gtest.h>
+#include <sys/mman.h>
+
+class AdfTest : public testing::Test {
+public:
+ AdfTest() : intf_id(0), intf(-1), eng_id(0), eng(-1) { }
+
+ virtual void SetUp() {
+ int err = adf_device_open(dev_id, O_RDWR, &dev);
+ ASSERT_GE(err, 0) << "opening ADF device " << dev_id <<
+ " failed: " << strerror(-err);
+
+ err = adf_find_simple_post_configuration(&dev, fmt8888, n_fmt8888,
+ &intf_id, &eng_id);
+ ASSERT_GE(err, 0) << "finding ADF configuration failed: " <<
+ strerror(-err);
+
+ intf = adf_interface_open(&dev, intf_id, O_RDWR);
+ ASSERT_GE(intf, 0) << "opening ADF interface " << dev_id << "." <<
+ intf_id << " failed: " << strerror(-intf);
+
+ eng = adf_overlay_engine_open(&dev, eng_id, O_RDWR);
+ ASSERT_GE(eng, 0) << "opening ADF overlay engine " << dev_id << "." <<
+ eng_id << " failed: " << strerror(-eng);
+ }
+
+ virtual void TearDown() {
+ if (eng >= 0)
+ close(eng);
+ if (intf >= 0)
+ close(intf);
+ adf_device_close(&dev);
+ }
+
+ void get8888Format(uint32_t &fmt, char fmt_str[ADF_FORMAT_STR_SIZE]) {
+ adf_overlay_engine_data data;
+ int err = adf_get_overlay_engine_data(eng, &data);
+ ASSERT_GE(err, 0) << "getting ADF overlay engine data failed: " <<
+ strerror(-err);
+
+ for (size_t i = 0; i < data.n_supported_formats; i++) {
+ for (size_t j = 0; j < n_fmt8888; j++) {
+ if (data.supported_formats[i] == fmt8888[j]) {
+ fmt = data.supported_formats[i];
+ adf_format_str(fmt, fmt_str);
+ adf_free_overlay_engine_data(&data);
+ return;
+ }
+ }
+ }
+
+ adf_free_overlay_engine_data(&data);
+ FAIL(); /* this should never happen */
+ }
+
+ void drawCheckerboard(void *buf, uint32_t w, uint32_t h, uint32_t pitch) {
+ uint8_t *buf8 = reinterpret_cast<uint8_t *>(buf);
+ for (uint32_t y = 0; y < h / 2; y++) {
+ uint32_t *scanline = reinterpret_cast<uint32_t *>(buf8 + y * pitch);
+ for (uint32_t x = 0; x < w / 2; x++)
+ scanline[x] = 0xFF0000FF;
+ for (uint32_t x = w / 2; x < w; x++)
+ scanline[x] = 0xFF00FFFF;
+ }
+ for (uint32_t y = h / 2; y < h; y++) {
+ uint32_t *scanline = reinterpret_cast<uint32_t *>(buf8 + y * pitch);
+ for (uint32_t x = 0; x < w / 2; x++)
+ scanline[x] = 0xFFFF00FF;
+ for (uint32_t x = w / 2; x < w; x++)
+ scanline[x] = 0xFFFFFFFF;
+ }
+ }
+
+ /* various helpers to call ADF and die on failure */
+
+ void getInterfaceData(adf_interface_data &data) {
+ int err = adf_get_interface_data(intf, &data);
+ ASSERT_GE(err, 0) << "getting ADF interface data failed: " <<
+ strerror(-err);
+ }
+
+ void getCurrentMode(uint32_t &w, uint32_t &h) {
+ adf_interface_data data;
+ ASSERT_NO_FATAL_FAILURE(getInterfaceData(data));
+ w = data.current_mode.hdisplay;
+ h = data.current_mode.vdisplay;
+ adf_free_interface_data(&data);
+ }
+
+ void blank(uint8_t mode) {
+ int err = adf_interface_blank(intf, mode);
+ ASSERT_FALSE(err < 0 && err != -EBUSY) <<
+ "unblanking interface failed: " << strerror(-err);
+ }
+
+ void attach() {
+ int err = adf_device_attach(&dev, eng_id, intf_id);
+ ASSERT_FALSE(err < 0 && err != -EALREADY) <<
+ "attaching overlay engine " << eng_id << " to interface " <<
+ intf_id << " failed: " << strerror(-err);
+ }
+
+ void detach() {
+ int err = adf_device_detach(&dev, eng_id, intf_id);
+ ASSERT_FALSE(err < 0 && err != -EINVAL) <<
+ "detaching overlay engine " << eng_id << " from interface " <<
+ intf_id << " failed: " << strerror(-err);
+ }
+
+ void readVsyncTimestamp(uint64_t ×tamp) {
+ adf_event *event;
+ int err = adf_read_event(intf, &event);
+ ASSERT_GE(err, 0) << "reading ADF event failed: " << strerror(-err);
+
+ ASSERT_EQ(ADF_EVENT_VSYNC, event->type);
+ ASSERT_EQ(sizeof(adf_vsync_event), event->length);
+
+ adf_vsync_event *vsync_event =
+ reinterpret_cast<adf_vsync_event *>(event);
+ timestamp = vsync_event->timestamp;
+ free(event);
+ }
+
+protected:
+ adf_device dev;
+ adf_id_t intf_id;
+ int intf;
+ adf_id_t eng_id;
+ int eng;
+
+private:
+ const static adf_id_t dev_id = 0;
+ const static __u32 fmt8888[];
+ const static size_t n_fmt8888;
+};
+
+const __u32 AdfTest::fmt8888[] = {
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_XBGR8888,
+ DRM_FORMAT_RGBX8888,
+ DRM_FORMAT_BGRX8888,
+ DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_ABGR8888,
+ DRM_FORMAT_RGBA8888,
+ DRM_FORMAT_BGRA8888
+};
+const size_t AdfTest::n_fmt8888 = sizeof(fmt8888) / sizeof(fmt8888[0]);
+
+TEST(adf, devices) {
+ adf_id_t *devs;
+ ssize_t n_devs = adf_devices(&devs);
+ free(devs);
+
+ ASSERT_GE(n_devs, 0) << "enumerating ADF devices failed: " <<
+ strerror(-n_devs);
+ ASSERT_TRUE(devs != NULL);
+}
+
+TEST_F(AdfTest, device_data) {
+ adf_device_data data;
+ int err = adf_get_device_data(&dev, &data);
+ ASSERT_GE(err, 0) << "getting ADF device data failed: " << strerror(-err);
+
+ EXPECT_LT(data.n_attachments, ADF_MAX_ATTACHMENTS);
+ EXPECT_GT(data.n_allowed_attachments, 0U);
+ EXPECT_LT(data.n_allowed_attachments, ADF_MAX_ATTACHMENTS);
+ EXPECT_LT(data.custom_data_size, (size_t)ADF_MAX_CUSTOM_DATA_SIZE);
+ adf_free_device_data(&data);
+}
+
+TEST_F(AdfTest, interface_data) {
+ adf_interface_data data;
+ ASSERT_NO_FATAL_FAILURE(getInterfaceData(data));
+
+ EXPECT_LT(data.type, ADF_INTF_TYPE_MAX);
+ EXPECT_LE(data.dpms_state, DRM_MODE_DPMS_OFF);
+ EXPECT_EQ(1, data.hotplug_detect);
+ EXPECT_GT(data.n_available_modes, 0U);
+ EXPECT_LT(data.custom_data_size, (size_t)ADF_MAX_CUSTOM_DATA_SIZE);
+ adf_free_interface_data(&data);
+}
+
+TEST_F(AdfTest, overlay_engine_data) {
+ adf_overlay_engine_data data;
+ int err = adf_get_overlay_engine_data(eng, &data);
+ ASSERT_GE(err, 0) << "getting ADF overlay engine failed: " <<
+ strerror(-err);
+
+ EXPECT_GT(data.n_supported_formats, 0U);
+ EXPECT_LT(data.n_supported_formats, ADF_MAX_SUPPORTED_FORMATS);
+ EXPECT_LT(data.custom_data_size, (size_t)ADF_MAX_CUSTOM_DATA_SIZE);
+ adf_free_overlay_engine_data(&data);
+}
+
+TEST_F(AdfTest, blank) {
+ int err = adf_interface_blank(intf, (uint8_t)-1);
+ EXPECT_EQ(-EINVAL, err) << "setting bogus DPMS mode should have failed";
+
+ err = adf_interface_blank(eng, DRM_MODE_DPMS_OFF);
+ EXPECT_EQ(-EINVAL, err) << "blanking overlay engine should have failed";
+
+ ASSERT_NO_FATAL_FAILURE(blank(DRM_MODE_DPMS_OFF));
+ err = adf_interface_blank(intf, DRM_MODE_DPMS_OFF);
+ EXPECT_EQ(-EBUSY, err) << "blanking interface twice should have failed";
+
+ ASSERT_NO_FATAL_FAILURE(blank(DRM_MODE_DPMS_ON));
+ err = adf_interface_blank(intf, DRM_MODE_DPMS_ON);
+ EXPECT_EQ(-EBUSY, err) << "unblanking interface twice should have failed";
+
+ adf_interface_data data;
+ ASSERT_NO_FATAL_FAILURE(getInterfaceData(data));
+ EXPECT_EQ(DRM_MODE_DPMS_ON, data.dpms_state);
+ adf_free_interface_data(&data);
+}
+
+TEST_F(AdfTest, event) {
+ int err = adf_set_event(intf, ADF_EVENT_TYPE_MAX, true);
+ EXPECT_EQ(-EINVAL, err) << "enabling bogus ADF event should have failed";
+
+ err = adf_set_event(intf, ADF_EVENT_TYPE_MAX, false);
+ EXPECT_EQ(-EINVAL, err) << "disabling bogus ADF event should have failed";
+
+ err = adf_set_event(intf, ADF_EVENT_VSYNC, true);
+ ASSERT_GE(err, 0) << "enabling vsync event failed: " << strerror(-err);
+
+ err = adf_set_event(intf, ADF_EVENT_VSYNC, true);
+ EXPECT_EQ(-EALREADY, err) <<
+ "enabling vsync event twice should have failed";
+
+ ASSERT_NO_FATAL_FAILURE(blank(DRM_MODE_DPMS_ON));
+
+ uint64_t timestamp1, timestamp2;
+ ASSERT_NO_FATAL_FAILURE(readVsyncTimestamp(timestamp1));
+ ASSERT_NO_FATAL_FAILURE(readVsyncTimestamp(timestamp2));
+ EXPECT_GT(timestamp2, timestamp1);
+
+ err = adf_set_event(intf, ADF_EVENT_VSYNC, false);
+ EXPECT_GE(err, 0) << "disabling vsync event failed: " << strerror(-err);
+
+ err = adf_set_event(intf, ADF_EVENT_VSYNC, false);
+ EXPECT_EQ(-EALREADY, err) <<
+ "disabling vsync event twice should have failed";
+}
+
+TEST_F(AdfTest, attach) {
+ ASSERT_NO_FATAL_FAILURE(attach());
+ int err = adf_device_attach(&dev, eng_id, intf_id);
+ EXPECT_EQ(-EALREADY, err) << "attaching overlay engine " << eng_id <<
+ " to interface " << intf_id << " twice should have failed";
+
+ ASSERT_NO_FATAL_FAILURE(detach());
+ err = adf_device_detach(&dev, eng_id, intf_id);
+ EXPECT_EQ(-EINVAL, err) << "detaching overlay engine " << eng_id <<
+ " from interface " << intf_id << " twice should have failed";
+
+ err = adf_device_attach(&dev, eng_id, ADF_MAX_INTERFACES);
+ EXPECT_EQ(-EINVAL, err) << "attaching overlay engine " << eng_id <<
+ " to bogus interface should have failed";
+
+ err = adf_device_detach(&dev, eng_id, ADF_MAX_INTERFACES);
+ EXPECT_EQ(-EINVAL, err) << "detaching overlay engine " << eng_id <<
+ " from bogus interface should have failed";
+}
+
+TEST_F(AdfTest, simple_buffer_alloc) {
+ uint32_t w = 0, h = 0;
+ ASSERT_NO_FATAL_FAILURE(getCurrentMode(w, h));
+
+ uint32_t format;
+ char format_str[ADF_FORMAT_STR_SIZE];
+ ASSERT_NO_FATAL_FAILURE(get8888Format(format, format_str));
+
+ uint32_t offset;
+ uint32_t pitch;
+ int buf_fd = adf_interface_simple_buffer_alloc(intf, w, h, format, &offset,
+ &pitch);
+ EXPECT_GE(buf_fd, 0) << "allocating " << w << "x" << h << " " <<
+ format_str << " buffer failed: " << strerror(-buf_fd);
+ EXPECT_GE(pitch, w * 4);
+ close(buf_fd);
+
+ buf_fd = adf_interface_simple_buffer_alloc(intf, w, h, 0xDEADBEEF, &offset,
+ &pitch);
+ /* n.b.: ADF only allows simple buffers with built-in RGB formats,
+ so this should fail even if a driver supports custom format 0xDEADBEEF */
+ EXPECT_EQ(-EINVAL, buf_fd) <<
+ "allocating buffer with bogus format should have failed";
+}
+
+TEST_F(AdfTest, simple_buffer) {
+ uint32_t w = 0, h = 0;
+ ASSERT_NO_FATAL_FAILURE(getCurrentMode(w, h));
+
+ uint32_t format = 0;
+ char format_str[ADF_FORMAT_STR_SIZE];
+ ASSERT_NO_FATAL_FAILURE(get8888Format(format, format_str));
+
+ uint32_t offset;
+ uint32_t pitch;
+ int buf_fd = adf_interface_simple_buffer_alloc(intf, w, h, format, &offset,
+ &pitch);
+ ASSERT_GE(buf_fd, 0) << "allocating " << w << "x" << h << " " <<
+ format_str << " buffer failed: " << strerror(-buf_fd);
+ EXPECT_GE(pitch, w * 4);
+
+ void *mapped = mmap(NULL, pitch * h, PROT_WRITE, MAP_SHARED, buf_fd,
+ offset);
+ ASSERT_NE(mapped, MAP_FAILED) << "mapping " << w << "x" << h << " " <<
+ format_str << " buffer failed: " << strerror(-errno);
+ drawCheckerboard(mapped, w, h, pitch);
+ munmap(mapped, pitch * h);
+
+ ASSERT_NO_FATAL_FAILURE(attach());
+ ASSERT_NO_FATAL_FAILURE(blank(DRM_MODE_DPMS_ON));
+
+ int release_fence = adf_interface_simple_post(intf, eng_id, w, h, format,
+ buf_fd, offset, pitch, -1);
+ close(buf_fd);
+ ASSERT_GE(release_fence, 0) << "posting " << w << "x" << h << " " <<
+ format_str << " buffer failed: " << strerror(-release_fence);
+ close(release_fence);
+}
diff --git a/adf/libadfhwc/Android.mk b/adf/libadfhwc/Android.mk
new file mode 100644
index 0000000..898f9c9
--- /dev/null
+++ b/adf/libadfhwc/Android.mk
@@ -0,0 +1,25 @@
+# Copyright (C) 2013 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := adfhwc.cpp
+LOCAL_MODULE := libadfhwc
+LOCAL_MODULE_TAGS := optional
+LOCAL_STATIC_LIBRARIES := libadf liblog libutils
+LOCAL_CFLAGS += -DLOG_TAG=\"adfhwc\" -Werror
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES += $(LOCAL_EXPORT_C_INCLUDE_DIRS)
+include $(BUILD_STATIC_LIBRARY)
diff --git a/adf/libadfhwc/adfhwc.cpp b/adf/libadfhwc/adfhwc.cpp
new file mode 100644
index 0000000..21f245e
--- /dev/null
+++ b/adf/libadfhwc/adfhwc.cpp
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <malloc.h>
+#include <poll.h>
+#include <pthread.h>
+#include <sys/resource.h>
+
+#include <adf/adf.h>
+#include <adfhwc/adfhwc.h>
+
+#include <cutils/log.h>
+#include <utils/Vector.h>
+
+struct adf_hwc_helper {
+ adf_hwc_event_callbacks const *event_cb;
+ void *event_cb_data;
+
+ pthread_t event_thread;
+
+ android::Vector<int> intf_fds;
+ android::Vector<drm_mode_modeinfo> display_configs;
+};
+
+template<typename T> inline T min(T a, T b) { return (a < b) ? a : b; }
+
+int adf_eventControl(struct adf_hwc_helper *dev, int disp, int event,
+ int enabled)
+{
+ if (enabled != !!enabled)
+ return -EINVAL;
+
+ if ((size_t)disp >= dev->intf_fds.size())
+ return -EINVAL;
+
+ switch (event) {
+ case HWC_EVENT_VSYNC:
+ return adf_set_event(dev->intf_fds[disp], ADF_EVENT_VSYNC, enabled);
+ }
+
+ return -EINVAL;
+}
+
+static inline int32_t dpi(uint16_t res, uint16_t size_mm)
+{
+ if (size_mm)
+ return 1000 * (res * 25.4f) / size_mm;
+ return 0;
+}
+
+int adf_blank(struct adf_hwc_helper *dev, int disp, int blank)
+{
+ if ((size_t)disp >= dev->intf_fds.size())
+ return -EINVAL;
+
+ uint8_t dpms_mode = blank ? DRM_MODE_DPMS_OFF : DRM_MODE_DPMS_ON;
+ return adf_interface_blank(dev->intf_fds[disp], dpms_mode);
+}
+
+int adf_query_display_types_supported(struct adf_hwc_helper *dev, int *value)
+{
+ *value = 0;
+ if (dev->intf_fds.size() > 0)
+ *value |= HWC_DISPLAY_PRIMARY_BIT;
+ if (dev->intf_fds.size() > 1)
+ *value |= HWC_DISPLAY_EXTERNAL_BIT;
+
+ return 0;
+}
+
+int adf_getDisplayConfigs(struct adf_hwc_helper *dev, int disp,
+ uint32_t *configs, size_t *numConfigs)
+{
+ if ((size_t)disp >= dev->intf_fds.size())
+ return -EINVAL;
+
+ adf_interface_data data;
+ int err = adf_get_interface_data(dev->intf_fds[disp], &data);
+ if (err < 0) {
+ ALOGE("failed to get ADF interface data: %s", strerror(err));
+ return err;
+ }
+
+ if (!data.hotplug_detect)
+ return -ENODEV;
+
+ android::Vector<drm_mode_modeinfo *> unique_configs;
+ unique_configs.push_back(&data.current_mode);
+ for (size_t i = 0; i < data.n_available_modes; i++)
+ if (memcmp(&data.available_modes[i], &data.current_mode,
+ sizeof(data.current_mode)))
+ unique_configs.push_back(&data.available_modes[i]);
+
+ for (size_t i = 0; i < min(*numConfigs, unique_configs.size()); i++) {
+ configs[i] = dev->display_configs.size();
+ dev->display_configs.push_back(*unique_configs[i]);
+ }
+ *numConfigs = unique_configs.size();
+
+ adf_free_interface_data(&data);
+ return 0;
+}
+
+static int32_t adf_display_attribute(const adf_interface_data &data,
+ const drm_mode_modeinfo &mode, const uint32_t attribute)
+{
+ switch (attribute) {
+ case HWC_DISPLAY_VSYNC_PERIOD:
+ if (mode.vrefresh)
+ return 1000000000 / mode.vrefresh;
+ return 0;
+
+ case HWC_DISPLAY_WIDTH:
+ return mode.hdisplay;
+
+ case HWC_DISPLAY_HEIGHT:
+ return mode.vdisplay;
+
+ case HWC_DISPLAY_DPI_X:
+ return dpi(mode.hdisplay, data.width_mm);
+
+ case HWC_DISPLAY_DPI_Y:
+ return dpi(mode.vdisplay, data.height_mm);
+
+ default:
+ ALOGE("unknown display attribute %u", attribute);
+ return -EINVAL;
+ }
+}
+
+int adf_getDisplayAttributes(struct adf_hwc_helper *dev, int disp,
+ uint32_t config, const uint32_t *attributes, int32_t *values)
+{
+ if ((size_t)disp >= dev->intf_fds.size())
+ return -EINVAL;
+
+ if (config >= dev->display_configs.size())
+ return -EINVAL;
+
+ adf_interface_data data;
+ int err = adf_get_interface_data(dev->intf_fds[disp], &data);
+ if (err < 0) {
+ ALOGE("failed to get ADF interface data: %s", strerror(err));
+ return err;
+ }
+
+ for (int i = 0; attributes[i] != HWC_DISPLAY_NO_ATTRIBUTE; i++)
+ values[i] = adf_display_attribute(data, dev->display_configs[config],
+ attributes[i]);
+
+ adf_free_interface_data(&data);
+ return 0;
+}
+
+static void handle_adf_event(struct adf_hwc_helper *dev, int disp)
+{
+ adf_event *event;
+ int err = adf_read_event(dev->intf_fds[disp], &event);
+ if (err < 0) {
+ ALOGE("error reading event from display %d: %s", disp, strerror(err));
+ return;
+ }
+
+ void *vsync_temp;
+ adf_vsync_event *vsync;
+ adf_hotplug_event *hotplug;
+
+ switch (event->type) {
+ case ADF_EVENT_VSYNC:
+ vsync_temp = event;
+ vsync = static_cast<adf_vsync_event *>(vsync_temp);
+ // casting directly to adf_vsync_event * makes g++ warn about
+ // potential alignment issues that don't apply here
+ dev->event_cb->vsync(dev->event_cb_data, disp, vsync->timestamp);
+ break;
+ case ADF_EVENT_HOTPLUG:
+ hotplug = reinterpret_cast<adf_hotplug_event *>(event);
+ dev->event_cb->hotplug(dev->event_cb_data, disp, hotplug->connected);
+ break;
+ default:
+ if (event->type < ADF_EVENT_DEVICE_CUSTOM)
+ ALOGW("unrecognized event type %u", event->type);
+ else if (!dev->event_cb || !dev->event_cb->custom_event)
+ ALOGW("unhandled event type %u", event->type);
+ else
+ dev->event_cb->custom_event(dev->event_cb_data, disp, event);
+ }
+ free(event);
+}
+
+static void *adf_event_thread(void *data)
+{
+ adf_hwc_helper *dev = static_cast<adf_hwc_helper *>(data);
+
+ setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY);
+
+ pollfd *fds = new pollfd[dev->intf_fds.size()];
+ for (size_t i = 0; i < dev->intf_fds.size(); i++) {
+ fds[i].fd = dev->intf_fds[i];
+ fds[i].events = POLLIN | POLLPRI;
+ }
+
+ while (true) {
+ int err = poll(fds, dev->intf_fds.size(), -1);
+
+ if (err > 0) {
+ for (size_t i = 0; i < dev->intf_fds.size(); i++)
+ if (fds[i].revents & (POLLIN | POLLPRI))
+ handle_adf_event(dev, i);
+ }
+ else if (err == -1) {
+ if (errno == EINTR)
+ break;
+ ALOGE("error in event thread: %s", strerror(errno));
+ }
+ }
+
+ delete [] fds;
+ return NULL;
+}
+
+int adf_hwc_open(int *intf_fds, size_t n_intfs,
+ const struct adf_hwc_event_callbacks *event_cb, void *event_cb_data,
+ struct adf_hwc_helper **dev)
+{
+ if (!n_intfs)
+ return -EINVAL;
+
+ adf_hwc_helper *dev_ret = new adf_hwc_helper;
+ dev_ret->event_cb = event_cb;
+ dev_ret->event_cb_data = event_cb_data;
+
+ int ret;
+
+ for (size_t i = 0; i < n_intfs; i++) {
+ int dup_intf_fd = dup(intf_fds[i]);
+ if (dup_intf_fd < 0) {
+ ALOGE("failed to dup interface fd: %s", strerror(errno));
+ ret = -errno;
+ goto err;
+ }
+
+ dev_ret->intf_fds.push_back(dup_intf_fd);
+
+ ret = adf_set_event(dup_intf_fd, ADF_EVENT_HOTPLUG, 1);
+ if (ret < 0 && ret != -EINVAL) {
+ ALOGE("failed to enable hotplug event on display %zu: %s",
+ i, strerror(errno));
+ goto err;
+ }
+ }
+
+ ret = pthread_create(&dev_ret->event_thread, NULL, adf_event_thread,
+ dev_ret);
+ if (ret) {
+ ALOGE("failed to create event thread: %s", strerror(ret));
+ goto err;
+ }
+
+ *dev = dev_ret;
+ return 0;
+
+err:
+ for (size_t i = 0; i < dev_ret->intf_fds.size(); i++)
+ close(dev_ret->intf_fds[i]);
+
+ delete dev_ret;
+ return ret;
+}
+
+void adf_hwc_close(struct adf_hwc_helper *dev)
+{
+ pthread_kill(dev->event_thread, SIGTERM);
+ pthread_join(dev->event_thread, NULL);
+
+ for (size_t i = 0; i < dev->intf_fds.size(); i++)
+ close(dev->intf_fds[i]);
+
+ delete dev;
+}
diff --git a/adf/libadfhwc/include/adfhwc/adfhwc.h b/adf/libadfhwc/include/adfhwc/adfhwc.h
new file mode 100644
index 0000000..71e7624
--- /dev/null
+++ b/adf/libadfhwc/include/adfhwc/adfhwc.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBADFHWC_ADFHWC_H_
+#define _LIBADFHWC_ADFHWC_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/cdefs.h>
+#include <video/adf.h>
+
+#include <hardware/hwcomposer.h>
+
+struct adf_hwc_helper;
+
+struct adf_hwc_event_callbacks {
+ /**
+ * Called on vsync (required)
+ */
+ void (*vsync)(void *data, int disp, uint64_t timestamp);
+ /**
+ * Called on hotplug (required)
+ */
+ void (*hotplug)(void *data, int disp, bool connected);
+ /**
+ * Called on hardware-custom ADF events (optional)
+ */
+ void (*custom_event)(void *data, int disp, struct adf_event *event);
+};
+
+/**
+ * Converts HAL pixel formats to equivalent ADF/DRM format FourCCs.
+ */
+static inline uint32_t adf_fourcc_for_hal_pixel_format(int format)
+{
+ switch (format) {
+ case HAL_PIXEL_FORMAT_RGBA_8888:
+ return DRM_FORMAT_RGBA8888;
+ case HAL_PIXEL_FORMAT_RGBX_8888:
+ return DRM_FORMAT_RGBX8888;
+ case HAL_PIXEL_FORMAT_RGB_888:
+ return DRM_FORMAT_RGB888;
+ case HAL_PIXEL_FORMAT_RGB_565:
+ return DRM_FORMAT_RGB565;
+ case HAL_PIXEL_FORMAT_BGRA_8888:
+ return DRM_FORMAT_BGRA8888;
+ case HAL_PIXEL_FORMAT_YV12:
+ return DRM_FORMAT_YVU420;
+ case HAL_PIXEL_FORMAT_YCbCr_422_SP:
+ return DRM_FORMAT_NV16;
+ case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+ return DRM_FORMAT_NV21;
+ case HAL_PIXEL_FORMAT_YCbCr_422_I:
+ return DRM_FORMAT_YUYV;
+ default:
+ return 0;
+ }
+}
+
+/**
+ * Converts HAL display types to equivalent ADF interface flags.
+ */
+static inline uint32_t adf_hwc_interface_flag_for_disp(int disp)
+{
+ switch (disp) {
+ case HWC_DISPLAY_PRIMARY:
+ return ADF_INTF_FLAG_PRIMARY;
+ case HWC_DISPLAY_EXTERNAL:
+ return ADF_INTF_FLAG_EXTERNAL;
+ default:
+ return 0;
+ }
+}
+
+__BEGIN_DECLS
+
+/**
+ * Create a HWC helper for the specified ADF interfaces.
+ *
+ * intf_fds must be indexed by HWC display type: e.g.,
+ * intf_fds[HWC_DISPLAY_PRIMARY] is the fd for the primary display
+ * interface. n_intfs must be >= 1.
+ *
+ * The caller retains ownership of the fds in intf_fds and must close()
+ * them when they are no longer needed.
+ *
+ * On error, returns -errno.
+ */
+int adf_hwc_open(int *intf_fds, size_t n_intfs,
+ const struct adf_hwc_event_callbacks *event_cb, void *event_cb_data,
+ struct adf_hwc_helper **dev);
+
+/**
+ * Destroys a HWC helper.
+ */
+void adf_hwc_close(struct adf_hwc_helper *dev);
+
+/**
+ * Generic implementations of common HWC ops.
+ *
+ * The HWC should not point its ops directly at these helpers. Instead, the HWC
+ * should provide stub ops which call these helpers after converting the
+ * hwc_composer_device_1* to a struct adf_hwc_helper*.
+ */
+int adf_eventControl(struct adf_hwc_helper *dev, int disp, int event,
+ int enabled);
+int adf_blank(struct adf_hwc_helper *dev, int disp, int blank);
+int adf_query_display_types_supported(struct adf_hwc_helper *dev, int *value);
+int adf_getDisplayConfigs(struct adf_hwc_helper *dev, int disp,
+ uint32_t *configs, size_t *numConfigs);
+int adf_getDisplayAttributes(struct adf_hwc_helper *dev, int disp,
+ uint32_t config, const uint32_t *attributes, int32_t *values);
+
+__END_DECLS
+
+#endif /* _LIBADFHWC_ADFHWC_H_ */
diff --git a/base/.clang-format b/base/.clang-format
new file mode 100644
index 0000000..2b83a1f
--- /dev/null
+++ b/base/.clang-format
@@ -0,0 +1,11 @@
+BasedOnStyle: Google
+AllowShortBlocksOnASingleLine: false
+AllowShortFunctionsOnASingleLine: false
+
+CommentPragmas: NOLINT:.*
+DerivePointerAlignment: false
+IndentWidth: 2
+PointerAlignment: Left
+TabWidth: 2
+UseTab: Never
+PenaltyExcessCharacter: 32
diff --git a/base/Android.mk b/base/Android.mk
new file mode 100644
index 0000000..7bd317b
--- /dev/null
+++ b/base/Android.mk
@@ -0,0 +1,105 @@
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+libbase_src_files := \
+ file.cpp \
+ logging.cpp \
+ stringprintf.cpp \
+ strings.cpp \
+
+libbase_test_src_files := \
+ file_test.cpp \
+ logging_test.cpp \
+ stringprintf_test.cpp \
+ strings_test.cpp \
+ test_main.cpp \
+ test_utils.cpp \
+
+libbase_cppflags := \
+ -Wall \
+ -Wextra \
+ -Werror \
+
+# Device
+# ------------------------------------------------------------------------------
+include $(CLEAR_VARS)
+LOCAL_MODULE := libbase
+LOCAL_CLANG := true
+LOCAL_SRC_FILES := $(libbase_src_files)
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_CPPFLAGS := $(libbase_cppflags)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_STATIC_LIBRARIES := libcutils
+LOCAL_MULTILIB := both
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libbase
+LOCAL_CLANG := true
+LOCAL_WHOLE_STATIC_LIBRARIES := libbase
+LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_SHARED_LIBRARIES := libcutils
+LOCAL_MULTILIB := both
+include $(BUILD_SHARED_LIBRARY)
+
+# Host
+# ------------------------------------------------------------------------------
+include $(CLEAR_VARS)
+LOCAL_MODULE := libbase
+LOCAL_SRC_FILES := $(libbase_src_files)
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_CPPFLAGS := $(libbase_cppflags)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_STATIC_LIBRARIES := libcutils
+LOCAL_MULTILIB := both
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libbase
+LOCAL_WHOLE_STATIC_LIBRARIES := libbase
+LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_STATIC_LIBRARIES := libcutils
+LOCAL_MULTILIB := both
+include $(BUILD_HOST_SHARED_LIBRARY)
+
+# Tests
+# ------------------------------------------------------------------------------
+include $(CLEAR_VARS)
+LOCAL_MODULE := libbase_test
+LOCAL_CLANG := true
+LOCAL_SRC_FILES := $(libbase_test_src_files)
+LOCAL_C_INCLUDES := $(LOCAL_PATH)
+LOCAL_CPPFLAGS := $(libbase_cppflags)
+LOCAL_SHARED_LIBRARIES := libbase
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+include $(BUILD_NATIVE_TEST)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libbase_test
+LOCAL_SRC_FILES := $(libbase_test_src_files)
+LOCAL_C_INCLUDES := $(LOCAL_PATH)
+LOCAL_CPPFLAGS := $(libbase_cppflags)
+LOCAL_SHARED_LIBRARIES := libbase
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+include $(BUILD_HOST_NATIVE_TEST)
diff --git a/base/CPPLINT.cfg b/base/CPPLINT.cfg
new file mode 100644
index 0000000..d94a89c
--- /dev/null
+++ b/base/CPPLINT.cfg
@@ -0,0 +1,2 @@
+set noparent
+filter=-build/header_guard,-build/include,-build/c++11,-whitespace/operators
diff --git a/base/file.cpp b/base/file.cpp
new file mode 100644
index 0000000..9a340b7
--- /dev/null
+++ b/base/file.cpp
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "base/file.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <string>
+
+#include "base/macros.h" // For TEMP_FAILURE_RETRY on Darwin.
+#define LOG_TAG "base.file"
+#include "cutils/log.h"
+#include "utils/Compat.h"
+
+namespace android {
+namespace base {
+
+bool ReadFdToString(int fd, std::string* content) {
+ content->clear();
+
+ char buf[BUFSIZ];
+ ssize_t n;
+ while ((n = TEMP_FAILURE_RETRY(read(fd, &buf[0], sizeof(buf)))) > 0) {
+ content->append(buf, n);
+ }
+ return (n == 0) ? true : false;
+}
+
+bool ReadFileToString(const std::string& path, std::string* content) {
+ content->clear();
+
+ int fd =
+ TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
+ if (fd == -1) {
+ return false;
+ }
+ bool result = ReadFdToString(fd, content);
+ close(fd);
+ return result;
+}
+
+bool WriteStringToFd(const std::string& content, int fd) {
+ const char* p = content.data();
+ size_t left = content.size();
+ while (left > 0) {
+ ssize_t n = TEMP_FAILURE_RETRY(write(fd, p, left));
+ if (n == -1) {
+ return false;
+ }
+ p += n;
+ left -= n;
+ }
+ return true;
+}
+
+static bool CleanUpAfterFailedWrite(const std::string& path) {
+ // Something went wrong. Let's not leave a corrupt file lying around.
+ int saved_errno = errno;
+ unlink(path.c_str());
+ errno = saved_errno;
+ return false;
+}
+
+#if !defined(_WIN32)
+bool WriteStringToFile(const std::string& content, const std::string& path,
+ mode_t mode, uid_t owner, gid_t group) {
+ int fd = TEMP_FAILURE_RETRY(
+ open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
+ mode));
+ if (fd == -1) {
+ ALOGE("android::WriteStringToFile open failed: %s", strerror(errno));
+ return false;
+ }
+
+ // We do an explicit fchmod here because we assume that the caller really
+ // meant what they said and doesn't want the umask-influenced mode.
+ if (fchmod(fd, mode) == -1) {
+ ALOGE("android::WriteStringToFile fchmod failed: %s", strerror(errno));
+ return CleanUpAfterFailedWrite(path);
+ }
+ if (fchown(fd, owner, group) == -1) {
+ ALOGE("android::WriteStringToFile fchown failed: %s", strerror(errno));
+ return CleanUpAfterFailedWrite(path);
+ }
+ if (!WriteStringToFd(content, fd)) {
+ ALOGE("android::WriteStringToFile write failed: %s", strerror(errno));
+ return CleanUpAfterFailedWrite(path);
+ }
+ close(fd);
+ return true;
+}
+#endif
+
+bool WriteStringToFile(const std::string& content, const std::string& path) {
+ int fd = TEMP_FAILURE_RETRY(
+ open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
+ DEFFILEMODE));
+ if (fd == -1) {
+ return false;
+ }
+
+ bool result = WriteStringToFd(content, fd);
+ close(fd);
+ return result || CleanUpAfterFailedWrite(path);
+}
+
+bool ReadFully(int fd, void* data, size_t byte_count) {
+ uint8_t* p = reinterpret_cast<uint8_t*>(data);
+ size_t remaining = byte_count;
+ while (remaining > 0) {
+ ssize_t n = TEMP_FAILURE_RETRY(read(fd, p, remaining));
+ if (n <= 0) return false;
+ p += n;
+ remaining -= n;
+ }
+ return true;
+}
+
+bool WriteFully(int fd, const void* data, size_t byte_count) {
+ const uint8_t* p = reinterpret_cast<const uint8_t*>(data);
+ size_t remaining = byte_count;
+ while (remaining > 0) {
+ ssize_t n = TEMP_FAILURE_RETRY(write(fd, p, remaining));
+ if (n == -1) return false;
+ p += n;
+ remaining -= n;
+ }
+ return true;
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/file_test.cpp b/base/file_test.cpp
new file mode 100644
index 0000000..b138094
--- /dev/null
+++ b/base/file_test.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "base/file.h"
+
+#include <gtest/gtest.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <string>
+
+#include "test_utils.h"
+
+TEST(file, ReadFileToString_ENOENT) {
+ std::string s("hello");
+ errno = 0;
+ ASSERT_FALSE(android::base::ReadFileToString("/proc/does-not-exist", &s));
+ EXPECT_EQ(ENOENT, errno);
+ EXPECT_EQ("", s); // s was cleared.
+}
+
+TEST(file, ReadFileToString_success) {
+ std::string s("hello");
+ ASSERT_TRUE(android::base::ReadFileToString("/proc/version", &s))
+ << strerror(errno);
+ EXPECT_GT(s.length(), 6U);
+ EXPECT_EQ('\n', s[s.length() - 1]);
+ s[5] = 0;
+ EXPECT_STREQ("Linux", s.c_str());
+}
+
+TEST(file, WriteStringToFile) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ ASSERT_TRUE(android::base::WriteStringToFile("abc", tf.filename))
+ << strerror(errno);
+ std::string s;
+ ASSERT_TRUE(android::base::ReadFileToString(tf.filename, &s))
+ << strerror(errno);
+ EXPECT_EQ("abc", s);
+}
+
+// WriteStringToFile2 is explicitly for setting Unix permissions, which make no
+// sense on Windows.
+#if !defined(_WIN32)
+TEST(file, WriteStringToFile2) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ ASSERT_TRUE(android::base::WriteStringToFile("abc", tf.filename, 0660,
+ getuid(), getgid()))
+ << strerror(errno);
+ struct stat sb;
+ ASSERT_EQ(0, stat(tf.filename, &sb));
+ ASSERT_EQ(0660U, static_cast<unsigned int>(sb.st_mode & ~S_IFMT));
+ ASSERT_EQ(getuid(), sb.st_uid);
+ ASSERT_EQ(getgid(), sb.st_gid);
+ std::string s;
+ ASSERT_TRUE(android::base::ReadFileToString(tf.filename, &s))
+ << strerror(errno);
+ EXPECT_EQ("abc", s);
+}
+#endif
+
+TEST(file, WriteStringToFd) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ ASSERT_TRUE(android::base::WriteStringToFd("abc", tf.fd));
+
+ ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
+
+ std::string s;
+ ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s)) << strerror(errno);
+ EXPECT_EQ("abc", s);
+}
+
+TEST(file, ReadFully) {
+ int fd = open("/proc/version", O_RDONLY);
+ ASSERT_NE(-1, fd) << strerror(errno);
+
+ char buf[1024];
+ memset(buf, 0, sizeof(buf));
+ ASSERT_TRUE(android::base::ReadFully(fd, buf, 5));
+ ASSERT_STREQ("Linux", buf);
+
+ ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
+
+ ASSERT_FALSE(android::base::ReadFully(fd, buf, sizeof(buf)));
+
+ close(fd);
+}
+
+TEST(file, WriteFully) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ ASSERT_TRUE(android::base::WriteFully(tf.fd, "abc", 3));
+ std::string s;
+ ASSERT_TRUE(android::base::ReadFileToString(tf.filename, &s))
+ << strerror(errno);
+ EXPECT_EQ("abc", s);
+}
diff --git a/base/include/base/file.h b/base/include/base/file.h
new file mode 100644
index 0000000..acd29b3
--- /dev/null
+++ b/base/include/base/file.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BASE_FILE_H
+#define BASE_FILE_H
+
+#include <sys/stat.h>
+#include <string>
+
+namespace android {
+namespace base {
+
+bool ReadFdToString(int fd, std::string* content);
+bool ReadFileToString(const std::string& path, std::string* content);
+
+bool WriteStringToFile(const std::string& content, const std::string& path);
+bool WriteStringToFd(const std::string& content, int fd);
+
+#if !defined(_WIN32)
+bool WriteStringToFile(const std::string& content, const std::string& path,
+ mode_t mode, uid_t owner, gid_t group);
+#endif
+
+bool ReadFully(int fd, void* data, size_t byte_count);
+bool WriteFully(int fd, const void* data, size_t byte_count);
+
+} // namespace base
+} // namespace android
+
+#endif // BASE_FILE_H
diff --git a/base/include/base/logging.h b/base/include/base/logging.h
new file mode 100644
index 0000000..283a7bc
--- /dev/null
+++ b/base/include/base/logging.h
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef BASE_LOGGING_H
+#define BASE_LOGGING_H
+
+// NOTE: For Windows, you must include logging.h after windows.h to allow the
+// following code to suppress the evil ERROR macro:
+#ifdef _WIN32
+// windows.h includes wingdi.h which defines an evil macro ERROR.
+#ifdef ERROR
+#undef ERROR
+#endif
+#endif
+
+#include <functional>
+#include <memory>
+#include <ostream>
+
+#include "base/macros.h"
+
+namespace android {
+namespace base {
+
+enum LogSeverity {
+ VERBOSE,
+ DEBUG,
+ INFO,
+ WARNING,
+ ERROR,
+ FATAL,
+};
+
+enum LogId {
+ DEFAULT,
+ MAIN,
+ SYSTEM,
+};
+
+typedef std::function<void(LogId, LogSeverity, const char*, const char*,
+ unsigned int, const char*)> LogFunction;
+
+extern void StderrLogger(LogId, LogSeverity, const char*, const char*,
+ unsigned int, const char*);
+
+#ifdef __ANDROID__
+// We expose this even though it is the default because a user that wants to
+// override the default log buffer will have to construct this themselves.
+class LogdLogger {
+ public:
+ explicit LogdLogger(LogId default_log_id = android::base::MAIN);
+
+ void operator()(LogId, LogSeverity, const char* tag, const char* file,
+ unsigned int line, const char* message);
+
+ private:
+ LogId default_log_id_;
+};
+#endif
+
+// Configure logging based on ANDROID_LOG_TAGS environment variable.
+// We need to parse a string that looks like
+//
+// *:v jdwp:d dalvikvm:d dalvikvm-gc:i dalvikvmi:i
+//
+// The tag (or '*' for the global level) comes first, followed by a colon and a
+// letter indicating the minimum priority level we're expected to log. This can
+// be used to reveal or conceal logs with specific tags.
+extern void InitLogging(char* argv[], LogFunction&& logger);
+
+// Configures logging using the default logger (logd for the device, stderr for
+// the host).
+extern void InitLogging(char* argv[]);
+
+// Replace the current logger.
+extern void SetLogger(LogFunction&& logger);
+
+// Logs a message to logcat on Android otherwise to stderr. If the severity is
+// FATAL it also causes an abort. For example:
+//
+// LOG(FATAL) << "We didn't expect to reach here";
+#define LOG(severity) \
+ ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \
+ ::android::base::severity, -1).stream()
+
+// Logs a message to logcat with the specified log ID on Android otherwise to
+// stderr. If the severity is FATAL it also causes an abort.
+#define LOG_TO(dest, severity) \
+ ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::dest, \
+ ::android::base::severity, -1).stream()
+
+// A variant of LOG that also logs the current errno value. To be used when
+// library calls fail.
+#define PLOG(severity) \
+ ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \
+ ::android::base::severity, errno).stream()
+
+// Behaves like PLOG, but logs to the specified log ID.
+#define PLOG_TO(dest, severity) \
+ ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::dest, \
+ ::android::base::severity, errno).stream()
+
+// Marker that code is yet to be implemented.
+#define UNIMPLEMENTED(level) \
+ LOG(level) << __PRETTY_FUNCTION__ << " unimplemented "
+
+// Check whether condition x holds and LOG(FATAL) if not. The value of the
+// expression x is only evaluated once. Extra logging can be appended using <<
+// after. For example:
+//
+// CHECK(false == true) results in a log message of
+// "Check failed: false == true".
+#define CHECK(x) \
+ if (UNLIKELY(!(x))) \
+ ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \
+ ::android::base::FATAL, -1).stream() \
+ << "Check failed: " #x << " "
+
+// Helper for CHECK_xx(x,y) macros.
+#define CHECK_OP(LHS, RHS, OP) \
+ for (auto _values = ::android::base::MakeEagerEvaluator(LHS, RHS); \
+ UNLIKELY(!(_values.lhs OP _values.rhs)); \
+ /* empty */) \
+ ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \
+ ::android::base::FATAL, -1).stream() \
+ << "Check failed: " << #LHS << " " << #OP << " " << #RHS \
+ << " (" #LHS "=" << _values.lhs << ", " #RHS "=" << _values.rhs << ") "
+
+// Check whether a condition holds between x and y, LOG(FATAL) if not. The value
+// of the expressions x and y is evaluated once. Extra logging can be appended
+// using << after. For example:
+//
+// CHECK_NE(0 == 1, false) results in
+// "Check failed: false != false (0==1=false, false=false) ".
+#define CHECK_EQ(x, y) CHECK_OP(x, y, == )
+#define CHECK_NE(x, y) CHECK_OP(x, y, != )
+#define CHECK_LE(x, y) CHECK_OP(x, y, <= )
+#define CHECK_LT(x, y) CHECK_OP(x, y, < )
+#define CHECK_GE(x, y) CHECK_OP(x, y, >= )
+#define CHECK_GT(x, y) CHECK_OP(x, y, > )
+
+// Helper for CHECK_STRxx(s1,s2) macros.
+#define CHECK_STROP(s1, s2, sense) \
+ if (UNLIKELY((strcmp(s1, s2) == 0) != sense)) \
+ LOG(FATAL) << "Check failed: " \
+ << "\"" << s1 << "\"" \
+ << (sense ? " == " : " != ") << "\"" << s2 << "\""
+
+// Check for string (const char*) equality between s1 and s2, LOG(FATAL) if not.
+#define CHECK_STREQ(s1, s2) CHECK_STROP(s1, s2, true)
+#define CHECK_STRNE(s1, s2) CHECK_STROP(s1, s2, false)
+
+// Perform the pthread function call(args), LOG(FATAL) on error.
+#define CHECK_PTHREAD_CALL(call, args, what) \
+ do { \
+ int rc = call args; \
+ if (rc != 0) { \
+ errno = rc; \
+ PLOG(FATAL) << #call << " failed for " << what; \
+ } \
+ } while (false)
+
+// CHECK that can be used in a constexpr function. For example:
+//
+// constexpr int half(int n) {
+// return
+// DCHECK_CONSTEXPR(n >= 0, , 0)
+// CHECK_CONSTEXPR((n & 1) == 0),
+// << "Extra debugging output: n = " << n, 0)
+// n / 2;
+// }
+#define CHECK_CONSTEXPR(x, out, dummy) \
+ (UNLIKELY(!(x))) \
+ ? (LOG(FATAL) << "Check failed: " << #x out, dummy) \
+ :
+
+// DCHECKs are debug variants of CHECKs only enabled in debug builds. Generally
+// CHECK should be used unless profiling identifies a CHECK as being in
+// performance critical code.
+#if defined(NDEBUG)
+static constexpr bool kEnableDChecks = false;
+#else
+static constexpr bool kEnableDChecks = true;
+#endif
+
+#define DCHECK(x) \
+ if (::android::base::kEnableDChecks) CHECK(x)
+#define DCHECK_EQ(x, y) \
+ if (::android::base::kEnableDChecks) CHECK_EQ(x, y)
+#define DCHECK_NE(x, y) \
+ if (::android::base::kEnableDChecks) CHECK_NE(x, y)
+#define DCHECK_LE(x, y) \
+ if (::android::base::kEnableDChecks) CHECK_LE(x, y)
+#define DCHECK_LT(x, y) \
+ if (::android::base::kEnableDChecks) CHECK_LT(x, y)
+#define DCHECK_GE(x, y) \
+ if (::android::base::kEnableDChecks) CHECK_GE(x, y)
+#define DCHECK_GT(x, y) \
+ if (::android::base::kEnableDChecks) CHECK_GT(x, y)
+#define DCHECK_STREQ(s1, s2) \
+ if (::android::base::kEnableDChecks) CHECK_STREQ(s1, s2)
+#define DCHECK_STRNE(s1, s2) \
+ if (::android::base::kEnableDChecks) CHECK_STRNE(s1, s2)
+#if defined(NDEBUG)
+#define DCHECK_CONSTEXPR(x, out, dummy)
+#else
+#define DCHECK_CONSTEXPR(x, out, dummy) CHECK_CONSTEXPR(x, out, dummy)
+#endif
+
+// Temporary class created to evaluate the LHS and RHS, used with
+// MakeEagerEvaluator to infer the types of LHS and RHS.
+template <typename LHS, typename RHS>
+struct EagerEvaluator {
+ EagerEvaluator(LHS l, RHS r) : lhs(l), rhs(r) {
+ }
+ LHS lhs;
+ RHS rhs;
+};
+
+// Helper function for CHECK_xx.
+template <typename LHS, typename RHS>
+static inline EagerEvaluator<LHS, RHS> MakeEagerEvaluator(LHS lhs, RHS rhs) {
+ return EagerEvaluator<LHS, RHS>(lhs, rhs);
+}
+
+// Explicitly instantiate EagerEvalue for pointers so that char*s aren't treated
+// as strings. To compare strings use CHECK_STREQ and CHECK_STRNE. We rely on
+// signed/unsigned warnings to protect you against combinations not explicitly
+// listed below.
+#define EAGER_PTR_EVALUATOR(T1, T2) \
+ template <> \
+ struct EagerEvaluator<T1, T2> { \
+ EagerEvaluator(T1 l, T2 r) \
+ : lhs(reinterpret_cast<const void*>(l)), \
+ rhs(reinterpret_cast<const void*>(r)) { \
+ } \
+ const void* lhs; \
+ const void* rhs; \
+ }
+EAGER_PTR_EVALUATOR(const char*, const char*);
+EAGER_PTR_EVALUATOR(const char*, char*);
+EAGER_PTR_EVALUATOR(char*, const char*);
+EAGER_PTR_EVALUATOR(char*, char*);
+EAGER_PTR_EVALUATOR(const unsigned char*, const unsigned char*);
+EAGER_PTR_EVALUATOR(const unsigned char*, unsigned char*);
+EAGER_PTR_EVALUATOR(unsigned char*, const unsigned char*);
+EAGER_PTR_EVALUATOR(unsigned char*, unsigned char*);
+EAGER_PTR_EVALUATOR(const signed char*, const signed char*);
+EAGER_PTR_EVALUATOR(const signed char*, signed char*);
+EAGER_PTR_EVALUATOR(signed char*, const signed char*);
+EAGER_PTR_EVALUATOR(signed char*, signed char*);
+
+// Data for the log message, not stored in LogMessage to avoid increasing the
+// stack size.
+class LogMessageData;
+
+// A LogMessage is a temporarily scoped object used by LOG and the unlikely part
+// of a CHECK. The destructor will abort if the severity is FATAL.
+class LogMessage {
+ public:
+ LogMessage(const char* file, unsigned int line, LogId id,
+ LogSeverity severity, int error);
+
+ ~LogMessage();
+
+ // Returns the stream associated with the message, the LogMessage performs
+ // output when it goes out of scope.
+ std::ostream& stream();
+
+ // The routine that performs the actual logging.
+ static void LogLine(const char* file, unsigned int line, LogId id,
+ LogSeverity severity, const char* msg);
+
+ private:
+ const std::unique_ptr<LogMessageData> data_;
+
+ DISALLOW_COPY_AND_ASSIGN(LogMessage);
+};
+
+// Allows to temporarily change the minimum severity level for logging.
+class ScopedLogSeverity {
+ public:
+ explicit ScopedLogSeverity(LogSeverity level);
+ ~ScopedLogSeverity();
+
+ private:
+ LogSeverity old_;
+};
+
+} // namespace base
+} // namespace android
+
+#endif // BASE_LOGGING_H
diff --git a/base/include/base/macros.h b/base/include/base/macros.h
new file mode 100644
index 0000000..b1ce7c6
--- /dev/null
+++ b/base/include/base/macros.h
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef UTILS_MACROS_H
+#define UTILS_MACROS_H
+
+#include <stddef.h> // for size_t
+#include <unistd.h> // for TEMP_FAILURE_RETRY
+
+// bionic and glibc both have TEMP_FAILURE_RETRY, but eg Mac OS' libc doesn't.
+#ifndef TEMP_FAILURE_RETRY
+#define TEMP_FAILURE_RETRY(exp) \
+ ({ \
+ decltype(exp) _rc; \
+ do { \
+ _rc = (exp); \
+ } while (_rc == -1 && errno == EINTR); \
+ _rc; \
+ })
+#endif
+
+// A macro to disallow the copy constructor and operator= functions
+// This must be placed in the private: declarations for a class.
+//
+// For disallowing only assign or copy, delete the relevant operator or
+// constructor, for example:
+// void operator=(const TypeName&) = delete;
+// Note, that most uses of DISALLOW_ASSIGN and DISALLOW_COPY are broken
+// semantically, one should either use disallow both or neither. Try to
+// avoid these in new code.
+//
+// When building with C++11 toolchains, just use the language support
+// for explicitly deleted methods.
+#if __cplusplus >= 201103L
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName&) = delete; \
+ void operator=(const TypeName&) = delete
+#else
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName&); \
+ void operator=(const TypeName&)
+#endif
+
+// A macro to disallow all the implicit constructors, namely the
+// default constructor, copy constructor and operator= functions.
+//
+// This should be used in the private: declarations for a class
+// that wants to prevent anyone from instantiating it. This is
+// especially useful for classes containing only static methods.
+#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
+ TypeName(); \
+ DISALLOW_COPY_AND_ASSIGN(TypeName)
+
+// The arraysize(arr) macro returns the # of elements in an array arr.
+// The expression is a compile-time constant, and therefore can be
+// used in defining new arrays, for example. If you use arraysize on
+// a pointer by mistake, you will get a compile-time error.
+//
+// One caveat is that arraysize() doesn't accept any array of an
+// anonymous type or a type defined inside a function. In these rare
+// cases, you have to use the unsafe ARRAYSIZE_UNSAFE() macro below. This is
+// due to a limitation in C++'s template system. The limitation might
+// eventually be removed, but it hasn't happened yet.
+
+// This template function declaration is used in defining arraysize.
+// Note that the function doesn't need an implementation, as we only
+// use its type.
+template <typename T, size_t N>
+char(&ArraySizeHelper(T(&array)[N]))[N]; // NOLINT(readability/casting)
+
+#define arraysize(array) (sizeof(ArraySizeHelper(array)))
+
+// ARRAYSIZE_UNSAFE performs essentially the same calculation as arraysize,
+// but can be used on anonymous types or types defined inside
+// functions. It's less safe than arraysize as it accepts some
+// (although not all) pointers. Therefore, you should use arraysize
+// whenever possible.
+//
+// The expression ARRAYSIZE_UNSAFE(a) is a compile-time constant of type
+// size_t.
+//
+// ARRAYSIZE_UNSAFE catches a few type errors. If you see a compiler error
+//
+// "warning: division by zero in ..."
+//
+// when using ARRAYSIZE_UNSAFE, you are (wrongfully) giving it a pointer.
+// You should only use ARRAYSIZE_UNSAFE on statically allocated arrays.
+//
+// The following comments are on the implementation details, and can
+// be ignored by the users.
+//
+// ARRAYSIZE_UNSAFE(arr) works by inspecting sizeof(arr) (the # of bytes in
+// the array) and sizeof(*(arr)) (the # of bytes in one array
+// element). If the former is divisible by the latter, perhaps arr is
+// indeed an array, in which case the division result is the # of
+// elements in the array. Otherwise, arr cannot possibly be an array,
+// and we generate a compiler error to prevent the code from
+// compiling.
+//
+// Since the size of bool is implementation-defined, we need to cast
+// !(sizeof(a) & sizeof(*(a))) to size_t in order to ensure the final
+// result has type size_t.
+//
+// This macro is not perfect as it wrongfully accepts certain
+// pointers, namely where the pointer size is divisible by the pointee
+// size. Since all our code has to go through a 32-bit compiler,
+// where a pointer is 4 bytes, this means all pointers to a type whose
+// size is 3 or greater than 4 will be (righteously) rejected.
+#define ARRAYSIZE_UNSAFE(a) \
+ ((sizeof(a) / sizeof(*(a))) / \
+ static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
+
+#define LIKELY(x) __builtin_expect((x), true)
+#define UNLIKELY(x) __builtin_expect((x), false)
+
+#define WARN_UNUSED __attribute__((warn_unused_result))
+
+// A deprecated function to call to create a false use of the parameter, for
+// example:
+// int foo(int x) { UNUSED(x); return 10; }
+// to avoid compiler warnings. Going forward we prefer ATTRIBUTE_UNUSED.
+template <typename... T>
+void UNUSED(const T&...) {
+}
+
+// An attribute to place on a parameter to a function, for example:
+// int foo(int x ATTRIBUTE_UNUSED) { return 10; }
+// to avoid compiler warnings.
+#define ATTRIBUTE_UNUSED __attribute__((__unused__))
+
+// The FALLTHROUGH_INTENDED macro can be used to annotate implicit fall-through
+// between switch labels:
+// switch (x) {
+// case 40:
+// case 41:
+// if (truth_is_out_there) {
+// ++x;
+// FALLTHROUGH_INTENDED; // Use instead of/along with annotations in
+// // comments.
+// } else {
+// return x;
+// }
+// case 42:
+// ...
+//
+// As shown in the example above, the FALLTHROUGH_INTENDED macro should be
+// followed by a semicolon. It is designed to mimic control-flow statements
+// like 'break;', so it can be placed in most places where 'break;' can, but
+// only if there are no statements on the execution path between it and the
+// next switch label.
+//
+// When compiled with clang in C++11 mode, the FALLTHROUGH_INTENDED macro is
+// expanded to [[clang::fallthrough]] attribute, which is analysed when
+// performing switch labels fall-through diagnostic ('-Wimplicit-fallthrough').
+// See clang documentation on language extensions for details:
+// http://clang.llvm.org/docs/LanguageExtensions.html#clang__fallthrough
+//
+// When used with unsupported compilers, the FALLTHROUGH_INTENDED macro has no
+// effect on diagnostics.
+//
+// In either case this macro has no effect on runtime behavior and performance
+// of code.
+#if defined(__clang__) && __cplusplus >= 201103L && defined(__has_warning)
+#if __has_feature(cxx_attributes) && __has_warning("-Wimplicit-fallthrough")
+#define FALLTHROUGH_INTENDED [[clang::fallthrough]] // NOLINT
+#endif
+#endif
+
+#ifndef FALLTHROUGH_INTENDED
+#define FALLTHROUGH_INTENDED \
+ do { \
+ } while (0)
+#endif
+
+#endif // UTILS_MACROS_H
diff --git a/base/include/base/memory.h b/base/include/base/memory.h
new file mode 100644
index 0000000..882582f
--- /dev/null
+++ b/base/include/base/memory.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BASE_MEMORY_H
+#define BASE_MEMORY_H
+
+namespace android {
+namespace base {
+
+// Use packed structures for access to unaligned data on targets with alignment
+// restrictions. The compiler will generate appropriate code to access these
+// structures without generating alignment exceptions.
+template <typename T>
+static inline T get_unaligned(const T* address) {
+ struct unaligned {
+ T v;
+ } __attribute__((packed));
+ const unaligned* p = reinterpret_cast<const unaligned*>(address);
+ return p->v;
+}
+
+template <typename T>
+static inline void put_unaligned(T* address, T v) {
+ struct unaligned {
+ T v;
+ } __attribute__((packed));
+ unaligned* p = reinterpret_cast<unaligned*>(address);
+ p->v = v;
+}
+
+} // namespace base
+} // namespace android
+
+#endif // BASE_MEMORY_H
diff --git a/base/include/base/stringprintf.h b/base/include/base/stringprintf.h
new file mode 100644
index 0000000..d68af87
--- /dev/null
+++ b/base/include/base/stringprintf.h
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+#ifndef BASE_STRINGPRINTF_H
+#define BASE_STRINGPRINTF_H
+
+#include <stdarg.h>
+#include <string>
+
+namespace android {
+namespace base {
+
+// These printf-like functions are implemented in terms of vsnprintf, so they
+// use the same attribute for compile-time format string checking. On Windows,
+// if the mingw version of vsnprintf is used, use `gnu_printf' which allows z
+// in %zd and PRIu64 (and related) to be recognized by the compile-time
+// checking.
+#define FORMAT_ARCHETYPE __printf__
+#ifdef __USE_MINGW_ANSI_STDIO
+#if __USE_MINGW_ANSI_STDIO
+#undef FORMAT_ARCHETYPE
+#define FORMAT_ARCHETYPE gnu_printf
+#endif
+#endif
+
+// Returns a string corresponding to printf-like formatting of the arguments.
+std::string StringPrintf(const char* fmt, ...)
+ __attribute__((__format__(FORMAT_ARCHETYPE, 1, 2)));
+
+// Appends a printf-like formatting of the arguments to 'dst'.
+void StringAppendF(std::string* dst, const char* fmt, ...)
+ __attribute__((__format__(FORMAT_ARCHETYPE, 2, 3)));
+
+// Appends a printf-like formatting of the arguments to 'dst'.
+void StringAppendV(std::string* dst, const char* format, va_list ap)
+ __attribute__((__format__(FORMAT_ARCHETYPE, 2, 0)));
+
+#undef FORMAT_ARCHETYPE
+
+} // namespace base
+} // namespace android
+
+#endif // BASE_STRINGPRINTF_H
diff --git a/base/include/base/strings.h b/base/include/base/strings.h
new file mode 100644
index 0000000..638f845
--- /dev/null
+++ b/base/include/base/strings.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BASE_STRINGS_H
+#define BASE_STRINGS_H
+
+#include <sstream>
+#include <string>
+#include <vector>
+
+namespace android {
+namespace base {
+
+// Splits a string into a vector of strings.
+//
+// The string is split at each occurrence of a character in delimiters.
+//
+// The empty string is not a valid delimiter list.
+std::vector<std::string> Split(const std::string& s,
+ const std::string& delimiters);
+
+// Trims whitespace off both ends of the given string.
+std::string Trim(const std::string& s);
+
+// Joins a container of things into a single string, using the given separator.
+template <typename ContainerT>
+std::string Join(const ContainerT& things, char separator) {
+ if (things.empty()) {
+ return "";
+ }
+
+ std::ostringstream result;
+ result << *things.begin();
+ for (auto it = std::next(things.begin()); it != things.end(); ++it) {
+ result << separator << *it;
+ }
+ return result.str();
+}
+
+// We instantiate the common cases in strings.cpp.
+extern template std::string Join(const std::vector<std::string>&, char);
+extern template std::string Join(const std::vector<const char*>&, char);
+
+// Tests whether 's' starts with 'prefix'.
+bool StartsWith(const std::string& s, const char* prefix);
+
+// Tests whether 's' ends with 'suffix'.
+bool EndsWith(const std::string& s, const char* suffix);
+
+} // namespace base
+} // namespace android
+
+#endif // BASE_STRINGS_H
diff --git a/base/logging.cpp b/base/logging.cpp
new file mode 100644
index 0000000..34229a2
--- /dev/null
+++ b/base/logging.cpp
@@ -0,0 +1,375 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+#include "base/logging.h"
+
+#include <libgen.h>
+
+// For getprogname(3) or program_invocation_short_name.
+#if defined(__ANDROID__) || defined(__APPLE__)
+#include <stdlib.h>
+#elif defined(__GLIBC__)
+#include <errno.h>
+#endif
+
+#include <iostream>
+#include <limits>
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#ifndef _WIN32
+#include <mutex>
+#endif
+
+#include "base/macros.h"
+#include "base/strings.h"
+#include "cutils/threads.h"
+
+// Headers for LogMessage::LogLine.
+#ifdef __ANDROID__
+#include <android/set_abort_message.h>
+#include "cutils/log.h"
+#else
+#include <sys/types.h>
+#include <unistd.h>
+#endif
+
+namespace {
+#ifndef _WIN32
+using std::mutex;
+using std::lock_guard;
+
+#if defined(__GLIBC__)
+const char* getprogname() {
+ return program_invocation_short_name;
+}
+#endif
+
+#else
+const char* getprogname() {
+ static bool first = true;
+ static char progname[MAX_PATH] = {};
+
+ if (first) {
+ // TODO(danalbert): This is a full path on Windows. Just get the basename.
+ DWORD nchars = GetModuleFileName(nullptr, progname, sizeof(progname));
+ DCHECK_GT(nchars, 0U);
+ first = false;
+ }
+
+ return progname;
+}
+
+class mutex {
+ public:
+ mutex() {
+ semaphore_ = CreateSemaphore(nullptr, 1, 1, nullptr);
+ CHECK(semaphore_ != nullptr) << "Failed to create Mutex";
+ }
+ ~mutex() {
+ CloseHandle(semaphore_);
+ }
+
+ void lock() {
+ DWORD result = WaitForSingleObject(semaphore_, INFINITE);
+ CHECK_EQ(result, WAIT_OBJECT_0) << GetLastError();
+ }
+
+ void unlock() {
+ bool result = ReleaseSemaphore(semaphore_, 1, nullptr);
+ CHECK(result);
+ }
+
+ private:
+ HANDLE semaphore_;
+};
+
+template <typename LockT>
+class lock_guard {
+ public:
+ explicit lock_guard(LockT& lock) : lock_(lock) {
+ lock_.lock();
+ }
+
+ ~lock_guard() {
+ lock_.unlock();
+ }
+
+ private:
+ LockT& lock_;
+
+ DISALLOW_COPY_AND_ASSIGN(lock_guard);
+};
+#endif
+} // namespace
+
+namespace android {
+namespace base {
+
+static mutex logging_lock;
+
+#ifdef __ANDROID__
+static LogFunction gLogger = LogdLogger();
+#else
+static LogFunction gLogger = StderrLogger;
+#endif
+
+static bool gInitialized = false;
+static LogSeverity gMinimumLogSeverity = INFO;
+static std::unique_ptr<std::string> gProgramInvocationName;
+
+static const char* ProgramInvocationName() {
+ if (gProgramInvocationName == nullptr) {
+ gProgramInvocationName.reset(new std::string(getprogname()));
+ }
+
+ return gProgramInvocationName->c_str();
+}
+
+void StderrLogger(LogId, LogSeverity severity, const char*, const char* file,
+ unsigned int line, const char* message) {
+ static const char* log_characters = "VDIWEF";
+ CHECK_EQ(strlen(log_characters), FATAL + 1U);
+ char severity_char = log_characters[severity];
+ fprintf(stderr, "%s %c %5d %5d %s:%u] %s\n", ProgramInvocationName(),
+ severity_char, getpid(), gettid(), file, line, message);
+}
+
+
+#ifdef __ANDROID__
+LogdLogger::LogdLogger(LogId default_log_id) : default_log_id_(default_log_id) {
+}
+
+static const android_LogPriority kLogSeverityToAndroidLogPriority[] = {
+ ANDROID_LOG_VERBOSE, ANDROID_LOG_DEBUG, ANDROID_LOG_INFO,
+ ANDROID_LOG_WARN, ANDROID_LOG_ERROR, ANDROID_LOG_FATAL,
+};
+static_assert(arraysize(kLogSeverityToAndroidLogPriority) == FATAL + 1,
+ "Mismatch in size of kLogSeverityToAndroidLogPriority and values "
+ "in LogSeverity");
+
+static const log_id kLogIdToAndroidLogId[] = {
+ LOG_ID_MAX, LOG_ID_MAIN, LOG_ID_SYSTEM,
+};
+static_assert(arraysize(kLogIdToAndroidLogId) == SYSTEM + 1,
+ "Mismatch in size of kLogIdToAndroidLogId and values in LogId");
+
+void LogdLogger::operator()(LogId id, LogSeverity severity, const char* tag,
+ const char* file, unsigned int line,
+ const char* message) {
+ int priority = kLogSeverityToAndroidLogPriority[severity];
+ if (id == DEFAULT) {
+ id = default_log_id_;
+ }
+
+ log_id lg_id = kLogIdToAndroidLogId[id];
+
+ if (priority == ANDROID_LOG_FATAL) {
+ __android_log_buf_print(lg_id, priority, tag, "%s:%u] %s", file, line,
+ message);
+ } else {
+ __android_log_buf_print(lg_id, priority, tag, "%s", message);
+ }
+}
+#endif
+
+void InitLogging(char* argv[], LogFunction&& logger) {
+ SetLogger(std::forward<LogFunction>(logger));
+ InitLogging(argv);
+}
+
+void InitLogging(char* argv[]) {
+ if (gInitialized) {
+ return;
+ }
+
+ gInitialized = true;
+
+ // Stash the command line for later use. We can use /proc/self/cmdline on
+ // Linux to recover this, but we don't have that luxury on the Mac, and there
+ // are a couple of argv[0] variants that are commonly used.
+ if (argv != nullptr) {
+ gProgramInvocationName.reset(new std::string(basename(argv[0])));
+ }
+
+ const char* tags = getenv("ANDROID_LOG_TAGS");
+ if (tags == nullptr) {
+ return;
+ }
+
+ std::vector<std::string> specs = Split(tags, " ");
+ for (size_t i = 0; i < specs.size(); ++i) {
+ // "tag-pattern:[vdiwefs]"
+ std::string spec(specs[i]);
+ if (spec.size() == 3 && StartsWith(spec, "*:")) {
+ switch (spec[2]) {
+ case 'v':
+ gMinimumLogSeverity = VERBOSE;
+ continue;
+ case 'd':
+ gMinimumLogSeverity = DEBUG;
+ continue;
+ case 'i':
+ gMinimumLogSeverity = INFO;
+ continue;
+ case 'w':
+ gMinimumLogSeverity = WARNING;
+ continue;
+ case 'e':
+ gMinimumLogSeverity = ERROR;
+ continue;
+ case 'f':
+ gMinimumLogSeverity = FATAL;
+ continue;
+ // liblog will even suppress FATAL if you say 's' for silent, but that's
+ // crazy!
+ case 's':
+ gMinimumLogSeverity = FATAL;
+ continue;
+ }
+ }
+ LOG(FATAL) << "unsupported '" << spec << "' in ANDROID_LOG_TAGS (" << tags
+ << ")";
+ }
+}
+
+void SetLogger(LogFunction&& logger) {
+ lock_guard<mutex> lock(logging_lock);
+ gLogger = std::move(logger);
+}
+
+// This indirection greatly reduces the stack impact of having lots of
+// checks/logging in a function.
+class LogMessageData {
+ public:
+ LogMessageData(const char* file, unsigned int line, LogId id,
+ LogSeverity severity, int error)
+ : file_(file),
+ line_number_(line),
+ id_(id),
+ severity_(severity),
+ error_(error) {
+ const char* last_slash = strrchr(file, '/');
+ file = (last_slash == nullptr) ? file : last_slash + 1;
+ }
+
+ const char* GetFile() const {
+ return file_;
+ }
+
+ unsigned int GetLineNumber() const {
+ return line_number_;
+ }
+
+ LogSeverity GetSeverity() const {
+ return severity_;
+ }
+
+ LogId GetId() const {
+ return id_;
+ }
+
+ int GetError() const {
+ return error_;
+ }
+
+ std::ostream& GetBuffer() {
+ return buffer_;
+ }
+
+ std::string ToString() const {
+ return buffer_.str();
+ }
+
+ private:
+ std::ostringstream buffer_;
+ const char* const file_;
+ const unsigned int line_number_;
+ const LogId id_;
+ const LogSeverity severity_;
+ const int error_;
+
+ DISALLOW_COPY_AND_ASSIGN(LogMessageData);
+};
+
+LogMessage::LogMessage(const char* file, unsigned int line, LogId id,
+ LogSeverity severity, int error)
+ : data_(new LogMessageData(file, line, id, severity, error)) {
+}
+
+LogMessage::~LogMessage() {
+ if (data_->GetSeverity() < gMinimumLogSeverity) {
+ return; // No need to format something we're not going to output.
+ }
+
+ // Finish constructing the message.
+ if (data_->GetError() != -1) {
+ data_->GetBuffer() << ": " << strerror(data_->GetError());
+ }
+ std::string msg(data_->ToString());
+
+ if (msg.find('\n') == std::string::npos) {
+ LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetId(),
+ data_->GetSeverity(), msg.c_str());
+ } else {
+ msg += '\n';
+ size_t i = 0;
+ while (i < msg.size()) {
+ size_t nl = msg.find('\n', i);
+ msg[nl] = '\0';
+ LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetId(),
+ data_->GetSeverity(), &msg[i]);
+ i = nl + 1;
+ }
+ }
+
+ // Abort if necessary.
+ if (data_->GetSeverity() == FATAL) {
+#ifdef __ANDROID__
+ android_set_abort_message(msg.c_str());
+#endif
+ abort();
+ }
+}
+
+std::ostream& LogMessage::stream() {
+ return data_->GetBuffer();
+}
+
+void LogMessage::LogLine(const char* file, unsigned int line, LogId id,
+ LogSeverity severity, const char* message) {
+ const char* tag = ProgramInvocationName();
+ lock_guard<mutex> lock(logging_lock);
+ gLogger(id, severity, tag, file, line, message);
+}
+
+ScopedLogSeverity::ScopedLogSeverity(LogSeverity level) {
+ old_ = gMinimumLogSeverity;
+ gMinimumLogSeverity = level;
+}
+
+ScopedLogSeverity::~ScopedLogSeverity() {
+ gMinimumLogSeverity = old_;
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/logging_test.cpp b/base/logging_test.cpp
new file mode 100644
index 0000000..c91857a
--- /dev/null
+++ b/base/logging_test.cpp
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "base/logging.h"
+
+#include <regex>
+#include <string>
+
+#include "base/file.h"
+#include "base/stringprintf.h"
+#include "test_utils.h"
+
+#include <gtest/gtest.h>
+
+#ifdef __ANDROID__
+#define HOST_TEST(suite, name) TEST(suite, DISABLED_ ## name)
+#else
+#define HOST_TEST(suite, name) TEST(suite, name)
+#endif
+
+class CapturedStderr {
+ public:
+ CapturedStderr() : old_stderr_(-1) {
+ init();
+ }
+
+ ~CapturedStderr() {
+ reset();
+ }
+
+ int fd() const {
+ return temp_file_.fd;
+ }
+
+ private:
+ void init() {
+ old_stderr_ = dup(STDERR_FILENO);
+ ASSERT_NE(-1, old_stderr_);
+ ASSERT_NE(-1, dup2(fd(), STDERR_FILENO));
+ }
+
+ void reset() {
+ ASSERT_NE(-1, dup2(old_stderr_, STDERR_FILENO));
+ ASSERT_EQ(0, close(old_stderr_));
+ }
+
+ TemporaryFile temp_file_;
+ int old_stderr_;
+};
+
+TEST(logging, CHECK) {
+ ASSERT_DEATH(CHECK(false), "Check failed: false ");
+ CHECK(true);
+
+ ASSERT_DEATH(CHECK_EQ(0, 1), "Check failed: 0 == 1 ");
+ CHECK_EQ(0, 0);
+
+ ASSERT_DEATH(CHECK_STREQ("foo", "bar"), R"(Check failed: "foo" == "bar")");
+ CHECK_STREQ("foo", "foo");
+}
+
+std::string make_log_pattern(android::base::LogSeverity severity,
+ const char* message) {
+ static const char* log_characters = "VDIWEF";
+ char log_char = log_characters[severity];
+ return android::base::StringPrintf(
+ "%c[[:space:]]+[[:digit:]]+[[:space:]]+[[:digit:]]+ " __FILE__
+ ":[[:digit:]]+] %s",
+ log_char, message);
+}
+
+TEST(logging, LOG) {
+ ASSERT_DEATH(LOG(FATAL) << "foobar", "foobar");
+
+ // We can't usefully check the output of any of these on Windows because we
+ // don't have std::regex, but we can at least make sure we printed at least as
+ // many characters are in the log message.
+ {
+ CapturedStderr cap;
+ LOG(WARNING) << "foobar";
+ ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
+
+ std::string output;
+ android::base::ReadFdToString(cap.fd(), &output);
+ ASSERT_GT(output.length(), strlen("foobar"));
+
+#if !defined(_WIN32)
+ std::regex message_regex(
+ make_log_pattern(android::base::WARNING, "foobar"));
+ ASSERT_TRUE(std::regex_search(output, message_regex));
+#endif
+ }
+
+ {
+ CapturedStderr cap;
+ LOG(INFO) << "foobar";
+ ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
+
+ std::string output;
+ android::base::ReadFdToString(cap.fd(), &output);
+ ASSERT_GT(output.length(), strlen("foobar"));
+
+#if !defined(_WIN32)
+ std::regex message_regex(
+ make_log_pattern(android::base::INFO, "foobar"));
+ ASSERT_TRUE(std::regex_search(output, message_regex));
+#endif
+ }
+
+ {
+ CapturedStderr cap;
+ LOG(DEBUG) << "foobar";
+ ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
+
+ std::string output;
+ android::base::ReadFdToString(cap.fd(), &output);
+ ASSERT_TRUE(output.empty());
+ }
+
+ {
+ android::base::ScopedLogSeverity severity(android::base::DEBUG);
+ CapturedStderr cap;
+ LOG(DEBUG) << "foobar";
+ ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
+
+ std::string output;
+ android::base::ReadFdToString(cap.fd(), &output);
+ ASSERT_GT(output.length(), strlen("foobar"));
+
+#if !defined(_WIN32)
+ std::regex message_regex(
+ make_log_pattern(android::base::DEBUG, "foobar"));
+ ASSERT_TRUE(std::regex_search(output, message_regex));
+#endif
+ }
+}
+
+TEST(logging, PLOG) {
+ {
+ CapturedStderr cap;
+ errno = ENOENT;
+ PLOG(INFO) << "foobar";
+ ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
+
+ std::string output;
+ android::base::ReadFdToString(cap.fd(), &output);
+ ASSERT_GT(output.length(), strlen("foobar"));
+
+#if !defined(_WIN32)
+ std::regex message_regex(make_log_pattern(
+ android::base::INFO, "foobar: No such file or directory"));
+ ASSERT_TRUE(std::regex_search(output, message_regex));
+#endif
+ }
+}
+
+TEST(logging, UNIMPLEMENTED) {
+ {
+ CapturedStderr cap;
+ errno = ENOENT;
+ UNIMPLEMENTED(ERROR);
+ ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
+
+ std::string output;
+ android::base::ReadFdToString(cap.fd(), &output);
+ ASSERT_GT(output.length(), strlen("unimplemented"));
+
+#if !defined(_WIN32)
+ std::string expected_message =
+ android::base::StringPrintf("%s unimplemented ", __PRETTY_FUNCTION__);
+ std::regex message_regex(
+ make_log_pattern(android::base::ERROR, expected_message.c_str()));
+ ASSERT_TRUE(std::regex_search(output, message_regex));
+#endif
+ }
+}
diff --git a/base/stringprintf.cpp b/base/stringprintf.cpp
new file mode 100644
index 0000000..d55ff52
--- /dev/null
+++ b/base/stringprintf.cpp
@@ -0,0 +1,85 @@
+/*
+ * 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 "base/stringprintf.h"
+
+#include <stdio.h>
+
+#include <string>
+
+namespace android {
+namespace base {
+
+void StringAppendV(std::string* dst, const char* format, va_list ap) {
+ // First try with a small fixed size buffer
+ char space[1024];
+
+ // It's possible for methods that use a va_list to invalidate
+ // the data in it upon use. The fix is to make a copy
+ // of the structure before using it and use that copy instead.
+ va_list backup_ap;
+ va_copy(backup_ap, ap);
+ int result = vsnprintf(space, sizeof(space), format, backup_ap);
+ va_end(backup_ap);
+
+ if (result < static_cast<int>(sizeof(space))) {
+ if (result >= 0) {
+ // Normal case -- everything fit.
+ dst->append(space, result);
+ return;
+ }
+
+ if (result < 0) {
+ // Just an error.
+ return;
+ }
+ }
+
+ // Increase the buffer size to the size requested by vsnprintf,
+ // plus one for the closing \0.
+ int length = result + 1;
+ char* buf = new char[length];
+
+ // Restore the va_list before we use it again
+ va_copy(backup_ap, ap);
+ result = vsnprintf(buf, length, format, backup_ap);
+ va_end(backup_ap);
+
+ if (result >= 0 && result < length) {
+ // It fit
+ dst->append(buf, result);
+ }
+ delete[] buf;
+}
+
+std::string StringPrintf(const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ std::string result;
+ StringAppendV(&result, fmt, ap);
+ va_end(ap);
+ return result;
+}
+
+void StringAppendF(std::string* dst, const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ StringAppendV(dst, format, ap);
+ va_end(ap);
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/stringprintf_test.cpp b/base/stringprintf_test.cpp
new file mode 100644
index 0000000..54b2b6c
--- /dev/null
+++ b/base/stringprintf_test.cpp
@@ -0,0 +1,63 @@
+/*
+ * 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 "base/stringprintf.h"
+
+#include <gtest/gtest.h>
+
+#include <string>
+
+// The z size sepcifier isn't supported on Windows, so this test isn't useful.
+#if !defined(_WIN32)
+TEST(StringPrintfTest, HexSizeT) {
+ size_t size = 0x00107e59;
+ EXPECT_EQ("00107e59", android::base::StringPrintf("%08zx", size));
+ EXPECT_EQ("0x00107e59", android::base::StringPrintf("0x%08zx", size));
+}
+#endif
+
+TEST(StringPrintfTest, StringAppendF) {
+ std::string s("a");
+ android::base::StringAppendF(&s, "b");
+ EXPECT_EQ("ab", s);
+}
+
+TEST(StringPrintfTest, Errno) {
+ errno = 123;
+ android::base::StringPrintf("hello %s", "world");
+ EXPECT_EQ(123, errno);
+}
+
+void TestN(size_t n) {
+ char* buf = new char[n + 1];
+ memset(buf, 'x', n);
+ buf[n] = '\0';
+ std::string s(android::base::StringPrintf("%s", buf));
+ EXPECT_EQ(buf, s);
+ delete[] buf;
+}
+
+TEST(StringPrintfTest, At1023) {
+ TestN(1023);
+}
+
+TEST(StringPrintfTest, At1024) {
+ TestN(1024);
+}
+
+TEST(StringPrintfTest, At1025) {
+ TestN(1025);
+}
diff --git a/base/strings.cpp b/base/strings.cpp
new file mode 100644
index 0000000..bac983b
--- /dev/null
+++ b/base/strings.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "base/strings.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <string>
+#include <vector>
+
+namespace android {
+namespace base {
+
+#define CHECK_NE(a, b) \
+ if ((a) == (b)) abort();
+
+std::vector<std::string> Split(const std::string& s,
+ const std::string& delimiters) {
+ CHECK_NE(delimiters.size(), 0U);
+
+ std::vector<std::string> result;
+
+ size_t base = 0;
+ size_t found;
+ do {
+ found = s.find_first_of(delimiters, base);
+ result.push_back(s.substr(base, found - base));
+ base = found + 1;
+ } while (found != s.npos);
+
+ return result;
+}
+
+std::string Trim(const std::string& s) {
+ std::string result;
+
+ if (s.size() == 0) {
+ return result;
+ }
+
+ size_t start_index = 0;
+ size_t end_index = s.size() - 1;
+
+ // Skip initial whitespace.
+ while (start_index < s.size()) {
+ if (!isspace(s[start_index])) {
+ break;
+ }
+ start_index++;
+ }
+
+ // Skip terminating whitespace.
+ while (end_index >= start_index) {
+ if (!isspace(s[end_index])) {
+ break;
+ }
+ end_index--;
+ }
+
+ // All spaces, no beef.
+ if (end_index < start_index) {
+ return "";
+ }
+ // Start_index is the first non-space, end_index is the last one.
+ return s.substr(start_index, end_index - start_index + 1);
+}
+
+// These cases are probably the norm, so we mark them extern in the header to
+// aid compile time and binary size.
+template std::string Join(const std::vector<std::string>&, char);
+template std::string Join(const std::vector<const char*>&, char);
+
+bool StartsWith(const std::string& s, const char* prefix) {
+ return s.compare(0, strlen(prefix), prefix) == 0;
+}
+
+bool EndsWith(const std::string& s, const char* suffix) {
+ size_t suffix_length = strlen(suffix);
+ size_t string_length = s.size();
+ if (suffix_length > string_length) {
+ return false;
+ }
+ size_t offset = string_length - suffix_length;
+ return s.compare(offset, suffix_length, suffix) == 0;
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/strings_test.cpp b/base/strings_test.cpp
new file mode 100644
index 0000000..5f67575
--- /dev/null
+++ b/base/strings_test.cpp
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "base/strings.h"
+
+#include <gtest/gtest.h>
+
+#include <string>
+#include <vector>
+#include <set>
+#include <unordered_set>
+
+TEST(strings, split_empty) {
+ std::vector<std::string> parts = android::base::Split("", ",");
+ ASSERT_EQ(1U, parts.size());
+ ASSERT_EQ("", parts[0]);
+}
+
+TEST(strings, split_single) {
+ std::vector<std::string> parts = android::base::Split("foo", ",");
+ ASSERT_EQ(1U, parts.size());
+ ASSERT_EQ("foo", parts[0]);
+}
+
+TEST(strings, split_simple) {
+ std::vector<std::string> parts = android::base::Split("foo,bar,baz", ",");
+ ASSERT_EQ(3U, parts.size());
+ ASSERT_EQ("foo", parts[0]);
+ ASSERT_EQ("bar", parts[1]);
+ ASSERT_EQ("baz", parts[2]);
+}
+
+TEST(strings, split_with_empty_part) {
+ std::vector<std::string> parts = android::base::Split("foo,,bar", ",");
+ ASSERT_EQ(3U, parts.size());
+ ASSERT_EQ("foo", parts[0]);
+ ASSERT_EQ("", parts[1]);
+ ASSERT_EQ("bar", parts[2]);
+}
+
+TEST(strings, split_null_char) {
+ std::vector<std::string> parts =
+ android::base::Split(std::string("foo\0bar", 7), std::string("\0", 1));
+ ASSERT_EQ(2U, parts.size());
+ ASSERT_EQ("foo", parts[0]);
+ ASSERT_EQ("bar", parts[1]);
+}
+
+TEST(strings, split_any) {
+ std::vector<std::string> parts = android::base::Split("foo:bar,baz", ",:");
+ ASSERT_EQ(3U, parts.size());
+ ASSERT_EQ("foo", parts[0]);
+ ASSERT_EQ("bar", parts[1]);
+ ASSERT_EQ("baz", parts[2]);
+}
+
+TEST(strings, split_any_with_empty_part) {
+ std::vector<std::string> parts = android::base::Split("foo:,bar", ",:");
+ ASSERT_EQ(3U, parts.size());
+ ASSERT_EQ("foo", parts[0]);
+ ASSERT_EQ("", parts[1]);
+ ASSERT_EQ("bar", parts[2]);
+}
+
+TEST(strings, trim_empty) {
+ ASSERT_EQ("", android::base::Trim(""));
+}
+
+TEST(strings, trim_already_trimmed) {
+ ASSERT_EQ("foo", android::base::Trim("foo"));
+}
+
+TEST(strings, trim_left) {
+ ASSERT_EQ("foo", android::base::Trim(" foo"));
+}
+
+TEST(strings, trim_right) {
+ ASSERT_EQ("foo", android::base::Trim("foo "));
+}
+
+TEST(strings, trim_both) {
+ ASSERT_EQ("foo", android::base::Trim(" foo "));
+}
+
+TEST(strings, trim_no_trim_middle) {
+ ASSERT_EQ("foo bar", android::base::Trim("foo bar"));
+}
+
+TEST(strings, trim_other_whitespace) {
+ ASSERT_EQ("foo", android::base::Trim("\v\tfoo\n\f"));
+}
+
+TEST(strings, join_nothing) {
+ std::vector<std::string> list = {};
+ ASSERT_EQ("", android::base::Join(list, ','));
+}
+
+TEST(strings, join_single) {
+ std::vector<std::string> list = {"foo"};
+ ASSERT_EQ("foo", android::base::Join(list, ','));
+}
+
+TEST(strings, join_simple) {
+ std::vector<std::string> list = {"foo", "bar", "baz"};
+ ASSERT_EQ("foo,bar,baz", android::base::Join(list, ','));
+}
+
+TEST(strings, join_separator_in_vector) {
+ std::vector<std::string> list = {",", ","};
+ ASSERT_EQ(",,,", android::base::Join(list, ','));
+}
+
+TEST(strings, join_simple_ints) {
+ std::set<int> list = {1, 2, 3};
+ ASSERT_EQ("1,2,3", android::base::Join(list, ','));
+}
+
+TEST(strings, join_unordered_set) {
+ std::unordered_set<int> list = {1, 2};
+ ASSERT_TRUE("1,2" == android::base::Join(list, ',') ||
+ "2,1" == android::base::Join(list, ','));
+}
+
+TEST(strings, startswith_empty) {
+ ASSERT_FALSE(android::base::StartsWith("", "foo"));
+ ASSERT_TRUE(android::base::StartsWith("", ""));
+}
+
+TEST(strings, startswith_simple) {
+ ASSERT_TRUE(android::base::StartsWith("foo", ""));
+ ASSERT_TRUE(android::base::StartsWith("foo", "f"));
+ ASSERT_TRUE(android::base::StartsWith("foo", "fo"));
+ ASSERT_TRUE(android::base::StartsWith("foo", "foo"));
+}
+
+TEST(strings, startswith_prefix_too_long) {
+ ASSERT_FALSE(android::base::StartsWith("foo", "foobar"));
+}
+
+TEST(strings, startswith_contains_prefix) {
+ ASSERT_FALSE(android::base::StartsWith("foobar", "oba"));
+ ASSERT_FALSE(android::base::StartsWith("foobar", "bar"));
+}
+
+TEST(strings, endswith_empty) {
+ ASSERT_FALSE(android::base::EndsWith("", "foo"));
+ ASSERT_TRUE(android::base::EndsWith("", ""));
+}
+
+TEST(strings, endswith_simple) {
+ ASSERT_TRUE(android::base::EndsWith("foo", ""));
+ ASSERT_TRUE(android::base::EndsWith("foo", "o"));
+ ASSERT_TRUE(android::base::EndsWith("foo", "oo"));
+ ASSERT_TRUE(android::base::EndsWith("foo", "foo"));
+}
+
+TEST(strings, endswith_prefix_too_long) {
+ ASSERT_FALSE(android::base::EndsWith("foo", "foobar"));
+}
+
+TEST(strings, endswith_contains_prefix) {
+ ASSERT_FALSE(android::base::EndsWith("foobar", "oba"));
+ ASSERT_FALSE(android::base::EndsWith("foobar", "foo"));
+}
diff --git a/base/test_main.cpp b/base/test_main.cpp
new file mode 100644
index 0000000..546923d
--- /dev/null
+++ b/base/test_main.cpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "base/logging.h"
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ android::base::InitLogging(argv, android::base::StderrLogger);
+ return RUN_ALL_TESTS();
+}
diff --git a/base/test_utils.cpp b/base/test_utils.cpp
new file mode 100644
index 0000000..0517bc7
--- /dev/null
+++ b/base/test_utils.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "test_utils.h"
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#if defined(_WIN32)
+#include <windows.h>
+#endif
+
+TemporaryFile::TemporaryFile() {
+#if defined(__ANDROID__)
+ init("/data/local/tmp");
+#elif defined(_WIN32)
+ char wd[MAX_PATH] = {};
+ _getcwd(wd, sizeof(wd));
+ init(wd);
+#else
+ init("/tmp");
+#endif
+}
+
+TemporaryFile::~TemporaryFile() {
+ close(fd);
+ unlink(filename);
+}
+
+void TemporaryFile::init(const char* tmp_dir) {
+ snprintf(filename, sizeof(filename), "%s/TemporaryFile-XXXXXX", tmp_dir);
+#if !defined(_WIN32)
+ fd = mkstemp(filename);
+#else
+ // Windows doesn't have mkstemp, and tmpfile creates the file in the root
+ // directory, requiring root (?!) permissions. We have to settle for mktemp.
+ if (mktemp(filename) == nullptr) {
+ abort();
+ }
+
+ fd = open(filename, O_RDWR | O_NOINHERIT | O_CREAT, _S_IREAD | _S_IWRITE);
+#endif
+}
diff --git a/base/test_utils.h b/base/test_utils.h
new file mode 100644
index 0000000..132d3a7
--- /dev/null
+++ b/base/test_utils.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TEST_UTILS_H
+#define TEST_UTILS_H
+
+class TemporaryFile {
+ public:
+ TemporaryFile();
+ ~TemporaryFile();
+
+ int fd;
+ char filename[1024];
+
+ private:
+ void init(const char* tmp_dir);
+};
+
+#endif // TEST_UTILS_H
diff --git a/cpio/Android.mk b/cpio/Android.mk
new file mode 100644
index 0000000..2aa7297
--- /dev/null
+++ b/cpio/Android.mk
@@ -0,0 +1,17 @@
+# Copyright 2005 The Android Open Source Project
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ mkbootfs.c
+
+LOCAL_MODULE := mkbootfs
+
+LOCAL_CFLAGS := -Werror
+
+LOCAL_SHARED_LIBRARIES := libcutils
+
+include $(BUILD_HOST_EXECUTABLE)
+
+$(call dist-for-goals,dist_files,$(LOCAL_BUILT_MODULE))
diff --git a/cpio/mkbootfs.c b/cpio/mkbootfs.c
new file mode 100644
index 0000000..7175749
--- /dev/null
+++ b/cpio/mkbootfs.c
@@ -0,0 +1,355 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+
+#include <stdarg.h>
+#include <fcntl.h>
+
+#include <private/android_filesystem_config.h>
+
+/* NOTES
+**
+** - see buffer-format.txt from the linux kernel docs for
+** an explanation of this file format
+** - dotfiles are ignored
+** - directories named 'root' are ignored
+** - device notes, pipes, etc are not supported (error)
+*/
+
+void die(const char *why, ...)
+{
+ va_list ap;
+
+ va_start(ap, why);
+ fprintf(stderr,"error: ");
+ vfprintf(stderr, why, ap);
+ fprintf(stderr,"\n");
+ va_end(ap);
+ exit(1);
+}
+
+struct fs_config_entry {
+ char* name;
+ int uid, gid, mode;
+};
+
+static struct fs_config_entry* canned_config = NULL;
+
+/* Each line in the canned file should be a path plus three ints (uid,
+ * gid, mode). */
+#ifdef PATH_MAX
+#define CANNED_LINE_LENGTH (PATH_MAX+100)
+#else
+#define CANNED_LINE_LENGTH (1024)
+#endif
+
+static int verbose = 0;
+static int total_size = 0;
+
+static void fix_stat(const char *path, struct stat *s)
+{
+ uint64_t capabilities;
+ if (canned_config) {
+ // Use the list of file uid/gid/modes loaded from the file
+ // given with -f.
+
+ struct fs_config_entry* empty_path_config = NULL;
+ struct fs_config_entry* p;
+ for (p = canned_config; p->name; ++p) {
+ if (!p->name[0]) {
+ empty_path_config = p;
+ }
+ if (strcmp(p->name, path) == 0) {
+ s->st_uid = p->uid;
+ s->st_gid = p->gid;
+ s->st_mode = p->mode | (s->st_mode & ~07777);
+ return;
+ }
+ }
+ s->st_uid = empty_path_config->uid;
+ s->st_gid = empty_path_config->gid;
+ s->st_mode = empty_path_config->mode | (s->st_mode & ~07777);
+ } else {
+ // Use the compiled-in fs_config() function.
+ unsigned st_mode = s->st_mode;
+ fs_config(path, S_ISDIR(s->st_mode), &s->st_uid, &s->st_gid, &st_mode, &capabilities);
+ s->st_mode = (typeof(s->st_mode)) st_mode;
+ }
+}
+
+static void _eject(struct stat *s, char *out, int olen, char *data, unsigned datasize)
+{
+ // Nothing is special about this value, just picked something in the
+ // approximate range that was being used already, and avoiding small
+ // values which may be special.
+ static unsigned next_inode = 300000;
+
+ while(total_size & 3) {
+ total_size++;
+ putchar(0);
+ }
+
+ fix_stat(out, s);
+// fprintf(stderr, "_eject %s: mode=0%o\n", out, s->st_mode);
+
+ printf("%06x%08x%08x%08x%08x%08x%08x"
+ "%08x%08x%08x%08x%08x%08x%08x%s%c",
+ 0x070701,
+ next_inode++, // s.st_ino,
+ s->st_mode,
+ 0, // s.st_uid,
+ 0, // s.st_gid,
+ 1, // s.st_nlink,
+ 0, // s.st_mtime,
+ datasize,
+ 0, // volmajor
+ 0, // volminor
+ 0, // devmajor
+ 0, // devminor,
+ olen + 1,
+ 0,
+ out,
+ 0
+ );
+
+ total_size += 6 + 8*13 + olen + 1;
+
+ if(strlen(out) != (unsigned int)olen) die("ACK!");
+
+ while(total_size & 3) {
+ total_size++;
+ putchar(0);
+ }
+
+ if(datasize) {
+ fwrite(data, datasize, 1, stdout);
+ total_size += datasize;
+ }
+}
+
+static void _eject_trailer()
+{
+ struct stat s;
+ memset(&s, 0, sizeof(s));
+ _eject(&s, "TRAILER!!!", 10, 0, 0);
+
+ while(total_size & 0xff) {
+ total_size++;
+ putchar(0);
+ }
+}
+
+static void _archive(char *in, char *out, int ilen, int olen);
+
+static int compare(const void* a, const void* b) {
+ return strcmp(*(const char**)a, *(const char**)b);
+}
+
+static void _archive_dir(char *in, char *out, int ilen, int olen)
+{
+ int i, t;
+ DIR *d;
+ struct dirent *de;
+
+ if(verbose) {
+ fprintf(stderr,"_archive_dir('%s','%s',%d,%d)\n",
+ in, out, ilen, olen);
+ }
+
+ d = opendir(in);
+ if(d == 0) die("cannot open directory '%s'", in);
+
+ int size = 32;
+ int entries = 0;
+ char** names = malloc(size * sizeof(char*));
+ if (names == NULL) {
+ fprintf(stderr, "failed to allocate dir names array (size %d)\n", size);
+ exit(1);
+ }
+
+ while((de = readdir(d)) != 0){
+ /* xxx: feature? maybe some dotfiles are okay */
+ if(de->d_name[0] == '.') continue;
+
+ /* xxx: hack. use a real exclude list */
+ if(!strcmp(de->d_name, "root")) continue;
+
+ if (entries >= size) {
+ size *= 2;
+ names = realloc(names, size * sizeof(char*));
+ if (names == NULL) {
+ fprintf(stderr, "failed to reallocate dir names array (size %d)\n",
+ size);
+ exit(1);
+ }
+ }
+ names[entries] = strdup(de->d_name);
+ if (names[entries] == NULL) {
+ fprintf(stderr, "failed to strdup name \"%s\"\n",
+ de->d_name);
+ exit(1);
+ }
+ ++entries;
+ }
+
+ qsort(names, entries, sizeof(char*), compare);
+
+ for (i = 0; i < entries; ++i) {
+ t = strlen(names[i]);
+ in[ilen] = '/';
+ memcpy(in + ilen + 1, names[i], t + 1);
+
+ if(olen > 0) {
+ out[olen] = '/';
+ memcpy(out + olen + 1, names[i], t + 1);
+ _archive(in, out, ilen + t + 1, olen + t + 1);
+ } else {
+ memcpy(out, names[i], t + 1);
+ _archive(in, out, ilen + t + 1, t);
+ }
+
+ in[ilen] = 0;
+ out[olen] = 0;
+
+ free(names[i]);
+ }
+ free(names);
+
+ closedir(d);
+}
+
+static void _archive(char *in, char *out, int ilen, int olen)
+{
+ struct stat s;
+
+ if(verbose) {
+ fprintf(stderr,"_archive('%s','%s',%d,%d)\n",
+ in, out, ilen, olen);
+ }
+
+ if(lstat(in, &s)) die("could not stat '%s'\n", in);
+
+ if(S_ISREG(s.st_mode)){
+ char *tmp;
+ int fd;
+
+ fd = open(in, O_RDONLY);
+ if(fd < 0) die("cannot open '%s' for read", in);
+
+ tmp = (char*) malloc(s.st_size);
+ if(tmp == 0) die("cannot allocate %d bytes", s.st_size);
+
+ if(read(fd, tmp, s.st_size) != s.st_size) {
+ die("cannot read %d bytes", s.st_size);
+ }
+
+ _eject(&s, out, olen, tmp, s.st_size);
+
+ free(tmp);
+ close(fd);
+ } else if(S_ISDIR(s.st_mode)) {
+ _eject(&s, out, olen, 0, 0);
+ _archive_dir(in, out, ilen, olen);
+ } else if(S_ISLNK(s.st_mode)) {
+ char buf[1024];
+ int size;
+ size = readlink(in, buf, 1024);
+ if(size < 0) die("cannot read symlink '%s'", in);
+ _eject(&s, out, olen, buf, size);
+ } else {
+ die("Unknown '%s' (mode %d)?\n", in, s.st_mode);
+ }
+}
+
+void archive(const char *start, const char *prefix)
+{
+ char in[8192];
+ char out[8192];
+
+ strcpy(in, start);
+ strcpy(out, prefix);
+
+ _archive_dir(in, out, strlen(in), strlen(out));
+}
+
+static void read_canned_config(char* filename)
+{
+ int allocated = 8;
+ int used = 0;
+
+ canned_config =
+ (struct fs_config_entry*)malloc(allocated * sizeof(struct fs_config_entry));
+
+ char line[CANNED_LINE_LENGTH];
+ FILE* f = fopen(filename, "r");
+ if (f == NULL) die("failed to open canned file");
+
+ while (fgets(line, CANNED_LINE_LENGTH, f) != NULL) {
+ if (!line[0]) break;
+ if (used >= allocated) {
+ allocated *= 2;
+ canned_config = (struct fs_config_entry*)realloc(
+ canned_config, allocated * sizeof(struct fs_config_entry));
+ }
+
+ struct fs_config_entry* cc = canned_config + used;
+
+ if (isspace(line[0])) {
+ cc->name = strdup("");
+ cc->uid = atoi(strtok(line, " \n"));
+ } else {
+ cc->name = strdup(strtok(line, " \n"));
+ cc->uid = atoi(strtok(NULL, " \n"));
+ }
+ cc->gid = atoi(strtok(NULL, " \n"));
+ cc->mode = strtol(strtok(NULL, " \n"), NULL, 8);
+ ++used;
+ }
+ if (used >= allocated) {
+ ++allocated;
+ canned_config = (struct fs_config_entry*)realloc(
+ canned_config, allocated * sizeof(struct fs_config_entry));
+ }
+ canned_config[used].name = NULL;
+
+ fclose(f);
+}
+
+
+int main(int argc, char *argv[])
+{
+ argc--;
+ argv++;
+
+ if (argc > 1 && strcmp(argv[0], "-f") == 0) {
+ read_canned_config(argv[1]);
+ argc -= 2;
+ argv += 2;
+ }
+
+ if(argc == 0) die("no directories to process?!");
+
+ while(argc-- > 0){
+ char *x = strchr(*argv, '=');
+ if(x != 0) {
+ *x++ = 0;
+ } else {
+ x = "";
+ }
+
+ archive(*argv, x);
+
+ argv++;
+ }
+
+ _eject_trailer();
+
+ return 0;
+}
diff --git a/crash_reporter/.project_alias b/crash_reporter/.project_alias
new file mode 100644
index 0000000..0bc3798
--- /dev/null
+++ b/crash_reporter/.project_alias
@@ -0,0 +1 @@
+crash
diff --git a/crash_reporter/99-crash-reporter.rules b/crash_reporter/99-crash-reporter.rules
new file mode 100644
index 0000000..aea5b1c
--- /dev/null
+++ b/crash_reporter/99-crash-reporter.rules
@@ -0,0 +1,6 @@
+ACTION=="change", SUBSYSTEM=="drm", KERNEL=="card0", ENV{ERROR}=="1", RUN+="/sbin/crash_reporter --udev=KERNEL=card0:SUBSYSTEM=drm:ACTION=change"
+# For detecting cypress trackpad issue. Passing into crash_reporter SUBSYSTEM=i2c-cyapa since crash_reporter does not handle DRIVER string.
+ACTION=="change", SUBSYSTEM=="i2c", DRIVER=="cyapa", ENV{ERROR}=="1", RUN+="/sbin/crash_reporter --udev=SUBSYSTEM=i2c-cyapa:ACTION=change"
+# For detecting Atmel trackpad/touchscreen issue. Passing into crash_reporter SUBSYSTEM=i2c-atmel_mxt_ts since crash_reporter does not handle DRIVER string.
+ACTION=="change", SUBSYSTEM=="i2c", DRIVER=="atmel_mxt_ts", ENV{ERROR}=="1", RUN+="/sbin/crash_reporter --udev=SUBSYSTEM=i2c-atmel_mxt_ts:ACTION=change"
+ACTION=="add", SUBSYSTEM=="devcoredump", RUN+="/sbin/crash_reporter --udev=SUBSYSTEM=devcoredump:ACTION=add:KERNEL_NUMBER=%n"
diff --git a/crash_reporter/OWNERS b/crash_reporter/OWNERS
new file mode 100644
index 0000000..96ea5b2
--- /dev/null
+++ b/crash_reporter/OWNERS
@@ -0,0 +1,2 @@
+set noparent
+vapier@chromium.org
diff --git a/crash_reporter/TEST_WARNING b/crash_reporter/TEST_WARNING
new file mode 100644
index 0000000..64ad2e9
--- /dev/null
+++ b/crash_reporter/TEST_WARNING
@@ -0,0 +1,31 @@
+Apr 31 25:25:25 localhost kernel: [117959.226729] [<ffffffff810e16bf>] do_vfs_ioctl+0x469/0x4b3
+Apr 31 25:25:25 localhost kernel: [117959.226738] [<ffffffff810d3117>] ? fsnotify_access+0x58/0x60
+Apr 31 25:25:25 localhost kernel: [117959.226747] [<ffffffff810d3791>] ? vfs_read+0xad/0xd7
+Apr 31 25:25:25 localhost kernel: [117959.226756] [<ffffffff810e175f>] sys_ioctl+0x56/0x7b
+Apr 31 25:25:25 localhost kernel: [117959.226765] [<ffffffff810d37fe>] ? sys_read+0x43/0x73
+Apr 31 25:25:25 localhost kernel: [117959.226774] [<ffffffff8146b7d2>] system_call_fastpath+0x16/0x1b
+Apr 31 25:25:25 localhost kernel: [117959.226782] ---[ end trace f16822cad7406cec ]---
+Apr 31 25:25:25 localhost kernel: [117959.231085] ------------[ cut here ]------------
+Apr 31 25:25:25 localhost kernel: [117959.231100] WARNING: at /mnt/host/source/src/third_party/kernel/files/drivers/gpu/drm/i915/intel_dp.c:351 intel_dp_check_edp+0x6b/0xb9()
+Apr 31 25:25:25 localhost kernel: [117959.231113] Hardware name: Link
+Apr 31 25:25:25 localhost kernel: [117959.231117] eDP powered off while attempting aux channel communication.
+Apr 31 25:25:25 localhost kernel: [117959.231240] Pid: 10508, comm: X Tainted: G WC 3.4.0 #1
+Apr 31 25:25:25 localhost kernel: [117959.231247] Call Trace:
+Apr 31 25:25:25 localhost kernel: [117959.231393] [<ffffffff810d3117>] ? fsnotify_access+0x58/0x60
+Apr 31 25:25:25 localhost kernel: [117959.231402] [<ffffffff810d3791>] ? vfs_read+0xad/0xd7
+Apr 31 25:25:25 localhost kernel: [117959.231411] [<ffffffff810e175f>] sys_ioctl+0x56/0x7b
+Apr 31 25:25:25 localhost kernel: [117959.231420] [<ffffffff810d37fe>] ? sys_read+0x43/0x73
+Apr 31 25:25:25 localhost kernel: [117959.231431] [<ffffffff8146b7d2>] system_call_fastpath+0x16/0x1b
+Apr 31 25:25:25 localhost kernel: [117959.231439] ---[ end trace f16822cad7406ced ]---
+Apr 31 25:25:25 localhost kernel: [117959.231450] ------------[ cut here ]------------
+Apr 31 25:25:25 localhost kernel: [117959.231458] BARNING: at /mnt/host/source/src/third_party/kernel/files/drivers/gpu/drm/i915/intel_dp.c:351 intel_dp_check_edp+0x6b/0xb9()
+Apr 31 25:25:25 localhost kernel: [117959.231458] ("BARNING" above is intentional)
+Apr 31 25:25:25 localhost kernel: [117959.231471] Hardware name: Link
+Apr 31 25:25:25 localhost kernel: [117959.231475] eDP powered off while attempting aux channel communication.
+Apr 31 25:25:25 localhost kernel: [117959.231482] Modules linked in: nls_iso8859_1 nls_cp437 vfat fat rfcomm i2c_dev ath9k_btcoex snd_hda_codec_hdmi snd_hda_codec_ca0132 mac80211 snd_hda_intel ath9k_common_btcoex snd_hda_codec ath9k_hw_btcoex aesni_intel cryptd snd_hwdep ath snd_pcm aes_x86_64 isl29018(C) memconsole snd_timer snd_page_alloc industrialio(C) cfg80211 rtc_cmos nm10_gpio zram(C) zsmalloc(C) lzo_decompress lzo_compress fuse nf_conntrack_ipv6 nf_defrag_ipv6 ip6table_filter ip6_tables xt_mark option usb_wwan cdc_ether usbnet ath3k btusb bluetooth uvcvideo videobuf2_core videodev videobuf2_vmalloc videobuf2_memops joydev
+Apr 31 25:25:25 localhost kernel: [117959.231588] Pid: 10508, comm: X Tainted: G WC 3.4.0 #1
+Apr 31 25:25:25 localhost kernel: [117959.231595] Call Trace:
+Apr 31 25:25:25 localhost kernel: [117959.231601] [<ffffffff8102a931>] warn_slowpath_common+0x83/0x9c
+Apr 31 25:25:25 localhost kernel: [117959.231610] [<ffffffff8102a9ed>] warn_slowpath_fmt+0x46/0x48
+Apr 31 25:25:25 localhost kernel: [117959.231620] [<ffffffff812af495>] intel_dp_check_edp+0x6b/0xb9
+Apr 31 25:25:25 localhost kernel: [117959.231629] [<ffffffff8102a9ed>] ? warn_slowpath_fmt+
diff --git a/crash_reporter/chrome_collector.cc b/crash_reporter/chrome_collector.cc
new file mode 100644
index 0000000..ec291c0
--- /dev/null
+++ b/crash_reporter/chrome_collector.cc
@@ -0,0 +1,335 @@
+// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crash-reporter/chrome_collector.h"
+
+#include <pcrecpp.h>
+#include <stdint.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include <base/files/file_util.h>
+#include <base/logging.h>
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/string_util.h>
+#include <chromeos/data_encoding.h>
+#include <chromeos/dbus/service_constants.h>
+#include <chromeos/process.h>
+#include <chromeos/syslog_logging.h>
+
+using base::FilePath;
+using base::StringPrintf;
+
+namespace {
+
+const char kDefaultMinidumpName[] = "upload_file_minidump";
+
+// Path to the gzip binary.
+const char kGzipPath[] = "/bin/gzip";
+
+// Filenames for logs attached to crash reports. Also used as metadata keys.
+const char kChromeLogFilename[] = "chrome.txt";
+const char kGpuStateFilename[] = "i915_error_state.log.xz";
+
+// From //net/crash/collector/collector.h
+const int kDefaultMaxUploadBytes = 1024 * 1024;
+
+// Extract a string delimited by the given character, from the given offset
+// into a source string. Returns false if the string is zero-sized or no
+// delimiter was found.
+bool GetDelimitedString(const std::string &str, char ch, size_t offset,
+ std::string *substr) {
+ size_t at = str.find_first_of(ch, offset);
+ if (at == std::string::npos || at == offset)
+ return false;
+ *substr = str.substr(offset, at - offset);
+ return true;
+}
+
+// Gets the GPU's error state from debugd and writes it to |error_state_path|.
+// Returns true on success.
+bool GetDriErrorState(const FilePath &error_state_path,
+ org::chromium::debugdProxy *proxy) {
+ chromeos::ErrorPtr error;
+ std::string error_state_str;
+
+ proxy->GetLog("i915_error_state", &error_state_str, &error);
+
+ if (error) {
+ LOG(ERROR) << "Error calling D-Bus proxy call to interface "
+ << "'" << proxy->GetObjectPath().value() << "':"
+ << error->GetMessage();
+ return false;
+ }
+
+ if (error_state_str == "<empty>")
+ return false;
+
+ const char kBase64Header[] = "<base64>: ";
+ const size_t kBase64HeaderLength = sizeof(kBase64Header) - 1;
+ if (error_state_str.compare(0, kBase64HeaderLength, kBase64Header)) {
+ LOG(ERROR) << "i915_error_state is missing base64 header";
+ return false;
+ }
+
+ std::string decoded_error_state;
+
+ if (!chromeos::data_encoding::Base64Decode(
+ error_state_str.c_str() + kBase64HeaderLength,
+ &decoded_error_state)) {
+ LOG(ERROR) << "Could not decode i915_error_state";
+ return false;
+ }
+
+ int written = base::WriteFile(error_state_path,
+ decoded_error_state.c_str(),
+ decoded_error_state.length());
+ if (written < 0 ||
+ static_cast<size_t>(written) != decoded_error_state.length()) {
+ LOG(ERROR) << "Could not write file " << error_state_path.value()
+ << " Written: " << written << " Len: "
+ << decoded_error_state.length();
+ base::DeleteFile(error_state_path, false);
+ return false;
+ }
+
+ return true;
+}
+
+// Gzip-compresses |path|, removes the original file, and returns the path of
+// the new file. On failure, the original file is left alone and an empty path
+// is returned.
+FilePath GzipFile(const FilePath& path) {
+ chromeos::ProcessImpl proc;
+ proc.AddArg(kGzipPath);
+ proc.AddArg(path.value());
+ const int res = proc.Run();
+ if (res != 0) {
+ LOG(ERROR) << "Failed to gzip " << path.value();
+ return FilePath();
+ }
+ return path.AddExtension(".gz");
+}
+
+} // namespace
+
+
+ChromeCollector::ChromeCollector() : output_file_ptr_(stdout) {}
+
+ChromeCollector::~ChromeCollector() {}
+
+bool ChromeCollector::HandleCrash(const FilePath &file_path,
+ const std::string &pid_string,
+ const std::string &uid_string,
+ const std::string &exe_name) {
+ if (!is_feedback_allowed_function_())
+ return true;
+
+ LOG(WARNING) << "Received crash notification for " << exe_name << "["
+ << pid_string << "] user " << uid_string << " (called directly)";
+
+ if (exe_name.find('/') != std::string::npos) {
+ LOG(ERROR) << "exe_name contains illegal characters: " << exe_name;
+ return false;
+ }
+
+ FilePath dir;
+ uid_t uid = atoi(uid_string.c_str());
+ pid_t pid = atoi(pid_string.c_str());
+ if (!GetCreatedCrashDirectoryByEuid(uid, &dir, nullptr)) {
+ LOG(ERROR) << "Can't create crash directory for uid " << uid;
+ return false;
+ }
+
+ std::string dump_basename = FormatDumpBasename(exe_name, time(nullptr), pid);
+ FilePath meta_path = GetCrashPath(dir, dump_basename, "meta");
+ FilePath minidump_path = GetCrashPath(dir, dump_basename, "dmp");
+
+ std::string data;
+ if (!base::ReadFileToString(file_path, &data)) {
+ LOG(ERROR) << "Can't read crash log: " << file_path.value();
+ return false;
+ }
+
+ if (!ParseCrashLog(data, dir, minidump_path, dump_basename)) {
+ LOG(ERROR) << "Failed to parse Chrome's crash log";
+ return false;
+ }
+
+
+ int64_t report_size = 0;
+ base::GetFileSize(minidump_path, &report_size);
+
+ // Keyed by crash metadata key name.
+ const std::map<std::string, base::FilePath> additional_logs =
+ GetAdditionalLogs(dir, dump_basename, exe_name);
+ for (auto it : additional_logs) {
+ int64_t file_size = 0;
+ if (!base::GetFileSize(it.second, &file_size)) {
+ PLOG(WARNING) << "Unable to get size of " << it.second.value();
+ continue;
+ }
+ if (report_size + file_size > kDefaultMaxUploadBytes) {
+ LOG(INFO) << "Skipping upload of " << it.second.value() << "("
+ << file_size << "B) because report size would exceed limit ("
+ << kDefaultMaxUploadBytes << "B)";
+ continue;
+ }
+ VLOG(1) << "Adding metadata: " << it.first << " -> " << it.second.value();
+ // Call AddCrashMetaUploadFile() rather than AddCrashMetaData() here. The
+ // former adds a prefix to the key name; without the prefix, only the key
+ // "logs" appears to be displayed on the crash server.
+ AddCrashMetaUploadFile(it.first, it.second.value());
+ report_size += file_size;
+ }
+
+ // We're done.
+ WriteCrashMetaData(meta_path, exe_name, minidump_path.value());
+
+ fprintf(output_file_ptr_, "%s", kSuccessMagic);
+ fflush(output_file_ptr_);
+
+ return true;
+}
+
+void ChromeCollector::SetUpDBus() {
+ CrashCollector::SetUpDBus();
+
+ debugd_proxy_.reset(
+ new org::chromium::debugdProxy(bus_, debugd::kDebugdServiceName));
+}
+
+bool ChromeCollector::ParseCrashLog(const std::string &data,
+ const FilePath &dir,
+ const FilePath &minidump,
+ const std::string &basename) {
+ size_t at = 0;
+ while (at < data.size()) {
+ // Look for a : followed by a decimal number, followed by another :
+ // followed by N bytes of data.
+ std::string name, size_string;
+ if (!GetDelimitedString(data, ':', at, &name)) {
+ LOG(ERROR) << "Can't find : after name @ offset " << at;
+ break;
+ }
+ at += name.size() + 1; // Skip the name & : delimiter.
+
+ if (!GetDelimitedString(data, ':', at, &size_string)) {
+ LOG(ERROR) << "Can't find : after size @ offset " << at;
+ break;
+ }
+ at += size_string.size() + 1; // Skip the size & : delimiter.
+
+ size_t size;
+ if (!base::StringToSizeT(size_string, &size)) {
+ LOG(ERROR) << "String not convertible to integer: " << size_string;
+ break;
+ }
+
+ // Data would run past the end, did we get a truncated file?
+ if (at + size > data.size()) {
+ LOG(ERROR) << "Overrun, expected " << size << " bytes of data, got "
+ << (data.size() - at);
+ break;
+ }
+
+ if (name.find("filename") != std::string::npos) {
+ // File.
+ // Name will be in a semi-MIME format of
+ // <descriptive name>"; filename="<name>"
+ // Descriptive name will be upload_file_minidump for the dump.
+ std::string desc, filename;
+ pcrecpp::RE re("(.*)\" *; *filename=\"(.*)\"");
+ if (!re.FullMatch(name.c_str(), &desc, &filename)) {
+ LOG(ERROR) << "Filename was not in expected format: " << name;
+ break;
+ }
+
+ if (desc.compare(kDefaultMinidumpName) == 0) {
+ // The minidump.
+ WriteNewFile(minidump, data.c_str() + at, size);
+ } else {
+ // Some other file.
+ FilePath path = GetCrashPath(dir, basename + "-" + filename, "other");
+ if (WriteNewFile(path, data.c_str() + at, size) >= 0) {
+ AddCrashMetaUploadFile(desc, path.value());
+ }
+ }
+ } else {
+ // Other attribute.
+ std::string value_str;
+ value_str.reserve(size);
+
+ // Since metadata is one line/value the values must be escaped properly.
+ for (size_t i = at; i < at + size; i++) {
+ switch (data[i]) {
+ case '"':
+ case '\\':
+ value_str.push_back('\\');
+ value_str.push_back(data[i]);
+ break;
+
+ case '\r':
+ value_str += "\\r";
+ break;
+
+ case '\n':
+ value_str += "\\n";
+ break;
+
+ case '\t':
+ value_str += "\\t";
+ break;
+
+ case '\0':
+ value_str += "\\0";
+ break;
+
+ default:
+ value_str.push_back(data[i]);
+ break;
+ }
+ }
+ AddCrashMetaUploadData(name, value_str);
+ }
+
+ at += size;
+ }
+
+ return at == data.size();
+}
+
+std::map<std::string, base::FilePath> ChromeCollector::GetAdditionalLogs(
+ const FilePath &dir,
+ const std::string &basename,
+ const std::string &exe_name) {
+ std::map<std::string, base::FilePath> logs;
+
+ // Run the command specified by the config file to gather logs.
+ const FilePath chrome_log_path =
+ GetCrashPath(dir, basename, kChromeLogFilename);
+ if (GetLogContents(log_config_path_, exe_name, chrome_log_path)) {
+ const FilePath compressed_path = GzipFile(chrome_log_path);
+ if (!compressed_path.empty())
+ logs[kChromeLogFilename] = compressed_path;
+ else
+ base::DeleteFile(chrome_log_path, false /* recursive */);
+ }
+
+ // For unit testing, debugd_proxy_ isn't initialized, so skip attempting to
+ // get the GPU error state from debugd.
+ if (debugd_proxy_) {
+ const FilePath dri_error_state_path =
+ GetCrashPath(dir, basename, kGpuStateFilename);
+ if (GetDriErrorState(dri_error_state_path, debugd_proxy_.get()))
+ logs[kGpuStateFilename] = dri_error_state_path;
+ }
+
+ return logs;
+}
+
+// static
+const char ChromeCollector::kSuccessMagic[] = "_sys_cr_finished";
diff --git a/crash_reporter/chrome_collector.h b/crash_reporter/chrome_collector.h
new file mode 100644
index 0000000..0b58c19
--- /dev/null
+++ b/crash_reporter/chrome_collector.h
@@ -0,0 +1,72 @@
+// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CRASH_REPORTER_CHROME_COLLECTOR_H_
+#define CRASH_REPORTER_CHROME_COLLECTOR_H_
+
+#include <map>
+#include <string>
+
+#include <base/files/file_path.h>
+#include <base/macros.h>
+#include <gtest/gtest_prod.h> // for FRIEND_TEST
+
+#include "crash-reporter/crash_collector.h"
+#include "debugd/dbus-proxies.h"
+
+class SystemLogging;
+
+// Chrome crash collector.
+class ChromeCollector : public CrashCollector {
+ public:
+ ChromeCollector();
+ ~ChromeCollector() override;
+
+ // Magic string to let Chrome know the crash report succeeded.
+ static const char kSuccessMagic[];
+
+ // Handle a specific chrome crash. Returns true on success.
+ bool HandleCrash(const base::FilePath &file_path,
+ const std::string &pid_string,
+ const std::string &uid_string,
+ const std::string &exe_name);
+
+ protected:
+ void SetUpDBus() override;
+
+ private:
+ friend class ChromeCollectorTest;
+ FRIEND_TEST(ChromeCollectorTest, GoodValues);
+ FRIEND_TEST(ChromeCollectorTest, BadValues);
+ FRIEND_TEST(ChromeCollectorTest, Newlines);
+ FRIEND_TEST(ChromeCollectorTest, File);
+ FRIEND_TEST(ChromeCollectorTest, HandleCrash);
+
+ // Crashes are expected to be in a TLV-style format of:
+ // <name>:<length>:<value>
+ // Length is encoded as a decimal number. It can be zero, but must consist of
+ // at least one character
+ // For file values, name actually contains both a description and a filename,
+ // in a fixed format of: <description>"; filename="<filename>"
+ bool ParseCrashLog(const std::string &data, const base::FilePath &dir,
+ const base::FilePath &minidump,
+ const std::string &basename);
+
+ // Writes additional logs for |exe_name| to files based on |basename| within
+ // |dir|. Crash report metadata key names and the corresponding file paths are
+ // returned.
+ std::map<std::string, base::FilePath> GetAdditionalLogs(
+ const base::FilePath &dir,
+ const std::string &basename,
+ const std::string &exe_name);
+
+ FILE *output_file_ptr_;
+
+ // D-Bus proxy for debugd interface. Unset in unit tests.
+ std::unique_ptr<org::chromium::debugdProxy> debugd_proxy_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChromeCollector);
+};
+
+#endif // CRASH_REPORTER_CHROME_COLLECTOR_H_
diff --git a/crash_reporter/chrome_collector_test.cc b/crash_reporter/chrome_collector_test.cc
new file mode 100644
index 0000000..0d6a7ce
--- /dev/null
+++ b/crash_reporter/chrome_collector_test.cc
@@ -0,0 +1,150 @@
+// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crash-reporter/chrome_collector.h"
+
+#include <stdio.h>
+
+#include <base/auto_reset.h>
+#include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
+#include <chromeos/syslog_logging.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using base::FilePath;
+
+namespace {
+
+const char kCrashFormatGood[] = "value1:10:abcdefghijvalue2:5:12345";
+const char kCrashFormatEmbeddedNewline[] =
+ "value1:10:abcd\r\nghijvalue2:5:12\n34";
+const char kCrashFormatBad1[] = "value1:10:abcdefghijvalue2:6=12345";
+const char kCrashFormatBad2[] = "value1:10:abcdefghijvalue2:512345";
+const char kCrashFormatBad3[] = "value1:10::abcdefghijvalue2:5=12345";
+const char kCrashFormatBad4[] = "value1:10:abcdefghijvalue2:4=12345";
+
+const char kCrashFormatWithFile[] =
+ "value1:10:abcdefghijvalue2:5:12345"
+ "some_file\"; filename=\"foo.txt\":15:12345\n789\n12345"
+ "value3:2:ok";
+
+void CountCrash() {
+}
+
+bool s_allow_crash = false;
+
+bool IsMetrics() {
+ return s_allow_crash;
+}
+
+} // namespace
+
+class ChromeCollectorMock : public ChromeCollector {
+ public:
+ MOCK_METHOD0(SetUpDBus, void());
+};
+
+class ChromeCollectorTest : public ::testing::Test {
+ protected:
+ void ExpectFileEquals(const char *golden,
+ const FilePath &file_path) {
+ std::string contents;
+ EXPECT_TRUE(base::ReadFileToString(file_path, &contents));
+ EXPECT_EQ(golden, contents);
+ }
+
+ ChromeCollectorMock collector_;
+
+ private:
+ void SetUp() override {
+ EXPECT_CALL(collector_, SetUpDBus()).WillRepeatedly(testing::Return());
+
+ collector_.Initialize(CountCrash, IsMetrics);
+ chromeos::ClearLog();
+ }
+};
+
+TEST_F(ChromeCollectorTest, GoodValues) {
+ FilePath dir(".");
+ EXPECT_TRUE(collector_.ParseCrashLog(kCrashFormatGood,
+ dir, dir.Append("minidump.dmp"),
+ "base"));
+
+ // Check to see if the values made it in properly.
+ std::string meta = collector_.extra_metadata_;
+ EXPECT_TRUE(meta.find("value1=abcdefghij") != std::string::npos);
+ EXPECT_TRUE(meta.find("value2=12345") != std::string::npos);
+}
+
+TEST_F(ChromeCollectorTest, Newlines) {
+ FilePath dir(".");
+ EXPECT_TRUE(collector_.ParseCrashLog(kCrashFormatEmbeddedNewline,
+ dir, dir.Append("minidump.dmp"),
+ "base"));
+
+ // Check to see if the values were escaped.
+ std::string meta = collector_.extra_metadata_;
+ EXPECT_TRUE(meta.find("value1=abcd\\r\\nghij") != std::string::npos);
+ EXPECT_TRUE(meta.find("value2=12\\n34") != std::string::npos);
+}
+
+TEST_F(ChromeCollectorTest, BadValues) {
+ FilePath dir(".");
+ const struct {
+ const char *data;
+ } list[] = {
+ {kCrashFormatBad1, },
+ {kCrashFormatBad2, },
+ {kCrashFormatBad3, },
+ {kCrashFormatBad4, },
+ };
+
+ for (size_t i = 0; i < sizeof(list) / sizeof(list[0]); i++) {
+ chromeos::ClearLog();
+ EXPECT_FALSE(collector_.ParseCrashLog(list[i].data,
+ dir, dir.Append("minidump.dmp"),
+ "base"));
+ }
+}
+
+TEST_F(ChromeCollectorTest, File) {
+ base::ScopedTempDir scoped_temp_dir;
+ ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
+ const FilePath& dir = scoped_temp_dir.path();
+ EXPECT_TRUE(collector_.ParseCrashLog(kCrashFormatWithFile,
+ dir, dir.Append("minidump.dmp"),
+ "base"));
+
+ // Check to see if the values are still correct and that the file was
+ // written with the right data.
+ std::string meta = collector_.extra_metadata_;
+ EXPECT_TRUE(meta.find("value1=abcdefghij") != std::string::npos);
+ EXPECT_TRUE(meta.find("value2=12345") != std::string::npos);
+ EXPECT_TRUE(meta.find("value3=ok") != std::string::npos);
+ ExpectFileEquals("12345\n789\n12345", dir.Append("base-foo.txt.other"));
+}
+
+TEST_F(ChromeCollectorTest, HandleCrash) {
+ base::AutoReset<bool> auto_reset(&s_allow_crash, true);
+ base::ScopedTempDir scoped_temp_dir;
+ ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
+ const FilePath& dir = scoped_temp_dir.path();
+ FilePath dump_file = dir.Append("test.dmp");
+ ASSERT_EQ(strlen(kCrashFormatWithFile),
+ base::WriteFile(dump_file, kCrashFormatWithFile,
+ strlen(kCrashFormatWithFile)));
+ collector_.ForceCrashDirectory(dir);
+
+ FilePath log_file;
+ {
+ base::ScopedFILE output(
+ base::CreateAndOpenTemporaryFileInDir(dir, &log_file));
+ ASSERT_TRUE(output.get());
+ base::AutoReset<FILE*> auto_reset_file_ptr(&collector_.output_file_ptr_,
+ output.get());
+ EXPECT_TRUE(collector_.HandleCrash(dump_file, "123", "456", "chrome_test"));
+ }
+ ExpectFileEquals(ChromeCollector::kSuccessMagic, log_file);
+}
diff --git a/crash_reporter/crash-reporter.gyp b/crash_reporter/crash-reporter.gyp
new file mode 100644
index 0000000..a7f0e7e
--- /dev/null
+++ b/crash_reporter/crash-reporter.gyp
@@ -0,0 +1,147 @@
+{
+ # Shouldn't need this, but doesn't work otherwise.
+ # http://crbug.com/340086 and http://crbug.com/385186
+ # Note: the unused dependencies are optimized out by the compiler.
+ 'target_defaults': {
+ 'variables': {
+ 'deps': [
+ 'libchromeos-<(libbase_ver)',
+ ],
+ },
+ },
+ 'targets': [
+ {
+ 'target_name': 'libcrash',
+ 'type': 'static_library',
+ 'variables': {
+ 'exported_deps': [
+ 'libchrome-<(libbase_ver)',
+ 'libpcrecpp',
+ ],
+ 'deps': ['<@(exported_deps)'],
+ },
+ 'all_dependent_settings': {
+ 'variables': {
+ 'deps': [
+ '<@(exported_deps)',
+ ],
+ },
+ },
+ 'sources': [
+ 'chrome_collector.cc',
+ 'crash_collector.cc',
+ 'kernel_collector.cc',
+ 'kernel_warning_collector.cc',
+ 'udev_collector.cc',
+ 'unclean_shutdown_collector.cc',
+ 'user_collector.cc',
+ ],
+ 'actions': [
+ {
+ 'action_name': 'generate-session-manager-proxies',
+ 'variables': {
+ 'proxy_output_file': 'include/session_manager/dbus-proxies.h'
+ },
+ 'sources': [
+ '../login_manager/org.chromium.SessionManagerInterface.xml',
+ ],
+ 'includes': ['../common-mk/generate-dbus-proxies.gypi'],
+ },
+ {
+ 'action_name': 'generate-debugd-proxies',
+ 'variables': {
+ 'proxy_output_file': 'include/debugd/dbus-proxies.h'
+ },
+ 'sources': [
+ '../debugd/share/org.chromium.debugd.xml',
+ ],
+ 'includes': ['../common-mk/generate-dbus-proxies.gypi'],
+ },
+ ],
+ },
+ {
+ 'target_name': 'crash_reporter',
+ 'type': 'executable',
+ 'variables': {
+ 'deps': [
+ 'dbus-1',
+ 'libmetrics-<(libbase_ver)',
+ ],
+ },
+ 'dependencies': [
+ 'libcrash',
+ ],
+ 'sources': [
+ 'crash_reporter.cc',
+ ],
+ },
+ {
+ 'target_name': 'list_proxies',
+ 'type': 'executable',
+ 'variables': {
+ 'deps': [
+ 'dbus-1',
+ 'libchrome-<(libbase_ver)',
+ ],
+ },
+ 'sources': [
+ 'list_proxies.cc',
+ ],
+ 'actions': [
+ {
+ 'action_name': 'generate-lib-cros-service-proxies',
+ 'variables': {
+ 'proxy_output_file': 'include/libcrosservice/dbus-proxies.h'
+ },
+ 'sources': [
+ './dbus_bindings/org.chromium.LibCrosService.xml',
+ ],
+ 'includes': ['../common-mk/generate-dbus-proxies.gypi'],
+ },
+ ],
+ },
+ {
+ 'target_name': 'warn_collector',
+ 'type': 'executable',
+ 'variables': {
+ 'lexer_out_dir': 'crash-reporter',
+ 'deps': [
+ 'libmetrics-<(libbase_ver)',
+ ],
+ },
+ 'link_settings': {
+ 'libraries': [
+ '-lfl',
+ ],
+ },
+ 'sources': [
+ 'warn_collector.l',
+ ],
+ 'includes': ['../common-mk/lex.gypi'],
+ },
+ ],
+ 'conditions': [
+ ['USE_test == 1', {
+ 'targets': [
+ {
+ 'target_name': 'crash_reporter_test',
+ 'type': 'executable',
+ 'includes': ['../common-mk/common_test.gypi'],
+ 'dependencies': ['libcrash'],
+ 'sources': [
+ 'chrome_collector_test.cc',
+ 'crash_collector_test.cc',
+ 'crash_collector_test.h',
+ 'crash_reporter_logs_test.cc',
+ 'kernel_collector_test.cc',
+ 'kernel_collector_test.h',
+ 'testrunner.cc',
+ 'udev_collector_test.cc',
+ 'unclean_shutdown_collector_test.cc',
+ 'user_collector_test.cc',
+ ],
+ },
+ ],
+ }],
+ ],
+}
diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc
new file mode 100644
index 0000000..04f3ba8
--- /dev/null
+++ b/crash_reporter/crash_collector.cc
@@ -0,0 +1,512 @@
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crash-reporter/crash_collector.h"
+
+#include <dirent.h>
+#include <fcntl.h> // For file creation modes.
+#include <inttypes.h>
+#include <linux/limits.h> // PATH_MAX
+#include <pwd.h> // For struct passwd.
+#include <sys/types.h> // for mode_t.
+#include <sys/wait.h> // For waitpid.
+#include <unistd.h> // For execv and fork.
+
+#include <set>
+#include <utility>
+#include <vector>
+
+#include <base/files/file_util.h>
+#include <base/logging.h>
+#include <base/posix/eintr_wrapper.h>
+#include <base/strings/string_split.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <chromeos/cryptohome.h>
+#include <chromeos/dbus/service_constants.h>
+#include <chromeos/key_value_store.h>
+#include <chromeos/process.h>
+
+namespace {
+
+const char kCollectChromeFile[] =
+ "/mnt/stateful_partition/etc/collect_chrome_crashes";
+const char kCrashTestInProgressPath[] = "/tmp/crash-test-in-progress";
+const char kDefaultLogConfig[] = "/etc/crash_reporter_logs.conf";
+const char kDefaultUserName[] = "chronos";
+const char kLeaveCoreFile[] = "/root/.leave_core";
+const char kLsbRelease[] = "/etc/lsb-release";
+const char kShellPath[] = "/bin/sh";
+const char kSystemCrashPath[] = "/var/spool/crash";
+const char kUploadVarPrefix[] = "upload_var_";
+const char kUploadFilePrefix[] = "upload_file_";
+
+// Key of the lsb-release entry containing the OS version.
+const char kLsbVersionKey[] = "CHROMEOS_RELEASE_VERSION";
+
+// Normally this path is not used. Unfortunately, there are a few edge cases
+// where we need this. Any process that runs as kDefaultUserName that crashes
+// is consider a "user crash". That includes the initial Chrome browser that
+// runs the login screen. If that blows up, there is no logged in user yet,
+// so there is no per-user dir for us to stash things in. Instead we fallback
+// to this path as it is at least encrypted on a per-system basis.
+//
+// This also comes up when running autotests. The GUI is sitting at the login
+// screen while tests are sshing in, changing users, and triggering crashes as
+// the user (purposefully).
+const char kFallbackUserCrashPath[] = "/home/chronos/crash";
+
+// Directory mode of the user crash spool directory.
+const mode_t kUserCrashPathMode = 0755;
+
+// Directory mode of the system crash spool directory.
+const mode_t kSystemCrashPathMode = 01755;
+
+const uid_t kRootOwner = 0;
+const uid_t kRootGroup = 0;
+
+} // namespace
+
+// Maximum crash reports per crash spool directory. Note that this is
+// a separate maximum from the maximum rate at which we upload these
+// diagnostics. The higher this rate is, the more space we allow for
+// core files, minidumps, and kcrash logs, and equivalently the more
+// processor and I/O bandwidth we dedicate to handling these crashes when
+// many occur at once. Also note that if core files are configured to
+// be left on the file system, we stop adding crashes when either the
+// number of core files or minidumps reaches this number.
+const int CrashCollector::kMaxCrashDirectorySize = 32;
+
+using base::FilePath;
+using base::StringPrintf;
+
+CrashCollector::CrashCollector()
+ : lsb_release_(kLsbRelease),
+ log_config_path_(kDefaultLogConfig) {
+}
+
+CrashCollector::~CrashCollector() {
+ if (bus_)
+ bus_->ShutdownAndBlock();
+}
+
+void CrashCollector::Initialize(
+ CrashCollector::CountCrashFunction count_crash_function,
+ CrashCollector::IsFeedbackAllowedFunction is_feedback_allowed_function) {
+ CHECK(count_crash_function);
+ CHECK(is_feedback_allowed_function);
+
+ count_crash_function_ = count_crash_function;
+ is_feedback_allowed_function_ = is_feedback_allowed_function;
+
+ SetUpDBus();
+}
+
+void CrashCollector::SetUpDBus() {
+ dbus::Bus::Options options;
+ options.bus_type = dbus::Bus::SYSTEM;
+
+ bus_ = new dbus::Bus(options);
+ CHECK(bus_->Connect());
+
+ session_manager_proxy_.reset(
+ new org::chromium::SessionManagerInterfaceProxy(
+ bus_,
+ login_manager::kSessionManagerServiceName));
+}
+
+int CrashCollector::WriteNewFile(const FilePath &filename,
+ const char *data,
+ int size) {
+ int fd = HANDLE_EINTR(open(filename.value().c_str(),
+ O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0666));
+ if (fd < 0) {
+ return -1;
+ }
+
+ int rv = base::WriteFileDescriptor(fd, data, size) ? size : -1;
+ IGNORE_EINTR(close(fd));
+ return rv;
+}
+
+std::string CrashCollector::Sanitize(const std::string &name) {
+ // Make sure the sanitized name does not include any periods.
+ // The logic in crash_sender relies on this.
+ std::string result = name;
+ for (size_t i = 0; i < name.size(); ++i) {
+ if (!isalnum(result[i]) && result[i] != '_')
+ result[i] = '_';
+ }
+ return result;
+}
+
+std::string CrashCollector::FormatDumpBasename(const std::string &exec_name,
+ time_t timestamp,
+ pid_t pid) {
+ struct tm tm;
+ localtime_r(×tamp, &tm);
+ std::string sanitized_exec_name = Sanitize(exec_name);
+ return StringPrintf("%s.%04d%02d%02d.%02d%02d%02d.%d",
+ sanitized_exec_name.c_str(),
+ tm.tm_year + 1900,
+ tm.tm_mon + 1,
+ tm.tm_mday,
+ tm.tm_hour,
+ tm.tm_min,
+ tm.tm_sec,
+ pid);
+}
+
+FilePath CrashCollector::GetCrashPath(const FilePath &crash_directory,
+ const std::string &basename,
+ const std::string &extension) {
+ return crash_directory.Append(StringPrintf("%s.%s",
+ basename.c_str(),
+ extension.c_str()));
+}
+
+bool CrashCollector::GetActiveUserSessions(
+ std::map<std::string, std::string> *sessions) {
+ chromeos::ErrorPtr error;
+ session_manager_proxy_->RetrieveActiveSessions(sessions, &error);
+
+ if (error) {
+ LOG(ERROR) << "Error calling D-Bus proxy call to interface "
+ << "'" << session_manager_proxy_->GetObjectPath().value() << "':"
+ << error->GetMessage();
+ return false;
+ }
+
+ return true;
+}
+
+FilePath CrashCollector::GetUserCrashPath() {
+ // In this multiprofile world, there is no one-specific user dir anymore.
+ // Ask the session manager for the active ones, then just run with the
+ // first result we get back.
+ FilePath user_path = FilePath(kFallbackUserCrashPath);
+ std::map<std::string, std::string> active_sessions;
+ if (!GetActiveUserSessions(&active_sessions) || active_sessions.empty()) {
+ LOG(ERROR) << "Could not get active user sessions, using default.";
+ return user_path;
+ }
+
+ user_path = chromeos::cryptohome::home::GetHashedUserPath(
+ active_sessions.begin()->second).Append("crash");
+
+ return user_path;
+}
+
+FilePath CrashCollector::GetCrashDirectoryInfo(
+ uid_t process_euid,
+ uid_t default_user_id,
+ gid_t default_user_group,
+ mode_t *mode,
+ uid_t *directory_owner,
+ gid_t *directory_group) {
+ // TODO(mkrebs): This can go away once Chrome crashes are handled
+ // normally (see crosbug.com/5872).
+ // Check if the user crash directory should be used. If we are
+ // collecting chrome crashes during autotesting, we want to put them in
+ // the system crash directory so they are outside the cryptohome -- in
+ // case we are being run during logout (see crosbug.com/18637).
+ if (process_euid == default_user_id && IsUserSpecificDirectoryEnabled()) {
+ *mode = kUserCrashPathMode;
+ *directory_owner = default_user_id;
+ *directory_group = default_user_group;
+ return GetUserCrashPath();
+ } else {
+ *mode = kSystemCrashPathMode;
+ *directory_owner = kRootOwner;
+ *directory_group = kRootGroup;
+ return FilePath(kSystemCrashPath);
+ }
+}
+
+bool CrashCollector::GetUserInfoFromName(const std::string &name,
+ uid_t *uid,
+ gid_t *gid) {
+ char storage[256];
+ struct passwd passwd_storage;
+ struct passwd *passwd_result = nullptr;
+
+ if (getpwnam_r(name.c_str(), &passwd_storage, storage, sizeof(storage),
+ &passwd_result) != 0 || passwd_result == nullptr) {
+ LOG(ERROR) << "Cannot find user named " << name;
+ return false;
+ }
+
+ *uid = passwd_result->pw_uid;
+ *gid = passwd_result->pw_gid;
+ return true;
+}
+
+bool CrashCollector::GetCreatedCrashDirectoryByEuid(uid_t euid,
+ FilePath *crash_directory,
+ bool *out_of_capacity) {
+ uid_t default_user_id;
+ gid_t default_user_group;
+
+ if (out_of_capacity) *out_of_capacity = false;
+
+ // For testing.
+ if (!forced_crash_directory_.empty()) {
+ *crash_directory = forced_crash_directory_;
+ return true;
+ }
+
+ if (!GetUserInfoFromName(kDefaultUserName,
+ &default_user_id,
+ &default_user_group)) {
+ LOG(ERROR) << "Could not find default user info";
+ return false;
+ }
+ mode_t directory_mode;
+ uid_t directory_owner;
+ gid_t directory_group;
+ *crash_directory =
+ GetCrashDirectoryInfo(euid,
+ default_user_id,
+ default_user_group,
+ &directory_mode,
+ &directory_owner,
+ &directory_group);
+
+ if (!base::PathExists(*crash_directory)) {
+ // Create the spool directory with the appropriate mode (regardless of
+ // umask) and ownership.
+ mode_t old_mask = umask(0);
+ if (mkdir(crash_directory->value().c_str(), directory_mode) < 0 ||
+ chown(crash_directory->value().c_str(),
+ directory_owner,
+ directory_group) < 0) {
+ LOG(ERROR) << "Unable to create appropriate crash directory";
+ return false;
+ }
+ umask(old_mask);
+ }
+
+ if (!base::PathExists(*crash_directory)) {
+ LOG(ERROR) << "Unable to create crash directory "
+ << crash_directory->value().c_str();
+ return false;
+ }
+
+ if (!CheckHasCapacity(*crash_directory)) {
+ if (out_of_capacity) *out_of_capacity = true;
+ return false;
+ }
+
+ return true;
+}
+
+FilePath CrashCollector::GetProcessPath(pid_t pid) {
+ return FilePath(StringPrintf("/proc/%d", pid));
+}
+
+bool CrashCollector::GetSymlinkTarget(const FilePath &symlink,
+ FilePath *target) {
+ ssize_t max_size = 64;
+ std::vector<char> buffer;
+
+ while (true) {
+ buffer.resize(max_size + 1);
+ ssize_t size = readlink(symlink.value().c_str(), buffer.data(), max_size);
+ if (size < 0) {
+ int saved_errno = errno;
+ LOG(ERROR) << "Readlink failed on " << symlink.value() << " with "
+ << saved_errno;
+ return false;
+ }
+
+ buffer[size] = 0;
+ if (size == max_size) {
+ max_size *= 2;
+ if (max_size > PATH_MAX) {
+ return false;
+ }
+ continue;
+ }
+ break;
+ }
+
+ *target = FilePath(buffer.data());
+ return true;
+}
+
+bool CrashCollector::GetExecutableBaseNameFromPid(pid_t pid,
+ std::string *base_name) {
+ FilePath target;
+ FilePath process_path = GetProcessPath(pid);
+ FilePath exe_path = process_path.Append("exe");
+ if (!GetSymlinkTarget(exe_path, &target)) {
+ LOG(INFO) << "GetSymlinkTarget failed - Path " << process_path.value()
+ << " DirectoryExists: "
+ << base::DirectoryExists(process_path);
+ // Try to further diagnose exe readlink failure cause.
+ struct stat buf;
+ int stat_result = stat(exe_path.value().c_str(), &buf);
+ int saved_errno = errno;
+ if (stat_result < 0) {
+ LOG(INFO) << "stat " << exe_path.value() << " failed: " << stat_result
+ << " " << saved_errno;
+ } else {
+ LOG(INFO) << "stat " << exe_path.value() << " succeeded: st_mode="
+ << buf.st_mode;
+ }
+ return false;
+ }
+ *base_name = target.BaseName().value();
+ return true;
+}
+
+// Return true if the given crash directory has not already reached
+// maximum capacity.
+bool CrashCollector::CheckHasCapacity(const FilePath &crash_directory) {
+ DIR* dir = opendir(crash_directory.value().c_str());
+ if (!dir) {
+ return false;
+ }
+ struct dirent ent_buf;
+ struct dirent* ent;
+ bool full = false;
+ std::set<std::string> basenames;
+ while (readdir_r(dir, &ent_buf, &ent) == 0 && ent) {
+ if ((strcmp(ent->d_name, ".") == 0) ||
+ (strcmp(ent->d_name, "..") == 0))
+ continue;
+
+ std::string filename(ent->d_name);
+ size_t last_dot = filename.rfind(".");
+ std::string basename;
+ // If there is a valid looking extension, use the base part of the
+ // name. If the only dot is the first byte (aka a dot file), treat
+ // it as unique to avoid allowing a directory full of dot files
+ // from accumulating.
+ if (last_dot != std::string::npos && last_dot != 0)
+ basename = filename.substr(0, last_dot);
+ else
+ basename = filename;
+ basenames.insert(basename);
+
+ if (basenames.size() >= static_cast<size_t>(kMaxCrashDirectorySize)) {
+ LOG(WARNING) << "Crash directory " << crash_directory.value()
+ << " already full with " << kMaxCrashDirectorySize
+ << " pending reports";
+ full = true;
+ break;
+ }
+ }
+ closedir(dir);
+ return !full;
+}
+
+bool CrashCollector::GetLogContents(const FilePath &config_path,
+ const std::string &exec_name,
+ const FilePath &output_file) {
+ chromeos::KeyValueStore store;
+ if (!store.Load(config_path)) {
+ LOG(INFO) << "Unable to read log configuration file "
+ << config_path.value();
+ return false;
+ }
+
+ std::string command;
+ if (!store.GetString(exec_name, &command))
+ return false;
+
+ chromeos::ProcessImpl diag_process;
+ diag_process.AddArg(kShellPath);
+ diag_process.AddStringOption("-c", command);
+ diag_process.RedirectOutput(output_file.value());
+
+ const int result = diag_process.Run();
+ if (result != 0) {
+ LOG(INFO) << "Log command \"" << command << "\" exited with " << result;
+ return false;
+ }
+ return true;
+}
+
+void CrashCollector::AddCrashMetaData(const std::string &key,
+ const std::string &value) {
+ extra_metadata_.append(StringPrintf("%s=%s\n", key.c_str(), value.c_str()));
+}
+
+void CrashCollector::AddCrashMetaUploadFile(const std::string &key,
+ const std::string &path) {
+ if (!path.empty())
+ AddCrashMetaData(kUploadFilePrefix + key, path);
+}
+
+void CrashCollector::AddCrashMetaUploadData(const std::string &key,
+ const std::string &value) {
+ if (!value.empty())
+ AddCrashMetaData(kUploadVarPrefix + key, value);
+}
+
+void CrashCollector::WriteCrashMetaData(const FilePath &meta_path,
+ const std::string &exec_name,
+ const std::string &payload_path) {
+ chromeos::KeyValueStore store;
+ if (!store.Load(FilePath(lsb_release_))) {
+ LOG(ERROR) << "Problem parsing " << lsb_release_;
+ // Even though there was some failure, take as much as we could read.
+ }
+
+ std::string version("unknown");
+ if (!store.GetString(kLsbVersionKey, &version)) {
+ LOG(ERROR) << "Unable to read " << kLsbVersionKey << " from "
+ << lsb_release_;
+ }
+ int64_t payload_size = -1;
+ base::GetFileSize(FilePath(payload_path), &payload_size);
+ std::string meta_data = StringPrintf("%sexec_name=%s\n"
+ "ver=%s\n"
+ "payload=%s\n"
+ "payload_size=%" PRId64 "\n"
+ "done=1\n",
+ extra_metadata_.c_str(),
+ exec_name.c_str(),
+ version.c_str(),
+ payload_path.c_str(),
+ payload_size);
+ // We must use WriteNewFile instead of base::WriteFile as we
+ // do not want to write with root access to a symlink that an attacker
+ // might have created.
+ if (WriteNewFile(meta_path, meta_data.c_str(), meta_data.size()) < 0) {
+ LOG(ERROR) << "Unable to write " << meta_path.value();
+ }
+}
+
+bool CrashCollector::IsCrashTestInProgress() {
+ return base::PathExists(FilePath(kCrashTestInProgressPath));
+}
+
+bool CrashCollector::IsDeveloperImage() {
+ // If we're testing crash reporter itself, we don't want to special-case
+ // for developer images.
+ if (IsCrashTestInProgress())
+ return false;
+ return base::PathExists(FilePath(kLeaveCoreFile));
+}
+
+bool CrashCollector::ShouldHandleChromeCrashes() {
+ // If we're testing crash reporter itself, we don't want to allow an
+ // override for chrome crashes. And, let's be conservative and only
+ // allow an override for developer images.
+ if (!IsCrashTestInProgress() && IsDeveloperImage()) {
+ // Check if there's an override to indicate we should indeed collect
+ // chrome crashes. This allows the crashes to still be tracked when
+ // they occur in autotests. See "crosbug.com/17987".
+ if (base::PathExists(FilePath(kCollectChromeFile)))
+ return true;
+ }
+ // We default to ignoring chrome crashes.
+ return false;
+}
+
+bool CrashCollector::IsUserSpecificDirectoryEnabled() {
+ return !ShouldHandleChromeCrashes();
+}
diff --git a/crash_reporter/crash_collector.h b/crash_reporter/crash_collector.h
new file mode 100644
index 0000000..ef443d3
--- /dev/null
+++ b/crash_reporter/crash_collector.h
@@ -0,0 +1,179 @@
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CRASH_REPORTER_CRASH_COLLECTOR_H_
+#define CRASH_REPORTER_CRASH_COLLECTOR_H_
+
+#include <sys/stat.h>
+
+#include <map>
+#include <string>
+
+#include <base/files/file_path.h>
+#include <base/macros.h>
+#include <base/memory/scoped_ptr.h>
+#include <gtest/gtest_prod.h> // for FRIEND_TEST
+
+#include "session_manager/dbus-proxies.h"
+
+// User crash collector.
+class CrashCollector {
+ public:
+ typedef void (*CountCrashFunction)();
+ typedef bool (*IsFeedbackAllowedFunction)();
+
+ CrashCollector();
+
+ virtual ~CrashCollector();
+
+ // Initialize the crash collector for detection of crashes, given a
+ // crash counting function, and metrics collection enabled oracle.
+ void Initialize(CountCrashFunction count_crash,
+ IsFeedbackAllowedFunction is_metrics_allowed);
+
+ protected:
+ friend class CrashCollectorTest;
+ FRIEND_TEST(ChromeCollectorTest, HandleCrash);
+ FRIEND_TEST(CrashCollectorTest, CheckHasCapacityCorrectBasename);
+ FRIEND_TEST(CrashCollectorTest, CheckHasCapacityStrangeNames);
+ FRIEND_TEST(CrashCollectorTest, CheckHasCapacityUsual);
+ FRIEND_TEST(CrashCollectorTest, GetCrashDirectoryInfo);
+ FRIEND_TEST(CrashCollectorTest, GetCrashPath);
+ FRIEND_TEST(CrashCollectorTest, GetLogContents);
+ FRIEND_TEST(CrashCollectorTest, ForkExecAndPipe);
+ FRIEND_TEST(CrashCollectorTest, FormatDumpBasename);
+ FRIEND_TEST(CrashCollectorTest, Initialize);
+ FRIEND_TEST(CrashCollectorTest, IsUserSpecificDirectoryEnabled);
+ FRIEND_TEST(CrashCollectorTest, MetaData);
+ FRIEND_TEST(CrashCollectorTest, Sanitize);
+ FRIEND_TEST(CrashCollectorTest, WriteNewFile);
+ FRIEND_TEST(ForkExecAndPipeTest, Basic);
+ FRIEND_TEST(ForkExecAndPipeTest, NonZeroReturnValue);
+ FRIEND_TEST(ForkExecAndPipeTest, BadOutputFile);
+ FRIEND_TEST(ForkExecAndPipeTest, ExistingOutputFile);
+ FRIEND_TEST(ForkExecAndPipeTest, BadExecutable);
+ FRIEND_TEST(ForkExecAndPipeTest, StderrCaptured);
+ FRIEND_TEST(ForkExecAndPipeTest, NULLParam);
+ FRIEND_TEST(ForkExecAndPipeTest, NoParams);
+ FRIEND_TEST(ForkExecAndPipeTest, SegFaultHandling);
+
+ // Set maximum enqueued crashes in a crash directory.
+ static const int kMaxCrashDirectorySize;
+
+ // Set up D-Bus.
+ virtual void SetUpDBus();
+
+ // Writes |data| of |size| to |filename|, which must be a new file.
+ // If the file already exists or writing fails, return a negative value.
+ // Otherwise returns the number of bytes written.
+ int WriteNewFile(const base::FilePath &filename, const char *data, int size);
+
+ // Return a filename that has only [a-z0-1_] characters by mapping
+ // all others into '_'.
+ std::string Sanitize(const std::string &name);
+
+ // For testing, set the directory always returned by
+ // GetCreatedCrashDirectoryByEuid.
+ void ForceCrashDirectory(const base::FilePath &forced_directory) {
+ forced_crash_directory_ = forced_directory;
+ }
+
+ virtual bool GetActiveUserSessions(
+ std::map<std::string, std::string> *sessions);
+ base::FilePath GetUserCrashPath();
+ base::FilePath GetCrashDirectoryInfo(uid_t process_euid,
+ uid_t default_user_id,
+ gid_t default_user_group,
+ mode_t *mode,
+ uid_t *directory_owner,
+ gid_t *directory_group);
+ bool GetUserInfoFromName(const std::string &name,
+ uid_t *uid,
+ gid_t *gid);
+
+ // Determines the crash directory for given euid, and creates the
+ // directory if necessary with appropriate permissions. If
+ // |out_of_capacity| is not nullptr, it is set to indicate if the call
+ // failed due to not having capacity in the crash directory. Returns
+ // true whether or not directory needed to be created, false on any
+ // failure. If the crash directory is at capacity, returns false.
+ bool GetCreatedCrashDirectoryByEuid(uid_t euid,
+ base::FilePath *crash_file_path,
+ bool *out_of_capacity);
+
+ // Format crash name based on components.
+ std::string FormatDumpBasename(const std::string &exec_name,
+ time_t timestamp,
+ pid_t pid);
+
+ // Create a file path to a file in |crash_directory| with the given
+ // |basename| and |extension|.
+ base::FilePath GetCrashPath(const base::FilePath &crash_directory,
+ const std::string &basename,
+ const std::string &extension);
+
+ base::FilePath GetProcessPath(pid_t pid);
+ bool GetSymlinkTarget(const base::FilePath &symlink,
+ base::FilePath *target);
+ bool GetExecutableBaseNameFromPid(pid_t pid,
+ std::string *base_name);
+
+ // Check given crash directory still has remaining capacity for another
+ // crash.
+ bool CheckHasCapacity(const base::FilePath &crash_directory);
+
+ // Write a log applicable to |exec_name| to |output_file| based on the
+ // log configuration file at |config_path|.
+ bool GetLogContents(const base::FilePath &config_path,
+ const std::string &exec_name,
+ const base::FilePath &output_file);
+
+ // Add non-standard meta data to the crash metadata file. Call
+ // before calling WriteCrashMetaData. Key must not contain "=" or
+ // "\n" characters. Value must not contain "\n" characters.
+ void AddCrashMetaData(const std::string &key, const std::string &value);
+
+ // Add a file to be uploaded to the crash reporter server. The file must
+ // persist until the crash report is sent; ideally it should live in the same
+ // place as the .meta file, so it can be cleaned up automatically.
+ void AddCrashMetaUploadFile(const std::string &key, const std::string &path);
+
+ // Add non-standard meta data to the crash metadata file.
+ // Data added though this call will be uploaded to the crash reporter server,
+ // appearing as a form field.
+ void AddCrashMetaUploadData(const std::string &key, const std::string &value);
+
+ // Write a file of metadata about crash.
+ void WriteCrashMetaData(const base::FilePath &meta_path,
+ const std::string &exec_name,
+ const std::string &payload_path);
+
+ // Returns true if the a crash test is currently running.
+ bool IsCrashTestInProgress();
+ // Returns true if we should consider ourselves to be running on a
+ // developer image.
+ bool IsDeveloperImage();
+ // Returns true if chrome crashes should be handled.
+ bool ShouldHandleChromeCrashes();
+ // Returns true if user crash directory may be used.
+ bool IsUserSpecificDirectoryEnabled();
+
+ CountCrashFunction count_crash_function_;
+ IsFeedbackAllowedFunction is_feedback_allowed_function_;
+ std::string extra_metadata_;
+ base::FilePath forced_crash_directory_;
+ std::string lsb_release_;
+ base::FilePath log_config_path_;
+
+ scoped_refptr<dbus::Bus> bus_;
+
+ private:
+ // D-Bus proxy for session manager interface.
+ std::unique_ptr<org::chromium::SessionManagerInterfaceProxy>
+ session_manager_proxy_;
+
+ DISALLOW_COPY_AND_ASSIGN(CrashCollector);
+};
+
+#endif // CRASH_REPORTER_CRASH_COLLECTOR_H_
diff --git a/crash_reporter/crash_collector_test.cc b/crash_reporter/crash_collector_test.cc
new file mode 100644
index 0000000..ce9af2b
--- /dev/null
+++ b/crash_reporter/crash_collector_test.cc
@@ -0,0 +1,299 @@
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crash-reporter/crash_collector_test.h"
+
+#include <unistd.h>
+#include <utility>
+
+#include <base/files/file_util.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <chromeos/syslog_logging.h>
+#include <gtest/gtest.h>
+
+#include "crash-reporter/crash_collector.h"
+
+using base::FilePath;
+using base::StringPrintf;
+using chromeos::FindLog;
+using ::testing::Invoke;
+using ::testing::Return;
+
+namespace {
+
+void CountCrash() {
+ ADD_FAILURE();
+}
+
+bool IsMetrics() {
+ ADD_FAILURE();
+ return false;
+}
+
+bool GetActiveUserSessionsImpl(std::map<std::string, std::string> *sessions) {
+ char kUser[] = "chicken@butt.com";
+ char kHash[] = "hashcakes";
+ sessions->insert(std::pair<std::string, std::string>(kUser, kHash));
+ return true;
+}
+
+} // namespace
+
+class CrashCollectorTest : public ::testing::Test {
+ public:
+ void SetUp() {
+ EXPECT_CALL(collector_, SetUpDBus()).WillRepeatedly(Return());
+
+ collector_.Initialize(CountCrash, IsMetrics);
+ test_dir_ = FilePath("test");
+ base::CreateDirectory(test_dir_);
+ chromeos::ClearLog();
+ }
+
+ void TearDown() {
+ base::DeleteFile(test_dir_, true);
+ }
+
+ bool CheckHasCapacity();
+
+ protected:
+ CrashCollectorMock collector_;
+ FilePath test_dir_;
+};
+
+TEST_F(CrashCollectorTest, Initialize) {
+ ASSERT_TRUE(CountCrash == collector_.count_crash_function_);
+ ASSERT_TRUE(IsMetrics == collector_.is_feedback_allowed_function_);
+}
+
+TEST_F(CrashCollectorTest, WriteNewFile) {
+ FilePath test_file = test_dir_.Append("test_new");
+ const char kBuffer[] = "buffer";
+ EXPECT_EQ(strlen(kBuffer),
+ collector_.WriteNewFile(test_file,
+ kBuffer,
+ strlen(kBuffer)));
+ EXPECT_LT(collector_.WriteNewFile(test_file,
+ kBuffer,
+ strlen(kBuffer)), 0);
+}
+
+TEST_F(CrashCollectorTest, Sanitize) {
+ EXPECT_EQ("chrome", collector_.Sanitize("chrome"));
+ EXPECT_EQ("CHROME", collector_.Sanitize("CHROME"));
+ EXPECT_EQ("1chrome2", collector_.Sanitize("1chrome2"));
+ EXPECT_EQ("chrome__deleted_", collector_.Sanitize("chrome (deleted)"));
+ EXPECT_EQ("foo_bar", collector_.Sanitize("foo.bar"));
+ EXPECT_EQ("", collector_.Sanitize(""));
+ EXPECT_EQ("_", collector_.Sanitize(" "));
+}
+
+TEST_F(CrashCollectorTest, GetCrashDirectoryInfo) {
+ FilePath path;
+ const int kRootUid = 0;
+ const int kRootGid = 0;
+ const int kNtpUid = 5;
+ const int kChronosUid = 1000;
+ const int kChronosGid = 1001;
+ const mode_t kExpectedSystemMode = 01755;
+ const mode_t kExpectedUserMode = 0755;
+
+ mode_t directory_mode;
+ uid_t directory_owner;
+ gid_t directory_group;
+
+ path = collector_.GetCrashDirectoryInfo(kRootUid,
+ kChronosUid,
+ kChronosGid,
+ &directory_mode,
+ &directory_owner,
+ &directory_group);
+ EXPECT_EQ("/var/spool/crash", path.value());
+ EXPECT_EQ(kExpectedSystemMode, directory_mode);
+ EXPECT_EQ(kRootUid, directory_owner);
+ EXPECT_EQ(kRootGid, directory_group);
+
+ path = collector_.GetCrashDirectoryInfo(kNtpUid,
+ kChronosUid,
+ kChronosGid,
+ &directory_mode,
+ &directory_owner,
+ &directory_group);
+ EXPECT_EQ("/var/spool/crash", path.value());
+ EXPECT_EQ(kExpectedSystemMode, directory_mode);
+ EXPECT_EQ(kRootUid, directory_owner);
+ EXPECT_EQ(kRootGid, directory_group);
+
+ EXPECT_CALL(collector_, GetActiveUserSessions(testing::_))
+ .WillOnce(Invoke(&GetActiveUserSessionsImpl));
+
+ EXPECT_EQ(collector_.IsUserSpecificDirectoryEnabled(), true);
+
+ path = collector_.GetCrashDirectoryInfo(kChronosUid,
+ kChronosUid,
+ kChronosGid,
+ &directory_mode,
+ &directory_owner,
+ &directory_group);
+ EXPECT_EQ("/home/user/hashcakes/crash", path.value());
+ EXPECT_EQ(kExpectedUserMode, directory_mode);
+ EXPECT_EQ(kChronosUid, directory_owner);
+ EXPECT_EQ(kChronosGid, directory_group);
+}
+
+TEST_F(CrashCollectorTest, FormatDumpBasename) {
+ struct tm tm = {0};
+ tm.tm_sec = 15;
+ tm.tm_min = 50;
+ tm.tm_hour = 13;
+ tm.tm_mday = 23;
+ tm.tm_mon = 4;
+ tm.tm_year = 110;
+ tm.tm_isdst = -1;
+ std::string basename =
+ collector_.FormatDumpBasename("foo", mktime(&tm), 100);
+ ASSERT_EQ("foo.20100523.135015.100", basename);
+}
+
+TEST_F(CrashCollectorTest, GetCrashPath) {
+ EXPECT_EQ("/var/spool/crash/myprog.20100101.1200.1234.core",
+ collector_.GetCrashPath(FilePath("/var/spool/crash"),
+ "myprog.20100101.1200.1234",
+ "core").value());
+ EXPECT_EQ("/home/chronos/user/crash/chrome.20100101.1200.1234.dmp",
+ collector_.GetCrashPath(FilePath("/home/chronos/user/crash"),
+ "chrome.20100101.1200.1234",
+ "dmp").value());
+}
+
+
+bool CrashCollectorTest::CheckHasCapacity() {
+ static const char kFullMessage[] = "Crash directory test already full";
+ bool has_capacity = collector_.CheckHasCapacity(test_dir_);
+ bool has_message = FindLog(kFullMessage);
+ EXPECT_EQ(has_message, !has_capacity);
+ return has_capacity;
+}
+
+TEST_F(CrashCollectorTest, CheckHasCapacityUsual) {
+ // Test kMaxCrashDirectorySize - 1 non-meta files can be added.
+ for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) {
+ base::WriteFile(test_dir_.Append(StringPrintf("file%d.core", i)), "", 0);
+ EXPECT_TRUE(CheckHasCapacity());
+ }
+
+ // Test an additional kMaxCrashDirectorySize - 1 meta files fit.
+ for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) {
+ base::WriteFile(test_dir_.Append(StringPrintf("file%d.meta", i)), "", 0);
+ EXPECT_TRUE(CheckHasCapacity());
+ }
+
+ // Test an additional kMaxCrashDirectorySize meta files don't fit.
+ for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize; ++i) {
+ base::WriteFile(test_dir_.Append(StringPrintf("overage%d.meta", i)), "", 0);
+ EXPECT_FALSE(CheckHasCapacity());
+ }
+}
+
+TEST_F(CrashCollectorTest, CheckHasCapacityCorrectBasename) {
+ // Test kMaxCrashDirectorySize - 1 files can be added.
+ for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) {
+ base::WriteFile(test_dir_.Append(StringPrintf("file.%d.core", i)), "", 0);
+ EXPECT_TRUE(CheckHasCapacity());
+ }
+ base::WriteFile(test_dir_.Append("file.last.core"), "", 0);
+ EXPECT_FALSE(CheckHasCapacity());
+}
+
+TEST_F(CrashCollectorTest, CheckHasCapacityStrangeNames) {
+ // Test many files with different extensions and same base fit.
+ for (int i = 0; i < 5 * CrashCollector::kMaxCrashDirectorySize; ++i) {
+ base::WriteFile(test_dir_.Append(StringPrintf("a.%d", i)), "", 0);
+ EXPECT_TRUE(CheckHasCapacity());
+ }
+ // Test dot files are treated as individual files.
+ for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 2; ++i) {
+ base::WriteFile(test_dir_.Append(StringPrintf(".file%d", i)), "", 0);
+ EXPECT_TRUE(CheckHasCapacity());
+ }
+ base::WriteFile(test_dir_.Append("normal.meta"), "", 0);
+ EXPECT_FALSE(CheckHasCapacity());
+}
+
+TEST_F(CrashCollectorTest, MetaData) {
+ const char kMetaFileBasename[] = "generated.meta";
+ FilePath meta_file = test_dir_.Append(kMetaFileBasename);
+ FilePath lsb_release = test_dir_.Append("lsb-release");
+ FilePath payload_file = test_dir_.Append("payload-file");
+ std::string contents;
+ collector_.lsb_release_ = lsb_release.value();
+ const char kLsbContents[] =
+ "CHROMEOS_RELEASE_BOARD=lumpy\n"
+ "CHROMEOS_RELEASE_VERSION=6727.0.2015_01_26_0853\n"
+ "CHROMEOS_RELEASE_NAME=Chromium OS\n";
+ ASSERT_TRUE(base::WriteFile(lsb_release, kLsbContents, strlen(kLsbContents)));
+ const char kPayload[] = "foo";
+ ASSERT_TRUE(base::WriteFile(payload_file, kPayload, strlen(kPayload)));
+ collector_.AddCrashMetaData("foo", "bar");
+ collector_.WriteCrashMetaData(meta_file, "kernel", payload_file.value());
+ EXPECT_TRUE(base::ReadFileToString(meta_file, &contents));
+ const char kExpectedMeta[] =
+ "foo=bar\n"
+ "exec_name=kernel\n"
+ "ver=6727.0.2015_01_26_0853\n"
+ "payload=test/payload-file\n"
+ "payload_size=3\n"
+ "done=1\n";
+ EXPECT_EQ(kExpectedMeta, contents);
+
+ // Test target of symlink is not overwritten.
+ payload_file = test_dir_.Append("payload2-file");
+ ASSERT_TRUE(base::WriteFile(payload_file, kPayload, strlen(kPayload)));
+ FilePath meta_symlink_path = test_dir_.Append("symlink.meta");
+ ASSERT_EQ(0,
+ symlink(kMetaFileBasename,
+ meta_symlink_path.value().c_str()));
+ ASSERT_TRUE(base::PathExists(meta_symlink_path));
+ chromeos::ClearLog();
+ collector_.WriteCrashMetaData(meta_symlink_path,
+ "kernel",
+ payload_file.value());
+ // Target metadata contents should have stayed the same.
+ contents.clear();
+ EXPECT_TRUE(base::ReadFileToString(meta_file, &contents));
+ EXPECT_EQ(kExpectedMeta, contents);
+ EXPECT_TRUE(FindLog("Unable to write"));
+
+ // Test target of dangling symlink is not created.
+ base::DeleteFile(meta_file, false);
+ ASSERT_FALSE(base::PathExists(meta_file));
+ chromeos::ClearLog();
+ collector_.WriteCrashMetaData(meta_symlink_path, "kernel",
+ payload_file.value());
+ EXPECT_FALSE(base::PathExists(meta_file));
+ EXPECT_TRUE(FindLog("Unable to write"));
+}
+
+TEST_F(CrashCollectorTest, GetLogContents) {
+ FilePath config_file = test_dir_.Append("crash_config");
+ FilePath output_file = test_dir_.Append("crash_log");
+ const char kConfigContents[] =
+ "foobar=echo hello there | \\\n sed -e \"s/there/world/\"";
+ ASSERT_TRUE(
+ base::WriteFile(config_file, kConfigContents, strlen(kConfigContents)));
+ base::DeleteFile(FilePath(output_file), false);
+ EXPECT_FALSE(collector_.GetLogContents(config_file,
+ "barfoo",
+ output_file));
+ EXPECT_FALSE(base::PathExists(output_file));
+ base::DeleteFile(FilePath(output_file), false);
+ EXPECT_TRUE(collector_.GetLogContents(config_file,
+ "foobar",
+ output_file));
+ ASSERT_TRUE(base::PathExists(output_file));
+ std::string contents;
+ EXPECT_TRUE(base::ReadFileToString(output_file, &contents));
+ EXPECT_EQ("hello world\n", contents);
+}
diff --git a/crash_reporter/crash_collector_test.h b/crash_reporter/crash_collector_test.h
new file mode 100644
index 0000000..8339fa0
--- /dev/null
+++ b/crash_reporter/crash_collector_test.h
@@ -0,0 +1,23 @@
+// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CRASH_REPORTER_CRASH_COLLECTOR_TEST_H_
+#define CRASH_REPORTER_CRASH_COLLECTOR_TEST_H_
+
+#include "crash-reporter/crash_collector.h"
+
+#include <map>
+#include <string>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+class CrashCollectorMock : public CrashCollector {
+ public:
+ MOCK_METHOD0(SetUpDBus, void());
+ MOCK_METHOD1(GetActiveUserSessions,
+ bool(std::map<std::string, std::string> *sessions));
+};
+
+#endif // CRASH_REPORTER_CRASH_COLLECTOR_TEST_H_
diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc
new file mode 100644
index 0000000..1528b3f
--- /dev/null
+++ b/crash_reporter/crash_reporter.cc
@@ -0,0 +1,334 @@
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <fcntl.h> // for open
+
+#include <string>
+#include <vector>
+
+#include <base/files/file_util.h>
+#include <base/logging.h>
+#include <base/strings/string_split.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <chromeos/flag_helper.h>
+#include <chromeos/syslog_logging.h>
+#include <metrics/metrics_library.h>
+
+#include "crash-reporter/chrome_collector.h"
+#include "crash-reporter/kernel_collector.h"
+#include "crash-reporter/kernel_warning_collector.h"
+#include "crash-reporter/udev_collector.h"
+#include "crash-reporter/unclean_shutdown_collector.h"
+#include "crash-reporter/user_collector.h"
+
+static const char kCrashCounterHistogram[] = "Logging.CrashCounter";
+static const char kUserCrashSignal[] =
+ "org.chromium.CrashReporter.UserCrash";
+static const char kKernelCrashDetected[] = "/var/run/kernel-crash-detected";
+static const char kUncleanShutdownDetected[] =
+ "/var/run/unclean-shutdown-detected";
+
+// Enumeration of kinds of crashes to be used in the CrashCounter histogram.
+enum CrashKinds {
+ kCrashKindUncleanShutdown = 1,
+ kCrashKindUser = 2,
+ kCrashKindKernel = 3,
+ kCrashKindUdev = 4,
+ kCrashKindKernelWarning = 5,
+ kCrashKindMax
+};
+
+static MetricsLibrary s_metrics_lib;
+
+using base::FilePath;
+using base::StringPrintf;
+
+static bool IsFeedbackAllowed() {
+ return s_metrics_lib.AreMetricsEnabled();
+}
+
+static bool TouchFile(const FilePath &file_path) {
+ return base::WriteFile(file_path, "", 0) == 0;
+}
+
+static void SendCrashMetrics(CrashKinds type, const char* name) {
+ // TODO(kmixter): We can remove this histogram as part of
+ // crosbug.com/11163.
+ s_metrics_lib.SendEnumToUMA(kCrashCounterHistogram, type, kCrashKindMax);
+ s_metrics_lib.SendCrashToUMA(name);
+}
+
+static void CountKernelCrash() {
+ SendCrashMetrics(kCrashKindKernel, "kernel");
+}
+
+static void CountUdevCrash() {
+ SendCrashMetrics(kCrashKindUdev, "udevcrash");
+}
+
+static void CountUncleanShutdown() {
+ SendCrashMetrics(kCrashKindUncleanShutdown, "uncleanshutdown");
+}
+
+static void CountUserCrash() {
+ SendCrashMetrics(kCrashKindUser, "user");
+ std::string command = StringPrintf(
+ "/usr/bin/dbus-send --type=signal --system / \"%s\" &",
+ kUserCrashSignal);
+ // Announce through D-Bus whenever a user crash happens. This is
+ // used by the metrics daemon to log active use time between
+ // crashes.
+ //
+ // This could be done more efficiently by explicit fork/exec or
+ // using a dbus library directly. However, this should run
+ // relatively rarely and longer term we may need to implement a
+ // better way to do this that doesn't rely on D-Bus.
+ //
+ // We run in the background in case dbus daemon itself is crashed
+ // and not responding. This allows us to not block and potentially
+ // deadlock on a dbus-daemon crash. If dbus-daemon crashes without
+ // restarting, each crash will fork off a lot of dbus-send
+ // processes. Such a system is in a unusable state and will need
+ // to be restarted anyway.
+
+ int status = system(command.c_str());
+ LOG_IF(WARNING, status != 0) << "dbus-send running failed";
+}
+
+static void CountChromeCrash() {
+ // For now, consider chrome crashes the same as user crashes for reporting
+ // purposes.
+ CountUserCrash();
+}
+
+
+static int Initialize(KernelCollector *kernel_collector,
+ UserCollector *user_collector,
+ UncleanShutdownCollector *unclean_shutdown_collector,
+ const bool unclean_check,
+ const bool clean_shutdown) {
+ CHECK(!clean_shutdown) << "Incompatible options";
+
+ bool was_kernel_crash = false;
+ bool was_unclean_shutdown = false;
+ kernel_collector->Enable();
+ if (kernel_collector->is_enabled()) {
+ was_kernel_crash = kernel_collector->Collect();
+ }
+
+ if (unclean_check) {
+ was_unclean_shutdown = unclean_shutdown_collector->Collect();
+ }
+
+ // Touch a file to notify the metrics daemon that a kernel
+ // crash has been detected so that it can log the time since
+ // the last kernel crash.
+ if (IsFeedbackAllowed()) {
+ if (was_kernel_crash) {
+ TouchFile(FilePath(kKernelCrashDetected));
+ } else if (was_unclean_shutdown) {
+ // We only count an unclean shutdown if it did not come with
+ // an associated kernel crash.
+ TouchFile(FilePath(kUncleanShutdownDetected));
+ }
+ }
+
+ // Must enable the unclean shutdown collector *after* collecting.
+ unclean_shutdown_collector->Enable();
+ user_collector->Enable();
+
+ return 0;
+}
+
+static int HandleUserCrash(UserCollector *user_collector,
+ const std::string& user, const bool crash_test) {
+ // Handle a specific user space crash.
+ CHECK(!user.empty()) << "--user= must be set";
+
+ // Make it possible to test what happens when we crash while
+ // handling a crash.
+ if (crash_test) {
+ *(volatile char *)0 = 0;
+ return 0;
+ }
+
+ // Accumulate logs to help in diagnosing failures during user collection.
+ chromeos::LogToString(true);
+ // Handle the crash, get the name of the process from procfs.
+ bool handled = user_collector->HandleCrash(user, nullptr);
+ chromeos::LogToString(false);
+ if (!handled)
+ return 1;
+ return 0;
+}
+
+static int HandleChromeCrash(ChromeCollector *chrome_collector,
+ const std::string& chrome_dump_file,
+ const std::string& pid,
+ const std::string& uid,
+ const std::string& exe) {
+ CHECK(!chrome_dump_file.empty()) << "--chrome= must be set";
+ CHECK(!pid.empty()) << "--pid= must be set";
+ CHECK(!uid.empty()) << "--uid= must be set";
+ CHECK(!exe.empty()) << "--exe= must be set";
+
+ chromeos::LogToString(true);
+ bool handled = chrome_collector->HandleCrash(FilePath(chrome_dump_file),
+ pid, uid, exe);
+ chromeos::LogToString(false);
+ if (!handled)
+ return 1;
+ return 0;
+}
+
+static int HandleUdevCrash(UdevCollector *udev_collector,
+ const std::string& udev_event) {
+ // Handle a crash indicated by a udev event.
+ CHECK(!udev_event.empty()) << "--udev= must be set";
+
+ // Accumulate logs to help in diagnosing failures during user collection.
+ chromeos::LogToString(true);
+ bool handled = udev_collector->HandleCrash(udev_event);
+ chromeos::LogToString(false);
+ if (!handled)
+ return 1;
+ return 0;
+}
+
+static int HandleKernelWarning(KernelWarningCollector
+ *kernel_warning_collector) {
+ // Accumulate logs to help in diagnosing failures during collection.
+ chromeos::LogToString(true);
+ bool handled = kernel_warning_collector->Collect();
+ chromeos::LogToString(false);
+ if (!handled)
+ return 1;
+ return 0;
+}
+
+// Interactive/diagnostics mode for generating kernel crash signatures.
+static int GenerateKernelSignature(KernelCollector *kernel_collector,
+ const std::string& kernel_signature_file) {
+ std::string kcrash_contents;
+ std::string signature;
+ if (!base::ReadFileToString(FilePath(kernel_signature_file),
+ &kcrash_contents)) {
+ fprintf(stderr, "Could not read file.\n");
+ return 1;
+ }
+ if (!kernel_collector->ComputeKernelStackSignature(
+ kcrash_contents,
+ &signature,
+ true)) {
+ fprintf(stderr, "Signature could not be generated.\n");
+ return 1;
+ }
+ printf("Kernel crash signature is \"%s\".\n", signature.c_str());
+ return 0;
+}
+
+// Ensure stdout, stdin, and stderr are open file descriptors. If
+// they are not, any code which writes to stderr/stdout may write out
+// to files opened during execution. In particular, when
+// crash_reporter is run by the kernel coredump pipe handler (via
+// kthread_create/kernel_execve), it will not have file table entries
+// 1 and 2 (stdout and stderr) populated. We populate them here.
+static void OpenStandardFileDescriptors() {
+ int new_fd = -1;
+ // We open /dev/null to fill in any of the standard [0, 2] file
+ // descriptors. We leave these open for the duration of the
+ // process. This works because open returns the lowest numbered
+ // invalid fd.
+ do {
+ new_fd = open("/dev/null", 0);
+ CHECK_GE(new_fd, 0) << "Unable to open /dev/null";
+ } while (new_fd >= 0 && new_fd <= 2);
+ close(new_fd);
+}
+
+int main(int argc, char *argv[]) {
+ DEFINE_bool(init, false, "Initialize crash logging");
+ DEFINE_bool(clean_shutdown, false, "Signal clean shutdown");
+ DEFINE_string(generate_kernel_signature, "",
+ "Generate signature from given kcrash file");
+ DEFINE_bool(crash_test, false, "Crash test");
+ DEFINE_string(user, "", "User crash info (pid:signal:exec_name)");
+ DEFINE_bool(unclean_check, true, "Check for unclean shutdown");
+ DEFINE_string(udev, "", "Udev event description (type:device:subsystem)");
+ DEFINE_bool(kernel_warning, false, "Report collected kernel warning");
+ DEFINE_string(chrome, "", "Chrome crash dump file");
+ DEFINE_string(pid, "", "PID of crashing process");
+ DEFINE_string(uid, "", "UID of crashing process");
+ DEFINE_string(exe, "", "Executable name of crashing process");
+ DEFINE_bool(core2md_failure, false, "Core2md failure test");
+ DEFINE_bool(directory_failure, false, "Spool directory failure test");
+ DEFINE_string(filter_in, "",
+ "Ignore all crashes but this for testing");
+
+ OpenStandardFileDescriptors();
+ FilePath my_path = base::MakeAbsoluteFilePath(FilePath(argv[0]));
+ s_metrics_lib.Init();
+ chromeos::FlagHelper::Init(argc, argv, "Chromium OS Crash Reporter");
+ chromeos::OpenLog(my_path.BaseName().value().c_str(), true);
+ chromeos::InitLog(chromeos::kLogToSyslog);
+
+ KernelCollector kernel_collector;
+ kernel_collector.Initialize(CountKernelCrash, IsFeedbackAllowed);
+ UserCollector user_collector;
+ user_collector.Initialize(CountUserCrash,
+ my_path.value(),
+ IsFeedbackAllowed,
+ true, // generate_diagnostics
+ FLAGS_core2md_failure,
+ FLAGS_directory_failure,
+ FLAGS_filter_in);
+ UncleanShutdownCollector unclean_shutdown_collector;
+ unclean_shutdown_collector.Initialize(CountUncleanShutdown,
+ IsFeedbackAllowed);
+ UdevCollector udev_collector;
+ udev_collector.Initialize(CountUdevCrash, IsFeedbackAllowed);
+ ChromeCollector chrome_collector;
+ chrome_collector.Initialize(CountChromeCrash, IsFeedbackAllowed);
+
+ KernelWarningCollector kernel_warning_collector;
+ kernel_warning_collector.Initialize(CountUdevCrash, IsFeedbackAllowed);
+
+ if (FLAGS_init) {
+ return Initialize(&kernel_collector,
+ &user_collector,
+ &unclean_shutdown_collector,
+ FLAGS_unclean_check,
+ FLAGS_clean_shutdown);
+ }
+
+ if (FLAGS_clean_shutdown) {
+ unclean_shutdown_collector.Disable();
+ user_collector.Disable();
+ return 0;
+ }
+
+ if (!FLAGS_generate_kernel_signature.empty()) {
+ return GenerateKernelSignature(&kernel_collector,
+ FLAGS_generate_kernel_signature);
+ }
+
+ if (!FLAGS_udev.empty()) {
+ return HandleUdevCrash(&udev_collector, FLAGS_udev);
+ }
+
+ if (FLAGS_kernel_warning) {
+ return HandleKernelWarning(&kernel_warning_collector);
+ }
+
+ if (!FLAGS_chrome.empty()) {
+ return HandleChromeCrash(&chrome_collector,
+ FLAGS_chrome,
+ FLAGS_pid,
+ FLAGS_uid,
+ FLAGS_exe);
+ }
+
+ return HandleUserCrash(&user_collector, FLAGS_user, FLAGS_crash_test);
+}
diff --git a/crash_reporter/crash_reporter_logs.conf b/crash_reporter/crash_reporter_logs.conf
new file mode 100644
index 0000000..f5ca80c
--- /dev/null
+++ b/crash_reporter/crash_reporter_logs.conf
@@ -0,0 +1,112 @@
+# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can
+# be found in the LICENSE file.
+
+# This file is parsed by chromeos::KeyValueStore. It has the format:
+#
+# <basename>=<shell command>\n
+#
+# Commands may be split across multiple lines using trailing backslashes.
+#
+# When an executable named <basename> crashes, the corresponding command is
+# executed and its standard output and standard error are attached to the crash
+# report.
+#
+# Use caution in modifying this file. Only run common Unix commands here, as
+# these commands will be run when a crash has recently occurred and we should
+# avoid running anything that might cause another crash. Similarly, these
+# commands block notification of the crash to parent processes, so commands
+# should execute quickly.
+
+update_engine=cat $(ls -1tr /var/log/update_engine | tail -5 | \
+ sed s.^./var/log/update_engine/.) | tail -c 50000
+
+# The cros_installer output is logged into the update engine log file,
+# so it is handled in the same way as update_engine.
+cros_installer=cat $(ls -1tr /var/log/update_engine | tail -5 | \
+ sed s.^./var/log/update_engine/.) | tail -c 50000
+
+# Dump the last 20 lines of the last two files in Chrome's system and user log
+# directories, along with the last 20 messages from the session manager.
+chrome=\
+ for f in $(ls -1rt /var/log/chrome/chrome_[0-9]* | tail -2) \
+ $(ls -1rt /home/chronos/u-*/log/chrome_[0-9]* 2>/dev/null | tail -2); do \
+ echo "===$f (tail)==="; \
+ tail -20 $f; \
+ echo EOF; \
+ echo; \
+ done; \
+ echo "===session_manager (tail)==="; \
+ awk '$3 ~ "^session_manager\[" { print }' /var/log/messages | tail -20; \
+ echo EOF
+
+# The following rule is used for generating additional diagnostics when
+# collection of user crashes fails. This output should not be too large
+# as it is stored in memory. The output format specified for 'ps' is the
+# same as with the "u" ("user-oriented") option, except it doesn't show
+# the commands' arguments (i.e. "comm" instead of "command").
+crash_reporter-user-collection=\
+ echo "===ps output==="; \
+ ps axw -o user,pid,%cpu,%mem,vsz,rss,tname,stat,start_time,bsdtime,comm | \
+ tail -c 25000; \
+ echo "===meminfo==="; \
+ cat /proc/meminfo
+
+# This rule is similar to the crash_reporter-user-collection rule, except it is
+# run for kernel errors reported through udev events.
+crash_reporter-udev-collection-change-card0-drm=\
+ for dri in /sys/kernel/debug/dri/*; do \
+ echo "===$dri/i915_error_state==="; \
+ cat $dri/i915_error_state; \
+ done
+
+# When trackpad driver cyapa detects some abnormal behavior, we collect
+# additional logs from kernel messages.
+crash_reporter-udev-collection-change--i2c-cyapa=\
+ /usr/sbin/kernel_log_collector.sh cyapa 30
+# When trackpad/touchscreen driver atmel_mxt_ts detects some abnormal behavior,
+# we collect additional logs from kernel messages.
+crash_reporter-udev-collection-change--i2c-atmel_mxt_ts=\
+ /usr/sbin/kernel_log_collector.sh atmel 30
+# When touch device noise are detected, we collect relevant logs.
+# (crosbug.com/p/16788)
+crash_reporter-udev-collection---TouchNoise=cat /var/log/touch_noise.log
+# Periodically collect touch event log for debugging (crosbug.com/p/17244)
+crash_reporter-udev-collection---TouchEvent=cat /var/log/touch_event.log
+
+# Collect the last 50 lines of /var/log/messages and /var/log/net.log for
+# intel wifi driver (iwlwifi) for debugging purpose.
+crash_reporter-udev-collection-devcoredump-iwlwifi=\
+ echo "===/var/log/messages==="; \
+ tail -n 50 /var/log/messages; \
+ echo "===/var/log/net.log==="; \
+ tail -n 50 /var/log/net.log; \
+ echo EOF
+
+# Dump the last 50 lines of the last two powerd log files -- if the job has
+# already restarted, we want to see the end of the previous instance's logs.
+powerd=\
+ for f in $(ls -1tr /var/log/power_manager/powerd.[0-9]* | tail -2); do \
+ echo "===$(basename $f) (tail)==="; \
+ tail -50 $f; \
+ echo EOF; \
+ done
+# If power_supply_info aborts (due to e.g. a bad battery), its failure message
+# could end up in various places depending on which process was running it.
+# Attach the end of powerd's log since it might've also logged the underlying
+# problem.
+power_supply_info=\
+ echo "===powerd.LATEST (tail)==="; \
+ tail -50 /var/log/power_manager/powerd.LATEST; \
+ echo EOF
+# powerd_setuid_helper gets run by powerd, so its stdout/stderr will be mixed in
+# with powerd's stdout/stderr.
+powerd_setuid_helper=\
+ echo "===powerd.OUT (tail)==="; \
+ tail -50 /var/log/powerd.out; \
+ echo EOF
+
+# The following rules are only for testing purposes.
+crash_log_test=echo hello world
+crash_log_recursion_test=sleep 1 && \
+ /usr/local/autotest/tests/crash_log_recursion_test
diff --git a/crash_reporter/crash_reporter_logs_test.cc b/crash_reporter/crash_reporter_logs_test.cc
new file mode 100644
index 0000000..9879470
--- /dev/null
+++ b/crash_reporter/crash_reporter_logs_test.cc
@@ -0,0 +1,28 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+
+#include <base/files/file_path.h>
+#include <chromeos/key_value_store.h>
+#include <gtest/gtest.h>
+
+namespace {
+
+// Name of the checked-in configuration file containing log-collection commands.
+const char kConfigFile[] = "crash_reporter_logs.conf";
+
+// Executable name for Chrome. kConfigFile is expected to contain this entry.
+const char kChromeExecName[] = "chrome";
+
+} // namespace
+
+// Tests that the config file is parsable and that Chrome is listed.
+TEST(CrashReporterLogsTest, ReadConfig) {
+ chromeos::KeyValueStore store;
+ ASSERT_TRUE(store.Load(base::FilePath(kConfigFile)));
+ std::string command;
+ EXPECT_TRUE(store.GetString(kChromeExecName, &command));
+ EXPECT_FALSE(command.empty());
+}
diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender
new file mode 100755
index 0000000..641ae2d
--- /dev/null
+++ b/crash_reporter/crash_sender
@@ -0,0 +1,713 @@
+#!/bin/sh
+
+# Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+set -e
+
+# Default product ID in crash report (used if GOOGLE_CRASH_* is undefined).
+CHROMEOS_PRODUCT=ChromeOS
+
+# File whose existence implies crash reports may be sent, and whose
+# contents includes our machine's anonymized guid.
+CONSENT_ID="/home/chronos/Consent To Send Stats"
+
+# Crash sender lock in case the sender is already running.
+CRASH_SENDER_LOCK="/var/lock/crash_sender"
+
+# Path to file that indicates a crash test is currently running.
+CRASH_TEST_IN_PROGRESS_FILE="/tmp/crash-test-in-progress"
+
+# Path to find which is required for computing the crash rate.
+FIND="/usr/bin/find"
+
+# Set this to 1 in the environment to allow uploading crash reports
+# for unofficial versions.
+FORCE_OFFICIAL=${FORCE_OFFICIAL:-0}
+
+# Path to hardware class description.
+HWCLASS_PATH="/sys/devices/platform/chromeos_acpi/HWID"
+
+# Path to file that indicates this is a developer image.
+LEAVE_CORE_FILE="/root/.leave_core"
+
+# Path to list_proxies.
+LIST_PROXIES="/usr/bin/list_proxies"
+
+# Maximum crashes to send per day.
+MAX_CRASH_RATE=${MAX_CRASH_RATE:-32}
+
+# Path to metrics_client.
+METRICS_CLIENT="/usr/bin/metrics_client"
+
+# File whose existence mocks crash sending. If empty we pretend the
+# crash sending was successful, otherwise unsuccessful.
+MOCK_CRASH_SENDING="/tmp/mock-crash-sending"
+
+# Set this to 1 in the environment to pretend to have booted in developer
+# mode. This is used by autotests.
+MOCK_DEVELOPER_MODE=${MOCK_DEVELOPER_MODE:-0}
+
+# Ignore PAUSE_CRASH_SENDING file if set.
+OVERRIDE_PAUSE_SENDING=${OVERRIDE_PAUSE_SENDING:-0}
+
+# File whose existence causes crash sending to be delayed (for testing).
+# Must be stateful to enable testing kernel crashes.
+PAUSE_CRASH_SENDING="/var/lib/crash_sender_paused"
+
+# URL to send official build crash reports to.
+REPORT_UPLOAD_PROD_URL="https://clients2.google.com/cr/report"
+
+# Path to a directory of restricted certificates which includes
+# a certificate for ${REPORT_UPLOAD_PROD_URL}.
+RESTRICTED_CERTIFICATES_PATH="/usr/share/chromeos-ca-certificates"
+
+# File whose existence implies we're running and not to start again.
+RUN_FILE="/var/run/crash_sender.pid"
+
+# Maximum time to sleep between sends.
+SECONDS_SEND_SPREAD=${SECONDS_SEND_SPREAD:-600}
+
+# Set this to 1 to allow uploading of device coredumps.
+DEVCOREDUMP_UPLOAD_FLAG_FILE=\
+"/var/lib/crash_reporter/device_coredump_upload_allowed"
+
+# The syslog tag for all logging we emit.
+TAG="$(basename $0)[$$]"
+
+# Directory to store timestamp files indicating the uploads in the past 24
+# hours.
+TIMESTAMPS_DIR="/var/lib/crash_sender"
+
+# Temp directory for this process.
+TMP_DIR=""
+
+# Chrome's crash report log file.
+CHROME_CRASH_LOG="/var/log/chrome/Crash Reports/uploads.log"
+
+lecho() {
+ logger -t "${TAG}" "$@"
+}
+
+# Returns true if mock is enabled.
+is_mock() {
+ [ -f "${MOCK_CRASH_SENDING}" ] && return 0
+ return 1
+}
+
+is_mock_successful() {
+ local mock_in=$(cat "${MOCK_CRASH_SENDING}")
+ [ "${mock_in}" = "" ] && return 0 # empty file means success
+ return 1
+}
+
+cleanup() {
+ if [ -n "${TMP_DIR}" ]; then
+ rm -rf "${TMP_DIR}"
+ fi
+ rm -f "${RUN_FILE}"
+ crash_done
+}
+
+crash_done() {
+ if is_mock; then
+ # For testing purposes, emit a message to log so that we
+ # know when the test has received all the messages from this run.
+ lecho "crash_sender done."
+ fi
+}
+
+is_official_image() {
+ [ ${FORCE_OFFICIAL} -ne 0 ] && return 0
+ grep ^CHROMEOS_RELEASE_DESCRIPTION /etc/lsb-release | grep -q Official
+}
+
+# Returns 0 if the a crash test is currently running. NOTE: Mirrors
+# crash_collector.cc:CrashCollector::IsCrashTestInProgress().
+is_crash_test_in_progress() {
+ [ -f "${CRASH_TEST_IN_PROGRESS_FILE}" ] && return 0
+ return 1
+}
+
+# Returns 0 if we should consider ourselves to be running on a developer
+# image. NOTE: Mirrors crash_collector.cc:CrashCollector::IsDeveloperImage().
+is_developer_image() {
+ # If we're testing crash reporter itself, we don't want to special-case
+ # for developer images.
+ is_crash_test_in_progress && return 1
+ [ -f "${LEAVE_CORE_FILE}" ] && return 0
+ return 1
+}
+
+# Returns 0 if we should consider ourselves to be running on a test image.
+is_test_image() {
+ # If we're testing crash reporter itself, we don't want to special-case
+ # for test images.
+ is_crash_test_in_progress && return 1
+ case $(get_channel) in
+ test*) return 0;;
+ esac
+ return 1
+}
+
+# Returns 0 if the machine booted up in developer mode.
+is_developer_mode() {
+ [ ${MOCK_DEVELOPER_MODE} -ne 0 ] && return 0
+ # If we're testing crash reporter itself, we don't want to special-case
+ # for developer mode.
+ is_crash_test_in_progress && return 1
+ crossystem "devsw_boot?1" # exit status will be accurate
+}
+
+# Return 0 if the uploading of device coredumps is allowed.
+is_device_coredump_upload_allowed() {
+ [ -f "${DEVCOREDUMP_UPLOAD_FLAG_FILE}" ] && return 0
+ return 1
+}
+
+# Generate a uniform random number in 0..max-1.
+generate_uniform_random() {
+ local max=$1
+ local random="$(od -An -N4 -tu /dev/urandom)"
+ echo $((random % max))
+}
+
+# Check if sending a crash now does not exceed the maximum 24hr rate and
+# commit to doing so, if not.
+check_rate() {
+ mkdir -p ${TIMESTAMPS_DIR}
+ # Only consider minidumps written in the past 24 hours by removing all older.
+ ${FIND} "${TIMESTAMPS_DIR}" -mindepth 1 -mmin +$((24 * 60)) \
+ -exec rm -- '{}' ';'
+ local sends_in_24hrs=$(echo "${TIMESTAMPS_DIR}"/* | wc -w)
+ lecho "Current send rate: ${sends_in_24hrs}sends/24hrs"
+ if [ ${sends_in_24hrs} -ge ${MAX_CRASH_RATE} ]; then
+ lecho "Cannot send more crashes:"
+ lecho " current ${sends_in_24hrs}send/24hrs >= " \
+ "max ${MAX_CRASH_RATE}send/24hrs"
+ return 1
+ fi
+ mktemp "${TIMESTAMPS_DIR}"/XXXX > /dev/null
+ return 0
+}
+
+# Gets the base part of a crash report file, such as name.01234.5678.9012 from
+# name.01234.5678.9012.meta or name.01234.5678.9012.log.tar.xz. We make sure
+# "name" is sanitized in CrashCollector::Sanitize to not include any periods.
+get_base() {
+ echo "$1" | cut -d. -f-4
+}
+
+get_extension() {
+ local extension="${1##*.}"
+ local filename="${1%.*}"
+ # For gzipped file, we ignore .gz and get the real extension
+ if [ "${extension}" = "gz" ]; then
+ echo "${filename##*.}"
+ else
+ echo "${extension}"
+ fi
+}
+
+# Return which kind of report the given metadata file relates to
+get_kind() {
+ local payload="$(get_key_value "$1" "payload")"
+ if [ ! -r "${payload}" ]; then
+ lecho "Missing payload: ${payload}"
+ echo "undefined"
+ return
+ fi
+ local kind="$(get_extension "${payload}")"
+ if [ "${kind}" = "dmp" ]; then
+ echo "minidump"
+ return
+ fi
+ echo "${kind}"
+}
+
+get_key_value() {
+ local file="$1" key="$2" value
+
+ if [ -f "${file}" ]; then
+ # Return the first entry. There shouldn't be more than one anyways.
+ # Substr at length($1) + 2 skips past the key and following = sign (awk
+ # uses 1-based indexes), but preserves embedded = characters.
+ value=$(sed -n "/^${key}[[:space:]]*=/{s:^[^=]*=::p;q}" "${file}")
+ fi
+
+ echo "${value:-undefined}"
+}
+
+get_keys() {
+ local file="$1" regex="$2"
+
+ awk -F'[[:space:]=]' -vregex="${regex}" \
+ 'match($1, regex) { print $1 }' "${file}"
+}
+
+# Return the board name.
+get_board() {
+ get_key_value "/etc/lsb-release" "CHROMEOS_RELEASE_BOARD"
+}
+
+# Return the channel name (sans "-channel" suffix).
+get_channel() {
+ get_key_value "/etc/lsb-release" "CHROMEOS_RELEASE_TRACK" |
+ sed 's:-channel$::'
+}
+
+# Return the hardware class or "undefined".
+get_hardware_class() {
+ if [ -r "${HWCLASS_PATH}" ]; then
+ cat "${HWCLASS_PATH}"
+ elif crossystem hwid > /dev/null 2>&1; then
+ echo "$(crossystem hwid)"
+ else
+ echo "undefined"
+ fi
+}
+
+send_crash() {
+ local meta_path="$1"
+ local report_payload="$(get_key_value "${meta_path}" "payload")"
+ local kind="$(get_kind "${meta_path}")"
+ local exec_name="$(get_key_value "${meta_path}" "exec_name")"
+ local url="${REPORT_UPLOAD_PROD_URL}"
+ local chromeos_version="$(get_key_value "${meta_path}" "ver")"
+ local board="$(get_board)"
+ local hwclass="$(get_hardware_class)"
+ local write_payload_size="$(get_key_value "${meta_path}" "payload_size")"
+ local log="$(get_key_value "${meta_path}" "log")"
+ local sig="$(get_key_value "${meta_path}" "sig")"
+ local send_payload_size="$(stat --printf=%s "${report_payload}" 2>/dev/null)"
+ local product="$(get_key_value "${meta_path}" "upload_var_prod")"
+ local version="$(get_key_value "${meta_path}" "upload_var_ver")"
+ local upload_prefix="$(get_key_value "${meta_path}" "upload_prefix")"
+ local guid
+
+ set -- \
+ -F "write_payload_size=${write_payload_size}" \
+ -F "send_payload_size=${send_payload_size}"
+ if [ "${sig}" != "undefined" ]; then
+ set -- "$@" \
+ -F "sig=${sig}" \
+ -F "sig2=${sig}"
+ fi
+ if [ -r "${report_payload}" ]; then
+ set -- "$@" \
+ -F "upload_file_${kind}=@${report_payload}"
+ fi
+ if [ "${log}" != "undefined" -a -r "${log}" ]; then
+ set -- "$@" \
+ -F "log=@${log}"
+ fi
+
+ if [ "${upload_prefix}" = "undefined" ]; then
+ upload_prefix=""
+ fi
+
+ # Grab any variable that begins with upload_.
+ local v
+ for k in $(get_keys "${meta_path}" "^upload_"); do
+ v="$(get_key_value "${meta_path}" "${k}")"
+ case ${k} in
+ # Product & version are handled separately.
+ upload_var_prod) ;;
+ upload_var_ver) ;;
+ upload_var_*)
+ set -- "$@" -F "${upload_prefix}${k#upload_var_}=${v}"
+ ;;
+ upload_file_*)
+ if [ -r "${v}" ]; then
+ set -- "$@" -F "${upload_prefix}${k#upload_file_}=@${v}"
+ fi
+ ;;
+ esac
+ done
+
+ # When uploading Chrome reports we need to report the right product and
+ # version. If the meta file does not specify it, use GOOGLE_CRASH_ID
+ # as the product and GOOGLE_CRASH_VERSION_ID as the version.
+ if [ "${product}" = "undefined" ]; then
+ product="$(get_key_value /etc/os-release 'GOOGLE_CRASH_ID')"
+ fi
+ if [ "${version}" = "undefined" ]; then
+ version="$(get_key_value /etc/os-release 'GOOGLE_CRASH_VERSION_ID')"
+ fi
+
+ # If GOOGLE_CRASH_* is undefined, we look for ID and VERSION_ID in
+ # /etc/os-release.
+ if [ "${product}" = "undefined" ]; then
+ product="$(get_key_value /etc/os-release 'ID')"
+ fi
+ if [ "${version}" = "undefined" ]; then
+ version="$(get_key_value /etc/os-release 'VERSION_ID')"
+ fi
+
+ # If ID or VERSION_ID is undefined, we use the default product name
+ # and CHROMEOS_RELEASE_VERSION from /etc/lsb-release.
+ if [ "${product}" = "undefined" ]; then
+ product="${CHROMEOS_PRODUCT}"
+ fi
+ if [ "${version}" = "undefined" ]; then
+ version="${chromeos_version}"
+ fi
+
+ local image_type
+ if is_test_image; then
+ image_type="test"
+ elif is_developer_image; then
+ image_type="dev"
+ elif [ ${FORCE_OFFICIAL} -ne 0 ]; then
+ image_type="force-official"
+ elif is_mock && ! is_mock_successful; then
+ image_type="mock-fail"
+ fi
+
+ local boot_mode
+ if ! crossystem "cros_debug" > /dev/null 2>&1; then
+ # Sanity-check failed that makes sure crossystem exists.
+ lecho "Cannot determine boot mode due to error running crossystem command"
+ boot_mode="missing-crossystem"
+ elif is_developer_mode; then
+ boot_mode="dev"
+ fi
+
+ # Need to strip dashes ourselves as Chrome preserves it in the file
+ # nowadays. This is also what the Chrome breakpad client does.
+ guid=$(tr -d '-' < "${CONSENT_ID}")
+
+ local error_type="$(get_key_value "${meta_path}" "error_type")"
+ [ "${error_type}" = "undefined" ] && error_type=
+
+ lecho "Sending crash:"
+ if [ "${product}" != "${CHROMEOS_PRODUCT}" ]; then
+ lecho " Sending crash report on behalf of ${product}"
+ fi
+ lecho " Metadata: ${meta_path} (${kind})"
+ lecho " Payload: ${report_payload}"
+ lecho " Version: ${version}"
+ [ -n "${image_type}" ] && lecho " Image type: ${image_type}"
+ [ -n "${boot_mode}" ] && lecho " Boot mode: ${boot_mode}"
+ if is_mock; then
+ lecho " Product: ${product}"
+ lecho " URL: ${url}"
+ lecho " Board: ${board}"
+ lecho " HWClass: ${hwclass}"
+ lecho " write_payload_size: ${write_payload_size}"
+ lecho " send_payload_size: ${send_payload_size}"
+ if [ "${log}" != "undefined" ]; then
+ lecho " log: @${log}"
+ fi
+ if [ "${sig}" != "undefined" ]; then
+ lecho " sig: ${sig}"
+ fi
+ fi
+ lecho " Exec name: ${exec_name}"
+ [ -n "${error_type}" ] && lecho " Error type: ${error_type}"
+ if is_mock; then
+ if ! is_mock_successful; then
+ lecho "Mocking unsuccessful send"
+ return 1
+ fi
+ lecho "Mocking successful send"
+ return 0
+ fi
+
+ # Read in the first proxy, if any, for a given URL. NOTE: The
+ # double-quotes are necessary due to a bug in dash with the "local"
+ # builtin command and values that have spaces in them (see
+ # "https://bugs.launchpad.net/ubuntu/+source/dash/+bug/139097").
+ if [ -f "${LIST_PROXIES}" ]; then
+ local proxy ret
+ proxy=$("${LIST_PROXIES}" --quiet "${url}")
+ ret=$?
+ if [ ${ret} -ne 0 ]; then
+ proxy=''
+ lecho -psyslog.warn \
+ "Listing proxies failed with exit code ${ret}"
+ else
+ proxy=$(echo "${proxy}" | head -1)
+ fi
+ fi
+ # if a direct connection should be used, unset the proxy variable.
+ [ "${proxy}" = "direct://" ] && proxy=
+ local report_id="${TMP_DIR}/report_id"
+ local curl_stderr="${TMP_DIR}/curl_stderr"
+
+ set +e
+ curl "${url}" -v ${proxy:+--proxy "$proxy"} \
+ --capath "${RESTRICTED_CERTIFICATES_PATH}" --ciphers HIGH \
+ -F "prod=${product}" \
+ -F "ver=${version}" \
+ -F "board=${board}" \
+ -F "hwclass=${hwclass}" \
+ -F "exec_name=${exec_name}" \
+ ${image_type:+-F "image_type=${image_type}"} \
+ ${boot_mode:+-F "boot_mode=${boot_mode}"} \
+ ${error_type:+-F "error_type=${error_type}"} \
+ -F "guid=${guid}" \
+ -o "${report_id}" \
+ "$@" \
+ 2>"${curl_stderr}"
+ curl_result=$?
+ set -e
+
+ if [ ${curl_result} -eq 0 ]; then
+ local id="$(cat "${report_id}")"
+ local product_name
+ local timestamp="$(date +%s)"
+ case ${product} in
+ Chrome_ChromeOS)
+ if is_official_image; then
+ product_name="Chrome"
+ else
+ product_name="Chromium"
+ fi
+ ;;
+ *)
+ if is_official_image; then
+ product_name="ChromeOS"
+ else
+ product_name="ChromiumOS"
+ fi
+ ;;
+ esac
+ printf '%s,%s,%s\n' \
+ "${timestamp}" "${id}" "${product_name}" >> "${CHROME_CRASH_LOG}"
+ lecho "Crash report receipt ID ${id}"
+ else
+ lecho "Crash sending failed with exit code ${curl_result}: " \
+ "$(cat "${curl_stderr}")"
+ fi
+
+ rm -f "${report_id}"
+
+ return ${curl_result}
+}
+
+# *.meta files always end with done=1 so we can tell if they are complete.
+is_complete_metadata() {
+ grep -q "done=1" "$1"
+}
+
+# Remove the given report path.
+remove_report() {
+ local base="${1%.*}"
+ rm -f -- "${base}".*
+}
+
+# Send all crashes from the given directory. This applies even when we're on a
+# 3G connection (see crosbug.com/3304 for discussion).
+send_crashes() {
+ local dir="$1"
+
+ if [ ! -d "${dir}" ]; then
+ return
+ fi
+
+ # Consider any old files which still have no corresponding meta file
+ # as orphaned, and remove them.
+ for old_file in $(${FIND} "${dir}" -mindepth 1 \
+ -mmin +$((24 * 60)) -type f); do
+ if [ ! -e "$(get_base "${old_file}").meta" ]; then
+ lecho "Removing old orphaned file: ${old_file}."
+ rm -f -- "${old_file}"
+ fi
+ done
+
+ # Look through all metadata (*.meta) files, oldest first. That way, the rate
+ # limit does not stall old crashes if there's a high amount of new crashes
+ # coming in.
+ # For each crash report, first evaluate conditions that might lead to its
+ # removal to honor user choice and to free disk space as soon as possible,
+ # then decide whether it should be sent right now or kept for later sending.
+ for meta_path in $(ls -1tr "${dir}"/*.meta 2>/dev/null); do
+ lecho "Considering metadata ${meta_path}."
+
+ local kind=$(get_kind "${meta_path}")
+ if [ "${kind}" != "minidump" ] && \
+ [ "${kind}" != "kcrash" ] && \
+ [ "${kind}" != "log" ] &&
+ [ "${kind}" != "devcore" ]; then
+ lecho "Unknown report kind ${kind}. Removing report."
+ remove_report "${meta_path}"
+ continue
+ fi
+
+ if ! is_complete_metadata "${meta_path}"; then
+ # This report is incomplete, so if it's old, just remove it.
+ local old_meta=$(${FIND} "${dir}" -mindepth 1 -name \
+ $(basename "${meta_path}") -mmin +$((24 * 60)) -type f)
+ if [ -n "${old_meta}" ]; then
+ lecho "Removing old incomplete metadata."
+ remove_report "${meta_path}"
+ else
+ lecho "Ignoring recent incomplete metadata."
+ fi
+ continue
+ fi
+
+ # Ignore device coredump if device coredump uploading is not allowed.
+ if [ "${kind}" = "devcore" ] && ! is_device_coredump_upload_allowed; then
+ lecho "Ignoring device coredump. Device coredump upload not allowed."
+ continue
+ fi
+
+ if ! is_mock && ! is_official_image; then
+ lecho "Not an official OS version. Removing crash."
+ remove_report "${meta_path}"
+ continue
+ fi
+
+ # Don't send crash reports from previous sessions while we're in guest mode
+ # to avoid the impression that crash reporting was enabled, which it isn't.
+ # (Don't exit right now because subsequent reports may be candidates for
+ # deletion.)
+ if ${METRICS_CLIENT} -g; then
+ lecho "Guest mode has been entered. Delaying crash sending until exited."
+ continue
+ fi
+
+ # Remove existing crashes in case user consent has not (yet) been given or
+ # has been revoked. This must come after the guest mode check because
+ # ${METRICS_CLIENT} always returns "not consented" in guest mode.
+ if ! ${METRICS_CLIENT} -c; then
+ lecho "Crash reporting is disabled. Removing crash."
+ remove_report "${meta_path}"
+ continue
+ fi
+
+ # Skip report if the upload rate is exceeded. (Don't exit right now because
+ # subsequent reports may be candidates for deletion.)
+ if ! check_rate; then
+ lecho "Sending ${meta_path} would exceed rate. Leaving for later."
+ continue
+ fi
+
+ # The .meta file should be written *after* all to-be-uploaded files that it
+ # references. Nevertheless, as a safeguard, a hold-off time of thirty
+ # seconds after writing the .meta file is ensured. Also, sending of crash
+ # reports is spread out randomly by up to SECONDS_SEND_SPREAD. Thus, for
+ # the sleep call the greater of the two delays is used.
+ local now=$(date +%s)
+ local holdoff_time=$(($(stat --format=%Y "${meta_path}") + 30 - ${now}))
+ local spread_time=$(generate_uniform_random "${SECONDS_SEND_SPREAD}")
+ local sleep_time
+ if [ ${spread_time} -gt ${holdoff_time} ]; then
+ sleep_time="${spread_time}"
+ else
+ sleep_time="${holdoff_time}"
+ fi
+ lecho "Scheduled to send in ${sleep_time}s."
+ if ! is_mock; then
+ if ! sleep "${sleep_time}"; then
+ lecho "Sleep failed"
+ return 1
+ fi
+ fi
+
+ # Try to upload.
+ if ! send_crash "${meta_path}"; then
+ lecho "Problem sending ${meta_path}, not removing."
+ continue
+ fi
+
+ # Send was successful, now remove.
+ lecho "Successfully sent crash ${meta_path} and removing."
+ remove_report "${meta_path}"
+ done
+}
+
+usage() {
+ cat <<EOF
+Usage: crash_sender [options]
+
+Options:
+ -e <var>=<val> Set env |var| to |val| (only some vars)
+EOF
+ exit ${1:-1}
+}
+
+parseargs() {
+ # Parse the command line arguments.
+ while [ $# -gt 0 ]; do
+ case $1 in
+ -e)
+ shift
+ case $1 in
+ FORCE_OFFICIAL=*|\
+ MAX_CRASH_RATE=*|\
+ MOCK_DEVELOPER_MODE=*|\
+ OVERRIDE_PAUSE_SENDING=*|\
+ SECONDS_SEND_SPREAD=*)
+ export "$1"
+ ;;
+ *)
+ lecho "Unknown var passed to -e: $1"
+ exit 1
+ ;;
+ esac
+ ;;
+ -h)
+ usage 0
+ ;;
+ *)
+ lecho "Unknown options: $*"
+ exit 1
+ ;;
+ esac
+ shift
+ done
+}
+
+main() {
+ trap cleanup EXIT INT TERM
+
+ parseargs "$@"
+
+ if [ -e "${PAUSE_CRASH_SENDING}" ] && \
+ [ ${OVERRIDE_PAUSE_SENDING} -eq 0 ]; then
+ lecho "Exiting early due to ${PAUSE_CRASH_SENDING}."
+ exit 1
+ fi
+
+ if is_test_image; then
+ lecho "Exiting early due to test image."
+ exit 1
+ fi
+
+ # We don't perform checks on this because we have a master lock with the
+ # CRASH_SENDER_LOCK file. This pid file is for the system to keep track
+ # (like with autotests) that we're still running.
+ echo $$ > "${RUN_FILE}"
+
+ for dependency in "${FIND}" "${METRICS_CLIENT}" \
+ "${RESTRICTED_CERTIFICATES_PATH}"; do
+ if [ ! -x "${dependency}" ]; then
+ lecho "Fatal: Crash sending disabled: ${dependency} not found."
+ exit 1
+ fi
+ done
+
+ TMP_DIR="$(mktemp -d /tmp/crash_sender.XXXXXX)"
+
+ # Send system-wide crashes
+ send_crashes "/var/spool/crash"
+
+ # Send user-specific crashes
+ local d
+ for d in /home/chronos/crash /home/chronos/u-*/crash; do
+ send_crashes "${d}"
+ done
+}
+
+(
+if ! flock -n 9; then
+ lecho "Already running; quitting."
+ crash_done
+ exit 1
+fi
+main "$@"
+) 9>"${CRASH_SENDER_LOCK}"
diff --git a/crash_reporter/dbus_bindings/org.chromium.LibCrosService.xml b/crash_reporter/dbus_bindings/org.chromium.LibCrosService.xml
new file mode 100644
index 0000000..64b8b84
--- /dev/null
+++ b/crash_reporter/dbus_bindings/org.chromium.LibCrosService.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<node name="/org/chromium/LibCrosService"
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+ <interface name="org.chromium.LibCrosServiceInterface">
+ <method name="ResolveNetworkProxy">
+ <arg name="source_url" type="s" direction="in"/>
+ <arg name="signal_interface" type="s" direction="in"/>
+ <arg name="signal_name" type="s" direction="in"/>
+ <annotation name="org.chromium.DBus.Method.Kind" value="simple"/>
+ </method>
+ </interface>
+ <interface name="org.chromium.CrashReporterLibcrosProxyResolvedInterface">
+ <signal name="ProxyResolved">
+ <arg name="source_url" type="s" direction="out"/>
+ <arg name="proxy_info" type="s" direction="out"/>
+ <arg name="error_message" type="s" direction="out"/>
+ </signal>
+ </interface>
+</node>
diff --git a/crash_reporter/init/crash-reporter.conf b/crash_reporter/init/crash-reporter.conf
new file mode 100644
index 0000000..19f2cdb
--- /dev/null
+++ b/crash_reporter/init/crash-reporter.conf
@@ -0,0 +1,27 @@
+# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+description "Initialize crash reporting services"
+author "chromium-os-dev@chromium.org"
+
+# This job merely initializes its service and then terminates; the
+# actual checking and reporting of crash dumps is triggered by an
+# hourly cron job.
+start on starting system-services
+
+pre-start script
+ mkdir -p /var/spool
+
+ # Only allow device coredumps on a "developer system".
+ if ! is_developer_end_user; then
+ # consumer end-user - disable device coredumps, if driver exists.
+ echo 1 > /sys/class/devcoredump/disabled || true
+ fi
+end script
+
+# crash_reporter uses argv[0] as part of the command line for
+# /proc/sys/kernel/core_pattern. That command line is invoked by
+# the kernel, and can't rely on PATH, so argv[0] must be a full
+# path; we invoke it as such here.
+exec /sbin/crash_reporter --init
diff --git a/crash_reporter/init/crash-sender.conf b/crash_reporter/init/crash-sender.conf
new file mode 100644
index 0000000..892186f
--- /dev/null
+++ b/crash_reporter/init/crash-sender.conf
@@ -0,0 +1,11 @@
+# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+description "Run the crash sender periodically"
+author "chromium-os-dev@chromium.org"
+
+start on starting system-services
+stop on stopping system-services
+
+exec periodic_scheduler 3600 14400 crash_sender /sbin/crash_sender
diff --git a/crash_reporter/init/warn-collector.conf b/crash_reporter/init/warn-collector.conf
new file mode 100644
index 0000000..3be80da
--- /dev/null
+++ b/crash_reporter/init/warn-collector.conf
@@ -0,0 +1,12 @@
+# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+description "Runs a daemon which collects and reports kernel warnings"
+author "chromium-os-dev@chromium.org"
+
+start on started system-services
+stop on stopping system-services
+respawn
+
+exec warn_collector
diff --git a/crash_reporter/kernel_collector.cc b/crash_reporter/kernel_collector.cc
new file mode 100644
index 0000000..d86efbd
--- /dev/null
+++ b/crash_reporter/kernel_collector.cc
@@ -0,0 +1,591 @@
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crash-reporter/kernel_collector.h"
+
+#include <map>
+#include <sys/stat.h>
+
+#include <base/files/file_util.h>
+#include <base/logging.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+
+using base::FilePath;
+using base::StringPrintf;
+
+namespace {
+
+const char kDefaultKernelStackSignature[] = "kernel-UnspecifiedStackSignature";
+const char kDumpParentPath[] = "/dev";
+const char kDumpPath[] = "/dev/pstore";
+const char kDumpFormat[] = "dmesg-ramoops-%zu";
+const char kKernelExecName[] = "kernel";
+// Maximum number of records to examine in the kDumpPath.
+const size_t kMaxDumpRecords = 100;
+const pid_t kKernelPid = 0;
+const char kKernelSignatureKey[] = "sig";
+// Byte length of maximum human readable portion of a kernel crash signature.
+const int kMaxHumanStringLength = 40;
+const uid_t kRootUid = 0;
+// Time in seconds from the final kernel log message for a call stack
+// to count towards the signature of the kcrash.
+const int kSignatureTimestampWindow = 2;
+// Kernel log timestamp regular expression.
+const char kTimestampRegex[] = "^<.*>\\[\\s*(\\d+\\.\\d+)\\]";
+
+//
+// These regular expressions enable to us capture the PC in a backtrace.
+// The backtrace is obtained through dmesg or the kernel's preserved/kcrashmem
+// feature.
+//
+// For ARM we see:
+// "<5>[ 39.458982] PC is at write_breakme+0xd0/0x1b4"
+// For MIPS we see:
+// "<5>[ 3378.552000] epc : 804010f0 lkdtm_do_action+0x68/0x3f8"
+// For x86:
+// "<0>[ 37.474699] EIP: [<790ed488>] write_breakme+0x80/0x108
+// SS:ESP 0068:e9dd3efc"
+//
+const char* const kPCRegex[] = {
+ 0,
+ " PC is at ([^\\+ ]+).*",
+ " epc\\s+:\\s+\\S+\\s+([^\\+ ]+).*", // MIPS has an exception program counter
+ " EIP: \\[<.*>\\] ([^\\+ ]+).*", // X86 uses EIP for the program counter
+ " RIP \\[<.*>\\] ([^\\+ ]+).*", // X86_64 uses RIP for the program counter
+};
+
+COMPILE_ASSERT(arraysize(kPCRegex) == KernelCollector::kArchCount,
+ missing_arch_pc_regexp);
+
+} // namespace
+
+KernelCollector::KernelCollector()
+ : is_enabled_(false),
+ ramoops_dump_path_(kDumpPath),
+ records_(0),
+ // We expect crash dumps in the format of architecture we are built for.
+ arch_(GetCompilerArch()) {
+}
+
+KernelCollector::~KernelCollector() {
+}
+
+void KernelCollector::OverridePreservedDumpPath(const FilePath &file_path) {
+ ramoops_dump_path_ = file_path;
+}
+
+bool KernelCollector::ReadRecordToString(std::string *contents,
+ size_t current_record,
+ bool *record_found) {
+ // A record is a ramoops dump. It has an associated size of "record_size".
+ std::string record;
+ std::string captured;
+
+ // Ramoops appends a header to a crash which contains ==== followed by a
+ // timestamp. Ignore the header.
+ pcrecpp::RE record_re(
+ "====\\d+\\.\\d+\n(.*)",
+ pcrecpp::RE_Options().set_multiline(true).set_dotall(true));
+
+ pcrecpp::RE sanity_check_re("\n<\\d+>\\[\\s*(\\d+\\.\\d+)\\]");
+
+ FilePath ramoops_record;
+ GetRamoopsRecordPath(&ramoops_record, current_record);
+ if (!base::ReadFileToString(ramoops_record, &record)) {
+ LOG(ERROR) << "Unable to open " << ramoops_record.value();
+ return false;
+ }
+
+ *record_found = false;
+ if (record_re.FullMatch(record, &captured)) {
+ // Found a ramoops header, so strip the header and append the rest.
+ contents->append(captured);
+ *record_found = true;
+ } else if (sanity_check_re.PartialMatch(record.substr(0, 1024))) {
+ // pstore compression has been added since kernel 3.12. In order to
+ // decompress dmesg correctly, ramoops driver has to strip the header
+ // before handing over the record to the pstore driver, so we don't
+ // need to do it here anymore. However, the sanity check is needed because
+ // sometimes a pstore record is just a chunk of uninitialized memory which
+ // is not the result of a kernel crash. See crbug.com/443764
+ contents->append(record);
+ *record_found = true;
+ } else {
+ LOG(WARNING) << "Found invalid record at " << ramoops_record.value();
+ }
+
+ // Remove the record from pstore after it's found.
+ if (*record_found)
+ base::DeleteFile(ramoops_record, false);
+
+ return true;
+}
+
+void KernelCollector::GetRamoopsRecordPath(FilePath *path,
+ size_t record) {
+ // Disable error "format not a string literal, argument types not checked"
+ // because this is valid, but GNU apparently doesn't bother checking a const
+ // format string.
+ #pragma GCC diagnostic push
+ #pragma GCC diagnostic ignored "-Wformat-nonliteral"
+ *path = ramoops_dump_path_.Append(StringPrintf(kDumpFormat, record));
+ #pragma GCC diagnostic pop
+}
+
+bool KernelCollector::LoadParameters() {
+ // Discover how many ramoops records are being exported by the driver.
+ size_t count;
+
+ for (count = 0; count < kMaxDumpRecords; ++count) {
+ FilePath ramoops_record;
+ GetRamoopsRecordPath(&ramoops_record, count);
+
+ if (!base::PathExists(ramoops_record))
+ break;
+ }
+
+ records_ = count;
+ return (records_ > 0);
+}
+
+bool KernelCollector::LoadPreservedDump(std::string *contents) {
+ // Load dumps from the preserved memory and save them in contents.
+ // Since the system is set to restart on oops we won't actually ever have
+ // multiple records (only 0 or 1), but check in case we don't restart on
+ // oops in the future.
+ bool any_records_found = false;
+ bool record_found = false;
+ // clear contents since ReadFileToString actually appends to the string.
+ contents->clear();
+
+ for (size_t i = 0; i < records_; ++i) {
+ if (!ReadRecordToString(contents, i, &record_found)) {
+ break;
+ }
+ if (record_found) {
+ any_records_found = true;
+ }
+ }
+
+ if (!any_records_found) {
+ LOG(ERROR) << "No valid records found in " << ramoops_dump_path_.value();
+ return false;
+ }
+
+ return true;
+}
+
+void KernelCollector::StripSensitiveData(std::string *kernel_dump) {
+ // Strip any data that the user might not want sent up to the crash servers.
+ // We'll read in from kernel_dump and also place our output there.
+ //
+ // At the moment, the only sensitive data we strip is MAC addresses.
+
+ // Get rid of things that look like MAC addresses, since they could possibly
+ // give information about where someone has been. This is strings that look
+ // like this: 11:22:33:44:55:66
+ // Complications:
+ // - Within a given kernel_dump, want to be able to tell when the same MAC
+ // was used more than once. Thus, we'll consistently replace the first
+ // MAC found with 00:00:00:00:00:01, the second with ...:02, etc.
+ // - ACPI commands look like MAC addresses. We'll specifically avoid getting
+ // rid of those.
+ std::ostringstream result;
+ std::string pre_mac_str;
+ std::string mac_str;
+ std::map<std::string, std::string> mac_map;
+ pcrecpp::StringPiece input(*kernel_dump);
+
+ // This RE will find the next MAC address and can return us the data preceding
+ // the MAC and the MAC itself.
+ pcrecpp::RE mac_re("(.*?)("
+ "[0-9a-fA-F][0-9a-fA-F]:"
+ "[0-9a-fA-F][0-9a-fA-F]:"
+ "[0-9a-fA-F][0-9a-fA-F]:"
+ "[0-9a-fA-F][0-9a-fA-F]:"
+ "[0-9a-fA-F][0-9a-fA-F]:"
+ "[0-9a-fA-F][0-9a-fA-F])",
+ pcrecpp::RE_Options()
+ .set_multiline(true)
+ .set_dotall(true));
+
+ // This RE will identify when the 'pre_mac_str' shows that the MAC address
+ // was really an ACPI cmd. The full string looks like this:
+ // ata1.00: ACPI cmd ef/10:03:00:00:00:a0 (SET FEATURES) filtered out
+ pcrecpp::RE acpi_re("ACPI cmd ef/$",
+ pcrecpp::RE_Options()
+ .set_multiline(true)
+ .set_dotall(true));
+
+ // Keep consuming, building up a result string as we go.
+ while (mac_re.Consume(&input, &pre_mac_str, &mac_str)) {
+ if (acpi_re.PartialMatch(pre_mac_str)) {
+ // We really saw an ACPI command; add to result w/ no stripping.
+ result << pre_mac_str << mac_str;
+ } else {
+ // Found a MAC address; look up in our hash for the mapping.
+ std::string replacement_mac = mac_map[mac_str];
+ if (replacement_mac == "") {
+ // It wasn't present, so build up a replacement string.
+ int mac_id = mac_map.size();
+
+ // Handle up to 2^32 unique MAC address; overkill, but doesn't hurt.
+ replacement_mac = StringPrintf("00:00:%02x:%02x:%02x:%02x",
+ (mac_id & 0xff000000) >> 24,
+ (mac_id & 0x00ff0000) >> 16,
+ (mac_id & 0x0000ff00) >> 8,
+ (mac_id & 0x000000ff));
+ mac_map[mac_str] = replacement_mac;
+ }
+
+ // Dump the string before the MAC and the fake MAC address into result.
+ result << pre_mac_str << replacement_mac;
+ }
+ }
+
+ // One last bit of data might still be in the input.
+ result << input;
+
+ // We'll just assign right back to kernel_dump.
+ *kernel_dump = result.str();
+}
+
+bool KernelCollector::DumpDirMounted() {
+ struct stat st_parent;
+ if (stat(kDumpParentPath, &st_parent)) {
+ PLOG(WARNING) << "Could not stat " << kDumpParentPath;
+ return false;
+ }
+
+ struct stat st_dump;
+ if (stat(kDumpPath, &st_dump)) {
+ PLOG(WARNING) << "Could not stat " << kDumpPath;
+ return false;
+ }
+
+ if (st_parent.st_dev == st_dump.st_dev) {
+ LOG(WARNING) << "Dump dir " << kDumpPath << " not mounted";
+ return false;
+ }
+
+ return true;
+}
+
+bool KernelCollector::Enable() {
+ if (arch_ == kArchUnknown || arch_ >= kArchCount ||
+ kPCRegex[arch_] == nullptr) {
+ LOG(WARNING) << "KernelCollector does not understand this architecture";
+ return false;
+ }
+
+ if (!DumpDirMounted()) {
+ LOG(WARNING) << "Kernel does not support crash dumping";
+ return false;
+ }
+
+ // To enable crashes, we will eventually need to set
+ // the chnv bit in BIOS, but it does not yet work.
+ LOG(INFO) << "Enabling kernel crash handling";
+ is_enabled_ = true;
+ return true;
+}
+
+// Hash a string to a number. We define our own hash function to not
+// be dependent on a C++ library that might change. This function
+// uses basically the same approach as tr1/functional_hash.h but with
+// a larger prime number (16127 vs 131).
+static unsigned HashString(const std::string &input) {
+ unsigned hash = 0;
+ for (size_t i = 0; i < input.length(); ++i)
+ hash = hash * 16127 + input[i];
+ return hash;
+}
+
+void KernelCollector::ProcessStackTrace(
+ pcrecpp::StringPiece kernel_dump,
+ bool print_diagnostics,
+ unsigned *hash,
+ float *last_stack_timestamp,
+ bool *is_watchdog_crash) {
+ pcrecpp::RE line_re("(.+)", pcrecpp::MULTILINE());
+ pcrecpp::RE stack_trace_start_re(std::string(kTimestampRegex) +
+ " (Call Trace|Backtrace):$");
+
+ // Match lines such as the following and grab out "function_name".
+ // The ? may or may not be present.
+ //
+ // For ARM:
+ // <4>[ 3498.731164] [<c0057220>] ? (function_name+0x20/0x2c) from
+ // [<c018062c>] (foo_bar+0xdc/0x1bc)
+ //
+ // For MIPS:
+ // <5>[ 3378.656000] [<804010f0>] lkdtm_do_action+0x68/0x3f8
+ //
+ // For X86:
+ // <4>[ 6066.849504] [<7937bcee>] ? function_name+0x66/0x6c
+ //
+ pcrecpp::RE stack_entry_re(std::string(kTimestampRegex) +
+ "\\s+\\[<[[:xdigit:]]+>\\]" // Matches " [<7937bcee>]"
+ "([\\s\\?(]+)" // Matches " ? (" (ARM) or " ? " (X86)
+ "([^\\+ )]+)"); // Matches until delimiter reached
+ std::string line;
+ std::string hashable;
+ std::string previous_hashable;
+ bool is_watchdog = false;
+
+ *hash = 0;
+ *last_stack_timestamp = 0;
+
+ // Find the last and second-to-last stack traces. The latter is used when
+ // the panic is from a watchdog timeout.
+ while (line_re.FindAndConsume(&kernel_dump, &line)) {
+ std::string certainty;
+ std::string function_name;
+ if (stack_trace_start_re.PartialMatch(line, last_stack_timestamp)) {
+ if (print_diagnostics) {
+ printf("Stack trace starting.%s\n",
+ hashable.empty() ? "" : " Saving prior trace.");
+ }
+ previous_hashable = hashable;
+ hashable.clear();
+ is_watchdog = false;
+ } else if (stack_entry_re.PartialMatch(line,
+ last_stack_timestamp,
+ &certainty,
+ &function_name)) {
+ bool is_certain = certainty.find('?') == std::string::npos;
+ if (print_diagnostics) {
+ printf("@%f: stack entry for %s (%s)\n",
+ *last_stack_timestamp,
+ function_name.c_str(),
+ is_certain ? "certain" : "uncertain");
+ }
+ // Do not include any uncertain (prefixed by '?') frames in our hash.
+ if (!is_certain)
+ continue;
+ if (!hashable.empty())
+ hashable.append("|");
+ if (function_name == "watchdog_timer_fn" ||
+ function_name == "watchdog") {
+ is_watchdog = true;
+ }
+ hashable.append(function_name);
+ }
+ }
+
+ // If the last stack trace contains a watchdog function we assume the panic
+ // is from the watchdog timer, and we hash the previous stack trace rather
+ // than the last one, assuming that the previous stack is that of the hung
+ // thread.
+ //
+ // In addition, if the hashable is empty (meaning all frames are uncertain,
+ // for whatever reason) also use the previous frame, as it cannot be any
+ // worse.
+ if (is_watchdog || hashable.empty()) {
+ hashable = previous_hashable;
+ }
+
+ *hash = HashString(hashable);
+ *is_watchdog_crash = is_watchdog;
+
+ if (print_diagnostics) {
+ printf("Hash based on stack trace: \"%s\" at %f.\n",
+ hashable.c_str(), *last_stack_timestamp);
+ }
+}
+
+// static
+KernelCollector::ArchKind KernelCollector::GetCompilerArch() {
+#if defined(COMPILER_GCC) && defined(ARCH_CPU_ARM_FAMILY)
+ return kArchArm;
+#elif defined(COMPILER_GCC) && defined(ARCH_CPU_MIPS_FAMILY)
+ return kArchMips;
+#elif defined(COMPILER_GCC) && defined(ARCH_CPU_X86_64)
+ return kArchX86_64;
+#elif defined(COMPILER_GCC) && defined(ARCH_CPU_X86_FAMILY)
+ return kArchX86;
+#else
+ return kArchUnknown;
+#endif
+}
+
+bool KernelCollector::FindCrashingFunction(
+ pcrecpp::StringPiece kernel_dump,
+ bool print_diagnostics,
+ float stack_trace_timestamp,
+ std::string *crashing_function) {
+ float timestamp = 0;
+
+ // Use the correct regex for this architecture.
+ pcrecpp::RE eip_re(std::string(kTimestampRegex) + kPCRegex[arch_],
+ pcrecpp::MULTILINE());
+
+ while (eip_re.FindAndConsume(&kernel_dump, ×tamp, crashing_function)) {
+ if (print_diagnostics) {
+ printf("@%f: found crashing function %s\n",
+ timestamp,
+ crashing_function->c_str());
+ }
+ }
+ if (timestamp == 0) {
+ if (print_diagnostics) {
+ printf("Found no crashing function.\n");
+ }
+ return false;
+ }
+ if (stack_trace_timestamp != 0 &&
+ abs(static_cast<int>(stack_trace_timestamp - timestamp))
+ > kSignatureTimestampWindow) {
+ if (print_diagnostics) {
+ printf("Found crashing function but not within window.\n");
+ }
+ return false;
+ }
+ if (print_diagnostics) {
+ printf("Found crashing function %s\n", crashing_function->c_str());
+ }
+ return true;
+}
+
+bool KernelCollector::FindPanicMessage(pcrecpp::StringPiece kernel_dump,
+ bool print_diagnostics,
+ std::string *panic_message) {
+ // Match lines such as the following and grab out "Fatal exception"
+ // <0>[ 342.841135] Kernel panic - not syncing: Fatal exception
+ pcrecpp::RE kernel_panic_re(std::string(kTimestampRegex) +
+ " Kernel panic[^\\:]*\\:\\s*(.*)",
+ pcrecpp::MULTILINE());
+ float timestamp = 0;
+ while (kernel_panic_re.FindAndConsume(&kernel_dump,
+ ×tamp,
+ panic_message)) {
+ if (print_diagnostics) {
+ printf("@%f: panic message %s\n",
+ timestamp,
+ panic_message->c_str());
+ }
+ }
+ if (timestamp == 0) {
+ if (print_diagnostics) {
+ printf("Found no panic message.\n");
+ }
+ return false;
+ }
+ return true;
+}
+
+bool KernelCollector::ComputeKernelStackSignature(
+ const std::string &kernel_dump,
+ std::string *kernel_signature,
+ bool print_diagnostics) {
+ unsigned stack_hash = 0;
+ float last_stack_timestamp = 0;
+ std::string human_string;
+ bool is_watchdog_crash;
+
+ ProcessStackTrace(kernel_dump,
+ print_diagnostics,
+ &stack_hash,
+ &last_stack_timestamp,
+ &is_watchdog_crash);
+
+ if (!FindCrashingFunction(kernel_dump,
+ print_diagnostics,
+ last_stack_timestamp,
+ &human_string)) {
+ if (!FindPanicMessage(kernel_dump, print_diagnostics, &human_string)) {
+ if (print_diagnostics) {
+ printf("Found no human readable string, using empty string.\n");
+ }
+ human_string.clear();
+ }
+ }
+
+ if (human_string.empty() && stack_hash == 0) {
+ if (print_diagnostics) {
+ printf("Found neither a stack nor a human readable string, failing.\n");
+ }
+ return false;
+ }
+
+ human_string = human_string.substr(0, kMaxHumanStringLength);
+ *kernel_signature = StringPrintf("%s-%s%s-%08X",
+ kKernelExecName,
+ (is_watchdog_crash ? "(HANG)-" : ""),
+ human_string.c_str(),
+ stack_hash);
+ return true;
+}
+
+bool KernelCollector::Collect() {
+ std::string kernel_dump;
+ FilePath root_crash_directory;
+
+ if (!LoadParameters()) {
+ return false;
+ }
+ if (!LoadPreservedDump(&kernel_dump)) {
+ return false;
+ }
+ StripSensitiveData(&kernel_dump);
+ if (kernel_dump.empty()) {
+ return false;
+ }
+ std::string signature;
+ if (!ComputeKernelStackSignature(kernel_dump, &signature, false)) {
+ signature = kDefaultKernelStackSignature;
+ }
+
+ std::string reason = "handling";
+ bool feedback = true;
+ if (IsDeveloperImage()) {
+ reason = "developer build - always dumping";
+ feedback = true;
+ } else if (!is_feedback_allowed_function_()) {
+ reason = "ignoring - no consent";
+ feedback = false;
+ }
+
+ LOG(INFO) << "Received prior crash notification from "
+ << "kernel (signature " << signature << ") (" << reason << ")";
+
+ if (feedback) {
+ count_crash_function_();
+
+ if (!GetCreatedCrashDirectoryByEuid(kRootUid,
+ &root_crash_directory,
+ nullptr)) {
+ return true;
+ }
+
+ std::string dump_basename =
+ FormatDumpBasename(kKernelExecName, time(nullptr), kKernelPid);
+ FilePath kernel_crash_path = root_crash_directory.Append(
+ StringPrintf("%s.kcrash", dump_basename.c_str()));
+
+ // We must use WriteNewFile instead of base::WriteFile as we
+ // do not want to write with root access to a symlink that an attacker
+ // might have created.
+ if (WriteNewFile(kernel_crash_path,
+ kernel_dump.data(),
+ kernel_dump.length()) !=
+ static_cast<int>(kernel_dump.length())) {
+ LOG(INFO) << "Failed to write kernel dump to "
+ << kernel_crash_path.value().c_str();
+ return true;
+ }
+
+ AddCrashMetaData(kKernelSignatureKey, signature);
+ WriteCrashMetaData(
+ root_crash_directory.Append(
+ StringPrintf("%s.meta", dump_basename.c_str())),
+ kKernelExecName,
+ kernel_crash_path.value());
+
+ LOG(INFO) << "Stored kcrash to " << kernel_crash_path.value();
+ }
+
+ return true;
+}
diff --git a/crash_reporter/kernel_collector.h b/crash_reporter/kernel_collector.h
new file mode 100644
index 0000000..c8aedfe
--- /dev/null
+++ b/crash_reporter/kernel_collector.h
@@ -0,0 +1,111 @@
+// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CRASH_REPORTER_KERNEL_COLLECTOR_H_
+#define CRASH_REPORTER_KERNEL_COLLECTOR_H_
+
+#include <pcrecpp.h>
+
+#include <string>
+
+#include <base/files/file_path.h>
+#include <base/macros.h>
+#include <gtest/gtest_prod.h> // for FRIEND_TEST
+
+#include "crash-reporter/crash_collector.h"
+
+// Kernel crash collector.
+class KernelCollector : public CrashCollector {
+ public:
+ // Enumeration to specify architecture type.
+ enum ArchKind {
+ kArchUnknown,
+ kArchArm,
+ kArchMips,
+ kArchX86,
+ kArchX86_64,
+
+ kArchCount // Number of architectures.
+ };
+
+ KernelCollector();
+
+ ~KernelCollector() override;
+
+ void OverridePreservedDumpPath(const base::FilePath &file_path);
+
+ // Enable collection.
+ bool Enable();
+
+ // Returns true if the kernel collection currently enabled.
+ bool is_enabled() const { return is_enabled_; }
+
+ // Collect any preserved kernel crash dump. Returns true if there was
+ // a dump (even if there were problems storing the dump), false otherwise.
+ bool Collect();
+
+ // Compute a stack signature string from a kernel dump.
+ bool ComputeKernelStackSignature(const std::string &kernel_dump,
+ std::string *kernel_signature,
+ bool print_diagnostics);
+
+ // Set the architecture of the crash dumps we are looking at.
+ void set_arch(ArchKind arch) { arch_ = arch; }
+ ArchKind arch() const { return arch_; }
+
+ private:
+ friend class KernelCollectorTest;
+ FRIEND_TEST(KernelCollectorTest, LoadPreservedDump);
+ FRIEND_TEST(KernelCollectorTest, StripSensitiveDataBasic);
+ FRIEND_TEST(KernelCollectorTest, StripSensitiveDataBulk);
+ FRIEND_TEST(KernelCollectorTest, StripSensitiveDataSample);
+ FRIEND_TEST(KernelCollectorTest, CollectOK);
+
+ virtual bool DumpDirMounted();
+
+ bool LoadPreservedDump(std::string *contents);
+ void StripSensitiveData(std::string *kernel_dump);
+
+ void GetRamoopsRecordPath(base::FilePath *path, size_t record);
+ bool LoadParameters();
+ bool HasMoreRecords();
+
+ // Read a record to string, modified from file_utils since that didn't
+ // provide a way to restrict the read length.
+ // Return value indicates (only) error state:
+ // * false when we get an error (can't read from dump location).
+ // * true if no error occured.
+ // Not finding a valid record is not an error state and is signaled by the
+ // record_found output parameter.
+ bool ReadRecordToString(std::string *contents,
+ size_t current_record,
+ bool *record_found);
+
+ void ProcessStackTrace(pcrecpp::StringPiece kernel_dump,
+ bool print_diagnostics,
+ unsigned *hash,
+ float *last_stack_timestamp,
+ bool *is_watchdog_crash);
+ bool FindCrashingFunction(pcrecpp::StringPiece kernel_dump,
+ bool print_diagnostics,
+ float stack_trace_timestamp,
+ std::string *crashing_function);
+ bool FindPanicMessage(pcrecpp::StringPiece kernel_dump,
+ bool print_diagnostics,
+ std::string *panic_message);
+
+ // Returns the architecture kind for which we are built.
+ static ArchKind GetCompilerArch();
+
+ bool is_enabled_;
+ base::FilePath ramoops_dump_path_;
+ size_t records_;
+
+ // The architecture of kernel dump strings we are working with.
+ ArchKind arch_;
+
+ DISALLOW_COPY_AND_ASSIGN(KernelCollector);
+};
+
+#endif // CRASH_REPORTER_KERNEL_COLLECTOR_H_
diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc
new file mode 100644
index 0000000..48d94ea
--- /dev/null
+++ b/crash_reporter/kernel_collector_test.cc
@@ -0,0 +1,674 @@
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crash-reporter/kernel_collector_test.h"
+
+#include <unistd.h>
+
+#include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <chromeos/syslog_logging.h>
+#include <gtest/gtest.h>
+
+using base::FilePath;
+using base::StringPrintf;
+using chromeos::FindLog;
+using chromeos::GetLog;
+
+namespace {
+
+int s_crashes = 0;
+bool s_metrics = false;
+
+void CountCrash() {
+ ++s_crashes;
+}
+
+bool IsMetrics() {
+ return s_metrics;
+}
+
+} // namespace
+
+class KernelCollectorTest : public ::testing::Test {
+ protected:
+ void WriteStringToFile(const FilePath &file_path,
+ const char *data) {
+ ASSERT_EQ(strlen(data), base::WriteFile(file_path, data, strlen(data)));
+ }
+
+ void SetUpSuccessfulCollect();
+ void ComputeKernelStackSignatureCommon();
+
+ const FilePath &kcrash_file() const { return test_kcrash_; }
+ const FilePath &test_crash_directory() const { return test_crash_directory_; }
+
+ KernelCollectorMock collector_;
+
+ private:
+ void SetUp() override {
+ s_crashes = 0;
+ s_metrics = true;
+
+ EXPECT_CALL(collector_, SetUpDBus()).WillRepeatedly(testing::Return());
+
+ collector_.Initialize(CountCrash, IsMetrics);
+ ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
+ test_kcrash_ = scoped_temp_dir_.path().Append("kcrash");
+ ASSERT_TRUE(base::CreateDirectory(test_kcrash_));
+ collector_.OverridePreservedDumpPath(test_kcrash_);
+
+ test_kcrash_ = test_kcrash_.Append("dmesg-ramoops-0");
+ ASSERT_FALSE(base::PathExists(test_kcrash_));
+
+ test_crash_directory_ = scoped_temp_dir_.path().Append("crash_directory");
+ ASSERT_TRUE(base::CreateDirectory(test_crash_directory_));
+ chromeos::ClearLog();
+ }
+
+ FilePath test_kcrash_;
+ FilePath test_crash_directory_;
+ base::ScopedTempDir scoped_temp_dir_;
+};
+
+TEST_F(KernelCollectorTest, ComputeKernelStackSignatureBase) {
+ // Make sure the normal build architecture is detected
+ EXPECT_NE(KernelCollector::kArchUnknown, collector_.arch());
+}
+
+TEST_F(KernelCollectorTest, LoadPreservedDump) {
+ ASSERT_FALSE(base::PathExists(kcrash_file()));
+ std::string dump;
+ dump.clear();
+
+ WriteStringToFile(kcrash_file(),
+ "CrashRecordWithoutRamoopsHeader\n<6>[ 0.078852]");
+ ASSERT_TRUE(collector_.LoadParameters());
+ ASSERT_TRUE(collector_.LoadPreservedDump(&dump));
+ ASSERT_EQ("CrashRecordWithoutRamoopsHeader\n<6>[ 0.078852]", dump);
+
+ WriteStringToFile(kcrash_file(), "====1.1\nsomething");
+ ASSERT_TRUE(collector_.LoadParameters());
+ ASSERT_TRUE(collector_.LoadPreservedDump(&dump));
+ ASSERT_EQ("something", dump);
+
+ WriteStringToFile(kcrash_file(), "\x01\x02\xfe\xff random blob");
+ ASSERT_TRUE(collector_.LoadParameters());
+ ASSERT_FALSE(collector_.LoadPreservedDump(&dump));
+ ASSERT_EQ("", dump);
+}
+
+TEST_F(KernelCollectorTest, EnableMissingKernel) {
+ ASSERT_FALSE(collector_.Enable());
+ ASSERT_FALSE(collector_.is_enabled());
+ ASSERT_TRUE(FindLog(
+ "Kernel does not support crash dumping"));
+ ASSERT_EQ(s_crashes, 0);
+}
+
+TEST_F(KernelCollectorTest, EnableOK) {
+ WriteStringToFile(kcrash_file(), "");
+ EXPECT_CALL(collector_, DumpDirMounted()).WillOnce(::testing::Return(true));
+ ASSERT_TRUE(collector_.Enable());
+ ASSERT_TRUE(collector_.is_enabled());
+ ASSERT_TRUE(FindLog("Enabling kernel crash handling"));
+ ASSERT_EQ(s_crashes, 0);
+}
+
+TEST_F(KernelCollectorTest, StripSensitiveDataBasic) {
+ // Basic tests of StripSensitiveData...
+
+ // Make sure we work OK with a string w/ no MAC addresses.
+ const std::string kCrashWithNoMacsOrig =
+ "<7>[111566.131728] PM: Entering mem sleep\n";
+ std::string crash_with_no_macs(kCrashWithNoMacsOrig);
+ collector_.StripSensitiveData(&crash_with_no_macs);
+ EXPECT_EQ(kCrashWithNoMacsOrig, crash_with_no_macs);
+
+ // Make sure that we handle the case where there's nothing before/after the
+ // MAC address.
+ const std::string kJustAMacOrig =
+ "11:22:33:44:55:66";
+ const std::string kJustAMacStripped =
+ "00:00:00:00:00:01";
+ std::string just_a_mac(kJustAMacOrig);
+ collector_.StripSensitiveData(&just_a_mac);
+ EXPECT_EQ(kJustAMacStripped, just_a_mac);
+
+ // Test MAC addresses crammed together to make sure it gets both of them.
+ //
+ // I'm not sure that the code does ideal on these two test cases (they don't
+ // look like two MAC addresses to me), but since we don't see them I think
+ // it's OK to behave as shown here.
+ const std::string kCrammedMacs1Orig =
+ "11:22:33:44:55:66:11:22:33:44:55:66";
+ const std::string kCrammedMacs1Stripped =
+ "00:00:00:00:00:01:00:00:00:00:00:01";
+ std::string crammed_macs_1(kCrammedMacs1Orig);
+ collector_.StripSensitiveData(&crammed_macs_1);
+ EXPECT_EQ(kCrammedMacs1Stripped, crammed_macs_1);
+
+ const std::string kCrammedMacs2Orig =
+ "11:22:33:44:55:6611:22:33:44:55:66";
+ const std::string kCrammedMacs2Stripped =
+ "00:00:00:00:00:0100:00:00:00:00:01";
+ std::string crammed_macs_2(kCrammedMacs2Orig);
+ collector_.StripSensitiveData(&crammed_macs_2);
+ EXPECT_EQ(kCrammedMacs2Stripped, crammed_macs_2);
+
+ // Test case-sensitiveness (we shouldn't be case-senstive).
+ const std::string kCapsMacOrig =
+ "AA:BB:CC:DD:EE:FF";
+ const std::string kCapsMacStripped =
+ "00:00:00:00:00:01";
+ std::string caps_mac(kCapsMacOrig);
+ collector_.StripSensitiveData(&caps_mac);
+ EXPECT_EQ(kCapsMacStripped, caps_mac);
+
+ const std::string kLowerMacOrig =
+ "aa:bb:cc:dd:ee:ff";
+ const std::string kLowerMacStripped =
+ "00:00:00:00:00:01";
+ std::string lower_mac(kLowerMacOrig);
+ collector_.StripSensitiveData(&lower_mac);
+ EXPECT_EQ(kLowerMacStripped, lower_mac);
+}
+
+TEST_F(KernelCollectorTest, StripSensitiveDataBulk) {
+ // Test calling StripSensitiveData w/ lots of MAC addresses in the "log".
+
+ // Test that stripping code handles more than 256 unique MAC addresses, since
+ // that overflows past the last byte...
+ // We'll write up some code that generates 258 unique MAC addresses. Sorta
+ // cheating since the code is very similar to the current code in
+ // StripSensitiveData(), but would catch if someone changed that later.
+ std::string lotsa_macs_orig;
+ std::string lotsa_macs_stripped;
+ int i;
+ for (i = 0; i < 258; i++) {
+ lotsa_macs_orig += StringPrintf(" 11:11:11:11:%02X:%02x",
+ (i & 0xff00) >> 8, i & 0x00ff);
+ lotsa_macs_stripped += StringPrintf(" 00:00:00:00:%02X:%02x",
+ ((i+1) & 0xff00) >> 8, (i+1) & 0x00ff);
+ }
+ std::string lotsa_macs(lotsa_macs_orig);
+ collector_.StripSensitiveData(&lotsa_macs);
+ EXPECT_EQ(lotsa_macs_stripped, lotsa_macs);
+}
+
+TEST_F(KernelCollectorTest, StripSensitiveDataSample) {
+ // Test calling StripSensitiveData w/ some actual lines from a real crash;
+ // included two MAC addresses (though replaced them with some bogusness).
+ const std::string kCrashWithMacsOrig =
+ "<6>[111567.195339] ata1.00: ACPI cmd ef/10:03:00:00:00:a0 (SET FEATURES)"
+ " filtered out\n"
+ "<7>[108539.540144] wlan0: authenticate with 11:22:33:44:55:66 (try 1)\n"
+ "<7>[108539.554973] wlan0: associate with 11:22:33:44:55:66 (try 1)\n"
+ "<6>[110136.587583] usb0: register 'QCUSBNet2k' at usb-0000:00:1d.7-2,"
+ " QCUSBNet Ethernet Device, 99:88:77:66:55:44\n"
+ "<7>[110964.314648] wlan0: deauthenticated from 11:22:33:44:55:66"
+ " (Reason: 6)\n"
+ "<7>[110964.325057] phy0: Removed STA 11:22:33:44:55:66\n"
+ "<7>[110964.325115] phy0: Destroyed STA 11:22:33:44:55:66\n"
+ "<6>[110969.219172] usb0: register 'QCUSBNet2k' at usb-0000:00:1d.7-2,"
+ " QCUSBNet Ethernet Device, 99:88:77:66:55:44\n"
+ "<7>[111566.131728] PM: Entering mem sleep\n";
+ const std::string kCrashWithMacsStripped =
+ "<6>[111567.195339] ata1.00: ACPI cmd ef/10:03:00:00:00:a0 (SET FEATURES)"
+ " filtered out\n"
+ "<7>[108539.540144] wlan0: authenticate with 00:00:00:00:00:01 (try 1)\n"
+ "<7>[108539.554973] wlan0: associate with 00:00:00:00:00:01 (try 1)\n"
+ "<6>[110136.587583] usb0: register 'QCUSBNet2k' at usb-0000:00:1d.7-2,"
+ " QCUSBNet Ethernet Device, 00:00:00:00:00:02\n"
+ "<7>[110964.314648] wlan0: deauthenticated from 00:00:00:00:00:01"
+ " (Reason: 6)\n"
+ "<7>[110964.325057] phy0: Removed STA 00:00:00:00:00:01\n"
+ "<7>[110964.325115] phy0: Destroyed STA 00:00:00:00:00:01\n"
+ "<6>[110969.219172] usb0: register 'QCUSBNet2k' at usb-0000:00:1d.7-2,"
+ " QCUSBNet Ethernet Device, 00:00:00:00:00:02\n"
+ "<7>[111566.131728] PM: Entering mem sleep\n";
+ std::string crash_with_macs(kCrashWithMacsOrig);
+ collector_.StripSensitiveData(&crash_with_macs);
+ EXPECT_EQ(kCrashWithMacsStripped, crash_with_macs);
+}
+
+TEST_F(KernelCollectorTest, CollectPreservedFileMissing) {
+ ASSERT_FALSE(collector_.Collect());
+ ASSERT_FALSE(FindLog("Stored kcrash to "));
+ ASSERT_EQ(0, s_crashes);
+}
+
+TEST_F(KernelCollectorTest, CollectBadDirectory) {
+ WriteStringToFile(kcrash_file(), "====1.1\nsomething");
+ ASSERT_TRUE(collector_.Collect());
+ ASSERT_TRUE(FindLog("Unable to create appropriate crash directory"))
+ << "Did not find expected error string in log: {\n"
+ << GetLog() << "}";
+ ASSERT_EQ(1, s_crashes);
+}
+
+void KernelCollectorTest::SetUpSuccessfulCollect() {
+ collector_.ForceCrashDirectory(test_crash_directory());
+ WriteStringToFile(kcrash_file(), "====1.1\nsomething");
+ ASSERT_EQ(0, s_crashes);
+}
+
+TEST_F(KernelCollectorTest, CollectOptedOut) {
+ SetUpSuccessfulCollect();
+ s_metrics = false;
+ ASSERT_TRUE(collector_.Collect());
+ ASSERT_TRUE(FindLog("(ignoring - no consent)"));
+ ASSERT_EQ(0, s_crashes);
+}
+
+TEST_F(KernelCollectorTest, CollectOK) {
+ SetUpSuccessfulCollect();
+ ASSERT_TRUE(collector_.Collect());
+ ASSERT_EQ(1, s_crashes);
+ ASSERT_TRUE(FindLog("(handling)"));
+ static const char kNamePrefix[] = "Stored kcrash to ";
+ std::string log = chromeos::GetLog();
+ size_t pos = log.find(kNamePrefix);
+ ASSERT_NE(std::string::npos, pos)
+ << "Did not find string \"" << kNamePrefix << "\" in log: {\n"
+ << log << "}";
+ pos += strlen(kNamePrefix);
+ std::string filename = log.substr(pos, std::string::npos);
+ // Take the name up until \n
+ size_t end_pos = filename.find_first_of("\n");
+ ASSERT_NE(std::string::npos, end_pos);
+ filename = filename.substr(0, end_pos);
+ ASSERT_EQ(0, filename.find(test_crash_directory().value()));
+ ASSERT_TRUE(base::PathExists(FilePath(filename)));
+ std::string contents;
+ ASSERT_TRUE(base::ReadFileToString(FilePath(filename), &contents));
+ ASSERT_EQ("something", contents);
+}
+
+// Perform tests which are common across architectures
+void KernelCollectorTest::ComputeKernelStackSignatureCommon() {
+ std::string signature;
+
+ const char kStackButNoPC[] =
+ "<4>[ 6066.829029] [<790340af>] __do_softirq+0xa6/0x143\n";
+ EXPECT_TRUE(
+ collector_.ComputeKernelStackSignature(kStackButNoPC, &signature, false));
+ EXPECT_EQ("kernel--83615F0A", signature);
+
+ const char kMissingEverything[] =
+ "<4>[ 6066.829029] [<790340af>] ? __do_softirq+0xa6/0x143\n";
+ EXPECT_FALSE(
+ collector_.ComputeKernelStackSignature(kMissingEverything,
+ &signature,
+ false));
+
+ // Long message.
+ const char kTruncatedMessage[] =
+ "<0>[ 87.485611] Kernel panic - not syncing: 01234567890123456789"
+ "01234567890123456789X\n";
+ EXPECT_TRUE(
+ collector_.ComputeKernelStackSignature(kTruncatedMessage,
+ &signature,
+ false));
+ EXPECT_EQ("kernel-0123456789012345678901234567890123456789-00000000",
+ signature);
+}
+
+TEST_F(KernelCollectorTest, ComputeKernelStackSignatureARM) {
+ const char kBugToPanic[] =
+ "<5>[ 123.412524] Modules linked in:\n"
+ "<5>[ 123.412534] CPU: 0 Tainted: G W "
+ "(2.6.37-01030-g51cee64 #153)\n"
+ "<5>[ 123.412552] PC is at write_breakme+0xd0/0x1b4\n"
+ "<5>[ 123.412560] LR is at write_breakme+0xc8/0x1b4\n"
+ "<5>[ 123.412569] pc : [<c0058220>] lr : [<c005821c>] "
+ "psr: 60000013\n"
+ "<5>[ 123.412574] sp : f4e0ded8 ip : c04d104c fp : 000e45e0\n"
+ "<5>[ 123.412581] r10: 400ff000 r9 : f4e0c000 r8 : 00000004\n"
+ "<5>[ 123.412589] r7 : f4e0df80 r6 : f4820c80 r5 : 00000004 "
+ "r4 : f4e0dee8\n"
+ "<5>[ 123.412598] r3 : 00000000 r2 : f4e0decc r1 : c05f88a9 "
+ "r0 : 00000039\n"
+ "<5>[ 123.412608] Flags: nZCv IRQs on FIQs on Mode SVC_32 ISA "
+ "ARM Segment user\n"
+ "<5>[ 123.412617] Control: 10c53c7d Table: 34dcc04a DAC: 00000015\n"
+ "<0>[ 123.412626] Process bash (pid: 1014, stack limit = 0xf4e0c2f8)\n"
+ "<0>[ 123.412634] Stack: (0xf4e0ded8 to 0xf4e0e000)\n"
+ "<0>[ 123.412641] dec0: "
+ " f4e0dee8 c0183678\n"
+ "<0>[ 123.412654] dee0: 00000000 00000000 00677562 0000081f c06a6a78 "
+ "400ff000 f4e0dfb0 00000000\n"
+ "<0>[ 123.412666] df00: bec7ab44 000b1719 bec7ab0c c004f498 bec7a314 "
+ "c024acc8 00000001 c018359c\n"
+ "<0>[ 123.412679] df20: f4e0df34 c04d10fc f5803c80 271beb39 000e45e0 "
+ "f5803c80 c018359c c017bfe0\n"
+ "<0>[ 123.412691] df40: 00000004 f4820c80 400ff000 f4e0df80 00000004 "
+ "f4e0c000 00000000 c01383e4\n"
+ "<0>[ 123.412703] df60: f4820c80 400ff000 f4820c80 400ff000 00000000 "
+ "00000000 00000004 c0138578\n"
+ "<0>[ 123.412715] df80: 00000000 00000000 00000004 00000000 00000004 "
+ "402f95d0 00000004 00000004\n"
+ "<0>[ 123.412727] dfa0: c0054984 c00547c0 00000004 402f95d0 00000001 "
+ "400ff000 00000004 00000000\n"
+ "<0>[ 123.412739] dfc0: 00000004 402f95d0 00000004 00000004 400ff000 "
+ "000c194c bec7ab58 000e45e0\n"
+ "<0>[ 123.412751] dfe0: 00000000 bec7aad8 40232520 40284e9c 60000010 "
+ "00000001 00000000 00000000\n"
+ "<5>[ 39.496577] Backtrace:\n"
+ "<5>[ 123.412782] [<c0058220>] (__bug+0x20/0x2c) from [<c0183678>] "
+ "(write_breakme+0xdc/0x1bc)\n"
+ "<5>[ 123.412798] [<c0183678>] (write_breakme+0xdc/0x1bc) from "
+ "[<c017bfe0>] (proc_reg_write+0x88/0x9c)\n";
+ std::string signature;
+
+ collector_.set_arch(KernelCollector::kArchArm);
+ EXPECT_TRUE(
+ collector_.ComputeKernelStackSignature(kBugToPanic, &signature, false));
+ EXPECT_EQ("kernel-write_breakme-97D3E92F", signature);
+
+ ComputeKernelStackSignatureCommon();
+}
+
+TEST_F(KernelCollectorTest, ComputeKernelStackSignatureMIPS) {
+ const char kBugToPanic[] =
+ "<5>[ 3378.472000] lkdtm: Performing direct entry BUG\n"
+ "<5>[ 3378.476000] Kernel bug detected[#1]:\n"
+ "<5>[ 3378.484000] CPU: 0 PID: 185 Comm: dash Not tainted 3.14.0 #1\n"
+ "<5>[ 3378.488000] task: 8fed5220 ti: 8ec4a000 task.ti: 8ec4a000\n"
+ "<5>[ 3378.496000] $ 0 : 00000000 804018b8 804010f0 7785b507\n"
+ "<5>[ 3378.500000] $ 4 : 8061ab64 81204478 81205b20 00000000\n"
+ "<5>[ 3378.508000] $ 8 : 80830000 20746365 72746e65 55422079\n"
+ "<5>[ 3378.512000] $12 : 8ec4be94 000000fc 00000000 00000048\n"
+ "<5>[ 3378.520000] $16 : 00000004 8ef54000 80710000 00000002\n"
+ "<5>[ 3378.528000] $20 : 7765b6d4 00000004 7fffffff 00000002\n"
+ "<5>[ 3378.532000] $24 : 00000001 803dc0dc \n"
+ "<5>[ 3378.540000] $28 : 8ec4a000 8ec4be20 7775438d 804018b8\n"
+ "<5>[ 3378.544000] Hi : 00000000\n"
+ "<5>[ 3378.548000] Lo : 49bf8080\n"
+ "<5>[ 3378.552000] epc : 804010f0 lkdtm_do_action+0x68/0x3f8\n"
+ "<5>[ 3378.560000] Not tainted\n"
+ "<5>[ 3378.564000] ra : 804018b8 direct_entry+0x110/0x154\n"
+ "<5>[ 3378.568000] Status: 3100dc03 KERNEL EXL IE \n"
+ "<5>[ 3378.572000] Cause : 10800024\n"
+ "<5>[ 3378.576000] PrId : 0001a120 (MIPS interAptiv (multi))\n"
+ "<5>[ 3378.580000] Modules linked in: uinput cfg80211 nf_conntrack_ipv6 "
+ "nf_defrag_ipv6 ip6table_filter ip6_tables pcnet32 mii fuse "
+ "ppp_async ppp_generic slhc tun\n"
+ "<5>[ 3378.600000] Process dash (pid: 185, threadinfo=8ec4a000, "
+ "task=8fed5220, tls=77632490)\n"
+ "<5>[ 3378.608000] Stack : 00000006 ffffff9c 00000000 00000000 00000000 "
+ "00000000 8083454a 00000022\n"
+ "<5> 7765baa1 00001fee 80710000 8ef54000 8ec4bf08 00000002 "
+ "7765b6d4 00000004\n"
+ "<5> 7fffffff 00000002 7775438d 805e5158 7fffffff 00000002 "
+ "00000000 7785b507\n"
+ "<5> 806a96bc 00000004 8ef54000 8ec4bf08 00000002 804018b8 "
+ "80710000 806a98bc\n"
+ "<5> 00000002 00000020 00000004 8d515600 77756450 00000004 "
+ "8ec4bf08 802377e4\n"
+ "<5> ...\n"
+ "<5>[ 3378.652000] Call Trace:\n"
+ "<5>[ 3378.656000] [<804010f0>] lkdtm_do_action+0x68/0x3f8\n"
+ "<5>[ 3378.660000] [<804018b8>] direct_entry+0x110/0x154\n"
+ "<5>[ 3378.664000] [<802377e4>] vfs_write+0xe0/0x1bc\n"
+ "<5>[ 3378.672000] [<80237f90>] SyS_write+0x78/0xf8\n"
+ "<5>[ 3378.676000] [<80111888>] handle_sys+0x128/0x14c\n"
+ "<5>[ 3378.680000] \n"
+ "<5>[ 3378.684000] \n"
+ "<5>Code: 3c04806b 0c1793aa 248494f0 <000c000d> 3c04806b 248494fc "
+ "0c04cc7f 2405017a 08100514 \n"
+ "<5>[ 3378.696000] ---[ end trace 75067432f24bbc93 ]---\n";
+ std::string signature;
+
+ collector_.set_arch(KernelCollector::kArchMips);
+ EXPECT_TRUE(
+ collector_.ComputeKernelStackSignature(kBugToPanic, &signature, false));
+ EXPECT_EQ("kernel-lkdtm_do_action-5E600A6B", signature);
+
+ ComputeKernelStackSignatureCommon();
+}
+
+TEST_F(KernelCollectorTest, ComputeKernelStackSignatureX86) {
+ const char kBugToPanic[] =
+ "<4>[ 6066.829029] [<79039d16>] ? run_timer_softirq+0x165/0x1e6\n"
+ "<4>[ 6066.829029] [<790340af>] ignore_old_stack+0xa6/0x143\n"
+ "<0>[ 6066.829029] EIP: [<b82d7c15>] ieee80211_stop_tx_ba_session+"
+ "0xa3/0xb5 [mac80211] SS:ESP 0068:7951febc\n"
+ "<0>[ 6066.829029] CR2: 00000000323038a7\n"
+ "<4>[ 6066.845422] ---[ end trace 12b058bb46c43500 ]---\n"
+ "<0>[ 6066.845747] Kernel panic - not syncing: Fatal exception "
+ "in interrupt\n"
+ "<0>[ 6066.846902] Call Trace:\n"
+ "<4>[ 6066.846902] [<7937a07b>] ? printk+0x14/0x19\n"
+ "<4>[ 6066.949779] [<79379fc1>] panic+0x3e/0xe4\n"
+ "<4>[ 6066.949971] [<7937c5c5>] oops_end+0x73/0x81\n"
+ "<4>[ 6066.950208] [<7901b260>] no_context+0x10d/0x117\n";
+ std::string signature;
+
+ collector_.set_arch(KernelCollector::kArchX86);
+ EXPECT_TRUE(
+ collector_.ComputeKernelStackSignature(kBugToPanic, &signature, false));
+ EXPECT_EQ("kernel-ieee80211_stop_tx_ba_session-DE253569", signature);
+
+ const char kPCButNoStack[] =
+ "<0>[ 6066.829029] EIP: [<b82d7c15>] ieee80211_stop_tx_ba_session+";
+ EXPECT_TRUE(
+ collector_.ComputeKernelStackSignature(kPCButNoStack, &signature, false));
+ EXPECT_EQ("kernel-ieee80211_stop_tx_ba_session-00000000", signature);
+
+ const char kBreakmeBug[] =
+ "<4>[ 180.492137] [<790970c6>] ? handle_mm_fault+0x67f/0x96d\n"
+ "<4>[ 180.492137] [<790dcdfe>] ? proc_reg_write+0x5f/0x73\n"
+ "<4>[ 180.492137] [<790e2224>] ? write_breakme+0x0/0x108\n"
+ "<4>[ 180.492137] [<790dcd9f>] ? proc_reg_write+0x0/0x73\n"
+ "<4>[ 180.492137] [<790ac0aa>] vfs_write+0x85/0xe4\n"
+ "<0>[ 180.492137] Code: c6 44 05 b2 00 89 d8 e8 0c ef 09 00 85 c0 75 "
+ "0b c7 00 00 00 00 00 e9 8e 00 00 00 ba e6 75 4b 79 89 d8 e8 f1 ee 09 "
+ "00 85 c0 75 04 <0f> 0b eb fe ba 58 47 49 79 89 d8 e8 dd ee 09 00 85 "
+ "c0 75 0a 68\n"
+ "<0>[ 180.492137] EIP: [<790e22a4>] write_breakme+0x80/0x108 SS:ESP "
+ "0068:aa3e9efc\n"
+ "<4>[ 180.501800] ---[ end trace 2a6b72965e1b1523 ]---\n"
+ "<0>[ 180.502026] Kernel panic - not syncing: Fatal exception\n"
+ "<4>[ 180.502026] Call Trace:\n"
+ "<4>[ 180.502806] [<79379aba>] ? printk+0x14/0x1a\n"
+ "<4>[ 180.503033] [<79379a00>] panic+0x3e/0xe4\n"
+ "<4>[ 180.503287] [<7937c005>] oops_end+0x73/0x81\n"
+ "<4>[ 180.503520] [<790055dd>] die+0x58/0x5e\n"
+ "<4>[ 180.503538] [<7937b96c>] do_trap+0x8e/0xa7\n"
+ "<4>[ 180.503555] [<79003d70>] ? do_invalid_op+0x0/0x80\n";
+
+ EXPECT_TRUE(
+ collector_.ComputeKernelStackSignature(kBreakmeBug, &signature, false));
+ EXPECT_EQ("kernel-write_breakme-122AB3CD", signature);
+
+ const char kPCLineTooOld[] =
+ "<4>[ 174.492137] [<790970c6>] ignored_function+0x67f/0x96d\n"
+ "<4>[ 175.492137] [<790970c6>] ignored_function2+0x67f/0x96d\n"
+ "<0>[ 174.492137] EIP: [<790e22a4>] write_breakme+0x80/0x108 SS:ESP "
+ "0068:aa3e9efc\n"
+ "<4>[ 180.501800] ---[ end trace 2a6b72965e1b1523 ]---\n"
+ "<4>[ 180.502026] Call Trace:\n"
+ "<0>[ 180.502026] Kernel panic - not syncing: Fatal exception\n"
+ "<4>[ 180.502806] [<79379aba>] printk+0x14/0x1a\n";
+
+ EXPECT_TRUE(
+ collector_.ComputeKernelStackSignature(kPCLineTooOld, &signature, false));
+ EXPECT_EQ("kernel-Fatal exception-ED4C84FE", signature);
+
+ // Panic without EIP line.
+ const char kExamplePanicOnly[] =
+ "<0>[ 87.485611] Kernel panic - not syncing: Testing panic\n"
+ "<4>[ 87.485630] Pid: 2825, comm: bash Tainted: G "
+ "C 2.6.32.23+drm33.10 #1\n"
+ "<4>[ 87.485639] Call Trace:\n"
+ "<4>[ 87.485660] [<8133f71d>] ? printk+0x14/0x17\n"
+ "<4>[ 87.485674] [<8133f663>] panic+0x3e/0xe4\n"
+ "<4>[ 87.485689] [<810d062e>] write_breakme+0xaa/0x124\n";
+ EXPECT_TRUE(
+ collector_.ComputeKernelStackSignature(kExamplePanicOnly,
+ &signature,
+ false));
+ EXPECT_EQ("kernel-Testing panic-E0FC3552", signature);
+
+ // Panic from hung task.
+ const char kHungTaskBreakMe[] =
+ "<3>[ 720.459157] INFO: task bash:2287 blocked blah blah\n"
+ "<5>[ 720.459282] Call Trace:\n"
+ "<5>[ 720.459307] [<810a457b>] ? __dentry_open+0x186/0x23e\n"
+ "<5>[ 720.459323] [<810b9c71>] ? mntput_no_expire+0x29/0xe2\n"
+ "<5>[ 720.459336] [<810b9d48>] ? mntput+0x1e/0x20\n"
+ "<5>[ 720.459350] [<810ad135>] ? path_put+0x1a/0x1d\n"
+ "<5>[ 720.459366] [<8137cacc>] schedule+0x4d/0x4f\n"
+ "<5>[ 720.459379] [<8137ccfb>] schedule_timeout+0x26/0xaf\n"
+ "<5>[ 720.459394] [<8102127e>] ? should_resched+0xd/0x27\n"
+ "<5>[ 720.459409] [<81174d1f>] ? _copy_from_user+0x3c/0x50\n"
+ "<5>[ 720.459423] [<8137cd9e>] "
+ "schedule_timeout_uninterruptible+0x1a/0x1c\n"
+ "<5>[ 720.459438] [<810dee63>] write_breakme+0xb3/0x178\n"
+ "<5>[ 720.459453] [<810dedb0>] ? meminfo_proc_show+0x2f2/0x2f2\n"
+ "<5>[ 720.459467] [<810d94ae>] proc_reg_write+0x6d/0x87\n"
+ "<5>[ 720.459481] [<810d9441>] ? proc_reg_poll+0x76/0x76\n"
+ "<5>[ 720.459493] [<810a5e9e>] vfs_write+0x79/0xa5\n"
+ "<5>[ 720.459505] [<810a6011>] sys_write+0x40/0x65\n"
+ "<5>[ 720.459519] [<8137e677>] sysenter_do_call+0x12/0x26\n"
+ "<0>[ 720.459530] Kernel panic - not syncing: hung_task: blocked tasks\n"
+ "<5>[ 720.459768] Pid: 31, comm: khungtaskd Tainted: "
+ "G C 3.0.8 #1\n"
+ "<5>[ 720.459998] Call Trace:\n"
+ "<5>[ 720.460140] [<81378a35>] panic+0x53/0x14a\n"
+ "<5>[ 720.460312] [<8105f875>] watchdog+0x15b/0x1a0\n"
+ "<5>[ 720.460495] [<8105f71a>] ? hung_task_panic+0x16/0x16\n"
+ "<5>[ 720.460693] [<81043af3>] kthread+0x67/0x6c\n"
+ "<5>[ 720.460862] [<81043a8c>] ? __init_kthread_worker+0x2d/0x2d\n"
+ "<5>[ 720.461106] [<8137eb9e>] kernel_thread_helper+0x6/0x10\n";
+
+ EXPECT_TRUE(
+ collector_.ComputeKernelStackSignature(kHungTaskBreakMe,
+ &signature,
+ false));
+
+ EXPECT_EQ("kernel-(HANG)-hung_task: blocked tasks-600B37EA", signature);
+
+ // Panic with all question marks in the last stack trace.
+ const char kUncertainStackTrace[] =
+ "<0>[56279.689669] ------------[ cut here ]------------\n"
+ "<2>[56279.689677] kernel BUG at /build/x86-alex/tmp/portage/"
+ "sys-kernel/chromeos-kernel-0.0.1-r516/work/chromeos-kernel-0.0.1/"
+ "kernel/timer.c:844!\n"
+ "<0>[56279.689683] invalid opcode: 0000 [#1] SMP \n"
+ "<0>[56279.689688] last sysfs file: /sys/power/state\n"
+ "<5>[56279.689692] Modules linked in: nls_iso8859_1 nls_cp437 vfat fat "
+ "gobi usbnet tsl2583(C) industrialio(C) snd_hda_codec_realtek "
+ "snd_hda_intel i2c_dev snd_hda_codec snd_hwdep qcserial snd_pcm usb_wwan "
+ "i2c_i801 snd_timer nm10_gpio snd_page_alloc rtc_cmos fuse "
+ "nf_conntrack_ipv6 nf_defrag_ipv6 uvcvideo videodev ip6table_filter "
+ "ath9k ip6_tables ipv6 mac80211 ath9k_common ath9k_hw ath cfg80211 "
+ "xt_mark\n"
+ "<5>[56279.689731] \n"
+ "<5>[56279.689738] Pid: 24607, comm: powerd_suspend Tainted: G "
+ "WC 2.6.38.3+ #1 SAMSUNG ELECTRONICS CO., LTD. Alex/G100 \n"
+ "<5>[56279.689748] EIP: 0060:[<8103e3ea>] EFLAGS: 00210286 CPU: 3\n"
+ "<5>[56279.689758] EIP is at add_timer+0xd/0x1b\n"
+ "<5>[56279.689762] EAX: f5e00684 EBX: f5e003c0 ECX: 00000002 EDX: "
+ "00200246\n"
+ "<5>[56279.689767] ESI: f5e003c0 EDI: d28bc03c EBP: d2be5e40 ESP: "
+ "d2be5e40\n"
+ "<5>[56279.689772] DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068\n"
+ "<0>[56279.689778] Process powerd_suspend (pid: 24607, ti=d2be4000 "
+ "task=f5dc9b60 task.ti=d2be4000)\n"
+ "<0>[56279.689782] Stack:\n"
+ "<5>[56279.689785] d2be5e4c f8dccced f4ac02c0 d2be5e70 f8ddc752 "
+ "f5e003c0 f4ac0458 f4ac092c\n"
+ "<5>[56279.689797] f4ac043c f4ac02c0 f4ac0000 f4ac007c d2be5e7c "
+ "f8dd4a33 f4ac0164 d2be5e94\n"
+ "<5>[56279.689809] f87e0304 f69ff0cc f4ac0164 f87e02a4 f4ac0164 "
+ "d2be5eb0 81248968 00000000\n"
+ "<0>[56279.689821] Call Trace:\n"
+ "<5>[56279.689840] [<f8dccced>] ieee80211_sta_restart+0x25/0x8c "
+ "[mac80211]\n"
+ "<5>[56279.689854] [<f8ddc752>] ieee80211_reconfig+0x2e9/0x339 "
+ "[mac80211]\n"
+ "<5>[56279.689869] [<f8dd4a33>] ieee80211_aes_cmac+0x182d/0x184e "
+ "[mac80211]\n"
+ "<5>[56279.689883] [<f87e0304>] cfg80211_get_dev_from_info+0x29b/0x2c0 "
+ "[cfg80211]\n"
+ "<5>[56279.689895] [<f87e02a4>] ? "
+ "cfg80211_get_dev_from_info+0x23b/0x2c0 [cfg80211]\n"
+ "<5>[56279.689904] [<81248968>] legacy_resume+0x25/0x5d\n"
+ "<5>[56279.689910] [<812490ae>] device_resume+0xdd/0x110\n"
+ "<5>[56279.689917] [<812491c2>] dpm_resume_end+0xe1/0x271\n"
+ "<5>[56279.689925] [<81060481>] suspend_devices_and_enter+0x18b/0x1de\n"
+ "<5>[56279.689932] [<810605ba>] enter_state+0xe6/0x132\n"
+ "<5>[56279.689939] [<8105fd4b>] state_store+0x91/0x9d\n"
+ "<5>[56279.689945] [<8105fcba>] ? state_store+0x0/0x9d\n"
+ "<5>[56279.689953] [<81178fb1>] kobj_attr_store+0x16/0x22\n"
+ "<5>[56279.689961] [<810eea5e>] sysfs_write_file+0xc1/0xec\n"
+ "<5>[56279.689969] [<810af443>] vfs_write+0x8f/0x101\n"
+ "<5>[56279.689975] [<810ee99d>] ? sysfs_write_file+0x0/0xec\n"
+ "<5>[56279.689982] [<810af556>] sys_write+0x40/0x65\n"
+ "<5>[56279.689989] [<81002d57>] sysenter_do_call+0x12/0x26\n"
+ "<0>[56279.689993] Code: c1 d3 e2 4a 89 55 f4 f7 d2 21 f2 6a 00 31 c9 89 "
+ "d8 e8 6e fd ff ff 5a 8d 65 f8 5b 5e 5d c3 55 89 e5 3e 8d 74 26 00 83 38 "
+ "00 74 04 <0f> 0b eb fe 8b 50 08 e8 6f ff ff ff 5d c3 55 89 e5 3e 8d 74 "
+ "26 \n"
+ "<0>[56279.690009] EIP: [<8103e3ea>] add_timer+0xd/0x1b SS:ESP "
+ "0068:d2be5e40\n"
+ "<4>[56279.690113] ---[ end trace b71141bb67c6032a ]---\n"
+ "<7>[56279.694069] wlan0: deauthenticated from 00:00:00:00:00:01 "
+ "(Reason: 6)\n"
+ "<0>[56279.703465] Kernel panic - not syncing: Fatal exception\n"
+ "<5>[56279.703471] Pid: 24607, comm: powerd_suspend Tainted: G D "
+ "WC 2.6.38.3+ #1\n"
+ "<5>[56279.703475] Call Trace:\n"
+ "<5>[56279.703483] [<8136648c>] ? panic+0x55/0x152\n"
+ "<5>[56279.703491] [<810057fa>] ? oops_end+0x73/0x81\n"
+ "<5>[56279.703497] [<81005a44>] ? die+0xed/0xf5\n"
+ "<5>[56279.703503] [<810033cb>] ? do_trap+0x7a/0x80\n"
+ "<5>[56279.703509] [<8100369b>] ? do_invalid_op+0x0/0x80\n"
+ "<5>[56279.703515] [<81003711>] ? do_invalid_op+0x76/0x80\n"
+ "<5>[56279.703522] [<8103e3ea>] ? add_timer+0xd/0x1b\n"
+ "<5>[56279.703529] [<81025e23>] ? check_preempt_curr+0x2e/0x69\n"
+ "<5>[56279.703536] [<8102ef28>] ? ttwu_post_activation+0x5a/0x11b\n"
+ "<5>[56279.703543] [<8102fa8d>] ? try_to_wake_up+0x213/0x21d\n"
+ "<5>[56279.703550] [<81368b7f>] ? error_code+0x67/0x6c\n"
+ "<5>[56279.703557] [<8103e3ea>] ? add_timer+0xd/0x1b\n"
+ "<5>[56279.703577] [<f8dccced>] ? ieee80211_sta_restart+0x25/0x8c "
+ "[mac80211]\n"
+ "<5>[56279.703591] [<f8ddc752>] ? ieee80211_reconfig+0x2e9/0x339 "
+ "[mac80211]\n"
+ "<5>[56279.703605] [<f8dd4a33>] ? ieee80211_aes_cmac+0x182d/0x184e "
+ "[mac80211]\n"
+ "<5>[56279.703618] [<f87e0304>] ? "
+ "cfg80211_get_dev_from_info+0x29b/0x2c0 [cfg80211]\n"
+ "<5>[56279.703630] [<f87e02a4>] ? "
+ "cfg80211_get_dev_from_info+0x23b/0x2c0 [cfg80211]\n"
+ "<5>[56279.703637] [<81248968>] ? legacy_resume+0x25/0x5d\n"
+ "<5>[56279.703643] [<812490ae>] ? device_resume+0xdd/0x110\n"
+ "<5>[56279.703649] [<812491c2>] ? dpm_resume_end+0xe1/0x271\n"
+ "<5>[56279.703657] [<81060481>] ? "
+ "suspend_devices_and_enter+0x18b/0x1de\n"
+ "<5>[56279.703663] [<810605ba>] ? enter_state+0xe6/0x132\n"
+ "<5>[56279.703670] [<8105fd4b>] ? state_store+0x91/0x9d\n"
+ "<5>[56279.703676] [<8105fcba>] ? state_store+0x0/0x9d\n"
+ "<5>[56279.703683] [<81178fb1>] ? kobj_attr_store+0x16/0x22\n"
+ "<5>[56279.703690] [<810eea5e>] ? sysfs_write_file+0xc1/0xec\n"
+ "<5>[56279.703697] [<810af443>] ? vfs_write+0x8f/0x101\n"
+ "<5>[56279.703703] [<810ee99d>] ? sysfs_write_file+0x0/0xec\n"
+ "<5>[56279.703709] [<810af556>] ? sys_write+0x40/0x65\n"
+ "<5>[56279.703716] [<81002d57>] ? sysenter_do_call+0x12/0x26\n";
+
+ EXPECT_TRUE(
+ collector_.ComputeKernelStackSignature(kUncertainStackTrace,
+ &signature,
+ false));
+ // The first trace contains only uncertain entries and its hash is 00000000,
+ // so, if we used that, the signature would be kernel-add_timer-00000000.
+ // Instead we use the second-to-last trace for the hash.
+ EXPECT_EQ("kernel-add_timer-B5178878", signature);
+
+ ComputeKernelStackSignatureCommon();
+}
diff --git a/crash_reporter/kernel_collector_test.h b/crash_reporter/kernel_collector_test.h
new file mode 100644
index 0000000..75ac01e
--- /dev/null
+++ b/crash_reporter/kernel_collector_test.h
@@ -0,0 +1,19 @@
+// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CRASH_REPORTER_KERNEL_COLLECTOR_TEST_H_
+#define CRASH_REPORTER_KERNEL_COLLECTOR_TEST_H_
+
+#include "crash-reporter/kernel_collector.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+class KernelCollectorMock : public KernelCollector {
+ public:
+ MOCK_METHOD0(DumpDirMounted, bool());
+ MOCK_METHOD0(SetUpDBus, void());
+};
+
+#endif // CRASH_REPORTER_KERNEL_COLLECTOR_TEST_H_
diff --git a/crash_reporter/kernel_log_collector.sh b/crash_reporter/kernel_log_collector.sh
new file mode 100644
index 0000000..d38479e
--- /dev/null
+++ b/crash_reporter/kernel_log_collector.sh
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Usage example: "kernel_log_collector.sh XXX YYY"
+# This script searches logs in the /var/log/messages which have the keyword XXX.
+# And only those logs which are within the last YYY seconds of the latest log
+# that has the keyword XXX are printed.
+
+# Kernel log has the possible formats:
+# 2013-06-14T16:31:40.514513-07:00 localhost kernel: [ 2.682472] MSG MSG ...
+# 2013-06-19T20:38:58.661826+00:00 localhost kernel: [ 1.668092] MSG MSG ...
+
+search_key=$1
+time_duration=$2
+msg_pattern="^[0-9-]*T[0-9:.+-]* localhost kernel"
+
+die() {
+ echo "kernel_log_collector: $*" >&2
+ exit 1
+}
+
+get_timestamp() {
+ timestamp="$(echo $1 | cut -d " " -f 1)"
+ timestamp="$(date -d "${timestamp}" +%s)" || exit $?
+ echo "${timestamp}"
+}
+
+last_line=$(grep "${msg_pattern}" /var/log/messages | grep -- "${search_key}" | tail -n 1)
+
+if [ -n "${last_line}" ]; then
+ if ! allowed_timestamp=$(get_timestamp "${last_line}"); then
+ die "coule not get timestamp from: ${last_line}"
+ fi
+ : $(( allowed_timestamp -= ${time_duration} ))
+ grep "${msg_pattern}" /var/log/messages | grep -- "${search_key}" | while read line; do
+ if ! timestamp=$(get_timestamp "${line}"); then
+ die "could not get timestamp from: ${line}"
+ fi
+ if [ ${timestamp} -gt ${allowed_timestamp} ]; then
+ echo "${line}"
+ fi
+ done
+fi
+
+echo "END-OF-LOG"
+
diff --git a/crash_reporter/kernel_warning_collector.cc b/crash_reporter/kernel_warning_collector.cc
new file mode 100644
index 0000000..5dcd1f6
--- /dev/null
+++ b/crash_reporter/kernel_warning_collector.cc
@@ -0,0 +1,101 @@
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crash-reporter/kernel_warning_collector.h"
+
+#include <base/files/file_util.h>
+#include <base/logging.h>
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+
+namespace {
+const char kExecName[] = "kernel-warning";
+const char kKernelWarningSignatureKey[] = "sig";
+const char kKernelWarningPath[] = "/var/run/kwarn/warning";
+const pid_t kKernelPid = 0;
+const uid_t kRootUid = 0;
+} // namespace
+
+using base::FilePath;
+using base::StringPrintf;
+
+KernelWarningCollector::KernelWarningCollector() {
+}
+
+KernelWarningCollector::~KernelWarningCollector() {
+}
+
+bool KernelWarningCollector::LoadKernelWarning(std::string *content,
+ std::string *signature) {
+ FilePath kernel_warning_path(kKernelWarningPath);
+ if (!base::ReadFileToString(kernel_warning_path, content)) {
+ LOG(ERROR) << "Could not open " << kKernelWarningPath;
+ return false;
+ }
+ // The signature is in the first line.
+ std::string::size_type end_position = content->find('\n');
+ if (end_position == std::string::npos) {
+ LOG(ERROR) << "unexpected kernel warning format";
+ return false;
+ }
+ *signature = content->substr(0, end_position);
+ return true;
+}
+
+bool KernelWarningCollector::Collect() {
+ std::string reason = "normal collection";
+ bool feedback = true;
+ if (IsDeveloperImage()) {
+ reason = "always collect from developer builds";
+ feedback = true;
+ } else if (!is_feedback_allowed_function_()) {
+ reason = "no user consent";
+ feedback = false;
+ }
+
+ LOG(INFO) << "Processing kernel warning: " << reason;
+
+ if (!feedback) {
+ return true;
+ }
+
+ std::string kernel_warning;
+ std::string warning_signature;
+ if (!LoadKernelWarning(&kernel_warning, &warning_signature)) {
+ return true;
+ }
+
+ FilePath root_crash_directory;
+ if (!GetCreatedCrashDirectoryByEuid(kRootUid, &root_crash_directory,
+ nullptr)) {
+ return true;
+ }
+
+ std::string dump_basename =
+ FormatDumpBasename(kExecName, time(nullptr), kKernelPid);
+ FilePath kernel_crash_path = root_crash_directory.Append(
+ StringPrintf("%s.kcrash", dump_basename.c_str()));
+
+ // We must use WriteNewFile instead of base::WriteFile as we
+ // do not want to write with root access to a symlink that an attacker
+ // might have created.
+ if (WriteNewFile(kernel_crash_path,
+ kernel_warning.data(),
+ kernel_warning.length()) !=
+ static_cast<int>(kernel_warning.length())) {
+ LOG(INFO) << "Failed to write kernel warning to "
+ << kernel_crash_path.value().c_str();
+ return true;
+ }
+
+ AddCrashMetaData(kKernelWarningSignatureKey, warning_signature);
+ WriteCrashMetaData(
+ root_crash_directory.Append(
+ StringPrintf("%s.meta", dump_basename.c_str())),
+ kExecName, kernel_crash_path.value());
+
+ LOG(INFO) << "Stored kernel warning into " << kernel_crash_path.value();
+ return true;
+}
diff --git a/crash_reporter/kernel_warning_collector.h b/crash_reporter/kernel_warning_collector.h
new file mode 100644
index 0000000..f326b23
--- /dev/null
+++ b/crash_reporter/kernel_warning_collector.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CRASH_REPORTER_KERNEL_WARNING_COLLECTOR_H_
+#define CRASH_REPORTER_KERNEL_WARNING_COLLECTOR_H_
+
+#include <string>
+
+#include <base/macros.h>
+#include <gtest/gtest_prod.h> // for FRIEND_TEST
+
+#include "crash-reporter/crash_collector.h"
+
+// Kernel warning collector.
+class KernelWarningCollector : public CrashCollector {
+ public:
+ KernelWarningCollector();
+
+ ~KernelWarningCollector() override;
+
+ // Collects warning.
+ bool Collect();
+
+ private:
+ friend class KernelWarningCollectorTest;
+ FRIEND_TEST(KernelWarningCollectorTest, CollectOK);
+
+ // Reads the full content of the kernel warn dump and its signature.
+ bool LoadKernelWarning(std::string *content, std::string *signature);
+
+ DISALLOW_COPY_AND_ASSIGN(KernelWarningCollector);
+};
+
+#endif // CRASH_REPORTER_KERNEL_WARNING_COLLECTOR_H_
diff --git a/crash_reporter/list_proxies.cc b/crash_reporter/list_proxies.cc
new file mode 100644
index 0000000..de6ef0a
--- /dev/null
+++ b/crash_reporter/list_proxies.cc
@@ -0,0 +1,291 @@
+// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <sysexits.h>
+#include <unistd.h> // for isatty()
+
+#include <string>
+#include <vector>
+
+#include <base/cancelable_callback.h>
+#include <base/command_line.h>
+#include <base/files/file_util.h>
+#include <base/memory/weak_ptr.h>
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/string_tokenizer.h>
+#include <base/strings/string_util.h>
+#include <base/values.h>
+#include <chromeos/daemons/dbus_daemon.h>
+#include <chromeos/syslog_logging.h>
+
+#include "libcrosservice/dbus-proxies.h"
+
+using std::unique_ptr;
+
+namespace {
+
+const char kLibCrosProxyResolvedSignalInterface[] =
+ "org.chromium.CrashReporterLibcrosProxyResolvedInterface";
+const char kLibCrosProxyResolvedName[] = "ProxyResolved";
+const char kLibCrosServiceName[] = "org.chromium.LibCrosService";
+const char kNoProxy[] = "direct://";
+
+const int kTimeoutDefaultSeconds = 5;
+
+const char kHelp[] = "help";
+const char kQuiet[] = "quiet";
+const char kTimeout[] = "timeout";
+const char kVerbose[] = "verbose";
+// Help message to show when the --help command line switch is specified.
+const char kHelpMessage[] =
+ "Chromium OS Crash helper: proxy lister\n"
+ "\n"
+ "Available Switches:\n"
+ " --quiet Only print the proxies\n"
+ " --verbose Print additional messages even when not run from a TTY\n"
+ " --timeout=N Set timeout for browser resolving proxies (default is 5)\n"
+ " --help Show this help.\n";
+
+// Copied from src/update_engine/chrome_browser_proxy_resolver.cc
+// Parses the browser's answer for resolved proxies. It returns a
+// list of strings, each of which is a resolved proxy.
+std::vector<std::string> ParseProxyString(const std::string& input) {
+ std::vector<std::string> ret;
+ // Some of this code taken from
+ // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_server.cc and
+ // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_list.cc
+ base::StringTokenizer entry_tok(input, ";");
+ while (entry_tok.GetNext()) {
+ std::string token = entry_tok.token();
+ base::TrimWhitespaceASCII(token, base::TRIM_ALL, &token);
+
+ // Start by finding the first space (if any).
+ std::string::iterator space;
+ for (space = token.begin(); space != token.end(); ++space) {
+ if (IsAsciiWhitespace(*space)) {
+ break;
+ }
+ }
+
+ std::string scheme = std::string(token.begin(), space);
+ base::StringToLowerASCII(&scheme);
+ // Chrome uses "socks" to mean socks4 and "proxy" to mean http.
+ if (scheme == "socks")
+ scheme += "4";
+ else if (scheme == "proxy")
+ scheme = "http";
+ else if (scheme != "https" &&
+ scheme != "socks4" &&
+ scheme != "socks5" &&
+ scheme != "direct")
+ continue; // Invalid proxy scheme
+
+ std::string host_and_port = std::string(space, token.end());
+ base::TrimWhitespaceASCII(host_and_port, base::TRIM_ALL, &host_and_port);
+ if (scheme != "direct" && host_and_port.empty())
+ continue; // Must supply host/port when non-direct proxy used.
+ ret.push_back(scheme + "://" + host_and_port);
+ }
+ if (ret.empty() || *ret.rbegin() != kNoProxy)
+ ret.push_back(kNoProxy);
+ return ret;
+}
+
+// A class for interfacing with Chrome to resolve proxies for a given source
+// url. The class is initialized with the given source url to check, the
+// signal interface and name that Chrome will reply to, and how long to wait
+// for the resolve request to timeout. Once initialized, the Run() function
+// must be called, which blocks on the D-Bus call to Chrome. The call returns
+// after either the timeout or the proxy has been resolved. The resolved
+// proxies can then be accessed through the proxies() function.
+class ProxyResolver : public chromeos::DBusDaemon {
+ public:
+ ProxyResolver(const std::string& source_url,
+ const std::string& signal_interface,
+ const std::string& signal_name,
+ base::TimeDelta timeout)
+ : source_url_(source_url),
+ signal_interface_(signal_interface),
+ signal_name_(signal_name),
+ timeout_(timeout),
+ weak_ptr_factory_(this),
+ timeout_callback_(base::Bind(&ProxyResolver::HandleBrowserTimeout,
+ weak_ptr_factory_.GetWeakPtr())) {}
+
+ ~ProxyResolver() override {}
+
+ const std::vector<std::string>& proxies() {
+ return proxies_;
+ }
+
+ int Run() override {
+ // Add task for if the browser proxy call times out.
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ timeout_callback_.callback(),
+ timeout_);
+
+ return chromeos::DBusDaemon::Run();
+ }
+
+ protected:
+ // If the browser times out, quit the run loop.
+ void HandleBrowserTimeout() {
+ LOG(ERROR) << "Timeout while waiting for browser to resolve proxy";
+ Quit();
+ }
+
+ // If the signal handler connects successfully, call the browser's
+ // ResolveNetworkProxy D-Bus method. Otherwise, don't do anything and let
+ // the timeout task quit the run loop.
+ void HandleDBusSignalConnected(const std::string& interface,
+ const std::string& signal,
+ bool success) {
+ if (!success) {
+ LOG(ERROR) << "Could not connect to signal " << interface << "."
+ << signal;
+ timeout_callback_.Cancel();
+ Quit();
+ return;
+ }
+
+ chromeos::ErrorPtr error;
+ call_proxy_->ResolveNetworkProxy(source_url_,
+ signal_interface_,
+ signal_name_,
+ &error);
+
+ if (error) {
+ LOG(ERROR) << "Call to ResolveNetworkProxy failed: "
+ << error->GetMessage();
+ timeout_callback_.Cancel();
+ Quit();
+ }
+ }
+
+ // Handle incoming ProxyResolved signal.
+ void HandleProxyResolvedSignal(const std::string& source_url,
+ const std::string& proxy_info,
+ const std::string& error_message) {
+ timeout_callback_.Cancel();
+ proxies_ = ParseProxyString(proxy_info);
+ LOG(INFO) << "Found proxies via browser signal: "
+ << JoinString(proxies_, 'x');
+
+ Quit();
+ }
+
+ int OnInit() override {
+ int return_code = chromeos::DBusDaemon::OnInit();
+ if (return_code != EX_OK)
+ return return_code;
+
+ // Initialize D-Bus proxies.
+ call_proxy_.reset(
+ new org::chromium::LibCrosServiceInterfaceProxy(bus_,
+ kLibCrosServiceName));
+ signal_proxy_.reset(
+ new org::chromium::CrashReporterLibcrosProxyResolvedInterfaceProxy(
+ bus_,
+ kLibCrosServiceName));
+
+ // Set up the D-Bus signal handler.
+ // TODO(crbug.com/446115): Update ResolveNetworkProxy call to use an
+ // asynchronous return value rather than a return signal.
+ signal_proxy_->RegisterProxyResolvedSignalHandler(
+ base::Bind(&ProxyResolver::HandleProxyResolvedSignal,
+ weak_ptr_factory_.GetWeakPtr()),
+ base::Bind(&ProxyResolver::HandleDBusSignalConnected,
+ weak_ptr_factory_.GetWeakPtr()));
+
+ return EX_OK;
+ }
+
+ private:
+ unique_ptr<org::chromium::LibCrosServiceInterfaceProxy> call_proxy_;
+ unique_ptr<org::chromium::CrashReporterLibcrosProxyResolvedInterfaceProxy>
+ signal_proxy_;
+
+ const std::string source_url_;
+ const std::string signal_interface_;
+ const std::string signal_name_;
+ base::TimeDelta timeout_;
+
+ std::vector<std::string> proxies_;
+ base::WeakPtrFactory<ProxyResolver> weak_ptr_factory_;
+
+ base::CancelableClosure timeout_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProxyResolver);
+};
+
+static bool ShowBrowserProxies(std::string url, base::TimeDelta timeout) {
+ // Initialize and run the proxy resolver to watch for signals.
+ ProxyResolver resolver(url,
+ kLibCrosProxyResolvedSignalInterface,
+ kLibCrosProxyResolvedName,
+ timeout);
+ resolver.Run();
+
+ std::vector<std::string> proxies = resolver.proxies();
+
+ // If proxies is empty, then the timeout was reached waiting for the proxy
+ // resolved signal. If no proxies are defined, proxies will be populated
+ // with "direct://".
+ if (proxies.empty())
+ return false;
+
+ for (const auto& proxy : proxies) {
+ printf("%s\n", proxy.c_str());
+ }
+ return true;
+}
+
+} // namespace
+
+int main(int argc, char *argv[]) {
+ base::CommandLine::Init(argc, argv);
+ base::CommandLine* cl = base::CommandLine::ForCurrentProcess();
+
+ if (cl->HasSwitch(kHelp)) {
+ LOG(INFO) << kHelpMessage;
+ return 0;
+ }
+
+ bool quiet = cl->HasSwitch(kQuiet);
+ bool verbose = cl->HasSwitch(kVerbose);
+
+ int timeout = kTimeoutDefaultSeconds;
+ std::string str_timeout = cl->GetSwitchValueASCII(kTimeout);
+ if (!str_timeout.empty() && !base::StringToInt(str_timeout, &timeout)) {
+ LOG(ERROR) << "Invalid timeout value: " << str_timeout;
+ return 1;
+ }
+
+ // Default to logging to syslog.
+ int init_flags = chromeos::kLogToSyslog;
+ // Log to stderr if a TTY (and "-quiet" wasn't passed), or if "-verbose"
+ // was passed.
+
+ if ((!quiet && isatty(STDERR_FILENO)) || verbose)
+ init_flags |= chromeos::kLogToStderr;
+ chromeos::InitLog(init_flags);
+
+ std::string url;
+ base::CommandLine::StringVector urls = cl->GetArgs();
+ if (!urls.empty()) {
+ url = urls[0];
+ LOG(INFO) << "Resolving proxies for URL: " << url;
+ } else {
+ LOG(INFO) << "Resolving proxies without URL";
+ }
+
+ if (!ShowBrowserProxies(url, base::TimeDelta::FromSeconds(timeout))) {
+ LOG(ERROR) << "Error resolving proxies via the browser";
+ LOG(INFO) << "Assuming direct proxy";
+ printf("%s\n", kNoProxy);
+ }
+
+ return 0;
+}
diff --git a/crash_reporter/testrunner.cc b/crash_reporter/testrunner.cc
new file mode 100644
index 0000000..d45bbf8
--- /dev/null
+++ b/crash_reporter/testrunner.cc
@@ -0,0 +1,11 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <chromeos/test_helpers.h>
+#include <gtest/gtest.h>
+
+int main(int argc, char** argv) {
+ SetUpTests(&argc, argv, true);
+ return RUN_ALL_TESTS();
+}
diff --git a/crash_reporter/udev_collector.cc b/crash_reporter/udev_collector.cc
new file mode 100644
index 0000000..908bbc9
--- /dev/null
+++ b/crash_reporter/udev_collector.cc
@@ -0,0 +1,232 @@
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crash-reporter/udev_collector.h"
+
+#include <map>
+#include <utility>
+#include <vector>
+
+#include <base/files/file_enumerator.h>
+#include <base/files/file_util.h>
+#include <base/logging.h>
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/string_split.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <chromeos/process.h>
+
+using base::FilePath;
+
+namespace {
+
+const char kCollectUdevSignature[] = "crash_reporter-udev-collection";
+const char kGzipPath[] = "/bin/gzip";
+const char kUdevExecName[] = "udev";
+const char kUdevSignatureKey[] = "sig";
+const char kUdevSubsystemDevCoredump[] = "devcoredump";
+const char kDefaultDevCoredumpDirectory[] = "/sys/class/devcoredump";
+const char kDevCoredumpFilePrefixFormat[] = "devcoredump_%s";
+
+} // namespace
+
+UdevCollector::UdevCollector()
+ : dev_coredump_directory_(kDefaultDevCoredumpDirectory) {}
+
+UdevCollector::~UdevCollector() {}
+
+bool UdevCollector::HandleCrash(const std::string &udev_event) {
+ if (IsDeveloperImage()) {
+ LOG(INFO) << "developer image - collect udev crash info.";
+ } else if (is_feedback_allowed_function_()) {
+ LOG(INFO) << "Consent given - collect udev crash info.";
+ } else {
+ LOG(INFO) << "Ignoring - Non-developer image and no consent given.";
+ return false;
+ }
+
+ // Process the udev event string.
+ // First get all the key-value pairs.
+ std::vector<std::pair<std::string, std::string>> udev_event_keyval;
+ base::SplitStringIntoKeyValuePairs(udev_event, '=', ':', &udev_event_keyval);
+ std::vector<std::pair<std::string, std::string>>::const_iterator iter;
+ std::map<std::string, std::string> udev_event_map;
+ for (iter = udev_event_keyval.begin();
+ iter != udev_event_keyval.end();
+ ++iter) {
+ udev_event_map[iter->first] = iter->second;
+ }
+
+ // Make sure the crash directory exists, or create it if it doesn't.
+ FilePath crash_directory;
+ if (!GetCreatedCrashDirectoryByEuid(0, &crash_directory, nullptr)) {
+ LOG(ERROR) << "Could not get crash directory.";
+ return false;
+ }
+
+ if (udev_event_map["SUBSYSTEM"] == kUdevSubsystemDevCoredump) {
+ int instance_number;
+ if (!base::StringToInt(udev_event_map["KERNEL_NUMBER"], &instance_number)) {
+ LOG(ERROR) << "Invalid kernel number: "
+ << udev_event_map["KERNEL_NUMBER"];
+ return false;
+ }
+ return ProcessDevCoredump(crash_directory, instance_number);
+ }
+
+ return ProcessUdevCrashLogs(crash_directory,
+ udev_event_map["ACTION"],
+ udev_event_map["KERNEL"],
+ udev_event_map["SUBSYSTEM"]);
+}
+
+bool UdevCollector::ProcessUdevCrashLogs(const FilePath& crash_directory,
+ const std::string& action,
+ const std::string& kernel,
+ const std::string& subsystem) {
+ // Construct the basename string for crash_reporter_logs.conf:
+ // "crash_reporter-udev-collection-[action]-[name]-[subsystem]"
+ // If a udev field is not provided, "" is used in its place, e.g.:
+ // "crash_reporter-udev-collection-[action]--[subsystem]"
+ // Hence, "" is used as a wildcard name string.
+ // TODO(sque, crosbug.com/32238): Implement wildcard checking.
+ std::string basename = action + "-" + kernel + "-" + subsystem;
+ std::string udev_log_name = std::string(kCollectUdevSignature) + '-' +
+ basename;
+
+ // Create the destination path.
+ std::string log_file_name =
+ FormatDumpBasename(basename, time(nullptr), 0);
+ FilePath crash_path = GetCrashPath(crash_directory, log_file_name, "log");
+
+ // Handle the crash.
+ bool result = GetLogContents(log_config_path_, udev_log_name, crash_path);
+ if (!result) {
+ LOG(ERROR) << "Error reading udev log info " << udev_log_name;
+ return false;
+ }
+
+ // Compress the output using gzip.
+ chromeos::ProcessImpl gzip_process;
+ gzip_process.AddArg(kGzipPath);
+ gzip_process.AddArg(crash_path.value());
+ int process_result = gzip_process.Run();
+ FilePath crash_path_zipped = FilePath(crash_path.value() + ".gz");
+ // If the zip file was not created, use the uncompressed file.
+ if (process_result != 0 || !base::PathExists(crash_path_zipped))
+ LOG(ERROR) << "Could not create zip file " << crash_path_zipped.value();
+ else
+ crash_path = crash_path_zipped;
+
+ std::string exec_name = std::string(kUdevExecName) + "-" + subsystem;
+ AddCrashMetaData(kUdevSignatureKey, udev_log_name);
+ WriteCrashMetaData(GetCrashPath(crash_directory, log_file_name, "meta"),
+ exec_name, crash_path.value());
+ return true;
+}
+
+bool UdevCollector::ProcessDevCoredump(const FilePath& crash_directory,
+ int instance_number) {
+ FilePath coredump_path =
+ FilePath(base::StringPrintf("%s/devcd%d/data",
+ dev_coredump_directory_.c_str(),
+ instance_number));
+ if (!base::PathExists(coredump_path)) {
+ LOG(ERROR) << "Device coredump file " << coredump_path.value()
+ << " does not exist";
+ return false;
+ }
+
+ // Add coredump file to the crash directory.
+ if (!AppendDevCoredump(crash_directory, coredump_path, instance_number)) {
+ ClearDevCoredump(coredump_path);
+ return false;
+ }
+
+ // Clear the coredump data to allow generation of future device coredumps
+ // without having to wait for the 5-minutes timeout.
+ return ClearDevCoredump(coredump_path);
+}
+
+bool UdevCollector::AppendDevCoredump(const FilePath& crash_directory,
+ const FilePath& coredump_path,
+ int instance_number) {
+ // Retrieve the driver name of the failing device.
+ std::string driver_name = GetFailingDeviceDriverName(instance_number);
+ if (driver_name.empty()) {
+ LOG(ERROR) << "Failed to obtain driver name for instance: "
+ << instance_number;
+ return false;
+ }
+
+ std::string coredump_prefix =
+ base::StringPrintf(kDevCoredumpFilePrefixFormat, driver_name.c_str());
+
+ std::string dump_basename = FormatDumpBasename(coredump_prefix,
+ time(nullptr),
+ instance_number);
+ FilePath core_path = GetCrashPath(crash_directory, dump_basename, "devcore");
+ FilePath log_path = GetCrashPath(crash_directory, dump_basename, "log");
+ FilePath meta_path = GetCrashPath(crash_directory, dump_basename, "meta");
+
+ // Collect coredump data.
+ if (!base::CopyFile(coredump_path, core_path)) {
+ LOG(ERROR) << "Failed to copy device coredumpm file from "
+ << coredump_path.value() << " to " << core_path.value();
+ return false;
+ }
+
+ // Collect additional logs if one is specified in the config file.
+ std::string udev_log_name = std::string(kCollectUdevSignature) + '-' +
+ kUdevSubsystemDevCoredump + '-' + driver_name;
+ bool result = GetLogContents(log_config_path_, udev_log_name, log_path);
+ if (result) {
+ AddCrashMetaUploadFile("logs", log_path.value());
+ }
+
+ WriteCrashMetaData(meta_path, coredump_prefix, core_path.value());
+
+ return true;
+}
+
+bool UdevCollector::ClearDevCoredump(const FilePath& coredump_path) {
+ if (!base::WriteFile(coredump_path, "0", 1)) {
+ LOG(ERROR) << "Failed to delete the coredump data file "
+ << coredump_path.value();
+ return false;
+ }
+ return true;
+}
+
+std::string UdevCollector::GetFailingDeviceDriverName(int instance_number) {
+ FilePath failing_uevent_path =
+ FilePath(base::StringPrintf("%s/devcd%d/failing_device/uevent",
+ dev_coredump_directory_.c_str(),
+ instance_number));
+ if (!base::PathExists(failing_uevent_path)) {
+ LOG(ERROR) << "Failing uevent path " << failing_uevent_path.value()
+ << " does not exist";
+ return "";
+ }
+
+ std::string uevent_content;
+ if (!base::ReadFileToString(failing_uevent_path, &uevent_content)) {
+ LOG(ERROR) << "Failed to read uevent file " << failing_uevent_path.value();
+ return "";
+ }
+
+ // Parse uevent file contents as key-value pairs.
+ std::vector<std::pair<std::string, std::string>> uevent_keyval;
+ base::SplitStringIntoKeyValuePairs(uevent_content, '=', '\n', &uevent_keyval);
+ std::vector<std::pair<std::string, std::string>>::const_iterator iter;
+ for (iter = uevent_keyval.begin();
+ iter != uevent_keyval.end();
+ ++iter) {
+ if (iter->first == "DRIVER") {
+ return iter->second;
+ }
+ }
+
+ return "";
+}
diff --git a/crash_reporter/udev_collector.h b/crash_reporter/udev_collector.h
new file mode 100644
index 0000000..1689dd3
--- /dev/null
+++ b/crash_reporter/udev_collector.h
@@ -0,0 +1,64 @@
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CRASH_REPORTER_UDEV_COLLECTOR_H_
+#define CRASH_REPORTER_UDEV_COLLECTOR_H_
+
+#include <string>
+
+#include <base/files/file_path.h>
+#include <base/macros.h>
+#include <gtest/gtest_prod.h> // for FRIEND_TEST
+
+#include "crash-reporter/crash_collector.h"
+
+// Udev crash collector.
+class UdevCollector : public CrashCollector {
+ public:
+ UdevCollector();
+
+ ~UdevCollector() override;
+
+ // The udev event string should be formatted as follows:
+ // "ACTION=[action]:KERNEL=[name]:SUBSYSTEM=[subsystem]"
+ // The values don't have to be in any particular order. One or more of them
+ // could be omitted, in which case it would be treated as a wildcard (*).
+ bool HandleCrash(const std::string& udev_event);
+
+ protected:
+ std::string dev_coredump_directory_;
+
+ private:
+ friend class UdevCollectorTest;
+
+ // Process udev crash logs, collecting log files according to the config
+ // file (crash_reporter_logs.conf).
+ bool ProcessUdevCrashLogs(const base::FilePath& crash_directory,
+ const std::string& action,
+ const std::string& kernel,
+ const std::string& subsystem);
+ // Process device coredump, collecting device coredump file.
+ // |instance_number| is the kernel number of the virtual device for the device
+ // coredump instance.
+ bool ProcessDevCoredump(const base::FilePath& crash_directory,
+ int instance_number);
+ // Copy device coredump file to crash directory, and perform necessary
+ // coredump file management.
+ bool AppendDevCoredump(const base::FilePath& crash_directory,
+ const base::FilePath& coredump_path,
+ int instance_number);
+ // Clear the device coredump file by performing a dummy write to it.
+ bool ClearDevCoredump(const base::FilePath& coredump_path);
+ // Return the driver name of the device that generates the coredump.
+ std::string GetFailingDeviceDriverName(int instance_number);
+
+ // Mutator for unit testing.
+ void set_log_config_path(const std::string& path) {
+ log_config_path_ = base::FilePath(path);
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(UdevCollector);
+};
+
+#endif // CRASH_REPORTER_UDEV_COLLECTOR_H_
diff --git a/crash_reporter/udev_collector_test.cc b/crash_reporter/udev_collector_test.cc
new file mode 100644
index 0000000..08d9b2c
--- /dev/null
+++ b/crash_reporter/udev_collector_test.cc
@@ -0,0 +1,171 @@
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <base/files/file_enumerator.h>
+#include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
+#include <base/strings/stringprintf.h>
+#include <chromeos/syslog_logging.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "crash-reporter/udev_collector.h"
+
+using base::FilePath;
+
+namespace {
+
+// Dummy log config file name.
+const char kLogConfigFileName[] = "log_config_file";
+
+// Dummy directory for storing device coredumps.
+const char kDevCoredumpDirectory[] = "devcoredump";
+
+// A bunch of random rules to put into the dummy log config file.
+const char kLogConfigFileContents[] =
+ "crash_reporter-udev-collection-change-card0-drm=echo change card0 drm\n"
+ "crash_reporter-udev-collection-add-state0-cpu=echo change state0 cpu\n"
+ "crash_reporter-udev-collection-devcoredump-iwlwifi=echo devcoredump\n"
+ "cros_installer=echo not for udev";
+
+const char kCrashLogFilePattern[] = "*.log.gz";
+const char kDevCoredumpFilePattern[] = "*.devcore";
+
+// Dummy content for device coredump data file.
+const char kDevCoredumpDataContents[] = "coredump";
+
+// Content for failing device's uevent file.
+const char kFailingDeviceUeventContents[] = "DRIVER=iwlwifi\n";
+
+void CountCrash() {}
+
+bool s_consent_given = true;
+
+bool IsMetrics() {
+ return s_consent_given;
+}
+
+// Returns the number of files found in the given path that matches the
+// specified file name pattern.
+int GetNumFiles(const FilePath& path, const std::string& file_pattern) {
+ base::FileEnumerator enumerator(path, false, base::FileEnumerator::FILES,
+ file_pattern);
+ int num_files = 0;
+ for (FilePath file_path = enumerator.Next();
+ !file_path.value().empty();
+ file_path = enumerator.Next()) {
+ num_files++;
+ }
+ return num_files;
+}
+
+} // namespace
+
+class UdevCollectorMock : public UdevCollector {
+ public:
+ MOCK_METHOD0(SetUpDBus, void());
+};
+
+class UdevCollectorTest : public ::testing::Test {
+ protected:
+ base::ScopedTempDir temp_dir_generator_;
+
+ void HandleCrash(const std::string &udev_event) {
+ collector_.HandleCrash(udev_event);
+ }
+
+ void GenerateDevCoredump(const std::string& device_name) {
+ // Generate coredump data file.
+ ASSERT_TRUE(CreateDirectory(
+ FilePath(base::StringPrintf("%s/%s",
+ collector_.dev_coredump_directory_.c_str(),
+ device_name.c_str()))));
+ FilePath data_path =
+ FilePath(base::StringPrintf("%s/%s/data",
+ collector_.dev_coredump_directory_.c_str(),
+ device_name.c_str()));
+ ASSERT_EQ(strlen(kDevCoredumpDataContents),
+ base::WriteFile(data_path,
+ kDevCoredumpDataContents,
+ strlen(kDevCoredumpDataContents)));
+ // Generate uevent file for failing device.
+ ASSERT_TRUE(CreateDirectory(
+ FilePath(base::StringPrintf("%s/%s/failing_device",
+ collector_.dev_coredump_directory_.c_str(),
+ device_name.c_str()))));
+ FilePath uevent_path =
+ FilePath(base::StringPrintf("%s/%s/failing_device/uevent",
+ collector_.dev_coredump_directory_.c_str(),
+ device_name.c_str()));
+ ASSERT_EQ(strlen(kFailingDeviceUeventContents),
+ base::WriteFile(uevent_path,
+ kFailingDeviceUeventContents,
+ strlen(kFailingDeviceUeventContents)));
+ }
+
+ private:
+ void SetUp() override {
+ s_consent_given = true;
+
+ EXPECT_CALL(collector_, SetUpDBus()).WillRepeatedly(testing::Return());
+
+ collector_.Initialize(CountCrash, IsMetrics);
+
+ ASSERT_TRUE(temp_dir_generator_.CreateUniqueTempDir());
+
+ FilePath log_config_path =
+ temp_dir_generator_.path().Append(kLogConfigFileName);
+ collector_.log_config_path_ = log_config_path;
+ collector_.ForceCrashDirectory(temp_dir_generator_.path());
+
+ FilePath dev_coredump_path =
+ temp_dir_generator_.path().Append(kDevCoredumpDirectory);
+ collector_.dev_coredump_directory_ = dev_coredump_path.value();
+
+ // Write to a dummy log config file.
+ ASSERT_EQ(strlen(kLogConfigFileContents),
+ base::WriteFile(log_config_path,
+ kLogConfigFileContents,
+ strlen(kLogConfigFileContents)));
+
+ chromeos::ClearLog();
+ }
+
+ UdevCollectorMock collector_;
+};
+
+TEST_F(UdevCollectorTest, TestNoConsent) {
+ s_consent_given = false;
+ HandleCrash("ACTION=change:KERNEL=card0:SUBSYSTEM=drm");
+ EXPECT_EQ(0, GetNumFiles(temp_dir_generator_.path(), kCrashLogFilePattern));
+}
+
+TEST_F(UdevCollectorTest, TestNoMatch) {
+ // No rule should match this.
+ HandleCrash("ACTION=change:KERNEL=foo:SUBSYSTEM=bar");
+ EXPECT_EQ(0, GetNumFiles(temp_dir_generator_.path(), kCrashLogFilePattern));
+}
+
+TEST_F(UdevCollectorTest, TestMatches) {
+ // Try multiple udev events in sequence. The number of log files generated
+ // should increase.
+ HandleCrash("ACTION=change:KERNEL=card0:SUBSYSTEM=drm");
+ EXPECT_EQ(1, GetNumFiles(temp_dir_generator_.path(), kCrashLogFilePattern));
+ HandleCrash("ACTION=add:KERNEL=state0:SUBSYSTEM=cpu");
+ EXPECT_EQ(2, GetNumFiles(temp_dir_generator_.path(), kCrashLogFilePattern));
+}
+
+TEST_F(UdevCollectorTest, TestDevCoredump) {
+ GenerateDevCoredump("devcd0");
+ HandleCrash("ACTION=add:KERNEL_NUMBER=0:SUBSYSTEM=devcoredump");
+ EXPECT_EQ(1, GetNumFiles(temp_dir_generator_.path(),
+ kDevCoredumpFilePattern));
+ GenerateDevCoredump("devcd1");
+ HandleCrash("ACTION=add:KERNEL_NUMBER=1:SUBSYSTEM=devcoredump");
+ EXPECT_EQ(2, GetNumFiles(temp_dir_generator_.path(),
+ kDevCoredumpFilePattern));
+}
+
+// TODO(sque, crosbug.com/32238) - test wildcard cases, multiple identical udev
+// events.
diff --git a/crash_reporter/unclean_shutdown_collector.cc b/crash_reporter/unclean_shutdown_collector.cc
new file mode 100644
index 0000000..e8273b5
--- /dev/null
+++ b/crash_reporter/unclean_shutdown_collector.cc
@@ -0,0 +1,81 @@
+// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crash-reporter/unclean_shutdown_collector.h"
+
+#include <base/files/file_util.h>
+#include <base/logging.h>
+
+static const char kUncleanShutdownFile[] =
+ "/var/lib/crash_reporter/pending_clean_shutdown";
+
+// Files created by power manager used for crash reporting.
+static const char kPowerdTracePath[] = "/var/lib/power_manager";
+// Presence of this file indicates that the system was suspended
+static const char kPowerdSuspended[] = "powerd_suspended";
+
+using base::FilePath;
+
+UncleanShutdownCollector::UncleanShutdownCollector()
+ : unclean_shutdown_file_(kUncleanShutdownFile),
+ powerd_trace_path_(kPowerdTracePath),
+ powerd_suspended_file_(powerd_trace_path_.Append(kPowerdSuspended)) {
+}
+
+UncleanShutdownCollector::~UncleanShutdownCollector() {
+}
+
+bool UncleanShutdownCollector::Enable() {
+ FilePath file_path(unclean_shutdown_file_);
+ base::CreateDirectory(file_path.DirName());
+ if (base::WriteFile(file_path, "", 0) != 0) {
+ LOG(ERROR) << "Unable to create shutdown check file";
+ return false;
+ }
+ return true;
+}
+
+bool UncleanShutdownCollector::DeleteUncleanShutdownFiles() {
+ if (!base::DeleteFile(FilePath(unclean_shutdown_file_), false)) {
+ LOG(ERROR) << "Failed to delete unclean shutdown file "
+ << unclean_shutdown_file_;
+ return false;
+ }
+ // Delete power manager state file if it exists.
+ base::DeleteFile(powerd_suspended_file_, false);
+ return true;
+}
+
+bool UncleanShutdownCollector::Collect() {
+ FilePath unclean_file_path(unclean_shutdown_file_);
+ if (!base::PathExists(unclean_file_path)) {
+ return false;
+ }
+ LOG(WARNING) << "Last shutdown was not clean";
+ if (DeadBatteryCausedUncleanShutdown()) {
+ DeleteUncleanShutdownFiles();
+ return false;
+ }
+ DeleteUncleanShutdownFiles();
+
+ if (is_feedback_allowed_function_()) {
+ count_crash_function_();
+ }
+ return true;
+}
+
+bool UncleanShutdownCollector::Disable() {
+ LOG(INFO) << "Clean shutdown signalled";
+ return DeleteUncleanShutdownFiles();
+}
+
+bool UncleanShutdownCollector::DeadBatteryCausedUncleanShutdown() {
+ // Check for case of battery running out while suspended.
+ if (base::PathExists(powerd_suspended_file_)) {
+ LOG(INFO) << "Unclean shutdown occurred while suspended. Not counting "
+ << "toward unclean shutdown statistic.";
+ return true;
+ }
+ return false;
+}
diff --git a/crash_reporter/unclean_shutdown_collector.h b/crash_reporter/unclean_shutdown_collector.h
new file mode 100644
index 0000000..d30a0b2
--- /dev/null
+++ b/crash_reporter/unclean_shutdown_collector.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CRASH_REPORTER_UNCLEAN_SHUTDOWN_COLLECTOR_H_
+#define CRASH_REPORTER_UNCLEAN_SHUTDOWN_COLLECTOR_H_
+
+#include <string>
+
+#include <base/files/file_path.h>
+#include <base/macros.h>
+#include <gtest/gtest_prod.h> // for FRIEND_TEST
+
+#include "crash-reporter/crash_collector.h"
+
+// Unclean shutdown collector.
+class UncleanShutdownCollector : public CrashCollector {
+ public:
+ UncleanShutdownCollector();
+ ~UncleanShutdownCollector() override;
+
+ // Enable collection - signal that a boot has started.
+ bool Enable();
+
+ // Collect if there is was an unclean shutdown. Returns true if
+ // there was, false otherwise.
+ bool Collect();
+
+ // Disable collection - signal that the system has been shutdown cleanly.
+ bool Disable();
+
+ private:
+ friend class UncleanShutdownCollectorTest;
+ FRIEND_TEST(UncleanShutdownCollectorTest, EnableCannotWrite);
+ FRIEND_TEST(UncleanShutdownCollectorTest, CollectDeadBatterySuspended);
+
+ bool DeleteUncleanShutdownFiles();
+
+ // Check for unclean shutdown due to battery running out by analyzing powerd
+ // trace files.
+ bool DeadBatteryCausedUncleanShutdown();
+
+ const char *unclean_shutdown_file_;
+ base::FilePath powerd_trace_path_;
+ base::FilePath powerd_suspended_file_;
+
+ DISALLOW_COPY_AND_ASSIGN(UncleanShutdownCollector);
+};
+
+#endif // CRASH_REPORTER_UNCLEAN_SHUTDOWN_COLLECTOR_H_
diff --git a/crash_reporter/unclean_shutdown_collector_test.cc b/crash_reporter/unclean_shutdown_collector_test.cc
new file mode 100644
index 0000000..f5e1b32
--- /dev/null
+++ b/crash_reporter/unclean_shutdown_collector_test.cc
@@ -0,0 +1,135 @@
+// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crash-reporter/unclean_shutdown_collector.h"
+
+#include <unistd.h>
+
+#include <base/files/file_util.h>
+#include <base/strings/string_util.h>
+#include <chromeos/syslog_logging.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using base::FilePath;
+using ::chromeos::FindLog;
+
+namespace {
+
+int s_crashes = 0;
+bool s_metrics = true;
+
+const char kTestDirectory[] = "test";
+const char kTestSuspended[] = "test/suspended";
+const char kTestUnclean[] = "test/unclean";
+
+void CountCrash() {
+ ++s_crashes;
+}
+
+bool IsMetrics() {
+ return s_metrics;
+}
+
+} // namespace
+
+class UncleanShutdownCollectorMock : public UncleanShutdownCollector {
+ public:
+ MOCK_METHOD0(SetUpDBus, void());
+};
+
+class UncleanShutdownCollectorTest : public ::testing::Test {
+ void SetUp() {
+ s_crashes = 0;
+
+ EXPECT_CALL(collector_, SetUpDBus()).WillRepeatedly(testing::Return());
+
+ collector_.Initialize(CountCrash,
+ IsMetrics);
+ rmdir(kTestDirectory);
+ test_unclean_ = FilePath(kTestUnclean);
+ collector_.unclean_shutdown_file_ = kTestUnclean;
+ base::DeleteFile(test_unclean_, true);
+ // Set up an alternate power manager state file as well
+ collector_.powerd_suspended_file_ = FilePath(kTestSuspended);
+ chromeos::ClearLog();
+ }
+
+ protected:
+ void WriteStringToFile(const FilePath &file_path,
+ const char *data) {
+ ASSERT_EQ(strlen(data), base::WriteFile(file_path, data, strlen(data)));
+ }
+
+ UncleanShutdownCollectorMock collector_;
+ FilePath test_unclean_;
+};
+
+TEST_F(UncleanShutdownCollectorTest, EnableWithoutParent) {
+ ASSERT_TRUE(collector_.Enable());
+ ASSERT_TRUE(base::PathExists(test_unclean_));
+}
+
+TEST_F(UncleanShutdownCollectorTest, EnableWithParent) {
+ mkdir(kTestDirectory, 0777);
+ ASSERT_TRUE(collector_.Enable());
+ ASSERT_TRUE(base::PathExists(test_unclean_));
+}
+
+TEST_F(UncleanShutdownCollectorTest, EnableCannotWrite) {
+ collector_.unclean_shutdown_file_ = "/bad/path";
+ ASSERT_FALSE(collector_.Enable());
+ ASSERT_TRUE(FindLog("Unable to create shutdown check file"));
+}
+
+TEST_F(UncleanShutdownCollectorTest, CollectTrue) {
+ ASSERT_TRUE(collector_.Enable());
+ ASSERT_TRUE(base::PathExists(test_unclean_));
+ ASSERT_TRUE(collector_.Collect());
+ ASSERT_FALSE(base::PathExists(test_unclean_));
+ ASSERT_EQ(1, s_crashes);
+ ASSERT_TRUE(FindLog("Last shutdown was not clean"));
+}
+
+TEST_F(UncleanShutdownCollectorTest, CollectFalse) {
+ ASSERT_FALSE(collector_.Collect());
+ ASSERT_EQ(0, s_crashes);
+}
+
+TEST_F(UncleanShutdownCollectorTest, CollectDeadBatterySuspended) {
+ ASSERT_TRUE(collector_.Enable());
+ ASSERT_TRUE(base::PathExists(test_unclean_));
+ base::WriteFile(collector_.powerd_suspended_file_, "", 0);
+ ASSERT_FALSE(collector_.Collect());
+ ASSERT_FALSE(base::PathExists(test_unclean_));
+ ASSERT_FALSE(base::PathExists(collector_.powerd_suspended_file_));
+ ASSERT_EQ(0, s_crashes);
+ ASSERT_TRUE(FindLog("Unclean shutdown occurred while suspended."));
+}
+
+TEST_F(UncleanShutdownCollectorTest, Disable) {
+ ASSERT_TRUE(collector_.Enable());
+ ASSERT_TRUE(base::PathExists(test_unclean_));
+ ASSERT_TRUE(collector_.Disable());
+ ASSERT_FALSE(base::PathExists(test_unclean_));
+ ASSERT_FALSE(collector_.Collect());
+}
+
+TEST_F(UncleanShutdownCollectorTest, DisableWhenNotEnabled) {
+ ASSERT_TRUE(collector_.Disable());
+}
+
+TEST_F(UncleanShutdownCollectorTest, CantDisable) {
+ mkdir(kTestDirectory, 0700);
+ if (mkdir(kTestUnclean, 0700)) {
+ ASSERT_EQ(EEXIST, errno)
+ << "Error while creating directory '" << kTestUnclean
+ << "': " << strerror(errno);
+ }
+ ASSERT_EQ(0, base::WriteFile(test_unclean_.Append("foo"), "", 0))
+ << "Error while creating empty file '"
+ << test_unclean_.Append("foo").value() << "': " << strerror(errno);
+ ASSERT_FALSE(collector_.Disable());
+ rmdir(kTestUnclean);
+}
diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc
new file mode 100644
index 0000000..302b130
--- /dev/null
+++ b/crash_reporter/user_collector.cc
@@ -0,0 +1,673 @@
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crash-reporter/user_collector.h"
+
+#include <bits/wordsize.h>
+#include <elf.h>
+#include <fcntl.h>
+#include <grp.h> // For struct group.
+#include <pcrecpp.h>
+#include <pwd.h> // For struct passwd.
+#include <stdint.h>
+#include <sys/types.h> // For getpwuid_r, getgrnam_r, WEXITSTATUS.
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include <base/files/file_util.h>
+#include <base/logging.h>
+#include <base/posix/eintr_wrapper.h>
+#include <base/stl_util.h>
+#include <base/strings/string_split.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <chromeos/process.h>
+#include <chromeos/syslog_logging.h>
+
+static const char kCollectionErrorSignature[] =
+ "crash_reporter-user-collection";
+// This procfs file is used to cause kernel core file writing to
+// instead pipe the core file into a user space process. See
+// core(5) man page.
+static const char kCorePatternFile[] = "/proc/sys/kernel/core_pattern";
+static const char kCorePipeLimitFile[] = "/proc/sys/kernel/core_pipe_limit";
+// Set core_pipe_limit to 4 so that we can catch a few unrelated concurrent
+// crashes, but finite to avoid infinitely recursing on crash handling.
+static const char kCorePipeLimit[] = "4";
+static const char kCoreToMinidumpConverterPath[] = "/usr/bin/core2md";
+
+static const char kStatePrefix[] = "State:\t";
+
+// Define an otherwise invalid value that represents an unknown UID.
+static const uid_t kUnknownUid = -1;
+
+const char *UserCollector::kUserId = "Uid:\t";
+const char *UserCollector::kGroupId = "Gid:\t";
+
+using base::FilePath;
+using base::StringPrintf;
+
+UserCollector::UserCollector()
+ : generate_diagnostics_(false),
+ core_pattern_file_(kCorePatternFile),
+ core_pipe_limit_file_(kCorePipeLimitFile),
+ initialized_(false) {
+}
+
+void UserCollector::Initialize(
+ UserCollector::CountCrashFunction count_crash_function,
+ const std::string &our_path,
+ UserCollector::IsFeedbackAllowedFunction is_feedback_allowed_function,
+ bool generate_diagnostics,
+ bool core2md_failure,
+ bool directory_failure,
+ const std::string &filter_in) {
+ CrashCollector::Initialize(count_crash_function,
+ is_feedback_allowed_function);
+ our_path_ = our_path;
+ initialized_ = true;
+ generate_diagnostics_ = generate_diagnostics;
+ core2md_failure_ = core2md_failure;
+ directory_failure_ = directory_failure;
+ filter_in_ = filter_in;
+}
+
+UserCollector::~UserCollector() {
+}
+
+std::string UserCollector::GetErrorTypeSignature(ErrorType error_type) const {
+ switch (error_type) {
+ case kErrorSystemIssue:
+ return "system-issue";
+ case kErrorReadCoreData:
+ return "read-core-data";
+ case kErrorUnusableProcFiles:
+ return "unusable-proc-files";
+ case kErrorInvalidCoreFile:
+ return "invalid-core-file";
+ case kErrorUnsupported32BitCoreFile:
+ return "unsupported-32bit-core-file";
+ case kErrorCore2MinidumpConversion:
+ return "core2md-conversion";
+ default:
+ return "";
+ }
+}
+
+// Return the string that should be used for the kernel's core_pattern file.
+// Note that if you change the format of the enabled pattern, you'll probably
+// also need to change the ParseCrashAttributes() function below, the
+// user_collector_test.cc unittest, and the logging_UserCrash.py autotest.
+std::string UserCollector::GetPattern(bool enabled) const {
+ if (enabled) {
+ // Combine the four crash attributes into one parameter to try to reduce
+ // the size of the invocation line for crash_reporter, since the kernel
+ // has a fixed-sized (128B) buffer for it (before parameter expansion).
+ // Note that the kernel does not support quoted arguments in core_pattern.
+ return StringPrintf("|%s --user=%%P:%%s:%%u:%%e", our_path_.c_str());
+ } else {
+ return "core";
+ }
+}
+
+bool UserCollector::SetUpInternal(bool enabled) {
+ CHECK(initialized_);
+ LOG(INFO) << (enabled ? "Enabling" : "Disabling") << " user crash handling";
+
+ if (base::WriteFile(FilePath(core_pipe_limit_file_), kCorePipeLimit,
+ strlen(kCorePipeLimit)) !=
+ static_cast<int>(strlen(kCorePipeLimit))) {
+ PLOG(ERROR) << "Unable to write " << core_pipe_limit_file_;
+ return false;
+ }
+ std::string pattern = GetPattern(enabled);
+ if (base::WriteFile(FilePath(core_pattern_file_), pattern.c_str(),
+ pattern.length()) != static_cast<int>(pattern.length())) {
+ PLOG(ERROR) << "Unable to write " << core_pattern_file_;
+ return false;
+ }
+ return true;
+}
+
+bool UserCollector::GetFirstLineWithPrefix(
+ const std::vector<std::string> &lines,
+ const char *prefix, std::string *line) {
+ std::vector<std::string>::const_iterator line_iterator;
+ for (line_iterator = lines.begin(); line_iterator != lines.end();
+ ++line_iterator) {
+ if (line_iterator->find(prefix) == 0) {
+ *line = *line_iterator;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool UserCollector::GetIdFromStatus(
+ const char *prefix, IdKind kind,
+ const std::vector<std::string> &status_lines, int *id) {
+ // From fs/proc/array.c:task_state(), this file contains:
+ // \nUid:\t<uid>\t<euid>\t<suid>\t<fsuid>\n
+ std::string id_line;
+ if (!GetFirstLineWithPrefix(status_lines, prefix, &id_line)) {
+ return false;
+ }
+ std::string id_substring = id_line.substr(strlen(prefix), std::string::npos);
+ std::vector<std::string> ids;
+ base::SplitString(id_substring, '\t', &ids);
+ if (ids.size() != kIdMax || kind < 0 || kind >= kIdMax) {
+ return false;
+ }
+ const char *number = ids[kind].c_str();
+ char *end_number = nullptr;
+ *id = strtol(number, &end_number, 10);
+ if (*end_number != '\0') {
+ return false;
+ }
+ return true;
+}
+
+bool UserCollector::GetStateFromStatus(
+ const std::vector<std::string> &status_lines, std::string *state) {
+ std::string state_line;
+ if (!GetFirstLineWithPrefix(status_lines, kStatePrefix, &state_line)) {
+ return false;
+ }
+ *state = state_line.substr(strlen(kStatePrefix), std::string::npos);
+ return true;
+}
+
+void UserCollector::EnqueueCollectionErrorLog(pid_t pid,
+ ErrorType error_type,
+ const std::string &exec) {
+ FilePath crash_path;
+ LOG(INFO) << "Writing conversion problems as separate crash report.";
+ if (!GetCreatedCrashDirectoryByEuid(0, &crash_path, nullptr)) {
+ LOG(ERROR) << "Could not even get log directory; out of space?";
+ return;
+ }
+ AddCrashMetaData("sig", kCollectionErrorSignature);
+ AddCrashMetaData("error_type", GetErrorTypeSignature(error_type));
+ std::string dump_basename = FormatDumpBasename(exec, time(nullptr), pid);
+ std::string error_log = chromeos::GetLog();
+ FilePath diag_log_path = GetCrashPath(crash_path, dump_basename, "diaglog");
+ if (GetLogContents(FilePath(log_config_path_), kCollectionErrorSignature,
+ diag_log_path)) {
+ // We load the contents of diag_log into memory and append it to
+ // the error log. We cannot just append to files because we need
+ // to always create new files to prevent attack.
+ std::string diag_log_contents;
+ base::ReadFileToString(diag_log_path, &diag_log_contents);
+ error_log.append(diag_log_contents);
+ base::DeleteFile(diag_log_path, false);
+ }
+ FilePath log_path = GetCrashPath(crash_path, dump_basename, "log");
+ FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta");
+ // We must use WriteNewFile instead of base::WriteFile as we do
+ // not want to write with root access to a symlink that an attacker
+ // might have created.
+ if (WriteNewFile(log_path, error_log.data(), error_log.length()) < 0) {
+ LOG(ERROR) << "Error writing new file " << log_path.value();
+ return;
+ }
+ WriteCrashMetaData(meta_path, exec, log_path.value());
+}
+
+bool UserCollector::CopyOffProcFiles(pid_t pid,
+ const FilePath &container_dir) {
+ if (!base::CreateDirectory(container_dir)) {
+ PLOG(ERROR) << "Could not create " << container_dir.value().c_str();
+ return false;
+ }
+ FilePath process_path = GetProcessPath(pid);
+ if (!base::PathExists(process_path)) {
+ LOG(ERROR) << "Path " << process_path.value() << " does not exist";
+ return false;
+ }
+ static const char *proc_files[] = {
+ "auxv",
+ "cmdline",
+ "environ",
+ "maps",
+ "status"
+ };
+ for (unsigned i = 0; i < arraysize(proc_files); ++i) {
+ if (!base::CopyFile(process_path.Append(proc_files[i]),
+ container_dir.Append(proc_files[i]))) {
+ LOG(ERROR) << "Could not copy " << proc_files[i] << " file";
+ return false;
+ }
+ }
+ return true;
+}
+
+bool UserCollector::ValidateProcFiles(const FilePath &container_dir) const {
+ // Check if the maps file is empty, which could be due to the crashed
+ // process being reaped by the kernel before finishing a core dump.
+ int64_t file_size = 0;
+ if (!base::GetFileSize(container_dir.Append("maps"), &file_size)) {
+ LOG(ERROR) << "Could not get the size of maps file";
+ return false;
+ }
+ if (file_size == 0) {
+ LOG(ERROR) << "maps file is empty";
+ return false;
+ }
+ return true;
+}
+
+UserCollector::ErrorType UserCollector::ValidateCoreFile(
+ const FilePath &core_path) const {
+ int fd = HANDLE_EINTR(open(core_path.value().c_str(), O_RDONLY));
+ if (fd < 0) {
+ PLOG(ERROR) << "Could not open core file " << core_path.value();
+ return kErrorInvalidCoreFile;
+ }
+
+ char e_ident[EI_NIDENT];
+ bool read_ok = base::ReadFromFD(fd, e_ident, sizeof(e_ident));
+ IGNORE_EINTR(close(fd));
+ if (!read_ok) {
+ LOG(ERROR) << "Could not read header of core file";
+ return kErrorInvalidCoreFile;
+ }
+
+ if (e_ident[EI_MAG0] != ELFMAG0 || e_ident[EI_MAG1] != ELFMAG1 ||
+ e_ident[EI_MAG2] != ELFMAG2 || e_ident[EI_MAG3] != ELFMAG3) {
+ LOG(ERROR) << "Invalid core file";
+ return kErrorInvalidCoreFile;
+ }
+
+#if __WORDSIZE == 64
+ // TODO(benchan, mkrebs): Remove this check once core2md can
+ // handles both 32-bit and 64-bit ELF on a 64-bit platform.
+ if (e_ident[EI_CLASS] == ELFCLASS32) {
+ LOG(ERROR) << "Conversion of 32-bit core file on 64-bit platform is "
+ << "currently not supported";
+ return kErrorUnsupported32BitCoreFile;
+ }
+#endif
+
+ return kErrorNone;
+}
+
+bool UserCollector::GetCreatedCrashDirectory(pid_t pid, uid_t supplied_ruid,
+ FilePath *crash_file_path,
+ bool *out_of_capacity) {
+ FilePath process_path = GetProcessPath(pid);
+ std::string status;
+ if (directory_failure_) {
+ LOG(ERROR) << "Purposefully failing to create spool directory";
+ return false;
+ }
+
+ uid_t uid;
+ if (base::ReadFileToString(process_path.Append("status"), &status)) {
+ std::vector<std::string> status_lines;
+ base::SplitString(status, '\n', &status_lines);
+
+ std::string process_state;
+ if (!GetStateFromStatus(status_lines, &process_state)) {
+ LOG(ERROR) << "Could not find process state in status file";
+ return false;
+ }
+ LOG(INFO) << "State of crashed process [" << pid << "]: " << process_state;
+
+ // Get effective UID of crashing process.
+ int id;
+ if (!GetIdFromStatus(kUserId, kIdEffective, status_lines, &id)) {
+ LOG(ERROR) << "Could not find euid in status file";
+ return false;
+ }
+ uid = id;
+ } else if (supplied_ruid != kUnknownUid) {
+ LOG(INFO) << "Using supplied UID " << supplied_ruid
+ << " for crashed process [" << pid
+ << "] due to error reading status file";
+ uid = supplied_ruid;
+ } else {
+ LOG(ERROR) << "Could not read status file and kernel did not supply UID";
+ LOG(INFO) << "Path " << process_path.value() << " DirectoryExists: "
+ << base::DirectoryExists(process_path);
+ return false;
+ }
+
+ if (!GetCreatedCrashDirectoryByEuid(uid, crash_file_path, out_of_capacity)) {
+ LOG(ERROR) << "Could not create crash directory";
+ return false;
+ }
+ return true;
+}
+
+bool UserCollector::CopyStdinToCoreFile(const FilePath &core_path) {
+ // Copy off all stdin to a core file.
+ FilePath stdin_path("/dev/fd/0");
+ if (base::CopyFile(stdin_path, core_path)) {
+ return true;
+ }
+
+ PLOG(ERROR) << "Could not write core file";
+ // If the file system was full, make sure we remove any remnants.
+ base::DeleteFile(core_path, false);
+ return false;
+}
+
+bool UserCollector::RunCoreToMinidump(const FilePath &core_path,
+ const FilePath &procfs_directory,
+ const FilePath &minidump_path,
+ const FilePath &temp_directory) {
+ FilePath output_path = temp_directory.Append("output");
+ chromeos::ProcessImpl core2md;
+ core2md.RedirectOutput(output_path.value());
+ core2md.AddArg(kCoreToMinidumpConverterPath);
+ core2md.AddArg(core_path.value());
+ core2md.AddArg(procfs_directory.value());
+
+ if (!core2md_failure_) {
+ core2md.AddArg(minidump_path.value());
+ } else {
+ // To test how core2md errors are propagaged, cause an error
+ // by forgetting a required argument.
+ }
+
+ int errorlevel = core2md.Run();
+
+ std::string output;
+ base::ReadFileToString(output_path, &output);
+ if (errorlevel != 0) {
+ LOG(ERROR) << "Problem during " << kCoreToMinidumpConverterPath
+ << " [result=" << errorlevel << "]: " << output;
+ return false;
+ }
+
+ if (!base::PathExists(minidump_path)) {
+ LOG(ERROR) << "Minidump file " << minidump_path.value()
+ << " was not created";
+ return false;
+ }
+ return true;
+}
+
+UserCollector::ErrorType UserCollector::ConvertCoreToMinidump(
+ pid_t pid,
+ const FilePath &container_dir,
+ const FilePath &core_path,
+ const FilePath &minidump_path) {
+ // If proc files are unuable, we continue to read the core file from stdin,
+ // but only skip the core-to-minidump conversion, so that we may still use
+ // the core file for debugging.
+ bool proc_files_usable =
+ CopyOffProcFiles(pid, container_dir) && ValidateProcFiles(container_dir);
+
+ if (!CopyStdinToCoreFile(core_path)) {
+ return kErrorReadCoreData;
+ }
+
+ if (!proc_files_usable) {
+ LOG(INFO) << "Skipped converting core file to minidump due to "
+ << "unusable proc files";
+ return kErrorUnusableProcFiles;
+ }
+
+ ErrorType error = ValidateCoreFile(core_path);
+ if (error != kErrorNone) {
+ return error;
+ }
+
+ if (!RunCoreToMinidump(core_path,
+ container_dir, // procfs directory
+ minidump_path,
+ container_dir)) { // temporary directory
+ return kErrorCore2MinidumpConversion;
+ }
+
+ LOG(INFO) << "Stored minidump to " << minidump_path.value();
+ return kErrorNone;
+}
+
+UserCollector::ErrorType UserCollector::ConvertAndEnqueueCrash(
+ pid_t pid, const std::string &exec, uid_t supplied_ruid,
+ bool *out_of_capacity) {
+ FilePath crash_path;
+ if (!GetCreatedCrashDirectory(pid, supplied_ruid, &crash_path,
+ out_of_capacity)) {
+ LOG(ERROR) << "Unable to find/create process-specific crash path";
+ return kErrorSystemIssue;
+ }
+
+ // Directory like /tmp/crash_reporter/1234 which contains the
+ // procfs entries and other temporary files used during conversion.
+ FilePath container_dir(StringPrintf("/tmp/crash_reporter/%d", pid));
+ // Delete a pre-existing directory from crash reporter that may have
+ // been left around for diagnostics from a failed conversion attempt.
+ // If we don't, existing files can cause forking to fail.
+ base::DeleteFile(container_dir, true);
+ std::string dump_basename = FormatDumpBasename(exec, time(nullptr), pid);
+ FilePath core_path = GetCrashPath(crash_path, dump_basename, "core");
+ FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta");
+ FilePath minidump_path = GetCrashPath(crash_path, dump_basename, "dmp");
+ FilePath log_path = GetCrashPath(crash_path, dump_basename, "log");
+
+ if (GetLogContents(FilePath(log_config_path_), exec, log_path))
+ AddCrashMetaData("log", log_path.value());
+
+ ErrorType error_type =
+ ConvertCoreToMinidump(pid, container_dir, core_path, minidump_path);
+ if (error_type != kErrorNone) {
+ LOG(INFO) << "Leaving core file at " << core_path.value()
+ << " due to conversion error";
+ return error_type;
+ }
+
+ // Here we commit to sending this file. We must not return false
+ // after this point or we will generate a log report as well as a
+ // crash report.
+ WriteCrashMetaData(meta_path,
+ exec,
+ minidump_path.value());
+
+ if (!IsDeveloperImage()) {
+ base::DeleteFile(core_path, false);
+ } else {
+ LOG(INFO) << "Leaving core file at " << core_path.value()
+ << " due to developer image";
+ }
+
+ base::DeleteFile(container_dir, true);
+ return kErrorNone;
+}
+
+bool UserCollector::ParseCrashAttributes(const std::string &crash_attributes,
+ pid_t *pid, int *signal, uid_t *uid,
+ std::string *kernel_supplied_name) {
+ pcrecpp::RE re("(\\d+):(\\d+):(\\d+):(.*)");
+ if (re.FullMatch(crash_attributes, pid, signal, uid, kernel_supplied_name))
+ return true;
+
+ LOG(INFO) << "Falling back to parsing crash attributes '"
+ << crash_attributes << "' without UID";
+ pcrecpp::RE re_without_uid("(\\d+):(\\d+):(.*)");
+ *uid = kUnknownUid;
+ return re_without_uid.FullMatch(crash_attributes, pid, signal,
+ kernel_supplied_name);
+}
+
+// Returns true if the given executable name matches that of Chrome. This
+// includes checks for threads that Chrome has renamed.
+static bool IsChromeExecName(const std::string &exec) {
+ static const char *kChromeNames[] = {
+ "chrome",
+ // These are additional thread names seen in http://crash/
+ "MediaPipeline",
+ // These come from the use of base::PlatformThread::SetName() directly
+ "CrBrowserMain", "CrRendererMain", "CrUtilityMain", "CrPPAPIMain",
+ "CrPPAPIBrokerMain", "CrPluginMain", "CrWorkerMain", "CrGpuMain",
+ "BrokerEvent", "CrVideoRenderer", "CrShutdownDetector",
+ "UsbEventHandler", "CrNaClMain", "CrServiceMain",
+ // These thread names come from the use of base::Thread
+ "Gamepad polling thread", "Chrome_InProcGpuThread",
+ "Chrome_DragDropThread", "Renderer::FILE", "VC manager",
+ "VideoCaptureModuleImpl", "JavaBridge", "VideoCaptureManagerThread",
+ "Geolocation", "Geolocation_wifi_provider",
+ "Device orientation polling thread", "Chrome_InProcRendererThread",
+ "NetworkChangeNotifier", "Watchdog", "inotify_reader",
+ "cf_iexplore_background_thread", "BrowserWatchdog",
+ "Chrome_HistoryThread", "Chrome_SyncThread", "Chrome_ShellDialogThread",
+ "Printing_Worker", "Chrome_SafeBrowsingThread", "SimpleDBThread",
+ "D-Bus thread", "AudioThread", "NullAudioThread", "V4L2Thread",
+ "ChromotingClientDecodeThread", "Profiling_Flush",
+ "worker_thread_ticker", "AudioMixerAlsa", "AudioMixerCras",
+ "FakeAudioRecordingThread", "CaptureThread",
+ "Chrome_WebSocketproxyThread", "ProcessWatcherThread",
+ "Chrome_CameraThread", "import_thread", "NaCl_IOThread",
+ "Chrome_CloudPrintJobPrintThread", "Chrome_CloudPrintProxyCoreThread",
+ "DaemonControllerFileIO", "ChromotingMainThread",
+ "ChromotingEncodeThread", "ChromotingDesktopThread",
+ "ChromotingIOThread", "ChromotingFileIOThread",
+ "Chrome_libJingle_WorkerThread", "Chrome_ChildIOThread",
+ "GLHelperThread", "RemotingHostPlugin",
+ // "PAC thread #%d", // not easy to check because of "%d"
+ "Chrome_DBThread", "Chrome_WebKitThread", "Chrome_FileThread",
+ "Chrome_FileUserBlockingThread", "Chrome_ProcessLauncherThread",
+ "Chrome_CacheThread", "Chrome_IOThread", "Cache Thread", "File Thread",
+ "ServiceProcess_IO", "ServiceProcess_File",
+ "extension_crash_uploader", "gpu-process_crash_uploader",
+ "plugin_crash_uploader", "renderer_crash_uploader",
+ // These come from the use of webkit_glue::WebThreadImpl
+ "Compositor", "Browser Compositor",
+ // "WorkerPool/%d", // not easy to check because of "%d"
+ // These come from the use of base::Watchdog
+ "Startup watchdog thread Watchdog", "Shutdown watchdog thread Watchdog",
+ // These come from the use of AudioDeviceThread::Start
+ "AudioDevice", "AudioInputDevice", "AudioOutputDevice",
+ // These come from the use of MessageLoopFactory::GetMessageLoop
+ "GpuVideoDecoder", "RtcVideoDecoderThread", "PipelineThread",
+ "AudioDecoderThread", "VideoDecoderThread",
+ // These come from the use of MessageLoopFactory::GetMessageLoopProxy
+ "CaptureVideoDecoderThread", "CaptureVideoDecoder",
+ // These come from the use of base::SimpleThread
+ "LocalInputMonitor/%d", // "%d" gets lopped off for kernel-supplied
+ // These come from the use of base::DelegateSimpleThread
+ "ipc_channel_nacl reader thread/%d", "plugin_audio_input_thread/%d",
+ "plugin_audio_thread/%d",
+ // These come from the use of base::SequencedWorkerPool
+ "BrowserBlockingWorker%d/%d", // "%d" gets lopped off for kernel-supplied
+ };
+ static std::set<std::string> chrome_names;
+
+ // Initialize a set of chrome names, for efficient lookup
+ if (chrome_names.empty()) {
+ for (size_t i = 0; i < arraysize(kChromeNames); i++) {
+ std::string check_name(kChromeNames[i]);
+ chrome_names.insert(check_name);
+ // When checking a kernel-supplied name, it should be truncated to 15
+ // chars. See PR_SET_NAME in
+ // http://www.kernel.org/doc/man-pages/online/pages/man2/prctl.2.html,
+ // although that page misleads by saying "16 bytes".
+ chrome_names.insert("supplied_" + std::string(check_name, 0, 15));
+ }
+ }
+
+ return ContainsKey(chrome_names, exec);
+}
+
+bool UserCollector::ShouldDump(bool has_owner_consent,
+ bool is_developer,
+ bool handle_chrome_crashes,
+ const std::string &exec,
+ std::string *reason) {
+ reason->clear();
+
+ // Treat Chrome crashes as if the user opted-out. We stop counting Chrome
+ // crashes towards user crashes, so user crashes really mean non-Chrome
+ // user-space crashes.
+ if (!handle_chrome_crashes && IsChromeExecName(exec)) {
+ *reason = "ignoring call by kernel - chrome crash; "
+ "waiting for chrome to call us directly";
+ return false;
+ }
+
+ // For developer builds, we always want to keep the crash reports unless
+ // we're testing the crash facilities themselves. This overrides
+ // feedback. Crash sending still obeys consent.
+ if (is_developer) {
+ *reason = "developer build - not testing - always dumping";
+ return true;
+ }
+
+ if (!has_owner_consent) {
+ *reason = "ignoring - no consent";
+ return false;
+ }
+
+ *reason = "handling";
+ return true;
+}
+
+bool UserCollector::HandleCrash(const std::string &crash_attributes,
+ const char *force_exec) {
+ CHECK(initialized_);
+ pid_t pid = 0;
+ int signal = 0;
+ uid_t supplied_ruid = kUnknownUid;
+ std::string kernel_supplied_name;
+
+ if (!ParseCrashAttributes(crash_attributes, &pid, &signal, &supplied_ruid,
+ &kernel_supplied_name)) {
+ LOG(ERROR) << "Invalid parameter: --user=" << crash_attributes;
+ return false;
+ }
+
+ std::string exec;
+ if (force_exec) {
+ exec.assign(force_exec);
+ } else if (!GetExecutableBaseNameFromPid(pid, &exec)) {
+ // If we cannot find the exec name, use the kernel supplied name.
+ // We don't always use the kernel's since it truncates the name to
+ // 16 characters.
+ exec = StringPrintf("supplied_%s", kernel_supplied_name.c_str());
+ }
+
+ // Allow us to test the crash reporting mechanism successfully even if
+ // other parts of the system crash.
+ if (!filter_in_.empty() &&
+ (filter_in_ == "none" ||
+ filter_in_ != exec)) {
+ // We use a different format message to make it more obvious in tests
+ // which crashes are test generated and which are real.
+ LOG(WARNING) << "Ignoring crash from " << exec << "[" << pid << "] while "
+ << "filter_in=" << filter_in_ << ".";
+ return true;
+ }
+
+ std::string reason;
+ bool dump = ShouldDump(is_feedback_allowed_function_(),
+ IsDeveloperImage(),
+ ShouldHandleChromeCrashes(),
+ exec,
+ &reason);
+
+ LOG(WARNING) << "Received crash notification for " << exec << "[" << pid
+ << "] sig " << signal << ", user " << supplied_ruid
+ << " (" << reason << ")";
+
+ if (dump) {
+ count_crash_function_();
+
+ if (generate_diagnostics_) {
+ bool out_of_capacity = false;
+ ErrorType error_type =
+ ConvertAndEnqueueCrash(pid, exec, supplied_ruid, &out_of_capacity);
+ if (error_type != kErrorNone) {
+ if (!out_of_capacity)
+ EnqueueCollectionErrorLog(pid, error_type, exec);
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
diff --git a/crash_reporter/user_collector.h b/crash_reporter/user_collector.h
new file mode 100644
index 0000000..aac94cb
--- /dev/null
+++ b/crash_reporter/user_collector.h
@@ -0,0 +1,192 @@
+// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CRASH_REPORTER_USER_COLLECTOR_H_
+#define CRASH_REPORTER_USER_COLLECTOR_H_
+
+#include <string>
+#include <vector>
+
+#include <base/files/file_path.h>
+#include <base/macros.h>
+#include <gtest/gtest_prod.h> // for FRIEND_TEST
+
+#include "crash-reporter/crash_collector.h"
+
+class SystemLogging;
+
+// User crash collector.
+class UserCollector : public CrashCollector {
+ public:
+ UserCollector();
+
+ // Initialize the user crash collector for detection of crashes,
+ // given a crash counting function, the path to this executable,
+ // metrics collection enabled oracle, and system logger facility.
+ // Crash detection/reporting is not enabled until Enable is called.
+ // |generate_diagnostics| is used to indicate whether or not to try
+ // to generate a minidump from crashes.
+ void Initialize(CountCrashFunction count_crash,
+ const std::string &our_path,
+ IsFeedbackAllowedFunction is_metrics_allowed,
+ bool generate_diagnostics,
+ bool core2md_failure,
+ bool directory_failure,
+ const std::string &filter_in);
+
+ ~UserCollector() override;
+
+ // Enable collection.
+ bool Enable() { return SetUpInternal(true); }
+
+ // Disable collection.
+ bool Disable() { return SetUpInternal(false); }
+
+ // Handle a specific user crash. Returns true on success.
+ bool HandleCrash(const std::string &crash_attributes,
+ const char *force_exec);
+
+ // Set (override the default) core file pattern.
+ void set_core_pattern_file(const std::string &pattern) {
+ core_pattern_file_ = pattern;
+ }
+
+ // Set (override the default) core pipe limit file.
+ void set_core_pipe_limit_file(const std::string &path) {
+ core_pipe_limit_file_ = path;
+ }
+
+ private:
+ friend class UserCollectorTest;
+ FRIEND_TEST(UserCollectorTest, CopyOffProcFilesBadPath);
+ FRIEND_TEST(UserCollectorTest, CopyOffProcFilesBadPid);
+ FRIEND_TEST(UserCollectorTest, CopyOffProcFilesOK);
+ FRIEND_TEST(UserCollectorTest, GetExecutableBaseNameFromPid);
+ FRIEND_TEST(UserCollectorTest, GetFirstLineWithPrefix);
+ FRIEND_TEST(UserCollectorTest, GetIdFromStatus);
+ FRIEND_TEST(UserCollectorTest, GetStateFromStatus);
+ FRIEND_TEST(UserCollectorTest, GetProcessPath);
+ FRIEND_TEST(UserCollectorTest, GetSymlinkTarget);
+ FRIEND_TEST(UserCollectorTest, GetUserInfoFromName);
+ FRIEND_TEST(UserCollectorTest, ParseCrashAttributes);
+ FRIEND_TEST(UserCollectorTest, ShouldDumpChromeOverridesDeveloperImage);
+ FRIEND_TEST(UserCollectorTest, ShouldDumpDeveloperImageOverridesConsent);
+ FRIEND_TEST(UserCollectorTest, ShouldDumpUseConsentProductionImage);
+ FRIEND_TEST(UserCollectorTest, ValidateProcFiles);
+ FRIEND_TEST(UserCollectorTest, ValidateCoreFile);
+
+ // Enumeration to pass to GetIdFromStatus. Must match the order
+ // that the kernel lists IDs in the status file.
+ enum IdKind {
+ kIdReal = 0, // uid and gid
+ kIdEffective = 1, // euid and egid
+ kIdSet = 2, // suid and sgid
+ kIdFileSystem = 3, // fsuid and fsgid
+ kIdMax
+ };
+
+ enum ErrorType {
+ kErrorNone,
+ kErrorSystemIssue,
+ kErrorReadCoreData,
+ kErrorUnusableProcFiles,
+ kErrorInvalidCoreFile,
+ kErrorUnsupported32BitCoreFile,
+ kErrorCore2MinidumpConversion,
+ };
+
+ static const int kForkProblem = 255;
+
+ // Returns an error type signature for a given |error_type| value,
+ // which is reported to the crash server along with the
+ // crash_reporter-user-collection signature.
+ std::string GetErrorTypeSignature(ErrorType error_type) const;
+
+ std::string GetPattern(bool enabled) const;
+ bool SetUpInternal(bool enabled);
+
+ // Returns, via |line|, the first line in |lines| that starts with |prefix|.
+ // Returns true if a line is found, or false otherwise.
+ bool GetFirstLineWithPrefix(const std::vector<std::string> &lines,
+ const char *prefix, std::string *line);
+
+ // Returns the identifier of |kind|, via |id|, found in |status_lines| on
+ // the line starting with |prefix|. |status_lines| contains the lines in
+ // the status file. Returns true if the identifier can be determined.
+ bool GetIdFromStatus(const char *prefix,
+ IdKind kind,
+ const std::vector<std::string> &status_lines,
+ int *id);
+
+ // Returns the process state, via |state|, found in |status_lines|, which
+ // contains the lines in the status file. Returns true if the process state
+ // can be determined.
+ bool GetStateFromStatus(const std::vector<std::string> &status_lines,
+ std::string *state);
+
+ void LogCollectionError(const std::string &error_message);
+ void EnqueueCollectionErrorLog(pid_t pid, ErrorType error_type,
+ const std::string &exec_name);
+
+ bool CopyOffProcFiles(pid_t pid, const base::FilePath &container_dir);
+
+ // Validates the proc files at |container_dir| and returns true if they
+ // are usable for the core-to-minidump conversion later. For instance, if
+ // a process is reaped by the kernel before the copying of its proc files
+ // takes place, some proc files like /proc/<pid>/maps may contain nothing
+ // and thus become unusable.
+ bool ValidateProcFiles(const base::FilePath &container_dir) const;
+
+ // Validates the core file at |core_path| and returns kErrorNone if
+ // the file contains the ELF magic bytes and an ELF class that matches the
+ // platform (i.e. 32-bit ELF on a 32-bit platform or 64-bit ELF on a 64-bit
+ // platform), which is due to the limitation in core2md. It returns an error
+ // type otherwise.
+ ErrorType ValidateCoreFile(const base::FilePath &core_path) const;
+
+ // Determines the crash directory for given pid based on pid's owner,
+ // and creates the directory if necessary with appropriate permissions.
+ // Returns true whether or not directory needed to be created, false on
+ // any failure.
+ bool GetCreatedCrashDirectory(pid_t pid, uid_t supplied_ruid,
+ base::FilePath *crash_file_path,
+ bool *out_of_capacity);
+ bool CopyStdinToCoreFile(const base::FilePath &core_path);
+ bool RunCoreToMinidump(const base::FilePath &core_path,
+ const base::FilePath &procfs_directory,
+ const base::FilePath &minidump_path,
+ const base::FilePath &temp_directory);
+ ErrorType ConvertCoreToMinidump(pid_t pid,
+ const base::FilePath &container_dir,
+ const base::FilePath &core_path,
+ const base::FilePath &minidump_path);
+ ErrorType ConvertAndEnqueueCrash(pid_t pid, const std::string &exec_name,
+ uid_t supplied_ruid, bool *out_of_capacity);
+ bool ParseCrashAttributes(const std::string &crash_attributes,
+ pid_t *pid, int *signal, uid_t *uid,
+ std::string *kernel_supplied_name);
+
+ bool ShouldDump(bool has_owner_consent,
+ bool is_developer,
+ bool handle_chrome_crashes,
+ const std::string &exec,
+ std::string *reason);
+
+ bool generate_diagnostics_;
+ std::string core_pattern_file_;
+ std::string core_pipe_limit_file_;
+ std::string our_path_;
+ bool initialized_;
+
+ bool core2md_failure_;
+ bool directory_failure_;
+ std::string filter_in_;
+
+ static const char *kUserId;
+ static const char *kGroupId;
+
+ DISALLOW_COPY_AND_ASSIGN(UserCollector);
+};
+
+#endif // CRASH_REPORTER_USER_COLLECTOR_H_
diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc
new file mode 100644
index 0000000..823d8b4
--- /dev/null
+++ b/crash_reporter/user_collector_test.cc
@@ -0,0 +1,553 @@
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crash-reporter/user_collector.h"
+
+#include <bits/wordsize.h>
+#include <elf.h>
+#include <unistd.h>
+
+#include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
+#include <base/strings/string_split.h>
+#include <chromeos/syslog_logging.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using base::FilePath;
+using chromeos::FindLog;
+
+namespace {
+
+int s_crashes = 0;
+bool s_metrics = false;
+
+const char kFilePath[] = "/my/path";
+
+// Keep in sync with UserCollector::ShouldDump.
+const char kChromeIgnoreMsg[] =
+ "ignoring call by kernel - chrome crash; "
+ "waiting for chrome to call us directly";
+
+void CountCrash() {
+ ++s_crashes;
+}
+
+bool IsMetrics() {
+ return s_metrics;
+}
+
+} // namespace
+
+class UserCollectorMock : public UserCollector {
+ public:
+ MOCK_METHOD0(SetUpDBus, void());
+};
+
+class UserCollectorTest : public ::testing::Test {
+ void SetUp() {
+ s_crashes = 0;
+
+ EXPECT_CALL(collector_, SetUpDBus()).WillRepeatedly(testing::Return());
+
+ collector_.Initialize(CountCrash,
+ kFilePath,
+ IsMetrics,
+ false,
+ false,
+ false,
+ "");
+ base::DeleteFile(FilePath("test"), true);
+ mkdir("test", 0777);
+ collector_.set_core_pattern_file("test/core_pattern");
+ collector_.set_core_pipe_limit_file("test/core_pipe_limit");
+ pid_ = getpid();
+ chromeos::ClearLog();
+ }
+
+ protected:
+ void ExpectFileEquals(const char *golden,
+ const FilePath &file_path) {
+ std::string contents;
+ EXPECT_TRUE(base::ReadFileToString(file_path, &contents));
+ EXPECT_EQ(golden, contents);
+ }
+
+ std::vector<std::string> SplitLines(const std::string &lines) const {
+ std::vector<std::string> result;
+ base::SplitString(lines, '\n', &result);
+ return result;
+ }
+
+ UserCollectorMock collector_;
+ pid_t pid_;
+};
+
+TEST_F(UserCollectorTest, EnableOK) {
+ ASSERT_TRUE(collector_.Enable());
+ ExpectFileEquals("|/my/path --user=%P:%s:%u:%e",
+ FilePath("test/core_pattern"));
+ ExpectFileEquals("4", FilePath("test/core_pipe_limit"));
+ ASSERT_EQ(s_crashes, 0);
+ EXPECT_TRUE(FindLog("Enabling user crash handling"));
+}
+
+TEST_F(UserCollectorTest, EnableNoPatternFileAccess) {
+ collector_.set_core_pattern_file("/does_not_exist");
+ ASSERT_FALSE(collector_.Enable());
+ ASSERT_EQ(s_crashes, 0);
+ EXPECT_TRUE(FindLog("Enabling user crash handling"));
+ EXPECT_TRUE(FindLog("Unable to write /does_not_exist"));
+}
+
+TEST_F(UserCollectorTest, EnableNoPipeLimitFileAccess) {
+ collector_.set_core_pipe_limit_file("/does_not_exist");
+ ASSERT_FALSE(collector_.Enable());
+ ASSERT_EQ(s_crashes, 0);
+ // Core pattern should not be written if we cannot access the pipe limit
+ // or otherwise we may set a pattern that results in infinite recursion.
+ ASSERT_FALSE(base::PathExists(FilePath("test/core_pattern")));
+ EXPECT_TRUE(FindLog("Enabling user crash handling"));
+ EXPECT_TRUE(FindLog("Unable to write /does_not_exist"));
+}
+
+TEST_F(UserCollectorTest, DisableOK) {
+ ASSERT_TRUE(collector_.Disable());
+ ExpectFileEquals("core", FilePath("test/core_pattern"));
+ ASSERT_EQ(s_crashes, 0);
+ EXPECT_TRUE(FindLog("Disabling user crash handling"));
+}
+
+TEST_F(UserCollectorTest, DisableNoFileAccess) {
+ collector_.set_core_pattern_file("/does_not_exist");
+ ASSERT_FALSE(collector_.Disable());
+ ASSERT_EQ(s_crashes, 0);
+ EXPECT_TRUE(FindLog("Disabling user crash handling"));
+ EXPECT_TRUE(FindLog("Unable to write /does_not_exist"));
+}
+
+TEST_F(UserCollectorTest, ParseCrashAttributes) {
+ pid_t pid;
+ int signal;
+ uid_t uid;
+ std::string exec_name;
+ EXPECT_TRUE(collector_.ParseCrashAttributes("123456:11:1000:foobar",
+ &pid, &signal, &uid, &exec_name));
+ EXPECT_EQ(123456, pid);
+ EXPECT_EQ(11, signal);
+ EXPECT_EQ(1000, uid);
+ EXPECT_EQ("foobar", exec_name);
+ EXPECT_TRUE(collector_.ParseCrashAttributes("4321:6:barfoo",
+ &pid, &signal, &uid, &exec_name));
+ EXPECT_EQ(4321, pid);
+ EXPECT_EQ(6, signal);
+ EXPECT_EQ(-1, uid);
+ EXPECT_EQ("barfoo", exec_name);
+
+ EXPECT_FALSE(collector_.ParseCrashAttributes("123456:11",
+ &pid, &signal, &uid, &exec_name));
+
+ EXPECT_TRUE(collector_.ParseCrashAttributes("123456:11:exec:extra",
+ &pid, &signal, &uid, &exec_name));
+ EXPECT_EQ("exec:extra", exec_name);
+
+ EXPECT_FALSE(collector_.ParseCrashAttributes("12345p:11:foobar",
+ &pid, &signal, &uid, &exec_name));
+
+ EXPECT_FALSE(collector_.ParseCrashAttributes("123456:1 :foobar",
+ &pid, &signal, &uid, &exec_name));
+
+ EXPECT_FALSE(collector_.ParseCrashAttributes("123456::foobar",
+ &pid, &signal, &uid, &exec_name));
+}
+
+TEST_F(UserCollectorTest, ShouldDumpDeveloperImageOverridesConsent) {
+ std::string reason;
+ EXPECT_TRUE(collector_.ShouldDump(false, true, false,
+ "chrome-wm", &reason));
+ EXPECT_EQ("developer build - not testing - always dumping", reason);
+
+ // When running a crash test, behave as normal.
+ EXPECT_FALSE(collector_.ShouldDump(false, false, false,
+ "chrome-wm", &reason));
+ EXPECT_EQ("ignoring - no consent", reason);
+}
+
+TEST_F(UserCollectorTest, ShouldDumpChromeOverridesDeveloperImage) {
+ std::string reason;
+ // When running a crash test, behave as normal.
+ EXPECT_FALSE(collector_.ShouldDump(false, false, false,
+ "chrome", &reason));
+ EXPECT_EQ(kChromeIgnoreMsg, reason);
+ EXPECT_FALSE(collector_.ShouldDump(false, false, false,
+ "supplied_Compositor", &reason));
+ EXPECT_EQ(kChromeIgnoreMsg, reason);
+ EXPECT_FALSE(collector_.ShouldDump(false, false, false,
+ "supplied_PipelineThread", &reason));
+ EXPECT_EQ(kChromeIgnoreMsg, reason);
+ EXPECT_FALSE(collector_.ShouldDump(false, false, false,
+ "Chrome_ChildIOThread", &reason));
+ EXPECT_EQ(kChromeIgnoreMsg, reason);
+ EXPECT_FALSE(collector_.ShouldDump(false, false, false,
+ "supplied_Chrome_ChildIOT", &reason));
+ EXPECT_EQ(kChromeIgnoreMsg, reason);
+ EXPECT_FALSE(collector_.ShouldDump(false, false, false,
+ "supplied_ChromotingClien", &reason));
+ EXPECT_EQ(kChromeIgnoreMsg, reason);
+ EXPECT_FALSE(collector_.ShouldDump(false, false, false,
+ "supplied_LocalInputMonit", &reason));
+ EXPECT_EQ(kChromeIgnoreMsg, reason);
+
+ // When running a developer image, test that chrome crashes are handled
+ // when the "handle_chrome_crashes" flag is set.
+ EXPECT_TRUE(collector_.ShouldDump(false, true, true,
+ "chrome", &reason));
+ EXPECT_EQ("developer build - not testing - always dumping",
+ reason);
+ EXPECT_TRUE(collector_.ShouldDump(false, true, true,
+ "supplied_Compositor", &reason));
+ EXPECT_EQ("developer build - not testing - always dumping",
+ reason);
+ EXPECT_TRUE(collector_.ShouldDump(false, true, true,
+ "supplied_PipelineThread", &reason));
+ EXPECT_EQ("developer build - not testing - always dumping",
+ reason);
+ EXPECT_TRUE(collector_.ShouldDump(false, true, true,
+ "Chrome_ChildIOThread", &reason));
+ EXPECT_EQ("developer build - not testing - always dumping",
+ reason);
+ EXPECT_TRUE(collector_.ShouldDump(false, true, true,
+ "supplied_Chrome_ChildIOT", &reason));
+ EXPECT_EQ("developer build - not testing - always dumping",
+ reason);
+ EXPECT_TRUE(collector_.ShouldDump(false, true, true,
+ "supplied_ChromotingClien", &reason));
+ EXPECT_EQ("developer build - not testing - always dumping",
+ reason);
+ EXPECT_TRUE(collector_.ShouldDump(false, true, true,
+ "supplied_LocalInputMonit", &reason));
+ EXPECT_EQ("developer build - not testing - always dumping",
+ reason);
+}
+
+TEST_F(UserCollectorTest, ShouldDumpUseConsentProductionImage) {
+ std::string result;
+ EXPECT_FALSE(collector_.ShouldDump(false, false, false,
+ "chrome-wm", &result));
+ EXPECT_EQ("ignoring - no consent", result);
+
+ EXPECT_TRUE(collector_.ShouldDump(true, false, false,
+ "chrome-wm", &result));
+ EXPECT_EQ("handling", result);
+}
+
+TEST_F(UserCollectorTest, HandleCrashWithoutConsent) {
+ s_metrics = false;
+ collector_.HandleCrash("20:10:ignored", "foobar");
+ EXPECT_TRUE(FindLog(
+ "Received crash notification for foobar[20] sig 10"));
+ ASSERT_EQ(s_crashes, 0);
+}
+
+TEST_F(UserCollectorTest, HandleNonChromeCrashWithConsent) {
+ s_metrics = true;
+ collector_.HandleCrash("5:2:ignored", "chromeos-wm");
+ EXPECT_TRUE(FindLog(
+ "Received crash notification for chromeos-wm[5] sig 2"));
+ ASSERT_EQ(s_crashes, 1);
+}
+
+TEST_F(UserCollectorTest, HandleChromeCrashWithConsent) {
+ s_metrics = true;
+ collector_.HandleCrash("5:2:ignored", "chrome");
+ EXPECT_TRUE(FindLog(
+ "Received crash notification for chrome[5] sig 2"));
+ EXPECT_TRUE(FindLog(kChromeIgnoreMsg));
+ ASSERT_EQ(s_crashes, 0);
+}
+
+TEST_F(UserCollectorTest, HandleSuppliedChromeCrashWithConsent) {
+ s_metrics = true;
+ collector_.HandleCrash("0:2:chrome", nullptr);
+ EXPECT_TRUE(FindLog(
+ "Received crash notification for supplied_chrome[0] sig 2"));
+ EXPECT_TRUE(FindLog(kChromeIgnoreMsg));
+ ASSERT_EQ(s_crashes, 0);
+}
+
+TEST_F(UserCollectorTest, GetProcessPath) {
+ FilePath path = collector_.GetProcessPath(100);
+ ASSERT_EQ("/proc/100", path.value());
+}
+
+TEST_F(UserCollectorTest, GetSymlinkTarget) {
+ FilePath result;
+ ASSERT_FALSE(collector_.GetSymlinkTarget(FilePath("/does_not_exist"),
+ &result));
+ ASSERT_TRUE(FindLog(
+ "Readlink failed on /does_not_exist with 2"));
+ std::string long_link;
+ for (int i = 0; i < 50; ++i)
+ long_link += "0123456789";
+ long_link += "/gold";
+
+ for (size_t len = 1; len <= long_link.size(); ++len) {
+ std::string this_link;
+ static const char kLink[] = "test/this_link";
+ this_link.assign(long_link.c_str(), len);
+ ASSERT_EQ(len, this_link.size());
+ unlink(kLink);
+ ASSERT_EQ(0, symlink(this_link.c_str(), kLink));
+ ASSERT_TRUE(collector_.GetSymlinkTarget(FilePath(kLink), &result));
+ ASSERT_EQ(this_link, result.value());
+ }
+}
+
+TEST_F(UserCollectorTest, GetExecutableBaseNameFromPid) {
+ std::string base_name;
+ EXPECT_FALSE(collector_.GetExecutableBaseNameFromPid(0, &base_name));
+ EXPECT_TRUE(FindLog(
+ "Readlink failed on /proc/0/exe with 2"));
+ EXPECT_TRUE(FindLog(
+ "GetSymlinkTarget failed - Path /proc/0 DirectoryExists: 0"));
+ EXPECT_TRUE(FindLog("stat /proc/0/exe failed: -1 2"));
+
+ chromeos::ClearLog();
+ pid_t my_pid = getpid();
+ EXPECT_TRUE(collector_.GetExecutableBaseNameFromPid(my_pid, &base_name));
+ EXPECT_FALSE(FindLog("Readlink failed"));
+ EXPECT_EQ("crash_reporter_test", base_name);
+}
+
+TEST_F(UserCollectorTest, GetFirstLineWithPrefix) {
+ std::vector<std::string> lines;
+ std::string line;
+
+ EXPECT_FALSE(collector_.GetFirstLineWithPrefix(lines, "Name:", &line));
+ EXPECT_EQ("", line);
+
+ lines.push_back("Name:\tls");
+ lines.push_back("State:\tR (running)");
+ lines.push_back(" Foo:\t1000");
+
+ line.clear();
+ EXPECT_TRUE(collector_.GetFirstLineWithPrefix(lines, "Name:", &line));
+ EXPECT_EQ(lines[0], line);
+
+ line.clear();
+ EXPECT_TRUE(collector_.GetFirstLineWithPrefix(lines, "State:", &line));
+ EXPECT_EQ(lines[1], line);
+
+ line.clear();
+ EXPECT_FALSE(collector_.GetFirstLineWithPrefix(lines, "Foo:", &line));
+ EXPECT_EQ("", line);
+
+ line.clear();
+ EXPECT_TRUE(collector_.GetFirstLineWithPrefix(lines, " Foo:", &line));
+ EXPECT_EQ(lines[2], line);
+
+ line.clear();
+ EXPECT_FALSE(collector_.GetFirstLineWithPrefix(lines, "Bar:", &line));
+ EXPECT_EQ("", line);
+}
+
+TEST_F(UserCollectorTest, GetIdFromStatus) {
+ int id = 1;
+ EXPECT_FALSE(collector_.GetIdFromStatus(UserCollector::kUserId,
+ UserCollector::kIdEffective,
+ SplitLines("nothing here"),
+ &id));
+ EXPECT_EQ(id, 1);
+
+ // Not enough parameters.
+ EXPECT_FALSE(collector_.GetIdFromStatus(UserCollector::kUserId,
+ UserCollector::kIdReal,
+ SplitLines("line 1\nUid:\t1\n"),
+ &id));
+
+ const std::vector<std::string> valid_contents =
+ SplitLines("\nUid:\t1\t2\t3\t4\nGid:\t5\t6\t7\t8\n");
+ EXPECT_TRUE(collector_.GetIdFromStatus(UserCollector::kUserId,
+ UserCollector::kIdReal,
+ valid_contents,
+ &id));
+ EXPECT_EQ(1, id);
+
+ EXPECT_TRUE(collector_.GetIdFromStatus(UserCollector::kUserId,
+ UserCollector::kIdEffective,
+ valid_contents,
+ &id));
+ EXPECT_EQ(2, id);
+
+ EXPECT_TRUE(collector_.GetIdFromStatus(UserCollector::kUserId,
+ UserCollector::kIdFileSystem,
+ valid_contents,
+ &id));
+ EXPECT_EQ(4, id);
+
+ EXPECT_TRUE(collector_.GetIdFromStatus(UserCollector::kGroupId,
+ UserCollector::kIdEffective,
+ valid_contents,
+ &id));
+ EXPECT_EQ(6, id);
+
+ EXPECT_TRUE(collector_.GetIdFromStatus(UserCollector::kGroupId,
+ UserCollector::kIdSet,
+ valid_contents,
+ &id));
+ EXPECT_EQ(7, id);
+
+ EXPECT_FALSE(collector_.GetIdFromStatus(UserCollector::kGroupId,
+ UserCollector::IdKind(5),
+ valid_contents,
+ &id));
+ EXPECT_FALSE(collector_.GetIdFromStatus(UserCollector::kGroupId,
+ UserCollector::IdKind(-1),
+ valid_contents,
+ &id));
+
+ // Fail if junk after number
+ EXPECT_FALSE(collector_.GetIdFromStatus(UserCollector::kUserId,
+ UserCollector::kIdReal,
+ SplitLines("Uid:\t1f\t2\t3\t4\n"),
+ &id));
+ EXPECT_TRUE(collector_.GetIdFromStatus(UserCollector::kUserId,
+ UserCollector::kIdReal,
+ SplitLines("Uid:\t1\t2\t3\t4\n"),
+ &id));
+ EXPECT_EQ(1, id);
+
+ // Fail if more than 4 numbers.
+ EXPECT_FALSE(collector_.GetIdFromStatus(UserCollector::kUserId,
+ UserCollector::kIdReal,
+ SplitLines("Uid:\t1\t2\t3\t4\t5\n"),
+ &id));
+}
+
+TEST_F(UserCollectorTest, GetStateFromStatus) {
+ std::string state;
+ EXPECT_FALSE(collector_.GetStateFromStatus(SplitLines("nothing here"),
+ &state));
+ EXPECT_EQ("", state);
+
+ EXPECT_TRUE(collector_.GetStateFromStatus(SplitLines("State:\tR (running)"),
+ &state));
+ EXPECT_EQ("R (running)", state);
+
+ EXPECT_TRUE(collector_.GetStateFromStatus(
+ SplitLines("Name:\tls\nState:\tZ (zombie)\n"), &state));
+ EXPECT_EQ("Z (zombie)", state);
+}
+
+TEST_F(UserCollectorTest, GetUserInfoFromName) {
+ gid_t gid = 100;
+ uid_t uid = 100;
+ EXPECT_TRUE(collector_.GetUserInfoFromName("root", &uid, &gid));
+ EXPECT_EQ(0, uid);
+ EXPECT_EQ(0, gid);
+}
+
+TEST_F(UserCollectorTest, CopyOffProcFilesBadPath) {
+ // Try a path that is not writable.
+ ASSERT_FALSE(collector_.CopyOffProcFiles(pid_, FilePath("/bad/path")));
+ EXPECT_TRUE(FindLog("Could not create /bad/path"));
+}
+
+TEST_F(UserCollectorTest, CopyOffProcFilesBadPid) {
+ FilePath container_path("test/container");
+ ASSERT_FALSE(collector_.CopyOffProcFiles(0, container_path));
+ EXPECT_TRUE(FindLog("Path /proc/0 does not exist"));
+}
+
+TEST_F(UserCollectorTest, CopyOffProcFilesOK) {
+ FilePath container_path("test/container");
+ ASSERT_TRUE(collector_.CopyOffProcFiles(pid_, container_path));
+ EXPECT_FALSE(FindLog("Could not copy"));
+ static struct {
+ const char *name;
+ bool exists;
+ } expectations[] = {
+ { "auxv", true },
+ { "cmdline", true },
+ { "environ", true },
+ { "maps", true },
+ { "mem", false },
+ { "mounts", false },
+ { "sched", false },
+ { "status", true }
+ };
+ for (unsigned i = 0; i < sizeof(expectations)/sizeof(expectations[0]); ++i) {
+ EXPECT_EQ(expectations[i].exists,
+ base::PathExists(
+ container_path.Append(expectations[i].name)));
+ }
+}
+
+TEST_F(UserCollectorTest, ValidateProcFiles) {
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ FilePath container_dir = temp_dir.path();
+
+ // maps file not exists (i.e. GetFileSize fails)
+ EXPECT_FALSE(collector_.ValidateProcFiles(container_dir));
+
+ // maps file is empty
+ FilePath maps_file = container_dir.Append("maps");
+ ASSERT_EQ(0, base::WriteFile(maps_file, nullptr, 0));
+ ASSERT_TRUE(base::PathExists(maps_file));
+ EXPECT_FALSE(collector_.ValidateProcFiles(container_dir));
+
+ // maps file is not empty
+ const char data[] = "test data";
+ ASSERT_EQ(sizeof(data), base::WriteFile(maps_file, data, sizeof(data)));
+ ASSERT_TRUE(base::PathExists(maps_file));
+ EXPECT_TRUE(collector_.ValidateProcFiles(container_dir));
+}
+
+TEST_F(UserCollectorTest, ValidateCoreFile) {
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ FilePath container_dir = temp_dir.path();
+ FilePath core_file = container_dir.Append("core");
+
+ // Core file does not exist
+ EXPECT_EQ(UserCollector::kErrorInvalidCoreFile,
+ collector_.ValidateCoreFile(core_file));
+ char e_ident[EI_NIDENT];
+ e_ident[EI_MAG0] = ELFMAG0;
+ e_ident[EI_MAG1] = ELFMAG1;
+ e_ident[EI_MAG2] = ELFMAG2;
+ e_ident[EI_MAG3] = ELFMAG3;
+#if __WORDSIZE == 32
+ e_ident[EI_CLASS] = ELFCLASS32;
+#elif __WORDSIZE == 64
+ e_ident[EI_CLASS] = ELFCLASS64;
+#else
+#error Unknown/unsupported value of __WORDSIZE.
+#endif
+
+ // Core file has the expected header
+ ASSERT_TRUE(base::WriteFile(core_file, e_ident, sizeof(e_ident)));
+ EXPECT_EQ(UserCollector::kErrorNone,
+ collector_.ValidateCoreFile(core_file));
+
+#if __WORDSIZE == 64
+ // 32-bit core file on 64-bit platform
+ e_ident[EI_CLASS] = ELFCLASS32;
+ ASSERT_TRUE(base::WriteFile(core_file, e_ident, sizeof(e_ident)));
+ EXPECT_EQ(UserCollector::kErrorUnsupported32BitCoreFile,
+ collector_.ValidateCoreFile(core_file));
+ e_ident[EI_CLASS] = ELFCLASS64;
+#endif
+
+ // Invalid core files
+ ASSERT_TRUE(base::WriteFile(core_file, e_ident, sizeof(e_ident) - 1));
+ EXPECT_EQ(UserCollector::kErrorInvalidCoreFile,
+ collector_.ValidateCoreFile(core_file));
+
+ e_ident[EI_MAG0] = 0;
+ ASSERT_TRUE(base::WriteFile(core_file, e_ident, sizeof(e_ident)));
+ EXPECT_EQ(UserCollector::kErrorInvalidCoreFile,
+ collector_.ValidateCoreFile(core_file));
+}
diff --git a/crash_reporter/warn_collector.l b/crash_reporter/warn_collector.l
new file mode 100644
index 0000000..691ef99
--- /dev/null
+++ b/crash_reporter/warn_collector.l
@@ -0,0 +1,322 @@
+/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * This flex program reads /var/log/messages as it grows and saves kernel
+ * warnings to files. It keeps track of warnings it has seen (based on
+ * file/line only, ignoring differences in the stack trace), and reports only
+ * the first warning of each kind, but maintains a count of all warnings by
+ * using their hashes as buckets in a UMA sparse histogram. It also invokes
+ * the crash collector, which collects the warnings and prepares them for later
+ * shipment to the crash server.
+ */
+
+%{
+#include <fcntl.h>
+#include <inttypes.h>
+#include <pwd.h>
+#include <stdarg.h>
+#include <sys/inotify.h>
+#include <sys/select.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "metrics/c_metrics_library.h"
+
+int WarnStart(void);
+void WarnEnd(void);
+void WarnInput(char *buf, yy_size_t *result, size_t max_size);
+
+#define YY_INPUT(buf, result, max_size) WarnInput(buf, &result, max_size)
+
+%}
+
+/* Define a few useful regular expressions. */
+
+D [0-9]
+PREFIX .*" kernel: [ "*{D}+"."{D}+"]"
+CUT_HERE {PREFIX}" ------------[ cut here".*
+WARNING {PREFIX}" WARNING: at "
+END_TRACE {PREFIX}" ---[ end trace".*
+
+/* Use exclusive start conditions. */
+%x PRE_WARN WARN
+
+%%
+ /* The scanner itself. */
+
+^{CUT_HERE}\n{WARNING} BEGIN(PRE_WARN);
+.|\n /* ignore all other input in state 0 */
+<PRE_WARN>[^ ]+.[^ ]+\n if (WarnStart()) {
+ /* yytext is
+ "file:line func+offset/offset()\n" */
+ BEGIN(WARN); ECHO;
+ } else {
+ BEGIN(0);
+ }
+
+ /* Assume the warning ends at the "end trace" line */
+<WARN>^{END_TRACE}\n ECHO; BEGIN(0); WarnEnd();
+<WARN>^.*\n ECHO;
+
+%%
+
+#define HASH_BITMAP_SIZE (1 << 15) /* size in bits */
+#define HASH_BITMAP_MASK (HASH_BITMAP_SIZE - 1)
+
+const char warn_hist_name[] = "Platform.KernelWarningHashes";
+uint32_t hash_bitmap[HASH_BITMAP_SIZE / 32];
+CMetricsLibrary metrics_library;
+
+const char *prog_name; /* the name of this program */
+int yyin_fd; /* instead of FILE *yyin to avoid buffering */
+int i_fd; /* for inotify, to detect file changes */
+int testing; /* 1 if running test */
+int filter; /* 1 when using as filter (for development) */
+int fifo; /* 1 when reading from fifo (for devel) */
+int draining; /* 1 when draining renamed log file */
+
+const char *msg_path = "/var/log/messages";
+const char warn_dump_dir[] = "/var/run/kwarn";
+const char *warn_dump_path = "/var/run/kwarn/warning";
+const char *crash_reporter_command;
+
+__attribute__((__format__(__printf__, 1, 2)))
+static void Die(const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ fprintf(stderr, "%s: ", prog_name);
+ vfprintf(stderr, format, ap);
+ exit(1);
+}
+
+static void RunCrashReporter(void) {
+ int status = system(crash_reporter_command);
+ if (status != 0)
+ Die("%s exited with status %d\n", crash_reporter_command, status);
+}
+
+static uint32_t StringHash(const char *string) {
+ uint32_t hash = 0;
+ while (*string != '\0') {
+ hash = (hash << 5) + hash + *string++;
+ }
+ return hash;
+}
+
+/* We expect only a handful of different warnings per boot session, so the
+ * probability of a collision is very low, and statistically it won't matter
+ * (unless warnings with the same hash also happens in tandem, which is even
+ * rarer).
+ */
+static int HashSeen(uint32_t hash) {
+ int word_index = (hash & HASH_BITMAP_MASK) / 32;
+ int bit_index = (hash & HASH_BITMAP_MASK) % 32;
+ return hash_bitmap[word_index] & 1 << bit_index;
+}
+
+static void SetHashSeen(uint32_t hash) {
+ int word_index = (hash & HASH_BITMAP_MASK) / 32;
+ int bit_index = (hash & HASH_BITMAP_MASK) % 32;
+ hash_bitmap[word_index] |= 1 << bit_index;
+}
+
+int WarnStart(void) {
+ uint32_t hash;
+ char *spacep;
+
+ if (filter)
+ return 1;
+
+ hash = StringHash(yytext);
+ if (!(testing || fifo || filter)) {
+ CMetricsLibrarySendSparseToUMA(metrics_library, warn_hist_name, (int) hash);
+ }
+ if (HashSeen(hash))
+ return 0;
+ SetHashSeen(hash);
+
+ yyout = fopen(warn_dump_path, "w");
+ if (yyout == NULL)
+ Die("fopen %s failed: %s\n", warn_dump_path, strerror(errno));
+ spacep = index(yytext, ' ');
+ if (spacep == NULL || spacep[1] == '\0')
+ spacep = "unknown-function";
+ fprintf(yyout, "%08x-%s\n", hash, spacep + 1);
+ return 1;
+}
+
+void WarnEnd(void) {
+ if (filter)
+ return;
+ fclose(yyout);
+ yyout = stdout; /* for debugging */
+ RunCrashReporter();
+}
+
+static void WarnOpenInput(const char *path) {
+ yyin_fd = open(path, O_RDONLY);
+ if (yyin_fd < 0)
+ Die("could not open %s: %s\n", path, strerror(errno));
+ if (!fifo) {
+ /* Go directly to the end of the file. We don't want to parse the same
+ * warnings multiple times on reboot/restart. We might miss some
+ * warnings, but so be it---it's too hard to keep track reliably of the
+ * last parsed position in the syslog.
+ */
+ if (lseek(yyin_fd, 0, SEEK_END) < 0)
+ Die("could not lseek %s: %s\n", path, strerror(errno));
+ /* Set up notification of file growth and rename. */
+ i_fd = inotify_init();
+ if (i_fd < 0)
+ Die("inotify_init: %s\n", strerror(errno));
+ if (inotify_add_watch(i_fd, path, IN_MODIFY | IN_MOVE_SELF) < 0)
+ Die("inotify_add_watch: %s\n", strerror(errno));
+ }
+}
+
+/* We replace the default YY_INPUT() for the following reasons:
+ *
+ * 1. We want to read data as soon as it becomes available, but the default
+ * YY_INPUT() uses buffered I/O.
+ *
+ * 2. We want to block on end of input and wait for the file to grow.
+ *
+ * 3. We want to detect log rotation, and reopen the input file as needed.
+ */
+void WarnInput(char *buf, yy_size_t *result, size_t max_size) {
+ while (1) {
+ ssize_t ret = read(yyin_fd, buf, max_size);
+ if (ret < 0)
+ Die("read: %s", strerror(errno));
+ *result = ret;
+ if (*result > 0 || fifo || filter)
+ return;
+ if (draining) {
+ /* Assume we're done with this log, and move to next
+ * log. Rsyslogd may keep writing to the old log file
+ * for a while, but we don't care since we don't have
+ * to be exact.
+ */
+ close(yyin_fd);
+ if (YYSTATE == WARN) {
+ /* Be conservative in case we lose the warn
+ * terminator during the switch---or we may
+ * collect personally identifiable information.
+ */
+ WarnEnd();
+ }
+ BEGIN(0); /* see above comment */
+ sleep(1); /* avoid race with log rotator */
+ WarnOpenInput(msg_path);
+ draining = 0;
+ continue;
+ }
+ /* Nothing left to read, so we must wait. */
+ struct inotify_event event;
+ while (1) {
+ int n = read(i_fd, &event, sizeof(event));
+ if (n <= 0) {
+ if (errno == EINTR)
+ continue;
+ else
+ Die("inotify: %s\n", strerror(errno));
+ } else
+ break;
+ }
+ if (event.mask & IN_MOVE_SELF) {
+ /* The file has been renamed. Before switching
+ * to the new one, we process any remaining
+ * content of this file.
+ */
+ draining = 1;
+ }
+ }
+}
+
+int main(int argc, char **argv) {
+ int result;
+ struct passwd *user;
+ prog_name = argv[0];
+
+ if (argc == 2 && strcmp(argv[1], "--test") == 0)
+ testing = 1;
+ else if (argc == 2 && strcmp(argv[1], "--filter") == 0)
+ filter = 1;
+ else if (argc == 2 && strcmp(argv[1], "--fifo") == 0) {
+ fifo = 1;
+ } else if (argc != 1) {
+ fprintf(stderr,
+ "usage: %s [single-flag]\n"
+ "flags (for testing only):\n"
+ "--fifo\tinput is fifo \"fifo\", output is stdout\n"
+ "--filter\tinput is stdin, output is stdout\n"
+ "--test\trun self-test\n",
+ prog_name);
+ exit(1);
+ }
+
+ metrics_library = CMetricsLibraryNew();
+ CMetricsLibraryInit(metrics_library);
+
+ crash_reporter_command = testing ?
+ "./warn_collector_test_reporter.sh" :
+ "/sbin/crash_reporter --kernel_warning";
+
+ /* When filtering with --filter (for development) use stdin for input.
+ * Otherwise read input from a file or a fifo.
+ */
+ yyin_fd = fileno(stdin);
+ if (testing) {
+ msg_path = "messages";
+ warn_dump_path = "warning";
+ }
+ if (fifo) {
+ msg_path = "fifo";
+ }
+ if (!filter) {
+ WarnOpenInput(msg_path);
+ }
+
+ /* Create directory for dump file. Still need to be root here. */
+ unlink(warn_dump_path);
+ if (!testing && !fifo && !filter) {
+ rmdir(warn_dump_dir);
+ result = mkdir(warn_dump_dir, 0755);
+ if (result < 0)
+ Die("could not create %s: %s\n",
+ warn_dump_dir, strerror(errno));
+ }
+
+ if (0) {
+ /* TODO(semenzato): put this back in once we decide it's safe
+ * to make /var/spool/crash rwxrwxrwx root, or use a different
+ * owner and setuid for the crash reporter as well.
+ */
+
+ /* Get low privilege uid, gid. */
+ user = getpwnam("chronos");
+ if (user == NULL)
+ Die("getpwnam failed\n");
+
+ /* Change dump directory ownership. */
+ if (chown(warn_dump_dir, user->pw_uid, user->pw_gid) < 0)
+ Die("chown: %s\n", strerror(errno));
+
+ /* Drop privileges. */
+ if (setuid(user->pw_uid) < 0) {
+ Die("setuid: %s\n", strerror(errno));
+ }
+ }
+
+ /* Go! */
+ return yylex();
+}
+
+/* Flex should really know not to generate these functions.
+ */
+void UnusedFunctionWarningSuppressor(void) {
+ yyunput(0, 0);
+ (void) input();
+}
diff --git a/crash_reporter/warn_collector_test.c b/crash_reporter/warn_collector_test.c
new file mode 100644
index 0000000..7e25d01
--- /dev/null
+++ b/crash_reporter/warn_collector_test.c
@@ -0,0 +1,14 @@
+/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/*
+ * Test driver for the warn_collector daemon.
+ */
+#include <stdlib.h>
+
+int main(int ac, char **av) {
+ int status = system("exec \"${SRC}\"/warn_collector_test.sh");
+ return status < 0 ? EXIT_FAILURE : WEXITSTATUS(status);
+}
diff --git a/crash_reporter/warn_collector_test.sh b/crash_reporter/warn_collector_test.sh
new file mode 100755
index 0000000..d9bb6f9
--- /dev/null
+++ b/crash_reporter/warn_collector_test.sh
@@ -0,0 +1,79 @@
+#! /bin/bash
+# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Test for warn_collector. Run the warn collector in the background, emulate
+# the kernel by appending lines to the log file "messages", and observe the log
+# of the (fake) crash reporter each time is run by the warn collector daemon.
+
+set -e
+
+fail() {
+ printf '[ FAIL ] %b\n' "$*"
+ exit 1
+}
+
+if [[ -z ${SYSROOT} ]]; then
+ fail "SYSROOT must be set for this test to work"
+fi
+: ${OUT:=${PWD}}
+cd "${OUT}"
+PATH=${OUT}:${PATH}
+TESTLOG="${OUT}/warn-test-log"
+
+echo "Testing: $(which warn_collector)"
+
+cleanup() {
+ # Kill daemon (if started) on exit
+ kill %
+}
+
+check_log() {
+ local n_expected=$1
+ if [[ ! -f ${TESTLOG} ]]; then
+ fail "${TESTLOG} was not created"
+ fi
+ if [[ $(wc -l < "${TESTLOG}") -ne ${n_expected} ]]; then
+ fail "expected ${n_expected} lines in ${TESTLOG}, found this instead:
+$(<"${TESTLOG}")"
+ fi
+ if egrep -qv '^[0-9a-f]{8}' "${TESTLOG}"; then
+ fail "found bad lines in ${TESTLOG}:
+$(<"${TESTLOG}")"
+ fi
+}
+
+rm -f "${TESTLOG}"
+cp "${SRC}/warn_collector_test_reporter.sh" .
+cp "${SRC}/TEST_WARNING" .
+cp TEST_WARNING messages
+
+# Start the collector daemon. With the --test option, the daemon reads input
+# from ./messages, writes the warning into ./warning, and invokes
+# ./warn_collector_test_reporter.sh to report the warning.
+warn_collector --test &
+trap cleanup EXIT
+
+# After a while, check that the first warning has been collected.
+sleep 1
+check_log 1
+
+# Add the same warning to messages, verify that it is NOT collected
+cat TEST_WARNING >> messages
+sleep 1
+check_log 1
+
+# Add a slightly different warning to messages, check that it is collected.
+sed s/intel_dp.c/intel_xx.c/ < TEST_WARNING >> messages
+sleep 1
+check_log 2
+
+# Emulate log rotation, add a warning, and check.
+mv messages messages.1
+sed s/intel_dp.c/intel_xy.c/ < TEST_WARNING > messages
+sleep 2
+check_log 3
+
+# Success!
+exit 0
diff --git a/crash_reporter/warn_collector_test_reporter.sh b/crash_reporter/warn_collector_test_reporter.sh
new file mode 100755
index 0000000..d8f3fad
--- /dev/null
+++ b/crash_reporter/warn_collector_test_reporter.sh
@@ -0,0 +1,16 @@
+#! /bin/sh
+# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Replacement for the crash reporter, for testing. Log the first line of the
+# "warning" file, which by convention contains the warning hash, and remove the
+# file.
+
+set -e
+
+exec 1>> warn-test-log
+exec 2>> warn-test-log
+
+head -1 warning
+rm warning
diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk
new file mode 100644
index 0000000..3fca709
--- /dev/null
+++ b/debuggerd/Android.mk
@@ -0,0 +1,129 @@
+LOCAL_PATH := $(call my-dir)
+
+common_cppflags := \
+ -std=gnu++11 \
+ -W \
+ -Wall \
+ -Wextra \
+ -Wunused \
+ -Werror \
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ backtrace.cpp \
+ debuggerd.cpp \
+ elf_utils.cpp \
+ getevent.cpp \
+ tombstone.cpp \
+ utility.cpp \
+
+LOCAL_SRC_FILES_arm := arm/machine.cpp
+LOCAL_SRC_FILES_arm64 := arm64/machine.cpp
+LOCAL_SRC_FILES_mips := mips/machine.cpp
+LOCAL_SRC_FILES_mips64 := mips64/machine.cpp
+LOCAL_SRC_FILES_x86 := x86/machine.cpp
+LOCAL_SRC_FILES_x86_64 := x86_64/machine.cpp
+
+LOCAL_CPPFLAGS := $(common_cppflags)
+
+ifeq ($(TARGET_IS_64_BIT),true)
+LOCAL_CPPFLAGS += -DTARGET_IS_64_BIT
+endif
+
+LOCAL_SHARED_LIBRARIES := \
+ libbacktrace \
+ libbase \
+ libcutils \
+ liblog \
+ libselinux \
+
+LOCAL_CLANG := true
+
+LOCAL_MODULE := debuggerd
+LOCAL_MODULE_STEM_32 := debuggerd
+LOCAL_MODULE_STEM_64 := debuggerd64
+LOCAL_MULTILIB := both
+
+include $(BUILD_EXECUTABLE)
+
+
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := crasher.c
+LOCAL_SRC_FILES_arm := arm/crashglue.S
+LOCAL_SRC_FILES_arm64 := arm64/crashglue.S
+LOCAL_SRC_FILES_mips := mips/crashglue.S
+LOCAL_SRC_FILES_mips64 := mips64/crashglue.S
+LOCAL_SRC_FILES_x86 := x86/crashglue.S
+LOCAL_SRC_FILES_x86_64 := x86_64/crashglue.S
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS += -fstack-protector-all -Werror -Wno-free-nonheap-object
+#LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_SHARED_LIBRARIES := libcutils liblog libc
+
+# The arm emulator has VFP but not VFPv3-D32.
+ifeq ($(ARCH_ARM_HAVE_VFP_D32),true)
+LOCAL_ASFLAGS_arm += -DHAS_VFP_D32
+endif
+
+LOCAL_MODULE := crasher
+LOCAL_MODULE_STEM_32 := crasher
+LOCAL_MODULE_STEM_64 := crasher64
+LOCAL_MULTILIB := both
+
+include $(BUILD_EXECUTABLE)
+
+debuggerd_test_src_files := \
+ utility.cpp \
+ test/dump_memory_test.cpp \
+ test/elf_fake.cpp \
+ test/log_fake.cpp \
+ test/property_fake.cpp \
+ test/ptrace_fake.cpp \
+ test/tombstone_test.cpp \
+ test/selinux_fake.cpp \
+
+debuggerd_shared_libraries := \
+ libbacktrace \
+ libbase \
+ libcutils \
+
+debuggerd_c_includes := \
+ $(LOCAL_PATH)/test \
+
+debuggerd_cpp_flags := \
+ $(common_cppflags) \
+ -Wno-missing-field-initializers \
+
+# Only build the host tests on linux.
+ifeq ($(HOST_OS),linux)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := debuggerd_test
+LOCAL_SRC_FILES := $(debuggerd_test_src_files)
+LOCAL_SHARED_LIBRARIES := $(debuggerd_shared_libraries)
+LOCAL_C_INCLUDES := $(debuggerd_c_includes)
+LOCAL_CPPFLAGS := $(debuggerd_cpp_flags)
+
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_MULTILIB := both
+include $(BUILD_HOST_NATIVE_TEST)
+
+endif
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := debuggerd_test
+LOCAL_SRC_FILES := $(debuggerd_test_src_files)
+LOCAL_SHARED_LIBRARIES := $(debuggerd_shared_libraries)
+LOCAL_C_INCLUDES := $(debuggerd_c_includes)
+LOCAL_CPPFLAGS := $(debuggerd_cpp_flags)
+
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_MULTILIB := both
+include $(BUILD_NATIVE_TEST)
diff --git a/debuggerd/MODULE_LICENSE_APACHE2 b/debuggerd/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/debuggerd/MODULE_LICENSE_APACHE2
diff --git a/debuggerd/NOTICE b/debuggerd/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/debuggerd/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/debuggerd/arm/crashglue.S b/debuggerd/arm/crashglue.S
new file mode 100644
index 0000000..4fbfd6e
--- /dev/null
+++ b/debuggerd/arm/crashglue.S
@@ -0,0 +1,64 @@
+.globl crash1
+.type crash1, %function
+crash1:
+ ldr r0, =0xa5a50000
+ ldr r1, =0xa5a50001
+ ldr r2, =0xa5a50002
+ ldr r3, =0xa5a50003
+ ldr r4, =0xa5a50004
+ ldr r5, =0xa5a50005
+ ldr r6, =0xa5a50006
+ ldr r7, =0xa5a50007
+ ldr r8, =0xa5a50008
+ ldr r9, =0xa5a50009
+ ldr r10, =0xa5a50010
+ ldr r11, =0xa5a50011
+ ldr r12, =0xa5a50012
+
+
+ fconstd d0, #0
+ fconstd d1, #1
+ fconstd d2, #2
+ fconstd d3, #3
+ fconstd d4, #4
+ fconstd d5, #5
+ fconstd d6, #6
+ fconstd d7, #7
+ fconstd d8, #8
+ fconstd d9, #9
+ fconstd d10, #10
+ fconstd d11, #11
+ fconstd d12, #12
+ fconstd d13, #13
+ fconstd d14, #14
+ fconstd d15, #15
+#if defined(HAS_VFP_D32)
+ fconstd d16, #16
+ fconstd d17, #17
+ fconstd d18, #18
+ fconstd d19, #19
+ fconstd d20, #20
+ fconstd d21, #21
+ fconstd d22, #22
+ fconstd d23, #23
+ fconstd d24, #24
+ fconstd d25, #25
+ fconstd d26, #26
+ fconstd d27, #27
+ fconstd d28, #28
+ fconstd d29, #29
+ fconstd d30, #30
+ fconstd d31, #31
+#endif
+
+ mov lr, #0
+ ldr lr, [lr]
+ b .
+
+.globl crashnostack
+.type crashnostack, %function
+crashnostack:
+ mov sp, #0
+ mov r0, #0
+ ldr r0, [r0]
+ b .
diff --git a/debuggerd/arm/machine.cpp b/debuggerd/arm/machine.cpp
new file mode 100644
index 0000000..78c2306
--- /dev/null
+++ b/debuggerd/arm/machine.cpp
@@ -0,0 +1,83 @@
+/*
+ *
+ * Copyright 2006, 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 "DEBUG"
+
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/ptrace.h>
+
+#include <backtrace/Backtrace.h>
+#include <log/log.h>
+
+#include "machine.h"
+#include "utility.h"
+
+void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
+ pt_regs regs;
+ if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, ®s)) {
+ ALOGE("cannot get registers: %s\n", strerror(errno));
+ return;
+ }
+
+ static const char reg_names[] = "r0r1r2r3r4r5r6r7r8r9slfpipsp";
+
+ for (int reg = 0; reg < 14; reg++) {
+ dump_memory(log, backtrace, regs.uregs[reg], "memory near %.2s:", ®_names[reg * 2]);
+ }
+
+ dump_memory(log, backtrace, static_cast<uintptr_t>(regs.ARM_pc), "code around pc:");
+
+ if (regs.ARM_pc != regs.ARM_lr) {
+ dump_memory(log, backtrace, static_cast<uintptr_t>(regs.ARM_lr), "code around lr:");
+ }
+}
+
+void dump_registers(log_t* log, pid_t tid) {
+ pt_regs r;
+ if (ptrace(PTRACE_GETREGS, tid, 0, &r)) {
+ ALOGE("cannot get registers: %s\n", strerror(errno));
+ return;
+ }
+
+ _LOG(log, logtype::REGISTERS, " r0 %08x r1 %08x r2 %08x r3 %08x\n",
+ static_cast<uint32_t>(r.ARM_r0), static_cast<uint32_t>(r.ARM_r1),
+ static_cast<uint32_t>(r.ARM_r2), static_cast<uint32_t>(r.ARM_r3));
+ _LOG(log, logtype::REGISTERS, " r4 %08x r5 %08x r6 %08x r7 %08x\n",
+ static_cast<uint32_t>(r.ARM_r4), static_cast<uint32_t>(r.ARM_r5),
+ static_cast<uint32_t>(r.ARM_r6), static_cast<uint32_t>(r.ARM_r7));
+ _LOG(log, logtype::REGISTERS, " r8 %08x r9 %08x sl %08x fp %08x\n",
+ static_cast<uint32_t>(r.ARM_r8), static_cast<uint32_t>(r.ARM_r9),
+ static_cast<uint32_t>(r.ARM_r10), static_cast<uint32_t>(r.ARM_fp));
+ _LOG(log, logtype::REGISTERS, " ip %08x sp %08x lr %08x pc %08x cpsr %08x\n",
+ static_cast<uint32_t>(r.ARM_ip), static_cast<uint32_t>(r.ARM_sp),
+ static_cast<uint32_t>(r.ARM_lr), static_cast<uint32_t>(r.ARM_pc),
+ static_cast<uint32_t>(r.ARM_cpsr));
+
+ user_vfp vfp_regs;
+ if (ptrace(PTRACE_GETVFPREGS, tid, 0, &vfp_regs)) {
+ ALOGE("cannot get FP registers: %s\n", strerror(errno));
+ return;
+ }
+
+ for (size_t i = 0; i < 32; i += 2) {
+ _LOG(log, logtype::FP_REGISTERS, " d%-2d %016llx d%-2d %016llx\n",
+ i, vfp_regs.fpregs[i], i+1, vfp_regs.fpregs[i+1]);
+ }
+ _LOG(log, logtype::FP_REGISTERS, " scr %08lx\n", vfp_regs.fpscr);
+}
diff --git a/debuggerd/arm64/crashglue.S b/debuggerd/arm64/crashglue.S
new file mode 100644
index 0000000..e58b542
--- /dev/null
+++ b/debuggerd/arm64/crashglue.S
@@ -0,0 +1,79 @@
+.globl crash1
+.type crash1, %function
+crash1:
+ ldr x0, =0xa5a50000
+ ldr x1, =0xa5a50001
+ ldr x2, =0xa5a50002
+ ldr x3, =0xa5a50003
+ ldr x4, =0xa5a50004
+ ldr x5, =0xa5a50005
+ ldr x6, =0xa5a50006
+ ldr x7, =0xa5a50007
+ ldr x8, =0xa5a50008
+ ldr x9, =0xa5a50009
+ ldr x10, =0xa5a50010
+ ldr x11, =0xa5a50011
+ ldr x12, =0xa5a50012
+ ldr x13, =0xa5a50013
+ ldr x14, =0xa5a50014
+ ldr x15, =0xa5a50015
+ ldr x16, =0xa5a50016
+ ldr x17, =0xa5a50017
+ ldr x18, =0xa5a50018
+ ldr x19, =0xa5a50019
+ ldr x20, =0xa5a50020
+ ldr x21, =0xa5a50021
+ ldr x22, =0xa5a50022
+ ldr x23, =0xa5a50023
+ ldr x24, =0xa5a50024
+ ldr x25, =0xa5a50025
+ ldr x26, =0xa5a50026
+ ldr x27, =0xa5a50027
+ ldr x28, =0xa5a50028
+ ldr x29, =0xa5a50029
+
+ fmov d0, -1.0 // -1 is more convincing than 0.
+ fmov d1, 1.0
+ fmov d2, 2.0
+ fmov d3, 3.0
+ fmov d4, 4.0
+ fmov d5, 5.0
+ fmov d6, 6.0
+ fmov d7, 7.0
+ fmov d8, 8.0
+ fmov d9, 9.0
+ fmov d10, 10.0
+ fmov d11, 11.0
+ fmov d12, 12.0
+ fmov d13, 13.0
+ fmov d14, 14.0
+ fmov d15, 15.0
+ fmov d16, 16.0
+ fmov d17, 17.0
+ fmov d18, 18.0
+ fmov d19, 19.0
+ fmov d20, 20.0
+ fmov d21, 21.0
+ fmov d22, 22.0
+ fmov d23, 23.0
+ fmov d24, 24.0
+ fmov d25, 25.0
+ fmov d26, 26.0
+ fmov d27, 27.0
+ fmov d28, 28.0
+ fmov d29, 29.0
+ fmov d30, 30.0
+ fmov d31, 31.0
+
+ mov x30, xzr
+ ldr x30, [x30]
+ b .
+
+
+.globl crashnostack
+.type crashnostack, %function
+crashnostack:
+ mov x0, xzr
+ add sp, x0, xzr
+ ldr x0, [x0]
+ b .
diff --git a/debuggerd/arm64/machine.cpp b/debuggerd/arm64/machine.cpp
new file mode 100644
index 0000000..e7bf79a
--- /dev/null
+++ b/debuggerd/arm64/machine.cpp
@@ -0,0 +1,101 @@
+/*
+ *
+ * Copyright 2014, 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 "DEBUG"
+
+#include <elf.h>
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/ptrace.h>
+#include <sys/uio.h>
+
+#include <backtrace/Backtrace.h>
+#include <log/log.h>
+
+#include "machine.h"
+#include "utility.h"
+
+void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
+ struct user_pt_regs regs;
+ struct iovec io;
+ io.iov_base = ®s;
+ io.iov_len = sizeof(regs);
+
+ if (ptrace(PTRACE_GETREGSET, backtrace->Tid(), reinterpret_cast<void*>(NT_PRSTATUS), &io) == -1) {
+ ALOGE("ptrace failed to get registers: %s", strerror(errno));
+ return;
+ }
+
+ for (int reg = 0; reg < 31; reg++) {
+ dump_memory(log, backtrace, regs.regs[reg], "memory near x%d:", reg);
+ }
+
+ dump_memory(log, backtrace, static_cast<uintptr_t>(regs.pc), "code around pc:");
+
+ if (regs.pc != regs.sp) {
+ dump_memory(log, backtrace, static_cast<uintptr_t>(regs.sp), "code around sp:");
+ }
+}
+
+void dump_registers(log_t* log, pid_t tid) {
+ struct user_pt_regs r;
+ struct iovec io;
+ io.iov_base = &r;
+ io.iov_len = sizeof(r);
+
+ if (ptrace(PTRACE_GETREGSET, tid, (void*) NT_PRSTATUS, (void*) &io) == -1) {
+ ALOGE("ptrace error: %s\n", strerror(errno));
+ return;
+ }
+
+ for (int i = 0; i < 28; i += 4) {
+ _LOG(log, logtype::REGISTERS,
+ " x%-2d %016llx x%-2d %016llx x%-2d %016llx x%-2d %016llx\n",
+ i, r.regs[i],
+ i+1, r.regs[i+1],
+ i+2, r.regs[i+2],
+ i+3, r.regs[i+3]);
+ }
+
+ _LOG(log, logtype::REGISTERS, " x28 %016llx x29 %016llx x30 %016llx\n",
+ r.regs[28], r.regs[29], r.regs[30]);
+
+ _LOG(log, logtype::REGISTERS, " sp %016llx pc %016llx pstate %016llx\n",
+ r.sp, r.pc, r.pstate);
+
+ struct user_fpsimd_state f;
+ io.iov_base = &f;
+ io.iov_len = sizeof(f);
+
+ if (ptrace(PTRACE_GETREGSET, tid, (void*) NT_PRFPREG, (void*) &io) == -1) {
+ ALOGE("ptrace error: %s\n", strerror(errno));
+ return;
+ }
+
+ for (int i = 0; i < 32; i += 2) {
+ _LOG(log, logtype::FP_REGISTERS,
+ " v%-2d %016" PRIx64 "%016" PRIx64 " v%-2d %016" PRIx64 "%016" PRIx64 "\n",
+ i,
+ static_cast<uint64_t>(f.vregs[i] >> 64),
+ static_cast<uint64_t>(f.vregs[i]),
+ i+1,
+ static_cast<uint64_t>(f.vregs[i+1] >> 64),
+ static_cast<uint64_t>(f.vregs[i+1]));
+ }
+ _LOG(log, logtype::FP_REGISTERS, " fpsr %08x fpcr %08x\n", f.fpsr, f.fpcr);
+}
diff --git a/debuggerd/backtrace.cpp b/debuggerd/backtrace.cpp
new file mode 100644
index 0000000..b46f8f4
--- /dev/null
+++ b/debuggerd/backtrace.cpp
@@ -0,0 +1,150 @@
+/*
+ * 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 LOG_TAG "DEBUG"
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+#include <limits.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/ptrace.h>
+
+#include <memory>
+
+#include <backtrace/Backtrace.h>
+
+#include <log/log.h>
+
+#include "backtrace.h"
+
+#include "utility.h"
+
+static void dump_process_header(log_t* log, pid_t pid) {
+ char path[PATH_MAX];
+ char procnamebuf[1024];
+ char* procname = NULL;
+ FILE* fp;
+
+ snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
+ if ((fp = fopen(path, "r"))) {
+ procname = fgets(procnamebuf, sizeof(procnamebuf), fp);
+ fclose(fp);
+ }
+
+ time_t t = time(NULL);
+ struct tm tm;
+ localtime_r(&t, &tm);
+ char timestr[64];
+ strftime(timestr, sizeof(timestr), "%F %T", &tm);
+ _LOG(log, logtype::BACKTRACE, "\n\n----- pid %d at %s -----\n", pid, timestr);
+
+ if (procname) {
+ _LOG(log, logtype::BACKTRACE, "Cmd line: %s\n", procname);
+ }
+ _LOG(log, logtype::BACKTRACE, "ABI: '%s'\n", ABI_STRING);
+}
+
+static void dump_process_footer(log_t* log, pid_t pid) {
+ _LOG(log, logtype::BACKTRACE, "\n----- end %d -----\n", pid);
+}
+
+static void dump_thread(
+ log_t* log, pid_t tid, bool attached, bool* detach_failed, int* total_sleep_time_usec) {
+ char path[PATH_MAX];
+ char threadnamebuf[1024];
+ char* threadname = NULL;
+ FILE* fp;
+
+ snprintf(path, sizeof(path), "/proc/%d/comm", tid);
+ if ((fp = fopen(path, "r"))) {
+ threadname = fgets(threadnamebuf, sizeof(threadnamebuf), fp);
+ fclose(fp);
+ if (threadname) {
+ size_t len = strlen(threadname);
+ if (len && threadname[len - 1] == '\n') {
+ threadname[len - 1] = '\0';
+ }
+ }
+ }
+
+ _LOG(log, logtype::BACKTRACE, "\n\"%s\" sysTid=%d\n", threadname ? threadname : "<unknown>", tid);
+
+ if (!attached && ptrace(PTRACE_ATTACH, tid, 0, 0) < 0) {
+ _LOG(log, logtype::BACKTRACE, "Could not attach to thread: %s\n", strerror(errno));
+ return;
+ }
+
+ if (!attached && wait_for_sigstop(tid, total_sleep_time_usec, detach_failed) == -1) {
+ return;
+ }
+
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(tid, BACKTRACE_CURRENT_THREAD));
+ if (backtrace->Unwind(0)) {
+ dump_backtrace_to_log(backtrace.get(), log, " ");
+ } else {
+ ALOGE("Unwind failed: tid = %d", tid);
+ }
+
+ if (!attached && ptrace(PTRACE_DETACH, tid, 0, 0) != 0) {
+ ALOGE("ptrace detach from %d failed: %s\n", tid, strerror(errno));
+ *detach_failed = true;
+ }
+}
+
+void dump_backtrace(int fd, int amfd, pid_t pid, pid_t tid, bool* detach_failed,
+ int* total_sleep_time_usec) {
+ log_t log;
+ log.tfd = fd;
+ log.amfd = amfd;
+
+ dump_process_header(&log, pid);
+ dump_thread(&log, tid, true, detach_failed, total_sleep_time_usec);
+
+ char task_path[64];
+ snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid);
+ DIR* d = opendir(task_path);
+ if (d != NULL) {
+ struct dirent* de = NULL;
+ while ((de = readdir(d)) != NULL) {
+ if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
+ continue;
+ }
+
+ char* end;
+ pid_t new_tid = strtoul(de->d_name, &end, 10);
+ if (*end || new_tid == tid) {
+ continue;
+ }
+
+ dump_thread(&log, new_tid, false, detach_failed, total_sleep_time_usec);
+ }
+ closedir(d);
+ }
+
+ dump_process_footer(&log, pid);
+}
+
+void dump_backtrace_to_log(Backtrace* backtrace, log_t* log, const char* prefix) {
+ for (size_t i = 0; i < backtrace->NumFrames(); i++) {
+ _LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, backtrace->FormatFrameData(i).c_str());
+ }
+}
diff --git a/debuggerd/backtrace.h b/debuggerd/backtrace.h
new file mode 100644
index 0000000..da14cd4
--- /dev/null
+++ b/debuggerd/backtrace.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DEBUGGERD_BACKTRACE_H
+#define _DEBUGGERD_BACKTRACE_H
+
+#include <sys/types.h>
+
+#include "utility.h"
+
+class Backtrace;
+
+// Dumps a backtrace using a format similar to what Dalvik uses so that the result
+// can be intermixed in a bug report.
+void dump_backtrace(int fd, int amfd, pid_t pid, pid_t tid, bool* detach_failed,
+ int* total_sleep_time_usec);
+
+/* Dumps the backtrace in the backtrace data structure to the log. */
+void dump_backtrace_to_log(Backtrace* backtrace, log_t* log, const char* prefix);
+
+#endif // _DEBUGGERD_BACKTRACE_H
diff --git a/debuggerd/crasher.c b/debuggerd/crasher.c
new file mode 100644
index 0000000..af86fe9
--- /dev/null
+++ b/debuggerd/crasher.c
@@ -0,0 +1,213 @@
+#include <assert.h>
+#include <errno.h>
+#include <pthread.h>
+#include <sched.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/cdefs.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <cutils/sockets.h>
+#include <log/log.h>
+
+#ifndef __unused
+#define __unused __attribute__((__unused__))
+#endif
+
+extern const char* __progname;
+
+void crash1(void);
+void crashnostack(void);
+static int do_action(const char* arg);
+
+static void maybe_abort() {
+ if (time(0) != 42) {
+ abort();
+ }
+}
+
+static char* smash_stack_dummy_buf;
+__attribute__ ((noinline)) static void smash_stack_dummy_function(volatile int* plen) {
+ smash_stack_dummy_buf[*plen] = 0;
+}
+
+// This must be marked with "__attribute__ ((noinline))", to ensure the
+// compiler generates the proper stack guards around this function.
+// Assign local array address to global variable to force stack guards.
+// Use another noinline function to corrupt the stack.
+__attribute__ ((noinline)) static int smash_stack(volatile int* plen) {
+ printf("crasher: deliberately corrupting stack...\n");
+
+ char buf[128];
+ smash_stack_dummy_buf = buf;
+ // This should corrupt stack guards and make process abort.
+ smash_stack_dummy_function(plen);
+ return 0;
+}
+
+static void* global = 0; // So GCC doesn't optimize the tail recursion out of overflow_stack.
+
+__attribute__((noinline)) static void overflow_stack(void* p) {
+ void* buf[1];
+ buf[0] = p;
+ global = buf;
+ overflow_stack(&buf);
+}
+
+static void *noisy(void *x)
+{
+ char c = (uintptr_t) x;
+ for(;;) {
+ usleep(250*1000);
+ write(2, &c, 1);
+ if(c == 'C') *((volatile unsigned*) 0) = 42;
+ }
+ return NULL;
+}
+
+static int ctest()
+{
+ pthread_t thr;
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ pthread_create(&thr, &attr, noisy, (void*) 'A');
+ pthread_create(&thr, &attr, noisy, (void*) 'B');
+ pthread_create(&thr, &attr, noisy, (void*) 'C');
+ for(;;) ;
+ return 0;
+}
+
+static void* thread_callback(void* raw_arg)
+{
+ return (void*) (uintptr_t) do_action((const char*) raw_arg);
+}
+
+static int do_action_on_thread(const char* arg)
+{
+ pthread_t t;
+ pthread_create(&t, NULL, thread_callback, (void*) arg);
+ void* result = NULL;
+ pthread_join(t, &result);
+ return (int) (uintptr_t) result;
+}
+
+__attribute__((noinline)) static int crash3(int a) {
+ *((int*) 0xdead) = a;
+ return a*4;
+}
+
+__attribute__((noinline)) static int crash2(int a) {
+ a = crash3(a) + 2;
+ return a*3;
+}
+
+__attribute__((noinline)) static int crash(int a) {
+ a = crash2(a) + 1;
+ return a*2;
+}
+
+static void abuse_heap() {
+ char buf[16];
+ free((void*) buf); // GCC is smart enough to warn about this, but we're doing it deliberately.
+}
+
+static void sigsegv_non_null() {
+ int* a = (int *)(&do_action);
+ *a = 42;
+}
+
+static int do_action(const char* arg)
+{
+ fprintf(stderr,"crasher: init pid=%d tid=%d\n", getpid(), gettid());
+
+ if (!strncmp(arg, "thread-", strlen("thread-"))) {
+ return do_action_on_thread(arg + strlen("thread-"));
+ } else if (!strcmp(arg, "SIGSEGV-non-null")) {
+ sigsegv_non_null();
+ } else if (!strcmp(arg, "smash-stack")) {
+ volatile int len = 128;
+ return smash_stack(&len);
+ } else if (!strcmp(arg, "stack-overflow")) {
+ overflow_stack(NULL);
+ } else if (!strcmp(arg, "nostack")) {
+ crashnostack();
+ } else if (!strcmp(arg, "ctest")) {
+ return ctest();
+ } else if (!strcmp(arg, "exit")) {
+ exit(1);
+ } else if (!strcmp(arg, "crash") || !strcmp(arg, "SIGSEGV")) {
+ return crash(42);
+ } else if (!strcmp(arg, "abort")) {
+ maybe_abort();
+ } else if (!strcmp(arg, "assert")) {
+ __assert("some_file.c", 123, "false");
+ } else if (!strcmp(arg, "assert2")) {
+ __assert2("some_file.c", 123, "some_function", "false");
+ } else if (!strcmp(arg, "LOG_ALWAYS_FATAL")) {
+ LOG_ALWAYS_FATAL("hello %s", "world");
+ } else if (!strcmp(arg, "LOG_ALWAYS_FATAL_IF")) {
+ LOG_ALWAYS_FATAL_IF(true, "hello %s", "world");
+ } else if (!strcmp(arg, "SIGFPE")) {
+ raise(SIGFPE);
+ return EXIT_SUCCESS;
+ } else if (!strcmp(arg, "SIGPIPE")) {
+ int pipe_fds[2];
+ pipe(pipe_fds);
+ close(pipe_fds[0]);
+ write(pipe_fds[1], "oops", 4);
+ return EXIT_SUCCESS;
+ } else if (!strcmp(arg, "SIGTRAP")) {
+ raise(SIGTRAP);
+ return EXIT_SUCCESS;
+ } else if (!strcmp(arg, "heap-usage")) {
+ abuse_heap();
+ } else if (!strcmp(arg, "SIGSEGV-unmapped")) {
+ char* map = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+ munmap(map, sizeof(int));
+ map[0] = '8';
+ }
+
+ fprintf(stderr, "%s OP\n", __progname);
+ fprintf(stderr, "where OP is:\n");
+ fprintf(stderr, " smash-stack overwrite a stack-guard canary\n");
+ fprintf(stderr, " stack-overflow recurse until the stack overflows\n");
+ fprintf(stderr, " heap-corruption cause a libc abort by corrupting the heap\n");
+ fprintf(stderr, " heap-usage cause a libc abort by abusing a heap function\n");
+ fprintf(stderr, " nostack crash with a NULL stack pointer\n");
+ fprintf(stderr, " ctest (obsoleted by thread-crash?)\n");
+ fprintf(stderr, " exit call exit(1)\n");
+ fprintf(stderr, " abort call abort()\n");
+ fprintf(stderr, " assert call assert() without a function\n");
+ fprintf(stderr, " assert2 call assert() with a function\n");
+ fprintf(stderr, " LOG_ALWAYS_FATAL call LOG_ALWAYS_FATAL\n");
+ fprintf(stderr, " LOG_ALWAYS_FATAL_IF call LOG_ALWAYS_FATAL\n");
+ fprintf(stderr, " SIGFPE cause a SIGFPE\n");
+ fprintf(stderr, " SIGPIPE cause a SIGPIPE\n");
+ fprintf(stderr, " SIGSEGV cause a SIGSEGV at address 0x0 (synonym: crash)\n");
+ fprintf(stderr, " SIGSEGV-non-null cause a SIGSEGV at a non-zero address\n");
+ fprintf(stderr, " SIGSEGV-unmapped mmap/munmap a region of memory and then attempt to access it\n");
+ fprintf(stderr, " SIGTRAP cause a SIGTRAP\n");
+ fprintf(stderr, "prefix any of the above with 'thread-' to not run\n");
+ fprintf(stderr, "on the process' main thread.\n");
+ return EXIT_SUCCESS;
+}
+
+int main(int argc, char **argv)
+{
+ fprintf(stderr,"crasher: built at " __TIME__ "!@\n");
+
+ if(argc > 1) {
+ return do_action(argv[1]);
+ } else {
+ crash1();
+ }
+
+ return 0;
+}
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
new file mode 100644
index 0000000..787b7aa
--- /dev/null
+++ b/debuggerd/debuggerd.cpp
@@ -0,0 +1,594 @@
+/*
+ * Copyright 2006, 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 <errno.h>
+#include <signal.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <time.h>
+
+#include <sys/ptrace.h>
+#include <sys/wait.h>
+#include <elf.h>
+#include <sys/stat.h>
+#include <sys/poll.h>
+
+#include <selinux/android.h>
+
+#include <log/logger.h>
+
+#include <cutils/sockets.h>
+#include <cutils/properties.h>
+#include <cutils/debugger.h>
+
+#include <linux/input.h>
+
+#include <private/android_filesystem_config.h>
+
+#include "backtrace.h"
+#include "getevent.h"
+#include "tombstone.h"
+#include "utility.h"
+
+// If the 32 bit executable is compiled on a 64 bit system,
+// use the 32 bit socket name.
+#if defined(TARGET_IS_64_BIT) && !defined(__LP64__)
+#define SOCKET_NAME DEBUGGER32_SOCKET_NAME
+#else
+#define SOCKET_NAME DEBUGGER_SOCKET_NAME
+#endif
+
+struct debugger_request_t {
+ debugger_action_t action;
+ pid_t pid, tid;
+ uid_t uid, gid;
+ uintptr_t abort_msg_address;
+ int32_t original_si_code;
+};
+
+static void wait_for_user_action(const debugger_request_t& request) {
+ // Explain how to attach the debugger.
+ ALOGI("***********************************************************\n"
+ "* Process %d has been suspended while crashing.\n"
+ "* To attach gdbserver and start gdb, run this on the host:\n"
+ "*\n"
+ "* gdbclient %d\n"
+ "*\n"
+ "* Wait for gdb to start, then press the VOLUME DOWN key\n"
+ "* to let the process continue crashing.\n"
+ "***********************************************************",
+ request.pid, request.tid);
+
+ // Wait for VOLUME DOWN.
+ if (init_getevent() == 0) {
+ while (true) {
+ input_event e;
+ if (get_event(&e, -1) == 0) {
+ if (e.type == EV_KEY && e.code == KEY_VOLUMEDOWN && e.value == 0) {
+ break;
+ }
+ }
+ }
+ uninit_getevent();
+ }
+
+ ALOGI("debuggerd resuming process %d", request.pid);
+}
+
+static int get_process_info(pid_t tid, pid_t* out_pid, uid_t* out_uid, uid_t* out_gid) {
+ char path[64];
+ snprintf(path, sizeof(path), "/proc/%d/status", tid);
+
+ FILE* fp = fopen(path, "r");
+ if (!fp) {
+ return -1;
+ }
+
+ int fields = 0;
+ char line[1024];
+ while (fgets(line, sizeof(line), fp)) {
+ size_t len = strlen(line);
+ if (len > 6 && !memcmp(line, "Tgid:\t", 6)) {
+ *out_pid = atoi(line + 6);
+ fields |= 1;
+ } else if (len > 5 && !memcmp(line, "Uid:\t", 5)) {
+ *out_uid = atoi(line + 5);
+ fields |= 2;
+ } else if (len > 5 && !memcmp(line, "Gid:\t", 5)) {
+ *out_gid = atoi(line + 5);
+ fields |= 4;
+ }
+ }
+ fclose(fp);
+ return fields == 7 ? 0 : -1;
+}
+
+/*
+ * Corresponds with debugger_action_t enum type in
+ * include/cutils/debugger.h.
+ */
+static const char *debuggerd_perms[] = {
+ NULL, /* crash is only used on self, no check applied */
+ "dump_tombstone",
+ "dump_backtrace"
+};
+
+static bool selinux_action_allowed(int s, pid_t tid, debugger_action_t action)
+{
+ char *scon = NULL, *tcon = NULL;
+ const char *tclass = "debuggerd";
+ const char *perm;
+ bool allowed = false;
+
+ if (action <= 0 || action >= (sizeof(debuggerd_perms)/sizeof(debuggerd_perms[0]))) {
+ ALOGE("SELinux: No permission defined for debugger action %d", action);
+ return false;
+ }
+
+ perm = debuggerd_perms[action];
+
+ if (getpeercon(s, &scon) < 0) {
+ ALOGE("Cannot get peer context from socket\n");
+ goto out;
+ }
+
+ if (getpidcon(tid, &tcon) < 0) {
+ ALOGE("Cannot get context for tid %d\n", tid);
+ goto out;
+ }
+
+ allowed = (selinux_check_access(scon, tcon, tclass, perm, NULL) == 0);
+
+out:
+ freecon(scon);
+ freecon(tcon);
+ return allowed;
+}
+
+static int read_request(int fd, debugger_request_t* out_request) {
+ ucred cr;
+ socklen_t len = sizeof(cr);
+ int status = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len);
+ if (status != 0) {
+ ALOGE("cannot get credentials");
+ return -1;
+ }
+
+ ALOGV("reading tid");
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+
+ pollfd pollfds[1];
+ pollfds[0].fd = fd;
+ pollfds[0].events = POLLIN;
+ pollfds[0].revents = 0;
+ status = TEMP_FAILURE_RETRY(poll(pollfds, 1, 3000));
+ if (status != 1) {
+ ALOGE("timed out reading tid (from pid=%d uid=%d)\n", cr.pid, cr.uid);
+ return -1;
+ }
+
+ debugger_msg_t msg;
+ memset(&msg, 0, sizeof(msg));
+ status = TEMP_FAILURE_RETRY(read(fd, &msg, sizeof(msg)));
+ if (status < 0) {
+ ALOGE("read failure? %s (pid=%d uid=%d)\n", strerror(errno), cr.pid, cr.uid);
+ return -1;
+ }
+ if (status != sizeof(debugger_msg_t)) {
+ ALOGE("invalid crash request of size %d (from pid=%d uid=%d)\n", status, cr.pid, cr.uid);
+ return -1;
+ }
+
+ out_request->action = static_cast<debugger_action_t>(msg.action);
+ out_request->tid = msg.tid;
+ out_request->pid = cr.pid;
+ out_request->uid = cr.uid;
+ out_request->gid = cr.gid;
+ out_request->abort_msg_address = msg.abort_msg_address;
+ out_request->original_si_code = msg.original_si_code;
+
+ if (msg.action == DEBUGGER_ACTION_CRASH) {
+ // Ensure that the tid reported by the crashing process is valid.
+ char buf[64];
+ struct stat s;
+ snprintf(buf, sizeof buf, "/proc/%d/task/%d", out_request->pid, out_request->tid);
+ if (stat(buf, &s)) {
+ ALOGE("tid %d does not exist in pid %d. ignoring debug request\n",
+ out_request->tid, out_request->pid);
+ return -1;
+ }
+ } else if (cr.uid == 0
+ || (cr.uid == AID_SYSTEM && msg.action == DEBUGGER_ACTION_DUMP_BACKTRACE)) {
+ // Only root or system can ask us to attach to any process and dump it explicitly.
+ // However, system is only allowed to collect backtraces but cannot dump tombstones.
+ status = get_process_info(out_request->tid, &out_request->pid,
+ &out_request->uid, &out_request->gid);
+ if (status < 0) {
+ ALOGE("tid %d does not exist. ignoring explicit dump request\n", out_request->tid);
+ return -1;
+ }
+
+ if (!selinux_action_allowed(fd, out_request->tid, out_request->action))
+ return -1;
+ } else {
+ // No one else is allowed to dump arbitrary processes.
+ return -1;
+ }
+ return 0;
+}
+
+static bool should_attach_gdb(debugger_request_t* request) {
+ if (request->action == DEBUGGER_ACTION_CRASH) {
+ return property_get_bool("debug.debuggerd.wait_for_gdb", false);
+ }
+ return false;
+}
+
+#if defined(__LP64__)
+static bool is32bit(pid_t tid) {
+ char* exeline;
+ if (asprintf(&exeline, "/proc/%d/exe", tid) == -1) {
+ return false;
+ }
+ int fd = TEMP_FAILURE_RETRY(open(exeline, O_RDONLY | O_CLOEXEC));
+ int saved_errno = errno;
+ free(exeline);
+ if (fd == -1) {
+ ALOGW("Failed to open /proc/%d/exe %s", tid, strerror(saved_errno));
+ return false;
+ }
+
+ char ehdr[EI_NIDENT];
+ ssize_t bytes = TEMP_FAILURE_RETRY(read(fd, &ehdr, sizeof(ehdr)));
+ close(fd);
+ if (bytes != (ssize_t) sizeof(ehdr) || memcmp(ELFMAG, ehdr, SELFMAG) != 0) {
+ return false;
+ }
+ if (ehdr[EI_CLASS] == ELFCLASS32) {
+ return true;
+ }
+ return false;
+}
+
+static void redirect_to_32(int fd, debugger_request_t* request) {
+ debugger_msg_t msg;
+ memset(&msg, 0, sizeof(msg));
+ msg.tid = request->tid;
+ msg.action = request->action;
+
+ int sock_fd = socket_local_client(DEBUGGER32_SOCKET_NAME, ANDROID_SOCKET_NAMESPACE_ABSTRACT,
+ SOCK_STREAM | SOCK_CLOEXEC);
+ if (sock_fd < 0) {
+ ALOGE("Failed to connect to debuggerd32: %s", strerror(errno));
+ return;
+ }
+
+ if (TEMP_FAILURE_RETRY(write(sock_fd, &msg, sizeof(msg))) != (ssize_t) sizeof(msg)) {
+ ALOGE("Failed to write request to debuggerd32 socket: %s", strerror(errno));
+ close(sock_fd);
+ return;
+ }
+
+ char ack;
+ if (TEMP_FAILURE_RETRY(read(sock_fd, &ack, 1)) == -1) {
+ ALOGE("Failed to read ack from debuggerd32 socket: %s", strerror(errno));
+ close(sock_fd);
+ return;
+ }
+
+ char buffer[1024];
+ ssize_t bytes_read;
+ while ((bytes_read = TEMP_FAILURE_RETRY(read(sock_fd, buffer, sizeof(buffer)))) > 0) {
+ ssize_t bytes_to_send = bytes_read;
+ ssize_t bytes_written;
+ do {
+ bytes_written = TEMP_FAILURE_RETRY(write(fd, buffer + bytes_read - bytes_to_send,
+ bytes_to_send));
+ if (bytes_written == -1) {
+ if (errno == EAGAIN) {
+ // Retry the write.
+ continue;
+ }
+ ALOGE("Error while writing data to fd: %s", strerror(errno));
+ break;
+ }
+ bytes_to_send -= bytes_written;
+ } while (bytes_written != 0 && bytes_to_send > 0);
+ if (bytes_to_send != 0) {
+ ALOGE("Failed to write all data to fd: read %zd, sent %zd", bytes_read, bytes_to_send);
+ break;
+ }
+ }
+ close(sock_fd);
+}
+#endif
+
+static void handle_request(int fd) {
+ ALOGV("handle_request(%d)\n", fd);
+
+ debugger_request_t request;
+ memset(&request, 0, sizeof(request));
+ int status = read_request(fd, &request);
+ if (!status) {
+ ALOGV("BOOM: pid=%d uid=%d gid=%d tid=%d\n",
+ request.pid, request.uid, request.gid, request.tid);
+
+#if defined(__LP64__)
+ // On 64 bit systems, requests to dump 32 bit and 64 bit tids come
+ // to the 64 bit debuggerd. If the process is a 32 bit executable,
+ // redirect the request to the 32 bit debuggerd.
+ if (is32bit(request.tid)) {
+ // Only dump backtrace and dump tombstone requests can be redirected.
+ if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE
+ || request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
+ redirect_to_32(fd, &request);
+ } else {
+ ALOGE("debuggerd: Not allowed to redirect action %d to 32 bit debuggerd\n",
+ request.action);
+ }
+ close(fd);
+ return;
+ }
+#endif
+
+ // At this point, the thread that made the request is blocked in
+ // a read() call. If the thread has crashed, then this gives us
+ // time to PTRACE_ATTACH to it before it has a chance to really fault.
+ //
+ // The PTRACE_ATTACH sends a SIGSTOP to the target process, but it
+ // won't necessarily have stopped by the time ptrace() returns. (We
+ // currently assume it does.) We write to the file descriptor to
+ // ensure that it can run as soon as we call PTRACE_CONT below.
+ // See details in bionic/libc/linker/debugger.c, in function
+ // debugger_signal_handler().
+ if (ptrace(PTRACE_ATTACH, request.tid, 0, 0)) {
+ ALOGE("ptrace attach failed: %s\n", strerror(errno));
+ } else {
+ bool detach_failed = false;
+ bool tid_unresponsive = false;
+ bool attach_gdb = should_attach_gdb(&request);
+ if (TEMP_FAILURE_RETRY(write(fd, "\0", 1)) != 1) {
+ ALOGE("failed responding to client: %s\n", strerror(errno));
+ } else {
+ char* tombstone_path = NULL;
+
+ if (request.action == DEBUGGER_ACTION_CRASH) {
+ close(fd);
+ fd = -1;
+ }
+
+ int total_sleep_time_usec = 0;
+ for (;;) {
+ int signal = wait_for_sigstop(request.tid, &total_sleep_time_usec, &detach_failed);
+ if (signal == -1) {
+ tid_unresponsive = true;
+ break;
+ }
+
+ switch (signal) {
+ case SIGSTOP:
+ if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
+ ALOGV("stopped -- dumping to tombstone\n");
+ tombstone_path = engrave_tombstone(request.pid, request.tid,
+ signal, request.original_si_code,
+ request.abort_msg_address, true,
+ &detach_failed, &total_sleep_time_usec);
+ } else if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE) {
+ ALOGV("stopped -- dumping to fd\n");
+ dump_backtrace(fd, -1, request.pid, request.tid, &detach_failed,
+ &total_sleep_time_usec);
+ } else {
+ ALOGV("stopped -- continuing\n");
+ status = ptrace(PTRACE_CONT, request.tid, 0, 0);
+ if (status) {
+ ALOGE("ptrace continue failed: %s\n", strerror(errno));
+ }
+ continue; // loop again
+ }
+ break;
+
+ case SIGABRT:
+ case SIGBUS:
+ case SIGFPE:
+ case SIGILL:
+ case SIGPIPE:
+ case SIGSEGV:
+#ifdef SIGSTKFLT
+ case SIGSTKFLT:
+#endif
+ case SIGTRAP:
+ ALOGV("stopped -- fatal signal\n");
+ // Send a SIGSTOP to the process to make all of
+ // the non-signaled threads stop moving. Without
+ // this we get a lot of "ptrace detach failed:
+ // No such process".
+ kill(request.pid, SIGSTOP);
+ // don't dump sibling threads when attaching to GDB because it
+ // makes the process less reliable, apparently...
+ tombstone_path = engrave_tombstone(request.pid, request.tid,
+ signal, request.original_si_code,
+ request.abort_msg_address, !attach_gdb,
+ &detach_failed, &total_sleep_time_usec);
+ break;
+
+ default:
+ ALOGE("process stopped due to unexpected signal %d\n", signal);
+ break;
+ }
+ break;
+ }
+
+ if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
+ if (tombstone_path) {
+ write(fd, tombstone_path, strlen(tombstone_path));
+ }
+ close(fd);
+ fd = -1;
+ }
+ free(tombstone_path);
+ }
+
+ if (!tid_unresponsive) {
+ ALOGV("detaching");
+ if (attach_gdb) {
+ // stop the process so we can debug
+ kill(request.pid, SIGSTOP);
+ }
+ if (ptrace(PTRACE_DETACH, request.tid, 0, 0)) {
+ ALOGE("ptrace detach from %d failed: %s", request.tid, strerror(errno));
+ detach_failed = true;
+ } else if (attach_gdb) {
+ // if debug.db.uid is set, its value indicates if we should wait
+ // for user action for the crashing process.
+ // in this case, we log a message and turn the debug LED on
+ // waiting for a gdb connection (for instance)
+ wait_for_user_action(request);
+ }
+ }
+
+ // resume stopped process (so it can crash in peace).
+ kill(request.pid, SIGCONT);
+
+ // If we didn't successfully detach, we're still the parent, and the
+ // actual parent won't receive a death notification via wait(2). At this point
+ // there's not much we can do about that.
+ if (detach_failed) {
+ ALOGE("debuggerd committing suicide to free the zombie!\n");
+ kill(getpid(), SIGKILL);
+ }
+ }
+
+ }
+ if (fd >= 0) {
+ close(fd);
+ }
+}
+
+static int do_server() {
+ // debuggerd crashes can't be reported to debuggerd.
+ // Reset all of the crash handlers.
+ signal(SIGABRT, SIG_DFL);
+ signal(SIGBUS, SIG_DFL);
+ signal(SIGFPE, SIG_DFL);
+ signal(SIGILL, SIG_DFL);
+ signal(SIGSEGV, SIG_DFL);
+#ifdef SIGSTKFLT
+ signal(SIGSTKFLT, SIG_DFL);
+#endif
+ signal(SIGTRAP, SIG_DFL);
+
+ // Ignore failed writes to closed sockets
+ signal(SIGPIPE, SIG_IGN);
+
+ int logsocket = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_DGRAM);
+ if (logsocket < 0) {
+ logsocket = -1;
+ } else {
+ fcntl(logsocket, F_SETFD, FD_CLOEXEC);
+ }
+
+ struct sigaction act;
+ act.sa_handler = SIG_DFL;
+ sigemptyset(&act.sa_mask);
+ sigaddset(&act.sa_mask,SIGCHLD);
+ act.sa_flags = SA_NOCLDWAIT;
+ sigaction(SIGCHLD, &act, 0);
+
+ int s = socket_local_server(SOCKET_NAME, ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
+ if (s < 0)
+ return 1;
+ fcntl(s, F_SETFD, FD_CLOEXEC);
+
+ ALOGI("debuggerd: " __DATE__ " " __TIME__ "\n");
+
+ for (;;) {
+ sockaddr addr;
+ socklen_t alen = sizeof(addr);
+
+ ALOGV("waiting for connection\n");
+ int fd = accept(s, &addr, &alen);
+ if (fd < 0) {
+ ALOGV("accept failed: %s\n", strerror(errno));
+ continue;
+ }
+
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+
+ handle_request(fd);
+ }
+ return 0;
+}
+
+static int do_explicit_dump(pid_t tid, bool dump_backtrace) {
+ fprintf(stdout, "Sending request to dump task %d.\n", tid);
+
+ if (dump_backtrace) {
+ fflush(stdout);
+ if (dump_backtrace_to_file(tid, fileno(stdout)) < 0) {
+ fputs("Error dumping backtrace.\n", stderr);
+ return 1;
+ }
+ } else {
+ char tombstone_path[PATH_MAX];
+ if (dump_tombstone(tid, tombstone_path, sizeof(tombstone_path)) < 0) {
+ fputs("Error dumping tombstone.\n", stderr);
+ return 1;
+ }
+ fprintf(stderr, "Tombstone written to: %s\n", tombstone_path);
+ }
+ return 0;
+}
+
+static void usage() {
+ fputs("Usage: -b [<tid>]\n"
+ " -b dump backtrace to console, otherwise dump full tombstone file\n"
+ "\n"
+ "If tid specified, sends a request to debuggerd to dump that task.\n"
+ "Otherwise, starts the debuggerd server.\n", stderr);
+}
+
+int main(int argc, char** argv) {
+ union selinux_callback cb;
+ if (argc == 1) {
+ cb.func_log = selinux_log_callback;
+ selinux_set_callback(SELINUX_CB_LOG, cb);
+ return do_server();
+ }
+
+ bool dump_backtrace = false;
+ bool have_tid = false;
+ pid_t tid = 0;
+ for (int i = 1; i < argc; i++) {
+ if (!strcmp(argv[i], "-b")) {
+ dump_backtrace = true;
+ } else if (!have_tid) {
+ tid = atoi(argv[i]);
+ have_tid = true;
+ } else {
+ usage();
+ return 1;
+ }
+ }
+ if (!have_tid) {
+ usage();
+ return 1;
+ }
+ return do_explicit_dump(tid, dump_backtrace);
+}
diff --git a/debuggerd/elf_utils.cpp b/debuggerd/elf_utils.cpp
new file mode 100644
index 0000000..5ea03e7
--- /dev/null
+++ b/debuggerd/elf_utils.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "DEBUG"
+
+#include <elf.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <string>
+
+#include <backtrace/Backtrace.h>
+#include <base/stringprintf.h>
+#include <log/log.h>
+
+#include "elf_utils.h"
+
+#define NOTE_ALIGN(size) ((size + 3) & ~3)
+
+template <typename HdrType, typename PhdrType, typename NhdrType>
+static bool get_build_id(
+ Backtrace* backtrace, uintptr_t base_addr, uint8_t* e_ident, std::string* build_id) {
+ HdrType hdr;
+
+ memcpy(&hdr.e_ident[0], e_ident, EI_NIDENT);
+
+ // First read the rest of the header.
+ if (backtrace->Read(base_addr + EI_NIDENT, reinterpret_cast<uint8_t*>(&hdr) + EI_NIDENT,
+ sizeof(HdrType) - EI_NIDENT) != sizeof(HdrType) - EI_NIDENT) {
+ return false;
+ }
+
+ for (size_t i = 0; i < hdr.e_phnum; i++) {
+ PhdrType phdr;
+ if (backtrace->Read(base_addr + hdr.e_phoff + i * hdr.e_phentsize,
+ reinterpret_cast<uint8_t*>(&phdr), sizeof(phdr)) != sizeof(phdr)) {
+ return false;
+ }
+ // Looking for the .note.gnu.build-id note.
+ if (phdr.p_type == PT_NOTE) {
+ size_t hdr_size = phdr.p_filesz;
+ uintptr_t addr = base_addr + phdr.p_offset;
+ while (hdr_size >= sizeof(NhdrType)) {
+ NhdrType nhdr;
+ if (backtrace->Read(addr, reinterpret_cast<uint8_t*>(&nhdr), sizeof(nhdr)) != sizeof(nhdr)) {
+ return false;
+ }
+ addr += sizeof(nhdr);
+ if (nhdr.n_type == NT_GNU_BUILD_ID) {
+ // Skip the name (which is the owner and should be "GNU").
+ addr += NOTE_ALIGN(nhdr.n_namesz);
+ uint8_t build_id_data[128];
+ if (nhdr.n_namesz > sizeof(build_id_data)) {
+ ALOGE("Possible corrupted note, name size value is too large: %u",
+ nhdr.n_namesz);
+ return false;
+ }
+ if (backtrace->Read(addr, build_id_data, nhdr.n_descsz) != nhdr.n_descsz) {
+ return false;
+ }
+
+ build_id->clear();
+ for (size_t bytes = 0; bytes < nhdr.n_descsz; bytes++) {
+ *build_id += android::base::StringPrintf("%02x", build_id_data[bytes]);
+ }
+
+ return true;
+ } else {
+ // Move past the extra note data.
+ hdr_size -= sizeof(nhdr);
+ size_t skip_bytes = NOTE_ALIGN(nhdr.n_namesz) + NOTE_ALIGN(nhdr.n_descsz);
+ addr += skip_bytes;
+ if (hdr_size < skip_bytes) {
+ break;
+ }
+ hdr_size -= skip_bytes;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool elf_get_build_id(Backtrace* backtrace, uintptr_t addr, std::string* build_id) {
+ // Read and verify the elf magic number first.
+ uint8_t e_ident[EI_NIDENT];
+ if (backtrace->Read(addr, e_ident, SELFMAG) != SELFMAG) {
+ return false;
+ }
+
+ if (memcmp(e_ident, ELFMAG, SELFMAG) != 0) {
+ return false;
+ }
+
+ // Read the rest of EI_NIDENT.
+ if (backtrace->Read(addr + SELFMAG, e_ident + SELFMAG, EI_NIDENT - SELFMAG) != EI_NIDENT - SELFMAG) {
+ return false;
+ }
+
+ if (e_ident[EI_CLASS] == ELFCLASS32) {
+ return get_build_id<Elf32_Ehdr, Elf32_Phdr, Elf32_Nhdr>(backtrace, addr, e_ident, build_id);
+ } else if (e_ident[EI_CLASS] == ELFCLASS64) {
+ return get_build_id<Elf64_Ehdr, Elf64_Phdr, Elf64_Nhdr>(backtrace, addr, e_ident, build_id);
+ }
+
+ return false;
+}
diff --git a/debuggerd/elf_utils.h b/debuggerd/elf_utils.h
new file mode 100644
index 0000000..11d0a43
--- /dev/null
+++ b/debuggerd/elf_utils.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DEBUGGERD_ELF_UTILS_H
+#define _DEBUGGERD_ELF_UTILS_H
+
+#include <stdint.h>
+#include <string>
+
+class Backtrace;
+
+bool elf_get_build_id(Backtrace*, uintptr_t, std::string*);
+
+#endif // _DEBUGGERD_ELF_UTILS_H
diff --git a/debuggerd/getevent.cpp b/debuggerd/getevent.cpp
new file mode 100644
index 0000000..751c4fb
--- /dev/null
+++ b/debuggerd/getevent.cpp
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2014 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 <stdint.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/inotify.h>
+#include <sys/limits.h>
+#include <sys/poll.h>
+#include <linux/input.h>
+#include <errno.h>
+#include <cutils/log.h>
+
+static struct pollfd* ufds;
+static char** device_names;
+static int nfds;
+
+static int open_device(const char* device) {
+ int version;
+ int fd;
+ struct pollfd* new_ufds;
+ char** new_device_names;
+ char name[80];
+ char location[80];
+ char idstr[80];
+ struct input_id id;
+
+ fd = open(device, O_RDWR);
+ if (fd < 0) {
+ return -1;
+ }
+
+ if (ioctl(fd, EVIOCGVERSION, &version)) {
+ return -1;
+ }
+ if (ioctl(fd, EVIOCGID, &id)) {
+ return -1;
+ }
+ name[sizeof(name) - 1] = '\0';
+ location[sizeof(location) - 1] = '\0';
+ idstr[sizeof(idstr) - 1] = '\0';
+ if (ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) {
+ name[0] = '\0';
+ }
+ if (ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) {
+ location[0] = '\0';
+ }
+ if (ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1) {
+ idstr[0] = '\0';
+ }
+
+ new_ufds = reinterpret_cast<pollfd*>(realloc(ufds, sizeof(ufds[0]) * (nfds + 1)));
+ if (new_ufds == NULL) {
+ fprintf(stderr, "out of memory\n");
+ return -1;
+ }
+ ufds = new_ufds;
+ new_device_names = reinterpret_cast<char**>(realloc(
+ device_names, sizeof(device_names[0]) * (nfds + 1)));
+ if (new_device_names == NULL) {
+ fprintf(stderr, "out of memory\n");
+ return -1;
+ }
+ device_names = new_device_names;
+ ufds[nfds].fd = fd;
+ ufds[nfds].events = POLLIN;
+ device_names[nfds] = strdup(device);
+ nfds++;
+
+ return 0;
+}
+
+int close_device(const char* device) {
+ int i;
+ for (i = 1; i < nfds; i++) {
+ if (strcmp(device_names[i], device) == 0) {
+ int count = nfds - i - 1;
+ free(device_names[i]);
+ memmove(device_names + i, device_names + i + 1, sizeof(device_names[0]) * count);
+ memmove(ufds + i, ufds + i + 1, sizeof(ufds[0]) * count);
+ nfds--;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+static int read_notify(const char* dirname, int nfd) {
+ int res;
+ char devname[PATH_MAX];
+ char* filename;
+ char event_buf[512];
+ int event_size;
+ int event_pos = 0;
+ struct inotify_event *event;
+
+ res = read(nfd, event_buf, sizeof(event_buf));
+ if (res < (int)sizeof(*event)) {
+ if (errno == EINTR)
+ return 0;
+ fprintf(stderr, "could not get event, %s\n", strerror(errno));
+ return 1;
+ }
+
+ strcpy(devname, dirname);
+ filename = devname + strlen(devname);
+ *filename++ = '/';
+
+ while (res >= (int)sizeof(*event)) {
+ event = reinterpret_cast<struct inotify_event*>(event_buf + event_pos);
+ if (event->len) {
+ strcpy(filename, event->name);
+ if (event->mask & IN_CREATE) {
+ open_device(devname);
+ } else {
+ close_device(devname);
+ }
+ }
+ event_size = sizeof(*event) + event->len;
+ res -= event_size;
+ event_pos += event_size;
+ }
+ return 0;
+}
+
+static int scan_dir(const char* dirname) {
+ char devname[PATH_MAX];
+ char* filename;
+ DIR* dir;
+ struct dirent* de;
+ dir = opendir(dirname);
+ if (dir == NULL)
+ return -1;
+ strcpy(devname, dirname);
+ filename = devname + strlen(devname);
+ *filename++ = '/';
+ while ((de = readdir(dir))) {
+ if ((de->d_name[0] == '.' && de->d_name[1] == '\0') ||
+ (de->d_name[1] == '.' && de->d_name[2] == '\0'))
+ continue;
+ strcpy(filename, de->d_name);
+ open_device(devname);
+ }
+ closedir(dir);
+ return 0;
+}
+
+int init_getevent() {
+ int res;
+ const char* device_path = "/dev/input";
+
+ nfds = 1;
+ ufds = reinterpret_cast<pollfd*>(calloc(1, sizeof(ufds[0])));
+ ufds[0].fd = inotify_init();
+ ufds[0].events = POLLIN;
+
+ res = inotify_add_watch(ufds[0].fd, device_path, IN_DELETE | IN_CREATE);
+ if (res < 0) {
+ return 1;
+ }
+ res = scan_dir(device_path);
+ if (res < 0) {
+ return 1;
+ }
+ return 0;
+}
+
+void uninit_getevent() {
+ int i;
+ for (i = 0; i < nfds; i++) {
+ close(ufds[i].fd);
+ }
+ free(ufds);
+ ufds = 0;
+ nfds = 0;
+}
+
+int get_event(struct input_event* event, int timeout) {
+ int res;
+ int i;
+ int pollres;
+ const char* device_path = "/dev/input";
+ while (1) {
+ pollres = poll(ufds, nfds, timeout);
+ if (pollres == 0) {
+ return 1;
+ }
+ if (ufds[0].revents & POLLIN) {
+ read_notify(device_path, ufds[0].fd);
+ }
+ for (i = 1; i < nfds; i++) {
+ if (ufds[i].revents) {
+ if (ufds[i].revents & POLLIN) {
+ res = read(ufds[i].fd, event, sizeof(*event));
+ if (res < static_cast<int>(sizeof(event))) {
+ fprintf(stderr, "could not get event\n");
+ return -1;
+ }
+ return 0;
+ }
+ }
+ }
+ }
+ return 0;
+}
diff --git a/debuggerd/getevent.h b/debuggerd/getevent.h
new file mode 100644
index 0000000..426139d
--- /dev/null
+++ b/debuggerd/getevent.h
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+#ifndef _DEBUGGERD_GETEVENT_H
+#define _DEBUGGERD_GETEVENT_H
+
+int init_getevent();
+void uninit_getevent();
+int get_event(struct input_event* event, int timeout);
+
+#endif // _DEBUGGERD_GETEVENT_H
diff --git a/debuggerd/machine.h b/debuggerd/machine.h
new file mode 100644
index 0000000..e65b147
--- /dev/null
+++ b/debuggerd/machine.h
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+#ifndef _DEBUGGERD_MACHINE_H
+#define _DEBUGGERD_MACHINE_H
+
+#include <sys/types.h>
+
+#include <backtrace/Backtrace.h>
+
+#include "utility.h"
+
+void dump_memory_and_code(log_t* log, Backtrace* backtrace);
+void dump_registers(log_t* log, pid_t tid);
+
+#endif // _DEBUGGERD_MACHINE_H
diff --git a/debuggerd/mips/crashglue.S b/debuggerd/mips/crashglue.S
new file mode 100644
index 0000000..70a6641
--- /dev/null
+++ b/debuggerd/mips/crashglue.S
@@ -0,0 +1,48 @@
+ .set noat
+
+ .globl crash1
+ .globl crashnostack
+
+crash1:
+ li $0,0xdead0000+0
+ li $1,0xdead0000+1
+ li $2,0xdead0000+2
+ li $3,0xdead0000+3
+ li $4,0xdead0000+4
+ li $5,0xdead0000+5
+ li $6,0xdead0000+6
+ li $7,0xdead0000+7
+ li $8,0xdead0000+8
+ li $9,0xdead0000+9
+ li $10,0xdead0000+10
+ li $11,0xdead0000+11
+ li $12,0xdead0000+12
+ li $13,0xdead0000+13
+ li $14,0xdead0000+14
+ li $15,0xdead0000+15
+ li $16,0xdead0000+16
+ li $17,0xdead0000+17
+ li $18,0xdead0000+18
+ li $19,0xdead0000+19
+ li $20,0xdead0000+20
+ li $21,0xdead0000+21
+ li $22,0xdead0000+22
+ li $23,0xdead0000+23
+ li $24,0xdead0000+24
+ li $25,0xdead0000+25
+ li $26,0xdead0000+26
+ li $27,0xdead0000+27
+ li $28,0xdead0000+28
+ # don't trash the stack otherwise the signal handler won't run
+ #li $29,0xdead0000+29
+ li $30,0xdead0000+30
+ li $31,0xdead0000+31
+
+ lw $zero,($0)
+ b .
+
+
+crashnostack:
+ li $sp, 0
+ lw $zero,($0)
+ b .
diff --git a/debuggerd/mips/machine.cpp b/debuggerd/mips/machine.cpp
new file mode 100644
index 0000000..cbf272a
--- /dev/null
+++ b/debuggerd/mips/machine.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright 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 LOG_TAG "DEBUG"
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/ptrace.h>
+
+#include <backtrace/Backtrace.h>
+#include <log/log.h>
+
+#include "machine.h"
+#include "utility.h"
+
+#define R(x) (static_cast<uintptr_t>(x))
+
+// If configured to do so, dump memory around *all* registers
+// for the crashing thread.
+void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
+ pt_regs r;
+ if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r)) {
+ ALOGE("cannot get registers: %s\n", strerror(errno));
+ return;
+ }
+
+ static const char reg_names[] = "$0atv0v1a0a1a2a3t0t1t2t3t4t5t6t7s0s1s2s3s4s5s6s7t8t9k0k1gpsps8ra";
+
+ for (int reg = 0; reg < 32; reg++) {
+ // skip uninteresting registers
+ if (reg == 0 // $0
+ || reg == 26 // $k0
+ || reg == 27 // $k1
+ || reg == 31 // $ra (done below)
+ )
+ continue;
+
+ dump_memory(log, backtrace, R(r.regs[reg]), "memory near %.2s:", ®_names[reg * 2]);
+ }
+
+ uintptr_t pc = R(r.cp0_epc);
+ uintptr_t ra = R(r.regs[31]);
+ dump_memory(log, backtrace, pc, "code around pc:");
+ if (pc != ra) {
+ dump_memory(log, backtrace, ra, "code around ra:");
+ }
+}
+
+void dump_registers(log_t* log, pid_t tid) {
+ pt_regs r;
+ if(ptrace(PTRACE_GETREGS, tid, 0, &r)) {
+ ALOGE("cannot get registers: %s\n", strerror(errno));
+ return;
+ }
+
+ _LOG(log, logtype::REGISTERS, " zr %08" PRIxPTR " at %08" PRIxPTR
+ " v0 %08" PRIxPTR " v1 %08" PRIxPTR "\n",
+ R(r.regs[0]), R(r.regs[1]), R(r.regs[2]), R(r.regs[3]));
+ _LOG(log, logtype::REGISTERS, " a0 %08" PRIxPTR " a1 %08" PRIxPTR
+ " a2 %08" PRIxPTR " a3 %08" PRIxPTR "\n",
+ R(r.regs[4]), R(r.regs[5]), R(r.regs[6]), R(r.regs[7]));
+ _LOG(log, logtype::REGISTERS, " t0 %08" PRIxPTR " t1 %08" PRIxPTR
+ " t2 %08" PRIxPTR " t3 %08" PRIxPTR "\n",
+ R(r.regs[8]), R(r.regs[9]), R(r.regs[10]), R(r.regs[11]));
+ _LOG(log, logtype::REGISTERS, " t4 %08" PRIxPTR " t5 %08" PRIxPTR
+ " t6 %08" PRIxPTR " t7 %08" PRIxPTR "\n",
+ R(r.regs[12]), R(r.regs[13]), R(r.regs[14]), R(r.regs[15]));
+ _LOG(log, logtype::REGISTERS, " s0 %08" PRIxPTR " s1 %08" PRIxPTR
+ " s2 %08" PRIxPTR " s3 %08" PRIxPTR "\n",
+ R(r.regs[16]), R(r.regs[17]), R(r.regs[18]), R(r.regs[19]));
+ _LOG(log, logtype::REGISTERS, " s4 %08" PRIxPTR " s5 %08" PRIxPTR
+ " s6 %08" PRIxPTR " s7 %08" PRIxPTR "\n",
+ R(r.regs[20]), R(r.regs[21]), R(r.regs[22]), R(r.regs[23]));
+ _LOG(log, logtype::REGISTERS, " t8 %08" PRIxPTR " t9 %08" PRIxPTR
+ " k0 %08" PRIxPTR " k1 %08" PRIxPTR "\n",
+ R(r.regs[24]), R(r.regs[25]), R(r.regs[26]), R(r.regs[27]));
+ _LOG(log, logtype::REGISTERS, " gp %08" PRIxPTR " sp %08" PRIxPTR
+ " s8 %08" PRIxPTR " ra %08" PRIxPTR "\n",
+ R(r.regs[28]), R(r.regs[29]), R(r.regs[30]), R(r.regs[31]));
+ _LOG(log, logtype::REGISTERS, " hi %08" PRIxPTR " lo %08" PRIxPTR
+ " bva %08" PRIxPTR " epc %08" PRIxPTR "\n",
+ R(r.hi), R(r.lo), R(r.cp0_badvaddr), R(r.cp0_epc));
+}
diff --git a/debuggerd/mips64/crashglue.S b/debuggerd/mips64/crashglue.S
new file mode 100644
index 0000000..70a6641
--- /dev/null
+++ b/debuggerd/mips64/crashglue.S
@@ -0,0 +1,48 @@
+ .set noat
+
+ .globl crash1
+ .globl crashnostack
+
+crash1:
+ li $0,0xdead0000+0
+ li $1,0xdead0000+1
+ li $2,0xdead0000+2
+ li $3,0xdead0000+3
+ li $4,0xdead0000+4
+ li $5,0xdead0000+5
+ li $6,0xdead0000+6
+ li $7,0xdead0000+7
+ li $8,0xdead0000+8
+ li $9,0xdead0000+9
+ li $10,0xdead0000+10
+ li $11,0xdead0000+11
+ li $12,0xdead0000+12
+ li $13,0xdead0000+13
+ li $14,0xdead0000+14
+ li $15,0xdead0000+15
+ li $16,0xdead0000+16
+ li $17,0xdead0000+17
+ li $18,0xdead0000+18
+ li $19,0xdead0000+19
+ li $20,0xdead0000+20
+ li $21,0xdead0000+21
+ li $22,0xdead0000+22
+ li $23,0xdead0000+23
+ li $24,0xdead0000+24
+ li $25,0xdead0000+25
+ li $26,0xdead0000+26
+ li $27,0xdead0000+27
+ li $28,0xdead0000+28
+ # don't trash the stack otherwise the signal handler won't run
+ #li $29,0xdead0000+29
+ li $30,0xdead0000+30
+ li $31,0xdead0000+31
+
+ lw $zero,($0)
+ b .
+
+
+crashnostack:
+ li $sp, 0
+ lw $zero,($0)
+ b .
diff --git a/debuggerd/mips64/machine.cpp b/debuggerd/mips64/machine.cpp
new file mode 100644
index 0000000..0a8d532
--- /dev/null
+++ b/debuggerd/mips64/machine.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2014, 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 "DEBUG"
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/ptrace.h>
+
+#include <backtrace/Backtrace.h>
+#include <log/log.h>
+
+#include "machine.h"
+#include "utility.h"
+
+#define R(x) (static_cast<uintptr_t>(x))
+
+// If configured to do so, dump memory around *all* registers
+// for the crashing thread.
+void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
+ pt_regs r;
+ if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r)) {
+ ALOGE("cannot get registers: %s\n", strerror(errno));
+ return;
+ }
+
+ static const char reg_names[] = "$0atv0v1a0a1a2a3a4a5a6a7t0t1t2t3s0s1s2s3s4s5s6s7t8t9k0k1gpsps8ra";
+
+ for (int reg = 0; reg < 32; reg++) {
+ // skip uninteresting registers
+ if (reg == 0 // $0
+ || reg == 26 // $k0
+ || reg == 27 // $k1
+ || reg == 31 // $ra (done below)
+ )
+ continue;
+
+ dump_memory(log, backtrace, R(r.regs[reg]), "memory near %.2s:", ®_names[reg * 2]);
+ }
+
+ uintptr_t pc = R(r.cp0_epc);
+ uintptr_t ra = R(r.regs[31]);
+ dump_memory(log, backtrace, pc, "code around pc:");
+ if (pc != ra) {
+ dump_memory(log, backtrace, ra, "code around ra:");
+ }
+}
+
+void dump_registers(log_t* log, pid_t tid) {
+ pt_regs r;
+ if(ptrace(PTRACE_GETREGS, tid, 0, &r)) {
+ ALOGE("cannot get registers: %s\n", strerror(errno));
+ return;
+ }
+
+ _LOG(log, logtype::REGISTERS, " zr %016" PRIxPTR " at %016" PRIxPTR
+ " v0 %016" PRIxPTR " v1 %016" PRIxPTR "\n",
+ R(r.regs[0]), R(r.regs[1]), R(r.regs[2]), R(r.regs[3]));
+ _LOG(log, logtype::REGISTERS, " a0 %016" PRIxPTR " a1 %016" PRIxPTR
+ " a2 %016" PRIxPTR " a3 %016" PRIxPTR "\n",
+ R(r.regs[4]), R(r.regs[5]), R(r.regs[6]), R(r.regs[7]));
+ _LOG(log, logtype::REGISTERS, " a4 %016" PRIxPTR " a5 %016" PRIxPTR
+ " a6 %016" PRIxPTR " a7 %016" PRIxPTR "\n",
+ R(r.regs[8]), R(r.regs[9]), R(r.regs[10]), R(r.regs[11]));
+ _LOG(log, logtype::REGISTERS, " t0 %016" PRIxPTR " t1 %016" PRIxPTR
+ " t2 %016" PRIxPTR " t3 %016" PRIxPTR "\n",
+ R(r.regs[12]), R(r.regs[13]), R(r.regs[14]), R(r.regs[15]));
+ _LOG(log, logtype::REGISTERS, " s0 %016" PRIxPTR " s1 %016" PRIxPTR
+ " s2 %016" PRIxPTR " s3 %016" PRIxPTR "\n",
+ R(r.regs[16]), R(r.regs[17]), R(r.regs[18]), R(r.regs[19]));
+ _LOG(log, logtype::REGISTERS, " s4 %016" PRIxPTR " s5 %016" PRIxPTR
+ " s6 %016" PRIxPTR " s7 %016" PRIxPTR "\n",
+ R(r.regs[20]), R(r.regs[21]), R(r.regs[22]), R(r.regs[23]));
+ _LOG(log, logtype::REGISTERS, " t8 %016" PRIxPTR " t9 %016" PRIxPTR
+ " k0 %016" PRIxPTR " k1 %016" PRIxPTR "\n",
+ R(r.regs[24]), R(r.regs[25]), R(r.regs[26]), R(r.regs[27]));
+ _LOG(log, logtype::REGISTERS, " gp %016" PRIxPTR " sp %016" PRIxPTR
+ " s8 %016" PRIxPTR " ra %016" PRIxPTR "\n",
+ R(r.regs[28]), R(r.regs[29]), R(r.regs[30]), R(r.regs[31]));
+ _LOG(log, logtype::REGISTERS, " hi %016" PRIxPTR " lo %016" PRIxPTR
+ " bva %016" PRIxPTR " epc %016" PRIxPTR "\n",
+ R(r.hi), R(r.lo), R(r.cp0_badvaddr), R(r.cp0_epc));
+}
diff --git a/debuggerd/test/BacktraceMock.h b/debuggerd/test/BacktraceMock.h
new file mode 100644
index 0000000..f75534e
--- /dev/null
+++ b/debuggerd/test/BacktraceMock.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DEBUGGERD_TEST_BACKTRACE_MOCK_H
+#define _DEBUGGERD_TEST_BACKTRACE_MOCK_H
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ucontext.h>
+
+#include <string>
+#include <vector>
+
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+
+class BacktraceMapMock : public BacktraceMap {
+ public:
+ BacktraceMapMock() : BacktraceMap(0) {}
+ virtual ~BacktraceMapMock() {}
+
+ void AddMap(backtrace_map_t& map) {
+ maps_.push_back(map);
+ }
+};
+
+
+class BacktraceMock : public Backtrace {
+ public:
+ BacktraceMock(BacktraceMapMock* map) : Backtrace(0, 0, map) {
+ if (map_ == nullptr) {
+ abort();
+ }
+ }
+ virtual ~BacktraceMock() {}
+
+ virtual bool Unwind(size_t, ucontext_t*) { return false; }
+ virtual bool ReadWord(uintptr_t, word_t*) { return false;}
+
+ virtual std::string GetFunctionNameRaw(uintptr_t, uintptr_t*) { return ""; }
+
+ virtual size_t Read(uintptr_t addr, uint8_t* buffer, size_t bytes) {
+ size_t offset = 0;
+ if (last_read_addr_ > 0) {
+ offset = addr - last_read_addr_;
+ }
+ size_t bytes_available = buffer_.size() - offset;
+
+ if (do_partial_read_) {
+ // Do a partial read.
+ if (bytes > bytes_partial_read_) {
+ bytes = bytes_partial_read_;
+ }
+ bytes_partial_read_ -= bytes;
+ // Only support a single partial read.
+ do_partial_read_ = false;
+ } else if (bytes > bytes_available) {
+ bytes = bytes_available;
+ }
+
+ if (bytes > 0) {
+ memcpy(buffer, buffer_.data() + offset, bytes);
+ }
+
+ last_read_addr_ = addr;
+ return bytes;
+ }
+
+ void SetReadData(uint8_t* buffer, size_t bytes) {
+ buffer_.resize(bytes);
+ memcpy(buffer_.data(), buffer, bytes);
+ bytes_partial_read_ = 0;
+ do_partial_read_ = false;
+ last_read_addr_ = 0;
+ }
+
+ void SetPartialReadAmount(size_t bytes) {
+ if (bytes > buffer_.size()) {
+ abort();
+ }
+ bytes_partial_read_ = bytes;
+ do_partial_read_ = true;
+ }
+
+ private:
+ std::vector<uint8_t> buffer_;
+ size_t bytes_partial_read_ = 0;
+ uintptr_t last_read_addr_ = 0;
+ bool do_partial_read_ = false;
+};
+
+#endif // _DEBUGGERD_TEST_BACKTRACE_MOCK_H
diff --git a/debuggerd/test/dump_memory_test.cpp b/debuggerd/test/dump_memory_test.cpp
new file mode 100644
index 0000000..75e7028
--- /dev/null
+++ b/debuggerd/test/dump_memory_test.cpp
@@ -0,0 +1,740 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+
+#include <memory>
+#include <string>
+
+#include <gtest/gtest.h>
+#include <base/file.h>
+
+#include "BacktraceMock.h"
+#include "log_fake.h"
+#include "utility.h"
+
+const char g_expected_full_dump[] =
+"\nmemory near r1:\n"
+#if defined(__LP64__)
+" 0000000012345658 0706050403020100 0f0e0d0c0b0a0908 ................\n"
+" 0000000012345668 1716151413121110 1f1e1d1c1b1a1918 ................\n"
+" 0000000012345678 2726252423222120 2f2e2d2c2b2a2928 !\"#$%&'()*+,-./\n"
+" 0000000012345688 3736353433323130 3f3e3d3c3b3a3938 0123456789:;<=>?\n"
+" 0000000012345698 4746454443424140 4f4e4d4c4b4a4948 @ABCDEFGHIJKLMNO\n"
+" 00000000123456a8 5756555453525150 5f5e5d5c5b5a5958 PQRSTUVWXYZ[\\]^_\n"
+" 00000000123456b8 6766656463626160 6f6e6d6c6b6a6968 `abcdefghijklmno\n"
+" 00000000123456c8 7776757473727170 7f7e7d7c7b7a7978 pqrstuvwxyz{|}~.\n"
+" 00000000123456d8 8786858483828180 8f8e8d8c8b8a8988 ................\n"
+" 00000000123456e8 9796959493929190 9f9e9d9c9b9a9998 ................\n"
+" 00000000123456f8 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8 ................\n"
+" 0000000012345708 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8 ................\n"
+" 0000000012345718 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................\n"
+" 0000000012345728 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n"
+" 0000000012345738 e7e6e5e4e3e2e1e0 efeeedecebeae9e8 ................\n"
+" 0000000012345748 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8 ................\n";
+#else
+" 12345658 03020100 07060504 0b0a0908 0f0e0d0c ................\n"
+" 12345668 13121110 17161514 1b1a1918 1f1e1d1c ................\n"
+" 12345678 23222120 27262524 2b2a2928 2f2e2d2c !\"#$%&'()*+,-./\n"
+" 12345688 33323130 37363534 3b3a3938 3f3e3d3c 0123456789:;<=>?\n"
+" 12345698 43424140 47464544 4b4a4948 4f4e4d4c @ABCDEFGHIJKLMNO\n"
+" 123456a8 53525150 57565554 5b5a5958 5f5e5d5c PQRSTUVWXYZ[\\]^_\n"
+" 123456b8 63626160 67666564 6b6a6968 6f6e6d6c `abcdefghijklmno\n"
+" 123456c8 73727170 77767574 7b7a7978 7f7e7d7c pqrstuvwxyz{|}~.\n"
+" 123456d8 83828180 87868584 8b8a8988 8f8e8d8c ................\n"
+" 123456e8 93929190 97969594 9b9a9998 9f9e9d9c ................\n"
+" 123456f8 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac ................\n"
+" 12345708 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc ................\n"
+" 12345718 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc ................\n"
+" 12345728 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................\n"
+" 12345738 e3e2e1e0 e7e6e5e4 ebeae9e8 efeeedec ................\n"
+" 12345748 f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc ................\n";
+#endif
+
+const char g_expected_partial_dump[] = \
+"\nmemory near pc:\n"
+#if defined(__LP64__)
+" 00000000123455e0 0706050403020100 0f0e0d0c0b0a0908 ................\n"
+" 00000000123455f0 1716151413121110 1f1e1d1c1b1a1918 ................\n"
+" 0000000012345600 2726252423222120 2f2e2d2c2b2a2928 !\"#$%&'()*+,-./\n"
+" 0000000012345610 3736353433323130 3f3e3d3c3b3a3938 0123456789:;<=>?\n"
+" 0000000012345620 4746454443424140 4f4e4d4c4b4a4948 @ABCDEFGHIJKLMNO\n"
+" 0000000012345630 5756555453525150 5f5e5d5c5b5a5958 PQRSTUVWXYZ[\\]^_\n"
+" 0000000012345640 6766656463626160 ---------------- `abcdefg........\n"
+" 0000000012345650 ---------------- ---------------- ................\n"
+" 0000000012345660 ---------------- ---------------- ................\n"
+" 0000000012345670 ---------------- ---------------- ................\n"
+" 0000000012345680 ---------------- ---------------- ................\n"
+" 0000000012345690 ---------------- ---------------- ................\n"
+" 00000000123456a0 ---------------- ---------------- ................\n"
+" 00000000123456b0 ---------------- ---------------- ................\n"
+" 00000000123456c0 ---------------- ---------------- ................\n"
+" 00000000123456d0 ---------------- ---------------- ................\n";
+#else
+" 123455e0 03020100 07060504 0b0a0908 0f0e0d0c ................\n"
+" 123455f0 13121110 17161514 1b1a1918 1f1e1d1c ................\n"
+" 12345600 23222120 27262524 2b2a2928 2f2e2d2c !\"#$%&'()*+,-./\n"
+" 12345610 33323130 37363534 3b3a3938 3f3e3d3c 0123456789:;<=>?\n"
+" 12345620 43424140 47464544 4b4a4948 4f4e4d4c @ABCDEFGHIJKLMNO\n"
+" 12345630 53525150 57565554 5b5a5958 5f5e5d5c PQRSTUVWXYZ[\\]^_\n"
+" 12345640 63626160 67666564 -------- -------- `abcdefg........\n"
+" 12345650 -------- -------- -------- -------- ................\n"
+" 12345660 -------- -------- -------- -------- ................\n"
+" 12345670 -------- -------- -------- -------- ................\n"
+" 12345680 -------- -------- -------- -------- ................\n"
+" 12345690 -------- -------- -------- -------- ................\n"
+" 123456a0 -------- -------- -------- -------- ................\n"
+" 123456b0 -------- -------- -------- -------- ................\n"
+" 123456c0 -------- -------- -------- -------- ................\n"
+" 123456d0 -------- -------- -------- -------- ................\n";
+#endif
+
+class DumpMemoryTest : public ::testing::Test {
+ protected:
+ virtual void SetUp() {
+ map_mock_.reset(new BacktraceMapMock());
+ backtrace_mock_.reset(new BacktraceMock(map_mock_.get()));
+
+ char tmp_file[256];
+ const char data_template[] = "/data/local/tmp/debuggerd_memory_testXXXXXX";
+ memcpy(tmp_file, data_template, sizeof(data_template));
+ int tombstone_fd = mkstemp(tmp_file);
+ if (tombstone_fd == -1) {
+ const char tmp_template[] = "/tmp/debuggerd_memory_testXXXXXX";
+ memcpy(tmp_file, tmp_template, sizeof(tmp_template));
+ tombstone_fd = mkstemp(tmp_file);
+ if (tombstone_fd == -1) {
+ abort();
+ }
+ }
+ if (unlink(tmp_file) == -1) {
+ abort();
+ }
+
+ log_.tfd = tombstone_fd;
+ log_.amfd = -1;
+ log_.crashed_tid = 12;
+ log_.current_tid = 12;
+ log_.should_retrieve_logcat = false;
+
+ resetLogs();
+ }
+
+ virtual void TearDown() {
+ if (log_.tfd >= 0) {
+ close(log_.tfd);
+ }
+ }
+
+ std::unique_ptr<BacktraceMapMock> map_mock_;
+ std::unique_ptr<BacktraceMock> backtrace_mock_;
+
+ log_t log_;
+};
+
+TEST_F(DumpMemoryTest, aligned_addr) {
+ uint8_t buffer[256];
+ for (size_t i = 0; i < sizeof(buffer); i++) {
+ buffer[i] = i;
+ }
+ backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+
+ dump_memory(&log_, backtrace_mock_.get(), 0x12345678, "memory near %.2s:", "r1");
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ ASSERT_STREQ(g_expected_full_dump, tombstone_contents.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, partial_read) {
+ uint8_t buffer[256];
+ for (size_t i = 0; i < sizeof(buffer); i++) {
+ buffer[i] = i;
+ }
+ backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+ backtrace_mock_->SetPartialReadAmount(96);
+
+ dump_memory(&log_, backtrace_mock_.get(), 0x12345679, "memory near %.2s:", "r1");
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ ASSERT_STREQ(g_expected_full_dump, tombstone_contents.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, unaligned_addr) {
+ uint8_t buffer[256];
+ for (size_t i = 0; i < sizeof(buffer); i++) {
+ buffer[i] = i;
+ }
+ backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+
+ dump_memory(&log_, backtrace_mock_.get(), 0x12345679, "memory near %.2s:", "r1");
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ ASSERT_STREQ(g_expected_full_dump, tombstone_contents.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, memory_unreadable) {
+ dump_memory(&log_, backtrace_mock_.get(), 0xa2345678, "memory near pc:");
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ const char* expected_dump = \
+"\nmemory near pc:\n"
+#if defined(__LP64__)
+" 00000000a2345658 ---------------- ---------------- ................\n"
+" 00000000a2345668 ---------------- ---------------- ................\n"
+" 00000000a2345678 ---------------- ---------------- ................\n"
+" 00000000a2345688 ---------------- ---------------- ................\n"
+" 00000000a2345698 ---------------- ---------------- ................\n"
+" 00000000a23456a8 ---------------- ---------------- ................\n"
+" 00000000a23456b8 ---------------- ---------------- ................\n"
+" 00000000a23456c8 ---------------- ---------------- ................\n"
+" 00000000a23456d8 ---------------- ---------------- ................\n"
+" 00000000a23456e8 ---------------- ---------------- ................\n"
+" 00000000a23456f8 ---------------- ---------------- ................\n"
+" 00000000a2345708 ---------------- ---------------- ................\n"
+" 00000000a2345718 ---------------- ---------------- ................\n"
+" 00000000a2345728 ---------------- ---------------- ................\n"
+" 00000000a2345738 ---------------- ---------------- ................\n"
+" 00000000a2345748 ---------------- ---------------- ................\n";
+#else
+" a2345658 -------- -------- -------- -------- ................\n"
+" a2345668 -------- -------- -------- -------- ................\n"
+" a2345678 -------- -------- -------- -------- ................\n"
+" a2345688 -------- -------- -------- -------- ................\n"
+" a2345698 -------- -------- -------- -------- ................\n"
+" a23456a8 -------- -------- -------- -------- ................\n"
+" a23456b8 -------- -------- -------- -------- ................\n"
+" a23456c8 -------- -------- -------- -------- ................\n"
+" a23456d8 -------- -------- -------- -------- ................\n"
+" a23456e8 -------- -------- -------- -------- ................\n"
+" a23456f8 -------- -------- -------- -------- ................\n"
+" a2345708 -------- -------- -------- -------- ................\n"
+" a2345718 -------- -------- -------- -------- ................\n"
+" a2345728 -------- -------- -------- -------- ................\n"
+" a2345738 -------- -------- -------- -------- ................\n"
+" a2345748 -------- -------- -------- -------- ................\n";
+#endif
+ ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, memory_partially_unreadable) {
+ uint8_t buffer[104];
+ for (size_t i = 0; i < sizeof(buffer); i++) {
+ buffer[i] = i;
+ }
+ backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+
+ dump_memory(&log_, backtrace_mock_.get(), 0x12345600, "memory near pc:");
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ ASSERT_STREQ(g_expected_partial_dump, tombstone_contents.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, memory_partially_unreadable_unaligned_return) {
+ uint8_t buffer[104];
+ for (size_t i = 0; i < sizeof(buffer); i++) {
+ buffer[i] = i;
+ }
+ backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+ backtrace_mock_->SetPartialReadAmount(102);
+
+ dump_memory(&log_, backtrace_mock_.get(), 0x12345600, "memory near pc:");
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ ASSERT_STREQ(g_expected_partial_dump, tombstone_contents.c_str());
+
+#if defined(__LP64__)
+ ASSERT_STREQ("6 DEBUG Bytes read 102, is not a multiple of 8\n", getFakeLogPrint().c_str());
+#else
+ ASSERT_STREQ("6 DEBUG Bytes read 102, is not a multiple of 4\n", getFakeLogPrint().c_str());
+#endif
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+}
+
+TEST_F(DumpMemoryTest, memory_partially_unreadable_two_unaligned_reads) {
+ uint8_t buffer[106];
+ for (size_t i = 0; i < sizeof(buffer); i++) {
+ buffer[i] = i;
+ }
+ backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+ backtrace_mock_->SetPartialReadAmount(45);
+
+ dump_memory(&log_, backtrace_mock_.get(), 0x12345600, "memory near pc:");
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ ASSERT_STREQ(g_expected_partial_dump, tombstone_contents.c_str());
+
+#if defined(__LP64__)
+ ASSERT_STREQ("6 DEBUG Bytes read 45, is not a multiple of 8\n"
+ "6 DEBUG Bytes after second read 106, is not a multiple of 8\n",
+ getFakeLogPrint().c_str());
+#else
+ ASSERT_STREQ("6 DEBUG Bytes read 45, is not a multiple of 4\n"
+ "6 DEBUG Bytes after second read 106, is not a multiple of 4\n",
+ getFakeLogPrint().c_str());
+#endif
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+}
+
+TEST_F(DumpMemoryTest, address_low_fence) {
+ uint8_t buffer[256];
+ memset(buffer, 0, sizeof(buffer));
+ backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+
+ dump_memory(&log_, backtrace_mock_.get(), 0x1000, "memory near %.2s:", "r1");
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ const char* expected_dump = \
+"\nmemory near r1:\n"
+#if defined(__LP64__)
+" 0000000000001000 0000000000000000 0000000000000000 ................\n"
+" 0000000000001010 0000000000000000 0000000000000000 ................\n"
+" 0000000000001020 0000000000000000 0000000000000000 ................\n"
+" 0000000000001030 0000000000000000 0000000000000000 ................\n"
+" 0000000000001040 0000000000000000 0000000000000000 ................\n"
+" 0000000000001050 0000000000000000 0000000000000000 ................\n"
+" 0000000000001060 0000000000000000 0000000000000000 ................\n"
+" 0000000000001070 0000000000000000 0000000000000000 ................\n"
+" 0000000000001080 0000000000000000 0000000000000000 ................\n"
+" 0000000000001090 0000000000000000 0000000000000000 ................\n"
+" 00000000000010a0 0000000000000000 0000000000000000 ................\n"
+" 00000000000010b0 0000000000000000 0000000000000000 ................\n"
+" 00000000000010c0 0000000000000000 0000000000000000 ................\n"
+" 00000000000010d0 0000000000000000 0000000000000000 ................\n"
+" 00000000000010e0 0000000000000000 0000000000000000 ................\n"
+" 00000000000010f0 0000000000000000 0000000000000000 ................\n";
+#else
+" 00001000 00000000 00000000 00000000 00000000 ................\n"
+" 00001010 00000000 00000000 00000000 00000000 ................\n"
+" 00001020 00000000 00000000 00000000 00000000 ................\n"
+" 00001030 00000000 00000000 00000000 00000000 ................\n"
+" 00001040 00000000 00000000 00000000 00000000 ................\n"
+" 00001050 00000000 00000000 00000000 00000000 ................\n"
+" 00001060 00000000 00000000 00000000 00000000 ................\n"
+" 00001070 00000000 00000000 00000000 00000000 ................\n"
+" 00001080 00000000 00000000 00000000 00000000 ................\n"
+" 00001090 00000000 00000000 00000000 00000000 ................\n"
+" 000010a0 00000000 00000000 00000000 00000000 ................\n"
+" 000010b0 00000000 00000000 00000000 00000000 ................\n"
+" 000010c0 00000000 00000000 00000000 00000000 ................\n"
+" 000010d0 00000000 00000000 00000000 00000000 ................\n"
+" 000010e0 00000000 00000000 00000000 00000000 ................\n"
+" 000010f0 00000000 00000000 00000000 00000000 ................\n";
+#endif
+ ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, memory_address_too_low) {
+ uint8_t buffer[256];
+ memset(buffer, 0, sizeof(buffer));
+ backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+
+ dump_memory(&log_, backtrace_mock_.get(), 0, "memory near %.2s:", "r1");
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ ASSERT_STREQ("", tombstone_contents.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, memory_address_too_high) {
+ uint8_t buffer[256];
+ memset(buffer, 0, sizeof(buffer));
+ backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+
+#if defined(__LP64__)
+ dump_memory(&log_, backtrace_mock_.get(), 0x4000000000000000UL, "memory near %.2s:", "r1");
+ dump_memory(&log_, backtrace_mock_.get(), 0x4000000000000000UL - 32, "memory near %.2s:", "r1");
+ dump_memory(&log_, backtrace_mock_.get(), 0x4000000000000000UL - 216, "memory near %.2s:", "r1");
+#else
+ dump_memory(&log_, backtrace_mock_.get(), 0xffff0000, "memory near %.2s:", "r1");
+ dump_memory(&log_, backtrace_mock_.get(), 0xffff0000 - 32, "memory near %.2s:", "r1");
+ dump_memory(&log_, backtrace_mock_.get(), 0xffff0000 - 220, "memory near %.2s:", "r1");
+#endif
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ ASSERT_STREQ("", tombstone_contents.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, memory_address_would_overflow) {
+ uint8_t buffer[256];
+ memset(buffer, 0, sizeof(buffer));
+ backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+
+#if defined(__LP64__)
+ dump_memory(&log_, backtrace_mock_.get(), 0xfffffffffffffff0, "memory near %.2s:", "r1");
+#else
+ dump_memory(&log_, backtrace_mock_.get(), 0xfffffff0, "memory near %.2s:", "r1");
+#endif
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ ASSERT_STREQ("", tombstone_contents.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, memory_address_nearly_too_high) {
+ uint8_t buffer[256];
+ for (size_t i = 0; i < sizeof(buffer); i++) {
+ buffer[i] = i;
+ }
+ backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+
+#if defined(__LP64__)
+ dump_memory(&log_, backtrace_mock_.get(), 0x4000000000000000UL - 224, "memory near %.2s:", "r4");
+#else
+ dump_memory(&log_, backtrace_mock_.get(), 0xffff0000 - 224, "memory near %.2s:", "r4");
+#endif
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ const char* expected_dump = \
+"\nmemory near r4:\n"
+#if defined(__LP64__)
+" 3fffffffffffff00 0706050403020100 0f0e0d0c0b0a0908 ................\n"
+" 3fffffffffffff10 1716151413121110 1f1e1d1c1b1a1918 ................\n"
+" 3fffffffffffff20 2726252423222120 2f2e2d2c2b2a2928 !\"#$%&'()*+,-./\n"
+" 3fffffffffffff30 3736353433323130 3f3e3d3c3b3a3938 0123456789:;<=>?\n"
+" 3fffffffffffff40 4746454443424140 4f4e4d4c4b4a4948 @ABCDEFGHIJKLMNO\n"
+" 3fffffffffffff50 5756555453525150 5f5e5d5c5b5a5958 PQRSTUVWXYZ[\\]^_\n"
+" 3fffffffffffff60 6766656463626160 6f6e6d6c6b6a6968 `abcdefghijklmno\n"
+" 3fffffffffffff70 7776757473727170 7f7e7d7c7b7a7978 pqrstuvwxyz{|}~.\n"
+" 3fffffffffffff80 8786858483828180 8f8e8d8c8b8a8988 ................\n"
+" 3fffffffffffff90 9796959493929190 9f9e9d9c9b9a9998 ................\n"
+" 3fffffffffffffa0 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8 ................\n"
+" 3fffffffffffffb0 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8 ................\n"
+" 3fffffffffffffc0 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................\n"
+" 3fffffffffffffd0 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n"
+" 3fffffffffffffe0 e7e6e5e4e3e2e1e0 efeeedecebeae9e8 ................\n"
+" 3ffffffffffffff0 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8 ................\n";
+#else
+" fffeff00 03020100 07060504 0b0a0908 0f0e0d0c ................\n"
+" fffeff10 13121110 17161514 1b1a1918 1f1e1d1c ................\n"
+" fffeff20 23222120 27262524 2b2a2928 2f2e2d2c !\"#$%&'()*+,-./\n"
+" fffeff30 33323130 37363534 3b3a3938 3f3e3d3c 0123456789:;<=>?\n"
+" fffeff40 43424140 47464544 4b4a4948 4f4e4d4c @ABCDEFGHIJKLMNO\n"
+" fffeff50 53525150 57565554 5b5a5958 5f5e5d5c PQRSTUVWXYZ[\\]^_\n"
+" fffeff60 63626160 67666564 6b6a6968 6f6e6d6c `abcdefghijklmno\n"
+" fffeff70 73727170 77767574 7b7a7978 7f7e7d7c pqrstuvwxyz{|}~.\n"
+" fffeff80 83828180 87868584 8b8a8988 8f8e8d8c ................\n"
+" fffeff90 93929190 97969594 9b9a9998 9f9e9d9c ................\n"
+" fffeffa0 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac ................\n"
+" fffeffb0 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc ................\n"
+" fffeffc0 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc ................\n"
+" fffeffd0 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................\n"
+" fffeffe0 e3e2e1e0 e7e6e5e4 ebeae9e8 efeeedec ................\n"
+" fffefff0 f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc ................\n";
+#endif
+ ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, first_read_empty) {
+ uint8_t buffer[256];
+ for (size_t i = 0; i < sizeof(buffer); i++) {
+ buffer[i] = i;
+ }
+ backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+ backtrace_mock_->SetPartialReadAmount(0);
+
+ size_t page_size = sysconf(_SC_PAGE_SIZE);
+ uintptr_t addr = 0x10000020 + page_size - 120;
+ dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4");
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ const char* expected_dump = \
+"\nmemory near r4:\n"
+#if defined(__LP64__)
+" 0000000010000f88 ---------------- ---------------- ................\n"
+" 0000000010000f98 ---------------- ---------------- ................\n"
+" 0000000010000fa8 ---------------- ---------------- ................\n"
+" 0000000010000fb8 ---------------- ---------------- ................\n"
+" 0000000010000fc8 ---------------- ---------------- ................\n"
+" 0000000010000fd8 ---------------- ---------------- ................\n"
+" 0000000010000fe8 ---------------- ---------------- ................\n"
+" 0000000010000ff8 ---------------- 7f7e7d7c7b7a7978 ........xyz{|}~.\n"
+" 0000000010001008 8786858483828180 8f8e8d8c8b8a8988 ................\n"
+" 0000000010001018 9796959493929190 9f9e9d9c9b9a9998 ................\n"
+" 0000000010001028 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8 ................\n"
+" 0000000010001038 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8 ................\n"
+" 0000000010001048 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................\n"
+" 0000000010001058 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n"
+" 0000000010001068 e7e6e5e4e3e2e1e0 efeeedecebeae9e8 ................\n"
+" 0000000010001078 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8 ................\n";
+#else
+" 10000f88 -------- -------- -------- -------- ................\n"
+" 10000f98 -------- -------- -------- -------- ................\n"
+" 10000fa8 -------- -------- -------- -------- ................\n"
+" 10000fb8 -------- -------- -------- -------- ................\n"
+" 10000fc8 -------- -------- -------- -------- ................\n"
+" 10000fd8 -------- -------- -------- -------- ................\n"
+" 10000fe8 -------- -------- -------- -------- ................\n"
+" 10000ff8 -------- -------- 7b7a7978 7f7e7d7c ........xyz{|}~.\n"
+" 10001008 83828180 87868584 8b8a8988 8f8e8d8c ................\n"
+" 10001018 93929190 97969594 9b9a9998 9f9e9d9c ................\n"
+" 10001028 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac ................\n"
+" 10001038 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc ................\n"
+" 10001048 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc ................\n"
+" 10001058 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................\n"
+" 10001068 e3e2e1e0 e7e6e5e4 ebeae9e8 efeeedec ................\n"
+" 10001078 f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc ................\n";
+#endif
+ ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, first_read_empty_second_read_stops) {
+ uint8_t buffer[224];
+ for (size_t i = 0; i < sizeof(buffer); i++) {
+ buffer[i] = i;
+ }
+ backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+ backtrace_mock_->SetPartialReadAmount(0);
+
+ size_t page_size = sysconf(_SC_PAGE_SIZE);
+ uintptr_t addr = 0x10000020 + page_size - 192;
+ dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4");
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ const char* expected_dump = \
+"\nmemory near r4:\n"
+#if defined(__LP64__)
+" 0000000010000f40 ---------------- ---------------- ................\n"
+" 0000000010000f50 ---------------- ---------------- ................\n"
+" 0000000010000f60 ---------------- ---------------- ................\n"
+" 0000000010000f70 ---------------- ---------------- ................\n"
+" 0000000010000f80 ---------------- ---------------- ................\n"
+" 0000000010000f90 ---------------- ---------------- ................\n"
+" 0000000010000fa0 ---------------- ---------------- ................\n"
+" 0000000010000fb0 ---------------- ---------------- ................\n"
+" 0000000010000fc0 ---------------- ---------------- ................\n"
+" 0000000010000fd0 ---------------- ---------------- ................\n"
+" 0000000010000fe0 ---------------- ---------------- ................\n"
+" 0000000010000ff0 ---------------- ---------------- ................\n"
+" 0000000010001000 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................\n"
+" 0000000010001010 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n"
+" 0000000010001020 ---------------- ---------------- ................\n"
+" 0000000010001030 ---------------- ---------------- ................\n";
+#else
+" 10000f40 -------- -------- -------- -------- ................\n"
+" 10000f50 -------- -------- -------- -------- ................\n"
+" 10000f60 -------- -------- -------- -------- ................\n"
+" 10000f70 -------- -------- -------- -------- ................\n"
+" 10000f80 -------- -------- -------- -------- ................\n"
+" 10000f90 -------- -------- -------- -------- ................\n"
+" 10000fa0 -------- -------- -------- -------- ................\n"
+" 10000fb0 -------- -------- -------- -------- ................\n"
+" 10000fc0 -------- -------- -------- -------- ................\n"
+" 10000fd0 -------- -------- -------- -------- ................\n"
+" 10000fe0 -------- -------- -------- -------- ................\n"
+" 10000ff0 -------- -------- -------- -------- ................\n"
+" 10001000 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc ................\n"
+" 10001010 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................\n"
+" 10001020 -------- -------- -------- -------- ................\n"
+" 10001030 -------- -------- -------- -------- ................\n";
+#endif
+ ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, first_read_empty_next_page_out_of_range) {
+ uint8_t buffer[256];
+ for (size_t i = 0; i < sizeof(buffer); i++) {
+ buffer[i] = i;
+ }
+ backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+ backtrace_mock_->SetPartialReadAmount(0);
+
+ uintptr_t addr = 0x10000020;
+ dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4");
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ const char* expected_dump = \
+"\nmemory near r4:\n"
+#if defined(__LP64__)
+" 0000000010000000 ---------------- ---------------- ................\n"
+" 0000000010000010 ---------------- ---------------- ................\n"
+" 0000000010000020 ---------------- ---------------- ................\n"
+" 0000000010000030 ---------------- ---------------- ................\n"
+" 0000000010000040 ---------------- ---------------- ................\n"
+" 0000000010000050 ---------------- ---------------- ................\n"
+" 0000000010000060 ---------------- ---------------- ................\n"
+" 0000000010000070 ---------------- ---------------- ................\n"
+" 0000000010000080 ---------------- ---------------- ................\n"
+" 0000000010000090 ---------------- ---------------- ................\n"
+" 00000000100000a0 ---------------- ---------------- ................\n"
+" 00000000100000b0 ---------------- ---------------- ................\n"
+" 00000000100000c0 ---------------- ---------------- ................\n"
+" 00000000100000d0 ---------------- ---------------- ................\n"
+" 00000000100000e0 ---------------- ---------------- ................\n"
+" 00000000100000f0 ---------------- ---------------- ................\n";
+#else
+" 10000000 -------- -------- -------- -------- ................\n"
+" 10000010 -------- -------- -------- -------- ................\n"
+" 10000020 -------- -------- -------- -------- ................\n"
+" 10000030 -------- -------- -------- -------- ................\n"
+" 10000040 -------- -------- -------- -------- ................\n"
+" 10000050 -------- -------- -------- -------- ................\n"
+" 10000060 -------- -------- -------- -------- ................\n"
+" 10000070 -------- -------- -------- -------- ................\n"
+" 10000080 -------- -------- -------- -------- ................\n"
+" 10000090 -------- -------- -------- -------- ................\n"
+" 100000a0 -------- -------- -------- -------- ................\n"
+" 100000b0 -------- -------- -------- -------- ................\n"
+" 100000c0 -------- -------- -------- -------- ................\n"
+" 100000d0 -------- -------- -------- -------- ................\n"
+" 100000e0 -------- -------- -------- -------- ................\n"
+" 100000f0 -------- -------- -------- -------- ................\n";
+#endif
+ ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, first_read_empty_next_page_out_of_range_fence_post) {
+ uint8_t buffer[256];
+ for (size_t i = 0; i < sizeof(buffer); i++) {
+ buffer[i] = i;
+ }
+ backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+ backtrace_mock_->SetPartialReadAmount(0);
+
+ size_t page_size = sysconf(_SC_PAGE_SIZE);
+ uintptr_t addr = 0x10000020 + page_size - 256;
+
+ dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4");
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ const char* expected_dump = \
+"\nmemory near r4:\n"
+#if defined(__LP64__)
+" 0000000010000f00 ---------------- ---------------- ................\n"
+" 0000000010000f10 ---------------- ---------------- ................\n"
+" 0000000010000f20 ---------------- ---------------- ................\n"
+" 0000000010000f30 ---------------- ---------------- ................\n"
+" 0000000010000f40 ---------------- ---------------- ................\n"
+" 0000000010000f50 ---------------- ---------------- ................\n"
+" 0000000010000f60 ---------------- ---------------- ................\n"
+" 0000000010000f70 ---------------- ---------------- ................\n"
+" 0000000010000f80 ---------------- ---------------- ................\n"
+" 0000000010000f90 ---------------- ---------------- ................\n"
+" 0000000010000fa0 ---------------- ---------------- ................\n"
+" 0000000010000fb0 ---------------- ---------------- ................\n"
+" 0000000010000fc0 ---------------- ---------------- ................\n"
+" 0000000010000fd0 ---------------- ---------------- ................\n"
+" 0000000010000fe0 ---------------- ---------------- ................\n"
+" 0000000010000ff0 ---------------- ---------------- ................\n";
+#else
+" 10000f00 -------- -------- -------- -------- ................\n"
+" 10000f10 -------- -------- -------- -------- ................\n"
+" 10000f20 -------- -------- -------- -------- ................\n"
+" 10000f30 -------- -------- -------- -------- ................\n"
+" 10000f40 -------- -------- -------- -------- ................\n"
+" 10000f50 -------- -------- -------- -------- ................\n"
+" 10000f60 -------- -------- -------- -------- ................\n"
+" 10000f70 -------- -------- -------- -------- ................\n"
+" 10000f80 -------- -------- -------- -------- ................\n"
+" 10000f90 -------- -------- -------- -------- ................\n"
+" 10000fa0 -------- -------- -------- -------- ................\n"
+" 10000fb0 -------- -------- -------- -------- ................\n"
+" 10000fc0 -------- -------- -------- -------- ................\n"
+" 10000fd0 -------- -------- -------- -------- ................\n"
+" 10000fe0 -------- -------- -------- -------- ................\n"
+" 10000ff0 -------- -------- -------- -------- ................\n";
+#endif
+ ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
diff --git a/debuggerd/test/elf_fake.cpp b/debuggerd/test/elf_fake.cpp
new file mode 100644
index 0000000..bb52b59
--- /dev/null
+++ b/debuggerd/test/elf_fake.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <string>
+
+class Backtrace;
+
+std::string g_build_id;
+
+void elf_set_fake_build_id(const std::string& build_id) {
+ g_build_id = build_id;
+}
+
+bool elf_get_build_id(Backtrace*, uintptr_t, std::string* build_id) {
+ if (g_build_id != "") {
+ *build_id = g_build_id;
+ return true;
+ }
+ return false;
+}
diff --git a/debuggerd/test/elf_fake.h b/debuggerd/test/elf_fake.h
new file mode 100644
index 0000000..08a8454
--- /dev/null
+++ b/debuggerd/test/elf_fake.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DEBUGGERD_TEST_ELF_FAKE_H
+#define _DEBUGGERD_TEST_ELF_FAKE_H
+
+#include <string>
+
+void elf_set_fake_build_id(const std::string&);
+
+#endif // _DEBUGGERD_TEST_ELF_FAKE_H
diff --git a/debuggerd/test/host_signal_fixup.h b/debuggerd/test/host_signal_fixup.h
new file mode 100644
index 0000000..c7796ef
--- /dev/null
+++ b/debuggerd/test/host_signal_fixup.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DEBUGGERD_TEST_HOST_SIGNAL_FIXUP_H
+#define _DEBUGGERD_TEST_HOST_SIGNAL_FIXUP_H
+
+#include <signal.h>
+
+#if !defined(__BIONIC__)
+
+// In order to compile parts of debuggerd for the host, we need to
+// define these values.
+
+#if !defined(NSIGILL)
+#define NSIGILL ILL_BADSTK
+#endif
+
+#if !defined(BUS_MCEERR_AR)
+#define BUS_MCEERR_AR 4
+#endif
+#if !defined(BUS_MCEERR_AO)
+#define BUS_MCEERR_AO 5
+#endif
+#if !defined(NSIGBUS)
+#define NSIGBUS BUS_MCEERR_AO
+#endif
+
+#if !defined(NSIGFPE)
+#define NSIGFPE FPE_FLTSUB
+#endif
+
+#if !defined(NSIGSEGV)
+#define NSIGSEGV SEGV_ACCERR
+#endif
+
+#if !defined(TRAP_BRANCH)
+#define TRAP_BRANCH 3
+#endif
+#if !defined(TRAP_HWBKPT)
+#define TRAP_HWBKPT 4
+#endif
+#if !defined(NSIGTRAP)
+#define NSIGTRAP TRAP_HWBKPT
+#endif
+
+#if !defined(SI_DETHREAD)
+#define SI_DETHREAD -7
+#endif
+
+#endif
+
+#endif // _DEBUGGERD_TEST_HOST_SIGNAL_FIXUP_H
diff --git a/debuggerd/test/log_fake.cpp b/debuggerd/test/log_fake.cpp
new file mode 100644
index 0000000..d584a5e
--- /dev/null
+++ b/debuggerd/test/log_fake.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <stdarg.h>
+
+#include <string>
+
+#include <base/stringprintf.h>
+#include <log/log.h>
+#include <log/logger.h>
+
+// Forward declarations.
+class Backtrace;
+struct EventTagMap;
+struct AndroidLogEntry;
+
+std::string g_fake_log_buf;
+
+std::string g_fake_log_print;
+
+void resetLogs() {
+ g_fake_log_buf = "";
+ g_fake_log_print = "";
+}
+
+std::string getFakeLogBuf() {
+ return g_fake_log_buf;
+}
+
+std::string getFakeLogPrint() {
+ return g_fake_log_print;
+}
+
+extern "C" int __android_log_buf_write(int bufId, int prio, const char* tag, const char* msg) {
+ g_fake_log_buf += std::to_string(bufId) + ' ' + std::to_string(prio) + ' ';
+ g_fake_log_buf += tag;
+ g_fake_log_buf += ' ';
+ g_fake_log_buf += msg;
+ return 1;
+}
+
+extern "C" int __android_log_print(int prio, const char* tag, const char* fmt, ...) {
+ g_fake_log_print += std::to_string(prio) + ' ';
+ g_fake_log_print += tag;
+ g_fake_log_print += ' ';
+
+ va_list ap;
+ va_start(ap, fmt);
+ android::base::StringAppendV(&g_fake_log_print, fmt, ap);
+ va_end(ap);
+
+ g_fake_log_print += '\n';
+
+ return 1;
+}
+
+extern "C" log_id_t android_name_to_log_id(const char*) {
+ return LOG_ID_SYSTEM;
+}
+
+extern "C" struct logger_list* android_logger_list_open(log_id_t, int, unsigned int, pid_t) {
+ errno = EACCES;
+ return nullptr;
+}
+
+extern "C" int android_logger_list_read(struct logger_list*, struct log_msg*) {
+ return 0;
+}
+
+extern "C" EventTagMap* android_openEventTagMap(const char*) {
+ return nullptr;
+}
+
+extern "C" int android_log_processBinaryLogBuffer(
+ struct logger_entry*,
+ AndroidLogEntry*, const EventTagMap*, char*, int) {
+ return 0;
+}
+
+extern "C" void android_logger_list_free(struct logger_list*) {
+}
diff --git a/debuggerd/test/log_fake.h b/debuggerd/test/log_fake.h
new file mode 100644
index 0000000..5418fce
--- /dev/null
+++ b/debuggerd/test/log_fake.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DEBUGGERD_TEST_LOG_FAKE_H
+#define _DEBUGGERD_TEST_LOG_FAKE_H
+
+#include <string>
+
+void resetLogs();
+std::string getFakeLogBuf();
+std::string getFakeLogPrint();
+
+#endif // _DEBUGGERD_TEST_LOG_FAKE_H
diff --git a/debuggerd/test/property_fake.cpp b/debuggerd/test/property_fake.cpp
new file mode 100644
index 0000000..02069f1
--- /dev/null
+++ b/debuggerd/test/property_fake.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string.h>
+
+#include <string>
+#include <unordered_map>
+
+#include <sys/system_properties.h>
+
+std::unordered_map<std::string, std::string> g_properties;
+
+extern "C" int property_set(const char* name, const char* value) {
+ if (g_properties.count(name) != 0) {
+ g_properties.erase(name);
+ }
+ g_properties[name] = value;
+ return 0;
+}
+
+extern "C" int property_get(const char* key, char* value, const char* default_value) {
+ if (g_properties.count(key) == 0) {
+ if (default_value == nullptr) {
+ return 0;
+ }
+ strncpy(value, default_value, PROP_VALUE_MAX-1);
+ } else {
+ strncpy(value, g_properties[key].c_str(), PROP_VALUE_MAX-1);
+ }
+ value[PROP_VALUE_MAX-1] = '\0';
+ return strlen(value);
+}
diff --git a/debuggerd/test/ptrace_fake.cpp b/debuggerd/test/ptrace_fake.cpp
new file mode 100644
index 0000000..f40cbd4
--- /dev/null
+++ b/debuggerd/test/ptrace_fake.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <sys/ptrace.h>
+
+#include <string>
+
+#include "ptrace_fake.h"
+
+siginfo_t g_fake_si = {.si_signo = 0};
+
+void ptrace_set_fake_getsiginfo(const siginfo_t& si) {
+ g_fake_si = si;
+}
+
+#if !defined(__BIONIC__)
+extern "C" long ptrace_fake(enum __ptrace_request request, ...) {
+#else
+extern "C" long ptrace_fake(int request, ...) {
+#endif
+ if (request == PTRACE_GETSIGINFO) {
+ if (g_fake_si.si_signo == 0) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ va_list ap;
+ va_start(ap, request);
+ va_arg(ap, int);
+ va_arg(ap, int);
+ siginfo_t* si = va_arg(ap, siginfo*);
+ va_end(ap);
+ *si = g_fake_si;
+ return 0;
+ }
+ return -1;
+}
diff --git a/debuggerd/test/ptrace_fake.h b/debuggerd/test/ptrace_fake.h
new file mode 100644
index 0000000..fdbb663
--- /dev/null
+++ b/debuggerd/test/ptrace_fake.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DEBUGGERD_TEST_PTRACE_FAKE_H
+#define _DEBUGGERD_TEST_PTRACE_FAKE_H
+
+#include <signal.h>
+
+void ptrace_set_fake_getsiginfo(const siginfo_t&);
+
+#endif // _DEBUGGERD_TEST_PTRACE_FAKE_H
diff --git a/debuggerd/test/selinux_fake.cpp b/debuggerd/test/selinux_fake.cpp
new file mode 100644
index 0000000..acdd0a9
--- /dev/null
+++ b/debuggerd/test/selinux_fake.cpp
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+extern "C" int selinux_android_restorecon(const char*, unsigned int) {
+ return 0;
+}
diff --git a/debuggerd/test/sys/system_properties.h b/debuggerd/test/sys/system_properties.h
new file mode 100644
index 0000000..9d44345
--- /dev/null
+++ b/debuggerd/test/sys/system_properties.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _DEBUGGERD_TEST_SYS_SYSTEM_PROPERTIES_H
+#define _DEBUGGERD_TEST_SYS_SYSTEM_PROPERTIES_H
+
+// This is just enough to get the property code to compile on
+// the host.
+
+#define PROP_NAME_MAX 32
+#define PROP_VALUE_MAX 92
+
+#endif // _DEBUGGERD_TEST_SYS_SYSTEM_PROPERTIES_H
diff --git a/debuggerd/test/tombstone_test.cpp b/debuggerd/test/tombstone_test.cpp
new file mode 100644
index 0000000..a771a39
--- /dev/null
+++ b/debuggerd/test/tombstone_test.cpp
@@ -0,0 +1,599 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+
+#include <memory>
+#include <string>
+
+#include <gtest/gtest.h>
+#include <base/file.h>
+
+#include "utility.h"
+
+#include "BacktraceMock.h"
+#include "elf_fake.h"
+#include "host_signal_fixup.h"
+#include "log_fake.h"
+#include "ptrace_fake.h"
+
+// In order to test this code, we need to include the tombstone.cpp code.
+// Including it, also allows us to override the ptrace function.
+#define ptrace ptrace_fake
+
+#include "tombstone.cpp"
+
+void dump_registers(log_t*, pid_t) {
+}
+
+void dump_memory_and_code(log_t*, Backtrace*) {
+}
+
+void dump_backtrace_to_log(Backtrace*, log_t*, char const*) {
+}
+
+class TombstoneTest : public ::testing::Test {
+ protected:
+ virtual void SetUp() {
+ map_mock_.reset(new BacktraceMapMock());
+ backtrace_mock_.reset(new BacktraceMock(map_mock_.get()));
+
+ char tmp_file[256];
+ const char data_template[] = "/data/local/tmp/debuggerd_memory_testXXXXXX";
+ memcpy(tmp_file, data_template, sizeof(data_template));
+ int tombstone_fd = mkstemp(tmp_file);
+ if (tombstone_fd == -1) {
+ const char tmp_template[] = "/tmp/debuggerd_memory_testXXXXXX";
+ memcpy(tmp_file, tmp_template, sizeof(tmp_template));
+ tombstone_fd = mkstemp(tmp_file);
+ if (tombstone_fd == -1) {
+ abort();
+ }
+ }
+ if (unlink(tmp_file) == -1) {
+ abort();
+ }
+
+ log_.tfd = tombstone_fd;
+ log_.amfd = -1;
+ log_.crashed_tid = 12;
+ log_.current_tid = 12;
+ log_.should_retrieve_logcat = false;
+
+ resetLogs();
+ elf_set_fake_build_id("");
+ siginfo_t si;
+ si.si_signo = SIGPIPE;
+ ptrace_set_fake_getsiginfo(si);
+ }
+
+ virtual void TearDown() {
+ if (log_.tfd >= 0) {
+ close(log_.tfd);
+ }
+ }
+
+ std::unique_ptr<BacktraceMapMock> map_mock_;
+ std::unique_ptr<BacktraceMock> backtrace_mock_;
+
+ log_t log_;
+};
+
+TEST_F(TombstoneTest, single_map) {
+ backtrace_map_t map;
+#if defined(__LP64__)
+ map.start = 0x123456789abcd000UL;
+ map.end = 0x123456789abdf000UL;
+#else
+ map.start = 0x1234000;
+ map.end = 0x1235000;
+#endif
+ map_mock_->AddMap(map);
+
+ dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ const char* expected_dump = \
+"\nmemory map:\n"
+#if defined(__LP64__)
+" 12345678'9abcd000-12345678'9abdefff --- 0 12000\n";
+#else
+" 01234000-01234fff --- 0 1000\n";
+#endif
+ ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(TombstoneTest, single_map_elf_build_id) {
+ backtrace_map_t map;
+#if defined(__LP64__)
+ map.start = 0x123456789abcd000UL;
+ map.end = 0x123456789abdf000UL;
+#else
+ map.start = 0x1234000;
+ map.end = 0x1235000;
+#endif
+ map.flags = PROT_READ;
+ map.name = "/system/lib/libfake.so";
+ map_mock_->AddMap(map);
+
+ elf_set_fake_build_id("abcdef1234567890abcdef1234567890");
+ dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ const char* expected_dump = \
+"\nmemory map:\n"
+#if defined(__LP64__)
+" 12345678'9abcd000-12345678'9abdefff r-- 0 12000 /system/lib/libfake.so (BuildId: abcdef1234567890abcdef1234567890)\n";
+#else
+" 01234000-01234fff r-- 0 1000 /system/lib/libfake.so (BuildId: abcdef1234567890abcdef1234567890)\n";
+#endif
+ ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+// Even though build id is present, it should not be printed in either of
+// these cases.
+TEST_F(TombstoneTest, single_map_no_build_id) {
+ backtrace_map_t map;
+#if defined(__LP64__)
+ map.start = 0x123456789abcd000UL;
+ map.end = 0x123456789abdf000UL;
+#else
+ map.start = 0x1234000;
+ map.end = 0x1235000;
+#endif
+ map.flags = PROT_WRITE;
+ map_mock_->AddMap(map);
+
+ map.name = "/system/lib/libfake.so";
+ map_mock_->AddMap(map);
+
+ elf_set_fake_build_id("abcdef1234567890abcdef1234567890");
+ dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ const char* expected_dump = \
+"\nmemory map:\n"
+#if defined(__LP64__)
+" 12345678'9abcd000-12345678'9abdefff -w- 0 12000\n"
+" 12345678'9abcd000-12345678'9abdefff -w- 0 12000 /system/lib/libfake.so\n";
+#else
+" 01234000-01234fff -w- 0 1000\n"
+" 01234000-01234fff -w- 0 1000 /system/lib/libfake.so\n";
+#endif
+ ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(TombstoneTest, multiple_maps) {
+ backtrace_map_t map;
+
+ map.start = 0xa234000;
+ map.end = 0xa235000;
+ map_mock_->AddMap(map);
+
+ map.start = 0xa334000;
+ map.end = 0xa335000;
+ map.offset = 0xf000;
+ map.flags = PROT_READ;
+ map_mock_->AddMap(map);
+
+ map.start = 0xa434000;
+ map.end = 0xa435000;
+ map.offset = 0x1000;
+ map.load_base = 0xd000;
+ map.flags = PROT_WRITE;
+ map_mock_->AddMap(map);
+
+ map.start = 0xa534000;
+ map.end = 0xa535000;
+ map.offset = 0x3000;
+ map.load_base = 0x2000;
+ map.flags = PROT_EXEC;
+ map_mock_->AddMap(map);
+
+ map.start = 0xa634000;
+ map.end = 0xa635000;
+ map.offset = 0;
+ map.load_base = 0;
+ map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
+ map.name = "/system/lib/fake.so";
+ map_mock_->AddMap(map);
+
+ dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ const char* expected_dump = \
+"\nmemory map:\n"
+#if defined(__LP64__)
+" 00000000'0a234000-00000000'0a234fff --- 0 1000\n"
+" 00000000'0a334000-00000000'0a334fff r-- f000 1000\n"
+" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load base 0xd000)\n"
+" 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load base 0x2000)\n"
+" 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n";
+#else
+" 0a234000-0a234fff --- 0 1000\n"
+" 0a334000-0a334fff r-- f000 1000\n"
+" 0a434000-0a434fff -w- 1000 1000 (load base 0xd000)\n"
+" 0a534000-0a534fff --x 3000 1000 (load base 0x2000)\n"
+" 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n";
+#endif
+ ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(TombstoneTest, multiple_maps_fault_address_before) {
+ backtrace_map_t map;
+
+ map.start = 0xa434000;
+ map.end = 0xa435000;
+ map.offset = 0x1000;
+ map.load_base = 0xd000;
+ map.flags = PROT_WRITE;
+ map_mock_->AddMap(map);
+
+ map.start = 0xa534000;
+ map.end = 0xa535000;
+ map.offset = 0x3000;
+ map.load_base = 0x2000;
+ map.flags = PROT_EXEC;
+ map_mock_->AddMap(map);
+
+ map.start = 0xa634000;
+ map.end = 0xa635000;
+ map.offset = 0;
+ map.load_base = 0;
+ map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
+ map.name = "/system/lib/fake.so";
+ map_mock_->AddMap(map);
+
+ siginfo_t si;
+ si.si_signo = SIGBUS;
+ si.si_addr = reinterpret_cast<void*>(0x1000);
+ ptrace_set_fake_getsiginfo(si);
+ dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ const char* expected_dump = \
+"\nmemory map: (fault address prefixed with --->)\n"
+#if defined(__LP64__)
+"--->Fault address falls at 00000000'00001000 before any mapped regions\n"
+" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load base 0xd000)\n"
+" 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load base 0x2000)\n"
+" 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n";
+#else
+"--->Fault address falls at 00001000 before any mapped regions\n"
+" 0a434000-0a434fff -w- 1000 1000 (load base 0xd000)\n"
+" 0a534000-0a534fff --x 3000 1000 (load base 0x2000)\n"
+" 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n";
+#endif
+ ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(TombstoneTest, multiple_maps_fault_address_between) {
+ backtrace_map_t map;
+
+ map.start = 0xa434000;
+ map.end = 0xa435000;
+ map.offset = 0x1000;
+ map.load_base = 0xd000;
+ map.flags = PROT_WRITE;
+ map_mock_->AddMap(map);
+
+ map.start = 0xa534000;
+ map.end = 0xa535000;
+ map.offset = 0x3000;
+ map.load_base = 0x2000;
+ map.flags = PROT_EXEC;
+ map_mock_->AddMap(map);
+
+ map.start = 0xa634000;
+ map.end = 0xa635000;
+ map.offset = 0;
+ map.load_base = 0;
+ map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
+ map.name = "/system/lib/fake.so";
+ map_mock_->AddMap(map);
+
+ siginfo_t si;
+ si.si_signo = SIGBUS;
+ si.si_addr = reinterpret_cast<void*>(0xa533000);
+ ptrace_set_fake_getsiginfo(si);
+ dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ const char* expected_dump = \
+"\nmemory map: (fault address prefixed with --->)\n"
+#if defined(__LP64__)
+" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load base 0xd000)\n"
+"--->Fault address falls at 00000000'0a533000 between mapped regions\n"
+" 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load base 0x2000)\n"
+" 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n";
+#else
+" 0a434000-0a434fff -w- 1000 1000 (load base 0xd000)\n"
+"--->Fault address falls at 0a533000 between mapped regions\n"
+" 0a534000-0a534fff --x 3000 1000 (load base 0x2000)\n"
+" 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n";
+#endif
+ ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(TombstoneTest, multiple_maps_fault_address_in_map) {
+ backtrace_map_t map;
+
+ map.start = 0xa434000;
+ map.end = 0xa435000;
+ map.offset = 0x1000;
+ map.load_base = 0xd000;
+ map.flags = PROT_WRITE;
+ map_mock_->AddMap(map);
+
+ map.start = 0xa534000;
+ map.end = 0xa535000;
+ map.offset = 0x3000;
+ map.load_base = 0x2000;
+ map.flags = PROT_EXEC;
+ map_mock_->AddMap(map);
+
+ map.start = 0xa634000;
+ map.end = 0xa635000;
+ map.offset = 0;
+ map.load_base = 0;
+ map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
+ map.name = "/system/lib/fake.so";
+ map_mock_->AddMap(map);
+
+ siginfo_t si;
+ si.si_signo = SIGBUS;
+ si.si_addr = reinterpret_cast<void*>(0xa534040);
+ ptrace_set_fake_getsiginfo(si);
+ dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ const char* expected_dump = \
+"\nmemory map: (fault address prefixed with --->)\n"
+#if defined(__LP64__)
+" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load base 0xd000)\n"
+"--->00000000'0a534000-00000000'0a534fff --x 3000 1000 (load base 0x2000)\n"
+" 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n";
+#else
+" 0a434000-0a434fff -w- 1000 1000 (load base 0xd000)\n"
+"--->0a534000-0a534fff --x 3000 1000 (load base 0x2000)\n"
+" 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n";
+#endif
+ ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(TombstoneTest, multiple_maps_fault_address_after) {
+ backtrace_map_t map;
+
+ map.start = 0xa434000;
+ map.end = 0xa435000;
+ map.offset = 0x1000;
+ map.load_base = 0xd000;
+ map.flags = PROT_WRITE;
+ map_mock_->AddMap(map);
+
+ map.start = 0xa534000;
+ map.end = 0xa535000;
+ map.offset = 0x3000;
+ map.load_base = 0x2000;
+ map.flags = PROT_EXEC;
+ map_mock_->AddMap(map);
+
+ map.start = 0xa634000;
+ map.end = 0xa635000;
+ map.offset = 0;
+ map.load_base = 0;
+ map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
+ map.name = "/system/lib/fake.so";
+ map_mock_->AddMap(map);
+
+ siginfo_t si;
+ si.si_signo = SIGBUS;
+#if defined(__LP64__)
+ si.si_addr = reinterpret_cast<void*>(0x12345a534040UL);
+#else
+ si.si_addr = reinterpret_cast<void*>(0xf534040UL);
+#endif
+ ptrace_set_fake_getsiginfo(si);
+ dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ const char* expected_dump = \
+"\nmemory map: (fault address prefixed with --->)\n"
+#if defined(__LP64__)
+" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load base 0xd000)\n"
+" 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load base 0x2000)\n"
+" 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n"
+"--->Fault address falls at 00001234'5a534040 after any mapped regions\n";
+#else
+" 0a434000-0a434fff -w- 1000 1000 (load base 0xd000)\n"
+" 0a534000-0a534fff --x 3000 1000 (load base 0x2000)\n"
+" 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n"
+"--->Fault address falls at 0f534040 after any mapped regions\n";
+#endif
+ ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(TombstoneTest, multiple_maps_getsiginfo_fail) {
+ backtrace_map_t map;
+
+ map.start = 0xa434000;
+ map.end = 0xa435000;
+ map.offset = 0x1000;
+ map.load_base = 0xd000;
+ map.flags = PROT_WRITE;
+ map_mock_->AddMap(map);
+
+ siginfo_t si;
+ si.si_signo = 0;
+ ptrace_set_fake_getsiginfo(si);
+ dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ const char* expected_dump = \
+"\nmemory map:\n"
+#if defined(__LP64__)
+" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load base 0xd000)\n";
+#else
+" 0a434000-0a434fff -w- 1000 1000 (load base 0xd000)\n";
+#endif
+ ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("6 DEBUG Cannot get siginfo for 100: Bad address\n\n", getFakeLogPrint().c_str());
+}
+
+TEST_F(TombstoneTest, multiple_maps_check_signal_has_si_addr) {
+ backtrace_map_t map;
+
+ map.start = 0xa434000;
+ map.end = 0xa435000;
+ map.flags = PROT_WRITE;
+ map_mock_->AddMap(map);
+
+ for (int i = 1; i < 255; i++) {
+ ASSERT_TRUE(ftruncate(log_.tfd, 0) == 0);
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+
+ siginfo_t si;
+ si.si_signo = i;
+ si.si_addr = reinterpret_cast<void*>(0x1000);
+ ptrace_set_fake_getsiginfo(si);
+ dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ bool has_addr = false;
+ switch (si.si_signo) {
+ case SIGBUS:
+ case SIGFPE:
+ case SIGILL:
+ case SIGSEGV:
+ case SIGTRAP:
+ has_addr = true;
+ break;
+ }
+
+ const char* expected_addr_dump = \
+"\nmemory map: (fault address prefixed with --->)\n"
+#if defined(__LP64__)
+"--->Fault address falls at 00000000'00001000 before any mapped regions\n"
+" 00000000'0a434000-00000000'0a434fff -w- 0 1000\n";
+#else
+"--->Fault address falls at 00001000 before any mapped regions\n"
+" 0a434000-0a434fff -w- 0 1000\n";
+#endif
+ const char* expected_dump = \
+"\nmemory map:\n"
+#if defined(__LP64__)
+" 00000000'0a434000-00000000'0a434fff -w- 0 1000\n";
+#else
+" 0a434000-0a434fff -w- 0 1000\n";
+#endif
+ if (has_addr) {
+ ASSERT_STREQ(expected_addr_dump, tombstone_contents.c_str())
+ << "Signal " << si.si_signo << " expected to include an address.";
+ } else {
+ ASSERT_STREQ(expected_dump, tombstone_contents.c_str())
+ << "Signal " << si.si_signo << " is not expected to include an address.";
+ }
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+ }
+}
+
+TEST_F(TombstoneTest, dump_signal_info_error) {
+ siginfo_t si;
+ si.si_signo = 0;
+ ptrace_set_fake_getsiginfo(si);
+
+ dump_signal_info(&log_, 123, SIGSEGV, 10);
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ ASSERT_STREQ("", tombstone_contents.c_str());
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("6 DEBUG cannot get siginfo: Bad address\n\n", getFakeLogPrint().c_str());
+}
+
+TEST_F(TombstoneTest, dump_log_file_error) {
+ log_.should_retrieve_logcat = true;
+ dump_log_file(&log_, 123, "/fake/filename", 10);
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ ASSERT_STREQ("", tombstone_contents.c_str());
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("6 DEBUG Unable to open /fake/filename: Permission denied\n\n",
+ getFakeLogPrint().c_str());
+}
diff --git a/debuggerd/tombstone.cpp b/debuggerd/tombstone.cpp
new file mode 100644
index 0000000..114c7e4
--- /dev/null
+++ b/debuggerd/tombstone.cpp
@@ -0,0 +1,826 @@
+/*
+ * Copyright (C) 2012-2014 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 "DEBUG"
+
+#include <arpa/inet.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/ptrace.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+
+#include <memory>
+#include <string>
+
+#include <private/android_filesystem_config.h>
+
+#include <base/stringprintf.h>
+#include <cutils/properties.h>
+#include <log/log.h>
+#include <log/logger.h>
+#include <log/logprint.h>
+
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+
+#include <selinux/android.h>
+
+#include "backtrace.h"
+#include "elf_utils.h"
+#include "machine.h"
+#include "tombstone.h"
+
+#define STACK_WORDS 16
+
+#define MAX_TOMBSTONES 10
+#define TOMBSTONE_DIR "/data/tombstones"
+#define TOMBSTONE_TEMPLATE (TOMBSTONE_DIR"/tombstone_%02d")
+
+// Must match the path defined in NativeCrashListener.java
+#define NCRASH_SOCKET_PATH "/data/system/ndebugsocket"
+
+static bool signal_has_si_addr(int sig) {
+ switch (sig) {
+ case SIGBUS:
+ case SIGFPE:
+ case SIGILL:
+ case SIGSEGV:
+ case SIGTRAP:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const char* get_signame(int sig) {
+ switch(sig) {
+ case SIGABRT: return "SIGABRT";
+ case SIGBUS: return "SIGBUS";
+ case SIGFPE: return "SIGFPE";
+ case SIGILL: return "SIGILL";
+ case SIGPIPE: return "SIGPIPE";
+ case SIGSEGV: return "SIGSEGV";
+#if defined(SIGSTKFLT)
+ case SIGSTKFLT: return "SIGSTKFLT";
+#endif
+ case SIGSTOP: return "SIGSTOP";
+ case SIGTRAP: return "SIGTRAP";
+ default: return "?";
+ }
+}
+
+static const char* get_sigcode(int signo, int code) {
+ // Try the signal-specific codes...
+ switch (signo) {
+ case SIGILL:
+ switch (code) {
+ case ILL_ILLOPC: return "ILL_ILLOPC";
+ case ILL_ILLOPN: return "ILL_ILLOPN";
+ case ILL_ILLADR: return "ILL_ILLADR";
+ case ILL_ILLTRP: return "ILL_ILLTRP";
+ case ILL_PRVOPC: return "ILL_PRVOPC";
+ case ILL_PRVREG: return "ILL_PRVREG";
+ case ILL_COPROC: return "ILL_COPROC";
+ case ILL_BADSTK: return "ILL_BADSTK";
+ }
+ static_assert(NSIGILL == ILL_BADSTK, "missing ILL_* si_code");
+ break;
+ case SIGBUS:
+ switch (code) {
+ case BUS_ADRALN: return "BUS_ADRALN";
+ case BUS_ADRERR: return "BUS_ADRERR";
+ case BUS_OBJERR: return "BUS_OBJERR";
+ case BUS_MCEERR_AR: return "BUS_MCEERR_AR";
+ case BUS_MCEERR_AO: return "BUS_MCEERR_AO";
+ }
+ static_assert(NSIGBUS == BUS_MCEERR_AO, "missing BUS_* si_code");
+ break;
+ case SIGFPE:
+ switch (code) {
+ case FPE_INTDIV: return "FPE_INTDIV";
+ case FPE_INTOVF: return "FPE_INTOVF";
+ case FPE_FLTDIV: return "FPE_FLTDIV";
+ case FPE_FLTOVF: return "FPE_FLTOVF";
+ case FPE_FLTUND: return "FPE_FLTUND";
+ case FPE_FLTRES: return "FPE_FLTRES";
+ case FPE_FLTINV: return "FPE_FLTINV";
+ case FPE_FLTSUB: return "FPE_FLTSUB";
+ }
+ static_assert(NSIGFPE == FPE_FLTSUB, "missing FPE_* si_code");
+ break;
+ case SIGSEGV:
+ switch (code) {
+ case SEGV_MAPERR: return "SEGV_MAPERR";
+ case SEGV_ACCERR: return "SEGV_ACCERR";
+ }
+ static_assert(NSIGSEGV == SEGV_ACCERR, "missing SEGV_* si_code");
+ break;
+ case SIGTRAP:
+ switch (code) {
+ case TRAP_BRKPT: return "TRAP_BRKPT";
+ case TRAP_TRACE: return "TRAP_TRACE";
+ case TRAP_BRANCH: return "TRAP_BRANCH";
+ case TRAP_HWBKPT: return "TRAP_HWBKPT";
+ }
+ static_assert(NSIGTRAP == TRAP_HWBKPT, "missing TRAP_* si_code");
+ break;
+ }
+ // Then the other codes...
+ switch (code) {
+ case SI_USER: return "SI_USER";
+ case SI_KERNEL: return "SI_KERNEL";
+ case SI_QUEUE: return "SI_QUEUE";
+ case SI_TIMER: return "SI_TIMER";
+ case SI_MESGQ: return "SI_MESGQ";
+ case SI_ASYNCIO: return "SI_ASYNCIO";
+ case SI_SIGIO: return "SI_SIGIO";
+ case SI_TKILL: return "SI_TKILL";
+ case SI_DETHREAD: return "SI_DETHREAD";
+ }
+ // Then give up...
+ return "?";
+}
+
+static void dump_header_info(log_t* log) {
+ char fingerprint[PROPERTY_VALUE_MAX];
+ char revision[PROPERTY_VALUE_MAX];
+
+ property_get("ro.build.fingerprint", fingerprint, "unknown");
+ property_get("ro.revision", revision, "unknown");
+
+ _LOG(log, logtype::HEADER, "Build fingerprint: '%s'\n", fingerprint);
+ _LOG(log, logtype::HEADER, "Revision: '%s'\n", revision);
+ _LOG(log, logtype::HEADER, "ABI: '%s'\n", ABI_STRING);
+}
+
+static void dump_signal_info(log_t* log, pid_t tid, int signal, int si_code) {
+ siginfo_t si;
+ memset(&si, 0, sizeof(si));
+ if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si) == -1) {
+ ALOGE("cannot get siginfo: %s\n", strerror(errno));
+ return;
+ }
+
+ // bionic has to re-raise some signals, which overwrites the si_code with SI_TKILL.
+ si.si_code = si_code;
+
+ char addr_desc[32]; // ", fault addr 0x1234"
+ if (signal_has_si_addr(signal)) {
+ snprintf(addr_desc, sizeof(addr_desc), "%p", si.si_addr);
+ } else {
+ snprintf(addr_desc, sizeof(addr_desc), "--------");
+ }
+
+ _LOG(log, logtype::HEADER, "signal %d (%s), code %d (%s), fault addr %s\n",
+ signal, get_signame(signal), si.si_code, get_sigcode(signal, si.si_code), addr_desc);
+}
+
+static void dump_thread_info(log_t* log, pid_t pid, pid_t tid) {
+ char path[64];
+ char threadnamebuf[1024];
+ char* threadname = NULL;
+ FILE *fp;
+
+ snprintf(path, sizeof(path), "/proc/%d/comm", tid);
+ if ((fp = fopen(path, "r"))) {
+ threadname = fgets(threadnamebuf, sizeof(threadnamebuf), fp);
+ fclose(fp);
+ if (threadname) {
+ size_t len = strlen(threadname);
+ if (len && threadname[len - 1] == '\n') {
+ threadname[len - 1] = '\0';
+ }
+ }
+ }
+ // Blacklist logd, logd.reader, logd.writer, logd.auditd, logd.control ...
+ static const char logd[] = "logd";
+ if (!strncmp(threadname, logd, sizeof(logd) - 1)
+ && (!threadname[sizeof(logd) - 1] || (threadname[sizeof(logd) - 1] == '.'))) {
+ log->should_retrieve_logcat = false;
+ }
+
+ char procnamebuf[1024];
+ char* procname = NULL;
+
+ snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
+ if ((fp = fopen(path, "r"))) {
+ procname = fgets(procnamebuf, sizeof(procnamebuf), fp);
+ fclose(fp);
+ }
+
+ _LOG(log, logtype::HEADER, "pid: %d, tid: %d, name: %s >>> %s <<<\n", pid, tid,
+ threadname ? threadname : "UNKNOWN", procname ? procname : "UNKNOWN");
+}
+
+static void dump_stack_segment(
+ Backtrace* backtrace, log_t* log, uintptr_t* sp, size_t words, int label) {
+ // Read the data all at once.
+ word_t stack_data[words];
+ size_t bytes_read = backtrace->Read(*sp, reinterpret_cast<uint8_t*>(&stack_data[0]), sizeof(word_t) * words);
+ words = bytes_read / sizeof(word_t);
+ std::string line;
+ for (size_t i = 0; i < words; i++) {
+ line = " ";
+ if (i == 0 && label >= 0) {
+ // Print the label once.
+ line += android::base::StringPrintf("#%02d ", label);
+ } else {
+ line += " ";
+ }
+ line += android::base::StringPrintf("%" PRIPTR " %" PRIPTR, *sp, stack_data[i]);
+
+ backtrace_map_t map;
+ backtrace->FillInMap(stack_data[i], &map);
+ if (BacktraceMap::IsValid(map) && !map.name.empty()) {
+ line += " " + map.name;
+ uintptr_t offset = 0;
+ std::string func_name(backtrace->GetFunctionName(stack_data[i], &offset));
+ if (!func_name.empty()) {
+ line += " (" + func_name;
+ if (offset) {
+ line += android::base::StringPrintf("+%" PRIuPTR, offset);
+ }
+ line += ')';
+ }
+ }
+ _LOG(log, logtype::STACK, "%s\n", line.c_str());
+
+ *sp += sizeof(word_t);
+ }
+}
+
+static void dump_stack(Backtrace* backtrace, log_t* log) {
+ size_t first = 0, last;
+ for (size_t i = 0; i < backtrace->NumFrames(); i++) {
+ const backtrace_frame_data_t* frame = backtrace->GetFrame(i);
+ if (frame->sp) {
+ if (!first) {
+ first = i+1;
+ }
+ last = i;
+ }
+ }
+ if (!first) {
+ return;
+ }
+ first--;
+
+ // Dump a few words before the first frame.
+ word_t sp = backtrace->GetFrame(first)->sp - STACK_WORDS * sizeof(word_t);
+ dump_stack_segment(backtrace, log, &sp, STACK_WORDS, -1);
+
+ // Dump a few words from all successive frames.
+ // Only log the first 3 frames, put the rest in the tombstone.
+ for (size_t i = first; i <= last; i++) {
+ const backtrace_frame_data_t* frame = backtrace->GetFrame(i);
+ if (sp != frame->sp) {
+ _LOG(log, logtype::STACK, " ........ ........\n");
+ sp = frame->sp;
+ }
+ if (i == last) {
+ dump_stack_segment(backtrace, log, &sp, STACK_WORDS, i);
+ if (sp < frame->sp + frame->stack_size) {
+ _LOG(log, logtype::STACK, " ........ ........\n");
+ }
+ } else {
+ size_t words = frame->stack_size / sizeof(word_t);
+ if (words == 0) {
+ words = 1;
+ } else if (words > STACK_WORDS) {
+ words = STACK_WORDS;
+ }
+ dump_stack_segment(backtrace, log, &sp, words, i);
+ }
+ }
+}
+
+static std::string get_addr_string(uintptr_t addr) {
+ std::string addr_str;
+#if defined(__LP64__)
+ addr_str = android::base::StringPrintf("%08x'%08x",
+ static_cast<uint32_t>(addr >> 32),
+ static_cast<uint32_t>(addr & 0xffffffff));
+#else
+ addr_str = android::base::StringPrintf("%08x", addr);
+#endif
+ return addr_str;
+}
+
+static void dump_all_maps(Backtrace* backtrace, BacktraceMap* map, log_t* log, pid_t tid) {
+ bool print_fault_address_marker = false;
+ uintptr_t addr = 0;
+ siginfo_t si;
+ memset(&si, 0, sizeof(si));
+ if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si) != -1) {
+ print_fault_address_marker = signal_has_si_addr(si.si_signo);
+ addr = reinterpret_cast<uintptr_t>(si.si_addr);
+ } else {
+ ALOGE("Cannot get siginfo for %d: %s\n", tid, strerror(errno));
+ }
+
+ _LOG(log, logtype::MAPS, "\n");
+ if (!print_fault_address_marker) {
+ _LOG(log, logtype::MAPS, "memory map:\n");
+ } else {
+ _LOG(log, logtype::MAPS, "memory map: (fault address prefixed with --->)\n");
+ if (map->begin() != map->end() && addr < map->begin()->start) {
+ _LOG(log, logtype::MAPS, "--->Fault address falls at %s before any mapped regions\n",
+ get_addr_string(addr).c_str());
+ print_fault_address_marker = false;
+ }
+ }
+
+ std::string line;
+ for (BacktraceMap::const_iterator it = map->begin(); it != map->end(); ++it) {
+ line = " ";
+ if (print_fault_address_marker) {
+ if (addr < it->start) {
+ _LOG(log, logtype::MAPS, "--->Fault address falls at %s between mapped regions\n",
+ get_addr_string(addr).c_str());
+ print_fault_address_marker = false;
+ } else if (addr >= it->start && addr < it->end) {
+ line = "--->";
+ print_fault_address_marker = false;
+ }
+ }
+ line += get_addr_string(it->start) + '-' + get_addr_string(it->end - 1) + ' ';
+ if (it->flags & PROT_READ) {
+ line += 'r';
+ } else {
+ line += '-';
+ }
+ if (it->flags & PROT_WRITE) {
+ line += 'w';
+ } else {
+ line += '-';
+ }
+ if (it->flags & PROT_EXEC) {
+ line += 'x';
+ } else {
+ line += '-';
+ }
+ line += android::base::StringPrintf(" %8" PRIxPTR " %8" PRIxPTR,
+ it->offset, it->end - it->start);
+ bool space_needed = true;
+ if (it->name.length() > 0) {
+ space_needed = false;
+ line += " " + it->name;
+ std::string build_id;
+ if ((it->flags & PROT_READ) && elf_get_build_id(backtrace, it->start, &build_id)) {
+ line += " (BuildId: " + build_id + ")";
+ }
+ }
+ if (it->load_base != 0) {
+ if (space_needed) {
+ line += ' ';
+ }
+ line += android::base::StringPrintf(" (load base 0x%" PRIxPTR ")", it->load_base);
+ }
+ _LOG(log, logtype::MAPS, "%s\n", line.c_str());
+ }
+ if (print_fault_address_marker) {
+ _LOG(log, logtype::MAPS, "--->Fault address falls at %s after any mapped regions\n",
+ get_addr_string(addr).c_str());
+ }
+}
+
+static void dump_backtrace_and_stack(Backtrace* backtrace, log_t* log) {
+ if (backtrace->NumFrames()) {
+ _LOG(log, logtype::BACKTRACE, "\nbacktrace:\n");
+ dump_backtrace_to_log(backtrace, log, " ");
+
+ _LOG(log, logtype::STACK, "\nstack:\n");
+ dump_stack(backtrace, log);
+ }
+}
+
+// Return true if some thread is not detached cleanly
+static bool dump_sibling_thread_report(
+ log_t* log, pid_t pid, pid_t tid, int* total_sleep_time_usec, BacktraceMap* map) {
+ char task_path[64];
+
+ snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid);
+
+ DIR* d = opendir(task_path);
+ // Bail early if the task directory cannot be opened
+ if (d == NULL) {
+ ALOGE("Cannot open /proc/%d/task\n", pid);
+ return false;
+ }
+
+ bool detach_failed = false;
+ struct dirent* de;
+ while ((de = readdir(d)) != NULL) {
+ // Ignore "." and ".."
+ if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
+ continue;
+ }
+
+ // The main thread at fault has been handled individually
+ char* end;
+ pid_t new_tid = strtoul(de->d_name, &end, 10);
+ if (*end || new_tid == tid) {
+ continue;
+ }
+
+ // Skip this thread if cannot ptrace it
+ if (ptrace(PTRACE_ATTACH, new_tid, 0, 0) < 0) {
+ ALOGE("ptrace attach to %d failed: %s\n", new_tid, strerror(errno));
+ continue;
+ }
+
+ if (wait_for_sigstop(new_tid, total_sleep_time_usec, &detach_failed) == -1) {
+ continue;
+ }
+
+ log->current_tid = new_tid;
+ _LOG(log, logtype::THREAD, "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n");
+ dump_thread_info(log, pid, new_tid);
+
+ dump_registers(log, new_tid);
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, new_tid, map));
+ if (backtrace->Unwind(0)) {
+ dump_backtrace_and_stack(backtrace.get(), log);
+ } else {
+ ALOGE("Unwind of sibling failed: pid = %d, tid = %d", pid, new_tid);
+ }
+
+ log->current_tid = log->crashed_tid;
+
+ if (ptrace(PTRACE_DETACH, new_tid, 0, 0) != 0) {
+ ALOGE("ptrace detach from %d failed: %s\n", new_tid, strerror(errno));
+ detach_failed = true;
+ }
+ }
+
+ closedir(d);
+ return detach_failed;
+}
+
+// Reads the contents of the specified log device, filters out the entries
+// that don't match the specified pid, and writes them to the tombstone file.
+//
+// If "tail" is non-zero, log the last "tail" number of lines.
+static EventTagMap* g_eventTagMap = NULL;
+
+static void dump_log_file(
+ log_t* log, pid_t pid, const char* filename, unsigned int tail) {
+ bool first = true;
+ struct logger_list* logger_list;
+
+ if (!log->should_retrieve_logcat) {
+ return;
+ }
+
+ logger_list = android_logger_list_open(
+ android_name_to_log_id(filename), ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, tail, pid);
+
+ if (!logger_list) {
+ ALOGE("Unable to open %s: %s\n", filename, strerror(errno));
+ return;
+ }
+
+ struct log_msg log_entry;
+
+ while (true) {
+ ssize_t actual = android_logger_list_read(logger_list, &log_entry);
+ struct logger_entry* entry;
+
+ if (actual < 0) {
+ if (actual == -EINTR) {
+ // interrupted by signal, retry
+ continue;
+ } else if (actual == -EAGAIN) {
+ // non-blocking EOF; we're done
+ break;
+ } else {
+ ALOGE("Error while reading log: %s\n", strerror(-actual));
+ break;
+ }
+ } else if (actual == 0) {
+ ALOGE("Got zero bytes while reading log: %s\n", strerror(errno));
+ break;
+ }
+
+ // NOTE: if you ALOGV something here, this will spin forever,
+ // because you will be writing as fast as you're reading. Any
+ // high-frequency debug diagnostics should just be written to
+ // the tombstone file.
+
+ entry = &log_entry.entry_v1;
+
+ if (first) {
+ _LOG(log, logtype::LOGS, "--------- %slog %s\n",
+ tail ? "tail end of " : "", filename);
+ first = false;
+ }
+
+ // Msg format is: <priority:1><tag:N>\0<message:N>\0
+ //
+ // We want to display it in the same format as "logcat -v threadtime"
+ // (although in this case the pid is redundant).
+ static const char* kPrioChars = "!.VDIWEFS";
+ unsigned hdr_size = log_entry.entry.hdr_size;
+ if (!hdr_size) {
+ hdr_size = sizeof(log_entry.entry_v1);
+ }
+ char* msg = reinterpret_cast<char*>(log_entry.buf) + hdr_size;
+
+ char timeBuf[32];
+ time_t sec = static_cast<time_t>(entry->sec);
+ struct tm tmBuf;
+ struct tm* ptm;
+ ptm = localtime_r(&sec, &tmBuf);
+ strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm);
+
+ if (log_entry.id() == LOG_ID_EVENTS) {
+ if (!g_eventTagMap) {
+ g_eventTagMap = android_openEventTagMap(EVENT_TAG_MAP_FILE);
+ }
+ AndroidLogEntry e;
+ char buf[512];
+ android_log_processBinaryLogBuffer(entry, &e, g_eventTagMap, buf, sizeof(buf));
+ _LOG(log, logtype::LOGS, "%s.%03d %5d %5d %c %-8s: %s\n",
+ timeBuf, entry->nsec / 1000000, entry->pid, entry->tid,
+ 'I', e.tag, e.message);
+ continue;
+ }
+
+ unsigned char prio = msg[0];
+ char* tag = msg + 1;
+ msg = tag + strlen(tag) + 1;
+
+ // consume any trailing newlines
+ char* nl = msg + strlen(msg) - 1;
+ while (nl >= msg && *nl == '\n') {
+ *nl-- = '\0';
+ }
+
+ char prioChar = (prio < strlen(kPrioChars) ? kPrioChars[prio] : '?');
+
+ // Look for line breaks ('\n') and display each text line
+ // on a separate line, prefixed with the header, like logcat does.
+ do {
+ nl = strchr(msg, '\n');
+ if (nl) {
+ *nl = '\0';
+ ++nl;
+ }
+
+ _LOG(log, logtype::LOGS, "%s.%03d %5d %5d %c %-8s: %s\n",
+ timeBuf, entry->nsec / 1000000, entry->pid, entry->tid,
+ prioChar, tag, msg);
+ } while ((msg = nl));
+ }
+
+ android_logger_list_free(logger_list);
+}
+
+// Dumps the logs generated by the specified pid to the tombstone, from both
+// "system" and "main" log devices. Ideally we'd interleave the output.
+static void dump_logs(log_t* log, pid_t pid, unsigned int tail) {
+ dump_log_file(log, pid, "system", tail);
+ dump_log_file(log, pid, "main", tail);
+}
+
+static void dump_abort_message(Backtrace* backtrace, log_t* log, uintptr_t address) {
+ if (address == 0) {
+ return;
+ }
+
+ address += sizeof(size_t); // Skip the buffer length.
+
+ char msg[512];
+ memset(msg, 0, sizeof(msg));
+ char* p = &msg[0];
+ while (p < &msg[sizeof(msg)]) {
+ word_t data;
+ size_t len = sizeof(word_t);
+ if (!backtrace->ReadWord(address, &data)) {
+ break;
+ }
+ address += sizeof(word_t);
+
+ while (len > 0 && (*p++ = (data >> (sizeof(word_t) - len) * 8) & 0xff) != 0)
+ len--;
+ }
+ msg[sizeof(msg) - 1] = '\0';
+
+ _LOG(log, logtype::HEADER, "Abort message: '%s'\n", msg);
+}
+
+// Dumps all information about the specified pid to the tombstone.
+static bool dump_crash(log_t* log, pid_t pid, pid_t tid, int signal, int si_code,
+ uintptr_t abort_msg_address, bool dump_sibling_threads,
+ int* total_sleep_time_usec) {
+ // don't copy log messages to tombstone unless this is a dev device
+ char value[PROPERTY_VALUE_MAX];
+ property_get("ro.debuggable", value, "0");
+ bool want_logs = (value[0] == '1');
+
+ if (log->amfd >= 0) {
+ // Activity Manager protocol: binary 32-bit network-byte-order ints for the
+ // pid and signal number, followed by the raw text of the dump, culminating
+ // in a zero byte that marks end-of-data.
+ uint32_t datum = htonl(pid);
+ TEMP_FAILURE_RETRY( write(log->amfd, &datum, 4) );
+ datum = htonl(signal);
+ TEMP_FAILURE_RETRY( write(log->amfd, &datum, 4) );
+ }
+
+ _LOG(log, logtype::HEADER,
+ "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
+ dump_header_info(log);
+ dump_thread_info(log, pid, tid);
+
+ if (signal) {
+ dump_signal_info(log, tid, signal, si_code);
+ }
+
+ std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(pid));
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid, map.get()));
+ dump_abort_message(backtrace.get(), log, abort_msg_address);
+ dump_registers(log, tid);
+ if (backtrace->Unwind(0)) {
+ dump_backtrace_and_stack(backtrace.get(), log);
+ } else {
+ ALOGE("Unwind failed: pid = %d, tid = %d", pid, tid);
+ }
+ dump_memory_and_code(log, backtrace.get());
+ if (map.get() != nullptr) {
+ dump_all_maps(backtrace.get(), map.get(), log, tid);
+ }
+
+ if (want_logs) {
+ dump_logs(log, pid, 5);
+ }
+
+ bool detach_failed = false;
+ if (dump_sibling_threads) {
+ detach_failed = dump_sibling_thread_report(log, pid, tid, total_sleep_time_usec, map.get());
+ }
+
+ if (want_logs) {
+ dump_logs(log, pid, 0);
+ }
+
+ // send EOD to the Activity Manager, then wait for its ack to avoid racing ahead
+ // and killing the target out from under it
+ if (log->amfd >= 0) {
+ uint8_t eodMarker = 0;
+ TEMP_FAILURE_RETRY( write(log->amfd, &eodMarker, 1) );
+ // 3 sec timeout reading the ack; we're fine if that happens
+ TEMP_FAILURE_RETRY( read(log->amfd, &eodMarker, 1) );
+ }
+
+ return detach_failed;
+}
+
+// find_and_open_tombstone - find an available tombstone slot, if any, of the
+// form tombstone_XX where XX is 00 to MAX_TOMBSTONES-1, inclusive. If no
+// file is available, we reuse the least-recently-modified file.
+//
+// Returns the path of the tombstone file, allocated using malloc(). Caller must free() it.
+static char* find_and_open_tombstone(int* fd) {
+ // In a single pass, find an available slot and, in case none
+ // exist, find and record the least-recently-modified file.
+ char path[128];
+ int oldest = -1;
+ struct stat oldest_sb;
+ for (int i = 0; i < MAX_TOMBSTONES; i++) {
+ snprintf(path, sizeof(path), TOMBSTONE_TEMPLATE, i);
+
+ struct stat sb;
+ if (!stat(path, &sb)) {
+ if (oldest < 0 || sb.st_mtime < oldest_sb.st_mtime) {
+ oldest = i;
+ oldest_sb.st_mtime = sb.st_mtime;
+ }
+ continue;
+ }
+ if (errno != ENOENT)
+ continue;
+
+ *fd = open(path, O_CREAT | O_EXCL | O_WRONLY | O_NOFOLLOW | O_CLOEXEC, 0600);
+ if (*fd < 0)
+ continue; // raced ?
+
+ fchown(*fd, AID_SYSTEM, AID_SYSTEM);
+ return strdup(path);
+ }
+
+ if (oldest < 0) {
+ ALOGE("Failed to find a valid tombstone, default to using tombstone 0.\n");
+ oldest = 0;
+ }
+
+ // we didn't find an available file, so we clobber the oldest one
+ snprintf(path, sizeof(path), TOMBSTONE_TEMPLATE, oldest);
+ *fd = open(path, O_CREAT | O_TRUNC | O_WRONLY | O_NOFOLLOW | O_CLOEXEC, 0600);
+ if (*fd < 0) {
+ ALOGE("failed to open tombstone file '%s': %s\n", path, strerror(errno));
+ return NULL;
+ }
+ fchown(*fd, AID_SYSTEM, AID_SYSTEM);
+ return strdup(path);
+}
+
+static int activity_manager_connect() {
+ int amfd = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (amfd >= 0) {
+ struct sockaddr_un address;
+ int err;
+
+ memset(&address, 0, sizeof(address));
+ address.sun_family = AF_UNIX;
+ strncpy(address.sun_path, NCRASH_SOCKET_PATH, sizeof(address.sun_path));
+ err = TEMP_FAILURE_RETRY(connect(
+ amfd, reinterpret_cast<struct sockaddr*>(&address), sizeof(address)));
+ if (!err) {
+ struct timeval tv;
+ memset(&tv, 0, sizeof(tv));
+ tv.tv_sec = 1; // tight leash
+ err = setsockopt(amfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
+ if (!err) {
+ tv.tv_sec = 3; // 3 seconds on handshake read
+ err = setsockopt(amfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
+ }
+ }
+ if (err) {
+ close(amfd);
+ amfd = -1;
+ }
+ }
+
+ return amfd;
+}
+
+char* engrave_tombstone(pid_t pid, pid_t tid, int signal, int original_si_code,
+ uintptr_t abort_msg_address, bool dump_sibling_threads,
+ bool* detach_failed, int* total_sleep_time_usec) {
+
+ log_t log;
+ log.current_tid = tid;
+ log.crashed_tid = tid;
+
+ if ((mkdir(TOMBSTONE_DIR, 0755) == -1) && (errno != EEXIST)) {
+ ALOGE("failed to create %s: %s\n", TOMBSTONE_DIR, strerror(errno));
+ }
+
+ if (chown(TOMBSTONE_DIR, AID_SYSTEM, AID_SYSTEM) == -1) {
+ ALOGE("failed to change ownership of %s: %s\n", TOMBSTONE_DIR, strerror(errno));
+ }
+
+ int fd = -1;
+ char* path = NULL;
+ if (selinux_android_restorecon(TOMBSTONE_DIR, 0) == 0) {
+ path = find_and_open_tombstone(&fd);
+ } else {
+ ALOGE("Failed to restore security context, not writing tombstone.\n");
+ }
+
+ if (fd < 0) {
+ ALOGE("Skipping tombstone write, nothing to do.\n");
+ *detach_failed = false;
+ return NULL;
+ }
+
+ log.tfd = fd;
+ // Preserve amfd since it can be modified through the calls below without
+ // being closed.
+ int amfd = activity_manager_connect();
+ log.amfd = amfd;
+ *detach_failed = dump_crash(&log, pid, tid, signal, original_si_code, abort_msg_address,
+ dump_sibling_threads, total_sleep_time_usec);
+
+ _LOG(&log, logtype::BACKTRACE, "\nTombstone written to: %s\n", path);
+
+ // Either of these file descriptors can be -1, any error is ignored.
+ close(amfd);
+ close(fd);
+
+ return path;
+}
diff --git a/debuggerd/tombstone.h b/debuggerd/tombstone.h
new file mode 100644
index 0000000..7e2b2fe
--- /dev/null
+++ b/debuggerd/tombstone.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DEBUGGERD_TOMBSTONE_H
+#define _DEBUGGERD_TOMBSTONE_H
+
+#include <stddef.h>
+#include <stdbool.h>
+#include <sys/types.h>
+
+/* Creates a tombstone file and writes the crash dump to it.
+ * Returns the path of the tombstone, which must be freed using free(). */
+char* engrave_tombstone(pid_t pid, pid_t tid, int signal, int original_si_code,
+ uintptr_t abort_msg_address,
+ bool dump_sibling_threads, bool* detach_failed,
+ int* total_sleep_time_usec);
+
+#endif // _DEBUGGERD_TOMBSTONE_H
diff --git a/debuggerd/utility.cpp b/debuggerd/utility.cpp
new file mode 100644
index 0000000..f5d6ec1
--- /dev/null
+++ b/debuggerd/utility.cpp
@@ -0,0 +1,226 @@
+/*
+ * 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 "DEBUG"
+
+#include "utility.h"
+
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ptrace.h>
+#include <sys/wait.h>
+
+#include <backtrace/Backtrace.h>
+#include <base/file.h>
+#include <base/stringprintf.h>
+#include <log/log.h>
+
+const int SLEEP_TIME_USEC = 50000; // 0.05 seconds
+const int MAX_TOTAL_SLEEP_USEC = 10000000; // 10 seconds
+
+// Whitelist output desired in the logcat output.
+bool is_allowed_in_logcat(enum logtype ltype) {
+ if ((ltype == HEADER)
+ || (ltype == REGISTERS)
+ || (ltype == BACKTRACE)) {
+ return true;
+ }
+ return false;
+}
+
+void _LOG(log_t* log, enum logtype ltype, const char* fmt, ...) {
+ bool write_to_tombstone = (log->tfd != -1);
+ bool write_to_logcat = is_allowed_in_logcat(ltype)
+ && log->crashed_tid != -1
+ && log->current_tid != -1
+ && (log->crashed_tid == log->current_tid);
+ bool write_to_activitymanager = (log->amfd != -1);
+
+ char buf[512];
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ size_t len = strlen(buf);
+ if (len <= 0) {
+ return;
+ }
+
+ if (write_to_tombstone) {
+ TEMP_FAILURE_RETRY(write(log->tfd, buf, len));
+ }
+
+ if (write_to_logcat) {
+ __android_log_buf_write(LOG_ID_CRASH, ANDROID_LOG_FATAL, LOG_TAG, buf);
+ if (write_to_activitymanager) {
+ if (!android::base::WriteFully(log->amfd, buf, len)) {
+ // timeout or other failure on write; stop informing the activity manager
+ ALOGE("AM write failed: %s", strerror(errno));
+ log->amfd = -1;
+ }
+ }
+ }
+}
+
+int wait_for_sigstop(pid_t tid, int* total_sleep_time_usec, bool* detach_failed) {
+ bool allow_dead_tid = false;
+ for (;;) {
+ int status;
+ pid_t n = TEMP_FAILURE_RETRY(waitpid(tid, &status, __WALL | WNOHANG));
+ if (n == -1) {
+ ALOGE("waitpid failed: tid %d, %s", tid, strerror(errno));
+ break;
+ } else if (n == tid) {
+ if (WIFSTOPPED(status)) {
+ return WSTOPSIG(status);
+ } else {
+ ALOGE("unexpected waitpid response: n=%d, status=%08x\n", n, status);
+ // This is the only circumstance under which we can allow a detach
+ // to fail with ESRCH, which indicates the tid has exited.
+ allow_dead_tid = true;
+ break;
+ }
+ }
+
+ if (*total_sleep_time_usec > MAX_TOTAL_SLEEP_USEC) {
+ ALOGE("timed out waiting for stop signal: tid=%d", tid);
+ break;
+ }
+
+ usleep(SLEEP_TIME_USEC);
+ *total_sleep_time_usec += SLEEP_TIME_USEC;
+ }
+
+ if (ptrace(PTRACE_DETACH, tid, 0, 0) != 0) {
+ if (allow_dead_tid && errno == ESRCH) {
+ ALOGE("tid exited before attach completed: tid %d", tid);
+ } else {
+ *detach_failed = true;
+ ALOGE("detach failed: tid %d, %s", tid, strerror(errno));
+ }
+ }
+ return -1;
+}
+
+#define MEMORY_BYTES_TO_DUMP 256
+#define MEMORY_BYTES_PER_LINE 16
+
+void dump_memory(log_t* log, Backtrace* backtrace, uintptr_t addr, const char* fmt, ...) {
+ std::string log_msg;
+ va_list ap;
+ va_start(ap, fmt);
+ android::base::StringAppendV(&log_msg, fmt, ap);
+ va_end(ap);
+
+ // Align the address to sizeof(long) and start 32 bytes before the address.
+ addr &= ~(sizeof(long) - 1);
+ if (addr >= 4128) {
+ addr -= 32;
+ }
+
+ // Don't bother if the address looks too low, or looks too high.
+ if (addr < 4096 ||
+#if defined(__LP64__)
+ addr > 0x4000000000000000UL - MEMORY_BYTES_TO_DUMP) {
+#else
+ addr > 0xffff0000 - MEMORY_BYTES_TO_DUMP) {
+#endif
+ return;
+ }
+
+ _LOG(log, logtype::MEMORY, "\n%s\n", log_msg.c_str());
+
+ // Dump 256 bytes
+ uintptr_t data[MEMORY_BYTES_TO_DUMP/sizeof(uintptr_t)];
+ memset(data, 0, MEMORY_BYTES_TO_DUMP);
+ size_t bytes = backtrace->Read(addr, reinterpret_cast<uint8_t*>(data), sizeof(data));
+ if (bytes % sizeof(uintptr_t) != 0) {
+ // This should never happen, but just in case.
+ ALOGE("Bytes read %zu, is not a multiple of %zu", bytes, sizeof(uintptr_t));
+ bytes &= ~(sizeof(uintptr_t) - 1);
+ }
+
+ uintptr_t start = 0;
+ bool skip_2nd_read = false;
+ if (bytes == 0) {
+ // In this case, we might want to try another read at the beginning of
+ // the next page only if it's within the amount of memory we would have
+ // read.
+ size_t page_size = sysconf(_SC_PAGE_SIZE);
+ start = ((addr + (page_size - 1)) & ~(page_size - 1)) - addr;
+ if (start == 0 || start >= MEMORY_BYTES_TO_DUMP) {
+ skip_2nd_read = true;
+ }
+ }
+
+ if (bytes < MEMORY_BYTES_TO_DUMP && !skip_2nd_read) {
+ // Try to do one more read. This could happen if a read crosses a map,
+ // but the maps do not have any break between them. Or it could happen
+ // if reading from an unreadable map, but the read would cross back
+ // into a readable map. Only requires one extra read because a map has
+ // to contain at least one page, and the total number of bytes to dump
+ // is smaller than a page.
+ size_t bytes2 = backtrace->Read(addr + start + bytes, reinterpret_cast<uint8_t*>(data) + bytes,
+ sizeof(data) - bytes - start);
+ bytes += bytes2;
+ if (bytes2 > 0 && bytes % sizeof(uintptr_t) != 0) {
+ // This should never happen, but we'll try and continue any way.
+ ALOGE("Bytes after second read %zu, is not a multiple of %zu", bytes, sizeof(uintptr_t));
+ bytes &= ~(sizeof(uintptr_t) - 1);
+ }
+ }
+
+ // Dump the code around memory as:
+ // addr contents ascii
+ // 0000000000008d34 ef000000e8bd0090 e1b00000512fff1e ............../Q
+ // 0000000000008d44 ea00b1f9e92d0090 e3a070fcef000000 ......-..p......
+ // On 32-bit machines, there are still 16 bytes per line but addresses and
+ // words are of course presented differently.
+ uintptr_t* data_ptr = data;
+ size_t current = 0;
+ size_t total_bytes = start + bytes;
+ for (size_t line = 0; line < MEMORY_BYTES_TO_DUMP / MEMORY_BYTES_PER_LINE; line++) {
+ std::string logline;
+ android::base::StringAppendF(&logline, " %" PRIPTR, addr);
+
+ addr += MEMORY_BYTES_PER_LINE;
+ std::string ascii;
+ for (size_t i = 0; i < MEMORY_BYTES_PER_LINE / sizeof(uintptr_t); i++) {
+ if (current >= start && current + sizeof(uintptr_t) <= total_bytes) {
+ android::base::StringAppendF(&logline, " %" PRIPTR, *data_ptr);
+
+ // Fill out the ascii string from the data.
+ uint8_t* ptr = reinterpret_cast<uint8_t*>(data_ptr);
+ for (size_t val = 0; val < sizeof(uintptr_t); val++, ptr++) {
+ if (*ptr >= 0x20 && *ptr < 0x7f) {
+ ascii += *ptr;
+ } else {
+ ascii += '.';
+ }
+ }
+ data_ptr++;
+ } else {
+ logline += ' ' + std::string(sizeof(uintptr_t) * 2, '-');
+ ascii += std::string(sizeof(uintptr_t), '.');
+ }
+ current += sizeof(uintptr_t);
+ }
+ _LOG(log, logtype::MEMORY, "%s %s\n", logline.c_str(), ascii.c_str());
+ }
+}
diff --git a/debuggerd/utility.h b/debuggerd/utility.h
new file mode 100644
index 0000000..8bef192
--- /dev/null
+++ b/debuggerd/utility.h
@@ -0,0 +1,81 @@
+/* system/debuggerd/utility.h
+**
+** 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.
+*/
+
+#ifndef _DEBUGGERD_UTILITY_H
+#define _DEBUGGERD_UTILITY_H
+
+#include <stdbool.h>
+#include <sys/types.h>
+
+#include <backtrace/Backtrace.h>
+
+// Figure out the abi based on defined macros.
+#if defined(__arm__)
+#define ABI_STRING "arm"
+#elif defined(__aarch64__)
+#define ABI_STRING "arm64"
+#elif defined(__mips__) && !defined(__LP64__)
+#define ABI_STRING "mips"
+#elif defined(__mips__) && defined(__LP64__)
+#define ABI_STRING "mips64"
+#elif defined(__i386__)
+#define ABI_STRING "x86"
+#elif defined(__x86_64__)
+#define ABI_STRING "x86_64"
+#else
+#error "Unsupported ABI"
+#endif
+
+
+struct log_t{
+ /* tombstone file descriptor */
+ int tfd;
+ /* Activity Manager socket file descriptor */
+ int amfd;
+ // The tid of the thread that crashed.
+ pid_t crashed_tid;
+ // The tid of the thread we are currently working with.
+ pid_t current_tid;
+ // logd daemon crash, can block asking for logcat data, allow suppression.
+ bool should_retrieve_logcat;
+
+ log_t()
+ : tfd(-1), amfd(-1), crashed_tid(-1), current_tid(-1), should_retrieve_logcat(true) {}
+};
+
+// List of types of logs to simplify the logging decision in _LOG
+enum logtype {
+ HEADER,
+ THREAD,
+ REGISTERS,
+ FP_REGISTERS,
+ BACKTRACE,
+ MAPS,
+ MEMORY,
+ STACK,
+ LOGS
+};
+
+// Log information onto the tombstone.
+void _LOG(log_t* log, logtype ltype, const char *fmt, ...)
+ __attribute__ ((format(printf, 3, 4)));
+
+int wait_for_sigstop(pid_t, int*, bool*);
+
+void dump_memory(log_t* log, Backtrace* backtrace, uintptr_t addr, const char* fmt, ...);
+
+#endif // _DEBUGGERD_UTILITY_H
diff --git a/debuggerd/x86/crashglue.S b/debuggerd/x86/crashglue.S
new file mode 100644
index 0000000..59df432
--- /dev/null
+++ b/debuggerd/x86/crashglue.S
@@ -0,0 +1,15 @@
+.globl crash1
+.globl crashnostack
+
+crash1:
+ movl $0xa5a50000, %eax
+ movl $0xa5a50001, %ebx
+ movl $0xa5a50002, %ecx
+
+ movl $0, %edx
+ jmp *%edx
+
+
+crashnostack:
+ movl $0, %ebp
+ jmp *%ebp
diff --git a/debuggerd/x86/machine.cpp b/debuggerd/x86/machine.cpp
new file mode 100644
index 0000000..af10817
--- /dev/null
+++ b/debuggerd/x86/machine.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2006, 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 "DEBUG"
+
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/ptrace.h>
+
+#include <backtrace/Backtrace.h>
+#include <log/log.h>
+
+#include "machine.h"
+#include "utility.h"
+
+void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
+ struct pt_regs r;
+ if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r) == -1) {
+ ALOGE("cannot get registers: %s\n", strerror(errno));
+ return;
+ }
+
+ dump_memory(log, backtrace, static_cast<uintptr_t>(r.eax), "memory near eax:");
+ dump_memory(log, backtrace, static_cast<uintptr_t>(r.ebx), "memory near ebx:");
+ dump_memory(log, backtrace, static_cast<uintptr_t>(r.ecx), "memory near ecx:");
+ dump_memory(log, backtrace, static_cast<uintptr_t>(r.edx), "memory near edx:");
+ dump_memory(log, backtrace, static_cast<uintptr_t>(r.esi), "memory near esi:");
+ dump_memory(log, backtrace, static_cast<uintptr_t>(r.edi), "memory near edi:");
+
+ dump_memory(log, backtrace, static_cast<uintptr_t>(r.eip), "code around eip:");
+}
+
+void dump_registers(log_t* log, pid_t tid) {
+ struct pt_regs r;
+ if (ptrace(PTRACE_GETREGS, tid, 0, &r) == -1) {
+ ALOGE("cannot get registers: %s\n", strerror(errno));
+ return;
+ }
+
+ _LOG(log, logtype::REGISTERS, " eax %08lx ebx %08lx ecx %08lx edx %08lx\n",
+ r.eax, r.ebx, r.ecx, r.edx);
+ _LOG(log, logtype::REGISTERS, " esi %08lx edi %08lx\n",
+ r.esi, r.edi);
+ _LOG(log, logtype::REGISTERS, " xcs %08x xds %08x xes %08x xfs %08x xss %08x\n",
+ r.xcs, r.xds, r.xes, r.xfs, r.xss);
+ _LOG(log, logtype::REGISTERS, " eip %08lx ebp %08lx esp %08lx flags %08lx\n",
+ r.eip, r.ebp, r.esp, r.eflags);
+}
diff --git a/debuggerd/x86_64/crashglue.S b/debuggerd/x86_64/crashglue.S
new file mode 100644
index 0000000..4d2a5c0
--- /dev/null
+++ b/debuggerd/x86_64/crashglue.S
@@ -0,0 +1,15 @@
+.globl crash1
+.globl crashnostack
+
+crash1:
+ movl $0xa5a50000, %eax
+ movl $0xa5a50001, %ebx
+ movl $0xa5a50002, %ecx
+
+ movl $0, %edx
+ jmp *%rdx
+
+
+crashnostack:
+ movl $0, %ebp
+ jmp *%rbp
diff --git a/debuggerd/x86_64/machine.cpp b/debuggerd/x86_64/machine.cpp
new file mode 100644
index 0000000..fc86bc2
--- /dev/null
+++ b/debuggerd/x86_64/machine.cpp
@@ -0,0 +1,67 @@
+/*
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#define LOG_TAG "DEBUG"
+
+#include <errno.h>
+#include <stdint.h>
+#include <sys/ptrace.h>
+#include <string.h>
+#include <sys/user.h>
+
+#include <backtrace/Backtrace.h>
+#include <log/log.h>
+
+#include "machine.h"
+#include "utility.h"
+
+void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
+ struct user_regs_struct r;
+ if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r) == -1) {
+ ALOGE("cannot get registers: %s\n", strerror(errno));
+ return;
+ }
+
+ dump_memory(log, backtrace, static_cast<uintptr_t>(r.rax), "memory near rax:");
+ dump_memory(log, backtrace, static_cast<uintptr_t>(r.rbx), "memory near rbx:");
+ dump_memory(log, backtrace, static_cast<uintptr_t>(r.rcx), "memory near rcx:");
+ dump_memory(log, backtrace, static_cast<uintptr_t>(r.rdx), "memory near rdx:");
+ dump_memory(log, backtrace, static_cast<uintptr_t>(r.rsi), "memory near rsi:");
+ dump_memory(log, backtrace, static_cast<uintptr_t>(r.rdi), "memory near rdi:");
+
+ dump_memory(log, backtrace, static_cast<uintptr_t>(r.rip), "code around rip:");
+}
+
+void dump_registers(log_t* log, pid_t tid) {
+ struct user_regs_struct r;
+ if (ptrace(PTRACE_GETREGS, tid, 0, &r) == -1) {
+ ALOGE("cannot get registers: %s\n", strerror(errno));
+ return;
+ }
+
+ _LOG(log, logtype::REGISTERS, " rax %016lx rbx %016lx rcx %016lx rdx %016lx\n",
+ r.rax, r.rbx, r.rcx, r.rdx);
+ _LOG(log, logtype::REGISTERS, " rsi %016lx rdi %016lx\n",
+ r.rsi, r.rdi);
+ _LOG(log, logtype::REGISTERS, " r8 %016lx r9 %016lx r10 %016lx r11 %016lx\n",
+ r.r8, r.r9, r.r10, r.r11);
+ _LOG(log, logtype::REGISTERS, " r12 %016lx r13 %016lx r14 %016lx r15 %016lx\n",
+ r.r12, r.r13, r.r14, r.r15);
+ _LOG(log, logtype::REGISTERS, " cs %016lx ss %016lx\n",
+ r.cs, r.ss);
+ _LOG(log, logtype::REGISTERS, " rip %016lx rbp %016lx rsp %016lx eflags %016lx\n",
+ r.rip, r.rbp, r.rsp, r.eflags);
+}
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
new file mode 100644
index 0000000..ce8e15f
--- /dev/null
+++ b/fastboot/Android.mk
@@ -0,0 +1,110 @@
+# Copyright (C) 2007 Google Inc.
+#
+# 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)
+
+fastboot_version := $(shell git -C $(LOCAL_PATH) rev-parse --short=12 HEAD 2>/dev/null)-android
+
+include $(CLEAR_VARS)
+
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/../mkbootimg \
+ $(LOCAL_PATH)/../../extras/ext4_utils \
+ $(LOCAL_PATH)/../../extras/f2fs_utils
+LOCAL_SRC_FILES := protocol.cpp engine.cpp bootimg_utils.cpp fastboot.cpp util.cpp fs.cpp
+LOCAL_MODULE := fastboot
+LOCAL_MODULE_TAGS := debug
+LOCAL_CONLYFLAGS += -std=gnu99
+LOCAL_CFLAGS += -Wall -Wextra -Werror -Wunreachable-code
+
+LOCAL_CFLAGS += -DFASTBOOT_REVISION='"$(fastboot_version)"'
+
+ifeq ($(HOST_OS),linux)
+ LOCAL_SRC_FILES += usb_linux.cpp util_linux.cpp
+endif
+
+ifeq ($(HOST_OS),darwin)
+ LOCAL_SRC_FILES += usb_osx.cpp util_osx.cpp
+ LOCAL_LDLIBS += -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
+ LOCAL_CFLAGS += -Wno-unused-parameter
+endif
+
+ifeq ($(HOST_OS),windows)
+ LOCAL_SRC_FILES += usb_windows.cpp util_windows.cpp
+ EXTRA_STATIC_LIBS := AdbWinApi
+ ifneq ($(strip $(USE_CYGWIN)),)
+ # Pure cygwin case
+ LOCAL_LDLIBS += -lpthread
+ endif
+ ifneq ($(strip $(USE_MINGW)),)
+ # MinGW under Linux case
+ LOCAL_LDLIBS += -lws2_32
+ USE_SYSDEPS_WIN32 := 1
+ endif
+ LOCAL_C_INCLUDES += development/host/windows/usb/api
+endif
+
+LOCAL_STATIC_LIBRARIES := \
+ $(EXTRA_STATIC_LIBS) \
+ libziparchive-host \
+ libext4_utils_host \
+ libsparse_host \
+ libutils \
+ liblog \
+ libz \
+ libbase
+
+ifneq ($(HOST_OS),windows)
+LOCAL_STATIC_LIBRARIES += libselinux
+endif # HOST_OS != windows
+
+ifeq ($(HOST_OS),linux)
+# libf2fs_dlutils_host will dlopen("libf2fs_fmt_host_dyn")
+LOCAL_CFLAGS += -DUSE_F2FS
+LOCAL_LDFLAGS += -ldl -rdynamic -Wl,-rpath,.
+LOCAL_REQUIRED_MODULES := libf2fs_fmt_host_dyn
+# The following libf2fs_* are from system/extras/f2fs_utils,
+# and do not use code in external/f2fs-tools.
+LOCAL_STATIC_LIBRARIES += libf2fs_utils_host libf2fs_ioutils_host libf2fs_dlutils_host
+endif
+
+# libc++ not available on windows yet
+ifneq ($(HOST_OS),windows)
+ LOCAL_CXX_STL := libc++_static
+endif
+
+# Don't add anything here, we don't want additional shared dependencies
+# on the host fastboot tool, and shared libraries that link against libc++
+# will violate ODR
+LOCAL_SHARED_LIBRARIES :=
+
+include $(BUILD_HOST_EXECUTABLE)
+
+my_dist_files := $(LOCAL_BUILT_MODULE)
+ifeq ($(HOST_OS),linux)
+my_dist_files += $(HOST_LIBRARY_PATH)/libf2fs_fmt_host_dyn$(HOST_SHLIB_SUFFIX)
+endif
+$(call dist-for-goals,dist_files sdk,$(my_dist_files))
+my_dist_files :=
+
+ifeq ($(HOST_OS),linux)
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := usbtest.cpp usb_linux.cpp util.cpp
+LOCAL_MODULE := usbtest
+LOCAL_CFLAGS := -Werror
+include $(BUILD_HOST_EXECUTABLE)
+endif
+
+ifeq ($(HOST_OS),windows)
+$(LOCAL_INSTALLED_MODULE): $(HOST_OUT_EXECUTABLES)/AdbWinApi.dll
+endif
diff --git a/fastboot/bootimg_utils.cpp b/fastboot/bootimg_utils.cpp
new file mode 100644
index 0000000..d8905a6
--- /dev/null
+++ b/fastboot/bootimg_utils.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "bootimg_utils.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+void bootimg_set_cmdline(boot_img_hdr *h, const char *cmdline)
+{
+ strcpy((char*) h->cmdline, cmdline);
+}
+
+boot_img_hdr *mkbootimg(void *kernel, unsigned kernel_size, unsigned kernel_offset,
+ void *ramdisk, unsigned ramdisk_size, unsigned ramdisk_offset,
+ void *second, unsigned second_size, unsigned second_offset,
+ unsigned page_size, unsigned base, unsigned tags_offset,
+ unsigned *bootimg_size)
+{
+ unsigned kernel_actual;
+ unsigned ramdisk_actual;
+ unsigned second_actual;
+ unsigned page_mask;
+
+ page_mask = page_size - 1;
+
+ kernel_actual = (kernel_size + page_mask) & (~page_mask);
+ ramdisk_actual = (ramdisk_size + page_mask) & (~page_mask);
+ second_actual = (second_size + page_mask) & (~page_mask);
+
+ *bootimg_size = page_size + kernel_actual + ramdisk_actual + second_actual;
+
+ boot_img_hdr* hdr = reinterpret_cast<boot_img_hdr*>(calloc(*bootimg_size, 1));
+ if (hdr == 0) {
+ return hdr;
+ }
+
+ memcpy(hdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE);
+
+ hdr->kernel_size = kernel_size;
+ hdr->ramdisk_size = ramdisk_size;
+ hdr->second_size = second_size;
+
+ hdr->kernel_addr = base + kernel_offset;
+ hdr->ramdisk_addr = base + ramdisk_offset;
+ hdr->second_addr = base + second_offset;
+ hdr->tags_addr = base + tags_offset;
+
+ hdr->page_size = page_size;
+
+
+ memcpy(hdr->magic + page_size,
+ kernel, kernel_size);
+ memcpy(hdr->magic + page_size + kernel_actual,
+ ramdisk, ramdisk_size);
+ memcpy(hdr->magic + page_size + kernel_actual + ramdisk_actual,
+ second, second_size);
+ return hdr;
+}
diff --git a/fastboot/bootimg_utils.h b/fastboot/bootimg_utils.h
new file mode 100644
index 0000000..b1a86cd
--- /dev/null
+++ b/fastboot/bootimg_utils.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _FASTBOOT_BOOTIMG_UTILS_H_
+#define _FASTBOOT_BOOTIMG_UTILS_H_
+
+#include <bootimg.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+void bootimg_set_cmdline(boot_img_hdr *h, const char *cmdline);
+boot_img_hdr *mkbootimg(void *kernel, unsigned kernel_size, unsigned kernel_offset,
+ void *ramdisk, unsigned ramdisk_size, unsigned ramdisk_offset,
+ void *second, unsigned second_size, unsigned second_offset,
+ unsigned page_size, unsigned base, unsigned tags_offset,
+ unsigned *bootimg_size);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/fastboot/engine.cpp b/fastboot/engine.cpp
new file mode 100644
index 0000000..66b8140
--- /dev/null
+++ b/fastboot/engine.cpp
@@ -0,0 +1,406 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "fastboot.h"
+#include "fs.h"
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#ifdef USE_MINGW
+#include <fcntl.h>
+#else
+#include <sys/mman.h>
+#endif
+
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
+
+#define OP_DOWNLOAD 1
+#define OP_COMMAND 2
+#define OP_QUERY 3
+#define OP_NOTICE 4
+#define OP_DOWNLOAD_SPARSE 5
+#define OP_WAIT_FOR_DISCONNECT 6
+
+typedef struct Action Action;
+
+#define CMD_SIZE 64
+
+struct Action
+{
+ unsigned op;
+ Action *next;
+
+ char cmd[CMD_SIZE];
+ const char *prod;
+ void *data;
+ unsigned size;
+
+ const char *msg;
+ int (*func)(Action* a, int status, const char* resp);
+
+ double start;
+};
+
+static Action *action_list = 0;
+static Action *action_last = 0;
+
+
+
+
+int fb_getvar(struct usb_handle *usb, char *response, const char *fmt, ...)
+{
+ char cmd[CMD_SIZE] = "getvar:";
+ int getvar_len = strlen(cmd);
+ va_list args;
+
+ response[FB_RESPONSE_SZ] = '\0';
+ va_start(args, fmt);
+ vsnprintf(cmd + getvar_len, sizeof(cmd) - getvar_len, fmt, args);
+ va_end(args);
+ cmd[CMD_SIZE - 1] = '\0';
+ return fb_command_response(usb, cmd, response);
+}
+
+
+/* Return true if this partition is supported by the fastboot format command.
+ * It is also used to determine if we should first erase a partition before
+ * flashing it with an ext4 filesystem. See needs_erase()
+ *
+ * Not all devices report the filesystem type, so don't report any errors,
+ * just return false.
+ */
+int fb_format_supported(usb_handle *usb, const char *partition, const char *type_override)
+{
+ char fs_type[FB_RESPONSE_SZ + 1] = {0,};
+ int status;
+
+ if (type_override) {
+ return !!fs_get_generator(type_override);
+ }
+ status = fb_getvar(usb, fs_type, "partition-type:%s", partition);
+ if (status) {
+ return 0;
+ }
+ return !!fs_get_generator(fs_type);
+}
+
+static int cb_default(Action* a, int status, const char* resp) {
+ if (status) {
+ fprintf(stderr,"FAILED (%s)\n", resp);
+ } else {
+ double split = now();
+ fprintf(stderr,"OKAY [%7.3fs]\n", (split - a->start));
+ a->start = split;
+ }
+ return status;
+}
+
+static Action *queue_action(unsigned op, const char *fmt, ...)
+{
+ va_list ap;
+ size_t cmdsize;
+
+ Action* a = reinterpret_cast<Action*>(calloc(1, sizeof(Action)));
+ if (a == nullptr) die("out of memory");
+
+ va_start(ap, fmt);
+ cmdsize = vsnprintf(a->cmd, sizeof(a->cmd), fmt, ap);
+ va_end(ap);
+
+ if (cmdsize >= sizeof(a->cmd)) {
+ free(a);
+ die("Command length (%d) exceeds maximum size (%d)", cmdsize, sizeof(a->cmd));
+ }
+
+ if (action_last) {
+ action_last->next = a;
+ } else {
+ action_list = a;
+ }
+ action_last = a;
+ a->op = op;
+ a->func = cb_default;
+
+ a->start = -1;
+
+ return a;
+}
+
+void fb_queue_erase(const char *ptn)
+{
+ Action *a;
+ a = queue_action(OP_COMMAND, "erase:%s", ptn);
+ a->msg = mkmsg("erasing '%s'", ptn);
+}
+
+void fb_queue_flash(const char *ptn, void *data, unsigned sz)
+{
+ Action *a;
+
+ a = queue_action(OP_DOWNLOAD, "");
+ a->data = data;
+ a->size = sz;
+ a->msg = mkmsg("sending '%s' (%d KB)", ptn, sz / 1024);
+
+ a = queue_action(OP_COMMAND, "flash:%s", ptn);
+ a->msg = mkmsg("writing '%s'", ptn);
+}
+
+void fb_queue_flash_sparse(const char *ptn, struct sparse_file *s, unsigned sz)
+{
+ Action *a;
+
+ a = queue_action(OP_DOWNLOAD_SPARSE, "");
+ a->data = s;
+ a->size = 0;
+ a->msg = mkmsg("sending sparse '%s' (%d KB)", ptn, sz / 1024);
+
+ a = queue_action(OP_COMMAND, "flash:%s", ptn);
+ a->msg = mkmsg("writing '%s'", ptn);
+}
+
+static int match(const char* str, const char** value, unsigned count) {
+ unsigned n;
+
+ for (n = 0; n < count; n++) {
+ const char *val = value[n];
+ int len = strlen(val);
+ int match;
+
+ if ((len > 1) && (val[len-1] == '*')) {
+ len--;
+ match = !strncmp(val, str, len);
+ } else {
+ match = !strcmp(val, str);
+ }
+
+ if (match) return 1;
+ }
+
+ return 0;
+}
+
+
+
+static int cb_check(Action* a, int status, const char* resp, int invert)
+{
+ const char** value = reinterpret_cast<const char**>(a->data);
+ unsigned count = a->size;
+ unsigned n;
+ int yes;
+
+ if (status) {
+ fprintf(stderr,"FAILED (%s)\n", resp);
+ return status;
+ }
+
+ if (a->prod) {
+ if (strcmp(a->prod, cur_product) != 0) {
+ double split = now();
+ fprintf(stderr,"IGNORE, product is %s required only for %s [%7.3fs]\n",
+ cur_product, a->prod, (split - a->start));
+ a->start = split;
+ return 0;
+ }
+ }
+
+ yes = match(resp, value, count);
+ if (invert) yes = !yes;
+
+ if (yes) {
+ double split = now();
+ fprintf(stderr,"OKAY [%7.3fs]\n", (split - a->start));
+ a->start = split;
+ return 0;
+ }
+
+ fprintf(stderr,"FAILED\n\n");
+ fprintf(stderr,"Device %s is '%s'.\n", a->cmd + 7, resp);
+ fprintf(stderr,"Update %s '%s'",
+ invert ? "rejects" : "requires", value[0]);
+ for (n = 1; n < count; n++) {
+ fprintf(stderr," or '%s'", value[n]);
+ }
+ fprintf(stderr,".\n\n");
+ return -1;
+}
+
+static int cb_require(Action*a, int status, const char* resp) {
+ return cb_check(a, status, resp, 0);
+}
+
+static int cb_reject(Action* a, int status, const char* resp) {
+ return cb_check(a, status, resp, 1);
+}
+
+void fb_queue_require(const char *prod, const char *var,
+ int invert, unsigned nvalues, const char **value)
+{
+ Action *a;
+ a = queue_action(OP_QUERY, "getvar:%s", var);
+ a->prod = prod;
+ a->data = value;
+ a->size = nvalues;
+ a->msg = mkmsg("checking %s", var);
+ a->func = invert ? cb_reject : cb_require;
+ if (a->data == nullptr) die("out of memory");
+}
+
+static int cb_display(Action* a, int status, const char* resp) {
+ if (status) {
+ fprintf(stderr, "%s FAILED (%s)\n", a->cmd, resp);
+ return status;
+ }
+ fprintf(stderr, "%s: %s\n", (char*) a->data, resp);
+ return 0;
+}
+
+void fb_queue_display(const char *var, const char *prettyname)
+{
+ Action *a;
+ a = queue_action(OP_QUERY, "getvar:%s", var);
+ a->data = strdup(prettyname);
+ if (a->data == nullptr) die("out of memory");
+ a->func = cb_display;
+}
+
+static int cb_save(Action* a, int status, const char* resp) {
+ if (status) {
+ fprintf(stderr, "%s FAILED (%s)\n", a->cmd, resp);
+ return status;
+ }
+ strncpy(reinterpret_cast<char*>(a->data), resp, a->size);
+ return 0;
+}
+
+void fb_queue_query_save(const char *var, char *dest, unsigned dest_size)
+{
+ Action *a;
+ a = queue_action(OP_QUERY, "getvar:%s", var);
+ a->data = (void *)dest;
+ a->size = dest_size;
+ a->func = cb_save;
+}
+
+static int cb_do_nothing(Action*, int , const char*) {
+ fprintf(stderr,"\n");
+ return 0;
+}
+
+void fb_queue_reboot(void)
+{
+ Action *a = queue_action(OP_COMMAND, "reboot");
+ a->func = cb_do_nothing;
+ a->msg = "rebooting";
+}
+
+void fb_queue_command(const char *cmd, const char *msg)
+{
+ Action *a = queue_action(OP_COMMAND, cmd);
+ a->msg = msg;
+}
+
+void fb_queue_download(const char *name, void *data, unsigned size)
+{
+ Action *a = queue_action(OP_DOWNLOAD, "");
+ a->data = data;
+ a->size = size;
+ a->msg = mkmsg("downloading '%s'", name);
+}
+
+void fb_queue_notice(const char *notice)
+{
+ Action *a = queue_action(OP_NOTICE, "");
+ a->data = (void*) notice;
+}
+
+void fb_queue_wait_for_disconnect(void)
+{
+ queue_action(OP_WAIT_FOR_DISCONNECT, "");
+}
+
+int fb_execute_queue(usb_handle *usb)
+{
+ Action *a;
+ char resp[FB_RESPONSE_SZ+1];
+ int status = 0;
+
+ a = action_list;
+ if (!a)
+ return status;
+ resp[FB_RESPONSE_SZ] = 0;
+
+ double start = -1;
+ for (a = action_list; a; a = a->next) {
+ a->start = now();
+ if (start < 0) start = a->start;
+ if (a->msg) {
+ // fprintf(stderr,"%30s... ",a->msg);
+ fprintf(stderr,"%s...\n",a->msg);
+ }
+ if (a->op == OP_DOWNLOAD) {
+ status = fb_download_data(usb, a->data, a->size);
+ status = a->func(a, status, status ? fb_get_error() : "");
+ if (status) break;
+ } else if (a->op == OP_COMMAND) {
+ status = fb_command(usb, a->cmd);
+ status = a->func(a, status, status ? fb_get_error() : "");
+ if (status) break;
+ } else if (a->op == OP_QUERY) {
+ status = fb_command_response(usb, a->cmd, resp);
+ status = a->func(a, status, status ? fb_get_error() : resp);
+ if (status) break;
+ } else if (a->op == OP_NOTICE) {
+ fprintf(stderr,"%s\n",(char*)a->data);
+ } else if (a->op == OP_DOWNLOAD_SPARSE) {
+ status = fb_download_data_sparse(usb, reinterpret_cast<sparse_file*>(a->data));
+ status = a->func(a, status, status ? fb_get_error() : "");
+ if (status) break;
+ } else if (a->op == OP_WAIT_FOR_DISCONNECT) {
+ usb_wait_for_disconnect(usb);
+ } else {
+ die("bogus action");
+ }
+ }
+
+ fprintf(stderr,"finished. total time: %.3fs\n", (now() - start));
+ return status;
+}
+
+int fb_queue_is_empty(void)
+{
+ return (action_list == nullptr);
+}
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
new file mode 100644
index 0000000..b964a36
--- /dev/null
+++ b/fastboot/fastboot.cpp
@@ -0,0 +1,1287 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define _LARGEFILE64_SOURCE
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <sparse/sparse.h>
+#include <ziparchive/zip_archive.h>
+
+#include "bootimg_utils.h"
+#include "fastboot.h"
+#include "fs.h"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#define ARRAY_SIZE(a) (sizeof(a)/sizeof(*(a)))
+
+char cur_product[FB_RESPONSE_SZ + 1];
+
+static const char *serial = 0;
+static const char *product = 0;
+static const char *cmdline = 0;
+static unsigned short vendor_id = 0;
+static int long_listing = 0;
+static int64_t sparse_limit = -1;
+static int64_t target_sparse_limit = -1;
+
+unsigned page_size = 2048;
+unsigned base_addr = 0x10000000;
+unsigned kernel_offset = 0x00008000;
+unsigned ramdisk_offset = 0x01000000;
+unsigned second_offset = 0x00f00000;
+unsigned tags_offset = 0x00000100;
+
+enum fb_buffer_type {
+ FB_BUFFER,
+ FB_BUFFER_SPARSE,
+};
+
+struct fastboot_buffer {
+ enum fb_buffer_type type;
+ void *data;
+ unsigned int sz;
+};
+
+static struct {
+ char img_name[13];
+ char sig_name[13];
+ char part_name[9];
+ bool is_optional;
+} images[] = {
+ {"boot.img", "boot.sig", "boot", false},
+ {"recovery.img", "recovery.sig", "recovery", true},
+ {"system.img", "system.sig", "system", false},
+ {"vendor.img", "vendor.sig", "vendor", true},
+};
+
+char *find_item(const char *item, const char *product)
+{
+ char *dir;
+ const char *fn;
+ char path[PATH_MAX + 128];
+
+ if(!strcmp(item,"boot")) {
+ fn = "boot.img";
+ } else if(!strcmp(item,"recovery")) {
+ fn = "recovery.img";
+ } else if(!strcmp(item,"system")) {
+ fn = "system.img";
+ } else if(!strcmp(item,"vendor")) {
+ fn = "vendor.img";
+ } else if(!strcmp(item,"userdata")) {
+ fn = "userdata.img";
+ } else if(!strcmp(item,"cache")) {
+ fn = "cache.img";
+ } else if(!strcmp(item,"info")) {
+ fn = "android-info.txt";
+ } else {
+ fprintf(stderr,"unknown partition '%s'\n", item);
+ return 0;
+ }
+
+ if(product) {
+ get_my_path(path);
+ sprintf(path + strlen(path),
+ "../../../target/product/%s/%s", product, fn);
+ return strdup(path);
+ }
+
+ dir = getenv("ANDROID_PRODUCT_OUT");
+ if((dir == 0) || (dir[0] == 0)) {
+ die("neither -p product specified nor ANDROID_PRODUCT_OUT set");
+ return 0;
+ }
+
+ sprintf(path, "%s/%s", dir, fn);
+ return strdup(path);
+}
+
+static int64_t file_size(int fd)
+{
+ struct stat st;
+ int ret;
+
+ ret = fstat(fd, &st);
+
+ return ret ? -1 : st.st_size;
+}
+
+static void *load_fd(int fd, unsigned *_sz)
+{
+ char *data;
+ int sz;
+ int errno_tmp;
+
+ data = 0;
+
+ sz = file_size(fd);
+ if (sz < 0) {
+ goto oops;
+ }
+
+ data = (char*) malloc(sz);
+ if(data == 0) goto oops;
+
+ if(read(fd, data, sz) != sz) goto oops;
+ close(fd);
+
+ if(_sz) *_sz = sz;
+ return data;
+
+oops:
+ errno_tmp = errno;
+ close(fd);
+ if(data != 0) free(data);
+ errno = errno_tmp;
+ return 0;
+}
+
+static void *load_file(const char *fn, unsigned *_sz)
+{
+ int fd;
+
+ fd = open(fn, O_RDONLY | O_BINARY);
+ if(fd < 0) return 0;
+
+ return load_fd(fd, _sz);
+}
+
+int match_fastboot_with_serial(usb_ifc_info *info, const char *local_serial)
+{
+ if(!(vendor_id && (info->dev_vendor == vendor_id)) &&
+ (info->dev_vendor != 0x18d1) && // Google
+ (info->dev_vendor != 0x8087) && // Intel
+ (info->dev_vendor != 0x0451) &&
+ (info->dev_vendor != 0x0502) &&
+ (info->dev_vendor != 0x0fce) && // Sony Ericsson
+ (info->dev_vendor != 0x05c6) && // Qualcomm
+ (info->dev_vendor != 0x22b8) && // Motorola
+ (info->dev_vendor != 0x0955) && // Nvidia
+ (info->dev_vendor != 0x413c) && // DELL
+ (info->dev_vendor != 0x2314) && // INQ Mobile
+ (info->dev_vendor != 0x0b05) && // Asus
+ (info->dev_vendor != 0x0bb4)) // HTC
+ return -1;
+ if(info->ifc_class != 0xff) return -1;
+ if(info->ifc_subclass != 0x42) return -1;
+ if(info->ifc_protocol != 0x03) return -1;
+ // require matching serial number or device path if requested
+ // at the command line with the -s option.
+ if (local_serial && (strcmp(local_serial, info->serial_number) != 0 &&
+ strcmp(local_serial, info->device_path) != 0)) return -1;
+ return 0;
+}
+
+int match_fastboot(usb_ifc_info *info)
+{
+ return match_fastboot_with_serial(info, serial);
+}
+
+int list_devices_callback(usb_ifc_info *info)
+{
+ if (match_fastboot_with_serial(info, NULL) == 0) {
+ const char* serial = info->serial_number;
+ if (!info->writable) {
+ serial = "no permissions"; // like "adb devices"
+ }
+ if (!serial[0]) {
+ serial = "????????????";
+ }
+ // output compatible with "adb devices"
+ if (!long_listing) {
+ printf("%s\tfastboot\n", serial);
+ } else if (strcmp("", info->device_path) == 0) {
+ printf("%-22s fastboot\n", serial);
+ } else {
+ printf("%-22s fastboot %s\n", serial, info->device_path);
+ }
+ }
+
+ return -1;
+}
+
+usb_handle *open_device(void)
+{
+ static usb_handle *usb = 0;
+ int announce = 1;
+
+ if(usb) return usb;
+
+ for(;;) {
+ usb = usb_open(match_fastboot);
+ if(usb) return usb;
+ if(announce) {
+ announce = 0;
+ fprintf(stderr,"< waiting for device >\n");
+ }
+ usleep(1000);
+ }
+}
+
+void list_devices(void) {
+ // We don't actually open a USB device here,
+ // just getting our callback called so we can
+ // list all the connected devices.
+ usb_open(list_devices_callback);
+}
+
+void usage(void)
+{
+ fprintf(stderr,
+/* 1234567890123456789012345678901234567890123456789012345678901234567890123456 */
+ "usage: fastboot [ <option> ] <command>\n"
+ "\n"
+ "commands:\n"
+ " update <filename> Reflash device from update.zip.\n"
+ " flashall Flash boot, system, vendor, and --\n"
+ " if found -- recovery.\n"
+ " flash <partition> [ <filename> ] Write a file to a flash partition.\n"
+ " flashing lock Locks the device. Prevents flashing.\n"
+ " flashing unlock Unlocks the device. Allows flashing\n"
+ " any partition except\n"
+ " bootloader-related partitions.\n"
+ " flashing lock_critical Prevents flashing bootloader-related\n"
+ " partitions.\n"
+ " flashing unlock_critical Enables flashing bootloader-related\n"
+ " partitions.\n"
+ " flashing get_unlock_ability Queries bootloader to see if the\n"
+ " device is unlocked.\n"
+ " erase <partition> Erase a flash partition.\n"
+ " format[:[<fs type>][:[<size>]] <partition>\n"
+ " Format a flash partition. Can\n"
+ " override the fs type and/or size\n"
+ " the bootloader reports.\n"
+ " getvar <variable> Display a bootloader variable.\n"
+ " boot <kernel> [ <ramdisk> [ <second> ] ] Download and boot kernel.\n"
+ " flash:raw boot <kernel> [ <ramdisk> [ <second> ] ]\n"
+ " Create bootimage and flash it.\n"
+ " devices [-l] List all connected devices [with\n"
+ " device paths].\n"
+ " continue Continue with autoboot.\n"
+ " reboot [bootloader] Reboot device [into bootloader].\n"
+ " reboot-bootloader Reboot device into bootloader.\n"
+ " help Show this help message.\n"
+ "\n"
+ "options:\n"
+ " -w Erase userdata and cache (and format\n"
+ " if supported by partition type).\n"
+ " -u Do not erase partition before\n"
+ " formatting.\n"
+ " -s <specific device> Specify device serial number\n"
+ " or path to device port.\n"
+ " -p <product> Specify product name.\n"
+ " -c <cmdline> Override kernel commandline.\n"
+ " -i <vendor id> Specify a custom USB vendor id.\n"
+ " -b <base_addr> Specify a custom kernel base\n"
+ " address (default: 0x10000000).\n"
+ " -n <page size> Specify the nand page size\n"
+ " (default: 2048).\n"
+ " -S <size>[K|M|G] Automatically sparse files greater\n"
+ " than 'size'. 0 to disable.\n"
+ );
+}
+
+void *load_bootable_image(const char *kernel, const char *ramdisk,
+ const char *secondstage, unsigned *sz,
+ const char *cmdline)
+{
+ void *kdata = 0, *rdata = 0, *sdata = 0;
+ unsigned ksize = 0, rsize = 0, ssize = 0;
+ void *bdata;
+ unsigned bsize;
+
+ if(kernel == 0) {
+ fprintf(stderr, "no image specified\n");
+ return 0;
+ }
+
+ kdata = load_file(kernel, &ksize);
+ if(kdata == 0) {
+ fprintf(stderr, "cannot load '%s': %s\n", kernel, strerror(errno));
+ return 0;
+ }
+
+ /* is this actually a boot image? */
+ if(!memcmp(kdata, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
+ if(cmdline) bootimg_set_cmdline((boot_img_hdr*) kdata, cmdline);
+
+ if(ramdisk) {
+ fprintf(stderr, "cannot boot a boot.img *and* ramdisk\n");
+ return 0;
+ }
+
+ *sz = ksize;
+ return kdata;
+ }
+
+ if(ramdisk) {
+ rdata = load_file(ramdisk, &rsize);
+ if(rdata == 0) {
+ fprintf(stderr,"cannot load '%s': %s\n", ramdisk, strerror(errno));
+ return 0;
+ }
+ }
+
+ if (secondstage) {
+ sdata = load_file(secondstage, &ssize);
+ if(sdata == 0) {
+ fprintf(stderr,"cannot load '%s': %s\n", secondstage, strerror(errno));
+ return 0;
+ }
+ }
+
+ fprintf(stderr,"creating boot image...\n");
+ bdata = mkbootimg(kdata, ksize, kernel_offset,
+ rdata, rsize, ramdisk_offset,
+ sdata, ssize, second_offset,
+ page_size, base_addr, tags_offset, &bsize);
+ if(bdata == 0) {
+ fprintf(stderr,"failed to create boot.img\n");
+ return 0;
+ }
+ if(cmdline) bootimg_set_cmdline((boot_img_hdr*) bdata, cmdline);
+ fprintf(stderr,"creating boot image - %d bytes\n", bsize);
+ *sz = bsize;
+
+ return bdata;
+}
+
+static void* unzip_file(ZipArchiveHandle zip, const char* entry_name, unsigned* sz)
+{
+ ZipString zip_entry_name(entry_name);
+ ZipEntry zip_entry;
+ if (FindEntry(zip, zip_entry_name, &zip_entry) != 0) {
+ fprintf(stderr, "archive does not contain '%s'\n", entry_name);
+ return 0;
+ }
+
+ *sz = zip_entry.uncompressed_length;
+
+ uint8_t* data = reinterpret_cast<uint8_t*>(malloc(zip_entry.uncompressed_length));
+ if (data == NULL) {
+ fprintf(stderr, "failed to allocate %u bytes for '%s'\n", *sz, entry_name);
+ return 0;
+ }
+
+ int error = ExtractToMemory(zip, &zip_entry, data, zip_entry.uncompressed_length);
+ if (error != 0) {
+ fprintf(stderr, "failed to extract '%s': %s\n", entry_name, ErrorCodeString(error));
+ free(data);
+ return 0;
+ }
+
+ return data;
+}
+
+#if defined(_WIN32)
+
+// TODO: move this to somewhere it can be shared.
+
+#include <windows.h>
+
+// Windows' tmpfile(3) requires administrator rights because
+// it creates temporary files in the root directory.
+static FILE* win32_tmpfile() {
+ char temp_path[PATH_MAX];
+ DWORD nchars = GetTempPath(sizeof(temp_path), temp_path);
+ if (nchars == 0 || nchars >= sizeof(temp_path)) {
+ fprintf(stderr, "GetTempPath failed, error %ld\n", GetLastError());
+ return nullptr;
+ }
+
+ char filename[PATH_MAX];
+ if (GetTempFileName(temp_path, "fastboot", 0, filename) == 0) {
+ fprintf(stderr, "GetTempFileName failed, error %ld\n", GetLastError());
+ return nullptr;
+ }
+
+ return fopen(filename, "w+bTD");
+}
+
+#define tmpfile win32_tmpfile
+
+#endif
+
+static int unzip_to_file(ZipArchiveHandle zip, char* entry_name) {
+ FILE* fp = tmpfile();
+ if (fp == NULL) {
+ fprintf(stderr, "failed to create temporary file for '%s': %s\n",
+ entry_name, strerror(errno));
+ return -1;
+ }
+
+ ZipString zip_entry_name(entry_name);
+ ZipEntry zip_entry;
+ if (FindEntry(zip, zip_entry_name, &zip_entry) != 0) {
+ fprintf(stderr, "archive does not contain '%s'\n", entry_name);
+ return -1;
+ }
+
+ int fd = fileno(fp);
+ int error = ExtractEntryToFile(zip, &zip_entry, fd);
+ if (error != 0) {
+ fprintf(stderr, "failed to extract '%s': %s\n", entry_name, ErrorCodeString(error));
+ return -1;
+ }
+
+ lseek(fd, 0, SEEK_SET);
+ return fd;
+}
+
+static char *strip(char *s)
+{
+ int n;
+ while(*s && isspace(*s)) s++;
+ n = strlen(s);
+ while(n-- > 0) {
+ if(!isspace(s[n])) break;
+ s[n] = 0;
+ }
+ return s;
+}
+
+#define MAX_OPTIONS 32
+static int setup_requirement_line(char *name)
+{
+ char *val[MAX_OPTIONS];
+ char *prod = NULL;
+ unsigned n, count;
+ char *x;
+ int invert = 0;
+
+ if (!strncmp(name, "reject ", 7)) {
+ name += 7;
+ invert = 1;
+ } else if (!strncmp(name, "require ", 8)) {
+ name += 8;
+ invert = 0;
+ } else if (!strncmp(name, "require-for-product:", 20)) {
+ // Get the product and point name past it
+ prod = name + 20;
+ name = strchr(name, ' ');
+ if (!name) return -1;
+ *name = 0;
+ name += 1;
+ invert = 0;
+ }
+
+ x = strchr(name, '=');
+ if (x == 0) return 0;
+ *x = 0;
+ val[0] = x + 1;
+
+ for(count = 1; count < MAX_OPTIONS; count++) {
+ x = strchr(val[count - 1],'|');
+ if (x == 0) break;
+ *x = 0;
+ val[count] = x + 1;
+ }
+
+ name = strip(name);
+ for(n = 0; n < count; n++) val[n] = strip(val[n]);
+
+ name = strip(name);
+ if (name == 0) return -1;
+
+ const char* var = name;
+ // Work around an unfortunate name mismatch.
+ if (!strcmp(name,"board")) var = "product";
+
+ const char** out = reinterpret_cast<const char**>(malloc(sizeof(char*) * count));
+ if (out == 0) return -1;
+
+ for(n = 0; n < count; n++) {
+ out[n] = strdup(strip(val[n]));
+ if (out[n] == 0) {
+ for(size_t i = 0; i < n; ++i) {
+ free((char*) out[i]);
+ }
+ free(out);
+ return -1;
+ }
+ }
+
+ fb_queue_require(prod, var, invert, n, out);
+ return 0;
+}
+
+static void setup_requirements(char *data, unsigned sz)
+{
+ char *s;
+
+ s = data;
+ while (sz-- > 0) {
+ if(*s == '\n') {
+ *s++ = 0;
+ if (setup_requirement_line(data)) {
+ die("out of memory");
+ }
+ data = s;
+ } else {
+ s++;
+ }
+ }
+}
+
+void queue_info_dump(void)
+{
+ fb_queue_notice("--------------------------------------------");
+ fb_queue_display("version-bootloader", "Bootloader Version...");
+ fb_queue_display("version-baseband", "Baseband Version.....");
+ fb_queue_display("serialno", "Serial Number........");
+ fb_queue_notice("--------------------------------------------");
+}
+
+static struct sparse_file **load_sparse_files(int fd, int max_size)
+{
+ struct sparse_file* s = sparse_file_import_auto(fd, false, true);
+ if (!s) {
+ die("cannot sparse read file\n");
+ }
+
+ int files = sparse_file_resparse(s, max_size, NULL, 0);
+ if (files < 0) {
+ die("Failed to resparse\n");
+ }
+
+ sparse_file** out_s = reinterpret_cast<sparse_file**>(calloc(sizeof(struct sparse_file *), files + 1));
+ if (!out_s) {
+ die("Failed to allocate sparse file array\n");
+ }
+
+ files = sparse_file_resparse(s, max_size, out_s, files);
+ if (files < 0) {
+ die("Failed to resparse\n");
+ }
+
+ return out_s;
+}
+
+static int64_t get_target_sparse_limit(struct usb_handle *usb)
+{
+ int64_t limit = 0;
+ char response[FB_RESPONSE_SZ + 1];
+ int status = fb_getvar(usb, response, "max-download-size");
+
+ if (!status) {
+ limit = strtoul(response, NULL, 0);
+ if (limit > 0) {
+ fprintf(stderr, "target reported max download size of %" PRId64 " bytes\n",
+ limit);
+ }
+ }
+
+ return limit;
+}
+
+static int64_t get_sparse_limit(struct usb_handle *usb, int64_t size)
+{
+ int64_t limit;
+
+ if (sparse_limit == 0) {
+ return 0;
+ } else if (sparse_limit > 0) {
+ limit = sparse_limit;
+ } else {
+ if (target_sparse_limit == -1) {
+ target_sparse_limit = get_target_sparse_limit(usb);
+ }
+ if (target_sparse_limit > 0) {
+ limit = target_sparse_limit;
+ } else {
+ return 0;
+ }
+ }
+
+ if (size > limit) {
+ return limit;
+ }
+
+ return 0;
+}
+
+/* Until we get lazy inode table init working in make_ext4fs, we need to
+ * erase partitions of type ext4 before flashing a filesystem so no stale
+ * inodes are left lying around. Otherwise, e2fsck gets very upset.
+ */
+static int needs_erase(usb_handle* usb, const char *part)
+{
+ /* The function fb_format_supported() currently returns the value
+ * we want, so just call it.
+ */
+ return fb_format_supported(usb, part, NULL);
+}
+
+static int load_buf_fd(usb_handle *usb, int fd,
+ struct fastboot_buffer *buf)
+{
+ int64_t sz64;
+ void *data;
+ int64_t limit;
+
+
+ sz64 = file_size(fd);
+ if (sz64 < 0) {
+ return -1;
+ }
+
+ lseek(fd, 0, SEEK_SET);
+ limit = get_sparse_limit(usb, sz64);
+ if (limit) {
+ struct sparse_file **s = load_sparse_files(fd, limit);
+ if (s == NULL) {
+ return -1;
+ }
+ buf->type = FB_BUFFER_SPARSE;
+ buf->data = s;
+ } else {
+ unsigned int sz;
+ data = load_fd(fd, &sz);
+ if (data == 0) return -1;
+ buf->type = FB_BUFFER;
+ buf->data = data;
+ buf->sz = sz;
+ }
+
+ return 0;
+}
+
+static int load_buf(usb_handle *usb, const char *fname,
+ struct fastboot_buffer *buf)
+{
+ int fd;
+
+ fd = open(fname, O_RDONLY | O_BINARY);
+ if (fd < 0) {
+ return -1;
+ }
+
+ return load_buf_fd(usb, fd, buf);
+}
+
+static void flash_buf(const char *pname, struct fastboot_buffer *buf)
+{
+ sparse_file** s;
+
+ switch (buf->type) {
+ case FB_BUFFER_SPARSE:
+ s = reinterpret_cast<sparse_file**>(buf->data);
+ while (*s) {
+ int64_t sz64 = sparse_file_len(*s, true, false);
+ fb_queue_flash_sparse(pname, *s++, sz64);
+ }
+ break;
+ case FB_BUFFER:
+ fb_queue_flash(pname, buf->data, buf->sz);
+ break;
+ default:
+ die("unknown buffer type: %d", buf->type);
+ }
+}
+
+void do_flash(usb_handle *usb, const char *pname, const char *fname)
+{
+ struct fastboot_buffer buf;
+
+ if (load_buf(usb, fname, &buf)) {
+ die("cannot load '%s'", fname);
+ }
+ flash_buf(pname, &buf);
+}
+
+void do_update_signature(ZipArchiveHandle zip, char *fn)
+{
+ unsigned sz;
+ void* data = unzip_file(zip, fn, &sz);
+ if (data == 0) return;
+ fb_queue_download("signature", data, sz);
+ fb_queue_command("signature", "installing signature");
+}
+
+void do_update(usb_handle *usb, const char *filename, int erase_first)
+{
+ queue_info_dump();
+
+ fb_queue_query_save("product", cur_product, sizeof(cur_product));
+
+ ZipArchiveHandle zip;
+ int error = OpenArchive(filename, &zip);
+ if (error != 0) {
+ CloseArchive(zip);
+ die("failed to open zip file '%s': %s", filename, ErrorCodeString(error));
+ }
+
+ unsigned sz;
+ void* data = unzip_file(zip, "android-info.txt", &sz);
+ if (data == 0) {
+ CloseArchive(zip);
+ die("update package '%s' has no android-info.txt", filename);
+ }
+
+ setup_requirements(reinterpret_cast<char*>(data), sz);
+
+ for (size_t i = 0; i < ARRAY_SIZE(images); ++i) {
+ int fd = unzip_to_file(zip, images[i].img_name);
+ if (fd == -1) {
+ if (images[i].is_optional) {
+ continue;
+ }
+ CloseArchive(zip);
+ exit(1); // unzip_to_file already explained why.
+ }
+ fastboot_buffer buf;
+ int rc = load_buf_fd(usb, fd, &buf);
+ if (rc) die("cannot load %s from flash", images[i].img_name);
+ do_update_signature(zip, images[i].sig_name);
+ if (erase_first && needs_erase(usb, images[i].part_name)) {
+ fb_queue_erase(images[i].part_name);
+ }
+ flash_buf(images[i].part_name, &buf);
+ /* not closing the fd here since the sparse code keeps the fd around
+ * but hasn't mmaped data yet. The tmpfile will get cleaned up when the
+ * program exits.
+ */
+ }
+
+ CloseArchive(zip);
+}
+
+void do_send_signature(char *fn)
+{
+ void *data;
+ unsigned sz;
+ char *xtn;
+
+ xtn = strrchr(fn, '.');
+ if (!xtn) return;
+ if (strcmp(xtn, ".img")) return;
+
+ strcpy(xtn,".sig");
+ data = load_file(fn, &sz);
+ strcpy(xtn,".img");
+ if (data == 0) return;
+ fb_queue_download("signature", data, sz);
+ fb_queue_command("signature", "installing signature");
+}
+
+void do_flashall(usb_handle *usb, int erase_first)
+{
+ queue_info_dump();
+
+ fb_queue_query_save("product", cur_product, sizeof(cur_product));
+
+ char* fname = find_item("info", product);
+ if (fname == 0) die("cannot find android-info.txt");
+
+ unsigned sz;
+ void* data = load_file(fname, &sz);
+ if (data == 0) die("could not load android-info.txt: %s", strerror(errno));
+
+ setup_requirements(reinterpret_cast<char*>(data), sz);
+
+ for (size_t i = 0; i < ARRAY_SIZE(images); i++) {
+ fname = find_item(images[i].part_name, product);
+ fastboot_buffer buf;
+ if (load_buf(usb, fname, &buf)) {
+ if (images[i].is_optional)
+ continue;
+ die("could not load %s\n", images[i].img_name);
+ }
+ do_send_signature(fname);
+ if (erase_first && needs_erase(usb, images[i].part_name)) {
+ fb_queue_erase(images[i].part_name);
+ }
+ flash_buf(images[i].part_name, &buf);
+ }
+}
+
+#define skip(n) do { argc -= (n); argv += (n); } while (0)
+#define require(n) do { if (argc < (n)) {usage(); exit(1);}} while (0)
+
+int do_oem_command(int argc, char **argv)
+{
+ char command[256];
+ if (argc <= 1) return 0;
+
+ command[0] = 0;
+ while(1) {
+ strcat(command,*argv);
+ skip(1);
+ if(argc == 0) break;
+ strcat(command," ");
+ }
+
+ fb_queue_command(command,"");
+ return 0;
+}
+
+static int64_t parse_num(const char *arg)
+{
+ char *endptr;
+ unsigned long long num;
+
+ num = strtoull(arg, &endptr, 0);
+ if (endptr == arg) {
+ return -1;
+ }
+
+ if (*endptr == 'k' || *endptr == 'K') {
+ if (num >= (-1ULL) / 1024) {
+ return -1;
+ }
+ num *= 1024LL;
+ endptr++;
+ } else if (*endptr == 'm' || *endptr == 'M') {
+ if (num >= (-1ULL) / (1024 * 1024)) {
+ return -1;
+ }
+ num *= 1024LL * 1024LL;
+ endptr++;
+ } else if (*endptr == 'g' || *endptr == 'G') {
+ if (num >= (-1ULL) / (1024 * 1024 * 1024)) {
+ return -1;
+ }
+ num *= 1024LL * 1024LL * 1024LL;
+ endptr++;
+ }
+
+ if (*endptr != '\0') {
+ return -1;
+ }
+
+ if (num > INT64_MAX) {
+ return -1;
+ }
+
+ return num;
+}
+
+void fb_perform_format(usb_handle* usb,
+ const char *partition, int skip_if_not_supported,
+ const char *type_override, const char *size_override)
+{
+ char pTypeBuff[FB_RESPONSE_SZ + 1], pSizeBuff[FB_RESPONSE_SZ + 1];
+ char *pType = pTypeBuff;
+ char *pSize = pSizeBuff;
+ unsigned int limit = INT_MAX;
+ struct fastboot_buffer buf;
+ const char *errMsg = NULL;
+ const struct fs_generator *gen;
+ uint64_t pSz;
+ int status;
+ int fd;
+
+ if (target_sparse_limit > 0 && target_sparse_limit < limit)
+ limit = target_sparse_limit;
+ if (sparse_limit > 0 && sparse_limit < limit)
+ limit = sparse_limit;
+
+ status = fb_getvar(usb, pType, "partition-type:%s", partition);
+ if (status) {
+ errMsg = "Can't determine partition type.\n";
+ goto failed;
+ }
+ if (type_override) {
+ if (strcmp(type_override, pType)) {
+ fprintf(stderr,
+ "Warning: %s type is %s, but %s was requested for formating.\n",
+ partition, pType, type_override);
+ }
+ pType = (char *)type_override;
+ }
+
+ status = fb_getvar(usb, pSize, "partition-size:%s", partition);
+ if (status) {
+ errMsg = "Unable to get partition size\n";
+ goto failed;
+ }
+ if (size_override) {
+ if (strcmp(size_override, pSize)) {
+ fprintf(stderr,
+ "Warning: %s size is %s, but %s was requested for formating.\n",
+ partition, pSize, size_override);
+ }
+ pSize = (char *)size_override;
+ }
+
+ gen = fs_get_generator(pType);
+ if (!gen) {
+ if (skip_if_not_supported) {
+ fprintf(stderr, "Erase successful, but not automatically formatting.\n");
+ fprintf(stderr, "File system type %s not supported.\n", pType);
+ return;
+ }
+ fprintf(stderr, "Formatting is not supported for filesystem with type '%s'.\n", pType);
+ return;
+ }
+
+ pSz = strtoll(pSize, (char **)NULL, 16);
+
+ fd = fileno(tmpfile());
+ if (fs_generator_generate(gen, fd, pSz)) {
+ close(fd);
+ fprintf(stderr, "Cannot generate image.\n");
+ return;
+ }
+
+ if (load_buf_fd(usb, fd, &buf)) {
+ fprintf(stderr, "Cannot read image.\n");
+ close(fd);
+ return;
+ }
+ flash_buf(partition, &buf);
+
+ return;
+
+
+failed:
+ if (skip_if_not_supported) {
+ fprintf(stderr, "Erase successful, but not automatically formatting.\n");
+ if (errMsg)
+ fprintf(stderr, "%s", errMsg);
+ }
+ fprintf(stderr,"FAILED (%s)\n", fb_get_error());
+}
+
+int main(int argc, char **argv)
+{
+ int wants_wipe = 0;
+ int wants_reboot = 0;
+ int wants_reboot_bootloader = 0;
+ int erase_first = 1;
+ void *data;
+ unsigned sz;
+ int status;
+ int c;
+ int longindex;
+
+ const struct option longopts[] = {
+ {"base", required_argument, 0, 'b'},
+ {"kernel_offset", required_argument, 0, 'k'},
+ {"page_size", required_argument, 0, 'n'},
+ {"ramdisk_offset", required_argument, 0, 'r'},
+ {"tags_offset", required_argument, 0, 't'},
+ {"help", no_argument, 0, 'h'},
+ {"unbuffered", no_argument, 0, 0},
+ {"version", no_argument, 0, 0},
+ {0, 0, 0, 0}
+ };
+
+ serial = getenv("ANDROID_SERIAL");
+
+ while (1) {
+ c = getopt_long(argc, argv, "wub:k:n:r:t:s:S:lp:c:i:m:h", longopts, &longindex);
+ if (c < 0) {
+ break;
+ }
+ /* Alphabetical cases */
+ switch (c) {
+ case 'b':
+ base_addr = strtoul(optarg, 0, 16);
+ break;
+ case 'c':
+ cmdline = optarg;
+ break;
+ case 'h':
+ usage();
+ return 1;
+ case 'i': {
+ char *endptr = NULL;
+ unsigned long val;
+
+ val = strtoul(optarg, &endptr, 0);
+ if (!endptr || *endptr != '\0' || (val & ~0xffff))
+ die("invalid vendor id '%s'", optarg);
+ vendor_id = (unsigned short)val;
+ break;
+ }
+ case 'k':
+ kernel_offset = strtoul(optarg, 0, 16);
+ break;
+ case 'l':
+ long_listing = 1;
+ break;
+ case 'n':
+ page_size = (unsigned)strtoul(optarg, NULL, 0);
+ if (!page_size) die("invalid page size");
+ break;
+ case 'p':
+ product = optarg;
+ break;
+ case 'r':
+ ramdisk_offset = strtoul(optarg, 0, 16);
+ break;
+ case 't':
+ tags_offset = strtoul(optarg, 0, 16);
+ break;
+ case 's':
+ serial = optarg;
+ break;
+ case 'S':
+ sparse_limit = parse_num(optarg);
+ if (sparse_limit < 0) {
+ die("invalid sparse limit");
+ }
+ break;
+ case 'u':
+ erase_first = 0;
+ break;
+ case 'w':
+ wants_wipe = 1;
+ break;
+ case '?':
+ return 1;
+ case 0:
+ if (strcmp("unbuffered", longopts[longindex].name) == 0) {
+ setvbuf(stdout, NULL, _IONBF, 0);
+ setvbuf(stderr, NULL, _IONBF, 0);
+ } else if (strcmp("version", longopts[longindex].name) == 0) {
+ fprintf(stdout, "fastboot version %s\n", FASTBOOT_REVISION);
+ return 0;
+ }
+ break;
+ default:
+ abort();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0 && !wants_wipe) {
+ usage();
+ return 1;
+ }
+
+ if (argc > 0 && !strcmp(*argv, "devices")) {
+ skip(1);
+ list_devices();
+ return 0;
+ }
+
+ if (argc > 0 && !strcmp(*argv, "help")) {
+ usage();
+ return 0;
+ }
+
+ usb_handle* usb = open_device();
+
+ while (argc > 0) {
+ if(!strcmp(*argv, "getvar")) {
+ require(2);
+ fb_queue_display(argv[1], argv[1]);
+ skip(2);
+ } else if(!strcmp(*argv, "erase")) {
+ require(2);
+
+ if (fb_format_supported(usb, argv[1], NULL)) {
+ fprintf(stderr, "******** Did you mean to fastboot format this partition?\n");
+ }
+
+ fb_queue_erase(argv[1]);
+ skip(2);
+ } else if(!strncmp(*argv, "format", strlen("format"))) {
+ char *overrides;
+ char *type_override = NULL;
+ char *size_override = NULL;
+ require(2);
+ /*
+ * Parsing for: "format[:[type][:[size]]]"
+ * Some valid things:
+ * - select ontly the size, and leave default fs type:
+ * format::0x4000000 userdata
+ * - default fs type and size:
+ * format userdata
+ * format:: userdata
+ */
+ overrides = strchr(*argv, ':');
+ if (overrides) {
+ overrides++;
+ size_override = strchr(overrides, ':');
+ if (size_override) {
+ size_override[0] = '\0';
+ size_override++;
+ }
+ type_override = overrides;
+ }
+ if (type_override && !type_override[0]) type_override = NULL;
+ if (size_override && !size_override[0]) size_override = NULL;
+ if (erase_first && needs_erase(usb, argv[1])) {
+ fb_queue_erase(argv[1]);
+ }
+ fb_perform_format(usb, argv[1], 0, type_override, size_override);
+ skip(2);
+ } else if(!strcmp(*argv, "signature")) {
+ require(2);
+ data = load_file(argv[1], &sz);
+ if (data == 0) die("could not load '%s': %s", argv[1], strerror(errno));
+ if (sz != 256) die("signature must be 256 bytes");
+ fb_queue_download("signature", data, sz);
+ fb_queue_command("signature", "installing signature");
+ skip(2);
+ } else if(!strcmp(*argv, "reboot")) {
+ wants_reboot = 1;
+ skip(1);
+ if (argc > 0) {
+ if (!strcmp(*argv, "bootloader")) {
+ wants_reboot = 0;
+ wants_reboot_bootloader = 1;
+ skip(1);
+ }
+ }
+ require(0);
+ } else if(!strcmp(*argv, "reboot-bootloader")) {
+ wants_reboot_bootloader = 1;
+ skip(1);
+ } else if (!strcmp(*argv, "continue")) {
+ fb_queue_command("continue", "resuming boot");
+ skip(1);
+ } else if(!strcmp(*argv, "boot")) {
+ char *kname = 0;
+ char *rname = 0;
+ char *sname = 0;
+ skip(1);
+ if (argc > 0) {
+ kname = argv[0];
+ skip(1);
+ }
+ if (argc > 0) {
+ rname = argv[0];
+ skip(1);
+ }
+ if (argc > 0) {
+ sname = argv[0];
+ skip(1);
+ }
+ data = load_bootable_image(kname, rname, sname, &sz, cmdline);
+ if (data == 0) return 1;
+ fb_queue_download("boot.img", data, sz);
+ fb_queue_command("boot", "booting");
+ } else if(!strcmp(*argv, "flash")) {
+ char *pname = argv[1];
+ char *fname = 0;
+ require(2);
+ if (argc > 2) {
+ fname = argv[2];
+ skip(3);
+ } else {
+ fname = find_item(pname, product);
+ skip(2);
+ }
+ if (fname == 0) die("cannot determine image filename for '%s'", pname);
+ if (erase_first && needs_erase(usb, pname)) {
+ fb_queue_erase(pname);
+ }
+ do_flash(usb, pname, fname);
+ } else if(!strcmp(*argv, "flash:raw")) {
+ char *pname = argv[1];
+ char *kname = argv[2];
+ char *rname = 0;
+ char *sname = 0;
+ require(3);
+ skip(3);
+ if (argc > 0) {
+ rname = argv[0];
+ skip(1);
+ }
+ if (argc > 0) {
+ sname = argv[0];
+ skip(1);
+ }
+ data = load_bootable_image(kname, rname, sname, &sz, cmdline);
+ if (data == 0) die("cannot load bootable image");
+ fb_queue_flash(pname, data, sz);
+ } else if(!strcmp(*argv, "flashall")) {
+ skip(1);
+ do_flashall(usb, erase_first);
+ wants_reboot = 1;
+ } else if(!strcmp(*argv, "update")) {
+ if (argc > 1) {
+ do_update(usb, argv[1], erase_first);
+ skip(2);
+ } else {
+ do_update(usb, "update.zip", erase_first);
+ skip(1);
+ }
+ wants_reboot = 1;
+ } else if(!strcmp(*argv, "oem")) {
+ argc = do_oem_command(argc, argv);
+ } else if(!strcmp(*argv, "flashing") && argc == 2) {
+ if(!strcmp(*(argv+1), "unlock") || !strcmp(*(argv+1), "lock")
+ || !strcmp(*(argv+1), "unlock_critical")
+ || !strcmp(*(argv+1), "lock_critical")
+ || !strcmp(*(argv+1), "get_unlock_ability")) {
+ argc = do_oem_command(argc, argv);
+ } else {
+ usage();
+ return 1;
+ }
+ } else {
+ usage();
+ return 1;
+ }
+ }
+
+ if (wants_wipe) {
+ fb_queue_erase("userdata");
+ fb_perform_format(usb, "userdata", 1, NULL, NULL);
+ fb_queue_erase("cache");
+ fb_perform_format(usb, "cache", 1, NULL, NULL);
+ }
+ if (wants_reboot) {
+ fb_queue_reboot();
+ fb_queue_wait_for_disconnect();
+ } else if (wants_reboot_bootloader) {
+ fb_queue_command("reboot-bootloader", "rebooting into bootloader");
+ fb_queue_wait_for_disconnect();
+ }
+
+ if (fb_queue_is_empty())
+ return 0;
+
+ status = fb_execute_queue(usb);
+ return (status) ? 1 : 0;
+}
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
new file mode 100644
index 0000000..481c501
--- /dev/null
+++ b/fastboot/fastboot.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _FASTBOOT_H_
+#define _FASTBOOT_H_
+
+#include "usb.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct sparse_file;
+
+/* protocol.c - fastboot protocol */
+int fb_command(usb_handle *usb, const char *cmd);
+int fb_command_response(usb_handle *usb, const char *cmd, char *response);
+int fb_download_data(usb_handle *usb, const void *data, unsigned size);
+int fb_download_data_sparse(usb_handle *usb, struct sparse_file *s);
+char *fb_get_error(void);
+
+#define FB_COMMAND_SZ 64
+#define FB_RESPONSE_SZ 64
+
+/* engine.c - high level command queue engine */
+int fb_getvar(struct usb_handle *usb, char *response, const char *fmt, ...);
+int fb_format_supported(usb_handle *usb, const char *partition, const char *type_override);
+void fb_queue_flash(const char *ptn, void *data, unsigned sz);
+void fb_queue_flash_sparse(const char *ptn, struct sparse_file *s, unsigned sz);
+void fb_queue_erase(const char *ptn);
+void fb_queue_format(const char *ptn, int skip_if_not_supported, unsigned int max_chunk_sz);
+void fb_queue_require(const char *prod, const char *var, int invert,
+ unsigned nvalues, const char **value);
+void fb_queue_display(const char *var, const char *prettyname);
+void fb_queue_query_save(const char *var, char *dest, unsigned dest_size);
+void fb_queue_reboot(void);
+void fb_queue_command(const char *cmd, const char *msg);
+void fb_queue_download(const char *name, void *data, unsigned size);
+void fb_queue_notice(const char *notice);
+void fb_queue_wait_for_disconnect(void);
+int fb_execute_queue(usb_handle *usb);
+int fb_queue_is_empty(void);
+
+/* util stuff */
+double now();
+char *mkmsg(const char *fmt, ...);
+__attribute__((__noreturn__)) void die(const char *fmt, ...);
+
+void get_my_path(char *path);
+
+/* Current product */
+extern char cur_product[FB_RESPONSE_SZ + 1];
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/fastboot/fastboot_protocol.txt b/fastboot/fastboot_protocol.txt
new file mode 100644
index 0000000..37b1959
--- /dev/null
+++ b/fastboot/fastboot_protocol.txt
@@ -0,0 +1,173 @@
+
+FastBoot Version 0.4
+----------------------
+
+The fastboot protocol is a mechanism for communicating with bootloaders
+over USB. It is designed to be very straightforward to implement, to
+allow it to be used across a wide range of devices and from hosts running
+Linux, Windows, or OSX.
+
+
+Basic Requirements
+------------------
+
+* Two bulk endpoints (in, out) are required
+* Max packet size must be 64 bytes for full-speed, 512 bytes for
+ high-speed and 1024 bytes for Super Speed USB.
+* The protocol is entirely host-driven and synchronous (unlike the
+ multi-channel, bi-directional, asynchronous ADB protocol)
+
+
+Transport and Framing
+---------------------
+
+1. Host sends a command, which is an ascii string in a single
+ packet no greater than 64 bytes.
+
+2. Client response with a single packet no greater than 64 bytes.
+ The first four bytes of the response are "OKAY", "FAIL", "DATA",
+ or "INFO". Additional bytes may contain an (ascii) informative
+ message.
+
+ a. INFO -> the remaining 60 bytes are an informative message
+ (providing progress or diagnostic messages). They should
+ be displayed and then step #2 repeats
+
+ b. FAIL -> the requested command failed. The remaining 60 bytes
+ of the response (if present) provide a textual failure message
+ to present to the user. Stop.
+
+ c. OKAY -> the requested command completed successfully. Go to #5
+
+ d. DATA -> the requested command is ready for the data phase.
+ A DATA response packet will be 12 bytes long, in the form of
+ DATA00000000 where the 8 digit hexidecimal number represents
+ the total data size to transfer.
+
+3. Data phase. Depending on the command, the host or client will
+ send the indicated amount of data. Short packets are always
+ acceptable and zero-length packets are ignored. This phase continues
+ until the client has sent or received the number of bytes indicated
+ in the "DATA" response above.
+
+4. Client responds with a single packet no greater than 64 bytes.
+ The first four bytes of the response are "OKAY", "FAIL", or "INFO".
+ Similar to #2:
+
+ a. INFO -> display the remaining 60 bytes and return to #4
+
+ b. FAIL -> display the remaining 60 bytes (if present) as a failure
+ reason and consider the command failed. Stop.
+
+ c. OKAY -> success. Go to #5
+
+5. Success. Stop.
+
+
+Example Session
+---------------
+
+Host: "getvar:version" request version variable
+
+Client: "OKAY0.4" return version "0.4"
+
+Host: "getvar:nonexistant" request some undefined variable
+
+Client: "OKAY" return value ""
+
+Host: "download:00001234" request to send 0x1234 bytes of data
+
+Client: "DATA00001234" ready to accept data
+
+Host: < 0x1234 bytes > send data
+
+Client: "OKAY" success
+
+Host: "flash:bootloader" request to flash the data to the bootloader
+
+Client: "INFOerasing flash" indicate status / progress
+ "INFOwriting flash"
+ "OKAY" indicate success
+
+Host: "powerdown" send a command
+
+Client: "FAILunknown command" indicate failure
+
+
+Command Reference
+-----------------
+
+* Command parameters are indicated by printf-style escape sequences.
+
+* Commands are ascii strings and sent without the quotes (which are
+ for illustration only here) and without a trailing 0 byte.
+
+* Commands that begin with a lowercase letter are reserved for this
+ specification. OEM-specific commands should not begin with a
+ lowercase letter, to prevent incompatibilities with future specs.
+
+ "getvar:%s" Read a config/version variable from the bootloader.
+ The variable contents will be returned after the
+ OKAY response.
+
+ "download:%08x" Write data to memory which will be later used
+ by "boot", "ramdisk", "flash", etc. The client
+ will reply with "DATA%08x" if it has enough
+ space in RAM or "FAIL" if not. The size of
+ the download is remembered.
+
+ "verify:%08x" Send a digital signature to verify the downloaded
+ data. Required if the bootloader is "secure"
+ otherwise "flash" and "boot" will be ignored.
+
+ "flash:%s" Write the previously downloaded image to the
+ named partition (if possible).
+
+ "erase:%s" Erase the indicated partition (clear to 0xFFs)
+
+ "boot" The previously downloaded data is a boot.img
+ and should be booted according to the normal
+ procedure for a boot.img
+
+ "continue" Continue booting as normal (if possible)
+
+ "reboot" Reboot the device.
+
+ "reboot-bootloader" Reboot back into the bootloader.
+ Useful for upgrade processes that require upgrading
+ the bootloader and then upgrading other partitions
+ using the new bootloader.
+
+ "powerdown" Power off the device.
+
+
+
+Client Variables
+----------------
+
+The "getvar:%s" command is used to read client variables which
+represent various information about the device and the software
+on it.
+
+The various currently defined names are:
+
+ version Version of FastBoot protocol supported.
+ It should be "0.3" for this document.
+
+ version-bootloader Version string for the Bootloader.
+
+ version-baseband Version string of the Baseband Software
+
+ product Name of the product
+
+ serialno Product serial number
+
+ secure If the value is "yes", this is a secure
+ bootloader requiring a signature before
+ it will install or boot images.
+
+Names starting with a lowercase character are reserved by this
+specification. OEM-specific names should not start with lowercase
+characters.
+
+
diff --git a/fastboot/fs.cpp b/fastboot/fs.cpp
new file mode 100644
index 0000000..d8f9e16
--- /dev/null
+++ b/fastboot/fs.cpp
@@ -0,0 +1,65 @@
+#include "fastboot.h"
+#include "make_ext4fs.h"
+#include "make_f2fs.h"
+#include "fs.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sparse/sparse.h>
+#include <unistd.h>
+
+#ifdef USE_MINGW
+#include <fcntl.h>
+#else
+#include <sys/mman.h>
+#endif
+
+
+
+static int generate_ext4_image(int fd, long long partSize)
+{
+ make_ext4fs_sparse_fd(fd, partSize, NULL, NULL);
+
+ return 0;
+}
+
+#ifdef USE_F2FS
+static int generate_f2fs_image(int fd, long long partSize)
+{
+ return make_f2fs_sparse_fd(fd, partSize, NULL, NULL);
+}
+#endif
+
+static const struct fs_generator {
+
+ const char* fs_type; //must match what fastboot reports for partition type
+ int (*generate)(int fd, long long partSize); //returns 0 or error value
+
+} generators[] = {
+ { "ext4", generate_ext4_image},
+#ifdef USE_F2FS
+ { "f2fs", generate_f2fs_image},
+#endif
+};
+
+const struct fs_generator* fs_get_generator(const char *fs_type)
+{
+ unsigned i;
+
+ for (i = 0; i < sizeof(generators) / sizeof(*generators); i++)
+ if (!strcmp(generators[i].fs_type, fs_type))
+ return generators + i;
+
+ return NULL;
+}
+
+int fs_generator_generate(const struct fs_generator* gen, int tmpFileNo, long long partSize)
+{
+ return gen->generate(tmpFileNo, partSize);
+}
diff --git a/fastboot/fs.h b/fastboot/fs.h
new file mode 100644
index 0000000..307772b
--- /dev/null
+++ b/fastboot/fs.h
@@ -0,0 +1,20 @@
+#ifndef _FS_H_
+#define _FS_H_
+
+#include <stdint.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct fs_generator;
+
+const struct fs_generator* fs_get_generator(const char *fs_type);
+int fs_generator_generate(const struct fs_generator* gen, int tmpFileNo, long long partSize);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
+
diff --git a/fastboot/genkey.sh b/fastboot/genkey.sh
new file mode 100755
index 0000000..011e902
--- /dev/null
+++ b/fastboot/genkey.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+if [ $# -ne 2 ]
+then
+ echo "Usage: $0 alias \"pass phrase\""
+ exit -1
+fi
+
+# Generate a 2048 bit RSA key with public exponent 3.
+# Encrypt private key with provided password.
+openssl genrsa -3 -out $1.pem -passout pass:"$2" 2048
+
+# Create a self-signed cert for this key.
+openssl req -new -x509 -key $1.pem -passin pass:"$2" \
+ -out $1-cert.pem \
+ -batch -days 10000
+
+# Create a PKCS12 store containing the generated private key.
+# Protect the keystore and the private key with the provided password.
+openssl pkcs12 -export -in $1-cert.pem -inkey $1.pem -passin pass:"$2" \
+ -out $1.p12 -name $1 -passout pass:"$2"
+
+rm $1.pem
+rm $1-cert.pem
+
diff --git a/fastboot/protocol.cpp b/fastboot/protocol.cpp
new file mode 100644
index 0000000..00c8a03
--- /dev/null
+++ b/fastboot/protocol.cpp
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define min(a, b) \
+ ({ typeof(a) _a = (a); typeof(b) _b = (b); (_a < _b) ? _a : _b; })
+#define round_down(a, b) \
+ ({ typeof(a) _a = (a); typeof(b) _b = (b); _a - (_a % _b); })
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sparse/sparse.h>
+
+#include "fastboot.h"
+
+static char ERROR[128];
+
+char *fb_get_error(void)
+{
+ return ERROR;
+}
+
+static int check_response(usb_handle *usb, unsigned int size, char *response)
+{
+ unsigned char status[65];
+ int r;
+
+ for(;;) {
+ r = usb_read(usb, status, 64);
+ if(r < 0) {
+ sprintf(ERROR, "status read failed (%s)", strerror(errno));
+ usb_close(usb);
+ return -1;
+ }
+ status[r] = 0;
+
+ if(r < 4) {
+ sprintf(ERROR, "status malformed (%d bytes)", r);
+ usb_close(usb);
+ return -1;
+ }
+
+ if(!memcmp(status, "INFO", 4)) {
+ fprintf(stderr,"(bootloader) %s\n", status + 4);
+ continue;
+ }
+
+ if(!memcmp(status, "OKAY", 4)) {
+ if(response) {
+ strcpy(response, (char*) status + 4);
+ }
+ return 0;
+ }
+
+ if(!memcmp(status, "FAIL", 4)) {
+ if(r > 4) {
+ sprintf(ERROR, "remote: %s", status + 4);
+ } else {
+ strcpy(ERROR, "remote failure");
+ }
+ return -1;
+ }
+
+ if(!memcmp(status, "DATA", 4) && size > 0){
+ unsigned dsize = strtoul((char*) status + 4, 0, 16);
+ if(dsize > size) {
+ strcpy(ERROR, "data size too large");
+ usb_close(usb);
+ return -1;
+ }
+ return dsize;
+ }
+
+ strcpy(ERROR,"unknown status code");
+ usb_close(usb);
+ break;
+ }
+
+ return -1;
+}
+
+static int _command_start(usb_handle *usb, const char *cmd, unsigned size,
+ char *response)
+{
+ int cmdsize = strlen(cmd);
+
+ if(response) {
+ response[0] = 0;
+ }
+
+ if(cmdsize > 64) {
+ sprintf(ERROR,"command too large");
+ return -1;
+ }
+
+ if(usb_write(usb, cmd, cmdsize) != cmdsize) {
+ sprintf(ERROR,"command write failed (%s)", strerror(errno));
+ usb_close(usb);
+ return -1;
+ }
+
+ return check_response(usb, size, response);
+}
+
+static int _command_data(usb_handle *usb, const void *data, unsigned size)
+{
+ int r;
+
+ r = usb_write(usb, data, size);
+ if(r < 0) {
+ sprintf(ERROR, "data transfer failure (%s)", strerror(errno));
+ usb_close(usb);
+ return -1;
+ }
+ if(r != ((int) size)) {
+ sprintf(ERROR, "data transfer failure (short transfer)");
+ usb_close(usb);
+ return -1;
+ }
+
+ return r;
+}
+
+static int _command_end(usb_handle *usb)
+{
+ int r;
+ r = check_response(usb, 0, 0);
+ if(r < 0) {
+ return -1;
+ }
+ return 0;
+}
+
+static int _command_send(usb_handle *usb, const char *cmd,
+ const void *data, unsigned size,
+ char *response)
+{
+ int r;
+ if (size == 0) {
+ return -1;
+ }
+
+ r = _command_start(usb, cmd, size, response);
+ if (r < 0) {
+ return -1;
+ }
+
+ r = _command_data(usb, data, size);
+ if (r < 0) {
+ return -1;
+ }
+
+ r = _command_end(usb);
+ if(r < 0) {
+ return -1;
+ }
+
+ return size;
+}
+
+static int _command_send_no_data(usb_handle *usb, const char *cmd,
+ char *response)
+{
+ return _command_start(usb, cmd, 0, response);
+}
+
+int fb_command(usb_handle *usb, const char *cmd)
+{
+ return _command_send_no_data(usb, cmd, 0);
+}
+
+int fb_command_response(usb_handle *usb, const char *cmd, char *response)
+{
+ return _command_send_no_data(usb, cmd, response);
+}
+
+int fb_download_data(usb_handle *usb, const void *data, unsigned size)
+{
+ char cmd[64];
+ int r;
+
+ sprintf(cmd, "download:%08x", size);
+ r = _command_send(usb, cmd, data, size, 0);
+
+ if(r < 0) {
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+#define USB_BUF_SIZE 1024
+static char usb_buf[USB_BUF_SIZE];
+static int usb_buf_len;
+
+static int fb_download_data_sparse_write(void *priv, const void *data, int len)
+{
+ int r;
+ usb_handle* usb = reinterpret_cast<usb_handle*>(priv);
+ int to_write;
+ const char* ptr = reinterpret_cast<const char*>(data);
+
+ if (usb_buf_len) {
+ to_write = min(USB_BUF_SIZE - usb_buf_len, len);
+
+ memcpy(usb_buf + usb_buf_len, ptr, to_write);
+ usb_buf_len += to_write;
+ ptr += to_write;
+ len -= to_write;
+ }
+
+ if (usb_buf_len == USB_BUF_SIZE) {
+ r = _command_data(usb, usb_buf, USB_BUF_SIZE);
+ if (r != USB_BUF_SIZE) {
+ return -1;
+ }
+ usb_buf_len = 0;
+ }
+
+ if (len > USB_BUF_SIZE) {
+ if (usb_buf_len > 0) {
+ sprintf(ERROR, "internal error: usb_buf not empty\n");
+ return -1;
+ }
+ to_write = round_down(len, USB_BUF_SIZE);
+ r = _command_data(usb, ptr, to_write);
+ if (r != to_write) {
+ return -1;
+ }
+ ptr += to_write;
+ len -= to_write;
+ }
+
+ if (len > 0) {
+ if (len > USB_BUF_SIZE) {
+ sprintf(ERROR, "internal error: too much left for usb_buf\n");
+ return -1;
+ }
+ memcpy(usb_buf, ptr, len);
+ usb_buf_len = len;
+ }
+
+ return 0;
+}
+
+static int fb_download_data_sparse_flush(usb_handle *usb)
+{
+ int r;
+
+ if (usb_buf_len > 0) {
+ r = _command_data(usb, usb_buf, usb_buf_len);
+ if (r != usb_buf_len) {
+ return -1;
+ }
+ usb_buf_len = 0;
+ }
+
+ return 0;
+}
+
+int fb_download_data_sparse(usb_handle *usb, struct sparse_file *s)
+{
+ char cmd[64];
+ int r;
+ int size = sparse_file_len(s, true, false);
+ if (size <= 0) {
+ return -1;
+ }
+
+ sprintf(cmd, "download:%08x", size);
+ r = _command_start(usb, cmd, size, 0);
+ if (r < 0) {
+ return -1;
+ }
+
+ r = sparse_file_callback(s, true, false, fb_download_data_sparse_write, usb);
+ if (r < 0) {
+ return -1;
+ }
+
+ r = fb_download_data_sparse_flush(usb);
+ if (r < 0) {
+ return -1;
+ }
+
+ return _command_end(usb);
+}
diff --git a/fastboot/usb.h b/fastboot/usb.h
new file mode 100644
index 0000000..c7b748e
--- /dev/null
+++ b/fastboot/usb.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _USB_H_
+#define _USB_H_
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef struct usb_handle usb_handle;
+
+typedef struct usb_ifc_info usb_ifc_info;
+
+struct usb_ifc_info
+{
+ /* from device descriptor */
+ unsigned short dev_vendor;
+ unsigned short dev_product;
+
+ unsigned char dev_class;
+ unsigned char dev_subclass;
+ unsigned char dev_protocol;
+
+ unsigned char ifc_class;
+ unsigned char ifc_subclass;
+ unsigned char ifc_protocol;
+
+ unsigned char has_bulk_in;
+ unsigned char has_bulk_out;
+
+ unsigned char writable;
+
+ char serial_number[256];
+ char device_path[256];
+};
+
+typedef int (*ifc_match_func)(usb_ifc_info *ifc);
+
+usb_handle *usb_open(ifc_match_func callback);
+int usb_close(usb_handle *h);
+int usb_read(usb_handle *h, void *_data, int len);
+int usb_write(usb_handle *h, const void *_data, int len);
+int usb_wait_for_disconnect(usb_handle *h);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/fastboot/usb_linux.cpp b/fastboot/usb_linux.cpp
new file mode 100644
index 0000000..9078c8f
--- /dev/null
+++ b/fastboot/usb_linux.cpp
@@ -0,0 +1,490 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <pthread.h>
+#include <ctype.h>
+
+#include <linux/usbdevice_fs.h>
+#include <linux/usbdevice_fs.h>
+#include <linux/version.h>
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20)
+#include <linux/usb/ch9.h>
+#else
+#include <linux/usb_ch9.h>
+#endif
+#include <asm/byteorder.h>
+
+#include "fastboot.h"
+#include "usb.h"
+
+#define MAX_RETRIES 5
+
+/* Timeout in seconds for usb_wait_for_disconnect.
+ * It doesn't usually take long for a device to disconnect (almost always
+ * under 2 seconds) but we'll time out after 3 seconds just in case.
+ */
+#define WAIT_FOR_DISCONNECT_TIMEOUT 3
+
+#ifdef TRACE_USB
+#define DBG1(x...) fprintf(stderr, x)
+#define DBG(x...) fprintf(stderr, x)
+#else
+#define DBG(x...)
+#define DBG1(x...)
+#endif
+
+/* The max bulk size for linux is 16384 which is defined
+ * in drivers/usb/core/devio.c.
+ */
+#define MAX_USBFS_BULK_SIZE (16 * 1024)
+
+struct usb_handle
+{
+ char fname[64];
+ int desc;
+ unsigned char ep_in;
+ unsigned char ep_out;
+};
+
+/* True if name isn't a valid name for a USB device in /sys/bus/usb/devices.
+ * Device names are made up of numbers, dots, and dashes, e.g., '7-1.5'.
+ * We reject interfaces (e.g., '7-1.5:1.0') and host controllers (e.g. 'usb1').
+ * The name must also start with a digit, to disallow '.' and '..'
+ */
+static inline int badname(const char *name)
+{
+ if (!isdigit(*name))
+ return 1;
+ while(*++name) {
+ if(!isdigit(*name) && *name != '.' && *name != '-')
+ return 1;
+ }
+ return 0;
+}
+
+static int check(void *_desc, int len, unsigned type, int size)
+{
+ struct usb_descriptor_header *hdr = (struct usb_descriptor_header *)_desc;
+
+ if(len < size) return -1;
+ if(hdr->bLength < size) return -1;
+ if(hdr->bLength > len) return -1;
+ if(hdr->bDescriptorType != type) return -1;
+
+ return 0;
+}
+
+static int filter_usb_device(char* sysfs_name,
+ char *ptr, int len, int writable,
+ ifc_match_func callback,
+ int *ept_in_id, int *ept_out_id, int *ifc_id)
+{
+ struct usb_device_descriptor *dev;
+ struct usb_config_descriptor *cfg;
+ struct usb_interface_descriptor *ifc;
+ struct usb_endpoint_descriptor *ept;
+ struct usb_ifc_info info;
+
+ int in, out;
+ unsigned i;
+ unsigned e;
+
+ if (check(ptr, len, USB_DT_DEVICE, USB_DT_DEVICE_SIZE))
+ return -1;
+ dev = (struct usb_device_descriptor *)ptr;
+ len -= dev->bLength;
+ ptr += dev->bLength;
+
+ if (check(ptr, len, USB_DT_CONFIG, USB_DT_CONFIG_SIZE))
+ return -1;
+ cfg = (struct usb_config_descriptor *)ptr;
+ len -= cfg->bLength;
+ ptr += cfg->bLength;
+
+ info.dev_vendor = dev->idVendor;
+ info.dev_product = dev->idProduct;
+ info.dev_class = dev->bDeviceClass;
+ info.dev_subclass = dev->bDeviceSubClass;
+ info.dev_protocol = dev->bDeviceProtocol;
+ info.writable = writable;
+
+ snprintf(info.device_path, sizeof(info.device_path), "usb:%s", sysfs_name);
+
+ /* Read device serial number (if there is one).
+ * We read the serial number from sysfs, since it's faster and more
+ * reliable than issuing a control pipe read, and also won't
+ * cause problems for devices which don't like getting descriptor
+ * requests while they're in the middle of flashing.
+ */
+ info.serial_number[0] = '\0';
+ if (dev->iSerialNumber) {
+ char path[80];
+ int fd;
+
+ snprintf(path, sizeof(path),
+ "/sys/bus/usb/devices/%s/serial", sysfs_name);
+ path[sizeof(path) - 1] = '\0';
+
+ fd = open(path, O_RDONLY);
+ if (fd >= 0) {
+ int chars_read = read(fd, info.serial_number,
+ sizeof(info.serial_number) - 1);
+ close(fd);
+
+ if (chars_read <= 0)
+ info.serial_number[0] = '\0';
+ else if (info.serial_number[chars_read - 1] == '\n') {
+ // strip trailing newline
+ info.serial_number[chars_read - 1] = '\0';
+ }
+ }
+ }
+
+ for(i = 0; i < cfg->bNumInterfaces; i++) {
+
+ while (len > 0) {
+ struct usb_descriptor_header *hdr = (struct usb_descriptor_header *)ptr;
+ if (check(hdr, len, USB_DT_INTERFACE, USB_DT_INTERFACE_SIZE) == 0)
+ break;
+ len -= hdr->bLength;
+ ptr += hdr->bLength;
+ }
+
+ if (len <= 0)
+ return -1;
+
+ ifc = (struct usb_interface_descriptor *)ptr;
+ len -= ifc->bLength;
+ ptr += ifc->bLength;
+
+ in = -1;
+ out = -1;
+ info.ifc_class = ifc->bInterfaceClass;
+ info.ifc_subclass = ifc->bInterfaceSubClass;
+ info.ifc_protocol = ifc->bInterfaceProtocol;
+
+ for(e = 0; e < ifc->bNumEndpoints; e++) {
+ while (len > 0) {
+ struct usb_descriptor_header *hdr = (struct usb_descriptor_header *)ptr;
+ if (check(hdr, len, USB_DT_ENDPOINT, USB_DT_ENDPOINT_SIZE) == 0)
+ break;
+ len -= hdr->bLength;
+ ptr += hdr->bLength;
+ }
+ if (len < 0) {
+ break;
+ }
+
+ ept = (struct usb_endpoint_descriptor *)ptr;
+ len -= ept->bLength;
+ ptr += ept->bLength;
+
+ if((ept->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK)
+ continue;
+
+ if(ept->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
+ in = ept->bEndpointAddress;
+ } else {
+ out = ept->bEndpointAddress;
+ }
+
+ // For USB 3.0 devices skip the SS Endpoint Companion descriptor
+ if (check((struct usb_descriptor_hdr *)ptr, len,
+ USB_DT_SS_ENDPOINT_COMP, USB_DT_SS_EP_COMP_SIZE) == 0) {
+ len -= USB_DT_SS_EP_COMP_SIZE;
+ ptr += USB_DT_SS_EP_COMP_SIZE;
+ }
+ }
+
+ info.has_bulk_in = (in != -1);
+ info.has_bulk_out = (out != -1);
+
+ if(callback(&info) == 0) {
+ *ept_in_id = in;
+ *ept_out_id = out;
+ *ifc_id = ifc->bInterfaceNumber;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+static int read_sysfs_string(const char *sysfs_name, const char *sysfs_node,
+ char* buf, int bufsize)
+{
+ char path[80];
+ int fd, n;
+
+ snprintf(path, sizeof(path),
+ "/sys/bus/usb/devices/%s/%s", sysfs_name, sysfs_node);
+ path[sizeof(path) - 1] = '\0';
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+ n = read(fd, buf, bufsize - 1);
+ close(fd);
+
+ if (n < 0)
+ return -1;
+
+ buf[n] = '\0';
+
+ return n;
+}
+
+static int read_sysfs_number(const char *sysfs_name, const char *sysfs_node)
+{
+ char buf[16];
+ int value;
+
+ if (read_sysfs_string(sysfs_name, sysfs_node, buf, sizeof(buf)) < 0)
+ return -1;
+
+ if (sscanf(buf, "%d", &value) != 1)
+ return -1;
+
+ return value;
+}
+
+/* Given the name of a USB device in sysfs, get the name for the same
+ * device in devfs. Returns 0 for success, -1 for failure.
+ */
+static int convert_to_devfs_name(const char* sysfs_name,
+ char* devname, int devname_size)
+{
+ int busnum, devnum;
+
+ busnum = read_sysfs_number(sysfs_name, "busnum");
+ if (busnum < 0)
+ return -1;
+
+ devnum = read_sysfs_number(sysfs_name, "devnum");
+ if (devnum < 0)
+ return -1;
+
+ snprintf(devname, devname_size, "/dev/bus/usb/%03d/%03d", busnum, devnum);
+ return 0;
+}
+
+static usb_handle *find_usb_device(const char *base, ifc_match_func callback)
+{
+ usb_handle *usb = 0;
+ char devname[64];
+ char desc[1024];
+ int n, in, out, ifc;
+
+ DIR *busdir;
+ struct dirent *de;
+ int fd;
+ int writable;
+
+ busdir = opendir(base);
+ if(busdir == 0) return 0;
+
+ while((de = readdir(busdir)) && (usb == 0)) {
+ if(badname(de->d_name)) continue;
+
+ if(!convert_to_devfs_name(de->d_name, devname, sizeof(devname))) {
+
+// DBG("[ scanning %s ]\n", devname);
+ writable = 1;
+ if((fd = open(devname, O_RDWR)) < 0) {
+ // Check if we have read-only access, so we can give a helpful
+ // diagnostic like "adb devices" does.
+ writable = 0;
+ if((fd = open(devname, O_RDONLY)) < 0) {
+ continue;
+ }
+ }
+
+ n = read(fd, desc, sizeof(desc));
+
+ if(filter_usb_device(de->d_name, desc, n, writable, callback,
+ &in, &out, &ifc) == 0) {
+ usb = reinterpret_cast<usb_handle*>(calloc(1, sizeof(usb_handle)));
+ strcpy(usb->fname, devname);
+ usb->ep_in = in;
+ usb->ep_out = out;
+ usb->desc = fd;
+
+ n = ioctl(fd, USBDEVFS_CLAIMINTERFACE, &ifc);
+ if(n != 0) {
+ close(fd);
+ free(usb);
+ usb = 0;
+ continue;
+ }
+ } else {
+ close(fd);
+ }
+ }
+ }
+ closedir(busdir);
+
+ return usb;
+}
+
+int usb_write(usb_handle *h, const void *_data, int len)
+{
+ unsigned char *data = (unsigned char*) _data;
+ unsigned count = 0;
+ struct usbdevfs_bulktransfer bulk;
+ int n;
+
+ if(h->ep_out == 0 || h->desc == -1) {
+ return -1;
+ }
+
+ do {
+ int xfer;
+ xfer = (len > MAX_USBFS_BULK_SIZE) ? MAX_USBFS_BULK_SIZE : len;
+
+ bulk.ep = h->ep_out;
+ bulk.len = xfer;
+ bulk.data = data;
+ bulk.timeout = 0;
+
+ n = ioctl(h->desc, USBDEVFS_BULK, &bulk);
+ if(n != xfer) {
+ DBG("ERROR: n = %d, errno = %d (%s)\n",
+ n, errno, strerror(errno));
+ return -1;
+ }
+
+ count += xfer;
+ len -= xfer;
+ data += xfer;
+ } while(len > 0);
+
+ return count;
+}
+
+int usb_read(usb_handle *h, void *_data, int len)
+{
+ unsigned char *data = (unsigned char*) _data;
+ unsigned count = 0;
+ struct usbdevfs_bulktransfer bulk;
+ int n, retry;
+
+ if(h->ep_in == 0 || h->desc == -1) {
+ return -1;
+ }
+
+ while(len > 0) {
+ int xfer = (len > MAX_USBFS_BULK_SIZE) ? MAX_USBFS_BULK_SIZE : len;
+
+ bulk.ep = h->ep_in;
+ bulk.len = xfer;
+ bulk.data = data;
+ bulk.timeout = 0;
+ retry = 0;
+
+ do{
+ DBG("[ usb read %d fd = %d], fname=%s\n", xfer, h->desc, h->fname);
+ n = ioctl(h->desc, USBDEVFS_BULK, &bulk);
+ DBG("[ usb read %d ] = %d, fname=%s, Retry %d \n", xfer, n, h->fname, retry);
+
+ if( n < 0 ) {
+ DBG1("ERROR: n = %d, errno = %d (%s)\n",n, errno, strerror(errno));
+ if ( ++retry > MAX_RETRIES ) return -1;
+ sleep( 1 );
+ }
+ }
+ while( n < 0 );
+
+ count += n;
+ len -= n;
+ data += n;
+
+ if(n < xfer) {
+ break;
+ }
+ }
+
+ return count;
+}
+
+void usb_kick(usb_handle *h)
+{
+ int fd;
+
+ fd = h->desc;
+ h->desc = -1;
+ if(fd >= 0) {
+ close(fd);
+ DBG("[ usb closed %d ]\n", fd);
+ }
+}
+
+int usb_close(usb_handle *h)
+{
+ int fd;
+
+ fd = h->desc;
+ h->desc = -1;
+ if(fd >= 0) {
+ close(fd);
+ DBG("[ usb closed %d ]\n", fd);
+ }
+
+ return 0;
+}
+
+usb_handle *usb_open(ifc_match_func callback)
+{
+ return find_usb_device("/sys/bus/usb/devices", callback);
+}
+
+/* Wait for the system to notice the device is gone, so that a subsequent
+ * fastboot command won't try to access the device before it's rebooted.
+ * Returns 0 for success, -1 for timeout.
+ */
+int usb_wait_for_disconnect(usb_handle *usb)
+{
+ double deadline = now() + WAIT_FOR_DISCONNECT_TIMEOUT;
+ while (now() < deadline) {
+ if (access(usb->fname, F_OK))
+ return 0;
+ usleep(50000);
+ }
+ return -1;
+}
diff --git a/fastboot/usb_osx.cpp b/fastboot/usb_osx.cpp
new file mode 100644
index 0000000..a959566
--- /dev/null
+++ b/fastboot/usb_osx.cpp
@@ -0,0 +1,572 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/IOCFPlugIn.h>
+#include <IOKit/usb/IOUSBLib.h>
+#include <IOKit/IOMessage.h>
+#include <mach/mach_port.h>
+
+#include "usb.h"
+
+
+/*
+ * Internal helper functions and associated definitions.
+ */
+
+#if TRACE_USB
+#define WARN(x...) fprintf(stderr, x)
+#else
+#define WARN(x...)
+#endif
+
+#define ERR(x...) fprintf(stderr, "ERROR: " x)
+
+/** An open usb device */
+struct usb_handle
+{
+ int success;
+ ifc_match_func callback;
+ usb_ifc_info info;
+
+ UInt8 bulkIn;
+ UInt8 bulkOut;
+ IOUSBInterfaceInterface190 **interface;
+ unsigned int zero_mask;
+};
+
+/** Try out all the interfaces and see if there's a match. Returns 0 on
+ * success, -1 on failure. */
+static int try_interfaces(IOUSBDeviceInterface182 **dev, usb_handle *handle) {
+ IOReturn kr;
+ IOUSBFindInterfaceRequest request;
+ io_iterator_t iterator;
+ io_service_t usbInterface;
+ IOCFPlugInInterface **plugInInterface;
+ IOUSBInterfaceInterface190 **interface = NULL;
+ HRESULT result;
+ SInt32 score;
+ UInt8 interfaceNumEndpoints;
+ UInt8 endpoint;
+ UInt8 configuration;
+
+ // Placing the constant KIOUSBFindInterfaceDontCare into the following
+ // fields of the IOUSBFindInterfaceRequest structure will allow us to
+ // find all of the interfaces
+ request.bInterfaceClass = kIOUSBFindInterfaceDontCare;
+ request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
+ request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
+ request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
+
+ // SetConfiguration will kill an existing UMS connection, so let's
+ // not do this if not necessary.
+ configuration = 0;
+ (*dev)->GetConfiguration(dev, &configuration);
+ if (configuration != 1)
+ (*dev)->SetConfiguration(dev, 1);
+
+ // Get an iterator for the interfaces on the device
+ kr = (*dev)->CreateInterfaceIterator(dev, &request, &iterator);
+
+ if (kr != 0) {
+ ERR("Couldn't create a device interface iterator: (%08x)\n", kr);
+ return -1;
+ }
+
+ while ((usbInterface = IOIteratorNext(iterator))) {
+ // Create an intermediate plugin
+ kr = IOCreatePlugInInterfaceForService(
+ usbInterface,
+ kIOUSBInterfaceUserClientTypeID,
+ kIOCFPlugInInterfaceID,
+ &plugInInterface,
+ &score);
+
+ // No longer need the usbInterface object now that we have the plugin
+ (void) IOObjectRelease(usbInterface);
+
+ if ((kr != 0) || (!plugInInterface)) {
+ WARN("Unable to create plugin (%08x)\n", kr);
+ continue;
+ }
+
+ // Now create the interface interface for the interface
+ result = (*plugInInterface)->QueryInterface(
+ plugInInterface,
+ CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID),
+ (LPVOID*) &interface);
+
+ // No longer need the intermediate plugin
+ (*plugInInterface)->Release(plugInInterface);
+
+ if (result || !interface) {
+ ERR("Couldn't create interface interface: (%08x)\n",
+ (unsigned int) result);
+ // continue so we can try the next interface
+ continue;
+ }
+
+ /*
+ * Now open the interface. This will cause the pipes
+ * associated with the endpoints in the interface descriptor
+ * to be instantiated.
+ */
+
+ /*
+ * TODO: Earlier comments here indicated that it was a bad
+ * idea to just open any interface, because opening "mass
+ * storage endpoints" is bad. However, the only way to find
+ * out if an interface does bulk in or out is to open it, and
+ * the framework in this application wants to be told about
+ * bulk in / out before deciding whether it actually wants to
+ * use the interface. Maybe something needs to be done about
+ * this situation.
+ */
+
+ kr = (*interface)->USBInterfaceOpen(interface);
+
+ if (kr != 0) {
+ WARN("Could not open interface: (%08x)\n", kr);
+ (void) (*interface)->Release(interface);
+ // continue so we can try the next interface
+ continue;
+ }
+
+ // Get the number of endpoints associated with this interface.
+ kr = (*interface)->GetNumEndpoints(interface, &interfaceNumEndpoints);
+
+ if (kr != 0) {
+ ERR("Unable to get number of endpoints: (%08x)\n", kr);
+ goto next_interface;
+ }
+
+ // Get interface class, subclass and protocol
+ if ((*interface)->GetInterfaceClass(interface, &handle->info.ifc_class) != 0 ||
+ (*interface)->GetInterfaceSubClass(interface, &handle->info.ifc_subclass) != 0 ||
+ (*interface)->GetInterfaceProtocol(interface, &handle->info.ifc_protocol) != 0)
+ {
+ ERR("Unable to get interface class, subclass and protocol\n");
+ goto next_interface;
+ }
+
+ handle->info.has_bulk_in = 0;
+ handle->info.has_bulk_out = 0;
+
+ // Iterate over the endpoints for this interface and see if there
+ // are any that do bulk in/out.
+ for (endpoint = 0; endpoint <= interfaceNumEndpoints; endpoint++) {
+ UInt8 transferType;
+ UInt16 maxPacketSize;
+ UInt8 interval;
+ UInt8 number;
+ UInt8 direction;
+
+ kr = (*interface)->GetPipeProperties(interface, endpoint,
+ &direction,
+ &number, &transferType, &maxPacketSize, &interval);
+
+ if (kr == 0) {
+ if (transferType != kUSBBulk) {
+ continue;
+ }
+
+ if (direction == kUSBIn) {
+ handle->info.has_bulk_in = 1;
+ handle->bulkIn = endpoint;
+ } else if (direction == kUSBOut) {
+ handle->info.has_bulk_out = 1;
+ handle->bulkOut = endpoint;
+ }
+
+ if (handle->info.ifc_protocol == 0x01) {
+ handle->zero_mask = maxPacketSize - 1;
+ }
+ } else {
+ ERR("could not get pipe properties\n");
+ }
+
+ if (handle->info.has_bulk_in && handle->info.has_bulk_out) {
+ break;
+ }
+ }
+
+ if (handle->callback(&handle->info) == 0) {
+ handle->interface = interface;
+ handle->success = 1;
+
+ /*
+ * Clear both the endpoints, because it has been observed
+ * that the Mac may otherwise (incorrectly) start out with
+ * them in bad state.
+ */
+
+ if (handle->info.has_bulk_in) {
+ kr = (*interface)->ClearPipeStallBothEnds(interface,
+ handle->bulkIn);
+ if (kr != 0) {
+ ERR("could not clear input pipe; result %x, ignoring...\n", kr);
+ }
+ }
+
+ if (handle->info.has_bulk_out) {
+ kr = (*interface)->ClearPipeStallBothEnds(interface,
+ handle->bulkOut);
+ if (kr != 0) {
+ ERR("could not clear output pipe; result %x, ignoring....\n", kr);
+ }
+ }
+
+ return 0;
+ }
+
+next_interface:
+ (*interface)->USBInterfaceClose(interface);
+ (*interface)->Release(interface);
+ }
+
+ return 0;
+}
+
+/** Try out the given device and see if there's a match. Returns 0 on
+ * success, -1 on failure.
+ */
+static int try_device(io_service_t device, usb_handle *handle) {
+ kern_return_t kr;
+ IOCFPlugInInterface **plugin = NULL;
+ IOUSBDeviceInterface182 **dev = NULL;
+ SInt32 score;
+ HRESULT result;
+ UInt8 serialIndex;
+ UInt32 locationId;
+
+ // Create an intermediate plugin.
+ kr = IOCreatePlugInInterfaceForService(device,
+ kIOUSBDeviceUserClientTypeID,
+ kIOCFPlugInInterfaceID,
+ &plugin, &score);
+
+ if ((kr != 0) || (plugin == NULL)) {
+ ERR("Unable to create a plug-in (%08x)\n", kr);
+ goto error;
+ }
+
+ // Now create the device interface.
+ result = (*plugin)->QueryInterface(plugin,
+ CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID*) &dev);
+ if ((result != 0) || (dev == NULL)) {
+ ERR("Couldn't create a device interface (%08x)\n", (int) result);
+ goto error;
+ }
+
+ /*
+ * We don't need the intermediate interface after the device interface
+ * is created.
+ */
+ IODestroyPlugInInterface(plugin);
+
+ // So, we have a device, finally. Grab its vitals.
+
+ kr = (*dev)->GetDeviceVendor(dev, &handle->info.dev_vendor);
+ if (kr != 0) {
+ ERR("GetDeviceVendor");
+ goto error;
+ }
+
+ kr = (*dev)->GetDeviceProduct(dev, &handle->info.dev_product);
+ if (kr != 0) {
+ ERR("GetDeviceProduct");
+ goto error;
+ }
+
+ kr = (*dev)->GetDeviceClass(dev, &handle->info.dev_class);
+ if (kr != 0) {
+ ERR("GetDeviceClass");
+ goto error;
+ }
+
+ kr = (*dev)->GetDeviceSubClass(dev, &handle->info.dev_subclass);
+ if (kr != 0) {
+ ERR("GetDeviceSubClass");
+ goto error;
+ }
+
+ kr = (*dev)->GetDeviceProtocol(dev, &handle->info.dev_protocol);
+ if (kr != 0) {
+ ERR("GetDeviceProtocol");
+ goto error;
+ }
+
+ kr = (*dev)->GetLocationID(dev, &locationId);
+ if (kr != 0) {
+ ERR("GetLocationId");
+ goto error;
+ }
+ snprintf(handle->info.device_path, sizeof(handle->info.device_path),
+ "usb:%" PRIu32 "X", (unsigned int)locationId);
+
+ kr = (*dev)->USBGetSerialNumberStringIndex(dev, &serialIndex);
+
+ if (serialIndex > 0) {
+ IOUSBDevRequest req;
+ UInt16 buffer[256];
+
+ req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
+ req.bRequest = kUSBRqGetDescriptor;
+ req.wValue = (kUSBStringDesc << 8) | serialIndex;
+ //language ID (en-us) for serial number string
+ req.wIndex = 0x0409;
+ req.pData = buffer;
+ req.wLength = sizeof(buffer);
+ kr = (*dev)->DeviceRequest(dev, &req);
+
+ if (kr == kIOReturnSuccess && req.wLenDone > 0) {
+ int i, count;
+
+ // skip first word, and copy the rest to the serial string, changing shorts to bytes.
+ count = (req.wLenDone - 1) / 2;
+ for (i = 0; i < count; i++)
+ handle->info.serial_number[i] = buffer[i + 1];
+ handle->info.serial_number[i] = 0;
+ }
+ } else {
+ // device has no serial number
+ handle->info.serial_number[0] = 0;
+ }
+ handle->info.writable = 1;
+
+ if (try_interfaces(dev, handle)) {
+ goto error;
+ }
+
+ (*dev)->Release(dev);
+ return 0;
+
+ error:
+
+ if (dev != NULL) {
+ (*dev)->Release(dev);
+ }
+
+ return -1;
+}
+
+
+/** Initializes the USB system. Returns 0 on success, -1 on error. */
+static int init_usb(ifc_match_func callback, usb_handle **handle) {
+ int ret = -1;
+ CFMutableDictionaryRef matchingDict;
+ kern_return_t result;
+ io_iterator_t iterator;
+ usb_handle h;
+
+ h.success = 0;
+ h.callback = callback;
+
+ /*
+ * Create our matching dictionary to find appropriate devices.
+ * IOServiceAddMatchingNotification consumes the reference, so we
+ * do not need to release it.
+ */
+ matchingDict = IOServiceMatching(kIOUSBDeviceClassName);
+
+ if (matchingDict == NULL) {
+ ERR("Couldn't create USB matching dictionary.\n");
+ return -1;
+ }
+
+ result = IOServiceGetMatchingServices(
+ kIOMasterPortDefault, matchingDict, &iterator);
+
+ if (result != 0) {
+ ERR("Could not create iterator.");
+ return -1;
+ }
+
+ for (;;) {
+ if (! IOIteratorIsValid(iterator)) {
+ /*
+ * Apple documentation advises resetting the iterator if
+ * it should become invalid during iteration.
+ */
+ IOIteratorReset(iterator);
+ continue;
+ }
+
+ io_service_t device = IOIteratorNext(iterator);
+
+ if (device == 0) {
+ break;
+ }
+
+ if (try_device(device, &h) != 0) {
+ IOObjectRelease(device);
+ ret = -1;
+ break;
+ }
+
+ if (h.success) {
+ *handle = reinterpret_cast<usb_handle*>(calloc(1, sizeof(usb_handle)));
+ memcpy(*handle, &h, sizeof(usb_handle));
+ ret = 0;
+ break;
+ }
+
+ IOObjectRelease(device);
+ }
+
+ IOObjectRelease(iterator);
+
+ return ret;
+}
+
+
+
+/*
+ * Definitions of this file's public functions.
+ */
+
+usb_handle *usb_open(ifc_match_func callback) {
+ usb_handle *handle = NULL;
+
+ if (init_usb(callback, &handle) < 0) {
+ /* Something went wrong initializing USB. */
+ return NULL;
+ }
+
+ return handle;
+}
+
+int usb_close(usb_handle *h) {
+ /* TODO: Something better here? */
+ return 0;
+}
+
+int usb_wait_for_disconnect(usb_handle *usb) {
+ /* TODO: Punt for now */
+ return 0;
+}
+
+int usb_read(usb_handle *h, void *data, int len) {
+ IOReturn result;
+ UInt32 numBytes = len;
+
+ if (len == 0) {
+ return 0;
+ }
+
+ if (h == NULL) {
+ return -1;
+ }
+
+ if (h->interface == NULL) {
+ ERR("usb_read interface was null\n");
+ return -1;
+ }
+
+ if (h->bulkIn == 0) {
+ ERR("bulkIn endpoint not assigned\n");
+ return -1;
+ }
+
+ result = (*h->interface)->ReadPipe(
+ h->interface, h->bulkIn, data, &numBytes);
+
+ if (result == 0) {
+ return (int) numBytes;
+ } else {
+ ERR("usb_read failed with status %x\n", result);
+ }
+
+ return -1;
+}
+
+int usb_write(usb_handle *h, const void *data, int len) {
+ IOReturn result;
+
+ if (len == 0) {
+ return 0;
+ }
+
+ if (h == NULL) {
+ return -1;
+ }
+
+ if (h->interface == NULL) {
+ ERR("usb_write interface was null\n");
+ return -1;
+ }
+
+ if (h->bulkOut == 0) {
+ ERR("bulkOut endpoint not assigned\n");
+ return -1;
+ }
+
+#if 0
+ result = (*h->interface)->WritePipe(
+ h->interface, h->bulkOut, (void *)data, len);
+#else
+ /* Attempt to work around crashes in the USB driver that may be caused
+ * by trying to write too much data at once. The kernel IOCopyMapper
+ * panics if a single iovmAlloc needs more than half of its mapper pages.
+ */
+ const int maxLenToSend = 1048576; // 1 MiB
+ int lenRemaining = len;
+ result = 0;
+ while (lenRemaining > 0) {
+ int lenToSend = lenRemaining > maxLenToSend
+ ? maxLenToSend : lenRemaining;
+
+ result = (*h->interface)->WritePipe(
+ h->interface, h->bulkOut, (void *)data, lenToSend);
+ if (result != 0) break;
+
+ lenRemaining -= lenToSend;
+ data = (const char*)data + lenToSend;
+ }
+#endif
+
+ #if 0
+ if ((result == 0) && (h->zero_mask)) {
+ /* we need 0-markers and our transfer */
+ if(!(len & h->zero_mask)) {
+ result = (*h->interface)->WritePipe(
+ h->interface, h->bulkOut, (void *)data, 0);
+ }
+ }
+ #endif
+
+ if (result != 0) {
+ ERR("usb_write failed with status %x\n", result);
+ return -1;
+ }
+
+ return len;
+}
diff --git a/fastboot/usb_windows.cpp b/fastboot/usb_windows.cpp
new file mode 100644
index 0000000..a09610f
--- /dev/null
+++ b/fastboot/usb_windows.cpp
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <windows.h>
+#include <winerror.h>
+#include <errno.h>
+#include <usb100.h>
+#include <adb_api.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "usb.h"
+
+//#define TRACE_USB 1
+#if TRACE_USB
+#define DBG(x...) fprintf(stderr, x)
+#else
+#define DBG(x...)
+#endif
+
+#define MAX_USBFS_BULK_SIZE (1024 * 1024)
+
+/** Structure usb_handle describes our connection to the usb device via
+ AdbWinApi.dll. This structure is returned from usb_open() routine and
+ is expected in each subsequent call that is accessing the device.
+*/
+struct usb_handle {
+ /// Handle to USB interface
+ ADBAPIHANDLE adb_interface;
+
+ /// Handle to USB read pipe (endpoint)
+ ADBAPIHANDLE adb_read_pipe;
+
+ /// Handle to USB write pipe (endpoint)
+ ADBAPIHANDLE adb_write_pipe;
+
+ /// Interface name
+ char* interface_name;
+};
+
+/// Class ID assigned to the device by androidusb.sys
+static const GUID usb_class_id = ANDROID_USB_CLASS_ID;
+
+
+/// Checks if interface (device) matches certain criteria
+int recognized_device(usb_handle* handle, ifc_match_func callback);
+
+/// Opens usb interface (device) by interface (device) name.
+usb_handle* do_usb_open(const wchar_t* interface_name);
+
+/// Writes data to the opened usb handle
+int usb_write(usb_handle* handle, const void* data, int len);
+
+/// Reads data using the opened usb handle
+int usb_read(usb_handle *handle, void* data, int len);
+
+/// Cleans up opened usb handle
+void usb_cleanup_handle(usb_handle* handle);
+
+/// Cleans up (but don't close) opened usb handle
+void usb_kick(usb_handle* handle);
+
+/// Closes opened usb handle
+int usb_close(usb_handle* handle);
+
+
+usb_handle* do_usb_open(const wchar_t* interface_name) {
+ // Allocate our handle
+ usb_handle* ret = (usb_handle*)malloc(sizeof(usb_handle));
+ if (NULL == ret)
+ return NULL;
+
+ // Create interface.
+ ret->adb_interface = AdbCreateInterfaceByName(interface_name);
+
+ if (NULL == ret->adb_interface) {
+ free(ret);
+ errno = GetLastError();
+ return NULL;
+ }
+
+ // Open read pipe (endpoint)
+ ret->adb_read_pipe =
+ AdbOpenDefaultBulkReadEndpoint(ret->adb_interface,
+ AdbOpenAccessTypeReadWrite,
+ AdbOpenSharingModeReadWrite);
+ if (NULL != ret->adb_read_pipe) {
+ // Open write pipe (endpoint)
+ ret->adb_write_pipe =
+ AdbOpenDefaultBulkWriteEndpoint(ret->adb_interface,
+ AdbOpenAccessTypeReadWrite,
+ AdbOpenSharingModeReadWrite);
+ if (NULL != ret->adb_write_pipe) {
+ // Save interface name
+ unsigned long name_len = 0;
+
+ // First get expected name length
+ AdbGetInterfaceName(ret->adb_interface,
+ NULL,
+ &name_len,
+ true);
+ if (0 != name_len) {
+ ret->interface_name = (char*)malloc(name_len);
+
+ if (NULL != ret->interface_name) {
+ // Now save the name
+ if (AdbGetInterfaceName(ret->adb_interface,
+ ret->interface_name,
+ &name_len,
+ true)) {
+ // We're done at this point
+ return ret;
+ }
+ } else {
+ SetLastError(ERROR_OUTOFMEMORY);
+ }
+ }
+ }
+ }
+
+ // Something went wrong.
+ errno = GetLastError();
+ usb_cleanup_handle(ret);
+ free(ret);
+ SetLastError(errno);
+
+ return NULL;
+}
+
+int usb_write(usb_handle* handle, const void* data, int len) {
+ unsigned long time_out = 5000;
+ unsigned long written = 0;
+ unsigned count = 0;
+ int ret;
+
+ DBG("usb_write %d\n", len);
+ if (NULL != handle) {
+ // Perform write
+ while(len > 0) {
+ int xfer = (len > MAX_USBFS_BULK_SIZE) ? MAX_USBFS_BULK_SIZE : len;
+ ret = AdbWriteEndpointSync(handle->adb_write_pipe,
+ (void*)data,
+ (unsigned long)xfer,
+ &written,
+ time_out);
+ errno = GetLastError();
+ DBG("AdbWriteEndpointSync returned %d, errno: %d\n", ret, errno);
+ if (ret == 0) {
+ // assume ERROR_INVALID_HANDLE indicates we are disconnected
+ if (errno == ERROR_INVALID_HANDLE)
+ usb_kick(handle);
+ return -1;
+ }
+
+ count += written;
+ len -= written;
+ data = (const char *)data + written;
+
+ if (len == 0)
+ return count;
+ }
+ } else {
+ DBG("usb_write NULL handle\n");
+ SetLastError(ERROR_INVALID_HANDLE);
+ }
+
+ DBG("usb_write failed: %d\n", errno);
+
+ return -1;
+}
+
+int usb_read(usb_handle *handle, void* data, int len) {
+ unsigned long time_out = 0;
+ unsigned long read = 0;
+ int ret;
+
+ DBG("usb_read %d\n", len);
+ if (NULL != handle) {
+ while (1) {
+ int xfer = (len > MAX_USBFS_BULK_SIZE) ? MAX_USBFS_BULK_SIZE : len;
+
+ ret = AdbReadEndpointSync(handle->adb_read_pipe,
+ (void*)data,
+ (unsigned long)xfer,
+ &read,
+ time_out);
+ errno = GetLastError();
+ DBG("usb_read got: %ld, expected: %d, errno: %d\n", read, xfer, errno);
+ if (ret) {
+ return read;
+ } else {
+ // assume ERROR_INVALID_HANDLE indicates we are disconnected
+ if (errno == ERROR_INVALID_HANDLE)
+ usb_kick(handle);
+ break;
+ }
+ // else we timed out - try again
+ }
+ } else {
+ DBG("usb_read NULL handle\n");
+ SetLastError(ERROR_INVALID_HANDLE);
+ }
+
+ DBG("usb_read failed: %d\n", errno);
+
+ return -1;
+}
+
+void usb_cleanup_handle(usb_handle* handle) {
+ if (NULL != handle) {
+ if (NULL != handle->interface_name)
+ free(handle->interface_name);
+ if (NULL != handle->adb_write_pipe)
+ AdbCloseHandle(handle->adb_write_pipe);
+ if (NULL != handle->adb_read_pipe)
+ AdbCloseHandle(handle->adb_read_pipe);
+ if (NULL != handle->adb_interface)
+ AdbCloseHandle(handle->adb_interface);
+
+ handle->interface_name = NULL;
+ handle->adb_write_pipe = NULL;
+ handle->adb_read_pipe = NULL;
+ handle->adb_interface = NULL;
+ }
+}
+
+void usb_kick(usb_handle* handle) {
+ if (NULL != handle) {
+ usb_cleanup_handle(handle);
+ } else {
+ SetLastError(ERROR_INVALID_HANDLE);
+ errno = ERROR_INVALID_HANDLE;
+ }
+}
+
+int usb_close(usb_handle* handle) {
+ DBG("usb_close\n");
+
+ if (NULL != handle) {
+ // Cleanup handle
+ usb_cleanup_handle(handle);
+ free(handle);
+ }
+
+ return 0;
+}
+
+int usb_wait_for_disconnect(usb_handle *usb) {
+ /* TODO: Punt for now */
+ return 0;
+}
+
+int recognized_device(usb_handle* handle, ifc_match_func callback) {
+ struct usb_ifc_info info;
+ USB_DEVICE_DESCRIPTOR device_desc;
+ USB_INTERFACE_DESCRIPTOR interf_desc;
+
+ if (NULL == handle)
+ return 0;
+
+ // Check vendor and product id first
+ if (!AdbGetUsbDeviceDescriptor(handle->adb_interface,
+ &device_desc)) {
+ return 0;
+ }
+
+ // Then check interface properties
+ if (!AdbGetUsbInterfaceDescriptor(handle->adb_interface,
+ &interf_desc)) {
+ return 0;
+ }
+
+ // Must have two endpoints
+ if (2 != interf_desc.bNumEndpoints) {
+ return 0;
+ }
+
+ info.dev_vendor = device_desc.idVendor;
+ info.dev_product = device_desc.idProduct;
+ info.dev_class = device_desc.bDeviceClass;
+ info.dev_subclass = device_desc.bDeviceSubClass;
+ info.dev_protocol = device_desc.bDeviceProtocol;
+ info.ifc_class = interf_desc.bInterfaceClass;
+ info.ifc_subclass = interf_desc.bInterfaceSubClass;
+ info.ifc_protocol = interf_desc.bInterfaceProtocol;
+ info.writable = 1;
+
+ // read serial number (if there is one)
+ unsigned long serial_number_len = sizeof(info.serial_number);
+ if (!AdbGetSerialNumber(handle->adb_interface, info.serial_number,
+ &serial_number_len, true)) {
+ info.serial_number[0] = 0;
+ }
+
+ info.device_path[0] = 0;
+
+ if (callback(&info) == 0) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static usb_handle *find_usb_device(ifc_match_func callback) {
+ usb_handle* handle = NULL;
+ char entry_buffer[2048];
+ char interf_name[2048];
+ AdbInterfaceInfo* next_interface = (AdbInterfaceInfo*)(&entry_buffer[0]);
+ unsigned long entry_buffer_size = sizeof(entry_buffer);
+ char* copy_name;
+
+ // Enumerate all present and active interfaces.
+ ADBAPIHANDLE enum_handle =
+ AdbEnumInterfaces(usb_class_id, true, true, true);
+
+ if (NULL == enum_handle)
+ return NULL;
+
+ while (AdbNextInterface(enum_handle, next_interface, &entry_buffer_size)) {
+ // TODO(vchtchetkine): FIXME - temp hack converting wchar_t into char.
+ // It would be better to change AdbNextInterface so it will return
+ // interface name as single char string.
+ const wchar_t* wchar_name = next_interface->device_name;
+ for(copy_name = interf_name;
+ L'\0' != *wchar_name;
+ wchar_name++, copy_name++) {
+ *copy_name = (char)(*wchar_name);
+ }
+ *copy_name = '\0';
+
+ handle = do_usb_open(next_interface->device_name);
+ if (NULL != handle) {
+ // Lets see if this interface (device) belongs to us
+ if (recognized_device(handle, callback)) {
+ // found it!
+ break;
+ } else {
+ usb_cleanup_handle(handle);
+ free(handle);
+ handle = NULL;
+ }
+ }
+
+ entry_buffer_size = sizeof(entry_buffer);
+ }
+
+ AdbCloseHandle(enum_handle);
+ return handle;
+}
+
+usb_handle *usb_open(ifc_match_func callback)
+{
+ return find_usb_device(callback);
+}
+
+// called from fastboot.c
+void sleep(int seconds)
+{
+ Sleep(seconds * 1000);
+}
diff --git a/fastboot/usbtest.cpp b/fastboot/usbtest.cpp
new file mode 100644
index 0000000..e6e2b37
--- /dev/null
+++ b/fastboot/usbtest.cpp
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/time.h>
+
+#include "usb.h"
+
+static unsigned arg_size = 4096;
+static unsigned arg_count = 4096;
+
+long long NOW(void)
+{
+ struct timeval tv;
+ gettimeofday(&tv, 0);
+
+ return (((long long) tv.tv_sec) * ((long long) 1000000)) +
+ (((long long) tv.tv_usec));
+}
+
+int printifc(usb_ifc_info *info)
+{
+ printf("dev: csp=%02x/%02x/%02x v=%04x p=%04x ",
+ info->dev_class, info->dev_subclass, info->dev_protocol,
+ info->dev_vendor, info->dev_product);
+ printf("ifc: csp=%02x/%02x/%02x%s%s\n",
+ info->ifc_class, info->ifc_subclass, info->ifc_protocol,
+ info->has_bulk_in ? " in" : "",
+ info->has_bulk_out ? " out" : "");
+ return -1;
+}
+
+int match_null(usb_ifc_info *info)
+{
+ if(info->dev_vendor != 0x18d1) return -1;
+ if(info->ifc_class != 0xff) return -1;
+ if(info->ifc_subclass != 0xfe) return -1;
+ if(info->ifc_protocol != 0x01) return -1;
+ return 0;
+}
+
+int match_zero(usb_ifc_info *info)
+{
+ if(info->dev_vendor != 0x18d1) return -1;
+ if(info->ifc_class != 0xff) return -1;
+ if(info->ifc_subclass != 0xfe) return -1;
+ if(info->ifc_protocol != 0x02) return -1;
+ return 0;
+}
+
+int match_loop(usb_ifc_info *info)
+{
+ if(info->dev_vendor != 0x18d1) return -1;
+ if(info->ifc_class != 0xff) return -1;
+ if(info->ifc_subclass != 0xfe) return -1;
+ if(info->ifc_protocol != 0x03) return -1;
+ return 0;
+}
+
+int test_null(usb_handle *usb)
+{
+ unsigned i;
+ unsigned char buf[4096];
+ memset(buf, 0xee, 4096);
+ long long t0, t1;
+
+ t0 = NOW();
+ for(i = 0; i < arg_count; i++) {
+ if(usb_write(usb, buf, arg_size) != (int)arg_size) {
+ fprintf(stderr,"write failed (%s)\n", strerror(errno));
+ return -1;
+ }
+ }
+ t1 = NOW();
+ fprintf(stderr,"%d bytes in %lld uS\n", arg_count * arg_size, (t1 - t0));
+ return 0;
+}
+
+int test_zero(usb_handle *usb)
+{
+ unsigned i;
+ unsigned char buf[4096];
+ long long t0, t1;
+
+ t0 = NOW();
+ for(i = 0; i < arg_count; i++) {
+ if(usb_read(usb, buf, arg_size) != (int)arg_size) {
+ fprintf(stderr,"read failed (%s)\n", strerror(errno));
+ return -1;
+ }
+ }
+ t1 = NOW();
+ fprintf(stderr,"%d bytes in %lld uS\n", arg_count * arg_size, (t1 - t0));
+ return 0;
+}
+
+struct
+{
+ const char *cmd;
+ ifc_match_func match;
+ int (*test)(usb_handle *usb);
+ const char *help;
+} tests[] = {
+ { "list", printifc, NULL, "list interfaces" },
+ { "send", match_null, test_null, "send to null interface" },
+ { "recv", match_zero, test_zero, "recv from zero interface" },
+ { "loop", match_loop, NULL, "exercise loopback interface" },
+ { NULL, NULL, NULL, NULL },
+};
+
+int usage(void)
+{
+ int i;
+
+ fprintf(stderr,"usage: usbtest <testname>\n\navailable tests:\n");
+ for(i = 0; tests[i].cmd; i++) {
+ fprintf(stderr," %-8s %s\n", tests[i].cmd, tests[i].help);
+ }
+ return -1;
+}
+
+int process_args(int argc, char **argv)
+{
+ while(argc-- > 0) {
+ char *arg = *argv++;
+ if(!strncmp(arg,"count=",6)) {
+ arg_count = atoi(arg + 6);
+ } else if(!strncmp(arg,"size=",5)) {
+ arg_size = atoi(arg + 5);
+ } else {
+ fprintf(stderr,"unknown argument: %s\n", arg);
+ return -1;
+ }
+ }
+
+ if(arg_count == 0) {
+ fprintf(stderr,"count may not be zero\n");
+ return -1;
+ }
+
+ if(arg_size > 4096) {
+ fprintf(stderr,"size may not be greater than 4096\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ usb_handle *usb;
+ int i;
+
+ if(argc < 2)
+ return usage();
+
+ if(argc > 2) {
+ if(process_args(argc - 2, argv + 2))
+ return -1;
+ }
+
+ for(i = 0; tests[i].cmd; i++) {
+ if(!strcmp(argv[1], tests[i].cmd)) {
+ usb = usb_open(tests[i].match);
+ if(tests[i].test) {
+ if(usb == 0) {
+ fprintf(stderr,"usbtest: %s: could not find interface\n",
+ tests[i].cmd);
+ return -1;
+ }
+ if(tests[i].test(usb)) {
+ fprintf(stderr,"usbtest: %s: FAIL\n", tests[i].cmd);
+ return -1;
+ } else {
+ fprintf(stderr,"usbtest: %s: OKAY\n", tests[i].cmd);
+ }
+ }
+ return 0;
+ }
+ }
+
+ return usage();
+}
diff --git a/fastboot/util.cpp b/fastboot/util.cpp
new file mode 100644
index 0000000..f2bbd34
--- /dev/null
+++ b/fastboot/util.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/time.h>
+
+#include "fastboot.h"
+
+double now()
+{
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return (double)tv.tv_sec + (double)tv.tv_usec / 1000000;
+}
+
+char *mkmsg(const char *fmt, ...)
+{
+ char buf[256];
+ char *s;
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsprintf(buf, fmt, ap);
+ va_end(ap);
+
+ s = strdup(buf);
+ if (s == 0) die("out of memory");
+ return s;
+}
+
+void die(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ fprintf(stderr,"error: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr,"\n");
+ va_end(ap);
+ exit(1);
+}
diff --git a/fastboot/util_linux.cpp b/fastboot/util_linux.cpp
new file mode 100644
index 0000000..b788199
--- /dev/null
+++ b/fastboot/util_linux.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "fastboot.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+
+void get_my_path(char *path)
+{
+ char proc[64];
+ char *x;
+
+ sprintf(proc, "/proc/%d/exe", getpid());
+ int err = readlink(proc, path, PATH_MAX - 1);
+
+ if(err <= 0) {
+ path[0] = 0;
+ } else {
+ path[err] = 0;
+ x = strrchr(path,'/');
+ if(x) x[1] = 0;
+ }
+}
diff --git a/fastboot/util_osx.cpp b/fastboot/util_osx.cpp
new file mode 100644
index 0000000..ae0b024
--- /dev/null
+++ b/fastboot/util_osx.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "fastboot.h"
+
+#import <Carbon/Carbon.h>
+#include <unistd.h>
+
+void get_my_path(char s[PATH_MAX])
+{
+ CFBundleRef mainBundle = CFBundleGetMainBundle();
+ CFURLRef executableURL = CFBundleCopyExecutableURL(mainBundle);
+ CFStringRef executablePathString = CFURLCopyFileSystemPath(executableURL, kCFURLPOSIXPathStyle);
+ CFRelease(executableURL);
+
+ CFStringGetFileSystemRepresentation(executablePathString, s, PATH_MAX-1);
+ CFRelease(executablePathString);
+
+ char *x;
+ x = strrchr(s, '/');
+ if(x) x[1] = 0;
+}
+
+
diff --git a/fastboot/util_windows.cpp b/fastboot/util_windows.cpp
new file mode 100644
index 0000000..ec52f39
--- /dev/null
+++ b/fastboot/util_windows.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "fastboot.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+
+#include <windows.h>
+
+void get_my_path(char exe[PATH_MAX])
+{
+ char* r;
+
+ GetModuleFileName( NULL, exe, PATH_MAX-1 );
+ exe[PATH_MAX-1] = 0;
+ r = strrchr( exe, '\\' );
+ if (r)
+ *r = 0;
+}
+
diff --git a/fs_mgr/Android.mk b/fs_mgr/Android.mk
new file mode 100644
index 0000000..08d0671
--- /dev/null
+++ b/fs_mgr/Android.mk
@@ -0,0 +1,43 @@
+# Copyright 2011 The Android Open Source Project
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= fs_mgr.c fs_mgr_verity.c fs_mgr_fstab.c
+
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+
+LOCAL_MODULE:= libfs_mgr
+LOCAL_STATIC_LIBRARIES := liblogwrap libmincrypt libext4_utils_static libsquashfs_utils
+LOCAL_C_INCLUDES += system/extras/ext4_utils system/extras/squashfs_utils
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_CFLAGS := -Werror
+
+ifneq (,$(filter userdebug,$(TARGET_BUILD_VARIANT)))
+LOCAL_CFLAGS += -DALLOW_ADBD_DISABLE_VERITY=1
+endif
+
+include $(BUILD_STATIC_LIBRARY)
+
+
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= fs_mgr_main.c
+
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+
+LOCAL_MODULE:= fs_mgr
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)/sbin
+LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)
+
+LOCAL_STATIC_LIBRARIES := libfs_mgr liblogwrap libcutils liblog libc libmincrypt libext4_utils_static libsquashfs_utils
+LOCAL_CXX_STL := libc++_static
+
+LOCAL_CFLAGS := -Werror
+
+include $(BUILD_EXECUTABLE)
+
diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c
new file mode 100644
index 0000000..f467f81
--- /dev/null
+++ b/fs_mgr/fs_mgr.c
@@ -0,0 +1,857 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <libgen.h>
+#include <time.h>
+#include <sys/swap.h>
+#include <dirent.h>
+#include <ext4.h>
+#include <ext4_sb.h>
+#include <ext4_crypt_init_extensions.h>
+
+#include <linux/loop.h>
+#include <private/android_filesystem_config.h>
+#include <cutils/android_reboot.h>
+#include <cutils/partition_utils.h>
+#include <cutils/properties.h>
+#include <logwrap/logwrap.h>
+
+#include "mincrypt/rsa.h"
+#include "mincrypt/sha.h"
+#include "mincrypt/sha256.h"
+
+#include "fs_mgr_priv.h"
+#include "fs_mgr_priv_verity.h"
+
+#define KEY_LOC_PROP "ro.crypto.keyfile.userdata"
+#define KEY_IN_FOOTER "footer"
+
+#define E2FSCK_BIN "/system/bin/e2fsck"
+#define F2FS_FSCK_BIN "/system/bin/fsck.f2fs"
+#define MKSWAP_BIN "/system/bin/mkswap"
+
+#define FSCK_LOG_FILE "/dev/fscklogs/log"
+
+#define ZRAM_CONF_DEV "/sys/block/zram0/disksize"
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
+
+/*
+ * gettime() - returns the time in seconds of the system's monotonic clock or
+ * zero on error.
+ */
+static time_t gettime(void)
+{
+ struct timespec ts;
+ int ret;
+
+ ret = clock_gettime(CLOCK_MONOTONIC, &ts);
+ if (ret < 0) {
+ ERROR("clock_gettime(CLOCK_MONOTONIC) failed: %s\n", strerror(errno));
+ return 0;
+ }
+
+ return ts.tv_sec;
+}
+
+static int wait_for_file(const char *filename, int timeout)
+{
+ struct stat info;
+ time_t timeout_time = gettime() + timeout;
+ int ret = -1;
+
+ while (gettime() < timeout_time && ((ret = stat(filename, &info)) < 0))
+ usleep(10000);
+
+ return ret;
+}
+
+static void check_fs(char *blk_device, char *fs_type, char *target)
+{
+ int status;
+ int ret;
+ long tmpmnt_flags = MS_NOATIME | MS_NOEXEC | MS_NOSUID;
+ char tmpmnt_opts[64] = "errors=remount-ro";
+ char *e2fsck_argv[] = {
+ E2FSCK_BIN,
+ "-y",
+ blk_device
+ };
+
+ /* Check for the types of filesystems we know how to check */
+ if (!strcmp(fs_type, "ext2") || !strcmp(fs_type, "ext3") || !strcmp(fs_type, "ext4")) {
+ /*
+ * First try to mount and unmount the filesystem. We do this because
+ * the kernel is more efficient than e2fsck in running the journal and
+ * processing orphaned inodes, and on at least one device with a
+ * performance issue in the emmc firmware, it can take e2fsck 2.5 minutes
+ * to do what the kernel does in about a second.
+ *
+ * After mounting and unmounting the filesystem, run e2fsck, and if an
+ * error is recorded in the filesystem superblock, e2fsck will do a full
+ * check. Otherwise, it does nothing. If the kernel cannot mount the
+ * filesytsem due to an error, e2fsck is still run to do a full check
+ * fix the filesystem.
+ */
+ errno = 0;
+ if (!strcmp(fs_type, "ext4")) {
+ // This option is only valid with ext4
+ strlcat(tmpmnt_opts, ",nomblk_io_submit", sizeof(tmpmnt_opts));
+ }
+ ret = mount(blk_device, target, fs_type, tmpmnt_flags, tmpmnt_opts);
+ INFO("%s(): mount(%s,%s,%s)=%d: %s\n",
+ __func__, blk_device, target, fs_type, ret, strerror(errno));
+ if (!ret) {
+ int i;
+ for (i = 0; i < 5; i++) {
+ // Try to umount 5 times before continuing on.
+ // Should we try rebooting if all attempts fail?
+ int result = umount(target);
+ if (result == 0) {
+ INFO("%s(): unmount(%s) succeeded\n", __func__, target);
+ break;
+ }
+ ERROR("%s(): umount(%s)=%d: %s\n", __func__, target, result, strerror(errno));
+ sleep(1);
+ }
+ }
+
+ /*
+ * Some system images do not have e2fsck for licensing reasons
+ * (e.g. recent SDK system images). Detect these and skip the check.
+ */
+ if (access(E2FSCK_BIN, X_OK)) {
+ INFO("Not running %s on %s (executable not in system image)\n",
+ E2FSCK_BIN, blk_device);
+ } else {
+ INFO("Running %s on %s\n", E2FSCK_BIN, blk_device);
+
+ ret = android_fork_execvp_ext(ARRAY_SIZE(e2fsck_argv), e2fsck_argv,
+ &status, true, LOG_KLOG | LOG_FILE,
+ true, FSCK_LOG_FILE);
+
+ if (ret < 0) {
+ /* No need to check for error in fork, we can't really handle it now */
+ ERROR("Failed trying to run %s\n", E2FSCK_BIN);
+ }
+ }
+ } else if (!strcmp(fs_type, "f2fs")) {
+ char *f2fs_fsck_argv[] = {
+ F2FS_FSCK_BIN,
+ "-a",
+ blk_device
+ };
+ INFO("Running %s -a %s\n", F2FS_FSCK_BIN, blk_device);
+
+ ret = android_fork_execvp_ext(ARRAY_SIZE(f2fs_fsck_argv), f2fs_fsck_argv,
+ &status, true, LOG_KLOG | LOG_FILE,
+ true, FSCK_LOG_FILE);
+ if (ret < 0) {
+ /* No need to check for error in fork, we can't really handle it now */
+ ERROR("Failed trying to run %s\n", F2FS_FSCK_BIN);
+ }
+ }
+
+ return;
+}
+
+static void remove_trailing_slashes(char *n)
+{
+ int len;
+
+ len = strlen(n) - 1;
+ while ((*(n + len) == '/') && len) {
+ *(n + len) = '\0';
+ len--;
+ }
+}
+
+/*
+ * Mark the given block device as read-only, using the BLKROSET ioctl.
+ * Return 0 on success, and -1 on error.
+ */
+int fs_mgr_set_blk_ro(const char *blockdev)
+{
+ int fd;
+ int rc = -1;
+ int ON = 1;
+
+ fd = TEMP_FAILURE_RETRY(open(blockdev, O_RDONLY | O_CLOEXEC));
+ if (fd < 0) {
+ // should never happen
+ return rc;
+ }
+
+ rc = ioctl(fd, BLKROSET, &ON);
+ close(fd);
+
+ return rc;
+}
+
+/*
+ * __mount(): wrapper around the mount() system call which also
+ * sets the underlying block device to read-only if the mount is read-only.
+ * See "man 2 mount" for return values.
+ */
+static int __mount(const char *source, const char *target, const struct fstab_rec *rec)
+{
+ unsigned long mountflags = rec->flags;
+ int ret;
+ int save_errno;
+
+ /* We need this because sometimes we have legacy symlinks
+ * that are lingering around and need cleaning up.
+ */
+ struct stat info;
+ if (!lstat(target, &info))
+ if ((info.st_mode & S_IFMT) == S_IFLNK)
+ unlink(target);
+ mkdir(target, 0755);
+ ret = mount(source, target, rec->fs_type, mountflags, rec->fs_options);
+ save_errno = errno;
+ INFO("%s(source=%s,target=%s,type=%s)=%d\n", __func__, source, target, rec->fs_type, ret);
+ if ((ret == 0) && (mountflags & MS_RDONLY) != 0) {
+ fs_mgr_set_blk_ro(source);
+ }
+ errno = save_errno;
+ return ret;
+}
+
+static int fs_match(char *in1, char *in2)
+{
+ char *n1;
+ char *n2;
+ int ret;
+
+ n1 = strdup(in1);
+ n2 = strdup(in2);
+
+ remove_trailing_slashes(n1);
+ remove_trailing_slashes(n2);
+
+ ret = !strcmp(n1, n2);
+
+ free(n1);
+ free(n2);
+
+ return ret;
+}
+
+static int device_is_debuggable() {
+ int ret = -1;
+ char value[PROP_VALUE_MAX];
+ ret = __system_property_get("ro.debuggable", value);
+ if (ret < 0)
+ return ret;
+ return strcmp(value, "1") ? 0 : 1;
+}
+
+static int device_is_secure() {
+ int ret = -1;
+ char value[PROP_VALUE_MAX];
+ ret = __system_property_get("ro.secure", value);
+ /* If error, we want to fail secure */
+ if (ret < 0)
+ return 1;
+ return strcmp(value, "0") ? 1 : 0;
+}
+
+static int device_is_force_encrypted() {
+ int ret = -1;
+ char value[PROP_VALUE_MAX];
+ ret = __system_property_get("ro.vold.forceencryption", value);
+ if (ret < 0)
+ return 0;
+ return strcmp(value, "1") ? 0 : 1;
+}
+
+/*
+ * Tries to mount any of the consecutive fstab entries that match
+ * the mountpoint of the one given by fstab->recs[start_idx].
+ *
+ * end_idx: On return, will be the last rec that was looked at.
+ * attempted_idx: On return, will indicate which fstab rec
+ * succeeded. In case of failure, it will be the start_idx.
+ * Returns
+ * -1 on failure with errno set to match the 1st mount failure.
+ * 0 on success.
+ */
+static int mount_with_alternatives(struct fstab *fstab, int start_idx, int *end_idx, int *attempted_idx)
+{
+ int i;
+ int mount_errno = 0;
+ int mounted = 0;
+
+ if (!end_idx || !attempted_idx || start_idx >= fstab->num_entries) {
+ errno = EINVAL;
+ if (end_idx) *end_idx = start_idx;
+ if (attempted_idx) *end_idx = start_idx;
+ return -1;
+ }
+
+ /* Hunt down an fstab entry for the same mount point that might succeed */
+ for (i = start_idx;
+ /* We required that fstab entries for the same mountpoint be consecutive */
+ i < fstab->num_entries && !strcmp(fstab->recs[start_idx].mount_point, fstab->recs[i].mount_point);
+ i++) {
+ /*
+ * Don't try to mount/encrypt the same mount point again.
+ * Deal with alternate entries for the same point which are required to be all following
+ * each other.
+ */
+ if (mounted) {
+ ERROR("%s(): skipping fstab dup mountpoint=%s rec[%d].fs_type=%s already mounted as %s.\n", __func__,
+ fstab->recs[i].mount_point, i, fstab->recs[i].fs_type, fstab->recs[*attempted_idx].fs_type);
+ continue;
+ }
+
+ if (fstab->recs[i].fs_mgr_flags & MF_CHECK) {
+ check_fs(fstab->recs[i].blk_device, fstab->recs[i].fs_type,
+ fstab->recs[i].mount_point);
+ }
+ if (!__mount(fstab->recs[i].blk_device, fstab->recs[i].mount_point, &fstab->recs[i])) {
+ *attempted_idx = i;
+ mounted = 1;
+ if (i != start_idx) {
+ ERROR("%s(): Mounted %s on %s with fs_type=%s instead of %s\n", __func__,
+ fstab->recs[i].blk_device, fstab->recs[i].mount_point, fstab->recs[i].fs_type,
+ fstab->recs[start_idx].fs_type);
+ }
+ } else {
+ /* back up errno for crypto decisions */
+ mount_errno = errno;
+ }
+ }
+
+ /* Adjust i for the case where it was still withing the recs[] */
+ if (i < fstab->num_entries) --i;
+
+ *end_idx = i;
+ if (!mounted) {
+ *attempted_idx = start_idx;
+ errno = mount_errno;
+ return -1;
+ }
+ return 0;
+}
+
+static int translate_ext_labels(struct fstab_rec *rec)
+{
+ DIR *blockdir = NULL;
+ struct dirent *ent;
+ char *label;
+ size_t label_len;
+ int ret = -1;
+
+ if (strncmp(rec->blk_device, "LABEL=", 6))
+ return 0;
+
+ label = rec->blk_device + 6;
+ label_len = strlen(label);
+
+ if (label_len > 16) {
+ ERROR("FS label is longer than allowed by filesystem\n");
+ goto out;
+ }
+
+
+ blockdir = opendir("/dev/block");
+ if (!blockdir) {
+ ERROR("couldn't open /dev/block\n");
+ goto out;
+ }
+
+ while ((ent = readdir(blockdir))) {
+ int fd;
+ char super_buf[1024];
+ struct ext4_super_block *sb;
+
+ if (ent->d_type != DT_BLK)
+ continue;
+
+ fd = openat(dirfd(blockdir), ent->d_name, O_RDONLY);
+ if (fd < 0) {
+ ERROR("Cannot open block device /dev/block/%s\n", ent->d_name);
+ goto out;
+ }
+
+ if (TEMP_FAILURE_RETRY(lseek(fd, 1024, SEEK_SET)) < 0 ||
+ TEMP_FAILURE_RETRY(read(fd, super_buf, 1024)) != 1024) {
+ /* Probably a loopback device or something else without a readable
+ * superblock.
+ */
+ close(fd);
+ continue;
+ }
+
+ sb = (struct ext4_super_block *)super_buf;
+ if (sb->s_magic != EXT4_SUPER_MAGIC) {
+ INFO("/dev/block/%s not ext{234}\n", ent->d_name);
+ continue;
+ }
+
+ if (!strncmp(label, sb->s_volume_name, label_len)) {
+ char *new_blk_device;
+
+ if (asprintf(&new_blk_device, "/dev/block/%s", ent->d_name) < 0) {
+ ERROR("Could not allocate block device string\n");
+ goto out;
+ }
+
+ INFO("resolved label %s to %s\n", rec->blk_device, new_blk_device);
+
+ free(rec->blk_device);
+ rec->blk_device = new_blk_device;
+ ret = 0;
+ break;
+ }
+ }
+
+out:
+ closedir(blockdir);
+ return ret;
+}
+
+// Check to see if a mountable volume has encryption requirements
+static int handle_encryptable(struct fstab *fstab, const struct fstab_rec* rec)
+{
+ /* If this is block encryptable, need to trigger encryption */
+ if ( (rec->fs_mgr_flags & MF_FORCECRYPT)
+ || (device_is_force_encrypted() && fs_mgr_is_encryptable(rec))) {
+ if (umount(rec->mount_point) == 0) {
+ return FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION;
+ } else {
+ WARNING("Could not umount %s (%s) - allow continue unencrypted\n",
+ rec->mount_point, strerror(errno));
+ return FS_MGR_MNTALL_DEV_NOT_ENCRYPTED;
+ }
+ }
+
+ // Deal with file level encryption
+ if (rec->fs_mgr_flags & MF_FILEENCRYPTION) {
+ // Default or not yet initialized encryption requires no more work here
+ if (!e4crypt_non_default_key(rec->mount_point)) {
+ INFO("%s is default file encrypted\n", rec->mount_point);
+ return FS_MGR_MNTALL_DEV_DEFAULT_FILE_ENCRYPTED;
+ }
+
+ INFO("%s is non-default file encrypted\n", rec->mount_point);
+
+ // Uses non-default key, so must unmount and set up temp file system
+ if (umount(rec->mount_point)) {
+ ERROR("Failed to umount %s - rebooting\n", rec->mount_point);
+ return FS_MGR_MNTALL_FAIL;
+ }
+
+ if (fs_mgr_do_tmpfs_mount(rec->mount_point) != 0) {
+ ERROR("Failed to mount a tmpfs at %s\n", rec->mount_point);
+ return FS_MGR_MNTALL_FAIL;
+ }
+
+ // Mount data temporarily so we can access unencrypted dir
+ char tmp_mnt[PATH_MAX];
+ strlcpy(tmp_mnt, rec->mount_point, sizeof(tmp_mnt));
+ strlcat(tmp_mnt, "/tmp_mnt", sizeof(tmp_mnt));
+ if (mkdir(tmp_mnt, 0700)) {
+ ERROR("Failed to create temp mount point\n");
+ return FS_MGR_MNTALL_FAIL;
+ }
+
+ if (fs_mgr_do_mount(fstab, rec->mount_point,
+ rec->blk_device, tmp_mnt)) {
+ ERROR("Error temp mounting encrypted file system\n");
+ return FS_MGR_MNTALL_FAIL;
+ }
+
+ return FS_MGR_MNTALL_DEV_NON_DEFAULT_FILE_ENCRYPTED;
+ }
+
+ return FS_MGR_MNTALL_DEV_NOT_ENCRYPTED;
+}
+
+/* When multiple fstab records share the same mount_point, it will
+ * try to mount each one in turn, and ignore any duplicates after a
+ * first successful mount.
+ * Returns -1 on error, and FS_MGR_MNTALL_* otherwise.
+ */
+int fs_mgr_mount_all(struct fstab *fstab)
+{
+ int i = 0;
+ int encryptable = FS_MGR_MNTALL_DEV_NOT_ENCRYPTED;
+ int error_count = 0;
+ int mret = -1;
+ int mount_errno = 0;
+ int attempted_idx = -1;
+
+ if (!fstab) {
+ return -1;
+ }
+
+ for (i = 0; i < fstab->num_entries; i++) {
+ /* Don't mount entries that are managed by vold */
+ if (fstab->recs[i].fs_mgr_flags & (MF_VOLDMANAGED | MF_RECOVERYONLY)) {
+ continue;
+ }
+
+ /* Skip swap and raw partition entries such as boot, recovery, etc */
+ if (!strcmp(fstab->recs[i].fs_type, "swap") ||
+ !strcmp(fstab->recs[i].fs_type, "emmc") ||
+ !strcmp(fstab->recs[i].fs_type, "mtd")) {
+ continue;
+ }
+
+ /* Translate LABEL= file system labels into block devices */
+ if (!strcmp(fstab->recs[i].fs_type, "ext2") ||
+ !strcmp(fstab->recs[i].fs_type, "ext3") ||
+ !strcmp(fstab->recs[i].fs_type, "ext4")) {
+ int tret = translate_ext_labels(&fstab->recs[i]);
+ if (tret < 0) {
+ ERROR("Could not translate label to block device\n");
+ continue;
+ }
+ }
+
+ if (fstab->recs[i].fs_mgr_flags & MF_WAIT) {
+ wait_for_file(fstab->recs[i].blk_device, WAIT_TIMEOUT);
+ }
+
+ if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && device_is_secure()) {
+ int rc = fs_mgr_setup_verity(&fstab->recs[i]);
+ if (device_is_debuggable() && rc == FS_MGR_SETUP_VERITY_DISABLED) {
+ INFO("Verity disabled");
+ } else if (rc != FS_MGR_SETUP_VERITY_SUCCESS) {
+ ERROR("Could not set up verified partition, skipping!\n");
+ continue;
+ }
+ }
+ int last_idx_inspected;
+ mret = mount_with_alternatives(fstab, i, &last_idx_inspected, &attempted_idx);
+ i = last_idx_inspected;
+ mount_errno = errno;
+
+ /* Deal with encryptability. */
+ if (!mret) {
+ int status = handle_encryptable(fstab, &fstab->recs[attempted_idx]);
+
+ if (status == FS_MGR_MNTALL_FAIL) {
+ /* Fatal error - no point continuing */
+ return status;
+ }
+
+ if (status != FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
+ if (encryptable != FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
+ // Log and continue
+ ERROR("Only one encryptable/encrypted partition supported\n");
+ }
+ encryptable = status;
+ }
+
+ /* Success! Go get the next one */
+ continue;
+ }
+
+ /* mount(2) returned an error, check if it's encryptable and deal with it */
+ if (mret && mount_errno != EBUSY && mount_errno != EACCES &&
+ fs_mgr_is_encryptable(&fstab->recs[attempted_idx])) {
+ if(partition_wiped(fstab->recs[attempted_idx].blk_device)) {
+ ERROR("%s(): %s is wiped and %s %s is encryptable. Suggest recovery...\n", __func__,
+ fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point,
+ fstab->recs[attempted_idx].fs_type);
+ encryptable = FS_MGR_MNTALL_DEV_NEEDS_RECOVERY;
+ continue;
+ } else {
+ /* Need to mount a tmpfs at this mountpoint for now, and set
+ * properties that vold will query later for decrypting
+ */
+ ERROR("%s(): possibly an encryptable blkdev %s for mount %s type %s )\n", __func__,
+ fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point,
+ fstab->recs[attempted_idx].fs_type);
+ if (fs_mgr_do_tmpfs_mount(fstab->recs[attempted_idx].mount_point) < 0) {
+ ++error_count;
+ continue;
+ }
+ }
+ encryptable = FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED;
+ } else {
+ ERROR("Failed to mount an un-encryptable or wiped partition on"
+ "%s at %s options: %s error: %s\n",
+ fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point,
+ fstab->recs[attempted_idx].fs_options, strerror(mount_errno));
+ ++error_count;
+ continue;
+ }
+ }
+
+ if (error_count) {
+ return -1;
+ } else {
+ return encryptable;
+ }
+}
+
+/* If tmp_mount_point is non-null, mount the filesystem there. This is for the
+ * tmp mount we do to check the user password
+ * If multiple fstab entries are to be mounted on "n_name", it will try to mount each one
+ * in turn, and stop on 1st success, or no more match.
+ */
+int fs_mgr_do_mount(struct fstab *fstab, char *n_name, char *n_blk_device,
+ char *tmp_mount_point)
+{
+ int i = 0;
+ int ret = FS_MGR_DOMNT_FAILED;
+ int mount_errors = 0;
+ int first_mount_errno = 0;
+ char *m;
+
+ if (!fstab) {
+ return ret;
+ }
+
+ for (i = 0; i < fstab->num_entries; i++) {
+ if (!fs_match(fstab->recs[i].mount_point, n_name)) {
+ continue;
+ }
+
+ /* We found our match */
+ /* If this swap or a raw partition, report an error */
+ if (!strcmp(fstab->recs[i].fs_type, "swap") ||
+ !strcmp(fstab->recs[i].fs_type, "emmc") ||
+ !strcmp(fstab->recs[i].fs_type, "mtd")) {
+ ERROR("Cannot mount filesystem of type %s on %s\n",
+ fstab->recs[i].fs_type, n_blk_device);
+ goto out;
+ }
+
+ /* First check the filesystem if requested */
+ if (fstab->recs[i].fs_mgr_flags & MF_WAIT) {
+ wait_for_file(n_blk_device, WAIT_TIMEOUT);
+ }
+
+ if (fstab->recs[i].fs_mgr_flags & MF_CHECK) {
+ check_fs(n_blk_device, fstab->recs[i].fs_type,
+ fstab->recs[i].mount_point);
+ }
+
+ if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && device_is_secure()) {
+ int rc = fs_mgr_setup_verity(&fstab->recs[i]);
+ if (device_is_debuggable() && rc == FS_MGR_SETUP_VERITY_DISABLED) {
+ INFO("Verity disabled");
+ } else if (rc != FS_MGR_SETUP_VERITY_SUCCESS) {
+ ERROR("Could not set up verified partition, skipping!\n");
+ continue;
+ }
+ }
+
+ /* Now mount it where requested */
+ if (tmp_mount_point) {
+ m = tmp_mount_point;
+ } else {
+ m = fstab->recs[i].mount_point;
+ }
+ if (__mount(n_blk_device, m, &fstab->recs[i])) {
+ if (!first_mount_errno) first_mount_errno = errno;
+ mount_errors++;
+ continue;
+ } else {
+ ret = 0;
+ goto out;
+ }
+ }
+ if (mount_errors) {
+ ERROR("Cannot mount filesystem on %s at %s. error: %s\n",
+ n_blk_device, m, strerror(first_mount_errno));
+ if (first_mount_errno == EBUSY) {
+ ret = FS_MGR_DOMNT_BUSY;
+ } else {
+ ret = FS_MGR_DOMNT_FAILED;
+ }
+ } else {
+ /* We didn't find a match, say so and return an error */
+ ERROR("Cannot find mount point %s in fstab\n", fstab->recs[i].mount_point);
+ }
+
+out:
+ return ret;
+}
+
+/*
+ * mount a tmpfs filesystem at the given point.
+ * return 0 on success, non-zero on failure.
+ */
+int fs_mgr_do_tmpfs_mount(char *n_name)
+{
+ int ret;
+
+ ret = mount("tmpfs", n_name, "tmpfs",
+ MS_NOATIME | MS_NOSUID | MS_NODEV, CRYPTO_TMPFS_OPTIONS);
+ if (ret < 0) {
+ ERROR("Cannot mount tmpfs filesystem at %s\n", n_name);
+ return -1;
+ }
+
+ /* Success */
+ return 0;
+}
+
+int fs_mgr_unmount_all(struct fstab *fstab)
+{
+ int i = 0;
+ int ret = 0;
+
+ if (!fstab) {
+ return -1;
+ }
+
+ while (fstab->recs[i].blk_device) {
+ if (umount(fstab->recs[i].mount_point)) {
+ ERROR("Cannot unmount filesystem at %s\n", fstab->recs[i].mount_point);
+ ret = -1;
+ }
+ i++;
+ }
+
+ return ret;
+}
+
+/* This must be called after mount_all, because the mkswap command needs to be
+ * available.
+ */
+int fs_mgr_swapon_all(struct fstab *fstab)
+{
+ int i = 0;
+ int flags = 0;
+ int err = 0;
+ int ret = 0;
+ int status;
+ char *mkswap_argv[2] = {
+ MKSWAP_BIN,
+ NULL
+ };
+
+ if (!fstab) {
+ return -1;
+ }
+
+ for (i = 0; i < fstab->num_entries; i++) {
+ /* Skip non-swap entries */
+ if (strcmp(fstab->recs[i].fs_type, "swap")) {
+ continue;
+ }
+
+ if (fstab->recs[i].zram_size > 0) {
+ /* A zram_size was specified, so we need to configure the
+ * device. There is no point in having multiple zram devices
+ * on a system (all the memory comes from the same pool) so
+ * we can assume the device number is 0.
+ */
+ FILE *zram_fp;
+
+ zram_fp = fopen(ZRAM_CONF_DEV, "r+");
+ if (zram_fp == NULL) {
+ ERROR("Unable to open zram conf device %s\n", ZRAM_CONF_DEV);
+ ret = -1;
+ continue;
+ }
+ fprintf(zram_fp, "%d\n", fstab->recs[i].zram_size);
+ fclose(zram_fp);
+ }
+
+ if (fstab->recs[i].fs_mgr_flags & MF_WAIT) {
+ wait_for_file(fstab->recs[i].blk_device, WAIT_TIMEOUT);
+ }
+
+ /* Initialize the swap area */
+ mkswap_argv[1] = fstab->recs[i].blk_device;
+ err = android_fork_execvp_ext(ARRAY_SIZE(mkswap_argv), mkswap_argv,
+ &status, true, LOG_KLOG, false, NULL);
+ if (err) {
+ ERROR("mkswap failed for %s\n", fstab->recs[i].blk_device);
+ ret = -1;
+ continue;
+ }
+
+ /* If -1, then no priority was specified in fstab, so don't set
+ * SWAP_FLAG_PREFER or encode the priority */
+ if (fstab->recs[i].swap_prio >= 0) {
+ flags = (fstab->recs[i].swap_prio << SWAP_FLAG_PRIO_SHIFT) &
+ SWAP_FLAG_PRIO_MASK;
+ flags |= SWAP_FLAG_PREFER;
+ } else {
+ flags = 0;
+ }
+ err = swapon(fstab->recs[i].blk_device, flags);
+ if (err) {
+ ERROR("swapon failed for %s\n", fstab->recs[i].blk_device);
+ ret = -1;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * key_loc must be at least PROPERTY_VALUE_MAX bytes long
+ *
+ * real_blk_device must be at least PROPERTY_VALUE_MAX bytes long
+ */
+int fs_mgr_get_crypt_info(struct fstab *fstab, char *key_loc, char *real_blk_device, int size)
+{
+ int i = 0;
+
+ if (!fstab) {
+ return -1;
+ }
+ /* Initialize return values to null strings */
+ if (key_loc) {
+ *key_loc = '\0';
+ }
+ if (real_blk_device) {
+ *real_blk_device = '\0';
+ }
+
+ /* Look for the encryptable partition to find the data */
+ for (i = 0; i < fstab->num_entries; i++) {
+ /* Don't deal with vold managed enryptable partitions here */
+ if (fstab->recs[i].fs_mgr_flags & MF_VOLDMANAGED) {
+ continue;
+ }
+ if (!(fstab->recs[i].fs_mgr_flags & (MF_CRYPT | MF_FORCECRYPT))) {
+ continue;
+ }
+
+ /* We found a match */
+ if (key_loc) {
+ strlcpy(key_loc, fstab->recs[i].key_loc, size);
+ }
+ if (real_blk_device) {
+ strlcpy(real_blk_device, fstab->recs[i].blk_device, size);
+ }
+ break;
+ }
+
+ return 0;
+}
diff --git a/fs_mgr/fs_mgr_fstab.c b/fs_mgr/fs_mgr_fstab.c
new file mode 100644
index 0000000..8b0f714
--- /dev/null
+++ b/fs_mgr/fs_mgr_fstab.c
@@ -0,0 +1,450 @@
+/*
+ * Copyright (C) 2014 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 <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mount.h>
+
+#include "fs_mgr_priv.h"
+
+struct fs_mgr_flag_values {
+ char *key_loc;
+ char *verity_loc;
+ long long part_length;
+ char *label;
+ int partnum;
+ int swap_prio;
+ unsigned int zram_size;
+};
+
+struct flag_list {
+ const char *name;
+ unsigned flag;
+};
+
+static struct flag_list mount_flags[] = {
+ { "noatime", MS_NOATIME },
+ { "noexec", MS_NOEXEC },
+ { "nosuid", MS_NOSUID },
+ { "nodev", MS_NODEV },
+ { "nodiratime", MS_NODIRATIME },
+ { "ro", MS_RDONLY },
+ { "rw", 0 },
+ { "remount", MS_REMOUNT },
+ { "bind", MS_BIND },
+ { "rec", MS_REC },
+ { "unbindable", MS_UNBINDABLE },
+ { "private", MS_PRIVATE },
+ { "slave", MS_SLAVE },
+ { "shared", MS_SHARED },
+ { "defaults", 0 },
+ { 0, 0 },
+};
+
+static struct flag_list fs_mgr_flags[] = {
+ { "wait", MF_WAIT },
+ { "check", MF_CHECK },
+ { "encryptable=",MF_CRYPT },
+ { "forceencrypt=",MF_FORCECRYPT },
+ { "fileencryption",MF_FILEENCRYPTION },
+ { "nonremovable",MF_NONREMOVABLE },
+ { "voldmanaged=",MF_VOLDMANAGED},
+ { "length=", MF_LENGTH },
+ { "recoveryonly",MF_RECOVERYONLY },
+ { "swapprio=", MF_SWAPPRIO },
+ { "zramsize=", MF_ZRAMSIZE },
+ { "verify", MF_VERIFY },
+ { "noemulatedsd", MF_NOEMULATEDSD },
+ { "defaults", 0 },
+ { 0, 0 },
+};
+
+static int parse_flags(char *flags, struct flag_list *fl,
+ struct fs_mgr_flag_values *flag_vals,
+ char *fs_options, int fs_options_len)
+{
+ int f = 0;
+ int i;
+ char *p;
+ char *savep;
+
+ /* initialize flag values. If we find a relevant flag, we'll
+ * update the value */
+ if (flag_vals) {
+ memset(flag_vals, 0, sizeof(*flag_vals));
+ flag_vals->partnum = -1;
+ flag_vals->swap_prio = -1; /* negative means it wasn't specified. */
+ }
+
+ /* initialize fs_options to the null string */
+ if (fs_options && (fs_options_len > 0)) {
+ fs_options[0] = '\0';
+ }
+
+ p = strtok_r(flags, ",", &savep);
+ while (p) {
+ /* Look for the flag "p" in the flag list "fl"
+ * If not found, the loop exits with fl[i].name being null.
+ */
+ for (i = 0; fl[i].name; i++) {
+ if (!strncmp(p, fl[i].name, strlen(fl[i].name))) {
+ f |= fl[i].flag;
+ if ((fl[i].flag == MF_CRYPT) && flag_vals) {
+ /* The encryptable flag is followed by an = and the
+ * location of the keys. Get it and return it.
+ */
+ flag_vals->key_loc = strdup(strchr(p, '=') + 1);
+ } else if ((fl[i].flag == MF_VERIFY) && flag_vals) {
+ /* If the verify flag is followed by an = and the
+ * location for the verity state, get it and return it.
+ */
+ char *start = strchr(p, '=');
+ if (start) {
+ flag_vals->verity_loc = strdup(start + 1);
+ }
+ } else if ((fl[i].flag == MF_FORCECRYPT) && flag_vals) {
+ /* The forceencrypt flag is followed by an = and the
+ * location of the keys. Get it and return it.
+ */
+ flag_vals->key_loc = strdup(strchr(p, '=') + 1);
+ } else if ((fl[i].flag == MF_LENGTH) && flag_vals) {
+ /* The length flag is followed by an = and the
+ * size of the partition. Get it and return it.
+ */
+ flag_vals->part_length = strtoll(strchr(p, '=') + 1, NULL, 0);
+ } else if ((fl[i].flag == MF_VOLDMANAGED) && flag_vals) {
+ /* The voldmanaged flag is followed by an = and the
+ * label, a colon and the partition number or the
+ * word "auto", e.g.
+ * voldmanaged=sdcard:3
+ * Get and return them.
+ */
+ char *label_start;
+ char *label_end;
+ char *part_start;
+
+ label_start = strchr(p, '=') + 1;
+ label_end = strchr(p, ':');
+ if (label_end) {
+ flag_vals->label = strndup(label_start,
+ (int) (label_end - label_start));
+ part_start = strchr(p, ':') + 1;
+ if (!strcmp(part_start, "auto")) {
+ flag_vals->partnum = -1;
+ } else {
+ flag_vals->partnum = strtol(part_start, NULL, 0);
+ }
+ } else {
+ ERROR("Warning: voldmanaged= flag malformed\n");
+ }
+ } else if ((fl[i].flag == MF_SWAPPRIO) && flag_vals) {
+ flag_vals->swap_prio = strtoll(strchr(p, '=') + 1, NULL, 0);
+ } else if ((fl[i].flag == MF_ZRAMSIZE) && flag_vals) {
+ flag_vals->zram_size = strtoll(strchr(p, '=') + 1, NULL, 0);
+ }
+ break;
+ }
+ }
+
+ if (!fl[i].name) {
+ if (fs_options) {
+ /* It's not a known flag, so it must be a filesystem specific
+ * option. Add it to fs_options if it was passed in.
+ */
+ strlcat(fs_options, p, fs_options_len);
+ strlcat(fs_options, ",", fs_options_len);
+ } else {
+ /* fs_options was not passed in, so if the flag is unknown
+ * it's an error.
+ */
+ ERROR("Warning: unknown flag %s\n", p);
+ }
+ }
+ p = strtok_r(NULL, ",", &savep);
+ }
+
+ if (fs_options && fs_options[0]) {
+ /* remove the last trailing comma from the list of options */
+ fs_options[strlen(fs_options) - 1] = '\0';
+ }
+
+ return f;
+}
+
+struct fstab *fs_mgr_read_fstab(const char *fstab_path)
+{
+ FILE *fstab_file;
+ int cnt, entries;
+ ssize_t len;
+ size_t alloc_len = 0;
+ char *line = NULL;
+ const char *delim = " \t";
+ char *save_ptr, *p;
+ struct fstab *fstab = NULL;
+ struct fs_mgr_flag_values flag_vals;
+#define FS_OPTIONS_LEN 1024
+ char tmp_fs_options[FS_OPTIONS_LEN];
+
+ fstab_file = fopen(fstab_path, "r");
+ if (!fstab_file) {
+ ERROR("Cannot open file %s\n", fstab_path);
+ return 0;
+ }
+
+ entries = 0;
+ while ((len = getline(&line, &alloc_len, fstab_file)) != -1) {
+ /* if the last character is a newline, shorten the string by 1 byte */
+ if (line[len - 1] == '\n') {
+ line[len - 1] = '\0';
+ }
+ /* Skip any leading whitespace */
+ p = line;
+ while (isspace(*p)) {
+ p++;
+ }
+ /* ignore comments or empty lines */
+ if (*p == '#' || *p == '\0')
+ continue;
+ entries++;
+ }
+
+ if (!entries) {
+ ERROR("No entries found in fstab\n");
+ goto err;
+ }
+
+ /* Allocate and init the fstab structure */
+ fstab = calloc(1, sizeof(struct fstab));
+ fstab->num_entries = entries;
+ fstab->fstab_filename = strdup(fstab_path);
+ fstab->recs = calloc(fstab->num_entries, sizeof(struct fstab_rec));
+
+ fseek(fstab_file, 0, SEEK_SET);
+
+ cnt = 0;
+ while ((len = getline(&line, &alloc_len, fstab_file)) != -1) {
+ /* if the last character is a newline, shorten the string by 1 byte */
+ if (line[len - 1] == '\n') {
+ line[len - 1] = '\0';
+ }
+
+ /* Skip any leading whitespace */
+ p = line;
+ while (isspace(*p)) {
+ p++;
+ }
+ /* ignore comments or empty lines */
+ if (*p == '#' || *p == '\0')
+ continue;
+
+ /* If a non-comment entry is greater than the size we allocated, give an
+ * error and quit. This can happen in the unlikely case the file changes
+ * between the two reads.
+ */
+ if (cnt >= entries) {
+ ERROR("Tried to process more entries than counted\n");
+ break;
+ }
+
+ if (!(p = strtok_r(line, delim, &save_ptr))) {
+ ERROR("Error parsing mount source\n");
+ goto err;
+ }
+ fstab->recs[cnt].blk_device = strdup(p);
+
+ if (!(p = strtok_r(NULL, delim, &save_ptr))) {
+ ERROR("Error parsing mount_point\n");
+ goto err;
+ }
+ fstab->recs[cnt].mount_point = strdup(p);
+
+ if (!(p = strtok_r(NULL, delim, &save_ptr))) {
+ ERROR("Error parsing fs_type\n");
+ goto err;
+ }
+ fstab->recs[cnt].fs_type = strdup(p);
+
+ if (!(p = strtok_r(NULL, delim, &save_ptr))) {
+ ERROR("Error parsing mount_flags\n");
+ goto err;
+ }
+ tmp_fs_options[0] = '\0';
+ fstab->recs[cnt].flags = parse_flags(p, mount_flags, NULL,
+ tmp_fs_options, FS_OPTIONS_LEN);
+
+ /* fs_options are optional */
+ if (tmp_fs_options[0]) {
+ fstab->recs[cnt].fs_options = strdup(tmp_fs_options);
+ } else {
+ fstab->recs[cnt].fs_options = NULL;
+ }
+
+ if (!(p = strtok_r(NULL, delim, &save_ptr))) {
+ ERROR("Error parsing fs_mgr_options\n");
+ goto err;
+ }
+ fstab->recs[cnt].fs_mgr_flags = parse_flags(p, fs_mgr_flags,
+ &flag_vals, NULL, 0);
+ fstab->recs[cnt].key_loc = flag_vals.key_loc;
+ fstab->recs[cnt].verity_loc = flag_vals.verity_loc;
+ fstab->recs[cnt].length = flag_vals.part_length;
+ fstab->recs[cnt].label = flag_vals.label;
+ fstab->recs[cnt].partnum = flag_vals.partnum;
+ fstab->recs[cnt].swap_prio = flag_vals.swap_prio;
+ fstab->recs[cnt].zram_size = flag_vals.zram_size;
+ cnt++;
+ }
+ fclose(fstab_file);
+ free(line);
+ return fstab;
+
+err:
+ fclose(fstab_file);
+ free(line);
+ if (fstab)
+ fs_mgr_free_fstab(fstab);
+ return NULL;
+}
+
+void fs_mgr_free_fstab(struct fstab *fstab)
+{
+ int i;
+
+ if (!fstab) {
+ return;
+ }
+
+ for (i = 0; i < fstab->num_entries; i++) {
+ /* Free the pointers return by strdup(3) */
+ free(fstab->recs[i].blk_device);
+ free(fstab->recs[i].mount_point);
+ free(fstab->recs[i].fs_type);
+ free(fstab->recs[i].fs_options);
+ free(fstab->recs[i].key_loc);
+ free(fstab->recs[i].label);
+ }
+
+ /* Free the fstab_recs array created by calloc(3) */
+ free(fstab->recs);
+
+ /* Free the fstab filename */
+ free(fstab->fstab_filename);
+
+ /* Free fstab */
+ free(fstab);
+}
+
+/* Add an entry to the fstab, and return 0 on success or -1 on error */
+int fs_mgr_add_entry(struct fstab *fstab,
+ const char *mount_point, const char *fs_type,
+ const char *blk_device)
+{
+ struct fstab_rec *new_fstab_recs;
+ int n = fstab->num_entries;
+
+ new_fstab_recs = (struct fstab_rec *)
+ realloc(fstab->recs, sizeof(struct fstab_rec) * (n + 1));
+
+ if (!new_fstab_recs) {
+ return -1;
+ }
+
+ /* A new entry was added, so initialize it */
+ memset(&new_fstab_recs[n], 0, sizeof(struct fstab_rec));
+ new_fstab_recs[n].mount_point = strdup(mount_point);
+ new_fstab_recs[n].fs_type = strdup(fs_type);
+ new_fstab_recs[n].blk_device = strdup(blk_device);
+ new_fstab_recs[n].length = 0;
+
+ /* Update the fstab struct */
+ fstab->recs = new_fstab_recs;
+ fstab->num_entries++;
+
+ return 0;
+}
+
+/*
+ * Returns the 1st matching fstab_rec that follows the start_rec.
+ * start_rec is the result of a previous search or NULL.
+ */
+struct fstab_rec *fs_mgr_get_entry_for_mount_point_after(struct fstab_rec *start_rec, struct fstab *fstab, const char *path)
+{
+ int i;
+ if (!fstab) {
+ return NULL;
+ }
+
+ if (start_rec) {
+ for (i = 0; i < fstab->num_entries; i++) {
+ if (&fstab->recs[i] == start_rec) {
+ i++;
+ break;
+ }
+ }
+ } else {
+ i = 0;
+ }
+ for (; i < fstab->num_entries; i++) {
+ int len = strlen(fstab->recs[i].mount_point);
+ if (strncmp(path, fstab->recs[i].mount_point, len) == 0 &&
+ (path[len] == '\0' || path[len] == '/')) {
+ return &fstab->recs[i];
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Returns the 1st matching mount point.
+ * There might be more. To look for others, use fs_mgr_get_entry_for_mount_point_after()
+ * and give the fstab_rec from the previous search.
+ */
+struct fstab_rec *fs_mgr_get_entry_for_mount_point(struct fstab *fstab, const char *path)
+{
+ return fs_mgr_get_entry_for_mount_point_after(NULL, fstab, path);
+}
+
+int fs_mgr_is_voldmanaged(const struct fstab_rec *fstab)
+{
+ return fstab->fs_mgr_flags & MF_VOLDMANAGED;
+}
+
+int fs_mgr_is_nonremovable(const struct fstab_rec *fstab)
+{
+ return fstab->fs_mgr_flags & MF_NONREMOVABLE;
+}
+
+int fs_mgr_is_verified(const struct fstab_rec *fstab)
+{
+ return fstab->fs_mgr_flags & MF_VERIFY;
+}
+
+int fs_mgr_is_encryptable(const struct fstab_rec *fstab)
+{
+ return fstab->fs_mgr_flags & (MF_CRYPT | MF_FORCECRYPT);
+}
+
+int fs_mgr_is_file_encrypted(const struct fstab_rec *fstab)
+{
+ return fstab->fs_mgr_flags & MF_FILEENCRYPTION;
+}
+
+int fs_mgr_is_noemulatedsd(const struct fstab_rec *fstab)
+{
+ return fstab->fs_mgr_flags & MF_NOEMULATEDSD;
+}
diff --git a/fs_mgr/fs_mgr_main.c b/fs_mgr/fs_mgr_main.c
new file mode 100644
index 0000000..e5a00d5
--- /dev/null
+++ b/fs_mgr/fs_mgr_main.c
@@ -0,0 +1,114 @@
+/*
+ * 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <libgen.h>
+#include "fs_mgr_priv.h"
+
+char *me = "";
+
+static void usage(void)
+{
+ ERROR("%s: usage: %s <-a | -n mnt_point blk_dev | -u> <fstab_file>\n", me, me);
+ exit(1);
+}
+
+/* Parse the command line. If an error is encountered, print an error message
+ * and exit the program, do not return to the caller.
+ * Return the number of argv[] entries consumed.
+ */
+static void parse_options(int argc, char *argv[], int *a_flag, int *u_flag, int *n_flag,
+ char **n_name, char **n_blk_dev)
+{
+ me = basename(strdup(argv[0]));
+
+ if (argc <= 1) {
+ usage();
+ }
+
+ if (!strcmp(argv[1], "-a")) {
+ if (argc != 3) {
+ usage();
+ }
+ *a_flag = 1;
+ }
+ if (!strcmp(argv[1], "-n")) {
+ if (argc != 5) {
+ usage();
+ }
+ *n_flag = 1;
+ *n_name = argv[2];
+ *n_blk_dev = argv[3];
+ }
+ if (!strcmp(argv[1], "-u")) {
+ if (argc != 3) {
+ usage();
+ }
+ *u_flag = 1;
+ }
+
+ /* If no flag is specified, it's an error */
+ if (!(*a_flag | *n_flag | *u_flag)) {
+ usage();
+ }
+
+ /* If more than one flag is specified, it's an error */
+ if ((*a_flag + *n_flag + *u_flag) > 1) {
+ usage();
+ }
+
+ return;
+}
+
+int main(int argc, char *argv[])
+{
+ int a_flag=0;
+ int u_flag=0;
+ int n_flag=0;
+ char *n_name=NULL;
+ char *n_blk_dev=NULL;
+ char *fstab_file=NULL;
+ struct fstab *fstab=NULL;
+
+ klog_init();
+ klog_set_level(6);
+
+ parse_options(argc, argv, &a_flag, &u_flag, &n_flag, &n_name, &n_blk_dev);
+
+ /* The name of the fstab file is last, after the option */
+ fstab_file = argv[argc - 1];
+
+ fstab = fs_mgr_read_fstab(fstab_file);
+
+ if (a_flag) {
+ return fs_mgr_mount_all(fstab);
+ } else if (n_flag) {
+ return fs_mgr_do_mount(fstab, n_name, n_blk_dev, 0);
+ } else if (u_flag) {
+ return fs_mgr_unmount_all(fstab);
+ } else {
+ ERROR("%s: Internal error, unknown option\n", me);
+ exit(1);
+ }
+
+ fs_mgr_free_fstab(fstab);
+
+ /* Should not get here */
+ exit(1);
+}
+
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
new file mode 100644
index 0000000..d56111a
--- /dev/null
+++ b/fs_mgr/fs_mgr_priv.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CORE_FS_MGR_PRIV_H
+#define __CORE_FS_MGR_PRIV_H
+
+#include <cutils/klog.h>
+#include <fs_mgr.h>
+
+#define INFO(x...) KLOG_INFO("fs_mgr", x)
+#define WARNING(x...) KLOG_WARNING("fs_mgr", x)
+#define ERROR(x...) KLOG_ERROR("fs_mgr", x)
+
+#define CRYPTO_TMPFS_OPTIONS "size=256m,mode=0771,uid=1000,gid=1000"
+
+#define WAIT_TIMEOUT 20
+
+/* fstab has the following format:
+ *
+ * Any line starting with a # is a comment and ignored
+ *
+ * Any blank line is ignored
+ *
+ * All other lines must be in this format:
+ * <source> <mount_point> <fs_type> <mount_flags> <fs_options> <fs_mgr_options>
+ *
+ * <mount_flags> is a comma separated list of flags that can be passed to the
+ * mount command. The list includes noatime, nosuid, nodev, nodiratime,
+ * ro, rw, remount, defaults.
+ *
+ * <fs_options> is a comma separated list of options accepted by the filesystem being
+ * mounted. It is passed directly to mount without being parsed
+ *
+ * <fs_mgr_options> is a comma separated list of flags that control the operation of
+ * the fs_mgr program. The list includes "wait", which will wait till
+ * the <source> file exists, and "check", which requests that the fs_mgr
+ * run an fscheck program on the <source> before mounting the filesystem.
+ * If check is specifed on a read-only filesystem, it is ignored.
+ * Also, "encryptable" means that filesystem can be encrypted.
+ * The "encryptable" flag _MUST_ be followed by a = and a string which
+ * is the location of the encryption keys. It can either be a path
+ * to a file or partition which contains the keys, or the word "footer"
+ * which means the keys are in the last 16 Kbytes of the partition
+ * containing the filesystem.
+ *
+ * When the fs_mgr is requested to mount all filesystems, it will first mount all the
+ * filesystems that do _NOT_ specify check (including filesystems that are read-only and
+ * specify check, because check is ignored in that case) and then it will check and mount
+ * filesystem marked with check.
+ *
+ */
+
+#define MF_WAIT 0x1
+#define MF_CHECK 0x2
+#define MF_CRYPT 0x4
+#define MF_NONREMOVABLE 0x8
+#define MF_VOLDMANAGED 0x10
+#define MF_LENGTH 0x20
+#define MF_RECOVERYONLY 0x40
+#define MF_SWAPPRIO 0x80
+#define MF_ZRAMSIZE 0x100
+#define MF_VERIFY 0x200
+#define MF_FORCECRYPT 0x400
+#define MF_NOEMULATEDSD 0x800 /* no emulated sdcard daemon, sd card is the only
+ external storage */
+#define MF_FILEENCRYPTION 0x2000
+
+#define DM_BUF_SIZE 4096
+
+int fs_mgr_set_blk_ro(const char *blockdev);
+
+#endif /* __CORE_FS_MGR_PRIV_H */
+
diff --git a/fs_mgr/fs_mgr_priv_verity.h b/fs_mgr/fs_mgr_priv_verity.h
new file mode 100644
index 0000000..f90e596
--- /dev/null
+++ b/fs_mgr/fs_mgr_priv_verity.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define FS_MGR_SETUP_VERITY_DISABLED -2
+#define FS_MGR_SETUP_VERITY_FAIL -1
+#define FS_MGR_SETUP_VERITY_SUCCESS 0
+int fs_mgr_setup_verity(struct fstab_rec *fstab);
diff --git a/fs_mgr/fs_mgr_verity.c b/fs_mgr/fs_mgr_verity.c
new file mode 100644
index 0000000..a4a99c3
--- /dev/null
+++ b/fs_mgr/fs_mgr_verity.c
@@ -0,0 +1,1054 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <libgen.h>
+#include <time.h>
+
+#include <private/android_filesystem_config.h>
+#include <cutils/properties.h>
+#include <logwrap/logwrap.h>
+
+#include "mincrypt/rsa.h"
+#include "mincrypt/sha.h"
+#include "mincrypt/sha256.h"
+
+#include "ext4_sb.h"
+#include "squashfs_utils.h"
+
+#include "fs_mgr_priv.h"
+#include "fs_mgr_priv_verity.h"
+
+#define FSTAB_PREFIX "/fstab."
+
+#define VERITY_METADATA_SIZE 32768
+#define VERITY_TABLE_RSA_KEY "/verity_key"
+
+#define METADATA_MAGIC 0x01564c54
+#define METADATA_TAG_MAX_LENGTH 63
+#define METADATA_EOD "eod"
+
+#define VERITY_LASTSIG_TAG "verity_lastsig"
+
+#define VERITY_STATE_TAG "verity_state"
+#define VERITY_STATE_HEADER 0x83c0ae9d
+#define VERITY_STATE_VERSION 1
+
+#define VERITY_KMSG_RESTART "dm-verity device corrupted"
+#define VERITY_KMSG_BUFSIZE 1024
+
+#define __STRINGIFY(x) #x
+#define STRINGIFY(x) __STRINGIFY(x)
+
+struct verity_state {
+ uint32_t header;
+ uint32_t version;
+ int32_t mode;
+};
+
+extern struct fs_info info;
+
+static RSAPublicKey *load_key(char *path)
+{
+ FILE *f;
+ RSAPublicKey *key;
+
+ key = malloc(sizeof(RSAPublicKey));
+ if (!key) {
+ ERROR("Can't malloc key\n");
+ return NULL;
+ }
+
+ f = fopen(path, "r");
+ if (!f) {
+ ERROR("Can't open '%s'\n", path);
+ free(key);
+ return NULL;
+ }
+
+ if (!fread(key, sizeof(*key), 1, f)) {
+ ERROR("Could not read key!");
+ fclose(f);
+ free(key);
+ return NULL;
+ }
+
+ if (key->len != RSANUMWORDS) {
+ ERROR("Invalid key length %d\n", key->len);
+ fclose(f);
+ free(key);
+ return NULL;
+ }
+
+ fclose(f);
+ return key;
+}
+
+static int verify_table(char *signature, char *table, int table_length)
+{
+ RSAPublicKey *key;
+ uint8_t hash_buf[SHA256_DIGEST_SIZE];
+ int retval = -1;
+
+ // Hash the table
+ SHA256_hash((uint8_t*)table, table_length, hash_buf);
+
+ // Now get the public key from the keyfile
+ key = load_key(VERITY_TABLE_RSA_KEY);
+ if (!key) {
+ ERROR("Couldn't load verity keys");
+ goto out;
+ }
+
+ // verify the result
+ if (!RSA_verify(key,
+ (uint8_t*) signature,
+ RSANUMBYTES,
+ (uint8_t*) hash_buf,
+ SHA256_DIGEST_SIZE)) {
+ ERROR("Couldn't verify table.");
+ goto out;
+ }
+
+ retval = 0;
+
+out:
+ free(key);
+ return retval;
+}
+
+static int squashfs_get_target_device_size(char *blk_device, uint64_t *device_size)
+{
+ struct squashfs_info sq_info;
+
+ if (squashfs_parse_sb(blk_device, &sq_info) >= 0) {
+ *device_size = sq_info.bytes_used_4K_padded;
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+static int ext4_get_target_device_size(char *blk_device, uint64_t *device_size)
+{
+ int data_device;
+ struct ext4_super_block sb;
+ struct fs_info info;
+
+ info.len = 0; /* Only len is set to 0 to ask the device for real size. */
+
+ data_device = TEMP_FAILURE_RETRY(open(blk_device, O_RDONLY | O_CLOEXEC));
+ if (data_device == -1) {
+ ERROR("Error opening block device (%s)", strerror(errno));
+ return -1;
+ }
+
+ if (TEMP_FAILURE_RETRY(lseek64(data_device, 1024, SEEK_SET)) < 0) {
+ ERROR("Error seeking to superblock");
+ close(data_device);
+ return -1;
+ }
+
+ if (TEMP_FAILURE_RETRY(read(data_device, &sb, sizeof(sb))) != sizeof(sb)) {
+ ERROR("Error reading superblock");
+ close(data_device);
+ return -1;
+ }
+
+ ext4_parse_sb(&sb, &info);
+ *device_size = info.len;
+
+ close(data_device);
+ return 0;
+}
+
+static int get_fs_size(char *fs_type, char *blk_device, uint64_t *device_size) {
+ if (!strcmp(fs_type, "ext4")) {
+ if (ext4_get_target_device_size(blk_device, device_size) < 0) {
+ ERROR("Failed to get ext4 fs size on %s.", blk_device);
+ return -1;
+ }
+ } else if (!strcmp(fs_type, "squashfs")) {
+ if (squashfs_get_target_device_size(blk_device, device_size) < 0) {
+ ERROR("Failed to get squashfs fs size on %s.", blk_device);
+ return -1;
+ }
+ } else {
+ ERROR("%s: Unsupported filesystem for verity.", fs_type);
+ return -1;
+ }
+ return 0;
+}
+
+static int read_verity_metadata(uint64_t device_size, char *block_device, char **signature,
+ char **table)
+{
+ unsigned magic_number;
+ unsigned table_length;
+ int protocol_version;
+ int device;
+ int retval = FS_MGR_SETUP_VERITY_FAIL;
+
+ *signature = NULL;
+
+ if (table) {
+ *table = NULL;
+ }
+
+ device = TEMP_FAILURE_RETRY(open(block_device, O_RDONLY | O_CLOEXEC));
+ if (device == -1) {
+ ERROR("Could not open block device %s (%s).\n", block_device, strerror(errno));
+ goto out;
+ }
+
+ if (TEMP_FAILURE_RETRY(lseek64(device, device_size, SEEK_SET)) < 0) {
+ ERROR("Could not seek to start of verity metadata block.\n");
+ goto out;
+ }
+
+ // check the magic number
+ if (TEMP_FAILURE_RETRY(read(device, &magic_number, sizeof(magic_number))) !=
+ sizeof(magic_number)) {
+ ERROR("Couldn't read magic number!\n");
+ goto out;
+ }
+
+#ifdef ALLOW_ADBD_DISABLE_VERITY
+ if (magic_number == VERITY_METADATA_MAGIC_DISABLE) {
+ retval = FS_MGR_SETUP_VERITY_DISABLED;
+ INFO("Attempt to cleanly disable verity - only works in USERDEBUG");
+ goto out;
+ }
+#endif
+
+ if (magic_number != VERITY_METADATA_MAGIC_NUMBER) {
+ ERROR("Couldn't find verity metadata at offset %"PRIu64"!\n", device_size);
+ goto out;
+ }
+
+ // check the protocol version
+ if (TEMP_FAILURE_RETRY(read(device, &protocol_version,
+ sizeof(protocol_version))) != sizeof(protocol_version)) {
+ ERROR("Couldn't read verity metadata protocol version!\n");
+ goto out;
+ }
+ if (protocol_version != 0) {
+ ERROR("Got unknown verity metadata protocol version %d!\n", protocol_version);
+ goto out;
+ }
+
+ // get the signature
+ *signature = (char*) malloc(RSANUMBYTES);
+ if (!*signature) {
+ ERROR("Couldn't allocate memory for signature!\n");
+ goto out;
+ }
+ if (TEMP_FAILURE_RETRY(read(device, *signature, RSANUMBYTES)) != RSANUMBYTES) {
+ ERROR("Couldn't read signature from verity metadata!\n");
+ goto out;
+ }
+
+ if (!table) {
+ retval = FS_MGR_SETUP_VERITY_SUCCESS;
+ goto out;
+ }
+
+ // get the size of the table
+ if (TEMP_FAILURE_RETRY(read(device, &table_length, sizeof(table_length))) !=
+ sizeof(table_length)) {
+ ERROR("Couldn't get the size of the verity table from metadata!\n");
+ goto out;
+ }
+
+ // get the table + null terminator
+ *table = malloc(table_length + 1);
+ if (!*table) {
+ ERROR("Couldn't allocate memory for verity table!\n");
+ goto out;
+ }
+ if (TEMP_FAILURE_RETRY(read(device, *table, table_length)) !=
+ (ssize_t)table_length) {
+ ERROR("Couldn't read the verity table from metadata!\n");
+ goto out;
+ }
+
+ (*table)[table_length] = 0;
+ retval = FS_MGR_SETUP_VERITY_SUCCESS;
+
+out:
+ if (device != -1)
+ close(device);
+
+ if (retval != FS_MGR_SETUP_VERITY_SUCCESS) {
+ free(*signature);
+ *signature = NULL;
+
+ if (table) {
+ free(*table);
+ *table = NULL;
+ }
+ }
+
+ return retval;
+}
+
+static void verity_ioctl_init(struct dm_ioctl *io, char *name, unsigned flags)
+{
+ memset(io, 0, DM_BUF_SIZE);
+ io->data_size = DM_BUF_SIZE;
+ io->data_start = sizeof(struct dm_ioctl);
+ io->version[0] = 4;
+ io->version[1] = 0;
+ io->version[2] = 0;
+ io->flags = flags | DM_READONLY_FLAG;
+ if (name) {
+ strlcpy(io->name, name, sizeof(io->name));
+ }
+}
+
+static int create_verity_device(struct dm_ioctl *io, char *name, int fd)
+{
+ verity_ioctl_init(io, name, 1);
+ if (ioctl(fd, DM_DEV_CREATE, io)) {
+ ERROR("Error creating device mapping (%s)", strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+static int get_verity_device_name(struct dm_ioctl *io, char *name, int fd, char **dev_name)
+{
+ verity_ioctl_init(io, name, 0);
+ if (ioctl(fd, DM_DEV_STATUS, io)) {
+ ERROR("Error fetching verity device number (%s)", strerror(errno));
+ return -1;
+ }
+ int dev_num = (io->dev & 0xff) | ((io->dev >> 12) & 0xfff00);
+ if (asprintf(dev_name, "/dev/block/dm-%u", dev_num) < 0) {
+ ERROR("Error getting verity block device name (%s)", strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+static int load_verity_table(struct dm_ioctl *io, char *name, uint64_t device_size, int fd, char *table,
+ int mode)
+{
+ char *verity_params;
+ char *buffer = (char*) io;
+ size_t bufsize;
+
+ verity_ioctl_init(io, name, DM_STATUS_TABLE_FLAG);
+
+ struct dm_target_spec *tgt = (struct dm_target_spec *) &buffer[sizeof(struct dm_ioctl)];
+
+ // set tgt arguments here
+ io->target_count = 1;
+ tgt->status=0;
+ tgt->sector_start=0;
+ tgt->length=device_size/512;
+ strcpy(tgt->target_type, "verity");
+
+ // build the verity params here
+ verity_params = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec);
+ bufsize = DM_BUF_SIZE - (verity_params - buffer);
+
+ if (mode == VERITY_MODE_EIO) {
+ // allow operation with older dm-verity drivers that are unaware
+ // of the mode parameter by omitting it; this also means that we
+ // cannot use logging mode with these drivers, they always cause
+ // an I/O error for corrupted blocks
+ strcpy(verity_params, table);
+ } else if (snprintf(verity_params, bufsize, "%s %d", table, mode) < 0) {
+ return -1;
+ }
+
+ // set next target boundary
+ verity_params += strlen(verity_params) + 1;
+ verity_params = (char*) (((unsigned long)verity_params + 7) & ~8);
+ tgt->next = verity_params - buffer;
+
+ // send the ioctl to load the verity table
+ if (ioctl(fd, DM_TABLE_LOAD, io)) {
+ ERROR("Error loading verity table (%s)", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int resume_verity_table(struct dm_ioctl *io, char *name, int fd)
+{
+ verity_ioctl_init(io, name, 0);
+ if (ioctl(fd, DM_DEV_SUSPEND, io)) {
+ ERROR("Error activating verity device (%s)", strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+static int test_access(char *device) {
+ int tries = 25;
+ while (tries--) {
+ if (!access(device, F_OK) || errno != ENOENT) {
+ return 0;
+ }
+ usleep(40 * 1000);
+ }
+ return -1;
+}
+
+static int check_verity_restart(const char *fname)
+{
+ char buffer[VERITY_KMSG_BUFSIZE + 1];
+ int fd;
+ int rc = 0;
+ ssize_t size;
+ struct stat s;
+
+ fd = TEMP_FAILURE_RETRY(open(fname, O_RDONLY | O_CLOEXEC));
+
+ if (fd == -1) {
+ if (errno != ENOENT) {
+ ERROR("Failed to open %s (%s)\n", fname, strerror(errno));
+ }
+ goto out;
+ }
+
+ if (fstat(fd, &s) == -1) {
+ ERROR("Failed to fstat %s (%s)\n", fname, strerror(errno));
+ goto out;
+ }
+
+ size = VERITY_KMSG_BUFSIZE;
+
+ if (size > s.st_size) {
+ size = s.st_size;
+ }
+
+ if (lseek(fd, s.st_size - size, SEEK_SET) == -1) {
+ ERROR("Failed to lseek %jd %s (%s)\n", (intmax_t)(s.st_size - size), fname,
+ strerror(errno));
+ goto out;
+ }
+
+ if (TEMP_FAILURE_RETRY(read(fd, buffer, size)) != size) {
+ ERROR("Failed to read %zd bytes from %s (%s)\n", size, fname,
+ strerror(errno));
+ goto out;
+ }
+
+ buffer[size] = '\0';
+
+ if (strstr(buffer, VERITY_KMSG_RESTART) != NULL) {
+ rc = 1;
+ }
+
+out:
+ if (fd != -1) {
+ close(fd);
+ }
+
+ return rc;
+}
+
+static int was_verity_restart()
+{
+ static const char *files[] = {
+ "/sys/fs/pstore/console-ramoops",
+ "/proc/last_kmsg",
+ NULL
+ };
+ int i;
+
+ for (i = 0; files[i]; ++i) {
+ if (check_verity_restart(files[i])) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int metadata_add(FILE *fp, long start, const char *tag,
+ unsigned int length, off64_t *offset)
+{
+ if (fseek(fp, start, SEEK_SET) < 0 ||
+ fprintf(fp, "%s %u\n", tag, length) < 0) {
+ return -1;
+ }
+
+ *offset = ftell(fp);
+
+ if (fseek(fp, length, SEEK_CUR) < 0 ||
+ fprintf(fp, METADATA_EOD " 0\n") < 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int metadata_find(const char *fname, const char *stag,
+ unsigned int slength, off64_t *offset)
+{
+ FILE *fp = NULL;
+ char tag[METADATA_TAG_MAX_LENGTH + 1];
+ int rc = -1;
+ int n;
+ long start = 0x4000; /* skip cryptfs metadata area */
+ uint32_t magic;
+ unsigned int length = 0;
+
+ if (!fname) {
+ return -1;
+ }
+
+ fp = fopen(fname, "r+");
+
+ if (!fp) {
+ ERROR("Failed to open %s (%s)\n", fname, strerror(errno));
+ goto out;
+ }
+
+ /* check magic */
+ if (fseek(fp, start, SEEK_SET) < 0 ||
+ fread(&magic, sizeof(magic), 1, fp) != 1) {
+ ERROR("Failed to read magic from %s (%s)\n", fname, strerror(errno));
+ goto out;
+ }
+
+ if (magic != METADATA_MAGIC) {
+ magic = METADATA_MAGIC;
+
+ if (fseek(fp, start, SEEK_SET) < 0 ||
+ fwrite(&magic, sizeof(magic), 1, fp) != 1) {
+ ERROR("Failed to write magic to %s (%s)\n", fname, strerror(errno));
+ goto out;
+ }
+
+ rc = metadata_add(fp, start + sizeof(magic), stag, slength, offset);
+ if (rc < 0) {
+ ERROR("Failed to add metadata to %s: %s\n", fname, strerror(errno));
+ }
+
+ goto out;
+ }
+
+ start += sizeof(magic);
+
+ while (1) {
+ n = fscanf(fp, "%" STRINGIFY(METADATA_TAG_MAX_LENGTH) "s %u\n",
+ tag, &length);
+
+ if (n == 2 && strcmp(tag, METADATA_EOD)) {
+ /* found a tag */
+ start = ftell(fp);
+
+ if (!strcmp(tag, stag) && length == slength) {
+ *offset = start;
+ rc = 0;
+ goto out;
+ }
+
+ start += length;
+
+ if (fseek(fp, length, SEEK_CUR) < 0) {
+ ERROR("Failed to seek %s (%s)\n", fname, strerror(errno));
+ goto out;
+ }
+ } else {
+ rc = metadata_add(fp, start, stag, slength, offset);
+ if (rc < 0) {
+ ERROR("Failed to write metadata to %s: %s\n", fname,
+ strerror(errno));
+ }
+ goto out;
+ }
+ }
+
+out:
+ if (fp) {
+ fflush(fp);
+ fclose(fp);
+ }
+
+ return rc;
+}
+
+static int write_verity_state(const char *fname, off64_t offset, int32_t mode)
+{
+ int fd;
+ int rc = -1;
+ struct verity_state s = { VERITY_STATE_HEADER, VERITY_STATE_VERSION, mode };
+
+ fd = TEMP_FAILURE_RETRY(open(fname, O_WRONLY | O_SYNC | O_CLOEXEC));
+
+ if (fd == -1) {
+ ERROR("Failed to open %s (%s)\n", fname, strerror(errno));
+ goto out;
+ }
+
+ if (TEMP_FAILURE_RETRY(pwrite64(fd, &s, sizeof(s), offset)) != sizeof(s)) {
+ ERROR("Failed to write %zu bytes to %s to offset %" PRIu64 " (%s)\n",
+ sizeof(s), fname, offset, strerror(errno));
+ goto out;
+ }
+
+ rc = 0;
+
+out:
+ if (fd != -1) {
+ close(fd);
+ }
+
+ return rc;
+}
+
+static int read_verity_state(const char *fname, off64_t offset, int *mode)
+{
+ int fd = -1;
+ int rc = -1;
+ struct verity_state s;
+
+ fd = TEMP_FAILURE_RETRY(open(fname, O_RDONLY | O_CLOEXEC));
+
+ if (fd == -1) {
+ ERROR("Failed to open %s (%s)\n", fname, strerror(errno));
+ goto out;
+ }
+
+ if (TEMP_FAILURE_RETRY(pread64(fd, &s, sizeof(s), offset)) != sizeof(s)) {
+ ERROR("Failed to read %zu bytes from %s offset %" PRIu64 " (%s)\n",
+ sizeof(s), fname, offset, strerror(errno));
+ goto out;
+ }
+
+ if (s.header != VERITY_STATE_HEADER) {
+ /* space allocated, but no state written. write default state */
+ *mode = VERITY_MODE_DEFAULT;
+ rc = write_verity_state(fname, offset, *mode);
+ goto out;
+ }
+
+ if (s.version != VERITY_STATE_VERSION) {
+ ERROR("Unsupported verity state version (%u)\n", s.version);
+ goto out;
+ }
+
+ if (s.mode < VERITY_MODE_EIO ||
+ s.mode > VERITY_MODE_LAST) {
+ ERROR("Unsupported verity mode (%u)\n", s.mode);
+ goto out;
+ }
+
+ *mode = s.mode;
+ rc = 0;
+
+out:
+ if (fd != -1) {
+ close(fd);
+ }
+
+ return rc;
+}
+
+static int compare_last_signature(struct fstab_rec *fstab, int *match)
+{
+ char tag[METADATA_TAG_MAX_LENGTH + 1];
+ char *signature = NULL;
+ int fd = -1;
+ int rc = -1;
+ uint8_t curr[SHA256_DIGEST_SIZE];
+ uint8_t prev[SHA256_DIGEST_SIZE];
+ off64_t offset = 0;
+ uint64_t device_size;
+
+ *match = 1;
+
+ // get verity filesystem size
+ if (get_fs_size(fstab->fs_type, fstab->blk_device, &device_size) < 0) {
+ ERROR("Failed to get filesystem size\n");
+ goto out;
+ }
+
+ if (read_verity_metadata(device_size, fstab->blk_device, &signature, NULL) < 0) {
+ ERROR("Failed to read verity signature from %s\n", fstab->mount_point);
+ goto out;
+ }
+
+ SHA256_hash(signature, RSANUMBYTES, curr);
+
+ if (snprintf(tag, sizeof(tag), VERITY_LASTSIG_TAG "_%s",
+ basename(fstab->mount_point)) >= (int)sizeof(tag)) {
+ ERROR("Metadata tag name too long for %s\n", fstab->mount_point);
+ goto out;
+ }
+
+ if (metadata_find(fstab->verity_loc, tag, SHA256_DIGEST_SIZE,
+ &offset) < 0) {
+ goto out;
+ }
+
+ fd = TEMP_FAILURE_RETRY(open(fstab->verity_loc, O_RDWR | O_SYNC | O_CLOEXEC));
+
+ if (fd == -1) {
+ ERROR("Failed to open %s: %s\n", fstab->verity_loc, strerror(errno));
+ goto out;
+ }
+
+ if (TEMP_FAILURE_RETRY(pread64(fd, prev, sizeof(prev),
+ offset)) != sizeof(prev)) {
+ ERROR("Failed to read %zu bytes from %s offset %" PRIu64 " (%s)\n",
+ sizeof(prev), fstab->verity_loc, offset, strerror(errno));
+ goto out;
+ }
+
+ *match = !memcmp(curr, prev, SHA256_DIGEST_SIZE);
+
+ if (!*match) {
+ /* update current signature hash */
+ if (TEMP_FAILURE_RETRY(pwrite64(fd, curr, sizeof(curr),
+ offset)) != sizeof(curr)) {
+ ERROR("Failed to write %zu bytes to %s offset %" PRIu64 " (%s)\n",
+ sizeof(curr), fstab->verity_loc, offset, strerror(errno));
+ goto out;
+ }
+ }
+
+ rc = 0;
+
+out:
+ free(signature);
+
+ if (fd != -1) {
+ close(fd);
+ }
+
+ return rc;
+}
+
+static int get_verity_state_offset(struct fstab_rec *fstab, off64_t *offset)
+{
+ char tag[METADATA_TAG_MAX_LENGTH + 1];
+
+ if (snprintf(tag, sizeof(tag), VERITY_STATE_TAG "_%s",
+ basename(fstab->mount_point)) >= (int)sizeof(tag)) {
+ ERROR("Metadata tag name too long for %s\n", fstab->mount_point);
+ return -1;
+ }
+
+ return metadata_find(fstab->verity_loc, tag, sizeof(struct verity_state),
+ offset);
+}
+
+static int load_verity_state(struct fstab_rec *fstab, int *mode)
+{
+ char propbuf[PROPERTY_VALUE_MAX];
+ int match = 0;
+ off64_t offset = 0;
+
+ /* use the kernel parameter if set */
+ property_get("ro.boot.veritymode", propbuf, "");
+
+ if (*propbuf != '\0') {
+ if (!strcmp(propbuf, "enforcing")) {
+ *mode = VERITY_MODE_DEFAULT;
+ return 0;
+ } else if (!strcmp(propbuf, "logging")) {
+ *mode = VERITY_MODE_LOGGING;
+ return 0;
+ } else {
+ INFO("Unknown value %s for veritymode; ignoring", propbuf);
+ }
+ }
+
+ if (get_verity_state_offset(fstab, &offset) < 0) {
+ /* fall back to stateless behavior */
+ *mode = VERITY_MODE_EIO;
+ return 0;
+ }
+
+ if (was_verity_restart()) {
+ /* device was restarted after dm-verity detected a corrupted
+ * block, so switch to logging mode */
+ *mode = VERITY_MODE_LOGGING;
+ return write_verity_state(fstab->verity_loc, offset, *mode);
+ }
+
+ if (!compare_last_signature(fstab, &match) && !match) {
+ /* partition has been reflashed, reset dm-verity state */
+ *mode = VERITY_MODE_DEFAULT;
+ return write_verity_state(fstab->verity_loc, offset, *mode);
+ }
+
+ return read_verity_state(fstab->verity_loc, offset, mode);
+}
+
+int fs_mgr_load_verity_state(int *mode)
+{
+ char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)];
+ char propbuf[PROPERTY_VALUE_MAX];
+ int rc = -1;
+ int i;
+ int current;
+ struct fstab *fstab = NULL;
+
+ /* return the default mode, unless any of the verified partitions are in
+ * logging mode, in which case return that */
+ *mode = VERITY_MODE_DEFAULT;
+
+ property_get("ro.hardware", propbuf, "");
+ snprintf(fstab_filename, sizeof(fstab_filename), FSTAB_PREFIX"%s", propbuf);
+
+ fstab = fs_mgr_read_fstab(fstab_filename);
+
+ if (!fstab) {
+ ERROR("Failed to read %s\n", fstab_filename);
+ goto out;
+ }
+
+ for (i = 0; i < fstab->num_entries; i++) {
+ if (!fs_mgr_is_verified(&fstab->recs[i])) {
+ continue;
+ }
+
+ rc = load_verity_state(&fstab->recs[i], ¤t);
+ if (rc < 0) {
+ continue;
+ }
+
+ if (current == VERITY_MODE_LOGGING) {
+ *mode = current;
+ }
+ }
+
+ rc = 0;
+
+out:
+ if (fstab) {
+ fs_mgr_free_fstab(fstab);
+ }
+
+ return rc;
+}
+
+int fs_mgr_update_verity_state(fs_mgr_verity_state_callback callback)
+{
+ _Alignas(struct dm_ioctl) char buffer[DM_BUF_SIZE];
+ bool use_state = true;
+ char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)];
+ char *mount_point;
+ char propbuf[PROPERTY_VALUE_MAX];
+ char *status;
+ int fd = -1;
+ int i;
+ int mode;
+ int rc = -1;
+ off64_t offset = 0;
+ struct dm_ioctl *io = (struct dm_ioctl *) buffer;
+ struct fstab *fstab = NULL;
+
+ /* check if we need to store the state */
+ property_get("ro.boot.veritymode", propbuf, "");
+
+ if (*propbuf != '\0') {
+ if (fs_mgr_load_verity_state(&mode) == -1) {
+ return -1;
+ }
+ use_state = false; /* state is kept by the bootloader */
+ }
+
+ fd = TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC));
+
+ if (fd == -1) {
+ ERROR("Error opening device mapper (%s)\n", strerror(errno));
+ goto out;
+ }
+
+ property_get("ro.hardware", propbuf, "");
+ snprintf(fstab_filename, sizeof(fstab_filename), FSTAB_PREFIX"%s", propbuf);
+
+ fstab = fs_mgr_read_fstab(fstab_filename);
+
+ if (!fstab) {
+ ERROR("Failed to read %s\n", fstab_filename);
+ goto out;
+ }
+
+ for (i = 0; i < fstab->num_entries; i++) {
+ if (!fs_mgr_is_verified(&fstab->recs[i])) {
+ continue;
+ }
+
+ if (use_state) {
+ if (get_verity_state_offset(&fstab->recs[i], &offset) < 0 ||
+ read_verity_state(fstab->recs[i].verity_loc, offset, &mode) < 0) {
+ continue;
+ }
+ }
+
+ mount_point = basename(fstab->recs[i].mount_point);
+ verity_ioctl_init(io, mount_point, 0);
+
+ if (ioctl(fd, DM_TABLE_STATUS, io)) {
+ ERROR("Failed to query DM_TABLE_STATUS for %s (%s)\n", mount_point,
+ strerror(errno));
+ continue;
+ }
+
+ status = &buffer[io->data_start + sizeof(struct dm_target_spec)];
+
+ if (use_state && *status == 'C') {
+ if (write_verity_state(fstab->recs[i].verity_loc, offset,
+ VERITY_MODE_LOGGING) < 0) {
+ continue;
+ }
+ }
+
+ if (callback) {
+ callback(&fstab->recs[i], mount_point, mode, *status);
+ }
+ }
+
+ rc = 0;
+
+out:
+ if (fstab) {
+ fs_mgr_free_fstab(fstab);
+ }
+
+ if (fd) {
+ close(fd);
+ }
+
+ return rc;
+}
+
+int fs_mgr_setup_verity(struct fstab_rec *fstab) {
+
+ int retval = FS_MGR_SETUP_VERITY_FAIL;
+ int fd = -1;
+ int mode;
+
+ char *verity_blk_name = 0;
+ char *verity_table = 0;
+ char *verity_table_signature = 0;
+ uint64_t device_size = 0;
+
+ _Alignas(struct dm_ioctl) char buffer[DM_BUF_SIZE];
+ struct dm_ioctl *io = (struct dm_ioctl *) buffer;
+ char *mount_point = basename(fstab->mount_point);
+
+ // get verity filesystem size
+ if (get_fs_size(fstab->fs_type, fstab->blk_device, &device_size) < 0) {
+ return retval;
+ }
+
+ // read the verity block at the end of the block device
+ // send error code up the chain so we can detect attempts to disable verity
+ retval = read_verity_metadata(device_size,
+ fstab->blk_device,
+ &verity_table_signature,
+ &verity_table);
+ if (retval < 0) {
+ goto out;
+ }
+
+ retval = FS_MGR_SETUP_VERITY_FAIL;
+
+ // get the device mapper fd
+ if ((fd = open("/dev/device-mapper", O_RDWR)) < 0) {
+ ERROR("Error opening device mapper (%s)", strerror(errno));
+ goto out;
+ }
+
+ // create the device
+ if (create_verity_device(io, mount_point, fd) < 0) {
+ ERROR("Couldn't create verity device!");
+ goto out;
+ }
+
+ // get the name of the device file
+ if (get_verity_device_name(io, mount_point, fd, &verity_blk_name) < 0) {
+ ERROR("Couldn't get verity device number!");
+ goto out;
+ }
+
+ // verify the signature on the table
+ if (verify_table(verity_table_signature,
+ verity_table,
+ strlen(verity_table)) < 0) {
+ goto out;
+ }
+
+ if (load_verity_state(fstab, &mode) < 0) {
+ /* if accessing or updating the state failed, switch to the default
+ * safe mode. This makes sure the device won't end up in an endless
+ * restart loop, and no corrupted data will be exposed to userspace
+ * without a warning. */
+ mode = VERITY_MODE_EIO;
+ }
+
+ INFO("Enabling dm-verity for %s (mode %d)\n", mount_point, mode);
+
+ // load the verity mapping table
+ if (load_verity_table(io, mount_point, device_size, fd, verity_table,
+ mode) < 0) {
+ goto out;
+ }
+
+ // activate the device
+ if (resume_verity_table(io, mount_point, fd) < 0) {
+ goto out;
+ }
+
+ // mark the underlying block device as read-only
+ fs_mgr_set_blk_ro(fstab->blk_device);
+
+ // assign the new verity block device as the block device
+ free(fstab->blk_device);
+ fstab->blk_device = verity_blk_name;
+ verity_blk_name = 0;
+
+ // make sure we've set everything up properly
+ if (test_access(fstab->blk_device) < 0) {
+ goto out;
+ }
+
+ retval = FS_MGR_SETUP_VERITY_SUCCESS;
+
+out:
+ if (fd != -1) {
+ close(fd);
+ }
+
+ free(verity_table);
+ free(verity_table_signature);
+ free(verity_blk_name);
+
+ return retval;
+}
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
new file mode 100644
index 0000000..c58a888
--- /dev/null
+++ b/fs_mgr/include/fs_mgr.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CORE_FS_MGR_H
+#define __CORE_FS_MGR_H
+
+#include <stdint.h>
+#include <linux/dm-ioctl.h>
+
+// Magic number at start of verity metadata
+#define VERITY_METADATA_MAGIC_NUMBER 0xb001b001
+
+// Replacement magic number at start of verity metadata to cleanly
+// turn verity off in userdebug builds.
+#define VERITY_METADATA_MAGIC_DISABLE 0x46464f56 // "VOFF"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Verity modes
+enum verity_mode {
+ VERITY_MODE_EIO = 0,
+ VERITY_MODE_LOGGING = 1,
+ VERITY_MODE_RESTART = 2,
+ VERITY_MODE_LAST = VERITY_MODE_RESTART,
+ VERITY_MODE_DEFAULT = VERITY_MODE_RESTART
+};
+
+/*
+ * The entries must be kept in the same order as they were seen in the fstab.
+ * Unless explicitly requested, a lookup on mount point should always
+ * return the 1st one.
+ */
+struct fstab {
+ int num_entries;
+ struct fstab_rec *recs;
+ char *fstab_filename;
+};
+
+struct fstab_rec {
+ char *blk_device;
+ char *mount_point;
+ char *fs_type;
+ unsigned long flags;
+ char *fs_options;
+ int fs_mgr_flags;
+ char *key_loc;
+ char *verity_loc;
+ long long length;
+ char *label;
+ int partnum;
+ int swap_prio;
+ unsigned int zram_size;
+};
+
+// Callback function for verity status
+typedef void (*fs_mgr_verity_state_callback)(struct fstab_rec *fstab,
+ const char *mount_point, int mode, int status);
+
+struct fstab *fs_mgr_read_fstab(const char *fstab_path);
+void fs_mgr_free_fstab(struct fstab *fstab);
+
+#define FS_MGR_MNTALL_DEV_NON_DEFAULT_FILE_ENCRYPTED 5
+#define FS_MGR_MNTALL_DEV_DEFAULT_FILE_ENCRYPTED 4
+#define FS_MGR_MNTALL_DEV_NEEDS_RECOVERY 3
+#define FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION 2
+#define FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED 1
+#define FS_MGR_MNTALL_DEV_NOT_ENCRYPTED 0
+#define FS_MGR_MNTALL_FAIL -1
+int fs_mgr_mount_all(struct fstab *fstab);
+
+#define FS_MGR_DOMNT_FAILED -1
+#define FS_MGR_DOMNT_BUSY -2
+int fs_mgr_do_mount(struct fstab *fstab, char *n_name, char *n_blk_device,
+ char *tmp_mount_point);
+int fs_mgr_do_tmpfs_mount(char *n_name);
+int fs_mgr_unmount_all(struct fstab *fstab);
+int fs_mgr_get_crypt_info(struct fstab *fstab, char *key_loc,
+ char *real_blk_device, int size);
+int fs_mgr_load_verity_state(int *mode);
+int fs_mgr_update_verity_state(fs_mgr_verity_state_callback callback);
+int fs_mgr_add_entry(struct fstab *fstab,
+ const char *mount_point, const char *fs_type,
+ const char *blk_device);
+struct fstab_rec *fs_mgr_get_entry_for_mount_point(struct fstab *fstab, const char *path);
+int fs_mgr_is_voldmanaged(const struct fstab_rec *fstab);
+int fs_mgr_is_nonremovable(const struct fstab_rec *fstab);
+int fs_mgr_is_verified(const struct fstab_rec *fstab);
+int fs_mgr_is_encryptable(const struct fstab_rec *fstab);
+int fs_mgr_is_file_encrypted(const struct fstab_rec *fstab);
+int fs_mgr_is_noemulatedsd(const struct fstab_rec *fstab);
+int fs_mgr_swapon_all(struct fstab *fstab);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CORE_FS_MGR_H */
+
diff --git a/healthd/Android.mk b/healthd/Android.mk
new file mode 100644
index 0000000..07e1d73
--- /dev/null
+++ b/healthd/Android.mk
@@ -0,0 +1,77 @@
+# Copyright 2013 The Android Open Source Project
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := healthd_board_default.cpp
+LOCAL_MODULE := libhealthd.default
+LOCAL_CFLAGS := -Werror
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ healthd.cpp \
+ healthd_mode_android.cpp \
+ healthd_mode_charger.cpp \
+ BatteryMonitor.cpp \
+ BatteryPropertiesRegistrar.cpp
+
+LOCAL_MODULE := healthd
+LOCAL_MODULE_TAGS := optional
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN)
+LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED)
+
+LOCAL_CFLAGS := -D__STDC_LIMIT_MACROS -Werror
+
+ifeq ($(strip $(BOARD_CHARGER_DISABLE_INIT_BLANK)),true)
+LOCAL_CFLAGS += -DCHARGER_DISABLE_INIT_BLANK
+endif
+
+ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
+LOCAL_CFLAGS += -DCHARGER_ENABLE_SUSPEND
+endif
+
+LOCAL_C_INCLUDES := bootable/recovery
+
+LOCAL_STATIC_LIBRARIES := libbatteryservice libbinder libminui libpng libz libutils libstdc++ libcutils liblog libm libc
+
+ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
+LOCAL_STATIC_LIBRARIES += libsuspend
+endif
+
+LOCAL_HAL_STATIC_LIBRARIES := libhealthd
+
+# Symlink /charger to /sbin/healthd
+LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT) \
+ && ln -sf /sbin/healthd $(TARGET_ROOT_OUT)/charger
+
+include $(BUILD_EXECUTABLE)
+
+
+define _add-charger-image
+include $$(CLEAR_VARS)
+LOCAL_MODULE := system_core_charger_$(notdir $(1))
+LOCAL_MODULE_STEM := $(notdir $(1))
+_img_modules += $$(LOCAL_MODULE)
+LOCAL_SRC_FILES := $1
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $$(TARGET_ROOT_OUT)/res/images/charger
+include $$(BUILD_PREBUILT)
+endef
+
+_img_modules :=
+_images :=
+$(foreach _img, $(call find-subdir-subdir-files, "images", "*.png"), \
+ $(eval $(call _add-charger-image,$(_img))))
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := charger_res_images
+LOCAL_MODULE_TAGS := optional
+LOCAL_REQUIRED_MODULES := $(_img_modules)
+include $(BUILD_PHONY_PACKAGE)
+
+_add-charger-image :=
+_img_modules :=
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
new file mode 100644
index 0000000..7ea8250
--- /dev/null
+++ b/healthd/BatteryMonitor.cpp
@@ -0,0 +1,541 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "healthd"
+
+#include "healthd.h"
+#include "BatteryMonitor.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <batteryservice/BatteryService.h>
+#include <cutils/klog.h>
+#include <cutils/properties.h>
+#include <sys/types.h>
+#include <utils/Errors.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+#define POWER_SUPPLY_SUBSYSTEM "power_supply"
+#define POWER_SUPPLY_SYSFS_PATH "/sys/class/" POWER_SUPPLY_SUBSYSTEM
+#define FAKE_BATTERY_CAPACITY 42
+#define FAKE_BATTERY_TEMPERATURE 424
+
+namespace android {
+
+struct sysfsStringEnumMap {
+ const char* s;
+ int val;
+};
+
+static int mapSysfsString(const char* str,
+ struct sysfsStringEnumMap map[]) {
+ for (int i = 0; map[i].s; i++)
+ if (!strcmp(str, map[i].s))
+ return map[i].val;
+
+ return -1;
+}
+
+int BatteryMonitor::getBatteryStatus(const char* status) {
+ int ret;
+ struct sysfsStringEnumMap batteryStatusMap[] = {
+ { "Unknown", BATTERY_STATUS_UNKNOWN },
+ { "Charging", BATTERY_STATUS_CHARGING },
+ { "Discharging", BATTERY_STATUS_DISCHARGING },
+ { "Not charging", BATTERY_STATUS_NOT_CHARGING },
+ { "Full", BATTERY_STATUS_FULL },
+ { NULL, 0 },
+ };
+
+ ret = mapSysfsString(status, batteryStatusMap);
+ if (ret < 0) {
+ KLOG_WARNING(LOG_TAG, "Unknown battery status '%s'\n", status);
+ ret = BATTERY_STATUS_UNKNOWN;
+ }
+
+ return ret;
+}
+
+int BatteryMonitor::getBatteryHealth(const char* status) {
+ int ret;
+ struct sysfsStringEnumMap batteryHealthMap[] = {
+ { "Unknown", BATTERY_HEALTH_UNKNOWN },
+ { "Good", BATTERY_HEALTH_GOOD },
+ { "Overheat", BATTERY_HEALTH_OVERHEAT },
+ { "Dead", BATTERY_HEALTH_DEAD },
+ { "Over voltage", BATTERY_HEALTH_OVER_VOLTAGE },
+ { "Unspecified failure", BATTERY_HEALTH_UNSPECIFIED_FAILURE },
+ { "Cold", BATTERY_HEALTH_COLD },
+ { NULL, 0 },
+ };
+
+ ret = mapSysfsString(status, batteryHealthMap);
+ if (ret < 0) {
+ KLOG_WARNING(LOG_TAG, "Unknown battery health '%s'\n", status);
+ ret = BATTERY_HEALTH_UNKNOWN;
+ }
+
+ return ret;
+}
+
+int BatteryMonitor::readFromFile(const String8& path, char* buf, size_t size) {
+ char *cp = NULL;
+
+ if (path.isEmpty())
+ return -1;
+ int fd = open(path.string(), O_RDONLY, 0);
+ if (fd == -1) {
+ KLOG_ERROR(LOG_TAG, "Could not open '%s'\n", path.string());
+ return -1;
+ }
+
+ ssize_t count = TEMP_FAILURE_RETRY(read(fd, buf, size));
+ if (count > 0)
+ cp = (char *)memrchr(buf, '\n', count);
+
+ if (cp)
+ *cp = '\0';
+ else
+ buf[0] = '\0';
+
+ close(fd);
+ return count;
+}
+
+BatteryMonitor::PowerSupplyType BatteryMonitor::readPowerSupplyType(const String8& path) {
+ const int SIZE = 128;
+ char buf[SIZE];
+ int length = readFromFile(path, buf, SIZE);
+ BatteryMonitor::PowerSupplyType ret;
+ struct sysfsStringEnumMap supplyTypeMap[] = {
+ { "Unknown", ANDROID_POWER_SUPPLY_TYPE_UNKNOWN },
+ { "Battery", ANDROID_POWER_SUPPLY_TYPE_BATTERY },
+ { "UPS", ANDROID_POWER_SUPPLY_TYPE_AC },
+ { "Mains", ANDROID_POWER_SUPPLY_TYPE_AC },
+ { "USB", ANDROID_POWER_SUPPLY_TYPE_USB },
+ { "USB_DCP", ANDROID_POWER_SUPPLY_TYPE_AC },
+ { "USB_CDP", ANDROID_POWER_SUPPLY_TYPE_AC },
+ { "USB_ACA", ANDROID_POWER_SUPPLY_TYPE_AC },
+ { "Wireless", ANDROID_POWER_SUPPLY_TYPE_WIRELESS },
+ { NULL, 0 },
+ };
+
+ if (length <= 0)
+ return ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
+
+ ret = (BatteryMonitor::PowerSupplyType)mapSysfsString(buf, supplyTypeMap);
+ if (ret < 0)
+ ret = ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
+
+ return ret;
+}
+
+bool BatteryMonitor::getBooleanField(const String8& path) {
+ const int SIZE = 16;
+ char buf[SIZE];
+
+ bool value = false;
+ if (readFromFile(path, buf, SIZE) > 0) {
+ if (buf[0] != '0') {
+ value = true;
+ }
+ }
+
+ return value;
+}
+
+int BatteryMonitor::getIntField(const String8& path) {
+ const int SIZE = 128;
+ char buf[SIZE];
+
+ int value = 0;
+ if (readFromFile(path, buf, SIZE) > 0) {
+ value = strtol(buf, NULL, 0);
+ }
+ return value;
+}
+
+bool BatteryMonitor::update(void) {
+ bool logthis;
+
+ props.chargerAcOnline = false;
+ props.chargerUsbOnline = false;
+ props.chargerWirelessOnline = false;
+ props.batteryStatus = BATTERY_STATUS_UNKNOWN;
+ props.batteryHealth = BATTERY_HEALTH_UNKNOWN;
+
+ if (!mHealthdConfig->batteryPresentPath.isEmpty())
+ props.batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);
+ else
+ props.batteryPresent = mBatteryDevicePresent;
+
+ props.batteryLevel = mBatteryFixedCapacity ?
+ mBatteryFixedCapacity :
+ getIntField(mHealthdConfig->batteryCapacityPath);
+ props.batteryVoltage = getIntField(mHealthdConfig->batteryVoltagePath) / 1000;
+
+ props.batteryTemperature = mBatteryFixedTemperature ?
+ mBatteryFixedTemperature :
+ getIntField(mHealthdConfig->batteryTemperaturePath);
+
+ const int SIZE = 128;
+ char buf[SIZE];
+ String8 btech;
+
+ if (readFromFile(mHealthdConfig->batteryStatusPath, buf, SIZE) > 0)
+ props.batteryStatus = getBatteryStatus(buf);
+
+ if (readFromFile(mHealthdConfig->batteryHealthPath, buf, SIZE) > 0)
+ props.batteryHealth = getBatteryHealth(buf);
+
+ if (readFromFile(mHealthdConfig->batteryTechnologyPath, buf, SIZE) > 0)
+ props.batteryTechnology = String8(buf);
+
+ unsigned int i;
+
+ for (i = 0; i < mChargerNames.size(); i++) {
+ String8 path;
+ path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH,
+ mChargerNames[i].string());
+
+ if (readFromFile(path, buf, SIZE) > 0) {
+ if (buf[0] != '0') {
+ path.clear();
+ path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH,
+ mChargerNames[i].string());
+ switch(readPowerSupplyType(path)) {
+ case ANDROID_POWER_SUPPLY_TYPE_AC:
+ props.chargerAcOnline = true;
+ break;
+ case ANDROID_POWER_SUPPLY_TYPE_USB:
+ props.chargerUsbOnline = true;
+ break;
+ case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
+ props.chargerWirelessOnline = true;
+ break;
+ default:
+ KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n",
+ mChargerNames[i].string());
+ }
+ }
+ }
+ }
+
+ logthis = !healthd_board_battery_update(&props);
+
+ if (logthis) {
+ char dmesgline[256];
+
+ if (props.batteryPresent) {
+ snprintf(dmesgline, sizeof(dmesgline),
+ "battery l=%d v=%d t=%s%d.%d h=%d st=%d",
+ props.batteryLevel, props.batteryVoltage,
+ props.batteryTemperature < 0 ? "-" : "",
+ abs(props.batteryTemperature / 10),
+ abs(props.batteryTemperature % 10), props.batteryHealth,
+ props.batteryStatus);
+
+ if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+ int c = getIntField(mHealthdConfig->batteryCurrentNowPath);
+ char b[20];
+
+ snprintf(b, sizeof(b), " c=%d", c / 1000);
+ strlcat(dmesgline, b, sizeof(dmesgline));
+ }
+ } else {
+ snprintf(dmesgline, sizeof(dmesgline),
+ "battery none");
+ }
+
+ KLOG_WARNING(LOG_TAG, "%s chg=%s%s%s\n", dmesgline,
+ props.chargerAcOnline ? "a" : "",
+ props.chargerUsbOnline ? "u" : "",
+ props.chargerWirelessOnline ? "w" : "");
+ }
+
+ healthd_mode_ops->battery_update(&props);
+ return props.chargerAcOnline | props.chargerUsbOnline |
+ props.chargerWirelessOnline;
+}
+
+status_t BatteryMonitor::getProperty(int id, struct BatteryProperty *val) {
+ status_t ret = BAD_VALUE;
+
+ val->valueInt64 = LONG_MIN;
+
+ switch(id) {
+ case BATTERY_PROP_CHARGE_COUNTER:
+ if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
+ val->valueInt64 =
+ getIntField(mHealthdConfig->batteryChargeCounterPath);
+ ret = NO_ERROR;
+ } else {
+ ret = NAME_NOT_FOUND;
+ }
+ break;
+
+ case BATTERY_PROP_CURRENT_NOW:
+ if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+ val->valueInt64 =
+ getIntField(mHealthdConfig->batteryCurrentNowPath);
+ ret = NO_ERROR;
+ } else {
+ ret = NAME_NOT_FOUND;
+ }
+ break;
+
+ case BATTERY_PROP_CURRENT_AVG:
+ if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
+ val->valueInt64 =
+ getIntField(mHealthdConfig->batteryCurrentAvgPath);
+ ret = NO_ERROR;
+ } else {
+ ret = NAME_NOT_FOUND;
+ }
+ break;
+
+ case BATTERY_PROP_CAPACITY:
+ if (!mHealthdConfig->batteryCapacityPath.isEmpty()) {
+ val->valueInt64 =
+ getIntField(mHealthdConfig->batteryCapacityPath);
+ ret = NO_ERROR;
+ } else {
+ ret = NAME_NOT_FOUND;
+ }
+ break;
+
+ case BATTERY_PROP_ENERGY_COUNTER:
+ if (mHealthdConfig->energyCounter) {
+ ret = mHealthdConfig->energyCounter(&val->valueInt64);
+ } else {
+ ret = NAME_NOT_FOUND;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+void BatteryMonitor::dumpState(int fd) {
+ int v;
+ char vs[128];
+
+ snprintf(vs, sizeof(vs), "ac: %d usb: %d wireless: %d\n",
+ props.chargerAcOnline, props.chargerUsbOnline,
+ props.chargerWirelessOnline);
+ write(fd, vs, strlen(vs));
+ snprintf(vs, sizeof(vs), "status: %d health: %d present: %d\n",
+ props.batteryStatus, props.batteryHealth, props.batteryPresent);
+ write(fd, vs, strlen(vs));
+ snprintf(vs, sizeof(vs), "level: %d voltage: %d temp: %d\n",
+ props.batteryLevel, props.batteryVoltage,
+ props.batteryTemperature);
+ write(fd, vs, strlen(vs));
+
+ if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+ v = getIntField(mHealthdConfig->batteryCurrentNowPath);
+ snprintf(vs, sizeof(vs), "current now: %d\n", v);
+ write(fd, vs, strlen(vs));
+ }
+
+ if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
+ v = getIntField(mHealthdConfig->batteryCurrentAvgPath);
+ snprintf(vs, sizeof(vs), "current avg: %d\n", v);
+ write(fd, vs, strlen(vs));
+ }
+
+ if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
+ v = getIntField(mHealthdConfig->batteryChargeCounterPath);
+ snprintf(vs, sizeof(vs), "charge counter: %d\n", v);
+ write(fd, vs, strlen(vs));
+ }
+}
+
+void BatteryMonitor::init(struct healthd_config *hc) {
+ String8 path;
+ char pval[PROPERTY_VALUE_MAX];
+
+ mHealthdConfig = hc;
+ DIR* dir = opendir(POWER_SUPPLY_SYSFS_PATH);
+ if (dir == NULL) {
+ KLOG_ERROR(LOG_TAG, "Could not open %s\n", POWER_SUPPLY_SYSFS_PATH);
+ } else {
+ struct dirent* entry;
+
+ while ((entry = readdir(dir))) {
+ const char* name = entry->d_name;
+
+ if (!strcmp(name, ".") || !strcmp(name, ".."))
+ continue;
+
+ // Look for "type" file in each subdirectory
+ path.clear();
+ path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH, name);
+ switch(readPowerSupplyType(path)) {
+ case ANDROID_POWER_SUPPLY_TYPE_AC:
+ case ANDROID_POWER_SUPPLY_TYPE_USB:
+ case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
+ path.clear();
+ path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name);
+ if (access(path.string(), R_OK) == 0)
+ mChargerNames.add(String8(name));
+ break;
+
+ case ANDROID_POWER_SUPPLY_TYPE_BATTERY:
+ mBatteryDevicePresent = true;
+
+ if (mHealthdConfig->batteryStatusPath.isEmpty()) {
+ path.clear();
+ path.appendFormat("%s/%s/status", POWER_SUPPLY_SYSFS_PATH,
+ name);
+ if (access(path, R_OK) == 0)
+ mHealthdConfig->batteryStatusPath = path;
+ }
+
+ if (mHealthdConfig->batteryHealthPath.isEmpty()) {
+ path.clear();
+ path.appendFormat("%s/%s/health", POWER_SUPPLY_SYSFS_PATH,
+ name);
+ if (access(path, R_OK) == 0)
+ mHealthdConfig->batteryHealthPath = path;
+ }
+
+ if (mHealthdConfig->batteryPresentPath.isEmpty()) {
+ path.clear();
+ path.appendFormat("%s/%s/present", POWER_SUPPLY_SYSFS_PATH,
+ name);
+ if (access(path, R_OK) == 0)
+ mHealthdConfig->batteryPresentPath = path;
+ }
+
+ if (mHealthdConfig->batteryCapacityPath.isEmpty()) {
+ path.clear();
+ path.appendFormat("%s/%s/capacity", POWER_SUPPLY_SYSFS_PATH,
+ name);
+ if (access(path, R_OK) == 0)
+ mHealthdConfig->batteryCapacityPath = path;
+ }
+
+ if (mHealthdConfig->batteryVoltagePath.isEmpty()) {
+ path.clear();
+ path.appendFormat("%s/%s/voltage_now",
+ POWER_SUPPLY_SYSFS_PATH, name);
+ if (access(path, R_OK) == 0) {
+ mHealthdConfig->batteryVoltagePath = path;
+ } else {
+ path.clear();
+ path.appendFormat("%s/%s/batt_vol",
+ POWER_SUPPLY_SYSFS_PATH, name);
+ if (access(path, R_OK) == 0)
+ mHealthdConfig->batteryVoltagePath = path;
+ }
+ }
+
+ if (mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+ path.clear();
+ path.appendFormat("%s/%s/current_now",
+ POWER_SUPPLY_SYSFS_PATH, name);
+ if (access(path, R_OK) == 0)
+ mHealthdConfig->batteryCurrentNowPath = path;
+ }
+
+ if (mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
+ path.clear();
+ path.appendFormat("%s/%s/current_avg",
+ POWER_SUPPLY_SYSFS_PATH, name);
+ if (access(path, R_OK) == 0)
+ mHealthdConfig->batteryCurrentAvgPath = path;
+ }
+
+ if (mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
+ path.clear();
+ path.appendFormat("%s/%s/charge_counter",
+ POWER_SUPPLY_SYSFS_PATH, name);
+ if (access(path, R_OK) == 0)
+ mHealthdConfig->batteryChargeCounterPath = path;
+ }
+
+ if (mHealthdConfig->batteryTemperaturePath.isEmpty()) {
+ path.clear();
+ path.appendFormat("%s/%s/temp", POWER_SUPPLY_SYSFS_PATH,
+ name);
+ if (access(path, R_OK) == 0) {
+ mHealthdConfig->batteryTemperaturePath = path;
+ } else {
+ path.clear();
+ path.appendFormat("%s/%s/batt_temp",
+ POWER_SUPPLY_SYSFS_PATH, name);
+ if (access(path, R_OK) == 0)
+ mHealthdConfig->batteryTemperaturePath = path;
+ }
+ }
+
+ if (mHealthdConfig->batteryTechnologyPath.isEmpty()) {
+ path.clear();
+ path.appendFormat("%s/%s/technology",
+ POWER_SUPPLY_SYSFS_PATH, name);
+ if (access(path, R_OK) == 0)
+ mHealthdConfig->batteryTechnologyPath = path;
+ }
+
+ break;
+
+ case ANDROID_POWER_SUPPLY_TYPE_UNKNOWN:
+ break;
+ }
+ }
+ closedir(dir);
+ }
+
+ if (!mChargerNames.size())
+ KLOG_ERROR(LOG_TAG, "No charger supplies found\n");
+ if (!mBatteryDevicePresent) {
+ KLOG_WARNING(LOG_TAG, "No battery devices found\n");
+ hc->periodic_chores_interval_fast = -1;
+ hc->periodic_chores_interval_slow = -1;
+ } else {
+ if (mHealthdConfig->batteryStatusPath.isEmpty())
+ KLOG_WARNING(LOG_TAG, "BatteryStatusPath not found\n");
+ if (mHealthdConfig->batteryHealthPath.isEmpty())
+ KLOG_WARNING(LOG_TAG, "BatteryHealthPath not found\n");
+ if (mHealthdConfig->batteryPresentPath.isEmpty())
+ KLOG_WARNING(LOG_TAG, "BatteryPresentPath not found\n");
+ if (mHealthdConfig->batteryCapacityPath.isEmpty())
+ KLOG_WARNING(LOG_TAG, "BatteryCapacityPath not found\n");
+ if (mHealthdConfig->batteryVoltagePath.isEmpty())
+ KLOG_WARNING(LOG_TAG, "BatteryVoltagePath not found\n");
+ if (mHealthdConfig->batteryTemperaturePath.isEmpty())
+ KLOG_WARNING(LOG_TAG, "BatteryTemperaturePath not found\n");
+ if (mHealthdConfig->batteryTechnologyPath.isEmpty())
+ KLOG_WARNING(LOG_TAG, "BatteryTechnologyPath not found\n");
+ }
+
+ if (property_get("ro.boot.fake_battery", pval, NULL) > 0
+ && strtol(pval, NULL, 10) != 0) {
+ mBatteryFixedCapacity = FAKE_BATTERY_CAPACITY;
+ mBatteryFixedTemperature = FAKE_BATTERY_TEMPERATURE;
+ }
+}
+
+}; // namespace android
diff --git a/healthd/BatteryMonitor.h b/healthd/BatteryMonitor.h
new file mode 100644
index 0000000..3425f27
--- /dev/null
+++ b/healthd/BatteryMonitor.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef HEALTHD_BATTERYMONITOR_H
+#define HEALTHD_BATTERYMONITOR_H
+
+#include <batteryservice/BatteryService.h>
+#include <binder/IInterface.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+#include "healthd.h"
+
+namespace android {
+
+class BatteryMonitor {
+ public:
+
+ enum PowerSupplyType {
+ ANDROID_POWER_SUPPLY_TYPE_UNKNOWN = 0,
+ ANDROID_POWER_SUPPLY_TYPE_AC,
+ ANDROID_POWER_SUPPLY_TYPE_USB,
+ ANDROID_POWER_SUPPLY_TYPE_WIRELESS,
+ ANDROID_POWER_SUPPLY_TYPE_BATTERY
+ };
+
+ void init(struct healthd_config *hc);
+ bool update(void);
+ status_t getProperty(int id, struct BatteryProperty *val);
+ void dumpState(int fd);
+
+ private:
+ struct healthd_config *mHealthdConfig;
+ Vector<String8> mChargerNames;
+ bool mBatteryDevicePresent;
+ int mBatteryFixedCapacity;
+ int mBatteryFixedTemperature;
+ struct BatteryProperties props;
+
+ int getBatteryStatus(const char* status);
+ int getBatteryHealth(const char* status);
+ int readFromFile(const String8& path, char* buf, size_t size);
+ PowerSupplyType readPowerSupplyType(const String8& path);
+ bool getBooleanField(const String8& path);
+ int getIntField(const String8& path);
+};
+
+}; // namespace android
+
+#endif // HEALTHD_BATTERY_MONTIOR_H
diff --git a/healthd/BatteryPropertiesRegistrar.cpp b/healthd/BatteryPropertiesRegistrar.cpp
new file mode 100644
index 0000000..09667a1
--- /dev/null
+++ b/healthd/BatteryPropertiesRegistrar.cpp
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "BatteryPropertiesRegistrar.h"
+#include <batteryservice/BatteryService.h>
+#include <batteryservice/IBatteryPropertiesListener.h>
+#include <batteryservice/IBatteryPropertiesRegistrar.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/PermissionCache.h>
+#include <private/android_filesystem_config.h>
+#include <utils/Errors.h>
+#include <utils/Mutex.h>
+#include <utils/String16.h>
+
+#include "healthd.h"
+
+namespace android {
+
+void BatteryPropertiesRegistrar::publish() {
+ defaultServiceManager()->addService(String16("batteryproperties"), this);
+}
+
+void BatteryPropertiesRegistrar::notifyListeners(struct BatteryProperties props) {
+ Mutex::Autolock _l(mRegistrationLock);
+ for (size_t i = 0; i < mListeners.size(); i++) {
+ mListeners[i]->batteryPropertiesChanged(props);
+ }
+}
+
+void BatteryPropertiesRegistrar::registerListener(const sp<IBatteryPropertiesListener>& listener) {
+ {
+ if (listener == NULL)
+ return;
+ Mutex::Autolock _l(mRegistrationLock);
+ // check whether this is a duplicate
+ for (size_t i = 0; i < mListeners.size(); i++) {
+ if (IInterface::asBinder(mListeners[i]) == IInterface::asBinder(listener)) {
+ return;
+ }
+ }
+
+ mListeners.add(listener);
+ IInterface::asBinder(listener)->linkToDeath(this);
+ }
+ healthd_battery_update();
+}
+
+void BatteryPropertiesRegistrar::unregisterListener(const sp<IBatteryPropertiesListener>& listener) {
+ if (listener == NULL)
+ return;
+ Mutex::Autolock _l(mRegistrationLock);
+ for (size_t i = 0; i < mListeners.size(); i++) {
+ if (IInterface::asBinder(mListeners[i]) == IInterface::asBinder(listener)) {
+ IInterface::asBinder(mListeners[i])->unlinkToDeath(this);
+ mListeners.removeAt(i);
+ break;
+ }
+ }
+}
+
+status_t BatteryPropertiesRegistrar::getProperty(int id, struct BatteryProperty *val) {
+ return healthd_get_property(id, val);
+}
+
+status_t BatteryPropertiesRegistrar::dump(int fd, const Vector<String16>& /*args*/) {
+ IPCThreadState* self = IPCThreadState::self();
+ const int pid = self->getCallingPid();
+ const int uid = self->getCallingUid();
+ if ((uid != AID_SHELL) &&
+ !PermissionCache::checkPermission(
+ String16("android.permission.DUMP"), pid, uid))
+ return PERMISSION_DENIED;
+
+ healthd_dump_battery_state(fd);
+ return OK;
+}
+
+void BatteryPropertiesRegistrar::binderDied(const wp<IBinder>& who) {
+ Mutex::Autolock _l(mRegistrationLock);
+
+ for (size_t i = 0; i < mListeners.size(); i++) {
+ if (IInterface::asBinder(mListeners[i]) == who) {
+ mListeners.removeAt(i);
+ break;
+ }
+ }
+}
+
+} // namespace android
diff --git a/healthd/BatteryPropertiesRegistrar.h b/healthd/BatteryPropertiesRegistrar.h
new file mode 100644
index 0000000..8853874
--- /dev/null
+++ b/healthd/BatteryPropertiesRegistrar.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef HEALTHD_BATTERYPROPERTIES_REGISTRAR_H
+#define HEALTHD_BATTERYPROPERTIES_REGISTRAR_H
+
+#include <binder/IBinder.h>
+#include <utils/Mutex.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+#include <batteryservice/BatteryService.h>
+#include <batteryservice/IBatteryPropertiesListener.h>
+#include <batteryservice/IBatteryPropertiesRegistrar.h>
+
+namespace android {
+
+class BatteryPropertiesRegistrar : public BnBatteryPropertiesRegistrar,
+ public IBinder::DeathRecipient {
+public:
+ void publish();
+ void notifyListeners(struct BatteryProperties props);
+
+private:
+ Mutex mRegistrationLock;
+ Vector<sp<IBatteryPropertiesListener> > mListeners;
+
+ void registerListener(const sp<IBatteryPropertiesListener>& listener);
+ void unregisterListener(const sp<IBatteryPropertiesListener>& listener);
+ status_t getProperty(int id, struct BatteryProperty *val);
+ status_t dump(int fd, const Vector<String16>& args);
+ void binderDied(const wp<IBinder>& who);
+};
+
+}; // namespace android
+
+#endif // HEALTHD_BATTERYPROPERTIES_REGISTRAR_H
diff --git a/healthd/healthd.cpp b/healthd/healthd.cpp
new file mode 100644
index 0000000..1fee855
--- /dev/null
+++ b/healthd/healthd.cpp
@@ -0,0 +1,362 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "healthd"
+#define KLOG_LEVEL 6
+
+#include "healthd.h"
+#include "BatteryMonitor.h"
+
+#include <errno.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <batteryservice/BatteryService.h>
+#include <cutils/klog.h>
+#include <cutils/uevent.h>
+#include <sys/epoll.h>
+#include <sys/timerfd.h>
+#include <utils/Errors.h>
+
+using namespace android;
+
+// Periodic chores intervals in seconds
+#define DEFAULT_PERIODIC_CHORES_INTERVAL_FAST (60 * 1)
+#define DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW (60 * 10)
+
+static struct healthd_config healthd_config = {
+ .periodic_chores_interval_fast = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST,
+ .periodic_chores_interval_slow = DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW,
+ .batteryStatusPath = String8(String8::kEmptyString),
+ .batteryHealthPath = String8(String8::kEmptyString),
+ .batteryPresentPath = String8(String8::kEmptyString),
+ .batteryCapacityPath = String8(String8::kEmptyString),
+ .batteryVoltagePath = String8(String8::kEmptyString),
+ .batteryTemperaturePath = String8(String8::kEmptyString),
+ .batteryTechnologyPath = String8(String8::kEmptyString),
+ .batteryCurrentNowPath = String8(String8::kEmptyString),
+ .batteryCurrentAvgPath = String8(String8::kEmptyString),
+ .batteryChargeCounterPath = String8(String8::kEmptyString),
+ .energyCounter = NULL,
+ .screen_on = NULL,
+};
+
+static int eventct;
+static int epollfd;
+
+#define POWER_SUPPLY_SUBSYSTEM "power_supply"
+
+// epoll_create() parameter is actually unused
+#define MAX_EPOLL_EVENTS 40
+static int uevent_fd;
+static int wakealarm_fd;
+
+// -1 for no epoll timeout
+static int awake_poll_interval = -1;
+
+static int wakealarm_wake_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST;
+
+static BatteryMonitor* gBatteryMonitor;
+
+struct healthd_mode_ops *healthd_mode_ops;
+
+// Android mode
+
+extern void healthd_mode_android_init(struct healthd_config *config);
+extern int healthd_mode_android_preparetowait(void);
+extern void healthd_mode_android_battery_update(
+ struct android::BatteryProperties *props);
+
+// Charger mode
+
+extern void healthd_mode_charger_init(struct healthd_config *config);
+extern int healthd_mode_charger_preparetowait(void);
+extern void healthd_mode_charger_heartbeat(void);
+extern void healthd_mode_charger_battery_update(
+ struct android::BatteryProperties *props);
+
+// NOPs for modes that need no special action
+
+static void healthd_mode_nop_init(struct healthd_config *config);
+static int healthd_mode_nop_preparetowait(void);
+static void healthd_mode_nop_heartbeat(void);
+static void healthd_mode_nop_battery_update(
+ struct android::BatteryProperties *props);
+
+static struct healthd_mode_ops android_ops = {
+ .init = healthd_mode_android_init,
+ .preparetowait = healthd_mode_android_preparetowait,
+ .heartbeat = healthd_mode_nop_heartbeat,
+ .battery_update = healthd_mode_android_battery_update,
+};
+
+static struct healthd_mode_ops charger_ops = {
+ .init = healthd_mode_charger_init,
+ .preparetowait = healthd_mode_charger_preparetowait,
+ .heartbeat = healthd_mode_charger_heartbeat,
+ .battery_update = healthd_mode_charger_battery_update,
+};
+
+static struct healthd_mode_ops recovery_ops = {
+ .init = healthd_mode_nop_init,
+ .preparetowait = healthd_mode_nop_preparetowait,
+ .heartbeat = healthd_mode_nop_heartbeat,
+ .battery_update = healthd_mode_nop_battery_update,
+};
+
+static void healthd_mode_nop_init(struct healthd_config* /*config*/) {
+}
+
+static int healthd_mode_nop_preparetowait(void) {
+ return -1;
+}
+
+static void healthd_mode_nop_heartbeat(void) {
+}
+
+static void healthd_mode_nop_battery_update(
+ struct android::BatteryProperties* /*props*/) {
+}
+
+int healthd_register_event(int fd, void (*handler)(uint32_t)) {
+ struct epoll_event ev;
+
+ ev.events = EPOLLIN | EPOLLWAKEUP;
+ ev.data.ptr = (void *)handler;
+ if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) {
+ KLOG_ERROR(LOG_TAG,
+ "epoll_ctl failed; errno=%d\n", errno);
+ return -1;
+ }
+
+ eventct++;
+ return 0;
+}
+
+static void wakealarm_set_interval(int interval) {
+ struct itimerspec itval;
+
+ if (wakealarm_fd == -1)
+ return;
+
+ wakealarm_wake_interval = interval;
+
+ if (interval == -1)
+ interval = 0;
+
+ itval.it_interval.tv_sec = interval;
+ itval.it_interval.tv_nsec = 0;
+ itval.it_value.tv_sec = interval;
+ itval.it_value.tv_nsec = 0;
+
+ if (timerfd_settime(wakealarm_fd, 0, &itval, NULL) == -1)
+ KLOG_ERROR(LOG_TAG, "wakealarm_set_interval: timerfd_settime failed\n");
+}
+
+status_t healthd_get_property(int id, struct BatteryProperty *val) {
+ return gBatteryMonitor->getProperty(id, val);
+}
+
+void healthd_battery_update(void) {
+ // Fast wake interval when on charger (watch for overheat);
+ // slow wake interval when on battery (watch for drained battery).
+
+ int new_wake_interval = gBatteryMonitor->update() ?
+ healthd_config.periodic_chores_interval_fast :
+ healthd_config.periodic_chores_interval_slow;
+
+ if (new_wake_interval != wakealarm_wake_interval)
+ wakealarm_set_interval(new_wake_interval);
+
+ // During awake periods poll at fast rate. If wake alarm is set at fast
+ // rate then just use the alarm; if wake alarm is set at slow rate then
+ // poll at fast rate while awake and let alarm wake up at slow rate when
+ // asleep.
+
+ if (healthd_config.periodic_chores_interval_fast == -1)
+ awake_poll_interval = -1;
+ else
+ awake_poll_interval =
+ new_wake_interval == healthd_config.periodic_chores_interval_fast ?
+ -1 : healthd_config.periodic_chores_interval_fast * 1000;
+}
+
+void healthd_dump_battery_state(int fd) {
+ gBatteryMonitor->dumpState(fd);
+ fsync(fd);
+}
+
+static void periodic_chores() {
+ healthd_battery_update();
+}
+
+#define UEVENT_MSG_LEN 2048
+static void uevent_event(uint32_t /*epevents*/) {
+ char msg[UEVENT_MSG_LEN+2];
+ char *cp;
+ int n;
+
+ n = uevent_kernel_multicast_recv(uevent_fd, msg, UEVENT_MSG_LEN);
+ if (n <= 0)
+ return;
+ if (n >= UEVENT_MSG_LEN) /* overflow -- discard */
+ return;
+
+ msg[n] = '\0';
+ msg[n+1] = '\0';
+ cp = msg;
+
+ while (*cp) {
+ if (!strcmp(cp, "SUBSYSTEM=" POWER_SUPPLY_SUBSYSTEM)) {
+ healthd_battery_update();
+ break;
+ }
+
+ /* advance to after the next \0 */
+ while (*cp++)
+ ;
+ }
+}
+
+static void uevent_init(void) {
+ uevent_fd = uevent_open_socket(64*1024, true);
+
+ if (uevent_fd < 0) {
+ KLOG_ERROR(LOG_TAG, "uevent_init: uevent_open_socket failed\n");
+ return;
+ }
+
+ fcntl(uevent_fd, F_SETFL, O_NONBLOCK);
+ if (healthd_register_event(uevent_fd, uevent_event))
+ KLOG_ERROR(LOG_TAG,
+ "register for uevent events failed\n");
+}
+
+static void wakealarm_event(uint32_t /*epevents*/) {
+ unsigned long long wakeups;
+
+ if (read(wakealarm_fd, &wakeups, sizeof(wakeups)) == -1) {
+ KLOG_ERROR(LOG_TAG, "wakealarm_event: read wakealarm fd failed\n");
+ return;
+ }
+
+ periodic_chores();
+}
+
+static void wakealarm_init(void) {
+ wakealarm_fd = timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK);
+ if (wakealarm_fd == -1) {
+ KLOG_ERROR(LOG_TAG, "wakealarm_init: timerfd_create failed\n");
+ return;
+ }
+
+ if (healthd_register_event(wakealarm_fd, wakealarm_event))
+ KLOG_ERROR(LOG_TAG,
+ "Registration of wakealarm event failed\n");
+
+ wakealarm_set_interval(healthd_config.periodic_chores_interval_fast);
+}
+
+static void healthd_mainloop(void) {
+ while (1) {
+ struct epoll_event events[eventct];
+ int nevents;
+ int timeout = awake_poll_interval;
+ int mode_timeout;
+
+ mode_timeout = healthd_mode_ops->preparetowait();
+ if (timeout < 0 || (mode_timeout > 0 && mode_timeout < timeout))
+ timeout = mode_timeout;
+ nevents = epoll_wait(epollfd, events, eventct, timeout);
+
+ if (nevents == -1) {
+ if (errno == EINTR)
+ continue;
+ KLOG_ERROR(LOG_TAG, "healthd_mainloop: epoll_wait failed\n");
+ break;
+ }
+
+ for (int n = 0; n < nevents; ++n) {
+ if (events[n].data.ptr)
+ (*(void (*)(int))events[n].data.ptr)(events[n].events);
+ }
+
+ if (!nevents)
+ periodic_chores();
+
+ healthd_mode_ops->heartbeat();
+ }
+
+ return;
+}
+
+static int healthd_init() {
+ epollfd = epoll_create(MAX_EPOLL_EVENTS);
+ if (epollfd == -1) {
+ KLOG_ERROR(LOG_TAG,
+ "epoll_create failed; errno=%d\n",
+ errno);
+ return -1;
+ }
+
+ healthd_board_init(&healthd_config);
+ healthd_mode_ops->init(&healthd_config);
+ wakealarm_init();
+ uevent_init();
+ gBatteryMonitor = new BatteryMonitor();
+ gBatteryMonitor->init(&healthd_config);
+ return 0;
+}
+
+int main(int argc, char **argv) {
+ int ch;
+ int ret;
+
+ klog_set_level(KLOG_LEVEL);
+ healthd_mode_ops = &android_ops;
+
+ if (!strcmp(basename(argv[0]), "charger")) {
+ healthd_mode_ops = &charger_ops;
+ } else {
+ while ((ch = getopt(argc, argv, "cr")) != -1) {
+ switch (ch) {
+ case 'c':
+ healthd_mode_ops = &charger_ops;
+ break;
+ case 'r':
+ healthd_mode_ops = &recovery_ops;
+ break;
+ case '?':
+ default:
+ KLOG_ERROR(LOG_TAG, "Unrecognized healthd option: %c\n",
+ optopt);
+ exit(1);
+ }
+ }
+ }
+
+ ret = healthd_init();
+ if (ret) {
+ KLOG_ERROR("Initialization failed, exiting\n");
+ exit(2);
+ }
+
+ healthd_mainloop();
+ KLOG_ERROR("Main loop terminated, exiting\n");
+ return 3;
+}
diff --git a/healthd/healthd.h b/healthd/healthd.h
new file mode 100644
index 0000000..4704f0b
--- /dev/null
+++ b/healthd/healthd.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _HEALTHD_H_
+#define _HEALTHD_H_
+
+#include <batteryservice/BatteryService.h>
+#include <sys/types.h>
+#include <utils/Errors.h>
+#include <utils/String8.h>
+
+// periodic_chores_interval_fast, periodic_chores_interval_slow: intervals at
+// which healthd wakes up to poll health state and perform periodic chores,
+// in units of seconds:
+//
+// periodic_chores_interval_fast is used while the device is not in
+// suspend, or in suspend and connected to a charger (to watch for battery
+// overheat due to charging). The default value is 60 (1 minute). Value
+// -1 turns off periodic chores (and wakeups) in these conditions.
+//
+// periodic_chores_interval_slow is used when the device is in suspend and
+// not connected to a charger (to watch for a battery drained to zero
+// remaining capacity). The default value is 600 (10 minutes). Value -1
+// tuns off periodic chores (and wakeups) in these conditions.
+//
+// power_supply sysfs attribute file paths. Set these to specific paths
+// to use for the associated battery parameters. healthd will search for
+// appropriate power_supply attribute files to use for any paths left empty:
+//
+// batteryStatusPath: charging status (POWER_SUPPLY_PROP_STATUS)
+// batteryHealthPath: battery health (POWER_SUPPLY_PROP_HEALTH)
+// batteryPresentPath: battery present (POWER_SUPPLY_PROP_PRESENT)
+// batteryCapacityPath: remaining capacity (POWER_SUPPLY_PROP_CAPACITY)
+// batteryVoltagePath: battery voltage (POWER_SUPPLY_PROP_VOLTAGE_NOW)
+// batteryTemperaturePath: battery temperature (POWER_SUPPLY_PROP_TEMP)
+// batteryTechnologyPath: battery technology (POWER_SUPPLY_PROP_TECHNOLOGY)
+// batteryCurrentNowPath: battery current (POWER_SUPPLY_PROP_CURRENT_NOW)
+// batteryChargeCounterPath: battery accumulated charge
+// (POWER_SUPPLY_PROP_CHARGE_COUNTER)
+
+struct healthd_config {
+ int periodic_chores_interval_fast;
+ int periodic_chores_interval_slow;
+
+ android::String8 batteryStatusPath;
+ android::String8 batteryHealthPath;
+ android::String8 batteryPresentPath;
+ android::String8 batteryCapacityPath;
+ android::String8 batteryVoltagePath;
+ android::String8 batteryTemperaturePath;
+ android::String8 batteryTechnologyPath;
+ android::String8 batteryCurrentNowPath;
+ android::String8 batteryCurrentAvgPath;
+ android::String8 batteryChargeCounterPath;
+
+ int (*energyCounter)(int64_t *);
+ bool (*screen_on)(android::BatteryProperties *props);
+};
+
+// Global helper functions
+
+int healthd_register_event(int fd, void (*handler)(uint32_t));
+void healthd_battery_update();
+android::status_t healthd_get_property(int id,
+ struct android::BatteryProperty *val);
+void healthd_dump_battery_state(int fd);
+
+struct healthd_mode_ops {
+ void (*init)(struct healthd_config *config);
+ int (*preparetowait)(void);
+ void (*heartbeat)(void);
+ void (*battery_update)(struct android::BatteryProperties *props);
+};
+
+extern struct healthd_mode_ops *healthd_mode_ops;
+
+// Charger mode
+
+void healthd_mode_charger_init(struct healthd_config *config);
+int healthd_mode_charger_preparetowait(void);
+void healthd_mode_charger_heartbeat(void);
+void healthd_mode_charger_battery_update(
+ struct android::BatteryProperties *props);
+
+// The following are implemented in libhealthd_board to handle board-specific
+// behavior.
+//
+// healthd_board_init() is called at startup time to modify healthd's
+// configuration according to board-specific requirements. config
+// points to the healthd configuration values described above. To use default
+// values, this function can simply return without modifying the fields of the
+// config parameter.
+
+void healthd_board_init(struct healthd_config *config);
+
+// Process updated battery property values. This function is called when
+// the kernel sends updated battery status via a uevent from the power_supply
+// subsystem, or when updated values are polled by healthd, as for periodic
+// poll of battery state.
+//
+// props are the battery properties read from the kernel. These values may
+// be modified in this call, prior to sending the modified values to the
+// Android runtime.
+//
+// Return 0 to indicate the usual kernel log battery status heartbeat message
+// is to be logged, or non-zero to prevent logging this information.
+
+int healthd_board_battery_update(struct android::BatteryProperties *props);
+
+#endif /* _HEALTHD_H_ */
diff --git a/healthd/healthd_board_default.cpp b/healthd/healthd_board_default.cpp
new file mode 100644
index 0000000..ed4ddb4
--- /dev/null
+++ b/healthd/healthd_board_default.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <healthd.h>
+
+void healthd_board_init(struct healthd_config*)
+{
+ // use defaults
+}
+
+
+int healthd_board_battery_update(struct android::BatteryProperties*)
+{
+ // return 0 to log periodic polled battery status to kernel log
+ return 0;
+}
diff --git a/healthd/healthd_mode_android.cpp b/healthd/healthd_mode_android.cpp
new file mode 100644
index 0000000..fd153a2
--- /dev/null
+++ b/healthd/healthd_mode_android.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "healthd-android"
+
+#include "healthd.h"
+#include "BatteryPropertiesRegistrar.h"
+
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+#include <cutils/klog.h>
+#include <sys/epoll.h>
+
+using namespace android;
+
+static int gBinderFd;
+static sp<BatteryPropertiesRegistrar> gBatteryPropertiesRegistrar;
+
+void healthd_mode_android_battery_update(
+ struct android::BatteryProperties *props) {
+ if (gBatteryPropertiesRegistrar != NULL)
+ gBatteryPropertiesRegistrar->notifyListeners(*props);
+
+ return;
+}
+
+int healthd_mode_android_preparetowait(void) {
+ IPCThreadState::self()->flushCommands();
+ return -1;
+}
+
+static void binder_event(uint32_t /*epevents*/) {
+ IPCThreadState::self()->handlePolledCommands();
+}
+
+void healthd_mode_android_init(struct healthd_config* /*config*/) {
+ ProcessState::self()->setThreadPoolMaxThreadCount(0);
+ IPCThreadState::self()->disableBackgroundScheduling(true);
+ IPCThreadState::self()->setupPolling(&gBinderFd);
+
+ if (gBinderFd >= 0) {
+ if (healthd_register_event(gBinderFd, binder_event))
+ KLOG_ERROR(LOG_TAG,
+ "Register for binder events failed\n");
+ }
+
+ gBatteryPropertiesRegistrar = new BatteryPropertiesRegistrar();
+ gBatteryPropertiesRegistrar->publish();
+}
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
new file mode 100644
index 0000000..78f8403
--- /dev/null
+++ b/healthd/healthd_mode_charger.cpp
@@ -0,0 +1,708 @@
+/*
+ * Copyright (C) 2011-2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <linux/input.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <sys/socket.h>
+#include <linux/netlink.h>
+
+#include <batteryservice/BatteryService.h>
+#include <cutils/android_reboot.h>
+#include <cutils/klog.h>
+#include <cutils/misc.h>
+#include <cutils/uevent.h>
+#include <cutils/properties.h>
+
+#ifdef CHARGER_ENABLE_SUSPEND
+#include <suspend/autosuspend.h>
+#endif
+
+#include "minui/minui.h"
+
+#include "healthd.h"
+
+char *locale;
+
+#ifndef max
+#define max(a,b) ((a) > (b) ? (a) : (b))
+#endif
+
+#ifndef min
+#define min(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
+
+#define MSEC_PER_SEC (1000LL)
+#define NSEC_PER_MSEC (1000000LL)
+
+#define BATTERY_UNKNOWN_TIME (2 * MSEC_PER_SEC)
+#define POWER_ON_KEY_TIME (2 * MSEC_PER_SEC)
+#define UNPLUGGED_SHUTDOWN_TIME (10 * MSEC_PER_SEC)
+
+#define BATTERY_FULL_THRESH 95
+
+#define LAST_KMSG_PATH "/proc/last_kmsg"
+#define LAST_KMSG_PSTORE_PATH "/sys/fs/pstore/console-ramoops"
+#define LAST_KMSG_MAX_SZ (32 * 1024)
+
+#define LOGE(x...) do { KLOG_ERROR("charger", x); } while (0)
+#define LOGW(x...) do { KLOG_WARNING("charger", x); } while (0)
+#define LOGV(x...) do { KLOG_DEBUG("charger", x); } while (0)
+
+struct key_state {
+ bool pending;
+ bool down;
+ int64_t timestamp;
+};
+
+struct frame {
+ int disp_time;
+ int min_capacity;
+ bool level_only;
+
+ GRSurface* surface;
+};
+
+struct animation {
+ bool run;
+
+ struct frame *frames;
+ int cur_frame;
+ int num_frames;
+
+ int cur_cycle;
+ int num_cycles;
+
+ /* current capacity being animated */
+ int capacity;
+};
+
+struct charger {
+ bool have_battery_state;
+ bool charger_connected;
+ int64_t next_screen_transition;
+ int64_t next_key_check;
+ int64_t next_pwr_check;
+
+ struct key_state keys[KEY_MAX + 1];
+
+ struct animation *batt_anim;
+ GRSurface* surf_unknown;
+};
+
+static struct frame batt_anim_frames[] = {
+ {
+ .disp_time = 750,
+ .min_capacity = 0,
+ .level_only = false,
+ .surface = NULL,
+ },
+ {
+ .disp_time = 750,
+ .min_capacity = 20,
+ .level_only = false,
+ .surface = NULL,
+ },
+ {
+ .disp_time = 750,
+ .min_capacity = 40,
+ .level_only = false,
+ .surface = NULL,
+ },
+ {
+ .disp_time = 750,
+ .min_capacity = 60,
+ .level_only = false,
+ .surface = NULL,
+ },
+ {
+ .disp_time = 750,
+ .min_capacity = 80,
+ .level_only = true,
+ .surface = NULL,
+ },
+ {
+ .disp_time = 750,
+ .min_capacity = BATTERY_FULL_THRESH,
+ .level_only = false,
+ .surface = NULL,
+ },
+};
+
+static struct animation battery_animation = {
+ .run = false,
+ .frames = batt_anim_frames,
+ .cur_frame = 0,
+ .num_frames = ARRAY_SIZE(batt_anim_frames),
+ .cur_cycle = 0,
+ .num_cycles = 3,
+ .capacity = 0,
+};
+
+static struct charger charger_state;
+static struct healthd_config *healthd_config;
+static struct android::BatteryProperties *batt_prop;
+static int char_width;
+static int char_height;
+static bool minui_inited;
+
+/* current time in milliseconds */
+static int64_t curr_time_ms(void)
+{
+ struct timespec tm;
+ clock_gettime(CLOCK_MONOTONIC, &tm);
+ return tm.tv_sec * MSEC_PER_SEC + (tm.tv_nsec / NSEC_PER_MSEC);
+}
+
+static void clear_screen(void)
+{
+ gr_color(0, 0, 0, 255);
+ gr_clear();
+}
+
+#define MAX_KLOG_WRITE_BUF_SZ 256
+
+static void dump_last_kmsg(void)
+{
+ char *buf;
+ char *ptr;
+ unsigned sz = 0;
+ int len;
+
+ LOGW("\n");
+ LOGW("*************** LAST KMSG ***************\n");
+ LOGW("\n");
+ buf = (char *)load_file(LAST_KMSG_PSTORE_PATH, &sz);
+
+ if (!buf || !sz) {
+ buf = (char *)load_file(LAST_KMSG_PATH, &sz);
+ if (!buf || !sz) {
+ LOGW("last_kmsg not found. Cold reset?\n");
+ goto out;
+ }
+ }
+
+ len = min(sz, LAST_KMSG_MAX_SZ);
+ ptr = buf + (sz - len);
+
+ while (len > 0) {
+ int cnt = min(len, MAX_KLOG_WRITE_BUF_SZ);
+ char yoink;
+ char *nl;
+
+ nl = (char *)memrchr(ptr, '\n', cnt - 1);
+ if (nl)
+ cnt = nl - ptr + 1;
+
+ yoink = ptr[cnt];
+ ptr[cnt] = '\0';
+ klog_write(6, "<4>%s", ptr);
+ ptr[cnt] = yoink;
+
+ len -= cnt;
+ ptr += cnt;
+ }
+
+ free(buf);
+
+out:
+ LOGW("\n");
+ LOGW("************* END LAST KMSG *************\n");
+ LOGW("\n");
+}
+
+#ifdef CHARGER_ENABLE_SUSPEND
+static int request_suspend(bool enable)
+{
+ if (enable)
+ return autosuspend_enable();
+ else
+ return autosuspend_disable();
+}
+#else
+static int request_suspend(bool /*enable*/)
+{
+ return 0;
+}
+#endif
+
+static int draw_text(const char *str, int x, int y)
+{
+ int str_len_px = gr_measure(str);
+
+ if (x < 0)
+ x = (gr_fb_width() - str_len_px) / 2;
+ if (y < 0)
+ y = (gr_fb_height() - char_height) / 2;
+ gr_text(x, y, str, 0);
+
+ return y + char_height;
+}
+
+static void android_green(void)
+{
+ gr_color(0xa4, 0xc6, 0x39, 255);
+}
+
+/* returns the last y-offset of where the surface ends */
+static int draw_surface_centered(struct charger* /*charger*/, GRSurface* surface)
+{
+ int w;
+ int h;
+ int x;
+ int y;
+
+ w = gr_get_width(surface);
+ h = gr_get_height(surface);
+ x = (gr_fb_width() - w) / 2 ;
+ y = (gr_fb_height() - h) / 2 ;
+
+ LOGV("drawing surface %dx%d+%d+%d\n", w, h, x, y);
+ gr_blit(surface, 0, 0, w, h, x, y);
+ return y + h;
+}
+
+static void draw_unknown(struct charger *charger)
+{
+ int y;
+ if (charger->surf_unknown) {
+ draw_surface_centered(charger, charger->surf_unknown);
+ } else {
+ android_green();
+ y = draw_text("Charging!", -1, -1);
+ draw_text("?\?/100", -1, y + 25);
+ }
+}
+
+static void draw_battery(struct charger *charger)
+{
+ struct animation *batt_anim = charger->batt_anim;
+ struct frame *frame = &batt_anim->frames[batt_anim->cur_frame];
+
+ if (batt_anim->num_frames != 0) {
+ draw_surface_centered(charger, frame->surface);
+ LOGV("drawing frame #%d min_cap=%d time=%d\n",
+ batt_anim->cur_frame, frame->min_capacity,
+ frame->disp_time);
+ }
+}
+
+static void redraw_screen(struct charger *charger)
+{
+ struct animation *batt_anim = charger->batt_anim;
+
+ clear_screen();
+
+ /* try to display *something* */
+ if (batt_anim->capacity < 0 || batt_anim->num_frames == 0)
+ draw_unknown(charger);
+ else
+ draw_battery(charger);
+ gr_flip();
+}
+
+static void kick_animation(struct animation *anim)
+{
+ anim->run = true;
+}
+
+static void reset_animation(struct animation *anim)
+{
+ anim->cur_cycle = 0;
+ anim->cur_frame = 0;
+ anim->run = false;
+}
+
+static void update_screen_state(struct charger *charger, int64_t now)
+{
+ struct animation *batt_anim = charger->batt_anim;
+ int disp_time;
+
+ if (!batt_anim->run || now < charger->next_screen_transition)
+ return;
+
+ if (!minui_inited) {
+
+ if (healthd_config && healthd_config->screen_on) {
+ if (!healthd_config->screen_on(batt_prop)) {
+ LOGV("[%" PRId64 "] leave screen off\n", now);
+ batt_anim->run = false;
+ charger->next_screen_transition = -1;
+ if (charger->charger_connected)
+ request_suspend(true);
+ return;
+ }
+ }
+
+ gr_init();
+ gr_font_size(&char_width, &char_height);
+
+#ifndef CHARGER_DISABLE_INIT_BLANK
+ gr_fb_blank(true);
+#endif
+ minui_inited = true;
+ }
+
+ /* animation is over, blank screen and leave */
+ if (batt_anim->cur_cycle == batt_anim->num_cycles) {
+ reset_animation(batt_anim);
+ charger->next_screen_transition = -1;
+ gr_fb_blank(true);
+ LOGV("[%" PRId64 "] animation done\n", now);
+ if (charger->charger_connected)
+ request_suspend(true);
+ return;
+ }
+
+ disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time;
+
+ /* animation starting, set up the animation */
+ if (batt_anim->cur_frame == 0) {
+
+ LOGV("[%" PRId64 "] animation starting\n", now);
+ if (batt_prop && batt_prop->batteryLevel >= 0 && batt_anim->num_frames != 0) {
+ int i;
+
+ /* find first frame given current capacity */
+ for (i = 1; i < batt_anim->num_frames; i++) {
+ if (batt_prop->batteryLevel < batt_anim->frames[i].min_capacity)
+ break;
+ }
+ batt_anim->cur_frame = i - 1;
+
+ /* show the first frame for twice as long */
+ disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time * 2;
+ }
+ if (batt_prop)
+ batt_anim->capacity = batt_prop->batteryLevel;
+ }
+
+ /* unblank the screen on first cycle */
+ if (batt_anim->cur_cycle == 0)
+ gr_fb_blank(false);
+
+ /* draw the new frame (@ cur_frame) */
+ redraw_screen(charger);
+
+ /* if we don't have anim frames, we only have one image, so just bump
+ * the cycle counter and exit
+ */
+ if (batt_anim->num_frames == 0 || batt_anim->capacity < 0) {
+ LOGV("[%" PRId64 "] animation missing or unknown battery status\n", now);
+ charger->next_screen_transition = now + BATTERY_UNKNOWN_TIME;
+ batt_anim->cur_cycle++;
+ return;
+ }
+
+ /* schedule next screen transition */
+ charger->next_screen_transition = now + disp_time;
+
+ /* advance frame cntr to the next valid frame only if we are charging
+ * if necessary, advance cycle cntr, and reset frame cntr
+ */
+ if (charger->charger_connected) {
+ batt_anim->cur_frame++;
+
+ /* if the frame is used for level-only, that is only show it when it's
+ * the current level, skip it during the animation.
+ */
+ while (batt_anim->cur_frame < batt_anim->num_frames &&
+ batt_anim->frames[batt_anim->cur_frame].level_only)
+ batt_anim->cur_frame++;
+ if (batt_anim->cur_frame >= batt_anim->num_frames) {
+ batt_anim->cur_cycle++;
+ batt_anim->cur_frame = 0;
+
+ /* don't reset the cycle counter, since we use that as a signal
+ * in a test above to check if animation is over
+ */
+ }
+ } else {
+ /* Stop animating if we're not charging.
+ * If we stop it immediately instead of going through this loop, then
+ * the animation would stop somewhere in the middle.
+ */
+ batt_anim->cur_frame = 0;
+ batt_anim->cur_cycle++;
+ }
+}
+
+static int set_key_callback(int code, int value, void *data)
+{
+ struct charger *charger = (struct charger *)data;
+ int64_t now = curr_time_ms();
+ int down = !!value;
+
+ if (code > KEY_MAX)
+ return -1;
+
+ /* ignore events that don't modify our state */
+ if (charger->keys[code].down == down)
+ return 0;
+
+ /* only record the down even timestamp, as the amount
+ * of time the key spent not being pressed is not useful */
+ if (down)
+ charger->keys[code].timestamp = now;
+ charger->keys[code].down = down;
+ charger->keys[code].pending = true;
+ if (down) {
+ LOGV("[%" PRId64 "] key[%d] down\n", now, code);
+ } else {
+ int64_t duration = now - charger->keys[code].timestamp;
+ int64_t secs = duration / 1000;
+ int64_t msecs = duration - secs * 1000;
+ LOGV("[%" PRId64 "] key[%d] up (was down for %" PRId64 ".%" PRId64 "sec)\n",
+ now, code, secs, msecs);
+ }
+
+ return 0;
+}
+
+static void update_input_state(struct charger *charger,
+ struct input_event *ev)
+{
+ if (ev->type != EV_KEY)
+ return;
+ set_key_callback(ev->code, ev->value, charger);
+}
+
+static void set_next_key_check(struct charger *charger,
+ struct key_state *key,
+ int64_t timeout)
+{
+ int64_t then = key->timestamp + timeout;
+
+ if (charger->next_key_check == -1 || then < charger->next_key_check)
+ charger->next_key_check = then;
+}
+
+static void process_key(struct charger *charger, int code, int64_t now)
+{
+ struct key_state *key = &charger->keys[code];
+
+ if (code == KEY_POWER) {
+ if (key->down) {
+ int64_t reboot_timeout = key->timestamp + POWER_ON_KEY_TIME;
+ if (now >= reboot_timeout) {
+ /* We do not currently support booting from charger mode on
+ all devices. Check the property and continue booting or reboot
+ accordingly. */
+ if (property_get_bool("ro.enable_boot_charger_mode", false)) {
+ LOGW("[%" PRId64 "] booting from charger mode\n", now);
+ property_set("sys.boot_from_charger_mode", "1");
+ } else {
+ LOGW("[%" PRId64 "] rebooting\n", now);
+ android_reboot(ANDROID_RB_RESTART, 0, 0);
+ }
+ } else {
+ /* if the key is pressed but timeout hasn't expired,
+ * make sure we wake up at the right-ish time to check
+ */
+ set_next_key_check(charger, key, POWER_ON_KEY_TIME);
+ }
+ } else {
+ /* if the power key got released, force screen state cycle */
+ if (key->pending) {
+ request_suspend(false);
+ kick_animation(charger->batt_anim);
+ }
+ }
+ }
+
+ key->pending = false;
+}
+
+static void handle_input_state(struct charger *charger, int64_t now)
+{
+ process_key(charger, KEY_POWER, now);
+
+ if (charger->next_key_check != -1 && now > charger->next_key_check)
+ charger->next_key_check = -1;
+}
+
+static void handle_power_supply_state(struct charger *charger, int64_t now)
+{
+ if (!charger->have_battery_state)
+ return;
+
+ if (!charger->charger_connected) {
+ request_suspend(false);
+ if (charger->next_pwr_check == -1) {
+ charger->next_pwr_check = now + UNPLUGGED_SHUTDOWN_TIME;
+ LOGW("[%" PRId64 "] device unplugged: shutting down in %" PRId64 " (@ %" PRId64 ")\n",
+ now, (int64_t)UNPLUGGED_SHUTDOWN_TIME, charger->next_pwr_check);
+ } else if (now >= charger->next_pwr_check) {
+ LOGW("[%" PRId64 "] shutting down\n", now);
+ android_reboot(ANDROID_RB_POWEROFF, 0, 0);
+ } else {
+ /* otherwise we already have a shutdown timer scheduled */
+ }
+ } else {
+ /* online supply present, reset shutdown timer if set */
+ if (charger->next_pwr_check != -1) {
+ LOGW("[%" PRId64 "] device plugged in: shutdown cancelled\n", now);
+ kick_animation(charger->batt_anim);
+ }
+ charger->next_pwr_check = -1;
+ }
+}
+
+void healthd_mode_charger_heartbeat()
+{
+ struct charger *charger = &charger_state;
+ int64_t now = curr_time_ms();
+
+ handle_input_state(charger, now);
+ handle_power_supply_state(charger, now);
+
+ /* do screen update last in case any of the above want to start
+ * screen transitions (animations, etc)
+ */
+ update_screen_state(charger, now);
+}
+
+void healthd_mode_charger_battery_update(
+ struct android::BatteryProperties *props)
+{
+ struct charger *charger = &charger_state;
+
+ charger->charger_connected =
+ props->chargerAcOnline || props->chargerUsbOnline ||
+ props->chargerWirelessOnline;
+
+ if (!charger->have_battery_state) {
+ charger->have_battery_state = true;
+ charger->next_screen_transition = curr_time_ms() - 1;
+ reset_animation(charger->batt_anim);
+ kick_animation(charger->batt_anim);
+ }
+ batt_prop = props;
+}
+
+int healthd_mode_charger_preparetowait(void)
+{
+ struct charger *charger = &charger_state;
+ int64_t now = curr_time_ms();
+ int64_t next_event = INT64_MAX;
+ int64_t timeout;
+
+ LOGV("[%" PRId64 "] next screen: %" PRId64 " next key: %" PRId64 " next pwr: %" PRId64 "\n", now,
+ charger->next_screen_transition, charger->next_key_check,
+ charger->next_pwr_check);
+
+ if (charger->next_screen_transition != -1)
+ next_event = charger->next_screen_transition;
+ if (charger->next_key_check != -1 && charger->next_key_check < next_event)
+ next_event = charger->next_key_check;
+ if (charger->next_pwr_check != -1 && charger->next_pwr_check < next_event)
+ next_event = charger->next_pwr_check;
+
+ if (next_event != -1 && next_event != INT64_MAX)
+ timeout = max(0, next_event - now);
+ else
+ timeout = -1;
+
+ return (int)timeout;
+}
+
+static int input_callback(int fd, unsigned int epevents, void *data)
+{
+ struct charger *charger = (struct charger *)data;
+ struct input_event ev;
+ int ret;
+
+ ret = ev_get_input(fd, epevents, &ev);
+ if (ret)
+ return -1;
+ update_input_state(charger, &ev);
+ return 0;
+}
+
+static void charger_event_handler(uint32_t /*epevents*/)
+{
+ int ret;
+
+ ret = ev_wait(-1);
+ if (!ret)
+ ev_dispatch();
+}
+
+void healthd_mode_charger_init(struct healthd_config* config)
+{
+ int ret;
+ struct charger *charger = &charger_state;
+ int i;
+ int epollfd;
+
+ dump_last_kmsg();
+
+ LOGW("--------------- STARTING CHARGER MODE ---------------\n");
+
+ ret = ev_init(input_callback, charger);
+ if (!ret) {
+ epollfd = ev_get_epollfd();
+ healthd_register_event(epollfd, charger_event_handler);
+ }
+
+ ret = res_create_display_surface("charger/battery_fail", &charger->surf_unknown);
+ if (ret < 0) {
+ LOGE("Cannot load battery_fail image\n");
+ charger->surf_unknown = NULL;
+ }
+
+ charger->batt_anim = &battery_animation;
+
+ GRSurface** scale_frames;
+ int scale_count;
+ ret = res_create_multi_display_surface("charger/battery_scale", &scale_count, &scale_frames);
+ if (ret < 0) {
+ LOGE("Cannot load battery_scale image\n");
+ charger->batt_anim->num_frames = 0;
+ charger->batt_anim->num_cycles = 1;
+ } else if (scale_count != charger->batt_anim->num_frames) {
+ LOGE("battery_scale image has unexpected frame count (%d, expected %d)\n",
+ scale_count, charger->batt_anim->num_frames);
+ charger->batt_anim->num_frames = 0;
+ charger->batt_anim->num_cycles = 1;
+ } else {
+ for (i = 0; i < charger->batt_anim->num_frames; i++) {
+ charger->batt_anim->frames[i].surface = scale_frames[i];
+ }
+ }
+
+ ev_sync_key_state(set_key_callback, charger);
+
+ charger->next_screen_transition = -1;
+ charger->next_key_check = -1;
+ charger->next_pwr_check = -1;
+ healthd_config = config;
+}
diff --git a/healthd/images/battery_fail.png b/healthd/images/battery_fail.png
new file mode 100644
index 0000000..aded88a
--- /dev/null
+++ b/healthd/images/battery_fail.png
Binary files differ
diff --git a/healthd/images/battery_scale.png b/healthd/images/battery_scale.png
new file mode 100644
index 0000000..2ae8f0f
--- /dev/null
+++ b/healthd/images/battery_scale.png
Binary files differ
diff --git a/include/android/log.h b/include/android/log.h
new file mode 100644
index 0000000..1c171b7
--- /dev/null
+++ b/include/android/log.h
@@ -0,0 +1,144 @@
+/*
+ * 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.
+ */
+
+#ifndef _ANDROID_LOG_H
+#define _ANDROID_LOG_H
+
+/******************************************************************
+ *
+ * IMPORTANT NOTICE:
+ *
+ * This file is part of Android's set of stable system headers
+ * exposed by the Android NDK (Native Development Kit) since
+ * platform release 1.5
+ *
+ * Third-party source AND binary code relies on the definitions
+ * here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES.
+ *
+ * - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES)
+ * - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS
+ * - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY
+ * - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES
+ */
+
+/*
+ * Support routines to send messages to the Android in-kernel log buffer,
+ * which can later be accessed through the 'logcat' utility.
+ *
+ * Each log message must have
+ * - a priority
+ * - a log tag
+ * - some text
+ *
+ * The tag normally corresponds to the component that emits the log message,
+ * and should be reasonably small.
+ *
+ * Log message text may be truncated to less than an implementation-specific
+ * limit (e.g. 1023 characters max).
+ *
+ * Note that a newline character ("\n") will be appended automatically to your
+ * log message, if not already there. It is not possible to send several messages
+ * and have them appear on a single line in logcat.
+ *
+ * PLEASE USE LOGS WITH MODERATION:
+ *
+ * - Sending log messages eats CPU and slow down your application and the
+ * system.
+ *
+ * - The circular log buffer is pretty small (<64KB), sending many messages
+ * might push off other important log messages from the rest of the system.
+ *
+ * - In release builds, only send log messages to account for exceptional
+ * conditions.
+ *
+ * NOTE: These functions MUST be implemented by /system/lib/liblog.so
+ */
+
+#include <stdarg.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Android log priority values, in ascending priority order.
+ */
+typedef enum android_LogPriority {
+ ANDROID_LOG_UNKNOWN = 0,
+ ANDROID_LOG_DEFAULT, /* only for SetMinPriority() */
+ ANDROID_LOG_VERBOSE,
+ ANDROID_LOG_DEBUG,
+ ANDROID_LOG_INFO,
+ ANDROID_LOG_WARN,
+ ANDROID_LOG_ERROR,
+ ANDROID_LOG_FATAL,
+ ANDROID_LOG_SILENT, /* only for SetMinPriority(); must be last */
+} android_LogPriority;
+
+/*
+ * Send a simple string to the log.
+ */
+int __android_log_write(int prio, const char *tag, const char *text);
+
+/*
+ * Send a formatted string to the log, used like printf(fmt,...)
+ */
+int __android_log_print(int prio, const char *tag, const char *fmt, ...)
+#if defined(__GNUC__)
+#ifdef __USE_MINGW_ANSI_STDIO
+#if __USE_MINGW_ANSI_STDIO
+ __attribute__ ((format(gnu_printf, 3, 4)))
+#else
+ __attribute__ ((format(printf, 3, 4)))
+#endif
+#else
+ __attribute__ ((format(printf, 3, 4)))
+#endif
+#endif
+ ;
+
+/*
+ * A variant of __android_log_print() that takes a va_list to list
+ * additional parameters.
+ */
+int __android_log_vprint(int prio, const char *tag,
+ const char *fmt, va_list ap);
+
+/*
+ * Log an assertion failure and abort the process to have a chance
+ * to inspect it if a debugger is attached. This uses the FATAL priority.
+ */
+void __android_log_assert(const char *cond, const char *tag,
+ const char *fmt, ...)
+#if defined(__GNUC__)
+ __attribute__ ((noreturn))
+#ifdef __USE_MINGW_ANSI_STDIO
+#if __USE_MINGW_ANSI_STDIO
+ __attribute__ ((format(gnu_printf, 3, 4)))
+#else
+ __attribute__ ((format(printf, 3, 4)))
+#endif
+#else
+ __attribute__ ((format(printf, 3, 4)))
+#endif
+#endif
+ ;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ANDROID_LOG_H */
diff --git a/include/backtrace/Backtrace.h b/include/backtrace/Backtrace.h
new file mode 100644
index 0000000..290682a
--- /dev/null
+++ b/include/backtrace/Backtrace.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _BACKTRACE_BACKTRACE_H
+#define _BACKTRACE_BACKTRACE_H
+
+#include <inttypes.h>
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include <backtrace/backtrace_constants.h>
+#include <backtrace/BacktraceMap.h>
+
+#if __LP64__
+#define PRIPTR "016" PRIxPTR
+typedef uint64_t word_t;
+#else
+#define PRIPTR "08" PRIxPTR
+typedef uint32_t word_t;
+#endif
+
+struct backtrace_frame_data_t {
+ size_t num; // The current fame number.
+ uintptr_t pc; // The absolute pc.
+ uintptr_t sp; // The top of the stack.
+ size_t stack_size; // The size of the stack, zero indicate an unknown stack size.
+ backtrace_map_t map; // The map associated with the given pc.
+ std::string func_name; // The function name associated with this pc, NULL if not found.
+ uintptr_t func_offset; // pc relative to the start of the function, only valid if func_name is not NULL.
+};
+
+#if defined(__APPLE__)
+struct __darwin_ucontext;
+typedef __darwin_ucontext ucontext_t;
+#else
+struct ucontext;
+typedef ucontext ucontext_t;
+#endif
+
+class Backtrace {
+public:
+ // Create the correct Backtrace object based on what is to be unwound.
+ // If pid < 0 or equals the current pid, then the Backtrace object
+ // corresponds to the current process.
+ // If pid < 0 or equals the current pid and tid >= 0, then the Backtrace
+ // object corresponds to a thread in the current process.
+ // If pid >= 0 and tid < 0, then the Backtrace object corresponds to a
+ // different process.
+ // Tracing a thread in a different process is not supported.
+ // If map is NULL, then create the map and manage it internally.
+ // If map is not NULL, the map is still owned by the caller.
+ static Backtrace* Create(pid_t pid, pid_t tid, BacktraceMap* map = NULL);
+
+ virtual ~Backtrace();
+
+ // Get the current stack trace and store in the backtrace_ structure.
+ virtual bool Unwind(size_t num_ignore_frames, ucontext_t* context = NULL) = 0;
+
+ // Get the function name and offset into the function given the pc.
+ // If the string is empty, then no valid function name was found.
+ virtual std::string GetFunctionName(uintptr_t pc, uintptr_t* offset);
+
+ // Fill in the map data associated with the given pc.
+ virtual void FillInMap(uintptr_t pc, backtrace_map_t* map);
+
+ // Read the data at a specific address.
+ virtual bool ReadWord(uintptr_t ptr, word_t* out_value) = 0;
+
+ // Read arbitrary data from a specific address. If a read request would
+ // span from one map to another, this call only reads up until the end
+ // of the current map.
+ // Returns the total number of bytes actually read.
+ virtual size_t Read(uintptr_t addr, uint8_t* buffer, size_t bytes) = 0;
+
+ // Create a string representing the formatted line of backtrace information
+ // for a single frame.
+ virtual std::string FormatFrameData(size_t frame_num);
+ virtual std::string FormatFrameData(const backtrace_frame_data_t* frame);
+
+ pid_t Pid() const { return pid_; }
+ pid_t Tid() const { return tid_; }
+ size_t NumFrames() const { return frames_.size(); }
+
+ const backtrace_frame_data_t* GetFrame(size_t frame_num) {
+ if (frame_num >= frames_.size()) {
+ return NULL;
+ }
+ return &frames_[frame_num];
+ }
+
+ typedef std::vector<backtrace_frame_data_t>::iterator iterator;
+ iterator begin() { return frames_.begin(); }
+ iterator end() { return frames_.end(); }
+
+ typedef std::vector<backtrace_frame_data_t>::const_iterator const_iterator;
+ const_iterator begin() const { return frames_.begin(); }
+ const_iterator end() const { return frames_.end(); }
+
+ BacktraceMap* GetMap() { return map_; }
+
+protected:
+ Backtrace(pid_t pid, pid_t tid, BacktraceMap* map);
+
+ // The name returned is not demangled, GetFunctionName() takes care of
+ // demangling the name.
+ virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) = 0;
+
+ virtual bool VerifyReadWordArgs(uintptr_t ptr, word_t* out_value);
+
+ bool BuildMap();
+
+ pid_t pid_;
+ pid_t tid_;
+
+ BacktraceMap* map_;
+ bool map_shared_;
+
+ std::vector<backtrace_frame_data_t> frames_;
+};
+
+#endif // _BACKTRACE_BACKTRACE_H
diff --git a/include/backtrace/BacktraceMap.h b/include/backtrace/BacktraceMap.h
new file mode 100644
index 0000000..bb18aa2
--- /dev/null
+++ b/include/backtrace/BacktraceMap.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _BACKTRACE_BACKTRACE_MAP_H
+#define _BACKTRACE_BACKTRACE_MAP_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#ifdef USE_MINGW
+// MINGW does not define these constants.
+#define PROT_NONE 0
+#define PROT_READ 0x1
+#define PROT_WRITE 0x2
+#define PROT_EXEC 0x4
+#else
+#include <sys/mman.h>
+#endif
+
+#include <deque>
+#include <string>
+
+struct backtrace_map_t {
+ uintptr_t start = 0;
+ uintptr_t end = 0;
+ uintptr_t offset = 0;
+ uintptr_t load_base = 0;
+ int flags = 0;
+ std::string name;
+};
+
+class BacktraceMap {
+public:
+ // If uncached is true, then parse the current process map as of the call.
+ // Passing a map created with uncached set to true to Backtrace::Create()
+ // is unsupported.
+ static BacktraceMap* Create(pid_t pid, bool uncached = false);
+
+ virtual ~BacktraceMap();
+
+ // Fill in the map data structure for the given address.
+ virtual void FillIn(uintptr_t addr, backtrace_map_t* map);
+
+ // The flags returned are the same flags as used by the mmap call.
+ // The values are PROT_*.
+ int GetFlags(uintptr_t pc) {
+ backtrace_map_t map;
+ FillIn(pc, &map);
+ if (IsValid(map)) {
+ return map.flags;
+ }
+ return PROT_NONE;
+ }
+
+ bool IsReadable(uintptr_t pc) { return GetFlags(pc) & PROT_READ; }
+ bool IsWritable(uintptr_t pc) { return GetFlags(pc) & PROT_WRITE; }
+ bool IsExecutable(uintptr_t pc) { return GetFlags(pc) & PROT_EXEC; }
+
+ typedef std::deque<backtrace_map_t>::iterator iterator;
+ iterator begin() { return maps_.begin(); }
+ iterator end() { return maps_.end(); }
+
+ typedef std::deque<backtrace_map_t>::const_iterator const_iterator;
+ const_iterator begin() const { return maps_.begin(); }
+ const_iterator end() const { return maps_.end(); }
+
+ virtual bool Build();
+
+ static inline bool IsValid(const backtrace_map_t& map) {
+ return map.end > 0;
+ }
+
+ static uintptr_t GetRelativePc(const backtrace_map_t& map, uintptr_t pc) {
+ if (IsValid(map)) {
+ return pc - map.start + map.load_base;
+ } else {
+ return pc;
+ }
+ }
+
+protected:
+ BacktraceMap(pid_t pid);
+
+ virtual bool ParseLine(const char* line, backtrace_map_t* map);
+
+ std::deque<backtrace_map_t> maps_;
+ pid_t pid_;
+};
+
+#endif // _BACKTRACE_BACKTRACE_MAP_H
diff --git a/include/backtrace/backtrace_constants.h b/include/backtrace/backtrace_constants.h
new file mode 100644
index 0000000..f8c1575
--- /dev/null
+++ b/include/backtrace/backtrace_constants.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _BACKTRACE_BACKTRACE_CONSTANTS_H
+#define _BACKTRACE_BACKTRACE_CONSTANTS_H
+
+// When the pid to be traced is set to this value, then trace the current
+// process. If the tid value is not BACKTRACE_NO_TID, then the specified
+// thread from the current process will be traced.
+#define BACKTRACE_CURRENT_PROCESS -1
+// When the tid to be traced is set to this value, then trace the specified
+// current thread of the specified pid.
+#define BACKTRACE_CURRENT_THREAD -1
+
+#define MAX_BACKTRACE_FRAMES 64
+
+#endif // _BACKTRACE_BACKTRACE_CONSTANTS_H
diff --git a/include/cutils/android_reboot.h b/include/cutils/android_reboot.h
new file mode 100644
index 0000000..a3861a0
--- /dev/null
+++ b/include/cutils/android_reboot.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 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.
+ */
+
+#ifndef __CUTILS_ANDROID_REBOOT_H__
+#define __CUTILS_ANDROID_REBOOT_H__
+
+#include <mntent.h>
+
+__BEGIN_DECLS
+
+/* Commands */
+#define ANDROID_RB_RESTART 0xDEAD0001
+#define ANDROID_RB_POWEROFF 0xDEAD0002
+#define ANDROID_RB_RESTART2 0xDEAD0003
+
+/* Properties */
+#define ANDROID_RB_PROPERTY "sys.powerctl"
+
+int android_reboot(int cmd, int flags, const char *arg);
+int android_reboot_with_callback(
+ int cmd, int flags, const char *arg,
+ void (*cb_on_remount)(const struct mntent*));
+
+__END_DECLS
+
+#endif /* __CUTILS_ANDROID_REBOOT_H__ */
diff --git a/include/cutils/aref.h b/include/cutils/aref.h
new file mode 100644
index 0000000..3bd36ea
--- /dev/null
+++ b/include/cutils/aref.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _CUTILS_AREF_H_
+#define _CUTILS_AREF_H_
+
+#include <stddef.h>
+#include <sys/cdefs.h>
+
+#include <cutils/atomic.h>
+
+__BEGIN_DECLS
+
+#define AREF_TO_ITEM(aref, container, member) \
+ (container *) (((char*) (aref)) - offsetof(container, member))
+
+struct aref
+{
+ volatile int32_t count;
+};
+
+static inline void aref_init(struct aref *r)
+{
+ r->count = 1;
+}
+
+static inline int32_t aref_count(struct aref *r)
+{
+ return r->count;
+}
+
+static inline void aref_get(struct aref *r)
+{
+ android_atomic_inc(&r->count);
+}
+
+static inline void aref_put(struct aref *r, void (*release)(struct aref *))
+{
+ if (android_atomic_dec(&r->count) == 1)
+ release(r);
+}
+
+__END_DECLS
+
+#endif // _CUTILS_AREF_H_
diff --git a/include/cutils/ashmem.h b/include/cutils/ashmem.h
new file mode 100644
index 0000000..25b233e
--- /dev/null
+++ b/include/cutils/ashmem.h
@@ -0,0 +1,45 @@
+/* cutils/ashmem.h
+ **
+ ** Copyright 2008 The Android Open Source Project
+ **
+ ** This file is dual licensed. It may be redistributed and/or modified
+ ** under the terms of the Apache 2.0 License OR version 2 of the GNU
+ ** General Public License.
+ */
+
+#ifndef _CUTILS_ASHMEM_H
+#define _CUTILS_ASHMEM_H
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int ashmem_create_region(const char *name, size_t size);
+int ashmem_set_prot_region(int fd, int prot);
+int ashmem_pin_region(int fd, size_t offset, size_t len);
+int ashmem_unpin_region(int fd, size_t offset, size_t len);
+int ashmem_get_size_region(int fd);
+
+#ifdef __cplusplus
+}
+#endif
+
+#ifndef __ASHMEMIOC /* in case someone included <linux/ashmem.h> too */
+
+#define ASHMEM_NAME_LEN 256
+
+#define ASHMEM_NAME_DEF "dev/ashmem"
+
+/* Return values from ASHMEM_PIN: Was the mapping purged while unpinned? */
+#define ASHMEM_NOT_PURGED 0
+#define ASHMEM_WAS_PURGED 1
+
+/* Return values from ASHMEM_UNPIN: Is the mapping now pinned or unpinned? */
+#define ASHMEM_IS_UNPINNED 0
+#define ASHMEM_IS_PINNED 1
+
+#endif /* ! __ASHMEMIOC */
+
+#endif /* _CUTILS_ASHMEM_H */
diff --git a/include/cutils/atomic.h b/include/cutils/atomic.h
new file mode 100644
index 0000000..ded972a
--- /dev/null
+++ b/include/cutils/atomic.h
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_CUTILS_ATOMIC_H
+#define ANDROID_CUTILS_ATOMIC_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <stdatomic.h>
+
+#ifndef ANDROID_ATOMIC_INLINE
+#define ANDROID_ATOMIC_INLINE static inline
+#endif
+
+/*
+ * A handful of basic atomic operations.
+ * THESE ARE HERE FOR LEGACY REASONS ONLY. AVOID.
+ *
+ * PREFERRED ALTERNATIVES:
+ * - Use C++/C/pthread locks/mutexes whenever there is not a
+ * convincing reason to do otherwise. Note that very clever and
+ * complicated, but correct, lock-free code is often slower than
+ * using locks, especially where nontrivial data structures
+ * are involved.
+ * - C11 stdatomic.h.
+ * - Where supported, C++11 std::atomic<T> .
+ *
+ * PLEASE STOP READING HERE UNLESS YOU ARE TRYING TO UNDERSTAND
+ * OR UPDATE OLD CODE.
+ *
+ * The "acquire" and "release" terms can be defined intuitively in terms
+ * of the placement of memory barriers in a simple lock implementation:
+ * - wait until compare-and-swap(lock-is-free --> lock-is-held) succeeds
+ * - barrier
+ * - [do work]
+ * - barrier
+ * - store(lock-is-free)
+ * In very crude terms, the initial (acquire) barrier prevents any of the
+ * "work" from happening before the lock is held, and the later (release)
+ * barrier ensures that all of the work happens before the lock is released.
+ * (Think of cached writes, cache read-ahead, and instruction reordering
+ * around the CAS and store instructions.)
+ *
+ * The barriers must apply to both the compiler and the CPU. Note it is
+ * legal for instructions that occur before an "acquire" barrier to be
+ * moved down below it, and for instructions that occur after a "release"
+ * barrier to be moved up above it.
+ *
+ * The ARM-driven implementation we use here is short on subtlety,
+ * and actually requests a full barrier from the compiler and the CPU.
+ * The only difference between acquire and release is in whether they
+ * are issued before or after the atomic operation with which they
+ * are associated. To ease the transition to C/C++ atomic intrinsics,
+ * you should not rely on this, and instead assume that only the minimal
+ * acquire/release protection is provided.
+ *
+ * NOTE: all int32_t* values are expected to be aligned on 32-bit boundaries.
+ * If they are not, atomicity is not guaranteed.
+ */
+
+/*
+ * Basic arithmetic and bitwise operations. These all provide a
+ * barrier with "release" ordering, and return the previous value.
+ *
+ * These have the same characteristics (e.g. what happens on overflow)
+ * as the equivalent non-atomic C operations.
+ */
+ANDROID_ATOMIC_INLINE
+int32_t android_atomic_inc(volatile int32_t* addr)
+{
+ volatile atomic_int_least32_t* a = (volatile atomic_int_least32_t*)addr;
+ /* Int32_t, if it exists, is the same as int_least32_t. */
+ return atomic_fetch_add_explicit(a, 1, memory_order_release);
+}
+
+ANDROID_ATOMIC_INLINE
+int32_t android_atomic_dec(volatile int32_t* addr)
+{
+ volatile atomic_int_least32_t* a = (volatile atomic_int_least32_t*)addr;
+ return atomic_fetch_sub_explicit(a, 1, memory_order_release);
+}
+
+ANDROID_ATOMIC_INLINE
+int32_t android_atomic_add(int32_t value, volatile int32_t* addr)
+{
+ volatile atomic_int_least32_t* a = (volatile atomic_int_least32_t*)addr;
+ return atomic_fetch_add_explicit(a, value, memory_order_release);
+}
+
+ANDROID_ATOMIC_INLINE
+int32_t android_atomic_and(int32_t value, volatile int32_t* addr)
+{
+ volatile atomic_int_least32_t* a = (volatile atomic_int_least32_t*)addr;
+ return atomic_fetch_and_explicit(a, value, memory_order_release);
+}
+
+ANDROID_ATOMIC_INLINE
+int32_t android_atomic_or(int32_t value, volatile int32_t* addr)
+{
+ volatile atomic_int_least32_t* a = (volatile atomic_int_least32_t*)addr;
+ return atomic_fetch_or_explicit(a, value, memory_order_release);
+}
+
+/*
+ * Perform an atomic load with "acquire" or "release" ordering.
+ *
+ * Note that the notion of a "release" ordering for a load does not
+ * really fit into the C11 or C++11 memory model. The extra ordering
+ * is normally observable only by code using memory_order_relaxed
+ * atomics, or data races. In the rare cases in which such ordering
+ * is called for, use memory_order_relaxed atomics and a leading
+ * atomic_thread_fence (typically with memory_order_acquire,
+ * not memory_order_release!) instead. If you do not understand
+ * this comment, you are in the vast majority, and should not be
+ * using release loads or replacing them with anything other than
+ * locks or default sequentially consistent atomics.
+ */
+ANDROID_ATOMIC_INLINE
+int32_t android_atomic_acquire_load(volatile const int32_t* addr)
+{
+ volatile atomic_int_least32_t* a = (volatile atomic_int_least32_t*)addr;
+ return atomic_load_explicit(a, memory_order_acquire);
+}
+
+ANDROID_ATOMIC_INLINE
+int32_t android_atomic_release_load(volatile const int32_t* addr)
+{
+ volatile atomic_int_least32_t* a = (volatile atomic_int_least32_t*)addr;
+ atomic_thread_fence(memory_order_seq_cst);
+ /* Any reasonable clients of this interface would probably prefer */
+ /* something weaker. But some remaining clients seem to be */
+ /* abusing this API in strange ways, e.g. by using it as a fence. */
+ /* Thus we are conservative until we can get rid of remaining */
+ /* clients (and this function). */
+ return atomic_load_explicit(a, memory_order_relaxed);
+}
+
+/*
+ * Perform an atomic store with "acquire" or "release" ordering.
+ *
+ * Note that the notion of an "acquire" ordering for a store does not
+ * really fit into the C11 or C++11 memory model. The extra ordering
+ * is normally observable only by code using memory_order_relaxed
+ * atomics, or data races. In the rare cases in which such ordering
+ * is called for, use memory_order_relaxed atomics and a trailing
+ * atomic_thread_fence (typically with memory_order_release,
+ * not memory_order_acquire!) instead.
+ */
+ANDROID_ATOMIC_INLINE
+void android_atomic_acquire_store(int32_t value, volatile int32_t* addr)
+{
+ volatile atomic_int_least32_t* a = (volatile atomic_int_least32_t*)addr;
+ atomic_store_explicit(a, value, memory_order_relaxed);
+ atomic_thread_fence(memory_order_seq_cst);
+ /* Again overly conservative to accomodate weird clients. */
+}
+
+ANDROID_ATOMIC_INLINE
+void android_atomic_release_store(int32_t value, volatile int32_t* addr)
+{
+ volatile atomic_int_least32_t* a = (volatile atomic_int_least32_t*)addr;
+ atomic_store_explicit(a, value, memory_order_release);
+}
+
+/*
+ * Compare-and-set operation with "acquire" or "release" ordering.
+ *
+ * This returns zero if the new value was successfully stored, which will
+ * only happen when *addr == oldvalue.
+ *
+ * (The return value is inverted from implementations on other platforms,
+ * but matches the ARM ldrex/strex result.)
+ *
+ * Implementations that use the release CAS in a loop may be less efficient
+ * than possible, because we re-issue the memory barrier on each iteration.
+ */
+ANDROID_ATOMIC_INLINE
+int android_atomic_acquire_cas(int32_t oldvalue, int32_t newvalue,
+ volatile int32_t* addr)
+{
+ volatile atomic_int_least32_t* a = (volatile atomic_int_least32_t*)addr;
+ return (int)(!atomic_compare_exchange_strong_explicit(
+ a, &oldvalue, newvalue,
+ memory_order_acquire,
+ memory_order_acquire));
+}
+
+ANDROID_ATOMIC_INLINE
+int android_atomic_release_cas(int32_t oldvalue, int32_t newvalue,
+ volatile int32_t* addr)
+{
+ volatile atomic_int_least32_t* a = (volatile atomic_int_least32_t*)addr;
+ return (int)(!atomic_compare_exchange_strong_explicit(
+ a, &oldvalue, newvalue,
+ memory_order_release,
+ memory_order_relaxed));
+}
+
+/*
+ * Fence primitives.
+ */
+ANDROID_ATOMIC_INLINE
+void android_compiler_barrier(void)
+{
+ __asm__ __volatile__ ("" : : : "memory");
+ /* Could probably also be: */
+ /* atomic_signal_fence(memory_order_seq_cst); */
+}
+
+ANDROID_ATOMIC_INLINE
+void android_memory_barrier(void)
+{
+ atomic_thread_fence(memory_order_seq_cst);
+}
+
+/*
+ * Aliases for code using an older version of this header. These are now
+ * deprecated and should not be used. The definitions will be removed
+ * in a future release.
+ */
+#define android_atomic_write android_atomic_release_store
+#define android_atomic_cmpxchg android_atomic_release_cas
+
+#endif // ANDROID_CUTILS_ATOMIC_H
diff --git a/include/cutils/bitops.h b/include/cutils/bitops.h
new file mode 100644
index 0000000..045830d
--- /dev/null
+++ b/include/cutils/bitops.h
@@ -0,0 +1,120 @@
+/*
+ * 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.
+ */
+
+#ifndef __CUTILS_BITOPS_H
+#define __CUTILS_BITOPS_H
+
+#include <stdbool.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/*
+ * Bitmask Operations
+ *
+ * Note this doesn't provide any locking/exclusion, and isn't atomic.
+ * Additionally no bounds checking is done on the bitmask array.
+ *
+ * Example:
+ *
+ * int num_resources;
+ * unsigned int resource_bits[BITS_TO_WORDS(num_resources)];
+ * bitmask_init(resource_bits, num_resources);
+ * ...
+ * int bit = bitmask_ffz(resource_bits, num_resources);
+ * bitmask_set(resource_bits, bit);
+ * ...
+ * if (bitmask_test(resource_bits, bit)) { ... }
+ * ...
+ * bitmask_clear(resource_bits, bit);
+ *
+ */
+
+#define BITS_PER_WORD (sizeof(unsigned int) * 8)
+#define BITS_TO_WORDS(x) (((x) + BITS_PER_WORD - 1) / BITS_PER_WORD)
+#define BIT_IN_WORD(x) ((x) % BITS_PER_WORD)
+#define BIT_WORD(x) ((x) / BITS_PER_WORD)
+#define BIT_MASK(x) (1 << BIT_IN_WORD(x))
+
+static inline void bitmask_init(unsigned int *bitmask, int num_bits)
+{
+ memset(bitmask, 0, BITS_TO_WORDS(num_bits)*sizeof(unsigned int));
+}
+
+static inline int bitmask_ffz(unsigned int *bitmask, int num_bits)
+{
+ int bit, result;
+ size_t i;
+
+ for (i = 0; i < BITS_TO_WORDS(num_bits); i++) {
+ bit = ffs(~bitmask[i]);
+ if (bit) {
+ // ffs is 1-indexed, return 0-indexed result
+ bit--;
+ result = BITS_PER_WORD * i + bit;
+ if (result >= num_bits)
+ return -1;
+ return result;
+ }
+ }
+ return -1;
+}
+
+static inline int bitmask_weight(unsigned int *bitmask, int num_bits)
+{
+ size_t i;
+ int weight = 0;
+
+ for (i = 0; i < BITS_TO_WORDS(num_bits); i++)
+ weight += __builtin_popcount(bitmask[i]);
+ return weight;
+}
+
+static inline void bitmask_set(unsigned int *bitmask, int bit)
+{
+ bitmask[BIT_WORD(bit)] |= BIT_MASK(bit);
+}
+
+static inline void bitmask_clear(unsigned int *bitmask, int bit)
+{
+ bitmask[BIT_WORD(bit)] &= ~BIT_MASK(bit);
+}
+
+static inline bool bitmask_test(unsigned int *bitmask, int bit)
+{
+ return bitmask[BIT_WORD(bit)] & BIT_MASK(bit);
+}
+
+static inline int popcount(unsigned int x)
+{
+ return __builtin_popcount(x);
+}
+
+static inline int popcountl(unsigned long x)
+{
+ return __builtin_popcountl(x);
+}
+
+static inline int popcountll(unsigned long long x)
+{
+ return __builtin_popcountll(x);
+}
+
+__END_DECLS
+
+#endif /* __CUTILS_BITOPS_H */
diff --git a/include/cutils/compiler.h b/include/cutils/compiler.h
new file mode 100644
index 0000000..70f884a
--- /dev/null
+++ b/include/cutils/compiler.h
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_CUTILS_COMPILER_H
+#define ANDROID_CUTILS_COMPILER_H
+
+/*
+ * helps the compiler's optimizer predicting branches
+ */
+
+#ifdef __cplusplus
+# define CC_LIKELY( exp ) (__builtin_expect( !!(exp), true ))
+# define CC_UNLIKELY( exp ) (__builtin_expect( !!(exp), false ))
+#else
+# define CC_LIKELY( exp ) (__builtin_expect( !!(exp), 1 ))
+# define CC_UNLIKELY( exp ) (__builtin_expect( !!(exp), 0 ))
+#endif
+
+/**
+ * exports marked symbols
+ *
+ * if used on a C++ class declaration, this macro must be inserted
+ * after the "class" keyword. For instance:
+ *
+ * template <typename TYPE>
+ * class ANDROID_API Singleton { }
+ */
+
+#define ANDROID_API __attribute__((visibility("default")))
+
+#endif // ANDROID_CUTILS_COMPILER_H
diff --git a/include/cutils/config_utils.h b/include/cutils/config_utils.h
new file mode 100644
index 0000000..2dea6f1
--- /dev/null
+++ b/include/cutils/config_utils.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CUTILS_CONFIG_UTILS_H
+#define __CUTILS_CONFIG_UTILS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct cnode cnode;
+
+
+struct cnode
+{
+ cnode *next;
+ cnode *first_child;
+ cnode *last_child;
+ const char *name;
+ const char *value;
+};
+
+/* parse a text string into a config node tree */
+void config_load(cnode *root, char *data);
+
+/* parse a file into a config node tree */
+void config_load_file(cnode *root, const char *fn);
+
+/* create a single config node */
+cnode* config_node(const char *name, const char *value);
+
+/* locate a named child of a config node */
+cnode* config_find(cnode *root, const char *name);
+
+/* look up a child by name and return the boolean value */
+int config_bool(cnode *root, const char *name, int _default);
+
+/* look up a child by name and return the string value */
+const char* config_str(cnode *root, const char *name, const char *_default);
+
+/* add a named child to a config node (or modify it if it already exists) */
+void config_set(cnode *root, const char *name, const char *value);
+
+/* free a config node tree */
+void config_free(cnode *root);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/cutils/debugger.h b/include/cutils/debugger.h
new file mode 100644
index 0000000..285e1af
--- /dev/null
+++ b/include/cutils/debugger.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CUTILS_DEBUGGER_H
+#define __CUTILS_DEBUGGER_H
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+__BEGIN_DECLS
+
+#define DEBUGGER_SOCKET_NAME "android:debuggerd"
+#define DEBUGGER32_SOCKET_NAME "android:debuggerd32"
+#define DEBUGGER64_SOCKET_NAME DEBUGGER_SOCKET_NAME
+
+typedef enum {
+ // dump a crash
+ DEBUGGER_ACTION_CRASH,
+ // dump a tombstone file
+ DEBUGGER_ACTION_DUMP_TOMBSTONE,
+ // dump a backtrace only back to the socket
+ DEBUGGER_ACTION_DUMP_BACKTRACE,
+} debugger_action_t;
+
+// Make sure that all values have a fixed size so that this structure
+// is the same for 32 bit and 64 bit processes.
+// NOTE: Any changes to this structure must also be reflected in
+// bionic/linker/debugger.cpp.
+typedef struct __attribute__((packed)) {
+ int32_t action;
+ pid_t tid;
+ uint64_t abort_msg_address;
+ int32_t original_si_code;
+} debugger_msg_t;
+
+/* Dumps a process backtrace, registers, and stack to a tombstone file (requires root).
+ * Stores the tombstone path in the provided buffer.
+ * Returns 0 on success, -1 on error.
+ */
+int dump_tombstone(pid_t tid, char* pathbuf, size_t pathlen);
+
+/* Dumps a process backtrace, registers, and stack to a tombstone file (requires root).
+ * Stores the tombstone path in the provided buffer.
+ * If reading debugger data from debuggerd ever takes longer than timeout_secs
+ * seconds, then stop and return an error.
+ * Returns 0 on success, -1 on error.
+ */
+int dump_tombstone_timeout(pid_t tid, char* pathbuf, size_t pathlen, int timeout_secs);
+
+/* Dumps a process backtrace only to the specified file (requires root).
+ * Returns 0 on success, -1 on error.
+ */
+int dump_backtrace_to_file(pid_t tid, int fd);
+
+/* Dumps a process backtrace only to the specified file (requires root).
+ * If reading debugger data from debuggerd ever takes longer than timeout_secs
+ * seconds, then stop and return an error.
+ * Returns 0 on success, -1 on error.
+ */
+int dump_backtrace_to_file_timeout(pid_t tid, int fd, int timeout_secs);
+
+__END_DECLS
+
+#endif /* __CUTILS_DEBUGGER_H */
diff --git a/include/cutils/fs.h b/include/cutils/fs.h
new file mode 100644
index 0000000..70f0b92
--- /dev/null
+++ b/include/cutils/fs.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.
+ */
+
+#ifndef __CUTILS_FS_H
+#define __CUTILS_FS_H
+
+#include <sys/types.h>
+#include <unistd.h>
+
+/*
+ * TEMP_FAILURE_RETRY is defined by some, but not all, versions of
+ * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's
+ * not already defined, then define it here.
+ */
+#ifndef TEMP_FAILURE_RETRY
+/* Used to retry syscalls that can return EINTR. */
+#define TEMP_FAILURE_RETRY(exp) ({ \
+ typeof (exp) _rc; \
+ do { \
+ _rc = (exp); \
+ } while (_rc == -1 && errno == EINTR); \
+ _rc; })
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Ensure that directory exists with given mode and owners.
+ */
+extern int fs_prepare_dir(const char* path, mode_t mode, uid_t uid, gid_t gid);
+
+/*
+ * Read single plaintext integer from given file, correctly handling files
+ * partially written with fs_write_atomic_int().
+ */
+extern int fs_read_atomic_int(const char* path, int* value);
+
+/*
+ * Write single plaintext integer to given file, creating backup while
+ * in progress.
+ */
+extern int fs_write_atomic_int(const char* path, int value);
+
+/*
+ * Ensure that all directories along given path exist, creating parent
+ * directories as needed. Validates that given path is absolute and that
+ * it contains no relative "." or ".." paths or symlinks. Last path segment
+ * is treated as filename and ignored, unless the path ends with "/".
+ */
+extern int fs_mkdirs(const char* path, mode_t mode);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CUTILS_FS_H */
diff --git a/include/cutils/hashmap.h b/include/cutils/hashmap.h
new file mode 100644
index 0000000..5cb344c
--- /dev/null
+++ b/include/cutils/hashmap.h
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2007 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.
+ */
+
+/**
+ * Hash map.
+ */
+
+#ifndef __HASHMAP_H
+#define __HASHMAP_H
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** A hash map. */
+typedef struct Hashmap Hashmap;
+
+/**
+ * Creates a new hash map. Returns NULL if memory allocation fails.
+ *
+ * @param initialCapacity number of expected entries
+ * @param hash function which hashes keys
+ * @param equals function which compares keys for equality
+ */
+Hashmap* hashmapCreate(size_t initialCapacity,
+ int (*hash)(void* key), bool (*equals)(void* keyA, void* keyB));
+
+/**
+ * Frees the hash map. Does not free the keys or values themselves.
+ */
+void hashmapFree(Hashmap* map);
+
+/**
+ * Hashes the memory pointed to by key with the given size. Useful for
+ * implementing hash functions.
+ */
+int hashmapHash(void* key, size_t keySize);
+
+/**
+ * Puts value for the given key in the map. Returns pre-existing value if
+ * any.
+ *
+ * If memory allocation fails, this function returns NULL, the map's size
+ * does not increase, and errno is set to ENOMEM.
+ */
+void* hashmapPut(Hashmap* map, void* key, void* value);
+
+/**
+ * Gets a value from the map. Returns NULL if no entry for the given key is
+ * found or if the value itself is NULL.
+ */
+void* hashmapGet(Hashmap* map, void* key);
+
+/**
+ * Returns true if the map contains an entry for the given key.
+ */
+bool hashmapContainsKey(Hashmap* map, void* key);
+
+/**
+ * Gets the value for a key. If a value is not found, this function gets a
+ * value and creates an entry using the given callback.
+ *
+ * If memory allocation fails, the callback is not called, this function
+ * returns NULL, and errno is set to ENOMEM.
+ */
+void* hashmapMemoize(Hashmap* map, void* key,
+ void* (*initialValue)(void* key, void* context), void* context);
+
+/**
+ * Removes an entry from the map. Returns the removed value or NULL if no
+ * entry was present.
+ */
+void* hashmapRemove(Hashmap* map, void* key);
+
+/**
+ * Gets the number of entries in this map.
+ */
+size_t hashmapSize(Hashmap* map);
+
+/**
+ * Invokes the given callback on each entry in the map. Stops iterating if
+ * the callback returns false.
+ */
+void hashmapForEach(Hashmap* map,
+ bool (*callback)(void* key, void* value, void* context),
+ void* context);
+
+/**
+ * Concurrency support.
+ */
+
+/**
+ * Locks the hash map so only the current thread can access it.
+ */
+void hashmapLock(Hashmap* map);
+
+/**
+ * Unlocks the hash map so other threads can access it.
+ */
+void hashmapUnlock(Hashmap* map);
+
+/**
+ * Key utilities.
+ */
+
+/**
+ * Hashes int keys. 'key' is a pointer to int.
+ */
+int hashmapIntHash(void* key);
+
+/**
+ * Compares two int keys for equality.
+ */
+bool hashmapIntEquals(void* keyA, void* keyB);
+
+/**
+ * For debugging.
+ */
+
+/**
+ * Gets current capacity.
+ */
+size_t hashmapCurrentCapacity(Hashmap* map);
+
+/**
+ * Counts the number of entry collisions.
+ */
+size_t hashmapCountCollisions(Hashmap* map);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __HASHMAP_H */
diff --git a/include/cutils/iosched_policy.h b/include/cutils/iosched_policy.h
new file mode 100644
index 0000000..07c5d1f
--- /dev/null
+++ b/include/cutils/iosched_policy.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CUTILS_IOSCHED_POLICY_H
+#define __CUTILS_IOSCHED_POLICY_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+ IoSchedClass_NONE,
+ IoSchedClass_RT,
+ IoSchedClass_BE,
+ IoSchedClass_IDLE,
+} IoSchedClass;
+
+extern int android_set_ioprio(int pid, IoSchedClass clazz, int ioprio);
+extern int android_get_ioprio(int pid, IoSchedClass *clazz, int *ioprio);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CUTILS_IOSCHED_POLICY_H */
diff --git a/include/cutils/jstring.h b/include/cutils/jstring.h
new file mode 100644
index 0000000..a342608
--- /dev/null
+++ b/include/cutils/jstring.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CUTILS_STRING16_H
+#define __CUTILS_STRING16_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if __STDC_VERSION__ < 201112L && __cplusplus < 201103L
+ typedef uint16_t char16_t;
+#endif
+ // otherwise char16_t is a keyword with the right semantics
+
+extern char * strndup16to8 (const char16_t* s, size_t n);
+extern size_t strnlen16to8 (const char16_t* s, size_t n);
+extern char * strncpy16to8 (char *dest, const char16_t*s, size_t n);
+
+extern char16_t * strdup8to16 (const char* s, size_t *out_len);
+extern size_t strlen8to16 (const char* utf8Str);
+extern char16_t * strcpy8to16 (char16_t *dest, const char*s, size_t *out_len);
+extern char16_t * strcpylen8to16 (char16_t *dest, const char*s, int length,
+ size_t *out_len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CUTILS_STRING16_H */
diff --git a/include/cutils/klog.h b/include/cutils/klog.h
new file mode 100644
index 0000000..295d62b
--- /dev/null
+++ b/include/cutils/klog.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _CUTILS_KLOG_H_
+#define _CUTILS_KLOG_H_
+
+#include <sys/cdefs.h>
+#include <sys/uio.h>
+#include <stdarg.h>
+
+__BEGIN_DECLS
+
+void klog_init(void);
+int klog_get_level(void);
+void klog_set_level(int level);
+/* TODO: void klog_close(void); - and make klog_fd users thread safe. */
+
+void klog_write(int level, const char *fmt, ...)
+ __attribute__ ((format(printf, 2, 3)));
+void klog_writev(int level, const struct iovec* iov, int iov_count);
+
+__END_DECLS
+
+#define KLOG_ERROR_LEVEL 3
+#define KLOG_WARNING_LEVEL 4
+#define KLOG_NOTICE_LEVEL 5
+#define KLOG_INFO_LEVEL 6
+#define KLOG_DEBUG_LEVEL 7
+
+#define KLOG_ERROR(tag,x...) klog_write(KLOG_ERROR_LEVEL, "<3>" tag ": " x)
+#define KLOG_WARNING(tag,x...) klog_write(KLOG_WARNING_LEVEL, "<4>" tag ": " x)
+#define KLOG_NOTICE(tag,x...) klog_write(KLOG_NOTICE_LEVEL, "<5>" tag ": " x)
+#define KLOG_INFO(tag,x...) klog_write(KLOG_INFO_LEVEL, "<6>" tag ": " x)
+#define KLOG_DEBUG(tag,x...) klog_write(KLOG_DEBUG_LEVEL, "<7>" tag ": " x)
+
+#define KLOG_DEFAULT_LEVEL 3 /* messages <= this level are logged */
+
+#endif
diff --git a/include/cutils/list.h b/include/cutils/list.h
new file mode 100644
index 0000000..4ba2cfd
--- /dev/null
+++ b/include/cutils/list.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2008-2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _CUTILS_LIST_H_
+#define _CUTILS_LIST_H_
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+struct listnode
+{
+ struct listnode *next;
+ struct listnode *prev;
+};
+
+#define node_to_item(node, container, member) \
+ (container *) (((char*) (node)) - offsetof(container, member))
+
+#define list_declare(name) \
+ struct listnode name = { \
+ .next = &name, \
+ .prev = &name, \
+ }
+
+#define list_for_each(node, list) \
+ for (node = (list)->next; node != (list); node = node->next)
+
+#define list_for_each_reverse(node, list) \
+ for (node = (list)->prev; node != (list); node = node->prev)
+
+#define list_for_each_safe(node, n, list) \
+ for (node = (list)->next, n = node->next; \
+ node != (list); \
+ node = n, n = node->next)
+
+static inline void list_init(struct listnode *node)
+{
+ node->next = node;
+ node->prev = node;
+}
+
+static inline void list_add_tail(struct listnode *head, struct listnode *item)
+{
+ item->next = head;
+ item->prev = head->prev;
+ head->prev->next = item;
+ head->prev = item;
+}
+
+static inline void list_add_head(struct listnode *head, struct listnode *item)
+{
+ item->next = head->next;
+ item->prev = head;
+ head->next->prev = item;
+ head->next = item;
+}
+
+static inline void list_remove(struct listnode *item)
+{
+ item->next->prev = item->prev;
+ item->prev->next = item->next;
+}
+
+#define list_empty(list) ((list) == (list)->next)
+#define list_head(list) ((list)->next)
+#define list_tail(list) ((list)->prev)
+
+#ifdef __cplusplus
+};
+#endif /* __cplusplus */
+
+#endif
diff --git a/include/cutils/log.h b/include/cutils/log.h
new file mode 100644
index 0000000..0e0248e
--- /dev/null
+++ b/include/cutils/log.h
@@ -0,0 +1 @@
+#include <log/log.h>
diff --git a/include/cutils/memory.h b/include/cutils/memory.h
new file mode 100644
index 0000000..4d26882
--- /dev/null
+++ b/include/cutils/memory.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_CUTILS_MEMORY_H
+#define ANDROID_CUTILS_MEMORY_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* size is given in bytes and must be multiple of 2 */
+void android_memset16(uint16_t* dst, uint16_t value, size_t size);
+
+/* size is given in bytes and must be multiple of 4 */
+void android_memset32(uint32_t* dst, uint32_t value, size_t size);
+
+#if defined(__GLIBC__) || defined(_WIN32)
+/* Declaration of strlcpy() for platforms that don't already have it. */
+size_t strlcpy(char *dst, const char *src, size_t size);
+#endif
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // ANDROID_CUTILS_MEMORY_H
diff --git a/include/cutils/misc.h b/include/cutils/misc.h
new file mode 100644
index 0000000..0de505f
--- /dev/null
+++ b/include/cutils/misc.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CUTILS_MISC_H
+#define __CUTILS_MISC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /* Load an entire file into a malloc'd chunk of memory
+ * that is length_of_file + 1 (null terminator). If
+ * sz is non-zero, return the size of the file via sz.
+ * Returns 0 on failure.
+ */
+extern void *load_file(const char *fn, unsigned *sz);
+
+ /* This is the range of UIDs (and GIDs) that are reserved
+ * for assigning to applications.
+ */
+#define FIRST_APPLICATION_UID 10000
+#define LAST_APPLICATION_UID 99999
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CUTILS_MISC_H */
diff --git a/include/cutils/multiuser.h b/include/cutils/multiuser.h
new file mode 100644
index 0000000..635ddb1
--- /dev/null
+++ b/include/cutils/multiuser.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CUTILS_MULTIUSER_H
+#define __CUTILS_MULTIUSER_H
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// NOTE: keep in sync with android.os.UserId
+
+#define MULTIUSER_APP_PER_USER_RANGE 100000
+
+typedef uid_t userid_t;
+typedef uid_t appid_t;
+
+extern userid_t multiuser_get_user_id(uid_t uid);
+extern appid_t multiuser_get_app_id(uid_t uid);
+extern uid_t multiuser_get_uid(userid_t userId, appid_t appId);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CUTILS_MULTIUSER_H */
diff --git a/include/cutils/native_handle.h b/include/cutils/native_handle.h
new file mode 100644
index 0000000..268c5d3
--- /dev/null
+++ b/include/cutils/native_handle.h
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+#ifndef NATIVE_HANDLE_H_
+#define NATIVE_HANDLE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct native_handle
+{
+ int version; /* sizeof(native_handle_t) */
+ int numFds; /* number of file-descriptors at &data[0] */
+ int numInts; /* number of ints at &data[numFds] */
+ int data[0]; /* numFds + numInts ints */
+} native_handle_t;
+
+/*
+ * native_handle_close
+ *
+ * closes the file descriptors contained in this native_handle_t
+ *
+ * return 0 on success, or a negative error code on failure
+ *
+ */
+int native_handle_close(const native_handle_t* h);
+
+
+/*
+ * native_handle_create
+ *
+ * creates a native_handle_t and initializes it. must be destroyed with
+ * native_handle_delete().
+ *
+ */
+native_handle_t* native_handle_create(int numFds, int numInts);
+
+/*
+ * native_handle_delete
+ *
+ * frees a native_handle_t allocated with native_handle_create().
+ * This ONLY frees the memory allocated for the native_handle_t, but doesn't
+ * close the file descriptors; which can be achieved with native_handle_close().
+ *
+ * return 0 on success, or a negative error code on failure
+ *
+ */
+int native_handle_delete(native_handle_t* h);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NATIVE_HANDLE_H_ */
diff --git a/include/cutils/open_memstream.h b/include/cutils/open_memstream.h
new file mode 100644
index 0000000..c1a81eb
--- /dev/null
+++ b/include/cutils/open_memstream.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CUTILS_OPEN_MEMSTREAM_H__
+#define __CUTILS_OPEN_MEMSTREAM_H__
+
+#include <stdio.h>
+
+#if defined(__APPLE__)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+FILE* open_memstream(char** bufp, size_t* sizep);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __APPLE__ */
+
+#endif /*__CUTILS_OPEN_MEMSTREAM_H__*/
diff --git a/include/cutils/partition_utils.h b/include/cutils/partition_utils.h
new file mode 100644
index 0000000..72ca80d
--- /dev/null
+++ b/include/cutils/partition_utils.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright 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.
+ */
+
+#ifndef __CUTILS_PARTITION_WIPED_H__
+#define __CUTILS_PARTITION_WIPED_H__
+
+__BEGIN_DECLS
+
+int partition_wiped(char *source);
+
+__END_DECLS
+
+#endif /* __CUTILS_PARTITION_WIPED_H__ */
diff --git a/include/cutils/process_name.h b/include/cutils/process_name.h
new file mode 100644
index 0000000..1e72e5c
--- /dev/null
+++ b/include/cutils/process_name.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Gives the current process a name.
+ */
+
+#ifndef __PROCESS_NAME_H
+#define __PROCESS_NAME_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Sets the current process name.
+ *
+ * Warning: This leaks a string every time you call it. Use judiciously!
+ */
+void set_process_name(const char* process_name);
+
+/** Gets the current process name. */
+const char* get_process_name(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PROCESS_NAME_H */
diff --git a/include/cutils/properties.h b/include/cutils/properties.h
new file mode 100644
index 0000000..24aa224
--- /dev/null
+++ b/include/cutils/properties.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CUTILS_PROPERTIES_H
+#define __CUTILS_PROPERTIES_H
+
+#include <sys/cdefs.h>
+#include <stddef.h>
+#include <sys/system_properties.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* System properties are *small* name value pairs managed by the
+** property service. If your data doesn't fit in the provided
+** space it is not appropriate for a system property.
+**
+** WARNING: system/bionic/include/sys/system_properties.h also defines
+** these, but with different names. (TODO: fix that)
+*/
+#define PROPERTY_KEY_MAX PROP_NAME_MAX
+#define PROPERTY_VALUE_MAX PROP_VALUE_MAX
+
+/* property_get: returns the length of the value which will never be
+** greater than PROPERTY_VALUE_MAX - 1 and will always be zero terminated.
+** (the length does not include the terminating zero).
+**
+** If the property read fails or returns an empty value, the default
+** value is used (if nonnull).
+*/
+int property_get(const char *key, char *value, const char *default_value);
+
+/* property_get_bool: returns the value of key coerced into a
+** boolean. If the property is not set, then the default value is returned.
+**
+* The following is considered to be true (1):
+** "1", "true", "y", "yes", "on"
+**
+** The following is considered to be false (0):
+** "0", "false", "n", "no", "off"
+**
+** The conversion is whitespace-sensitive (e.g. " off" will not be false).
+**
+** If no property with this key is set (or the key is NULL) or the boolean
+** conversion fails, the default value is returned.
+**/
+int8_t property_get_bool(const char *key, int8_t default_value);
+
+/* property_get_int64: returns the value of key truncated and coerced into a
+** int64_t. If the property is not set, then the default value is used.
+**
+** The numeric conversion is identical to strtoimax with the base inferred:
+** - All digits up to the first non-digit characters are read
+** - The longest consecutive prefix of digits is converted to a long
+**
+** Valid strings of digits are:
+** - An optional sign character + or -
+** - An optional prefix indicating the base (otherwise base 10 is assumed)
+** -- 0 prefix is octal
+** -- 0x / 0X prefix is hex
+**
+** Leading/trailing whitespace is ignored. Overflow/underflow will cause
+** numeric conversion to fail.
+**
+** If no property with this key is set (or the key is NULL) or the numeric
+** conversion fails, the default value is returned.
+**/
+int64_t property_get_int64(const char *key, int64_t default_value);
+
+/* property_get_int32: returns the value of key truncated and coerced into an
+** int32_t. If the property is not set, then the default value is used.
+**
+** The numeric conversion is identical to strtoimax with the base inferred:
+** - All digits up to the first non-digit characters are read
+** - The longest consecutive prefix of digits is converted to a long
+**
+** Valid strings of digits are:
+** - An optional sign character + or -
+** - An optional prefix indicating the base (otherwise base 10 is assumed)
+** -- 0 prefix is octal
+** -- 0x / 0X prefix is hex
+**
+** Leading/trailing whitespace is ignored. Overflow/underflow will cause
+** numeric conversion to fail.
+**
+** If no property with this key is set (or the key is NULL) or the numeric
+** conversion fails, the default value is returned.
+**/
+int32_t property_get_int32(const char *key, int32_t default_value);
+
+/* property_set: returns 0 on success, < 0 on failure
+*/
+int property_set(const char *key, const char *value);
+
+int property_list(void (*propfn)(const char *key, const char *value, void *cookie), void *cookie);
+
+#if defined(__BIONIC_FORTIFY)
+
+extern int __property_get_real(const char *, char *, const char *)
+ __asm__(__USER_LABEL_PREFIX__ "property_get");
+__errordecl(__property_get_too_small_error, "property_get() called with too small of a buffer");
+
+__BIONIC_FORTIFY_INLINE
+int property_get(const char *key, char *value, const char *default_value) {
+ size_t bos = __bos(value);
+ if (bos < PROPERTY_VALUE_MAX) {
+ __property_get_too_small_error();
+ }
+ return __property_get_real(key, value, default_value);
+}
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/cutils/qtaguid.h b/include/cutils/qtaguid.h
new file mode 100644
index 0000000..f8550fd
--- /dev/null
+++ b/include/cutils/qtaguid.h
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#ifndef __CUTILS_QTAGUID_H
+#define __CUTILS_QTAGUID_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Set tags (and owning UIDs) for network sockets.
+*/
+extern int qtaguid_tagSocket(int sockfd, int tag, uid_t uid);
+
+/*
+ * Untag a network socket before closing.
+*/
+extern int qtaguid_untagSocket(int sockfd);
+
+/*
+ * For the given uid, switch counter sets.
+ * The kernel only keeps a limited number of sets.
+ * 2 for now.
+ */
+extern int qtaguid_setCounterSet(int counterSetNum, uid_t uid);
+
+/*
+ * Delete all tag info that relates to the given tag an uid.
+ * If the tag is 0, then ALL info about the uid is freeded.
+ * The delete data also affects active tagged socketd, which are
+ * then untagged.
+ * The calling process can only operate on its own tags.
+ * Unless it is part of the happy AID_NET_BW_ACCT group.
+ * In which case it can clobber everything.
+ */
+extern int qtaguid_deleteTagData(int tag, uid_t uid);
+
+/*
+ * Enable/disable qtaguid functionnality at a lower level.
+ * When pacified, the kernel will accept commands but do nothing.
+ */
+extern int qtaguid_setPacifier(int on);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CUTILS_QTAG_UID_H */
diff --git a/include/cutils/record_stream.h b/include/cutils/record_stream.h
new file mode 100644
index 0000000..bfac87a
--- /dev/null
+++ b/include/cutils/record_stream.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2006 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.
+ */
+
+/*
+ * A simple utility for reading fixed records out of a stream fd
+ */
+
+#ifndef _CUTILS_RECORD_STREAM_H
+#define _CUTILS_RECORD_STREAM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+typedef struct RecordStream RecordStream;
+
+extern RecordStream *record_stream_new(int fd, size_t maxRecordLen);
+extern void record_stream_free(RecordStream *p_rs);
+
+extern int record_stream_get_next (RecordStream *p_rs, void ** p_outRecord,
+ size_t *p_outRecordLen);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /*_CUTILS_RECORD_STREAM_H*/
+
diff --git a/include/cutils/sched_policy.h b/include/cutils/sched_policy.h
new file mode 100644
index 0000000..ba84ce3
--- /dev/null
+++ b/include/cutils/sched_policy.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CUTILS_SCHED_POLICY_H
+#define __CUTILS_SCHED_POLICY_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Keep in sync with THREAD_GROUP_* in frameworks/base/core/java/android/os/Process.java */
+typedef enum {
+ SP_DEFAULT = -1,
+ SP_BACKGROUND = 0,
+ SP_FOREGROUND = 1,
+ SP_SYSTEM = 2, // can't be used with set_sched_policy()
+ SP_AUDIO_APP = 3,
+ SP_AUDIO_SYS = 4,
+ SP_CNT,
+ SP_MAX = SP_CNT - 1,
+ SP_SYSTEM_DEFAULT = SP_FOREGROUND,
+} SchedPolicy;
+
+/* Assign thread tid to the cgroup associated with the specified policy.
+ * If the thread is a thread group leader, that is it's gettid() == getpid(),
+ * then the other threads in the same thread group are _not_ affected.
+ * On platforms which support gettid(), zero tid means current thread.
+ * Return value: 0 for success, or -errno for error.
+ */
+extern int set_sched_policy(int tid, SchedPolicy policy);
+
+/* Return the policy associated with the cgroup of thread tid via policy pointer.
+ * On platforms which support gettid(), zero tid means current thread.
+ * Return value: 0 for success, or -1 for error and set errno.
+ */
+extern int get_sched_policy(int tid, SchedPolicy *policy);
+
+/* Return a displayable string corresponding to policy.
+ * Return value: non-NULL NUL-terminated name of unspecified length;
+ * the caller is responsible for displaying the useful part of the string.
+ */
+extern const char *get_sched_policy_name(SchedPolicy policy);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CUTILS_SCHED_POLICY_H */
diff --git a/include/cutils/sockets.h b/include/cutils/sockets.h
new file mode 100644
index 0000000..07d1351
--- /dev/null
+++ b/include/cutils/sockets.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CUTILS_SOCKETS_H
+#define __CUTILS_SOCKETS_H
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+
+#ifdef HAVE_WINSOCK
+#include <winsock2.h>
+typedef int socklen_t;
+#else
+#include <sys/socket.h>
+#endif
+
+#define ANDROID_SOCKET_ENV_PREFIX "ANDROID_SOCKET_"
+#define ANDROID_SOCKET_DIR "/dev/socket"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * android_get_control_socket - simple helper function to get the file
+ * descriptor of our init-managed Unix domain socket. `name' is the name of the
+ * socket, as given in init.rc. Returns -1 on error.
+ *
+ * This is inline and not in libcutils proper because we want to use this in
+ * third-party daemons with minimal modification.
+ */
+static inline int android_get_control_socket(const char *name)
+{
+ char key[64];
+ snprintf(key, sizeof(key), ANDROID_SOCKET_ENV_PREFIX "%s", name);
+
+ const char* val = getenv(key);
+ if (!val) {
+ return -1;
+ }
+
+ errno = 0;
+ int fd = strtol(val, NULL, 10);
+ if (errno) {
+ return -1;
+ }
+
+ return fd;
+}
+
+/*
+ * See also android.os.LocalSocketAddress.Namespace
+ */
+// Linux "abstract" (non-filesystem) namespace
+#define ANDROID_SOCKET_NAMESPACE_ABSTRACT 0
+// Android "reserved" (/dev/socket) namespace
+#define ANDROID_SOCKET_NAMESPACE_RESERVED 1
+// Normal filesystem namespace
+#define ANDROID_SOCKET_NAMESPACE_FILESYSTEM 2
+
+extern int socket_loopback_client(int port, int type);
+extern int socket_network_client(const char *host, int port, int type);
+extern int socket_network_client_timeout(const char *host, int port, int type,
+ int timeout, int* getaddrinfo_error);
+extern int socket_loopback_server(int port, int type);
+extern int socket_local_server(const char *name, int namespaceId, int type);
+extern int socket_local_server_bind(int s, const char *name, int namespaceId);
+extern int socket_local_client_connect(int fd,
+ const char *name, int namespaceId, int type);
+extern int socket_local_client(const char *name, int namespaceId, int type);
+extern int socket_inaddr_any_server(int port, int type);
+
+/*
+ * socket_peer_is_trusted - Takes a socket which is presumed to be a
+ * connected local socket (e.g. AF_LOCAL) and returns whether the peer
+ * (the userid that owns the process on the other end of that socket)
+ * is one of the two trusted userids, root or shell.
+ *
+ * Note: This only works as advertised on the Android OS and always
+ * just returns true when called on other operating systems.
+ */
+extern bool socket_peer_is_trusted(int fd);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CUTILS_SOCKETS_H */
diff --git a/include/cutils/str_parms.h b/include/cutils/str_parms.h
new file mode 100644
index 0000000..aa1435a
--- /dev/null
+++ b/include/cutils/str_parms.h
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+#ifndef __CUTILS_STR_PARMS_H
+#define __CUTILS_STR_PARMS_H
+
+#include <stdint.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+struct str_parms;
+
+struct str_parms *str_parms_create(void);
+struct str_parms *str_parms_create_str(const char *_string);
+void str_parms_destroy(struct str_parms *str_parms);
+
+void str_parms_del(struct str_parms *str_parms, const char *key);
+
+int str_parms_add_str(struct str_parms *str_parms, const char *key,
+ const char *value);
+int str_parms_add_int(struct str_parms *str_parms, const char *key, int value);
+
+int str_parms_add_float(struct str_parms *str_parms, const char *key,
+ float value);
+
+// Returns non-zero if the str_parms contains the specified key.
+int str_parms_has_key(struct str_parms *str_parms, const char *key);
+
+// Gets value associated with the specified key (if present), placing it in the buffer
+// pointed to by the out_val parameter. Returns the length of the returned string value.
+// If 'key' isn't in the parms, then return -ENOENT (-2) and leave 'out_val' untouched.
+int str_parms_get_str(struct str_parms *str_parms, const char *key,
+ char *out_val, int len);
+int str_parms_get_int(struct str_parms *str_parms, const char *key,
+ int *out_val);
+int str_parms_get_float(struct str_parms *str_parms, const char *key,
+ float *out_val);
+
+char *str_parms_to_str(struct str_parms *str_parms);
+
+/* debug */
+void str_parms_dump(struct str_parms *str_parms);
+
+__END_DECLS
+
+#endif /* __CUTILS_STR_PARMS_H */
diff --git a/include/cutils/threads.h b/include/cutils/threads.h
new file mode 100644
index 0000000..5727494
--- /dev/null
+++ b/include/cutils/threads.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBS_CUTILS_THREADS_H
+#define _LIBS_CUTILS_THREADS_H
+
+#include <sys/types.h>
+
+#if !defined(_WIN32)
+#include <pthread.h>
+#else
+#include <windows.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/***********************************************************************/
+/***********************************************************************/
+/***** *****/
+/***** local thread storage *****/
+/***** *****/
+/***********************************************************************/
+/***********************************************************************/
+
+extern pid_t gettid();
+
+#if !defined(_WIN32)
+
+typedef struct {
+ pthread_mutex_t lock;
+ int has_tls;
+ pthread_key_t tls;
+} thread_store_t;
+
+#define THREAD_STORE_INITIALIZER { PTHREAD_MUTEX_INITIALIZER, 0, 0 }
+
+#else // !defined(_WIN32)
+
+typedef struct {
+ int lock_init;
+ int has_tls;
+ DWORD tls;
+ CRITICAL_SECTION lock;
+} thread_store_t;
+
+#define THREAD_STORE_INITIALIZER { 0, 0, 0, {0, 0, 0, 0, 0, 0} }
+
+#endif // !defined(_WIN32)
+
+typedef void (*thread_store_destruct_t)(void* value);
+
+extern void* thread_store_get(thread_store_t* store);
+
+extern void thread_store_set(thread_store_t* store,
+ void* value,
+ thread_store_destruct_t destroy);
+
+/***********************************************************************/
+/***********************************************************************/
+/***** *****/
+/***** mutexes *****/
+/***** *****/
+/***********************************************************************/
+/***********************************************************************/
+
+#if !defined(_WIN32)
+
+typedef pthread_mutex_t mutex_t;
+
+#define MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
+
+static __inline__ void mutex_lock(mutex_t* lock)
+{
+ pthread_mutex_lock(lock);
+}
+static __inline__ void mutex_unlock(mutex_t* lock)
+{
+ pthread_mutex_unlock(lock);
+}
+static __inline__ int mutex_init(mutex_t* lock)
+{
+ return pthread_mutex_init(lock, NULL);
+}
+static __inline__ void mutex_destroy(mutex_t* lock)
+{
+ pthread_mutex_destroy(lock);
+}
+
+#else // !defined(_WIN32)
+
+typedef struct {
+ int init;
+ CRITICAL_SECTION lock[1];
+} mutex_t;
+
+#define MUTEX_INITIALIZER { 0, {{ NULL, 0, 0, NULL, NULL, 0 }} }
+
+static __inline__ void mutex_lock(mutex_t* lock)
+{
+ if (!lock->init) {
+ lock->init = 1;
+ InitializeCriticalSection( lock->lock );
+ lock->init = 2;
+ } else while (lock->init != 2)
+ Sleep(10);
+
+ EnterCriticalSection(lock->lock);
+}
+
+static __inline__ void mutex_unlock(mutex_t* lock)
+{
+ LeaveCriticalSection(lock->lock);
+}
+static __inline__ int mutex_init(mutex_t* lock)
+{
+ InitializeCriticalSection(lock->lock);
+ lock->init = 2;
+ return 0;
+}
+static __inline__ void mutex_destroy(mutex_t* lock)
+{
+ if (lock->init) {
+ lock->init = 0;
+ DeleteCriticalSection(lock->lock);
+ }
+}
+#endif // !defined(_WIN32)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBS_CUTILS_THREADS_H */
diff --git a/include/cutils/trace.h b/include/cutils/trace.h
new file mode 100644
index 0000000..e4ed179
--- /dev/null
+++ b/include/cutils/trace.h
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBS_CUTILS_TRACE_H
+#define _LIBS_CUTILS_TRACE_H
+
+#include <inttypes.h>
+#include <stdatomic.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <cutils/compiler.h>
+
+__BEGIN_DECLS
+
+/**
+ * The ATRACE_TAG macro can be defined before including this header to trace
+ * using one of the tags defined below. It must be defined to one of the
+ * following ATRACE_TAG_* macros. The trace tag is used to filter tracing in
+ * userland to avoid some of the runtime cost of tracing when it is not desired.
+ *
+ * Defining ATRACE_TAG to be ATRACE_TAG_ALWAYS will result in the tracing always
+ * being enabled - this should ONLY be done for debug code, as userland tracing
+ * has a performance cost even when the trace is not being recorded. Defining
+ * ATRACE_TAG to be ATRACE_TAG_NEVER or leaving ATRACE_TAG undefined will result
+ * in the tracing always being disabled.
+ *
+ * ATRACE_TAG_HAL should be bitwise ORed with the relevant tags for tracing
+ * within a hardware module. For example a camera hardware module would set:
+ * #define ATRACE_TAG (ATRACE_TAG_CAMERA | ATRACE_TAG_HAL)
+ *
+ * Keep these in sync with frameworks/base/core/java/android/os/Trace.java.
+ */
+#define ATRACE_TAG_NEVER 0 // This tag is never enabled.
+#define ATRACE_TAG_ALWAYS (1<<0) // This tag is always enabled.
+#define ATRACE_TAG_GRAPHICS (1<<1)
+#define ATRACE_TAG_INPUT (1<<2)
+#define ATRACE_TAG_VIEW (1<<3)
+#define ATRACE_TAG_WEBVIEW (1<<4)
+#define ATRACE_TAG_WINDOW_MANAGER (1<<5)
+#define ATRACE_TAG_ACTIVITY_MANAGER (1<<6)
+#define ATRACE_TAG_SYNC_MANAGER (1<<7)
+#define ATRACE_TAG_AUDIO (1<<8)
+#define ATRACE_TAG_VIDEO (1<<9)
+#define ATRACE_TAG_CAMERA (1<<10)
+#define ATRACE_TAG_HAL (1<<11)
+#define ATRACE_TAG_APP (1<<12)
+#define ATRACE_TAG_RESOURCES (1<<13)
+#define ATRACE_TAG_DALVIK (1<<14)
+#define ATRACE_TAG_RS (1<<15)
+#define ATRACE_TAG_BIONIC (1<<16)
+#define ATRACE_TAG_POWER (1<<17)
+#define ATRACE_TAG_LAST ATRACE_TAG_POWER
+
+// Reserved for initialization.
+#define ATRACE_TAG_NOT_READY (1LL<<63)
+
+#define ATRACE_TAG_VALID_MASK ((ATRACE_TAG_LAST - 1) | ATRACE_TAG_LAST)
+
+#ifndef ATRACE_TAG
+#define ATRACE_TAG ATRACE_TAG_NEVER
+#elif ATRACE_TAG > ATRACE_TAG_VALID_MASK
+#error ATRACE_TAG must be defined to be one of the tags defined in cutils/trace.h
+#endif
+
+/**
+ * Opens the trace file for writing and reads the property for initial tags.
+ * The atrace.tags.enableflags property sets the tags to trace.
+ * This function should not be explicitly called, the first call to any normal
+ * trace function will cause it to be run safely.
+ */
+void atrace_setup();
+
+/**
+ * If tracing is ready, set atrace_enabled_tags to the system property
+ * debug.atrace.tags.enableflags. Can be used as a sysprop change callback.
+ */
+void atrace_update_tags();
+
+/**
+ * Set whether the process is debuggable. By default the process is not
+ * considered debuggable. If the process is not debuggable then application-
+ * level tracing is not allowed unless the ro.debuggable system property is
+ * set to '1'.
+ */
+void atrace_set_debuggable(bool debuggable);
+
+/**
+ * Set whether tracing is enabled for the current process. This is used to
+ * prevent tracing within the Zygote process.
+ */
+void atrace_set_tracing_enabled(bool enabled);
+
+/**
+ * Flag indicating whether setup has been completed, initialized to 0.
+ * Nonzero indicates setup has completed.
+ * Note: This does NOT indicate whether or not setup was successful.
+ */
+extern atomic_bool atrace_is_ready;
+
+/**
+ * Set of ATRACE_TAG flags to trace for, initialized to ATRACE_TAG_NOT_READY.
+ * A value of zero indicates setup has failed.
+ * Any other nonzero value indicates setup has succeeded, and tracing is on.
+ */
+extern uint64_t atrace_enabled_tags;
+
+/**
+ * Handle to the kernel's trace buffer, initialized to -1.
+ * Any other value indicates setup has succeeded, and is a valid fd for tracing.
+ */
+extern int atrace_marker_fd;
+
+/**
+ * atrace_init readies the process for tracing by opening the trace_marker file.
+ * Calling any trace function causes this to be run, so calling it is optional.
+ * This can be explicitly run to avoid setup delay on first trace function.
+ */
+#define ATRACE_INIT() atrace_init()
+static inline void atrace_init()
+{
+ if (CC_UNLIKELY(!atomic_load_explicit(&atrace_is_ready, memory_order_acquire))) {
+ atrace_setup();
+ }
+}
+
+/**
+ * Get the mask of all tags currently enabled.
+ * It can be used as a guard condition around more expensive trace calculations.
+ * Every trace function calls this, which ensures atrace_init is run.
+ */
+#define ATRACE_GET_ENABLED_TAGS() atrace_get_enabled_tags()
+static inline uint64_t atrace_get_enabled_tags()
+{
+ atrace_init();
+ return atrace_enabled_tags;
+}
+
+/**
+ * Test if a given tag is currently enabled.
+ * Returns nonzero if the tag is enabled, otherwise zero.
+ * It can be used as a guard condition around more expensive trace calculations.
+ */
+#define ATRACE_ENABLED() atrace_is_tag_enabled(ATRACE_TAG)
+static inline uint64_t atrace_is_tag_enabled(uint64_t tag)
+{
+ return atrace_get_enabled_tags() & tag;
+}
+
+/**
+ * Trace the beginning of a context. name is used to identify the context.
+ * This is often used to time function execution.
+ */
+#define ATRACE_BEGIN(name) atrace_begin(ATRACE_TAG, name)
+static inline void atrace_begin(uint64_t tag, const char* name)
+{
+ if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
+ void atrace_begin_body(const char*);
+ atrace_begin_body(name);
+ }
+}
+
+/**
+ * Trace the end of a context.
+ * This should match up (and occur after) a corresponding ATRACE_BEGIN.
+ */
+#define ATRACE_END() atrace_end(ATRACE_TAG)
+static inline void atrace_end(uint64_t tag)
+{
+ if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
+ char c = 'E';
+ write(atrace_marker_fd, &c, 1);
+ }
+}
+
+/**
+ * Trace the beginning of an asynchronous event. Unlike ATRACE_BEGIN/ATRACE_END
+ * contexts, asynchronous events do not need to be nested. The name describes
+ * the event, and the cookie provides a unique identifier for distinguishing
+ * simultaneous events. The name and cookie used to begin an event must be
+ * used to end it.
+ */
+#define ATRACE_ASYNC_BEGIN(name, cookie) \
+ atrace_async_begin(ATRACE_TAG, name, cookie)
+static inline void atrace_async_begin(uint64_t tag, const char* name,
+ int32_t cookie)
+{
+ if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
+ void atrace_async_begin_body(const char*, int32_t);
+ atrace_async_begin_body(name, cookie);
+ }
+}
+
+/**
+ * Trace the end of an asynchronous event.
+ * This should have a corresponding ATRACE_ASYNC_BEGIN.
+ */
+#define ATRACE_ASYNC_END(name, cookie) atrace_async_end(ATRACE_TAG, name, cookie)
+static inline void atrace_async_end(uint64_t tag, const char* name, int32_t cookie)
+{
+ if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
+ void atrace_async_end_body(const char*, int32_t);
+ atrace_async_end_body(name, cookie);
+ }
+}
+
+/**
+ * Traces an integer counter value. name is used to identify the counter.
+ * This can be used to track how a value changes over time.
+ */
+#define ATRACE_INT(name, value) atrace_int(ATRACE_TAG, name, value)
+static inline void atrace_int(uint64_t tag, const char* name, int32_t value)
+{
+ if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
+ void atrace_int_body(const char*, int32_t);
+ atrace_int_body(name, value);
+ }
+}
+
+/**
+ * Traces a 64-bit integer counter value. name is used to identify the
+ * counter. This can be used to track how a value changes over time.
+ */
+#define ATRACE_INT64(name, value) atrace_int64(ATRACE_TAG, name, value)
+static inline void atrace_int64(uint64_t tag, const char* name, int64_t value)
+{
+ if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
+ void atrace_int64_body(const char*, int64_t);
+ atrace_int64_body(name, value);
+ }
+}
+
+__END_DECLS
+
+#endif // _LIBS_CUTILS_TRACE_H
diff --git a/include/cutils/uevent.h b/include/cutils/uevent.h
new file mode 100644
index 0000000..da1c2aa
--- /dev/null
+++ b/include/cutils/uevent.h
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+#ifndef __CUTILS_UEVENT_H
+#define __CUTILS_UEVENT_H
+
+#include <stdbool.h>
+#include <sys/socket.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int uevent_open_socket(int buf_sz, bool passcred);
+ssize_t uevent_kernel_multicast_recv(int socket, void *buffer, size_t length);
+ssize_t uevent_kernel_multicast_uid_recv(int socket, void *buffer, size_t length, uid_t *uid);
+ssize_t uevent_kernel_recv(int socket, void *buffer, size_t length, bool require_group, uid_t *uid);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CUTILS_UEVENT_H */
diff --git a/include/diskconfig/diskconfig.h b/include/diskconfig/diskconfig.h
new file mode 100644
index 0000000..d45b99e
--- /dev/null
+++ b/include/diskconfig/diskconfig.h
@@ -0,0 +1,130 @@
+/* system/core/include/diskconfig/diskconfig.h
+ *
+ * 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.
+ */
+
+#ifndef __LIBS_DISKCONFIG_H
+#define __LIBS_DISKCONFIG_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MAX_NAME_LEN 512
+#define MAX_NUM_PARTS 16
+
+/* known partition schemes */
+#define PART_SCHEME_MBR 0x1
+#define PART_SCHEME_GPT 0x2
+
+/* PC Bios partition status */
+#define PC_PART_ACTIVE 0x80
+#define PC_PART_NORMAL 0x0
+
+/* Known (rather, used by us) partition types */
+#define PC_PART_TYPE_LINUX 0x83
+#define PC_PART_TYPE_EXTENDED 0x05
+#define PC_PART_TYPE_FAT32 0x0c
+
+#define PC_NUM_BOOT_RECORD_PARTS 4
+
+#define PC_EBR_LOGICAL_PART 0
+#define PC_EBR_NEXT_PTR_PART 1
+
+#define PC_BIOS_BOOT_SIG 0xAA55
+
+#define PC_MBR_DISK_OFFSET 0
+#define PC_MBR_SIZE 512
+
+#define PART_ACTIVE_FLAG 0x1
+
+struct chs {
+ uint8_t head;
+ uint8_t sector;
+ uint8_t cylinder;
+} __attribute__((__packed__));
+
+/* 16 byte pc partition descriptor that sits in MBR and EPBR.
+ * Note: multi-byte entities have little-endian layout on disk */
+struct pc_partition {
+ uint8_t status; /* byte 0 */
+ struct chs start; /* bytes 1-3 */
+ uint8_t type; /* byte 4 */
+ struct chs end; /* bytes 5-7 */
+ uint32_t start_lba; /* bytes 8-11 */
+ uint32_t len_lba; /* bytes 12-15 */
+} __attribute__((__packed__));
+
+struct pc_boot_record {
+ uint8_t code[440]; /* bytes 0-439 */
+ uint32_t disk_sig; /* bytes 440-443 */
+ uint16_t pad; /* bytes 444-445 */
+ struct pc_partition ptable[PC_NUM_BOOT_RECORD_PARTS]; /* bytes 446-509 */
+ uint16_t mbr_sig; /* bytes 510-511 */
+} __attribute__((__packed__));
+
+struct part_info {
+ char *name;
+ uint8_t flags;
+ uint8_t type;
+ uint32_t len_kb; /* in 1K-bytes */
+ uint32_t start_lba; /* the LBA where this partition begins */
+};
+
+struct disk_info {
+ char *device;
+ uint8_t scheme;
+ int sect_size; /* expected sector size in bytes. MUST BE POWER OF 2 */
+ uint32_t skip_lba; /* in sectors (1 unit of LBA) */
+ uint32_t num_lba; /* the size of the disk in LBA units */
+ struct part_info *part_lst;
+ int num_parts;
+};
+
+struct write_list {
+ struct write_list *next;
+ loff_t offset;
+ uint32_t len;
+ uint8_t data[0];
+};
+
+
+struct write_list *alloc_wl(uint32_t data_len);
+void free_wl(struct write_list *item);
+struct write_list *wlist_add(struct write_list **lst, struct write_list *item);
+void wlist_free(struct write_list *lst);
+int wlist_commit(int fd, struct write_list *lst, int test);
+
+struct disk_info *load_diskconfig(const char *fn, char *path_override);
+int dump_disk_config(struct disk_info *dinfo);
+int apply_disk_config(struct disk_info *dinfo, int test);
+char *find_part_device(struct disk_info *dinfo, const char *name);
+int process_disk_config(struct disk_info *dinfo);
+struct part_info *find_part(struct disk_info *dinfo, const char *name);
+
+int write_raw_image(const char *dst, const char *src, loff_t offset, int test);
+
+/* For MBR partition schemes */
+struct write_list *config_mbr(struct disk_info *dinfo);
+char *find_mbr_part(struct disk_info *dinfo, const char *name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LIBS_DISKCONFIG_H */
diff --git a/include/log/event_tag_map.h b/include/log/event_tag_map.h
new file mode 100644
index 0000000..1653c61
--- /dev/null
+++ b/include/log/event_tag_map.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBS_CUTILS_EVENTTAGMAP_H
+#define _LIBS_CUTILS_EVENTTAGMAP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define EVENT_TAG_MAP_FILE "/system/etc/event-log-tags"
+
+struct EventTagMap;
+typedef struct EventTagMap EventTagMap;
+
+/*
+ * Open the specified file as an event log tag map.
+ *
+ * Returns NULL on failure.
+ */
+EventTagMap* android_openEventTagMap(const char* fileName);
+
+/*
+ * Close the map.
+ */
+void android_closeEventTagMap(EventTagMap* map);
+
+/*
+ * Look up a tag by index. Returns the tag string, or NULL if not found.
+ */
+const char* android_lookupEventTag(const EventTagMap* map, int tag);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*_LIBS_CUTILS_EVENTTAGMAP_H*/
diff --git a/include/log/log.h b/include/log/log.h
new file mode 100644
index 0000000..0b17574
--- /dev/null
+++ b/include/log/log.h
@@ -0,0 +1,629 @@
+/*
+ * Copyright (C) 2005-2014 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.
+ */
+
+//
+// C/C++ logging functions. See the logging documentation for API details.
+//
+// We'd like these to be available from C code (in case we import some from
+// somewhere), so this has a C interface.
+//
+// The output will be correct when the log file is shared between multiple
+// threads and/or multiple processes so long as the operating system
+// supports O_APPEND. These calls have mutex-protected data structures
+// and so are NOT reentrant. Do not use LOG in a signal handler.
+//
+#ifndef _LIBS_LOG_LOG_H
+#define _LIBS_LOG_LOG_H
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <log/logd.h>
+#include <log/uio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// ---------------------------------------------------------------------
+
+/*
+ * Normally we strip ALOGV (VERBOSE messages) from release builds.
+ * You can modify this (for example with "#define LOG_NDEBUG 0"
+ * at the top of your source file) to change that behavior.
+ */
+#ifndef LOG_NDEBUG
+#ifdef NDEBUG
+#define LOG_NDEBUG 1
+#else
+#define LOG_NDEBUG 0
+#endif
+#endif
+
+/*
+ * This is the local tag used for the following simplified
+ * logging macros. You can change this preprocessor definition
+ * before using the other macros to change the tag.
+ */
+#ifndef LOG_TAG
+#define LOG_TAG NULL
+#endif
+
+// ---------------------------------------------------------------------
+
+#ifndef __predict_false
+#define __predict_false(exp) __builtin_expect((exp) != 0, 0)
+#endif
+
+/*
+ * -DLINT_RLOG in sources that you want to enforce that all logging
+ * goes to the radio log buffer. If any logging goes to any of the other
+ * log buffers, there will be a compile or link error to highlight the
+ * problem. This is not a replacement for a full audit of the code since
+ * this only catches compiled code, not ifdef'd debug code. Options to
+ * defining this, either temporarily to do a spot check, or permanently
+ * to enforce, in all the communications trees; We have hopes to ensure
+ * that by supplying just the radio log buffer that the communications
+ * teams will have their one-stop shop for triaging issues.
+ */
+#ifndef LINT_RLOG
+
+/*
+ * Simplified macro to send a verbose log message using the current LOG_TAG.
+ */
+#ifndef ALOGV
+#define __ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
+#if LOG_NDEBUG
+#define ALOGV(...) do { if (0) { __ALOGV(__VA_ARGS__); } } while (0)
+#else
+#define ALOGV(...) __ALOGV(__VA_ARGS__)
+#endif
+#endif
+
+#ifndef ALOGV_IF
+#if LOG_NDEBUG
+#define ALOGV_IF(cond, ...) ((void)0)
+#else
+#define ALOGV_IF(cond, ...) \
+ ( (__predict_false(cond)) \
+ ? ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+#endif
+
+/*
+ * Simplified macro to send a debug log message using the current LOG_TAG.
+ */
+#ifndef ALOGD
+#define ALOGD(...) ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef ALOGD_IF
+#define ALOGD_IF(cond, ...) \
+ ( (__predict_false(cond)) \
+ ? ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send an info log message using the current LOG_TAG.
+ */
+#ifndef ALOGI
+#define ALOGI(...) ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef ALOGI_IF
+#define ALOGI_IF(cond, ...) \
+ ( (__predict_false(cond)) \
+ ? ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send a warning log message using the current LOG_TAG.
+ */
+#ifndef ALOGW
+#define ALOGW(...) ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef ALOGW_IF
+#define ALOGW_IF(cond, ...) \
+ ( (__predict_false(cond)) \
+ ? ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send an error log message using the current LOG_TAG.
+ */
+#ifndef ALOGE
+#define ALOGE(...) ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef ALOGE_IF
+#define ALOGE_IF(cond, ...) \
+ ( (__predict_false(cond)) \
+ ? ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+// ---------------------------------------------------------------------
+
+/*
+ * Conditional based on whether the current LOG_TAG is enabled at
+ * verbose priority.
+ */
+#ifndef IF_ALOGV
+#if LOG_NDEBUG
+#define IF_ALOGV() if (false)
+#else
+#define IF_ALOGV() IF_ALOG(LOG_VERBOSE, LOG_TAG)
+#endif
+#endif
+
+/*
+ * Conditional based on whether the current LOG_TAG is enabled at
+ * debug priority.
+ */
+#ifndef IF_ALOGD
+#define IF_ALOGD() IF_ALOG(LOG_DEBUG, LOG_TAG)
+#endif
+
+/*
+ * Conditional based on whether the current LOG_TAG is enabled at
+ * info priority.
+ */
+#ifndef IF_ALOGI
+#define IF_ALOGI() IF_ALOG(LOG_INFO, LOG_TAG)
+#endif
+
+/*
+ * Conditional based on whether the current LOG_TAG is enabled at
+ * warn priority.
+ */
+#ifndef IF_ALOGW
+#define IF_ALOGW() IF_ALOG(LOG_WARN, LOG_TAG)
+#endif
+
+/*
+ * Conditional based on whether the current LOG_TAG is enabled at
+ * error priority.
+ */
+#ifndef IF_ALOGE
+#define IF_ALOGE() IF_ALOG(LOG_ERROR, LOG_TAG)
+#endif
+
+
+// ---------------------------------------------------------------------
+
+/*
+ * Simplified macro to send a verbose system log message using the current LOG_TAG.
+ */
+#ifndef SLOGV
+#define __SLOGV(...) \
+ ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
+#if LOG_NDEBUG
+#define SLOGV(...) do { if (0) { __SLOGV(__VA_ARGS__); } } while (0)
+#else
+#define SLOGV(...) __SLOGV(__VA_ARGS__)
+#endif
+#endif
+
+#ifndef SLOGV_IF
+#if LOG_NDEBUG
+#define SLOGV_IF(cond, ...) ((void)0)
+#else
+#define SLOGV_IF(cond, ...) \
+ ( (__predict_false(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+#endif
+
+/*
+ * Simplified macro to send a debug system log message using the current LOG_TAG.
+ */
+#ifndef SLOGD
+#define SLOGD(...) \
+ ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef SLOGD_IF
+#define SLOGD_IF(cond, ...) \
+ ( (__predict_false(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send an info system log message using the current LOG_TAG.
+ */
+#ifndef SLOGI
+#define SLOGI(...) \
+ ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef SLOGI_IF
+#define SLOGI_IF(cond, ...) \
+ ( (__predict_false(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send a warning system log message using the current LOG_TAG.
+ */
+#ifndef SLOGW
+#define SLOGW(...) \
+ ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef SLOGW_IF
+#define SLOGW_IF(cond, ...) \
+ ( (__predict_false(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send an error system log message using the current LOG_TAG.
+ */
+#ifndef SLOGE
+#define SLOGE(...) \
+ ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef SLOGE_IF
+#define SLOGE_IF(cond, ...) \
+ ( (__predict_false(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+#endif /* !LINT_RLOG */
+
+// ---------------------------------------------------------------------
+
+/*
+ * Simplified macro to send a verbose radio log message using the current LOG_TAG.
+ */
+#ifndef RLOGV
+#define __RLOGV(...) \
+ ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
+#if LOG_NDEBUG
+#define RLOGV(...) do { if (0) { __RLOGV(__VA_ARGS__); } } while (0)
+#else
+#define RLOGV(...) __RLOGV(__VA_ARGS__)
+#endif
+#endif
+
+#ifndef RLOGV_IF
+#if LOG_NDEBUG
+#define RLOGV_IF(cond, ...) ((void)0)
+#else
+#define RLOGV_IF(cond, ...) \
+ ( (__predict_false(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+#endif
+
+/*
+ * Simplified macro to send a debug radio log message using the current LOG_TAG.
+ */
+#ifndef RLOGD
+#define RLOGD(...) \
+ ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef RLOGD_IF
+#define RLOGD_IF(cond, ...) \
+ ( (__predict_false(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send an info radio log message using the current LOG_TAG.
+ */
+#ifndef RLOGI
+#define RLOGI(...) \
+ ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef RLOGI_IF
+#define RLOGI_IF(cond, ...) \
+ ( (__predict_false(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send a warning radio log message using the current LOG_TAG.
+ */
+#ifndef RLOGW
+#define RLOGW(...) \
+ ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef RLOGW_IF
+#define RLOGW_IF(cond, ...) \
+ ( (__predict_false(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send an error radio log message using the current LOG_TAG.
+ */
+#ifndef RLOGE
+#define RLOGE(...) \
+ ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef RLOGE_IF
+#define RLOGE_IF(cond, ...) \
+ ( (__predict_false(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+
+// ---------------------------------------------------------------------
+
+/*
+ * Log a fatal error. If the given condition fails, this stops program
+ * execution like a normal assertion, but also generating the given message.
+ * It is NOT stripped from release builds. Note that the condition test
+ * is -inverted- from the normal assert() semantics.
+ */
+#ifndef LOG_ALWAYS_FATAL_IF
+#define LOG_ALWAYS_FATAL_IF(cond, ...) \
+ ( (__predict_false(cond)) \
+ ? ((void)android_printAssert(#cond, LOG_TAG, ## __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+#ifndef LOG_ALWAYS_FATAL
+#define LOG_ALWAYS_FATAL(...) \
+ ( ((void)android_printAssert(NULL, LOG_TAG, ## __VA_ARGS__)) )
+#endif
+
+/*
+ * Versions of LOG_ALWAYS_FATAL_IF and LOG_ALWAYS_FATAL that
+ * are stripped out of release builds.
+ */
+#if LOG_NDEBUG
+
+#ifndef LOG_FATAL_IF
+#define LOG_FATAL_IF(cond, ...) ((void)0)
+#endif
+#ifndef LOG_FATAL
+#define LOG_FATAL(...) ((void)0)
+#endif
+
+#else
+
+#ifndef LOG_FATAL_IF
+#define LOG_FATAL_IF(cond, ...) LOG_ALWAYS_FATAL_IF(cond, ## __VA_ARGS__)
+#endif
+#ifndef LOG_FATAL
+#define LOG_FATAL(...) LOG_ALWAYS_FATAL(__VA_ARGS__)
+#endif
+
+#endif
+
+/*
+ * Assertion that generates a log message when the assertion fails.
+ * Stripped out of release builds. Uses the current LOG_TAG.
+ */
+#ifndef ALOG_ASSERT
+#define ALOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ## __VA_ARGS__)
+//#define ALOG_ASSERT(cond) LOG_FATAL_IF(!(cond), "Assertion failed: " #cond)
+#endif
+
+// ---------------------------------------------------------------------
+
+/*
+ * Basic log message macro.
+ *
+ * Example:
+ * ALOG(LOG_WARN, NULL, "Failed with error %d", errno);
+ *
+ * The second argument may be NULL or "" to indicate the "global" tag.
+ */
+#ifndef ALOG
+#define ALOG(priority, tag, ...) \
+ LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__)
+#endif
+
+/*
+ * Log macro that allows you to specify a number for the priority.
+ */
+#ifndef LOG_PRI
+#define LOG_PRI(priority, tag, ...) \
+ android_printLog(priority, tag, __VA_ARGS__)
+#endif
+
+/*
+ * Log macro that allows you to pass in a varargs ("args" is a va_list).
+ */
+#ifndef LOG_PRI_VA
+#define LOG_PRI_VA(priority, tag, fmt, args) \
+ android_vprintLog(priority, NULL, tag, fmt, args)
+#endif
+
+/*
+ * Conditional given a desired logging priority and tag.
+ */
+#ifndef IF_ALOG
+#define IF_ALOG(priority, tag) \
+ if (android_testLog(ANDROID_##priority, tag))
+#endif
+
+// ---------------------------------------------------------------------
+
+/*
+ * Event logging.
+ */
+
+/*
+ * Event log entry types. These must match up with the declarations in
+ * java/android/android/util/EventLog.java.
+ */
+typedef enum {
+ EVENT_TYPE_INT = 0,
+ EVENT_TYPE_LONG = 1,
+ EVENT_TYPE_STRING = 2,
+ EVENT_TYPE_LIST = 3,
+ EVENT_TYPE_FLOAT = 4,
+} AndroidEventLogType;
+#define sizeof_AndroidEventLogType sizeof(typeof_AndroidEventLogType)
+#define typeof_AndroidEventLogType unsigned char
+
+#ifndef LOG_EVENT_INT
+#define LOG_EVENT_INT(_tag, _value) { \
+ int intBuf = _value; \
+ (void) android_btWriteLog(_tag, EVENT_TYPE_INT, &intBuf, \
+ sizeof(intBuf)); \
+ }
+#endif
+#ifndef LOG_EVENT_LONG
+#define LOG_EVENT_LONG(_tag, _value) { \
+ long long longBuf = _value; \
+ (void) android_btWriteLog(_tag, EVENT_TYPE_LONG, &longBuf, \
+ sizeof(longBuf)); \
+ }
+#endif
+#ifndef LOG_EVENT_FLOAT
+#define LOG_EVENT_FLOAT(_tag, _value) { \
+ float floatBuf = _value; \
+ (void) android_btWriteLog(_tag, EVENT_TYPE_FLOAT, &floatBuf, \
+ sizeof(floatBuf)); \
+ }
+#endif
+#ifndef LOG_EVENT_STRING
+#define LOG_EVENT_STRING(_tag, _value) \
+ (void) __android_log_bswrite(_tag, _value);
+#endif
+/* TODO: something for LIST */
+
+/*
+ * ===========================================================================
+ *
+ * The stuff in the rest of this file should not be used directly.
+ */
+
+#define android_printLog(prio, tag, fmt...) \
+ __android_log_print(prio, tag, fmt)
+
+#define android_vprintLog(prio, cond, tag, fmt...) \
+ __android_log_vprint(prio, tag, fmt)
+
+/* XXX Macros to work around syntax errors in places where format string
+ * arg is not passed to ALOG_ASSERT, LOG_ALWAYS_FATAL or LOG_ALWAYS_FATAL_IF
+ * (happens only in debug builds).
+ */
+
+/* Returns 2nd arg. Used to substitute default value if caller's vararg list
+ * is empty.
+ */
+#define __android_second(dummy, second, ...) second
+
+/* If passed multiple args, returns ',' followed by all but 1st arg, otherwise
+ * returns nothing.
+ */
+#define __android_rest(first, ...) , ## __VA_ARGS__
+
+#define android_printAssert(cond, tag, fmt...) \
+ __android_log_assert(cond, tag, \
+ __android_second(0, ## fmt, NULL) __android_rest(fmt))
+
+#define android_writeLog(prio, tag, text) \
+ __android_log_write(prio, tag, text)
+
+#define android_bWriteLog(tag, payload, len) \
+ __android_log_bwrite(tag, payload, len)
+#define android_btWriteLog(tag, type, payload, len) \
+ __android_log_btwrite(tag, type, payload, len)
+
+/*
+ * IF_ALOG uses android_testLog, but IF_ALOG can be overridden.
+ * android_testLog will remain constant in its purpose as a wrapper
+ * for Android logging filter policy, and can be subject to
+ * change. It can be reused by the developers that override
+ * IF_ALOG as a convenient means to reimplement their policy
+ * over Android.
+ */
+#if LOG_NDEBUG /* Production */
+#define android_testLog(prio, tag) \
+ (__android_log_is_loggable(prio, tag, ANDROID_LOG_DEBUG) != 0)
+#else
+#define android_testLog(prio, tag) \
+ (__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE) != 0)
+#endif
+
+// TODO: remove these prototypes and their users
+#define android_writevLog(vec,num) do{}while(0)
+#define android_write1Log(str,len) do{}while (0)
+#define android_setMinPriority(tag, prio) do{}while(0)
+//#define android_logToCallback(func) do{}while(0)
+#define android_logToFile(tag, file) (0)
+#define android_logToFd(tag, fd) (0)
+
+typedef enum log_id {
+ LOG_ID_MIN = 0,
+
+#ifndef LINT_RLOG
+ LOG_ID_MAIN = 0,
+#endif
+ LOG_ID_RADIO = 1,
+#ifndef LINT_RLOG
+ LOG_ID_EVENTS = 2,
+ LOG_ID_SYSTEM = 3,
+ LOG_ID_CRASH = 4,
+ LOG_ID_KERNEL = 5,
+#endif
+
+ LOG_ID_MAX
+} log_id_t;
+#define sizeof_log_id_t sizeof(typeof_log_id_t)
+#define typeof_log_id_t unsigned char
+
+/*
+ * Use the per-tag properties "log.tag.<tagname>" to generate a runtime
+ * result of non-zero to expose a log.
+ */
+int __android_log_is_loggable(int prio, const char *tag, int def);
+
+/*
+ * Send a simple string to the log.
+ */
+int __android_log_buf_write(int bufID, int prio, const char *tag, const char *text);
+int __android_log_buf_print(int bufID, int prio, const char *tag, const char *fmt, ...)
+#if defined(__GNUC__)
+ __attribute__((__format__(printf, 4, 5)))
+#endif
+ ;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBS_LOG_LOG_H */
diff --git a/include/log/log_read.h b/include/log/log_read.h
new file mode 100644
index 0000000..1b70aff
--- /dev/null
+++ b/include/log/log_read.h
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2013-2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBS_LOG_LOG_READ_H
+#define _LIBS_LOG_LOG_READ_H
+
+#include <stdint.h>
+#include <time.h>
+
+/* struct log_time is a wire-format variant of struct timespec */
+#define NS_PER_SEC 1000000000ULL
+
+#ifdef __cplusplus
+
+// NB: do NOT define a copy constructor. This will result in structure
+// no longer being compatible with pass-by-value which is desired
+// efficient behavior. Also, pass-by-reference breaks C/C++ ABI.
+struct log_time {
+public:
+ uint32_t tv_sec; // good to Feb 5 2106
+ uint32_t tv_nsec;
+
+ static const uint32_t tv_sec_max = 0xFFFFFFFFUL;
+ static const uint32_t tv_nsec_max = 999999999UL;
+
+ log_time(const timespec &T)
+ {
+ tv_sec = T.tv_sec;
+ tv_nsec = T.tv_nsec;
+ }
+ log_time(uint32_t sec, uint32_t nsec)
+ {
+ tv_sec = sec;
+ tv_nsec = nsec;
+ }
+ static const timespec EPOCH;
+ log_time()
+ {
+ }
+ log_time(clockid_t id)
+ {
+ timespec T;
+ clock_gettime(id, &T);
+ tv_sec = T.tv_sec;
+ tv_nsec = T.tv_nsec;
+ }
+ log_time(const char *T)
+ {
+ const uint8_t *c = (const uint8_t *) T;
+ tv_sec = c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24);
+ tv_nsec = c[4] | (c[5] << 8) | (c[6] << 16) | (c[7] << 24);
+ }
+
+ // timespec
+ bool operator== (const timespec &T) const
+ {
+ return (tv_sec == static_cast<uint32_t>(T.tv_sec))
+ && (tv_nsec == static_cast<uint32_t>(T.tv_nsec));
+ }
+ bool operator!= (const timespec &T) const
+ {
+ return !(*this == T);
+ }
+ bool operator< (const timespec &T) const
+ {
+ return (tv_sec < static_cast<uint32_t>(T.tv_sec))
+ || ((tv_sec == static_cast<uint32_t>(T.tv_sec))
+ && (tv_nsec < static_cast<uint32_t>(T.tv_nsec)));
+ }
+ bool operator>= (const timespec &T) const
+ {
+ return !(*this < T);
+ }
+ bool operator> (const timespec &T) const
+ {
+ return (tv_sec > static_cast<uint32_t>(T.tv_sec))
+ || ((tv_sec == static_cast<uint32_t>(T.tv_sec))
+ && (tv_nsec > static_cast<uint32_t>(T.tv_nsec)));
+ }
+ bool operator<= (const timespec &T) const
+ {
+ return !(*this > T);
+ }
+ log_time operator-= (const timespec &T);
+ log_time operator- (const timespec &T) const
+ {
+ log_time local(*this);
+ return local -= T;
+ }
+ log_time operator+= (const timespec &T);
+ log_time operator+ (const timespec &T) const
+ {
+ log_time local(*this);
+ return local += T;
+ }
+
+ // log_time
+ bool operator== (const log_time &T) const
+ {
+ return (tv_sec == T.tv_sec) && (tv_nsec == T.tv_nsec);
+ }
+ bool operator!= (const log_time &T) const
+ {
+ return !(*this == T);
+ }
+ bool operator< (const log_time &T) const
+ {
+ return (tv_sec < T.tv_sec)
+ || ((tv_sec == T.tv_sec) && (tv_nsec < T.tv_nsec));
+ }
+ bool operator>= (const log_time &T) const
+ {
+ return !(*this < T);
+ }
+ bool operator> (const log_time &T) const
+ {
+ return (tv_sec > T.tv_sec)
+ || ((tv_sec == T.tv_sec) && (tv_nsec > T.tv_nsec));
+ }
+ bool operator<= (const log_time &T) const
+ {
+ return !(*this > T);
+ }
+ log_time operator-= (const log_time &T);
+ log_time operator- (const log_time &T) const
+ {
+ log_time local(*this);
+ return local -= T;
+ }
+ log_time operator+= (const log_time &T);
+ log_time operator+ (const log_time &T) const
+ {
+ log_time local(*this);
+ return local += T;
+ }
+
+ uint64_t nsec() const
+ {
+ return static_cast<uint64_t>(tv_sec) * NS_PER_SEC + tv_nsec;
+ }
+
+ static const char default_format[];
+
+ // Add %#q for the fraction of a second to the standard library functions
+ char *strptime(const char *s, const char *format = default_format);
+} __attribute__((__packed__));
+
+#else
+
+typedef struct log_time {
+ uint32_t tv_sec;
+ uint32_t tv_nsec;
+} __attribute__((__packed__)) log_time;
+
+#endif
+
+#endif /* define _LIBS_LOG_LOG_READ_H */
diff --git a/include/log/logd.h b/include/log/logd.h
new file mode 100644
index 0000000..0fe515f
--- /dev/null
+++ b/include/log/logd.h
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+#ifndef _ANDROID_CUTILS_LOGD_H
+#define _ANDROID_CUTILS_LOGD_H
+
+/* the stable/frozen log-related definitions have been
+ * moved to this header, which is exposed by the NDK
+ */
+#include <android/log.h>
+
+/* the rest is only used internally by the system */
+#if !defined(_WIN32)
+#include <pthread.h>
+#endif
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <log/uio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int __android_log_bwrite(int32_t tag, const void *payload, size_t len);
+int __android_log_btwrite(int32_t tag, char type, const void *payload,
+ size_t len);
+int __android_log_bswrite(int32_t tag, const char *payload);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LOGD_H */
diff --git a/include/log/logger.h b/include/log/logger.h
new file mode 100644
index 0000000..f030dab
--- /dev/null
+++ b/include/log/logger.h
@@ -0,0 +1,196 @@
+/*
+**
+** Copyright 2007-2014, The Android Open Source Project
+**
+** This file is dual licensed. It may be redistributed and/or modified
+** under the terms of the Apache 2.0 License OR version 2 of the GNU
+** General Public License.
+*/
+
+#ifndef _LIBS_LOG_LOGGER_H
+#define _LIBS_LOG_LOGGER_H
+
+#include <stdint.h>
+#include <log/log.h>
+#include <log/log_read.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * The userspace structure for version 1 of the logger_entry ABI.
+ * This structure is returned to userspace by the kernel logger
+ * driver unless an upgrade to a newer ABI version is requested.
+ */
+struct logger_entry {
+ uint16_t len; /* length of the payload */
+ uint16_t __pad; /* no matter what, we get 2 bytes of padding */
+ int32_t pid; /* generating process's pid */
+ int32_t tid; /* generating process's tid */
+ int32_t sec; /* seconds since Epoch */
+ int32_t nsec; /* nanoseconds */
+ char msg[0]; /* the entry's payload */
+} __attribute__((__packed__));
+
+/*
+ * The userspace structure for version 2 of the logger_entry ABI.
+ * This structure is returned to userspace if ioctl(LOGGER_SET_VERSION)
+ * is called with version==2; or used with the user space log daemon.
+ */
+struct logger_entry_v2 {
+ uint16_t len; /* length of the payload */
+ uint16_t hdr_size; /* sizeof(struct logger_entry_v2) */
+ int32_t pid; /* generating process's pid */
+ int32_t tid; /* generating process's tid */
+ int32_t sec; /* seconds since Epoch */
+ int32_t nsec; /* nanoseconds */
+ uint32_t euid; /* effective UID of logger */
+ char msg[0]; /* the entry's payload */
+} __attribute__((__packed__));
+
+struct logger_entry_v3 {
+ uint16_t len; /* length of the payload */
+ uint16_t hdr_size; /* sizeof(struct logger_entry_v3) */
+ int32_t pid; /* generating process's pid */
+ int32_t tid; /* generating process's tid */
+ int32_t sec; /* seconds since Epoch */
+ int32_t nsec; /* nanoseconds */
+ uint32_t lid; /* log id of the payload */
+ char msg[0]; /* the entry's payload */
+} __attribute__((__packed__));
+
+/*
+ * The maximum size of the log entry payload that can be
+ * written to the logger. An attempt to write more than
+ * this amount will result in a truncated log entry.
+ */
+#define LOGGER_ENTRY_MAX_PAYLOAD 4076
+
+/*
+ * The maximum size of a log entry which can be read from the
+ * kernel logger driver. An attempt to read less than this amount
+ * may result in read() returning EINVAL.
+ */
+#define LOGGER_ENTRY_MAX_LEN (5*1024)
+
+#define NS_PER_SEC 1000000000ULL
+
+struct log_msg {
+ union {
+ unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1];
+ struct logger_entry_v3 entry;
+ struct logger_entry_v3 entry_v3;
+ struct logger_entry_v2 entry_v2;
+ struct logger_entry entry_v1;
+ } __attribute__((aligned(4)));
+#ifdef __cplusplus
+ /* Matching log_time operators */
+ bool operator== (const log_msg &T) const
+ {
+ return (entry.sec == T.entry.sec) && (entry.nsec == T.entry.nsec);
+ }
+ bool operator!= (const log_msg &T) const
+ {
+ return !(*this == T);
+ }
+ bool operator< (const log_msg &T) const
+ {
+ return (entry.sec < T.entry.sec)
+ || ((entry.sec == T.entry.sec)
+ && (entry.nsec < T.entry.nsec));
+ }
+ bool operator>= (const log_msg &T) const
+ {
+ return !(*this < T);
+ }
+ bool operator> (const log_msg &T) const
+ {
+ return (entry.sec > T.entry.sec)
+ || ((entry.sec == T.entry.sec)
+ && (entry.nsec > T.entry.nsec));
+ }
+ bool operator<= (const log_msg &T) const
+ {
+ return !(*this > T);
+ }
+ uint64_t nsec() const
+ {
+ return static_cast<uint64_t>(entry.sec) * NS_PER_SEC + entry.nsec;
+ }
+
+ /* packet methods */
+ log_id_t id()
+ {
+ return (log_id_t) entry.lid;
+ }
+ char *msg()
+ {
+ return entry.hdr_size ? (char *) buf + entry.hdr_size : entry_v1.msg;
+ }
+ unsigned int len()
+ {
+ return (entry.hdr_size ? entry.hdr_size : sizeof(entry_v1)) + entry.len;
+ }
+#endif
+};
+
+struct logger;
+
+log_id_t android_logger_get_id(struct logger *logger);
+
+int android_logger_clear(struct logger *logger);
+long android_logger_get_log_size(struct logger *logger);
+int android_logger_set_log_size(struct logger *logger, unsigned long size);
+long android_logger_get_log_readable_size(struct logger *logger);
+int android_logger_get_log_version(struct logger *logger);
+
+struct logger_list;
+
+ssize_t android_logger_get_statistics(struct logger_list *logger_list,
+ char *buf, size_t len);
+ssize_t android_logger_get_prune_list(struct logger_list *logger_list,
+ char *buf, size_t len);
+int android_logger_set_prune_list(struct logger_list *logger_list,
+ char *buf, size_t len);
+
+#define ANDROID_LOG_RDONLY O_RDONLY
+#define ANDROID_LOG_WRONLY O_WRONLY
+#define ANDROID_LOG_RDWR O_RDWR
+#define ANDROID_LOG_ACCMODE O_ACCMODE
+#define ANDROID_LOG_NONBLOCK O_NONBLOCK
+#define ANDROID_LOG_PSTORE 0x80000000
+
+struct logger_list *android_logger_list_alloc(int mode,
+ unsigned int tail,
+ pid_t pid);
+struct logger_list *android_logger_list_alloc_time(int mode,
+ log_time start,
+ pid_t pid);
+void android_logger_list_free(struct logger_list *logger_list);
+/* In the purest sense, the following two are orthogonal interfaces */
+int android_logger_list_read(struct logger_list *logger_list,
+ struct log_msg *log_msg);
+
+/* Multiple log_id_t opens */
+struct logger *android_logger_open(struct logger_list *logger_list,
+ log_id_t id);
+#define android_logger_close android_logger_free
+/* Single log_id_t open */
+struct logger_list *android_logger_list_open(log_id_t id,
+ int mode,
+ unsigned int tail,
+ pid_t pid);
+#define android_logger_list_close android_logger_list_free
+
+/*
+ * log_id_t helpers
+ */
+log_id_t android_name_to_log_id(const char *logName);
+const char *android_log_id_to_name(log_id_t log_id);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBS_LOG_LOGGER_H */
diff --git a/include/log/logprint.h b/include/log/logprint.h
new file mode 100644
index 0000000..4b812cc
--- /dev/null
+++ b/include/log/logprint.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LOGPRINT_H
+#define _LOGPRINT_H
+
+#include <log/log.h>
+#include <log/logger.h>
+#include <log/event_tag_map.h>
+#include <pthread.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+ FORMAT_OFF = 0,
+ FORMAT_BRIEF,
+ FORMAT_PROCESS,
+ FORMAT_TAG,
+ FORMAT_THREAD,
+ FORMAT_RAW,
+ FORMAT_TIME,
+ FORMAT_THREADTIME,
+ FORMAT_LONG,
+ /* The following three are modifiers to above formats */
+ FORMAT_MODIFIER_COLOR, /* converts priority to color */
+ FORMAT_MODIFIER_TIME_USEC, /* switches from msec to usec time precision */
+ FORMAT_MODIFIER_PRINTABLE, /* converts non-printable to printable escapes */
+} AndroidLogPrintFormat;
+
+typedef struct AndroidLogFormat_t AndroidLogFormat;
+
+typedef struct AndroidLogEntry_t {
+ time_t tv_sec;
+ long tv_nsec;
+ android_LogPriority priority;
+ int32_t pid;
+ int32_t tid;
+ const char * tag;
+ size_t messageLen;
+ const char * message;
+} AndroidLogEntry;
+
+AndroidLogFormat *android_log_format_new();
+
+void android_log_format_free(AndroidLogFormat *p_format);
+
+/* currently returns 0 if format is a modifier, 1 if not */
+int android_log_setPrintFormat(AndroidLogFormat *p_format,
+ AndroidLogPrintFormat format);
+
+/**
+ * Returns FORMAT_OFF on invalid string
+ */
+AndroidLogPrintFormat android_log_formatFromString(const char *s);
+
+/**
+ * filterExpression: a single filter expression
+ * eg "AT:d"
+ *
+ * returns 0 on success and -1 on invalid expression
+ *
+ * Assumes single threaded execution
+ *
+ */
+
+int android_log_addFilterRule(AndroidLogFormat *p_format,
+ const char *filterExpression);
+
+
+/**
+ * filterString: a whitespace-separated set of filter expressions
+ * eg "AT:d *:i"
+ *
+ * returns 0 on success and -1 on invalid expression
+ *
+ * Assumes single threaded execution
+ *
+ */
+
+int android_log_addFilterString(AndroidLogFormat *p_format,
+ const char *filterString);
+
+
+/**
+ * returns 1 if this log line should be printed based on its priority
+ * and tag, and 0 if it should not
+ */
+int android_log_shouldPrintLine (
+ AndroidLogFormat *p_format, const char *tag, android_LogPriority pri);
+
+
+/**
+ * Splits a wire-format buffer into an AndroidLogEntry
+ * entry allocated by caller. Pointers will point directly into buf
+ *
+ * Returns 0 on success and -1 on invalid wire format (entry will be
+ * in unspecified state)
+ */
+int android_log_processLogBuffer(struct logger_entry *buf,
+ AndroidLogEntry *entry);
+
+/**
+ * Like android_log_processLogBuffer, but for binary logs.
+ *
+ * If "map" is non-NULL, it will be used to convert the log tag number
+ * into a string.
+ */
+int android_log_processBinaryLogBuffer(struct logger_entry *buf,
+ AndroidLogEntry *entry, const EventTagMap* map, char* messageBuf,
+ int messageBufLen);
+
+
+/**
+ * Formats a log message into a buffer
+ *
+ * Uses defaultBuffer if it can, otherwise malloc()'s a new buffer
+ * If return value != defaultBuffer, caller must call free()
+ * Returns NULL on malloc error
+ */
+
+char *android_log_formatLogLine (
+ AndroidLogFormat *p_format,
+ char *defaultBuffer,
+ size_t defaultBufferSize,
+ const AndroidLogEntry *p_line,
+ size_t *p_outLength);
+
+
+/**
+ * Either print or do not print log line, based on filter
+ *
+ * Assumes single threaded execution
+ *
+ */
+int android_log_printLogLine(
+ AndroidLogFormat *p_format,
+ int fd,
+ const AndroidLogEntry *entry);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /*_LOGPRINT_H*/
diff --git a/include/log/uio.h b/include/log/uio.h
new file mode 100644
index 0000000..7059da5
--- /dev/null
+++ b/include/log/uio.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2007-2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBS_CUTILS_UIO_H
+#define _LIBS_CUTILS_UIO_H
+
+#if !defined(_WIN32)
+
+#include <sys/uio.h>
+
+#else
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//
+// Implementation of sys/uio.h for Win32.
+//
+
+#include <stddef.h>
+
+struct iovec {
+ void* iov_base;
+ size_t iov_len;
+};
+
+extern int readv( int fd, struct iovec* vecs, int count );
+extern int writev( int fd, const struct iovec* vecs, int count );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+#endif /* _LIBS_UTILS_UIO_H */
+
diff --git a/include/memtrack/memtrack.h b/include/memtrack/memtrack.h
new file mode 100644
index 0000000..3917300
--- /dev/null
+++ b/include/memtrack/memtrack.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBMEMTRACK_MEMTRACK_H_
+#define _LIBMEMTRACK_MEMTRACK_H_
+
+#include <sys/types.h>
+#include <stddef.h>
+#include <cutils/compiler.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * struct memtrack_proc
+ *
+ * an opaque handle to the memory stats on a process.
+ * Created with memtrack_proc_new, destroyed by
+ * memtrack_proc_destroy. Can be reused multiple times with
+ * memtrack_proc_get.
+ */
+struct memtrack_proc;
+
+/**
+ * memtrack_init
+ *
+ * Must be called once before calling any other functions. After this function
+ * is called, everything else is thread-safe.
+ *
+ * Returns 0 on success, -errno on error.
+ */
+int memtrack_init(void);
+
+/**
+ * memtrack_proc_new
+ *
+ * Return a new handle to hold process memory stats.
+ *
+ * Returns NULL on error.
+ */
+struct memtrack_proc *memtrack_proc_new(void);
+
+/**
+ * memtrack_proc_destroy
+ *
+ * Free all memory associated with a process memory stats handle.
+ */
+void memtrack_proc_destroy(struct memtrack_proc *p);
+
+/**
+ * memtrack_proc_get
+ *
+ * Fill a process memory stats handle with data about the given pid. Can be
+ * called on a handle that was just allocated with memtrack_proc_new,
+ * or on a handle that has been previously passed to memtrack_proc_get
+ * to replace the data with new data on the same or another process. It is
+ * expected that the second call on the same handle should not require
+ * allocating any new memory.
+ *
+ * Returns 0 on success, -errno on error.
+ */
+int memtrack_proc_get(struct memtrack_proc *p, pid_t pid);
+
+/**
+ * memtrack_proc_graphics_total
+ *
+ * Return total amount of memory that has been allocated for use as window
+ * buffers. Does not differentiate between memory that has already been
+ * accounted for by reading /proc/pid/smaps and memory that has not been
+ * accounted for.
+ *
+ * Returns non-negative size in bytes on success, -errno on error.
+ */
+ssize_t memtrack_proc_graphics_total(struct memtrack_proc *p);
+
+/**
+ * memtrack_proc_graphics_pss
+ *
+ * Return total amount of memory that has been allocated for use as window
+ * buffers, but has not already been accounted for by reading /proc/pid/smaps.
+ * Memory that is shared across processes may already be divided by the
+ * number of processes that share it (preferred), or may be charged in full to
+ * every process that shares it, depending on the capabilities of the driver.
+ *
+ * Returns non-negative size in bytes on success, -errno on error.
+ */
+ssize_t memtrack_proc_graphics_pss(struct memtrack_proc *p);
+
+/**
+ * memtrack_proc_gl_total
+ *
+ * Same as memtrack_proc_graphics_total, but counts GL memory (which
+ * should not overlap with graphics memory) instead of graphics memory.
+ *
+ * Returns non-negative size in bytes on success, -errno on error.
+ */
+ssize_t memtrack_proc_gl_total(struct memtrack_proc *p);
+
+/**
+ * memtrack_proc_gl_pss
+ *
+ * Same as memtrack_proc_graphics_total, but counts GL memory (which
+ * should not overlap with graphics memory) instead of graphics memory.
+ *
+ * Returns non-negative size in bytes on success, -errno on error.
+ */
+ssize_t memtrack_proc_gl_pss(struct memtrack_proc *p);
+
+/**
+ * memtrack_proc_other_total
+ *
+ * Same as memtrack_proc_graphics_total, but counts miscellaneous memory
+ * not tracked by gl or graphics calls above.
+ *
+ * Returns non-negative size in bytes on success, -errno on error.
+ */
+ssize_t memtrack_proc_other_total(struct memtrack_proc *p);
+
+/**
+ * memtrack_proc_other_pss
+ *
+ * Same as memtrack_proc_graphics_total, but counts miscellaneous memory
+ * not tracked by gl or graphics calls above.
+ *
+ * Returns non-negative size in bytes on success, -errno on error.
+ */
+ssize_t memtrack_proc_other_pss(struct memtrack_proc *p);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/mincrypt/dsa_sig.h b/include/mincrypt/dsa_sig.h
new file mode 100644
index 0000000..b0d91cd
--- /dev/null
+++ b/include/mincrypt/dsa_sig.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Google Inc. nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_DSA_SIG_H_
+#define SYSTEM_CORE_INCLUDE_MINCRYPT_DSA_SIG_H_
+
+#include "mincrypt/p256.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Returns 0 if input sig is not a valid ASN.1 sequence
+int dsa_sig_unpack(unsigned char* sig, int sig_len, p256_int* r_int, p256_int* s_int);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SYSTEM_CORE_INCLUDE_MINCRYPT_DSA_SIG_H_ */
diff --git a/include/mincrypt/hash-internal.h b/include/mincrypt/hash-internal.h
new file mode 100644
index 0000000..c813b44
--- /dev/null
+++ b/include/mincrypt/hash-internal.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2007 The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Google Inc. nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_HASH_INTERNAL_H_
+#define SYSTEM_CORE_INCLUDE_MINCRYPT_HASH_INTERNAL_H_
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+struct HASH_CTX; // forward decl
+
+typedef struct HASH_VTAB {
+ void (* const init)(struct HASH_CTX*);
+ void (* const update)(struct HASH_CTX*, const void*, int);
+ const uint8_t* (* const final)(struct HASH_CTX*);
+ const uint8_t* (* const hash)(const void*, int, uint8_t*);
+ int size;
+} HASH_VTAB;
+
+typedef struct HASH_CTX {
+ const HASH_VTAB * f;
+ uint64_t count;
+ uint8_t buf[64];
+ uint32_t state[8]; // upto SHA2
+} HASH_CTX;
+
+#define HASH_init(ctx) (ctx)->f->init(ctx)
+#define HASH_update(ctx, data, len) (ctx)->f->update(ctx, data, len)
+#define HASH_final(ctx) (ctx)->f->final(ctx)
+#define HASH_hash(data, len, digest) (ctx)->f->hash(data, len, digest)
+#define HASH_size(ctx) (ctx)->f->size
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif // SYSTEM_CORE_INCLUDE_MINCRYPT_HASH_INTERNAL_H_
diff --git a/include/mincrypt/p256.h b/include/mincrypt/p256.h
new file mode 100644
index 0000000..465a1b9
--- /dev/null
+++ b/include/mincrypt/p256.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Google Inc. nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_LITE_P256_H_
+#define SYSTEM_CORE_INCLUDE_MINCRYPT_LITE_P256_H_
+
+// Collection of routines manipulating 256 bit unsigned integers.
+// Just enough to implement ecdsa-p256 and related algorithms.
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define P256_BITSPERDIGIT 32
+#define P256_NDIGITS 8
+#define P256_NBYTES 32
+
+typedef int p256_err;
+typedef uint32_t p256_digit;
+typedef int32_t p256_sdigit;
+typedef uint64_t p256_ddigit;
+typedef int64_t p256_sddigit;
+
+// Defining p256_int as struct to leverage struct assigment.
+typedef struct {
+ p256_digit a[P256_NDIGITS];
+} p256_int;
+
+extern const p256_int SECP256r1_n; // Curve order
+extern const p256_int SECP256r1_p; // Curve prime
+extern const p256_int SECP256r1_b; // Curve param
+
+// Initialize a p256_int to zero.
+void p256_init(p256_int* a);
+
+// Clear a p256_int to zero.
+void p256_clear(p256_int* a);
+
+// Return bit. Index 0 is least significant.
+int p256_get_bit(const p256_int* a, int index);
+
+// b := a % MOD
+void p256_mod(
+ const p256_int* MOD,
+ const p256_int* a,
+ p256_int* b);
+
+// c := a * (top_b | b) % MOD
+void p256_modmul(
+ const p256_int* MOD,
+ const p256_int* a,
+ const p256_digit top_b,
+ const p256_int* b,
+ p256_int* c);
+
+// b := 1 / a % MOD
+// MOD best be SECP256r1_n
+void p256_modinv(
+ const p256_int* MOD,
+ const p256_int* a,
+ p256_int* b);
+
+// b := 1 / a % MOD
+// MOD best be SECP256r1_n
+// Faster than p256_modinv()
+void p256_modinv_vartime(
+ const p256_int* MOD,
+ const p256_int* a,
+ p256_int* b);
+
+// b := a << (n % P256_BITSPERDIGIT)
+// Returns the bits shifted out of most significant digit.
+p256_digit p256_shl(const p256_int* a, int n, p256_int* b);
+
+// b := a >> (n % P256_BITSPERDIGIT)
+void p256_shr(const p256_int* a, int n, p256_int* b);
+
+int p256_is_zero(const p256_int* a);
+int p256_is_odd(const p256_int* a);
+int p256_is_even(const p256_int* a);
+
+// Returns -1, 0 or 1.
+int p256_cmp(const p256_int* a, const p256_int *b);
+
+// c: = a - b
+// Returns -1 on borrow.
+int p256_sub(const p256_int* a, const p256_int* b, p256_int* c);
+
+// c := a + b
+// Returns 1 on carry.
+int p256_add(const p256_int* a, const p256_int* b, p256_int* c);
+
+// c := a + (single digit)b
+// Returns carry 1 on carry.
+int p256_add_d(const p256_int* a, p256_digit b, p256_int* c);
+
+// ec routines.
+
+// {out_x,out_y} := nG
+void p256_base_point_mul(const p256_int *n,
+ p256_int *out_x,
+ p256_int *out_y);
+
+// {out_x,out_y} := n{in_x,in_y}
+void p256_point_mul(const p256_int *n,
+ const p256_int *in_x,
+ const p256_int *in_y,
+ p256_int *out_x,
+ p256_int *out_y);
+
+// {out_x,out_y} := n1G + n2{in_x,in_y}
+void p256_points_mul_vartime(
+ const p256_int *n1, const p256_int *n2,
+ const p256_int *in_x, const p256_int *in_y,
+ p256_int *out_x, p256_int *out_y);
+
+// Return whether point {x,y} is on curve.
+int p256_is_valid_point(const p256_int* x, const p256_int* y);
+
+// Outputs big-endian binary form. No leading zero skips.
+void p256_to_bin(const p256_int* src, uint8_t dst[P256_NBYTES]);
+
+// Reads from big-endian binary form,
+// thus pre-pad with leading zeros if short.
+void p256_from_bin(const uint8_t src[P256_NBYTES], p256_int* dst);
+
+#define P256_DIGITS(x) ((x)->a)
+#define P256_DIGIT(x,y) ((x)->a[y])
+
+#define P256_ZERO {{0}}
+#define P256_ONE {{1}}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // SYSTEM_CORE_INCLUDE_MINCRYPT_LITE_P256_H_
diff --git a/include/mincrypt/p256_ecdsa.h b/include/mincrypt/p256_ecdsa.h
new file mode 100644
index 0000000..da339fa
--- /dev/null
+++ b/include/mincrypt/p256_ecdsa.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Google Inc. nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_P256_ECDSA_H_
+#define SYSTEM_CORE_INCLUDE_MINCRYPT_P256_ECDSA_H_
+
+// Using current directory as relative include path here since
+// this code typically gets lifted into a variety of build systems
+// and directory structures.
+#include "p256.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Returns 0 if {r,s} is not a signature on message for
+// public key {key_x,key_y}.
+//
+// Note: message is a p256_int.
+// Convert from a binary string using p256_from_bin().
+int p256_ecdsa_verify(const p256_int* key_x,
+ const p256_int* key_y,
+ const p256_int* message,
+ const p256_int* r, const p256_int* s);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // SYSTEM_CORE_INCLUDE_MINCRYPT_P256_ECDSA_H_
diff --git a/include/mincrypt/rsa.h b/include/mincrypt/rsa.h
new file mode 100644
index 0000000..3d0556b
--- /dev/null
+++ b/include/mincrypt/rsa.h
@@ -0,0 +1,58 @@
+/* rsa.h
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+** * Neither the name of Google Inc. nor the names of its contributors may
+** be used to endorse or promote products derived from this software
+** without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR
+** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+** EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_RSA_H_
+#define SYSTEM_CORE_INCLUDE_MINCRYPT_RSA_H_
+
+#include <inttypes.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define RSANUMBYTES 256 /* 2048 bit key length */
+#define RSANUMWORDS (RSANUMBYTES / sizeof(uint32_t))
+
+typedef struct RSAPublicKey {
+ int len; /* Length of n[] in number of uint32_t */
+ uint32_t n0inv; /* -1 / n[0] mod 2^32 */
+ uint32_t n[RSANUMWORDS]; /* modulus as little endian array */
+ uint32_t rr[RSANUMWORDS]; /* R^2 as little endian array */
+ int exponent; /* 3 or 65537 */
+} RSAPublicKey;
+
+int RSA_verify(const RSAPublicKey *key,
+ const uint8_t* signature,
+ const int len,
+ const uint8_t* hash,
+ const int hash_len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // SYSTEM_CORE_INCLUDE_MINCRYPT_RSA_H_
diff --git a/include/mincrypt/sha.h b/include/mincrypt/sha.h
new file mode 100644
index 0000000..ef60aab
--- /dev/null
+++ b/include/mincrypt/sha.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2005 The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Google Inc. nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_SHA1_H_
+#define SYSTEM_CORE_INCLUDE_MINCRYPT_SHA1_H_
+
+#include <stdint.h>
+#include "hash-internal.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+typedef HASH_CTX SHA_CTX;
+
+void SHA_init(SHA_CTX* ctx);
+void SHA_update(SHA_CTX* ctx, const void* data, int len);
+const uint8_t* SHA_final(SHA_CTX* ctx);
+
+// Convenience method. Returns digest address.
+// NOTE: *digest needs to hold SHA_DIGEST_SIZE bytes.
+const uint8_t* SHA_hash(const void* data, int len, uint8_t* digest);
+
+#define SHA_DIGEST_SIZE 20
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif // SYSTEM_CORE_INCLUDE_MINCRYPT_SHA1_H_
diff --git a/include/mincrypt/sha256.h b/include/mincrypt/sha256.h
new file mode 100644
index 0000000..3a87c31
--- /dev/null
+++ b/include/mincrypt/sha256.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2011 The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Google Inc. nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_SHA256_H_
+#define SYSTEM_CORE_INCLUDE_MINCRYPT_SHA256_H_
+
+#include <stdint.h>
+#include "hash-internal.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+typedef HASH_CTX SHA256_CTX;
+
+void SHA256_init(SHA256_CTX* ctx);
+void SHA256_update(SHA256_CTX* ctx, const void* data, int len);
+const uint8_t* SHA256_final(SHA256_CTX* ctx);
+
+// Convenience method. Returns digest address.
+const uint8_t* SHA256_hash(const void* data, int len, uint8_t* digest);
+
+#define SHA256_DIGEST_SIZE 32
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif // SYSTEM_CORE_INCLUDE_MINCRYPT_SHA256_H_
diff --git a/include/nativebridge/native_bridge.h b/include/nativebridge/native_bridge.h
new file mode 100644
index 0000000..18300bc
--- /dev/null
+++ b/include/nativebridge/native_bridge.h
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef NATIVE_BRIDGE_H_
+#define NATIVE_BRIDGE_H_
+
+#include "jni.h"
+#include <signal.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+namespace android {
+
+struct NativeBridgeRuntimeCallbacks;
+struct NativeBridgeRuntimeValues;
+
+// Function pointer type for sigaction. This is mostly the signature of a signal handler, except
+// for the return type. The runtime needs to know whether the signal was handled or should be given
+// to the chain.
+typedef bool (*NativeBridgeSignalHandlerFn)(int, siginfo_t*, void*);
+
+
+// Open the native bridge, if any. Should be called by Runtime::Init(). A null library filename
+// signals that we do not want to load a native bridge.
+bool LoadNativeBridge(const char* native_bridge_library_filename,
+ const NativeBridgeRuntimeCallbacks* runtime_callbacks);
+
+// Quick check whether a native bridge will be needed. This is based off of the instruction set
+// of the process.
+bool NeedsNativeBridge(const char* instruction_set);
+
+// Do the early initialization part of the native bridge, if necessary. This should be done under
+// high privileges.
+bool PreInitializeNativeBridge(const char* app_data_dir, const char* instruction_set);
+
+// Initialize the native bridge, if any. Should be called by Runtime::DidForkFromZygote. The JNIEnv*
+// will be used to modify the app environment for the bridge.
+bool InitializeNativeBridge(JNIEnv* env, const char* instruction_set);
+
+// Unload the native bridge, if any. Should be called by Runtime::DidForkFromZygote.
+void UnloadNativeBridge();
+
+// Check whether a native bridge is available (opened or initialized). Requires a prior call to
+// LoadNativeBridge.
+bool NativeBridgeAvailable();
+
+// Check whether a native bridge is available (initialized). Requires a prior call to
+// LoadNativeBridge & InitializeNativeBridge.
+bool NativeBridgeInitialized();
+
+// Load a shared library that is supported by the native bridge.
+void* NativeBridgeLoadLibrary(const char* libpath, int flag);
+
+// Get a native bridge trampoline for specified native method.
+void* NativeBridgeGetTrampoline(void* handle, const char* name, const char* shorty, uint32_t len);
+
+// True if native library is valid and is for an ABI that is supported by native bridge.
+bool NativeBridgeIsSupported(const char* libpath);
+
+// Returns the version number of the native bridge. This information is available after a
+// successful LoadNativeBridge() and before closing it, that is, as long as NativeBridgeAvailable()
+// returns true. Returns 0 otherwise.
+uint32_t NativeBridgeGetVersion();
+
+// Returns a signal handler that the bridge would like to be managed. Only valid for a native
+// bridge supporting the version 2 interface. Will return null if the bridge does not support
+// version 2, or if it doesn't have a signal handler it wants to be known.
+NativeBridgeSignalHandlerFn NativeBridgeGetSignalHandler(int signal);
+
+// Returns whether we have seen a native bridge error. This could happen because the library
+// was not found, rejected, could not be initialized and so on.
+//
+// This functionality is mainly for testing.
+bool NativeBridgeError();
+
+// Returns whether a given string is acceptable as a native bridge library filename.
+//
+// This functionality is exposed mainly for testing.
+bool NativeBridgeNameAcceptable(const char* native_bridge_library_filename);
+
+// Native bridge interfaces to runtime.
+struct NativeBridgeCallbacks {
+ // Version number of the interface.
+ uint32_t version;
+
+ // Initialize native bridge. Native bridge's internal implementation must ensure MT safety and
+ // that the native bridge is initialized only once. Thus it is OK to call this interface for an
+ // already initialized native bridge.
+ //
+ // Parameters:
+ // runtime_cbs [IN] the pointer to NativeBridgeRuntimeCallbacks.
+ // Returns:
+ // true iff initialization was successful.
+ bool (*initialize)(const NativeBridgeRuntimeCallbacks* runtime_cbs, const char* private_dir,
+ const char* instruction_set);
+
+ // Load a shared library that is supported by the native bridge.
+ //
+ // Parameters:
+ // libpath [IN] path to the shared library
+ // flag [IN] the stardard RTLD_XXX defined in bionic dlfcn.h
+ // Returns:
+ // The opaque handle of the shared library if sucessful, otherwise NULL
+ void* (*loadLibrary)(const char* libpath, int flag);
+
+ // Get a native bridge trampoline for specified native method. The trampoline has same
+ // sigature as the native method.
+ //
+ // Parameters:
+ // handle [IN] the handle returned from loadLibrary
+ // shorty [IN] short descriptor of native method
+ // len [IN] length of shorty
+ // Returns:
+ // address of trampoline if successful, otherwise NULL
+ void* (*getTrampoline)(void* handle, const char* name, const char* shorty, uint32_t len);
+
+ // Check whether native library is valid and is for an ABI that is supported by native bridge.
+ //
+ // Parameters:
+ // libpath [IN] path to the shared library
+ // Returns:
+ // TRUE if library is supported by native bridge, FALSE otherwise
+ bool (*isSupported)(const char* libpath);
+
+ // Provide environment values required by the app running with native bridge according to the
+ // instruction set.
+ //
+ // Parameters:
+ // instruction_set [IN] the instruction set of the app
+ // Returns:
+ // NULL if not supported by native bridge.
+ // Otherwise, return all environment values to be set after fork.
+ const struct NativeBridgeRuntimeValues* (*getAppEnv)(const char* instruction_set);
+
+ // Added callbacks in version 2.
+
+ // Check whether the bridge is compatible with the given version. A bridge may decide not to be
+ // forwards- or backwards-compatible, and libnativebridge will then stop using it.
+ //
+ // Parameters:
+ // bridge_version [IN] the version of libnativebridge.
+ // Returns:
+ // true iff the native bridge supports the given version of libnativebridge.
+ bool (*isCompatibleWith)(uint32_t bridge_version);
+
+ // A callback to retrieve a native bridge's signal handler for the specified signal. The runtime
+ // will ensure that the signal handler is being called after the runtime's own handler, but before
+ // all chained handlers. The native bridge should not try to install the handler by itself, as
+ // that will potentially lead to cycles.
+ //
+ // Parameters:
+ // signal [IN] the signal for which the handler is asked for. Currently, only SIGSEGV is
+ // supported by the runtime.
+ // Returns:
+ // NULL if the native bridge doesn't use a handler or doesn't want it to be managed by the
+ // runtime.
+ // Otherwise, a pointer to the signal handler.
+ NativeBridgeSignalHandlerFn (*getSignalHandler)(int signal);
+};
+
+// Runtime interfaces to native bridge.
+struct NativeBridgeRuntimeCallbacks {
+ // Get shorty of a Java method. The shorty is supposed to be persistent in memory.
+ //
+ // Parameters:
+ // env [IN] pointer to JNIenv.
+ // mid [IN] Java methodID.
+ // Returns:
+ // short descriptor for method.
+ const char* (*getMethodShorty)(JNIEnv* env, jmethodID mid);
+
+ // Get number of native methods for specified class.
+ //
+ // Parameters:
+ // env [IN] pointer to JNIenv.
+ // clazz [IN] Java class object.
+ // Returns:
+ // number of native methods.
+ uint32_t (*getNativeMethodCount)(JNIEnv* env, jclass clazz);
+
+ // Get at most 'method_count' native methods for specified class 'clazz'. Results are outputed
+ // via 'methods' [OUT]. The signature pointer in JNINativeMethod is reused as the method shorty.
+ //
+ // Parameters:
+ // env [IN] pointer to JNIenv.
+ // clazz [IN] Java class object.
+ // methods [OUT] array of method with the name, shorty, and fnPtr.
+ // method_count [IN] max number of elements in methods.
+ // Returns:
+ // number of method it actually wrote to methods.
+ uint32_t (*getNativeMethods)(JNIEnv* env, jclass clazz, JNINativeMethod* methods,
+ uint32_t method_count);
+};
+
+}; // namespace android
+
+#endif // NATIVE_BRIDGE_H_
diff --git a/include/netutils/dhcp.h b/include/netutils/dhcp.h
new file mode 100644
index 0000000..de6bc82
--- /dev/null
+++ b/include/netutils/dhcp.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _NETUTILS_DHCP_H_
+#define _NETUTILS_DHCP_H_
+
+#include <sys/cdefs.h>
+#include <arpa/inet.h>
+
+__BEGIN_DECLS
+
+extern int do_dhcp(char *iname);
+extern int dhcp_do_request(const char *ifname,
+ char *ipaddr,
+ char *gateway,
+ uint32_t *prefixLength,
+ char *dns[],
+ char *server,
+ uint32_t *lease,
+ char *vendorInfo,
+ char *domain,
+ char *mtu);
+extern int dhcp_do_request_renew(const char *ifname,
+ char *ipaddr,
+ char *gateway,
+ uint32_t *prefixLength,
+ char *dns[],
+ char *server,
+ uint32_t *lease,
+ char *vendorInfo,
+ char *domain,
+ char *mtu);
+extern int dhcp_stop(const char *ifname);
+extern int dhcp_release_lease(const char *ifname);
+extern char *dhcp_get_errmsg();
+
+__END_DECLS
+
+#endif /* _NETUTILS_DHCP_H_ */
diff --git a/include/netutils/ifc.h b/include/netutils/ifc.h
new file mode 100644
index 0000000..3b27234
--- /dev/null
+++ b/include/netutils/ifc.h
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#ifndef _NETUTILS_IFC_H_
+#define _NETUTILS_IFC_H_
+
+#include <sys/cdefs.h>
+#include <arpa/inet.h>
+
+__BEGIN_DECLS
+
+extern int ifc_init(void);
+extern void ifc_close(void);
+
+extern int ifc_get_ifindex(const char *name, int *if_indexp);
+extern int ifc_get_hwaddr(const char *name, void *ptr);
+
+extern int ifc_up(const char *name);
+extern int ifc_down(const char *name);
+
+extern int ifc_enable(const char *ifname);
+extern int ifc_disable(const char *ifname);
+
+#define RESET_IPV4_ADDRESSES 0x01
+#define RESET_IPV6_ADDRESSES 0x02
+#define RESET_IGNORE_INTERFACE_ADDRESS 0x04
+#define RESET_ALL_ADDRESSES (RESET_IPV4_ADDRESSES | RESET_IPV6_ADDRESSES)
+extern int ifc_reset_connections(const char *ifname, const int reset_mask);
+
+extern int ifc_get_addr(const char *name, in_addr_t *addr);
+extern int ifc_set_addr(const char *name, in_addr_t addr);
+extern int ifc_add_address(const char *name, const char *address,
+ int prefixlen);
+extern int ifc_del_address(const char *name, const char *address,
+ int prefixlen);
+extern int ifc_set_prefixLength(const char *name, int prefixLength);
+extern int ifc_set_hwaddr(const char *name, const void *ptr);
+extern int ifc_clear_addresses(const char *name);
+
+extern int ifc_create_default_route(const char *name, in_addr_t addr);
+extern int ifc_remove_default_route(const char *ifname);
+extern int ifc_get_info(const char *name, in_addr_t *addr, int *prefixLength,
+ unsigned *flags);
+
+extern int ifc_configure(const char *ifname, in_addr_t address,
+ uint32_t prefixLength, in_addr_t gateway,
+ in_addr_t dns1, in_addr_t dns2);
+
+extern in_addr_t prefixLengthToIpv4Netmask(int prefix_length);
+
+__END_DECLS
+
+#endif /* _NETUTILS_IFC_H_ */
diff --git a/include/private/android_filesystem_capability.h b/include/private/android_filesystem_capability.h
new file mode 100644
index 0000000..b92d3db
--- /dev/null
+++ b/include/private/android_filesystem_capability.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Taken from linux/capability.h, with minor modifications
+ */
+
+#ifndef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_FILESYSTEM_CAPABILITY_H
+#define _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_FILESYSTEM_CAPABILITY_H
+
+#include <stdint.h>
+
+#define __user
+#define __u32 uint32_t
+#define __le32 uint32_t
+
+#define _LINUX_CAPABILITY_VERSION_1 0x19980330
+#define _LINUX_CAPABILITY_U32S_1 1
+#define _LINUX_CAPABILITY_VERSION_2 0x20071026
+#define _LINUX_CAPABILITY_U32S_2 2
+#define _LINUX_CAPABILITY_VERSION_3 0x20080522
+#define _LINUX_CAPABILITY_U32S_3 2
+
+typedef struct __user_cap_header_struct {
+ __u32 version;
+ int pid;
+} __user *cap_user_header_t;
+
+typedef struct __user_cap_data_struct {
+ __u32 effective;
+ __u32 permitted;
+ __u32 inheritable;
+} __user *cap_user_data_t;
+
+#define VFS_CAP_REVISION_MASK 0xFF000000
+#define VFS_CAP_REVISION_SHIFT 24
+#define VFS_CAP_FLAGS_MASK ~VFS_CAP_REVISION_MASK
+#define VFS_CAP_FLAGS_EFFECTIVE 0x000001
+#define VFS_CAP_REVISION_1 0x01000000
+#define VFS_CAP_U32_1 1
+#define XATTR_CAPS_SZ_1 (sizeof(__le32)*(1 + 2*VFS_CAP_U32_1))
+#define VFS_CAP_REVISION_2 0x02000000
+#define VFS_CAP_U32_2 2
+#define XATTR_CAPS_SZ_2 (sizeof(__le32)*(1 + 2*VFS_CAP_U32_2))
+#define XATTR_CAPS_SZ XATTR_CAPS_SZ_2
+#define VFS_CAP_U32 VFS_CAP_U32_2
+#define VFS_CAP_REVISION VFS_CAP_REVISION_2
+
+struct vfs_cap_data {
+ __le32 magic_etc;
+ struct {
+ __le32 permitted;
+ __le32 inheritable;
+ } data[VFS_CAP_U32];
+};
+
+#define _LINUX_CAPABILITY_VERSION _LINUX_CAPABILITY_VERSION_1
+#define _LINUX_CAPABILITY_U32S _LINUX_CAPABILITY_U32S_1
+#define CAP_CHOWN 0
+#define CAP_DAC_OVERRIDE 1
+#define CAP_DAC_READ_SEARCH 2
+#define CAP_FOWNER 3
+#define CAP_FSETID 4
+#define CAP_KILL 5
+#define CAP_SETGID 6
+#define CAP_SETUID 7
+#define CAP_SETPCAP 8
+#define CAP_LINUX_IMMUTABLE 9
+#define CAP_NET_BIND_SERVICE 10
+#define CAP_NET_BROADCAST 11
+#define CAP_NET_ADMIN 12
+#define CAP_NET_RAW 13
+#define CAP_IPC_LOCK 14
+#define CAP_IPC_OWNER 15
+#define CAP_SYS_MODULE 16
+#define CAP_SYS_RAWIO 17
+#define CAP_SYS_CHROOT 18
+#define CAP_SYS_PTRACE 19
+#define CAP_SYS_PACCT 20
+#define CAP_SYS_ADMIN 21
+#define CAP_SYS_BOOT 22
+#define CAP_SYS_NICE 23
+#define CAP_SYS_RESOURCE 24
+#define CAP_SYS_TIME 25
+#define CAP_SYS_TTY_CONFIG 26
+#define CAP_MKNOD 27
+#define CAP_LEASE 28
+#define CAP_AUDIT_WRITE 29
+#define CAP_AUDIT_CONTROL 30
+#define CAP_SETFCAP 31
+#define CAP_MAC_OVERRIDE 32
+#define CAP_MAC_ADMIN 33
+#define CAP_SYSLOG 34
+#define CAP_WAKE_ALARM 35
+#define CAP_BLOCK_SUSPEND 36
+#define CAP_AUDIT_READ 37
+#define CAP_LAST_CAP CAP_AUDIT_READ
+#define cap_valid(x) ((x) >= 0 && (x) <= CAP_LAST_CAP)
+#define CAP_TO_INDEX(x) ((x) >> 5)
+#define CAP_TO_MASK(x) (1 << ((x) & 31))
+
+#undef __user
+#undef __u32
+#undef __le32
+
+#endif
diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h
new file mode 100644
index 0000000..0c071ca
--- /dev/null
+++ b/include/private/android_filesystem_config.h
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* This file is used to define the properties of the filesystem
+** images generated by build tools (mkbootfs and mkyaffs2image) and
+** by the device side of adb.
+*/
+
+#ifndef _ANDROID_FILESYSTEM_CONFIG_H_
+#define _ANDROID_FILESYSTEM_CONFIG_H_
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <stdint.h>
+
+#ifdef HAVE_ANDROID_OS
+#include <linux/capability.h>
+#else
+#include "android_filesystem_capability.h"
+#endif
+
+/* This is the master Users and Groups config for the platform.
+ * DO NOT EVER RENUMBER
+ */
+
+#define AID_ROOT 0 /* traditional unix root user */
+
+#define AID_SYSTEM 1000 /* system server */
+
+#define AID_RADIO 1001 /* telephony subsystem, RIL */
+#define AID_BLUETOOTH 1002 /* bluetooth subsystem */
+#define AID_GRAPHICS 1003 /* graphics devices */
+#define AID_INPUT 1004 /* input devices */
+#define AID_AUDIO 1005 /* audio devices */
+#define AID_CAMERA 1006 /* camera devices */
+#define AID_LOG 1007 /* log devices */
+#define AID_COMPASS 1008 /* compass device */
+#define AID_MOUNT 1009 /* mountd socket */
+#define AID_WIFI 1010 /* wifi subsystem */
+#define AID_ADB 1011 /* android debug bridge (adbd) */
+#define AID_INSTALL 1012 /* group for installing packages */
+#define AID_MEDIA 1013 /* mediaserver process */
+#define AID_DHCP 1014 /* dhcp client */
+#define AID_SDCARD_RW 1015 /* external storage write access */
+#define AID_VPN 1016 /* vpn system */
+#define AID_KEYSTORE 1017 /* keystore subsystem */
+#define AID_USB 1018 /* USB devices */
+#define AID_DRM 1019 /* DRM server */
+#define AID_MDNSR 1020 /* MulticastDNSResponder (service discovery) */
+#define AID_GPS 1021 /* GPS daemon */
+#define AID_UNUSED1 1022 /* deprecated, DO NOT USE */
+#define AID_MEDIA_RW 1023 /* internal media storage write access */
+#define AID_MTP 1024 /* MTP USB driver access */
+#define AID_UNUSED2 1025 /* deprecated, DO NOT USE */
+#define AID_DRMRPC 1026 /* group for drm rpc */
+#define AID_NFC 1027 /* nfc subsystem */
+#define AID_SDCARD_R 1028 /* external storage read access */
+#define AID_CLAT 1029 /* clat part of nat464 */
+#define AID_LOOP_RADIO 1030 /* loop radio devices */
+#define AID_MEDIA_DRM 1031 /* MediaDrm plugins */
+#define AID_PACKAGE_INFO 1032 /* access to installed package details */
+#define AID_SDCARD_PICS 1033 /* external storage photos access */
+#define AID_SDCARD_AV 1034 /* external storage audio/video access */
+#define AID_SDCARD_ALL 1035 /* access all users external storage */
+#define AID_LOGD 1036 /* log daemon */
+#define AID_SHARED_RELRO 1037 /* creator of shared GNU RELRO files */
+#define AID_DBUS 1038 /* dbus-daemon IPC broker process */
+
+#define AID_SHELL 2000 /* adb and debug shell user */
+#define AID_CACHE 2001 /* cache access */
+#define AID_DIAG 2002 /* access to diagnostic resources */
+
+/* The range 2900-2999 is reserved for OEM, and must never be
+ * used here */
+#define AID_OEM_RESERVED_START 2900
+#define AID_OEM_RESERVED_END 2999
+
+/* The 3000 series are intended for use as supplemental group id's only.
+ * They indicate special Android capabilities that the kernel is aware of. */
+#define AID_NET_BT_ADMIN 3001 /* bluetooth: create any socket */
+#define AID_NET_BT 3002 /* bluetooth: create sco, rfcomm or l2cap sockets */
+#define AID_INET 3003 /* can create AF_INET and AF_INET6 sockets */
+#define AID_NET_RAW 3004 /* can create raw INET sockets */
+#define AID_NET_ADMIN 3005 /* can configure interfaces and routing tables. */
+#define AID_NET_BW_STATS 3006 /* read bandwidth statistics */
+#define AID_NET_BW_ACCT 3007 /* change bandwidth statistics accounting */
+#define AID_NET_BT_STACK 3008 /* bluetooth: access config files */
+
+#define AID_EVERYBODY 9997 /* shared between all apps in the same profile */
+#define AID_MISC 9998 /* access to misc storage */
+#define AID_NOBODY 9999
+
+#define AID_APP 10000 /* first app user */
+
+#define AID_ISOLATED_START 99000 /* start of uids for fully isolated sandboxed processes */
+#define AID_ISOLATED_END 99999 /* end of uids for fully isolated sandboxed processes */
+
+#define AID_USER 100000 /* offset for uid ranges for each user */
+
+#define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */
+#define AID_SHARED_GID_END 59999 /* start of gids for apps in each user to share */
+
+#if !defined(EXCLUDE_FS_CONFIG_STRUCTURES)
+/*
+ * Used in:
+ * bionic/libc/bionic/stubs.cpp
+ * external/libselinux/src/android.c
+ * system/core/logd/LogStatistics.cpp
+ * system/core/init/ueventd.cpp
+ * system/core/init/util.cpp
+ */
+struct android_id_info {
+ const char *name;
+ unsigned aid;
+};
+
+static const struct android_id_info android_ids[] = {
+ { "root", AID_ROOT, },
+
+ { "system", AID_SYSTEM, },
+
+ { "radio", AID_RADIO, },
+ { "bluetooth", AID_BLUETOOTH, },
+ { "graphics", AID_GRAPHICS, },
+ { "input", AID_INPUT, },
+ { "audio", AID_AUDIO, },
+ { "camera", AID_CAMERA, },
+ { "log", AID_LOG, },
+ { "compass", AID_COMPASS, },
+ { "mount", AID_MOUNT, },
+ { "wifi", AID_WIFI, },
+ { "adb", AID_ADB, },
+ { "install", AID_INSTALL, },
+ { "media", AID_MEDIA, },
+ { "dhcp", AID_DHCP, },
+ { "sdcard_rw", AID_SDCARD_RW, },
+ { "vpn", AID_VPN, },
+ { "keystore", AID_KEYSTORE, },
+ { "usb", AID_USB, },
+ { "drm", AID_DRM, },
+ { "mdnsr", AID_MDNSR, },
+ { "gps", AID_GPS, },
+ // AID_UNUSED1
+ { "media_rw", AID_MEDIA_RW, },
+ { "mtp", AID_MTP, },
+ // AID_UNUSED2
+ { "drmrpc", AID_DRMRPC, },
+ { "nfc", AID_NFC, },
+ { "sdcard_r", AID_SDCARD_R, },
+ { "clat", AID_CLAT, },
+ { "loop_radio", AID_LOOP_RADIO, },
+ { "mediadrm", AID_MEDIA_DRM, },
+ { "package_info", AID_PACKAGE_INFO, },
+ { "sdcard_pics", AID_SDCARD_PICS, },
+ { "sdcard_av", AID_SDCARD_AV, },
+ { "sdcard_all", AID_SDCARD_ALL, },
+ { "logd", AID_LOGD, },
+ { "shared_relro", AID_SHARED_RELRO, },
+ { "dbus", AID_DBUS, },
+
+ { "shell", AID_SHELL, },
+ { "cache", AID_CACHE, },
+ { "diag", AID_DIAG, },
+
+ { "net_bt_admin", AID_NET_BT_ADMIN, },
+ { "net_bt", AID_NET_BT, },
+ { "inet", AID_INET, },
+ { "net_raw", AID_NET_RAW, },
+ { "net_admin", AID_NET_ADMIN, },
+ { "net_bw_stats", AID_NET_BW_STATS, },
+ { "net_bw_acct", AID_NET_BW_ACCT, },
+ { "net_bt_stack", AID_NET_BT_STACK, },
+
+ { "everybody", AID_EVERYBODY, },
+ { "misc", AID_MISC, },
+ { "nobody", AID_NOBODY, },
+};
+
+#define android_id_count \
+ (sizeof(android_ids) / sizeof(android_ids[0]))
+
+struct fs_path_config {
+ unsigned mode;
+ unsigned uid;
+ unsigned gid;
+ uint64_t capabilities;
+ const char *prefix;
+};
+
+/* Rules for directories and files has moved to system/code/libcutils/fs_config.c */
+
+__BEGIN_DECLS
+
+/*
+ * Used in:
+ * build/tools/fs_config/fs_config.c
+ * build/tools/fs_get_stats/fs_get_stats.c
+ * external/genext2fs/genext2fs.c
+ * external/squashfs-tools/squashfs-tools/android.c
+ * system/core/cpio/mkbootfs.c
+ * system/core/adb/file_sync_service.cpp
+ * system/extras/ext4_utils/canned_fs_config.c
+ */
+void fs_config(const char *path, int dir,
+ unsigned *uid, unsigned *gid, unsigned *mode, uint64_t *capabilities);
+
+ssize_t fs_config_generate(char *buffer, size_t length, const struct fs_path_config *pc);
+
+__END_DECLS
+
+#endif
+#endif
diff --git a/include/private/android_logger.h b/include/private/android_logger.h
new file mode 100644
index 0000000..04238a6
--- /dev/null
+++ b/include/private/android_logger.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* This file is used to define the internal protocol for the Android Logger */
+
+#ifndef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
+#define _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
+
+#include <stdint.h>
+
+#include <log/log.h>
+#include <log/log_read.h>
+
+#define LOGGER_MAGIC 'l'
+
+/* Header Structure to pstore */
+typedef struct __attribute__((__packed__)) {
+ uint8_t magic;
+ uint16_t len;
+ uint16_t uid;
+ uint16_t pid;
+} android_pmsg_log_header_t;
+
+/* Header Structure to logd, and second header for pstore */
+typedef struct __attribute__((__packed__)) {
+ typeof_log_id_t id;
+ uint16_t tid;
+ log_time realtime;
+} android_log_header_t;
+
+/* Event Header Structure to logd */
+typedef struct __attribute__((__packed__)) {
+ int32_t tag; // Little Endian Order
+} android_event_header_t;
+
+/* Event payload EVENT_TYPE_INT */
+typedef struct __attribute__((__packed__)) {
+ int8_t type; // EVENT_TYPE_INT
+ int32_t data; // Little Endian Order
+} android_event_int_t;
+
+/* Event with single EVENT_TYPE_INT */
+typedef struct __attribute__((__packed__)) {
+ android_event_header_t header;
+ android_event_int_t payload;
+} android_log_event_int_t;
+
+/* Event payload EVENT_TYPE_LONG */
+typedef struct __attribute__((__packed__)) {
+ int8_t type; // EVENT_TYPE_LONG
+ int64_t data; // Little Endian Order
+} android_event_long_t;
+
+/* Event with single EVENT_TYPE_LONG */
+typedef struct __attribute__((__packed__)) {
+ android_event_header_t header;
+ android_event_long_t payload;
+} android_log_event_long_t;
+
+/*
+ * Event payload EVENT_TYPE_STRING
+ *
+ * Danger: do not embed this structure into another structure.
+ * This structure uses a flexible array member, and when
+ * compiled using g++, __builtin_object_size(data, 1) returns
+ * a bad value. This is possibly a g++ bug, or a bug due to
+ * the fact that flexible array members are not supported
+ * in C++.
+ * http://stackoverflow.com/questions/4412749/are-flexible-array-members-valid-in-c
+ */
+typedef struct __attribute__((__packed__)) {
+ int8_t type; // EVENT_TYPE_STRING;
+ int32_t length; // Little Endian Order
+ char data[];
+} android_event_string_t;
+
+/* Event with single EVENT_TYPE_STRING */
+typedef struct __attribute__((__packed__)) {
+ android_event_header_t header;
+ int8_t type; // EVENT_TYPE_STRING;
+ int32_t length; // Little Endian Order
+ char data[];
+} android_log_event_string_t;
+
+#endif
diff --git a/include/system/audio.h b/include/system/audio.h
new file mode 100644
index 0000000..ced2005
--- /dev/null
+++ b/include/system/audio.h
@@ -0,0 +1,1375 @@
+/*
+ * 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.
+ */
+
+
+#ifndef ANDROID_AUDIO_CORE_H
+#define ANDROID_AUDIO_CORE_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#include <cutils/bitops.h>
+
+__BEGIN_DECLS
+
+/* The enums were moved here mostly from
+ * frameworks/base/include/media/AudioSystem.h
+ */
+
+/* device address used to refer to the standard remote submix */
+#define AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS "0"
+
+/* AudioFlinger and AudioPolicy services use I/O handles to identify audio sources and sinks */
+typedef int audio_io_handle_t;
+#define AUDIO_IO_HANDLE_NONE 0
+
+/* Audio stream types */
+typedef enum {
+ /* These values must kept in sync with
+ * frameworks/base/media/java/android/media/AudioSystem.java
+ */
+ AUDIO_STREAM_DEFAULT = -1,
+ AUDIO_STREAM_MIN = 0,
+ AUDIO_STREAM_VOICE_CALL = 0,
+ AUDIO_STREAM_SYSTEM = 1,
+ AUDIO_STREAM_RING = 2,
+ AUDIO_STREAM_MUSIC = 3,
+ AUDIO_STREAM_ALARM = 4,
+ AUDIO_STREAM_NOTIFICATION = 5,
+ AUDIO_STREAM_BLUETOOTH_SCO = 6,
+ AUDIO_STREAM_ENFORCED_AUDIBLE = 7, /* Sounds that cannot be muted by user
+ * and must be routed to speaker
+ */
+ AUDIO_STREAM_DTMF = 8,
+ AUDIO_STREAM_TTS = 9, /* Transmitted Through Speaker.
+ * Plays over speaker only, silent on other devices.
+ */
+ AUDIO_STREAM_ACCESSIBILITY = 10, /* For accessibility talk back prompts */
+ AUDIO_STREAM_REROUTING = 11, /* For dynamic policy output mixes */
+ AUDIO_STREAM_PATCH = 12, /* For internal audio flinger tracks. Fixed volume */
+ AUDIO_STREAM_PUBLIC_CNT = AUDIO_STREAM_TTS + 1,
+ AUDIO_STREAM_CNT = AUDIO_STREAM_PATCH + 1,
+} audio_stream_type_t;
+
+/* Do not change these values without updating their counterparts
+ * in frameworks/base/media/java/android/media/AudioAttributes.java
+ */
+typedef enum {
+ AUDIO_CONTENT_TYPE_UNKNOWN = 0,
+ AUDIO_CONTENT_TYPE_SPEECH = 1,
+ AUDIO_CONTENT_TYPE_MUSIC = 2,
+ AUDIO_CONTENT_TYPE_MOVIE = 3,
+ AUDIO_CONTENT_TYPE_SONIFICATION = 4,
+
+ AUDIO_CONTENT_TYPE_CNT,
+ AUDIO_CONTENT_TYPE_MAX = AUDIO_CONTENT_TYPE_CNT - 1,
+} audio_content_type_t;
+
+/* Do not change these values without updating their counterparts
+ * in frameworks/base/media/java/android/media/AudioAttributes.java
+ */
+typedef enum {
+ AUDIO_USAGE_UNKNOWN = 0,
+ AUDIO_USAGE_MEDIA = 1,
+ AUDIO_USAGE_VOICE_COMMUNICATION = 2,
+ AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING = 3,
+ AUDIO_USAGE_ALARM = 4,
+ AUDIO_USAGE_NOTIFICATION = 5,
+ AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE = 6,
+ AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST = 7,
+ AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT = 8,
+ AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED = 9,
+ AUDIO_USAGE_NOTIFICATION_EVENT = 10,
+ AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY = 11,
+ AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 12,
+ AUDIO_USAGE_ASSISTANCE_SONIFICATION = 13,
+ AUDIO_USAGE_GAME = 14,
+ AUDIO_USAGE_VIRTUAL_SOURCE = 15,
+
+ AUDIO_USAGE_CNT,
+ AUDIO_USAGE_MAX = AUDIO_USAGE_CNT - 1,
+} audio_usage_t;
+
+typedef uint32_t audio_flags_mask_t;
+
+/* Do not change these values without updating their counterparts
+ * in frameworks/base/media/java/android/media/AudioAttributes.java
+ */
+enum {
+ AUDIO_FLAG_AUDIBILITY_ENFORCED = 0x1,
+ AUDIO_FLAG_SECURE = 0x2,
+ AUDIO_FLAG_SCO = 0x4,
+ AUDIO_FLAG_BEACON = 0x8,
+ AUDIO_FLAG_HW_AV_SYNC = 0x10,
+ AUDIO_FLAG_HW_HOTWORD = 0x20,
+};
+
+/* Do not change these values without updating their counterparts
+ * in frameworks/base/media/java/android/media/MediaRecorder.java,
+ * frameworks/av/services/audiopolicy/AudioPolicyService.cpp,
+ * and system/media/audio_effects/include/audio_effects/audio_effects_conf.h!
+ */
+typedef enum {
+ AUDIO_SOURCE_DEFAULT = 0,
+ AUDIO_SOURCE_MIC = 1,
+ AUDIO_SOURCE_VOICE_UPLINK = 2,
+ AUDIO_SOURCE_VOICE_DOWNLINK = 3,
+ AUDIO_SOURCE_VOICE_CALL = 4,
+ AUDIO_SOURCE_CAMCORDER = 5,
+ AUDIO_SOURCE_VOICE_RECOGNITION = 6,
+ AUDIO_SOURCE_VOICE_COMMUNICATION = 7,
+ AUDIO_SOURCE_REMOTE_SUBMIX = 8, /* Source for the mix to be presented remotely. */
+ /* An example of remote presentation is Wifi Display */
+ /* where a dongle attached to a TV can be used to */
+ /* play the mix captured by this audio source. */
+ AUDIO_SOURCE_CNT,
+ AUDIO_SOURCE_MAX = AUDIO_SOURCE_CNT - 1,
+ AUDIO_SOURCE_FM_TUNER = 1998,
+ AUDIO_SOURCE_HOTWORD = 1999, /* A low-priority, preemptible audio source for
+ for background software hotword detection.
+ Same tuning as AUDIO_SOURCE_VOICE_RECOGNITION.
+ Used only internally to the framework. Not exposed
+ at the audio HAL. */
+} audio_source_t;
+
+/* Audio attributes */
+#define AUDIO_ATTRIBUTES_TAGS_MAX_SIZE 256
+typedef struct {
+ audio_content_type_t content_type;
+ audio_usage_t usage;
+ audio_source_t source;
+ audio_flags_mask_t flags;
+ char tags[AUDIO_ATTRIBUTES_TAGS_MAX_SIZE]; /* UTF8 */
+} audio_attributes_t;
+
+/* special audio session values
+ * (XXX: should this be living in the audio effects land?)
+ */
+typedef enum {
+ /* session for effects attached to a particular output stream
+ * (value must be less than 0)
+ */
+ AUDIO_SESSION_OUTPUT_STAGE = -1,
+
+ /* session for effects applied to output mix. These effects can
+ * be moved by audio policy manager to another output stream
+ * (value must be 0)
+ */
+ AUDIO_SESSION_OUTPUT_MIX = 0,
+
+ /* application does not specify an explicit session ID to be used,
+ * and requests a new session ID to be allocated
+ * TODO use unique values for AUDIO_SESSION_OUTPUT_MIX and AUDIO_SESSION_ALLOCATE,
+ * after all uses have been updated from 0 to the appropriate symbol, and have been tested.
+ */
+ AUDIO_SESSION_ALLOCATE = 0,
+} audio_session_t;
+
+/* a unique ID allocated by AudioFlinger for use as a audio_io_handle_t or audio_session_t */
+typedef int audio_unique_id_t;
+
+#define AUDIO_UNIQUE_ID_ALLOCATE AUDIO_SESSION_ALLOCATE
+
+/* Audio sub formats (see enum audio_format). */
+
+/* PCM sub formats */
+typedef enum {
+ /* All of these are in native byte order */
+ AUDIO_FORMAT_PCM_SUB_16_BIT = 0x1, /* DO NOT CHANGE - PCM signed 16 bits */
+ AUDIO_FORMAT_PCM_SUB_8_BIT = 0x2, /* DO NOT CHANGE - PCM unsigned 8 bits */
+ AUDIO_FORMAT_PCM_SUB_32_BIT = 0x3, /* PCM signed .31 fixed point */
+ AUDIO_FORMAT_PCM_SUB_8_24_BIT = 0x4, /* PCM signed 7.24 fixed point */
+ AUDIO_FORMAT_PCM_SUB_FLOAT = 0x5, /* PCM single-precision floating point */
+ AUDIO_FORMAT_PCM_SUB_24_BIT_PACKED = 0x6, /* PCM signed .23 fixed point packed in 3 bytes */
+} audio_format_pcm_sub_fmt_t;
+
+/* The audio_format_*_sub_fmt_t declarations are not currently used */
+
+/* MP3 sub format field definition : can use 11 LSBs in the same way as MP3
+ * frame header to specify bit rate, stereo mode, version...
+ */
+typedef enum {
+ AUDIO_FORMAT_MP3_SUB_NONE = 0x0,
+} audio_format_mp3_sub_fmt_t;
+
+/* AMR NB/WB sub format field definition: specify frame block interleaving,
+ * bandwidth efficient or octet aligned, encoding mode for recording...
+ */
+typedef enum {
+ AUDIO_FORMAT_AMR_SUB_NONE = 0x0,
+} audio_format_amr_sub_fmt_t;
+
+/* AAC sub format field definition: specify profile or bitrate for recording... */
+typedef enum {
+ AUDIO_FORMAT_AAC_SUB_MAIN = 0x1,
+ AUDIO_FORMAT_AAC_SUB_LC = 0x2,
+ AUDIO_FORMAT_AAC_SUB_SSR = 0x4,
+ AUDIO_FORMAT_AAC_SUB_LTP = 0x8,
+ AUDIO_FORMAT_AAC_SUB_HE_V1 = 0x10,
+ AUDIO_FORMAT_AAC_SUB_SCALABLE = 0x20,
+ AUDIO_FORMAT_AAC_SUB_ERLC = 0x40,
+ AUDIO_FORMAT_AAC_SUB_LD = 0x80,
+ AUDIO_FORMAT_AAC_SUB_HE_V2 = 0x100,
+ AUDIO_FORMAT_AAC_SUB_ELD = 0x200,
+} audio_format_aac_sub_fmt_t;
+
+/* VORBIS sub format field definition: specify quality for recording... */
+typedef enum {
+ AUDIO_FORMAT_VORBIS_SUB_NONE = 0x0,
+} audio_format_vorbis_sub_fmt_t;
+
+/* Audio format consists of a main format field (upper 8 bits) and a sub format
+ * field (lower 24 bits).
+ *
+ * The main format indicates the main codec type. The sub format field
+ * indicates options and parameters for each format. The sub format is mainly
+ * used for record to indicate for instance the requested bitrate or profile.
+ * It can also be used for certain formats to give informations not present in
+ * the encoded audio stream (e.g. octet alignement for AMR).
+ */
+typedef enum {
+ AUDIO_FORMAT_INVALID = 0xFFFFFFFFUL,
+ AUDIO_FORMAT_DEFAULT = 0,
+ AUDIO_FORMAT_PCM = 0x00000000UL, /* DO NOT CHANGE */
+ AUDIO_FORMAT_MP3 = 0x01000000UL,
+ AUDIO_FORMAT_AMR_NB = 0x02000000UL,
+ AUDIO_FORMAT_AMR_WB = 0x03000000UL,
+ AUDIO_FORMAT_AAC = 0x04000000UL,
+ AUDIO_FORMAT_HE_AAC_V1 = 0x05000000UL, /* Deprecated, Use AUDIO_FORMAT_AAC_HE_V1*/
+ AUDIO_FORMAT_HE_AAC_V2 = 0x06000000UL, /* Deprecated, Use AUDIO_FORMAT_AAC_HE_V2*/
+ AUDIO_FORMAT_VORBIS = 0x07000000UL,
+ AUDIO_FORMAT_OPUS = 0x08000000UL,
+ AUDIO_FORMAT_AC3 = 0x09000000UL,
+ AUDIO_FORMAT_E_AC3 = 0x0A000000UL,
+ AUDIO_FORMAT_MAIN_MASK = 0xFF000000UL,
+ AUDIO_FORMAT_SUB_MASK = 0x00FFFFFFUL,
+
+ /* Aliases */
+ /* note != AudioFormat.ENCODING_PCM_16BIT */
+ AUDIO_FORMAT_PCM_16_BIT = (AUDIO_FORMAT_PCM |
+ AUDIO_FORMAT_PCM_SUB_16_BIT),
+ /* note != AudioFormat.ENCODING_PCM_8BIT */
+ AUDIO_FORMAT_PCM_8_BIT = (AUDIO_FORMAT_PCM |
+ AUDIO_FORMAT_PCM_SUB_8_BIT),
+ AUDIO_FORMAT_PCM_32_BIT = (AUDIO_FORMAT_PCM |
+ AUDIO_FORMAT_PCM_SUB_32_BIT),
+ AUDIO_FORMAT_PCM_8_24_BIT = (AUDIO_FORMAT_PCM |
+ AUDIO_FORMAT_PCM_SUB_8_24_BIT),
+ AUDIO_FORMAT_PCM_FLOAT = (AUDIO_FORMAT_PCM |
+ AUDIO_FORMAT_PCM_SUB_FLOAT),
+ AUDIO_FORMAT_PCM_24_BIT_PACKED = (AUDIO_FORMAT_PCM |
+ AUDIO_FORMAT_PCM_SUB_24_BIT_PACKED),
+ AUDIO_FORMAT_AAC_MAIN = (AUDIO_FORMAT_AAC |
+ AUDIO_FORMAT_AAC_SUB_MAIN),
+ AUDIO_FORMAT_AAC_LC = (AUDIO_FORMAT_AAC |
+ AUDIO_FORMAT_AAC_SUB_LC),
+ AUDIO_FORMAT_AAC_SSR = (AUDIO_FORMAT_AAC |
+ AUDIO_FORMAT_AAC_SUB_SSR),
+ AUDIO_FORMAT_AAC_LTP = (AUDIO_FORMAT_AAC |
+ AUDIO_FORMAT_AAC_SUB_LTP),
+ AUDIO_FORMAT_AAC_HE_V1 = (AUDIO_FORMAT_AAC |
+ AUDIO_FORMAT_AAC_SUB_HE_V1),
+ AUDIO_FORMAT_AAC_SCALABLE = (AUDIO_FORMAT_AAC |
+ AUDIO_FORMAT_AAC_SUB_SCALABLE),
+ AUDIO_FORMAT_AAC_ERLC = (AUDIO_FORMAT_AAC |
+ AUDIO_FORMAT_AAC_SUB_ERLC),
+ AUDIO_FORMAT_AAC_LD = (AUDIO_FORMAT_AAC |
+ AUDIO_FORMAT_AAC_SUB_LD),
+ AUDIO_FORMAT_AAC_HE_V2 = (AUDIO_FORMAT_AAC |
+ AUDIO_FORMAT_AAC_SUB_HE_V2),
+ AUDIO_FORMAT_AAC_ELD = (AUDIO_FORMAT_AAC |
+ AUDIO_FORMAT_AAC_SUB_ELD),
+} audio_format_t;
+
+/* For the channel mask for position assignment representation */
+enum {
+
+/* These can be a complete audio_channel_mask_t. */
+
+ AUDIO_CHANNEL_NONE = 0x0,
+ AUDIO_CHANNEL_INVALID = 0xC0000000,
+
+/* These can be the bits portion of an audio_channel_mask_t
+ * with representation AUDIO_CHANNEL_REPRESENTATION_POSITION.
+ * Using these bits as a complete audio_channel_mask_t is deprecated.
+ */
+
+ /* output channels */
+ AUDIO_CHANNEL_OUT_FRONT_LEFT = 0x1,
+ AUDIO_CHANNEL_OUT_FRONT_RIGHT = 0x2,
+ AUDIO_CHANNEL_OUT_FRONT_CENTER = 0x4,
+ AUDIO_CHANNEL_OUT_LOW_FREQUENCY = 0x8,
+ AUDIO_CHANNEL_OUT_BACK_LEFT = 0x10,
+ AUDIO_CHANNEL_OUT_BACK_RIGHT = 0x20,
+ AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER = 0x40,
+ AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER = 0x80,
+ AUDIO_CHANNEL_OUT_BACK_CENTER = 0x100,
+ AUDIO_CHANNEL_OUT_SIDE_LEFT = 0x200,
+ AUDIO_CHANNEL_OUT_SIDE_RIGHT = 0x400,
+ AUDIO_CHANNEL_OUT_TOP_CENTER = 0x800,
+ AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT = 0x1000,
+ AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER = 0x2000,
+ AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT = 0x4000,
+ AUDIO_CHANNEL_OUT_TOP_BACK_LEFT = 0x8000,
+ AUDIO_CHANNEL_OUT_TOP_BACK_CENTER = 0x10000,
+ AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT = 0x20000,
+
+/* TODO: should these be considered complete channel masks, or only bits? */
+
+ AUDIO_CHANNEL_OUT_MONO = AUDIO_CHANNEL_OUT_FRONT_LEFT,
+ AUDIO_CHANNEL_OUT_STEREO = (AUDIO_CHANNEL_OUT_FRONT_LEFT |
+ AUDIO_CHANNEL_OUT_FRONT_RIGHT),
+ AUDIO_CHANNEL_OUT_QUAD = (AUDIO_CHANNEL_OUT_FRONT_LEFT |
+ AUDIO_CHANNEL_OUT_FRONT_RIGHT |
+ AUDIO_CHANNEL_OUT_BACK_LEFT |
+ AUDIO_CHANNEL_OUT_BACK_RIGHT),
+ AUDIO_CHANNEL_OUT_QUAD_BACK = AUDIO_CHANNEL_OUT_QUAD,
+ /* like AUDIO_CHANNEL_OUT_QUAD_BACK with *_SIDE_* instead of *_BACK_* */
+ AUDIO_CHANNEL_OUT_QUAD_SIDE = (AUDIO_CHANNEL_OUT_FRONT_LEFT |
+ AUDIO_CHANNEL_OUT_FRONT_RIGHT |
+ AUDIO_CHANNEL_OUT_SIDE_LEFT |
+ AUDIO_CHANNEL_OUT_SIDE_RIGHT),
+ AUDIO_CHANNEL_OUT_5POINT1 = (AUDIO_CHANNEL_OUT_FRONT_LEFT |
+ AUDIO_CHANNEL_OUT_FRONT_RIGHT |
+ AUDIO_CHANNEL_OUT_FRONT_CENTER |
+ AUDIO_CHANNEL_OUT_LOW_FREQUENCY |
+ AUDIO_CHANNEL_OUT_BACK_LEFT |
+ AUDIO_CHANNEL_OUT_BACK_RIGHT),
+ AUDIO_CHANNEL_OUT_5POINT1_BACK = AUDIO_CHANNEL_OUT_5POINT1,
+ /* like AUDIO_CHANNEL_OUT_5POINT1_BACK with *_SIDE_* instead of *_BACK_* */
+ AUDIO_CHANNEL_OUT_5POINT1_SIDE = (AUDIO_CHANNEL_OUT_FRONT_LEFT |
+ AUDIO_CHANNEL_OUT_FRONT_RIGHT |
+ AUDIO_CHANNEL_OUT_FRONT_CENTER |
+ AUDIO_CHANNEL_OUT_LOW_FREQUENCY |
+ AUDIO_CHANNEL_OUT_SIDE_LEFT |
+ AUDIO_CHANNEL_OUT_SIDE_RIGHT),
+ // matches the correct AudioFormat.CHANNEL_OUT_7POINT1_SURROUND definition for 7.1
+ AUDIO_CHANNEL_OUT_7POINT1 = (AUDIO_CHANNEL_OUT_FRONT_LEFT |
+ AUDIO_CHANNEL_OUT_FRONT_RIGHT |
+ AUDIO_CHANNEL_OUT_FRONT_CENTER |
+ AUDIO_CHANNEL_OUT_LOW_FREQUENCY |
+ AUDIO_CHANNEL_OUT_BACK_LEFT |
+ AUDIO_CHANNEL_OUT_BACK_RIGHT |
+ AUDIO_CHANNEL_OUT_SIDE_LEFT |
+ AUDIO_CHANNEL_OUT_SIDE_RIGHT),
+ AUDIO_CHANNEL_OUT_ALL = (AUDIO_CHANNEL_OUT_FRONT_LEFT |
+ AUDIO_CHANNEL_OUT_FRONT_RIGHT |
+ AUDIO_CHANNEL_OUT_FRONT_CENTER |
+ AUDIO_CHANNEL_OUT_LOW_FREQUENCY |
+ AUDIO_CHANNEL_OUT_BACK_LEFT |
+ AUDIO_CHANNEL_OUT_BACK_RIGHT |
+ AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER |
+ AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER |
+ AUDIO_CHANNEL_OUT_BACK_CENTER|
+ AUDIO_CHANNEL_OUT_SIDE_LEFT|
+ AUDIO_CHANNEL_OUT_SIDE_RIGHT|
+ AUDIO_CHANNEL_OUT_TOP_CENTER|
+ AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT|
+ AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER|
+ AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT|
+ AUDIO_CHANNEL_OUT_TOP_BACK_LEFT|
+ AUDIO_CHANNEL_OUT_TOP_BACK_CENTER|
+ AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT),
+
+/* These are bits only, not complete values */
+
+ /* input channels */
+ AUDIO_CHANNEL_IN_LEFT = 0x4,
+ AUDIO_CHANNEL_IN_RIGHT = 0x8,
+ AUDIO_CHANNEL_IN_FRONT = 0x10,
+ AUDIO_CHANNEL_IN_BACK = 0x20,
+ AUDIO_CHANNEL_IN_LEFT_PROCESSED = 0x40,
+ AUDIO_CHANNEL_IN_RIGHT_PROCESSED = 0x80,
+ AUDIO_CHANNEL_IN_FRONT_PROCESSED = 0x100,
+ AUDIO_CHANNEL_IN_BACK_PROCESSED = 0x200,
+ AUDIO_CHANNEL_IN_PRESSURE = 0x400,
+ AUDIO_CHANNEL_IN_X_AXIS = 0x800,
+ AUDIO_CHANNEL_IN_Y_AXIS = 0x1000,
+ AUDIO_CHANNEL_IN_Z_AXIS = 0x2000,
+ AUDIO_CHANNEL_IN_VOICE_UPLINK = 0x4000,
+ AUDIO_CHANNEL_IN_VOICE_DNLINK = 0x8000,
+
+/* TODO: should these be considered complete channel masks, or only bits, or deprecated? */
+
+ AUDIO_CHANNEL_IN_MONO = AUDIO_CHANNEL_IN_FRONT,
+ AUDIO_CHANNEL_IN_STEREO = (AUDIO_CHANNEL_IN_LEFT | AUDIO_CHANNEL_IN_RIGHT),
+ AUDIO_CHANNEL_IN_FRONT_BACK = (AUDIO_CHANNEL_IN_FRONT | AUDIO_CHANNEL_IN_BACK),
+ AUDIO_CHANNEL_IN_ALL = (AUDIO_CHANNEL_IN_LEFT |
+ AUDIO_CHANNEL_IN_RIGHT |
+ AUDIO_CHANNEL_IN_FRONT |
+ AUDIO_CHANNEL_IN_BACK|
+ AUDIO_CHANNEL_IN_LEFT_PROCESSED |
+ AUDIO_CHANNEL_IN_RIGHT_PROCESSED |
+ AUDIO_CHANNEL_IN_FRONT_PROCESSED |
+ AUDIO_CHANNEL_IN_BACK_PROCESSED|
+ AUDIO_CHANNEL_IN_PRESSURE |
+ AUDIO_CHANNEL_IN_X_AXIS |
+ AUDIO_CHANNEL_IN_Y_AXIS |
+ AUDIO_CHANNEL_IN_Z_AXIS |
+ AUDIO_CHANNEL_IN_VOICE_UPLINK |
+ AUDIO_CHANNEL_IN_VOICE_DNLINK),
+};
+
+/* A channel mask per se only defines the presence or absence of a channel, not the order.
+ * But see AUDIO_INTERLEAVE_* below for the platform convention of order.
+ *
+ * audio_channel_mask_t is an opaque type and its internal layout should not
+ * be assumed as it may change in the future.
+ * Instead, always use the functions declared in this header to examine.
+ *
+ * These are the current representations:
+ *
+ * AUDIO_CHANNEL_REPRESENTATION_POSITION
+ * is a channel mask representation for position assignment.
+ * Each low-order bit corresponds to the spatial position of a transducer (output),
+ * or interpretation of channel (input).
+ * The user of a channel mask needs to know the context of whether it is for output or input.
+ * The constants AUDIO_CHANNEL_OUT_* or AUDIO_CHANNEL_IN_* apply to the bits portion.
+ * It is not permitted for no bits to be set.
+ *
+ * AUDIO_CHANNEL_REPRESENTATION_INDEX
+ * is a channel mask representation for index assignment.
+ * Each low-order bit corresponds to a selected channel.
+ * There is no platform interpretation of the various bits.
+ * There is no concept of output or input.
+ * It is not permitted for no bits to be set.
+ *
+ * All other representations are reserved for future use.
+ *
+ * Warning: current representation distinguishes between input and output, but this will not the be
+ * case in future revisions of the platform. Wherever there is an ambiguity between input and output
+ * that is currently resolved by checking the channel mask, the implementer should look for ways to
+ * fix it with additional information outside of the mask.
+ */
+typedef uint32_t audio_channel_mask_t;
+
+/* Maximum number of channels for all representations */
+#define AUDIO_CHANNEL_COUNT_MAX 30
+
+/* log(2) of maximum number of representations, not part of public API */
+#define AUDIO_CHANNEL_REPRESENTATION_LOG2 2
+
+/* Representations */
+typedef enum {
+ AUDIO_CHANNEL_REPRESENTATION_POSITION = 0, // must be zero for compatibility
+ // 1 is reserved for future use
+ AUDIO_CHANNEL_REPRESENTATION_INDEX = 2,
+ // 3 is reserved for future use
+} audio_channel_representation_t;
+
+/* The return value is undefined if the channel mask is invalid. */
+static inline uint32_t audio_channel_mask_get_bits(audio_channel_mask_t channel)
+{
+ return channel & ((1 << AUDIO_CHANNEL_COUNT_MAX) - 1);
+}
+
+/* The return value is undefined if the channel mask is invalid. */
+static inline audio_channel_representation_t audio_channel_mask_get_representation(
+ audio_channel_mask_t channel)
+{
+ // The right shift should be sufficient, but also "and" for safety in case mask is not 32 bits
+ return (audio_channel_representation_t)
+ ((channel >> AUDIO_CHANNEL_COUNT_MAX) & ((1 << AUDIO_CHANNEL_REPRESENTATION_LOG2) - 1));
+}
+
+/* Returns true if the channel mask is valid,
+ * or returns false for AUDIO_CHANNEL_NONE, AUDIO_CHANNEL_INVALID, and other invalid values.
+ * This function is unable to determine whether a channel mask for position assignment
+ * is invalid because an output mask has an invalid output bit set,
+ * or because an input mask has an invalid input bit set.
+ * All other APIs that take a channel mask assume that it is valid.
+ */
+static inline bool audio_channel_mask_is_valid(audio_channel_mask_t channel)
+{
+ uint32_t bits = audio_channel_mask_get_bits(channel);
+ audio_channel_representation_t representation = audio_channel_mask_get_representation(channel);
+ switch (representation) {
+ case AUDIO_CHANNEL_REPRESENTATION_POSITION:
+ case AUDIO_CHANNEL_REPRESENTATION_INDEX:
+ break;
+ default:
+ bits = 0;
+ break;
+ }
+ return bits != 0;
+}
+
+/* Not part of public API */
+static inline audio_channel_mask_t audio_channel_mask_from_representation_and_bits(
+ audio_channel_representation_t representation, uint32_t bits)
+{
+ return (audio_channel_mask_t) ((representation << AUDIO_CHANNEL_COUNT_MAX) | bits);
+}
+
+/* Expresses the convention when stereo audio samples are stored interleaved
+ * in an array. This should improve readability by allowing code to use
+ * symbolic indices instead of hard-coded [0] and [1].
+ *
+ * For multi-channel beyond stereo, the platform convention is that channels
+ * are interleaved in order from least significant channel mask bit
+ * to most significant channel mask bit, with unused bits skipped.
+ * Any exceptions to this convention will be noted at the appropriate API.
+ */
+enum {
+ AUDIO_INTERLEAVE_LEFT = 0,
+ AUDIO_INTERLEAVE_RIGHT = 1,
+};
+
+typedef enum {
+ AUDIO_MODE_INVALID = -2,
+ AUDIO_MODE_CURRENT = -1,
+ AUDIO_MODE_NORMAL = 0,
+ AUDIO_MODE_RINGTONE = 1,
+ AUDIO_MODE_IN_CALL = 2,
+ AUDIO_MODE_IN_COMMUNICATION = 3,
+
+ AUDIO_MODE_CNT,
+ AUDIO_MODE_MAX = AUDIO_MODE_CNT - 1,
+} audio_mode_t;
+
+/* This enum is deprecated */
+typedef enum {
+ AUDIO_IN_ACOUSTICS_NONE = 0,
+ AUDIO_IN_ACOUSTICS_AGC_ENABLE = 0x0001,
+ AUDIO_IN_ACOUSTICS_AGC_DISABLE = 0,
+ AUDIO_IN_ACOUSTICS_NS_ENABLE = 0x0002,
+ AUDIO_IN_ACOUSTICS_NS_DISABLE = 0,
+ AUDIO_IN_ACOUSTICS_TX_IIR_ENABLE = 0x0004,
+ AUDIO_IN_ACOUSTICS_TX_DISABLE = 0,
+} audio_in_acoustics_t;
+
+enum {
+ AUDIO_DEVICE_NONE = 0x0,
+ /* reserved bits */
+ AUDIO_DEVICE_BIT_IN = 0x80000000,
+ AUDIO_DEVICE_BIT_DEFAULT = 0x40000000,
+ /* output devices */
+ AUDIO_DEVICE_OUT_EARPIECE = 0x1,
+ AUDIO_DEVICE_OUT_SPEAKER = 0x2,
+ AUDIO_DEVICE_OUT_WIRED_HEADSET = 0x4,
+ AUDIO_DEVICE_OUT_WIRED_HEADPHONE = 0x8,
+ AUDIO_DEVICE_OUT_BLUETOOTH_SCO = 0x10,
+ AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET = 0x20,
+ AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT = 0x40,
+ AUDIO_DEVICE_OUT_BLUETOOTH_A2DP = 0x80,
+ AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100,
+ AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200,
+ AUDIO_DEVICE_OUT_AUX_DIGITAL = 0x400,
+ AUDIO_DEVICE_OUT_HDMI = AUDIO_DEVICE_OUT_AUX_DIGITAL,
+ /* uses an analog connection (multiplexed over the USB connector pins for instance) */
+ AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET = 0x800,
+ AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET = 0x1000,
+ /* USB accessory mode: your Android device is a USB device and the dock is a USB host */
+ AUDIO_DEVICE_OUT_USB_ACCESSORY = 0x2000,
+ /* USB host mode: your Android device is a USB host and the dock is a USB device */
+ AUDIO_DEVICE_OUT_USB_DEVICE = 0x4000,
+ AUDIO_DEVICE_OUT_REMOTE_SUBMIX = 0x8000,
+ /* Telephony voice TX path */
+ AUDIO_DEVICE_OUT_TELEPHONY_TX = 0x10000,
+ /* Analog jack with line impedance detected */
+ AUDIO_DEVICE_OUT_LINE = 0x20000,
+ /* HDMI Audio Return Channel */
+ AUDIO_DEVICE_OUT_HDMI_ARC = 0x40000,
+ /* S/PDIF out */
+ AUDIO_DEVICE_OUT_SPDIF = 0x80000,
+ /* FM transmitter out */
+ AUDIO_DEVICE_OUT_FM = 0x100000,
+ /* Line out for av devices */
+ AUDIO_DEVICE_OUT_AUX_LINE = 0x200000,
+ /* limited-output speaker device for acoustic safety */
+ AUDIO_DEVICE_OUT_SPEAKER_SAFE = 0x400000,
+ AUDIO_DEVICE_OUT_DEFAULT = AUDIO_DEVICE_BIT_DEFAULT,
+ AUDIO_DEVICE_OUT_ALL = (AUDIO_DEVICE_OUT_EARPIECE |
+ AUDIO_DEVICE_OUT_SPEAKER |
+ AUDIO_DEVICE_OUT_WIRED_HEADSET |
+ AUDIO_DEVICE_OUT_WIRED_HEADPHONE |
+ AUDIO_DEVICE_OUT_BLUETOOTH_SCO |
+ AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET |
+ AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT |
+ AUDIO_DEVICE_OUT_BLUETOOTH_A2DP |
+ AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
+ AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER |
+ AUDIO_DEVICE_OUT_HDMI |
+ AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET |
+ AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET |
+ AUDIO_DEVICE_OUT_USB_ACCESSORY |
+ AUDIO_DEVICE_OUT_USB_DEVICE |
+ AUDIO_DEVICE_OUT_REMOTE_SUBMIX |
+ AUDIO_DEVICE_OUT_TELEPHONY_TX |
+ AUDIO_DEVICE_OUT_LINE |
+ AUDIO_DEVICE_OUT_HDMI_ARC |
+ AUDIO_DEVICE_OUT_SPDIF |
+ AUDIO_DEVICE_OUT_FM |
+ AUDIO_DEVICE_OUT_AUX_LINE |
+ AUDIO_DEVICE_OUT_SPEAKER_SAFE |
+ AUDIO_DEVICE_OUT_DEFAULT),
+ AUDIO_DEVICE_OUT_ALL_A2DP = (AUDIO_DEVICE_OUT_BLUETOOTH_A2DP |
+ AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
+ AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER),
+ AUDIO_DEVICE_OUT_ALL_SCO = (AUDIO_DEVICE_OUT_BLUETOOTH_SCO |
+ AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET |
+ AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT),
+ AUDIO_DEVICE_OUT_ALL_USB = (AUDIO_DEVICE_OUT_USB_ACCESSORY |
+ AUDIO_DEVICE_OUT_USB_DEVICE),
+
+ /* input devices */
+ AUDIO_DEVICE_IN_COMMUNICATION = AUDIO_DEVICE_BIT_IN | 0x1,
+ AUDIO_DEVICE_IN_AMBIENT = AUDIO_DEVICE_BIT_IN | 0x2,
+ AUDIO_DEVICE_IN_BUILTIN_MIC = AUDIO_DEVICE_BIT_IN | 0x4,
+ AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET = AUDIO_DEVICE_BIT_IN | 0x8,
+ AUDIO_DEVICE_IN_WIRED_HEADSET = AUDIO_DEVICE_BIT_IN | 0x10,
+ AUDIO_DEVICE_IN_AUX_DIGITAL = AUDIO_DEVICE_BIT_IN | 0x20,
+ AUDIO_DEVICE_IN_HDMI = AUDIO_DEVICE_IN_AUX_DIGITAL,
+ /* Telephony voice RX path */
+ AUDIO_DEVICE_IN_VOICE_CALL = AUDIO_DEVICE_BIT_IN | 0x40,
+ AUDIO_DEVICE_IN_TELEPHONY_RX = AUDIO_DEVICE_IN_VOICE_CALL,
+ AUDIO_DEVICE_IN_BACK_MIC = AUDIO_DEVICE_BIT_IN | 0x80,
+ AUDIO_DEVICE_IN_REMOTE_SUBMIX = AUDIO_DEVICE_BIT_IN | 0x100,
+ AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET = AUDIO_DEVICE_BIT_IN | 0x200,
+ AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET = AUDIO_DEVICE_BIT_IN | 0x400,
+ AUDIO_DEVICE_IN_USB_ACCESSORY = AUDIO_DEVICE_BIT_IN | 0x800,
+ AUDIO_DEVICE_IN_USB_DEVICE = AUDIO_DEVICE_BIT_IN | 0x1000,
+ /* FM tuner input */
+ AUDIO_DEVICE_IN_FM_TUNER = AUDIO_DEVICE_BIT_IN | 0x2000,
+ /* TV tuner input */
+ AUDIO_DEVICE_IN_TV_TUNER = AUDIO_DEVICE_BIT_IN | 0x4000,
+ /* Analog jack with line impedance detected */
+ AUDIO_DEVICE_IN_LINE = AUDIO_DEVICE_BIT_IN | 0x8000,
+ /* S/PDIF in */
+ AUDIO_DEVICE_IN_SPDIF = AUDIO_DEVICE_BIT_IN | 0x10000,
+ AUDIO_DEVICE_IN_BLUETOOTH_A2DP = AUDIO_DEVICE_BIT_IN | 0x20000,
+ AUDIO_DEVICE_IN_LOOPBACK = AUDIO_DEVICE_BIT_IN | 0x40000,
+ AUDIO_DEVICE_IN_DEFAULT = AUDIO_DEVICE_BIT_IN | AUDIO_DEVICE_BIT_DEFAULT,
+
+ AUDIO_DEVICE_IN_ALL = (AUDIO_DEVICE_IN_COMMUNICATION |
+ AUDIO_DEVICE_IN_AMBIENT |
+ AUDIO_DEVICE_IN_BUILTIN_MIC |
+ AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET |
+ AUDIO_DEVICE_IN_WIRED_HEADSET |
+ AUDIO_DEVICE_IN_HDMI |
+ AUDIO_DEVICE_IN_TELEPHONY_RX |
+ AUDIO_DEVICE_IN_BACK_MIC |
+ AUDIO_DEVICE_IN_REMOTE_SUBMIX |
+ AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET |
+ AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET |
+ AUDIO_DEVICE_IN_USB_ACCESSORY |
+ AUDIO_DEVICE_IN_USB_DEVICE |
+ AUDIO_DEVICE_IN_FM_TUNER |
+ AUDIO_DEVICE_IN_TV_TUNER |
+ AUDIO_DEVICE_IN_LINE |
+ AUDIO_DEVICE_IN_SPDIF |
+ AUDIO_DEVICE_IN_BLUETOOTH_A2DP |
+ AUDIO_DEVICE_IN_LOOPBACK |
+ AUDIO_DEVICE_IN_DEFAULT),
+ AUDIO_DEVICE_IN_ALL_SCO = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET,
+ AUDIO_DEVICE_IN_ALL_USB = (AUDIO_DEVICE_IN_USB_ACCESSORY |
+ AUDIO_DEVICE_IN_USB_DEVICE),
+};
+
+typedef uint32_t audio_devices_t;
+
+/* the audio output flags serve two purposes:
+ * - when an AudioTrack is created they indicate a "wish" to be connected to an
+ * output stream with attributes corresponding to the specified flags
+ * - when present in an output profile descriptor listed for a particular audio
+ * hardware module, they indicate that an output stream can be opened that
+ * supports the attributes indicated by the flags.
+ * the audio policy manager will try to match the flags in the request
+ * (when getOuput() is called) to an available output stream.
+ */
+typedef enum {
+ AUDIO_OUTPUT_FLAG_NONE = 0x0, // no attributes
+ AUDIO_OUTPUT_FLAG_DIRECT = 0x1, // this output directly connects a track
+ // to one output stream: no software mixer
+ AUDIO_OUTPUT_FLAG_PRIMARY = 0x2, // this output is the primary output of
+ // the device. It is unique and must be
+ // present. It is opened by default and
+ // receives routing, audio mode and volume
+ // controls related to voice calls.
+ AUDIO_OUTPUT_FLAG_FAST = 0x4, // output supports "fast tracks",
+ // defined elsewhere
+ AUDIO_OUTPUT_FLAG_DEEP_BUFFER = 0x8, // use deep audio buffers
+ AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD = 0x10, // offload playback of compressed
+ // streams to hardware codec
+ AUDIO_OUTPUT_FLAG_NON_BLOCKING = 0x20, // use non-blocking write
+ AUDIO_OUTPUT_FLAG_HW_AV_SYNC = 0x40 // output uses a hardware A/V synchronization source
+} audio_output_flags_t;
+
+/* The audio input flags are analogous to audio output flags.
+ * Currently they are used only when an AudioRecord is created,
+ * to indicate a preference to be connected to an input stream with
+ * attributes corresponding to the specified flags.
+ */
+typedef enum {
+ AUDIO_INPUT_FLAG_NONE = 0x0, // no attributes
+ AUDIO_INPUT_FLAG_FAST = 0x1, // prefer an input that supports "fast tracks"
+ AUDIO_INPUT_FLAG_HW_HOTWORD = 0x2, // prefer an input that captures from hw hotword source
+} audio_input_flags_t;
+
+/* Additional information about compressed streams offloaded to
+ * hardware playback
+ * The version and size fields must be initialized by the caller by using
+ * one of the constants defined here.
+ */
+typedef struct {
+ uint16_t version; // version of the info structure
+ uint16_t size; // total size of the structure including version and size
+ uint32_t sample_rate; // sample rate in Hz
+ audio_channel_mask_t channel_mask; // channel mask
+ audio_format_t format; // audio format
+ audio_stream_type_t stream_type; // stream type
+ uint32_t bit_rate; // bit rate in bits per second
+ int64_t duration_us; // duration in microseconds, -1 if unknown
+ bool has_video; // true if stream is tied to a video stream
+ bool is_streaming; // true if streaming, false if local playback
+} audio_offload_info_t;
+
+#define AUDIO_MAKE_OFFLOAD_INFO_VERSION(maj,min) \
+ ((((maj) & 0xff) << 8) | ((min) & 0xff))
+
+#define AUDIO_OFFLOAD_INFO_VERSION_0_1 AUDIO_MAKE_OFFLOAD_INFO_VERSION(0, 1)
+#define AUDIO_OFFLOAD_INFO_VERSION_CURRENT AUDIO_OFFLOAD_INFO_VERSION_0_1
+
+static const audio_offload_info_t AUDIO_INFO_INITIALIZER = {
+ version: AUDIO_OFFLOAD_INFO_VERSION_CURRENT,
+ size: sizeof(audio_offload_info_t),
+ sample_rate: 0,
+ channel_mask: 0,
+ format: AUDIO_FORMAT_DEFAULT,
+ stream_type: AUDIO_STREAM_VOICE_CALL,
+ bit_rate: 0,
+ duration_us: 0,
+ has_video: false,
+ is_streaming: false
+};
+
+/* common audio stream configuration parameters
+ * You should memset() the entire structure to zero before use to
+ * ensure forward compatibility
+ */
+struct audio_config {
+ uint32_t sample_rate;
+ audio_channel_mask_t channel_mask;
+ audio_format_t format;
+ audio_offload_info_t offload_info;
+ size_t frame_count;
+};
+typedef struct audio_config audio_config_t;
+
+static const audio_config_t AUDIO_CONFIG_INITIALIZER = {
+ sample_rate: 0,
+ channel_mask: AUDIO_CHANNEL_NONE,
+ format: AUDIO_FORMAT_DEFAULT,
+ offload_info: {
+ version: AUDIO_OFFLOAD_INFO_VERSION_CURRENT,
+ size: sizeof(audio_offload_info_t),
+ sample_rate: 0,
+ channel_mask: 0,
+ format: AUDIO_FORMAT_DEFAULT,
+ stream_type: AUDIO_STREAM_VOICE_CALL,
+ bit_rate: 0,
+ duration_us: 0,
+ has_video: false,
+ is_streaming: false
+ },
+ frame_count: 0,
+};
+
+
+/* audio hw module handle functions or structures referencing a module */
+typedef int audio_module_handle_t;
+
+/******************************
+ * Volume control
+ *****************************/
+
+/* If the audio hardware supports gain control on some audio paths,
+ * the platform can expose them in the audio_policy.conf file. The audio HAL
+ * will then implement gain control functions that will use the following data
+ * structures. */
+
+/* Type of gain control exposed by an audio port */
+#define AUDIO_GAIN_MODE_JOINT 0x1 /* supports joint channel gain control */
+#define AUDIO_GAIN_MODE_CHANNELS 0x2 /* supports separate channel gain control */
+#define AUDIO_GAIN_MODE_RAMP 0x4 /* supports gain ramps */
+
+typedef uint32_t audio_gain_mode_t;
+
+
+/* An audio_gain struct is a representation of a gain stage.
+ * A gain stage is always attached to an audio port. */
+struct audio_gain {
+ audio_gain_mode_t mode; /* e.g. AUDIO_GAIN_MODE_JOINT */
+ audio_channel_mask_t channel_mask; /* channels which gain an be controlled.
+ N/A if AUDIO_GAIN_MODE_CHANNELS is not supported */
+ int min_value; /* minimum gain value in millibels */
+ int max_value; /* maximum gain value in millibels */
+ int default_value; /* default gain value in millibels */
+ unsigned int step_value; /* gain step in millibels */
+ unsigned int min_ramp_ms; /* minimum ramp duration in ms */
+ unsigned int max_ramp_ms; /* maximum ramp duration in ms */
+};
+
+/* The gain configuration structure is used to get or set the gain values of a
+ * given port */
+struct audio_gain_config {
+ int index; /* index of the corresponding audio_gain in the
+ audio_port gains[] table */
+ audio_gain_mode_t mode; /* mode requested for this command */
+ audio_channel_mask_t channel_mask; /* channels which gain value follows.
+ N/A in joint mode */
+ int values[sizeof(audio_channel_mask_t) * 8]; /* gain values in millibels
+ for each channel ordered from LSb to MSb in
+ channel mask. The number of values is 1 in joint
+ mode or popcount(channel_mask) */
+ unsigned int ramp_duration_ms; /* ramp duration in ms */
+};
+
+/******************************
+ * Routing control
+ *****************************/
+
+/* Types defined here are used to describe an audio source or sink at internal
+ * framework interfaces (audio policy, patch panel) or at the audio HAL.
+ * Sink and sources are grouped in a concept of “audio port” representing an
+ * audio end point at the edge of the system managed by the module exposing
+ * the interface. */
+
+/* Audio port role: either source or sink */
+typedef enum {
+ AUDIO_PORT_ROLE_NONE,
+ AUDIO_PORT_ROLE_SOURCE,
+ AUDIO_PORT_ROLE_SINK,
+} audio_port_role_t;
+
+/* Audio port type indicates if it is a session (e.g AudioTrack),
+ * a mix (e.g PlaybackThread output) or a physical device
+ * (e.g AUDIO_DEVICE_OUT_SPEAKER) */
+typedef enum {
+ AUDIO_PORT_TYPE_NONE,
+ AUDIO_PORT_TYPE_DEVICE,
+ AUDIO_PORT_TYPE_MIX,
+ AUDIO_PORT_TYPE_SESSION,
+} audio_port_type_t;
+
+/* Each port has a unique ID or handle allocated by policy manager */
+typedef int audio_port_handle_t;
+#define AUDIO_PORT_HANDLE_NONE 0
+
+
+/* maximum audio device address length */
+#define AUDIO_DEVICE_MAX_ADDRESS_LEN 32
+
+/* extension for audio port configuration structure when the audio port is a
+ * hardware device */
+struct audio_port_config_device_ext {
+ audio_module_handle_t hw_module; /* module the device is attached to */
+ audio_devices_t type; /* device type (e.g AUDIO_DEVICE_OUT_SPEAKER) */
+ char address[AUDIO_DEVICE_MAX_ADDRESS_LEN]; /* device address. "" if N/A */
+};
+
+/* extension for audio port configuration structure when the audio port is a
+ * sub mix */
+struct audio_port_config_mix_ext {
+ audio_module_handle_t hw_module; /* module the stream is attached to */
+ audio_io_handle_t handle; /* I/O handle of the input/output stream */
+ union {
+ //TODO: change use case for output streams: use strategy and mixer attributes
+ audio_stream_type_t stream;
+ audio_source_t source;
+ } usecase;
+};
+
+/* extension for audio port configuration structure when the audio port is an
+ * audio session */
+struct audio_port_config_session_ext {
+ audio_session_t session; /* audio session */
+};
+
+/* Flags indicating which fields are to be considered in struct audio_port_config */
+#define AUDIO_PORT_CONFIG_SAMPLE_RATE 0x1
+#define AUDIO_PORT_CONFIG_CHANNEL_MASK 0x2
+#define AUDIO_PORT_CONFIG_FORMAT 0x4
+#define AUDIO_PORT_CONFIG_GAIN 0x8
+#define AUDIO_PORT_CONFIG_ALL (AUDIO_PORT_CONFIG_SAMPLE_RATE | \
+ AUDIO_PORT_CONFIG_CHANNEL_MASK | \
+ AUDIO_PORT_CONFIG_FORMAT | \
+ AUDIO_PORT_CONFIG_GAIN)
+
+/* audio port configuration structure used to specify a particular configuration of
+ * an audio port */
+struct audio_port_config {
+ audio_port_handle_t id; /* port unique ID */
+ audio_port_role_t role; /* sink or source */
+ audio_port_type_t type; /* device, mix ... */
+ unsigned int config_mask; /* e.g AUDIO_PORT_CONFIG_ALL */
+ unsigned int sample_rate; /* sampling rate in Hz */
+ audio_channel_mask_t channel_mask; /* channel mask if applicable */
+ audio_format_t format; /* format if applicable */
+ struct audio_gain_config gain; /* gain to apply if applicable */
+ union {
+ struct audio_port_config_device_ext device; /* device specific info */
+ struct audio_port_config_mix_ext mix; /* mix specific info */
+ struct audio_port_config_session_ext session; /* session specific info */
+ } ext;
+};
+
+
+/* max number of sampling rates in audio port */
+#define AUDIO_PORT_MAX_SAMPLING_RATES 16
+/* max number of channel masks in audio port */
+#define AUDIO_PORT_MAX_CHANNEL_MASKS 16
+/* max number of audio formats in audio port */
+#define AUDIO_PORT_MAX_FORMATS 16
+/* max number of gain controls in audio port */
+#define AUDIO_PORT_MAX_GAINS 16
+
+/* extension for audio port structure when the audio port is a hardware device */
+struct audio_port_device_ext {
+ audio_module_handle_t hw_module; /* module the device is attached to */
+ audio_devices_t type; /* device type (e.g AUDIO_DEVICE_OUT_SPEAKER) */
+ char address[AUDIO_DEVICE_MAX_ADDRESS_LEN];
+};
+
+/* Latency class of the audio mix */
+typedef enum {
+ AUDIO_LATENCY_LOW,
+ AUDIO_LATENCY_NORMAL,
+} audio_mix_latency_class_t;
+
+/* extension for audio port structure when the audio port is a sub mix */
+struct audio_port_mix_ext {
+ audio_module_handle_t hw_module; /* module the stream is attached to */
+ audio_io_handle_t handle; /* I/O handle of the input.output stream */
+ audio_mix_latency_class_t latency_class; /* latency class */
+ // other attributes: routing strategies
+};
+
+/* extension for audio port structure when the audio port is an audio session */
+struct audio_port_session_ext {
+ audio_session_t session; /* audio session */
+};
+
+
+struct audio_port {
+ audio_port_handle_t id; /* port unique ID */
+ audio_port_role_t role; /* sink or source */
+ audio_port_type_t type; /* device, mix ... */
+ unsigned int num_sample_rates; /* number of sampling rates in following array */
+ unsigned int sample_rates[AUDIO_PORT_MAX_SAMPLING_RATES];
+ unsigned int num_channel_masks; /* number of channel masks in following array */
+ audio_channel_mask_t channel_masks[AUDIO_PORT_MAX_CHANNEL_MASKS];
+ unsigned int num_formats; /* number of formats in following array */
+ audio_format_t formats[AUDIO_PORT_MAX_FORMATS];
+ unsigned int num_gains; /* number of gains in following array */
+ struct audio_gain gains[AUDIO_PORT_MAX_GAINS];
+ struct audio_port_config active_config; /* current audio port configuration */
+ union {
+ struct audio_port_device_ext device;
+ struct audio_port_mix_ext mix;
+ struct audio_port_session_ext session;
+ } ext;
+};
+
+/* An audio patch represents a connection between one or more source ports and
+ * one or more sink ports. Patches are connected and disconnected by audio policy manager or by
+ * applications via framework APIs.
+ * Each patch is identified by a handle at the interface used to create that patch. For instance,
+ * when a patch is created by the audio HAL, the HAL allocates and returns a handle.
+ * This handle is unique to a given audio HAL hardware module.
+ * But the same patch receives another system wide unique handle allocated by the framework.
+ * This unique handle is used for all transactions inside the framework.
+ */
+typedef int audio_patch_handle_t;
+#define AUDIO_PATCH_HANDLE_NONE 0
+
+#define AUDIO_PATCH_PORTS_MAX 16
+
+struct audio_patch {
+ audio_patch_handle_t id; /* patch unique ID */
+ unsigned int num_sources; /* number of sources in following array */
+ struct audio_port_config sources[AUDIO_PATCH_PORTS_MAX];
+ unsigned int num_sinks; /* number of sinks in following array */
+ struct audio_port_config sinks[AUDIO_PATCH_PORTS_MAX];
+};
+
+
+
+/* a HW synchronization source returned by the audio HAL */
+typedef uint32_t audio_hw_sync_t;
+
+/* an invalid HW synchronization source indicating an error */
+#define AUDIO_HW_SYNC_INVALID 0
+
+static inline bool audio_is_output_device(audio_devices_t device)
+{
+ if (((device & AUDIO_DEVICE_BIT_IN) == 0) &&
+ (popcount(device) == 1) && ((device & ~AUDIO_DEVICE_OUT_ALL) == 0))
+ return true;
+ else
+ return false;
+}
+
+static inline bool audio_is_input_device(audio_devices_t device)
+{
+ if ((device & AUDIO_DEVICE_BIT_IN) != 0) {
+ device &= ~AUDIO_DEVICE_BIT_IN;
+ if ((popcount(device) == 1) && ((device & ~AUDIO_DEVICE_IN_ALL) == 0))
+ return true;
+ }
+ return false;
+}
+
+static inline bool audio_is_output_devices(audio_devices_t device)
+{
+ return (device & AUDIO_DEVICE_BIT_IN) == 0;
+}
+
+static inline bool audio_is_a2dp_in_device(audio_devices_t device)
+{
+ if ((device & AUDIO_DEVICE_BIT_IN) != 0) {
+ device &= ~AUDIO_DEVICE_BIT_IN;
+ if ((popcount(device) == 1) && (device & AUDIO_DEVICE_IN_BLUETOOTH_A2DP))
+ return true;
+ }
+ return false;
+}
+
+static inline bool audio_is_a2dp_out_device(audio_devices_t device)
+{
+ if ((popcount(device) == 1) && (device & AUDIO_DEVICE_OUT_ALL_A2DP))
+ return true;
+ else
+ return false;
+}
+
+// Deprecated - use audio_is_a2dp_out_device() instead
+static inline bool audio_is_a2dp_device(audio_devices_t device)
+{
+ return audio_is_a2dp_out_device(device);
+}
+
+static inline bool audio_is_bluetooth_sco_device(audio_devices_t device)
+{
+ if ((device & AUDIO_DEVICE_BIT_IN) == 0) {
+ if ((popcount(device) == 1) && ((device & ~AUDIO_DEVICE_OUT_ALL_SCO) == 0))
+ return true;
+ } else {
+ device &= ~AUDIO_DEVICE_BIT_IN;
+ if ((popcount(device) == 1) && ((device & ~AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) == 0))
+ return true;
+ }
+
+ return false;
+}
+
+static inline bool audio_is_usb_out_device(audio_devices_t device)
+{
+ return ((popcount(device) == 1) && (device & AUDIO_DEVICE_OUT_ALL_USB));
+}
+
+static inline bool audio_is_usb_in_device(audio_devices_t device)
+{
+ if ((device & AUDIO_DEVICE_BIT_IN) != 0) {
+ device &= ~AUDIO_DEVICE_BIT_IN;
+ if (popcount(device) == 1 && (device & AUDIO_DEVICE_IN_ALL_USB) != 0)
+ return true;
+ }
+ return false;
+}
+
+/* OBSOLETE - use audio_is_usb_out_device() instead. */
+static inline bool audio_is_usb_device(audio_devices_t device)
+{
+ return audio_is_usb_out_device(device);
+}
+
+static inline bool audio_is_remote_submix_device(audio_devices_t device)
+{
+ if ((audio_is_output_devices(device) &&
+ (device & AUDIO_DEVICE_OUT_REMOTE_SUBMIX) == AUDIO_DEVICE_OUT_REMOTE_SUBMIX)
+ || (!audio_is_output_devices(device) &&
+ (device & AUDIO_DEVICE_IN_REMOTE_SUBMIX) == AUDIO_DEVICE_IN_REMOTE_SUBMIX))
+ return true;
+ else
+ return false;
+}
+
+/* Returns true if:
+ * representation is valid, and
+ * there is at least one channel bit set which _could_ correspond to an input channel, and
+ * there are no channel bits set which could _not_ correspond to an input channel.
+ * Otherwise returns false.
+ */
+static inline bool audio_is_input_channel(audio_channel_mask_t channel)
+{
+ uint32_t bits = audio_channel_mask_get_bits(channel);
+ switch (audio_channel_mask_get_representation(channel)) {
+ case AUDIO_CHANNEL_REPRESENTATION_POSITION:
+ if (bits & ~AUDIO_CHANNEL_IN_ALL) {
+ bits = 0;
+ }
+ // fall through
+ case AUDIO_CHANNEL_REPRESENTATION_INDEX:
+ return bits != 0;
+ default:
+ return false;
+ }
+}
+
+/* Returns true if:
+ * representation is valid, and
+ * there is at least one channel bit set which _could_ correspond to an output channel, and
+ * there are no channel bits set which could _not_ correspond to an output channel.
+ * Otherwise returns false.
+ */
+static inline bool audio_is_output_channel(audio_channel_mask_t channel)
+{
+ uint32_t bits = audio_channel_mask_get_bits(channel);
+ switch (audio_channel_mask_get_representation(channel)) {
+ case AUDIO_CHANNEL_REPRESENTATION_POSITION:
+ if (bits & ~AUDIO_CHANNEL_OUT_ALL) {
+ bits = 0;
+ }
+ // fall through
+ case AUDIO_CHANNEL_REPRESENTATION_INDEX:
+ return bits != 0;
+ default:
+ return false;
+ }
+}
+
+/* Returns the number of channels from an input channel mask,
+ * used in the context of audio input or recording.
+ * If a channel bit is set which could _not_ correspond to an input channel,
+ * it is excluded from the count.
+ * Returns zero if the representation is invalid.
+ */
+static inline uint32_t audio_channel_count_from_in_mask(audio_channel_mask_t channel)
+{
+ uint32_t bits = audio_channel_mask_get_bits(channel);
+ switch (audio_channel_mask_get_representation(channel)) {
+ case AUDIO_CHANNEL_REPRESENTATION_POSITION:
+ // TODO: We can now merge with from_out_mask and remove anding
+ bits &= AUDIO_CHANNEL_IN_ALL;
+ // fall through
+ case AUDIO_CHANNEL_REPRESENTATION_INDEX:
+ return popcount(bits);
+ default:
+ return 0;
+ }
+}
+
+/* Returns the number of channels from an output channel mask,
+ * used in the context of audio output or playback.
+ * If a channel bit is set which could _not_ correspond to an output channel,
+ * it is excluded from the count.
+ * Returns zero if the representation is invalid.
+ */
+static inline uint32_t audio_channel_count_from_out_mask(audio_channel_mask_t channel)
+{
+ uint32_t bits = audio_channel_mask_get_bits(channel);
+ switch (audio_channel_mask_get_representation(channel)) {
+ case AUDIO_CHANNEL_REPRESENTATION_POSITION:
+ // TODO: We can now merge with from_in_mask and remove anding
+ bits &= AUDIO_CHANNEL_OUT_ALL;
+ // fall through
+ case AUDIO_CHANNEL_REPRESENTATION_INDEX:
+ return popcount(bits);
+ default:
+ return 0;
+ }
+}
+
+/* Derive an output channel mask for position assignment from a channel count.
+ * This is to be used when the content channel mask is unknown. The 1, 2, 4, 5, 6, 7 and 8 channel
+ * cases are mapped to the standard game/home-theater layouts, but note that 4 is mapped to quad,
+ * and not stereo + FC + mono surround. A channel count of 3 is arbitrarily mapped to stereo + FC
+ * for continuity with stereo.
+ * Returns the matching channel mask,
+ * or AUDIO_CHANNEL_NONE if the channel count is zero,
+ * or AUDIO_CHANNEL_INVALID if the channel count exceeds that of the
+ * configurations for which a default output channel mask is defined.
+ */
+static inline audio_channel_mask_t audio_channel_out_mask_from_count(uint32_t channel_count)
+{
+ uint32_t bits;
+ switch (channel_count) {
+ case 0:
+ return AUDIO_CHANNEL_NONE;
+ case 1:
+ bits = AUDIO_CHANNEL_OUT_MONO;
+ break;
+ case 2:
+ bits = AUDIO_CHANNEL_OUT_STEREO;
+ break;
+ case 3:
+ bits = AUDIO_CHANNEL_OUT_STEREO | AUDIO_CHANNEL_OUT_FRONT_CENTER;
+ break;
+ case 4: // 4.0
+ bits = AUDIO_CHANNEL_OUT_QUAD;
+ break;
+ case 5: // 5.0
+ bits = AUDIO_CHANNEL_OUT_QUAD | AUDIO_CHANNEL_OUT_FRONT_CENTER;
+ break;
+ case 6: // 5.1
+ bits = AUDIO_CHANNEL_OUT_5POINT1;
+ break;
+ case 7: // 6.1
+ bits = AUDIO_CHANNEL_OUT_5POINT1 | AUDIO_CHANNEL_OUT_BACK_CENTER;
+ break;
+ case 8:
+ bits = AUDIO_CHANNEL_OUT_7POINT1;
+ break;
+ default:
+ return AUDIO_CHANNEL_INVALID;
+ }
+ return audio_channel_mask_from_representation_and_bits(
+ AUDIO_CHANNEL_REPRESENTATION_POSITION, bits);
+}
+
+/* Derive an input channel mask for position assignment from a channel count.
+ * Currently handles only mono and stereo.
+ * Returns the matching channel mask,
+ * or AUDIO_CHANNEL_NONE if the channel count is zero,
+ * or AUDIO_CHANNEL_INVALID if the channel count exceeds that of the
+ * configurations for which a default input channel mask is defined.
+ */
+static inline audio_channel_mask_t audio_channel_in_mask_from_count(uint32_t channel_count)
+{
+ uint32_t bits;
+ switch (channel_count) {
+ case 0:
+ return AUDIO_CHANNEL_NONE;
+ case 1:
+ bits = AUDIO_CHANNEL_IN_MONO;
+ break;
+ case 2:
+ bits = AUDIO_CHANNEL_IN_STEREO;
+ break;
+ default:
+ return AUDIO_CHANNEL_INVALID;
+ }
+ return audio_channel_mask_from_representation_and_bits(
+ AUDIO_CHANNEL_REPRESENTATION_POSITION, bits);
+}
+
+/* Derive a channel mask for index assignment from a channel count.
+ * Returns the matching channel mask,
+ * or AUDIO_CHANNEL_NONE if the channel count is zero,
+ * or AUDIO_CHANNEL_INVALID if the channel count exceeds AUDIO_CHANNEL_COUNT_MAX.
+ */
+static inline audio_channel_mask_t audio_channel_mask_for_index_assignment_from_count(
+ uint32_t channel_count)
+{
+ if (channel_count == 0) {
+ return AUDIO_CHANNEL_NONE;
+ }
+ if (channel_count > AUDIO_CHANNEL_COUNT_MAX) {
+ return AUDIO_CHANNEL_INVALID;
+ }
+ uint32_t bits = (1 << channel_count) - 1;
+ return audio_channel_mask_from_representation_and_bits(
+ AUDIO_CHANNEL_REPRESENTATION_INDEX, bits);
+}
+
+static inline bool audio_is_valid_format(audio_format_t format)
+{
+ switch (format & AUDIO_FORMAT_MAIN_MASK) {
+ case AUDIO_FORMAT_PCM:
+ switch (format) {
+ case AUDIO_FORMAT_PCM_16_BIT:
+ case AUDIO_FORMAT_PCM_8_BIT:
+ case AUDIO_FORMAT_PCM_32_BIT:
+ case AUDIO_FORMAT_PCM_8_24_BIT:
+ case AUDIO_FORMAT_PCM_FLOAT:
+ case AUDIO_FORMAT_PCM_24_BIT_PACKED:
+ return true;
+ default:
+ return false;
+ }
+ /* not reached */
+ case AUDIO_FORMAT_MP3:
+ case AUDIO_FORMAT_AMR_NB:
+ case AUDIO_FORMAT_AMR_WB:
+ case AUDIO_FORMAT_AAC:
+ case AUDIO_FORMAT_HE_AAC_V1:
+ case AUDIO_FORMAT_HE_AAC_V2:
+ case AUDIO_FORMAT_VORBIS:
+ case AUDIO_FORMAT_OPUS:
+ case AUDIO_FORMAT_AC3:
+ case AUDIO_FORMAT_E_AC3:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static inline bool audio_is_linear_pcm(audio_format_t format)
+{
+ return ((format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_PCM);
+}
+
+static inline size_t audio_bytes_per_sample(audio_format_t format)
+{
+ size_t size = 0;
+
+ switch (format) {
+ case AUDIO_FORMAT_PCM_32_BIT:
+ case AUDIO_FORMAT_PCM_8_24_BIT:
+ size = sizeof(int32_t);
+ break;
+ case AUDIO_FORMAT_PCM_24_BIT_PACKED:
+ size = sizeof(uint8_t) * 3;
+ break;
+ case AUDIO_FORMAT_PCM_16_BIT:
+ size = sizeof(int16_t);
+ break;
+ case AUDIO_FORMAT_PCM_8_BIT:
+ size = sizeof(uint8_t);
+ break;
+ case AUDIO_FORMAT_PCM_FLOAT:
+ size = sizeof(float);
+ break;
+ default:
+ break;
+ }
+ return size;
+}
+
+/* converts device address to string sent to audio HAL via set_parameters */
+static inline char *audio_device_address_to_parameter(audio_devices_t device, const char *address)
+{
+ const size_t kSize = AUDIO_DEVICE_MAX_ADDRESS_LEN + sizeof("a2dp_sink_address=");
+ char param[kSize];
+
+ if (device & AUDIO_DEVICE_OUT_ALL_A2DP)
+ snprintf(param, kSize, "%s=%s", "a2dp_sink_address", address);
+ else if (device & AUDIO_DEVICE_OUT_REMOTE_SUBMIX)
+ snprintf(param, kSize, "%s=%s", "mix", address);
+ else
+ snprintf(param, kSize, "%s", address);
+
+ return strdup(param);
+}
+
+
+__END_DECLS
+
+#endif // ANDROID_AUDIO_CORE_H
diff --git a/include/system/audio_policy.h b/include/system/audio_policy.h
new file mode 100644
index 0000000..2881104
--- /dev/null
+++ b/include/system/audio_policy.h
@@ -0,0 +1,103 @@
+/*
+ * 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.
+ */
+
+
+#ifndef ANDROID_AUDIO_POLICY_CORE_H
+#define ANDROID_AUDIO_POLICY_CORE_H
+
+#include <stdint.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#include <cutils/bitops.h>
+
+__BEGIN_DECLS
+
+/* The enums were moved here mostly from
+ * frameworks/base/include/media/AudioSystem.h
+ */
+
+/* device categories used for audio_policy->set_force_use() */
+typedef enum {
+ AUDIO_POLICY_FORCE_NONE,
+ AUDIO_POLICY_FORCE_SPEAKER,
+ AUDIO_POLICY_FORCE_HEADPHONES,
+ AUDIO_POLICY_FORCE_BT_SCO,
+ AUDIO_POLICY_FORCE_BT_A2DP,
+ AUDIO_POLICY_FORCE_WIRED_ACCESSORY,
+ AUDIO_POLICY_FORCE_BT_CAR_DOCK,
+ AUDIO_POLICY_FORCE_BT_DESK_DOCK,
+ AUDIO_POLICY_FORCE_ANALOG_DOCK,
+ AUDIO_POLICY_FORCE_DIGITAL_DOCK,
+ AUDIO_POLICY_FORCE_NO_BT_A2DP, /* A2DP sink is not preferred to speaker or wired HS */
+ AUDIO_POLICY_FORCE_SYSTEM_ENFORCED,
+ AUDIO_POLICY_FORCE_HDMI_SYSTEM_AUDIO_ENFORCED,
+
+ AUDIO_POLICY_FORCE_CFG_CNT,
+ AUDIO_POLICY_FORCE_CFG_MAX = AUDIO_POLICY_FORCE_CFG_CNT - 1,
+
+ AUDIO_POLICY_FORCE_DEFAULT = AUDIO_POLICY_FORCE_NONE,
+} audio_policy_forced_cfg_t;
+
+/* usages used for audio_policy->set_force_use() */
+typedef enum {
+ AUDIO_POLICY_FORCE_FOR_COMMUNICATION,
+ AUDIO_POLICY_FORCE_FOR_MEDIA,
+ AUDIO_POLICY_FORCE_FOR_RECORD,
+ AUDIO_POLICY_FORCE_FOR_DOCK,
+ AUDIO_POLICY_FORCE_FOR_SYSTEM,
+ AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO,
+
+ AUDIO_POLICY_FORCE_USE_CNT,
+ AUDIO_POLICY_FORCE_USE_MAX = AUDIO_POLICY_FORCE_USE_CNT - 1,
+} audio_policy_force_use_t;
+
+/* device connection states used for audio_policy->set_device_connection_state()
+ */
+typedef enum {
+ AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
+ AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
+
+ AUDIO_POLICY_DEVICE_STATE_CNT,
+ AUDIO_POLICY_DEVICE_STATE_MAX = AUDIO_POLICY_DEVICE_STATE_CNT - 1,
+} audio_policy_dev_state_t;
+
+typedef enum {
+ /* Used to generate a tone to notify the user of a
+ * notification/alarm/ringtone while they are in a call. */
+ AUDIO_POLICY_TONE_IN_CALL_NOTIFICATION = 0,
+
+ AUDIO_POLICY_TONE_CNT,
+ AUDIO_POLICY_TONE_MAX = AUDIO_POLICY_TONE_CNT - 1,
+} audio_policy_tone_t;
+
+
+static inline bool audio_is_low_visibility(audio_stream_type_t stream)
+{
+ switch (stream) {
+ case AUDIO_STREAM_SYSTEM:
+ case AUDIO_STREAM_NOTIFICATION:
+ case AUDIO_STREAM_RING:
+ return true;
+ default:
+ return false;
+ }
+}
+
+
+__END_DECLS
+
+#endif // ANDROID_AUDIO_POLICY_CORE_H
diff --git a/include/system/camera.h b/include/system/camera.h
new file mode 100644
index 0000000..7a4dd53
--- /dev/null
+++ b/include/system/camera.h
@@ -0,0 +1,277 @@
+/*
+ * 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.
+ */
+
+#ifndef SYSTEM_CORE_INCLUDE_ANDROID_CAMERA_H
+#define SYSTEM_CORE_INCLUDE_ANDROID_CAMERA_H
+
+#include <stdint.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <cutils/native_handle.h>
+#include <hardware/hardware.h>
+#include <hardware/gralloc.h>
+
+__BEGIN_DECLS
+
+/**
+ * A set of bit masks for specifying how the received preview frames are
+ * handled before the previewCallback() call.
+ *
+ * The least significant 3 bits of an "int" value are used for this purpose:
+ *
+ * ..... 0 0 0
+ * ^ ^ ^
+ * | | |---------> determine whether the callback is enabled or not
+ * | |-----------> determine whether the callback is one-shot or not
+ * |-------------> determine whether the frame is copied out or not
+ *
+ * WARNING: When a frame is sent directly without copying, it is the frame
+ * receiver's responsiblity to make sure that the frame data won't get
+ * corrupted by subsequent preview frames filled by the camera. This flag is
+ * recommended only when copying out data brings significant performance price
+ * and the handling/processing of the received frame data is always faster than
+ * the preview frame rate so that data corruption won't occur.
+ *
+ * For instance,
+ * 1. 0x00 disables the callback. In this case, copy out and one shot bits
+ * are ignored.
+ * 2. 0x01 enables a callback without copying out the received frames. A
+ * typical use case is the Camcorder application to avoid making costly
+ * frame copies.
+ * 3. 0x05 is enabling a callback with frame copied out repeatedly. A typical
+ * use case is the Camera application.
+ * 4. 0x07 is enabling a callback with frame copied out only once. A typical
+ * use case is the Barcode scanner application.
+ */
+
+enum {
+ CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK = 0x01,
+ CAMERA_FRAME_CALLBACK_FLAG_ONE_SHOT_MASK = 0x02,
+ CAMERA_FRAME_CALLBACK_FLAG_COPY_OUT_MASK = 0x04,
+ /** Typical use cases */
+ CAMERA_FRAME_CALLBACK_FLAG_NOOP = 0x00,
+ CAMERA_FRAME_CALLBACK_FLAG_CAMCORDER = 0x01,
+ CAMERA_FRAME_CALLBACK_FLAG_CAMERA = 0x05,
+ CAMERA_FRAME_CALLBACK_FLAG_BARCODE_SCANNER = 0x07
+};
+
+/** msgType in notifyCallback and dataCallback functions */
+enum {
+ CAMERA_MSG_ERROR = 0x0001, // notifyCallback
+ CAMERA_MSG_SHUTTER = 0x0002, // notifyCallback
+ CAMERA_MSG_FOCUS = 0x0004, // notifyCallback
+ CAMERA_MSG_ZOOM = 0x0008, // notifyCallback
+ CAMERA_MSG_PREVIEW_FRAME = 0x0010, // dataCallback
+ CAMERA_MSG_VIDEO_FRAME = 0x0020, // data_timestamp_callback
+ CAMERA_MSG_POSTVIEW_FRAME = 0x0040, // dataCallback
+ CAMERA_MSG_RAW_IMAGE = 0x0080, // dataCallback
+ CAMERA_MSG_COMPRESSED_IMAGE = 0x0100, // dataCallback
+ CAMERA_MSG_RAW_IMAGE_NOTIFY = 0x0200, // dataCallback
+ // Preview frame metadata. This can be combined with
+ // CAMERA_MSG_PREVIEW_FRAME in dataCallback. For example, the apps can
+ // request FRAME and METADATA. Or the apps can request only FRAME or only
+ // METADATA.
+ CAMERA_MSG_PREVIEW_METADATA = 0x0400, // dataCallback
+ // Notify on autofocus start and stop. This is useful in continuous
+ // autofocus - FOCUS_MODE_CONTINUOUS_VIDEO and FOCUS_MODE_CONTINUOUS_PICTURE.
+ CAMERA_MSG_FOCUS_MOVE = 0x0800, // notifyCallback
+ CAMERA_MSG_ALL_MSGS = 0xFFFF
+};
+
+/** cmdType in sendCommand functions */
+enum {
+ CAMERA_CMD_START_SMOOTH_ZOOM = 1,
+ CAMERA_CMD_STOP_SMOOTH_ZOOM = 2,
+
+ /**
+ * Set the clockwise rotation of preview display (setPreviewDisplay) in
+ * degrees. This affects the preview frames and the picture displayed after
+ * snapshot. This method is useful for portrait mode applications. Note
+ * that preview display of front-facing cameras is flipped horizontally
+ * before the rotation, that is, the image is reflected along the central
+ * vertical axis of the camera sensor. So the users can see themselves as
+ * looking into a mirror.
+ *
+ * This does not affect the order of byte array of
+ * CAMERA_MSG_PREVIEW_FRAME, CAMERA_MSG_VIDEO_FRAME,
+ * CAMERA_MSG_POSTVIEW_FRAME, CAMERA_MSG_RAW_IMAGE, or
+ * CAMERA_MSG_COMPRESSED_IMAGE. This is allowed to be set during preview
+ * since API level 14.
+ */
+ CAMERA_CMD_SET_DISPLAY_ORIENTATION = 3,
+
+ /**
+ * cmdType to disable/enable shutter sound. In sendCommand passing arg1 =
+ * 0 will disable, while passing arg1 = 1 will enable the shutter sound.
+ */
+ CAMERA_CMD_ENABLE_SHUTTER_SOUND = 4,
+
+ /* cmdType to play recording sound */
+ CAMERA_CMD_PLAY_RECORDING_SOUND = 5,
+
+ /**
+ * Start the face detection. This should be called after preview is started.
+ * The camera will notify the listener of CAMERA_MSG_FACE and the detected
+ * faces in the preview frame. The detected faces may be the same as the
+ * previous ones. Apps should call CAMERA_CMD_STOP_FACE_DETECTION to stop
+ * the face detection. This method is supported if CameraParameters
+ * KEY_MAX_NUM_HW_DETECTED_FACES or KEY_MAX_NUM_SW_DETECTED_FACES is
+ * bigger than 0. Hardware and software face detection should not be running
+ * at the same time. If the face detection has started, apps should not send
+ * this again.
+ *
+ * In hardware face detection mode, CameraParameters KEY_WHITE_BALANCE,
+ * KEY_FOCUS_AREAS and KEY_METERING_AREAS have no effect.
+ *
+ * arg1 is the face detection type. It can be CAMERA_FACE_DETECTION_HW or
+ * CAMERA_FACE_DETECTION_SW. If the type of face detection requested is not
+ * supported, the HAL must return BAD_VALUE.
+ */
+ CAMERA_CMD_START_FACE_DETECTION = 6,
+
+ /**
+ * Stop the face detection.
+ */
+ CAMERA_CMD_STOP_FACE_DETECTION = 7,
+
+ /**
+ * Enable/disable focus move callback (CAMERA_MSG_FOCUS_MOVE). Passing
+ * arg1 = 0 will disable, while passing arg1 = 1 will enable the callback.
+ */
+ CAMERA_CMD_ENABLE_FOCUS_MOVE_MSG = 8,
+
+ /**
+ * Ping camera service to see if camera hardware is released.
+ *
+ * When any camera method returns error, the client can use ping command
+ * to see if the camera has been taken away by other clients. If the result
+ * is NO_ERROR, it means the camera hardware is not released. If the result
+ * is not NO_ERROR, the camera has been released and the existing client
+ * can silently finish itself or show a dialog.
+ */
+ CAMERA_CMD_PING = 9,
+
+ /**
+ * Configure the number of video buffers used for recording. The intended
+ * video buffer count for recording is passed as arg1, which must be
+ * greater than 0. This command must be sent before recording is started.
+ * This command returns INVALID_OPERATION error if it is sent after video
+ * recording is started, or the command is not supported at all. This
+ * command also returns a BAD_VALUE error if the intended video buffer
+ * count is non-positive or too big to be realized.
+ */
+ CAMERA_CMD_SET_VIDEO_BUFFER_COUNT = 10,
+};
+
+/** camera fatal errors */
+enum {
+ CAMERA_ERROR_UNKNOWN = 1,
+ /**
+ * Camera was released because another client has connected to the camera.
+ * The original client should call Camera::disconnect immediately after
+ * getting this notification. Otherwise, the camera will be released by
+ * camera service in a short time. The client should not call any method
+ * (except disconnect and sending CAMERA_CMD_PING) after getting this.
+ */
+ CAMERA_ERROR_RELEASED = 2,
+ CAMERA_ERROR_SERVER_DIED = 100
+};
+
+enum {
+ /** The facing of the camera is opposite to that of the screen. */
+ CAMERA_FACING_BACK = 0,
+ /** The facing of the camera is the same as that of the screen. */
+ CAMERA_FACING_FRONT = 1
+};
+
+enum {
+ /** Hardware face detection. It does not use much CPU. */
+ CAMERA_FACE_DETECTION_HW = 0,
+ /**
+ * Software face detection. It uses some CPU. Applications must use
+ * Camera.setPreviewTexture for preview in this mode.
+ */
+ CAMERA_FACE_DETECTION_SW = 1
+};
+
+/**
+ * The information of a face from camera face detection.
+ */
+typedef struct camera_face {
+ /**
+ * Bounds of the face [left, top, right, bottom]. (-1000, -1000) represents
+ * the top-left of the camera field of view, and (1000, 1000) represents the
+ * bottom-right of the field of view. The width and height cannot be 0 or
+ * negative. This is supported by both hardware and software face detection.
+ *
+ * The direction is relative to the sensor orientation, that is, what the
+ * sensor sees. The direction is not affected by the rotation or mirroring
+ * of CAMERA_CMD_SET_DISPLAY_ORIENTATION.
+ */
+ int32_t rect[4];
+
+ /**
+ * The confidence level of the face. The range is 1 to 100. 100 is the
+ * highest confidence. This is supported by both hardware and software
+ * face detection.
+ */
+ int32_t score;
+
+ /**
+ * An unique id per face while the face is visible to the tracker. If
+ * the face leaves the field-of-view and comes back, it will get a new
+ * id. If the value is 0, id is not supported.
+ */
+ int32_t id;
+
+ /**
+ * The coordinates of the center of the left eye. The range is -1000 to
+ * 1000. -2000, -2000 if this is not supported.
+ */
+ int32_t left_eye[2];
+
+ /**
+ * The coordinates of the center of the right eye. The range is -1000 to
+ * 1000. -2000, -2000 if this is not supported.
+ */
+ int32_t right_eye[2];
+
+ /**
+ * The coordinates of the center of the mouth. The range is -1000 to 1000.
+ * -2000, -2000 if this is not supported.
+ */
+ int32_t mouth[2];
+
+} camera_face_t;
+
+/**
+ * The metadata of the frame data.
+ */
+typedef struct camera_frame_metadata {
+ /**
+ * The number of detected faces in the frame.
+ */
+ int32_t number_of_faces;
+
+ /**
+ * An array of the detected faces. The length is number_of_faces.
+ */
+ camera_face_t *faces;
+} camera_frame_metadata_t;
+
+__END_DECLS
+
+#endif /* SYSTEM_CORE_INCLUDE_ANDROID_CAMERA_H */
diff --git a/include/system/graphics.h b/include/system/graphics.h
new file mode 100644
index 0000000..efd48cb
--- /dev/null
+++ b/include/system/graphics.h
@@ -0,0 +1,649 @@
+/*
+ * 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.
+ */
+
+#ifndef SYSTEM_CORE_INCLUDE_ANDROID_GRAPHICS_H
+#define SYSTEM_CORE_INCLUDE_ANDROID_GRAPHICS_H
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * If the HAL needs to create service threads to handle graphics related
+ * tasks, these threads need to run at HAL_PRIORITY_URGENT_DISPLAY priority
+ * if they can block the main rendering thread in any way.
+ *
+ * the priority of the current thread can be set with:
+ *
+ * #include <sys/resource.h>
+ * setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY);
+ *
+ */
+
+#define HAL_PRIORITY_URGENT_DISPLAY (-8)
+
+/**
+ * pixel format definitions
+ */
+
+enum {
+ /*
+ * "linear" color pixel formats:
+ *
+ * When used with ANativeWindow, the dataSpace field describes the color
+ * space of the buffer.
+ *
+ * The color space determines, for example, if the formats are linear or
+ * gamma-corrected; or whether any special operations are performed when
+ * reading or writing into a buffer in one of these formats.
+ */
+ HAL_PIXEL_FORMAT_RGBA_8888 = 1,
+ HAL_PIXEL_FORMAT_RGBX_8888 = 2,
+ HAL_PIXEL_FORMAT_RGB_888 = 3,
+ HAL_PIXEL_FORMAT_RGB_565 = 4,
+ HAL_PIXEL_FORMAT_BGRA_8888 = 5,
+
+ // Deprecated sRGB formats for source code compatibility
+ // Not for use in new code
+ HAL_PIXEL_FORMAT_sRGB_A_8888 = 0xC,
+ HAL_PIXEL_FORMAT_sRGB_X_8888 = 0xD,
+
+ /*
+ * 0x100 - 0x1FF
+ *
+ * This range is reserved for pixel formats that are specific to the HAL
+ * implementation. Implementations can use any value in this range to
+ * communicate video pixel formats between their HAL modules. These formats
+ * must not have an alpha channel. Additionally, an EGLimage created from a
+ * gralloc buffer of one of these formats must be supported for use with the
+ * GL_OES_EGL_image_external OpenGL ES extension.
+ */
+
+ /*
+ * Android YUV format:
+ *
+ * This format is exposed outside of the HAL to software decoders and
+ * applications. EGLImageKHR must support it in conjunction with the
+ * OES_EGL_image_external extension.
+ *
+ * YV12 is a 4:2:0 YCrCb planar format comprised of a WxH Y plane followed
+ * by (W/2) x (H/2) Cr and Cb planes.
+ *
+ * This format assumes
+ * - an even width
+ * - an even height
+ * - a horizontal stride multiple of 16 pixels
+ * - a vertical stride equal to the height
+ *
+ * y_size = stride * height
+ * c_stride = ALIGN(stride/2, 16)
+ * c_size = c_stride * height/2
+ * size = y_size + c_size * 2
+ * cr_offset = y_size
+ * cb_offset = y_size + c_size
+ *
+ * When used with ANativeWindow, the dataSpace field describes the color
+ * space of the buffer.
+ */
+ HAL_PIXEL_FORMAT_YV12 = 0x32315659, // YCrCb 4:2:0 Planar
+
+
+ /*
+ * Android Y8 format:
+ *
+ * This format is exposed outside of the HAL to the framework.
+ * The expected gralloc usage flags are SW_* and HW_CAMERA_*,
+ * and no other HW_ flags will be used.
+ *
+ * Y8 is a YUV planar format comprised of a WxH Y plane,
+ * with each pixel being represented by 8 bits.
+ *
+ * It is equivalent to just the Y plane from YV12.
+ *
+ * This format assumes
+ * - an even width
+ * - an even height
+ * - a horizontal stride multiple of 16 pixels
+ * - a vertical stride equal to the height
+ *
+ * size = stride * height
+ *
+ * When used with ANativeWindow, the dataSpace field describes the color
+ * space of the buffer.
+ */
+ HAL_PIXEL_FORMAT_Y8 = 0x20203859,
+
+ /*
+ * Android Y16 format:
+ *
+ * This format is exposed outside of the HAL to the framework.
+ * The expected gralloc usage flags are SW_* and HW_CAMERA_*,
+ * and no other HW_ flags will be used.
+ *
+ * Y16 is a YUV planar format comprised of a WxH Y plane,
+ * with each pixel being represented by 16 bits.
+ *
+ * It is just like Y8, but has double the bits per pixel (little endian).
+ *
+ * This format assumes
+ * - an even width
+ * - an even height
+ * - a horizontal stride multiple of 16 pixels
+ * - a vertical stride equal to the height
+ * - strides are specified in pixels, not in bytes
+ *
+ * size = stride * height * 2
+ *
+ * When used with ANativeWindow, the dataSpace field describes the color
+ * space of the buffer, except that dataSpace field
+ * HAL_DATASPACE_DEPTH indicates that this buffer contains a depth
+ * image where each sample is a distance value measured by a depth camera.
+ */
+ HAL_PIXEL_FORMAT_Y16 = 0x20363159,
+
+ /*
+ * Android RAW sensor format:
+ *
+ * This format is exposed outside of the camera HAL to applications.
+ *
+ * RAW16 is a single-channel, 16-bit, little endian format, typically
+ * representing raw Bayer-pattern images from an image sensor, with minimal
+ * processing.
+ *
+ * The exact pixel layout of the data in the buffer is sensor-dependent, and
+ * needs to be queried from the camera device.
+ *
+ * Generally, not all 16 bits are used; more common values are 10 or 12
+ * bits. If not all bits are used, the lower-order bits are filled first.
+ * All parameters to interpret the raw data (black and white points,
+ * color space, etc) must be queried from the camera device.
+ *
+ * This format assumes
+ * - an even width
+ * - an even height
+ * - a horizontal stride multiple of 16 pixels
+ * - a vertical stride equal to the height
+ * - strides are specified in pixels, not in bytes
+ *
+ * size = stride * height * 2
+ *
+ * This format must be accepted by the gralloc module when used with the
+ * following usage flags:
+ * - GRALLOC_USAGE_HW_CAMERA_*
+ * - GRALLOC_USAGE_SW_*
+ * - GRALLOC_USAGE_RENDERSCRIPT
+ *
+ * When used with ANativeWindow, the dataSpace should be
+ * HAL_DATASPACE_ARBITRARY, as raw image sensor buffers require substantial
+ * extra metadata to define.
+ */
+ HAL_PIXEL_FORMAT_RAW16 = 0x20,
+
+ // Temporary alias for source code compatibility; do not use in new code
+ HAL_PIXEL_FORMAT_RAW_SENSOR = HAL_PIXEL_FORMAT_RAW16,
+
+ /*
+ * Android RAW10 format:
+ *
+ * This format is exposed outside of the camera HAL to applications.
+ *
+ * RAW10 is a single-channel, 10-bit per pixel, densely packed in each row,
+ * unprocessed format, usually representing raw Bayer-pattern images coming from
+ * an image sensor.
+ *
+ * In an image buffer with this format, starting from the first pixel of each
+ * row, each 4 consecutive pixels are packed into 5 bytes (40 bits). Each one
+ * of the first 4 bytes contains the top 8 bits of each pixel, The fifth byte
+ * contains the 2 least significant bits of the 4 pixels, the exact layout data
+ * for each 4 consecutive pixels is illustrated below (Pi[j] stands for the jth
+ * bit of the ith pixel):
+ *
+ * bit 7 bit 0
+ * =====|=====|=====|=====|=====|=====|=====|=====|
+ * Byte 0: |P0[9]|P0[8]|P0[7]|P0[6]|P0[5]|P0[4]|P0[3]|P0[2]|
+ * |-----|-----|-----|-----|-----|-----|-----|-----|
+ * Byte 1: |P1[9]|P1[8]|P1[7]|P1[6]|P1[5]|P1[4]|P1[3]|P1[2]|
+ * |-----|-----|-----|-----|-----|-----|-----|-----|
+ * Byte 2: |P2[9]|P2[8]|P2[7]|P2[6]|P2[5]|P2[4]|P2[3]|P2[2]|
+ * |-----|-----|-----|-----|-----|-----|-----|-----|
+ * Byte 3: |P3[9]|P3[8]|P3[7]|P3[6]|P3[5]|P3[4]|P3[3]|P3[2]|
+ * |-----|-----|-----|-----|-----|-----|-----|-----|
+ * Byte 4: |P3[1]|P3[0]|P2[1]|P2[0]|P1[1]|P1[0]|P0[1]|P0[0]|
+ * ===============================================
+ *
+ * This format assumes
+ * - a width multiple of 4 pixels
+ * - an even height
+ * - a vertical stride equal to the height
+ * - strides are specified in bytes, not in pixels
+ *
+ * size = stride * height
+ *
+ * When stride is equal to width * (10 / 8), there will be no padding bytes at
+ * the end of each row, the entire image data is densely packed. When stride is
+ * larger than width * (10 / 8), padding bytes will be present at the end of each
+ * row (including the last row).
+ *
+ * This format must be accepted by the gralloc module when used with the
+ * following usage flags:
+ * - GRALLOC_USAGE_HW_CAMERA_*
+ * - GRALLOC_USAGE_SW_*
+ * - GRALLOC_USAGE_RENDERSCRIPT
+ *
+ * When used with ANativeWindow, the dataSpace field should be
+ * HAL_DATASPACE_ARBITRARY, as raw image sensor buffers require substantial
+ * extra metadata to define.
+ */
+ HAL_PIXEL_FORMAT_RAW10 = 0x25,
+
+ /*
+ * Android opaque RAW format:
+ *
+ * This format is exposed outside of the camera HAL to applications.
+ *
+ * RAW_OPAQUE is a format for unprocessed raw image buffers coming from an
+ * image sensor. The actual structure of buffers of this format is
+ * implementation-dependent.
+ *
+ * This format must be accepted by the gralloc module when used with the
+ * following usage flags:
+ * - GRALLOC_USAGE_HW_CAMERA_*
+ * - GRALLOC_USAGE_SW_*
+ * - GRALLOC_USAGE_RENDERSCRIPT
+ *
+ * When used with ANativeWindow, the dataSpace field should be
+ * HAL_DATASPACE_ARBITRARY, as raw image sensor buffers require substantial
+ * extra metadata to define.
+ */
+ HAL_PIXEL_FORMAT_RAW_OPAQUE = 0x24,
+
+ /*
+ * Android binary blob graphics buffer format:
+ *
+ * This format is used to carry task-specific data which does not have a
+ * standard image structure. The details of the format are left to the two
+ * endpoints.
+ *
+ * A typical use case is for transporting JPEG-compressed images from the
+ * Camera HAL to the framework or to applications.
+ *
+ * Buffers of this format must have a height of 1, and width equal to their
+ * size in bytes.
+ *
+ * When used with ANativeWindow, the mapping of the dataSpace field to
+ * buffer contents for BLOB is as follows:
+ *
+ * dataSpace value | Buffer contents
+ * -------------------------------+-----------------------------------------
+ * HAL_DATASPACE_JFIF | An encoded JPEG image
+ * HAL_DATASPACE_DEPTH | An android_depth_points buffer
+ * Other | Unsupported
+ *
+ */
+ HAL_PIXEL_FORMAT_BLOB = 0x21,
+
+ /*
+ * Android format indicating that the choice of format is entirely up to the
+ * device-specific Gralloc implementation.
+ *
+ * The Gralloc implementation should examine the usage bits passed in when
+ * allocating a buffer with this format, and it should derive the pixel
+ * format from those usage flags. This format will never be used with any
+ * of the GRALLOC_USAGE_SW_* usage flags.
+ *
+ * If a buffer of this format is to be used as an OpenGL ES texture, the
+ * framework will assume that sampling the texture will always return an
+ * alpha value of 1.0 (i.e. the buffer contains only opaque pixel values).
+ *
+ * When used with ANativeWindow, the dataSpace field describes the color
+ * space of the buffer.
+ */
+ HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 0x22,
+
+ /*
+ * Android flexible YCbCr formats
+ *
+ * This format allows platforms to use an efficient YCbCr/YCrCb buffer
+ * layout, while still describing the buffer layout in a way accessible to
+ * the CPU in a device-independent manner. While called YCbCr, it can be
+ * used to describe formats with either chromatic ordering, as well as
+ * whole planar or semiplanar layouts.
+ *
+ * struct android_ycbcr (below) is the the struct used to describe it.
+ *
+ * This format must be accepted by the gralloc module when
+ * USAGE_HW_CAMERA_WRITE and USAGE_SW_READ_* are set.
+ *
+ * This format is locked for use by gralloc's (*lock_ycbcr) method, and
+ * locking with the (*lock) method will return an error.
+ *
+ * When used with ANativeWindow, the dataSpace field describes the color
+ * space of the buffer.
+ */
+ HAL_PIXEL_FORMAT_YCbCr_420_888 = 0x23,
+
+ /* Legacy formats (deprecated), used by ImageFormat.java */
+ HAL_PIXEL_FORMAT_YCbCr_422_SP = 0x10, // NV16
+ HAL_PIXEL_FORMAT_YCrCb_420_SP = 0x11, // NV21
+ HAL_PIXEL_FORMAT_YCbCr_422_I = 0x14, // YUY2
+};
+
+/*
+ * Structure for describing YCbCr formats for consumption by applications.
+ * This is used with HAL_PIXEL_FORMAT_YCbCr_*_888.
+ *
+ * Buffer chroma subsampling is defined in the format.
+ * e.g. HAL_PIXEL_FORMAT_YCbCr_420_888 has subsampling 4:2:0.
+ *
+ * Buffers must have a 8 bit depth.
+ *
+ * @y, @cb, and @cr point to the first byte of their respective planes.
+ *
+ * Stride describes the distance in bytes from the first value of one row of
+ * the image to the first value of the next row. It includes the width of the
+ * image plus padding.
+ * @ystride is the stride of the luma plane.
+ * @cstride is the stride of the chroma planes.
+ *
+ * @chroma_step is the distance in bytes from one chroma pixel value to the
+ * next. This is 2 bytes for semiplanar (because chroma values are interleaved
+ * and each chroma value is one byte) and 1 for planar.
+ */
+
+struct android_ycbcr {
+ void *y;
+ void *cb;
+ void *cr;
+ size_t ystride;
+ size_t cstride;
+ size_t chroma_step;
+
+ /** reserved for future use, set to 0 by gralloc's (*lock_ycbcr)() */
+ uint32_t reserved[8];
+};
+
+/**
+ * Structure used to define depth point clouds for format HAL_PIXEL_FORMAT_BLOB
+ * with dataSpace value of HAL_DATASPACE_DEPTH.
+ * When locking a native buffer of the above format and dataSpace value,
+ * the vaddr pointer can be cast to this structure.
+ *
+ * A variable-length list of (x,y,z) 3D points, as floats.
+ *
+ * @num_points is the number of points in the list
+ *
+ * @xyz_points is the flexible array of floating-point values.
+ * It contains (num_points) * 3 floats.
+ *
+ * For example:
+ * android_depth_points d = get_depth_buffer();
+ * struct {
+ * float x; float y; float z;
+ * } firstPoint, lastPoint;
+ *
+ * firstPoint.x = d.xyz_points[0];
+ * firstPoint.y = d.xyz_points[1];
+ * firstPoint.z = d.xyz_points[2];
+ * lastPoint.x = d.xyz_points[(d.num_points - 1) * 3 + 0];
+ * lastPoint.y = d.xyz_points[(d.num_points - 1) * 3 + 1];
+ * lastPoint.z = d.xyz_points[(d.num_points - 1) * 3 + 2];
+ */
+
+struct android_depth_points {
+ uint32_t num_points;
+
+ /** reserved for future use, set to 0 by gralloc's (*lock)() */
+ uint32_t reserved[8];
+
+ float xyz_points[];
+};
+
+/**
+ * Transformation definitions
+ *
+ * IMPORTANT NOTE:
+ * HAL_TRANSFORM_ROT_90 is applied CLOCKWISE and AFTER HAL_TRANSFORM_FLIP_{H|V}.
+ *
+ */
+
+enum {
+ /* flip source image horizontally (around the vertical axis) */
+ HAL_TRANSFORM_FLIP_H = 0x01,
+ /* flip source image vertically (around the horizontal axis)*/
+ HAL_TRANSFORM_FLIP_V = 0x02,
+ /* rotate source image 90 degrees clockwise */
+ HAL_TRANSFORM_ROT_90 = 0x04,
+ /* rotate source image 180 degrees */
+ HAL_TRANSFORM_ROT_180 = 0x03,
+ /* rotate source image 270 degrees clockwise */
+ HAL_TRANSFORM_ROT_270 = 0x07,
+ /* don't use. see system/window.h */
+ HAL_TRANSFORM_RESERVED = 0x08,
+};
+
+/**
+ * Dataspace Definitions
+ * ======================
+ *
+ * Dataspace is the definition of how pixel values should be interpreted.
+ *
+ * For many formats, this is the colorspace of the image data, which includes
+ * primaries (including white point) and the transfer characteristic function,
+ * which describes both gamma curve and numeric range (within the bit depth).
+ *
+ * Other dataspaces include depth measurement data from a depth camera.
+ */
+
+typedef enum android_dataspace {
+ /*
+ * Default-assumption data space, when not explicitly specified.
+ *
+ * It is safest to assume the buffer is an image with sRGB primaries and
+ * encoding ranges, but the consumer and/or the producer of the data may
+ * simply be using defaults. No automatic gamma transform should be
+ * expected, except for a possible display gamma transform when drawn to a
+ * screen.
+ */
+ HAL_DATASPACE_UNKNOWN = 0x0,
+
+ /*
+ * Arbitrary dataspace with manually defined characteristics. Definition
+ * for colorspaces or other meaning must be communicated separately.
+ *
+ * This is used when specifying primaries, transfer characteristics,
+ * etc. separately.
+ *
+ * A typical use case is in video encoding parameters (e.g. for H.264),
+ * where a colorspace can have separately defined primaries, transfer
+ * characteristics, etc.
+ */
+ HAL_DATASPACE_ARBITRARY = 0x1,
+
+ /*
+ * RGB Colorspaces
+ * -----------------
+ *
+ * Primaries are given using (x,y) coordinates in the CIE 1931 definition
+ * of x and y specified by ISO 11664-1.
+ *
+ * Transfer characteristics are the opto-electronic transfer characteristic
+ * at the source as a function of linear optical intensity (luminance).
+ */
+
+ /*
+ * sRGB linear encoding:
+ *
+ * The red, green, and blue components are stored in sRGB space, but
+ * are linear, not gamma-encoded.
+ * The RGB primaries and the white point are the same as BT.709.
+ *
+ * The values are encoded using the full range ([0,255] for 8-bit) for all
+ * components.
+ */
+ HAL_DATASPACE_SRGB_LINEAR = 0x200,
+
+ /*
+ * sRGB gamma encoding:
+ *
+ * The red, green and blue components are stored in sRGB space, and
+ * converted to linear space when read, using the standard sRGB to linear
+ * equation:
+ *
+ * Clinear = Csrgb / 12.92 for Csrgb <= 0.04045
+ * = (Csrgb + 0.055 / 1.055)^2.4 for Csrgb > 0.04045
+ *
+ * When written the inverse transformation is performed:
+ *
+ * Csrgb = 12.92 * Clinear for Clinear <= 0.0031308
+ * = 1.055 * Clinear^(1/2.4) - 0.055 for Clinear > 0.0031308
+ *
+ *
+ * The alpha component, if present, is always stored in linear space and
+ * is left unmodified when read or written.
+ *
+ * The RGB primaries and the white point are the same as BT.709.
+ *
+ * The values are encoded using the full range ([0,255] for 8-bit) for all
+ * components.
+ *
+ */
+ HAL_DATASPACE_SRGB = 0x201,
+
+ /*
+ * YCbCr Colorspaces
+ * -----------------
+ *
+ * Primaries are given using (x,y) coordinates in the CIE 1931 definition
+ * of x and y specified by ISO 11664-1.
+ *
+ * Transfer characteristics are the opto-electronic transfer characteristic
+ * at the source as a function of linear optical intensity (luminance).
+ */
+
+ /*
+ * JPEG File Interchange Format (JFIF)
+ *
+ * Same model as BT.601-625, but all values (Y, Cb, Cr) range from 0 to 255
+ *
+ * Transfer characteristic curve:
+ * E = 1.099 * L ^ 0.45 - 0.099, 1.00 >= L >= 0.018
+ * E = 4.500 L, 0.018 > L >= 0
+ * L - luminance of image 0 <= L <= 1 for conventional colorimetry
+ * E - corresponding electrical signal
+ *
+ * Primaries: x y
+ * green 0.290 0.600
+ * blue 0.150 0.060
+ * red 0.640 0.330
+ * white (D65) 0.3127 0.3290
+ */
+ HAL_DATASPACE_JFIF = 0x101,
+
+ /*
+ * ITU-R Recommendation 601 (BT.601) - 625-line
+ *
+ * Standard-definition television, 625 Lines (PAL)
+ *
+ * For 8-bit-depth formats:
+ * Luma (Y) samples should range from 16 to 235, inclusive
+ * Chroma (Cb, Cr) samples should range from 16 to 240, inclusive
+ *
+ * For 10-bit-depth formats:
+ * Luma (Y) samples should range from 64 to 940, inclusive
+ * Chroma (Cb, Cr) samples should range from 64 to 960, inclusive
+ *
+ * Transfer characteristic curve:
+ * E = 1.099 * L ^ 0.45 - 0.099, 1.00 >= L >= 0.018
+ * E = 4.500 L, 0.018 > L >= 0
+ * L - luminance of image 0 <= L <= 1 for conventional colorimetry
+ * E - corresponding electrical signal
+ *
+ * Primaries: x y
+ * green 0.290 0.600
+ * blue 0.150 0.060
+ * red 0.640 0.330
+ * white (D65) 0.3127 0.3290
+ */
+ HAL_DATASPACE_BT601_625 = 0x102,
+
+ /*
+ * ITU-R Recommendation 601 (BT.601) - 525-line
+ *
+ * Standard-definition television, 525 Lines (NTSC)
+ *
+ * For 8-bit-depth formats:
+ * Luma (Y) samples should range from 16 to 235, inclusive
+ * Chroma (Cb, Cr) samples should range from 16 to 240, inclusive
+ *
+ * For 10-bit-depth formats:
+ * Luma (Y) samples should range from 64 to 940, inclusive
+ * Chroma (Cb, Cr) samples should range from 64 to 960, inclusive
+ *
+ * Transfer characteristic curve:
+ * E = 1.099 * L ^ 0.45 - 0.099, 1.00 >= L >= 0.018
+ * E = 4.500 L, 0.018 > L >= 0
+ * L - luminance of image 0 <= L <= 1 for conventional colorimetry
+ * E - corresponding electrical signal
+ *
+ * Primaries: x y
+ * green 0.310 0.595
+ * blue 0.155 0.070
+ * red 0.630 0.340
+ * white (D65) 0.3127 0.3290
+ */
+ HAL_DATASPACE_BT601_525 = 0x103,
+
+ /*
+ * ITU-R Recommendation 709 (BT.709)
+ *
+ * High-definition television
+ *
+ * For 8-bit-depth formats:
+ * Luma (Y) samples should range from 16 to 235, inclusive
+ * Chroma (Cb, Cr) samples should range from 16 to 240, inclusive
+ *
+ * For 10-bit-depth formats:
+ * Luma (Y) samples should range from 64 to 940, inclusive
+ * Chroma (Cb, Cr) samples should range from 64 to 960, inclusive
+ *
+ * Primaries: x y
+ * green 0.300 0.600
+ * blue 0.150 0.060
+ * red 0.640 0.330
+ * white (D65) 0.3127 0.3290
+ */
+ HAL_DATASPACE_BT709 = 0x104,
+
+ /*
+ * The buffer contains depth ranging measurements from a depth camera.
+ * This value is valid with formats:
+ * HAL_PIXEL_FORMAT_Y16: 16-bit single channel depth image.
+ * HAL_PIXEL_FORMAT_BLOB: A depth point cloud, as
+ * a variable-length float (x,y,z) coordinate point list.
+ * The point cloud will be represented with the android_depth_points
+ * structure.
+ */
+ HAL_DATASPACE_DEPTH = 0x1000
+
+} android_dataspace_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SYSTEM_CORE_INCLUDE_ANDROID_GRAPHICS_H */
diff --git a/include/system/sound_trigger.h b/include/system/sound_trigger.h
new file mode 100644
index 0000000..773e4f7
--- /dev/null
+++ b/include/system/sound_trigger.h
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SOUND_TRIGGER_H
+#define ANDROID_SOUND_TRIGGER_H
+
+#include <stdbool.h>
+#include <system/audio.h>
+
+#define SOUND_TRIGGER_MAX_STRING_LEN 64 /* max length of strings in properties or
+ descriptor structs */
+#define SOUND_TRIGGER_MAX_LOCALE_LEN 6 /* max length of locale string. e.g en_US */
+#define SOUND_TRIGGER_MAX_USERS 10 /* max number of concurrent users */
+#define SOUND_TRIGGER_MAX_PHRASES 10 /* max number of concurrent phrases */
+
+typedef enum {
+ SOUND_TRIGGER_STATE_NO_INIT = -1, /* The sound trigger service is not initialized */
+ SOUND_TRIGGER_STATE_ENABLED = 0, /* The sound trigger service is enabled */
+ SOUND_TRIGGER_STATE_DISABLED = 1 /* The sound trigger service is disabled */
+} sound_trigger_service_state_t;
+
+#define RECOGNITION_MODE_VOICE_TRIGGER 0x1 /* simple voice trigger */
+#define RECOGNITION_MODE_USER_IDENTIFICATION 0x2 /* trigger only if one user in model identified */
+#define RECOGNITION_MODE_USER_AUTHENTICATION 0x4 /* trigger only if one user in mode
+ authenticated */
+#define RECOGNITION_STATUS_SUCCESS 0
+#define RECOGNITION_STATUS_ABORT 1
+#define RECOGNITION_STATUS_FAILURE 2
+
+#define SOUND_MODEL_STATUS_UPDATED 0
+
+typedef enum {
+ SOUND_MODEL_TYPE_UNKNOWN = -1, /* use for unspecified sound model type */
+ SOUND_MODEL_TYPE_KEYPHRASE = 0 /* use for key phrase sound models */
+} sound_trigger_sound_model_type_t;
+
+typedef struct sound_trigger_uuid_s {
+ unsigned int timeLow;
+ unsigned short timeMid;
+ unsigned short timeHiAndVersion;
+ unsigned short clockSeq;
+ unsigned char node[6];
+} sound_trigger_uuid_t;
+
+/*
+ * sound trigger implementation descriptor read by the framework via get_properties().
+ * Used by SoundTrigger service to report to applications and manage concurrency and policy.
+ */
+struct sound_trigger_properties {
+ char implementor[SOUND_TRIGGER_MAX_STRING_LEN]; /* implementor name */
+ char description[SOUND_TRIGGER_MAX_STRING_LEN]; /* implementation description */
+ unsigned int version; /* implementation version */
+ sound_trigger_uuid_t uuid; /* unique implementation ID.
+ Must change with version each version */
+ unsigned int max_sound_models; /* maximum number of concurrent sound models
+ loaded */
+ unsigned int max_key_phrases; /* maximum number of key phrases */
+ unsigned int max_users; /* maximum number of concurrent users detected */
+ unsigned int recognition_modes; /* all supported modes.
+ e.g RECOGNITION_MODE_VOICE_TRIGGER */
+ bool capture_transition; /* supports seamless transition from detection
+ to capture */
+ unsigned int max_buffer_ms; /* maximum buffering capacity in ms if
+ capture_transition is true*/
+ bool concurrent_capture; /* supports capture by other use cases while
+ detection is active */
+ bool trigger_in_event; /* returns the trigger capture in event */
+ unsigned int power_consumption_mw; /* Rated power consumption when detection is active
+ with TDB silence/sound/speech ratio */
+};
+
+typedef int sound_trigger_module_handle_t;
+
+struct sound_trigger_module_descriptor {
+ sound_trigger_module_handle_t handle;
+ struct sound_trigger_properties properties;
+};
+
+typedef int sound_model_handle_t;
+
+/*
+ * Generic sound model descriptor. This struct is the header of a larger block passed to
+ * load_sound_model() and containing the binary data of the sound model.
+ * Proprietary representation of users in binary data must match information indicated
+ * by users field
+ */
+struct sound_trigger_sound_model {
+ sound_trigger_sound_model_type_t type; /* model type. e.g. SOUND_MODEL_TYPE_KEYPHRASE */
+ sound_trigger_uuid_t uuid; /* unique sound model ID. */
+ sound_trigger_uuid_t vendor_uuid; /* unique vendor ID. Identifies the engine the
+ sound model was build for */
+ unsigned int data_size; /* size of opaque model data */
+ unsigned int data_offset; /* offset of opaque data start from head of struct
+ (e.g sizeof struct sound_trigger_sound_model) */
+};
+
+/* key phrase descriptor */
+struct sound_trigger_phrase {
+ unsigned int id; /* keyphrase ID */
+ unsigned int recognition_mode; /* recognition modes supported by this key phrase */
+ unsigned int num_users; /* number of users in the key phrase */
+ unsigned int users[SOUND_TRIGGER_MAX_USERS]; /* users ids: (not uid_t but sound trigger
+ specific IDs */
+ char locale[SOUND_TRIGGER_MAX_LOCALE_LEN]; /* locale - JAVA Locale style (e.g. en_US) */
+ char text[SOUND_TRIGGER_MAX_STRING_LEN]; /* phrase text in UTF-8 format. */
+};
+
+/*
+ * Specialized sound model for key phrase detection.
+ * Proprietary representation of key phrases in binary data must match information indicated
+ * by phrases field
+ */
+struct sound_trigger_phrase_sound_model {
+ struct sound_trigger_sound_model common;
+ unsigned int num_phrases; /* number of key phrases in model */
+ struct sound_trigger_phrase phrases[SOUND_TRIGGER_MAX_PHRASES];
+};
+
+
+/*
+ * Generic recognition event sent via recognition callback
+ */
+struct sound_trigger_recognition_event {
+ int status; /* recognition status e.g.
+ RECOGNITION_STATUS_SUCCESS */
+ sound_trigger_sound_model_type_t type; /* event type, same as sound model type.
+ e.g. SOUND_MODEL_TYPE_KEYPHRASE */
+ sound_model_handle_t model; /* loaded sound model that triggered the
+ event */
+ bool capture_available; /* it is possible to capture audio from this
+ utterance buffered by the
+ implementation */
+ int capture_session; /* audio session ID. framework use */
+ int capture_delay_ms; /* delay in ms between end of model
+ detection and start of audio available
+ for capture. A negative value is possible
+ (e.g. if key phrase is also available for
+ capture */
+ int capture_preamble_ms; /* duration in ms of audio captured
+ before the start of the trigger.
+ 0 if none. */
+ bool trigger_in_data; /* the opaque data is the capture of
+ the trigger sound */
+ audio_config_t audio_config; /* audio format of either the trigger in
+ event data or to use for capture of the
+ rest of the utterance */
+ unsigned int data_size; /* size of opaque event data */
+ unsigned int data_offset; /* offset of opaque data start from start of
+ this struct (e.g sizeof struct
+ sound_trigger_phrase_recognition_event) */
+};
+
+/*
+ * Confidence level for each user in struct sound_trigger_phrase_recognition_extra
+ */
+struct sound_trigger_confidence_level {
+ unsigned int user_id; /* user ID */
+ unsigned int level; /* confidence level in percent (0 - 100).
+ - min level for recognition configuration
+ - detected level for recognition event */
+};
+
+/*
+ * Specialized recognition event for key phrase detection
+ */
+struct sound_trigger_phrase_recognition_extra {
+ unsigned int id; /* keyphrase ID */
+ unsigned int recognition_modes; /* recognition modes used for this keyphrase */
+ unsigned int confidence_level; /* confidence level for mode RECOGNITION_MODE_VOICE_TRIGGER */
+ unsigned int num_levels; /* number of user confidence levels */
+ struct sound_trigger_confidence_level levels[SOUND_TRIGGER_MAX_USERS];
+};
+
+struct sound_trigger_phrase_recognition_event {
+ struct sound_trigger_recognition_event common;
+ unsigned int num_phrases;
+ struct sound_trigger_phrase_recognition_extra phrase_extras[SOUND_TRIGGER_MAX_PHRASES];
+};
+
+/*
+ * configuration for sound trigger capture session passed to start_recognition()
+ */
+struct sound_trigger_recognition_config {
+ audio_io_handle_t capture_handle; /* IO handle that will be used for capture.
+ N/A if capture_requested is false */
+ audio_devices_t capture_device; /* input device requested for detection capture */
+ bool capture_requested; /* capture and buffer audio for this recognition
+ instance */
+ unsigned int num_phrases; /* number of key phrases recognition extras */
+ struct sound_trigger_phrase_recognition_extra phrases[SOUND_TRIGGER_MAX_PHRASES];
+ /* configuration for each key phrase */
+ unsigned int data_size; /* size of opaque capture configuration data */
+ unsigned int data_offset; /* offset of opaque data start from start of this struct
+ (e.g sizeof struct sound_trigger_recognition_config) */
+};
+
+/*
+ * Event sent via load sound model callback
+ */
+struct sound_trigger_model_event {
+ int status; /* sound model status e.g. SOUND_MODEL_STATUS_UPDATED */
+ sound_model_handle_t model; /* loaded sound model that triggered the event */
+ unsigned int data_size; /* size of event data if any. Size of updated sound model if
+ status is SOUND_MODEL_STATUS_UPDATED */
+ unsigned int data_offset; /* offset of data start from start of this struct
+ (e.g sizeof struct sound_trigger_model_event) */
+};
+
+
+#endif // ANDROID_SOUND_TRIGGER_H
diff --git a/include/system/thread_defs.h b/include/system/thread_defs.h
new file mode 100644
index 0000000..377a48c
--- /dev/null
+++ b/include/system/thread_defs.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_THREAD_DEFS_H
+#define ANDROID_THREAD_DEFS_H
+
+#include "graphics.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+enum {
+ /*
+ * ***********************************************
+ * ** Keep in sync with android.os.Process.java **
+ * ***********************************************
+ *
+ * This maps directly to the "nice" priorities we use in Android.
+ * A thread priority should be chosen inverse-proportionally to
+ * the amount of work the thread is expected to do. The more work
+ * a thread will do, the less favorable priority it should get so that
+ * it doesn't starve the system. Threads not behaving properly might
+ * be "punished" by the kernel.
+ * Use the levels below when appropriate. Intermediate values are
+ * acceptable, preferably use the {MORE|LESS}_FAVORABLE constants below.
+ */
+ ANDROID_PRIORITY_LOWEST = 19,
+
+ /* use for background tasks */
+ ANDROID_PRIORITY_BACKGROUND = 10,
+
+ /* most threads run at normal priority */
+ ANDROID_PRIORITY_NORMAL = 0,
+
+ /* threads currently running a UI that the user is interacting with */
+ ANDROID_PRIORITY_FOREGROUND = -2,
+
+ /* the main UI thread has a slightly more favorable priority */
+ ANDROID_PRIORITY_DISPLAY = -4,
+
+ /* ui service treads might want to run at a urgent display (uncommon) */
+ ANDROID_PRIORITY_URGENT_DISPLAY = HAL_PRIORITY_URGENT_DISPLAY,
+
+ /* all normal audio threads */
+ ANDROID_PRIORITY_AUDIO = -16,
+
+ /* service audio threads (uncommon) */
+ ANDROID_PRIORITY_URGENT_AUDIO = -19,
+
+ /* should never be used in practice. regular process might not
+ * be allowed to use this level */
+ ANDROID_PRIORITY_HIGHEST = -20,
+
+ ANDROID_PRIORITY_DEFAULT = ANDROID_PRIORITY_NORMAL,
+ ANDROID_PRIORITY_MORE_FAVORABLE = -1,
+ ANDROID_PRIORITY_LESS_FAVORABLE = +1,
+};
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* ANDROID_THREAD_DEFS_H */
diff --git a/include/system/window.h b/include/system/window.h
new file mode 100644
index 0000000..508ce00
--- /dev/null
+++ b/include/system/window.h
@@ -0,0 +1,954 @@
+/*
+ * 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.
+ */
+
+#ifndef SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H
+#define SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H
+
+#include <cutils/native_handle.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/cdefs.h>
+#include <system/graphics.h>
+#include <unistd.h>
+
+#ifndef __UNUSED
+#define __UNUSED __attribute__((__unused__))
+#endif
+#ifndef __deprecated
+#define __deprecated __attribute__((__deprecated__))
+#endif
+
+__BEGIN_DECLS
+
+/*****************************************************************************/
+
+#define ANDROID_NATIVE_MAKE_CONSTANT(a,b,c,d) \
+ (((unsigned)(a)<<24)|((unsigned)(b)<<16)|((unsigned)(c)<<8)|(unsigned)(d))
+
+#define ANDROID_NATIVE_WINDOW_MAGIC \
+ ANDROID_NATIVE_MAKE_CONSTANT('_','w','n','d')
+
+#define ANDROID_NATIVE_BUFFER_MAGIC \
+ ANDROID_NATIVE_MAKE_CONSTANT('_','b','f','r')
+
+// ---------------------------------------------------------------------------
+
+// This #define may be used to conditionally compile device-specific code to
+// support either the prior ANativeWindow interface, which did not pass libsync
+// fences around, or the new interface that does. This #define is only present
+// when the ANativeWindow interface does include libsync support.
+#define ANDROID_NATIVE_WINDOW_HAS_SYNC 1
+
+// ---------------------------------------------------------------------------
+
+typedef const native_handle_t* buffer_handle_t;
+
+// ---------------------------------------------------------------------------
+
+typedef struct android_native_rect_t
+{
+ int32_t left;
+ int32_t top;
+ int32_t right;
+ int32_t bottom;
+} android_native_rect_t;
+
+// ---------------------------------------------------------------------------
+
+typedef struct android_native_base_t
+{
+ /* a magic value defined by the actual EGL native type */
+ int magic;
+
+ /* the sizeof() of the actual EGL native type */
+ int version;
+
+ void* reserved[4];
+
+ /* reference-counting interface */
+ void (*incRef)(struct android_native_base_t* base);
+ void (*decRef)(struct android_native_base_t* base);
+} android_native_base_t;
+
+typedef struct ANativeWindowBuffer
+{
+#ifdef __cplusplus
+ ANativeWindowBuffer() {
+ common.magic = ANDROID_NATIVE_BUFFER_MAGIC;
+ common.version = sizeof(ANativeWindowBuffer);
+ memset(common.reserved, 0, sizeof(common.reserved));
+ }
+
+ // Implement the methods that sp<ANativeWindowBuffer> expects so that it
+ // can be used to automatically refcount ANativeWindowBuffer's.
+ void incStrong(const void* /*id*/) const {
+ common.incRef(const_cast<android_native_base_t*>(&common));
+ }
+ void decStrong(const void* /*id*/) const {
+ common.decRef(const_cast<android_native_base_t*>(&common));
+ }
+#endif
+
+ struct android_native_base_t common;
+
+ int width;
+ int height;
+ int stride;
+ int format;
+ int usage;
+
+ void* reserved[2];
+
+ buffer_handle_t handle;
+
+ void* reserved_proc[8];
+} ANativeWindowBuffer_t;
+
+// Old typedef for backwards compatibility.
+typedef ANativeWindowBuffer_t android_native_buffer_t;
+
+// ---------------------------------------------------------------------------
+
+/* attributes queriable with query() */
+enum {
+ NATIVE_WINDOW_WIDTH = 0,
+ NATIVE_WINDOW_HEIGHT = 1,
+ NATIVE_WINDOW_FORMAT = 2,
+
+ /* The minimum number of buffers that must remain un-dequeued after a buffer
+ * has been queued. This value applies only if set_buffer_count was used to
+ * override the number of buffers and if a buffer has since been queued.
+ * Users of the set_buffer_count ANativeWindow method should query this
+ * value before calling set_buffer_count. If it is necessary to have N
+ * buffers simultaneously dequeued as part of the steady-state operation,
+ * and this query returns M then N+M buffers should be requested via
+ * native_window_set_buffer_count.
+ *
+ * Note that this value does NOT apply until a single buffer has been
+ * queued. In particular this means that it is possible to:
+ *
+ * 1. Query M = min undequeued buffers
+ * 2. Set the buffer count to N + M
+ * 3. Dequeue all N + M buffers
+ * 4. Cancel M buffers
+ * 5. Queue, dequeue, queue, dequeue, ad infinitum
+ */
+ NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS = 3,
+
+ /* Check whether queueBuffer operations on the ANativeWindow send the buffer
+ * to the window compositor. The query sets the returned 'value' argument
+ * to 1 if the ANativeWindow DOES send queued buffers directly to the window
+ * compositor and 0 if the buffers do not go directly to the window
+ * compositor.
+ *
+ * This can be used to determine whether protected buffer content should be
+ * sent to the ANativeWindow. Note, however, that a result of 1 does NOT
+ * indicate that queued buffers will be protected from applications or users
+ * capturing their contents. If that behavior is desired then some other
+ * mechanism (e.g. the GRALLOC_USAGE_PROTECTED flag) should be used in
+ * conjunction with this query.
+ */
+ NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER = 4,
+
+ /* Get the concrete type of a ANativeWindow. See below for the list of
+ * possible return values.
+ *
+ * This query should not be used outside the Android framework and will
+ * likely be removed in the near future.
+ */
+ NATIVE_WINDOW_CONCRETE_TYPE = 5,
+
+
+ /*
+ * Default width and height of ANativeWindow buffers, these are the
+ * dimensions of the window buffers irrespective of the
+ * NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS call and match the native window
+ * size unless overridden by NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS.
+ */
+ NATIVE_WINDOW_DEFAULT_WIDTH = 6,
+ NATIVE_WINDOW_DEFAULT_HEIGHT = 7,
+
+ /*
+ * transformation that will most-likely be applied to buffers. This is only
+ * a hint, the actual transformation applied might be different.
+ *
+ * INTENDED USE:
+ *
+ * The transform hint can be used by a producer, for instance the GLES
+ * driver, to pre-rotate the rendering such that the final transformation
+ * in the composer is identity. This can be very useful when used in
+ * conjunction with the h/w composer HAL, in situations where it
+ * cannot handle arbitrary rotations.
+ *
+ * 1. Before dequeuing a buffer, the GL driver (or any other ANW client)
+ * queries the ANW for NATIVE_WINDOW_TRANSFORM_HINT.
+ *
+ * 2. The GL driver overrides the width and height of the ANW to
+ * account for NATIVE_WINDOW_TRANSFORM_HINT. This is done by querying
+ * NATIVE_WINDOW_DEFAULT_{WIDTH | HEIGHT}, swapping the dimensions
+ * according to NATIVE_WINDOW_TRANSFORM_HINT and calling
+ * native_window_set_buffers_dimensions().
+ *
+ * 3. The GL driver dequeues a buffer of the new pre-rotated size.
+ *
+ * 4. The GL driver renders to the buffer such that the image is
+ * already transformed, that is applying NATIVE_WINDOW_TRANSFORM_HINT
+ * to the rendering.
+ *
+ * 5. The GL driver calls native_window_set_transform to apply
+ * inverse transformation to the buffer it just rendered.
+ * In order to do this, the GL driver needs
+ * to calculate the inverse of NATIVE_WINDOW_TRANSFORM_HINT, this is
+ * done easily:
+ *
+ * int hintTransform, inverseTransform;
+ * query(..., NATIVE_WINDOW_TRANSFORM_HINT, &hintTransform);
+ * inverseTransform = hintTransform;
+ * if (hintTransform & HAL_TRANSFORM_ROT_90)
+ * inverseTransform ^= HAL_TRANSFORM_ROT_180;
+ *
+ *
+ * 6. The GL driver queues the pre-transformed buffer.
+ *
+ * 7. The composer combines the buffer transform with the display
+ * transform. If the buffer transform happens to cancel out the
+ * display transform then no rotation is needed.
+ *
+ */
+ NATIVE_WINDOW_TRANSFORM_HINT = 8,
+
+ /*
+ * Boolean that indicates whether the consumer is running more than
+ * one buffer behind the producer.
+ */
+ NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND = 9,
+
+ /*
+ * The consumer gralloc usage bits currently set by the consumer.
+ * The values are defined in hardware/libhardware/include/gralloc.h.
+ */
+ NATIVE_WINDOW_CONSUMER_USAGE_BITS = 10,
+
+ /**
+ * Transformation that will by applied to buffers by the hwcomposer.
+ * This must not be set or checked by producer endpoints, and will
+ * disable the transform hint set in SurfaceFlinger (see
+ * NATIVE_WINDOW_TRANSFORM_HINT).
+ *
+ * INTENDED USE:
+ * Temporary - Please do not use this. This is intended only to be used
+ * by the camera's LEGACY mode.
+ *
+ * In situations where a SurfaceFlinger client wishes to set a transform
+ * that is not visible to the producer, and will always be applied in the
+ * hardware composer, the client can set this flag with
+ * native_window_set_buffers_sticky_transform. This can be used to rotate
+ * and flip buffers consumed by hardware composer without actually changing
+ * the aspect ratio of the buffers produced.
+ */
+ NATIVE_WINDOW_STICKY_TRANSFORM = 11,
+
+ /**
+ * The default data space for the buffers as set by the consumer.
+ * The values are defined in graphics.h.
+ */
+ NATIVE_WINDOW_DEFAULT_DATASPACE = 12,
+
+ /*
+ * Returns the age of the contents of the most recently dequeued buffer as
+ * the number of frames that have elapsed since it was last queued. For
+ * example, if the window is double-buffered, the age of any given buffer in
+ * steady state will be 2. If the dequeued buffer has never been queued, its
+ * age will be 0.
+ */
+ NATIVE_WINDOW_BUFFER_AGE = 13,
+};
+
+/* Valid operations for the (*perform)() hook.
+ *
+ * Values marked as 'deprecated' are supported, but have been superceded by
+ * other functionality.
+ *
+ * Values marked as 'private' should be considered private to the framework.
+ * HAL implementation code with access to an ANativeWindow should not use these,
+ * as it may not interact properly with the framework's use of the
+ * ANativeWindow.
+ */
+enum {
+ NATIVE_WINDOW_SET_USAGE = 0,
+ NATIVE_WINDOW_CONNECT = 1, /* deprecated */
+ NATIVE_WINDOW_DISCONNECT = 2, /* deprecated */
+ NATIVE_WINDOW_SET_CROP = 3, /* private */
+ NATIVE_WINDOW_SET_BUFFER_COUNT = 4,
+ NATIVE_WINDOW_SET_BUFFERS_GEOMETRY = 5, /* deprecated */
+ NATIVE_WINDOW_SET_BUFFERS_TRANSFORM = 6,
+ NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP = 7,
+ NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS = 8,
+ NATIVE_WINDOW_SET_BUFFERS_FORMAT = 9,
+ NATIVE_WINDOW_SET_SCALING_MODE = 10, /* private */
+ NATIVE_WINDOW_LOCK = 11, /* private */
+ NATIVE_WINDOW_UNLOCK_AND_POST = 12, /* private */
+ NATIVE_WINDOW_API_CONNECT = 13, /* private */
+ NATIVE_WINDOW_API_DISCONNECT = 14, /* private */
+ NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS = 15, /* private */
+ NATIVE_WINDOW_SET_POST_TRANSFORM_CROP = 16, /* private */
+ NATIVE_WINDOW_SET_BUFFERS_STICKY_TRANSFORM = 17,/* private */
+ NATIVE_WINDOW_SET_SIDEBAND_STREAM = 18,
+ NATIVE_WINDOW_SET_BUFFERS_DATASPACE = 19,
+ NATIVE_WINDOW_SET_SURFACE_DAMAGE = 20, /* private */
+};
+
+/* parameter for NATIVE_WINDOW_[API_][DIS]CONNECT */
+enum {
+ /* Buffers will be queued by EGL via eglSwapBuffers after being filled using
+ * OpenGL ES.
+ */
+ NATIVE_WINDOW_API_EGL = 1,
+
+ /* Buffers will be queued after being filled using the CPU
+ */
+ NATIVE_WINDOW_API_CPU = 2,
+
+ /* Buffers will be queued by Stagefright after being filled by a video
+ * decoder. The video decoder can either be a software or hardware decoder.
+ */
+ NATIVE_WINDOW_API_MEDIA = 3,
+
+ /* Buffers will be queued by the the camera HAL.
+ */
+ NATIVE_WINDOW_API_CAMERA = 4,
+};
+
+/* parameter for NATIVE_WINDOW_SET_BUFFERS_TRANSFORM */
+enum {
+ /* flip source image horizontally */
+ NATIVE_WINDOW_TRANSFORM_FLIP_H = HAL_TRANSFORM_FLIP_H ,
+ /* flip source image vertically */
+ NATIVE_WINDOW_TRANSFORM_FLIP_V = HAL_TRANSFORM_FLIP_V,
+ /* rotate source image 90 degrees clock-wise, and is applied after TRANSFORM_FLIP_{H|V} */
+ NATIVE_WINDOW_TRANSFORM_ROT_90 = HAL_TRANSFORM_ROT_90,
+ /* rotate source image 180 degrees */
+ NATIVE_WINDOW_TRANSFORM_ROT_180 = HAL_TRANSFORM_ROT_180,
+ /* rotate source image 270 degrees clock-wise */
+ NATIVE_WINDOW_TRANSFORM_ROT_270 = HAL_TRANSFORM_ROT_270,
+ /* transforms source by the inverse transform of the screen it is displayed onto. This
+ * transform is applied last */
+ NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY = 0x08
+};
+
+/* parameter for NATIVE_WINDOW_SET_SCALING_MODE */
+enum {
+ /* the window content is not updated (frozen) until a buffer of
+ * the window size is received (enqueued)
+ */
+ NATIVE_WINDOW_SCALING_MODE_FREEZE = 0,
+ /* the buffer is scaled in both dimensions to match the window size */
+ NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW = 1,
+ /* the buffer is scaled uniformly such that the smaller dimension
+ * of the buffer matches the window size (cropping in the process)
+ */
+ NATIVE_WINDOW_SCALING_MODE_SCALE_CROP = 2,
+ /* the window is clipped to the size of the buffer's crop rectangle; pixels
+ * outside the crop rectangle are treated as if they are completely
+ * transparent.
+ */
+ NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP = 3,
+};
+
+/* values returned by the NATIVE_WINDOW_CONCRETE_TYPE query */
+enum {
+ NATIVE_WINDOW_FRAMEBUFFER = 0, /* FramebufferNativeWindow */
+ NATIVE_WINDOW_SURFACE = 1, /* Surface */
+};
+
+/* parameter for NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP
+ *
+ * Special timestamp value to indicate that timestamps should be auto-generated
+ * by the native window when queueBuffer is called. This is equal to INT64_MIN,
+ * defined directly to avoid problems with C99/C++ inclusion of stdint.h.
+ */
+static const int64_t NATIVE_WINDOW_TIMESTAMP_AUTO = (-9223372036854775807LL-1);
+
+struct ANativeWindow
+{
+#ifdef __cplusplus
+ ANativeWindow()
+ : flags(0), minSwapInterval(0), maxSwapInterval(0), xdpi(0), ydpi(0)
+ {
+ common.magic = ANDROID_NATIVE_WINDOW_MAGIC;
+ common.version = sizeof(ANativeWindow);
+ memset(common.reserved, 0, sizeof(common.reserved));
+ }
+
+ /* Implement the methods that sp<ANativeWindow> expects so that it
+ can be used to automatically refcount ANativeWindow's. */
+ void incStrong(const void* /*id*/) const {
+ common.incRef(const_cast<android_native_base_t*>(&common));
+ }
+ void decStrong(const void* /*id*/) const {
+ common.decRef(const_cast<android_native_base_t*>(&common));
+ }
+#endif
+
+ struct android_native_base_t common;
+
+ /* flags describing some attributes of this surface or its updater */
+ const uint32_t flags;
+
+ /* min swap interval supported by this updated */
+ const int minSwapInterval;
+
+ /* max swap interval supported by this updated */
+ const int maxSwapInterval;
+
+ /* horizontal and vertical resolution in DPI */
+ const float xdpi;
+ const float ydpi;
+
+ /* Some storage reserved for the OEM's driver. */
+ intptr_t oem[4];
+
+ /*
+ * Set the swap interval for this surface.
+ *
+ * Returns 0 on success or -errno on error.
+ */
+ int (*setSwapInterval)(struct ANativeWindow* window,
+ int interval);
+
+ /*
+ * Hook called by EGL to acquire a buffer. After this call, the buffer
+ * is not locked, so its content cannot be modified. This call may block if
+ * no buffers are available.
+ *
+ * The window holds a reference to the buffer between dequeueBuffer and
+ * either queueBuffer or cancelBuffer, so clients only need their own
+ * reference if they might use the buffer after queueing or canceling it.
+ * Holding a reference to a buffer after queueing or canceling it is only
+ * allowed if a specific buffer count has been set.
+ *
+ * Returns 0 on success or -errno on error.
+ *
+ * XXX: This function is deprecated. It will continue to work for some
+ * time for binary compatibility, but the new dequeueBuffer function that
+ * outputs a fence file descriptor should be used in its place.
+ */
+ int (*dequeueBuffer_DEPRECATED)(struct ANativeWindow* window,
+ struct ANativeWindowBuffer** buffer);
+
+ /*
+ * hook called by EGL to lock a buffer. This MUST be called before modifying
+ * the content of a buffer. The buffer must have been acquired with
+ * dequeueBuffer first.
+ *
+ * Returns 0 on success or -errno on error.
+ *
+ * XXX: This function is deprecated. It will continue to work for some
+ * time for binary compatibility, but it is essentially a no-op, and calls
+ * to it should be removed.
+ */
+ int (*lockBuffer_DEPRECATED)(struct ANativeWindow* window,
+ struct ANativeWindowBuffer* buffer);
+
+ /*
+ * Hook called by EGL when modifications to the render buffer are done.
+ * This unlocks and post the buffer.
+ *
+ * The window holds a reference to the buffer between dequeueBuffer and
+ * either queueBuffer or cancelBuffer, so clients only need their own
+ * reference if they might use the buffer after queueing or canceling it.
+ * Holding a reference to a buffer after queueing or canceling it is only
+ * allowed if a specific buffer count has been set.
+ *
+ * Buffers MUST be queued in the same order than they were dequeued.
+ *
+ * Returns 0 on success or -errno on error.
+ *
+ * XXX: This function is deprecated. It will continue to work for some
+ * time for binary compatibility, but the new queueBuffer function that
+ * takes a fence file descriptor should be used in its place (pass a value
+ * of -1 for the fence file descriptor if there is no valid one to pass).
+ */
+ int (*queueBuffer_DEPRECATED)(struct ANativeWindow* window,
+ struct ANativeWindowBuffer* buffer);
+
+ /*
+ * hook used to retrieve information about the native window.
+ *
+ * Returns 0 on success or -errno on error.
+ */
+ int (*query)(const struct ANativeWindow* window,
+ int what, int* value);
+
+ /*
+ * hook used to perform various operations on the surface.
+ * (*perform)() is a generic mechanism to add functionality to
+ * ANativeWindow while keeping backward binary compatibility.
+ *
+ * DO NOT CALL THIS HOOK DIRECTLY. Instead, use the helper functions
+ * defined below.
+ *
+ * (*perform)() returns -ENOENT if the 'what' parameter is not supported
+ * by the surface's implementation.
+ *
+ * See above for a list of valid operations, such as
+ * NATIVE_WINDOW_SET_USAGE or NATIVE_WINDOW_CONNECT
+ */
+ int (*perform)(struct ANativeWindow* window,
+ int operation, ... );
+
+ /*
+ * Hook used to cancel a buffer that has been dequeued.
+ * No synchronization is performed between dequeue() and cancel(), so
+ * either external synchronization is needed, or these functions must be
+ * called from the same thread.
+ *
+ * The window holds a reference to the buffer between dequeueBuffer and
+ * either queueBuffer or cancelBuffer, so clients only need their own
+ * reference if they might use the buffer after queueing or canceling it.
+ * Holding a reference to a buffer after queueing or canceling it is only
+ * allowed if a specific buffer count has been set.
+ *
+ * XXX: This function is deprecated. It will continue to work for some
+ * time for binary compatibility, but the new cancelBuffer function that
+ * takes a fence file descriptor should be used in its place (pass a value
+ * of -1 for the fence file descriptor if there is no valid one to pass).
+ */
+ int (*cancelBuffer_DEPRECATED)(struct ANativeWindow* window,
+ struct ANativeWindowBuffer* buffer);
+
+ /*
+ * Hook called by EGL to acquire a buffer. This call may block if no
+ * buffers are available.
+ *
+ * The window holds a reference to the buffer between dequeueBuffer and
+ * either queueBuffer or cancelBuffer, so clients only need their own
+ * reference if they might use the buffer after queueing or canceling it.
+ * Holding a reference to a buffer after queueing or canceling it is only
+ * allowed if a specific buffer count has been set.
+ *
+ * The libsync fence file descriptor returned in the int pointed to by the
+ * fenceFd argument will refer to the fence that must signal before the
+ * dequeued buffer may be written to. A value of -1 indicates that the
+ * caller may access the buffer immediately without waiting on a fence. If
+ * a valid file descriptor is returned (i.e. any value except -1) then the
+ * caller is responsible for closing the file descriptor.
+ *
+ * Returns 0 on success or -errno on error.
+ */
+ int (*dequeueBuffer)(struct ANativeWindow* window,
+ struct ANativeWindowBuffer** buffer, int* fenceFd);
+
+ /*
+ * Hook called by EGL when modifications to the render buffer are done.
+ * This unlocks and post the buffer.
+ *
+ * The window holds a reference to the buffer between dequeueBuffer and
+ * either queueBuffer or cancelBuffer, so clients only need their own
+ * reference if they might use the buffer after queueing or canceling it.
+ * Holding a reference to a buffer after queueing or canceling it is only
+ * allowed if a specific buffer count has been set.
+ *
+ * The fenceFd argument specifies a libsync fence file descriptor for a
+ * fence that must signal before the buffer can be accessed. If the buffer
+ * can be accessed immediately then a value of -1 should be used. The
+ * caller must not use the file descriptor after it is passed to
+ * queueBuffer, and the ANativeWindow implementation is responsible for
+ * closing it.
+ *
+ * Returns 0 on success or -errno on error.
+ */
+ int (*queueBuffer)(struct ANativeWindow* window,
+ struct ANativeWindowBuffer* buffer, int fenceFd);
+
+ /*
+ * Hook used to cancel a buffer that has been dequeued.
+ * No synchronization is performed between dequeue() and cancel(), so
+ * either external synchronization is needed, or these functions must be
+ * called from the same thread.
+ *
+ * The window holds a reference to the buffer between dequeueBuffer and
+ * either queueBuffer or cancelBuffer, so clients only need their own
+ * reference if they might use the buffer after queueing or canceling it.
+ * Holding a reference to a buffer after queueing or canceling it is only
+ * allowed if a specific buffer count has been set.
+ *
+ * The fenceFd argument specifies a libsync fence file decsriptor for a
+ * fence that must signal before the buffer can be accessed. If the buffer
+ * can be accessed immediately then a value of -1 should be used.
+ *
+ * Note that if the client has not waited on the fence that was returned
+ * from dequeueBuffer, that same fence should be passed to cancelBuffer to
+ * ensure that future uses of the buffer are preceded by a wait on that
+ * fence. The caller must not use the file descriptor after it is passed
+ * to cancelBuffer, and the ANativeWindow implementation is responsible for
+ * closing it.
+ *
+ * Returns 0 on success or -errno on error.
+ */
+ int (*cancelBuffer)(struct ANativeWindow* window,
+ struct ANativeWindowBuffer* buffer, int fenceFd);
+};
+
+ /* Backwards compatibility: use ANativeWindow (struct ANativeWindow in C).
+ * android_native_window_t is deprecated.
+ */
+typedef struct ANativeWindow ANativeWindow;
+typedef struct ANativeWindow android_native_window_t __deprecated;
+
+/*
+ * native_window_set_usage(..., usage)
+ * Sets the intended usage flags for the next buffers
+ * acquired with (*lockBuffer)() and on.
+ * By default (if this function is never called), a usage of
+ * GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE
+ * is assumed.
+ * Calling this function will usually cause following buffers to be
+ * reallocated.
+ */
+
+static inline int native_window_set_usage(
+ struct ANativeWindow* window, int usage)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_USAGE, usage);
+}
+
+/* deprecated. Always returns 0. Don't call. */
+static inline int native_window_connect(
+ struct ANativeWindow* window __UNUSED, int api __UNUSED) __deprecated;
+
+static inline int native_window_connect(
+ struct ANativeWindow* window __UNUSED, int api __UNUSED) {
+ return 0;
+}
+
+/* deprecated. Always returns 0. Don't call. */
+static inline int native_window_disconnect(
+ struct ANativeWindow* window __UNUSED, int api __UNUSED) __deprecated;
+
+static inline int native_window_disconnect(
+ struct ANativeWindow* window __UNUSED, int api __UNUSED) {
+ return 0;
+}
+
+/*
+ * native_window_set_crop(..., crop)
+ * Sets which region of the next queued buffers needs to be considered.
+ * Depending on the scaling mode, a buffer's crop region is scaled and/or
+ * cropped to match the surface's size. This function sets the crop in
+ * pre-transformed buffer pixel coordinates.
+ *
+ * The specified crop region applies to all buffers queued after it is called.
+ *
+ * If 'crop' is NULL, subsequently queued buffers won't be cropped.
+ *
+ * An error is returned if for instance the crop region is invalid, out of the
+ * buffer's bound or if the window is invalid.
+ */
+static inline int native_window_set_crop(
+ struct ANativeWindow* window,
+ android_native_rect_t const * crop)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_CROP, crop);
+}
+
+/*
+ * native_window_set_post_transform_crop(..., crop)
+ * Sets which region of the next queued buffers needs to be considered.
+ * Depending on the scaling mode, a buffer's crop region is scaled and/or
+ * cropped to match the surface's size. This function sets the crop in
+ * post-transformed pixel coordinates.
+ *
+ * The specified crop region applies to all buffers queued after it is called.
+ *
+ * If 'crop' is NULL, subsequently queued buffers won't be cropped.
+ *
+ * An error is returned if for instance the crop region is invalid, out of the
+ * buffer's bound or if the window is invalid.
+ */
+static inline int native_window_set_post_transform_crop(
+ struct ANativeWindow* window,
+ android_native_rect_t const * crop)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_POST_TRANSFORM_CROP, crop);
+}
+
+/*
+ * native_window_set_active_rect(..., active_rect)
+ *
+ * This function is deprecated and will be removed soon. For now it simply
+ * sets the post-transform crop for compatibility while multi-project commits
+ * get checked.
+ */
+static inline int native_window_set_active_rect(
+ struct ANativeWindow* window,
+ android_native_rect_t const * active_rect) __deprecated;
+
+static inline int native_window_set_active_rect(
+ struct ANativeWindow* window,
+ android_native_rect_t const * active_rect)
+{
+ return native_window_set_post_transform_crop(window, active_rect);
+}
+
+/*
+ * native_window_set_buffer_count(..., count)
+ * Sets the number of buffers associated with this native window.
+ */
+static inline int native_window_set_buffer_count(
+ struct ANativeWindow* window,
+ size_t bufferCount)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_BUFFER_COUNT, bufferCount);
+}
+
+/*
+ * native_window_set_buffers_geometry(..., int w, int h, int format)
+ * All buffers dequeued after this call will have the dimensions and format
+ * specified. A successful call to this function has the same effect as calling
+ * native_window_set_buffers_size and native_window_set_buffers_format.
+ *
+ * XXX: This function is deprecated. The native_window_set_buffers_dimensions
+ * and native_window_set_buffers_format functions should be used instead.
+ */
+static inline int native_window_set_buffers_geometry(
+ struct ANativeWindow* window,
+ int w, int h, int format) __deprecated;
+
+static inline int native_window_set_buffers_geometry(
+ struct ANativeWindow* window,
+ int w, int h, int format)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_GEOMETRY,
+ w, h, format);
+}
+
+/*
+ * native_window_set_buffers_dimensions(..., int w, int h)
+ * All buffers dequeued after this call will have the dimensions specified.
+ * In particular, all buffers will have a fixed-size, independent from the
+ * native-window size. They will be scaled according to the scaling mode
+ * (see native_window_set_scaling_mode) upon window composition.
+ *
+ * If w and h are 0, the normal behavior is restored. That is, dequeued buffers
+ * following this call will be sized to match the window's size.
+ *
+ * Calling this function will reset the window crop to a NULL value, which
+ * disables cropping of the buffers.
+ */
+static inline int native_window_set_buffers_dimensions(
+ struct ANativeWindow* window,
+ int w, int h)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS,
+ w, h);
+}
+
+/*
+ * native_window_set_buffers_user_dimensions(..., int w, int h)
+ *
+ * Sets the user buffer size for the window, which overrides the
+ * window's size. All buffers dequeued after this call will have the
+ * dimensions specified unless overridden by
+ * native_window_set_buffers_dimensions. All buffers will have a
+ * fixed-size, independent from the native-window size. They will be
+ * scaled according to the scaling mode (see
+ * native_window_set_scaling_mode) upon window composition.
+ *
+ * If w and h are 0, the normal behavior is restored. That is, the
+ * default buffer size will match the windows's size.
+ *
+ * Calling this function will reset the window crop to a NULL value, which
+ * disables cropping of the buffers.
+ */
+static inline int native_window_set_buffers_user_dimensions(
+ struct ANativeWindow* window,
+ int w, int h)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS,
+ w, h);
+}
+
+/*
+ * native_window_set_buffers_format(..., int format)
+ * All buffers dequeued after this call will have the format specified.
+ *
+ * If the specified format is 0, the default buffer format will be used.
+ */
+static inline int native_window_set_buffers_format(
+ struct ANativeWindow* window,
+ int format)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_FORMAT, format);
+}
+
+/*
+ * native_window_set_buffers_data_space(..., int dataSpace)
+ * All buffers queued after this call will be associated with the dataSpace
+ * parameter specified.
+ *
+ * dataSpace specifies additional information about the buffer that's dependent
+ * on the buffer format and the endpoints. For example, it can be used to convey
+ * the color space of the image data in the buffer, or it can be used to
+ * indicate that the buffers contain depth measurement data instead of color
+ * images. The default dataSpace is 0, HAL_DATASPACE_UNKNOWN, unless it has been
+ * overridden by the consumer.
+ */
+static inline int native_window_set_buffers_data_space(
+ struct ANativeWindow* window,
+ android_dataspace_t dataSpace)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_DATASPACE,
+ dataSpace);
+}
+
+/*
+ * native_window_set_buffers_transform(..., int transform)
+ * All buffers queued after this call will be displayed transformed according
+ * to the transform parameter specified.
+ */
+static inline int native_window_set_buffers_transform(
+ struct ANativeWindow* window,
+ int transform)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_TRANSFORM,
+ transform);
+}
+
+/*
+ * native_window_set_buffers_sticky_transform(..., int transform)
+ * All buffers queued after this call will be displayed transformed according
+ * to the transform parameter specified applied on top of the regular buffer
+ * transform. Setting this transform will disable the transform hint.
+ *
+ * Temporary - This is only intended to be used by the LEGACY camera mode, do
+ * not use this for anything else.
+ */
+static inline int native_window_set_buffers_sticky_transform(
+ struct ANativeWindow* window,
+ int transform)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_STICKY_TRANSFORM,
+ transform);
+}
+
+/*
+ * native_window_set_buffers_timestamp(..., int64_t timestamp)
+ * All buffers queued after this call will be associated with the timestamp
+ * parameter specified. If the timestamp is set to NATIVE_WINDOW_TIMESTAMP_AUTO
+ * (the default), timestamps will be generated automatically when queueBuffer is
+ * called. The timestamp is measured in nanoseconds, and is normally monotonically
+ * increasing. The timestamp should be unaffected by time-of-day adjustments,
+ * and for a camera should be strictly monotonic but for a media player may be
+ * reset when the position is set.
+ */
+static inline int native_window_set_buffers_timestamp(
+ struct ANativeWindow* window,
+ int64_t timestamp)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP,
+ timestamp);
+}
+
+/*
+ * native_window_set_scaling_mode(..., int mode)
+ * All buffers queued after this call will be associated with the scaling mode
+ * specified.
+ */
+static inline int native_window_set_scaling_mode(
+ struct ANativeWindow* window,
+ int mode)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_SCALING_MODE,
+ mode);
+}
+
+/*
+ * native_window_api_connect(..., int api)
+ * connects an API to this window. only one API can be connected at a time.
+ * Returns -EINVAL if for some reason the window cannot be connected, which
+ * can happen if it's connected to some other API.
+ */
+static inline int native_window_api_connect(
+ struct ANativeWindow* window, int api)
+{
+ return window->perform(window, NATIVE_WINDOW_API_CONNECT, api);
+}
+
+/*
+ * native_window_api_disconnect(..., int api)
+ * disconnect the API from this window.
+ * An error is returned if for instance the window wasn't connected in the
+ * first place.
+ */
+static inline int native_window_api_disconnect(
+ struct ANativeWindow* window, int api)
+{
+ return window->perform(window, NATIVE_WINDOW_API_DISCONNECT, api);
+}
+
+/*
+ * native_window_dequeue_buffer_and_wait(...)
+ * Dequeue a buffer and wait on the fence associated with that buffer. The
+ * buffer may safely be accessed immediately upon this function returning. An
+ * error is returned if either of the dequeue or the wait operations fail.
+ */
+static inline int native_window_dequeue_buffer_and_wait(ANativeWindow *anw,
+ struct ANativeWindowBuffer** anb) {
+ return anw->dequeueBuffer_DEPRECATED(anw, anb);
+}
+
+/*
+ * native_window_set_sideband_stream(..., native_handle_t*)
+ * Attach a sideband buffer stream to a native window.
+ */
+static inline int native_window_set_sideband_stream(
+ struct ANativeWindow* window,
+ native_handle_t* sidebandHandle)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_SIDEBAND_STREAM,
+ sidebandHandle);
+}
+
+/*
+ * native_window_set_surface_damage(..., android_native_rect_t* rects, int numRects)
+ * Set the surface damage (i.e., the region of the surface that has changed
+ * since the previous frame). The damage set by this call will be reset (to the
+ * default of full-surface damage) after calling queue, so this must be called
+ * prior to every frame with damage that does not cover the whole surface if the
+ * caller desires downstream consumers to use this optimization.
+ *
+ * The damage region is specified as an array of rectangles, with the important
+ * caveat that the origin of the surface is considered to be the bottom-left
+ * corner, as in OpenGL ES.
+ *
+ * If numRects is set to 0, rects may be NULL, and the surface damage will be
+ * set to the full surface (the same as if this function had not been called for
+ * this frame).
+ */
+static inline int native_window_set_surface_damage(
+ struct ANativeWindow* window,
+ const android_native_rect_t* rects, size_t numRects)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_SURFACE_DAMAGE,
+ rects, numRects);
+}
+
+__END_DECLS
+
+#endif /* SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H */
diff --git a/include/sysutils/FrameworkClient.h b/include/sysutils/FrameworkClient.h
new file mode 100644
index 0000000..4a3f0de
--- /dev/null
+++ b/include/sysutils/FrameworkClient.h
@@ -0,0 +1,21 @@
+#ifndef _FRAMEWORK_CLIENT_H
+#define _FRAMEWORK_CLIENT_H
+
+#include "List.h"
+
+#include <pthread.h>
+
+class FrameworkClient {
+ int mSocket;
+ pthread_mutex_t mWriteMutex;
+
+public:
+ FrameworkClient(int sock);
+ virtual ~FrameworkClient() {}
+
+ int sendMsg(const char *msg);
+ int sendMsg(const char *msg, const char *data);
+};
+
+typedef android::sysutils::List<FrameworkClient *> FrameworkClientCollection;
+#endif
diff --git a/include/sysutils/FrameworkCommand.h b/include/sysutils/FrameworkCommand.h
new file mode 100644
index 0000000..3e6264b
--- /dev/null
+++ b/include/sysutils/FrameworkCommand.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef __FRAMEWORK_CMD_HANDLER_H
+#define __FRAMEWORK_CMD_HANDLER_H
+
+#include "List.h"
+
+class SocketClient;
+
+class FrameworkCommand {
+private:
+ const char *mCommand;
+
+public:
+
+ FrameworkCommand(const char *cmd);
+ virtual ~FrameworkCommand() { }
+
+ virtual int runCommand(SocketClient *c, int argc, char **argv) = 0;
+
+ const char *getCommand() { return mCommand; }
+};
+
+typedef android::sysutils::List<FrameworkCommand *> FrameworkCommandCollection;
+#endif
diff --git a/include/sysutils/FrameworkListener.h b/include/sysutils/FrameworkListener.h
new file mode 100644
index 0000000..18049cd
--- /dev/null
+++ b/include/sysutils/FrameworkListener.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _FRAMEWORKSOCKETLISTENER_H
+#define _FRAMEWORKSOCKETLISTENER_H
+
+#include "SocketListener.h"
+#include "FrameworkCommand.h"
+
+class SocketClient;
+
+class FrameworkListener : public SocketListener {
+public:
+ static const int CMD_ARGS_MAX = 26;
+
+ /* 1 out of errorRate will be dropped */
+ int errorRate;
+
+private:
+ int mCommandCount;
+ bool mWithSeq;
+ FrameworkCommandCollection *mCommands;
+
+public:
+ FrameworkListener(const char *socketName);
+ FrameworkListener(const char *socketName, bool withSeq);
+ FrameworkListener(int sock);
+ virtual ~FrameworkListener() {}
+
+protected:
+ void registerCmd(FrameworkCommand *cmd);
+ virtual bool onDataAvailable(SocketClient *c);
+
+private:
+ void dispatchCommand(SocketClient *c, char *data);
+ void init(const char *socketName, bool withSeq);
+};
+#endif
diff --git a/include/sysutils/List.h b/include/sysutils/List.h
new file mode 100644
index 0000000..31f7b37
--- /dev/null
+++ b/include/sysutils/List.h
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2005 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.
+ */
+
+//
+// Templated list class. Normally we'd use STL, but we don't have that.
+// This class mimics STL's interfaces.
+//
+// Objects are copied into the list with the '=' operator or with copy-
+// construction, so if the compiler's auto-generated versions won't work for
+// you, define your own.
+//
+// The only class you want to use from here is "List".
+//
+#ifndef _SYSUTILS_LIST_H
+#define _SYSUTILS_LIST_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+namespace android {
+namespace sysutils {
+
+/*
+ * Doubly-linked list. Instantiate with "List<MyClass> myList".
+ *
+ * Objects added to the list are copied using the assignment operator,
+ * so this must be defined.
+ */
+template<typename T>
+class List
+{
+protected:
+ /*
+ * One element in the list.
+ */
+ class _Node {
+ public:
+ explicit _Node(const T& val) : mVal(val) {}
+ ~_Node() {}
+ inline T& getRef() { return mVal; }
+ inline const T& getRef() const { return mVal; }
+ inline _Node* getPrev() const { return mpPrev; }
+ inline _Node* getNext() const { return mpNext; }
+ inline void setVal(const T& val) { mVal = val; }
+ inline void setPrev(_Node* ptr) { mpPrev = ptr; }
+ inline void setNext(_Node* ptr) { mpNext = ptr; }
+ private:
+ friend class List;
+ friend class _ListIterator;
+ T mVal;
+ _Node* mpPrev;
+ _Node* mpNext;
+ };
+
+ /*
+ * Iterator for walking through the list.
+ */
+
+ template <typename TYPE>
+ struct CONST_ITERATOR {
+ typedef _Node const * NodePtr;
+ typedef const TYPE Type;
+ };
+
+ template <typename TYPE>
+ struct NON_CONST_ITERATOR {
+ typedef _Node* NodePtr;
+ typedef TYPE Type;
+ };
+
+ template<
+ typename U,
+ template <class> class Constness
+ >
+ class _ListIterator {
+ typedef _ListIterator<U, Constness> _Iter;
+ typedef typename Constness<U>::NodePtr _NodePtr;
+ typedef typename Constness<U>::Type _Type;
+
+ explicit _ListIterator(_NodePtr ptr) : mpNode(ptr) {}
+
+ public:
+ _ListIterator() {}
+ _ListIterator(const _Iter& rhs) : mpNode(rhs.mpNode) {}
+ ~_ListIterator() {}
+
+ // this will handle conversions from iterator to const_iterator
+ // (and also all convertible iterators)
+ // Here, in this implementation, the iterators can be converted
+ // if the nodes can be converted
+ template<typename V> explicit
+ _ListIterator(const V& rhs) : mpNode(rhs.mpNode) {}
+
+
+ /*
+ * Dereference operator. Used to get at the juicy insides.
+ */
+ _Type& operator*() const { return mpNode->getRef(); }
+ _Type* operator->() const { return &(mpNode->getRef()); }
+
+ /*
+ * Iterator comparison.
+ */
+ inline bool operator==(const _Iter& right) const {
+ return mpNode == right.mpNode; }
+
+ inline bool operator!=(const _Iter& right) const {
+ return mpNode != right.mpNode; }
+
+ /*
+ * handle comparisons between iterator and const_iterator
+ */
+ template<typename OTHER>
+ inline bool operator==(const OTHER& right) const {
+ return mpNode == right.mpNode; }
+
+ template<typename OTHER>
+ inline bool operator!=(const OTHER& right) const {
+ return mpNode != right.mpNode; }
+
+ /*
+ * Incr/decr, used to move through the list.
+ */
+ inline _Iter& operator++() { // pre-increment
+ mpNode = mpNode->getNext();
+ return *this;
+ }
+ const _Iter operator++(int) { // post-increment
+ _Iter tmp(*this);
+ mpNode = mpNode->getNext();
+ return tmp;
+ }
+ inline _Iter& operator--() { // pre-increment
+ mpNode = mpNode->getPrev();
+ return *this;
+ }
+ const _Iter operator--(int) { // post-increment
+ _Iter tmp(*this);
+ mpNode = mpNode->getPrev();
+ return tmp;
+ }
+
+ inline _NodePtr getNode() const { return mpNode; }
+
+ _NodePtr mpNode; /* should be private, but older gcc fails */
+ private:
+ friend class List;
+ };
+
+public:
+ List() {
+ prep();
+ }
+ List(const List<T>& src) { // copy-constructor
+ prep();
+ insert(begin(), src.begin(), src.end());
+ }
+ virtual ~List() {
+ clear();
+ delete[] (unsigned char*) mpMiddle;
+ }
+
+ typedef _ListIterator<T, NON_CONST_ITERATOR> iterator;
+ typedef _ListIterator<T, CONST_ITERATOR> const_iterator;
+
+ List<T>& operator=(const List<T>& right);
+
+ /* returns true if the list is empty */
+ inline bool empty() const { return mpMiddle->getNext() == mpMiddle; }
+
+ /* return #of elements in list */
+ size_t size() const {
+ return size_t(distance(begin(), end()));
+ }
+
+ /*
+ * Return the first element or one past the last element. The
+ * _Node* we're returning is converted to an "iterator" by a
+ * constructor in _ListIterator.
+ */
+ inline iterator begin() {
+ return iterator(mpMiddle->getNext());
+ }
+ inline const_iterator begin() const {
+ return const_iterator(const_cast<_Node const*>(mpMiddle->getNext()));
+ }
+ inline iterator end() {
+ return iterator(mpMiddle);
+ }
+ inline const_iterator end() const {
+ return const_iterator(const_cast<_Node const*>(mpMiddle));
+ }
+
+ /* add the object to the head or tail of the list */
+ void push_front(const T& val) { insert(begin(), val); }
+ void push_back(const T& val) { insert(end(), val); }
+
+ /* insert before the current node; returns iterator at new node */
+ iterator insert(iterator posn, const T& val)
+ {
+ _Node* newNode = new _Node(val); // alloc & copy-construct
+ newNode->setNext(posn.getNode());
+ newNode->setPrev(posn.getNode()->getPrev());
+ posn.getNode()->getPrev()->setNext(newNode);
+ posn.getNode()->setPrev(newNode);
+ return iterator(newNode);
+ }
+
+ /* insert a range of elements before the current node */
+ void insert(iterator posn, const_iterator first, const_iterator last) {
+ for ( ; first != last; ++first)
+ insert(posn, *first);
+ }
+
+ /* remove one entry; returns iterator at next node */
+ iterator erase(iterator posn) {
+ _Node* pNext = posn.getNode()->getNext();
+ _Node* pPrev = posn.getNode()->getPrev();
+ pPrev->setNext(pNext);
+ pNext->setPrev(pPrev);
+ delete posn.getNode();
+ return iterator(pNext);
+ }
+
+ /* remove a range of elements */
+ iterator erase(iterator first, iterator last) {
+ while (first != last)
+ erase(first++); // don't erase than incr later!
+ return iterator(last);
+ }
+
+ /* remove all contents of the list */
+ void clear() {
+ _Node* pCurrent = mpMiddle->getNext();
+ _Node* pNext;
+
+ while (pCurrent != mpMiddle) {
+ pNext = pCurrent->getNext();
+ delete pCurrent;
+ pCurrent = pNext;
+ }
+ mpMiddle->setPrev(mpMiddle);
+ mpMiddle->setNext(mpMiddle);
+ }
+
+ /*
+ * Measure the distance between two iterators. On exist, "first"
+ * will be equal to "last". The iterators must refer to the same
+ * list.
+ *
+ * FIXME: This is actually a generic iterator function. It should be a
+ * template function at the top-level with specializations for things like
+ * vector<>, which can just do pointer math). Here we limit it to
+ * _ListIterator of the same type but different constness.
+ */
+ template<
+ typename U,
+ template <class> class CL,
+ template <class> class CR
+ >
+ ptrdiff_t distance(
+ _ListIterator<U, CL> first, _ListIterator<U, CR> last) const
+ {
+ ptrdiff_t count = 0;
+ while (first != last) {
+ ++first;
+ ++count;
+ }
+ return count;
+ }
+
+private:
+ /*
+ * I want a _Node but don't need it to hold valid data. More
+ * to the point, I don't want T's constructor to fire, since it
+ * might have side-effects or require arguments. So, we do this
+ * slightly uncouth storage alloc.
+ */
+ void prep() {
+ mpMiddle = (_Node*) new unsigned char[sizeof(_Node)];
+ mpMiddle->setPrev(mpMiddle);
+ mpMiddle->setNext(mpMiddle);
+ }
+
+ /*
+ * This node plays the role of "pointer to head" and "pointer to tail".
+ * It sits in the middle of a circular list of nodes. The iterator
+ * runs around the circle until it encounters this one.
+ */
+ _Node* mpMiddle;
+};
+
+/*
+ * Assignment operator.
+ *
+ * The simplest way to do this would be to clear out the target list and
+ * fill it with the source. However, we can speed things along by
+ * re-using existing elements.
+ */
+template<class T>
+List<T>& List<T>::operator=(const List<T>& right)
+{
+ if (this == &right)
+ return *this; // self-assignment
+ iterator firstDst = begin();
+ iterator lastDst = end();
+ const_iterator firstSrc = right.begin();
+ const_iterator lastSrc = right.end();
+ while (firstSrc != lastSrc && firstDst != lastDst)
+ *firstDst++ = *firstSrc++;
+ if (firstSrc == lastSrc) // ran out of elements in source?
+ erase(firstDst, lastDst); // yes, erase any extras
+ else
+ insert(lastDst, firstSrc, lastSrc); // copy remaining over
+ return *this;
+}
+
+}; // namespace sysutils
+}; // namespace android
+
+#endif // _SYSUTILS_LIST_H
diff --git a/include/sysutils/NetlinkEvent.h b/include/sysutils/NetlinkEvent.h
new file mode 100644
index 0000000..4fa49c5
--- /dev/null
+++ b/include/sysutils/NetlinkEvent.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _NETLINKEVENT_H
+#define _NETLINKEVENT_H
+
+#include <sysutils/NetlinkListener.h>
+
+#define NL_PARAMS_MAX 32
+
+class NetlinkEvent {
+ int mSeq;
+ char *mPath;
+ int mAction;
+ char *mSubsystem;
+ char *mParams[NL_PARAMS_MAX];
+
+public:
+ const static int NlActionUnknown;
+ const static int NlActionAdd;
+ const static int NlActionRemove;
+ const static int NlActionChange;
+ const static int NlActionLinkDown;
+ const static int NlActionLinkUp;
+ const static int NlActionAddressUpdated;
+ const static int NlActionAddressRemoved;
+ const static int NlActionRdnss;
+ const static int NlActionRouteUpdated;
+ const static int NlActionRouteRemoved;
+
+ NetlinkEvent();
+ virtual ~NetlinkEvent();
+
+ bool decode(char *buffer, int size, int format = NetlinkListener::NETLINK_FORMAT_ASCII);
+ const char *findParam(const char *paramName);
+
+ const char *getSubsystem() { return mSubsystem; }
+ int getAction() { return mAction; }
+
+ void dump();
+
+ protected:
+ bool parseBinaryNetlinkMessage(char *buffer, int size);
+ bool parseAsciiNetlinkMessage(char *buffer, int size);
+ bool parseIfInfoMessage(const struct nlmsghdr *nh);
+ bool parseIfAddrMessage(const struct nlmsghdr *nh);
+ bool parseUlogPacketMessage(const struct nlmsghdr *nh);
+ bool parseNfPacketMessage(struct nlmsghdr *nh);
+ bool parseRtMessage(const struct nlmsghdr *nh);
+ bool parseNdUserOptMessage(const struct nlmsghdr *nh);
+};
+
+#endif
diff --git a/include/sysutils/NetlinkListener.h b/include/sysutils/NetlinkListener.h
new file mode 100644
index 0000000..82465d6
--- /dev/null
+++ b/include/sysutils/NetlinkListener.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _NETLINKLISTENER_H
+#define _NETLINKLISTENER_H
+
+#include "SocketListener.h"
+
+class NetlinkEvent;
+
+class NetlinkListener : public SocketListener {
+ char mBuffer[64 * 1024] __attribute__((aligned(4)));
+ int mFormat;
+
+public:
+ static const int NETLINK_FORMAT_ASCII = 0;
+ static const int NETLINK_FORMAT_BINARY = 1;
+ static const int NETLINK_FORMAT_BINARY_UNICAST = 2;
+
+#if 1
+ /* temporary version until we can get Motorola to update their
+ * ril.so. Their prebuilt ril.so is using this private class
+ * so changing the NetlinkListener() constructor breaks their ril.
+ */
+ NetlinkListener(int socket);
+ NetlinkListener(int socket, int format);
+#else
+ NetlinkListener(int socket, int format = NETLINK_FORMAT_ASCII);
+#endif
+ virtual ~NetlinkListener() {}
+
+protected:
+ virtual bool onDataAvailable(SocketClient *cli);
+ virtual void onEvent(NetlinkEvent *evt) = 0;
+};
+
+#endif
diff --git a/include/sysutils/ServiceManager.h b/include/sysutils/ServiceManager.h
new file mode 100644
index 0000000..c31dd8f
--- /dev/null
+++ b/include/sysutils/ServiceManager.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _SERVICE_MANAGER_H
+#define _SERVICE_MANAGER_H
+
+class ServiceManager {
+public:
+ ServiceManager();
+ virtual ~ServiceManager() {}
+
+ int start(const char *name);
+ int stop(const char *name);
+ bool isRunning(const char *name);
+};
+
+#endif
diff --git a/include/sysutils/SocketClient.h b/include/sysutils/SocketClient.h
new file mode 100644
index 0000000..1004f06
--- /dev/null
+++ b/include/sysutils/SocketClient.h
@@ -0,0 +1,88 @@
+#ifndef _SOCKET_CLIENT_H
+#define _SOCKET_CLIENT_H
+
+#include "List.h"
+
+#include <pthread.h>
+#include <cutils/atomic.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+
+class SocketClient {
+ int mSocket;
+ bool mSocketOwned;
+ pthread_mutex_t mWriteMutex;
+
+ // Peer process ID
+ pid_t mPid;
+
+ // Peer user ID
+ uid_t mUid;
+
+ // Peer group ID
+ gid_t mGid;
+
+ // Reference count (starts at 1)
+ pthread_mutex_t mRefCountMutex;
+ int mRefCount;
+
+ int mCmdNum;
+
+ bool mUseCmdNum;
+
+public:
+ SocketClient(int sock, bool owned);
+ SocketClient(int sock, bool owned, bool useCmdNum);
+ virtual ~SocketClient();
+
+ int getSocket() { return mSocket; }
+ pid_t getPid() const { return mPid; }
+ uid_t getUid() const { return mUid; }
+ gid_t getGid() const { return mGid; }
+ void setCmdNum(int cmdNum) {
+ android_atomic_release_store(cmdNum, &mCmdNum);
+ }
+ int getCmdNum() { return mCmdNum; }
+
+ // Send null-terminated C strings:
+ int sendMsg(int code, const char *msg, bool addErrno);
+ int sendMsg(int code, const char *msg, bool addErrno, bool useCmdNum);
+ int sendMsg(const char *msg);
+
+ // Provides a mechanism to send a response code to the client.
+ // Sends the code and a null character.
+ int sendCode(int code);
+
+ // Provides a mechanism to send binary data to client.
+ // Sends the code and a null character, followed by 4 bytes of
+ // big-endian length, and the data.
+ int sendBinaryMsg(int code, const void *data, int len);
+
+ // Sending binary data:
+ int sendData(const void *data, int len);
+ // iovec contents not preserved through call
+ int sendDatav(struct iovec *iov, int iovcnt);
+
+ // Optional reference counting. Reference count starts at 1. If
+ // it's decremented to 0, it deletes itself.
+ // SocketListener creates a SocketClient (at refcount 1) and calls
+ // decRef() when it's done with the client.
+ void incRef();
+ bool decRef(); // returns true at 0 (but note: SocketClient already deleted)
+
+ // return a new string in quotes with '\\' and '\"' escaped for "my arg"
+ // transmissions
+ static char *quoteArg(const char *arg);
+
+private:
+ void init(int socket, bool owned, bool useCmdNum);
+
+ // Sending binary data. The caller should make sure this is protected
+ // from multiple threads entering simultaneously.
+ // returns 0 if successful, -1 if there is a 0 byte write or if any
+ // other error occurred (use errno to get the error)
+ int sendDataLockedv(struct iovec *iov, int iovcnt);
+};
+
+typedef android::sysutils::List<SocketClient *> SocketClientCollection;
+#endif
diff --git a/include/sysutils/SocketClientCommand.h b/include/sysutils/SocketClientCommand.h
new file mode 100644
index 0000000..746bc25
--- /dev/null
+++ b/include/sysutils/SocketClientCommand.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _SOCKETCLIENTCOMMAND_H
+#define _SOCKETCLIENTCOMMAND_H
+
+#include <sysutils/SocketClient.h>
+
+class SocketClientCommand {
+public:
+ virtual ~SocketClientCommand() { }
+ virtual void runSocketCommand(SocketClient *client) = 0;
+};
+
+#endif
diff --git a/include/sysutils/SocketListener.h b/include/sysutils/SocketListener.h
new file mode 100644
index 0000000..bc93b86
--- /dev/null
+++ b/include/sysutils/SocketListener.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2008-2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _SOCKETLISTENER_H
+#define _SOCKETLISTENER_H
+
+#include <pthread.h>
+
+#include <sysutils/SocketClient.h>
+#include "SocketClientCommand.h"
+
+class SocketListener {
+ bool mListen;
+ const char *mSocketName;
+ int mSock;
+ SocketClientCollection *mClients;
+ pthread_mutex_t mClientsLock;
+ int mCtrlPipe[2];
+ pthread_t mThread;
+ bool mUseCmdNum;
+
+public:
+ SocketListener(const char *socketName, bool listen);
+ SocketListener(const char *socketName, bool listen, bool useCmdNum);
+ SocketListener(int socketFd, bool listen);
+
+ virtual ~SocketListener();
+ int startListener();
+ int startListener(int backlog);
+ int stopListener();
+
+ void sendBroadcast(int code, const char *msg, bool addErrno);
+
+ void runOnEachSocket(SocketClientCommand *command);
+
+ bool release(SocketClient *c) { return release(c, true); }
+
+protected:
+ virtual bool onDataAvailable(SocketClient *c) = 0;
+
+private:
+ bool release(SocketClient *c, bool wakeup);
+ static void *threadStart(void *obj);
+ void runListener();
+ void init(const char *socketName, int socketFd, bool listen, bool useCmdNum);
+};
+#endif
diff --git a/include/usbhost/usbhost.h b/include/usbhost/usbhost.h
new file mode 100644
index 0000000..d26e931
--- /dev/null
+++ b/include/usbhost/usbhost.h
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __USB_HOST_H
+#define __USB_HOST_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20)
+#include <linux/usb/ch9.h>
+#else
+#include <linux/usb_ch9.h>
+#endif
+
+struct usb_host_context;
+struct usb_endpoint_descriptor;
+
+struct usb_descriptor_iter {
+ unsigned char* config;
+ unsigned char* config_end;
+ unsigned char* curr_desc;
+};
+
+struct usb_request
+{
+ struct usb_device *dev;
+ void* buffer;
+ int buffer_length;
+ int actual_length;
+ int max_packet_size;
+ void *private_data; /* struct usbdevfs_urb* */
+ int endpoint;
+ void *client_data; /* free for use by client */
+};
+
+/* Callback for notification when new USB devices are attached.
+ * Return true to exit from usb_host_run.
+ */
+typedef int (* usb_device_added_cb)(const char *dev_name, void *client_data);
+
+/* Callback for notification when USB devices are removed.
+ * Return true to exit from usb_host_run.
+ */
+typedef int (* usb_device_removed_cb)(const char *dev_name, void *client_data);
+
+/* Callback indicating that initial device discovery is done.
+ * Return true to exit from usb_host_run.
+ */
+typedef int (* usb_discovery_done_cb)(void *client_data);
+
+/* Call this to initialize the USB host library. */
+struct usb_host_context *usb_host_init(void);
+
+/* Call this to cleanup the USB host library. */
+void usb_host_cleanup(struct usb_host_context *context);
+
+/* Call this to get the inotify file descriptor. */
+int usb_host_get_fd(struct usb_host_context *context);
+
+/* Call this to initialize the usb host context. */
+int usb_host_load(struct usb_host_context *context,
+ usb_device_added_cb added_cb,
+ usb_device_removed_cb removed_cb,
+ usb_discovery_done_cb discovery_done_cb,
+ void *client_data);
+
+/* Call this to read and handle occuring usb event. */
+int usb_host_read_event(struct usb_host_context *context);
+
+/* Call this to monitor the USB bus for new and removed devices.
+ * This is intended to be called from a dedicated thread,
+ * as it will not return until one of the callbacks returns true.
+ * added_cb will be called immediately for each existing USB device,
+ * and subsequently each time a new device is added.
+ * removed_cb is called when USB devices are removed from the bus.
+ * discovery_done_cb is called after the initial discovery of already
+ * connected devices is complete.
+ */
+void usb_host_run(struct usb_host_context *context,
+ usb_device_added_cb added_cb,
+ usb_device_removed_cb removed_cb,
+ usb_discovery_done_cb discovery_done_cb,
+ void *client_data);
+
+/* Creates a usb_device object for a USB device */
+struct usb_device *usb_device_open(const char *dev_name);
+
+/* Releases all resources associated with the USB device */
+void usb_device_close(struct usb_device *device);
+
+/* Creates a usb_device object for already open USB device */
+struct usb_device *usb_device_new(const char *dev_name, int fd);
+
+/* Returns the file descriptor for the usb_device */
+int usb_device_get_fd(struct usb_device *device);
+
+/* Returns the name for the USB device, which is the same as
+ * the dev_name passed to usb_device_open()
+ */
+const char* usb_device_get_name(struct usb_device *device);
+
+/* Returns a unique ID for the device.
+ *Currently this is generated from the dev_name path.
+ */
+int usb_device_get_unique_id(struct usb_device *device);
+
+/* Returns a unique ID for the device name.
+ * Currently this is generated from the device path.
+ */
+int usb_device_get_unique_id_from_name(const char* name);
+
+/* Returns the device name for the unique ID.
+ * Call free() to deallocate the returned string */
+char* usb_device_get_name_from_unique_id(int id);
+
+/* Returns the USB vendor ID from the device descriptor for the USB device */
+uint16_t usb_device_get_vendor_id(struct usb_device *device);
+
+/* Returns the USB product ID from the device descriptor for the USB device */
+uint16_t usb_device_get_product_id(struct usb_device *device);
+
+const struct usb_device_descriptor* usb_device_get_device_descriptor(struct usb_device *device);
+
+/* Returns a USB descriptor string for the given string ID.
+ * Used to implement usb_device_get_manufacturer_name,
+ * usb_device_get_product_name and usb_device_get_serial.
+ * Call free() to free the result when you are done with it.
+ */
+char* usb_device_get_string(struct usb_device *device, int id);
+
+/* Returns the manufacturer name for the USB device.
+ * Call free() to free the result when you are done with it.
+ */
+char* usb_device_get_manufacturer_name(struct usb_device *device);
+
+/* Returns the product name for the USB device.
+ * Call free() to free the result when you are done with it.
+ */
+char* usb_device_get_product_name(struct usb_device *device);
+
+/* Returns the USB serial number for the USB device.
+ * Call free() to free the result when you are done with it.
+ */
+char* usb_device_get_serial(struct usb_device *device);
+
+/* Returns true if we have write access to the USB device,
+ * and false if we only have access to the USB device configuration.
+ */
+int usb_device_is_writeable(struct usb_device *device);
+
+/* Initializes a usb_descriptor_iter, which can be used to iterate through all
+ * the USB descriptors for a USB device.
+ */
+void usb_descriptor_iter_init(struct usb_device *device, struct usb_descriptor_iter *iter);
+
+/* Returns the next USB descriptor for a device, or NULL if we have reached the
+ * end of the list.
+ */
+struct usb_descriptor_header *usb_descriptor_iter_next(struct usb_descriptor_iter *iter);
+
+/* Claims the specified interface of a USB device */
+int usb_device_claim_interface(struct usb_device *device, unsigned int interface);
+
+/* Releases the specified interface of a USB device */
+int usb_device_release_interface(struct usb_device *device, unsigned int interface);
+
+/* Requests the kernel to connect or disconnect its driver for the specified interface.
+ * This can be used to ask the kernel to disconnect its driver for a device
+ * so usb_device_claim_interface can claim it instead.
+ */
+int usb_device_connect_kernel_driver(struct usb_device *device,
+ unsigned int interface, int connect);
+
+/* Sets the current configuration for the device to the specified configuration */
+int usb_device_set_configuration(struct usb_device *device, int configuration);
+
+/* Sets the specified interface of a USB device */
+int usb_device_set_interface(struct usb_device *device, unsigned int interface,
+ unsigned int alt_setting);
+
+/* Sends a control message to the specified device on endpoint zero */
+int usb_device_control_transfer(struct usb_device *device,
+ int requestType,
+ int request,
+ int value,
+ int index,
+ void* buffer,
+ int length,
+ unsigned int timeout);
+
+/* Reads or writes on a bulk endpoint.
+ * Returns number of bytes transferred, or negative value for error.
+ */
+int usb_device_bulk_transfer(struct usb_device *device,
+ int endpoint,
+ void* buffer,
+ int length,
+ unsigned int timeout);
+
+/* Creates a new usb_request. */
+struct usb_request *usb_request_new(struct usb_device *dev,
+ const struct usb_endpoint_descriptor *ep_desc);
+
+/* Releases all resources associated with the request */
+void usb_request_free(struct usb_request *req);
+
+/* Submits a read or write request on the specified device */
+int usb_request_queue(struct usb_request *req);
+
+ /* Waits for the results of a previous usb_request_queue operation.
+ * Returns a usb_request, or NULL for error.
+ */
+struct usb_request *usb_request_wait(struct usb_device *dev);
+
+/* Cancels a pending usb_request_queue() operation. */
+int usb_request_cancel(struct usb_request *req);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __USB_HOST_H */
diff --git a/include/utils/AndroidThreads.h b/include/utils/AndroidThreads.h
new file mode 100644
index 0000000..aad1e82
--- /dev/null
+++ b/include/utils/AndroidThreads.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBS_UTILS_ANDROID_THREADS_H
+#define _LIBS_UTILS_ANDROID_THREADS_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#if !defined(_WIN32)
+# include <pthread.h>
+#endif
+
+#include <utils/ThreadDefs.h>
+
+// ---------------------------------------------------------------------------
+// C API
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Create and run a new thread.
+extern int androidCreateThread(android_thread_func_t, void *);
+
+// Create thread with lots of parameters
+extern int androidCreateThreadEtc(android_thread_func_t entryFunction,
+ void *userData,
+ const char* threadName,
+ int32_t threadPriority,
+ size_t threadStackSize,
+ android_thread_id_t *threadId);
+
+// Get some sort of unique identifier for the current thread.
+extern android_thread_id_t androidGetThreadId();
+
+// Low-level thread creation -- never creates threads that can
+// interact with the Java VM.
+extern int androidCreateRawThreadEtc(android_thread_func_t entryFunction,
+ void *userData,
+ const char* threadName,
+ int32_t threadPriority,
+ size_t threadStackSize,
+ android_thread_id_t *threadId);
+
+// set the same of the running thread
+extern void androidSetThreadName(const char* name);
+
+// Used by the Java Runtime to control how threads are created, so that
+// they can be proper and lovely Java threads.
+typedef int (*android_create_thread_fn)(android_thread_func_t entryFunction,
+ void *userData,
+ const char* threadName,
+ int32_t threadPriority,
+ size_t threadStackSize,
+ android_thread_id_t *threadId);
+
+extern void androidSetCreateThreadFunc(android_create_thread_fn func);
+
+// ------------------------------------------------------------------
+// Extra functions working with raw pids.
+
+#ifdef HAVE_ANDROID_OS
+// Change the priority AND scheduling group of a particular thread. The priority
+// should be one of the ANDROID_PRIORITY constants. Returns INVALID_OPERATION
+// if the priority set failed, else another value if just the group set failed;
+// in either case errno is set. Thread ID zero means current thread.
+extern int androidSetThreadPriority(pid_t tid, int prio);
+
+// Get the current priority of a particular thread. Returns one of the
+// ANDROID_PRIORITY constants or a negative result in case of error.
+extern int androidGetThreadPriority(pid_t tid);
+#endif
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+// ----------------------------------------------------------------------------
+// C++ API
+#ifdef __cplusplus
+namespace android {
+// ----------------------------------------------------------------------------
+
+// Create and run a new thread.
+inline bool createThread(thread_func_t f, void *a) {
+ return androidCreateThread(f, a) ? true : false;
+}
+
+// Create thread with lots of parameters
+inline bool createThreadEtc(thread_func_t entryFunction,
+ void *userData,
+ const char* threadName = "android:unnamed_thread",
+ int32_t threadPriority = PRIORITY_DEFAULT,
+ size_t threadStackSize = 0,
+ thread_id_t *threadId = 0)
+{
+ return androidCreateThreadEtc(entryFunction, userData, threadName,
+ threadPriority, threadStackSize, threadId) ? true : false;
+}
+
+// Get some sort of unique identifier for the current thread.
+inline thread_id_t getThreadId() {
+ return androidGetThreadId();
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+#endif // __cplusplus
+// ----------------------------------------------------------------------------
+
+#endif // _LIBS_UTILS_ANDROID_THREADS_H
diff --git a/include/utils/Atomic.h b/include/utils/Atomic.h
new file mode 100644
index 0000000..7eb476c
--- /dev/null
+++ b/include/utils/Atomic.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_UTILS_ATOMIC_H
+#define ANDROID_UTILS_ATOMIC_H
+
+#include <cutils/atomic.h>
+
+#endif // ANDROID_UTILS_ATOMIC_H
diff --git a/include/utils/BasicHashtable.h b/include/utils/BasicHashtable.h
new file mode 100644
index 0000000..c235d62
--- /dev/null
+++ b/include/utils/BasicHashtable.h
@@ -0,0 +1,402 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_BASIC_HASHTABLE_H
+#define ANDROID_BASIC_HASHTABLE_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <utils/SharedBuffer.h>
+#include <utils/TypeHelpers.h>
+
+namespace android {
+
+/* Implementation type. Nothing to see here. */
+class BasicHashtableImpl {
+protected:
+ struct Bucket {
+ // The collision flag indicates that the bucket is part of a collision chain
+ // such that at least two entries both hash to this bucket. When true, we
+ // may need to seek further along the chain to find the entry.
+ static const uint32_t COLLISION = 0x80000000UL;
+
+ // The present flag indicates that the bucket contains an initialized entry value.
+ static const uint32_t PRESENT = 0x40000000UL;
+
+ // Mask for 30 bits worth of the hash code that are stored within the bucket to
+ // speed up lookups and rehashing by eliminating the need to recalculate the
+ // hash code of the entry's key.
+ static const uint32_t HASH_MASK = 0x3fffffffUL;
+
+ // Combined value that stores the collision and present flags as well as
+ // a 30 bit hash code.
+ uint32_t cookie;
+
+ // Storage for the entry begins here.
+ char entry[0];
+ };
+
+ BasicHashtableImpl(size_t entrySize, bool hasTrivialDestructor,
+ size_t minimumInitialCapacity, float loadFactor);
+ BasicHashtableImpl(const BasicHashtableImpl& other);
+ virtual ~BasicHashtableImpl();
+
+ void dispose();
+
+ inline void edit() {
+ if (mBuckets && !SharedBuffer::bufferFromData(mBuckets)->onlyOwner()) {
+ clone();
+ }
+ }
+
+ void setTo(const BasicHashtableImpl& other);
+ void clear();
+
+ ssize_t next(ssize_t index) const;
+ ssize_t find(ssize_t index, hash_t hash, const void* __restrict__ key) const;
+ size_t add(hash_t hash, const void* __restrict__ entry);
+ void removeAt(size_t index);
+ void rehash(size_t minimumCapacity, float loadFactor);
+
+ const size_t mBucketSize; // number of bytes per bucket including the entry
+ const bool mHasTrivialDestructor; // true if the entry type does not require destruction
+ size_t mCapacity; // number of buckets that can be filled before exceeding load factor
+ float mLoadFactor; // load factor
+ size_t mSize; // number of elements actually in the table
+ size_t mFilledBuckets; // number of buckets for which collision or present is true
+ size_t mBucketCount; // number of slots in the mBuckets array
+ void* mBuckets; // array of buckets, as a SharedBuffer
+
+ inline const Bucket& bucketAt(const void* __restrict__ buckets, size_t index) const {
+ return *reinterpret_cast<const Bucket*>(
+ static_cast<const uint8_t*>(buckets) + index * mBucketSize);
+ }
+
+ inline Bucket& bucketAt(void* __restrict__ buckets, size_t index) const {
+ return *reinterpret_cast<Bucket*>(static_cast<uint8_t*>(buckets) + index * mBucketSize);
+ }
+
+ virtual bool compareBucketKey(const Bucket& bucket, const void* __restrict__ key) const = 0;
+ virtual void initializeBucketEntry(Bucket& bucket, const void* __restrict__ entry) const = 0;
+ virtual void destroyBucketEntry(Bucket& bucket) const = 0;
+
+private:
+ void clone();
+
+ // Allocates a bucket array as a SharedBuffer.
+ void* allocateBuckets(size_t count) const;
+
+ // Releases a bucket array's associated SharedBuffer.
+ void releaseBuckets(void* __restrict__ buckets, size_t count) const;
+
+ // Destroys the contents of buckets (invokes destroyBucketEntry for each
+ // populated bucket if needed).
+ void destroyBuckets(void* __restrict__ buckets, size_t count) const;
+
+ // Copies the content of buckets (copies the cookie and invokes copyBucketEntry
+ // for each populated bucket if needed).
+ void copyBuckets(const void* __restrict__ fromBuckets,
+ void* __restrict__ toBuckets, size_t count) const;
+
+ // Determines the appropriate size of a bucket array to store a certain minimum
+ // number of entries and returns its effective capacity.
+ static void determineCapacity(size_t minimumCapacity, float loadFactor,
+ size_t* __restrict__ outBucketCount, size_t* __restrict__ outCapacity);
+
+ // Trim a hash code to 30 bits to match what we store in the bucket's cookie.
+ inline static hash_t trimHash(hash_t hash) {
+ return (hash & Bucket::HASH_MASK) ^ (hash >> 30);
+ }
+
+ // Returns the index of the first bucket that is in the collision chain
+ // for the specified hash code, given the total number of buckets.
+ // (Primary hash)
+ inline static size_t chainStart(hash_t hash, size_t count) {
+ return hash % count;
+ }
+
+ // Returns the increment to add to a bucket index to seek to the next bucket
+ // in the collision chain for the specified hash code, given the total number of buckets.
+ // (Secondary hash)
+ inline static size_t chainIncrement(hash_t hash, size_t count) {
+ return ((hash >> 7) | (hash << 25)) % (count - 1) + 1;
+ }
+
+ // Returns the index of the next bucket that is in the collision chain
+ // that is defined by the specified increment, given the total number of buckets.
+ inline static size_t chainSeek(size_t index, size_t increment, size_t count) {
+ return (index + increment) % count;
+ }
+};
+
+/*
+ * A BasicHashtable stores entries that are indexed by hash code in place
+ * within an array. The basic operations are finding entries by key,
+ * adding new entries and removing existing entries.
+ *
+ * This class provides a very limited set of operations with simple semantics.
+ * It is intended to be used as a building block to construct more complex
+ * and interesting data structures such as HashMap. Think very hard before
+ * adding anything extra to BasicHashtable, it probably belongs at a
+ * higher level of abstraction.
+ *
+ * TKey: The key type.
+ * TEntry: The entry type which is what is actually stored in the array.
+ *
+ * TKey must support the following contract:
+ * bool operator==(const TKey& other) const; // return true if equal
+ * bool operator!=(const TKey& other) const; // return true if unequal
+ *
+ * TEntry must support the following contract:
+ * const TKey& getKey() const; // get the key from the entry
+ *
+ * This class supports storing entries with duplicate keys. Of course, it can't
+ * tell them apart during removal so only the first entry will be removed.
+ * We do this because it means that operations like add() can't fail.
+ */
+template <typename TKey, typename TEntry>
+class BasicHashtable : private BasicHashtableImpl {
+public:
+ /* Creates a hashtable with the specified minimum initial capacity.
+ * The underlying array will be created when the first entry is added.
+ *
+ * minimumInitialCapacity: The minimum initial capacity for the hashtable.
+ * Default is 0.
+ * loadFactor: The desired load factor for the hashtable, between 0 and 1.
+ * Default is 0.75.
+ */
+ BasicHashtable(size_t minimumInitialCapacity = 0, float loadFactor = 0.75f);
+
+ /* Copies a hashtable.
+ * The underlying storage is shared copy-on-write.
+ */
+ BasicHashtable(const BasicHashtable& other);
+
+ /* Clears and destroys the hashtable.
+ */
+ virtual ~BasicHashtable();
+
+ /* Making this hashtable a copy of the other hashtable.
+ * The underlying storage is shared copy-on-write.
+ *
+ * other: The hashtable to copy.
+ */
+ inline BasicHashtable<TKey, TEntry>& operator =(const BasicHashtable<TKey, TEntry> & other) {
+ setTo(other);
+ return *this;
+ }
+
+ /* Returns the number of entries in the hashtable.
+ */
+ inline size_t size() const {
+ return mSize;
+ }
+
+ /* Returns the capacity of the hashtable, which is the number of elements that can
+ * added to the hashtable without requiring it to be grown.
+ */
+ inline size_t capacity() const {
+ return mCapacity;
+ }
+
+ /* Returns the number of buckets that the hashtable has, which is the size of its
+ * underlying array.
+ */
+ inline size_t bucketCount() const {
+ return mBucketCount;
+ }
+
+ /* Returns the load factor of the hashtable. */
+ inline float loadFactor() const {
+ return mLoadFactor;
+ };
+
+ /* Returns a const reference to the entry at the specified index.
+ *
+ * index: The index of the entry to retrieve. Must be a valid index within
+ * the bounds of the hashtable.
+ */
+ inline const TEntry& entryAt(size_t index) const {
+ return entryFor(bucketAt(mBuckets, index));
+ }
+
+ /* Returns a non-const reference to the entry at the specified index.
+ *
+ * index: The index of the entry to edit. Must be a valid index within
+ * the bounds of the hashtable.
+ */
+ inline TEntry& editEntryAt(size_t index) {
+ edit();
+ return entryFor(bucketAt(mBuckets, index));
+ }
+
+ /* Clears the hashtable.
+ * All entries in the hashtable are destroyed immediately.
+ * If you need to do something special with the entries in the hashtable then iterate
+ * over them and do what you need before clearing the hashtable.
+ */
+ inline void clear() {
+ BasicHashtableImpl::clear();
+ }
+
+ /* Returns the index of the next entry in the hashtable given the index of a previous entry.
+ * If the given index is -1, then returns the index of the first entry in the hashtable,
+ * if there is one, or -1 otherwise.
+ * If the given index is not -1, then returns the index of the next entry in the hashtable,
+ * in strictly increasing order, or -1 if there are none left.
+ *
+ * index: The index of the previous entry that was iterated, or -1 to begin
+ * iteration at the beginning of the hashtable.
+ */
+ inline ssize_t next(ssize_t index) const {
+ return BasicHashtableImpl::next(index);
+ }
+
+ /* Finds the index of an entry with the specified key.
+ * If the given index is -1, then returns the index of the first matching entry,
+ * otherwise returns the index of the next matching entry.
+ * If the hashtable contains multiple entries with keys that match the requested
+ * key, then the sequence of entries returned is arbitrary.
+ * Returns -1 if no entry was found.
+ *
+ * index: The index of the previous entry with the specified key, or -1 to
+ * find the first matching entry.
+ * hash: The hashcode of the key.
+ * key: The key.
+ */
+ inline ssize_t find(ssize_t index, hash_t hash, const TKey& key) const {
+ return BasicHashtableImpl::find(index, hash, &key);
+ }
+
+ /* Adds the entry to the hashtable.
+ * Returns the index of the newly added entry.
+ * If an entry with the same key already exists, then a duplicate entry is added.
+ * If the entry will not fit, then the hashtable's capacity is increased and
+ * its contents are rehashed. See rehash().
+ *
+ * hash: The hashcode of the key.
+ * entry: The entry to add.
+ */
+ inline size_t add(hash_t hash, const TEntry& entry) {
+ return BasicHashtableImpl::add(hash, &entry);
+ }
+
+ /* Removes the entry with the specified index from the hashtable.
+ * The entry is destroyed immediately.
+ * The index must be valid.
+ *
+ * The hashtable is not compacted after an item is removed, so it is legal
+ * to continue iterating over the hashtable using next() or find().
+ *
+ * index: The index of the entry to remove. Must be a valid index within the
+ * bounds of the hashtable, and it must refer to an existing entry.
+ */
+ inline void removeAt(size_t index) {
+ BasicHashtableImpl::removeAt(index);
+ }
+
+ /* Rehashes the contents of the hashtable.
+ * Grows the hashtable to at least the specified minimum capacity or the
+ * current number of elements, whichever is larger.
+ *
+ * Rehashing causes all entries to be copied and the entry indices may change.
+ * Although the hash codes are cached by the hashtable, rehashing can be an
+ * expensive operation and should be avoided unless the hashtable's size
+ * needs to be changed.
+ *
+ * Rehashing is the only way to change the capacity or load factor of the
+ * hashtable once it has been created. It can be used to compact the
+ * hashtable by choosing a minimum capacity that is smaller than the current
+ * capacity (such as 0).
+ *
+ * minimumCapacity: The desired minimum capacity after rehashing.
+ * loadFactor: The desired load factor after rehashing.
+ */
+ inline void rehash(size_t minimumCapacity, float loadFactor) {
+ BasicHashtableImpl::rehash(minimumCapacity, loadFactor);
+ }
+
+ /* Determines whether there is room to add another entry without rehashing.
+ * When this returns true, a subsequent add() operation is guaranteed to
+ * complete without performing a rehash.
+ */
+ inline bool hasMoreRoom() const {
+ return mCapacity > mFilledBuckets;
+ }
+
+protected:
+ static inline const TEntry& entryFor(const Bucket& bucket) {
+ return reinterpret_cast<const TEntry&>(bucket.entry);
+ }
+
+ static inline TEntry& entryFor(Bucket& bucket) {
+ return reinterpret_cast<TEntry&>(bucket.entry);
+ }
+
+ virtual bool compareBucketKey(const Bucket& bucket, const void* __restrict__ key) const;
+ virtual void initializeBucketEntry(Bucket& bucket, const void* __restrict__ entry) const;
+ virtual void destroyBucketEntry(Bucket& bucket) const;
+
+private:
+ // For dumping the raw contents of a hashtable during testing.
+ friend class BasicHashtableTest;
+ inline uint32_t cookieAt(size_t index) const {
+ return bucketAt(mBuckets, index).cookie;
+ }
+};
+
+template <typename TKey, typename TEntry>
+BasicHashtable<TKey, TEntry>::BasicHashtable(size_t minimumInitialCapacity, float loadFactor) :
+ BasicHashtableImpl(sizeof(TEntry), traits<TEntry>::has_trivial_dtor,
+ minimumInitialCapacity, loadFactor) {
+}
+
+template <typename TKey, typename TEntry>
+BasicHashtable<TKey, TEntry>::BasicHashtable(const BasicHashtable<TKey, TEntry>& other) :
+ BasicHashtableImpl(other) {
+}
+
+template <typename TKey, typename TEntry>
+BasicHashtable<TKey, TEntry>::~BasicHashtable() {
+ dispose();
+}
+
+template <typename TKey, typename TEntry>
+bool BasicHashtable<TKey, TEntry>::compareBucketKey(const Bucket& bucket,
+ const void* __restrict__ key) const {
+ return entryFor(bucket).getKey() == *static_cast<const TKey*>(key);
+}
+
+template <typename TKey, typename TEntry>
+void BasicHashtable<TKey, TEntry>::initializeBucketEntry(Bucket& bucket,
+ const void* __restrict__ entry) const {
+ if (!traits<TEntry>::has_trivial_copy) {
+ new (&entryFor(bucket)) TEntry(*(static_cast<const TEntry*>(entry)));
+ } else {
+ memcpy(&entryFor(bucket), entry, sizeof(TEntry));
+ }
+}
+
+template <typename TKey, typename TEntry>
+void BasicHashtable<TKey, TEntry>::destroyBucketEntry(Bucket& bucket) const {
+ if (!traits<TEntry>::has_trivial_dtor) {
+ entryFor(bucket).~TEntry();
+ }
+}
+
+}; // namespace android
+
+#endif // ANDROID_BASIC_HASHTABLE_H
diff --git a/include/utils/BitSet.h b/include/utils/BitSet.h
new file mode 100644
index 0000000..8c61293
--- /dev/null
+++ b/include/utils/BitSet.h
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef UTILS_BITSET_H
+#define UTILS_BITSET_H
+
+#include <stdint.h>
+#include <utils/TypeHelpers.h>
+
+/*
+ * Contains some bit manipulation helpers.
+ */
+
+namespace android {
+
+// A simple set of 32 bits that can be individually marked or cleared.
+struct BitSet32 {
+ uint32_t value;
+
+ inline BitSet32() : value(0UL) { }
+ explicit inline BitSet32(uint32_t value) : value(value) { }
+
+ // Gets the value associated with a particular bit index.
+ static inline uint32_t valueForBit(uint32_t n) { return 0x80000000UL >> n; }
+
+ // Clears the bit set.
+ inline void clear() { clear(value); }
+
+ static inline void clear(uint32_t& value) { value = 0UL; }
+
+ // Returns the number of marked bits in the set.
+ inline uint32_t count() const { return count(value); }
+
+ static inline uint32_t count(uint32_t value) { return __builtin_popcountl(value); }
+
+ // Returns true if the bit set does not contain any marked bits.
+ inline bool isEmpty() const { return isEmpty(value); }
+
+ static inline bool isEmpty(uint32_t value) { return ! value; }
+
+ // Returns true if the bit set does not contain any unmarked bits.
+ inline bool isFull() const { return isFull(value); }
+
+ static inline bool isFull(uint32_t value) { return value == 0xffffffffUL; }
+
+ // Returns true if the specified bit is marked.
+ inline bool hasBit(uint32_t n) const { return hasBit(value, n); }
+
+ static inline bool hasBit(uint32_t value, uint32_t n) { return value & valueForBit(n); }
+
+ // Marks the specified bit.
+ inline void markBit(uint32_t n) { markBit(value, n); }
+
+ static inline void markBit (uint32_t& value, uint32_t n) { value |= valueForBit(n); }
+
+ // Clears the specified bit.
+ inline void clearBit(uint32_t n) { clearBit(value, n); }
+
+ static inline void clearBit(uint32_t& value, uint32_t n) { value &= ~ valueForBit(n); }
+
+ // Finds the first marked bit in the set.
+ // Result is undefined if all bits are unmarked.
+ inline uint32_t firstMarkedBit() const { return firstMarkedBit(value); }
+
+ static uint32_t firstMarkedBit(uint32_t value) { return clz_checked(value); }
+
+ // Finds the first unmarked bit in the set.
+ // Result is undefined if all bits are marked.
+ inline uint32_t firstUnmarkedBit() const { return firstUnmarkedBit(value); }
+
+ static inline uint32_t firstUnmarkedBit(uint32_t value) { return clz_checked(~ value); }
+
+ // Finds the last marked bit in the set.
+ // Result is undefined if all bits are unmarked.
+ inline uint32_t lastMarkedBit() const { return lastMarkedBit(value); }
+
+ static inline uint32_t lastMarkedBit(uint32_t value) { return 31 - ctz_checked(value); }
+
+ // Finds the first marked bit in the set and clears it. Returns the bit index.
+ // Result is undefined if all bits are unmarked.
+ inline uint32_t clearFirstMarkedBit() { return clearFirstMarkedBit(value); }
+
+ static inline uint32_t clearFirstMarkedBit(uint32_t& value) {
+ uint32_t n = firstMarkedBit(value);
+ clearBit(value, n);
+ return n;
+ }
+
+ // Finds the first unmarked bit in the set and marks it. Returns the bit index.
+ // Result is undefined if all bits are marked.
+ inline uint32_t markFirstUnmarkedBit() { return markFirstUnmarkedBit(value); }
+
+ static inline uint32_t markFirstUnmarkedBit(uint32_t& value) {
+ uint32_t n = firstUnmarkedBit(value);
+ markBit(value, n);
+ return n;
+ }
+
+ // Finds the last marked bit in the set and clears it. Returns the bit index.
+ // Result is undefined if all bits are unmarked.
+ inline uint32_t clearLastMarkedBit() { return clearLastMarkedBit(value); }
+
+ static inline uint32_t clearLastMarkedBit(uint32_t& value) {
+ uint32_t n = lastMarkedBit(value);
+ clearBit(value, n);
+ return n;
+ }
+
+ // Gets the index of the specified bit in the set, which is the number of
+ // marked bits that appear before the specified bit.
+ inline uint32_t getIndexOfBit(uint32_t n) const {
+ return getIndexOfBit(value, n);
+ }
+
+ static inline uint32_t getIndexOfBit(uint32_t value, uint32_t n) {
+ return __builtin_popcountl(value & ~(0xffffffffUL >> n));
+ }
+
+ inline bool operator== (const BitSet32& other) const { return value == other.value; }
+ inline bool operator!= (const BitSet32& other) const { return value != other.value; }
+ inline BitSet32 operator& (const BitSet32& other) const {
+ return BitSet32(value & other.value);
+ }
+ inline BitSet32& operator&= (const BitSet32& other) {
+ value &= other.value;
+ return *this;
+ }
+ inline BitSet32 operator| (const BitSet32& other) const {
+ return BitSet32(value | other.value);
+ }
+ inline BitSet32& operator|= (const BitSet32& other) {
+ value |= other.value;
+ return *this;
+ }
+
+private:
+ // We use these helpers as the signature of __builtin_c{l,t}z has "unsigned int" for the
+ // input, which is only guaranteed to be 16b, not 32. The compiler should optimize this away.
+ static inline uint32_t clz_checked(uint32_t value) {
+ if (sizeof(unsigned int) == sizeof(uint32_t)) {
+ return __builtin_clz(value);
+ } else {
+ return __builtin_clzl(value);
+ }
+ }
+
+ static inline uint32_t ctz_checked(uint32_t value) {
+ if (sizeof(unsigned int) == sizeof(uint32_t)) {
+ return __builtin_ctz(value);
+ } else {
+ return __builtin_ctzl(value);
+ }
+ }
+};
+
+ANDROID_BASIC_TYPES_TRAITS(BitSet32)
+
+// A simple set of 64 bits that can be individually marked or cleared.
+struct BitSet64 {
+ uint64_t value;
+
+ inline BitSet64() : value(0ULL) { }
+ explicit inline BitSet64(uint64_t value) : value(value) { }
+
+ // Gets the value associated with a particular bit index.
+ static inline uint64_t valueForBit(uint32_t n) { return 0x8000000000000000ULL >> n; }
+
+ // Clears the bit set.
+ inline void clear() { clear(value); }
+
+ static inline void clear(uint64_t& value) { value = 0ULL; }
+
+ // Returns the number of marked bits in the set.
+ inline uint32_t count() const { return count(value); }
+
+ static inline uint32_t count(uint64_t value) { return __builtin_popcountll(value); }
+
+ // Returns true if the bit set does not contain any marked bits.
+ inline bool isEmpty() const { return isEmpty(value); }
+
+ static inline bool isEmpty(uint64_t value) { return ! value; }
+
+ // Returns true if the bit set does not contain any unmarked bits.
+ inline bool isFull() const { return isFull(value); }
+
+ static inline bool isFull(uint64_t value) { return value == 0xffffffffffffffffULL; }
+
+ // Returns true if the specified bit is marked.
+ inline bool hasBit(uint32_t n) const { return hasBit(value, n); }
+
+ static inline bool hasBit(uint64_t value, uint32_t n) { return value & valueForBit(n); }
+
+ // Marks the specified bit.
+ inline void markBit(uint32_t n) { markBit(value, n); }
+
+ static inline void markBit(uint64_t& value, uint32_t n) { value |= valueForBit(n); }
+
+ // Clears the specified bit.
+ inline void clearBit(uint32_t n) { clearBit(value, n); }
+
+ static inline void clearBit(uint64_t& value, uint32_t n) { value &= ~ valueForBit(n); }
+
+ // Finds the first marked bit in the set.
+ // Result is undefined if all bits are unmarked.
+ inline uint32_t firstMarkedBit() const { return firstMarkedBit(value); }
+
+ static inline uint32_t firstMarkedBit(uint64_t value) { return __builtin_clzll(value); }
+
+ // Finds the first unmarked bit in the set.
+ // Result is undefined if all bits are marked.
+ inline uint32_t firstUnmarkedBit() const { return firstUnmarkedBit(value); }
+
+ static inline uint32_t firstUnmarkedBit(uint64_t value) { return __builtin_clzll(~ value); }
+
+ // Finds the last marked bit in the set.
+ // Result is undefined if all bits are unmarked.
+ inline uint32_t lastMarkedBit() const { return lastMarkedBit(value); }
+
+ static inline uint32_t lastMarkedBit(uint64_t value) { return 63 - __builtin_ctzll(value); }
+
+ // Finds the first marked bit in the set and clears it. Returns the bit index.
+ // Result is undefined if all bits are unmarked.
+ inline uint32_t clearFirstMarkedBit() { return clearFirstMarkedBit(value); }
+
+ static inline uint32_t clearFirstMarkedBit(uint64_t& value) {
+ uint64_t n = firstMarkedBit(value);
+ clearBit(value, n);
+ return n;
+ }
+
+ // Finds the first unmarked bit in the set and marks it. Returns the bit index.
+ // Result is undefined if all bits are marked.
+ inline uint32_t markFirstUnmarkedBit() { return markFirstUnmarkedBit(value); }
+
+ static inline uint32_t markFirstUnmarkedBit(uint64_t& value) {
+ uint64_t n = firstUnmarkedBit(value);
+ markBit(value, n);
+ return n;
+ }
+
+ // Finds the last marked bit in the set and clears it. Returns the bit index.
+ // Result is undefined if all bits are unmarked.
+ inline uint32_t clearLastMarkedBit() { return clearLastMarkedBit(value); }
+
+ static inline uint32_t clearLastMarkedBit(uint64_t& value) {
+ uint64_t n = lastMarkedBit(value);
+ clearBit(value, n);
+ return n;
+ }
+
+ // Gets the index of the specified bit in the set, which is the number of
+ // marked bits that appear before the specified bit.
+ inline uint32_t getIndexOfBit(uint32_t n) const { return getIndexOfBit(value, n); }
+
+ static inline uint32_t getIndexOfBit(uint64_t value, uint32_t n) {
+ return __builtin_popcountll(value & ~(0xffffffffffffffffULL >> n));
+ }
+
+ inline bool operator== (const BitSet64& other) const { return value == other.value; }
+ inline bool operator!= (const BitSet64& other) const { return value != other.value; }
+ inline BitSet64 operator& (const BitSet64& other) const {
+ return BitSet64(value & other.value);
+ }
+ inline BitSet64& operator&= (const BitSet64& other) {
+ value &= other.value;
+ return *this;
+ }
+ inline BitSet64 operator| (const BitSet64& other) const {
+ return BitSet64(value | other.value);
+ }
+ inline BitSet64& operator|= (const BitSet64& other) {
+ value |= other.value;
+ return *this;
+ }
+};
+
+ANDROID_BASIC_TYPES_TRAITS(BitSet64)
+
+} // namespace android
+
+#endif // UTILS_BITSET_H
diff --git a/include/utils/BlobCache.h b/include/utils/BlobCache.h
new file mode 100644
index 0000000..7d621e4
--- /dev/null
+++ b/include/utils/BlobCache.h
@@ -0,0 +1,243 @@
+/*
+ ** Copyright 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.
+ */
+
+#ifndef ANDROID_BLOB_CACHE_H
+#define ANDROID_BLOB_CACHE_H
+
+#include <stddef.h>
+
+#include <utils/Flattenable.h>
+#include <utils/RefBase.h>
+#include <utils/SortedVector.h>
+#include <utils/threads.h>
+
+namespace android {
+
+// A BlobCache is an in-memory cache for binary key/value pairs. A BlobCache
+// does NOT provide any thread-safety guarantees.
+//
+// The cache contents can be serialized to an in-memory buffer or mmap'd file
+// and then reloaded in a subsequent execution of the program. This
+// serialization is non-portable and the data should only be used by the device
+// that generated it.
+class BlobCache : public RefBase {
+
+public:
+
+ // Create an empty blob cache. The blob cache will cache key/value pairs
+ // with key and value sizes less than or equal to maxKeySize and
+ // maxValueSize, respectively. The total combined size of ALL cache entries
+ // (key sizes plus value sizes) will not exceed maxTotalSize.
+ BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize);
+
+ // set inserts a new binary value into the cache and associates it with the
+ // given binary key. If the key or value are too large for the cache then
+ // the cache remains unchanged. This includes the case where a different
+ // value was previously associated with the given key - the old value will
+ // remain in the cache. If the given key and value are small enough to be
+ // put in the cache (based on the maxKeySize, maxValueSize, and maxTotalSize
+ // values specified to the BlobCache constructor), then the key/value pair
+ // will be in the cache after set returns. Note, however, that a subsequent
+ // call to set may evict old key/value pairs from the cache.
+ //
+ // Preconditions:
+ // key != NULL
+ // 0 < keySize
+ // value != NULL
+ // 0 < valueSize
+ void set(const void* key, size_t keySize, const void* value,
+ size_t valueSize);
+
+ // get retrieves from the cache the binary value associated with a given
+ // binary key. If the key is present in the cache then the length of the
+ // binary value associated with that key is returned. If the value argument
+ // is non-NULL and the size of the cached value is less than valueSize bytes
+ // then the cached value is copied into the buffer pointed to by the value
+ // argument. If the key is not present in the cache then 0 is returned and
+ // the buffer pointed to by the value argument is not modified.
+ //
+ // Note that when calling get multiple times with the same key, the later
+ // calls may fail, returning 0, even if earlier calls succeeded. The return
+ // value must be checked for each call.
+ //
+ // Preconditions:
+ // key != NULL
+ // 0 < keySize
+ // 0 <= valueSize
+ size_t get(const void* key, size_t keySize, void* value, size_t valueSize);
+
+
+ // getFlattenedSize returns the number of bytes needed to store the entire
+ // serialized cache.
+ size_t getFlattenedSize() const;
+
+ // flatten serializes the current contents of the cache into the memory
+ // pointed to by 'buffer'. The serialized cache contents can later be
+ // loaded into a BlobCache object using the unflatten method. The contents
+ // of the BlobCache object will not be modified.
+ //
+ // Preconditions:
+ // size >= this.getFlattenedSize()
+ status_t flatten(void* buffer, size_t size) const;
+
+ // unflatten replaces the contents of the cache with the serialized cache
+ // contents in the memory pointed to by 'buffer'. The previous contents of
+ // the BlobCache will be evicted from the cache. If an error occurs while
+ // unflattening the serialized cache contents then the BlobCache will be
+ // left in an empty state.
+ //
+ status_t unflatten(void const* buffer, size_t size);
+
+private:
+ // Copying is disallowed.
+ BlobCache(const BlobCache&);
+ void operator=(const BlobCache&);
+
+ // A random function helper to get around MinGW not having nrand48()
+ long int blob_random();
+
+ // clean evicts a randomly chosen set of entries from the cache such that
+ // the total size of all remaining entries is less than mMaxTotalSize/2.
+ void clean();
+
+ // isCleanable returns true if the cache is full enough for the clean method
+ // to have some effect, and false otherwise.
+ bool isCleanable() const;
+
+ // A Blob is an immutable sized unstructured data blob.
+ class Blob : public RefBase {
+ public:
+ Blob(const void* data, size_t size, bool copyData);
+ ~Blob();
+
+ bool operator<(const Blob& rhs) const;
+
+ const void* getData() const;
+ size_t getSize() const;
+
+ private:
+ // Copying is not allowed.
+ Blob(const Blob&);
+ void operator=(const Blob&);
+
+ // mData points to the buffer containing the blob data.
+ const void* mData;
+
+ // mSize is the size of the blob data in bytes.
+ size_t mSize;
+
+ // mOwnsData indicates whether or not this Blob object should free the
+ // memory pointed to by mData when the Blob gets destructed.
+ bool mOwnsData;
+ };
+
+ // A CacheEntry is a single key/value pair in the cache.
+ class CacheEntry {
+ public:
+ CacheEntry();
+ CacheEntry(const sp<Blob>& key, const sp<Blob>& value);
+ CacheEntry(const CacheEntry& ce);
+
+ bool operator<(const CacheEntry& rhs) const;
+ const CacheEntry& operator=(const CacheEntry&);
+
+ sp<Blob> getKey() const;
+ sp<Blob> getValue() const;
+
+ void setValue(const sp<Blob>& value);
+
+ private:
+
+ // mKey is the key that identifies the cache entry.
+ sp<Blob> mKey;
+
+ // mValue is the cached data associated with the key.
+ sp<Blob> mValue;
+ };
+
+ // A Header is the header for the entire BlobCache serialization format. No
+ // need to make this portable, so we simply write the struct out.
+ struct Header {
+ // mMagicNumber is the magic number that identifies the data as
+ // serialized BlobCache contents. It must always contain 'Blb$'.
+ uint32_t mMagicNumber;
+
+ // mBlobCacheVersion is the serialization format version.
+ uint32_t mBlobCacheVersion;
+
+ // mDeviceVersion is the device-specific version of the cache. This can
+ // be used to invalidate the cache.
+ uint32_t mDeviceVersion;
+
+ // mNumEntries is number of cache entries following the header in the
+ // data.
+ size_t mNumEntries;
+ };
+
+ // An EntryHeader is the header for a serialized cache entry. No need to
+ // make this portable, so we simply write the struct out. Each EntryHeader
+ // is followed imediately by the key data and then the value data.
+ //
+ // The beginning of each serialized EntryHeader is 4-byte aligned, so the
+ // number of bytes that a serialized cache entry will occupy is:
+ //
+ // ((sizeof(EntryHeader) + keySize + valueSize) + 3) & ~3
+ //
+ struct EntryHeader {
+ // mKeySize is the size of the entry key in bytes.
+ size_t mKeySize;
+
+ // mValueSize is the size of the entry value in bytes.
+ size_t mValueSize;
+
+ // mData contains both the key and value data for the cache entry. The
+ // key comes first followed immediately by the value.
+ uint8_t mData[];
+ };
+
+ // mMaxKeySize is the maximum key size that will be cached. Calls to
+ // BlobCache::set with a keySize parameter larger than mMaxKeySize will
+ // simply not add the key/value pair to the cache.
+ const size_t mMaxKeySize;
+
+ // mMaxValueSize is the maximum value size that will be cached. Calls to
+ // BlobCache::set with a valueSize parameter larger than mMaxValueSize will
+ // simply not add the key/value pair to the cache.
+ const size_t mMaxValueSize;
+
+ // mMaxTotalSize is the maximum size that all cache entries can occupy. This
+ // includes space for both keys and values. When a call to BlobCache::set
+ // would otherwise cause this limit to be exceeded, either the key/value
+ // pair passed to BlobCache::set will not be cached or other cache entries
+ // will be evicted from the cache to make room for the new entry.
+ const size_t mMaxTotalSize;
+
+ // mTotalSize is the total combined size of all keys and values currently in
+ // the cache.
+ size_t mTotalSize;
+
+ // mRandState is the pseudo-random number generator state. It is passed to
+ // nrand48 to generate random numbers when needed.
+ unsigned short mRandState[3];
+
+ // mCacheEntries stores all the cache entries that are resident in memory.
+ // Cache entries are added to it by the 'set' method.
+ SortedVector<CacheEntry> mCacheEntries;
+};
+
+}
+
+#endif // ANDROID_BLOB_CACHE_H
diff --git a/include/utils/ByteOrder.h b/include/utils/ByteOrder.h
new file mode 100644
index 0000000..baa3a83
--- /dev/null
+++ b/include/utils/ByteOrder.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+
+#ifndef _LIBS_UTILS_BYTE_ORDER_H
+#define _LIBS_UTILS_BYTE_ORDER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#ifdef HAVE_WINSOCK
+#include <winsock2.h>
+#else
+#include <netinet/in.h>
+#endif
+
+/*
+ * These macros are like the hton/ntoh byte swapping macros,
+ * except they allow you to swap to and from the "device" byte
+ * order. The device byte order is the endianness of the target
+ * device -- for the ARM CPUs we use today, this is little endian.
+ *
+ * Note that the byte swapping functions have not been optimized
+ * much; performance is currently not an issue for them since the
+ * intent is to allow us to avoid byte swapping on the device.
+ */
+
+static inline uint32_t android_swap_long(uint32_t v)
+{
+ return (v<<24) | ((v<<8)&0x00FF0000) | ((v>>8)&0x0000FF00) | (v>>24);
+}
+
+static inline uint16_t android_swap_short(uint16_t v)
+{
+ return (v<<8) | (v>>8);
+}
+
+#define DEVICE_BYTE_ORDER LITTLE_ENDIAN
+
+#if BYTE_ORDER == DEVICE_BYTE_ORDER
+
+#define dtohl(x) (x)
+#define dtohs(x) (x)
+#define htodl(x) (x)
+#define htods(x) (x)
+
+#else
+
+#define dtohl(x) (android_swap_long(x))
+#define dtohs(x) (android_swap_short(x))
+#define htodl(x) (android_swap_long(x))
+#define htods(x) (android_swap_short(x))
+
+#endif
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define fromlel(x) (x)
+#define fromles(x) (x)
+#define tolel(x) (x)
+#define toles(x) (x)
+#else
+#define fromlel(x) (android_swap_long(x))
+#define fromles(x) (android_swap_short(x))
+#define tolel(x) (android_swap_long(x))
+#define toles(x) (android_swap_short(x))
+#endif
+
+#endif // _LIBS_UTILS_BYTE_ORDER_H
diff --git a/include/utils/CallStack.h b/include/utils/CallStack.h
new file mode 100644
index 0000000..27e89f4
--- /dev/null
+++ b/include/utils/CallStack.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_CALLSTACK_H
+#define ANDROID_CALLSTACK_H
+
+#include <android/log.h>
+#include <backtrace/backtrace_constants.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+
+namespace android {
+
+class Printer;
+
+// Collect/print the call stack (function, file, line) traces for a single thread.
+class CallStack {
+public:
+ // Create an empty call stack. No-op.
+ CallStack();
+ // Create a callstack with the current thread's stack trace.
+ // Immediately dump it to logcat using the given logtag.
+ CallStack(const char* logtag, int32_t ignoreDepth=1);
+ ~CallStack();
+
+ // Reset the stack frames (same as creating an empty call stack).
+ void clear() { mFrameLines.clear(); }
+
+ // Immediately collect the stack traces for the specified thread.
+ // The default is to dump the stack of the current call.
+ void update(int32_t ignoreDepth=1, pid_t tid=BACKTRACE_CURRENT_THREAD);
+
+ // Dump a stack trace to the log using the supplied logtag.
+ void log(const char* logtag,
+ android_LogPriority priority = ANDROID_LOG_DEBUG,
+ const char* prefix = 0) const;
+
+ // Dump a stack trace to the specified file descriptor.
+ void dump(int fd, int indent = 0, const char* prefix = 0) const;
+
+ // Return a string (possibly very long) containing the complete stack trace.
+ String8 toString(const char* prefix = 0) const;
+
+ // Dump a serialized representation of the stack trace to the specified printer.
+ void print(Printer& printer) const;
+
+ // Get the count of stack frames that are in this call stack.
+ size_t size() const { return mFrameLines.size(); }
+
+private:
+ Vector<String8> mFrameLines;
+};
+
+}; // namespace android
+
+#endif // ANDROID_CALLSTACK_H
diff --git a/include/utils/Compat.h b/include/utils/Compat.h
new file mode 100644
index 0000000..7d96310
--- /dev/null
+++ b/include/utils/Compat.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LIB_UTILS_COMPAT_H
+#define __LIB_UTILS_COMPAT_H
+
+#include <unistd.h>
+
+#if defined(__APPLE__)
+
+/* Mac OS has always had a 64-bit off_t, so it doesn't have off64_t. */
+
+typedef off_t off64_t;
+
+static inline off64_t lseek64(int fd, off64_t offset, int whence) {
+ return lseek(fd, offset, whence);
+}
+
+static inline ssize_t pread64(int fd, void* buf, size_t nbytes, off64_t offset) {
+ return pread(fd, buf, nbytes, offset);
+}
+
+#endif /* __APPLE__ */
+
+#if defined(_WIN32)
+#define O_CLOEXEC O_NOINHERIT
+#define O_NOFOLLOW 0
+#define DEFFILEMODE 0666
+#endif /* _WIN32 */
+
+#if defined(_WIN32)
+#define ZD "%ld"
+#define ZD_TYPE long
+#else
+#define ZD "%zd"
+#define ZD_TYPE ssize_t
+#endif
+
+/*
+ * Needed for cases where something should be constexpr if possible, but not
+ * being constexpr is fine if in pre-C++11 code (such as a const static float
+ * member variable).
+ */
+#if __cplusplus >= 201103L
+#define CONSTEXPR constexpr
+#else
+#define CONSTEXPR
+#endif
+
+/*
+ * TEMP_FAILURE_RETRY is defined by some, but not all, versions of
+ * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's
+ * not already defined, then define it here.
+ */
+#ifndef TEMP_FAILURE_RETRY
+/* Used to retry syscalls that can return EINTR. */
+#define TEMP_FAILURE_RETRY(exp) ({ \
+ typeof (exp) _rc; \
+ do { \
+ _rc = (exp); \
+ } while (_rc == -1 && errno == EINTR); \
+ _rc; })
+#endif
+
+#endif /* __LIB_UTILS_COMPAT_H */
diff --git a/include/utils/Condition.h b/include/utils/Condition.h
new file mode 100644
index 0000000..5a72519
--- /dev/null
+++ b/include/utils/Condition.h
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBS_UTILS_CONDITION_H
+#define _LIBS_UTILS_CONDITION_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <time.h>
+
+#if !defined(_WIN32)
+# include <pthread.h>
+#endif
+
+#include <utils/Errors.h>
+#include <utils/Mutex.h>
+#include <utils/Timers.h>
+
+// ---------------------------------------------------------------------------
+namespace android {
+// ---------------------------------------------------------------------------
+
+/*
+ * Condition variable class. The implementation is system-dependent.
+ *
+ * Condition variables are paired up with mutexes. Lock the mutex,
+ * call wait(), then either re-wait() if things aren't quite what you want,
+ * or unlock the mutex and continue. All threads calling wait() must
+ * use the same mutex for a given Condition.
+ */
+class Condition {
+public:
+ enum {
+ PRIVATE = 0,
+ SHARED = 1
+ };
+
+ enum WakeUpType {
+ WAKE_UP_ONE = 0,
+ WAKE_UP_ALL = 1
+ };
+
+ Condition();
+ Condition(int type);
+ ~Condition();
+ // Wait on the condition variable. Lock the mutex before calling.
+ status_t wait(Mutex& mutex);
+ // same with relative timeout
+ status_t waitRelative(Mutex& mutex, nsecs_t reltime);
+ // Signal the condition variable, allowing exactly one thread to continue.
+ void signal();
+ // Signal the condition variable, allowing one or all threads to continue.
+ void signal(WakeUpType type) {
+ if (type == WAKE_UP_ONE) {
+ signal();
+ } else {
+ broadcast();
+ }
+ }
+ // Signal the condition variable, allowing all threads to continue.
+ void broadcast();
+
+private:
+#if !defined(_WIN32)
+ pthread_cond_t mCond;
+#else
+ void* mState;
+#endif
+};
+
+// ---------------------------------------------------------------------------
+
+#if !defined(_WIN32)
+
+inline Condition::Condition() {
+ pthread_cond_init(&mCond, NULL);
+}
+inline Condition::Condition(int type) {
+ if (type == SHARED) {
+ pthread_condattr_t attr;
+ pthread_condattr_init(&attr);
+ pthread_condattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
+ pthread_cond_init(&mCond, &attr);
+ pthread_condattr_destroy(&attr);
+ } else {
+ pthread_cond_init(&mCond, NULL);
+ }
+}
+inline Condition::~Condition() {
+ pthread_cond_destroy(&mCond);
+}
+inline status_t Condition::wait(Mutex& mutex) {
+ return -pthread_cond_wait(&mCond, &mutex.mMutex);
+}
+inline status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) {
+#if defined(HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE)
+ struct timespec ts;
+ ts.tv_sec = reltime/1000000000;
+ ts.tv_nsec = reltime%1000000000;
+ return -pthread_cond_timedwait_relative_np(&mCond, &mutex.mMutex, &ts);
+#else // HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE
+ struct timespec ts;
+#if defined(__linux__)
+ clock_gettime(CLOCK_REALTIME, &ts);
+#else // __APPLE__
+ // we don't support the clocks here.
+ struct timeval t;
+ gettimeofday(&t, NULL);
+ ts.tv_sec = t.tv_sec;
+ ts.tv_nsec= t.tv_usec*1000;
+#endif
+ ts.tv_sec += reltime/1000000000;
+ ts.tv_nsec+= reltime%1000000000;
+ if (ts.tv_nsec >= 1000000000) {
+ ts.tv_nsec -= 1000000000;
+ ts.tv_sec += 1;
+ }
+ return -pthread_cond_timedwait(&mCond, &mutex.mMutex, &ts);
+#endif // HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE
+}
+inline void Condition::signal() {
+ /*
+ * POSIX says pthread_cond_signal wakes up "one or more" waiting threads.
+ * However bionic follows the glibc guarantee which wakes up "exactly one"
+ * waiting thread.
+ *
+ * man 3 pthread_cond_signal
+ * pthread_cond_signal restarts one of the threads that are waiting on
+ * the condition variable cond. If no threads are waiting on cond,
+ * nothing happens. If several threads are waiting on cond, exactly one
+ * is restarted, but it is not specified which.
+ */
+ pthread_cond_signal(&mCond);
+}
+inline void Condition::broadcast() {
+ pthread_cond_broadcast(&mCond);
+}
+
+#endif // !defined(_WIN32)
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+// ---------------------------------------------------------------------------
+
+#endif // _LIBS_UTILS_CONDITON_H
diff --git a/include/utils/Debug.h b/include/utils/Debug.h
new file mode 100644
index 0000000..08893bd
--- /dev/null
+++ b/include/utils/Debug.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_UTILS_DEBUG_H
+#define ANDROID_UTILS_DEBUG_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+#ifdef __cplusplus
+template<bool> struct CompileTimeAssert;
+template<> struct CompileTimeAssert<true> {};
+#define COMPILE_TIME_ASSERT(_exp) \
+ template class CompileTimeAssert< (_exp) >;
+#endif
+#define COMPILE_TIME_ASSERT_FUNCTION_SCOPE(_exp) \
+ CompileTimeAssert<( _exp )>();
+
+// ---------------------------------------------------------------------------
+
+#ifdef __cplusplus
+template<bool C, typename LSH, typename RHS> struct CompileTimeIfElse;
+template<typename LHS, typename RHS>
+struct CompileTimeIfElse<true, LHS, RHS> { typedef LHS TYPE; };
+template<typename LHS, typename RHS>
+struct CompileTimeIfElse<false, LHS, RHS> { typedef RHS TYPE; };
+#endif
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_UTILS_DEBUG_H
diff --git a/include/utils/Endian.h b/include/utils/Endian.h
new file mode 100644
index 0000000..591cae0
--- /dev/null
+++ b/include/utils/Endian.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2005 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.
+ */
+
+//
+// Android endian-ness defines.
+//
+#ifndef _LIBS_UTILS_ENDIAN_H
+#define _LIBS_UTILS_ENDIAN_H
+
+#if defined(__APPLE__) || defined(_WIN32)
+
+#define __BIG_ENDIAN 0x1000
+#define __LITTLE_ENDIAN 0x0001
+#define __BYTE_ORDER __LITTLE_ENDIAN
+
+#else
+
+#include <endian.h>
+
+#endif
+
+#endif /*_LIBS_UTILS_ENDIAN_H*/
diff --git a/include/utils/Errors.h b/include/utils/Errors.h
new file mode 100644
index 0000000..46173db
--- /dev/null
+++ b/include/utils/Errors.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_ERRORS_H
+#define ANDROID_ERRORS_H
+
+#include <sys/types.h>
+#include <errno.h>
+
+namespace android {
+
+// use this type to return error codes
+#ifdef HAVE_MS_C_RUNTIME
+typedef int status_t;
+#else
+typedef int32_t status_t;
+#endif
+
+/* the MS C runtime lacks a few error codes */
+
+/*
+ * Error codes.
+ * All error codes are negative values.
+ */
+
+// Win32 #defines NO_ERROR as well. It has the same value, so there's no
+// real conflict, though it's a bit awkward.
+#ifdef _WIN32
+# undef NO_ERROR
+#endif
+
+enum {
+ OK = 0, // Everything's swell.
+ NO_ERROR = 0, // No errors.
+
+ UNKNOWN_ERROR = (-2147483647-1), // INT32_MIN value
+
+ NO_MEMORY = -ENOMEM,
+ INVALID_OPERATION = -ENOSYS,
+ BAD_VALUE = -EINVAL,
+ BAD_TYPE = (UNKNOWN_ERROR + 1),
+ NAME_NOT_FOUND = -ENOENT,
+ PERMISSION_DENIED = -EPERM,
+ NO_INIT = -ENODEV,
+ ALREADY_EXISTS = -EEXIST,
+ DEAD_OBJECT = -EPIPE,
+ FAILED_TRANSACTION = (UNKNOWN_ERROR + 2),
+ JPARKS_BROKE_IT = -EPIPE,
+#if !defined(HAVE_MS_C_RUNTIME)
+ BAD_INDEX = -EOVERFLOW,
+ NOT_ENOUGH_DATA = -ENODATA,
+ WOULD_BLOCK = -EWOULDBLOCK,
+ TIMED_OUT = -ETIMEDOUT,
+ UNKNOWN_TRANSACTION = -EBADMSG,
+#else
+ BAD_INDEX = -E2BIG,
+ NOT_ENOUGH_DATA = (UNKNOWN_ERROR + 3),
+ WOULD_BLOCK = (UNKNOWN_ERROR + 4),
+ TIMED_OUT = (UNKNOWN_ERROR + 5),
+ UNKNOWN_TRANSACTION = (UNKNOWN_ERROR + 6),
+#endif
+ FDS_NOT_ALLOWED = (UNKNOWN_ERROR + 7),
+};
+
+// Restore define; enumeration is in "android" namespace, so the value defined
+// there won't work for Win32 code in a different namespace.
+#ifdef _WIN32
+# define NO_ERROR 0L
+#endif
+
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_ERRORS_H
diff --git a/include/utils/FileMap.h b/include/utils/FileMap.h
new file mode 100644
index 0000000..f70fc92
--- /dev/null
+++ b/include/utils/FileMap.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2006 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.
+ */
+
+//
+// Encapsulate a shared file mapping.
+//
+#ifndef __LIBS_FILE_MAP_H
+#define __LIBS_FILE_MAP_H
+
+#include <sys/types.h>
+
+#include <utils/Compat.h>
+
+#if defined(__MINGW32__)
+// Ensure that we always pull in winsock2.h before windows.h
+#ifdef HAVE_WINSOCK
+#include <winsock2.h>
+#endif
+#include <windows.h>
+#endif
+
+namespace android {
+
+/*
+ * This represents a memory-mapped file. It might be the entire file or
+ * only part of it. This requires a little bookkeeping because the mapping
+ * needs to be aligned on page boundaries, and in some cases we'd like to
+ * have multiple references to the mapped area without creating additional
+ * maps.
+ *
+ * This always uses MAP_SHARED.
+ *
+ * TODO: we should be able to create a new FileMap that is a subset of
+ * an existing FileMap and shares the underlying mapped pages. Requires
+ * completing the refcounting stuff and possibly introducing the notion
+ * of a FileMap hierarchy.
+ */
+class FileMap {
+public:
+ FileMap(void);
+
+ /*
+ * Create a new mapping on an open file.
+ *
+ * Closing the file descriptor does not unmap the pages, so we don't
+ * claim ownership of the fd.
+ *
+ * Returns "false" on failure.
+ */
+ bool create(const char* origFileName, int fd,
+ off64_t offset, size_t length, bool readOnly);
+
+ ~FileMap(void);
+
+ /*
+ * Return the name of the file this map came from, if known.
+ */
+ const char* getFileName(void) const { return mFileName; }
+
+ /*
+ * Get a pointer to the piece of the file we requested.
+ */
+ void* getDataPtr(void) const { return mDataPtr; }
+
+ /*
+ * Get the length we requested.
+ */
+ size_t getDataLength(void) const { return mDataLength; }
+
+ /*
+ * Get the data offset used to create this map.
+ */
+ off64_t getDataOffset(void) const { return mDataOffset; }
+
+ /*
+ * This maps directly to madvise() values, but allows us to avoid
+ * including <sys/mman.h> everywhere.
+ */
+ enum MapAdvice {
+ NORMAL, RANDOM, SEQUENTIAL, WILLNEED, DONTNEED
+ };
+
+ /*
+ * Apply an madvise() call to the entire file.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+ int advise(MapAdvice advice);
+
+protected:
+
+private:
+ // these are not implemented
+ FileMap(const FileMap& src);
+ const FileMap& operator=(const FileMap& src);
+
+ char* mFileName; // original file name, if known
+ void* mBasePtr; // base of mmap area; page aligned
+ size_t mBaseLength; // length, measured from "mBasePtr"
+ off64_t mDataOffset; // offset used when map was created
+ void* mDataPtr; // start of requested data, offset from base
+ size_t mDataLength; // length, measured from "mDataPtr"
+#if defined(__MINGW32__)
+ HANDLE mFileHandle; // Win32 file handle
+ HANDLE mFileMapping; // Win32 file mapping handle
+#endif
+
+ static long mPageSize;
+};
+
+}; // namespace android
+
+#endif // __LIBS_FILE_MAP_H
diff --git a/include/utils/Flattenable.h b/include/utils/Flattenable.h
new file mode 100644
index 0000000..882a8b2
--- /dev/null
+++ b/include/utils/Flattenable.h
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_UTILS_FLATTENABLE_H
+#define ANDROID_UTILS_FLATTENABLE_H
+
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <utils/Errors.h>
+#include <utils/Debug.h>
+
+namespace android {
+
+
+class FlattenableUtils {
+public:
+ template<int N>
+ static size_t align(size_t size) {
+ COMPILE_TIME_ASSERT_FUNCTION_SCOPE( !(N & (N-1)) );
+ return (size + (N-1)) & ~(N-1);
+ }
+
+ template<int N>
+ static size_t align(void const*& buffer) {
+ COMPILE_TIME_ASSERT_FUNCTION_SCOPE( !(N & (N-1)) );
+ intptr_t b = intptr_t(buffer);
+ buffer = (void*)((intptr_t(buffer) + (N-1)) & ~(N-1));
+ return size_t(intptr_t(buffer) - b);
+ }
+
+ template<int N>
+ static size_t align(void*& buffer) {
+ return align<N>( const_cast<void const*&>(buffer) );
+ }
+
+ static void advance(void*& buffer, size_t& size, size_t offset) {
+ buffer = reinterpret_cast<void*>( intptr_t(buffer) + offset );
+ size -= offset;
+ }
+
+ static void advance(void const*& buffer, size_t& size, size_t offset) {
+ buffer = reinterpret_cast<void const*>( intptr_t(buffer) + offset );
+ size -= offset;
+ }
+
+ // write a POD structure
+ template<typename T>
+ static void write(void*& buffer, size_t& size, const T& value) {
+ *static_cast<T*>(buffer) = value;
+ advance(buffer, size, sizeof(T));
+ }
+
+ // read a POD structure
+ template<typename T>
+ static void read(void const*& buffer, size_t& size, T& value) {
+ value = *static_cast<T const*>(buffer);
+ advance(buffer, size, sizeof(T));
+ }
+};
+
+
+/*
+ * The Flattenable protocol allows an object to serialize itself out
+ * to a byte-buffer and an array of file descriptors.
+ * Flattenable objects must implement this protocol.
+ */
+
+template <typename T>
+class Flattenable {
+public:
+ // size in bytes of the flattened object
+ inline size_t getFlattenedSize() const;
+
+ // number of file descriptors to flatten
+ inline size_t getFdCount() const;
+
+ // flattens the object into buffer.
+ // size should be at least of getFlattenedSize()
+ // file descriptors are written in the fds[] array but ownership is
+ // not transfered (ie: they must be dupped by the caller of
+ // flatten() if needed).
+ inline status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;
+
+ // unflattens the object from buffer.
+ // size should be equal to the value of getFlattenedSize() when the
+ // object was flattened.
+ // unflattened file descriptors are found in the fds[] array and
+ // don't need to be dupped(). ie: the caller of unflatten doesn't
+ // keep ownership. If a fd is not retained by unflatten() it must be
+ // explicitly closed.
+ inline status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count);
+};
+
+template<typename T>
+inline size_t Flattenable<T>::getFlattenedSize() const {
+ return static_cast<T const*>(this)->T::getFlattenedSize();
+}
+template<typename T>
+inline size_t Flattenable<T>::getFdCount() const {
+ return static_cast<T const*>(this)->T::getFdCount();
+}
+template<typename T>
+inline status_t Flattenable<T>::flatten(
+ void*& buffer, size_t& size, int*& fds, size_t& count) const {
+ return static_cast<T const*>(this)->T::flatten(buffer, size, fds, count);
+}
+template<typename T>
+inline status_t Flattenable<T>::unflatten(
+ void const*& buffer, size_t& size, int const*& fds, size_t& count) {
+ return static_cast<T*>(this)->T::unflatten(buffer, size, fds, count);
+}
+
+/*
+ * LightFlattenable is a protocol allowing object to serialize themselves out
+ * to a byte-buffer. Because it doesn't handle file-descriptors,
+ * LightFlattenable is usually more size efficient than Flattenable.
+ * LightFlattenable objects must implement this protocol.
+ */
+template <typename T>
+class LightFlattenable {
+public:
+ // returns whether this object always flatten into the same size.
+ // for efficiency, this should always be inline.
+ inline bool isFixedSize() const;
+
+ // returns size in bytes of the flattened object. must be a constant.
+ inline size_t getFlattenedSize() const;
+
+ // flattens the object into buffer.
+ inline status_t flatten(void* buffer, size_t size) const;
+
+ // unflattens the object from buffer of given size.
+ inline status_t unflatten(void const* buffer, size_t size);
+};
+
+template <typename T>
+inline bool LightFlattenable<T>::isFixedSize() const {
+ return static_cast<T const*>(this)->T::isFixedSize();
+}
+template <typename T>
+inline size_t LightFlattenable<T>::getFlattenedSize() const {
+ return static_cast<T const*>(this)->T::getFlattenedSize();
+}
+template <typename T>
+inline status_t LightFlattenable<T>::flatten(void* buffer, size_t size) const {
+ return static_cast<T const*>(this)->T::flatten(buffer, size);
+}
+template <typename T>
+inline status_t LightFlattenable<T>::unflatten(void const* buffer, size_t size) {
+ return static_cast<T*>(this)->T::unflatten(buffer, size);
+}
+
+/*
+ * LightFlattenablePod is an implementation of the LightFlattenable protocol
+ * for POD (plain-old-data) objects.
+ * Simply derive from LightFlattenablePod<Foo> to make Foo flattenable; no
+ * need to implement any methods; obviously Foo must be a POD structure.
+ */
+template <typename T>
+class LightFlattenablePod : public LightFlattenable<T> {
+public:
+ inline bool isFixedSize() const {
+ return true;
+ }
+
+ inline size_t getFlattenedSize() const {
+ return sizeof(T);
+ }
+ inline status_t flatten(void* buffer, size_t size) const {
+ if (size < sizeof(T)) return NO_MEMORY;
+ *reinterpret_cast<T*>(buffer) = *static_cast<T const*>(this);
+ return NO_ERROR;
+ }
+ inline status_t unflatten(void const* buffer, size_t) {
+ *static_cast<T*>(this) = *reinterpret_cast<T const*>(buffer);
+ return NO_ERROR;
+ }
+};
+
+
+}; // namespace android
+
+
+#endif /* ANDROID_UTILS_FLATTENABLE_H */
diff --git a/include/utils/Functor.h b/include/utils/Functor.h
new file mode 100644
index 0000000..09ea614
--- /dev/null
+++ b/include/utils/Functor.h
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_FUNCTOR_H
+#define ANDROID_FUNCTOR_H
+
+#include <utils/Errors.h>
+
+namespace android {
+
+class Functor {
+public:
+ Functor() {}
+ virtual ~Functor() {}
+ virtual status_t operator ()(int /*what*/, void* /*data*/) { return NO_ERROR; }
+};
+
+}; // namespace android
+
+#endif // ANDROID_FUNCTOR_H
diff --git a/include/utils/JenkinsHash.h b/include/utils/JenkinsHash.h
new file mode 100644
index 0000000..7da5dbd
--- /dev/null
+++ b/include/utils/JenkinsHash.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Implementation of Jenkins one-at-a-time hash function. These choices are
+ * optimized for code size and portability, rather than raw speed. But speed
+ * should still be quite good.
+ **/
+
+#ifndef ANDROID_JENKINS_HASH_H
+#define ANDROID_JENKINS_HASH_H
+
+#include <utils/TypeHelpers.h>
+
+namespace android {
+
+/* The Jenkins hash of a sequence of 32 bit words A, B, C is:
+ * Whiten(Mix(Mix(Mix(0, A), B), C)) */
+
+inline uint32_t JenkinsHashMix(uint32_t hash, uint32_t data) {
+ hash += data;
+ hash += (hash << 10);
+ hash ^= (hash >> 6);
+ return hash;
+}
+
+hash_t JenkinsHashWhiten(uint32_t hash);
+
+/* Helpful utility functions for hashing data in 32 bit chunks */
+uint32_t JenkinsHashMixBytes(uint32_t hash, const uint8_t* bytes, size_t size);
+
+uint32_t JenkinsHashMixShorts(uint32_t hash, const uint16_t* shorts, size_t size);
+
+}
+
+#endif // ANDROID_JENKINS_HASH_H
diff --git a/include/utils/KeyedVector.h b/include/utils/KeyedVector.h
new file mode 100644
index 0000000..c4faae0
--- /dev/null
+++ b/include/utils/KeyedVector.h
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_KEYED_VECTOR_H
+#define ANDROID_KEYED_VECTOR_H
+
+#include <assert.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <cutils/log.h>
+
+#include <utils/SortedVector.h>
+#include <utils/TypeHelpers.h>
+#include <utils/Errors.h>
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+template <typename KEY, typename VALUE>
+class KeyedVector
+{
+public:
+ typedef KEY key_type;
+ typedef VALUE value_type;
+
+ inline KeyedVector();
+
+ /*
+ * empty the vector
+ */
+
+ inline void clear() { mVector.clear(); }
+
+ /*!
+ * vector stats
+ */
+
+ //! returns number of items in the vector
+ inline size_t size() const { return mVector.size(); }
+ //! returns whether or not the vector is empty
+ inline bool isEmpty() const { return mVector.isEmpty(); }
+ //! returns how many items can be stored without reallocating the backing store
+ inline size_t capacity() const { return mVector.capacity(); }
+ //! sets the capacity. capacity can never be reduced less than size()
+ inline ssize_t setCapacity(size_t size) { return mVector.setCapacity(size); }
+
+ // returns true if the arguments is known to be identical to this vector
+ inline bool isIdenticalTo(const KeyedVector& rhs) const;
+
+ /*!
+ * accessors
+ */
+ const VALUE& valueFor(const KEY& key) const;
+ const VALUE& valueAt(size_t index) const;
+ const KEY& keyAt(size_t index) const;
+ ssize_t indexOfKey(const KEY& key) const;
+ const VALUE& operator[] (size_t index) const;
+
+ /*!
+ * modifying the array
+ */
+
+ VALUE& editValueFor(const KEY& key);
+ VALUE& editValueAt(size_t index);
+
+ /*!
+ * add/insert/replace items
+ */
+
+ ssize_t add(const KEY& key, const VALUE& item);
+ ssize_t replaceValueFor(const KEY& key, const VALUE& item);
+ ssize_t replaceValueAt(size_t index, const VALUE& item);
+
+ /*!
+ * remove items
+ */
+
+ ssize_t removeItem(const KEY& key);
+ ssize_t removeItemsAt(size_t index, size_t count = 1);
+
+private:
+ SortedVector< key_value_pair_t<KEY, VALUE> > mVector;
+};
+
+// KeyedVector<KEY, VALUE> can be trivially moved using memcpy() because its
+// underlying SortedVector can be trivially moved.
+template<typename KEY, typename VALUE> struct trait_trivial_move<KeyedVector<KEY, VALUE> > {
+ enum { value = trait_trivial_move<SortedVector< key_value_pair_t<KEY, VALUE> > >::value };
+};
+
+
+// ---------------------------------------------------------------------------
+
+/**
+ * Variation of KeyedVector that holds a default value to return when
+ * valueFor() is called with a key that doesn't exist.
+ */
+template <typename KEY, typename VALUE>
+class DefaultKeyedVector : public KeyedVector<KEY, VALUE>
+{
+public:
+ inline DefaultKeyedVector(const VALUE& defValue = VALUE());
+ const VALUE& valueFor(const KEY& key) const;
+
+private:
+ VALUE mDefault;
+};
+
+// ---------------------------------------------------------------------------
+
+template<typename KEY, typename VALUE> inline
+KeyedVector<KEY,VALUE>::KeyedVector()
+{
+}
+
+template<typename KEY, typename VALUE> inline
+bool KeyedVector<KEY,VALUE>::isIdenticalTo(const KeyedVector<KEY,VALUE>& rhs) const {
+ return mVector.array() == rhs.mVector.array();
+}
+
+template<typename KEY, typename VALUE> inline
+ssize_t KeyedVector<KEY,VALUE>::indexOfKey(const KEY& key) const {
+ return mVector.indexOf( key_value_pair_t<KEY,VALUE>(key) );
+}
+
+template<typename KEY, typename VALUE> inline
+const VALUE& KeyedVector<KEY,VALUE>::valueFor(const KEY& key) const {
+ ssize_t i = this->indexOfKey(key);
+ LOG_ALWAYS_FATAL_IF(i<0, "%s: key not found", __PRETTY_FUNCTION__);
+ return mVector.itemAt(i).value;
+}
+
+template<typename KEY, typename VALUE> inline
+const VALUE& KeyedVector<KEY,VALUE>::valueAt(size_t index) const {
+ return mVector.itemAt(index).value;
+}
+
+template<typename KEY, typename VALUE> inline
+const VALUE& KeyedVector<KEY,VALUE>::operator[] (size_t index) const {
+ return valueAt(index);
+}
+
+template<typename KEY, typename VALUE> inline
+const KEY& KeyedVector<KEY,VALUE>::keyAt(size_t index) const {
+ return mVector.itemAt(index).key;
+}
+
+template<typename KEY, typename VALUE> inline
+VALUE& KeyedVector<KEY,VALUE>::editValueFor(const KEY& key) {
+ ssize_t i = this->indexOfKey(key);
+ LOG_ALWAYS_FATAL_IF(i<0, "%s: key not found", __PRETTY_FUNCTION__);
+ return mVector.editItemAt(i).value;
+}
+
+template<typename KEY, typename VALUE> inline
+VALUE& KeyedVector<KEY,VALUE>::editValueAt(size_t index) {
+ return mVector.editItemAt(index).value;
+}
+
+template<typename KEY, typename VALUE> inline
+ssize_t KeyedVector<KEY,VALUE>::add(const KEY& key, const VALUE& value) {
+ return mVector.add( key_value_pair_t<KEY,VALUE>(key, value) );
+}
+
+template<typename KEY, typename VALUE> inline
+ssize_t KeyedVector<KEY,VALUE>::replaceValueFor(const KEY& key, const VALUE& value) {
+ key_value_pair_t<KEY,VALUE> pair(key, value);
+ mVector.remove(pair);
+ return mVector.add(pair);
+}
+
+template<typename KEY, typename VALUE> inline
+ssize_t KeyedVector<KEY,VALUE>::replaceValueAt(size_t index, const VALUE& item) {
+ if (index<size()) {
+ mVector.editItemAt(index).value = item;
+ return index;
+ }
+ return BAD_INDEX;
+}
+
+template<typename KEY, typename VALUE> inline
+ssize_t KeyedVector<KEY,VALUE>::removeItem(const KEY& key) {
+ return mVector.remove(key_value_pair_t<KEY,VALUE>(key));
+}
+
+template<typename KEY, typename VALUE> inline
+ssize_t KeyedVector<KEY, VALUE>::removeItemsAt(size_t index, size_t count) {
+ return mVector.removeItemsAt(index, count);
+}
+
+// ---------------------------------------------------------------------------
+
+template<typename KEY, typename VALUE> inline
+DefaultKeyedVector<KEY,VALUE>::DefaultKeyedVector(const VALUE& defValue)
+ : mDefault(defValue)
+{
+}
+
+template<typename KEY, typename VALUE> inline
+const VALUE& DefaultKeyedVector<KEY,VALUE>::valueFor(const KEY& key) const {
+ ssize_t i = this->indexOfKey(key);
+ return i >= 0 ? KeyedVector<KEY,VALUE>::valueAt(i) : mDefault;
+}
+
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_KEYED_VECTOR_H
diff --git a/include/utils/LinearAllocator.h b/include/utils/LinearAllocator.h
new file mode 100644
index 0000000..4772bc8
--- /dev/null
+++ b/include/utils/LinearAllocator.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2012, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ANDROID_LINEARALLOCATOR_H
+#define ANDROID_LINEARALLOCATOR_H
+
+#include <stddef.h>
+
+namespace android {
+
+/**
+ * A memory manager that internally allocates multi-kbyte buffers for placing objects in. It avoids
+ * the overhead of malloc when many objects are allocated. It is most useful when creating many
+ * small objects with a similar lifetime, and doesn't add significant overhead for large
+ * allocations.
+ */
+class LinearAllocator {
+public:
+ LinearAllocator();
+ ~LinearAllocator();
+
+ /**
+ * Reserves and returns a region of memory of at least size 'size', aligning as needed.
+ * Typically this is used in an object's overridden new() method or as a replacement for malloc.
+ *
+ * The lifetime of the returned buffers is tied to that of the LinearAllocator. If calling
+ * delete() on an object stored in a buffer is needed, it should be overridden to use
+ * rewindIfLastAlloc()
+ */
+ void* alloc(size_t size);
+
+ /**
+ * Attempt to deallocate the given buffer, with the LinearAllocator attempting to rewind its
+ * state if possible. No destructors are called.
+ */
+ void rewindIfLastAlloc(void* ptr, size_t allocSize);
+
+ /**
+ * Dump memory usage statistics to the log (allocated and wasted space)
+ */
+ void dumpMemoryStats(const char* prefix = "");
+
+ /**
+ * The number of bytes used for buffers allocated in the LinearAllocator (does not count space
+ * wasted)
+ */
+ size_t usedSize() const { return mTotalAllocated - mWastedSpace; }
+
+private:
+ LinearAllocator(const LinearAllocator& other);
+
+ class Page;
+
+ Page* newPage(size_t pageSize);
+ bool fitsInCurrentPage(size_t size);
+ void ensureNext(size_t size);
+ void* start(Page *p);
+ void* end(Page* p);
+
+ size_t mPageSize;
+ size_t mMaxAllocSize;
+ void* mNext;
+ Page* mCurrentPage;
+ Page* mPages;
+
+ // Memory usage tracking
+ size_t mTotalAllocated;
+ size_t mWastedSpace;
+ size_t mPageCount;
+ size_t mDedicatedPageCount;
+};
+
+}; // namespace android
+
+#endif // ANDROID_LINEARALLOCATOR_H
diff --git a/include/utils/LinearTransform.h b/include/utils/LinearTransform.h
new file mode 100644
index 0000000..04cb355
--- /dev/null
+++ b/include/utils/LinearTransform.h
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+#ifndef _LIBS_UTILS_LINEAR_TRANSFORM_H
+#define _LIBS_UTILS_LINEAR_TRANSFORM_H
+
+#include <stdint.h>
+
+namespace android {
+
+// LinearTransform defines a structure which hold the definition of a
+// transformation from single dimensional coordinate system A into coordinate
+// system B (and back again). Values in A and in B are 64 bit, the linear
+// scale factor is expressed as a rational number using two 32 bit values.
+//
+// Specifically, let
+// f(a) = b
+// F(b) = f^-1(b) = a
+// then
+//
+// f(a) = (((a - a_zero) * a_to_b_numer) / a_to_b_denom) + b_zero;
+//
+// and
+//
+// F(b) = (((b - b_zero) * a_to_b_denom) / a_to_b_numer) + a_zero;
+//
+struct LinearTransform {
+ int64_t a_zero;
+ int64_t b_zero;
+ int32_t a_to_b_numer;
+ uint32_t a_to_b_denom;
+
+ // Transform from A->B
+ // Returns true on success, or false in the case of a singularity or an
+ // overflow.
+ bool doForwardTransform(int64_t a_in, int64_t* b_out) const;
+
+ // Transform from B->A
+ // Returns true on success, or false in the case of a singularity or an
+ // overflow.
+ bool doReverseTransform(int64_t b_in, int64_t* a_out) const;
+
+ // Helpers which will reduce the fraction N/D using Euclid's method.
+ template <class T> static void reduce(T* N, T* D);
+ static void reduce(int32_t* N, uint32_t* D);
+};
+
+
+}
+
+#endif // _LIBS_UTILS_LINEAR_TRANSFORM_H
diff --git a/include/utils/List.h b/include/utils/List.h
new file mode 100644
index 0000000..403cd7f
--- /dev/null
+++ b/include/utils/List.h
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2005 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.
+ */
+
+//
+// Templated list class. Normally we'd use STL, but we don't have that.
+// This class mimics STL's interfaces.
+//
+// Objects are copied into the list with the '=' operator or with copy-
+// construction, so if the compiler's auto-generated versions won't work for
+// you, define your own.
+//
+// The only class you want to use from here is "List".
+//
+#ifndef _LIBS_UTILS_LIST_H
+#define _LIBS_UTILS_LIST_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+namespace android {
+
+/*
+ * Doubly-linked list. Instantiate with "List<MyClass> myList".
+ *
+ * Objects added to the list are copied using the assignment operator,
+ * so this must be defined.
+ */
+template<typename T>
+class List
+{
+protected:
+ /*
+ * One element in the list.
+ */
+ class _Node {
+ public:
+ explicit _Node(const T& val) : mVal(val) {}
+ ~_Node() {}
+ inline T& getRef() { return mVal; }
+ inline const T& getRef() const { return mVal; }
+ inline _Node* getPrev() const { return mpPrev; }
+ inline _Node* getNext() const { return mpNext; }
+ inline void setVal(const T& val) { mVal = val; }
+ inline void setPrev(_Node* ptr) { mpPrev = ptr; }
+ inline void setNext(_Node* ptr) { mpNext = ptr; }
+ private:
+ friend class List;
+ friend class _ListIterator;
+ T mVal;
+ _Node* mpPrev;
+ _Node* mpNext;
+ };
+
+ /*
+ * Iterator for walking through the list.
+ */
+
+ template <typename TYPE>
+ struct CONST_ITERATOR {
+ typedef _Node const * NodePtr;
+ typedef const TYPE Type;
+ };
+
+ template <typename TYPE>
+ struct NON_CONST_ITERATOR {
+ typedef _Node* NodePtr;
+ typedef TYPE Type;
+ };
+
+ template<
+ typename U,
+ template <class> class Constness
+ >
+ class _ListIterator {
+ typedef _ListIterator<U, Constness> _Iter;
+ typedef typename Constness<U>::NodePtr _NodePtr;
+ typedef typename Constness<U>::Type _Type;
+
+ explicit _ListIterator(_NodePtr ptr) : mpNode(ptr) {}
+
+ public:
+ _ListIterator() {}
+ _ListIterator(const _Iter& rhs) : mpNode(rhs.mpNode) {}
+ ~_ListIterator() {}
+
+ // this will handle conversions from iterator to const_iterator
+ // (and also all convertible iterators)
+ // Here, in this implementation, the iterators can be converted
+ // if the nodes can be converted
+ template<typename V> explicit
+ _ListIterator(const V& rhs) : mpNode(rhs.mpNode) {}
+
+
+ /*
+ * Dereference operator. Used to get at the juicy insides.
+ */
+ _Type& operator*() const { return mpNode->getRef(); }
+ _Type* operator->() const { return &(mpNode->getRef()); }
+
+ /*
+ * Iterator comparison.
+ */
+ inline bool operator==(const _Iter& right) const {
+ return mpNode == right.mpNode; }
+
+ inline bool operator!=(const _Iter& right) const {
+ return mpNode != right.mpNode; }
+
+ /*
+ * handle comparisons between iterator and const_iterator
+ */
+ template<typename OTHER>
+ inline bool operator==(const OTHER& right) const {
+ return mpNode == right.mpNode; }
+
+ template<typename OTHER>
+ inline bool operator!=(const OTHER& right) const {
+ return mpNode != right.mpNode; }
+
+ /*
+ * Incr/decr, used to move through the list.
+ */
+ inline _Iter& operator++() { // pre-increment
+ mpNode = mpNode->getNext();
+ return *this;
+ }
+ const _Iter operator++(int) { // post-increment
+ _Iter tmp(*this);
+ mpNode = mpNode->getNext();
+ return tmp;
+ }
+ inline _Iter& operator--() { // pre-increment
+ mpNode = mpNode->getPrev();
+ return *this;
+ }
+ const _Iter operator--(int) { // post-increment
+ _Iter tmp(*this);
+ mpNode = mpNode->getPrev();
+ return tmp;
+ }
+
+ inline _NodePtr getNode() const { return mpNode; }
+
+ _NodePtr mpNode; /* should be private, but older gcc fails */
+ private:
+ friend class List;
+ };
+
+public:
+ List() {
+ prep();
+ }
+ List(const List<T>& src) { // copy-constructor
+ prep();
+ insert(begin(), src.begin(), src.end());
+ }
+ virtual ~List() {
+ clear();
+ delete[] (unsigned char*) mpMiddle;
+ }
+
+ typedef _ListIterator<T, NON_CONST_ITERATOR> iterator;
+ typedef _ListIterator<T, CONST_ITERATOR> const_iterator;
+
+ List<T>& operator=(const List<T>& right);
+
+ /* returns true if the list is empty */
+ inline bool empty() const { return mpMiddle->getNext() == mpMiddle; }
+
+ /* return #of elements in list */
+ size_t size() const {
+ return size_t(distance(begin(), end()));
+ }
+
+ /*
+ * Return the first element or one past the last element. The
+ * _Node* we're returning is converted to an "iterator" by a
+ * constructor in _ListIterator.
+ */
+ inline iterator begin() {
+ return iterator(mpMiddle->getNext());
+ }
+ inline const_iterator begin() const {
+ return const_iterator(const_cast<_Node const*>(mpMiddle->getNext()));
+ }
+ inline iterator end() {
+ return iterator(mpMiddle);
+ }
+ inline const_iterator end() const {
+ return const_iterator(const_cast<_Node const*>(mpMiddle));
+ }
+
+ /* add the object to the head or tail of the list */
+ void push_front(const T& val) { insert(begin(), val); }
+ void push_back(const T& val) { insert(end(), val); }
+
+ /* insert before the current node; returns iterator at new node */
+ iterator insert(iterator posn, const T& val)
+ {
+ _Node* newNode = new _Node(val); // alloc & copy-construct
+ newNode->setNext(posn.getNode());
+ newNode->setPrev(posn.getNode()->getPrev());
+ posn.getNode()->getPrev()->setNext(newNode);
+ posn.getNode()->setPrev(newNode);
+ return iterator(newNode);
+ }
+
+ /* insert a range of elements before the current node */
+ void insert(iterator posn, const_iterator first, const_iterator last) {
+ for ( ; first != last; ++first)
+ insert(posn, *first);
+ }
+
+ /* remove one entry; returns iterator at next node */
+ iterator erase(iterator posn) {
+ _Node* pNext = posn.getNode()->getNext();
+ _Node* pPrev = posn.getNode()->getPrev();
+ pPrev->setNext(pNext);
+ pNext->setPrev(pPrev);
+ delete posn.getNode();
+ return iterator(pNext);
+ }
+
+ /* remove a range of elements */
+ iterator erase(iterator first, iterator last) {
+ while (first != last)
+ erase(first++); // don't erase than incr later!
+ return iterator(last);
+ }
+
+ /* remove all contents of the list */
+ void clear() {
+ _Node* pCurrent = mpMiddle->getNext();
+ _Node* pNext;
+
+ while (pCurrent != mpMiddle) {
+ pNext = pCurrent->getNext();
+ delete pCurrent;
+ pCurrent = pNext;
+ }
+ mpMiddle->setPrev(mpMiddle);
+ mpMiddle->setNext(mpMiddle);
+ }
+
+ /*
+ * Measure the distance between two iterators. On exist, "first"
+ * will be equal to "last". The iterators must refer to the same
+ * list.
+ *
+ * FIXME: This is actually a generic iterator function. It should be a
+ * template function at the top-level with specializations for things like
+ * vector<>, which can just do pointer math). Here we limit it to
+ * _ListIterator of the same type but different constness.
+ */
+ template<
+ typename U,
+ template <class> class CL,
+ template <class> class CR
+ >
+ ptrdiff_t distance(
+ _ListIterator<U, CL> first, _ListIterator<U, CR> last) const
+ {
+ ptrdiff_t count = 0;
+ while (first != last) {
+ ++first;
+ ++count;
+ }
+ return count;
+ }
+
+private:
+ /*
+ * I want a _Node but don't need it to hold valid data. More
+ * to the point, I don't want T's constructor to fire, since it
+ * might have side-effects or require arguments. So, we do this
+ * slightly uncouth storage alloc.
+ */
+ void prep() {
+ mpMiddle = (_Node*) new unsigned char[sizeof(_Node)];
+ mpMiddle->setPrev(mpMiddle);
+ mpMiddle->setNext(mpMiddle);
+ }
+
+ /*
+ * This node plays the role of "pointer to head" and "pointer to tail".
+ * It sits in the middle of a circular list of nodes. The iterator
+ * runs around the circle until it encounters this one.
+ */
+ _Node* mpMiddle;
+};
+
+/*
+ * Assignment operator.
+ *
+ * The simplest way to do this would be to clear out the target list and
+ * fill it with the source. However, we can speed things along by
+ * re-using existing elements.
+ */
+template<class T>
+List<T>& List<T>::operator=(const List<T>& right)
+{
+ if (this == &right)
+ return *this; // self-assignment
+ iterator firstDst = begin();
+ iterator lastDst = end();
+ const_iterator firstSrc = right.begin();
+ const_iterator lastSrc = right.end();
+ while (firstSrc != lastSrc && firstDst != lastDst)
+ *firstDst++ = *firstSrc++;
+ if (firstSrc == lastSrc) // ran out of elements in source?
+ erase(firstDst, lastDst); // yes, erase any extras
+ else
+ insert(lastDst, firstSrc, lastSrc); // copy remaining over
+ return *this;
+}
+
+}; // namespace android
+
+#endif // _LIBS_UTILS_LIST_H
diff --git a/include/utils/Log.h b/include/utils/Log.h
new file mode 100644
index 0000000..4259c86
--- /dev/null
+++ b/include/utils/Log.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2005 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.
+ */
+
+//
+// C/C++ logging functions. See the logging documentation for API details.
+//
+// We'd like these to be available from C code (in case we import some from
+// somewhere), so this has a C interface.
+//
+// The output will be correct when the log file is shared between multiple
+// threads and/or multiple processes so long as the operating system
+// supports O_APPEND. These calls have mutex-protected data structures
+// and so are NOT reentrant. Do not use LOG in a signal handler.
+//
+#ifndef _LIBS_UTILS_LOG_H
+#define _LIBS_UTILS_LOG_H
+
+#include <cutils/log.h>
+#include <sys/types.h>
+
+#ifdef __cplusplus
+
+namespace android {
+
+/*
+ * A very simple utility that yells in the log when an operation takes too long.
+ */
+class LogIfSlow {
+public:
+ LogIfSlow(const char* tag, android_LogPriority priority,
+ int timeoutMillis, const char* message);
+ ~LogIfSlow();
+
+private:
+ const char* const mTag;
+ const android_LogPriority mPriority;
+ const int mTimeoutMillis;
+ const char* const mMessage;
+ const int64_t mStart;
+};
+
+/*
+ * Writes the specified debug log message if this block takes longer than the
+ * specified number of milliseconds to run. Includes the time actually taken.
+ *
+ * {
+ * ALOGD_IF_SLOW(50, "Excessive delay doing something.");
+ * doSomething();
+ * }
+ */
+#define ALOGD_IF_SLOW(timeoutMillis, message) \
+ android::LogIfSlow _logIfSlow(LOG_TAG, ANDROID_LOG_DEBUG, timeoutMillis, message);
+
+} // namespace android
+
+#endif // __cplusplus
+
+#endif // _LIBS_UTILS_LOG_H
diff --git a/include/utils/Looper.h b/include/utils/Looper.h
new file mode 100644
index 0000000..15c9891
--- /dev/null
+++ b/include/utils/Looper.h
@@ -0,0 +1,477 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef UTILS_LOOPER_H
+#define UTILS_LOOPER_H
+
+#include <utils/threads.h>
+#include <utils/RefBase.h>
+#include <utils/KeyedVector.h>
+#include <utils/Timers.h>
+
+#include <sys/epoll.h>
+
+namespace android {
+
+/*
+ * NOTE: Since Looper is used to implement the NDK ALooper, the Looper
+ * enums and the signature of Looper_callbackFunc need to align with
+ * that implementation.
+ */
+
+/**
+ * For callback-based event loops, this is the prototype of the function
+ * that is called when a file descriptor event occurs.
+ * It is given the file descriptor it is associated with,
+ * a bitmask of the poll events that were triggered (typically EVENT_INPUT),
+ * and the data pointer that was originally supplied.
+ *
+ * Implementations should return 1 to continue receiving callbacks, or 0
+ * to have this file descriptor and callback unregistered from the looper.
+ */
+typedef int (*Looper_callbackFunc)(int fd, int events, void* data);
+
+/**
+ * A message that can be posted to a Looper.
+ */
+struct Message {
+ Message() : what(0) { }
+ Message(int what) : what(what) { }
+
+ /* The message type. (interpretation is left up to the handler) */
+ int what;
+};
+
+
+/**
+ * Interface for a Looper message handler.
+ *
+ * The Looper holds a strong reference to the message handler whenever it has
+ * a message to deliver to it. Make sure to call Looper::removeMessages
+ * to remove any pending messages destined for the handler so that the handler
+ * can be destroyed.
+ */
+class MessageHandler : public virtual RefBase {
+protected:
+ virtual ~MessageHandler() { }
+
+public:
+ /**
+ * Handles a message.
+ */
+ virtual void handleMessage(const Message& message) = 0;
+};
+
+
+/**
+ * A simple proxy that holds a weak reference to a message handler.
+ */
+class WeakMessageHandler : public MessageHandler {
+protected:
+ virtual ~WeakMessageHandler();
+
+public:
+ WeakMessageHandler(const wp<MessageHandler>& handler);
+ virtual void handleMessage(const Message& message);
+
+private:
+ wp<MessageHandler> mHandler;
+};
+
+
+/**
+ * A looper callback.
+ */
+class LooperCallback : public virtual RefBase {
+protected:
+ virtual ~LooperCallback() { }
+
+public:
+ /**
+ * Handles a poll event for the given file descriptor.
+ * It is given the file descriptor it is associated with,
+ * a bitmask of the poll events that were triggered (typically EVENT_INPUT),
+ * and the data pointer that was originally supplied.
+ *
+ * Implementations should return 1 to continue receiving callbacks, or 0
+ * to have this file descriptor and callback unregistered from the looper.
+ */
+ virtual int handleEvent(int fd, int events, void* data) = 0;
+};
+
+/**
+ * Wraps a Looper_callbackFunc function pointer.
+ */
+class SimpleLooperCallback : public LooperCallback {
+protected:
+ virtual ~SimpleLooperCallback();
+
+public:
+ SimpleLooperCallback(Looper_callbackFunc callback);
+ virtual int handleEvent(int fd, int events, void* data);
+
+private:
+ Looper_callbackFunc mCallback;
+};
+
+/**
+ * A polling loop that supports monitoring file descriptor events, optionally
+ * using callbacks. The implementation uses epoll() internally.
+ *
+ * A looper can be associated with a thread although there is no requirement that it must be.
+ */
+class Looper : public RefBase {
+protected:
+ virtual ~Looper();
+
+public:
+ enum {
+ /**
+ * Result from Looper_pollOnce() and Looper_pollAll():
+ * The poll was awoken using wake() before the timeout expired
+ * and no callbacks were executed and no other file descriptors were ready.
+ */
+ POLL_WAKE = -1,
+
+ /**
+ * Result from Looper_pollOnce() and Looper_pollAll():
+ * One or more callbacks were executed.
+ */
+ POLL_CALLBACK = -2,
+
+ /**
+ * Result from Looper_pollOnce() and Looper_pollAll():
+ * The timeout expired.
+ */
+ POLL_TIMEOUT = -3,
+
+ /**
+ * Result from Looper_pollOnce() and Looper_pollAll():
+ * An error occurred.
+ */
+ POLL_ERROR = -4,
+ };
+
+ /**
+ * Flags for file descriptor events that a looper can monitor.
+ *
+ * These flag bits can be combined to monitor multiple events at once.
+ */
+ enum {
+ /**
+ * The file descriptor is available for read operations.
+ */
+ EVENT_INPUT = 1 << 0,
+
+ /**
+ * The file descriptor is available for write operations.
+ */
+ EVENT_OUTPUT = 1 << 1,
+
+ /**
+ * The file descriptor has encountered an error condition.
+ *
+ * The looper always sends notifications about errors; it is not necessary
+ * to specify this event flag in the requested event set.
+ */
+ EVENT_ERROR = 1 << 2,
+
+ /**
+ * The file descriptor was hung up.
+ * For example, indicates that the remote end of a pipe or socket was closed.
+ *
+ * The looper always sends notifications about hangups; it is not necessary
+ * to specify this event flag in the requested event set.
+ */
+ EVENT_HANGUP = 1 << 3,
+
+ /**
+ * The file descriptor is invalid.
+ * For example, the file descriptor was closed prematurely.
+ *
+ * The looper always sends notifications about invalid file descriptors; it is not necessary
+ * to specify this event flag in the requested event set.
+ */
+ EVENT_INVALID = 1 << 4,
+ };
+
+ enum {
+ /**
+ * Option for Looper_prepare: this looper will accept calls to
+ * Looper_addFd() that do not have a callback (that is provide NULL
+ * for the callback). In this case the caller of Looper_pollOnce()
+ * or Looper_pollAll() MUST check the return from these functions to
+ * discover when data is available on such fds and process it.
+ */
+ PREPARE_ALLOW_NON_CALLBACKS = 1<<0
+ };
+
+ /**
+ * Creates a looper.
+ *
+ * If allowNonCallbaks is true, the looper will allow file descriptors to be
+ * registered without associated callbacks. This assumes that the caller of
+ * pollOnce() is prepared to handle callback-less events itself.
+ */
+ Looper(bool allowNonCallbacks);
+
+ /**
+ * Returns whether this looper instance allows the registration of file descriptors
+ * using identifiers instead of callbacks.
+ */
+ bool getAllowNonCallbacks() const;
+
+ /**
+ * Waits for events to be available, with optional timeout in milliseconds.
+ * Invokes callbacks for all file descriptors on which an event occurred.
+ *
+ * If the timeout is zero, returns immediately without blocking.
+ * If the timeout is negative, waits indefinitely until an event appears.
+ *
+ * Returns POLL_WAKE if the poll was awoken using wake() before
+ * the timeout expired and no callbacks were invoked and no other file
+ * descriptors were ready.
+ *
+ * Returns POLL_CALLBACK if one or more callbacks were invoked.
+ *
+ * Returns POLL_TIMEOUT if there was no data before the given
+ * timeout expired.
+ *
+ * Returns POLL_ERROR if an error occurred.
+ *
+ * Returns a value >= 0 containing an identifier if its file descriptor has data
+ * and it has no callback function (requiring the caller here to handle it).
+ * In this (and only this) case outFd, outEvents and outData will contain the poll
+ * events and data associated with the fd, otherwise they will be set to NULL.
+ *
+ * This method does not return until it has finished invoking the appropriate callbacks
+ * for all file descriptors that were signalled.
+ */
+ int pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData);
+ inline int pollOnce(int timeoutMillis) {
+ return pollOnce(timeoutMillis, NULL, NULL, NULL);
+ }
+
+ /**
+ * Like pollOnce(), but performs all pending callbacks until all
+ * data has been consumed or a file descriptor is available with no callback.
+ * This function will never return POLL_CALLBACK.
+ */
+ int pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outData);
+ inline int pollAll(int timeoutMillis) {
+ return pollAll(timeoutMillis, NULL, NULL, NULL);
+ }
+
+ /**
+ * Wakes the poll asynchronously.
+ *
+ * This method can be called on any thread.
+ * This method returns immediately.
+ */
+ void wake();
+
+ /**
+ * Adds a new file descriptor to be polled by the looper.
+ * If the same file descriptor was previously added, it is replaced.
+ *
+ * "fd" is the file descriptor to be added.
+ * "ident" is an identifier for this event, which is returned from pollOnce().
+ * The identifier must be >= 0, or POLL_CALLBACK if providing a non-NULL callback.
+ * "events" are the poll events to wake up on. Typically this is EVENT_INPUT.
+ * "callback" is the function to call when there is an event on the file descriptor.
+ * "data" is a private data pointer to supply to the callback.
+ *
+ * There are two main uses of this function:
+ *
+ * (1) If "callback" is non-NULL, then this function will be called when there is
+ * data on the file descriptor. It should execute any events it has pending,
+ * appropriately reading from the file descriptor. The 'ident' is ignored in this case.
+ *
+ * (2) If "callback" is NULL, the 'ident' will be returned by Looper_pollOnce
+ * when its file descriptor has data available, requiring the caller to take
+ * care of processing it.
+ *
+ * Returns 1 if the file descriptor was added, 0 if the arguments were invalid.
+ *
+ * This method can be called on any thread.
+ * This method may block briefly if it needs to wake the poll.
+ *
+ * The callback may either be specified as a bare function pointer or as a smart
+ * pointer callback object. The smart pointer should be preferred because it is
+ * easier to avoid races when the callback is removed from a different thread.
+ * See removeFd() for details.
+ */
+ int addFd(int fd, int ident, int events, Looper_callbackFunc callback, void* data);
+ int addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data);
+
+ /**
+ * Removes a previously added file descriptor from the looper.
+ *
+ * When this method returns, it is safe to close the file descriptor since the looper
+ * will no longer have a reference to it. However, it is possible for the callback to
+ * already be running or for it to run one last time if the file descriptor was already
+ * signalled. Calling code is responsible for ensuring that this case is safely handled.
+ * For example, if the callback takes care of removing itself during its own execution either
+ * by returning 0 or by calling this method, then it can be guaranteed to not be invoked
+ * again at any later time unless registered anew.
+ *
+ * A simple way to avoid this problem is to use the version of addFd() that takes
+ * a sp<LooperCallback> instead of a bare function pointer. The LooperCallback will
+ * be released at the appropriate time by the Looper.
+ *
+ * Returns 1 if the file descriptor was removed, 0 if none was previously registered.
+ *
+ * This method can be called on any thread.
+ * This method may block briefly if it needs to wake the poll.
+ */
+ int removeFd(int fd);
+
+ /**
+ * Enqueues a message to be processed by the specified handler.
+ *
+ * The handler must not be null.
+ * This method can be called on any thread.
+ */
+ void sendMessage(const sp<MessageHandler>& handler, const Message& message);
+
+ /**
+ * Enqueues a message to be processed by the specified handler after all pending messages
+ * after the specified delay.
+ *
+ * The time delay is specified in uptime nanoseconds.
+ * The handler must not be null.
+ * This method can be called on any thread.
+ */
+ void sendMessageDelayed(nsecs_t uptimeDelay, const sp<MessageHandler>& handler,
+ const Message& message);
+
+ /**
+ * Enqueues a message to be processed by the specified handler after all pending messages
+ * at the specified time.
+ *
+ * The time is specified in uptime nanoseconds.
+ * The handler must not be null.
+ * This method can be called on any thread.
+ */
+ void sendMessageAtTime(nsecs_t uptime, const sp<MessageHandler>& handler,
+ const Message& message);
+
+ /**
+ * Removes all messages for the specified handler from the queue.
+ *
+ * The handler must not be null.
+ * This method can be called on any thread.
+ */
+ void removeMessages(const sp<MessageHandler>& handler);
+
+ /**
+ * Removes all messages of a particular type for the specified handler from the queue.
+ *
+ * The handler must not be null.
+ * This method can be called on any thread.
+ */
+ void removeMessages(const sp<MessageHandler>& handler, int what);
+
+ /**
+ * Return whether this looper's thread is currently idling -- that is, whether it
+ * stopped waiting for more work to do. Note that this is intrinsically racy, since
+ * its state can change before you get the result back.
+ */
+ bool isIdling() const;
+
+ /**
+ * Prepares a looper associated with the calling thread, and returns it.
+ * If the thread already has a looper, it is returned. Otherwise, a new
+ * one is created, associated with the thread, and returned.
+ *
+ * The opts may be PREPARE_ALLOW_NON_CALLBACKS or 0.
+ */
+ static sp<Looper> prepare(int opts);
+
+ /**
+ * Sets the given looper to be associated with the calling thread.
+ * If another looper is already associated with the thread, it is replaced.
+ *
+ * If "looper" is NULL, removes the currently associated looper.
+ */
+ static void setForThread(const sp<Looper>& looper);
+
+ /**
+ * Returns the looper associated with the calling thread, or NULL if
+ * there is not one.
+ */
+ static sp<Looper> getForThread();
+
+private:
+ struct Request {
+ int fd;
+ int ident;
+ sp<LooperCallback> callback;
+ void* data;
+ };
+
+ struct Response {
+ int events;
+ Request request;
+ };
+
+ struct MessageEnvelope {
+ MessageEnvelope() : uptime(0) { }
+
+ MessageEnvelope(nsecs_t uptime, const sp<MessageHandler> handler,
+ const Message& message) : uptime(uptime), handler(handler), message(message) {
+ }
+
+ nsecs_t uptime;
+ sp<MessageHandler> handler;
+ Message message;
+ };
+
+ const bool mAllowNonCallbacks; // immutable
+
+ int mWakeReadPipeFd; // immutable
+ int mWakeWritePipeFd; // immutable
+ Mutex mLock;
+
+ Vector<MessageEnvelope> mMessageEnvelopes; // guarded by mLock
+ bool mSendingMessage; // guarded by mLock
+
+ // Whether we are currently waiting for work. Not protected by a lock,
+ // any use of it is racy anyway.
+ volatile bool mIdling;
+
+ int mEpollFd; // immutable
+
+ // Locked list of file descriptor monitoring requests.
+ KeyedVector<int, Request> mRequests; // guarded by mLock
+
+ // This state is only used privately by pollOnce and does not require a lock since
+ // it runs on a single thread.
+ Vector<Response> mResponses;
+ size_t mResponseIndex;
+ nsecs_t mNextMessageUptime; // set to LLONG_MAX when none
+
+ int pollInner(int timeoutMillis);
+ void awoken();
+ void pushResponse(int events, const Request& request);
+
+ static void initTLSKey();
+ static void threadDestructor(void *st);
+};
+
+} // namespace android
+
+#endif // UTILS_LOOPER_H
diff --git a/include/utils/LruCache.h b/include/utils/LruCache.h
new file mode 100644
index 0000000..cd9d7f9
--- /dev/null
+++ b/include/utils/LruCache.h
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_UTILS_LRU_CACHE_H
+#define ANDROID_UTILS_LRU_CACHE_H
+
+#include <UniquePtr.h>
+#include <utils/BasicHashtable.h>
+
+namespace android {
+
+/**
+ * GenerationCache callback used when an item is removed
+ */
+template<typename EntryKey, typename EntryValue>
+class OnEntryRemoved {
+public:
+ virtual ~OnEntryRemoved() { };
+ virtual void operator()(EntryKey& key, EntryValue& value) = 0;
+}; // class OnEntryRemoved
+
+template <typename TKey, typename TValue>
+class LruCache {
+public:
+ explicit LruCache(uint32_t maxCapacity);
+
+ enum Capacity {
+ kUnlimitedCapacity,
+ };
+
+ void setOnEntryRemovedListener(OnEntryRemoved<TKey, TValue>* listener);
+ size_t size() const;
+ const TValue& get(const TKey& key);
+ bool put(const TKey& key, const TValue& value);
+ bool remove(const TKey& key);
+ bool removeOldest();
+ void clear();
+ const TValue& peekOldestValue();
+
+ class Iterator {
+ public:
+ Iterator(const LruCache<TKey, TValue>& cache): mCache(cache), mIndex(-1) {
+ }
+
+ bool next() {
+ mIndex = mCache.mTable->next(mIndex);
+ return (ssize_t)mIndex != -1;
+ }
+
+ size_t index() const {
+ return mIndex;
+ }
+
+ const TValue& value() const {
+ return mCache.mTable->entryAt(mIndex).value;
+ }
+
+ const TKey& key() const {
+ return mCache.mTable->entryAt(mIndex).key;
+ }
+ private:
+ const LruCache<TKey, TValue>& mCache;
+ size_t mIndex;
+ };
+
+private:
+ LruCache(const LruCache& that); // disallow copy constructor
+
+ struct Entry {
+ TKey key;
+ TValue value;
+ Entry* parent;
+ Entry* child;
+
+ Entry(TKey key_, TValue value_) : key(key_), value(value_), parent(NULL), child(NULL) {
+ }
+ const TKey& getKey() const { return key; }
+ };
+
+ void attachToCache(Entry& entry);
+ void detachFromCache(Entry& entry);
+ void rehash(size_t newCapacity);
+
+ UniquePtr<BasicHashtable<TKey, Entry> > mTable;
+ OnEntryRemoved<TKey, TValue>* mListener;
+ Entry* mOldest;
+ Entry* mYoungest;
+ uint32_t mMaxCapacity;
+ TValue mNullValue;
+};
+
+// Implementation is here, because it's fully templated
+template <typename TKey, typename TValue>
+LruCache<TKey, TValue>::LruCache(uint32_t maxCapacity)
+ : mTable(new BasicHashtable<TKey, Entry>)
+ , mListener(NULL)
+ , mOldest(NULL)
+ , mYoungest(NULL)
+ , mMaxCapacity(maxCapacity)
+ , mNullValue(NULL) {
+};
+
+template<typename K, typename V>
+void LruCache<K, V>::setOnEntryRemovedListener(OnEntryRemoved<K, V>* listener) {
+ mListener = listener;
+}
+
+template <typename TKey, typename TValue>
+size_t LruCache<TKey, TValue>::size() const {
+ return mTable->size();
+}
+
+template <typename TKey, typename TValue>
+const TValue& LruCache<TKey, TValue>::get(const TKey& key) {
+ hash_t hash = hash_type(key);
+ ssize_t index = mTable->find(-1, hash, key);
+ if (index == -1) {
+ return mNullValue;
+ }
+ Entry& entry = mTable->editEntryAt(index);
+ detachFromCache(entry);
+ attachToCache(entry);
+ return entry.value;
+}
+
+template <typename TKey, typename TValue>
+bool LruCache<TKey, TValue>::put(const TKey& key, const TValue& value) {
+ if (mMaxCapacity != kUnlimitedCapacity && size() >= mMaxCapacity) {
+ removeOldest();
+ }
+
+ hash_t hash = hash_type(key);
+ ssize_t index = mTable->find(-1, hash, key);
+ if (index >= 0) {
+ return false;
+ }
+ if (!mTable->hasMoreRoom()) {
+ rehash(mTable->capacity() * 2);
+ }
+
+ // Would it be better to initialize a blank entry and assign key, value?
+ Entry initEntry(key, value);
+ index = mTable->add(hash, initEntry);
+ Entry& entry = mTable->editEntryAt(index);
+ attachToCache(entry);
+ return true;
+}
+
+template <typename TKey, typename TValue>
+bool LruCache<TKey, TValue>::remove(const TKey& key) {
+ hash_t hash = hash_type(key);
+ ssize_t index = mTable->find(-1, hash, key);
+ if (index < 0) {
+ return false;
+ }
+ Entry& entry = mTable->editEntryAt(index);
+ if (mListener) {
+ (*mListener)(entry.key, entry.value);
+ }
+ detachFromCache(entry);
+ mTable->removeAt(index);
+ return true;
+}
+
+template <typename TKey, typename TValue>
+bool LruCache<TKey, TValue>::removeOldest() {
+ if (mOldest != NULL) {
+ return remove(mOldest->key);
+ // TODO: should probably abort if false
+ }
+ return false;
+}
+
+template <typename TKey, typename TValue>
+const TValue& LruCache<TKey, TValue>::peekOldestValue() {
+ if (mOldest) {
+ return mOldest->value;
+ }
+ return mNullValue;
+}
+
+template <typename TKey, typename TValue>
+void LruCache<TKey, TValue>::clear() {
+ if (mListener) {
+ for (Entry* p = mOldest; p != NULL; p = p->child) {
+ (*mListener)(p->key, p->value);
+ }
+ }
+ mYoungest = NULL;
+ mOldest = NULL;
+ mTable->clear();
+}
+
+template <typename TKey, typename TValue>
+void LruCache<TKey, TValue>::attachToCache(Entry& entry) {
+ if (mYoungest == NULL) {
+ mYoungest = mOldest = &entry;
+ } else {
+ entry.parent = mYoungest;
+ mYoungest->child = &entry;
+ mYoungest = &entry;
+ }
+}
+
+template <typename TKey, typename TValue>
+void LruCache<TKey, TValue>::detachFromCache(Entry& entry) {
+ if (entry.parent != NULL) {
+ entry.parent->child = entry.child;
+ } else {
+ mOldest = entry.child;
+ }
+ if (entry.child != NULL) {
+ entry.child->parent = entry.parent;
+ } else {
+ mYoungest = entry.parent;
+ }
+
+ entry.parent = NULL;
+ entry.child = NULL;
+}
+
+template <typename TKey, typename TValue>
+void LruCache<TKey, TValue>::rehash(size_t newCapacity) {
+ UniquePtr<BasicHashtable<TKey, Entry> > oldTable(mTable.release());
+ Entry* oldest = mOldest;
+
+ mOldest = NULL;
+ mYoungest = NULL;
+ mTable.reset(new BasicHashtable<TKey, Entry>(newCapacity));
+ for (Entry* p = oldest; p != NULL; p = p->child) {
+ put(p->key, p->value);
+ }
+}
+
+}
+
+#endif // ANDROID_UTILS_LRU_CACHE_H
diff --git a/include/utils/Mutex.h b/include/utils/Mutex.h
new file mode 100644
index 0000000..757519b
--- /dev/null
+++ b/include/utils/Mutex.h
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBS_UTILS_MUTEX_H
+#define _LIBS_UTILS_MUTEX_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <time.h>
+
+#if !defined(_WIN32)
+# include <pthread.h>
+#endif
+
+#include <utils/Errors.h>
+#include <utils/Timers.h>
+
+// ---------------------------------------------------------------------------
+namespace android {
+// ---------------------------------------------------------------------------
+
+class Condition;
+
+/*
+ * Simple mutex class. The implementation is system-dependent.
+ *
+ * The mutex must be unlocked by the thread that locked it. They are not
+ * recursive, i.e. the same thread can't lock it multiple times.
+ */
+class Mutex {
+public:
+ enum {
+ PRIVATE = 0,
+ SHARED = 1
+ };
+
+ Mutex();
+ Mutex(const char* name);
+ Mutex(int type, const char* name = NULL);
+ ~Mutex();
+
+ // lock or unlock the mutex
+ status_t lock();
+ void unlock();
+
+ // lock if possible; returns 0 on success, error otherwise
+ status_t tryLock();
+
+#if HAVE_ANDROID_OS
+ // lock the mutex, but don't wait longer than timeoutMilliseconds.
+ // Returns 0 on success, TIMED_OUT for failure due to timeout expiration.
+ //
+ // OSX doesn't have pthread_mutex_timedlock() or equivalent. To keep
+ // capabilities consistent across host OSes, this method is only available
+ // when building Android binaries.
+ status_t timedLock(nsecs_t timeoutMilliseconds);
+#endif
+
+ // Manages the mutex automatically. It'll be locked when Autolock is
+ // constructed and released when Autolock goes out of scope.
+ class Autolock {
+ public:
+ inline Autolock(Mutex& mutex) : mLock(mutex) { mLock.lock(); }
+ inline Autolock(Mutex* mutex) : mLock(*mutex) { mLock.lock(); }
+ inline ~Autolock() { mLock.unlock(); }
+ private:
+ Mutex& mLock;
+ };
+
+private:
+ friend class Condition;
+
+ // A mutex cannot be copied
+ Mutex(const Mutex&);
+ Mutex& operator = (const Mutex&);
+
+#if !defined(_WIN32)
+ pthread_mutex_t mMutex;
+#else
+ void _init();
+ void* mState;
+#endif
+};
+
+// ---------------------------------------------------------------------------
+
+#if !defined(_WIN32)
+
+inline Mutex::Mutex() {
+ pthread_mutex_init(&mMutex, NULL);
+}
+inline Mutex::Mutex(__attribute__((unused)) const char* name) {
+ pthread_mutex_init(&mMutex, NULL);
+}
+inline Mutex::Mutex(int type, __attribute__((unused)) const char* name) {
+ if (type == SHARED) {
+ pthread_mutexattr_t attr;
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
+ pthread_mutex_init(&mMutex, &attr);
+ pthread_mutexattr_destroy(&attr);
+ } else {
+ pthread_mutex_init(&mMutex, NULL);
+ }
+}
+inline Mutex::~Mutex() {
+ pthread_mutex_destroy(&mMutex);
+}
+inline status_t Mutex::lock() {
+ return -pthread_mutex_lock(&mMutex);
+}
+inline void Mutex::unlock() {
+ pthread_mutex_unlock(&mMutex);
+}
+inline status_t Mutex::tryLock() {
+ return -pthread_mutex_trylock(&mMutex);
+}
+#if HAVE_ANDROID_OS
+inline status_t Mutex::timedLock(nsecs_t timeoutNs) {
+ const struct timespec ts = {
+ /* .tv_sec = */ static_cast<time_t>(timeoutNs / 1000000000),
+ /* .tv_nsec = */ static_cast<long>(timeoutNs % 1000000000),
+ };
+ return -pthread_mutex_timedlock(&mMutex, &ts);
+}
+#endif
+
+#endif // !defined(_WIN32)
+
+// ---------------------------------------------------------------------------
+
+/*
+ * Automatic mutex. Declare one of these at the top of a function.
+ * When the function returns, it will go out of scope, and release the
+ * mutex.
+ */
+
+typedef Mutex::Autolock AutoMutex;
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+// ---------------------------------------------------------------------------
+
+#endif // _LIBS_UTILS_MUTEX_H
diff --git a/include/utils/NativeHandle.h b/include/utils/NativeHandle.h
new file mode 100644
index 0000000..b825168
--- /dev/null
+++ b/include/utils/NativeHandle.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_NATIVE_HANDLE_H
+#define ANDROID_NATIVE_HANDLE_H
+
+#include <utils/RefBase.h>
+#include <utils/StrongPointer.h>
+
+typedef struct native_handle native_handle_t;
+
+namespace android {
+
+class NativeHandle: public LightRefBase<NativeHandle> {
+public:
+ // Create a refcounted wrapper around a native_handle_t, and declare
+ // whether the wrapper owns the handle (so that it should clean up the
+ // handle upon destruction) or not.
+ // If handle is NULL, no NativeHandle will be created.
+ static sp<NativeHandle> create(native_handle_t* handle, bool ownsHandle);
+
+ const native_handle_t* handle() const {
+ return mHandle;
+ }
+
+private:
+ // for access to the destructor
+ friend class LightRefBase<NativeHandle>;
+
+ NativeHandle(native_handle_t* handle, bool ownsHandle);
+ virtual ~NativeHandle();
+
+ native_handle_t* mHandle;
+ bool mOwnsHandle;
+
+ // non-copyable
+ NativeHandle(const NativeHandle&);
+ NativeHandle& operator=(const NativeHandle&);
+};
+
+} // namespace android
+
+#endif // ANDROID_NATIVE_HANDLE_H
diff --git a/include/utils/Printer.h b/include/utils/Printer.h
new file mode 100644
index 0000000..bb66287
--- /dev/null
+++ b/include/utils/Printer.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PRINTER_H
+#define ANDROID_PRINTER_H
+
+#include <android/log.h>
+
+namespace android {
+
+// Interface for printing to an arbitrary data stream
+class Printer {
+public:
+ // Print a new line specified by 'string'. \n is appended automatically.
+ // -- Assumes that the string has no new line in it.
+ virtual void printLine(const char* string = "") = 0;
+
+ // Print a new line specified by the format string. \n is appended automatically.
+ // -- Assumes that the resulting string has no new line in it.
+ virtual void printFormatLine(const char* format, ...) __attribute__((format (printf, 2, 3)));
+
+protected:
+ Printer();
+ virtual ~Printer();
+}; // class Printer
+
+// Print to logcat
+class LogPrinter : public Printer {
+public:
+ // Create a printer using the specified logcat and log priority
+ // - Unless ignoreBlankLines is false, print blank lines to logcat
+ // (Note that the default ALOG behavior is to ignore blank lines)
+ LogPrinter(const char* logtag,
+ android_LogPriority priority = ANDROID_LOG_DEBUG,
+ const char* prefix = 0,
+ bool ignoreBlankLines = false);
+
+ // Print the specified line to logcat. No \n at the end is necessary.
+ virtual void printLine(const char* string);
+
+private:
+ void printRaw(const char* string);
+
+ const char* mLogTag;
+ android_LogPriority mPriority;
+ const char* mPrefix;
+ bool mIgnoreBlankLines;
+}; // class LogPrinter
+
+// Print to a file descriptor
+class FdPrinter : public Printer {
+public:
+ // Create a printer using the specified file descriptor.
+ // - Each line will be prefixed with 'indent' number of blank spaces.
+ // - In addition, each line will be prefixed with the 'prefix' string.
+ FdPrinter(int fd, unsigned int indent = 0, const char* prefix = 0);
+
+ // Print the specified line to the file descriptor. \n is appended automatically.
+ virtual void printLine(const char* string);
+
+private:
+ enum {
+ MAX_FORMAT_STRING = 20,
+ };
+
+ int mFd;
+ unsigned int mIndent;
+ const char* mPrefix;
+ char mFormatString[MAX_FORMAT_STRING];
+}; // class FdPrinter
+
+class String8;
+
+// Print to a String8
+class String8Printer : public Printer {
+public:
+ // Create a printer using the specified String8 as the target.
+ // - In addition, each line will be prefixed with the 'prefix' string.
+ // - target's memory lifetime must be a superset of this String8Printer.
+ String8Printer(String8* target, const char* prefix = 0);
+
+ // Append the specified line to the String8. \n is appended automatically.
+ virtual void printLine(const char* string);
+
+private:
+ String8* mTarget;
+ const char* mPrefix;
+}; // class String8Printer
+
+// Print to an existing Printer by adding a prefix to each line
+class PrefixPrinter : public Printer {
+public:
+ // Create a printer using the specified printer as the target.
+ PrefixPrinter(Printer& printer, const char* prefix);
+
+ // Print the line (prefixed with prefix) using the printer.
+ virtual void printLine(const char* string);
+
+private:
+ Printer& mPrinter;
+ const char* mPrefix;
+};
+
+}; // namespace android
+
+#endif // ANDROID_PRINTER_H
diff --git a/include/utils/ProcessCallStack.h b/include/utils/ProcessCallStack.h
new file mode 100644
index 0000000..32458b8
--- /dev/null
+++ b/include/utils/ProcessCallStack.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PROCESS_CALLSTACK_H
+#define ANDROID_PROCESS_CALLSTACK_H
+
+#include <utils/CallStack.h>
+#include <android/log.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+
+#include <time.h>
+#include <sys/types.h>
+
+namespace android {
+
+class Printer;
+
+// Collect/print the call stack (function, file, line) traces for all threads in a process.
+class ProcessCallStack {
+public:
+ // Create an empty call stack. No-op.
+ ProcessCallStack();
+ // Copy the existing process callstack (no other side effects).
+ ProcessCallStack(const ProcessCallStack& rhs);
+ ~ProcessCallStack();
+
+ // Immediately collect the stack traces for all threads.
+ void update();
+
+ // Print all stack traces to the log using the supplied logtag.
+ void log(const char* logtag, android_LogPriority priority = ANDROID_LOG_DEBUG,
+ const char* prefix = 0) const;
+
+ // Dump all stack traces to the specified file descriptor.
+ void dump(int fd, int indent = 0, const char* prefix = 0) const;
+
+ // Return a string (possibly very long) containing all the stack traces.
+ String8 toString(const char* prefix = 0) const;
+
+ // Dump a serialized representation of all the stack traces to the specified printer.
+ void print(Printer& printer) const;
+
+ // Get the number of threads whose stack traces were collected.
+ size_t size() const;
+
+private:
+ void printInternal(Printer& printer, Printer& csPrinter) const;
+
+ // Reset the process's stack frames and metadata.
+ void clear();
+
+ struct ThreadInfo {
+ CallStack callStack;
+ String8 threadName;
+ };
+
+ // tid -> ThreadInfo
+ KeyedVector<pid_t, ThreadInfo> mThreadMap;
+ // Time that update() was last called
+ struct tm mTimeUpdated;
+};
+
+}; // namespace android
+
+#endif // ANDROID_PROCESS_CALLSTACK_H
diff --git a/include/utils/PropertyMap.h b/include/utils/PropertyMap.h
new file mode 100644
index 0000000..a9e674f
--- /dev/null
+++ b/include/utils/PropertyMap.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UTILS_PROPERTY_MAP_H
+#define _UTILS_PROPERTY_MAP_H
+
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/Errors.h>
+#include <utils/Tokenizer.h>
+
+namespace android {
+
+/*
+ * Provides a mechanism for passing around string-based property key / value pairs
+ * and loading them from property files.
+ *
+ * The property files have the following simple structure:
+ *
+ * # Comment
+ * key = value
+ *
+ * Keys and values are any sequence of printable ASCII characters.
+ * The '=' separates the key from the value.
+ * The key and value may not contain whitespace.
+ *
+ * The '\' character is reserved for escape sequences and is not currently supported.
+ * The '"" character is reserved for quoting and is not currently supported.
+ * Files that contain the '\' or '"' character will fail to parse.
+ *
+ * The file must not contain duplicate keys.
+ *
+ * TODO Support escape sequences and quoted values when needed.
+ */
+class PropertyMap {
+public:
+ /* Creates an empty property map. */
+ PropertyMap();
+ ~PropertyMap();
+
+ /* Clears the property map. */
+ void clear();
+
+ /* Adds a property.
+ * Replaces the property with the same key if it is already present.
+ */
+ void addProperty(const String8& key, const String8& value);
+
+ /* Returns true if the property map contains the specified key. */
+ bool hasProperty(const String8& key) const;
+
+ /* Gets the value of a property and parses it.
+ * Returns true and sets outValue if the key was found and its value was parsed successfully.
+ * Otherwise returns false and does not modify outValue. (Also logs a warning.)
+ */
+ bool tryGetProperty(const String8& key, String8& outValue) const;
+ bool tryGetProperty(const String8& key, bool& outValue) const;
+ bool tryGetProperty(const String8& key, int32_t& outValue) const;
+ bool tryGetProperty(const String8& key, float& outValue) const;
+
+ /* Adds all values from the specified property map. */
+ void addAll(const PropertyMap* map);
+
+ /* Gets the underlying property map. */
+ inline const KeyedVector<String8, String8>& getProperties() const { return mProperties; }
+
+ /* Loads a property map from a file. */
+ static status_t load(const String8& filename, PropertyMap** outMap);
+
+private:
+ class Parser {
+ PropertyMap* mMap;
+ Tokenizer* mTokenizer;
+
+ public:
+ Parser(PropertyMap* map, Tokenizer* tokenizer);
+ ~Parser();
+ status_t parse();
+
+ private:
+ status_t parseType();
+ status_t parseKey();
+ status_t parseKeyProperty();
+ status_t parseModifier(const String8& token, int32_t* outMetaState);
+ status_t parseCharacterLiteral(char16_t* outCharacter);
+ };
+
+ KeyedVector<String8, String8> mProperties;
+};
+
+} // namespace android
+
+#endif // _UTILS_PROPERTY_MAP_H
diff --git a/include/utils/RWLock.h b/include/utils/RWLock.h
new file mode 100644
index 0000000..e743b1c
--- /dev/null
+++ b/include/utils/RWLock.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBS_UTILS_RWLOCK_H
+#define _LIBS_UTILS_RWLOCK_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#if !defined(_WIN32)
+# include <pthread.h>
+#endif
+
+#include <utils/Errors.h>
+#include <utils/ThreadDefs.h>
+
+// ---------------------------------------------------------------------------
+namespace android {
+// ---------------------------------------------------------------------------
+
+#if !defined(_WIN32)
+
+/*
+ * Simple mutex class. The implementation is system-dependent.
+ *
+ * The mutex must be unlocked by the thread that locked it. They are not
+ * recursive, i.e. the same thread can't lock it multiple times.
+ */
+class RWLock {
+public:
+ enum {
+ PRIVATE = 0,
+ SHARED = 1
+ };
+
+ RWLock();
+ RWLock(const char* name);
+ RWLock(int type, const char* name = NULL);
+ ~RWLock();
+
+ status_t readLock();
+ status_t tryReadLock();
+ status_t writeLock();
+ status_t tryWriteLock();
+ void unlock();
+
+ class AutoRLock {
+ public:
+ inline AutoRLock(RWLock& rwlock) : mLock(rwlock) { mLock.readLock(); }
+ inline ~AutoRLock() { mLock.unlock(); }
+ private:
+ RWLock& mLock;
+ };
+
+ class AutoWLock {
+ public:
+ inline AutoWLock(RWLock& rwlock) : mLock(rwlock) { mLock.writeLock(); }
+ inline ~AutoWLock() { mLock.unlock(); }
+ private:
+ RWLock& mLock;
+ };
+
+private:
+ // A RWLock cannot be copied
+ RWLock(const RWLock&);
+ RWLock& operator = (const RWLock&);
+
+ pthread_rwlock_t mRWLock;
+};
+
+inline RWLock::RWLock() {
+ pthread_rwlock_init(&mRWLock, NULL);
+}
+inline RWLock::RWLock(__attribute__((unused)) const char* name) {
+ pthread_rwlock_init(&mRWLock, NULL);
+}
+inline RWLock::RWLock(int type, __attribute__((unused)) const char* name) {
+ if (type == SHARED) {
+ pthread_rwlockattr_t attr;
+ pthread_rwlockattr_init(&attr);
+ pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
+ pthread_rwlock_init(&mRWLock, &attr);
+ pthread_rwlockattr_destroy(&attr);
+ } else {
+ pthread_rwlock_init(&mRWLock, NULL);
+ }
+}
+inline RWLock::~RWLock() {
+ pthread_rwlock_destroy(&mRWLock);
+}
+inline status_t RWLock::readLock() {
+ return -pthread_rwlock_rdlock(&mRWLock);
+}
+inline status_t RWLock::tryReadLock() {
+ return -pthread_rwlock_tryrdlock(&mRWLock);
+}
+inline status_t RWLock::writeLock() {
+ return -pthread_rwlock_wrlock(&mRWLock);
+}
+inline status_t RWLock::tryWriteLock() {
+ return -pthread_rwlock_trywrlock(&mRWLock);
+}
+inline void RWLock::unlock() {
+ pthread_rwlock_unlock(&mRWLock);
+}
+
+#endif // !defined(_WIN32)
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+// ---------------------------------------------------------------------------
+
+#endif // _LIBS_UTILS_RWLOCK_H
diff --git a/include/utils/RefBase.h b/include/utils/RefBase.h
new file mode 100644
index 0000000..eac6a78
--- /dev/null
+++ b/include/utils/RefBase.h
@@ -0,0 +1,555 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_REF_BASE_H
+#define ANDROID_REF_BASE_H
+
+#include <cutils/atomic.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <utils/StrongPointer.h>
+#include <utils/TypeHelpers.h>
+
+// ---------------------------------------------------------------------------
+namespace android {
+
+class TextOutput;
+TextOutput& printWeakPointer(TextOutput& to, const void* val);
+
+// ---------------------------------------------------------------------------
+
+#define COMPARE_WEAK(_op_) \
+inline bool operator _op_ (const sp<T>& o) const { \
+ return m_ptr _op_ o.m_ptr; \
+} \
+inline bool operator _op_ (const T* o) const { \
+ return m_ptr _op_ o; \
+} \
+template<typename U> \
+inline bool operator _op_ (const sp<U>& o) const { \
+ return m_ptr _op_ o.m_ptr; \
+} \
+template<typename U> \
+inline bool operator _op_ (const U* o) const { \
+ return m_ptr _op_ o; \
+}
+
+// ---------------------------------------------------------------------------
+
+class ReferenceRenamer {
+protected:
+ // destructor is purposedly not virtual so we avoid code overhead from
+ // subclasses; we have to make it protected to guarantee that it
+ // cannot be called from this base class (and to make strict compilers
+ // happy).
+ ~ReferenceRenamer() { }
+public:
+ virtual void operator()(size_t i) const = 0;
+};
+
+// ---------------------------------------------------------------------------
+
+class RefBase
+{
+public:
+ void incStrong(const void* id) const;
+ void decStrong(const void* id) const;
+
+ void forceIncStrong(const void* id) const;
+
+ //! DEBUGGING ONLY: Get current strong ref count.
+ int32_t getStrongCount() const;
+
+ class weakref_type
+ {
+ public:
+ RefBase* refBase() const;
+
+ void incWeak(const void* id);
+ void decWeak(const void* id);
+
+ // acquires a strong reference if there is already one.
+ bool attemptIncStrong(const void* id);
+
+ // acquires a weak reference if there is already one.
+ // This is not always safe. see ProcessState.cpp and BpBinder.cpp
+ // for proper use.
+ bool attemptIncWeak(const void* id);
+
+ //! DEBUGGING ONLY: Get current weak ref count.
+ int32_t getWeakCount() const;
+
+ //! DEBUGGING ONLY: Print references held on object.
+ void printRefs() const;
+
+ //! DEBUGGING ONLY: Enable tracking for this object.
+ // enable -- enable/disable tracking
+ // retain -- when tracking is enable, if true, then we save a stack trace
+ // for each reference and dereference; when retain == false, we
+ // match up references and dereferences and keep only the
+ // outstanding ones.
+
+ void trackMe(bool enable, bool retain);
+ };
+
+ weakref_type* createWeak(const void* id) const;
+
+ weakref_type* getWeakRefs() const;
+
+ //! DEBUGGING ONLY: Print references held on object.
+ inline void printRefs() const { getWeakRefs()->printRefs(); }
+
+ //! DEBUGGING ONLY: Enable tracking of object.
+ inline void trackMe(bool enable, bool retain)
+ {
+ getWeakRefs()->trackMe(enable, retain);
+ }
+
+ typedef RefBase basetype;
+
+protected:
+ RefBase();
+ virtual ~RefBase();
+
+ //! Flags for extendObjectLifetime()
+ enum {
+ OBJECT_LIFETIME_STRONG = 0x0000,
+ OBJECT_LIFETIME_WEAK = 0x0001,
+ OBJECT_LIFETIME_MASK = 0x0001
+ };
+
+ void extendObjectLifetime(int32_t mode);
+
+ //! Flags for onIncStrongAttempted()
+ enum {
+ FIRST_INC_STRONG = 0x0001
+ };
+
+ virtual void onFirstRef();
+ virtual void onLastStrongRef(const void* id);
+ virtual bool onIncStrongAttempted(uint32_t flags, const void* id);
+ virtual void onLastWeakRef(const void* id);
+
+private:
+ friend class weakref_type;
+ class weakref_impl;
+
+ RefBase(const RefBase& o);
+ RefBase& operator=(const RefBase& o);
+
+private:
+ friend class ReferenceMover;
+
+ static void renameRefs(size_t n, const ReferenceRenamer& renamer);
+
+ static void renameRefId(weakref_type* ref,
+ const void* old_id, const void* new_id);
+
+ static void renameRefId(RefBase* ref,
+ const void* old_id, const void* new_id);
+
+ weakref_impl* const mRefs;
+};
+
+// ---------------------------------------------------------------------------
+
+template <class T>
+class LightRefBase
+{
+public:
+ inline LightRefBase() : mCount(0) { }
+ inline void incStrong(__attribute__((unused)) const void* id) const {
+ android_atomic_inc(&mCount);
+ }
+ inline void decStrong(__attribute__((unused)) const void* id) const {
+ if (android_atomic_dec(&mCount) == 1) {
+ delete static_cast<const T*>(this);
+ }
+ }
+ //! DEBUGGING ONLY: Get current strong ref count.
+ inline int32_t getStrongCount() const {
+ return mCount;
+ }
+
+ typedef LightRefBase<T> basetype;
+
+protected:
+ inline ~LightRefBase() { }
+
+private:
+ friend class ReferenceMover;
+ inline static void renameRefs(size_t n, const ReferenceRenamer& renamer) { }
+ inline static void renameRefId(T* ref,
+ const void* old_id, const void* new_id) { }
+
+private:
+ mutable volatile int32_t mCount;
+};
+
+// This is a wrapper around LightRefBase that simply enforces a virtual
+// destructor to eliminate the template requirement of LightRefBase
+class VirtualLightRefBase : public LightRefBase<VirtualLightRefBase> {
+public:
+ virtual ~VirtualLightRefBase() {}
+};
+
+// ---------------------------------------------------------------------------
+
+template <typename T>
+class wp
+{
+public:
+ typedef typename RefBase::weakref_type weakref_type;
+
+ inline wp() : m_ptr(0) { }
+
+ wp(T* other);
+ wp(const wp<T>& other);
+ wp(const sp<T>& other);
+ template<typename U> wp(U* other);
+ template<typename U> wp(const sp<U>& other);
+ template<typename U> wp(const wp<U>& other);
+
+ ~wp();
+
+ // Assignment
+
+ wp& operator = (T* other);
+ wp& operator = (const wp<T>& other);
+ wp& operator = (const sp<T>& other);
+
+ template<typename U> wp& operator = (U* other);
+ template<typename U> wp& operator = (const wp<U>& other);
+ template<typename U> wp& operator = (const sp<U>& other);
+
+ void set_object_and_refs(T* other, weakref_type* refs);
+
+ // promotion to sp
+
+ sp<T> promote() const;
+
+ // Reset
+
+ void clear();
+
+ // Accessors
+
+ inline weakref_type* get_refs() const { return m_refs; }
+
+ inline T* unsafe_get() const { return m_ptr; }
+
+ // Operators
+
+ COMPARE_WEAK(==)
+ COMPARE_WEAK(!=)
+ COMPARE_WEAK(>)
+ COMPARE_WEAK(<)
+ COMPARE_WEAK(<=)
+ COMPARE_WEAK(>=)
+
+ inline bool operator == (const wp<T>& o) const {
+ return (m_ptr == o.m_ptr) && (m_refs == o.m_refs);
+ }
+ template<typename U>
+ inline bool operator == (const wp<U>& o) const {
+ return m_ptr == o.m_ptr;
+ }
+
+ inline bool operator > (const wp<T>& o) const {
+ return (m_ptr == o.m_ptr) ? (m_refs > o.m_refs) : (m_ptr > o.m_ptr);
+ }
+ template<typename U>
+ inline bool operator > (const wp<U>& o) const {
+ return (m_ptr == o.m_ptr) ? (m_refs > o.m_refs) : (m_ptr > o.m_ptr);
+ }
+
+ inline bool operator < (const wp<T>& o) const {
+ return (m_ptr == o.m_ptr) ? (m_refs < o.m_refs) : (m_ptr < o.m_ptr);
+ }
+ template<typename U>
+ inline bool operator < (const wp<U>& o) const {
+ return (m_ptr == o.m_ptr) ? (m_refs < o.m_refs) : (m_ptr < o.m_ptr);
+ }
+ inline bool operator != (const wp<T>& o) const { return m_refs != o.m_refs; }
+ template<typename U> inline bool operator != (const wp<U>& o) const { return !operator == (o); }
+ inline bool operator <= (const wp<T>& o) const { return !operator > (o); }
+ template<typename U> inline bool operator <= (const wp<U>& o) const { return !operator > (o); }
+ inline bool operator >= (const wp<T>& o) const { return !operator < (o); }
+ template<typename U> inline bool operator >= (const wp<U>& o) const { return !operator < (o); }
+
+private:
+ template<typename Y> friend class sp;
+ template<typename Y> friend class wp;
+
+ T* m_ptr;
+ weakref_type* m_refs;
+};
+
+template <typename T>
+TextOutput& operator<<(TextOutput& to, const wp<T>& val);
+
+#undef COMPARE_WEAK
+
+// ---------------------------------------------------------------------------
+// No user serviceable parts below here.
+
+template<typename T>
+wp<T>::wp(T* other)
+ : m_ptr(other)
+{
+ if (other) m_refs = other->createWeak(this);
+}
+
+template<typename T>
+wp<T>::wp(const wp<T>& other)
+ : m_ptr(other.m_ptr), m_refs(other.m_refs)
+{
+ if (m_ptr) m_refs->incWeak(this);
+}
+
+template<typename T>
+wp<T>::wp(const sp<T>& other)
+ : m_ptr(other.m_ptr)
+{
+ if (m_ptr) {
+ m_refs = m_ptr->createWeak(this);
+ }
+}
+
+template<typename T> template<typename U>
+wp<T>::wp(U* other)
+ : m_ptr(other)
+{
+ if (other) m_refs = other->createWeak(this);
+}
+
+template<typename T> template<typename U>
+wp<T>::wp(const wp<U>& other)
+ : m_ptr(other.m_ptr)
+{
+ if (m_ptr) {
+ m_refs = other.m_refs;
+ m_refs->incWeak(this);
+ }
+}
+
+template<typename T> template<typename U>
+wp<T>::wp(const sp<U>& other)
+ : m_ptr(other.m_ptr)
+{
+ if (m_ptr) {
+ m_refs = m_ptr->createWeak(this);
+ }
+}
+
+template<typename T>
+wp<T>::~wp()
+{
+ if (m_ptr) m_refs->decWeak(this);
+}
+
+template<typename T>
+wp<T>& wp<T>::operator = (T* other)
+{
+ weakref_type* newRefs =
+ other ? other->createWeak(this) : 0;
+ if (m_ptr) m_refs->decWeak(this);
+ m_ptr = other;
+ m_refs = newRefs;
+ return *this;
+}
+
+template<typename T>
+wp<T>& wp<T>::operator = (const wp<T>& other)
+{
+ weakref_type* otherRefs(other.m_refs);
+ T* otherPtr(other.m_ptr);
+ if (otherPtr) otherRefs->incWeak(this);
+ if (m_ptr) m_refs->decWeak(this);
+ m_ptr = otherPtr;
+ m_refs = otherRefs;
+ return *this;
+}
+
+template<typename T>
+wp<T>& wp<T>::operator = (const sp<T>& other)
+{
+ weakref_type* newRefs =
+ other != NULL ? other->createWeak(this) : 0;
+ T* otherPtr(other.m_ptr);
+ if (m_ptr) m_refs->decWeak(this);
+ m_ptr = otherPtr;
+ m_refs = newRefs;
+ return *this;
+}
+
+template<typename T> template<typename U>
+wp<T>& wp<T>::operator = (U* other)
+{
+ weakref_type* newRefs =
+ other ? other->createWeak(this) : 0;
+ if (m_ptr) m_refs->decWeak(this);
+ m_ptr = other;
+ m_refs = newRefs;
+ return *this;
+}
+
+template<typename T> template<typename U>
+wp<T>& wp<T>::operator = (const wp<U>& other)
+{
+ weakref_type* otherRefs(other.m_refs);
+ U* otherPtr(other.m_ptr);
+ if (otherPtr) otherRefs->incWeak(this);
+ if (m_ptr) m_refs->decWeak(this);
+ m_ptr = otherPtr;
+ m_refs = otherRefs;
+ return *this;
+}
+
+template<typename T> template<typename U>
+wp<T>& wp<T>::operator = (const sp<U>& other)
+{
+ weakref_type* newRefs =
+ other != NULL ? other->createWeak(this) : 0;
+ U* otherPtr(other.m_ptr);
+ if (m_ptr) m_refs->decWeak(this);
+ m_ptr = otherPtr;
+ m_refs = newRefs;
+ return *this;
+}
+
+template<typename T>
+void wp<T>::set_object_and_refs(T* other, weakref_type* refs)
+{
+ if (other) refs->incWeak(this);
+ if (m_ptr) m_refs->decWeak(this);
+ m_ptr = other;
+ m_refs = refs;
+}
+
+template<typename T>
+sp<T> wp<T>::promote() const
+{
+ sp<T> result;
+ if (m_ptr && m_refs->attemptIncStrong(&result)) {
+ result.set_pointer(m_ptr);
+ }
+ return result;
+}
+
+template<typename T>
+void wp<T>::clear()
+{
+ if (m_ptr) {
+ m_refs->decWeak(this);
+ m_ptr = 0;
+ }
+}
+
+template <typename T>
+inline TextOutput& operator<<(TextOutput& to, const wp<T>& val)
+{
+ return printWeakPointer(to, val.unsafe_get());
+}
+
+// ---------------------------------------------------------------------------
+
+// this class just serves as a namespace so TYPE::moveReferences can stay
+// private.
+class ReferenceMover {
+public:
+ // it would be nice if we could make sure no extra code is generated
+ // for sp<TYPE> or wp<TYPE> when TYPE is a descendant of RefBase:
+ // Using a sp<RefBase> override doesn't work; it's a bit like we wanted
+ // a template<typename TYPE inherits RefBase> template...
+
+ template<typename TYPE> static inline
+ void move_references(sp<TYPE>* d, sp<TYPE> const* s, size_t n) {
+
+ class Renamer : public ReferenceRenamer {
+ sp<TYPE>* d;
+ sp<TYPE> const* s;
+ virtual void operator()(size_t i) const {
+ // The id are known to be the sp<>'s this pointer
+ TYPE::renameRefId(d[i].get(), &s[i], &d[i]);
+ }
+ public:
+ Renamer(sp<TYPE>* d, sp<TYPE> const* s) : d(d), s(s) { }
+ virtual ~Renamer() { }
+ };
+
+ memmove(d, s, n*sizeof(sp<TYPE>));
+ TYPE::renameRefs(n, Renamer(d, s));
+ }
+
+
+ template<typename TYPE> static inline
+ void move_references(wp<TYPE>* d, wp<TYPE> const* s, size_t n) {
+
+ class Renamer : public ReferenceRenamer {
+ wp<TYPE>* d;
+ wp<TYPE> const* s;
+ virtual void operator()(size_t i) const {
+ // The id are known to be the wp<>'s this pointer
+ TYPE::renameRefId(d[i].get_refs(), &s[i], &d[i]);
+ }
+ public:
+ Renamer(wp<TYPE>* d, wp<TYPE> const* s) : d(d), s(s) { }
+ virtual ~Renamer() { }
+ };
+
+ memmove(d, s, n*sizeof(wp<TYPE>));
+ TYPE::renameRefs(n, Renamer(d, s));
+ }
+};
+
+// specialization for moving sp<> and wp<> types.
+// these are used by the [Sorted|Keyed]Vector<> implementations
+// sp<> and wp<> need to be handled specially, because they do not
+// have trivial copy operation in the general case (see RefBase.cpp
+// when DEBUG ops are enabled), but can be implemented very
+// efficiently in most cases.
+
+template<typename TYPE> inline
+void move_forward_type(sp<TYPE>* d, sp<TYPE> const* s, size_t n) {
+ ReferenceMover::move_references(d, s, n);
+}
+
+template<typename TYPE> inline
+void move_backward_type(sp<TYPE>* d, sp<TYPE> const* s, size_t n) {
+ ReferenceMover::move_references(d, s, n);
+}
+
+template<typename TYPE> inline
+void move_forward_type(wp<TYPE>* d, wp<TYPE> const* s, size_t n) {
+ ReferenceMover::move_references(d, s, n);
+}
+
+template<typename TYPE> inline
+void move_backward_type(wp<TYPE>* d, wp<TYPE> const* s, size_t n) {
+ ReferenceMover::move_references(d, s, n);
+}
+
+
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_REF_BASE_H
diff --git a/include/utils/SharedBuffer.h b/include/utils/SharedBuffer.h
new file mode 100644
index 0000000..b670953
--- /dev/null
+++ b/include/utils/SharedBuffer.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SHARED_BUFFER_H
+#define ANDROID_SHARED_BUFFER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+class SharedBuffer
+{
+public:
+
+ /* flags to use with release() */
+ enum {
+ eKeepStorage = 0x00000001
+ };
+
+ /*! allocate a buffer of size 'size' and acquire() it.
+ * call release() to free it.
+ */
+ static SharedBuffer* alloc(size_t size);
+
+ /*! free the memory associated with the SharedBuffer.
+ * Fails if there are any users associated with this SharedBuffer.
+ * In other words, the buffer must have been release by all its
+ * users.
+ */
+ static ssize_t dealloc(const SharedBuffer* released);
+
+ //! access the data for read
+ inline const void* data() const;
+
+ //! access the data for read/write
+ inline void* data();
+
+ //! get size of the buffer
+ inline size_t size() const;
+
+ //! get back a SharedBuffer object from its data
+ static inline SharedBuffer* bufferFromData(void* data);
+
+ //! get back a SharedBuffer object from its data
+ static inline const SharedBuffer* bufferFromData(const void* data);
+
+ //! get the size of a SharedBuffer object from its data
+ static inline size_t sizeFromData(const void* data);
+
+ //! edit the buffer (get a writtable, or non-const, version of it)
+ SharedBuffer* edit() const;
+
+ //! edit the buffer, resizing if needed
+ SharedBuffer* editResize(size_t size) const;
+
+ //! like edit() but fails if a copy is required
+ SharedBuffer* attemptEdit() const;
+
+ //! resize and edit the buffer, loose it's content.
+ SharedBuffer* reset(size_t size) const;
+
+ //! acquire/release a reference on this buffer
+ void acquire() const;
+
+ /*! release a reference on this buffer, with the option of not
+ * freeing the memory associated with it if it was the last reference
+ * returns the previous reference count
+ */
+ int32_t release(uint32_t flags = 0) const;
+
+ //! returns wether or not we're the only owner
+ inline bool onlyOwner() const;
+
+
+private:
+ inline SharedBuffer() { }
+ inline ~SharedBuffer() { }
+ SharedBuffer(const SharedBuffer&);
+ SharedBuffer& operator = (const SharedBuffer&);
+
+ // 16 bytes. must be sized to preserve correct alignment.
+ mutable int32_t mRefs;
+ size_t mSize;
+ uint32_t mReserved[2];
+};
+
+// ---------------------------------------------------------------------------
+
+const void* SharedBuffer::data() const {
+ return this + 1;
+}
+
+void* SharedBuffer::data() {
+ return this + 1;
+}
+
+size_t SharedBuffer::size() const {
+ return mSize;
+}
+
+SharedBuffer* SharedBuffer::bufferFromData(void* data) {
+ return data ? static_cast<SharedBuffer *>(data)-1 : 0;
+}
+
+const SharedBuffer* SharedBuffer::bufferFromData(const void* data) {
+ return data ? static_cast<const SharedBuffer *>(data)-1 : 0;
+}
+
+size_t SharedBuffer::sizeFromData(const void* data) {
+ return data ? bufferFromData(data)->mSize : 0;
+}
+
+bool SharedBuffer::onlyOwner() const {
+ return (mRefs == 1);
+}
+
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_VECTOR_H
diff --git a/include/utils/Singleton.h b/include/utils/Singleton.h
new file mode 100644
index 0000000..ffc03cb
--- /dev/null
+++ b/include/utils/Singleton.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_UTILS_SINGLETON_H
+#define ANDROID_UTILS_SINGLETON_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <utils/threads.h>
+#include <cutils/compiler.h>
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+template <typename TYPE>
+class ANDROID_API Singleton
+{
+public:
+ static TYPE& getInstance() {
+ Mutex::Autolock _l(sLock);
+ TYPE* instance = sInstance;
+ if (instance == 0) {
+ instance = new TYPE();
+ sInstance = instance;
+ }
+ return *instance;
+ }
+
+ static bool hasInstance() {
+ Mutex::Autolock _l(sLock);
+ return sInstance != 0;
+ }
+
+protected:
+ ~Singleton() { };
+ Singleton() { };
+
+private:
+ Singleton(const Singleton&);
+ Singleton& operator = (const Singleton&);
+ static Mutex sLock;
+ static TYPE* sInstance;
+};
+
+/*
+ * use ANDROID_SINGLETON_STATIC_INSTANCE(TYPE) in your implementation file
+ * (eg: <TYPE>.cpp) to create the static instance of Singleton<>'s attributes,
+ * and avoid to have a copy of them in each compilation units Singleton<TYPE>
+ * is used.
+ * NOTE: we use a version of Mutex ctor that takes a parameter, because
+ * for some unknown reason using the default ctor doesn't emit the variable!
+ */
+
+#define ANDROID_SINGLETON_STATIC_INSTANCE(TYPE) \
+ template<> ::android::Mutex \
+ (::android::Singleton< TYPE >::sLock)(::android::Mutex::PRIVATE); \
+ template<> TYPE* ::android::Singleton< TYPE >::sInstance(0); \
+ template class ::android::Singleton< TYPE >;
+
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_UTILS_SINGLETON_H
+
diff --git a/include/utils/SortedVector.h b/include/utils/SortedVector.h
new file mode 100644
index 0000000..2d3e82a
--- /dev/null
+++ b/include/utils/SortedVector.h
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SORTED_VECTOR_H
+#define ANDROID_SORTED_VECTOR_H
+
+#include <assert.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <cutils/log.h>
+
+#include <utils/Vector.h>
+#include <utils/VectorImpl.h>
+#include <utils/TypeHelpers.h>
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+template <class TYPE>
+class SortedVector : private SortedVectorImpl
+{
+ friend class Vector<TYPE>;
+
+public:
+ typedef TYPE value_type;
+
+ /*!
+ * Constructors and destructors
+ */
+
+ SortedVector();
+ SortedVector(const SortedVector<TYPE>& rhs);
+ virtual ~SortedVector();
+
+ /*! copy operator */
+ const SortedVector<TYPE>& operator = (const SortedVector<TYPE>& rhs) const;
+ SortedVector<TYPE>& operator = (const SortedVector<TYPE>& rhs);
+
+ /*
+ * empty the vector
+ */
+
+ inline void clear() { VectorImpl::clear(); }
+
+ /*!
+ * vector stats
+ */
+
+ //! returns number of items in the vector
+ inline size_t size() const { return VectorImpl::size(); }
+ //! returns whether or not the vector is empty
+ inline bool isEmpty() const { return VectorImpl::isEmpty(); }
+ //! returns how many items can be stored without reallocating the backing store
+ inline size_t capacity() const { return VectorImpl::capacity(); }
+ //! sets the capacity. capacity can never be reduced less than size()
+ inline ssize_t setCapacity(size_t size) { return VectorImpl::setCapacity(size); }
+
+ /*!
+ * C-style array access
+ */
+
+ //! read-only C-style access
+ inline const TYPE* array() const;
+
+ //! read-write C-style access. BE VERY CAREFUL when modifying the array
+ //! you must keep it sorted! You usually don't use this function.
+ TYPE* editArray();
+
+ //! finds the index of an item
+ ssize_t indexOf(const TYPE& item) const;
+
+ //! finds where this item should be inserted
+ size_t orderOf(const TYPE& item) const;
+
+
+ /*!
+ * accessors
+ */
+
+ //! read-only access to an item at a given index
+ inline const TYPE& operator [] (size_t index) const;
+ //! alternate name for operator []
+ inline const TYPE& itemAt(size_t index) const;
+ //! stack-usage of the vector. returns the top of the stack (last element)
+ const TYPE& top() const;
+
+ /*!
+ * modifying the array
+ */
+
+ //! add an item in the right place (and replace the one that is there)
+ ssize_t add(const TYPE& item);
+
+ //! editItemAt() MUST NOT change the order of this item
+ TYPE& editItemAt(size_t index) {
+ return *( static_cast<TYPE *>(VectorImpl::editItemLocation(index)) );
+ }
+
+ //! merges a vector into this one
+ ssize_t merge(const Vector<TYPE>& vector);
+ ssize_t merge(const SortedVector<TYPE>& vector);
+
+ //! removes an item
+ ssize_t remove(const TYPE&);
+
+ //! remove several items
+ inline ssize_t removeItemsAt(size_t index, size_t count = 1);
+ //! remove one item
+ inline ssize_t removeAt(size_t index) { return removeItemsAt(index); }
+
+protected:
+ virtual void do_construct(void* storage, size_t num) const;
+ virtual void do_destroy(void* storage, size_t num) const;
+ virtual void do_copy(void* dest, const void* from, size_t num) const;
+ virtual void do_splat(void* dest, const void* item, size_t num) const;
+ virtual void do_move_forward(void* dest, const void* from, size_t num) const;
+ virtual void do_move_backward(void* dest, const void* from, size_t num) const;
+ virtual int do_compare(const void* lhs, const void* rhs) const;
+};
+
+// SortedVector<T> can be trivially moved using memcpy() because moving does not
+// require any change to the underlying SharedBuffer contents or reference count.
+template<typename T> struct trait_trivial_move<SortedVector<T> > { enum { value = true }; };
+
+// ---------------------------------------------------------------------------
+// No user serviceable parts from here...
+// ---------------------------------------------------------------------------
+
+template<class TYPE> inline
+SortedVector<TYPE>::SortedVector()
+ : SortedVectorImpl(sizeof(TYPE),
+ ((traits<TYPE>::has_trivial_ctor ? HAS_TRIVIAL_CTOR : 0)
+ |(traits<TYPE>::has_trivial_dtor ? HAS_TRIVIAL_DTOR : 0)
+ |(traits<TYPE>::has_trivial_copy ? HAS_TRIVIAL_COPY : 0))
+ )
+{
+}
+
+template<class TYPE> inline
+SortedVector<TYPE>::SortedVector(const SortedVector<TYPE>& rhs)
+ : SortedVectorImpl(rhs) {
+}
+
+template<class TYPE> inline
+SortedVector<TYPE>::~SortedVector() {
+ finish_vector();
+}
+
+template<class TYPE> inline
+SortedVector<TYPE>& SortedVector<TYPE>::operator = (const SortedVector<TYPE>& rhs) {
+ SortedVectorImpl::operator = (rhs);
+ return *this;
+}
+
+template<class TYPE> inline
+const SortedVector<TYPE>& SortedVector<TYPE>::operator = (const SortedVector<TYPE>& rhs) const {
+ SortedVectorImpl::operator = (rhs);
+ return *this;
+}
+
+template<class TYPE> inline
+const TYPE* SortedVector<TYPE>::array() const {
+ return static_cast<const TYPE *>(arrayImpl());
+}
+
+template<class TYPE> inline
+TYPE* SortedVector<TYPE>::editArray() {
+ return static_cast<TYPE *>(editArrayImpl());
+}
+
+
+template<class TYPE> inline
+const TYPE& SortedVector<TYPE>::operator[](size_t index) const {
+ LOG_FATAL_IF(index>=size(),
+ "%s: index=%u out of range (%u)", __PRETTY_FUNCTION__,
+ int(index), int(size()));
+ return *(array() + index);
+}
+
+template<class TYPE> inline
+const TYPE& SortedVector<TYPE>::itemAt(size_t index) const {
+ return operator[](index);
+}
+
+template<class TYPE> inline
+const TYPE& SortedVector<TYPE>::top() const {
+ return *(array() + size() - 1);
+}
+
+template<class TYPE> inline
+ssize_t SortedVector<TYPE>::add(const TYPE& item) {
+ return SortedVectorImpl::add(&item);
+}
+
+template<class TYPE> inline
+ssize_t SortedVector<TYPE>::indexOf(const TYPE& item) const {
+ return SortedVectorImpl::indexOf(&item);
+}
+
+template<class TYPE> inline
+size_t SortedVector<TYPE>::orderOf(const TYPE& item) const {
+ return SortedVectorImpl::orderOf(&item);
+}
+
+template<class TYPE> inline
+ssize_t SortedVector<TYPE>::merge(const Vector<TYPE>& vector) {
+ return SortedVectorImpl::merge(reinterpret_cast<const VectorImpl&>(vector));
+}
+
+template<class TYPE> inline
+ssize_t SortedVector<TYPE>::merge(const SortedVector<TYPE>& vector) {
+ return SortedVectorImpl::merge(reinterpret_cast<const SortedVectorImpl&>(vector));
+}
+
+template<class TYPE> inline
+ssize_t SortedVector<TYPE>::remove(const TYPE& item) {
+ return SortedVectorImpl::remove(&item);
+}
+
+template<class TYPE> inline
+ssize_t SortedVector<TYPE>::removeItemsAt(size_t index, size_t count) {
+ return VectorImpl::removeItemsAt(index, count);
+}
+
+// ---------------------------------------------------------------------------
+
+template<class TYPE>
+void SortedVector<TYPE>::do_construct(void* storage, size_t num) const {
+ construct_type( reinterpret_cast<TYPE*>(storage), num );
+}
+
+template<class TYPE>
+void SortedVector<TYPE>::do_destroy(void* storage, size_t num) const {
+ destroy_type( reinterpret_cast<TYPE*>(storage), num );
+}
+
+template<class TYPE>
+void SortedVector<TYPE>::do_copy(void* dest, const void* from, size_t num) const {
+ copy_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
+}
+
+template<class TYPE>
+void SortedVector<TYPE>::do_splat(void* dest, const void* item, size_t num) const {
+ splat_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(item), num );
+}
+
+template<class TYPE>
+void SortedVector<TYPE>::do_move_forward(void* dest, const void* from, size_t num) const {
+ move_forward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
+}
+
+template<class TYPE>
+void SortedVector<TYPE>::do_move_backward(void* dest, const void* from, size_t num) const {
+ move_backward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
+}
+
+template<class TYPE>
+int SortedVector<TYPE>::do_compare(const void* lhs, const void* rhs) const {
+ return compare_type( *reinterpret_cast<const TYPE*>(lhs), *reinterpret_cast<const TYPE*>(rhs) );
+}
+
+}; // namespace android
+
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_SORTED_VECTOR_H
diff --git a/include/utils/StopWatch.h b/include/utils/StopWatch.h
new file mode 100644
index 0000000..693dd3c
--- /dev/null
+++ b/include/utils/StopWatch.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_STOPWATCH_H
+#define ANDROID_STOPWATCH_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Timers.h>
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+class StopWatch
+{
+public:
+ StopWatch( const char *name,
+ int clock = SYSTEM_TIME_MONOTONIC,
+ uint32_t flags = 0);
+ ~StopWatch();
+
+ const char* name() const;
+ nsecs_t lap();
+ nsecs_t elapsedTime() const;
+
+ void reset();
+
+private:
+ const char* mName;
+ int mClock;
+ uint32_t mFlags;
+
+ struct lap_t {
+ nsecs_t soFar;
+ nsecs_t thisLap;
+ };
+
+ nsecs_t mStartTime;
+ lap_t mLaps[8];
+ int mNumLaps;
+};
+
+
+}; // namespace android
+
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_STOPWATCH_H
diff --git a/include/utils/String16.h b/include/utils/String16.h
new file mode 100644
index 0000000..d131bfc
--- /dev/null
+++ b/include/utils/String16.h
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_STRING16_H
+#define ANDROID_STRING16_H
+
+#include <utils/Errors.h>
+#include <utils/SharedBuffer.h>
+#include <utils/Unicode.h>
+#include <utils/TypeHelpers.h>
+
+// ---------------------------------------------------------------------------
+
+extern "C" {
+
+}
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+class String8;
+class TextOutput;
+
+//! This is a string holding UTF-16 characters.
+class String16
+{
+public:
+ /* use String16(StaticLinkage) if you're statically linking against
+ * libutils and declaring an empty static String16, e.g.:
+ *
+ * static String16 sAStaticEmptyString(String16::kEmptyString);
+ * static String16 sAnotherStaticEmptyString(sAStaticEmptyString);
+ */
+ enum StaticLinkage { kEmptyString };
+
+ String16();
+ explicit String16(StaticLinkage);
+ String16(const String16& o);
+ String16(const String16& o,
+ size_t len,
+ size_t begin=0);
+ explicit String16(const char16_t* o);
+ explicit String16(const char16_t* o, size_t len);
+ explicit String16(const String8& o);
+ explicit String16(const char* o);
+ explicit String16(const char* o, size_t len);
+
+ ~String16();
+
+ inline const char16_t* string() const;
+ inline size_t size() const;
+
+ inline const SharedBuffer* sharedBuffer() const;
+
+ void setTo(const String16& other);
+ status_t setTo(const char16_t* other);
+ status_t setTo(const char16_t* other, size_t len);
+ status_t setTo(const String16& other,
+ size_t len,
+ size_t begin=0);
+
+ status_t append(const String16& other);
+ status_t append(const char16_t* other, size_t len);
+
+ inline String16& operator=(const String16& other);
+
+ inline String16& operator+=(const String16& other);
+ inline String16 operator+(const String16& other) const;
+
+ status_t insert(size_t pos, const char16_t* chrs);
+ status_t insert(size_t pos,
+ const char16_t* chrs, size_t len);
+
+ ssize_t findFirst(char16_t c) const;
+ ssize_t findLast(char16_t c) const;
+
+ bool startsWith(const String16& prefix) const;
+ bool startsWith(const char16_t* prefix) const;
+
+ status_t makeLower();
+
+ status_t replaceAll(char16_t replaceThis,
+ char16_t withThis);
+
+ status_t remove(size_t len, size_t begin=0);
+
+ inline int compare(const String16& other) const;
+
+ inline bool operator<(const String16& other) const;
+ inline bool operator<=(const String16& other) const;
+ inline bool operator==(const String16& other) const;
+ inline bool operator!=(const String16& other) const;
+ inline bool operator>=(const String16& other) const;
+ inline bool operator>(const String16& other) const;
+
+ inline bool operator<(const char16_t* other) const;
+ inline bool operator<=(const char16_t* other) const;
+ inline bool operator==(const char16_t* other) const;
+ inline bool operator!=(const char16_t* other) const;
+ inline bool operator>=(const char16_t* other) const;
+ inline bool operator>(const char16_t* other) const;
+
+ inline operator const char16_t*() const;
+
+private:
+ const char16_t* mString;
+};
+
+// String16 can be trivially moved using memcpy() because moving does not
+// require any change to the underlying SharedBuffer contents or reference count.
+ANDROID_TRIVIAL_MOVE_TRAIT(String16)
+
+// ---------------------------------------------------------------------------
+// No user servicable parts below.
+
+inline int compare_type(const String16& lhs, const String16& rhs)
+{
+ return lhs.compare(rhs);
+}
+
+inline int strictly_order_type(const String16& lhs, const String16& rhs)
+{
+ return compare_type(lhs, rhs) < 0;
+}
+
+inline const char16_t* String16::string() const
+{
+ return mString;
+}
+
+inline size_t String16::size() const
+{
+ return SharedBuffer::sizeFromData(mString)/sizeof(char16_t)-1;
+}
+
+inline const SharedBuffer* String16::sharedBuffer() const
+{
+ return SharedBuffer::bufferFromData(mString);
+}
+
+inline String16& String16::operator=(const String16& other)
+{
+ setTo(other);
+ return *this;
+}
+
+inline String16& String16::operator+=(const String16& other)
+{
+ append(other);
+ return *this;
+}
+
+inline String16 String16::operator+(const String16& other) const
+{
+ String16 tmp(*this);
+ tmp += other;
+ return tmp;
+}
+
+inline int String16::compare(const String16& other) const
+{
+ return strzcmp16(mString, size(), other.mString, other.size());
+}
+
+inline bool String16::operator<(const String16& other) const
+{
+ return strzcmp16(mString, size(), other.mString, other.size()) < 0;
+}
+
+inline bool String16::operator<=(const String16& other) const
+{
+ return strzcmp16(mString, size(), other.mString, other.size()) <= 0;
+}
+
+inline bool String16::operator==(const String16& other) const
+{
+ return strzcmp16(mString, size(), other.mString, other.size()) == 0;
+}
+
+inline bool String16::operator!=(const String16& other) const
+{
+ return strzcmp16(mString, size(), other.mString, other.size()) != 0;
+}
+
+inline bool String16::operator>=(const String16& other) const
+{
+ return strzcmp16(mString, size(), other.mString, other.size()) >= 0;
+}
+
+inline bool String16::operator>(const String16& other) const
+{
+ return strzcmp16(mString, size(), other.mString, other.size()) > 0;
+}
+
+inline bool String16::operator<(const char16_t* other) const
+{
+ return strcmp16(mString, other) < 0;
+}
+
+inline bool String16::operator<=(const char16_t* other) const
+{
+ return strcmp16(mString, other) <= 0;
+}
+
+inline bool String16::operator==(const char16_t* other) const
+{
+ return strcmp16(mString, other) == 0;
+}
+
+inline bool String16::operator!=(const char16_t* other) const
+{
+ return strcmp16(mString, other) != 0;
+}
+
+inline bool String16::operator>=(const char16_t* other) const
+{
+ return strcmp16(mString, other) >= 0;
+}
+
+inline bool String16::operator>(const char16_t* other) const
+{
+ return strcmp16(mString, other) > 0;
+}
+
+inline String16::operator const char16_t*() const
+{
+ return mString;
+}
+
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_STRING16_H
diff --git a/include/utils/String8.h b/include/utils/String8.h
new file mode 100644
index 0000000..ecfcf10
--- /dev/null
+++ b/include/utils/String8.h
@@ -0,0 +1,408 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_STRING8_H
+#define ANDROID_STRING8_H
+
+#include <utils/Errors.h>
+#include <utils/SharedBuffer.h>
+#include <utils/Unicode.h>
+#include <utils/TypeHelpers.h>
+
+#include <string.h> // for strcmp
+#include <stdarg.h>
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+class String16;
+class TextOutput;
+
+//! This is a string holding UTF-8 characters. Does not allow the value more
+// than 0x10FFFF, which is not valid unicode codepoint.
+class String8
+{
+public:
+ /* use String8(StaticLinkage) if you're statically linking against
+ * libutils and declaring an empty static String8, e.g.:
+ *
+ * static String8 sAStaticEmptyString(String8::kEmptyString);
+ * static String8 sAnotherStaticEmptyString(sAStaticEmptyString);
+ */
+ enum StaticLinkage { kEmptyString };
+
+ String8();
+ explicit String8(StaticLinkage);
+ String8(const String8& o);
+ explicit String8(const char* o);
+ explicit String8(const char* o, size_t numChars);
+
+ explicit String8(const String16& o);
+ explicit String8(const char16_t* o);
+ explicit String8(const char16_t* o, size_t numChars);
+ explicit String8(const char32_t* o);
+ explicit String8(const char32_t* o, size_t numChars);
+ ~String8();
+
+ static inline const String8 empty();
+
+ static String8 format(const char* fmt, ...) __attribute__((format (printf, 1, 2)));
+ static String8 formatV(const char* fmt, va_list args);
+
+ inline const char* string() const;
+ inline size_t size() const;
+ inline size_t length() const;
+ inline size_t bytes() const;
+ inline bool isEmpty() const;
+
+ inline const SharedBuffer* sharedBuffer() const;
+
+ void clear();
+
+ void setTo(const String8& other);
+ status_t setTo(const char* other);
+ status_t setTo(const char* other, size_t numChars);
+ status_t setTo(const char16_t* other, size_t numChars);
+ status_t setTo(const char32_t* other,
+ size_t length);
+
+ status_t append(const String8& other);
+ status_t append(const char* other);
+ status_t append(const char* other, size_t numChars);
+
+ status_t appendFormat(const char* fmt, ...)
+ __attribute__((format (printf, 2, 3)));
+ status_t appendFormatV(const char* fmt, va_list args);
+
+ // Note that this function takes O(N) time to calculate the value.
+ // No cache value is stored.
+ size_t getUtf32Length() const;
+ int32_t getUtf32At(size_t index,
+ size_t *next_index) const;
+ void getUtf32(char32_t* dst) const;
+
+ inline String8& operator=(const String8& other);
+ inline String8& operator=(const char* other);
+
+ inline String8& operator+=(const String8& other);
+ inline String8 operator+(const String8& other) const;
+
+ inline String8& operator+=(const char* other);
+ inline String8 operator+(const char* other) const;
+
+ inline int compare(const String8& other) const;
+
+ inline bool operator<(const String8& other) const;
+ inline bool operator<=(const String8& other) const;
+ inline bool operator==(const String8& other) const;
+ inline bool operator!=(const String8& other) const;
+ inline bool operator>=(const String8& other) const;
+ inline bool operator>(const String8& other) const;
+
+ inline bool operator<(const char* other) const;
+ inline bool operator<=(const char* other) const;
+ inline bool operator==(const char* other) const;
+ inline bool operator!=(const char* other) const;
+ inline bool operator>=(const char* other) const;
+ inline bool operator>(const char* other) const;
+
+ inline operator const char*() const;
+
+ char* lockBuffer(size_t size);
+ void unlockBuffer();
+ status_t unlockBuffer(size_t size);
+
+ // return the index of the first byte of other in this at or after
+ // start, or -1 if not found
+ ssize_t find(const char* other, size_t start = 0) const;
+
+ // return true if this string contains the specified substring
+ inline bool contains(const char* other) const;
+
+ // removes all occurrence of the specified substring
+ // returns true if any were found and removed
+ bool removeAll(const char* other);
+
+ void toLower();
+ void toLower(size_t start, size_t numChars);
+ void toUpper();
+ void toUpper(size_t start, size_t numChars);
+
+
+ /*
+ * These methods operate on the string as if it were a path name.
+ */
+
+ /*
+ * Set the filename field to a specific value.
+ *
+ * Normalizes the filename, removing a trailing '/' if present.
+ */
+ void setPathName(const char* name);
+ void setPathName(const char* name, size_t numChars);
+
+ /*
+ * Get just the filename component.
+ *
+ * "/tmp/foo/bar.c" --> "bar.c"
+ */
+ String8 getPathLeaf(void) const;
+
+ /*
+ * Remove the last (file name) component, leaving just the directory
+ * name.
+ *
+ * "/tmp/foo/bar.c" --> "/tmp/foo"
+ * "/tmp" --> "" // ????? shouldn't this be "/" ???? XXX
+ * "bar.c" --> ""
+ */
+ String8 getPathDir(void) const;
+
+ /*
+ * Retrieve the front (root dir) component. Optionally also return the
+ * remaining components.
+ *
+ * "/tmp/foo/bar.c" --> "tmp" (remain = "foo/bar.c")
+ * "/tmp" --> "tmp" (remain = "")
+ * "bar.c" --> "bar.c" (remain = "")
+ */
+ String8 walkPath(String8* outRemains = NULL) const;
+
+ /*
+ * Return the filename extension. This is the last '.' and any number
+ * of characters that follow it. The '.' is included in case we
+ * decide to expand our definition of what constitutes an extension.
+ *
+ * "/tmp/foo/bar.c" --> ".c"
+ * "/tmp" --> ""
+ * "/tmp/foo.bar/baz" --> ""
+ * "foo.jpeg" --> ".jpeg"
+ * "foo." --> ""
+ */
+ String8 getPathExtension(void) const;
+
+ /*
+ * Return the path without the extension. Rules for what constitutes
+ * an extension are described in the comment for getPathExtension().
+ *
+ * "/tmp/foo/bar.c" --> "/tmp/foo/bar"
+ */
+ String8 getBasePath(void) const;
+
+ /*
+ * Add a component to the pathname. We guarantee that there is
+ * exactly one path separator between the old path and the new.
+ * If there is no existing name, we just copy the new name in.
+ *
+ * If leaf is a fully qualified path (i.e. starts with '/', it
+ * replaces whatever was there before.
+ */
+ String8& appendPath(const char* leaf);
+ String8& appendPath(const String8& leaf) { return appendPath(leaf.string()); }
+
+ /*
+ * Like appendPath(), but does not affect this string. Returns a new one instead.
+ */
+ String8 appendPathCopy(const char* leaf) const
+ { String8 p(*this); p.appendPath(leaf); return p; }
+ String8 appendPathCopy(const String8& leaf) const { return appendPathCopy(leaf.string()); }
+
+ /*
+ * Converts all separators in this string to /, the default path separator.
+ *
+ * If the default OS separator is backslash, this converts all
+ * backslashes to slashes, in-place. Otherwise it does nothing.
+ * Returns self.
+ */
+ String8& convertToResPath();
+
+private:
+ status_t real_append(const char* other, size_t numChars);
+ char* find_extension(void) const;
+
+ const char* mString;
+};
+
+// String8 can be trivially moved using memcpy() because moving does not
+// require any change to the underlying SharedBuffer contents or reference count.
+ANDROID_TRIVIAL_MOVE_TRAIT(String8)
+
+// ---------------------------------------------------------------------------
+// No user servicable parts below.
+
+inline int compare_type(const String8& lhs, const String8& rhs)
+{
+ return lhs.compare(rhs);
+}
+
+inline int strictly_order_type(const String8& lhs, const String8& rhs)
+{
+ return compare_type(lhs, rhs) < 0;
+}
+
+inline const String8 String8::empty() {
+ return String8();
+}
+
+inline const char* String8::string() const
+{
+ return mString;
+}
+
+inline size_t String8::length() const
+{
+ return SharedBuffer::sizeFromData(mString)-1;
+}
+
+inline size_t String8::size() const
+{
+ return length();
+}
+
+inline bool String8::isEmpty() const
+{
+ return length() == 0;
+}
+
+inline size_t String8::bytes() const
+{
+ return SharedBuffer::sizeFromData(mString)-1;
+}
+
+inline const SharedBuffer* String8::sharedBuffer() const
+{
+ return SharedBuffer::bufferFromData(mString);
+}
+
+inline bool String8::contains(const char* other) const
+{
+ return find(other) >= 0;
+}
+
+inline String8& String8::operator=(const String8& other)
+{
+ setTo(other);
+ return *this;
+}
+
+inline String8& String8::operator=(const char* other)
+{
+ setTo(other);
+ return *this;
+}
+
+inline String8& String8::operator+=(const String8& other)
+{
+ append(other);
+ return *this;
+}
+
+inline String8 String8::operator+(const String8& other) const
+{
+ String8 tmp(*this);
+ tmp += other;
+ return tmp;
+}
+
+inline String8& String8::operator+=(const char* other)
+{
+ append(other);
+ return *this;
+}
+
+inline String8 String8::operator+(const char* other) const
+{
+ String8 tmp(*this);
+ tmp += other;
+ return tmp;
+}
+
+inline int String8::compare(const String8& other) const
+{
+ return strcmp(mString, other.mString);
+}
+
+inline bool String8::operator<(const String8& other) const
+{
+ return strcmp(mString, other.mString) < 0;
+}
+
+inline bool String8::operator<=(const String8& other) const
+{
+ return strcmp(mString, other.mString) <= 0;
+}
+
+inline bool String8::operator==(const String8& other) const
+{
+ return strcmp(mString, other.mString) == 0;
+}
+
+inline bool String8::operator!=(const String8& other) const
+{
+ return strcmp(mString, other.mString) != 0;
+}
+
+inline bool String8::operator>=(const String8& other) const
+{
+ return strcmp(mString, other.mString) >= 0;
+}
+
+inline bool String8::operator>(const String8& other) const
+{
+ return strcmp(mString, other.mString) > 0;
+}
+
+inline bool String8::operator<(const char* other) const
+{
+ return strcmp(mString, other) < 0;
+}
+
+inline bool String8::operator<=(const char* other) const
+{
+ return strcmp(mString, other) <= 0;
+}
+
+inline bool String8::operator==(const char* other) const
+{
+ return strcmp(mString, other) == 0;
+}
+
+inline bool String8::operator!=(const char* other) const
+{
+ return strcmp(mString, other) != 0;
+}
+
+inline bool String8::operator>=(const char* other) const
+{
+ return strcmp(mString, other) >= 0;
+}
+
+inline bool String8::operator>(const char* other) const
+{
+ return strcmp(mString, other) > 0;
+}
+
+inline String8::operator const char*() const
+{
+ return mString;
+}
+
+} // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_STRING8_H
diff --git a/include/utils/StrongPointer.h b/include/utils/StrongPointer.h
new file mode 100644
index 0000000..aba9577
--- /dev/null
+++ b/include/utils/StrongPointer.h
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_STRONG_POINTER_H
+#define ANDROID_STRONG_POINTER_H
+
+#include <cutils/atomic.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <stdlib.h>
+
+// ---------------------------------------------------------------------------
+namespace android {
+
+template<typename T> class wp;
+
+// ---------------------------------------------------------------------------
+
+#define COMPARE(_op_) \
+inline bool operator _op_ (const sp<T>& o) const { \
+ return m_ptr _op_ o.m_ptr; \
+} \
+inline bool operator _op_ (const T* o) const { \
+ return m_ptr _op_ o; \
+} \
+template<typename U> \
+inline bool operator _op_ (const sp<U>& o) const { \
+ return m_ptr _op_ o.m_ptr; \
+} \
+template<typename U> \
+inline bool operator _op_ (const U* o) const { \
+ return m_ptr _op_ o; \
+} \
+inline bool operator _op_ (const wp<T>& o) const { \
+ return m_ptr _op_ o.m_ptr; \
+} \
+template<typename U> \
+inline bool operator _op_ (const wp<U>& o) const { \
+ return m_ptr _op_ o.m_ptr; \
+}
+
+// ---------------------------------------------------------------------------
+
+template<typename T>
+class sp {
+public:
+ inline sp() : m_ptr(0) { }
+
+ sp(T* other);
+ sp(const sp<T>& other);
+ template<typename U> sp(U* other);
+ template<typename U> sp(const sp<U>& other);
+
+ ~sp();
+
+ // Assignment
+
+ sp& operator = (T* other);
+ sp& operator = (const sp<T>& other);
+
+ template<typename U> sp& operator = (const sp<U>& other);
+ template<typename U> sp& operator = (U* other);
+
+ //! Special optimization for use by ProcessState (and nobody else).
+ void force_set(T* other);
+
+ // Reset
+
+ void clear();
+
+ // Accessors
+
+ inline T& operator* () const { return *m_ptr; }
+ inline T* operator-> () const { return m_ptr; }
+ inline T* get() const { return m_ptr; }
+
+ // Operators
+
+ COMPARE(==)
+ COMPARE(!=)
+ COMPARE(>)
+ COMPARE(<)
+ COMPARE(<=)
+ COMPARE(>=)
+
+private:
+ template<typename Y> friend class sp;
+ template<typename Y> friend class wp;
+ void set_pointer(T* ptr);
+ T* m_ptr;
+};
+
+#undef COMPARE
+
+// ---------------------------------------------------------------------------
+// No user serviceable parts below here.
+
+template<typename T>
+sp<T>::sp(T* other)
+ : m_ptr(other) {
+ if (other)
+ other->incStrong(this);
+}
+
+template<typename T>
+sp<T>::sp(const sp<T>& other)
+ : m_ptr(other.m_ptr) {
+ if (m_ptr)
+ m_ptr->incStrong(this);
+}
+
+template<typename T> template<typename U>
+sp<T>::sp(U* other)
+ : m_ptr(other) {
+ if (other)
+ ((T*) other)->incStrong(this);
+}
+
+template<typename T> template<typename U>
+sp<T>::sp(const sp<U>& other)
+ : m_ptr(other.m_ptr) {
+ if (m_ptr)
+ m_ptr->incStrong(this);
+}
+
+template<typename T>
+sp<T>::~sp() {
+ if (m_ptr)
+ m_ptr->decStrong(this);
+}
+
+template<typename T>
+sp<T>& sp<T>::operator =(const sp<T>& other) {
+ T* otherPtr(other.m_ptr);
+ if (otherPtr)
+ otherPtr->incStrong(this);
+ if (m_ptr)
+ m_ptr->decStrong(this);
+ m_ptr = otherPtr;
+ return *this;
+}
+
+template<typename T>
+sp<T>& sp<T>::operator =(T* other) {
+ if (other)
+ other->incStrong(this);
+ if (m_ptr)
+ m_ptr->decStrong(this);
+ m_ptr = other;
+ return *this;
+}
+
+template<typename T> template<typename U>
+sp<T>& sp<T>::operator =(const sp<U>& other) {
+ T* otherPtr(other.m_ptr);
+ if (otherPtr)
+ otherPtr->incStrong(this);
+ if (m_ptr)
+ m_ptr->decStrong(this);
+ m_ptr = otherPtr;
+ return *this;
+}
+
+template<typename T> template<typename U>
+sp<T>& sp<T>::operator =(U* other) {
+ if (other)
+ ((T*) other)->incStrong(this);
+ if (m_ptr)
+ m_ptr->decStrong(this);
+ m_ptr = other;
+ return *this;
+}
+
+template<typename T>
+void sp<T>::force_set(T* other) {
+ other->forceIncStrong(this);
+ m_ptr = other;
+}
+
+template<typename T>
+void sp<T>::clear() {
+ if (m_ptr) {
+ m_ptr->decStrong(this);
+ m_ptr = 0;
+ }
+}
+
+template<typename T>
+void sp<T>::set_pointer(T* ptr) {
+ m_ptr = ptr;
+}
+
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_STRONG_POINTER_H
diff --git a/include/utils/SystemClock.h b/include/utils/SystemClock.h
new file mode 100644
index 0000000..01db340
--- /dev/null
+++ b/include/utils/SystemClock.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_UTILS_SYSTEMCLOCK_H
+#define ANDROID_UTILS_SYSTEMCLOCK_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+namespace android {
+
+int64_t uptimeMillis();
+int64_t elapsedRealtime();
+int64_t elapsedRealtimeNano();
+
+}; // namespace android
+
+#endif // ANDROID_UTILS_SYSTEMCLOCK_H
+
diff --git a/include/utils/Thread.h b/include/utils/Thread.h
new file mode 100644
index 0000000..28839fd
--- /dev/null
+++ b/include/utils/Thread.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBS_UTILS_THREAD_H
+#define _LIBS_UTILS_THREAD_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <time.h>
+
+#if !defined(_WIN32)
+# include <pthread.h>
+#endif
+
+#include <utils/Condition.h>
+#include <utils/Errors.h>
+#include <utils/Mutex.h>
+#include <utils/RefBase.h>
+#include <utils/Timers.h>
+#include <utils/ThreadDefs.h>
+
+// ---------------------------------------------------------------------------
+namespace android {
+// ---------------------------------------------------------------------------
+
+class Thread : virtual public RefBase
+{
+public:
+ // Create a Thread object, but doesn't create or start the associated
+ // thread. See the run() method.
+ Thread(bool canCallJava = true);
+ virtual ~Thread();
+
+ // Start the thread in threadLoop() which needs to be implemented.
+ virtual status_t run( const char* name = 0,
+ int32_t priority = PRIORITY_DEFAULT,
+ size_t stack = 0);
+
+ // Ask this object's thread to exit. This function is asynchronous, when the
+ // function returns the thread might still be running. Of course, this
+ // function can be called from a different thread.
+ virtual void requestExit();
+
+ // Good place to do one-time initializations
+ virtual status_t readyToRun();
+
+ // Call requestExit() and wait until this object's thread exits.
+ // BE VERY CAREFUL of deadlocks. In particular, it would be silly to call
+ // this function from this object's thread. Will return WOULD_BLOCK in
+ // that case.
+ status_t requestExitAndWait();
+
+ // Wait until this object's thread exits. Returns immediately if not yet running.
+ // Do not call from this object's thread; will return WOULD_BLOCK in that case.
+ status_t join();
+
+ // Indicates whether this thread is running or not.
+ bool isRunning() const;
+
+#ifdef HAVE_ANDROID_OS
+ // Return the thread's kernel ID, same as the thread itself calling gettid(),
+ // or -1 if the thread is not running.
+ pid_t getTid() const;
+#endif
+
+protected:
+ // exitPending() returns true if requestExit() has been called.
+ bool exitPending() const;
+
+private:
+ // Derived class must implement threadLoop(). The thread starts its life
+ // here. There are two ways of using the Thread object:
+ // 1) loop: if threadLoop() returns true, it will be called again if
+ // requestExit() wasn't called.
+ // 2) once: if threadLoop() returns false, the thread will exit upon return.
+ virtual bool threadLoop() = 0;
+
+private:
+ Thread& operator=(const Thread&);
+ static int _threadLoop(void* user);
+ const bool mCanCallJava;
+ // always hold mLock when reading or writing
+ thread_id_t mThread;
+ mutable Mutex mLock;
+ Condition mThreadExitedCondition;
+ status_t mStatus;
+ // note that all accesses of mExitPending and mRunning need to hold mLock
+ volatile bool mExitPending;
+ volatile bool mRunning;
+ sp<Thread> mHoldSelf;
+#ifdef HAVE_ANDROID_OS
+ // legacy for debugging, not used by getTid() as it is set by the child thread
+ // and so is not initialized until the child reaches that point
+ pid_t mTid;
+#endif
+};
+
+
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+#endif // _LIBS_UTILS_THREAD_H
+// ---------------------------------------------------------------------------
diff --git a/include/utils/ThreadDefs.h b/include/utils/ThreadDefs.h
new file mode 100644
index 0000000..9711c13
--- /dev/null
+++ b/include/utils/ThreadDefs.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBS_UTILS_THREAD_DEFS_H
+#define _LIBS_UTILS_THREAD_DEFS_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <system/graphics.h>
+#include <system/thread_defs.h>
+
+// ---------------------------------------------------------------------------
+// C API
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void* android_thread_id_t;
+
+typedef int (*android_thread_func_t)(void*);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+// ---------------------------------------------------------------------------
+// C++ API
+#ifdef __cplusplus
+namespace android {
+// ---------------------------------------------------------------------------
+
+typedef android_thread_id_t thread_id_t;
+typedef android_thread_func_t thread_func_t;
+
+enum {
+ PRIORITY_LOWEST = ANDROID_PRIORITY_LOWEST,
+ PRIORITY_BACKGROUND = ANDROID_PRIORITY_BACKGROUND,
+ PRIORITY_NORMAL = ANDROID_PRIORITY_NORMAL,
+ PRIORITY_FOREGROUND = ANDROID_PRIORITY_FOREGROUND,
+ PRIORITY_DISPLAY = ANDROID_PRIORITY_DISPLAY,
+ PRIORITY_URGENT_DISPLAY = ANDROID_PRIORITY_URGENT_DISPLAY,
+ PRIORITY_AUDIO = ANDROID_PRIORITY_AUDIO,
+ PRIORITY_URGENT_AUDIO = ANDROID_PRIORITY_URGENT_AUDIO,
+ PRIORITY_HIGHEST = ANDROID_PRIORITY_HIGHEST,
+ PRIORITY_DEFAULT = ANDROID_PRIORITY_DEFAULT,
+ PRIORITY_MORE_FAVORABLE = ANDROID_PRIORITY_MORE_FAVORABLE,
+ PRIORITY_LESS_FAVORABLE = ANDROID_PRIORITY_LESS_FAVORABLE,
+};
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+#endif // __cplusplus
+// ---------------------------------------------------------------------------
+
+
+#endif // _LIBS_UTILS_THREAD_DEFS_H
diff --git a/include/utils/Timers.h b/include/utils/Timers.h
new file mode 100644
index 0000000..54ec474
--- /dev/null
+++ b/include/utils/Timers.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2005 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.
+ */
+
+//
+// Timer functions.
+//
+#ifndef _LIBS_UTILS_TIMERS_H
+#define _LIBS_UTILS_TIMERS_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <utils/Compat.h>
+
+// ------------------------------------------------------------------
+// C API
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef int64_t nsecs_t; // nano-seconds
+
+static CONSTEXPR inline nsecs_t seconds_to_nanoseconds(nsecs_t secs)
+{
+ return secs*1000000000;
+}
+
+static CONSTEXPR inline nsecs_t milliseconds_to_nanoseconds(nsecs_t secs)
+{
+ return secs*1000000;
+}
+
+static CONSTEXPR inline nsecs_t microseconds_to_nanoseconds(nsecs_t secs)
+{
+ return secs*1000;
+}
+
+static CONSTEXPR inline nsecs_t nanoseconds_to_seconds(nsecs_t secs)
+{
+ return secs/1000000000;
+}
+
+static CONSTEXPR inline nsecs_t nanoseconds_to_milliseconds(nsecs_t secs)
+{
+ return secs/1000000;
+}
+
+static CONSTEXPR inline nsecs_t nanoseconds_to_microseconds(nsecs_t secs)
+{
+ return secs/1000;
+}
+
+static CONSTEXPR inline nsecs_t s2ns(nsecs_t v) {return seconds_to_nanoseconds(v);}
+static CONSTEXPR inline nsecs_t ms2ns(nsecs_t v) {return milliseconds_to_nanoseconds(v);}
+static CONSTEXPR inline nsecs_t us2ns(nsecs_t v) {return microseconds_to_nanoseconds(v);}
+static CONSTEXPR inline nsecs_t ns2s(nsecs_t v) {return nanoseconds_to_seconds(v);}
+static CONSTEXPR inline nsecs_t ns2ms(nsecs_t v) {return nanoseconds_to_milliseconds(v);}
+static CONSTEXPR inline nsecs_t ns2us(nsecs_t v) {return nanoseconds_to_microseconds(v);}
+
+static CONSTEXPR inline nsecs_t seconds(nsecs_t v) { return s2ns(v); }
+static CONSTEXPR inline nsecs_t milliseconds(nsecs_t v) { return ms2ns(v); }
+static CONSTEXPR inline nsecs_t microseconds(nsecs_t v) { return us2ns(v); }
+
+enum {
+ SYSTEM_TIME_REALTIME = 0, // system-wide realtime clock
+ SYSTEM_TIME_MONOTONIC = 1, // monotonic time since unspecified starting point
+ SYSTEM_TIME_PROCESS = 2, // high-resolution per-process clock
+ SYSTEM_TIME_THREAD = 3, // high-resolution per-thread clock
+ SYSTEM_TIME_BOOTTIME = 4 // same as SYSTEM_TIME_MONOTONIC, but including CPU suspend time
+};
+
+// return the system-time according to the specified clock
+#ifdef __cplusplus
+nsecs_t systemTime(int clock = SYSTEM_TIME_MONOTONIC);
+#else
+nsecs_t systemTime(int clock);
+#endif // def __cplusplus
+
+/**
+ * Returns the number of milliseconds to wait between the reference time and the timeout time.
+ * If the timeout is in the past relative to the reference time, returns 0.
+ * If the timeout is more than INT_MAX milliseconds in the future relative to the reference time,
+ * such as when timeoutTime == LLONG_MAX, returns -1 to indicate an infinite timeout delay.
+ * Otherwise, returns the difference between the reference time and timeout time
+ * rounded up to the next millisecond.
+ */
+int toMillisecondTimeoutDelay(nsecs_t referenceTime, nsecs_t timeoutTime);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _LIBS_UTILS_TIMERS_H
diff --git a/include/utils/Tokenizer.h b/include/utils/Tokenizer.h
new file mode 100644
index 0000000..bb25f37
--- /dev/null
+++ b/include/utils/Tokenizer.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UTILS_TOKENIZER_H
+#define _UTILS_TOKENIZER_H
+
+#include <assert.h>
+#include <utils/Errors.h>
+#include <utils/FileMap.h>
+#include <utils/String8.h>
+
+namespace android {
+
+/**
+ * A simple tokenizer for loading and parsing ASCII text files line by line.
+ */
+class Tokenizer {
+ Tokenizer(const String8& filename, FileMap* fileMap, char* buffer,
+ bool ownBuffer, size_t length);
+
+public:
+ ~Tokenizer();
+
+ /**
+ * Opens a file and maps it into memory.
+ *
+ * Returns NO_ERROR and a tokenizer for the file, if successful.
+ * Otherwise returns an error and sets outTokenizer to NULL.
+ */
+ static status_t open(const String8& filename, Tokenizer** outTokenizer);
+
+ /**
+ * Prepares to tokenize the contents of a string.
+ *
+ * Returns NO_ERROR and a tokenizer for the string, if successful.
+ * Otherwise returns an error and sets outTokenizer to NULL.
+ */
+ static status_t fromContents(const String8& filename,
+ const char* contents, Tokenizer** outTokenizer);
+
+ /**
+ * Returns true if at the end of the file.
+ */
+ inline bool isEof() const { return mCurrent == getEnd(); }
+
+ /**
+ * Returns true if at the end of the line or end of the file.
+ */
+ inline bool isEol() const { return isEof() || *mCurrent == '\n'; }
+
+ /**
+ * Gets the name of the file.
+ */
+ inline String8 getFilename() const { return mFilename; }
+
+ /**
+ * Gets a 1-based line number index for the current position.
+ */
+ inline int32_t getLineNumber() const { return mLineNumber; }
+
+ /**
+ * Formats a location string consisting of the filename and current line number.
+ * Returns a string like "MyFile.txt:33".
+ */
+ String8 getLocation() const;
+
+ /**
+ * Gets the character at the current position.
+ * Returns null at end of file.
+ */
+ inline char peekChar() const { return isEof() ? '\0' : *mCurrent; }
+
+ /**
+ * Gets the remainder of the current line as a string, excluding the newline character.
+ */
+ String8 peekRemainderOfLine() const;
+
+ /**
+ * Gets the character at the current position and advances past it.
+ * Returns null at end of file.
+ */
+ inline char nextChar() { return isEof() ? '\0' : *(mCurrent++); }
+
+ /**
+ * Gets the next token on this line stopping at the specified delimiters
+ * or the end of the line whichever comes first and advances past it.
+ * Also stops at embedded nulls.
+ * Returns the token or an empty string if the current character is a delimiter
+ * or is at the end of the line.
+ */
+ String8 nextToken(const char* delimiters);
+
+ /**
+ * Advances to the next line.
+ * Does nothing if already at the end of the file.
+ */
+ void nextLine();
+
+ /**
+ * Skips over the specified delimiters in the line.
+ * Also skips embedded nulls.
+ */
+ void skipDelimiters(const char* delimiters);
+
+private:
+ Tokenizer(const Tokenizer& other); // not copyable
+
+ String8 mFilename;
+ FileMap* mFileMap;
+ char* mBuffer;
+ bool mOwnBuffer;
+ size_t mLength;
+
+ const char* mCurrent;
+ int32_t mLineNumber;
+
+ inline const char* getEnd() const { return mBuffer + mLength; }
+
+};
+
+} // namespace android
+
+#endif // _UTILS_TOKENIZER_H
diff --git a/include/utils/Trace.h b/include/utils/Trace.h
new file mode 100644
index 0000000..6ee343d
--- /dev/null
+++ b/include/utils/Trace.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_TRACE_H
+#define ANDROID_TRACE_H
+
+#ifdef HAVE_ANDROID_OS
+
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <cutils/compiler.h>
+#include <utils/threads.h>
+#include <cutils/trace.h>
+
+// See <cutils/trace.h> for more ATRACE_* macros.
+
+// ATRACE_NAME traces the beginning and end of the current scope. To trace
+// the correct start and end times this macro should be declared first in the
+// scope body.
+#define ATRACE_NAME(name) android::ScopedTrace ___tracer(ATRACE_TAG, name)
+// ATRACE_CALL is an ATRACE_NAME that uses the current function name.
+#define ATRACE_CALL() ATRACE_NAME(__FUNCTION__)
+
+namespace android {
+
+class ScopedTrace {
+public:
+inline ScopedTrace(uint64_t tag, const char* name)
+ : mTag(tag) {
+ atrace_begin(mTag,name);
+}
+
+inline ~ScopedTrace() {
+ atrace_end(mTag);
+}
+
+private:
+ uint64_t mTag;
+};
+
+}; // namespace android
+
+#else // HAVE_ANDROID_OS
+
+#define ATRACE_NAME(...)
+#define ATRACE_CALL()
+
+#endif // HAVE_ANDROID_OS
+
+#endif // ANDROID_TRACE_H
diff --git a/include/utils/TypeHelpers.h b/include/utils/TypeHelpers.h
new file mode 100644
index 0000000..61d618e
--- /dev/null
+++ b/include/utils/TypeHelpers.h
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_TYPE_HELPERS_H
+#define ANDROID_TYPE_HELPERS_H
+
+#include <new>
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+/*
+ * Types traits
+ */
+
+template <typename T> struct trait_trivial_ctor { enum { value = false }; };
+template <typename T> struct trait_trivial_dtor { enum { value = false }; };
+template <typename T> struct trait_trivial_copy { enum { value = false }; };
+template <typename T> struct trait_trivial_move { enum { value = false }; };
+template <typename T> struct trait_pointer { enum { value = false }; };
+template <typename T> struct trait_pointer<T*> { enum { value = true }; };
+
+template <typename TYPE>
+struct traits {
+ enum {
+ // whether this type is a pointer
+ is_pointer = trait_pointer<TYPE>::value,
+ // whether this type's constructor is a no-op
+ has_trivial_ctor = is_pointer || trait_trivial_ctor<TYPE>::value,
+ // whether this type's destructor is a no-op
+ has_trivial_dtor = is_pointer || trait_trivial_dtor<TYPE>::value,
+ // whether this type type can be copy-constructed with memcpy
+ has_trivial_copy = is_pointer || trait_trivial_copy<TYPE>::value,
+ // whether this type can be moved with memmove
+ has_trivial_move = is_pointer || trait_trivial_move<TYPE>::value
+ };
+};
+
+template <typename T, typename U>
+struct aggregate_traits {
+ enum {
+ is_pointer = false,
+ has_trivial_ctor =
+ traits<T>::has_trivial_ctor && traits<U>::has_trivial_ctor,
+ has_trivial_dtor =
+ traits<T>::has_trivial_dtor && traits<U>::has_trivial_dtor,
+ has_trivial_copy =
+ traits<T>::has_trivial_copy && traits<U>::has_trivial_copy,
+ has_trivial_move =
+ traits<T>::has_trivial_move && traits<U>::has_trivial_move
+ };
+};
+
+#define ANDROID_TRIVIAL_CTOR_TRAIT( T ) \
+ template<> struct trait_trivial_ctor< T > { enum { value = true }; };
+
+#define ANDROID_TRIVIAL_DTOR_TRAIT( T ) \
+ template<> struct trait_trivial_dtor< T > { enum { value = true }; };
+
+#define ANDROID_TRIVIAL_COPY_TRAIT( T ) \
+ template<> struct trait_trivial_copy< T > { enum { value = true }; };
+
+#define ANDROID_TRIVIAL_MOVE_TRAIT( T ) \
+ template<> struct trait_trivial_move< T > { enum { value = true }; };
+
+#define ANDROID_BASIC_TYPES_TRAITS( T ) \
+ ANDROID_TRIVIAL_CTOR_TRAIT( T ) \
+ ANDROID_TRIVIAL_DTOR_TRAIT( T ) \
+ ANDROID_TRIVIAL_COPY_TRAIT( T ) \
+ ANDROID_TRIVIAL_MOVE_TRAIT( T )
+
+// ---------------------------------------------------------------------------
+
+/*
+ * basic types traits
+ */
+
+ANDROID_BASIC_TYPES_TRAITS( void )
+ANDROID_BASIC_TYPES_TRAITS( bool )
+ANDROID_BASIC_TYPES_TRAITS( char )
+ANDROID_BASIC_TYPES_TRAITS( unsigned char )
+ANDROID_BASIC_TYPES_TRAITS( short )
+ANDROID_BASIC_TYPES_TRAITS( unsigned short )
+ANDROID_BASIC_TYPES_TRAITS( int )
+ANDROID_BASIC_TYPES_TRAITS( unsigned int )
+ANDROID_BASIC_TYPES_TRAITS( long )
+ANDROID_BASIC_TYPES_TRAITS( unsigned long )
+ANDROID_BASIC_TYPES_TRAITS( long long )
+ANDROID_BASIC_TYPES_TRAITS( unsigned long long )
+ANDROID_BASIC_TYPES_TRAITS( float )
+ANDROID_BASIC_TYPES_TRAITS( double )
+
+// ---------------------------------------------------------------------------
+
+
+/*
+ * compare and order types
+ */
+
+template<typename TYPE> inline
+int strictly_order_type(const TYPE& lhs, const TYPE& rhs) {
+ return (lhs < rhs) ? 1 : 0;
+}
+
+template<typename TYPE> inline
+int compare_type(const TYPE& lhs, const TYPE& rhs) {
+ return strictly_order_type(rhs, lhs) - strictly_order_type(lhs, rhs);
+}
+
+/*
+ * create, destroy, copy and move types...
+ */
+
+template<typename TYPE> inline
+void construct_type(TYPE* p, size_t n) {
+ if (!traits<TYPE>::has_trivial_ctor) {
+ while (n > 0) {
+ n--;
+ new(p++) TYPE;
+ }
+ }
+}
+
+template<typename TYPE> inline
+void destroy_type(TYPE* p, size_t n) {
+ if (!traits<TYPE>::has_trivial_dtor) {
+ while (n > 0) {
+ n--;
+ p->~TYPE();
+ p++;
+ }
+ }
+}
+
+template<typename TYPE> inline
+void copy_type(TYPE* d, const TYPE* s, size_t n) {
+ if (!traits<TYPE>::has_trivial_copy) {
+ while (n > 0) {
+ n--;
+ new(d) TYPE(*s);
+ d++, s++;
+ }
+ } else {
+ memcpy(d,s,n*sizeof(TYPE));
+ }
+}
+
+template<typename TYPE> inline
+void splat_type(TYPE* where, const TYPE* what, size_t n) {
+ if (!traits<TYPE>::has_trivial_copy) {
+ while (n > 0) {
+ n--;
+ new(where) TYPE(*what);
+ where++;
+ }
+ } else {
+ while (n > 0) {
+ n--;
+ *where++ = *what;
+ }
+ }
+}
+
+template<typename TYPE> inline
+void move_forward_type(TYPE* d, const TYPE* s, size_t n = 1) {
+ if ((traits<TYPE>::has_trivial_dtor && traits<TYPE>::has_trivial_copy)
+ || traits<TYPE>::has_trivial_move)
+ {
+ memmove(d,s,n*sizeof(TYPE));
+ } else {
+ d += n;
+ s += n;
+ while (n > 0) {
+ n--;
+ --d, --s;
+ if (!traits<TYPE>::has_trivial_copy) {
+ new(d) TYPE(*s);
+ } else {
+ *d = *s;
+ }
+ if (!traits<TYPE>::has_trivial_dtor) {
+ s->~TYPE();
+ }
+ }
+ }
+}
+
+template<typename TYPE> inline
+void move_backward_type(TYPE* d, const TYPE* s, size_t n = 1) {
+ if ((traits<TYPE>::has_trivial_dtor && traits<TYPE>::has_trivial_copy)
+ || traits<TYPE>::has_trivial_move)
+ {
+ memmove(d,s,n*sizeof(TYPE));
+ } else {
+ while (n > 0) {
+ n--;
+ if (!traits<TYPE>::has_trivial_copy) {
+ new(d) TYPE(*s);
+ } else {
+ *d = *s;
+ }
+ if (!traits<TYPE>::has_trivial_dtor) {
+ s->~TYPE();
+ }
+ d++, s++;
+ }
+ }
+}
+
+// ---------------------------------------------------------------------------
+
+/*
+ * a key/value pair
+ */
+
+template <typename KEY, typename VALUE>
+struct key_value_pair_t {
+ typedef KEY key_t;
+ typedef VALUE value_t;
+
+ KEY key;
+ VALUE value;
+ key_value_pair_t() { }
+ key_value_pair_t(const key_value_pair_t& o) : key(o.key), value(o.value) { }
+ key_value_pair_t(const KEY& k, const VALUE& v) : key(k), value(v) { }
+ key_value_pair_t(const KEY& k) : key(k) { }
+ inline bool operator < (const key_value_pair_t& o) const {
+ return strictly_order_type(key, o.key);
+ }
+ inline const KEY& getKey() const {
+ return key;
+ }
+ inline const VALUE& getValue() const {
+ return value;
+ }
+};
+
+template <typename K, typename V>
+struct trait_trivial_ctor< key_value_pair_t<K, V> >
+{ enum { value = aggregate_traits<K,V>::has_trivial_ctor }; };
+template <typename K, typename V>
+struct trait_trivial_dtor< key_value_pair_t<K, V> >
+{ enum { value = aggregate_traits<K,V>::has_trivial_dtor }; };
+template <typename K, typename V>
+struct trait_trivial_copy< key_value_pair_t<K, V> >
+{ enum { value = aggregate_traits<K,V>::has_trivial_copy }; };
+template <typename K, typename V>
+struct trait_trivial_move< key_value_pair_t<K, V> >
+{ enum { value = aggregate_traits<K,V>::has_trivial_move }; };
+
+// ---------------------------------------------------------------------------
+
+/*
+ * Hash codes.
+ */
+typedef uint32_t hash_t;
+
+template <typename TKey>
+hash_t hash_type(const TKey& key);
+
+/* Built-in hash code specializations.
+ * Assumes pointers are 32bit. */
+#define ANDROID_INT32_HASH(T) \
+ template <> inline hash_t hash_type(const T& value) { return hash_t(value); }
+#define ANDROID_INT64_HASH(T) \
+ template <> inline hash_t hash_type(const T& value) { \
+ return hash_t((value >> 32) ^ value); }
+#define ANDROID_REINTERPRET_HASH(T, R) \
+ template <> inline hash_t hash_type(const T& value) { \
+ return hash_type(*reinterpret_cast<const R*>(&value)); }
+
+ANDROID_INT32_HASH(bool)
+ANDROID_INT32_HASH(int8_t)
+ANDROID_INT32_HASH(uint8_t)
+ANDROID_INT32_HASH(int16_t)
+ANDROID_INT32_HASH(uint16_t)
+ANDROID_INT32_HASH(int32_t)
+ANDROID_INT32_HASH(uint32_t)
+ANDROID_INT64_HASH(int64_t)
+ANDROID_INT64_HASH(uint64_t)
+ANDROID_REINTERPRET_HASH(float, uint32_t)
+ANDROID_REINTERPRET_HASH(double, uint64_t)
+
+template <typename T> inline hash_t hash_type(T* const & value) {
+ return hash_type(uintptr_t(value));
+}
+
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_TYPE_HELPERS_H
diff --git a/include/utils/Unicode.h b/include/utils/Unicode.h
new file mode 100644
index 0000000..b76a5e2
--- /dev/null
+++ b/include/utils/Unicode.h
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_UNICODE_H
+#define ANDROID_UNICODE_H
+
+#include <sys/types.h>
+#include <stdint.h>
+
+extern "C" {
+
+// Standard string functions on char16_t strings.
+int strcmp16(const char16_t *, const char16_t *);
+int strncmp16(const char16_t *s1, const char16_t *s2, size_t n);
+size_t strlen16(const char16_t *);
+size_t strnlen16(const char16_t *, size_t);
+char16_t *strcpy16(char16_t *, const char16_t *);
+char16_t *strncpy16(char16_t *, const char16_t *, size_t);
+
+// Version of comparison that supports embedded nulls.
+// This is different than strncmp() because we don't stop
+// at a nul character and consider the strings to be different
+// if the lengths are different (thus we need to supply the
+// lengths of both strings). This can also be used when
+// your string is not nul-terminated as it will have the
+// equivalent result as strcmp16 (unlike strncmp16).
+int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2);
+
+// Version of strzcmp16 for comparing strings in different endianness.
+int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2);
+
+// Standard string functions on char32_t strings.
+size_t strlen32(const char32_t *);
+size_t strnlen32(const char32_t *, size_t);
+
+/**
+ * Measure the length of a UTF-32 string in UTF-8. If the string is invalid
+ * such as containing a surrogate character, -1 will be returned.
+ */
+ssize_t utf32_to_utf8_length(const char32_t *src, size_t src_len);
+
+/**
+ * Stores a UTF-8 string converted from "src" in "dst", if "dst_length" is not
+ * large enough to store the string, the part of the "src" string is stored
+ * into "dst" as much as possible. See the examples for more detail.
+ * Returns the size actually used for storing the string.
+ * dst" is not null-terminated when dst_len is fully used (like strncpy).
+ *
+ * Example 1
+ * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84)
+ * "src_len" == 2
+ * "dst_len" >= 7
+ * ->
+ * Returned value == 6
+ * "dst" becomes \xE3\x81\x82\xE3\x81\x84\0
+ * (note that "dst" is null-terminated)
+ *
+ * Example 2
+ * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84)
+ * "src_len" == 2
+ * "dst_len" == 5
+ * ->
+ * Returned value == 3
+ * "dst" becomes \xE3\x81\x82\0
+ * (note that "dst" is null-terminated, but \u3044 is not stored in "dst"
+ * since "dst" does not have enough size to store the character)
+ *
+ * Example 3
+ * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84)
+ * "src_len" == 2
+ * "dst_len" == 6
+ * ->
+ * Returned value == 6
+ * "dst" becomes \xE3\x81\x82\xE3\x81\x84
+ * (note that "dst" is NOT null-terminated, like strncpy)
+ */
+void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst);
+
+/**
+ * Returns the unicode value at "index".
+ * Returns -1 when the index is invalid (equals to or more than "src_len").
+ * If returned value is positive, it is able to be converted to char32_t, which
+ * is unsigned. Then, if "next_index" is not NULL, the next index to be used is
+ * stored in "next_index". "next_index" can be NULL.
+ */
+int32_t utf32_from_utf8_at(const char *src, size_t src_len, size_t index, size_t *next_index);
+
+
+/**
+ * Returns the UTF-8 length of UTF-16 string "src".
+ */
+ssize_t utf16_to_utf8_length(const char16_t *src, size_t src_len);
+
+/**
+ * Converts a UTF-16 string to UTF-8. The destination buffer must be large
+ * enough to fit the UTF-16 as measured by utf16_to_utf8_length with an added
+ * NULL terminator.
+ */
+void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst);
+
+/**
+ * Returns the length of "src" when "src" is valid UTF-8 string.
+ * Returns 0 if src is NULL or 0-length string. Returns -1 when the source
+ * is an invalid string.
+ *
+ * This function should be used to determine whether "src" is valid UTF-8
+ * characters with valid unicode codepoints. "src" must be null-terminated.
+ *
+ * If you are going to use other utf8_to_... functions defined in this header
+ * with string which may not be valid UTF-8 with valid codepoint (form 0 to
+ * 0x10FFFF), you should use this function before calling others, since the
+ * other functions do not check whether the string is valid UTF-8 or not.
+ *
+ * If you do not care whether "src" is valid UTF-8 or not, you should use
+ * strlen() as usual, which should be much faster.
+ */
+ssize_t utf8_length(const char *src);
+
+/**
+ * Measure the length of a UTF-32 string.
+ */
+size_t utf8_to_utf32_length(const char *src, size_t src_len);
+
+/**
+ * Stores a UTF-32 string converted from "src" in "dst". "dst" must be large
+ * enough to store the entire converted string as measured by
+ * utf8_to_utf32_length plus space for a NULL terminator.
+ */
+void utf8_to_utf32(const char* src, size_t src_len, char32_t* dst);
+
+/**
+ * Returns the UTF-16 length of UTF-8 string "src".
+ */
+ssize_t utf8_to_utf16_length(const uint8_t* src, size_t srcLen);
+
+/**
+ * Convert UTF-8 to UTF-16 including surrogate pairs.
+ * Returns a pointer to the end of the string (where a null terminator might go
+ * if you wanted to add one).
+ */
+char16_t* utf8_to_utf16_no_null_terminator(const uint8_t* src, size_t srcLen, char16_t* dst);
+
+/**
+ * Convert UTF-8 to UTF-16 including surrogate pairs. The destination buffer
+ * must be large enough to hold the result as measured by utf8_to_utf16_length
+ * plus an added NULL terminator.
+ */
+void utf8_to_utf16(const uint8_t* src, size_t srcLen, char16_t* dst);
+
+/**
+ * Like utf8_to_utf16_no_null_terminator, but you can supply a maximum length of the
+ * decoded string. The decoded string will fill up to that length; if it is longer
+ * the returned pointer will be to the character after dstLen.
+ */
+char16_t* utf8_to_utf16_n(const uint8_t* src, size_t srcLen, char16_t* dst, size_t dstLen);
+
+}
+
+#endif
diff --git a/include/utils/Vector.h b/include/utils/Vector.h
new file mode 100644
index 0000000..ed7b725
--- /dev/null
+++ b/include/utils/Vector.h
@@ -0,0 +1,423 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VECTOR_H
+#define ANDROID_VECTOR_H
+
+#include <new>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <cutils/log.h>
+
+#include <utils/VectorImpl.h>
+#include <utils/TypeHelpers.h>
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+template <typename TYPE>
+class SortedVector;
+
+/*!
+ * The main templated vector class ensuring type safety
+ * while making use of VectorImpl.
+ * This is the class users want to use.
+ */
+
+template <class TYPE>
+class Vector : private VectorImpl
+{
+public:
+ typedef TYPE value_type;
+
+ /*!
+ * Constructors and destructors
+ */
+
+ Vector();
+ Vector(const Vector<TYPE>& rhs);
+ explicit Vector(const SortedVector<TYPE>& rhs);
+ virtual ~Vector();
+
+ /*! copy operator */
+ const Vector<TYPE>& operator = (const Vector<TYPE>& rhs) const;
+ Vector<TYPE>& operator = (const Vector<TYPE>& rhs);
+
+ const Vector<TYPE>& operator = (const SortedVector<TYPE>& rhs) const;
+ Vector<TYPE>& operator = (const SortedVector<TYPE>& rhs);
+
+ /*
+ * empty the vector
+ */
+
+ inline void clear() { VectorImpl::clear(); }
+
+ /*!
+ * vector stats
+ */
+
+ //! returns number of items in the vector
+ inline size_t size() const { return VectorImpl::size(); }
+ //! returns whether or not the vector is empty
+ inline bool isEmpty() const { return VectorImpl::isEmpty(); }
+ //! returns how many items can be stored without reallocating the backing store
+ inline size_t capacity() const { return VectorImpl::capacity(); }
+ //! sets the capacity. capacity can never be reduced less than size()
+ inline ssize_t setCapacity(size_t size) { return VectorImpl::setCapacity(size); }
+
+ /*!
+ * set the size of the vector. items are appended with the default
+ * constructor, or removed from the end as needed.
+ */
+ inline ssize_t resize(size_t size) { return VectorImpl::resize(size); }
+
+ /*!
+ * C-style array access
+ */
+
+ //! read-only C-style access
+ inline const TYPE* array() const;
+ //! read-write C-style access
+ TYPE* editArray();
+
+ /*!
+ * accessors
+ */
+
+ //! read-only access to an item at a given index
+ inline const TYPE& operator [] (size_t index) const;
+ //! alternate name for operator []
+ inline const TYPE& itemAt(size_t index) const;
+ //! stack-usage of the vector. returns the top of the stack (last element)
+ const TYPE& top() const;
+
+ /*!
+ * modifying the array
+ */
+
+ //! copy-on write support, grants write access to an item
+ TYPE& editItemAt(size_t index);
+ //! grants right access to the top of the stack (last element)
+ TYPE& editTop();
+
+ /*!
+ * append/insert another vector
+ */
+
+ //! insert another vector at a given index
+ ssize_t insertVectorAt(const Vector<TYPE>& vector, size_t index);
+
+ //! append another vector at the end of this one
+ ssize_t appendVector(const Vector<TYPE>& vector);
+
+
+ //! insert an array at a given index
+ ssize_t insertArrayAt(const TYPE* array, size_t index, size_t length);
+
+ //! append an array at the end of this vector
+ ssize_t appendArray(const TYPE* array, size_t length);
+
+ /*!
+ * add/insert/replace items
+ */
+
+ //! insert one or several items initialized with their default constructor
+ inline ssize_t insertAt(size_t index, size_t numItems = 1);
+ //! insert one or several items initialized from a prototype item
+ ssize_t insertAt(const TYPE& prototype_item, size_t index, size_t numItems = 1);
+ //! pop the top of the stack (removes the last element). No-op if the stack's empty
+ inline void pop();
+ //! pushes an item initialized with its default constructor
+ inline void push();
+ //! pushes an item on the top of the stack
+ void push(const TYPE& item);
+ //! same as push() but returns the index the item was added at (or an error)
+ inline ssize_t add();
+ //! same as push() but returns the index the item was added at (or an error)
+ ssize_t add(const TYPE& item);
+ //! replace an item with a new one initialized with its default constructor
+ inline ssize_t replaceAt(size_t index);
+ //! replace an item with a new one
+ ssize_t replaceAt(const TYPE& item, size_t index);
+
+ /*!
+ * remove items
+ */
+
+ //! remove several items
+ inline ssize_t removeItemsAt(size_t index, size_t count = 1);
+ //! remove one item
+ inline ssize_t removeAt(size_t index) { return removeItemsAt(index); }
+
+ /*!
+ * sort (stable) the array
+ */
+
+ typedef int (*compar_t)(const TYPE* lhs, const TYPE* rhs);
+ typedef int (*compar_r_t)(const TYPE* lhs, const TYPE* rhs, void* state);
+
+ inline status_t sort(compar_t cmp);
+ inline status_t sort(compar_r_t cmp, void* state);
+
+ // for debugging only
+ inline size_t getItemSize() const { return itemSize(); }
+
+
+ /*
+ * these inlines add some level of compatibility with STL. eventually
+ * we should probably turn things around.
+ */
+ typedef TYPE* iterator;
+ typedef TYPE const* const_iterator;
+
+ inline iterator begin() { return editArray(); }
+ inline iterator end() { return editArray() + size(); }
+ inline const_iterator begin() const { return array(); }
+ inline const_iterator end() const { return array() + size(); }
+ inline void reserve(size_t n) { setCapacity(n); }
+ inline bool empty() const{ return isEmpty(); }
+ inline void push_back(const TYPE& item) { insertAt(item, size(), 1); }
+ inline void push_front(const TYPE& item) { insertAt(item, 0, 1); }
+ inline iterator erase(iterator pos) {
+ ssize_t index = removeItemsAt(pos-array());
+ return begin() + index;
+ }
+
+protected:
+ virtual void do_construct(void* storage, size_t num) const;
+ virtual void do_destroy(void* storage, size_t num) const;
+ virtual void do_copy(void* dest, const void* from, size_t num) const;
+ virtual void do_splat(void* dest, const void* item, size_t num) const;
+ virtual void do_move_forward(void* dest, const void* from, size_t num) const;
+ virtual void do_move_backward(void* dest, const void* from, size_t num) const;
+};
+
+// Vector<T> can be trivially moved using memcpy() because moving does not
+// require any change to the underlying SharedBuffer contents or reference count.
+template<typename T> struct trait_trivial_move<Vector<T> > { enum { value = true }; };
+
+// ---------------------------------------------------------------------------
+// No user serviceable parts from here...
+// ---------------------------------------------------------------------------
+
+template<class TYPE> inline
+Vector<TYPE>::Vector()
+ : VectorImpl(sizeof(TYPE),
+ ((traits<TYPE>::has_trivial_ctor ? HAS_TRIVIAL_CTOR : 0)
+ |(traits<TYPE>::has_trivial_dtor ? HAS_TRIVIAL_DTOR : 0)
+ |(traits<TYPE>::has_trivial_copy ? HAS_TRIVIAL_COPY : 0))
+ )
+{
+}
+
+template<class TYPE> inline
+Vector<TYPE>::Vector(const Vector<TYPE>& rhs)
+ : VectorImpl(rhs) {
+}
+
+template<class TYPE> inline
+Vector<TYPE>::Vector(const SortedVector<TYPE>& rhs)
+ : VectorImpl(static_cast<const VectorImpl&>(rhs)) {
+}
+
+template<class TYPE> inline
+Vector<TYPE>::~Vector() {
+ finish_vector();
+}
+
+template<class TYPE> inline
+Vector<TYPE>& Vector<TYPE>::operator = (const Vector<TYPE>& rhs) {
+ VectorImpl::operator = (rhs);
+ return *this;
+}
+
+template<class TYPE> inline
+const Vector<TYPE>& Vector<TYPE>::operator = (const Vector<TYPE>& rhs) const {
+ VectorImpl::operator = (static_cast<const VectorImpl&>(rhs));
+ return *this;
+}
+
+template<class TYPE> inline
+Vector<TYPE>& Vector<TYPE>::operator = (const SortedVector<TYPE>& rhs) {
+ VectorImpl::operator = (static_cast<const VectorImpl&>(rhs));
+ return *this;
+}
+
+template<class TYPE> inline
+const Vector<TYPE>& Vector<TYPE>::operator = (const SortedVector<TYPE>& rhs) const {
+ VectorImpl::operator = (rhs);
+ return *this;
+}
+
+template<class TYPE> inline
+const TYPE* Vector<TYPE>::array() const {
+ return static_cast<const TYPE *>(arrayImpl());
+}
+
+template<class TYPE> inline
+TYPE* Vector<TYPE>::editArray() {
+ return static_cast<TYPE *>(editArrayImpl());
+}
+
+
+template<class TYPE> inline
+const TYPE& Vector<TYPE>::operator[](size_t index) const {
+ LOG_FATAL_IF(index>=size(),
+ "%s: index=%u out of range (%u)", __PRETTY_FUNCTION__,
+ int(index), int(size()));
+ return *(array() + index);
+}
+
+template<class TYPE> inline
+const TYPE& Vector<TYPE>::itemAt(size_t index) const {
+ return operator[](index);
+}
+
+template<class TYPE> inline
+const TYPE& Vector<TYPE>::top() const {
+ return *(array() + size() - 1);
+}
+
+template<class TYPE> inline
+TYPE& Vector<TYPE>::editItemAt(size_t index) {
+ return *( static_cast<TYPE *>(editItemLocation(index)) );
+}
+
+template<class TYPE> inline
+TYPE& Vector<TYPE>::editTop() {
+ return *( static_cast<TYPE *>(editItemLocation(size()-1)) );
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::insertVectorAt(const Vector<TYPE>& vector, size_t index) {
+ return VectorImpl::insertVectorAt(reinterpret_cast<const VectorImpl&>(vector), index);
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::appendVector(const Vector<TYPE>& vector) {
+ return VectorImpl::appendVector(reinterpret_cast<const VectorImpl&>(vector));
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::insertArrayAt(const TYPE* array, size_t index, size_t length) {
+ return VectorImpl::insertArrayAt(array, index, length);
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::appendArray(const TYPE* array, size_t length) {
+ return VectorImpl::appendArray(array, length);
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::insertAt(const TYPE& item, size_t index, size_t numItems) {
+ return VectorImpl::insertAt(&item, index, numItems);
+}
+
+template<class TYPE> inline
+void Vector<TYPE>::push(const TYPE& item) {
+ return VectorImpl::push(&item);
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::add(const TYPE& item) {
+ return VectorImpl::add(&item);
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::replaceAt(const TYPE& item, size_t index) {
+ return VectorImpl::replaceAt(&item, index);
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::insertAt(size_t index, size_t numItems) {
+ return VectorImpl::insertAt(index, numItems);
+}
+
+template<class TYPE> inline
+void Vector<TYPE>::pop() {
+ VectorImpl::pop();
+}
+
+template<class TYPE> inline
+void Vector<TYPE>::push() {
+ VectorImpl::push();
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::add() {
+ return VectorImpl::add();
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::replaceAt(size_t index) {
+ return VectorImpl::replaceAt(index);
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::removeItemsAt(size_t index, size_t count) {
+ return VectorImpl::removeItemsAt(index, count);
+}
+
+template<class TYPE> inline
+status_t Vector<TYPE>::sort(Vector<TYPE>::compar_t cmp) {
+ return VectorImpl::sort((VectorImpl::compar_t)cmp);
+}
+
+template<class TYPE> inline
+status_t Vector<TYPE>::sort(Vector<TYPE>::compar_r_t cmp, void* state) {
+ return VectorImpl::sort((VectorImpl::compar_r_t)cmp, state);
+}
+
+// ---------------------------------------------------------------------------
+
+template<class TYPE>
+void Vector<TYPE>::do_construct(void* storage, size_t num) const {
+ construct_type( reinterpret_cast<TYPE*>(storage), num );
+}
+
+template<class TYPE>
+void Vector<TYPE>::do_destroy(void* storage, size_t num) const {
+ destroy_type( reinterpret_cast<TYPE*>(storage), num );
+}
+
+template<class TYPE>
+void Vector<TYPE>::do_copy(void* dest, const void* from, size_t num) const {
+ copy_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
+}
+
+template<class TYPE>
+void Vector<TYPE>::do_splat(void* dest, const void* item, size_t num) const {
+ splat_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(item), num );
+}
+
+template<class TYPE>
+void Vector<TYPE>::do_move_forward(void* dest, const void* from, size_t num) const {
+ move_forward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
+}
+
+template<class TYPE>
+void Vector<TYPE>::do_move_backward(void* dest, const void* from, size_t num) const {
+ move_backward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
+}
+
+}; // namespace android
+
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_VECTOR_H
diff --git a/include/utils/VectorImpl.h b/include/utils/VectorImpl.h
new file mode 100644
index 0000000..21ad71c
--- /dev/null
+++ b/include/utils/VectorImpl.h
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VECTOR_IMPL_H
+#define ANDROID_VECTOR_IMPL_H
+
+#include <assert.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <utils/Errors.h>
+
+// ---------------------------------------------------------------------------
+// No user serviceable parts in here...
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+/*!
+ * Implementation of the guts of the vector<> class
+ * this ensures backward binary compatibility and
+ * reduces code size.
+ * For performance reasons, we expose mStorage and mCount
+ * so these fields are set in stone.
+ *
+ */
+
+class VectorImpl
+{
+public:
+ enum { // flags passed to the ctor
+ HAS_TRIVIAL_CTOR = 0x00000001,
+ HAS_TRIVIAL_DTOR = 0x00000002,
+ HAS_TRIVIAL_COPY = 0x00000004,
+ };
+
+ VectorImpl(size_t itemSize, uint32_t flags);
+ VectorImpl(const VectorImpl& rhs);
+ virtual ~VectorImpl();
+
+ /*! must be called from subclasses destructor */
+ void finish_vector();
+
+ VectorImpl& operator = (const VectorImpl& rhs);
+
+ /*! C-style array access */
+ inline const void* arrayImpl() const { return mStorage; }
+ void* editArrayImpl();
+
+ /*! vector stats */
+ inline size_t size() const { return mCount; }
+ inline bool isEmpty() const { return mCount == 0; }
+ size_t capacity() const;
+ ssize_t setCapacity(size_t size);
+ ssize_t resize(size_t size);
+
+ /*! append/insert another vector or array */
+ ssize_t insertVectorAt(const VectorImpl& vector, size_t index);
+ ssize_t appendVector(const VectorImpl& vector);
+ ssize_t insertArrayAt(const void* array, size_t index, size_t length);
+ ssize_t appendArray(const void* array, size_t length);
+
+ /*! add/insert/replace items */
+ ssize_t insertAt(size_t where, size_t numItems = 1);
+ ssize_t insertAt(const void* item, size_t where, size_t numItems = 1);
+ void pop();
+ void push();
+ void push(const void* item);
+ ssize_t add();
+ ssize_t add(const void* item);
+ ssize_t replaceAt(size_t index);
+ ssize_t replaceAt(const void* item, size_t index);
+
+ /*! remove items */
+ ssize_t removeItemsAt(size_t index, size_t count = 1);
+ void clear();
+
+ const void* itemLocation(size_t index) const;
+ void* editItemLocation(size_t index);
+
+ typedef int (*compar_t)(const void* lhs, const void* rhs);
+ typedef int (*compar_r_t)(const void* lhs, const void* rhs, void* state);
+ status_t sort(compar_t cmp);
+ status_t sort(compar_r_t cmp, void* state);
+
+protected:
+ size_t itemSize() const;
+ void release_storage();
+
+ virtual void do_construct(void* storage, size_t num) const = 0;
+ virtual void do_destroy(void* storage, size_t num) const = 0;
+ virtual void do_copy(void* dest, const void* from, size_t num) const = 0;
+ virtual void do_splat(void* dest, const void* item, size_t num) const = 0;
+ virtual void do_move_forward(void* dest, const void* from, size_t num) const = 0;
+ virtual void do_move_backward(void* dest, const void* from, size_t num) const = 0;
+
+private:
+ void* _grow(size_t where, size_t amount);
+ void _shrink(size_t where, size_t amount);
+
+ inline void _do_construct(void* storage, size_t num) const;
+ inline void _do_destroy(void* storage, size_t num) const;
+ inline void _do_copy(void* dest, const void* from, size_t num) const;
+ inline void _do_splat(void* dest, const void* item, size_t num) const;
+ inline void _do_move_forward(void* dest, const void* from, size_t num) const;
+ inline void _do_move_backward(void* dest, const void* from, size_t num) const;
+
+ // These 2 fields are exposed in the inlines below,
+ // so they're set in stone.
+ void * mStorage; // base address of the vector
+ size_t mCount; // number of items
+
+ const uint32_t mFlags;
+ const size_t mItemSize;
+};
+
+
+
+class SortedVectorImpl : public VectorImpl
+{
+public:
+ SortedVectorImpl(size_t itemSize, uint32_t flags);
+ SortedVectorImpl(const VectorImpl& rhs);
+ virtual ~SortedVectorImpl();
+
+ SortedVectorImpl& operator = (const SortedVectorImpl& rhs);
+
+ //! finds the index of an item
+ ssize_t indexOf(const void* item) const;
+
+ //! finds where this item should be inserted
+ size_t orderOf(const void* item) const;
+
+ //! add an item in the right place (or replaces it if there is one)
+ ssize_t add(const void* item);
+
+ //! merges a vector into this one
+ ssize_t merge(const VectorImpl& vector);
+ ssize_t merge(const SortedVectorImpl& vector);
+
+ //! removes an item
+ ssize_t remove(const void* item);
+
+protected:
+ virtual int do_compare(const void* lhs, const void* rhs) const = 0;
+
+private:
+ ssize_t _indexOrderOf(const void* item, size_t* order = 0) const;
+
+ // these are made private, because they can't be used on a SortedVector
+ // (they don't have an implementation either)
+ ssize_t add();
+ void pop();
+ void push();
+ void push(const void* item);
+ ssize_t insertVectorAt(const VectorImpl& vector, size_t index);
+ ssize_t appendVector(const VectorImpl& vector);
+ ssize_t insertArrayAt(const void* array, size_t index, size_t length);
+ ssize_t appendArray(const void* array, size_t length);
+ ssize_t insertAt(size_t where, size_t numItems = 1);
+ ssize_t insertAt(const void* item, size_t where, size_t numItems = 1);
+ ssize_t replaceAt(size_t index);
+ ssize_t replaceAt(const void* item, size_t index);
+};
+
+}; // namespace android
+
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_VECTOR_IMPL_H
diff --git a/include/utils/ashmem.h b/include/utils/ashmem.h
new file mode 100644
index 0000000..0854775
--- /dev/null
+++ b/include/utils/ashmem.h
@@ -0,0 +1,41 @@
+/* utils/ashmem.h
+ **
+ ** Copyright 2008 The Android Open Source Project
+ **
+ ** This file is dual licensed. It may be redistributed and/or modified
+ ** under the terms of the Apache 2.0 License OR version 2 of the GNU
+ ** General Public License.
+ */
+
+#ifndef _UTILS_ASHMEM_H
+#define _UTILS_ASHMEM_H
+
+#include <linux/limits.h>
+#include <linux/ioctl.h>
+
+#define ASHMEM_NAME_LEN 256
+
+#define ASHMEM_NAME_DEF "dev/ashmem"
+
+/* Return values from ASHMEM_PIN: Was the mapping purged while unpinned? */
+#define ASHMEM_NOT_REAPED 0
+#define ASHMEM_WAS_REAPED 1
+
+/* Return values from ASHMEM_UNPIN: Is the mapping now pinned or unpinned? */
+#define ASHMEM_NOW_UNPINNED 0
+#define ASHMEM_NOW_PINNED 1
+
+#define __ASHMEMIOC 0x77
+
+#define ASHMEM_SET_NAME _IOW(__ASHMEMIOC, 1, char[ASHMEM_NAME_LEN])
+#define ASHMEM_GET_NAME _IOR(__ASHMEMIOC, 2, char[ASHMEM_NAME_LEN])
+#define ASHMEM_SET_SIZE _IOW(__ASHMEMIOC, 3, size_t)
+#define ASHMEM_GET_SIZE _IO(__ASHMEMIOC, 4)
+#define ASHMEM_SET_PROT_MASK _IOW(__ASHMEMIOC, 5, unsigned long)
+#define ASHMEM_GET_PROT_MASK _IO(__ASHMEMIOC, 6)
+#define ASHMEM_PIN _IO(__ASHMEMIOC, 7)
+#define ASHMEM_UNPIN _IO(__ASHMEMIOC, 8)
+#define ASHMEM_ISPINNED _IO(__ASHMEMIOC, 9)
+#define ASHMEM_PURGE_ALL_CACHES _IO(__ASHMEMIOC, 10)
+
+#endif /* _UTILS_ASHMEM_H */
diff --git a/include/utils/misc.h b/include/utils/misc.h
new file mode 100644
index 0000000..6cccec3
--- /dev/null
+++ b/include/utils/misc.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2005 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.
+ */
+
+//
+// Handy utility functions and portability code.
+//
+#ifndef _LIBS_UTILS_MISC_H
+#define _LIBS_UTILS_MISC_H
+
+#include <utils/Endian.h>
+
+/* get #of elements in a static array */
+#ifndef NELEM
+# define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
+#endif
+
+namespace android {
+
+typedef void (*sysprop_change_callback)(void);
+void add_sysprop_change_callback(sysprop_change_callback cb, int priority);
+void report_sysprop_change();
+
+}; // namespace android
+
+#endif // _LIBS_UTILS_MISC_H
diff --git a/include/utils/threads.h b/include/utils/threads.h
new file mode 100644
index 0000000..9de3382
--- /dev/null
+++ b/include/utils/threads.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBS_UTILS_THREADS_H
+#define _LIBS_UTILS_THREADS_H
+
+/*
+ * Please, DO NOT USE!
+ *
+ * This file is here only for legacy reasons. Instead, include directly
+ * the headers you need below.
+ *
+ */
+
+#include <utils/AndroidThreads.h>
+
+#ifdef __cplusplus
+#include <utils/Condition.h>
+#include <utils/Errors.h>
+#include <utils/Mutex.h>
+#include <utils/RWLock.h>
+#include <utils/Thread.h>
+#endif
+
+#endif // _LIBS_UTILS_THREADS_H
diff --git a/include/ziparchive/zip_archive.h b/include/ziparchive/zip_archive.h
new file mode 100644
index 0000000..5ef2ab0
--- /dev/null
+++ b/include/ziparchive/zip_archive.h
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Read-only access to Zip archives, with minimal heap allocation.
+ */
+#ifndef LIBZIPARCHIVE_ZIPARCHIVE_H_
+#define LIBZIPARCHIVE_ZIPARCHIVE_H_
+
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+#include <utils/Compat.h>
+
+__BEGIN_DECLS
+
+/* Zip compression methods we support */
+enum {
+ kCompressStored = 0, // no compression
+ kCompressDeflated = 8, // standard deflate
+};
+
+struct ZipString {
+ const uint8_t* name;
+ uint16_t name_length;
+
+ ZipString() {}
+
+ /*
+ * entry_name has to be an c-style string with only ASCII characters.
+ */
+ explicit ZipString(const char* entry_name)
+ : name(reinterpret_cast<const uint8_t*>(entry_name)), name_length(strlen(entry_name)) {}
+
+ bool operator==(const ZipString& rhs) const {
+ return name && (name_length == rhs.name_length) &&
+ (memcmp(name, rhs.name, name_length) == 0);
+ }
+
+ bool StartsWith(const ZipString& prefix) const {
+ return name && (name_length >= prefix.name_length) &&
+ (memcmp(name, prefix.name, prefix.name_length) == 0);
+ }
+
+ bool EndsWith(const ZipString& suffix) const {
+ return name && (name_length >= suffix.name_length) &&
+ (memcmp(name + name_length - suffix.name_length, suffix.name,
+ suffix.name_length) == 0);
+ }
+};
+
+/*
+ * Represents information about a zip entry in a zip file.
+ */
+struct ZipEntry {
+ // Compression method: One of kCompressStored or
+ // kCompressDeflated.
+ uint16_t method;
+
+ // Modification time. The zipfile format specifies
+ // that the first two little endian bytes contain the time
+ // and the last two little endian bytes contain the date.
+ uint32_t mod_time;
+
+ // 1 if this entry contains a data descriptor segment, 0
+ // otherwise.
+ uint8_t has_data_descriptor;
+
+ // Crc32 value of this ZipEntry. This information might
+ // either be stored in the local file header or in a special
+ // Data descriptor footer at the end of the file entry.
+ uint32_t crc32;
+
+ // Compressed length of this ZipEntry. Might be present
+ // either in the local file header or in the data descriptor
+ // footer.
+ uint32_t compressed_length;
+
+ // Uncompressed length of this ZipEntry. Might be present
+ // either in the local file header or in the data descriptor
+ // footer.
+ uint32_t uncompressed_length;
+
+ // The offset to the start of data for this ZipEntry.
+ off64_t offset;
+};
+
+typedef void* ZipArchiveHandle;
+
+/*
+ * Open a Zip archive, and sets handle to the value of the opaque
+ * handle for the file. This handle must be released by calling
+ * CloseArchive with this handle.
+ *
+ * Returns 0 on success, and negative values on failure.
+ */
+int32_t OpenArchive(const char* fileName, ZipArchiveHandle* handle);
+
+/*
+ * Like OpenArchive, but takes a file descriptor open for reading
+ * at the start of the file. The descriptor must be mappable (this does
+ * not allow access to a stream).
+ *
+ * Sets handle to the value of the opaque handle for this file descriptor.
+ * This handle must be released by calling CloseArchive with this handle.
+ *
+ * If assume_ownership parameter is 'true' calling CloseArchive will close
+ * the file.
+ *
+ * This function maps and scans the central directory and builds a table
+ * of entries for future lookups.
+ *
+ * "debugFileName" will appear in error messages, but is not otherwise used.
+ *
+ * Returns 0 on success, and negative values on failure.
+ */
+int32_t OpenArchiveFd(const int fd, const char* debugFileName,
+ ZipArchiveHandle *handle, bool assume_ownership = true);
+
+/*
+ * Close archive, releasing resources associated with it. This will
+ * unmap the central directory of the zipfile and free all internal
+ * data structures associated with the file. It is an error to use
+ * this handle for any further operations without an intervening
+ * call to one of the OpenArchive variants.
+ */
+void CloseArchive(ZipArchiveHandle handle);
+
+/*
+ * Find an entry in the Zip archive, by name. |entryName| must be a null
+ * terminated string, and |data| must point to a writeable memory location.
+ *
+ * Returns 0 if an entry is found, and populates |data| with information
+ * about this entry. Returns negative values otherwise.
+ *
+ * It's important to note that |data->crc32|, |data->compLen| and
+ * |data->uncompLen| might be set to values from the central directory
+ * if this file entry contains a data descriptor footer. To verify crc32s
+ * and length, a call to VerifyCrcAndLengths must be made after entry data
+ * has been processed.
+ */
+int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName,
+ ZipEntry* data);
+
+/*
+ * Start iterating over all entries of a zip file. The order of iteration
+ * is not guaranteed to be the same as the order of elements
+ * in the central directory but is stable for a given zip file. |cookie| will
+ * contain the value of an opaque cookie which can be used to make one or more
+ * calls to Next. All calls to StartIteration must be matched by a call to
+ * EndIteration to free any allocated memory.
+ *
+ * This method also accepts optional prefix and suffix to restrict iteration to
+ * entry names that start with |optional_prefix| or end with |optional_suffix|.
+ *
+ * Returns 0 on success and negative values on failure.
+ */
+int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr,
+ const ZipString* optional_prefix,
+ const ZipString* optional_suffix);
+
+/*
+ * Advance to the next element in the zipfile in iteration order.
+ *
+ * Returns 0 on success, -1 if there are no more elements in this
+ * archive and lower negative values on failure.
+ */
+int32_t Next(void* cookie, ZipEntry* data, ZipString* name);
+
+/*
+ * End iteration over all entries of a zip file and frees the memory allocated
+ * in StartIteration.
+ */
+void EndIteration(void* cookie);
+
+/*
+ * Uncompress and write an entry to an open file identified by |fd|.
+ * |entry->uncompressed_length| bytes will be written to the file at
+ * its current offset, and the file will be truncated at the end of
+ * the uncompressed data.
+ *
+ * Returns 0 on success and negative values on failure.
+ */
+int32_t ExtractEntryToFile(ZipArchiveHandle handle, ZipEntry* entry, int fd);
+
+/**
+ * Uncompress a given zip entry to the memory region at |begin| and of
+ * size |size|. This size is expected to be the same as the *declared*
+ * uncompressed length of the zip entry. It is an error if the *actual*
+ * number of uncompressed bytes differs from this number.
+ *
+ * Returns 0 on success and negative values on failure.
+ */
+int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry,
+ uint8_t* begin, uint32_t size);
+
+int GetFileDescriptor(const ZipArchiveHandle handle);
+
+const char* ErrorCodeString(int32_t error_code);
+
+__END_DECLS
+
+#endif // LIBZIPARCHIVE_ZIPARCHIVE_H_
diff --git a/init/Android.mk b/init/Android.mk
new file mode 100644
index 0000000..45b002d
--- /dev/null
+++ b/init/Android.mk
@@ -0,0 +1,98 @@
+# Copyright 2005 The Android Open Source Project
+
+LOCAL_PATH:= $(call my-dir)
+
+# --
+
+ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
+init_options += -DALLOW_LOCAL_PROP_OVERRIDE=1 -DALLOW_PERMISSIVE_SELINUX=1
+else
+init_options += -DALLOW_LOCAL_PROP_OVERRIDE=0 -DALLOW_PERMISSIVE_SELINUX=0
+endif
+
+init_options += -DLOG_UEVENTS=0
+
+init_cflags += \
+ $(init_options) \
+ -Wall -Wextra \
+ -Wno-unused-parameter \
+ -Werror \
+
+# --
+
+include $(CLEAR_VARS)
+LOCAL_CPPFLAGS := $(init_cflags)
+LOCAL_SRC_FILES:= \
+ init_parser.cpp \
+ log.cpp \
+ parser.cpp \
+ util.cpp \
+
+LOCAL_STATIC_LIBRARIES := libbase
+LOCAL_MODULE := libinit
+LOCAL_CLANG := true
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_CPPFLAGS := $(init_cflags)
+LOCAL_SRC_FILES:= \
+ bootchart.cpp \
+ builtins.cpp \
+ devices.cpp \
+ init.cpp \
+ keychords.cpp \
+ property_service.cpp \
+ signal_handler.cpp \
+ ueventd.cpp \
+ ueventd_parser.cpp \
+ watchdogd.cpp \
+
+LOCAL_MODULE:= init
+LOCAL_C_INCLUDES += \
+ system/extras/ext4_utils \
+ system/core/mkbootimg
+
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
+LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)
+
+LOCAL_STATIC_LIBRARIES := \
+ libinit \
+ libfs_mgr \
+ libsquashfs_utils \
+ liblogwrap \
+ libcutils \
+ libbase \
+ libext4_utils_static \
+ libutils \
+ liblog \
+ libc \
+ libselinux \
+ libmincrypt \
+ libc++_static \
+ libdl
+
+# Create symlinks
+LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT)/sbin; \
+ ln -sf ../init $(TARGET_ROOT_OUT)/sbin/ueventd; \
+ ln -sf ../init $(TARGET_ROOT_OUT)/sbin/watchdogd
+
+LOCAL_CLANG := true
+include $(BUILD_EXECUTABLE)
+
+
+
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := init_tests
+LOCAL_SRC_FILES := \
+ init_parser_test.cpp \
+ util_test.cpp \
+
+LOCAL_SHARED_LIBRARIES += \
+ libcutils \
+ libbase \
+
+LOCAL_STATIC_LIBRARIES := libinit
+LOCAL_CLANG := true
+include $(BUILD_NATIVE_TEST)
diff --git a/init/MODULE_LICENSE_APACHE2 b/init/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/init/MODULE_LICENSE_APACHE2
diff --git a/init/NOTICE b/init/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/init/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/init/bootchart.cpp b/init/bootchart.cpp
new file mode 100644
index 0000000..e5b153a
--- /dev/null
+++ b/init/bootchart.cpp
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "bootchart.h"
+#include "keywords.h"
+#include "log.h"
+#include "property_service.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+
+#include <base/file.h>
+
+#define LOG_ROOT "/data/bootchart"
+#define LOG_STAT LOG_ROOT"/proc_stat.log"
+#define LOG_PROCS LOG_ROOT"/proc_ps.log"
+#define LOG_DISK LOG_ROOT"/proc_diskstats.log"
+#define LOG_HEADER LOG_ROOT"/header"
+#define LOG_ACCT LOG_ROOT"/kernel_pacct"
+
+#define LOG_STARTFILE LOG_ROOT"/start"
+#define LOG_STOPFILE LOG_ROOT"/stop"
+
+// Polling period in ms.
+static const int BOOTCHART_POLLING_MS = 200;
+
+// Max polling time in seconds.
+static const int BOOTCHART_MAX_TIME_SEC = 10*60;
+
+static long long g_last_bootchart_time;
+static int g_remaining_samples;
+
+static FILE* log_stat;
+static FILE* log_procs;
+static FILE* log_disks;
+
+static long long get_uptime_jiffies() {
+ std::string uptime;
+ if (!android::base::ReadFileToString("/proc/uptime", &uptime)) {
+ return 0;
+ }
+ return 100LL * strtod(uptime.c_str(), NULL);
+}
+
+static void log_header() {
+ char date[32];
+ time_t now_t = time(NULL);
+ struct tm now = *localtime(&now_t);
+ strftime(date, sizeof(date), "%F %T", &now);
+
+ utsname uts;
+ if (uname(&uts) == -1) {
+ return;
+ }
+
+ std::string fingerprint = property_get("ro.build.fingerprint");
+ if (fingerprint.empty()) {
+ return;
+ }
+
+ std::string kernel_cmdline;
+ android::base::ReadFileToString("/proc/cmdline", &kernel_cmdline);
+
+ FILE* out = fopen(LOG_HEADER, "we");
+ if (out == NULL) {
+ return;
+ }
+ fprintf(out, "version = Android init 0.8 " __TIME__ "\n");
+ fprintf(out, "title = Boot chart for Android (%s)\n", date);
+ fprintf(out, "system.uname = %s %s %s %s\n", uts.sysname, uts.release, uts.version, uts.machine);
+ fprintf(out, "system.release = %s\n", fingerprint.c_str());
+ // TODO: use /proc/cpuinfo "model name" line for x86, "Processor" line for arm.
+ fprintf(out, "system.cpu = %s\n", uts.machine);
+ fprintf(out, "system.kernel.options = %s\n", kernel_cmdline.c_str());
+ fclose(out);
+}
+
+static void do_log_uptime(FILE* log) {
+ fprintf(log, "%lld\n", get_uptime_jiffies());
+}
+
+static void do_log_file(FILE* log, const char* procfile) {
+ do_log_uptime(log);
+
+ std::string content;
+ if (android::base::ReadFileToString(procfile, &content)) {
+ fprintf(log, "%s\n", content.c_str());
+ }
+}
+
+static void do_log_procs(FILE* log) {
+ do_log_uptime(log);
+
+ std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir("/proc"), closedir);
+ struct dirent* entry;
+ while ((entry = readdir(dir.get())) != NULL) {
+ // Only match numeric values.
+ char* end;
+ int pid = strtol(entry->d_name, &end, 10);
+ if (end != NULL && end > entry->d_name && *end == 0) {
+ char filename[32];
+
+ // /proc/<pid>/stat only has truncated task names, so get the full
+ // name from /proc/<pid>/cmdline.
+ snprintf(filename, sizeof(filename), "/proc/%d/cmdline", pid);
+ std::string cmdline;
+ android::base::ReadFileToString(filename, &cmdline);
+ const char* full_name = cmdline.c_str(); // So we stop at the first NUL.
+
+ // Read process stat line.
+ snprintf(filename, sizeof(filename), "/proc/%d/stat", pid);
+ std::string stat;
+ if (android::base::ReadFileToString(filename, &stat)) {
+ if (!cmdline.empty()) {
+ // Substitute the process name with its real name.
+ size_t open = stat.find('(');
+ size_t close = stat.find_last_of(')');
+ if (open != std::string::npos && close != std::string::npos) {
+ stat.replace(open + 1, close - open - 1, full_name);
+ }
+ }
+ fputs(stat.c_str(), log);
+ }
+ }
+ }
+
+ fputc('\n', log);
+}
+
+static int bootchart_init() {
+ int timeout = 0;
+
+ std::string start;
+ android::base::ReadFileToString(LOG_STARTFILE, &start);
+ if (!start.empty()) {
+ timeout = atoi(start.c_str());
+ } else {
+ // When running with emulator, androidboot.bootchart=<timeout>
+ // might be passed by as kernel parameters to specify the bootchart
+ // timeout. this is useful when using -wipe-data since the /data
+ // partition is fresh.
+ std::string cmdline;
+ const char* s;
+ android::base::ReadFileToString("/proc/cmdline", &cmdline);
+#define KERNEL_OPTION "androidboot.bootchart="
+ if ((s = strstr(cmdline.c_str(), KERNEL_OPTION)) != NULL) {
+ timeout = atoi(s + sizeof(KERNEL_OPTION) - 1);
+ }
+ }
+ if (timeout == 0)
+ return 0;
+
+ if (timeout > BOOTCHART_MAX_TIME_SEC)
+ timeout = BOOTCHART_MAX_TIME_SEC;
+
+ int count = (timeout*1000 + BOOTCHART_POLLING_MS-1)/BOOTCHART_POLLING_MS;
+
+ log_stat = fopen(LOG_STAT, "we");
+ if (log_stat == NULL) {
+ return -1;
+ }
+ log_procs = fopen(LOG_PROCS, "we");
+ if (log_procs == NULL) {
+ fclose(log_stat);
+ return -1;
+ }
+ log_disks = fopen(LOG_DISK, "we");
+ if (log_disks == NULL) {
+ fclose(log_stat);
+ fclose(log_procs);
+ return -1;
+ }
+
+ // Create kernel process accounting file.
+ close(open(LOG_ACCT, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644));
+ acct(LOG_ACCT);
+
+ log_header();
+ return count;
+}
+
+int do_bootchart_init(int nargs, char** args) {
+ g_remaining_samples = bootchart_init();
+ if (g_remaining_samples < 0) {
+ ERROR("Bootcharting init failure: %s\n", strerror(errno));
+ } else if (g_remaining_samples > 0) {
+ NOTICE("Bootcharting started (will run for %d s).\n",
+ (g_remaining_samples * BOOTCHART_POLLING_MS) / 1000);
+ } else {
+ NOTICE("Not bootcharting.\n");
+ }
+ return 0;
+}
+
+static int bootchart_step() {
+ do_log_file(log_stat, "/proc/stat");
+ do_log_file(log_disks, "/proc/diskstats");
+ do_log_procs(log_procs);
+
+ // Stop if /data/bootchart/stop contains 1.
+ std::string stop;
+ if (android::base::ReadFileToString(LOG_STOPFILE, &stop) && stop == "1") {
+ return -1;
+ }
+
+ return 0;
+}
+
+/* called to get time (in ms) used by bootchart */
+static long long bootchart_gettime() {
+ return 10LL*get_uptime_jiffies();
+}
+
+static void bootchart_finish() {
+ unlink(LOG_STOPFILE);
+ fclose(log_stat);
+ fclose(log_disks);
+ fclose(log_procs);
+ acct(NULL);
+}
+
+void bootchart_sample(int* timeout) {
+ // Do we have any more bootcharting to do?
+ if (g_remaining_samples <= 0) {
+ return;
+ }
+
+ long long current_time = bootchart_gettime();
+ int elapsed_time = current_time - g_last_bootchart_time;
+
+ if (elapsed_time >= BOOTCHART_POLLING_MS) {
+ /* count missed samples */
+ while (elapsed_time >= BOOTCHART_POLLING_MS) {
+ elapsed_time -= BOOTCHART_POLLING_MS;
+ g_remaining_samples--;
+ }
+ /* count may be negative, take a sample anyway */
+ g_last_bootchart_time = current_time;
+ if (bootchart_step() < 0 || g_remaining_samples <= 0) {
+ bootchart_finish();
+ g_remaining_samples = 0;
+ }
+ }
+ if (g_remaining_samples > 0) {
+ int remaining_time = BOOTCHART_POLLING_MS - elapsed_time;
+ if (*timeout < 0 || *timeout > remaining_time) {
+ *timeout = remaining_time;
+ }
+ }
+}
diff --git a/init/bootchart.h b/init/bootchart.h
new file mode 100644
index 0000000..cf61d83
--- /dev/null
+++ b/init/bootchart.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _BOOTCHART_H
+#define _BOOTCHART_H
+
+void bootchart_sample(int* timeout);
+
+#endif /* _BOOTCHART_H */
diff --git a/init/builtins.cpp b/init/builtins.cpp
new file mode 100644
index 0000000..d05f046
--- /dev/null
+++ b/init/builtins.cpp
@@ -0,0 +1,885 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <mntent.h>
+#include <net/if.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/mount.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <linux/loop.h>
+#include <ext4_crypt_init_extensions.h>
+
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+
+#include <fs_mgr.h>
+#include <base/stringprintf.h>
+#include <cutils/partition_utils.h>
+#include <cutils/android_reboot.h>
+#include <logwrap/logwrap.h>
+#include <private/android_filesystem_config.h>
+
+#include "init.h"
+#include "keywords.h"
+#include "property_service.h"
+#include "devices.h"
+#include "init_parser.h"
+#include "util.h"
+#include "log.h"
+
+#define chmod DO_NOT_USE_CHMOD_USE_FCHMODAT_SYMLINK_NOFOLLOW
+#define UNMOUNT_CHECK_MS 5000
+#define UNMOUNT_CHECK_TIMES 10
+
+int add_environment(const char *name, const char *value);
+
+// System call provided by bionic but not in any header file.
+extern "C" int init_module(void *, unsigned long, const char *);
+
+static int insmod(const char *filename, char *options)
+{
+ std::string module;
+ if (!read_file(filename, &module)) {
+ return -1;
+ }
+
+ // TODO: use finit_module for >= 3.8 kernels.
+ return init_module(&module[0], module.size(), options);
+}
+
+static int __ifupdown(const char *interface, int up)
+{
+ struct ifreq ifr;
+ int s, ret;
+
+ strlcpy(ifr.ifr_name, interface, IFNAMSIZ);
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0)
+ return -1;
+
+ ret = ioctl(s, SIOCGIFFLAGS, &ifr);
+ if (ret < 0) {
+ goto done;
+ }
+
+ if (up)
+ ifr.ifr_flags |= IFF_UP;
+ else
+ ifr.ifr_flags &= ~IFF_UP;
+
+ ret = ioctl(s, SIOCSIFFLAGS, &ifr);
+
+done:
+ close(s);
+ return ret;
+}
+
+static void service_start_if_not_disabled(struct service *svc)
+{
+ if (!(svc->flags & SVC_DISABLED)) {
+ service_start(svc, NULL);
+ } else {
+ svc->flags |= SVC_DISABLED_START;
+ }
+}
+
+static void unmount_and_fsck(const struct mntent *entry)
+{
+ if (strcmp(entry->mnt_type, "f2fs") && strcmp(entry->mnt_type, "ext4"))
+ return;
+
+ /* First, lazily unmount the directory. This unmount request finishes when
+ * all processes that open a file or directory in |entry->mnt_dir| exit.
+ */
+ TEMP_FAILURE_RETRY(umount2(entry->mnt_dir, MNT_DETACH));
+
+ /* Next, kill all processes except init, kthreadd, and kthreadd's
+ * children to finish the lazy unmount. Killing all processes here is okay
+ * because this callback function is only called right before reboot().
+ * It might be cleaner to selectively kill processes that actually use
+ * |entry->mnt_dir| rather than killing all, probably by reusing a function
+ * like killProcessesWithOpenFiles() in vold/, but the selinux policy does
+ * not allow init to scan /proc/<pid> files which the utility function
+ * heavily relies on. The policy does not allow the process to execute
+ * killall/pkill binaries either. Note that some processes might
+ * automatically restart after kill(), but that is not really a problem
+ * because |entry->mnt_dir| is no longer visible to such new processes.
+ */
+ service_for_each(service_stop);
+ TEMP_FAILURE_RETRY(kill(-1, SIGKILL));
+
+ int count = 0;
+ while (count++ < UNMOUNT_CHECK_TIMES) {
+ int fd = TEMP_FAILURE_RETRY(open(entry->mnt_fsname, O_RDONLY | O_EXCL));
+ if (fd >= 0) {
+ /* |entry->mnt_dir| has sucessfully been unmounted. */
+ close(fd);
+ break;
+ } else if (errno == EBUSY) {
+ /* Some processes using |entry->mnt_dir| are still alive. Wait for a
+ * while then retry.
+ */
+ TEMP_FAILURE_RETRY(
+ usleep(UNMOUNT_CHECK_MS * 1000 / UNMOUNT_CHECK_TIMES));
+ continue;
+ } else {
+ /* Cannot open the device. Give up. */
+ return;
+ }
+ }
+
+ int st;
+ if (!strcmp(entry->mnt_type, "f2fs")) {
+ const char *f2fs_argv[] = {
+ "/system/bin/fsck.f2fs", "-f", entry->mnt_fsname,
+ };
+ android_fork_execvp_ext(ARRAY_SIZE(f2fs_argv), (char **)f2fs_argv,
+ &st, true, LOG_KLOG, true, NULL);
+ } else if (!strcmp(entry->mnt_type, "ext4")) {
+ const char *ext4_argv[] = {
+ "/system/bin/e2fsck", "-f", "-y", entry->mnt_fsname,
+ };
+ android_fork_execvp_ext(ARRAY_SIZE(ext4_argv), (char **)ext4_argv,
+ &st, true, LOG_KLOG, true, NULL);
+ }
+}
+
+int do_class_start(int nargs, char **args)
+{
+ /* Starting a class does not start services
+ * which are explicitly disabled. They must
+ * be started individually.
+ */
+ service_for_each_class(args[1], service_start_if_not_disabled);
+ return 0;
+}
+
+int do_class_stop(int nargs, char **args)
+{
+ service_for_each_class(args[1], service_stop);
+ return 0;
+}
+
+int do_class_reset(int nargs, char **args)
+{
+ service_for_each_class(args[1], service_reset);
+ return 0;
+}
+
+int do_domainname(int nargs, char **args)
+{
+ return write_file("/proc/sys/kernel/domainname", args[1]);
+}
+
+int do_enable(int nargs, char **args)
+{
+ struct service *svc;
+ svc = service_find_by_name(args[1]);
+ if (svc) {
+ svc->flags &= ~(SVC_DISABLED | SVC_RC_DISABLED);
+ if (svc->flags & SVC_DISABLED_START) {
+ service_start(svc, NULL);
+ }
+ } else {
+ return -1;
+ }
+ return 0;
+}
+
+int do_exec(int nargs, char** args) {
+ service* svc = make_exec_oneshot_service(nargs, args);
+ if (svc == NULL) {
+ return -1;
+ }
+ service_start(svc, NULL);
+ return 0;
+}
+
+int do_export(int nargs, char **args)
+{
+ return add_environment(args[1], args[2]);
+}
+
+int do_hostname(int nargs, char **args)
+{
+ return write_file("/proc/sys/kernel/hostname", args[1]);
+}
+
+int do_ifup(int nargs, char **args)
+{
+ return __ifupdown(args[1], 1);
+}
+
+
+static int do_insmod_inner(int nargs, char **args, int opt_len)
+{
+ char options[opt_len + 1];
+ int i;
+
+ options[0] = '\0';
+ if (nargs > 2) {
+ strcpy(options, args[2]);
+ for (i = 3; i < nargs; ++i) {
+ strcat(options, " ");
+ strcat(options, args[i]);
+ }
+ }
+
+ return insmod(args[1], options);
+}
+
+int do_insmod(int nargs, char **args)
+{
+ int i;
+ int size = 0;
+
+ if (nargs > 2) {
+ for (i = 2; i < nargs; ++i)
+ size += strlen(args[i]) + 1;
+ }
+
+ return do_insmod_inner(nargs, args, size);
+}
+
+int do_mkdir(int nargs, char **args)
+{
+ mode_t mode = 0755;
+ int ret;
+
+ /* mkdir <path> [mode] [owner] [group] */
+
+ if (nargs >= 3) {
+ mode = strtoul(args[2], 0, 8);
+ }
+
+ ret = make_dir(args[1], mode);
+ /* chmod in case the directory already exists */
+ if (ret == -1 && errno == EEXIST) {
+ ret = fchmodat(AT_FDCWD, args[1], mode, AT_SYMLINK_NOFOLLOW);
+ }
+ if (ret == -1) {
+ return -errno;
+ }
+
+ if (nargs >= 4) {
+ uid_t uid = decode_uid(args[3]);
+ gid_t gid = -1;
+
+ if (nargs == 5) {
+ gid = decode_uid(args[4]);
+ }
+
+ if (lchown(args[1], uid, gid) == -1) {
+ return -errno;
+ }
+
+ /* chown may have cleared S_ISUID and S_ISGID, chmod again */
+ if (mode & (S_ISUID | S_ISGID)) {
+ ret = fchmodat(AT_FDCWD, args[1], mode, AT_SYMLINK_NOFOLLOW);
+ if (ret == -1) {
+ return -errno;
+ }
+ }
+ }
+
+ return e4crypt_set_directory_policy(args[1]);
+}
+
+static struct {
+ const char *name;
+ unsigned flag;
+} mount_flags[] = {
+ { "noatime", MS_NOATIME },
+ { "noexec", MS_NOEXEC },
+ { "nosuid", MS_NOSUID },
+ { "nodev", MS_NODEV },
+ { "nodiratime", MS_NODIRATIME },
+ { "ro", MS_RDONLY },
+ { "rw", 0 },
+ { "remount", MS_REMOUNT },
+ { "bind", MS_BIND },
+ { "rec", MS_REC },
+ { "unbindable", MS_UNBINDABLE },
+ { "private", MS_PRIVATE },
+ { "slave", MS_SLAVE },
+ { "shared", MS_SHARED },
+ { "defaults", 0 },
+ { 0, 0 },
+};
+
+#define DATA_MNT_POINT "/data"
+
+/* mount <type> <device> <path> <flags ...> <options> */
+int do_mount(int nargs, char **args)
+{
+ char tmp[64];
+ char *source, *target, *system;
+ char *options = NULL;
+ unsigned flags = 0;
+ int n, i;
+ int wait = 0;
+
+ for (n = 4; n < nargs; n++) {
+ for (i = 0; mount_flags[i].name; i++) {
+ if (!strcmp(args[n], mount_flags[i].name)) {
+ flags |= mount_flags[i].flag;
+ break;
+ }
+ }
+
+ if (!mount_flags[i].name) {
+ if (!strcmp(args[n], "wait"))
+ wait = 1;
+ /* if our last argument isn't a flag, wolf it up as an option string */
+ else if (n + 1 == nargs)
+ options = args[n];
+ }
+ }
+
+ system = args[1];
+ source = args[2];
+ target = args[3];
+
+ if (!strncmp(source, "mtd@", 4)) {
+ n = mtd_name_to_number(source + 4);
+ if (n < 0) {
+ return -1;
+ }
+
+ snprintf(tmp, sizeof(tmp), "/dev/block/mtdblock%d", n);
+
+ if (wait)
+ wait_for_file(tmp, COMMAND_RETRY_TIMEOUT);
+ if (mount(tmp, target, system, flags, options) < 0) {
+ return -1;
+ }
+
+ goto exit_success;
+ } else if (!strncmp(source, "loop@", 5)) {
+ int mode, loop, fd;
+ struct loop_info info;
+
+ mode = (flags & MS_RDONLY) ? O_RDONLY : O_RDWR;
+ fd = open(source + 5, mode | O_CLOEXEC);
+ if (fd < 0) {
+ return -1;
+ }
+
+ for (n = 0; ; n++) {
+ snprintf(tmp, sizeof(tmp), "/dev/block/loop%d", n);
+ loop = open(tmp, mode | O_CLOEXEC);
+ if (loop < 0) {
+ close(fd);
+ return -1;
+ }
+
+ /* if it is a blank loop device */
+ if (ioctl(loop, LOOP_GET_STATUS, &info) < 0 && errno == ENXIO) {
+ /* if it becomes our loop device */
+ if (ioctl(loop, LOOP_SET_FD, fd) >= 0) {
+ close(fd);
+
+ if (mount(tmp, target, system, flags, options) < 0) {
+ ioctl(loop, LOOP_CLR_FD, 0);
+ close(loop);
+ return -1;
+ }
+
+ close(loop);
+ goto exit_success;
+ }
+ }
+
+ close(loop);
+ }
+
+ close(fd);
+ ERROR("out of loopback devices");
+ return -1;
+ } else {
+ if (wait)
+ wait_for_file(source, COMMAND_RETRY_TIMEOUT);
+ if (mount(source, target, system, flags, options) < 0) {
+ return -1;
+ }
+
+ }
+
+exit_success:
+ return 0;
+
+}
+
+static int wipe_data_via_recovery()
+{
+ mkdir("/cache/recovery", 0700);
+ int fd = open("/cache/recovery/command", O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC, 0600);
+ if (fd >= 0) {
+ write(fd, "--wipe_data\n", strlen("--wipe_data\n") + 1);
+ write(fd, "--reason=wipe_data_via_recovery\n", strlen("--reason=wipe_data_via_recovery\n") + 1);
+ close(fd);
+ } else {
+ ERROR("could not open /cache/recovery/command\n");
+ return -1;
+ }
+ android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
+ while (1) { pause(); } // never reached
+}
+
+/*
+ * This function might request a reboot, in which case it will
+ * not return.
+ */
+int do_mount_all(int nargs, char **args)
+{
+ pid_t pid;
+ int ret = -1;
+ int child_ret = -1;
+ int status;
+ struct fstab *fstab;
+
+ if (nargs != 2) {
+ return -1;
+ }
+ const char* fstabfile = args[1];
+ /*
+ * Call fs_mgr_mount_all() to mount all filesystems. We fork(2) and
+ * do the call in the child to provide protection to the main init
+ * process if anything goes wrong (crash or memory leak), and wait for
+ * the child to finish in the parent.
+ */
+ pid = fork();
+ if (pid > 0) {
+ /* Parent. Wait for the child to return */
+ int wp_ret = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
+ if (wp_ret < 0) {
+ /* Unexpected error code. We will continue anyway. */
+ NOTICE("waitpid failed rc=%d: %s\n", wp_ret, strerror(errno));
+ }
+
+ if (WIFEXITED(status)) {
+ ret = WEXITSTATUS(status);
+ } else {
+ ret = -1;
+ }
+ } else if (pid == 0) {
+ /* child, call fs_mgr_mount_all() */
+ klog_set_level(6); /* So we can see what fs_mgr_mount_all() does */
+ fstab = fs_mgr_read_fstab(fstabfile);
+ child_ret = fs_mgr_mount_all(fstab);
+ fs_mgr_free_fstab(fstab);
+ if (child_ret == -1) {
+ ERROR("fs_mgr_mount_all returned an error\n");
+ }
+ _exit(child_ret);
+ } else {
+ /* fork failed, return an error */
+ return -1;
+ }
+
+ if (ret == FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION) {
+ property_set("vold.decrypt", "trigger_encryption");
+ } else if (ret == FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED) {
+ property_set("ro.crypto.state", "encrypted");
+ property_set("ro.crypto.type", "block");
+ property_set("vold.decrypt", "trigger_default_encryption");
+ } else if (ret == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
+ property_set("ro.crypto.state", "unencrypted");
+ /* If fs_mgr determined this is an unencrypted device, then trigger
+ * that action.
+ */
+ action_for_each_trigger("nonencrypted", action_add_queue_tail);
+ } else if (ret == FS_MGR_MNTALL_DEV_NEEDS_RECOVERY) {
+ /* Setup a wipe via recovery, and reboot into recovery */
+ ERROR("fs_mgr_mount_all suggested recovery, so wiping data via recovery.\n");
+ ret = wipe_data_via_recovery();
+ /* If reboot worked, there is no return. */
+ } else if (ret == FS_MGR_MNTALL_DEV_DEFAULT_FILE_ENCRYPTED) {
+ if (e4crypt_install_keyring()) {
+ return -1;
+ }
+ property_set("ro.crypto.state", "encrypted");
+ property_set("ro.crypto.type", "file");
+
+ // Although encrypted, we have device key, so we do not need to
+ // do anything different from the nonencrypted case.
+ action_for_each_trigger("nonencrypted", action_add_queue_tail);
+ } else if (ret == FS_MGR_MNTALL_DEV_NON_DEFAULT_FILE_ENCRYPTED) {
+ if (e4crypt_install_keyring()) {
+ return -1;
+ }
+ property_set("ro.crypto.state", "encrypted");
+ property_set("ro.crypto.type", "file");
+ property_set("vold.decrypt", "trigger_restart_min_framework");
+ } else if (ret > 0) {
+ ERROR("fs_mgr_mount_all returned unexpected error %d\n", ret);
+ }
+ /* else ... < 0: error */
+
+ return ret;
+}
+
+int do_swapon_all(int nargs, char **args)
+{
+ struct fstab *fstab;
+ int ret;
+
+ fstab = fs_mgr_read_fstab(args[1]);
+ ret = fs_mgr_swapon_all(fstab);
+ fs_mgr_free_fstab(fstab);
+
+ return ret;
+}
+
+int do_setprop(int nargs, char **args)
+{
+ const char *name = args[1];
+ const char *value = args[2];
+ property_set(name, value);
+ return 0;
+}
+
+int do_setrlimit(int nargs, char **args)
+{
+ struct rlimit limit;
+ int resource;
+ resource = atoi(args[1]);
+ limit.rlim_cur = atoi(args[2]);
+ limit.rlim_max = atoi(args[3]);
+ return setrlimit(resource, &limit);
+}
+
+int do_start(int nargs, char **args)
+{
+ struct service *svc;
+ svc = service_find_by_name(args[1]);
+ if (svc) {
+ service_start(svc, NULL);
+ }
+ return 0;
+}
+
+int do_stop(int nargs, char **args)
+{
+ struct service *svc;
+ svc = service_find_by_name(args[1]);
+ if (svc) {
+ service_stop(svc);
+ }
+ return 0;
+}
+
+int do_restart(int nargs, char **args)
+{
+ struct service *svc;
+ svc = service_find_by_name(args[1]);
+ if (svc) {
+ service_restart(svc);
+ }
+ return 0;
+}
+
+int do_powerctl(int nargs, char **args)
+{
+ const char* command = args[1];
+ int len = 0;
+ int cmd = 0;
+ const char *reboot_target;
+ void (*callback_on_ro_remount)(const struct mntent*) = NULL;
+
+ if (strncmp(command, "shutdown", 8) == 0) {
+ cmd = ANDROID_RB_POWEROFF;
+ len = 8;
+ callback_on_ro_remount = unmount_and_fsck;
+ } else if (strncmp(command, "reboot", 6) == 0) {
+ cmd = ANDROID_RB_RESTART2;
+ len = 6;
+ } else {
+ ERROR("powerctl: unrecognized command '%s'\n", command);
+ return -EINVAL;
+ }
+
+ if (command[len] == ',') {
+ reboot_target = &command[len + 1];
+ } else if (command[len] == '\0') {
+ reboot_target = "";
+ } else {
+ ERROR("powerctl: unrecognized reboot target '%s'\n", &command[len]);
+ return -EINVAL;
+ }
+
+ return android_reboot_with_callback(cmd, 0, reboot_target,
+ callback_on_ro_remount);
+}
+
+int do_trigger(int nargs, char **args)
+{
+ action_for_each_trigger(args[1], action_add_queue_tail);
+ return 0;
+}
+
+int do_symlink(int nargs, char **args)
+{
+ return symlink(args[1], args[2]);
+}
+
+int do_rm(int nargs, char **args)
+{
+ return unlink(args[1]);
+}
+
+int do_rmdir(int nargs, char **args)
+{
+ return rmdir(args[1]);
+}
+
+int do_sysclktz(int nargs, char **args)
+{
+ struct timezone tz;
+
+ if (nargs != 2)
+ return -1;
+
+ memset(&tz, 0, sizeof(tz));
+ tz.tz_minuteswest = atoi(args[1]);
+ if (settimeofday(NULL, &tz))
+ return -1;
+ return 0;
+}
+
+int do_verity_load_state(int nargs, char **args) {
+ int mode = -1;
+ int rc = fs_mgr_load_verity_state(&mode);
+ if (rc == 0 && mode == VERITY_MODE_LOGGING) {
+ action_for_each_trigger("verity-logging", action_add_queue_tail);
+ }
+ return rc;
+}
+
+static void verity_update_property(fstab_rec *fstab, const char *mount_point, int mode, int status) {
+ property_set(android::base::StringPrintf("partition.%s.verified", mount_point).c_str(),
+ android::base::StringPrintf("%d", mode).c_str());
+}
+
+int do_verity_update_state(int nargs, char** args) {
+ return fs_mgr_update_verity_state(verity_update_property);
+}
+
+int do_write(int nargs, char **args)
+{
+ const char *path = args[1];
+ const char *value = args[2];
+ return write_file(path, value);
+}
+
+int do_copy(int nargs, char **args)
+{
+ char *buffer = NULL;
+ int rc = 0;
+ int fd1 = -1, fd2 = -1;
+ struct stat info;
+ int brtw, brtr;
+ char *p;
+
+ if (nargs != 3)
+ return -1;
+
+ if (stat(args[1], &info) < 0)
+ return -1;
+
+ if ((fd1 = open(args[1], O_RDONLY|O_CLOEXEC)) < 0)
+ goto out_err;
+
+ if ((fd2 = open(args[2], O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0660)) < 0)
+ goto out_err;
+
+ if (!(buffer = (char*) malloc(info.st_size)))
+ goto out_err;
+
+ p = buffer;
+ brtr = info.st_size;
+ while(brtr) {
+ rc = read(fd1, p, brtr);
+ if (rc < 0)
+ goto out_err;
+ if (rc == 0)
+ break;
+ p += rc;
+ brtr -= rc;
+ }
+
+ p = buffer;
+ brtw = info.st_size;
+ while(brtw) {
+ rc = write(fd2, p, brtw);
+ if (rc < 0)
+ goto out_err;
+ if (rc == 0)
+ break;
+ p += rc;
+ brtw -= rc;
+ }
+
+ rc = 0;
+ goto out;
+out_err:
+ rc = -1;
+out:
+ if (buffer)
+ free(buffer);
+ if (fd1 >= 0)
+ close(fd1);
+ if (fd2 >= 0)
+ close(fd2);
+ return rc;
+}
+
+int do_chown(int nargs, char **args) {
+ /* GID is optional. */
+ if (nargs == 3) {
+ if (lchown(args[2], decode_uid(args[1]), -1) == -1)
+ return -errno;
+ } else if (nargs == 4) {
+ if (lchown(args[3], decode_uid(args[1]), decode_uid(args[2])) == -1)
+ return -errno;
+ } else {
+ return -1;
+ }
+ return 0;
+}
+
+static mode_t get_mode(const char *s) {
+ mode_t mode = 0;
+ while (*s) {
+ if (*s >= '0' && *s <= '7') {
+ mode = (mode<<3) | (*s-'0');
+ } else {
+ return -1;
+ }
+ s++;
+ }
+ return mode;
+}
+
+int do_chmod(int nargs, char **args) {
+ mode_t mode = get_mode(args[1]);
+ if (fchmodat(AT_FDCWD, args[2], mode, AT_SYMLINK_NOFOLLOW) < 0) {
+ return -errno;
+ }
+ return 0;
+}
+
+int do_restorecon(int nargs, char **args) {
+ int i;
+ int ret = 0;
+
+ for (i = 1; i < nargs; i++) {
+ if (restorecon(args[i]) < 0)
+ ret = -errno;
+ }
+ return ret;
+}
+
+int do_restorecon_recursive(int nargs, char **args) {
+ int i;
+ int ret = 0;
+
+ for (i = 1; i < nargs; i++) {
+ if (restorecon_recursive(args[i]) < 0)
+ ret = -errno;
+ }
+ return ret;
+}
+
+int do_loglevel(int nargs, char **args) {
+ if (nargs != 2) {
+ ERROR("loglevel: missing argument\n");
+ return -EINVAL;
+ }
+
+ int log_level = atoi(args[1]);
+ if (log_level < KLOG_ERROR_LEVEL || log_level > KLOG_DEBUG_LEVEL) {
+ ERROR("loglevel: invalid log level'%d'\n", log_level);
+ return -EINVAL;
+ }
+ klog_set_level(log_level);
+ return 0;
+}
+
+int do_load_persist_props(int nargs, char **args) {
+ if (nargs == 1) {
+ load_persist_props();
+ return 0;
+ }
+ return -1;
+}
+
+int do_load_all_props(int nargs, char **args) {
+ if (nargs == 1) {
+ load_all_props();
+ return 0;
+ }
+ return -1;
+}
+
+int do_wait(int nargs, char **args)
+{
+ if (nargs == 2) {
+ return wait_for_file(args[1], COMMAND_RETRY_TIMEOUT);
+ } else if (nargs == 3) {
+ return wait_for_file(args[1], atoi(args[2]));
+ } else
+ return -1;
+}
+
+/*
+ * Callback to make a directory from the ext4 code
+ */
+static int do_installkeys_ensure_dir_exists(const char* dir)
+{
+ if (make_dir(dir, 0700) && errno != EEXIST) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int do_installkey(int nargs, char **args)
+{
+ if (nargs != 2) {
+ return -1;
+ }
+
+ std::string prop_value = property_get("ro.crypto.type");
+ if (prop_value != "file") {
+ return 0;
+ }
+
+ return e4crypt_create_device_key(args[1],
+ do_installkeys_ensure_dir_exists);
+}
diff --git a/init/compare-bootcharts.py b/init/compare-bootcharts.py
new file mode 100755
index 0000000..2057b55
--- /dev/null
+++ b/init/compare-bootcharts.py
@@ -0,0 +1,146 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Compare two bootcharts and list start/end timestamps on key processes.
+
+This script extracts two bootchart.tgz files and compares the timestamps
+in proc_ps.log for selected processes. The proc_ps.log file consists of
+repetitive blocks of the following format:
+
+timestamp1 (jiffies)
+dumps of /proc/<pid>/stat
+
+timestamp2
+dumps of /proc/<pid>/stat
+
+The timestamps are 200ms apart, and the creation time of selected processes
+are listed. The termination time of the boot animation process is also listed
+as a coarse indication about when the boot process is complete as perceived by
+the user.
+"""
+
+import sys
+import tarfile
+
+# The bootchart timestamps are 200ms apart, but the USER_HZ value is not
+# reported in the bootchart, so we use the first two timestamps to calculate
+# the wall clock time of a jiffy.
+jiffy_to_wallclock = {
+ '1st_timestamp': -1,
+ '2nd_timestamp': -1,
+ 'jiffy_to_wallclock': -1
+}
+
+def analyze_process_maps(process_map1, process_map2, jiffy_record):
+ # List interesting processes here
+ processes_of_interest = [
+ '/init',
+ '/system/bin/surfaceflinger',
+ '/system/bin/bootanimation',
+ 'zygote64',
+ 'zygote',
+ 'system_server'
+ ]
+
+ jw = jiffy_record['jiffy_to_wallclock']
+ print "process: baseline experiment (delta)"
+ print " - Unit is ms (a jiffy is %d ms on the system)" % jw
+ print "------------------------------------"
+ for p in processes_of_interest:
+ # e.g., 32-bit system doesn't have zygote64
+ if p in process_map1 and p in process_map2:
+ print "%s: %d %d (%+d)" % (
+ p, process_map1[p]['start_time'] * jw,
+ process_map2[p]['start_time'] * jw,
+ (process_map2[p]['start_time'] -
+ process_map1[p]['start_time']) * jw)
+
+ # Print the last tick for the bootanimation process
+ print "bootanimation ends at: %d %d (%+d)" % (
+ process_map1['/system/bin/bootanimation']['last_tick'] * jw,
+ process_map2['/system/bin/bootanimation']['last_tick'] * jw,
+ (process_map2['/system/bin/bootanimation']['last_tick'] -
+ process_map1['/system/bin/bootanimation']['last_tick']) * jw)
+
+def parse_proc_file(pathname, process_map, jiffy_record=None):
+ # Uncompress bootchart.tgz
+ with tarfile.open(pathname + '/bootchart.tgz', 'r:*') as tf:
+ try:
+ # Read proc_ps.log
+ f = tf.extractfile('proc_ps.log')
+
+ # Break proc_ps into chunks based on timestamps
+ blocks = f.read().split('\n\n')
+ for b in blocks:
+ lines = b.split('\n')
+ if not lines[0]:
+ break
+
+ # 200ms apart in jiffies
+ timestamp = int(lines[0]);
+
+ # Figure out the wall clock time of a jiffy
+ if jiffy_record is not None:
+ if jiffy_record['1st_timestamp'] == -1:
+ jiffy_record['1st_timestamp'] = timestamp
+ elif jiffy_record['jiffy_to_wallclock'] == -1:
+ # Not really needed but for debugging purposes
+ jiffy_record['2nd_timestamp'] = timestamp
+ value = 200 / (timestamp -
+ jiffy_record['1st_timestamp'])
+ # Fix the rounding error
+ # e.g., 201 jiffies in 200ms when USER_HZ is 1000
+ if value == 0:
+ value = 1
+ # e.g., 21 jiffies in 200ms when USER_HZ is 100
+ elif value == 9:
+ value = 10
+ jiffy_record['jiffy_to_wallclock'] = value
+
+ # Populate the process_map table
+ for line in lines[1:]:
+ segs = line.split(' ')
+
+ # 0: pid
+ # 1: process name
+ # 17: priority
+ # 18: nice
+ # 21: creation time
+
+ proc_name = segs[1].strip('()')
+ if proc_name in process_map:
+ process = process_map[proc_name]
+ else:
+ process = {'start_time': int(segs[21])}
+ process_map[proc_name] = process
+
+ process['last_tick'] = timestamp
+ finally:
+ f.close()
+
+def main():
+ if len(sys.argv) != 3:
+ print "Usage: %s base_bootchart_dir exp_bootchart_dir" % sys.argv[0]
+ sys.exit(1)
+
+ process_map1 = {}
+ process_map2 = {}
+ parse_proc_file(sys.argv[1], process_map1, jiffy_to_wallclock)
+ parse_proc_file(sys.argv[2], process_map2)
+ analyze_process_maps(process_map1, process_map2, jiffy_to_wallclock)
+
+if __name__ == "__main__":
+ main()
diff --git a/init/devices.cpp b/init/devices.cpp
new file mode 100644
index 0000000..3652c57
--- /dev/null
+++ b/init/devices.cpp
@@ -0,0 +1,1001 @@
+/*
+ * Copyright (C) 2007-2014 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 <fnmatch.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <fcntl.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <linux/netlink.h>
+
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+#include <selinux/android.h>
+#include <selinux/avc.h>
+
+#include <private/android_filesystem_config.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+
+#include <cutils/list.h>
+#include <cutils/uevent.h>
+
+#include "devices.h"
+#include "ueventd_parser.h"
+#include "util.h"
+#include "log.h"
+
+#define SYSFS_PREFIX "/sys"
+static const char *firmware_dirs[] = { "/etc/firmware",
+ "/vendor/firmware",
+ "/firmware/image" };
+
+extern struct selabel_handle *sehandle;
+
+static int device_fd = -1;
+
+struct uevent {
+ const char *action;
+ const char *path;
+ const char *subsystem;
+ const char *firmware;
+ const char *partition_name;
+ const char *device_name;
+ int partition_num;
+ int major;
+ int minor;
+};
+
+struct perms_ {
+ char *name;
+ char *attr;
+ mode_t perm;
+ unsigned int uid;
+ unsigned int gid;
+ unsigned short prefix;
+ unsigned short wildcard;
+};
+
+struct perm_node {
+ struct perms_ dp;
+ struct listnode plist;
+};
+
+struct platform_node {
+ char *name;
+ char *path;
+ int path_len;
+ struct listnode list;
+};
+
+static list_declare(sys_perms);
+static list_declare(dev_perms);
+static list_declare(platform_names);
+
+int add_dev_perms(const char *name, const char *attr,
+ mode_t perm, unsigned int uid, unsigned int gid,
+ unsigned short prefix,
+ unsigned short wildcard) {
+ struct perm_node *node = (perm_node*) calloc(1, sizeof(*node));
+ if (!node)
+ return -ENOMEM;
+
+ node->dp.name = strdup(name);
+ if (!node->dp.name)
+ return -ENOMEM;
+
+ if (attr) {
+ node->dp.attr = strdup(attr);
+ if (!node->dp.attr)
+ return -ENOMEM;
+ }
+
+ node->dp.perm = perm;
+ node->dp.uid = uid;
+ node->dp.gid = gid;
+ node->dp.prefix = prefix;
+ node->dp.wildcard = wildcard;
+
+ if (attr)
+ list_add_tail(&sys_perms, &node->plist);
+ else
+ list_add_tail(&dev_perms, &node->plist);
+
+ return 0;
+}
+
+void fixup_sys_perms(const char *upath)
+{
+ char buf[512];
+ struct listnode *node;
+ struct perms_ *dp;
+
+ /* upaths omit the "/sys" that paths in this list
+ * contain, so we add 4 when comparing...
+ */
+ list_for_each(node, &sys_perms) {
+ dp = &(node_to_item(node, struct perm_node, plist))->dp;
+ if (dp->prefix) {
+ if (strncmp(upath, dp->name + 4, strlen(dp->name + 4)))
+ continue;
+ } else if (dp->wildcard) {
+ if (fnmatch(dp->name + 4, upath, FNM_PATHNAME) != 0)
+ continue;
+ } else {
+ if (strcmp(upath, dp->name + 4))
+ continue;
+ }
+
+ if ((strlen(upath) + strlen(dp->attr) + 6) > sizeof(buf))
+ break;
+
+ snprintf(buf, sizeof(buf), "/sys%s/%s", upath, dp->attr);
+ INFO("fixup %s %d %d 0%o\n", buf, dp->uid, dp->gid, dp->perm);
+ chown(buf, dp->uid, dp->gid);
+ chmod(buf, dp->perm);
+ }
+
+ // Now fixup SELinux file labels
+ int len = snprintf(buf, sizeof(buf), "/sys%s", upath);
+ if ((len < 0) || ((size_t) len >= sizeof(buf))) {
+ // Overflow
+ return;
+ }
+ if (access(buf, F_OK) == 0) {
+ INFO("restorecon_recursive: %s\n", buf);
+ restorecon_recursive(buf);
+ }
+}
+
+static bool perm_path_matches(const char *path, struct perms_ *dp)
+{
+ if (dp->prefix) {
+ if (strncmp(path, dp->name, strlen(dp->name)) == 0)
+ return true;
+ } else if (dp->wildcard) {
+ if (fnmatch(dp->name, path, FNM_PATHNAME) == 0)
+ return true;
+ } else {
+ if (strcmp(path, dp->name) == 0)
+ return true;
+ }
+
+ return false;
+}
+
+static mode_t get_device_perm(const char *path, const char **links,
+ unsigned *uid, unsigned *gid)
+{
+ struct listnode *node;
+ struct perm_node *perm_node;
+ struct perms_ *dp;
+
+ /* search the perms list in reverse so that ueventd.$hardware can
+ * override ueventd.rc
+ */
+ list_for_each_reverse(node, &dev_perms) {
+ bool match = false;
+
+ perm_node = node_to_item(node, struct perm_node, plist);
+ dp = &perm_node->dp;
+
+ if (perm_path_matches(path, dp)) {
+ match = true;
+ } else {
+ if (links) {
+ int i;
+ for (i = 0; links[i]; i++) {
+ if (perm_path_matches(links[i], dp)) {
+ match = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (match) {
+ *uid = dp->uid;
+ *gid = dp->gid;
+ return dp->perm;
+ }
+ }
+ /* Default if nothing found. */
+ *uid = 0;
+ *gid = 0;
+ return 0600;
+}
+
+static void make_device(const char *path,
+ const char */*upath*/,
+ int block, int major, int minor,
+ const char **links)
+{
+ unsigned uid;
+ unsigned gid;
+ mode_t mode;
+ dev_t dev;
+ char *secontext = NULL;
+
+ mode = get_device_perm(path, links, &uid, &gid) | (block ? S_IFBLK : S_IFCHR);
+
+ selabel_lookup_best_match(sehandle, &secontext, path, links, mode);
+ setfscreatecon(secontext);
+
+ dev = makedev(major, minor);
+ /* Temporarily change egid to avoid race condition setting the gid of the
+ * device node. Unforunately changing the euid would prevent creation of
+ * some device nodes, so the uid has to be set with chown() and is still
+ * racy. Fixing the gid race at least fixed the issue with system_server
+ * opening dynamic input devices under the AID_INPUT gid. */
+ setegid(gid);
+ mknod(path, mode, dev);
+ chown(path, uid, -1);
+ setegid(AID_ROOT);
+
+ if (secontext) {
+ freecon(secontext);
+ setfscreatecon(NULL);
+ }
+}
+
+static void add_platform_device(const char *path)
+{
+ int path_len = strlen(path);
+ struct platform_node *bus;
+ const char *name = path;
+
+ if (!strncmp(path, "/devices/", 9)) {
+ name += 9;
+ if (!strncmp(name, "platform/", 9))
+ name += 9;
+ }
+
+ INFO("adding platform device %s (%s)\n", name, path);
+
+ bus = (platform_node*) calloc(1, sizeof(struct platform_node));
+ bus->path = strdup(path);
+ bus->path_len = path_len;
+ bus->name = bus->path + (name - path);
+ list_add_tail(&platform_names, &bus->list);
+}
+
+/*
+ * given a path that may start with a platform device, find the length of the
+ * platform device prefix. If it doesn't start with a platform device, return
+ * 0.
+ */
+static struct platform_node *find_platform_device(const char *path)
+{
+ int path_len = strlen(path);
+ struct listnode *node;
+ struct platform_node *bus;
+
+ list_for_each_reverse(node, &platform_names) {
+ bus = node_to_item(node, struct platform_node, list);
+ if ((bus->path_len < path_len) &&
+ (path[bus->path_len] == '/') &&
+ !strncmp(path, bus->path, bus->path_len))
+ return bus;
+ }
+
+ return NULL;
+}
+
+static void remove_platform_device(const char *path)
+{
+ struct listnode *node;
+ struct platform_node *bus;
+
+ list_for_each_reverse(node, &platform_names) {
+ bus = node_to_item(node, struct platform_node, list);
+ if (!strcmp(path, bus->path)) {
+ INFO("removing platform device %s\n", bus->name);
+ free(bus->path);
+ list_remove(node);
+ free(bus);
+ return;
+ }
+ }
+}
+
+/* Given a path that may start with a PCI device, populate the supplied buffer
+ * with the PCI domain/bus number and the peripheral ID and return 0.
+ * If it doesn't start with a PCI device, or there is some error, return -1 */
+static int find_pci_device_prefix(const char *path, char *buf, ssize_t buf_sz)
+{
+ const char *start, *end;
+
+ if (strncmp(path, "/devices/pci", 12))
+ return -1;
+
+ /* Beginning of the prefix is the initial "pci" after "/devices/" */
+ start = path + 9;
+
+ /* End of the prefix is two path '/' later, capturing the domain/bus number
+ * and the peripheral ID. Example: pci0000:00/0000:00:1f.2 */
+ end = strchr(start, '/');
+ if (!end)
+ return -1;
+ end = strchr(end + 1, '/');
+ if (!end)
+ return -1;
+
+ /* Make sure we have enough room for the string plus null terminator */
+ if (end - start + 1 > buf_sz)
+ return -1;
+
+ strncpy(buf, start, end - start);
+ buf[end - start] = '\0';
+ return 0;
+}
+
+static void parse_event(const char *msg, struct uevent *uevent)
+{
+ uevent->action = "";
+ uevent->path = "";
+ uevent->subsystem = "";
+ uevent->firmware = "";
+ uevent->major = -1;
+ uevent->minor = -1;
+ uevent->partition_name = NULL;
+ uevent->partition_num = -1;
+ uevent->device_name = NULL;
+
+ /* currently ignoring SEQNUM */
+ while(*msg) {
+ if(!strncmp(msg, "ACTION=", 7)) {
+ msg += 7;
+ uevent->action = msg;
+ } else if(!strncmp(msg, "DEVPATH=", 8)) {
+ msg += 8;
+ uevent->path = msg;
+ } else if(!strncmp(msg, "SUBSYSTEM=", 10)) {
+ msg += 10;
+ uevent->subsystem = msg;
+ } else if(!strncmp(msg, "FIRMWARE=", 9)) {
+ msg += 9;
+ uevent->firmware = msg;
+ } else if(!strncmp(msg, "MAJOR=", 6)) {
+ msg += 6;
+ uevent->major = atoi(msg);
+ } else if(!strncmp(msg, "MINOR=", 6)) {
+ msg += 6;
+ uevent->minor = atoi(msg);
+ } else if(!strncmp(msg, "PARTN=", 6)) {
+ msg += 6;
+ uevent->partition_num = atoi(msg);
+ } else if(!strncmp(msg, "PARTNAME=", 9)) {
+ msg += 9;
+ uevent->partition_name = msg;
+ } else if(!strncmp(msg, "DEVNAME=", 8)) {
+ msg += 8;
+ uevent->device_name = msg;
+ }
+
+ /* advance to after the next \0 */
+ while(*msg++)
+ ;
+ }
+
+ if (LOG_UEVENTS) {
+ INFO("event { '%s', '%s', '%s', '%s', %d, %d }\n",
+ uevent->action, uevent->path, uevent->subsystem,
+ uevent->firmware, uevent->major, uevent->minor);
+ }
+}
+
+static char **get_character_device_symlinks(struct uevent *uevent)
+{
+ const char *parent;
+ char *slash;
+ char **links;
+ int link_num = 0;
+ int width;
+ struct platform_node *pdev;
+
+ pdev = find_platform_device(uevent->path);
+ if (!pdev)
+ return NULL;
+
+ links = (char**) malloc(sizeof(char *) * 2);
+ if (!links)
+ return NULL;
+ memset(links, 0, sizeof(char *) * 2);
+
+ /* skip "/devices/platform/<driver>" */
+ parent = strchr(uevent->path + pdev->path_len, '/');
+ if (!parent)
+ goto err;
+
+ if (!strncmp(parent, "/usb", 4)) {
+ /* skip root hub name and device. use device interface */
+ while (*++parent && *parent != '/');
+ if (*parent)
+ while (*++parent && *parent != '/');
+ if (!*parent)
+ goto err;
+ slash = strchr(++parent, '/');
+ if (!slash)
+ goto err;
+ width = slash - parent;
+ if (width <= 0)
+ goto err;
+
+ if (asprintf(&links[link_num], "/dev/usb/%s%.*s", uevent->subsystem, width, parent) > 0)
+ link_num++;
+ else
+ links[link_num] = NULL;
+ mkdir("/dev/usb", 0755);
+ }
+ else {
+ goto err;
+ }
+
+ return links;
+err:
+ free(links);
+ return NULL;
+}
+
+static char **get_block_device_symlinks(struct uevent *uevent)
+{
+ const char *device;
+ struct platform_node *pdev;
+ char *slash;
+ const char *type;
+ char buf[256];
+ char link_path[256];
+ int link_num = 0;
+ char *p;
+
+ pdev = find_platform_device(uevent->path);
+ if (pdev) {
+ device = pdev->name;
+ type = "platform";
+ } else if (!find_pci_device_prefix(uevent->path, buf, sizeof(buf))) {
+ device = buf;
+ type = "pci";
+ } else {
+ return NULL;
+ }
+
+ char **links = (char**) malloc(sizeof(char *) * 4);
+ if (!links)
+ return NULL;
+ memset(links, 0, sizeof(char *) * 4);
+
+ INFO("found %s device %s\n", type, device);
+
+ snprintf(link_path, sizeof(link_path), "/dev/block/%s/%s", type, device);
+
+ if (uevent->partition_name) {
+ p = strdup(uevent->partition_name);
+ sanitize(p);
+ if (strcmp(uevent->partition_name, p))
+ NOTICE("Linking partition '%s' as '%s'\n", uevent->partition_name, p);
+ if (asprintf(&links[link_num], "%s/by-name/%s", link_path, p) > 0)
+ link_num++;
+ else
+ links[link_num] = NULL;
+ free(p);
+ }
+
+ if (uevent->partition_num >= 0) {
+ if (asprintf(&links[link_num], "%s/by-num/p%d", link_path, uevent->partition_num) > 0)
+ link_num++;
+ else
+ links[link_num] = NULL;
+ }
+
+ slash = strrchr(uevent->path, '/');
+ if (asprintf(&links[link_num], "%s/%s", link_path, slash + 1) > 0)
+ link_num++;
+ else
+ links[link_num] = NULL;
+
+ return links;
+}
+
+static void handle_device(const char *action, const char *devpath,
+ const char *path, int block, int major, int minor, char **links)
+{
+ int i;
+
+ if(!strcmp(action, "add")) {
+ make_device(devpath, path, block, major, minor, (const char **)links);
+ if (links) {
+ for (i = 0; links[i]; i++)
+ make_link(devpath, links[i]);
+ }
+ }
+
+ if(!strcmp(action, "remove")) {
+ if (links) {
+ for (i = 0; links[i]; i++)
+ remove_link(devpath, links[i]);
+ }
+ unlink(devpath);
+ }
+
+ if (links) {
+ for (i = 0; links[i]; i++)
+ free(links[i]);
+ free(links);
+ }
+}
+
+static void handle_platform_device_event(struct uevent *uevent)
+{
+ const char *path = uevent->path;
+
+ if (!strcmp(uevent->action, "add"))
+ add_platform_device(path);
+ else if (!strcmp(uevent->action, "remove"))
+ remove_platform_device(path);
+}
+
+static const char *parse_device_name(struct uevent *uevent, unsigned int len)
+{
+ const char *name;
+
+ /* if it's not a /dev device, nothing else to do */
+ if((uevent->major < 0) || (uevent->minor < 0))
+ return NULL;
+
+ /* do we have a name? */
+ name = strrchr(uevent->path, '/');
+ if(!name)
+ return NULL;
+ name++;
+
+ /* too-long names would overrun our buffer */
+ if(strlen(name) > len) {
+ ERROR("DEVPATH=%s exceeds %u-character limit on filename; ignoring event\n",
+ name, len);
+ return NULL;
+ }
+
+ return name;
+}
+
+static void handle_block_device_event(struct uevent *uevent)
+{
+ const char *base = "/dev/block/";
+ const char *name;
+ char devpath[96];
+ char **links = NULL;
+
+ name = parse_device_name(uevent, 64);
+ if (!name)
+ return;
+
+ snprintf(devpath, sizeof(devpath), "%s%s", base, name);
+ make_dir(base, 0755);
+
+ if (!strncmp(uevent->path, "/devices/", 9))
+ links = get_block_device_symlinks(uevent);
+
+ handle_device(uevent->action, devpath, uevent->path, 1,
+ uevent->major, uevent->minor, links);
+}
+
+#define DEVPATH_LEN 96
+
+static bool assemble_devpath(char *devpath, const char *dirname,
+ const char *devname)
+{
+ int s = snprintf(devpath, DEVPATH_LEN, "%s/%s", dirname, devname);
+ if (s < 0) {
+ ERROR("failed to assemble device path (%s); ignoring event\n",
+ strerror(errno));
+ return false;
+ } else if (s >= DEVPATH_LEN) {
+ ERROR("%s/%s exceeds %u-character limit on path; ignoring event\n",
+ dirname, devname, DEVPATH_LEN);
+ return false;
+ }
+ return true;
+}
+
+static void mkdir_recursive_for_devpath(const char *devpath)
+{
+ char dir[DEVPATH_LEN];
+ char *slash;
+
+ strcpy(dir, devpath);
+ slash = strrchr(dir, '/');
+ *slash = '\0';
+ mkdir_recursive(dir, 0755);
+}
+
+static void handle_generic_device_event(struct uevent *uevent)
+{
+ const char *base;
+ const char *name;
+ char devpath[DEVPATH_LEN] = {0};
+ char **links = NULL;
+
+ name = parse_device_name(uevent, 64);
+ if (!name)
+ return;
+
+ struct ueventd_subsystem *subsystem =
+ ueventd_subsystem_find_by_name(uevent->subsystem);
+
+ if (subsystem) {
+ const char *devname;
+
+ switch (subsystem->devname_src) {
+ case DEVNAME_UEVENT_DEVNAME:
+ devname = uevent->device_name;
+ break;
+
+ case DEVNAME_UEVENT_DEVPATH:
+ devname = name;
+ break;
+
+ default:
+ ERROR("%s subsystem's devpath option is not set; ignoring event\n",
+ uevent->subsystem);
+ return;
+ }
+
+ if (!assemble_devpath(devpath, subsystem->dirname, devname))
+ return;
+ mkdir_recursive_for_devpath(devpath);
+ } else if (!strncmp(uevent->subsystem, "usb", 3)) {
+ if (!strcmp(uevent->subsystem, "usb")) {
+ if (uevent->device_name) {
+ if (!assemble_devpath(devpath, "/dev", uevent->device_name))
+ return;
+ mkdir_recursive_for_devpath(devpath);
+ }
+ else {
+ /* This imitates the file system that would be created
+ * if we were using devfs instead.
+ * Minors are broken up into groups of 128, starting at "001"
+ */
+ int bus_id = uevent->minor / 128 + 1;
+ int device_id = uevent->minor % 128 + 1;
+ /* build directories */
+ make_dir("/dev/bus", 0755);
+ make_dir("/dev/bus/usb", 0755);
+ snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d", bus_id);
+ make_dir(devpath, 0755);
+ snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d/%03d", bus_id, device_id);
+ }
+ } else {
+ /* ignore other USB events */
+ return;
+ }
+ } else if (!strncmp(uevent->subsystem, "graphics", 8)) {
+ base = "/dev/graphics/";
+ make_dir(base, 0755);
+ } else if (!strncmp(uevent->subsystem, "drm", 3)) {
+ base = "/dev/dri/";
+ make_dir(base, 0755);
+ } else if (!strncmp(uevent->subsystem, "oncrpc", 6)) {
+ base = "/dev/oncrpc/";
+ make_dir(base, 0755);
+ } else if (!strncmp(uevent->subsystem, "adsp", 4)) {
+ base = "/dev/adsp/";
+ make_dir(base, 0755);
+ } else if (!strncmp(uevent->subsystem, "msm_camera", 10)) {
+ base = "/dev/msm_camera/";
+ make_dir(base, 0755);
+ } else if(!strncmp(uevent->subsystem, "input", 5)) {
+ base = "/dev/input/";
+ make_dir(base, 0755);
+ } else if(!strncmp(uevent->subsystem, "mtd", 3)) {
+ base = "/dev/mtd/";
+ make_dir(base, 0755);
+ } else if(!strncmp(uevent->subsystem, "sound", 5)) {
+ base = "/dev/snd/";
+ make_dir(base, 0755);
+ } else if(!strncmp(uevent->subsystem, "misc", 4) &&
+ !strncmp(name, "log_", 4)) {
+ INFO("kernel logger is deprecated\n");
+ base = "/dev/log/";
+ make_dir(base, 0755);
+ name += 4;
+ } else
+ base = "/dev/";
+ links = get_character_device_symlinks(uevent);
+
+ if (!devpath[0])
+ snprintf(devpath, sizeof(devpath), "%s%s", base, name);
+
+ handle_device(uevent->action, devpath, uevent->path, 0,
+ uevent->major, uevent->minor, links);
+}
+
+static void handle_device_event(struct uevent *uevent)
+{
+ if (!strcmp(uevent->action,"add") || !strcmp(uevent->action, "change") || !strcmp(uevent->action, "online"))
+ fixup_sys_perms(uevent->path);
+
+ if (!strncmp(uevent->subsystem, "block", 5)) {
+ handle_block_device_event(uevent);
+ } else if (!strncmp(uevent->subsystem, "platform", 8)) {
+ handle_platform_device_event(uevent);
+ } else {
+ handle_generic_device_event(uevent);
+ }
+}
+
+static int load_firmware(int fw_fd, int loading_fd, int data_fd)
+{
+ struct stat st;
+ long len_to_copy;
+ int ret = 0;
+
+ if(fstat(fw_fd, &st) < 0)
+ return -1;
+ len_to_copy = st.st_size;
+
+ write(loading_fd, "1", 1); /* start transfer */
+
+ while (len_to_copy > 0) {
+ char buf[PAGE_SIZE];
+ ssize_t nr;
+
+ nr = read(fw_fd, buf, sizeof(buf));
+ if(!nr)
+ break;
+ if(nr < 0) {
+ ret = -1;
+ break;
+ }
+
+ len_to_copy -= nr;
+ while (nr > 0) {
+ ssize_t nw = 0;
+
+ nw = write(data_fd, buf + nw, nr);
+ if(nw <= 0) {
+ ret = -1;
+ goto out;
+ }
+ nr -= nw;
+ }
+ }
+
+out:
+ if(!ret)
+ write(loading_fd, "0", 1); /* successful end of transfer */
+ else
+ write(loading_fd, "-1", 2); /* abort transfer */
+
+ return ret;
+}
+
+static int is_booting(void)
+{
+ return access("/dev/.booting", F_OK) == 0;
+}
+
+static void process_firmware_event(struct uevent *uevent)
+{
+ char *root, *loading, *data;
+ int l, loading_fd, data_fd, fw_fd;
+ size_t i;
+ int booting = is_booting();
+
+ INFO("firmware: loading '%s' for '%s'\n",
+ uevent->firmware, uevent->path);
+
+ l = asprintf(&root, SYSFS_PREFIX"%s/", uevent->path);
+ if (l == -1)
+ return;
+
+ l = asprintf(&loading, "%sloading", root);
+ if (l == -1)
+ goto root_free_out;
+
+ l = asprintf(&data, "%sdata", root);
+ if (l == -1)
+ goto loading_free_out;
+
+ loading_fd = open(loading, O_WRONLY|O_CLOEXEC);
+ if(loading_fd < 0)
+ goto data_free_out;
+
+ data_fd = open(data, O_WRONLY|O_CLOEXEC);
+ if(data_fd < 0)
+ goto loading_close_out;
+
+try_loading_again:
+ for (i = 0; i < ARRAY_SIZE(firmware_dirs); i++) {
+ char *file = NULL;
+ l = asprintf(&file, "%s/%s", firmware_dirs[i], uevent->firmware);
+ if (l == -1)
+ goto data_free_out;
+ fw_fd = open(file, O_RDONLY|O_CLOEXEC);
+ free(file);
+ if (fw_fd >= 0) {
+ if(!load_firmware(fw_fd, loading_fd, data_fd))
+ INFO("firmware: copy success { '%s', '%s' }\n", root, uevent->firmware);
+ else
+ INFO("firmware: copy failure { '%s', '%s' }\n", root, uevent->firmware);
+ break;
+ }
+ }
+ if (fw_fd < 0) {
+ if (booting) {
+ /* If we're not fully booted, we may be missing
+ * filesystems needed for firmware, wait and retry.
+ */
+ usleep(100000);
+ booting = is_booting();
+ goto try_loading_again;
+ }
+ INFO("firmware: could not open '%s': %s\n", uevent->firmware, strerror(errno));
+ write(loading_fd, "-1", 2);
+ goto data_close_out;
+ }
+
+ close(fw_fd);
+data_close_out:
+ close(data_fd);
+loading_close_out:
+ close(loading_fd);
+data_free_out:
+ free(data);
+loading_free_out:
+ free(loading);
+root_free_out:
+ free(root);
+}
+
+static void handle_firmware_event(struct uevent *uevent)
+{
+ pid_t pid;
+
+ if(strcmp(uevent->subsystem, "firmware"))
+ return;
+
+ if(strcmp(uevent->action, "add"))
+ return;
+
+ /* we fork, to avoid making large memory allocations in init proper */
+ pid = fork();
+ if (!pid) {
+ process_firmware_event(uevent);
+ _exit(EXIT_SUCCESS);
+ } else if (pid < 0) {
+ ERROR("could not fork to process firmware event: %s\n", strerror(errno));
+ }
+}
+
+#define UEVENT_MSG_LEN 2048
+void handle_device_fd()
+{
+ char msg[UEVENT_MSG_LEN+2];
+ int n;
+ while ((n = uevent_kernel_multicast_recv(device_fd, msg, UEVENT_MSG_LEN)) > 0) {
+ if(n >= UEVENT_MSG_LEN) /* overflow -- discard */
+ continue;
+
+ msg[n] = '\0';
+ msg[n+1] = '\0';
+
+ struct uevent uevent;
+ parse_event(msg, &uevent);
+
+ if (selinux_status_updated() > 0) {
+ struct selabel_handle *sehandle2;
+ sehandle2 = selinux_android_file_context_handle();
+ if (sehandle2) {
+ selabel_close(sehandle);
+ sehandle = sehandle2;
+ }
+ }
+
+ handle_device_event(&uevent);
+ handle_firmware_event(&uevent);
+ }
+}
+
+/* Coldboot walks parts of the /sys tree and pokes the uevent files
+** to cause the kernel to regenerate device add events that happened
+** before init's device manager was started
+**
+** We drain any pending events from the netlink socket every time
+** we poke another uevent file to make sure we don't overrun the
+** socket's buffer.
+*/
+
+static void do_coldboot(DIR *d)
+{
+ struct dirent *de;
+ int dfd, fd;
+
+ dfd = dirfd(d);
+
+ fd = openat(dfd, "uevent", O_WRONLY);
+ if(fd >= 0) {
+ write(fd, "add\n", 4);
+ close(fd);
+ handle_device_fd();
+ }
+
+ while((de = readdir(d))) {
+ DIR *d2;
+
+ if(de->d_type != DT_DIR || de->d_name[0] == '.')
+ continue;
+
+ fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
+ if(fd < 0)
+ continue;
+
+ d2 = fdopendir(fd);
+ if(d2 == 0)
+ close(fd);
+ else {
+ do_coldboot(d2);
+ closedir(d2);
+ }
+ }
+}
+
+static void coldboot(const char *path)
+{
+ DIR *d = opendir(path);
+ if(d) {
+ do_coldboot(d);
+ closedir(d);
+ }
+}
+
+void device_init() {
+ sehandle = selinux_android_file_context_handle();
+ selinux_status_open(true);
+
+ /* is 256K enough? udev uses 16MB! */
+ device_fd = uevent_open_socket(256*1024, true);
+ if (device_fd == -1) {
+ return;
+ }
+ fcntl(device_fd, F_SETFL, O_NONBLOCK);
+
+ if (access(COLDBOOT_DONE, F_OK) == 0) {
+ NOTICE("Skipping coldboot, already done!\n");
+ return;
+ }
+
+ Timer t;
+ coldboot("/sys/class");
+ coldboot("/sys/block");
+ coldboot("/sys/devices");
+ close(open(COLDBOOT_DONE, O_WRONLY|O_CREAT|O_CLOEXEC, 0000));
+ NOTICE("Coldboot took %.2fs.\n", t.duration());
+}
+
+int get_device_fd()
+{
+ return device_fd;
+}
diff --git a/init/devices.h b/init/devices.h
new file mode 100644
index 0000000..6cb0a77
--- /dev/null
+++ b/init/devices.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_DEVICES_H
+#define _INIT_DEVICES_H
+
+#include <sys/stat.h>
+
+extern void handle_device_fd();
+extern void device_init(void);
+extern int add_dev_perms(const char *name, const char *attr,
+ mode_t perm, unsigned int uid,
+ unsigned int gid, unsigned short prefix,
+ unsigned short wildcard);
+int get_device_fd();
+
+#endif /* _INIT_DEVICES_H */
diff --git a/init/grab-bootchart.sh b/init/grab-bootchart.sh
new file mode 100755
index 0000000..d6082aa
--- /dev/null
+++ b/init/grab-bootchart.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+#
+# This script is used to retrieve a bootchart log generated by init.
+# All options are passed to adb, for better or for worse.
+# See the readme in this directory for more on bootcharting.
+
+TMPDIR=/tmp/android-bootchart
+rm -rf $TMPDIR
+mkdir -p $TMPDIR
+
+LOGROOT=/data/bootchart
+TARBALL=bootchart.tgz
+
+FILES="header proc_stat.log proc_ps.log proc_diskstats.log kernel_pacct"
+
+for f in $FILES; do
+ adb "${@}" pull $LOGROOT/$f $TMPDIR/$f 2>&1 > /dev/null
+done
+(cd $TMPDIR && tar -czf $TARBALL $FILES)
+bootchart ${TMPDIR}/${TARBALL}
+gnome-open ${TARBALL%.tgz}.png
+echo "Clean up ${TMPDIR}/ and ./${TARBALL%.tgz}.png when done"
diff --git a/init/init.cpp b/init/init.cpp
new file mode 100644
index 0000000..4be16ea
--- /dev/null
+++ b/init/init.cpp
@@ -0,0 +1,1118 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/mount.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include <mtd/mtd-user.h>
+
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+#include <selinux/android.h>
+
+#include <base/file.h>
+#include <base/stringprintf.h>
+#include <base/strings.h>
+#include <cutils/android_reboot.h>
+#include <cutils/fs.h>
+#include <cutils/iosched_policy.h>
+#include <cutils/list.h>
+#include <cutils/sockets.h>
+#include <private/android_filesystem_config.h>
+
+#include <memory>
+
+#include "devices.h"
+#include "init.h"
+#include "log.h"
+#include "property_service.h"
+#include "bootchart.h"
+#include "signal_handler.h"
+#include "keychords.h"
+#include "init_parser.h"
+#include "util.h"
+#include "ueventd.h"
+#include "watchdogd.h"
+
+struct selabel_handle *sehandle;
+struct selabel_handle *sehandle_prop;
+
+static int property_triggers_enabled = 0;
+
+static char qemu[32];
+
+static struct action *cur_action = NULL;
+static struct command *cur_command = NULL;
+
+static int have_console;
+static std::string console_name = "/dev/console";
+static time_t process_needs_restart;
+
+static const char *ENV[32];
+
+bool waiting_for_exec = false;
+
+static int epoll_fd = -1;
+
+void register_epoll_handler(int fd, void (*fn)()) {
+ epoll_event ev;
+ ev.events = EPOLLIN;
+ ev.data.ptr = reinterpret_cast<void*>(fn);
+ if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
+ ERROR("epoll_ctl failed: %s\n", strerror(errno));
+ }
+}
+
+void service::NotifyStateChange(const char* new_state) {
+ if (!properties_initialized()) {
+ // If properties aren't available yet, we can't set them.
+ return;
+ }
+
+ if ((flags & SVC_EXEC) != 0) {
+ // 'exec' commands don't have properties tracking their state.
+ return;
+ }
+
+ char prop_name[PROP_NAME_MAX];
+ if (snprintf(prop_name, sizeof(prop_name), "init.svc.%s", name) >= PROP_NAME_MAX) {
+ // If the property name would be too long, we can't set it.
+ ERROR("Property name \"init.svc.%s\" too long; not setting to %s\n", name, new_state);
+ return;
+ }
+
+ property_set(prop_name, new_state);
+}
+
+/* add_environment - add "key=value" to the current environment */
+int add_environment(const char *key, const char *val)
+{
+ size_t n;
+ size_t key_len = strlen(key);
+
+ /* The last environment entry is reserved to terminate the list */
+ for (n = 0; n < (ARRAY_SIZE(ENV) - 1); n++) {
+
+ /* Delete any existing entry for this key */
+ if (ENV[n] != NULL) {
+ size_t entry_key_len = strcspn(ENV[n], "=");
+ if ((entry_key_len == key_len) && (strncmp(ENV[n], key, entry_key_len) == 0)) {
+ free((char*)ENV[n]);
+ ENV[n] = NULL;
+ }
+ }
+
+ /* Add entry if a free slot is available */
+ if (ENV[n] == NULL) {
+ char* entry;
+ asprintf(&entry, "%s=%s", key, val);
+ ENV[n] = entry;
+ return 0;
+ }
+ }
+
+ ERROR("No env. room to store: '%s':'%s'\n", key, val);
+
+ return -1;
+}
+
+void zap_stdio(void)
+{
+ int fd;
+ fd = open("/dev/null", O_RDWR);
+ dup2(fd, 0);
+ dup2(fd, 1);
+ dup2(fd, 2);
+ close(fd);
+}
+
+static void open_console()
+{
+ int fd;
+ if ((fd = open(console_name.c_str(), O_RDWR)) < 0) {
+ fd = open("/dev/null", O_RDWR);
+ }
+ ioctl(fd, TIOCSCTTY, 0);
+ dup2(fd, 0);
+ dup2(fd, 1);
+ dup2(fd, 2);
+ close(fd);
+}
+
+static void publish_socket(const char *name, int fd)
+{
+ char key[64] = ANDROID_SOCKET_ENV_PREFIX;
+ char val[64];
+
+ strlcpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1,
+ name,
+ sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX));
+ snprintf(val, sizeof(val), "%d", fd);
+ add_environment(key, val);
+
+ /* make sure we don't close-on-exec */
+ fcntl(fd, F_SETFD, 0);
+}
+
+void service_start(struct service *svc, const char *dynamic_args)
+{
+ // Starting a service removes it from the disabled or reset state and
+ // immediately takes it out of the restarting state if it was in there.
+ svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));
+ svc->time_started = 0;
+
+ // Running processes require no additional work --- if they're in the
+ // process of exiting, we've ensured that they will immediately restart
+ // on exit, unless they are ONESHOT.
+ if (svc->flags & SVC_RUNNING) {
+ return;
+ }
+
+ bool needs_console = (svc->flags & SVC_CONSOLE);
+ if (needs_console && !have_console) {
+ ERROR("service '%s' requires console\n", svc->name);
+ svc->flags |= SVC_DISABLED;
+ return;
+ }
+
+ struct stat sb;
+ if (stat(svc->args[0], &sb) == -1) {
+ ERROR("cannot find '%s' (%s), disabling '%s'\n", svc->args[0], strerror(errno), svc->name);
+ svc->flags |= SVC_DISABLED;
+ return;
+ }
+
+ if ((!(svc->flags & SVC_ONESHOT)) && dynamic_args) {
+ ERROR("service '%s' must be one-shot to use dynamic args, disabling\n", svc->args[0]);
+ svc->flags |= SVC_DISABLED;
+ return;
+ }
+
+ char* scon = NULL;
+ if (svc->seclabel) {
+ scon = strdup(svc->seclabel);
+ if (!scon) {
+ ERROR("Out of memory while starting '%s'\n", svc->name);
+ return;
+ }
+ } else {
+ char *mycon = NULL, *fcon = NULL;
+
+ INFO("computing context for service '%s'\n", svc->args[0]);
+ int rc = getcon(&mycon);
+ if (rc < 0) {
+ ERROR("could not get context while starting '%s'\n", svc->name);
+ return;
+ }
+
+ rc = getfilecon(svc->args[0], &fcon);
+ if (rc < 0) {
+ ERROR("could not get context while starting '%s'\n", svc->name);
+ free(mycon);
+ return;
+ }
+
+ rc = security_compute_create(mycon, fcon, string_to_security_class("process"), &scon);
+ if (rc == 0 && !strcmp(scon, mycon)) {
+ ERROR("Service %s does not have a SELinux domain defined.\n", svc->name);
+ free(mycon);
+ free(fcon);
+ free(scon);
+ return;
+ }
+ free(mycon);
+ free(fcon);
+ if (rc < 0) {
+ ERROR("could not get context while starting '%s'\n", svc->name);
+ return;
+ }
+ }
+
+ NOTICE("Starting service '%s'...\n", svc->name);
+
+ pid_t pid = fork();
+ if (pid == 0) {
+ struct socketinfo *si;
+ struct svcenvinfo *ei;
+ char tmp[32];
+ int fd, sz;
+
+ umask(077);
+ if (properties_initialized()) {
+ get_property_workspace(&fd, &sz);
+ snprintf(tmp, sizeof(tmp), "%d,%d", dup(fd), sz);
+ add_environment("ANDROID_PROPERTY_WORKSPACE", tmp);
+ }
+
+ for (ei = svc->envvars; ei; ei = ei->next)
+ add_environment(ei->name, ei->value);
+
+ for (si = svc->sockets; si; si = si->next) {
+ int socket_type = (
+ !strcmp(si->type, "stream") ? SOCK_STREAM :
+ (!strcmp(si->type, "dgram") ? SOCK_DGRAM : SOCK_SEQPACKET));
+ int s = create_socket(si->name, socket_type,
+ si->perm, si->uid, si->gid, si->socketcon ?: scon);
+ if (s >= 0) {
+ publish_socket(si->name, s);
+ }
+ }
+
+ free(scon);
+ scon = NULL;
+
+ if (svc->writepid_files_) {
+ std::string pid_str = android::base::StringPrintf("%d", pid);
+ for (auto& file : *svc->writepid_files_) {
+ if (!android::base::WriteStringToFile(pid_str, file)) {
+ ERROR("couldn't write %s to %s: %s\n",
+ pid_str.c_str(), file.c_str(), strerror(errno));
+ }
+ }
+ }
+
+ if (svc->ioprio_class != IoSchedClass_NONE) {
+ if (android_set_ioprio(getpid(), svc->ioprio_class, svc->ioprio_pri)) {
+ ERROR("Failed to set pid %d ioprio = %d,%d: %s\n",
+ getpid(), svc->ioprio_class, svc->ioprio_pri, strerror(errno));
+ }
+ }
+
+ if (needs_console) {
+ setsid();
+ open_console();
+ } else {
+ zap_stdio();
+ }
+
+ if (false) {
+ for (size_t n = 0; svc->args[n]; n++) {
+ INFO("args[%zu] = '%s'\n", n, svc->args[n]);
+ }
+ for (size_t n = 0; ENV[n]; n++) {
+ INFO("env[%zu] = '%s'\n", n, ENV[n]);
+ }
+ }
+
+ setpgid(0, getpid());
+
+ // As requested, set our gid, supplemental gids, and uid.
+ if (svc->gid) {
+ if (setgid(svc->gid) != 0) {
+ ERROR("setgid failed: %s\n", strerror(errno));
+ _exit(127);
+ }
+ }
+ if (svc->nr_supp_gids) {
+ if (setgroups(svc->nr_supp_gids, svc->supp_gids) != 0) {
+ ERROR("setgroups failed: %s\n", strerror(errno));
+ _exit(127);
+ }
+ }
+ if (svc->uid) {
+ if (setuid(svc->uid) != 0) {
+ ERROR("setuid failed: %s\n", strerror(errno));
+ _exit(127);
+ }
+ }
+ if (svc->seclabel) {
+ if (setexeccon(svc->seclabel) < 0) {
+ ERROR("cannot setexeccon('%s'): %s\n", svc->seclabel, strerror(errno));
+ _exit(127);
+ }
+ }
+
+ if (!dynamic_args) {
+ if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) {
+ ERROR("cannot execve('%s'): %s\n", svc->args[0], strerror(errno));
+ }
+ } else {
+ char *arg_ptrs[INIT_PARSER_MAXARGS+1];
+ int arg_idx = svc->nargs;
+ char *tmp = strdup(dynamic_args);
+ char *next = tmp;
+ char *bword;
+
+ /* Copy the static arguments */
+ memcpy(arg_ptrs, svc->args, (svc->nargs * sizeof(char *)));
+
+ while((bword = strsep(&next, " "))) {
+ arg_ptrs[arg_idx++] = bword;
+ if (arg_idx == INIT_PARSER_MAXARGS)
+ break;
+ }
+ arg_ptrs[arg_idx] = NULL;
+ execve(svc->args[0], (char**) arg_ptrs, (char**) ENV);
+ }
+ _exit(127);
+ }
+
+ free(scon);
+
+ if (pid < 0) {
+ ERROR("failed to start '%s'\n", svc->name);
+ svc->pid = 0;
+ return;
+ }
+
+ svc->time_started = gettime();
+ svc->pid = pid;
+ svc->flags |= SVC_RUNNING;
+
+ if ((svc->flags & SVC_EXEC) != 0) {
+ INFO("SVC_EXEC pid %d (uid %d gid %d+%zu context %s) started; waiting...\n",
+ svc->pid, svc->uid, svc->gid, svc->nr_supp_gids,
+ svc->seclabel ? : "default");
+ waiting_for_exec = true;
+ }
+
+ svc->NotifyStateChange("running");
+}
+
+/* The how field should be either SVC_DISABLED, SVC_RESET, or SVC_RESTART */
+static void service_stop_or_reset(struct service *svc, int how)
+{
+ /* The service is still SVC_RUNNING until its process exits, but if it has
+ * already exited it shoudn't attempt a restart yet. */
+ svc->flags &= ~(SVC_RESTARTING | SVC_DISABLED_START);
+
+ if ((how != SVC_DISABLED) && (how != SVC_RESET) && (how != SVC_RESTART)) {
+ /* Hrm, an illegal flag. Default to SVC_DISABLED */
+ how = SVC_DISABLED;
+ }
+ /* if the service has not yet started, prevent
+ * it from auto-starting with its class
+ */
+ if (how == SVC_RESET) {
+ svc->flags |= (svc->flags & SVC_RC_DISABLED) ? SVC_DISABLED : SVC_RESET;
+ } else {
+ svc->flags |= how;
+ }
+
+ if (svc->pid) {
+ NOTICE("Service '%s' is being killed...\n", svc->name);
+ kill(-svc->pid, SIGKILL);
+ svc->NotifyStateChange("stopping");
+ } else {
+ svc->NotifyStateChange("stopped");
+ }
+}
+
+void service_reset(struct service *svc)
+{
+ service_stop_or_reset(svc, SVC_RESET);
+}
+
+void service_stop(struct service *svc)
+{
+ service_stop_or_reset(svc, SVC_DISABLED);
+}
+
+void service_restart(struct service *svc)
+{
+ if (svc->flags & SVC_RUNNING) {
+ /* Stop, wait, then start the service. */
+ service_stop_or_reset(svc, SVC_RESTART);
+ } else if (!(svc->flags & SVC_RESTARTING)) {
+ /* Just start the service since it's not running. */
+ service_start(svc, NULL);
+ } /* else: Service is restarting anyways. */
+}
+
+void property_changed(const char *name, const char *value)
+{
+ if (property_triggers_enabled)
+ queue_property_triggers(name, value);
+}
+
+static void restart_service_if_needed(struct service *svc)
+{
+ time_t next_start_time = svc->time_started + 5;
+
+ if (next_start_time <= gettime()) {
+ svc->flags &= (~SVC_RESTARTING);
+ service_start(svc, NULL);
+ return;
+ }
+
+ if ((next_start_time < process_needs_restart) ||
+ (process_needs_restart == 0)) {
+ process_needs_restart = next_start_time;
+ }
+}
+
+static void restart_processes()
+{
+ process_needs_restart = 0;
+ service_for_each_flags(SVC_RESTARTING,
+ restart_service_if_needed);
+}
+
+static void msg_start(const char *name)
+{
+ struct service *svc = NULL;
+ char *tmp = NULL;
+ char *args = NULL;
+
+ if (!strchr(name, ':'))
+ svc = service_find_by_name(name);
+ else {
+ tmp = strdup(name);
+ if (tmp) {
+ args = strchr(tmp, ':');
+ *args = '\0';
+ args++;
+
+ svc = service_find_by_name(tmp);
+ }
+ }
+
+ if (svc) {
+ service_start(svc, args);
+ } else {
+ ERROR("no such service '%s'\n", name);
+ }
+ if (tmp)
+ free(tmp);
+}
+
+static void msg_stop(const char *name)
+{
+ struct service *svc = service_find_by_name(name);
+
+ if (svc) {
+ service_stop(svc);
+ } else {
+ ERROR("no such service '%s'\n", name);
+ }
+}
+
+static void msg_restart(const char *name)
+{
+ struct service *svc = service_find_by_name(name);
+
+ if (svc) {
+ service_restart(svc);
+ } else {
+ ERROR("no such service '%s'\n", name);
+ }
+}
+
+void handle_control_message(const char *msg, const char *arg)
+{
+ if (!strcmp(msg,"start")) {
+ msg_start(arg);
+ } else if (!strcmp(msg,"stop")) {
+ msg_stop(arg);
+ } else if (!strcmp(msg,"restart")) {
+ msg_restart(arg);
+ } else {
+ ERROR("unknown control msg '%s'\n", msg);
+ }
+}
+
+static struct command *get_first_command(struct action *act)
+{
+ struct listnode *node;
+ node = list_head(&act->commands);
+ if (!node || list_empty(&act->commands))
+ return NULL;
+
+ return node_to_item(node, struct command, clist);
+}
+
+static struct command *get_next_command(struct action *act, struct command *cmd)
+{
+ struct listnode *node;
+ node = cmd->clist.next;
+ if (!node)
+ return NULL;
+ if (node == &act->commands)
+ return NULL;
+
+ return node_to_item(node, struct command, clist);
+}
+
+static int is_last_command(struct action *act, struct command *cmd)
+{
+ return (list_tail(&act->commands) == &cmd->clist);
+}
+
+
+std::string build_triggers_string(struct action *cur_action) {
+ std::string result;
+ struct listnode *node;
+ struct trigger *cur_trigger;
+
+ list_for_each(node, &cur_action->triggers) {
+ cur_trigger = node_to_item(node, struct trigger, nlist);
+ if (node != cur_action->triggers.next) {
+ result.push_back(' ');
+ }
+ result += cur_trigger->name;
+ }
+ return result;
+}
+
+bool expand_command_arguments(int nargs, char** args, std::vector<std::string>* expanded_args) {
+ std::vector<std::string>& strs = *expanded_args;
+ strs.resize(nargs);
+ strs[0] = args[0];
+ for (int i = 1; i < nargs; ++i) {
+ if (expand_props(args[i], &strs[i]) == -1) {
+ ERROR("%s: cannot expand '%s'\n", args[0], args[i]);
+ return false;
+ }
+ }
+ return true;
+}
+
+void execute_one_command() {
+ Timer t;
+
+ if (!cur_action || !cur_command || is_last_command(cur_action, cur_command)) {
+ cur_action = action_remove_queue_head();
+ cur_command = NULL;
+ if (!cur_action) {
+ return;
+ }
+
+ std::string trigger_name = build_triggers_string(cur_action);
+ INFO("processing action %p (%s)\n", cur_action, trigger_name.c_str());
+ cur_command = get_first_command(cur_action);
+ } else {
+ cur_command = get_next_command(cur_action, cur_command);
+ }
+
+ if (!cur_command) {
+ return;
+ }
+ int result = 0;
+ std::vector<std::string> arg_strs;
+ if (!expand_command_arguments(cur_command->nargs, cur_command->args, &arg_strs)) {
+ result = -EINVAL;
+ }
+ if (result == 0) {
+ std::vector<char*> args;
+ for (auto& s : arg_strs) {
+ args.push_back(&s[0]);
+ }
+ result = cur_command->func(args.size(), &args[0]);
+ }
+ if (klog_get_level() >= KLOG_INFO_LEVEL) {
+ std::string cmd_str;
+ for (int i = 0; i < cur_command->nargs; ++i) {
+ if (i > 0) {
+ cmd_str.push_back(' ');
+ }
+ cmd_str += cur_command->args[i];
+ }
+ std::string trigger_name = build_triggers_string(cur_action);
+
+ std::string source;
+ if (cur_command->filename) {
+ source = android::base::StringPrintf(" (%s:%d)", cur_command->filename, cur_command->line);
+ }
+
+ INFO("Command '%s' action=%s%s returned %d took %.2fs\n",
+ cmd_str.c_str(), trigger_name.c_str(), source.c_str(), result, t.duration());
+ }
+}
+
+static int wait_for_coldboot_done_action(int nargs, char **args) {
+ Timer t;
+
+ NOTICE("Waiting for %s...\n", COLDBOOT_DONE);
+ // Any longer than 1s is an unreasonable length of time to delay booting.
+ // If you're hitting this timeout, check that you didn't make your
+ // sepolicy regular expressions too expensive (http://b/19899875).
+ if (wait_for_file(COLDBOOT_DONE, 1)) {
+ ERROR("Timed out waiting for %s\n", COLDBOOT_DONE);
+ }
+
+ NOTICE("Waiting for %s took %.2fs.\n", COLDBOOT_DONE, t.duration());
+ return 0;
+}
+
+/*
+ * Writes 512 bytes of output from Hardware RNG (/dev/hw_random, backed
+ * by Linux kernel's hw_random framework) into Linux RNG's via /dev/urandom.
+ * Does nothing if Hardware RNG is not present.
+ *
+ * Since we don't yet trust the quality of Hardware RNG, these bytes are not
+ * mixed into the primary pool of Linux RNG and the entropy estimate is left
+ * unmodified.
+ *
+ * If the HW RNG device /dev/hw_random is present, we require that at least
+ * 512 bytes read from it are written into Linux RNG. QA is expected to catch
+ * devices/configurations where these I/O operations are blocking for a long
+ * time. We do not reboot or halt on failures, as this is a best-effort
+ * attempt.
+ */
+static int mix_hwrng_into_linux_rng_action(int nargs, char **args)
+{
+ int result = -1;
+ int hwrandom_fd = -1;
+ int urandom_fd = -1;
+ char buf[512];
+ ssize_t chunk_size;
+ size_t total_bytes_written = 0;
+
+ hwrandom_fd = TEMP_FAILURE_RETRY(
+ open("/dev/hw_random", O_RDONLY | O_NOFOLLOW | O_CLOEXEC));
+ if (hwrandom_fd == -1) {
+ if (errno == ENOENT) {
+ ERROR("/dev/hw_random not found\n");
+ /* It's not an error to not have a Hardware RNG. */
+ result = 0;
+ } else {
+ ERROR("Failed to open /dev/hw_random: %s\n", strerror(errno));
+ }
+ goto ret;
+ }
+
+ urandom_fd = TEMP_FAILURE_RETRY(
+ open("/dev/urandom", O_WRONLY | O_NOFOLLOW | O_CLOEXEC));
+ if (urandom_fd == -1) {
+ ERROR("Failed to open /dev/urandom: %s\n", strerror(errno));
+ goto ret;
+ }
+
+ while (total_bytes_written < sizeof(buf)) {
+ chunk_size = TEMP_FAILURE_RETRY(
+ read(hwrandom_fd, buf, sizeof(buf) - total_bytes_written));
+ if (chunk_size == -1) {
+ ERROR("Failed to read from /dev/hw_random: %s\n", strerror(errno));
+ goto ret;
+ } else if (chunk_size == 0) {
+ ERROR("Failed to read from /dev/hw_random: EOF\n");
+ goto ret;
+ }
+
+ chunk_size = TEMP_FAILURE_RETRY(write(urandom_fd, buf, chunk_size));
+ if (chunk_size == -1) {
+ ERROR("Failed to write to /dev/urandom: %s\n", strerror(errno));
+ goto ret;
+ }
+ total_bytes_written += chunk_size;
+ }
+
+ INFO("Mixed %zu bytes from /dev/hw_random into /dev/urandom",
+ total_bytes_written);
+ result = 0;
+
+ret:
+ if (hwrandom_fd != -1) {
+ close(hwrandom_fd);
+ }
+ if (urandom_fd != -1) {
+ close(urandom_fd);
+ }
+ return result;
+}
+
+static int keychord_init_action(int nargs, char **args)
+{
+ keychord_init();
+ return 0;
+}
+
+static int console_init_action(int nargs, char **args)
+{
+ std::string console = property_get("ro.boot.console");
+ if (!console.empty()) {
+ console_name = "/dev/" + console;
+ }
+
+ int fd = open(console_name.c_str(), O_RDWR | O_CLOEXEC);
+ if (fd >= 0)
+ have_console = 1;
+ close(fd);
+
+ fd = open("/dev/tty0", O_WRONLY | O_CLOEXEC);
+ if (fd >= 0) {
+ const char *msg;
+ msg = "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n" // console is 40 cols x 30 lines
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ " A N D R O I D ";
+ write(fd, msg, strlen(msg));
+ close(fd);
+ }
+
+ return 0;
+}
+
+static void import_kernel_nv(const std::string& key, const std::string& value, bool for_emulator) {
+ if (key.empty()) return;
+
+ if (for_emulator) {
+ // In the emulator, export any kernel option with the "ro.kernel." prefix.
+ property_set(android::base::StringPrintf("ro.kernel.%s", key.c_str()).c_str(), value.c_str());
+ return;
+ }
+
+ if (key == "qemu") {
+ strlcpy(qemu, value.c_str(), sizeof(qemu));
+ } else if (android::base::StartsWith(key, "androidboot.")) {
+ property_set(android::base::StringPrintf("ro.boot.%s", key.c_str() + 12).c_str(),
+ value.c_str());
+ }
+}
+
+static void export_kernel_boot_props() {
+ struct {
+ const char *src_prop;
+ const char *dst_prop;
+ const char *default_value;
+ } prop_map[] = {
+ { "ro.boot.serialno", "ro.serialno", "", },
+ { "ro.boot.mode", "ro.bootmode", "unknown", },
+ { "ro.boot.baseband", "ro.baseband", "unknown", },
+ { "ro.boot.bootloader", "ro.bootloader", "unknown", },
+ { "ro.boot.hardware", "ro.hardware", "unknown", },
+ { "ro.boot.revision", "ro.revision", "0", },
+ };
+ for (size_t i = 0; i < ARRAY_SIZE(prop_map); i++) {
+ std::string value = property_get(prop_map[i].src_prop);
+ property_set(prop_map[i].dst_prop, (!value.empty()) ? value.c_str() : prop_map[i].default_value);
+ }
+}
+
+static void process_kernel_dt() {
+ static const char android_dir[] = "/proc/device-tree/firmware/android";
+
+ std::string file_name = android::base::StringPrintf("%s/compatible", android_dir);
+
+ std::string dt_file;
+ android::base::ReadFileToString(file_name, &dt_file);
+ if (!dt_file.compare("android,firmware")) {
+ ERROR("firmware/android is not compatible with 'android,firmware'\n");
+ return;
+ }
+
+ std::unique_ptr<DIR, int(*)(DIR*)>dir(opendir(android_dir), closedir);
+ if (!dir) return;
+
+ struct dirent *dp;
+ while ((dp = readdir(dir.get())) != NULL) {
+ if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible")) {
+ continue;
+ }
+
+ file_name = android::base::StringPrintf("%s/%s", android_dir, dp->d_name);
+
+ android::base::ReadFileToString(file_name, &dt_file);
+ std::replace(dt_file.begin(), dt_file.end(), ',', '.');
+
+ std::string property_name = android::base::StringPrintf("ro.boot.%s", dp->d_name);
+ property_set(property_name.c_str(), dt_file.c_str());
+ }
+}
+
+static void process_kernel_cmdline() {
+ // Don't expose the raw commandline to unprivileged processes.
+ chmod("/proc/cmdline", 0440);
+
+ // The first pass does the common stuff, and finds if we are in qemu.
+ // The second pass is only necessary for qemu to export all kernel params
+ // as properties.
+ import_kernel_cmdline(false, import_kernel_nv);
+ if (qemu[0]) import_kernel_cmdline(true, import_kernel_nv);
+}
+
+static int queue_property_triggers_action(int nargs, char **args)
+{
+ queue_all_property_triggers();
+ /* enable property triggers */
+ property_triggers_enabled = 1;
+ return 0;
+}
+
+static void selinux_init_all_handles(void)
+{
+ sehandle = selinux_android_file_context_handle();
+ selinux_android_set_sehandle(sehandle);
+ sehandle_prop = selinux_android_prop_context_handle();
+}
+
+enum selinux_enforcing_status { SELINUX_PERMISSIVE, SELINUX_ENFORCING };
+
+static selinux_enforcing_status selinux_status_from_cmdline() {
+ selinux_enforcing_status status = SELINUX_ENFORCING;
+
+ import_kernel_cmdline(false, [&](const std::string& key, const std::string& value, bool in_qemu) {
+ if (key == "androidboot.selinux" && value == "permissive") {
+ status = SELINUX_PERMISSIVE;
+ }
+ });
+
+ return status;
+}
+
+static bool selinux_is_enforcing(void)
+{
+ if (ALLOW_PERMISSIVE_SELINUX) {
+ return selinux_status_from_cmdline() == SELINUX_ENFORCING;
+ }
+ return true;
+}
+
+int selinux_reload_policy(void)
+{
+ INFO("SELinux: Attempting to reload policy files\n");
+
+ if (selinux_android_reload_policy() == -1) {
+ return -1;
+ }
+
+ if (sehandle)
+ selabel_close(sehandle);
+
+ if (sehandle_prop)
+ selabel_close(sehandle_prop);
+
+ selinux_init_all_handles();
+ return 0;
+}
+
+static int audit_callback(void *data, security_class_t /*cls*/, char *buf, size_t len) {
+ snprintf(buf, len, "property=%s", !data ? "NULL" : (char *)data);
+ return 0;
+}
+
+static void security_failure() {
+ ERROR("Security failure; rebooting into recovery mode...\n");
+ android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
+ while (true) { pause(); } // never reached
+}
+
+static void selinux_initialize(bool in_kernel_domain) {
+ Timer t;
+
+ selinux_callback cb;
+ cb.func_log = selinux_klog_callback;
+ selinux_set_callback(SELINUX_CB_LOG, cb);
+ cb.func_audit = audit_callback;
+ selinux_set_callback(SELINUX_CB_AUDIT, cb);
+
+ if (in_kernel_domain) {
+ INFO("Loading SELinux policy...\n");
+ if (selinux_android_load_policy() < 0) {
+ ERROR("failed to load policy: %s\n", strerror(errno));
+ security_failure();
+ }
+
+ bool kernel_enforcing = (security_getenforce() == 1);
+ bool is_enforcing = selinux_is_enforcing();
+ if (kernel_enforcing != is_enforcing) {
+ if (security_setenforce(is_enforcing)) {
+ ERROR("security_setenforce(%s) failed: %s\n",
+ is_enforcing ? "true" : "false", strerror(errno));
+ security_failure();
+ }
+ }
+
+ if (write_file("/sys/fs/selinux/checkreqprot", "0") == -1) {
+ security_failure();
+ }
+
+ NOTICE("(Initializing SELinux %s took %.2fs.)\n",
+ is_enforcing ? "enforcing" : "non-enforcing", t.duration());
+ } else {
+ selinux_init_all_handles();
+ }
+}
+
+int main(int argc, char** argv) {
+ if (!strcmp(basename(argv[0]), "ueventd")) {
+ return ueventd_main(argc, argv);
+ }
+
+ if (!strcmp(basename(argv[0]), "watchdogd")) {
+ return watchdogd_main(argc, argv);
+ }
+
+ // Clear the umask.
+ umask(0);
+
+ add_environment("PATH", _PATH_DEFPATH);
+
+ bool is_first_stage = (argc == 1) || (strcmp(argv[1], "--second-stage") != 0);
+
+ // Get the basic filesystem setup we need put together in the initramdisk
+ // on / and then we'll let the rc file figure out the rest.
+ if (is_first_stage) {
+ mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
+ mkdir("/dev/pts", 0755);
+ mkdir("/dev/socket", 0755);
+ mount("devpts", "/dev/pts", "devpts", 0, NULL);
+ mount("proc", "/proc", "proc", 0, NULL);
+ mount("sysfs", "/sys", "sysfs", 0, NULL);
+ }
+
+ // We must have some place other than / to create the device nodes for
+ // kmsg and null, otherwise we won't be able to remount / read-only
+ // later on. Now that tmpfs is mounted on /dev, we can actually talk
+ // to the outside world.
+ open_devnull_stdio();
+ klog_init();
+ klog_set_level(KLOG_NOTICE_LEVEL);
+
+ NOTICE("init %s started!\n", is_first_stage ? "first stage" : "second stage");
+
+ if (!is_first_stage) {
+ // Indicate that booting is in progress to background fw loaders, etc.
+ close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
+
+ property_init();
+
+ // If arguments are passed both on the command line and in DT,
+ // properties set in DT always have priority over the command-line ones.
+ process_kernel_dt();
+ process_kernel_cmdline();
+
+ // Propagate the kernel variables to internal variables
+ // used by init as well as the current required properties.
+ export_kernel_boot_props();
+ }
+
+ // Set up SELinux, including loading the SELinux policy if we're in the kernel domain.
+ selinux_initialize(is_first_stage);
+
+ // If we're in the kernel domain, re-exec init to transition to the init domain now
+ // that the SELinux policy has been loaded.
+ if (is_first_stage) {
+ if (restorecon("/init") == -1) {
+ ERROR("restorecon failed: %s\n", strerror(errno));
+ security_failure();
+ }
+ char* path = argv[0];
+ char* args[] = { path, const_cast<char*>("--second-stage"), nullptr };
+ if (execv(path, args) == -1) {
+ ERROR("execv(\"%s\") failed: %s\n", path, strerror(errno));
+ security_failure();
+ }
+ }
+
+ // These directories were necessarily created before initial policy load
+ // and therefore need their security context restored to the proper value.
+ // This must happen before /dev is populated by ueventd.
+ NOTICE("Running restorecon...\n");
+ restorecon("/dev");
+ restorecon("/dev/socket");
+ restorecon("/dev/__properties__");
+ restorecon_recursive("/sys");
+
+ epoll_fd = epoll_create1(EPOLL_CLOEXEC);
+ if (epoll_fd == -1) {
+ ERROR("epoll_create1 failed: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ signal_handler_init();
+
+ property_load_boot_defaults();
+ start_property_service();
+
+ init_parse_config("/init.rc");
+
+ action_for_each_trigger("early-init", action_add_queue_tail);
+
+ // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
+ queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
+ // ... so that we can start queuing up actions that require stuff from /dev.
+ queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
+ queue_builtin_action(keychord_init_action, "keychord_init");
+ queue_builtin_action(console_init_action, "console_init");
+
+ // Trigger all the boot actions to get us started.
+ action_for_each_trigger("init", action_add_queue_tail);
+
+ // Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
+ // wasn't ready immediately after wait_for_coldboot_done
+ queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
+
+ // Don't mount filesystems or start core system services in charger mode.
+ std::string bootmode = property_get("ro.bootmode");
+ if (bootmode == "charger") {
+ action_for_each_trigger("charger", action_add_queue_tail);
+ } else {
+ action_for_each_trigger("late-init", action_add_queue_tail);
+ }
+
+ // Run all property triggers based on current state of the properties.
+ queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");
+
+ while (true) {
+ if (!waiting_for_exec) {
+ execute_one_command();
+ restart_processes();
+ }
+
+ int timeout = -1;
+ if (process_needs_restart) {
+ timeout = (process_needs_restart - gettime()) * 1000;
+ if (timeout < 0)
+ timeout = 0;
+ }
+
+ if (!action_queue_empty() || cur_action) {
+ timeout = 0;
+ }
+
+ bootchart_sample(&timeout);
+
+ epoll_event ev;
+ int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));
+ if (nr == -1) {
+ ERROR("epoll_wait failed: %s\n", strerror(errno));
+ } else if (nr == 1) {
+ ((void (*)()) ev.data.ptr)();
+ }
+ }
+
+ return 0;
+}
diff --git a/init/init.h b/init/init.h
new file mode 100644
index 0000000..d2b2dfb
--- /dev/null
+++ b/init/init.h
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_INIT_H
+#define _INIT_INIT_H
+
+#include <sys/types.h>
+
+#include <string>
+#include <vector>
+
+#include <cutils/list.h>
+#include <cutils/iosched_policy.h>
+
+struct command
+{
+ /* list of commands in an action */
+ struct listnode clist;
+
+ int (*func)(int nargs, char **args);
+
+ int line;
+ const char *filename;
+
+ int nargs;
+ char *args[1];
+};
+
+struct trigger {
+ struct listnode nlist;
+ const char *name;
+};
+
+struct action {
+ /* node in list of all actions */
+ struct listnode alist;
+ /* node in the queue of pending actions */
+ struct listnode qlist;
+ /* node in list of actions for a trigger */
+ struct listnode tlist;
+
+ unsigned hash;
+
+ /* list of actions which triggers the commands*/
+ struct listnode triggers;
+ struct listnode commands;
+ struct command *current;
+};
+
+struct socketinfo {
+ struct socketinfo *next;
+ const char *name;
+ const char *type;
+ uid_t uid;
+ gid_t gid;
+ int perm;
+ const char *socketcon;
+};
+
+struct svcenvinfo {
+ struct svcenvinfo *next;
+ const char *name;
+ const char *value;
+};
+
+#define SVC_DISABLED 0x001 // do not autostart with class
+#define SVC_ONESHOT 0x002 // do not restart on exit
+#define SVC_RUNNING 0x004 // currently active
+#define SVC_RESTARTING 0x008 // waiting to restart
+#define SVC_CONSOLE 0x010 // requires console
+#define SVC_CRITICAL 0x020 // will reboot into recovery if keeps crashing
+#define SVC_RESET 0x040 // Use when stopping a process, but not disabling so it can be restarted with its class.
+#define SVC_RC_DISABLED 0x080 // Remember if the disabled flag was set in the rc script.
+#define SVC_RESTART 0x100 // Use to safely restart (stop, wait, start) a service.
+#define SVC_DISABLED_START 0x200 // A start was requested but it was disabled at the time.
+#define SVC_EXEC 0x400 // This synthetic service corresponds to an 'exec'.
+
+#define NR_SVC_SUPP_GIDS 12 /* twelve supplementary groups */
+
+#define COMMAND_RETRY_TIMEOUT 5
+
+struct service {
+ void NotifyStateChange(const char* new_state);
+
+ /* list of all services */
+ struct listnode slist;
+
+ char *name;
+ const char *classname;
+
+ unsigned flags;
+ pid_t pid;
+ time_t time_started; /* time of last start */
+ time_t time_crashed; /* first crash within inspection window */
+ int nr_crashed; /* number of times crashed within window */
+
+ uid_t uid;
+ gid_t gid;
+ gid_t supp_gids[NR_SVC_SUPP_GIDS];
+ size_t nr_supp_gids;
+
+ const char* seclabel;
+
+ struct socketinfo *sockets;
+ struct svcenvinfo *envvars;
+
+ struct action onrestart; /* Actions to execute on restart. */
+
+ std::vector<std::string>* writepid_files_;
+
+ /* keycodes for triggering this service via /dev/keychord */
+ int *keycodes;
+ int nkeycodes;
+ int keychord_id;
+
+ IoSchedClass ioprio_class;
+ int ioprio_pri;
+
+ int nargs;
+ /* "MUST BE AT THE END OF THE STRUCT" */
+ char *args[1];
+}; /* ^-------'args' MUST be at the end of this struct! */
+
+extern bool waiting_for_exec;
+extern struct selabel_handle *sehandle;
+extern struct selabel_handle *sehandle_prop;
+
+std::string build_triggers_string(struct action *cur_action);
+
+void handle_control_message(const char *msg, const char *arg);
+
+struct service *service_find_by_name(const char *name);
+struct service *service_find_by_pid(pid_t pid);
+struct service *service_find_by_keychord(int keychord_id);
+void service_for_each(void (*func)(struct service *svc));
+void service_for_each_class(const char *classname,
+ void (*func)(struct service *svc));
+void service_for_each_flags(unsigned matchflags,
+ void (*func)(struct service *svc));
+void service_stop(struct service *svc);
+void service_reset(struct service *svc);
+void service_restart(struct service *svc);
+void service_start(struct service *svc, const char *dynamic_args);
+void property_changed(const char *name, const char *value);
+
+int selinux_reload_policy(void);
+
+void zap_stdio(void);
+
+void register_epoll_handler(int fd, void (*fn)());
+bool expand_command_arguments(int nargs, char** args, std::vector<std::string>* expanded_args);
+
+#endif /* _INIT_INIT_H */
diff --git a/init/init_parser.cpp b/init/init_parser.cpp
new file mode 100644
index 0000000..460f5ac
--- /dev/null
+++ b/init/init_parser.cpp
@@ -0,0 +1,994 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "init.h"
+#include "parser.h"
+#include "init_parser.h"
+#include "log.h"
+#include "property_service.h"
+#include "util.h"
+
+#include <base/stringprintf.h>
+#include <cutils/iosched_policy.h>
+#include <cutils/list.h>
+
+static list_declare(service_list);
+static list_declare(action_list);
+static list_declare(action_queue);
+
+struct import {
+ struct listnode list;
+ const char *filename;
+};
+
+static void *parse_service(struct parse_state *state, int nargs, char **args);
+static void parse_line_service(struct parse_state *state, int nargs, char **args);
+
+static void *parse_action(struct parse_state *state, int nargs, char **args);
+static void parse_line_action(struct parse_state *state, int nargs, char **args);
+
+#define SECTION 0x01
+#define COMMAND 0x02
+#define OPTION 0x04
+
+#include "keywords.h"
+
+#define KEYWORD(symbol, flags, nargs, func) \
+ [ K_##symbol ] = { #symbol, func, nargs + 1, flags, },
+
+static struct {
+ const char *name;
+ int (*func)(int nargs, char **args);
+ unsigned char nargs;
+ unsigned char flags;
+} keyword_info[KEYWORD_COUNT] = {
+ [ K_UNKNOWN ] = { "unknown", 0, 0, 0 },
+#include "keywords.h"
+};
+#undef KEYWORD
+
+#define kw_is(kw, type) (keyword_info[kw].flags & (type))
+#define kw_name(kw) (keyword_info[kw].name)
+#define kw_func(kw) (keyword_info[kw].func)
+#define kw_nargs(kw) (keyword_info[kw].nargs)
+
+void dump_parser_state() {
+ if (false) {
+ struct listnode* node;
+ list_for_each(node, &service_list) {
+ service* svc = node_to_item(node, struct service, slist);
+ INFO("service %s\n", svc->name);
+ INFO(" class '%s'\n", svc->classname);
+ INFO(" exec");
+ for (int n = 0; n < svc->nargs; n++) {
+ INFO(" '%s'", svc->args[n]);
+ }
+ INFO("\n");
+ for (socketinfo* si = svc->sockets; si; si = si->next) {
+ INFO(" socket %s %s 0%o\n", si->name, si->type, si->perm);
+ }
+ }
+
+ list_for_each(node, &action_list) {
+ action* act = node_to_item(node, struct action, alist);
+ INFO("on ");
+ std::string trigger_name = build_triggers_string(act);
+ INFO("%s", trigger_name.c_str());
+ INFO("\n");
+
+ struct listnode* node2;
+ list_for_each(node2, &act->commands) {
+ command* cmd = node_to_item(node2, struct command, clist);
+ INFO(" %p", cmd->func);
+ for (int n = 0; n < cmd->nargs; n++) {
+ INFO(" %s", cmd->args[n]);
+ }
+ INFO("\n");
+ }
+ INFO("\n");
+ }
+ }
+}
+
+static int lookup_keyword(const char *s)
+{
+ switch (*s++) {
+ case 'b':
+ if (!strcmp(s, "ootchart_init")) return K_bootchart_init;
+ break;
+ case 'c':
+ if (!strcmp(s, "opy")) return K_copy;
+ if (!strcmp(s, "lass")) return K_class;
+ if (!strcmp(s, "lass_start")) return K_class_start;
+ if (!strcmp(s, "lass_stop")) return K_class_stop;
+ if (!strcmp(s, "lass_reset")) return K_class_reset;
+ if (!strcmp(s, "onsole")) return K_console;
+ if (!strcmp(s, "hown")) return K_chown;
+ if (!strcmp(s, "hmod")) return K_chmod;
+ if (!strcmp(s, "ritical")) return K_critical;
+ break;
+ case 'd':
+ if (!strcmp(s, "isabled")) return K_disabled;
+ if (!strcmp(s, "omainname")) return K_domainname;
+ break;
+ case 'e':
+ if (!strcmp(s, "nable")) return K_enable;
+ if (!strcmp(s, "xec")) return K_exec;
+ if (!strcmp(s, "xport")) return K_export;
+ break;
+ case 'g':
+ if (!strcmp(s, "roup")) return K_group;
+ break;
+ case 'h':
+ if (!strcmp(s, "ostname")) return K_hostname;
+ break;
+ case 'i':
+ if (!strcmp(s, "oprio")) return K_ioprio;
+ if (!strcmp(s, "fup")) return K_ifup;
+ if (!strcmp(s, "nsmod")) return K_insmod;
+ if (!strcmp(s, "mport")) return K_import;
+ if (!strcmp(s, "nstallkey")) return K_installkey;
+ break;
+ case 'k':
+ if (!strcmp(s, "eycodes")) return K_keycodes;
+ break;
+ case 'l':
+ if (!strcmp(s, "oglevel")) return K_loglevel;
+ if (!strcmp(s, "oad_persist_props")) return K_load_persist_props;
+ if (!strcmp(s, "oad_all_props")) return K_load_all_props;
+ break;
+ case 'm':
+ if (!strcmp(s, "kdir")) return K_mkdir;
+ if (!strcmp(s, "ount_all")) return K_mount_all;
+ if (!strcmp(s, "ount")) return K_mount;
+ break;
+ case 'o':
+ if (!strcmp(s, "n")) return K_on;
+ if (!strcmp(s, "neshot")) return K_oneshot;
+ if (!strcmp(s, "nrestart")) return K_onrestart;
+ break;
+ case 'p':
+ if (!strcmp(s, "owerctl")) return K_powerctl;
+ break;
+ case 'r':
+ if (!strcmp(s, "estart")) return K_restart;
+ if (!strcmp(s, "estorecon")) return K_restorecon;
+ if (!strcmp(s, "estorecon_recursive")) return K_restorecon_recursive;
+ if (!strcmp(s, "mdir")) return K_rmdir;
+ if (!strcmp(s, "m")) return K_rm;
+ break;
+ case 's':
+ if (!strcmp(s, "eclabel")) return K_seclabel;
+ if (!strcmp(s, "ervice")) return K_service;
+ if (!strcmp(s, "etenv")) return K_setenv;
+ if (!strcmp(s, "etprop")) return K_setprop;
+ if (!strcmp(s, "etrlimit")) return K_setrlimit;
+ if (!strcmp(s, "ocket")) return K_socket;
+ if (!strcmp(s, "tart")) return K_start;
+ if (!strcmp(s, "top")) return K_stop;
+ if (!strcmp(s, "wapon_all")) return K_swapon_all;
+ if (!strcmp(s, "ymlink")) return K_symlink;
+ if (!strcmp(s, "ysclktz")) return K_sysclktz;
+ break;
+ case 't':
+ if (!strcmp(s, "rigger")) return K_trigger;
+ break;
+ case 'u':
+ if (!strcmp(s, "ser")) return K_user;
+ break;
+ case 'v':
+ if (!strcmp(s, "erity_load_state")) return K_verity_load_state;
+ if (!strcmp(s, "erity_update_state")) return K_verity_update_state;
+ break;
+ case 'w':
+ if (!strcmp(s, "rite")) return K_write;
+ if (!strcmp(s, "ritepid")) return K_writepid;
+ if (!strcmp(s, "ait")) return K_wait;
+ break;
+ }
+ return K_UNKNOWN;
+}
+
+static void parse_line_no_op(struct parse_state*, int, char**) {
+}
+
+int expand_props(const char *src, std::string *dst) {
+ const char *src_ptr = src;
+
+ if (!src || !dst) {
+ return -1;
+ }
+
+ /* - variables can either be $x.y or ${x.y}, in case they are only part
+ * of the string.
+ * - will accept $$ as a literal $.
+ * - no nested property expansion, i.e. ${foo.${bar}} is not supported,
+ * bad things will happen
+ */
+ while (*src_ptr) {
+ const char *c;
+
+ c = strchr(src_ptr, '$');
+ if (!c) {
+ dst->append(src_ptr);
+ break;
+ }
+
+ dst->append(src_ptr, c);
+ c++;
+
+ if (*c == '$') {
+ dst->push_back(*(c++));
+ src_ptr = c;
+ continue;
+ } else if (*c == '\0') {
+ break;
+ }
+
+ std::string prop_name;
+ if (*c == '{') {
+ c++;
+ const char* end = strchr(c, '}');
+ if (!end) {
+ // failed to find closing brace, abort.
+ ERROR("unexpected end of string in '%s', looking for }\n", src);
+ goto err;
+ }
+ prop_name = std::string(c, end);
+ c = end + 1;
+ } else {
+ prop_name = c;
+ ERROR("using deprecated syntax for specifying property '%s', use ${name} instead\n",
+ c);
+ c += prop_name.size();
+ }
+
+ if (prop_name.empty()) {
+ ERROR("invalid zero-length prop name in '%s'\n", src);
+ goto err;
+ }
+
+ std::string prop_val = property_get(prop_name.c_str());
+ if (prop_val.empty()) {
+ ERROR("property '%s' doesn't exist while expanding '%s'\n",
+ prop_name.c_str(), src);
+ goto err;
+ }
+
+ dst->append(prop_val);
+ src_ptr = c;
+ continue;
+ }
+
+ return 0;
+err:
+ return -1;
+}
+
+static void parse_import(struct parse_state *state, int nargs, char **args)
+{
+ if (nargs != 2) {
+ ERROR("single argument needed for import\n");
+ return;
+ }
+
+ std::string conf_file;
+ int ret = expand_props(args[1], &conf_file);
+ if (ret) {
+ ERROR("error while handling import on line '%d' in '%s'\n",
+ state->line, state->filename);
+ return;
+ }
+
+ struct import* import = (struct import*) calloc(1, sizeof(struct import));
+ import->filename = strdup(conf_file.c_str());
+
+ struct listnode *import_list = (listnode*) state->priv;
+ list_add_tail(import_list, &import->list);
+ INFO("Added '%s' to import list\n", import->filename);
+}
+
+static void parse_new_section(struct parse_state *state, int kw,
+ int nargs, char **args)
+{
+ printf("[ %s %s ]\n", args[0],
+ nargs > 1 ? args[1] : "");
+ switch(kw) {
+ case K_service:
+ state->context = parse_service(state, nargs, args);
+ if (state->context) {
+ state->parse_line = parse_line_service;
+ return;
+ }
+ break;
+ case K_on:
+ state->context = parse_action(state, nargs, args);
+ if (state->context) {
+ state->parse_line = parse_line_action;
+ return;
+ }
+ break;
+ case K_import:
+ parse_import(state, nargs, args);
+ break;
+ }
+ state->parse_line = parse_line_no_op;
+}
+
+static void parse_config(const char *fn, const std::string& data)
+{
+ struct listnode import_list;
+ struct listnode *node;
+ char *args[INIT_PARSER_MAXARGS];
+
+ int nargs = 0;
+
+ parse_state state;
+ state.filename = fn;
+ state.line = 0;
+ state.ptr = strdup(data.c_str()); // TODO: fix this code!
+ state.nexttoken = 0;
+ state.parse_line = parse_line_no_op;
+
+ list_init(&import_list);
+ state.priv = &import_list;
+
+ for (;;) {
+ switch (next_token(&state)) {
+ case T_EOF:
+ state.parse_line(&state, 0, 0);
+ goto parser_done;
+ case T_NEWLINE:
+ state.line++;
+ if (nargs) {
+ int kw = lookup_keyword(args[0]);
+ if (kw_is(kw, SECTION)) {
+ state.parse_line(&state, 0, 0);
+ parse_new_section(&state, kw, nargs, args);
+ } else {
+ state.parse_line(&state, nargs, args);
+ }
+ nargs = 0;
+ }
+ break;
+ case T_TEXT:
+ if (nargs < INIT_PARSER_MAXARGS) {
+ args[nargs++] = state.text;
+ }
+ break;
+ }
+ }
+
+parser_done:
+ list_for_each(node, &import_list) {
+ struct import* import = node_to_item(node, struct import, list);
+ if (!init_parse_config(import->filename)) {
+ ERROR("could not import file '%s' from '%s': %s\n",
+ import->filename, fn, strerror(errno));
+ }
+ }
+}
+
+static bool init_parse_config_file(const char* path) {
+ INFO("Parsing file %s...\n", path);
+ Timer t;
+ std::string data;
+ if (!read_file(path, &data)) {
+ return false;
+ }
+
+ data.push_back('\n'); // TODO: fix parse_config.
+ parse_config(path, data);
+ dump_parser_state();
+
+ NOTICE("(Parsing %s took %.2fs.)\n", path, t.duration());
+ return true;
+}
+
+static bool init_parse_config_dir(const char* path) {
+ INFO("Parsing directory %s...\n", path);
+ std::unique_ptr<DIR, int(*)(DIR*)> config_dir(opendir(path), closedir);
+ if (!config_dir) {
+ ERROR("Could not import directory '%s'\n", path);
+ return false;
+ }
+ dirent* current_file;
+ while ((current_file = readdir(config_dir.get()))) {
+ std::string current_path =
+ android::base::StringPrintf("%s/%s", path, current_file->d_name);
+ // Ignore directories and only process regular files.
+ if (current_file->d_type == DT_REG) {
+ if (!init_parse_config_file(current_path.c_str())) {
+ ERROR("could not import file '%s'\n", current_path.c_str());
+ }
+ }
+ }
+ return true;
+}
+
+bool init_parse_config(const char* path) {
+ if (is_dir(path)) {
+ return init_parse_config_dir(path);
+ }
+ return init_parse_config_file(path);
+}
+
+static int valid_name(const char *name)
+{
+ if (strlen(name) > 16) {
+ return 0;
+ }
+ while (*name) {
+ if (!isalnum(*name) && (*name != '_') && (*name != '-')) {
+ return 0;
+ }
+ name++;
+ }
+ return 1;
+}
+
+struct service *service_find_by_name(const char *name)
+{
+ struct listnode *node;
+ struct service *svc;
+ list_for_each(node, &service_list) {
+ svc = node_to_item(node, struct service, slist);
+ if (!strcmp(svc->name, name)) {
+ return svc;
+ }
+ }
+ return 0;
+}
+
+struct service *service_find_by_pid(pid_t pid)
+{
+ struct listnode *node;
+ struct service *svc;
+ list_for_each(node, &service_list) {
+ svc = node_to_item(node, struct service, slist);
+ if (svc->pid == pid) {
+ return svc;
+ }
+ }
+ return 0;
+}
+
+struct service *service_find_by_keychord(int keychord_id)
+{
+ struct listnode *node;
+ struct service *svc;
+ list_for_each(node, &service_list) {
+ svc = node_to_item(node, struct service, slist);
+ if (svc->keychord_id == keychord_id) {
+ return svc;
+ }
+ }
+ return 0;
+}
+
+void service_for_each(void (*func)(struct service *svc))
+{
+ struct listnode *node;
+ struct service *svc;
+ list_for_each(node, &service_list) {
+ svc = node_to_item(node, struct service, slist);
+ func(svc);
+ }
+}
+
+void service_for_each_class(const char *classname,
+ void (*func)(struct service *svc))
+{
+ struct listnode *node;
+ struct service *svc;
+ list_for_each(node, &service_list) {
+ svc = node_to_item(node, struct service, slist);
+ if (!strcmp(svc->classname, classname)) {
+ func(svc);
+ }
+ }
+}
+
+void service_for_each_flags(unsigned matchflags,
+ void (*func)(struct service *svc))
+{
+ struct listnode *node;
+ struct service *svc;
+ list_for_each(node, &service_list) {
+ svc = node_to_item(node, struct service, slist);
+ if (svc->flags & matchflags) {
+ func(svc);
+ }
+ }
+}
+
+void action_for_each_trigger(const char *trigger,
+ void (*func)(struct action *act))
+{
+ struct listnode *node, *node2;
+ struct action *act;
+ struct trigger *cur_trigger;
+
+ list_for_each(node, &action_list) {
+ act = node_to_item(node, struct action, alist);
+ list_for_each(node2, &act->triggers) {
+ cur_trigger = node_to_item(node2, struct trigger, nlist);
+ if (!strcmp(cur_trigger->name, trigger)) {
+ func(act);
+ }
+ }
+ }
+}
+
+
+void queue_property_triggers(const char *name, const char *value)
+{
+ struct listnode *node, *node2;
+ struct action *act;
+ struct trigger *cur_trigger;
+ bool match;
+ int name_length;
+
+ list_for_each(node, &action_list) {
+ act = node_to_item(node, struct action, alist);
+ match = !name;
+ list_for_each(node2, &act->triggers) {
+ cur_trigger = node_to_item(node2, struct trigger, nlist);
+ if (!strncmp(cur_trigger->name, "property:", strlen("property:"))) {
+ const char *test = cur_trigger->name + strlen("property:");
+ if (!match) {
+ name_length = strlen(name);
+ if (!strncmp(name, test, name_length) &&
+ test[name_length] == '=' &&
+ (!strcmp(test + name_length + 1, value) ||
+ !strcmp(test + name_length + 1, "*"))) {
+ match = true;
+ continue;
+ }
+ } else {
+ const char* equals = strchr(test, '=');
+ if (equals) {
+ int length = equals - test;
+ if (length <= PROP_NAME_MAX) {
+ std::string prop_name(test, length);
+ std::string value = property_get(prop_name.c_str());
+
+ /* does the property exist, and match the trigger value? */
+ if (!value.empty() && (!strcmp(equals + 1, value.c_str()) ||
+ !strcmp(equals + 1, "*"))) {
+ continue;
+ }
+ }
+ }
+ }
+ }
+ match = false;
+ break;
+ }
+ if (match) {
+ action_add_queue_tail(act);
+ }
+ }
+}
+
+void queue_all_property_triggers()
+{
+ queue_property_triggers(NULL, NULL);
+}
+
+void queue_builtin_action(int (*func)(int nargs, char **args), const char *name)
+{
+ action* act = (action*) calloc(1, sizeof(*act));
+ trigger* cur_trigger = (trigger*) calloc(1, sizeof(*cur_trigger));
+ cur_trigger->name = name;
+ list_init(&act->triggers);
+ list_add_tail(&act->triggers, &cur_trigger->nlist);
+ list_init(&act->commands);
+ list_init(&act->qlist);
+
+ command* cmd = (command*) calloc(1, sizeof(*cmd));
+ cmd->func = func;
+ cmd->args[0] = const_cast<char*>(name);
+ cmd->nargs = 1;
+ list_add_tail(&act->commands, &cmd->clist);
+
+ list_add_tail(&action_list, &act->alist);
+ action_add_queue_tail(act);
+}
+
+void action_add_queue_tail(struct action *act)
+{
+ if (list_empty(&act->qlist)) {
+ list_add_tail(&action_queue, &act->qlist);
+ }
+}
+
+struct action *action_remove_queue_head(void)
+{
+ if (list_empty(&action_queue)) {
+ return 0;
+ } else {
+ struct listnode *node = list_head(&action_queue);
+ struct action *act = node_to_item(node, struct action, qlist);
+ list_remove(node);
+ list_init(node);
+ return act;
+ }
+}
+
+int action_queue_empty()
+{
+ return list_empty(&action_queue);
+}
+
+service* make_exec_oneshot_service(int nargs, char** args) {
+ // Parse the arguments: exec [SECLABEL [UID [GID]*] --] COMMAND ARGS...
+ // SECLABEL can be a - to denote default
+ int command_arg = 1;
+ for (int i = 1; i < nargs; ++i) {
+ if (strcmp(args[i], "--") == 0) {
+ command_arg = i + 1;
+ break;
+ }
+ }
+ if (command_arg > 4 + NR_SVC_SUPP_GIDS) {
+ ERROR("exec called with too many supplementary group ids\n");
+ return NULL;
+ }
+
+ int argc = nargs - command_arg;
+ char** argv = (args + command_arg);
+ if (argc < 1) {
+ ERROR("exec called without command\n");
+ return NULL;
+ }
+
+ service* svc = (service*) calloc(1, sizeof(*svc) + sizeof(char*) * argc);
+ if (svc == NULL) {
+ ERROR("Couldn't allocate service for exec of '%s': %s", argv[0], strerror(errno));
+ return NULL;
+ }
+
+ if ((command_arg > 2) && strcmp(args[1], "-")) {
+ svc->seclabel = args[1];
+ }
+ if (command_arg > 3) {
+ svc->uid = decode_uid(args[2]);
+ }
+ if (command_arg > 4) {
+ svc->gid = decode_uid(args[3]);
+ svc->nr_supp_gids = command_arg - 1 /* -- */ - 4 /* exec SECLABEL UID GID */;
+ for (size_t i = 0; i < svc->nr_supp_gids; ++i) {
+ svc->supp_gids[i] = decode_uid(args[4 + i]);
+ }
+ }
+
+ static int exec_count; // Every service needs a unique name.
+ char* name = NULL;
+ asprintf(&name, "exec %d (%s)", exec_count++, argv[0]);
+ if (name == NULL) {
+ ERROR("Couldn't allocate name for exec service '%s'\n", argv[0]);
+ free(svc);
+ return NULL;
+ }
+ svc->name = name;
+ svc->classname = "default";
+ svc->flags = SVC_EXEC | SVC_ONESHOT;
+ svc->nargs = argc;
+ memcpy(svc->args, argv, sizeof(char*) * svc->nargs);
+ svc->args[argc] = NULL;
+ list_add_tail(&service_list, &svc->slist);
+ return svc;
+}
+
+static void *parse_service(struct parse_state *state, int nargs, char **args)
+{
+ if (nargs < 3) {
+ parse_error(state, "services must have a name and a program\n");
+ return 0;
+ }
+ if (!valid_name(args[1])) {
+ parse_error(state, "invalid service name '%s'\n", args[1]);
+ return 0;
+ }
+
+ service* svc = (service*) service_find_by_name(args[1]);
+ if (svc) {
+ parse_error(state, "ignored duplicate definition of service '%s'\n", args[1]);
+ return 0;
+ }
+
+ nargs -= 2;
+ svc = (service*) calloc(1, sizeof(*svc) + sizeof(char*) * nargs);
+ if (!svc) {
+ parse_error(state, "out of memory\n");
+ return 0;
+ }
+ svc->name = strdup(args[1]);
+ svc->classname = "default";
+ memcpy(svc->args, args + 2, sizeof(char*) * nargs);
+ trigger* cur_trigger = (trigger*) calloc(1, sizeof(*cur_trigger));
+ svc->args[nargs] = 0;
+ svc->nargs = nargs;
+ list_init(&svc->onrestart.triggers);
+ cur_trigger->name = "onrestart";
+ list_add_tail(&svc->onrestart.triggers, &cur_trigger->nlist);
+ list_init(&svc->onrestart.commands);
+ list_add_tail(&service_list, &svc->slist);
+ return svc;
+}
+
+static void parse_line_service(struct parse_state *state, int nargs, char **args)
+{
+ struct service *svc = (service*) state->context;
+ struct command *cmd;
+ int i, kw, kw_nargs;
+
+ if (nargs == 0) {
+ return;
+ }
+
+ svc->ioprio_class = IoSchedClass_NONE;
+
+ kw = lookup_keyword(args[0]);
+ switch (kw) {
+ case K_class:
+ if (nargs != 2) {
+ parse_error(state, "class option requires a classname\n");
+ } else {
+ svc->classname = args[1];
+ }
+ break;
+ case K_console:
+ svc->flags |= SVC_CONSOLE;
+ break;
+ case K_disabled:
+ svc->flags |= SVC_DISABLED;
+ svc->flags |= SVC_RC_DISABLED;
+ break;
+ case K_ioprio:
+ if (nargs != 3) {
+ parse_error(state, "ioprio optin usage: ioprio <rt|be|idle> <ioprio 0-7>\n");
+ } else {
+ svc->ioprio_pri = strtoul(args[2], 0, 8);
+
+ if (svc->ioprio_pri < 0 || svc->ioprio_pri > 7) {
+ parse_error(state, "priority value must be range 0 - 7\n");
+ break;
+ }
+
+ if (!strcmp(args[1], "rt")) {
+ svc->ioprio_class = IoSchedClass_RT;
+ } else if (!strcmp(args[1], "be")) {
+ svc->ioprio_class = IoSchedClass_BE;
+ } else if (!strcmp(args[1], "idle")) {
+ svc->ioprio_class = IoSchedClass_IDLE;
+ } else {
+ parse_error(state, "ioprio option usage: ioprio <rt|be|idle> <0-7>\n");
+ }
+ }
+ break;
+ case K_group:
+ if (nargs < 2) {
+ parse_error(state, "group option requires a group id\n");
+ } else if (nargs > NR_SVC_SUPP_GIDS + 2) {
+ parse_error(state, "group option accepts at most %d supp. groups\n",
+ NR_SVC_SUPP_GIDS);
+ } else {
+ int n;
+ svc->gid = decode_uid(args[1]);
+ for (n = 2; n < nargs; n++) {
+ svc->supp_gids[n-2] = decode_uid(args[n]);
+ }
+ svc->nr_supp_gids = n - 2;
+ }
+ break;
+ case K_keycodes:
+ if (nargs < 2) {
+ parse_error(state, "keycodes option requires atleast one keycode\n");
+ } else {
+ svc->keycodes = (int*) malloc((nargs - 1) * sizeof(svc->keycodes[0]));
+ if (!svc->keycodes) {
+ parse_error(state, "could not allocate keycodes\n");
+ } else {
+ svc->nkeycodes = nargs - 1;
+ for (i = 1; i < nargs; i++) {
+ svc->keycodes[i - 1] = atoi(args[i]);
+ }
+ }
+ }
+ break;
+ case K_oneshot:
+ svc->flags |= SVC_ONESHOT;
+ break;
+ case K_onrestart:
+ nargs--;
+ args++;
+ kw = lookup_keyword(args[0]);
+ if (!kw_is(kw, COMMAND)) {
+ parse_error(state, "invalid command '%s'\n", args[0]);
+ break;
+ }
+ kw_nargs = kw_nargs(kw);
+ if (nargs < kw_nargs) {
+ parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1,
+ kw_nargs > 2 ? "arguments" : "argument");
+ break;
+ }
+
+ cmd = (command*) malloc(sizeof(*cmd) + sizeof(char*) * nargs);
+ cmd->func = kw_func(kw);
+ cmd->nargs = nargs;
+ memcpy(cmd->args, args, sizeof(char*) * nargs);
+ list_add_tail(&svc->onrestart.commands, &cmd->clist);
+ break;
+ case K_critical:
+ svc->flags |= SVC_CRITICAL;
+ break;
+ case K_setenv: { /* name value */
+ if (nargs < 3) {
+ parse_error(state, "setenv option requires name and value arguments\n");
+ break;
+ }
+ svcenvinfo* ei = (svcenvinfo*) calloc(1, sizeof(*ei));
+ if (!ei) {
+ parse_error(state, "out of memory\n");
+ break;
+ }
+ ei->name = args[1];
+ ei->value = args[2];
+ ei->next = svc->envvars;
+ svc->envvars = ei;
+ break;
+ }
+ case K_socket: {/* name type perm [ uid gid context ] */
+ if (nargs < 4) {
+ parse_error(state, "socket option requires name, type, perm arguments\n");
+ break;
+ }
+ if (strcmp(args[2],"dgram") && strcmp(args[2],"stream")
+ && strcmp(args[2],"seqpacket")) {
+ parse_error(state, "socket type must be 'dgram', 'stream' or 'seqpacket'\n");
+ break;
+ }
+ socketinfo* si = (socketinfo*) calloc(1, sizeof(*si));
+ if (!si) {
+ parse_error(state, "out of memory\n");
+ break;
+ }
+ si->name = args[1];
+ si->type = args[2];
+ si->perm = strtoul(args[3], 0, 8);
+ if (nargs > 4)
+ si->uid = decode_uid(args[4]);
+ if (nargs > 5)
+ si->gid = decode_uid(args[5]);
+ if (nargs > 6)
+ si->socketcon = args[6];
+ si->next = svc->sockets;
+ svc->sockets = si;
+ break;
+ }
+ case K_user:
+ if (nargs != 2) {
+ parse_error(state, "user option requires a user id\n");
+ } else {
+ svc->uid = decode_uid(args[1]);
+ }
+ break;
+ case K_seclabel:
+ if (nargs != 2) {
+ parse_error(state, "seclabel option requires a label string\n");
+ } else {
+ svc->seclabel = args[1];
+ }
+ break;
+ case K_writepid:
+ if (nargs < 2) {
+ parse_error(state, "writepid option requires at least one filename\n");
+ break;
+ }
+ svc->writepid_files_ = new std::vector<std::string>;
+ for (int i = 1; i < nargs; ++i) {
+ svc->writepid_files_->push_back(args[i]);
+ }
+ break;
+
+ default:
+ parse_error(state, "invalid option '%s'\n", args[0]);
+ }
+}
+
+static void *parse_action(struct parse_state *state, int nargs, char **args)
+{
+ struct trigger *cur_trigger;
+ int i;
+ if (nargs < 2) {
+ parse_error(state, "actions must have a trigger\n");
+ return 0;
+ }
+
+ action* act = (action*) calloc(1, sizeof(*act));
+ list_init(&act->triggers);
+
+ for (i = 1; i < nargs; i++) {
+ if (!(i % 2)) {
+ if (strcmp(args[i], "&&")) {
+ struct listnode *node;
+ struct listnode *node2;
+ parse_error(state, "& is the only symbol allowed to concatenate actions\n");
+ list_for_each_safe(node, node2, &act->triggers) {
+ struct trigger *trigger = node_to_item(node, struct trigger, nlist);
+ free(trigger);
+ }
+ free(act);
+ return 0;
+ } else
+ continue;
+ }
+ cur_trigger = (trigger*) calloc(1, sizeof(*cur_trigger));
+ cur_trigger->name = args[i];
+ list_add_tail(&act->triggers, &cur_trigger->nlist);
+ }
+
+ list_init(&act->commands);
+ list_init(&act->qlist);
+ list_add_tail(&action_list, &act->alist);
+ /* XXX add to hash */
+ return act;
+}
+
+static void parse_line_action(struct parse_state* state, int nargs, char **args)
+{
+ struct action *act = (action*) state->context;
+ int kw, n;
+
+ if (nargs == 0) {
+ return;
+ }
+
+ kw = lookup_keyword(args[0]);
+ if (!kw_is(kw, COMMAND)) {
+ parse_error(state, "invalid command '%s'\n", args[0]);
+ return;
+ }
+
+ n = kw_nargs(kw);
+ if (nargs < n) {
+ parse_error(state, "%s requires %d %s\n", args[0], n - 1,
+ n > 2 ? "arguments" : "argument");
+ return;
+ }
+ command* cmd = (command*) malloc(sizeof(*cmd) + sizeof(char*) * nargs);
+ cmd->func = kw_func(kw);
+ cmd->line = state->line;
+ cmd->filename = state->filename;
+ cmd->nargs = nargs;
+ memcpy(cmd->args, args, sizeof(char*) * nargs);
+ list_add_tail(&act->commands, &cmd->clist);
+}
diff --git a/init/init_parser.h b/init/init_parser.h
new file mode 100644
index 0000000..1ebb1ef
--- /dev/null
+++ b/init/init_parser.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_INIT_PARSER_H_
+#define _INIT_INIT_PARSER_H_
+
+#include <string>
+
+#define INIT_PARSER_MAXARGS 64
+
+struct action;
+struct service;
+
+struct action *action_remove_queue_head(void);
+void action_add_queue_tail(struct action *act);
+void action_for_each_trigger(const char *trigger,
+ void (*func)(struct action *act));
+int action_queue_empty(void);
+void queue_property_triggers(const char *name, const char *value);
+void queue_all_property_triggers();
+void queue_builtin_action(int (*func)(int nargs, char **args), const char *name);
+
+bool init_parse_config(const char* path);
+int expand_props(const char *src, std::string *dst);
+
+service* make_exec_oneshot_service(int argc, char** argv);
+
+#endif
diff --git a/init/init_parser_test.cpp b/init/init_parser_test.cpp
new file mode 100644
index 0000000..170a73a
--- /dev/null
+++ b/init/init_parser_test.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "init_parser.h"
+
+#include "init.h"
+#include "util.h"
+
+#include <errno.h>
+#include <gtest/gtest.h>
+
+TEST(init_parser, make_exec_oneshot_service_invalid_syntax) {
+ char* argv[10];
+ memset(argv, 0, sizeof(argv));
+
+ // Nothing.
+ ASSERT_EQ(nullptr, make_exec_oneshot_service(0, argv));
+
+ // No arguments to 'exec'.
+ argv[0] = const_cast<char*>("exec");
+ ASSERT_EQ(nullptr, make_exec_oneshot_service(1, argv));
+
+ // No command in "exec --".
+ argv[1] = const_cast<char*>("--");
+ ASSERT_EQ(nullptr, make_exec_oneshot_service(2, argv));
+}
+
+TEST(init_parser, make_exec_oneshot_service_too_many_supplementary_gids) {
+ int argc = 0;
+ char* argv[4 + NR_SVC_SUPP_GIDS + 3];
+ argv[argc++] = const_cast<char*>("exec");
+ argv[argc++] = const_cast<char*>("seclabel");
+ argv[argc++] = const_cast<char*>("root"); // uid.
+ argv[argc++] = const_cast<char*>("root"); // gid.
+ for (int i = 0; i < NR_SVC_SUPP_GIDS; ++i) {
+ argv[argc++] = const_cast<char*>("root"); // Supplementary gid.
+ }
+ argv[argc++] = const_cast<char*>("--");
+ argv[argc++] = const_cast<char*>("/system/bin/id");
+ argv[argc] = nullptr;
+ ASSERT_EQ(nullptr, make_exec_oneshot_service(argc, argv));
+}
+
+static void Test_make_exec_oneshot_service(bool dash_dash, bool seclabel, bool uid, bool gid, bool supplementary_gids) {
+ int argc = 0;
+ char* argv[10];
+ argv[argc++] = const_cast<char*>("exec");
+ if (seclabel) {
+ argv[argc++] = const_cast<char*>("u:r:su:s0"); // seclabel
+ if (uid) {
+ argv[argc++] = const_cast<char*>("log"); // uid
+ if (gid) {
+ argv[argc++] = const_cast<char*>("shell"); // gid
+ if (supplementary_gids) {
+ argv[argc++] = const_cast<char*>("system"); // supplementary gid 0
+ argv[argc++] = const_cast<char*>("adb"); // supplementary gid 1
+ }
+ }
+ }
+ }
+ if (dash_dash) {
+ argv[argc++] = const_cast<char*>("--");
+ }
+ argv[argc++] = const_cast<char*>("/system/bin/toybox");
+ argv[argc++] = const_cast<char*>("id");
+ argv[argc] = nullptr;
+ service* svc = make_exec_oneshot_service(argc, argv);
+ ASSERT_NE(nullptr, svc);
+
+ if (seclabel) {
+ ASSERT_STREQ("u:r:su:s0", svc->seclabel);
+ } else {
+ ASSERT_EQ(nullptr, svc->seclabel);
+ }
+ if (uid) {
+ ASSERT_EQ(decode_uid("log"), svc->uid);
+ } else {
+ ASSERT_EQ(0U, svc->uid);
+ }
+ if (gid) {
+ ASSERT_EQ(decode_uid("shell"), svc->gid);
+ } else {
+ ASSERT_EQ(0U, svc->gid);
+ }
+ if (supplementary_gids) {
+ ASSERT_EQ(2U, svc->nr_supp_gids);
+ ASSERT_EQ(decode_uid("system"), svc->supp_gids[0]);
+ ASSERT_EQ(decode_uid("adb"), svc->supp_gids[1]);
+ } else {
+ ASSERT_EQ(0U, svc->nr_supp_gids);
+ }
+
+ ASSERT_EQ(2, svc->nargs);
+ ASSERT_EQ("/system/bin/toybox", svc->args[0]);
+ ASSERT_EQ("id", svc->args[1]);
+ ASSERT_EQ(nullptr, svc->args[2]);
+}
+
+TEST(init_parser, make_exec_oneshot_service_with_everything) {
+ Test_make_exec_oneshot_service(true, true, true, true, true);
+}
+
+TEST(init_parser, make_exec_oneshot_service_with_seclabel_uid_gid) {
+ Test_make_exec_oneshot_service(true, true, true, true, false);
+}
+
+TEST(init_parser, make_exec_oneshot_service_with_seclabel_uid) {
+ Test_make_exec_oneshot_service(true, true, true, false, false);
+}
+
+TEST(init_parser, make_exec_oneshot_service_with_seclabel) {
+ Test_make_exec_oneshot_service(true, true, false, false, false);
+}
+
+TEST(init_parser, make_exec_oneshot_service_with_just_command) {
+ Test_make_exec_oneshot_service(true, false, false, false, false);
+}
+
+TEST(init_parser, make_exec_oneshot_service_with_just_command_no_dash) {
+ Test_make_exec_oneshot_service(false, false, false, false, false);
+}
diff --git a/init/keychords.cpp b/init/keychords.cpp
new file mode 100644
index 0000000..c4ebdf9
--- /dev/null
+++ b/init/keychords.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <linux/keychord.h>
+#include <unistd.h>
+
+#include "init.h"
+#include "log.h"
+#include "property_service.h"
+
+static struct input_keychord *keychords = 0;
+static int keychords_count = 0;
+static int keychords_length = 0;
+static int keychord_fd = -1;
+
+void add_service_keycodes(struct service *svc)
+{
+ struct input_keychord *keychord;
+ int i, size;
+
+ if (svc->keycodes) {
+ /* add a new keychord to the list */
+ size = sizeof(*keychord) + svc->nkeycodes * sizeof(keychord->keycodes[0]);
+ keychords = (input_keychord*) realloc(keychords, keychords_length + size);
+ if (!keychords) {
+ ERROR("could not allocate keychords\n");
+ keychords_length = 0;
+ keychords_count = 0;
+ return;
+ }
+
+ keychord = (struct input_keychord *)((char *)keychords + keychords_length);
+ keychord->version = KEYCHORD_VERSION;
+ keychord->id = keychords_count + 1;
+ keychord->count = svc->nkeycodes;
+ svc->keychord_id = keychord->id;
+
+ for (i = 0; i < svc->nkeycodes; i++) {
+ keychord->keycodes[i] = svc->keycodes[i];
+ }
+ keychords_count++;
+ keychords_length += size;
+ }
+}
+
+static void handle_keychord() {
+ struct service *svc;
+ int ret;
+ __u16 id;
+
+ ret = read(keychord_fd, &id, sizeof(id));
+ if (ret != sizeof(id)) {
+ ERROR("could not read keychord id\n");
+ return;
+ }
+
+ // Only handle keychords if adb is enabled.
+ std::string adb_enabled = property_get("init.svc.adbd");
+ if (adb_enabled == "running") {
+ svc = service_find_by_keychord(id);
+ if (svc) {
+ INFO("Starting service %s from keychord\n", svc->name);
+ service_start(svc, NULL);
+ } else {
+ ERROR("service for keychord %d not found\n", id);
+ }
+ }
+}
+
+void keychord_init() {
+ service_for_each(add_service_keycodes);
+
+ // Nothing to do if no services require keychords.
+ if (!keychords) {
+ return;
+ }
+
+ keychord_fd = TEMP_FAILURE_RETRY(open("/dev/keychord", O_RDWR | O_CLOEXEC));
+ if (keychord_fd == -1) {
+ ERROR("could not open /dev/keychord: %s\n", strerror(errno));
+ return;
+ }
+
+ int ret = write(keychord_fd, keychords, keychords_length);
+ if (ret != keychords_length) {
+ ERROR("could not configure /dev/keychord %d: %s\n", ret, strerror(errno));
+ close(keychord_fd);
+ }
+
+ free(keychords);
+ keychords = nullptr;
+
+ register_epoll_handler(keychord_fd, handle_keychord);
+}
diff --git a/init/keychords.h b/init/keychords.h
new file mode 100644
index 0000000..d2723b7
--- /dev/null
+++ b/init/keychords.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_KEYCHORDS_H_
+#define _INIT_KEYCHORDS_H_
+
+struct service;
+
+void add_service_keycodes(service*);
+void keychord_init();
+
+#endif
diff --git a/init/keywords.h b/init/keywords.h
new file mode 100644
index 0000000..e637d7d
--- /dev/null
+++ b/init/keywords.h
@@ -0,0 +1,107 @@
+#ifndef KEYWORD
+int do_bootchart_init(int nargs, char **args);
+int do_class_start(int nargs, char **args);
+int do_class_stop(int nargs, char **args);
+int do_class_reset(int nargs, char **args);
+int do_domainname(int nargs, char **args);
+int do_enable(int nargs, char **args);
+int do_exec(int nargs, char **args);
+int do_export(int nargs, char **args);
+int do_hostname(int nargs, char **args);
+int do_ifup(int nargs, char **args);
+int do_insmod(int nargs, char **args);
+int do_installkey(int nargs, char **args);
+int do_mkdir(int nargs, char **args);
+int do_mount_all(int nargs, char **args);
+int do_mount(int nargs, char **args);
+int do_powerctl(int nargs, char **args);
+int do_restart(int nargs, char **args);
+int do_restorecon(int nargs, char **args);
+int do_restorecon_recursive(int nargs, char **args);
+int do_rm(int nargs, char **args);
+int do_rmdir(int nargs, char **args);
+int do_setprop(int nargs, char **args);
+int do_setrlimit(int nargs, char **args);
+int do_start(int nargs, char **args);
+int do_stop(int nargs, char **args);
+int do_swapon_all(int nargs, char **args);
+int do_trigger(int nargs, char **args);
+int do_symlink(int nargs, char **args);
+int do_sysclktz(int nargs, char **args);
+int do_write(int nargs, char **args);
+int do_copy(int nargs, char **args);
+int do_chown(int nargs, char **args);
+int do_chmod(int nargs, char **args);
+int do_loglevel(int nargs, char **args);
+int do_load_persist_props(int nargs, char **args);
+int do_load_all_props(int nargs, char **args);
+int do_verity_load_state(int nargs, char **args);
+int do_verity_update_state(int nargs, char **args);
+int do_wait(int nargs, char **args);
+#define __MAKE_KEYWORD_ENUM__
+#define KEYWORD(symbol, flags, nargs, func) K_##symbol,
+enum {
+ K_UNKNOWN,
+#endif
+ KEYWORD(bootchart_init, COMMAND, 0, do_bootchart_init)
+ KEYWORD(chmod, COMMAND, 2, do_chmod)
+ KEYWORD(chown, COMMAND, 2, do_chown)
+ KEYWORD(class, OPTION, 0, 0)
+ KEYWORD(class_reset, COMMAND, 1, do_class_reset)
+ KEYWORD(class_start, COMMAND, 1, do_class_start)
+ KEYWORD(class_stop, COMMAND, 1, do_class_stop)
+ KEYWORD(console, OPTION, 0, 0)
+ KEYWORD(copy, COMMAND, 2, do_copy)
+ KEYWORD(critical, OPTION, 0, 0)
+ KEYWORD(disabled, OPTION, 0, 0)
+ KEYWORD(domainname, COMMAND, 1, do_domainname)
+ KEYWORD(enable, COMMAND, 1, do_enable)
+ KEYWORD(exec, COMMAND, 1, do_exec)
+ KEYWORD(export, COMMAND, 2, do_export)
+ KEYWORD(group, OPTION, 0, 0)
+ KEYWORD(hostname, COMMAND, 1, do_hostname)
+ KEYWORD(ifup, COMMAND, 1, do_ifup)
+ KEYWORD(import, SECTION, 1, 0)
+ KEYWORD(insmod, COMMAND, 1, do_insmod)
+ KEYWORD(installkey, COMMAND, 1, do_installkey)
+ KEYWORD(ioprio, OPTION, 0, 0)
+ KEYWORD(keycodes, OPTION, 0, 0)
+ KEYWORD(load_all_props, COMMAND, 0, do_load_all_props)
+ KEYWORD(load_persist_props, COMMAND, 0, do_load_persist_props)
+ KEYWORD(loglevel, COMMAND, 1, do_loglevel)
+ KEYWORD(mkdir, COMMAND, 1, do_mkdir)
+ KEYWORD(mount_all, COMMAND, 1, do_mount_all)
+ KEYWORD(mount, COMMAND, 3, do_mount)
+ KEYWORD(oneshot, OPTION, 0, 0)
+ KEYWORD(onrestart, OPTION, 0, 0)
+ KEYWORD(on, SECTION, 0, 0)
+ KEYWORD(powerctl, COMMAND, 1, do_powerctl)
+ KEYWORD(restart, COMMAND, 1, do_restart)
+ KEYWORD(restorecon, COMMAND, 1, do_restorecon)
+ KEYWORD(restorecon_recursive, COMMAND, 1, do_restorecon_recursive)
+ KEYWORD(rm, COMMAND, 1, do_rm)
+ KEYWORD(rmdir, COMMAND, 1, do_rmdir)
+ KEYWORD(seclabel, OPTION, 0, 0)
+ KEYWORD(service, SECTION, 0, 0)
+ KEYWORD(setenv, OPTION, 2, 0)
+ KEYWORD(setprop, COMMAND, 2, do_setprop)
+ KEYWORD(setrlimit, COMMAND, 3, do_setrlimit)
+ KEYWORD(socket, OPTION, 0, 0)
+ KEYWORD(start, COMMAND, 1, do_start)
+ KEYWORD(stop, COMMAND, 1, do_stop)
+ KEYWORD(swapon_all, COMMAND, 1, do_swapon_all)
+ KEYWORD(symlink, COMMAND, 1, do_symlink)
+ KEYWORD(sysclktz, COMMAND, 1, do_sysclktz)
+ KEYWORD(trigger, COMMAND, 1, do_trigger)
+ KEYWORD(user, OPTION, 0, 0)
+ KEYWORD(verity_load_state, COMMAND, 0, do_verity_load_state)
+ KEYWORD(verity_update_state, COMMAND, 0, do_verity_update_state)
+ KEYWORD(wait, COMMAND, 1, do_wait)
+ KEYWORD(write, COMMAND, 2, do_write)
+ KEYWORD(writepid, OPTION, 0, 0)
+#ifdef __MAKE_KEYWORD_ENUM__
+ KEYWORD_COUNT,
+};
+#undef __MAKE_KEYWORD_ENUM__
+#undef KEYWORD
+#endif
diff --git a/init/log.cpp b/init/log.cpp
new file mode 100644
index 0000000..eb5ec42
--- /dev/null
+++ b/init/log.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "log.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/uio.h>
+
+#include <selinux/selinux.h>
+
+#include <base/stringprintf.h>
+
+static void init_klog_vwrite(int level, const char* fmt, va_list ap) {
+ static const char* tag = basename(getprogname());
+
+ // The kernel's printk buffer is only 1024 bytes.
+ // TODO: should we automatically break up long lines into multiple lines?
+ // Or we could log but with something like "..." at the end?
+ char buf[1024];
+ size_t prefix_size = snprintf(buf, sizeof(buf), "<%d>%s: ", level, tag);
+ size_t msg_size = vsnprintf(buf + prefix_size, sizeof(buf) - prefix_size, fmt, ap);
+ if (msg_size >= sizeof(buf) - prefix_size) {
+ msg_size = snprintf(buf + prefix_size, sizeof(buf) - prefix_size,
+ "(%zu-byte message too long for printk)\n", msg_size);
+ }
+
+ iovec iov[1];
+ iov[0].iov_base = buf;
+ iov[0].iov_len = prefix_size + msg_size;
+
+ klog_writev(level, iov, 1);
+}
+
+void init_klog_write(int level, const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ init_klog_vwrite(level, fmt, ap);
+ va_end(ap);
+}
+
+int selinux_klog_callback(int type, const char *fmt, ...) {
+ int level = KLOG_ERROR_LEVEL;
+ if (type == SELINUX_WARNING) {
+ level = KLOG_WARNING_LEVEL;
+ } else if (type == SELINUX_INFO) {
+ level = KLOG_INFO_LEVEL;
+ }
+ va_list ap;
+ va_start(ap, fmt);
+ init_klog_vwrite(level, fmt, ap);
+ va_end(ap);
+ return 0;
+}
diff --git a/init/log.h b/init/log.h
new file mode 100644
index 0000000..c5c30af
--- /dev/null
+++ b/init/log.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_LOG_H_
+#define _INIT_LOG_H_
+
+#include <cutils/klog.h>
+
+#define ERROR(x...) init_klog_write(KLOG_ERROR_LEVEL, x)
+#define WARNING(x...) init_klog_write(KLOG_WARNING_LEVEL, x)
+#define NOTICE(x...) init_klog_write(KLOG_NOTICE_LEVEL, x)
+#define INFO(x...) init_klog_write(KLOG_INFO_LEVEL, x)
+#define DEBUG(x...) init_klog_write(KLOG_DEBUG_LEVEL, x)
+#define VERBOSE(x...) init_klog_write(KLOG_DEBUG_LEVEL, x)
+
+void init_klog_write(int level, const char* fmt, ...) __printflike(2, 3);
+int selinux_klog_callback(int level, const char* fmt, ...) __printflike(2, 3);
+
+#endif
diff --git a/init/parser.cpp b/init/parser.cpp
new file mode 100644
index 0000000..8193729
--- /dev/null
+++ b/init/parser.cpp
@@ -0,0 +1,141 @@
+#include "parser.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "log.h"
+
+void parse_error(struct parse_state *state, const char *fmt, ...)
+{
+ va_list ap;
+ char buf[128];
+ int off;
+
+ snprintf(buf, 128, "%s: %d: ", state->filename, state->line);
+ buf[127] = 0;
+ off = strlen(buf);
+
+ va_start(ap, fmt);
+ vsnprintf(buf + off, 128 - off, fmt, ap);
+ va_end(ap);
+ buf[127] = 0;
+ ERROR("%s", buf);
+}
+
+int next_token(struct parse_state *state)
+{
+ char *x = state->ptr;
+ char *s;
+
+ if (state->nexttoken) {
+ int t = state->nexttoken;
+ state->nexttoken = 0;
+ return t;
+ }
+
+ for (;;) {
+ switch (*x) {
+ case 0:
+ state->ptr = x;
+ return T_EOF;
+ case '\n':
+ x++;
+ state->ptr = x;
+ return T_NEWLINE;
+ case ' ':
+ case '\t':
+ case '\r':
+ x++;
+ continue;
+ case '#':
+ while (*x && (*x != '\n')) x++;
+ if (*x == '\n') {
+ state->ptr = x+1;
+ return T_NEWLINE;
+ } else {
+ state->ptr = x;
+ return T_EOF;
+ }
+ default:
+ goto text;
+ }
+ }
+
+textdone:
+ state->ptr = x;
+ *s = 0;
+ return T_TEXT;
+text:
+ state->text = s = x;
+textresume:
+ for (;;) {
+ switch (*x) {
+ case 0:
+ goto textdone;
+ case ' ':
+ case '\t':
+ case '\r':
+ x++;
+ goto textdone;
+ case '\n':
+ state->nexttoken = T_NEWLINE;
+ x++;
+ goto textdone;
+ case '"':
+ x++;
+ for (;;) {
+ switch (*x) {
+ case 0:
+ /* unterminated quoted thing */
+ state->ptr = x;
+ return T_EOF;
+ case '"':
+ x++;
+ goto textresume;
+ default:
+ *s++ = *x++;
+ }
+ }
+ break;
+ case '\\':
+ x++;
+ switch (*x) {
+ case 0:
+ goto textdone;
+ case 'n':
+ *s++ = '\n';
+ break;
+ case 'r':
+ *s++ = '\r';
+ break;
+ case 't':
+ *s++ = '\t';
+ break;
+ case '\\':
+ *s++ = '\\';
+ break;
+ case '\r':
+ /* \ <cr> <lf> -> line continuation */
+ if (x[1] != '\n') {
+ x++;
+ continue;
+ }
+ case '\n':
+ /* \ <lf> -> line continuation */
+ state->line++;
+ x++;
+ /* eat any extra whitespace */
+ while((*x == ' ') || (*x == '\t')) x++;
+ continue;
+ default:
+ /* unknown escape -- just copy */
+ *s++ = *x++;
+ }
+ continue;
+ default:
+ *s++ = *x++;
+ }
+ }
+ return T_EOF;
+}
diff --git a/init/parser.h b/init/parser.h
new file mode 100644
index 0000000..95e1164
--- /dev/null
+++ b/init/parser.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef PARSER_H_
+#define PARSER_H_
+
+#define T_EOF 0
+#define T_TEXT 1
+#define T_NEWLINE 2
+
+struct parse_state
+{
+ char *ptr;
+ char *text;
+ int line;
+ int nexttoken;
+ void *context;
+ void (*parse_line)(struct parse_state *state, int nargs, char **args);
+ const char *filename;
+ void *priv;
+};
+
+void dump_parser_state(void);
+int next_token(struct parse_state *state);
+void parse_error(struct parse_state *state, const char *fmt, ...);
+
+#endif /* PARSER_H_ */
diff --git a/init/property_service.cpp b/init/property_service.cpp
new file mode 100644
index 0000000..aa939a5
--- /dev/null
+++ b/init/property_service.cpp
@@ -0,0 +1,575 @@
+/*
+ * Copyright (C) 2007 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 <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <dirent.h>
+#include <limits.h>
+#include <errno.h>
+#include <sys/poll.h>
+
+#include <memory>
+
+#include <cutils/misc.h>
+#include <cutils/sockets.h>
+#include <cutils/multiuser.h>
+
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/mman.h>
+#include <private/android_filesystem_config.h>
+
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+
+#include <fs_mgr.h>
+#include <base/file.h>
+#include "bootimg.h"
+
+#include "property_service.h"
+#include "init.h"
+#include "util.h"
+#include "log.h"
+
+#define PERSISTENT_PROPERTY_DIR "/data/property"
+#define FSTAB_PREFIX "/fstab."
+#define RECOVERY_MOUNT_POINT "/recovery"
+
+static int persistent_properties_loaded = 0;
+static bool property_area_initialized = false;
+
+static int property_set_fd = -1;
+
+struct workspace {
+ size_t size;
+ int fd;
+};
+
+static workspace pa_workspace;
+
+void property_init() {
+ if (property_area_initialized) {
+ return;
+ }
+
+ property_area_initialized = true;
+
+ if (__system_property_area_init()) {
+ return;
+ }
+
+ pa_workspace.size = 0;
+ pa_workspace.fd = open(PROP_FILENAME, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
+ if (pa_workspace.fd == -1) {
+ ERROR("Failed to open %s: %s\n", PROP_FILENAME, strerror(errno));
+ return;
+ }
+}
+
+static int check_mac_perms(const char *name, char *sctx)
+{
+ char *tctx = NULL;
+ int result = 0;
+
+ if (!sctx)
+ goto err;
+
+ if (!sehandle_prop)
+ goto err;
+
+ if (selabel_lookup(sehandle_prop, &tctx, name, 1) != 0)
+ goto err;
+
+ if (selinux_check_access(sctx, tctx, "property_service", "set", (void*) name) == 0)
+ result = 1;
+
+ freecon(tctx);
+ err:
+ return result;
+}
+
+static int check_control_mac_perms(const char *name, char *sctx)
+{
+ /*
+ * Create a name prefix out of ctl.<service name>
+ * The new prefix allows the use of the existing
+ * property service backend labeling while avoiding
+ * mislabels based on true property prefixes.
+ */
+ char ctl_name[PROP_VALUE_MAX+4];
+ int ret = snprintf(ctl_name, sizeof(ctl_name), "ctl.%s", name);
+
+ if (ret < 0 || (size_t) ret >= sizeof(ctl_name))
+ return 0;
+
+ return check_mac_perms(ctl_name, sctx);
+}
+
+/*
+ * Checks permissions for setting system properties.
+ * Returns 1 if uid allowed, 0 otherwise.
+ */
+static int check_perms(const char *name, char *sctx)
+{
+ if(!strncmp(name, "ro.", 3))
+ name +=3;
+
+ return check_mac_perms(name, sctx);
+}
+
+std::string property_get(const char* name) {
+ char value[PROP_VALUE_MAX] = {0};
+ __system_property_get(name, value);
+ return value;
+}
+
+static void write_persistent_property(const char *name, const char *value)
+{
+ char tempPath[PATH_MAX];
+ char path[PATH_MAX];
+ int fd;
+
+ snprintf(tempPath, sizeof(tempPath), "%s/.temp.XXXXXX", PERSISTENT_PROPERTY_DIR);
+ fd = mkstemp(tempPath);
+ if (fd < 0) {
+ ERROR("Unable to write persistent property to temp file %s: %s\n", tempPath, strerror(errno));
+ return;
+ }
+ write(fd, value, strlen(value));
+ fsync(fd);
+ close(fd);
+
+ snprintf(path, sizeof(path), "%s/%s", PERSISTENT_PROPERTY_DIR, name);
+ if (rename(tempPath, path)) {
+ unlink(tempPath);
+ ERROR("Unable to rename persistent property file %s to %s\n", tempPath, path);
+ }
+}
+
+static bool is_legal_property_name(const char* name, size_t namelen)
+{
+ size_t i;
+ if (namelen >= PROP_NAME_MAX) return false;
+ if (namelen < 1) return false;
+ if (name[0] == '.') return false;
+ if (name[namelen - 1] == '.') return false;
+
+ /* Only allow alphanumeric, plus '.', '-', or '_' */
+ /* Don't allow ".." to appear in a property name */
+ for (i = 0; i < namelen; i++) {
+ if (name[i] == '.') {
+ // i=0 is guaranteed to never have a dot. See above.
+ if (name[i-1] == '.') return false;
+ continue;
+ }
+ if (name[i] == '_' || name[i] == '-') continue;
+ if (name[i] >= 'a' && name[i] <= 'z') continue;
+ if (name[i] >= 'A' && name[i] <= 'Z') continue;
+ if (name[i] >= '0' && name[i] <= '9') continue;
+ return false;
+ }
+
+ return true;
+}
+
+static int property_set_impl(const char* name, const char* value) {
+ size_t namelen = strlen(name);
+ size_t valuelen = strlen(value);
+
+ if (!is_legal_property_name(name, namelen)) return -1;
+ if (valuelen >= PROP_VALUE_MAX) return -1;
+
+ prop_info* pi = (prop_info*) __system_property_find(name);
+
+ if(pi != 0) {
+ /* ro.* properties may NEVER be modified once set */
+ if(!strncmp(name, "ro.", 3)) return -1;
+
+ __system_property_update(pi, value, valuelen);
+ } else {
+ int rc = __system_property_add(name, namelen, value, valuelen);
+ if (rc < 0) {
+ return rc;
+ }
+ }
+ /* If name starts with "net." treat as a DNS property. */
+ if (strncmp("net.", name, strlen("net.")) == 0) {
+ if (strcmp("net.change", name) == 0) {
+ return 0;
+ }
+ /*
+ * The 'net.change' property is a special property used track when any
+ * 'net.*' property name is updated. It is _ONLY_ updated here. Its value
+ * contains the last updated 'net.*' property.
+ */
+ property_set("net.change", name);
+ } else if (persistent_properties_loaded &&
+ strncmp("persist.", name, strlen("persist.")) == 0) {
+ /*
+ * Don't write properties to disk until after we have read all default properties
+ * to prevent them from being overwritten by default values.
+ */
+ write_persistent_property(name, value);
+ } else if (strcmp("selinux.reload_policy", name) == 0 &&
+ strcmp("1", value) == 0) {
+ selinux_reload_policy();
+ }
+ property_changed(name, value);
+ return 0;
+}
+
+int property_set(const char* name, const char* value) {
+ int rc = property_set_impl(name, value);
+ if (rc == -1) {
+ ERROR("property_set(\"%s\", \"%s\") failed\n", name, value);
+ }
+ return rc;
+}
+
+static void handle_property_set_fd()
+{
+ prop_msg msg;
+ int s;
+ int r;
+ struct ucred cr;
+ struct sockaddr_un addr;
+ socklen_t addr_size = sizeof(addr);
+ socklen_t cr_size = sizeof(cr);
+ char * source_ctx = NULL;
+ struct pollfd ufds[1];
+ const int timeout_ms = 2 * 1000; /* Default 2 sec timeout for caller to send property. */
+ int nr;
+
+ if ((s = accept(property_set_fd, (struct sockaddr *) &addr, &addr_size)) < 0) {
+ return;
+ }
+
+ /* Check socket options here */
+ if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) {
+ close(s);
+ ERROR("Unable to receive socket options\n");
+ return;
+ }
+
+ ufds[0].fd = s;
+ ufds[0].events = POLLIN;
+ ufds[0].revents = 0;
+ nr = TEMP_FAILURE_RETRY(poll(ufds, 1, timeout_ms));
+ if (nr == 0) {
+ ERROR("sys_prop: timeout waiting for uid=%d to send property message.\n", cr.uid);
+ close(s);
+ return;
+ } else if (nr < 0) {
+ ERROR("sys_prop: error waiting for uid=%d to send property message: %s\n", cr.uid, strerror(errno));
+ close(s);
+ return;
+ }
+
+ r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), MSG_DONTWAIT));
+ if(r != sizeof(prop_msg)) {
+ ERROR("sys_prop: mis-match msg size received: %d expected: %zu: %s\n",
+ r, sizeof(prop_msg), strerror(errno));
+ close(s);
+ return;
+ }
+
+ switch(msg.cmd) {
+ case PROP_MSG_SETPROP:
+ msg.name[PROP_NAME_MAX-1] = 0;
+ msg.value[PROP_VALUE_MAX-1] = 0;
+
+ if (!is_legal_property_name(msg.name, strlen(msg.name))) {
+ ERROR("sys_prop: illegal property name. Got: \"%s\"\n", msg.name);
+ close(s);
+ return;
+ }
+
+ getpeercon(s, &source_ctx);
+
+ if(memcmp(msg.name,"ctl.",4) == 0) {
+ // Keep the old close-socket-early behavior when handling
+ // ctl.* properties.
+ close(s);
+ if (check_control_mac_perms(msg.value, source_ctx)) {
+ handle_control_message((char*) msg.name + 4, (char*) msg.value);
+ } else {
+ ERROR("sys_prop: Unable to %s service ctl [%s] uid:%d gid:%d pid:%d\n",
+ msg.name + 4, msg.value, cr.uid, cr.gid, cr.pid);
+ }
+ } else {
+ if (check_perms(msg.name, source_ctx)) {
+ property_set((char*) msg.name, (char*) msg.value);
+ } else {
+ ERROR("sys_prop: permission denied uid:%d name:%s\n",
+ cr.uid, msg.name);
+ }
+
+ // Note: bionic's property client code assumes that the
+ // property server will not close the socket until *AFTER*
+ // the property is written to memory.
+ close(s);
+ }
+ freecon(source_ctx);
+ break;
+
+ default:
+ close(s);
+ break;
+ }
+}
+
+void get_property_workspace(int *fd, int *sz)
+{
+ *fd = pa_workspace.fd;
+ *sz = pa_workspace.size;
+}
+
+static void load_properties_from_file(const char *, const char *);
+
+/*
+ * Filter is used to decide which properties to load: NULL loads all keys,
+ * "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match.
+ */
+static void load_properties(char *data, const char *filter)
+{
+ char *key, *value, *eol, *sol, *tmp, *fn;
+ size_t flen = 0;
+
+ if (filter) {
+ flen = strlen(filter);
+ }
+
+ sol = data;
+ while ((eol = strchr(sol, '\n'))) {
+ key = sol;
+ *eol++ = 0;
+ sol = eol;
+
+ while (isspace(*key)) key++;
+ if (*key == '#') continue;
+
+ tmp = eol - 2;
+ while ((tmp > key) && isspace(*tmp)) *tmp-- = 0;
+
+ if (!strncmp(key, "import ", 7) && flen == 0) {
+ fn = key + 7;
+ while (isspace(*fn)) fn++;
+
+ key = strchr(fn, ' ');
+ if (key) {
+ *key++ = 0;
+ while (isspace(*key)) key++;
+ }
+
+ load_properties_from_file(fn, key);
+
+ } else {
+ value = strchr(key, '=');
+ if (!value) continue;
+ *value++ = 0;
+
+ tmp = value - 2;
+ while ((tmp > key) && isspace(*tmp)) *tmp-- = 0;
+
+ while (isspace(*value)) value++;
+
+ if (flen > 0) {
+ if (filter[flen - 1] == '*') {
+ if (strncmp(key, filter, flen - 1)) continue;
+ } else {
+ if (strcmp(key, filter)) continue;
+ }
+ }
+
+ property_set(key, value);
+ }
+ }
+}
+
+/*
+ * Filter is used to decide which properties to load: NULL loads all keys,
+ * "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match.
+ */
+static void load_properties_from_file(const char* filename, const char* filter) {
+ Timer t;
+ std::string data;
+ if (read_file(filename, &data)) {
+ data.push_back('\n');
+ load_properties(&data[0], filter);
+ }
+ NOTICE("(Loading properties from %s took %.2fs.)\n", filename, t.duration());
+}
+
+static void load_persistent_properties() {
+ persistent_properties_loaded = 1;
+
+ std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir(PERSISTENT_PROPERTY_DIR), closedir);
+ if (!dir) {
+ ERROR("Unable to open persistent property directory \"%s\": %s\n",
+ PERSISTENT_PROPERTY_DIR, strerror(errno));
+ return;
+ }
+
+ struct dirent* entry;
+ while ((entry = readdir(dir.get())) != NULL) {
+ if (strncmp("persist.", entry->d_name, strlen("persist."))) {
+ continue;
+ }
+ if (entry->d_type != DT_REG) {
+ continue;
+ }
+
+ // Open the file and read the property value.
+ int fd = openat(dirfd(dir.get()), entry->d_name, O_RDONLY | O_NOFOLLOW);
+ if (fd == -1) {
+ ERROR("Unable to open persistent property file \"%s\": %s\n",
+ entry->d_name, strerror(errno));
+ continue;
+ }
+
+ struct stat sb;
+ if (fstat(fd, &sb) == -1) {
+ ERROR("fstat on property file \"%s\" failed: %s\n", entry->d_name, strerror(errno));
+ close(fd);
+ continue;
+ }
+
+ // File must not be accessible to others, be owned by root/root, and
+ // not be a hard link to any other file.
+ if (((sb.st_mode & (S_IRWXG | S_IRWXO)) != 0) || (sb.st_uid != 0) || (sb.st_gid != 0) ||
+ (sb.st_nlink != 1)) {
+ ERROR("skipping insecure property file %s (uid=%u gid=%u nlink=%u mode=%o)\n",
+ entry->d_name, (unsigned int)sb.st_uid, (unsigned int)sb.st_gid,
+ (unsigned int)sb.st_nlink, sb.st_mode);
+ close(fd);
+ continue;
+ }
+
+ char value[PROP_VALUE_MAX];
+ int length = read(fd, value, sizeof(value) - 1);
+ if (length >= 0) {
+ value[length] = 0;
+ property_set(entry->d_name, value);
+ } else {
+ ERROR("Unable to read persistent property file %s: %s\n",
+ entry->d_name, strerror(errno));
+ }
+ close(fd);
+ }
+}
+
+void property_load_boot_defaults() {
+ load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT, NULL);
+}
+
+bool properties_initialized() {
+ return property_area_initialized;
+}
+
+static void load_override_properties() {
+ if (ALLOW_LOCAL_PROP_OVERRIDE) {
+ std::string debuggable = property_get("ro.debuggable");
+ if (debuggable == "1") {
+ load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE, NULL);
+ }
+ }
+}
+
+/* When booting an encrypted system, /data is not mounted when the
+ * property service is started, so any properties stored there are
+ * not loaded. Vold triggers init to load these properties once it
+ * has mounted /data.
+ */
+void load_persist_props(void) {
+ load_override_properties();
+ /* Read persistent properties after all default values have been loaded. */
+ load_persistent_properties();
+}
+
+void load_recovery_id_prop() {
+ std::string ro_hardware = property_get("ro.hardware");
+ if (ro_hardware.empty()) {
+ ERROR("ro.hardware not set - unable to load recovery id\n");
+ return;
+ }
+ std::string fstab_filename = FSTAB_PREFIX + ro_hardware;
+
+ std::unique_ptr<fstab, void(*)(fstab*)> tab(fs_mgr_read_fstab(fstab_filename.c_str()),
+ fs_mgr_free_fstab);
+ if (!tab) {
+ ERROR("unable to read fstab %s: %s\n", fstab_filename.c_str(), strerror(errno));
+ return;
+ }
+
+ fstab_rec* rec = fs_mgr_get_entry_for_mount_point(tab.get(), RECOVERY_MOUNT_POINT);
+ if (rec == NULL) {
+ ERROR("/recovery not specified in fstab\n");
+ return;
+ }
+
+ int fd = open(rec->blk_device, O_RDONLY);
+ if (fd == -1) {
+ ERROR("error opening block device %s: %s\n", rec->blk_device, strerror(errno));
+ return;
+ }
+
+ boot_img_hdr hdr;
+ if (android::base::ReadFully(fd, &hdr, sizeof(hdr))) {
+ std::string hex = bytes_to_hex(reinterpret_cast<uint8_t*>(hdr.id), sizeof(hdr.id));
+ property_set("ro.recovery_id", hex.c_str());
+ } else {
+ ERROR("error reading /recovery: %s\n", strerror(errno));
+ }
+
+ close(fd);
+}
+
+void load_all_props() {
+ load_properties_from_file(PROP_PATH_SYSTEM_BUILD, NULL);
+ load_properties_from_file(PROP_PATH_VENDOR_BUILD, NULL);
+ load_properties_from_file(PROP_PATH_FACTORY, "ro.*");
+
+ load_override_properties();
+
+ /* Read persistent properties after all default values have been loaded. */
+ load_persistent_properties();
+
+ load_recovery_id_prop();
+}
+
+void start_property_service() {
+ property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
+ 0666, 0, 0, NULL);
+ if (property_set_fd == -1) {
+ ERROR("start_property_service socket creation failed: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ listen(property_set_fd, 8);
+
+ register_epoll_handler(property_set_fd, handle_property_set_fd);
+}
diff --git a/init/property_service.h b/init/property_service.h
new file mode 100644
index 0000000..51d7404
--- /dev/null
+++ b/init/property_service.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_PROPERTY_H
+#define _INIT_PROPERTY_H
+
+#include <stddef.h>
+#include <sys/system_properties.h>
+#include <string>
+
+extern void property_init(void);
+extern void property_load_boot_defaults(void);
+extern void load_persist_props(void);
+extern void load_all_props(void);
+extern void start_property_service(void);
+void get_property_workspace(int *fd, int *sz);
+std::string property_get(const char* name);
+extern int property_set(const char *name, const char *value);
+extern bool properties_initialized();
+
+
+#endif /* _INIT_PROPERTY_H */
diff --git a/init/readme.txt b/init/readme.txt
new file mode 100644
index 0000000..5a758d7
--- /dev/null
+++ b/init/readme.txt
@@ -0,0 +1,403 @@
+
+Android Init Language
+---------------------
+
+The Android Init Language consists of four broad classes of statements,
+which are Actions, Commands, Services, and Options.
+
+All of these are line-oriented, consisting of tokens separated by
+whitespace. The c-style backslash escapes may be used to insert
+whitespace into a token. Double quotes may also be used to prevent
+whitespace from breaking text into multiple tokens. The backslash,
+when it is the last character on a line, may be used for line-folding.
+
+Lines which start with a # (leading whitespace allowed) are comments.
+
+Actions and Services implicitly declare a new section. All commands
+or options belong to the section most recently declared. Commands
+or options before the first section are ignored.
+
+Actions and Services have unique names. If a second Action or Service
+is declared with the same name as an existing one, it is ignored as
+an error. (??? should we override instead)
+
+
+Actions
+-------
+Actions are named sequences of commands. Actions have a trigger which
+is used to determine when the action should occur. When an event
+occurs which matches an action's trigger, that action is added to
+the tail of a to-be-executed queue (unless it is already on the
+queue).
+
+Each action in the queue is dequeued in sequence and each command in
+that action is executed in sequence. Init handles other activities
+(device creation/destruction, property setting, process restarting)
+"between" the execution of the commands in activities.
+
+Actions take the form of:
+
+on <trigger>
+ <command>
+ <command>
+ <command>
+
+
+Services
+--------
+Services are programs which init launches and (optionally) restarts
+when they exit. Services take the form of:
+
+service <name> <pathname> [ <argument> ]*
+ <option>
+ <option>
+ ...
+
+
+Options
+-------
+Options are modifiers to services. They affect how and when init
+runs the service.
+
+critical
+ This is a device-critical service. If it exits more than four times in
+ four minutes, the device will reboot into recovery mode.
+
+disabled
+ This service will not automatically start with its class.
+ It must be explicitly started by name.
+
+setenv <name> <value>
+ Set the environment variable <name> to <value> in the launched process.
+
+socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]
+ Create a unix domain socket named /dev/socket/<name> and pass
+ its fd to the launched process. <type> must be "dgram", "stream" or "seqpacket".
+ User and group default to 0.
+ 'seclabel' is the SELinux security context for the socket.
+ It defaults to the service security context, as specified by seclabel or
+ computed based on the service executable file security context.
+
+user <username>
+ Change to username before exec'ing this service.
+ Currently defaults to root. (??? probably should default to nobody)
+ Currently, if your process requires linux capabilities then you cannot use
+ this command. You must instead request the capabilities in-process while
+ still root, and then drop to your desired uid.
+
+group <groupname> [ <groupname> ]*
+ Change to groupname before exec'ing this service. Additional
+ groupnames beyond the (required) first one are used to set the
+ supplemental groups of the process (via setgroups()).
+ Currently defaults to root. (??? probably should default to nobody)
+
+seclabel <seclabel>
+ Change to 'seclabel' before exec'ing this service.
+ Primarily for use by services run from the rootfs, e.g. ueventd, adbd.
+ Services on the system partition can instead use policy-defined transitions
+ based on their file security context.
+ If not specified and no transition is defined in policy, defaults to the init context.
+
+oneshot
+ Do not restart the service when it exits.
+
+class <name>
+ Specify a class name for the service. All services in a
+ named class may be started or stopped together. A service
+ is in the class "default" if one is not specified via the
+ class option.
+
+onrestart
+ Execute a Command (see below) when service restarts.
+
+writepid <file...>
+ Write the child's pid to the given files when it forks. Meant for
+ cgroup/cpuset usage.
+
+
+Triggers
+--------
+Triggers are strings which can be used to match certain kinds
+of events and used to cause an action to occur.
+
+boot
+ This is the first trigger that will occur when init starts
+ (after /init.conf is loaded)
+
+<name>=<value>
+ Triggers of this form occur when the property <name> is set
+ to the specific value <value>.
+
+ One can also test multiple properties to execute a group
+ of commands. For example:
+
+ on property:test.a=1 && property:test.b=1
+ setprop test.c 1
+
+ The above stub sets test.c to 1 only when
+ both test.a=1 and test.b=1
+
+
+Commands
+--------
+
+bootchart_init
+ Start bootcharting if configured (see below).
+ This is included in the default init.rc.
+
+chmod <octal-mode> <path>
+ Change file access permissions.
+
+chown <owner> <group> <path>
+ Change file owner and group.
+
+class_start <serviceclass>
+ Start all services of the specified class if they are
+ not already running.
+
+class_stop <serviceclass>
+ Stop and disable all services of the specified class if they are
+ currently running.
+
+class_reset <serviceclass>
+ Stop all services of the specified class if they are
+ currently running, without disabling them. They can be restarted
+ later using class_start.
+
+copy <src> <dst>
+ Copies a file. Similar to write, but useful for binary/large
+ amounts of data.
+
+domainname <name>
+ Set the domain name.
+
+enable <servicename>
+ Turns a disabled service into an enabled one as if the service did not
+ specify disabled.
+ If the service is supposed to be running, it will be started now.
+ Typically used when the bootloader sets a variable that indicates a specific
+ service should be started when needed. E.g.
+ on property:ro.boot.myfancyhardware=1
+ enable my_fancy_service_for_my_fancy_hardware
+
+exec [ <seclabel> [ <user> [ <group> ]* ] ] -- <command> [ <argument> ]*
+ Fork and execute command with the given arguments. The command starts
+ after "--" so that an optional security context, user, and supplementary
+ groups can be provided. No other commands will be run until this one
+ finishes. <seclabel> can be a - to denote default.
+
+export <name> <value>
+ Set the environment variable <name> equal to <value> in the
+ global environment (which will be inherited by all processes
+ started after this command is executed)
+
+hostname <name>
+ Set the host name.
+
+ifup <interface>
+ Bring the network interface <interface> online.
+
+import <path>
+ Parse an init config file, extending the current configuration.
+ If <path> is a directory, each file in the directory is parsed as
+ a config file. It is not recursive, nested directories will
+ not be parsed.
+
+insmod <path>
+ Install the module at <path>
+
+load_all_props
+ Loads properties from /system, /vendor, et cetera.
+ This is included in the default init.rc.
+
+load_persist_props
+ Loads persistent properties when /data has been decrypted.
+ This is included in the default init.rc.
+
+loglevel <level>
+ Sets the kernel log level to level. Properties are expanded within <level>.
+
+mkdir <path> [mode] [owner] [group]
+ Create a directory at <path>, optionally with the given mode, owner, and
+ group. If not provided, the directory is created with permissions 755 and
+ owned by the root user and root group. If provided, the mode, owner and group
+ will be updated if the directory exists already.
+
+mount_all <fstab>
+ Calls fs_mgr_mount_all on the given fs_mgr-format fstab.
+
+mount <type> <device> <dir> [ <flag> ]* [<options>]
+ Attempt to mount the named device at the directory <dir>
+ <device> may be of the form mtd@name to specify a mtd block
+ device by name.
+ <flag>s include "ro", "rw", "remount", "noatime", ...
+ <options> include "barrier=1", "noauto_da_alloc", "discard", ... as
+ a comma separated string, eg: barrier=1,noauto_da_alloc
+
+powerctl
+ Internal implementation detail used to respond to changes to the
+ "sys.powerctl" system property, used to implement rebooting.
+
+restart <service>
+ Like stop, but doesn't disable the service.
+
+restorecon <path> [ <path> ]*
+ Restore the file named by <path> to the security context specified
+ in the file_contexts configuration.
+ Not required for directories created by the init.rc as these are
+ automatically labeled correctly by init.
+
+restorecon_recursive <path> [ <path> ]*
+ Recursively restore the directory tree named by <path> to the
+ security contexts specified in the file_contexts configuration.
+
+rm <path>
+ Calls unlink(2) on the given path. You might want to
+ use "exec -- rm ..." instead (provided the system partition is
+ already mounted).
+
+rmdir <path>
+ Calls rmdir(2) on the given path.
+
+setprop <name> <value>
+ Set system property <name> to <value>. Properties are expanded
+ within <value>.
+
+setrlimit <resource> <cur> <max>
+ Set the rlimit for a resource.
+
+start <service>
+ Start a service running if it is not already running.
+
+stop <service>
+ Stop a service from running if it is currently running.
+
+swapon_all <fstab>
+ Calls fs_mgr_swapon_all on the given fstab file.
+
+symlink <target> <path>
+ Create a symbolic link at <path> with the value <target>
+
+sysclktz <mins_west_of_gmt>
+ Set the system clock base (0 if system clock ticks in GMT)
+
+trigger <event>
+ Trigger an event. Used to queue an action from another
+ action.
+
+verity_load_state
+ Internal implementation detail used to load dm-verity state.
+
+verity_update_state <mount_point>
+ Internal implementation detail used to update dm-verity state and
+ set the partition.<mount_point>.verified properties used by adb remount
+ because fs_mgr can't set them directly itself.
+
+wait <path> [ <timeout> ]
+ Poll for the existence of the given file and return when found,
+ or the timeout has been reached. If timeout is not specified it
+ currently defaults to five seconds.
+
+write <path> <content>
+ Open the file at <path> and write a string to it with write(2).
+ If the file does not exist, it will be created. If it does exist,
+ it will be truncated. Properties are expanded within <content>.
+
+
+Properties
+----------
+Init updates some system properties to provide some insight into
+what it's doing:
+
+init.action
+ Equal to the name of the action currently being executed or "" if none
+
+init.command
+ Equal to the command being executed or "" if none.
+
+init.svc.<name>
+ State of a named service ("stopped", "running", "restarting")
+
+
+Bootcharting
+------------
+This version of init contains code to perform "bootcharting": generating log
+files that can be later processed by the tools provided by www.bootchart.org.
+
+On the emulator, use the -bootchart <timeout> option to boot with bootcharting
+activated for <timeout> seconds.
+
+On a device, create /data/bootchart/start with a command like the following:
+
+ adb shell 'echo $TIMEOUT > /data/bootchart/start'
+
+Where the value of $TIMEOUT corresponds to the desired bootcharted period in
+seconds. Bootcharting will stop after that many seconds have elapsed.
+You can also stop the bootcharting at any moment by doing the following:
+
+ adb shell 'echo 1 > /data/bootchart/stop'
+
+Note that /data/bootchart/stop is deleted automatically by init at the end of
+the bootcharting. This is not the case with /data/bootchart/start, so don't
+forget to delete it when you're done collecting data.
+
+The log files are written to /data/bootchart/. A script is provided to
+retrieve them and create a bootchart.tgz file that can be used with the
+bootchart command-line utility:
+
+ sudo apt-get install pybootchartgui
+ # grab-bootchart.sh uses $ANDROID_SERIAL.
+ $ANDROID_BUILD_TOP/system/core/init/grab-bootchart.sh
+
+One thing to watch for is that the bootchart will show init as if it started
+running at 0s. You'll have to look at dmesg to work out when the kernel
+actually started init.
+
+
+Comparing two bootcharts
+------------------------
+A handy script named compare-bootcharts.py can be used to compare the
+start/end time of selected processes. The aforementioned grab-bootchart.sh
+will leave a bootchart tarball named bootchart.tgz at /tmp/android-bootchart.
+If two such barballs are preserved on the host machine under different
+directories, the script can list the timestamps differences. For example:
+
+Usage: system/core/init/compare-bootcharts.py base_bootchart_dir
+ exp_bootchart_dir
+
+process: baseline experiment (delta)
+ - Unit is ms (a jiffy is 10 ms on the system)
+------------------------------------
+/init: 50 40 (-10)
+/system/bin/surfaceflinger: 4320 4470 (+150)
+/system/bin/bootanimation: 6980 6990 (+10)
+zygote64: 10410 10640 (+230)
+zygote: 10410 10640 (+230)
+system_server: 15350 15150 (-200)
+bootanimation ends at: 33790 31230 (-2560)
+
+
+Debugging init
+--------------
+By default, programs executed by init will drop stdout and stderr into
+/dev/null. To help with debugging, you can execute your program via the
+Android program logwrapper. This will redirect stdout/stderr into the
+Android logging system (accessed via logcat).
+
+For example
+service akmd /system/bin/logwrapper /sbin/akmd
+
+For quicker turnaround when working on init itself, use:
+
+ mm -j
+ m ramdisk-nodeps
+ m bootimage-nodeps
+ adb reboot bootloader
+ fastboot boot $ANDROID_PRODUCT_OUT/boot.img
+
+Alternatively, use the emulator:
+
+ emulator -partition-size 1024 -verbose -show-kernel -no-window
+
+You might want to call klog_set_level(6) after the klog_init() call
+so you see the kernel logging in dmesg (or the emulator output).
diff --git a/init/signal_handler.cpp b/init/signal_handler.cpp
new file mode 100644
index 0000000..6893163
--- /dev/null
+++ b/init/signal_handler.cpp
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <base/stringprintf.h>
+#include <cutils/android_reboot.h>
+#include <cutils/list.h>
+#include <cutils/sockets.h>
+
+#include "init.h"
+#include "log.h"
+#include "util.h"
+
+#define CRITICAL_CRASH_THRESHOLD 4 /* if we crash >4 times ... */
+#define CRITICAL_CRASH_WINDOW (4*60) /* ... in 4 minutes, goto recovery */
+
+static int signal_write_fd = -1;
+static int signal_read_fd = -1;
+
+static std::string DescribeStatus(int status) {
+ if (WIFEXITED(status)) {
+ return android::base::StringPrintf("exited with status %d", WEXITSTATUS(status));
+ } else if (WIFSIGNALED(status)) {
+ return android::base::StringPrintf("killed by signal %d", WTERMSIG(status));
+ } else if (WIFSTOPPED(status)) {
+ return android::base::StringPrintf("stopped by signal %d", WSTOPSIG(status));
+ } else {
+ return "state changed";
+ }
+}
+
+static bool wait_for_one_process() {
+ int status;
+ pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG));
+ if (pid == 0) {
+ return false;
+ } else if (pid == -1) {
+ ERROR("waitpid failed: %s\n", strerror(errno));
+ return false;
+ }
+
+ service* svc = service_find_by_pid(pid);
+
+ std::string name;
+ if (svc) {
+ name = android::base::StringPrintf("Service '%s' (pid %d)", svc->name, pid);
+ } else {
+ name = android::base::StringPrintf("Untracked pid %d", pid);
+ }
+
+ NOTICE("%s %s\n", name.c_str(), DescribeStatus(status).c_str());
+
+ if (!svc) {
+ return true;
+ }
+
+ // TODO: all the code from here down should be a member function on service.
+
+ if (!(svc->flags & SVC_ONESHOT) || (svc->flags & SVC_RESTART)) {
+ NOTICE("Service '%s' (pid %d) killing any children in process group\n", svc->name, pid);
+ kill(-pid, SIGKILL);
+ }
+
+ // Remove any sockets we may have created.
+ for (socketinfo* si = svc->sockets; si; si = si->next) {
+ char tmp[128];
+ snprintf(tmp, sizeof(tmp), ANDROID_SOCKET_DIR"/%s", si->name);
+ unlink(tmp);
+ }
+
+ if (svc->flags & SVC_EXEC) {
+ INFO("SVC_EXEC pid %d finished...\n", svc->pid);
+ waiting_for_exec = false;
+ list_remove(&svc->slist);
+ free(svc->name);
+ free(svc);
+ return true;
+ }
+
+ svc->pid = 0;
+ svc->flags &= (~SVC_RUNNING);
+
+ // Oneshot processes go into the disabled state on exit,
+ // except when manually restarted.
+ if ((svc->flags & SVC_ONESHOT) && !(svc->flags & SVC_RESTART)) {
+ svc->flags |= SVC_DISABLED;
+ }
+
+ // Disabled and reset processes do not get restarted automatically.
+ if (svc->flags & (SVC_DISABLED | SVC_RESET)) {
+ svc->NotifyStateChange("stopped");
+ return true;
+ }
+
+ time_t now = gettime();
+ if ((svc->flags & SVC_CRITICAL) && !(svc->flags & SVC_RESTART)) {
+ if (svc->time_crashed + CRITICAL_CRASH_WINDOW >= now) {
+ if (++svc->nr_crashed > CRITICAL_CRASH_THRESHOLD) {
+ ERROR("critical process '%s' exited %d times in %d minutes; "
+ "rebooting into recovery mode\n", svc->name,
+ CRITICAL_CRASH_THRESHOLD, CRITICAL_CRASH_WINDOW / 60);
+ android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
+ return true;
+ }
+ } else {
+ svc->time_crashed = now;
+ svc->nr_crashed = 1;
+ }
+ }
+
+ svc->flags &= (~SVC_RESTART);
+ svc->flags |= SVC_RESTARTING;
+
+ // Execute all onrestart commands for this service.
+ struct listnode* node;
+ list_for_each(node, &svc->onrestart.commands) {
+ command* cmd = node_to_item(node, struct command, clist);
+ std::vector<std::string> arg_strs;
+ if (expand_command_arguments(cmd->nargs, cmd->args, &arg_strs)) {
+ std::vector<char*> args;
+ for (auto& s : arg_strs) {
+ args.push_back(&s[0]);
+ }
+ cmd->func(args.size(), &args[0]);
+ }
+ }
+ svc->NotifyStateChange("restarting");
+ return true;
+}
+
+static void reap_any_outstanding_children() {
+ while (wait_for_one_process()) {
+ }
+}
+
+static void handle_signal() {
+ // Clear outstanding requests.
+ char buf[32];
+ read(signal_read_fd, buf, sizeof(buf));
+
+ reap_any_outstanding_children();
+}
+
+static void SIGCHLD_handler(int) {
+ if (TEMP_FAILURE_RETRY(write(signal_write_fd, "1", 1)) == -1) {
+ ERROR("write(signal_write_fd) failed: %s\n", strerror(errno));
+ }
+}
+
+void signal_handler_init() {
+ // Create a signalling mechanism for SIGCHLD.
+ int s[2];
+ if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {
+ ERROR("socketpair failed: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ signal_write_fd = s[0];
+ signal_read_fd = s[1];
+
+ // Write to signal_write_fd if we catch SIGCHLD.
+ struct sigaction act;
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = SIGCHLD_handler;
+ act.sa_flags = SA_NOCLDSTOP;
+ sigaction(SIGCHLD, &act, 0);
+
+ reap_any_outstanding_children();
+
+ register_epoll_handler(signal_read_fd, handle_signal);
+}
diff --git a/init/signal_handler.h b/init/signal_handler.h
new file mode 100644
index 0000000..449b4af
--- /dev/null
+++ b/init/signal_handler.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_SIGNAL_HANDLER_H_
+#define _INIT_SIGNAL_HANDLER_H_
+
+void signal_handler_init(void);
+
+#endif
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
new file mode 100644
index 0000000..75924cb
--- /dev/null
+++ b/init/ueventd.cpp
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <base/stringprintf.h>
+#include <private/android_filesystem_config.h>
+#include <selinux/selinux.h>
+
+#include "ueventd.h"
+#include "log.h"
+#include "util.h"
+#include "devices.h"
+#include "ueventd_parser.h"
+#include "property_service.h"
+
+int ueventd_main(int argc, char **argv)
+{
+ /*
+ * init sets the umask to 077 for forked processes. We need to
+ * create files with exact permissions, without modification by
+ * the umask.
+ */
+ umask(000);
+
+ /* Prevent fire-and-forget children from becoming zombies.
+ * If we should need to wait() for some children in the future
+ * (as opposed to none right now), double-forking here instead
+ * of ignoring SIGCHLD may be the better solution.
+ */
+ signal(SIGCHLD, SIG_IGN);
+
+ open_devnull_stdio();
+ klog_init();
+ klog_set_level(KLOG_NOTICE_LEVEL);
+
+ NOTICE("ueventd started!\n");
+
+ selinux_callback cb;
+ cb.func_log = selinux_klog_callback;
+ selinux_set_callback(SELINUX_CB_LOG, cb);
+
+ std::string hardware = property_get("ro.hardware");
+
+ ueventd_parse_config_file("/ueventd.rc");
+ ueventd_parse_config_file(android::base::StringPrintf("/ueventd.%s.rc", hardware.c_str()).c_str());
+
+ device_init();
+
+ pollfd ufd;
+ ufd.events = POLLIN;
+ ufd.fd = get_device_fd();
+
+ while (true) {
+ ufd.revents = 0;
+ int nr = poll(&ufd, 1, -1);
+ if (nr <= 0) {
+ continue;
+ }
+ if (ufd.revents & POLLIN) {
+ handle_device_fd();
+ }
+ }
+
+ return 0;
+}
+
+static int get_android_id(const char *id)
+{
+ unsigned int i;
+ for (i = 0; i < ARRAY_SIZE(android_ids); i++)
+ if (!strcmp(id, android_ids[i].name))
+ return android_ids[i].aid;
+ return -1;
+}
+
+void set_device_permission(int nargs, char **args)
+{
+ char *name;
+ char *attr = 0;
+ mode_t perm;
+ uid_t uid;
+ gid_t gid;
+ int prefix = 0;
+ int wildcard = 0;
+ char *endptr;
+ int ret;
+ char *tmp = 0;
+
+ if (nargs == 0)
+ return;
+
+ if (args[0][0] == '#')
+ return;
+
+ name = args[0];
+
+ if (!strncmp(name,"/sys/", 5) && (nargs == 5)) {
+ INFO("/sys/ rule %s %s\n",args[0],args[1]);
+ attr = args[1];
+ args++;
+ nargs--;
+ }
+
+ if (nargs != 4) {
+ ERROR("invalid line ueventd.rc line for '%s'\n", args[0]);
+ return;
+ }
+
+ /* If path starts with mtd@ lookup the mount number. */
+ if (!strncmp(name, "mtd@", 4)) {
+ int n = mtd_name_to_number(name + 4);
+ if (n >= 0)
+ asprintf(&tmp, "/dev/mtd/mtd%d", n);
+ name = tmp;
+ } else {
+ int len = strlen(name);
+ char *wildcard_chr = strchr(name, '*');
+ if ((name[len - 1] == '*') &&
+ (wildcard_chr == (name + len - 1))) {
+ prefix = 1;
+ name[len - 1] = '\0';
+ } else if (wildcard_chr) {
+ wildcard = 1;
+ }
+ }
+
+ perm = strtol(args[1], &endptr, 8);
+ if (!endptr || *endptr != '\0') {
+ ERROR("invalid mode '%s'\n", args[1]);
+ free(tmp);
+ return;
+ }
+
+ ret = get_android_id(args[2]);
+ if (ret < 0) {
+ ERROR("invalid uid '%s'\n", args[2]);
+ free(tmp);
+ return;
+ }
+ uid = ret;
+
+ ret = get_android_id(args[3]);
+ if (ret < 0) {
+ ERROR("invalid gid '%s'\n", args[3]);
+ free(tmp);
+ return;
+ }
+ gid = ret;
+
+ add_dev_perms(name, attr, perm, uid, gid, prefix, wildcard);
+ free(tmp);
+}
diff --git a/init/ueventd.h b/init/ueventd.h
new file mode 100644
index 0000000..d12d7fe
--- /dev/null
+++ b/init/ueventd.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_UEVENTD_H_
+#define _INIT_UEVENTD_H_
+
+#include <cutils/list.h>
+#include <sys/types.h>
+
+enum devname_src_t {
+ DEVNAME_UNKNOWN = 0,
+ DEVNAME_UEVENT_DEVNAME,
+ DEVNAME_UEVENT_DEVPATH,
+};
+
+struct ueventd_subsystem {
+ struct listnode slist;
+
+ const char *name;
+ const char *dirname;
+ devname_src_t devname_src;
+};
+
+int ueventd_main(int argc, char **argv);
+
+#endif
diff --git a/init/ueventd_keywords.h b/init/ueventd_keywords.h
new file mode 100644
index 0000000..88e8f01
--- /dev/null
+++ b/init/ueventd_keywords.h
@@ -0,0 +1,15 @@
+#ifndef KEYWORD
+#define __MAKE_KEYWORD_ENUM__
+#define KEYWORD(symbol, flags, nargs) K_##symbol,
+enum {
+ K_UNKNOWN,
+#endif
+ KEYWORD(subsystem, SECTION, 1)
+ KEYWORD(devname, OPTION, 1)
+ KEYWORD(dirname, OPTION, 1)
+#ifdef __MAKE_KEYWORD_ENUM__
+ KEYWORD_COUNT,
+};
+#undef __MAKE_KEYWORD_ENUM__
+#undef KEYWORD
+#endif
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
new file mode 100644
index 0000000..497c606
--- /dev/null
+++ b/init/ueventd_parser.cpp
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ueventd.h"
+#include "ueventd_parser.h"
+#include "parser.h"
+#include "log.h"
+#include "util.h"
+
+static list_declare(subsystem_list);
+
+static void parse_line_device(struct parse_state *state, int nargs, char **args);
+
+#define SECTION 0x01
+#define OPTION 0x02
+
+#include "ueventd_keywords.h"
+
+#define KEYWORD(symbol, flags, nargs) \
+ [ K_##symbol ] = { #symbol, nargs + 1, flags, },
+
+static struct {
+ const char *name;
+ unsigned char nargs;
+ unsigned char flags;
+} keyword_info[KEYWORD_COUNT] = {
+ [ K_UNKNOWN ] = { "unknown", 0, 0 },
+#include "ueventd_keywords.h"
+};
+#undef KEYWORD
+
+#define kw_is(kw, type) (keyword_info[kw].flags & (type))
+#define kw_nargs(kw) (keyword_info[kw].nargs)
+
+static int lookup_keyword(const char *s)
+{
+ switch (*s++) {
+ case 'd':
+ if (!strcmp(s, "evname")) return K_devname;
+ if (!strcmp(s, "irname")) return K_dirname;
+ break;
+ case 's':
+ if (!strcmp(s, "ubsystem")) return K_subsystem;
+ break;
+ }
+ return K_UNKNOWN;
+}
+
+static void parse_line_no_op(struct parse_state*, int, char**) {
+}
+
+static int valid_name(const char *name)
+{
+ while (*name) {
+ if (!isalnum(*name) && (*name != '_') && (*name != '-')) {
+ return 0;
+ }
+ name++;
+ }
+ return 1;
+}
+
+struct ueventd_subsystem *ueventd_subsystem_find_by_name(const char *name)
+{
+ struct listnode *node;
+ struct ueventd_subsystem *s;
+
+ list_for_each(node, &subsystem_list) {
+ s = node_to_item(node, struct ueventd_subsystem, slist);
+ if (!strcmp(s->name, name)) {
+ return s;
+ }
+ }
+ return 0;
+}
+
+static void *parse_subsystem(parse_state* state, int /*nargs*/, char** args) {
+ if (!valid_name(args[1])) {
+ parse_error(state, "invalid subsystem name '%s'\n", args[1]);
+ return 0;
+ }
+
+ ueventd_subsystem* s = ueventd_subsystem_find_by_name(args[1]);
+ if (s) {
+ parse_error(state, "ignored duplicate definition of subsystem '%s'\n",
+ args[1]);
+ return 0;
+ }
+
+ s = (ueventd_subsystem*) calloc(1, sizeof(*s));
+ if (!s) {
+ parse_error(state, "out of memory\n");
+ return 0;
+ }
+ s->name = args[1];
+ s->dirname = "/dev";
+ list_add_tail(&subsystem_list, &s->slist);
+ return s;
+}
+
+static void parse_line_subsystem(struct parse_state *state, int nargs,
+ char **args)
+{
+ struct ueventd_subsystem *s = (ueventd_subsystem*) state->context;
+ int kw;
+
+ if (nargs == 0) {
+ return;
+ }
+
+ kw = lookup_keyword(args[0]);
+ switch (kw) {
+ case K_devname:
+ if (!strcmp(args[1], "uevent_devname"))
+ s->devname_src = DEVNAME_UEVENT_DEVNAME;
+ else if (!strcmp(args[1], "uevent_devpath"))
+ s->devname_src = DEVNAME_UEVENT_DEVPATH;
+ else
+ parse_error(state, "invalid devname '%s'\n", args[1]);
+ break;
+
+ case K_dirname:
+ if (args[1][0] == '/')
+ s->dirname = args[1];
+ else
+ parse_error(state, "dirname '%s' does not start with '/'\n",
+ args[1]);
+ break;
+
+ default:
+ parse_error(state, "invalid option '%s'\n", args[0]);
+ }
+}
+
+static void parse_new_section(struct parse_state *state, int kw,
+ int nargs, char **args)
+{
+ printf("[ %s %s ]\n", args[0],
+ nargs > 1 ? args[1] : "");
+
+ switch(kw) {
+ case K_subsystem:
+ state->context = parse_subsystem(state, nargs, args);
+ if (state->context) {
+ state->parse_line = parse_line_subsystem;
+ return;
+ }
+ break;
+ }
+ state->parse_line = parse_line_no_op;
+}
+
+static void parse_line(struct parse_state *state, char **args, int nargs)
+{
+ int kw = lookup_keyword(args[0]);
+ int kw_nargs = kw_nargs(kw);
+
+ if (nargs < kw_nargs) {
+ parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1,
+ kw_nargs > 2 ? "arguments" : "argument");
+ return;
+ }
+
+ if (kw_is(kw, SECTION)) {
+ parse_new_section(state, kw, nargs, args);
+ } else if (kw_is(kw, OPTION)) {
+ state->parse_line(state, nargs, args);
+ } else {
+ parse_line_device(state, nargs, args);
+ }
+}
+
+static void parse_config(const char *fn, const std::string& data)
+{
+ char *args[UEVENTD_PARSER_MAXARGS];
+
+ int nargs = 0;
+ parse_state state;
+ state.filename = fn;
+ state.line = 1;
+ state.ptr = strdup(data.c_str()); // TODO: fix this code!
+ state.nexttoken = 0;
+ state.parse_line = parse_line_no_op;
+ for (;;) {
+ int token = next_token(&state);
+ switch (token) {
+ case T_EOF:
+ parse_line(&state, args, nargs);
+ return;
+ case T_NEWLINE:
+ if (nargs) {
+ parse_line(&state, args, nargs);
+ nargs = 0;
+ }
+ state.line++;
+ break;
+ case T_TEXT:
+ if (nargs < UEVENTD_PARSER_MAXARGS) {
+ args[nargs++] = state.text;
+ }
+ break;
+ }
+ }
+}
+
+int ueventd_parse_config_file(const char *fn)
+{
+ std::string data;
+ if (!read_file(fn, &data)) {
+ return -1;
+ }
+
+ data.push_back('\n'); // TODO: fix parse_config.
+ parse_config(fn, data);
+ dump_parser_state();
+ return 0;
+}
+
+static void parse_line_device(parse_state*, int nargs, char** args) {
+ set_device_permission(nargs, args);
+}
diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h
new file mode 100644
index 0000000..907cc49
--- /dev/null
+++ b/init/ueventd_parser.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_UEVENTD_PARSER_H_
+#define _INIT_UEVENTD_PARSER_H_
+
+#include "ueventd.h"
+
+#define UEVENTD_PARSER_MAXARGS 5
+
+int ueventd_parse_config_file(const char *fn);
+void set_device_permission(int nargs, char **args);
+struct ueventd_subsystem *ueventd_subsystem_find_by_name(const char *name);
+
+#endif
diff --git a/init/util.cpp b/init/util.cpp
new file mode 100644
index 0000000..f6131e3
--- /dev/null
+++ b/init/util.cpp
@@ -0,0 +1,478 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <errno.h>
+#include <time.h>
+#include <ftw.h>
+
+#include <selinux/label.h>
+#include <selinux/android.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <base/file.h>
+#include <base/strings.h>
+
+/* for ANDROID_SOCKET_* */
+#include <cutils/sockets.h>
+#include <base/stringprintf.h>
+
+#include <private/android_filesystem_config.h>
+
+#include "init.h"
+#include "log.h"
+#include "util.h"
+
+/*
+ * android_name_to_id - returns the integer uid/gid associated with the given
+ * name, or UINT_MAX on error.
+ */
+static unsigned int android_name_to_id(const char *name)
+{
+ const struct android_id_info *info = android_ids;
+ unsigned int n;
+
+ for (n = 0; n < android_id_count; n++) {
+ if (!strcmp(info[n].name, name))
+ return info[n].aid;
+ }
+
+ return UINT_MAX;
+}
+
+static unsigned int do_decode_uid(const char *s)
+{
+ unsigned int v;
+
+ if (!s || *s == '\0')
+ return UINT_MAX;
+ if (isalpha(s[0]))
+ return android_name_to_id(s);
+
+ errno = 0;
+ v = (unsigned int) strtoul(s, 0, 0);
+ if (errno)
+ return UINT_MAX;
+ return v;
+}
+
+/*
+ * decode_uid - decodes and returns the given string, which can be either the
+ * numeric or name representation, into the integer uid or gid. Returns
+ * UINT_MAX on error.
+ */
+unsigned int decode_uid(const char *s) {
+ unsigned int v = do_decode_uid(s);
+ if (v == UINT_MAX) {
+ ERROR("decode_uid: Unable to find UID for '%s'. Returning UINT_MAX\n", s);
+ }
+ return v;
+}
+
+/*
+ * create_socket - creates a Unix domain socket in ANDROID_SOCKET_DIR
+ * ("/dev/socket") as dictated in init.rc. This socket is inherited by the
+ * daemon. We communicate the file descriptor's value via the environment
+ * variable ANDROID_SOCKET_ENV_PREFIX<name> ("ANDROID_SOCKET_foo").
+ */
+int create_socket(const char *name, int type, mode_t perm, uid_t uid,
+ gid_t gid, const char *socketcon)
+{
+ struct sockaddr_un addr;
+ int fd, ret;
+ char *filecon;
+
+ if (socketcon)
+ setsockcreatecon(socketcon);
+
+ fd = socket(PF_UNIX, type, 0);
+ if (fd < 0) {
+ ERROR("Failed to open socket '%s': %s\n", name, strerror(errno));
+ return -1;
+ }
+
+ if (socketcon)
+ setsockcreatecon(NULL);
+
+ memset(&addr, 0 , sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR"/%s",
+ name);
+
+ ret = unlink(addr.sun_path);
+ if (ret != 0 && errno != ENOENT) {
+ ERROR("Failed to unlink old socket '%s': %s\n", name, strerror(errno));
+ goto out_close;
+ }
+
+ filecon = NULL;
+ if (sehandle) {
+ ret = selabel_lookup(sehandle, &filecon, addr.sun_path, S_IFSOCK);
+ if (ret == 0)
+ setfscreatecon(filecon);
+ }
+
+ ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr));
+ if (ret) {
+ ERROR("Failed to bind socket '%s': %s\n", name, strerror(errno));
+ goto out_unlink;
+ }
+
+ setfscreatecon(NULL);
+ freecon(filecon);
+
+ chown(addr.sun_path, uid, gid);
+ chmod(addr.sun_path, perm);
+
+ INFO("Created socket '%s' with mode '%o', user '%d', group '%d'\n",
+ addr.sun_path, perm, uid, gid);
+
+ return fd;
+
+out_unlink:
+ unlink(addr.sun_path);
+out_close:
+ close(fd);
+ return -1;
+}
+
+bool read_file(const char* path, std::string* content) {
+ content->clear();
+
+ int fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY|O_NOFOLLOW|O_CLOEXEC));
+ if (fd == -1) {
+ return false;
+ }
+
+ // For security reasons, disallow world-writable
+ // or group-writable files.
+ struct stat sb;
+ if (fstat(fd, &sb) == -1) {
+ ERROR("fstat failed for '%s': %s\n", path, strerror(errno));
+ return false;
+ }
+ if ((sb.st_mode & (S_IWGRP | S_IWOTH)) != 0) {
+ ERROR("skipping insecure file '%s'\n", path);
+ return false;
+ }
+
+ bool okay = android::base::ReadFdToString(fd, content);
+ close(fd);
+ return okay;
+}
+
+int write_file(const char* path, const char* content) {
+ int fd = TEMP_FAILURE_RETRY(open(path, O_WRONLY|O_CREAT|O_NOFOLLOW|O_CLOEXEC, 0600));
+ if (fd == -1) {
+ NOTICE("write_file: Unable to open '%s': %s\n", path, strerror(errno));
+ return -1;
+ }
+ int result = android::base::WriteStringToFd(content, fd) ? 0 : -1;
+ if (result == -1) {
+ NOTICE("write_file: Unable to write to '%s': %s\n", path, strerror(errno));
+ }
+ close(fd);
+ return result;
+}
+
+#define MAX_MTD_PARTITIONS 16
+
+static struct {
+ char name[16];
+ int number;
+} mtd_part_map[MAX_MTD_PARTITIONS];
+
+static int mtd_part_count = -1;
+
+static void find_mtd_partitions(void)
+{
+ int fd;
+ char buf[1024];
+ char *pmtdbufp;
+ ssize_t pmtdsize;
+ int r;
+
+ fd = open("/proc/mtd", O_RDONLY|O_CLOEXEC);
+ if (fd < 0)
+ return;
+
+ buf[sizeof(buf) - 1] = '\0';
+ pmtdsize = read(fd, buf, sizeof(buf) - 1);
+ pmtdbufp = buf;
+ while (pmtdsize > 0) {
+ int mtdnum, mtdsize, mtderasesize;
+ char mtdname[16];
+ mtdname[0] = '\0';
+ mtdnum = -1;
+ r = sscanf(pmtdbufp, "mtd%d: %x %x %15s",
+ &mtdnum, &mtdsize, &mtderasesize, mtdname);
+ if ((r == 4) && (mtdname[0] == '"')) {
+ char *x = strchr(mtdname + 1, '"');
+ if (x) {
+ *x = 0;
+ }
+ INFO("mtd partition %d, %s\n", mtdnum, mtdname + 1);
+ if (mtd_part_count < MAX_MTD_PARTITIONS) {
+ strcpy(mtd_part_map[mtd_part_count].name, mtdname + 1);
+ mtd_part_map[mtd_part_count].number = mtdnum;
+ mtd_part_count++;
+ } else {
+ ERROR("too many mtd partitions\n");
+ }
+ }
+ while (pmtdsize > 0 && *pmtdbufp != '\n') {
+ pmtdbufp++;
+ pmtdsize--;
+ }
+ if (pmtdsize > 0) {
+ pmtdbufp++;
+ pmtdsize--;
+ }
+ }
+ close(fd);
+}
+
+int mtd_name_to_number(const char *name)
+{
+ int n;
+ if (mtd_part_count < 0) {
+ mtd_part_count = 0;
+ find_mtd_partitions();
+ }
+ for (n = 0; n < mtd_part_count; n++) {
+ if (!strcmp(name, mtd_part_map[n].name)) {
+ return mtd_part_map[n].number;
+ }
+ }
+ return -1;
+}
+
+time_t gettime() {
+ timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ return now.tv_sec;
+}
+
+uint64_t gettime_ns() {
+ timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ return static_cast<uint64_t>(now.tv_sec) * UINT64_C(1000000000) + now.tv_nsec;
+}
+
+int mkdir_recursive(const char *pathname, mode_t mode)
+{
+ char buf[128];
+ const char *slash;
+ const char *p = pathname;
+ int width;
+ int ret;
+ struct stat info;
+
+ while ((slash = strchr(p, '/')) != NULL) {
+ width = slash - pathname;
+ p = slash + 1;
+ if (width < 0)
+ break;
+ if (width == 0)
+ continue;
+ if ((unsigned int)width > sizeof(buf) - 1) {
+ ERROR("path too long for mkdir_recursive\n");
+ return -1;
+ }
+ memcpy(buf, pathname, width);
+ buf[width] = 0;
+ if (stat(buf, &info) != 0) {
+ ret = make_dir(buf, mode);
+ if (ret && errno != EEXIST)
+ return ret;
+ }
+ }
+ ret = make_dir(pathname, mode);
+ if (ret && errno != EEXIST)
+ return ret;
+ return 0;
+}
+
+/*
+ * replaces any unacceptable characters with '_', the
+ * length of the resulting string is equal to the input string
+ */
+void sanitize(char *s)
+{
+ const char* accept =
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "0123456789"
+ "_-.";
+
+ if (!s)
+ return;
+
+ while (*s) {
+ s += strspn(s, accept);
+ if (*s) *s++ = '_';
+ }
+}
+
+void make_link(const char *oldpath, const char *newpath)
+{
+ int ret;
+ char buf[256];
+ char *slash;
+ int width;
+
+ slash = strrchr(newpath, '/');
+ if (!slash)
+ return;
+ width = slash - newpath;
+ if (width <= 0 || width > (int)sizeof(buf) - 1)
+ return;
+ memcpy(buf, newpath, width);
+ buf[width] = 0;
+ ret = mkdir_recursive(buf, 0755);
+ if (ret)
+ ERROR("Failed to create directory %s: %s (%d)\n", buf, strerror(errno), errno);
+
+ ret = symlink(oldpath, newpath);
+ if (ret && errno != EEXIST)
+ ERROR("Failed to symlink %s to %s: %s (%d)\n", oldpath, newpath, strerror(errno), errno);
+}
+
+void remove_link(const char *oldpath, const char *newpath)
+{
+ char path[256];
+ ssize_t ret;
+ ret = readlink(newpath, path, sizeof(path) - 1);
+ if (ret <= 0)
+ return;
+ path[ret] = 0;
+ if (!strcmp(path, oldpath))
+ unlink(newpath);
+}
+
+int wait_for_file(const char *filename, int timeout)
+{
+ struct stat info;
+ time_t timeout_time = gettime() + timeout;
+ int ret = -1;
+
+ while (gettime() < timeout_time && ((ret = stat(filename, &info)) < 0))
+ usleep(10000);
+
+ return ret;
+}
+
+void open_devnull_stdio(void)
+{
+ // Try to avoid the mknod() call if we can. Since SELinux makes
+ // a /dev/null replacement available for free, let's use it.
+ int fd = open("/sys/fs/selinux/null", O_RDWR);
+ if (fd == -1) {
+ // OOPS, /sys/fs/selinux/null isn't available, likely because
+ // /sys/fs/selinux isn't mounted. Fall back to mknod.
+ static const char *name = "/dev/__null__";
+ if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) {
+ fd = open(name, O_RDWR);
+ unlink(name);
+ }
+ if (fd == -1) {
+ exit(1);
+ }
+ }
+
+ dup2(fd, 0);
+ dup2(fd, 1);
+ dup2(fd, 2);
+ if (fd > 2) {
+ close(fd);
+ }
+}
+
+void import_kernel_cmdline(bool in_qemu,
+ std::function<void(const std::string&, const std::string&, bool)> fn) {
+ std::string cmdline;
+ android::base::ReadFileToString("/proc/cmdline", &cmdline);
+
+ for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) {
+ std::vector<std::string> pieces = android::base::Split(entry, "=");
+ if (pieces.size() == 2) {
+ fn(pieces[0], pieces[1], in_qemu);
+ }
+ }
+}
+
+int make_dir(const char *path, mode_t mode)
+{
+ int rc;
+
+ char *secontext = NULL;
+
+ if (sehandle) {
+ selabel_lookup(sehandle, &secontext, path, mode);
+ setfscreatecon(secontext);
+ }
+
+ rc = mkdir(path, mode);
+
+ if (secontext) {
+ int save_errno = errno;
+ freecon(secontext);
+ setfscreatecon(NULL);
+ errno = save_errno;
+ }
+
+ return rc;
+}
+
+int restorecon(const char* pathname)
+{
+ return selinux_android_restorecon(pathname, 0);
+}
+
+int restorecon_recursive(const char* pathname)
+{
+ return selinux_android_restorecon(pathname, SELINUX_ANDROID_RESTORECON_RECURSE);
+}
+
+/*
+ * Writes hex_len hex characters (1/2 byte) to hex from bytes.
+ */
+std::string bytes_to_hex(const uint8_t* bytes, size_t bytes_len) {
+ std::string hex("0x");
+ for (size_t i = 0; i < bytes_len; i++)
+ android::base::StringAppendF(&hex, "%02x", bytes[i]);
+ return hex;
+}
+
+/*
+ * Returns true is pathname is a directory
+ */
+bool is_dir(const char* pathname) {
+ struct stat info;
+ if (stat(pathname, &info) == -1) {
+ return false;
+ }
+ return S_ISDIR(info.st_mode);
+}
diff --git a/init/util.h b/init/util.h
new file mode 100644
index 0000000..f08cb8d
--- /dev/null
+++ b/init/util.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_UTIL_H_
+#define _INIT_UTIL_H_
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <string>
+#include <functional>
+
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
+
+#define COLDBOOT_DONE "/dev/.coldboot_done"
+
+int mtd_name_to_number(const char *name);
+int create_socket(const char *name, int type, mode_t perm,
+ uid_t uid, gid_t gid, const char *socketcon);
+
+bool read_file(const char* path, std::string* content);
+int write_file(const char* path, const char* content);
+
+time_t gettime();
+uint64_t gettime_ns();
+
+class Timer {
+ public:
+ Timer() : t0(gettime_ns()) {
+ }
+
+ double duration() {
+ return static_cast<double>(gettime_ns() - t0) / 1000000000.0;
+ }
+
+ private:
+ uint64_t t0;
+};
+
+unsigned int decode_uid(const char *s);
+
+int mkdir_recursive(const char *pathname, mode_t mode);
+void sanitize(char *p);
+void make_link(const char *oldpath, const char *newpath);
+void remove_link(const char *oldpath, const char *newpath);
+int wait_for_file(const char *filename, int timeout);
+void open_devnull_stdio(void);
+void import_kernel_cmdline(bool in_qemu,
+ std::function<void(const std::string&, const std::string&, bool)>);
+int make_dir(const char *path, mode_t mode);
+int restorecon(const char *pathname);
+int restorecon_recursive(const char *pathname);
+std::string bytes_to_hex(const uint8_t *bytes, size_t bytes_len);
+bool is_dir(const char* pathname);
+#endif
diff --git a/init/util_test.cpp b/init/util_test.cpp
new file mode 100644
index 0000000..228954b
--- /dev/null
+++ b/init/util_test.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "util.h"
+
+#include <errno.h>
+#include <gtest/gtest.h>
+
+TEST(util, read_file_ENOENT) {
+ std::string s("hello");
+ errno = 0;
+ EXPECT_FALSE(read_file("/proc/does-not-exist", &s));
+ EXPECT_EQ(ENOENT, errno);
+ EXPECT_EQ("", s); // s was cleared.
+}
+
+TEST(util, read_file_success) {
+ std::string s("hello");
+ EXPECT_TRUE(read_file("/proc/version", &s));
+ EXPECT_GT(s.length(), 6U);
+ EXPECT_EQ('\n', s[s.length() - 1]);
+ s[5] = 0;
+ EXPECT_STREQ("Linux", s.c_str());
+}
+
+TEST(util, decode_uid) {
+ EXPECT_EQ(0U, decode_uid("root"));
+ EXPECT_EQ(UINT_MAX, decode_uid("toot"));
+ EXPECT_EQ(123U, decode_uid("123"));
+}
diff --git a/init/watchdogd.cpp b/init/watchdogd.cpp
new file mode 100644
index 0000000..0d16db9
--- /dev/null
+++ b/init/watchdogd.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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 <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <linux/watchdog.h>
+
+#include "log.h"
+#include "util.h"
+
+#define DEV_NAME "/dev/watchdog"
+
+int watchdogd_main(int argc, char **argv) {
+ open_devnull_stdio();
+ klog_init();
+ klog_set_level(KLOG_NOTICE_LEVEL);
+
+ int interval = 10;
+ if (argc >= 2) interval = atoi(argv[1]);
+
+ int margin = 10;
+ if (argc >= 3) margin = atoi(argv[2]);
+
+ NOTICE("started (interval %d, margin %d)!\n", interval, margin);
+
+ int fd = open(DEV_NAME, O_RDWR|O_CLOEXEC);
+ if (fd == -1) {
+ ERROR("Failed to open %s: %s\n", DEV_NAME, strerror(errno));
+ return 1;
+ }
+
+ int timeout = interval + margin;
+ int ret = ioctl(fd, WDIOC_SETTIMEOUT, &timeout);
+ if (ret) {
+ ERROR("Failed to set timeout to %d: %s\n", timeout, strerror(errno));
+ ret = ioctl(fd, WDIOC_GETTIMEOUT, &timeout);
+ if (ret) {
+ ERROR("Failed to get timeout: %s\n", strerror(errno));
+ } else {
+ if (timeout > margin) {
+ interval = timeout - margin;
+ } else {
+ interval = 1;
+ }
+ WARNING("Adjusted interval to timeout returned by driver:"
+ " timeout %d, interval %d, margin %d\n",
+ timeout, interval, margin);
+ }
+ }
+
+ while (true) {
+ write(fd, "", 1);
+ sleep(interval);
+ }
+}
diff --git a/init/watchdogd.h b/init/watchdogd.h
new file mode 100644
index 0000000..8b48ab8
--- /dev/null
+++ b/init/watchdogd.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_WATCHDOGD_H_
+#define _INIT_WATCHDOGD_H_
+
+int watchdogd_main(int argc, char **argv);
+
+#endif
diff --git a/libbacktrace/Android.build.mk b/libbacktrace/Android.build.mk
new file mode 100644
index 0000000..4983b55
--- /dev/null
+++ b/libbacktrace/Android.build.mk
@@ -0,0 +1,82 @@
+#
+# Copyright (C) 2014 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 $(CLEAR_VARS)
+
+LOCAL_MODULE := $(module)
+LOCAL_MODULE_TAGS := $(module_tag)
+LOCAL_MULTILIB := $($(module)_multilib)
+ifeq ($(LOCAL_MULTILIB),both)
+ifneq ($(build_target),$(filter $(build_target),SHARED_LIBRARY STATIC_LIBRARY))
+ LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+ LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+endif
+endif
+
+LOCAL_ADDITIONAL_DEPENDENCIES := \
+ $(LOCAL_PATH)/Android.mk \
+ $(LOCAL_PATH)/Android.build.mk \
+
+LOCAL_CFLAGS := \
+ $(libbacktrace_common_cflags) \
+ $($(module)_cflags) \
+ $($(module)_cflags_$(build_type)) \
+
+LOCAL_CLANG_CFLAGS += \
+ $(libbacktrace_common_clang_cflags) \
+
+LOCAL_CONLYFLAGS += \
+ $(libbacktrace_common_conlyflags) \
+ $($(module)_conlyflags) \
+ $($(module)_conlyflags_$(build_type)) \
+
+LOCAL_CPPFLAGS += \
+ $(libbacktrace_common_cppflags) \
+ $($(module)_cppflags) \
+ $($(module)_cppflags_$(build_type)) \
+
+LOCAL_C_INCLUDES := \
+ $(libbacktrace_common_c_includes) \
+ $($(module)_c_includes) \
+ $($(module)_c_includes_$(build_type)) \
+
+LOCAL_SRC_FILES := \
+ $($(module)_src_files) \
+ $($(module)_src_files_$(build_type)) \
+
+LOCAL_STATIC_LIBRARIES := \
+ $($(module)_static_libraries) \
+ $($(module)_static_libraries_$(build_type)) \
+
+LOCAL_SHARED_LIBRARIES := \
+ $($(module)_shared_libraries) \
+ $($(module)_shared_libraries_$(build_type)) \
+
+LOCAL_LDLIBS := \
+ $($(module)_ldlibs) \
+ $($(module)_ldlibs_$(build_type)) \
+
+ifeq ($(build_type),target)
+ include $(BUILD_$(build_target))
+endif
+
+ifeq ($(build_type),host)
+ # Only build if host builds are supported.
+ ifeq ($(build_host),true)
+ LOCAL_CFLAGS += -Wno-extern-c-compat -fno-omit-frame-pointer
+ include $(BUILD_HOST_$(build_target))
+ endif
+endif
diff --git a/libbacktrace/Android.mk b/libbacktrace/Android.mk
new file mode 100644
index 0000000..395d677
--- /dev/null
+++ b/libbacktrace/Android.mk
@@ -0,0 +1,156 @@
+#
+# Copyright (C) 2014 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)
+
+libbacktrace_common_cflags := \
+ -Wall \
+ -Werror \
+
+libbacktrace_common_conlyflags := \
+ -std=gnu99 \
+
+libbacktrace_common_cppflags := \
+ -std=gnu++11 \
+
+# The latest clang (r230699) does not allow SP/PC to be declared in inline asm lists.
+libbacktrace_common_clang_cflags += \
+ -Wno-inline-asm
+
+build_host := false
+ifeq ($(HOST_OS),linux)
+ifeq ($(HOST_ARCH),$(filter $(HOST_ARCH),x86 x86_64))
+build_host := true
+endif
+endif
+
+#-------------------------------------------------------------------------
+# The libbacktrace library.
+#-------------------------------------------------------------------------
+libbacktrace_src_files := \
+ Backtrace.cpp \
+ BacktraceCurrent.cpp \
+ BacktraceMap.cpp \
+ BacktracePtrace.cpp \
+ thread_utils.c \
+ ThreadEntry.cpp \
+ UnwindCurrent.cpp \
+ UnwindMap.cpp \
+ UnwindPtrace.cpp \
+
+libbacktrace_shared_libraries := \
+ libbase \
+ liblog \
+ libunwind \
+
+libbacktrace_ldlibs_host := \
+ -lpthread \
+ -lrt \
+
+module := libbacktrace
+module_tag := optional
+build_type := target
+build_target := SHARED_LIBRARY
+include $(LOCAL_PATH)/Android.build.mk
+build_type := host
+libbacktrace_multilib := both
+include $(LOCAL_PATH)/Android.build.mk
+libbacktrace_static_libraries := \
+ libbase \
+ liblog \
+ libunwind \
+
+build_target := STATIC_LIBRARY
+include $(LOCAL_PATH)/Android.build.mk
+libbacktrace_static_libraries :=
+
+#-------------------------------------------------------------------------
+# The libbacktrace_test library needed by backtrace_test.
+#-------------------------------------------------------------------------
+libbacktrace_test_cflags := \
+ -O0 \
+
+libbacktrace_test_src_files := \
+ backtrace_testlib.c \
+
+module := libbacktrace_test
+module_tag := debug
+build_type := target
+build_target := SHARED_LIBRARY
+libbacktrace_test_multilib := both
+include $(LOCAL_PATH)/Android.build.mk
+build_type := host
+include $(LOCAL_PATH)/Android.build.mk
+
+#-------------------------------------------------------------------------
+# The backtrace_test executable.
+#-------------------------------------------------------------------------
+backtrace_test_cflags := \
+ -fno-builtin \
+ -O0 \
+ -g \
+
+backtrace_test_cflags_target := \
+ -DENABLE_PSS_TESTS \
+
+backtrace_test_src_files := \
+ backtrace_test.cpp \
+ GetPss.cpp \
+ thread_utils.c \
+
+backtrace_test_ldlibs_host := \
+ -lpthread \
+ -lrt \
+
+backtrace_test_shared_libraries := \
+ libbacktrace_test \
+ libbacktrace \
+ libbase \
+ libcutils \
+
+backtrace_test_shared_libraries_target += \
+ libdl \
+
+backtrace_test_ldlibs_host += \
+ -ldl \
+
+module := backtrace_test
+module_tag := debug
+build_type := target
+build_target := NATIVE_TEST
+backtrace_test_multilib := both
+include $(LOCAL_PATH)/Android.build.mk
+build_type := host
+include $(LOCAL_PATH)/Android.build.mk
+
+#----------------------------------------------------------------------------
+# Special truncated libbacktrace library for mac.
+#----------------------------------------------------------------------------
+ifeq ($(HOST_OS),darwin)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libbacktrace
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+ BacktraceMap.cpp \
+
+LOCAL_MULTILIB := both
+
+include $(BUILD_HOST_SHARED_LIBRARY)
+
+endif # HOST_OS-darwin
diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/Backtrace.cpp
new file mode 100644
index 0000000..128bb04
--- /dev/null
+++ b/libbacktrace/Backtrace.cpp
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <ucontext.h>
+
+#include <string>
+
+#include <base/stringprintf.h>
+
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+
+#include <cutils/threads.h>
+
+#include "BacktraceLog.h"
+#include "thread_utils.h"
+#include "UnwindCurrent.h"
+#include "UnwindPtrace.h"
+
+using android::base::StringPrintf;
+
+//-------------------------------------------------------------------------
+// Backtrace functions.
+//-------------------------------------------------------------------------
+Backtrace::Backtrace(pid_t pid, pid_t tid, BacktraceMap* map)
+ : pid_(pid), tid_(tid), map_(map), map_shared_(true) {
+ if (map_ == nullptr) {
+ map_ = BacktraceMap::Create(pid);
+ map_shared_ = false;
+ }
+}
+
+Backtrace::~Backtrace() {
+ if (map_ && !map_shared_) {
+ delete map_;
+ map_ = nullptr;
+ }
+}
+
+extern "C" char* __cxa_demangle(const char* mangled, char* buf, size_t* len,
+ int* status);
+
+std::string Backtrace::GetFunctionName(uintptr_t pc, uintptr_t* offset) {
+ std::string func_name = GetFunctionNameRaw(pc, offset);
+ if (!func_name.empty()) {
+#if defined(__APPLE__)
+ // Mac OS' __cxa_demangle demangles "f" as "float"; last tested on 10.7.
+ if (func_name[0] != '_') {
+ return func_name;
+ }
+#endif
+ char* name = __cxa_demangle(func_name.c_str(), 0, 0, 0);
+ if (name) {
+ func_name = name;
+ free(name);
+ }
+ }
+ return func_name;
+}
+
+bool Backtrace::VerifyReadWordArgs(uintptr_t ptr, word_t* out_value) {
+ if (ptr & (sizeof(word_t)-1)) {
+ BACK_LOGW("invalid pointer %p", reinterpret_cast<void*>(ptr));
+ *out_value = static_cast<word_t>(-1);
+ return false;
+ }
+ return true;
+}
+
+std::string Backtrace::FormatFrameData(size_t frame_num) {
+ if (frame_num >= frames_.size()) {
+ return "";
+ }
+ return FormatFrameData(&frames_[frame_num]);
+}
+
+std::string Backtrace::FormatFrameData(const backtrace_frame_data_t* frame) {
+ const char* map_name;
+ if (BacktraceMap::IsValid(frame->map) && !frame->map.name.empty()) {
+ map_name = frame->map.name.c_str();
+ } else {
+ map_name = "<unknown>";
+ }
+
+ uintptr_t relative_pc = BacktraceMap::GetRelativePc(frame->map, frame->pc);
+
+ std::string line(StringPrintf("#%02zu pc %" PRIPTR " %s", frame->num, relative_pc, map_name));
+ if (!frame->func_name.empty()) {
+ line += " (" + frame->func_name;
+ if (frame->func_offset) {
+ line += StringPrintf("+%" PRIuPTR, frame->func_offset);
+ }
+ line += ')';
+ }
+
+ return line;
+}
+
+void Backtrace::FillInMap(uintptr_t pc, backtrace_map_t* map) {
+ if (map_ != nullptr) {
+ map_->FillIn(pc, map);
+ }
+}
+
+Backtrace* Backtrace::Create(pid_t pid, pid_t tid, BacktraceMap* map) {
+ if (pid == BACKTRACE_CURRENT_PROCESS) {
+ pid = getpid();
+ if (tid == BACKTRACE_CURRENT_THREAD) {
+ tid = gettid();
+ }
+ } else if (tid == BACKTRACE_CURRENT_THREAD) {
+ tid = pid;
+ }
+
+ if (pid == getpid()) {
+ return new UnwindCurrent(pid, tid, map);
+ } else {
+ return new UnwindPtrace(pid, tid, map);
+ }
+}
diff --git a/libbacktrace/BacktraceCurrent.cpp b/libbacktrace/BacktraceCurrent.cpp
new file mode 100644
index 0000000..2714d93
--- /dev/null
+++ b/libbacktrace/BacktraceCurrent.cpp
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _GNU_SOURCE 1
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <ucontext.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+
+#include <cutils/threads.h>
+
+#include "BacktraceCurrent.h"
+#include "BacktraceLog.h"
+#include "ThreadEntry.h"
+#include "thread_utils.h"
+
+bool BacktraceCurrent::ReadWord(uintptr_t ptr, word_t* out_value) {
+ if (!VerifyReadWordArgs(ptr, out_value)) {
+ return false;
+ }
+
+ backtrace_map_t map;
+ FillInMap(ptr, &map);
+ if (BacktraceMap::IsValid(map) && map.flags & PROT_READ) {
+ *out_value = *reinterpret_cast<word_t*>(ptr);
+ return true;
+ } else {
+ BACK_LOGW("pointer %p not in a readable map", reinterpret_cast<void*>(ptr));
+ *out_value = static_cast<word_t>(-1);
+ return false;
+ }
+}
+
+size_t BacktraceCurrent::Read(uintptr_t addr, uint8_t* buffer, size_t bytes) {
+ backtrace_map_t map;
+ FillInMap(addr, &map);
+ if (!BacktraceMap::IsValid(map) || !(map.flags & PROT_READ)) {
+ return 0;
+ }
+ bytes = MIN(map.end - addr, bytes);
+ memcpy(buffer, reinterpret_cast<uint8_t*>(addr), bytes);
+ return bytes;
+}
+
+bool BacktraceCurrent::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) {
+ if (GetMap() == nullptr) {
+ // Without a map object, we can't do anything.
+ return false;
+ }
+
+ if (ucontext) {
+ return UnwindFromContext(num_ignore_frames, ucontext);
+ }
+
+ if (Tid() != gettid()) {
+ return UnwindThread(num_ignore_frames);
+ }
+
+ return UnwindFromContext(num_ignore_frames, nullptr);
+}
+
+bool BacktraceCurrent::DiscardFrame(const backtrace_frame_data_t& frame) {
+ if (BacktraceMap::IsValid(frame.map)) {
+ const std::string library = basename(frame.map.name.c_str());
+ if (library == "libunwind.so" || library == "libbacktrace.so") {
+ return true;
+ }
+ }
+ return false;
+}
+
+static pthread_mutex_t g_sigaction_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static void SignalHandler(int, siginfo_t*, void* sigcontext) {
+ ThreadEntry* entry = ThreadEntry::Get(getpid(), gettid(), false);
+ if (!entry) {
+ BACK_LOGE("pid %d, tid %d entry not found", getpid(), gettid());
+ return;
+ }
+
+ entry->CopyUcontextFromSigcontext(sigcontext);
+
+ // Indicate the ucontext is now valid.
+ entry->Wake();
+
+ // Pause the thread until the unwind is complete. This avoids having
+ // the thread run ahead causing problems.
+ // The number indicates that we are waiting for the second Wake() call
+ // overall which is made by the thread requesting an unwind.
+ if (entry->Wait(2)) {
+ // Do not remove the entry here because that can result in a deadlock
+ // if the code cannot properly send a signal to the thread under test.
+ entry->Wake();
+ } else {
+ // At this point, it is possible that entry has been freed, so just exit.
+ BACK_LOGE("Timed out waiting for unwind thread to indicate it completed.");
+ }
+}
+
+bool BacktraceCurrent::UnwindThread(size_t num_ignore_frames) {
+ // Prevent multiple threads trying to set the trigger action on different
+ // threads at the same time.
+ pthread_mutex_lock(&g_sigaction_mutex);
+
+ ThreadEntry* entry = ThreadEntry::Get(Pid(), Tid());
+ entry->Lock();
+
+ struct sigaction act, oldact;
+ memset(&act, 0, sizeof(act));
+ act.sa_sigaction = SignalHandler;
+ act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
+ sigemptyset(&act.sa_mask);
+ if (sigaction(THREAD_SIGNAL, &act, &oldact) != 0) {
+ BACK_LOGE("sigaction failed: %s", strerror(errno));
+ ThreadEntry::Remove(entry);
+ pthread_mutex_unlock(&g_sigaction_mutex);
+ return false;
+ }
+
+ if (tgkill(Pid(), Tid(), THREAD_SIGNAL) != 0) {
+ BACK_LOGE("tgkill %d failed: %s", Tid(), strerror(errno));
+ sigaction(THREAD_SIGNAL, &oldact, nullptr);
+ ThreadEntry::Remove(entry);
+ pthread_mutex_unlock(&g_sigaction_mutex);
+ return false;
+ }
+
+ // Wait for the thread to get the ucontext. The number indicates
+ // that we are waiting for the first Wake() call made by the thread.
+ bool wait_completed = entry->Wait(1);
+
+ // After the thread has received the signal, allow other unwinders to
+ // continue.
+ sigaction(THREAD_SIGNAL, &oldact, nullptr);
+ pthread_mutex_unlock(&g_sigaction_mutex);
+
+ bool unwind_done = false;
+ if (wait_completed) {
+ unwind_done = UnwindFromContext(num_ignore_frames, entry->GetUcontext());
+
+ // Tell the signal handler to exit and release the entry.
+ entry->Wake();
+
+ // Wait for the thread to indicate it is done with the ThreadEntry.
+ if (!entry->Wait(3)) {
+ // Send a warning, but do not mark as a failure to unwind.
+ BACK_LOGW("Timed out waiting for signal handler to indicate it finished.");
+ }
+ } else {
+ BACK_LOGE("Timed out waiting for signal handler to get ucontext data.");
+ }
+
+ ThreadEntry::Remove(entry);
+
+ return unwind_done;
+}
diff --git a/libbacktrace/BacktraceCurrent.h b/libbacktrace/BacktraceCurrent.h
new file mode 100644
index 0000000..8aad36d
--- /dev/null
+++ b/libbacktrace/BacktraceCurrent.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBBACKTRACE_BACKTRACE_CURRENT_H
+#define _LIBBACKTRACE_BACKTRACE_CURRENT_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <ucontext.h>
+
+#include <backtrace/Backtrace.h>
+
+// The signal used to cause a thread to dump the stack.
+#if defined(__GLIBC__)
+// In order to run the backtrace_tests on the host, we can't use
+// the internal real time signals used by GLIBC. To avoid this,
+// use SIGRTMIN for the signal to dump the stack.
+#define THREAD_SIGNAL SIGRTMIN
+#else
+#define THREAD_SIGNAL (__SIGRTMIN+1)
+#endif
+
+class BacktraceMap;
+
+class BacktraceCurrent : public Backtrace {
+public:
+ BacktraceCurrent(pid_t pid, pid_t tid, BacktraceMap* map) : Backtrace(pid, tid, map) {}
+ virtual ~BacktraceCurrent() {}
+
+ size_t Read(uintptr_t addr, uint8_t* buffer, size_t bytes) override;
+
+ bool ReadWord(uintptr_t ptr, word_t* out_value) override;
+
+ bool Unwind(size_t num_ignore_frames, ucontext_t* ucontext) override;
+
+protected:
+ bool DiscardFrame(const backtrace_frame_data_t& frame);
+
+private:
+ bool UnwindThread(size_t num_ignore_frames);
+
+ virtual bool UnwindFromContext(size_t num_ignore_frames, ucontext_t* ucontext) = 0;
+};
+
+#endif // _LIBBACKTRACE_BACKTRACE_CURRENT_H
diff --git a/libbacktrace/BacktraceLog.h b/libbacktrace/BacktraceLog.h
new file mode 100644
index 0000000..5c39f1c
--- /dev/null
+++ b/libbacktrace/BacktraceLog.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBBACKTRACE_BACKTRACE_LOG_H
+#define _LIBBACKTRACE_BACKTRACE_LOG_H
+
+#define LOG_TAG "libbacktrace"
+
+#include <log/log.h>
+
+// Macro to log the function name along with the warning message.
+#define BACK_LOGW(format, ...) \
+ ALOGW("%s: " format, __PRETTY_FUNCTION__, ##__VA_ARGS__)
+
+#define BACK_LOGE(format, ...) \
+ ALOGE("%s: " format, __PRETTY_FUNCTION__, ##__VA_ARGS__)
+
+#endif // _LIBBACKTRACE_BACKTRACE_LOG_H
diff --git a/libbacktrace/BacktraceMap.cpp b/libbacktrace/BacktraceMap.cpp
new file mode 100644
index 0000000..ca47f67
--- /dev/null
+++ b/libbacktrace/BacktraceMap.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2014 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 <ctype.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <backtrace/backtrace_constants.h>
+#include <backtrace/BacktraceMap.h>
+#include <log/log.h>
+
+#include "thread_utils.h"
+
+BacktraceMap::BacktraceMap(pid_t pid) : pid_(pid) {
+ if (pid_ < 0) {
+ pid_ = getpid();
+ }
+}
+
+BacktraceMap::~BacktraceMap() {
+}
+
+void BacktraceMap::FillIn(uintptr_t addr, backtrace_map_t* map) {
+ for (BacktraceMap::const_iterator it = begin();
+ it != end(); ++it) {
+ if (addr >= it->start && addr < it->end) {
+ *map = *it;
+ return;
+ }
+ }
+ *map = {};
+}
+
+bool BacktraceMap::ParseLine(const char* line, backtrace_map_t* map) {
+ unsigned long int start;
+ unsigned long int end;
+ char permissions[5];
+ int name_pos;
+
+#if defined(__APPLE__)
+// Mac OS vmmap(1) output:
+// __TEXT 0009f000-000a1000 [ 8K 8K] r-x/rwx SM=COW /Volumes/android/dalvik-dev/out/host/darwin-x86/bin/libcorkscrew_test\n
+// 012345678901234567890123456789012345678901234567890123456789
+// 0 1 2 3 4 5
+ if (sscanf(line, "%*21c %lx-%lx [%*13c] %3c/%*3c SM=%*3c %n",
+ &start, &end, permissions, &name_pos) != 3) {
+#else
+// Linux /proc/<pid>/maps lines:
+// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so\n
+// 012345678901234567890123456789012345678901234567890123456789
+// 0 1 2 3 4 5
+ if (sscanf(line, "%lx-%lx %4s %*x %*x:%*x %*d%n",
+ &start, &end, permissions, &name_pos) != 3) {
+#endif
+ return false;
+ }
+
+ map->start = start;
+ map->end = end;
+ map->flags = PROT_NONE;
+ if (permissions[0] == 'r') {
+ map->flags |= PROT_READ;
+ }
+ if (permissions[1] == 'w') {
+ map->flags |= PROT_WRITE;
+ }
+ if (permissions[2] == 'x') {
+ map->flags |= PROT_EXEC;
+ }
+
+ while (isspace(line[name_pos])) {
+ name_pos += 1;
+ }
+ map->name = line+name_pos;
+ if (!map->name.empty() && map->name[map->name.length()-1] == '\n') {
+ map->name.erase(map->name.length()-1);
+ }
+
+ ALOGV("Parsed map: start=%p, end=%p, flags=%x, name=%s",
+ reinterpret_cast<void*>(map->start), reinterpret_cast<void*>(map->end),
+ map->flags, map->name.c_str());
+ return true;
+}
+
+bool BacktraceMap::Build() {
+#if defined(__APPLE__)
+ char cmd[sizeof(pid_t)*3 + sizeof("vmmap -w -resident -submap -allSplitLibs -interleaved ") + 1];
+#else
+ char path[sizeof(pid_t)*3 + sizeof("/proc//maps") + 1];
+#endif
+ char line[1024];
+
+#if defined(__APPLE__)
+ // cmd is guaranteed to always be big enough to hold this string.
+ snprintf(cmd, sizeof(cmd), "vmmap -w -resident -submap -allSplitLibs -interleaved %d", pid_);
+ FILE* fp = popen(cmd, "r");
+#else
+ // path is guaranteed to always be big enough to hold this string.
+ snprintf(path, sizeof(path), "/proc/%d/maps", pid_);
+ FILE* fp = fopen(path, "r");
+#endif
+ if (fp == nullptr) {
+ return false;
+ }
+
+ while(fgets(line, sizeof(line), fp)) {
+ backtrace_map_t map;
+ if (ParseLine(line, &map)) {
+ maps_.push_back(map);
+ }
+ }
+#if defined(__APPLE__)
+ pclose(fp);
+#else
+ fclose(fp);
+#endif
+
+ return true;
+}
+
+#if defined(__APPLE__)
+// Corkscrew and libunwind don't compile on the mac, so create a generic
+// map object.
+BacktraceMap* BacktraceMap::Create(pid_t pid, bool /*uncached*/) {
+ BacktraceMap* map = new BacktraceMap(pid);
+ if (!map->Build()) {
+ delete map;
+ return nullptr;
+ }
+ return map;
+}
+#endif
diff --git a/libbacktrace/BacktracePtrace.cpp b/libbacktrace/BacktracePtrace.cpp
new file mode 100644
index 0000000..fd8b713
--- /dev/null
+++ b/libbacktrace/BacktracePtrace.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <ucontext.h>
+#include <unistd.h>
+
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+
+#include "BacktraceLog.h"
+#include "BacktracePtrace.h"
+#include "thread_utils.h"
+
+#if !defined(__APPLE__)
+static bool PtraceRead(pid_t tid, uintptr_t addr, word_t* out_value) {
+ // ptrace() returns -1 and sets errno when the operation fails.
+ // To disambiguate -1 from a valid result, we clear errno beforehand.
+ errno = 0;
+ *out_value = ptrace(PTRACE_PEEKTEXT, tid, reinterpret_cast<void*>(addr), nullptr);
+ if (*out_value == static_cast<word_t>(-1) && errno) {
+ return false;
+ }
+ return true;
+}
+#endif
+
+bool BacktracePtrace::ReadWord(uintptr_t ptr, word_t* out_value) {
+#if defined(__APPLE__)
+ BACK_LOGW("MacOS does not support reading from another pid.");
+ return false;
+#else
+ if (!VerifyReadWordArgs(ptr, out_value)) {
+ return false;
+ }
+
+ backtrace_map_t map;
+ FillInMap(ptr, &map);
+ if (!BacktraceMap::IsValid(map) || !(map.flags & PROT_READ)) {
+ return false;
+ }
+
+ return PtraceRead(Tid(), ptr, out_value);
+#endif
+}
+
+size_t BacktracePtrace::Read(uintptr_t addr, uint8_t* buffer, size_t bytes) {
+#if defined(__APPLE__)
+ BACK_LOGW("MacOS does not support reading from another pid.");
+ return 0;
+#else
+ backtrace_map_t map;
+ FillInMap(addr, &map);
+ if (!BacktraceMap::IsValid(map) || !(map.flags & PROT_READ)) {
+ return 0;
+ }
+
+ bytes = MIN(map.end - addr, bytes);
+ size_t bytes_read = 0;
+ word_t data_word;
+ size_t align_bytes = addr & (sizeof(word_t) - 1);
+ if (align_bytes != 0) {
+ if (!PtraceRead(Tid(), addr & ~(sizeof(word_t) - 1), &data_word)) {
+ return 0;
+ }
+ size_t copy_bytes = MIN(sizeof(word_t) - align_bytes, bytes);
+ memcpy(buffer, reinterpret_cast<uint8_t*>(&data_word) + align_bytes, copy_bytes);
+ addr += copy_bytes;
+ buffer += copy_bytes;
+ bytes -= copy_bytes;
+ bytes_read += copy_bytes;
+ }
+
+ size_t num_words = bytes / sizeof(word_t);
+ for (size_t i = 0; i < num_words; i++) {
+ if (!PtraceRead(Tid(), addr, &data_word)) {
+ return bytes_read;
+ }
+ memcpy(buffer, &data_word, sizeof(word_t));
+ buffer += sizeof(word_t);
+ addr += sizeof(word_t);
+ bytes_read += sizeof(word_t);
+ }
+
+ size_t left_over = bytes & (sizeof(word_t) - 1);
+ if (left_over) {
+ if (!PtraceRead(Tid(), addr, &data_word)) {
+ return bytes_read;
+ }
+ memcpy(buffer, &data_word, left_over);
+ bytes_read += left_over;
+ }
+ return bytes_read;
+#endif
+}
diff --git a/libbacktrace/BacktracePtrace.h b/libbacktrace/BacktracePtrace.h
new file mode 100644
index 0000000..1d49811
--- /dev/null
+++ b/libbacktrace/BacktracePtrace.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBBACKTRACE_BACKTRACE_PTRACE_H
+#define _LIBBACKTRACE_BACKTRACE_PTRACE_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <backtrace/Backtrace.h>
+
+class BacktraceMap;
+
+class BacktracePtrace : public Backtrace {
+public:
+ BacktracePtrace(pid_t pid, pid_t tid, BacktraceMap* map) : Backtrace(pid, tid, map) {}
+ virtual ~BacktracePtrace() {}
+
+ size_t Read(uintptr_t addr, uint8_t* buffer, size_t bytes);
+
+ bool ReadWord(uintptr_t ptr, word_t* out_value);
+};
+
+#endif // _LIBBACKTRACE_BACKTRACE_PTRACE_H
diff --git a/libbacktrace/GetPss.cpp b/libbacktrace/GetPss.cpp
new file mode 100644
index 0000000..09a721d
--- /dev/null
+++ b/libbacktrace/GetPss.cpp
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2014 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 <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+// This is an extremely simplified version of libpagemap.
+
+#define _BITS(x, offset, bits) (((x) >> offset) & ((1LL << (bits)) - 1))
+
+#define PAGEMAP_PRESENT(x) (_BITS(x, 63, 1))
+#define PAGEMAP_SWAPPED(x) (_BITS(x, 62, 1))
+#define PAGEMAP_SHIFT(x) (_BITS(x, 55, 6))
+#define PAGEMAP_PFN(x) (_BITS(x, 0, 55))
+#define PAGEMAP_SWAP_OFFSET(x) (_BITS(x, 5, 50))
+#define PAGEMAP_SWAP_TYPE(x) (_BITS(x, 0, 5))
+
+static bool ReadData(int fd, unsigned long place, uint64_t *data) {
+ if (lseek(fd, place * sizeof(uint64_t), SEEK_SET) < 0) {
+ return false;
+ }
+ if (read(fd, (void*)data, sizeof(uint64_t)) != (ssize_t)sizeof(uint64_t)) {
+ return false;
+ }
+ return true;
+}
+
+size_t GetPssBytes() {
+ FILE* maps = fopen("/proc/self/maps", "r");
+ if (maps == nullptr) {
+ return 0;
+ }
+
+ int pagecount_fd = open("/proc/kpagecount", O_RDONLY);
+ if (pagecount_fd == -1) {
+ fclose(maps);
+ return 0;
+ }
+
+ int pagemap_fd = open("/proc/self/pagemap", O_RDONLY);
+ if (pagemap_fd == -1) {
+ fclose(maps);
+ close(pagecount_fd);
+ return 0;
+ }
+
+ char line[4096];
+ size_t total_pss = 0;
+ int pagesize = getpagesize();
+ while (fgets(line, sizeof(line), maps)) {
+ uintptr_t start, end;
+ if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " ", &start, &end) != 2) {
+ total_pss = 0;
+ break;
+ }
+ for (size_t page = start/pagesize; page < end/pagesize; page++) {
+ uint64_t data;
+ if (ReadData(pagemap_fd, page, &data)) {
+ if (PAGEMAP_PRESENT(data) && !PAGEMAP_SWAPPED(data)) {
+ uint64_t count;
+ if (ReadData(pagecount_fd, PAGEMAP_PFN(data), &count)) {
+ total_pss += (count >= 1) ? pagesize / count : 0;
+ }
+ }
+ }
+ }
+ }
+
+ fclose(maps);
+
+ close(pagecount_fd);
+ close(pagemap_fd);
+
+ return total_pss;
+}
diff --git a/libbacktrace/GetPss.h b/libbacktrace/GetPss.h
new file mode 100644
index 0000000..787c33d
--- /dev/null
+++ b/libbacktrace/GetPss.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBBACKTRACE_GET_PSS_H
+#define _LIBBACKTRACE_GET_PSS_H
+
+size_t GetPssBytes();
+
+#endif // _LIBBACKTRACE_GET_PSS_H
diff --git a/libbacktrace/ThreadEntry.cpp b/libbacktrace/ThreadEntry.cpp
new file mode 100644
index 0000000..084c1aa
--- /dev/null
+++ b/libbacktrace/ThreadEntry.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <pthread.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+#include <time.h>
+#include <ucontext.h>
+
+#include "BacktraceLog.h"
+#include "ThreadEntry.h"
+
+// Initialize static member variables.
+ThreadEntry* ThreadEntry::list_ = nullptr;
+pthread_mutex_t ThreadEntry::list_mutex_ = PTHREAD_MUTEX_INITIALIZER;
+
+// Assumes that ThreadEntry::list_mutex_ has already been locked before
+// creating a ThreadEntry object.
+ThreadEntry::ThreadEntry(pid_t pid, pid_t tid)
+ : pid_(pid), tid_(tid), ref_count_(1), mutex_(PTHREAD_MUTEX_INITIALIZER),
+ wait_mutex_(PTHREAD_MUTEX_INITIALIZER), wait_value_(0),
+ next_(ThreadEntry::list_), prev_(nullptr) {
+ pthread_condattr_t attr;
+ pthread_condattr_init(&attr);
+ pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
+ pthread_cond_init(&wait_cond_, &attr);
+
+ // Add ourselves to the list.
+ if (ThreadEntry::list_) {
+ ThreadEntry::list_->prev_ = this;
+ }
+ ThreadEntry::list_ = this;
+}
+
+ThreadEntry* ThreadEntry::Get(pid_t pid, pid_t tid, bool create) {
+ pthread_mutex_lock(&ThreadEntry::list_mutex_);
+ ThreadEntry* entry = list_;
+ while (entry != nullptr) {
+ if (entry->Match(pid, tid)) {
+ break;
+ }
+ entry = entry->next_;
+ }
+
+ if (!entry) {
+ if (create) {
+ entry = new ThreadEntry(pid, tid);
+ }
+ } else {
+ entry->ref_count_++;
+ }
+ pthread_mutex_unlock(&ThreadEntry::list_mutex_);
+
+ return entry;
+}
+
+void ThreadEntry::Remove(ThreadEntry* entry) {
+ entry->Unlock();
+
+ pthread_mutex_lock(&ThreadEntry::list_mutex_);
+ if (--entry->ref_count_ == 0) {
+ delete entry;
+ }
+ pthread_mutex_unlock(&ThreadEntry::list_mutex_);
+}
+
+// Assumes that ThreadEntry::list_mutex_ has already been locked before
+// deleting a ThreadEntry object.
+ThreadEntry::~ThreadEntry() {
+ if (list_ == this) {
+ list_ = next_;
+ } else {
+ if (next_) {
+ next_->prev_ = prev_;
+ }
+ prev_->next_ = next_;
+ }
+
+ next_ = nullptr;
+ prev_ = nullptr;
+
+ pthread_cond_destroy(&wait_cond_);
+}
+
+bool ThreadEntry::Wait(int value) {
+ timespec ts;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ ts.tv_sec += 5;
+
+ bool wait_completed = true;
+ pthread_mutex_lock(&wait_mutex_);
+ while (wait_value_ != value) {
+ int ret = pthread_cond_timedwait(&wait_cond_, &wait_mutex_, &ts);
+ if (ret != 0) {
+ BACK_LOGW("pthread_cond_timedwait for value %d failed: %s", value, strerror(ret));
+ wait_completed = false;
+ break;
+ }
+ }
+ pthread_mutex_unlock(&wait_mutex_);
+
+ return wait_completed;
+}
+
+void ThreadEntry::Wake() {
+ pthread_mutex_lock(&wait_mutex_);
+ wait_value_++;
+ pthread_mutex_unlock(&wait_mutex_);
+
+ pthread_cond_signal(&wait_cond_);
+}
+
+void ThreadEntry::CopyUcontextFromSigcontext(void* sigcontext) {
+ ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(sigcontext);
+ // The only thing the unwinder cares about is the mcontext data.
+ memcpy(&ucontext_.uc_mcontext, &ucontext->uc_mcontext, sizeof(ucontext->uc_mcontext));
+}
diff --git a/libbacktrace/ThreadEntry.h b/libbacktrace/ThreadEntry.h
new file mode 100644
index 0000000..11924a3
--- /dev/null
+++ b/libbacktrace/ThreadEntry.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBBACKTRACE_THREAD_ENTRY_H
+#define _LIBBACKTRACE_THREAD_ENTRY_H
+
+#include <pthread.h>
+#include <sys/types.h>
+#include <ucontext.h>
+
+class ThreadEntry {
+public:
+ static ThreadEntry* Get(pid_t pid, pid_t tid, bool create = true);
+
+ static void Remove(ThreadEntry* entry);
+
+ void Wake();
+
+ bool Wait(int);
+
+ void CopyUcontextFromSigcontext(void*);
+
+ inline void Lock() {
+ pthread_mutex_lock(&mutex_);
+
+ // Always reset the wait value since this could be the first or nth
+ // time this entry is locked.
+ wait_value_ = 0;
+ }
+
+ inline void Unlock() {
+ pthread_mutex_unlock(&mutex_);
+ }
+
+ inline ucontext_t* GetUcontext() { return &ucontext_; }
+
+private:
+ ThreadEntry(pid_t pid, pid_t tid);
+ ~ThreadEntry();
+
+ bool Match(pid_t chk_pid, pid_t chk_tid) { return (chk_pid == pid_ && chk_tid == tid_); }
+
+ pid_t pid_;
+ pid_t tid_;
+ int ref_count_;
+ pthread_mutex_t mutex_;
+ pthread_mutex_t wait_mutex_;
+ pthread_cond_t wait_cond_;
+ int wait_value_;
+ ThreadEntry* next_;
+ ThreadEntry* prev_;
+ ucontext_t ucontext_;
+
+ static ThreadEntry* list_;
+ static pthread_mutex_t list_mutex_;
+};
+
+#endif // _LIBBACKTRACE_THREAD_ENTRY_H
diff --git a/libbacktrace/UnwindCurrent.cpp b/libbacktrace/UnwindCurrent.cpp
new file mode 100644
index 0000000..67e583f
--- /dev/null
+++ b/libbacktrace/UnwindCurrent.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <ucontext.h>
+
+#include <memory>
+#include <string>
+
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+
+#include <backtrace/Backtrace.h>
+
+#include "BacktraceLog.h"
+#include "UnwindCurrent.h"
+
+std::string UnwindCurrent::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) {
+ *offset = 0;
+ char buf[512];
+ unw_word_t value;
+ if (unw_get_proc_name_by_ip(unw_local_addr_space, pc, buf, sizeof(buf),
+ &value, &context_) >= 0 && buf[0] != '\0') {
+ *offset = static_cast<uintptr_t>(value);
+ return buf;
+ }
+ return "";
+}
+
+void UnwindCurrent::GetUnwContextFromUcontext(const ucontext_t* ucontext) {
+ unw_tdep_context_t* unw_context = reinterpret_cast<unw_tdep_context_t*>(&context_);
+
+#if defined(__arm__)
+ unw_context->regs[0] = ucontext->uc_mcontext.arm_r0;
+ unw_context->regs[1] = ucontext->uc_mcontext.arm_r1;
+ unw_context->regs[2] = ucontext->uc_mcontext.arm_r2;
+ unw_context->regs[3] = ucontext->uc_mcontext.arm_r3;
+ unw_context->regs[4] = ucontext->uc_mcontext.arm_r4;
+ unw_context->regs[5] = ucontext->uc_mcontext.arm_r5;
+ unw_context->regs[6] = ucontext->uc_mcontext.arm_r6;
+ unw_context->regs[7] = ucontext->uc_mcontext.arm_r7;
+ unw_context->regs[8] = ucontext->uc_mcontext.arm_r8;
+ unw_context->regs[9] = ucontext->uc_mcontext.arm_r9;
+ unw_context->regs[10] = ucontext->uc_mcontext.arm_r10;
+ unw_context->regs[11] = ucontext->uc_mcontext.arm_fp;
+ unw_context->regs[12] = ucontext->uc_mcontext.arm_ip;
+ unw_context->regs[13] = ucontext->uc_mcontext.arm_sp;
+ unw_context->regs[14] = ucontext->uc_mcontext.arm_lr;
+ unw_context->regs[15] = ucontext->uc_mcontext.arm_pc;
+#else
+ unw_context->uc_mcontext = ucontext->uc_mcontext;
+#endif
+}
+
+bool UnwindCurrent::UnwindFromContext(size_t num_ignore_frames, ucontext_t* ucontext) {
+ if (ucontext == nullptr) {
+ int ret = unw_getcontext(&context_);
+ if (ret < 0) {
+ BACK_LOGW("unw_getcontext failed %d", ret);
+ return false;
+ }
+ } else {
+ GetUnwContextFromUcontext(ucontext);
+ }
+
+ // The cursor structure is pretty large, do not put it on the stack.
+ std::unique_ptr<unw_cursor_t> cursor(new unw_cursor_t);
+ int ret = unw_init_local(cursor.get(), &context_);
+ if (ret < 0) {
+ BACK_LOGW("unw_init_local failed %d", ret);
+ return false;
+ }
+
+ size_t num_frames = 0;
+ do {
+ unw_word_t pc;
+ ret = unw_get_reg(cursor.get(), UNW_REG_IP, &pc);
+ if (ret < 0) {
+ BACK_LOGW("Failed to read IP %d", ret);
+ break;
+ }
+ unw_word_t sp;
+ ret = unw_get_reg(cursor.get(), UNW_REG_SP, &sp);
+ if (ret < 0) {
+ BACK_LOGW("Failed to read SP %d", ret);
+ break;
+ }
+
+ frames_.resize(num_frames+1);
+ backtrace_frame_data_t* frame = &frames_.at(num_frames);
+ frame->num = num_frames;
+ frame->pc = static_cast<uintptr_t>(pc);
+ frame->sp = static_cast<uintptr_t>(sp);
+ frame->stack_size = 0;
+
+ FillInMap(frame->pc, &frame->map);
+ // Check to see if we should skip this frame because it's coming
+ // from within the library, and we are doing a local unwind.
+ if (ucontext != nullptr || num_frames != 0 || !DiscardFrame(*frame)) {
+ if (num_ignore_frames == 0) {
+ // GetFunctionName is an expensive call, only do it if we are
+ // keeping the frame.
+ frame->func_name = GetFunctionName(frame->pc, &frame->func_offset);
+ if (num_frames > 0) {
+ // Set the stack size for the previous frame.
+ backtrace_frame_data_t* prev = &frames_.at(num_frames-1);
+ prev->stack_size = frame->sp - prev->sp;
+ }
+ num_frames++;
+ } else {
+ num_ignore_frames--;
+ }
+ }
+ ret = unw_step (cursor.get());
+ } while (ret > 0 && num_frames < MAX_BACKTRACE_FRAMES);
+
+ return true;
+}
diff --git a/libbacktrace/UnwindCurrent.h b/libbacktrace/UnwindCurrent.h
new file mode 100644
index 0000000..3023996
--- /dev/null
+++ b/libbacktrace/UnwindCurrent.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBBACKTRACE_UNWIND_CURRENT_H
+#define _LIBBACKTRACE_UNWIND_CURRENT_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <ucontext.h>
+
+#include <string>
+
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+
+#include "BacktraceCurrent.h"
+
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+
+class UnwindCurrent : public BacktraceCurrent {
+public:
+ UnwindCurrent(pid_t pid, pid_t tid, BacktraceMap* map) : BacktraceCurrent(pid, tid, map) {}
+ virtual ~UnwindCurrent() {}
+
+ std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) override;
+
+private:
+ void GetUnwContextFromUcontext(const ucontext_t* ucontext);
+
+ bool UnwindFromContext(size_t num_ignore_frames, ucontext_t* ucontext) override;
+
+ unw_context_t context_;
+};
+
+#endif // _LIBBACKTRACE_UNWIND_CURRENT_H
diff --git a/libbacktrace/UnwindMap.cpp b/libbacktrace/UnwindMap.cpp
new file mode 100644
index 0000000..879fea5
--- /dev/null
+++ b/libbacktrace/UnwindMap.cpp
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2014 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 <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <backtrace/BacktraceMap.h>
+
+#include <libunwind.h>
+
+#include "BacktraceLog.h"
+#include "UnwindMap.h"
+
+//-------------------------------------------------------------------------
+// libunwind has a single shared address space for the current process
+// aka local. If multiple maps are created for the current pid, then
+// only update the local address space once, and keep a reference count
+// of maps using the same map cursor.
+//-------------------------------------------------------------------------
+UnwindMap::UnwindMap(pid_t pid) : BacktraceMap(pid) {
+}
+
+UnwindMap::~UnwindMap() {
+ unw_map_cursor_destroy(&map_cursor_);
+ unw_map_cursor_clear(&map_cursor_);
+}
+
+bool UnwindMap::GenerateMap() {
+ // Use the map_cursor information to construct the BacktraceMap data
+ // rather than reparsing /proc/self/maps.
+ unw_map_cursor_reset(&map_cursor_);
+
+ unw_map_t unw_map;
+ while (unw_map_cursor_get_next(&map_cursor_, &unw_map)) {
+ backtrace_map_t map;
+
+ map.start = unw_map.start;
+ map.end = unw_map.end;
+ map.offset = unw_map.offset;
+ map.load_base = unw_map.load_base;
+ map.flags = unw_map.flags;
+ map.name = unw_map.path;
+
+ // The maps are in descending order, but we want them in ascending order.
+ maps_.push_front(map);
+ }
+
+ return true;
+}
+
+bool UnwindMap::Build() {
+ return (unw_map_cursor_create(&map_cursor_, pid_) == 0) && GenerateMap();
+}
+
+UnwindMapLocal::UnwindMapLocal() : UnwindMap(getpid()), map_created_(false) {
+}
+
+UnwindMapLocal::~UnwindMapLocal() {
+ if (map_created_) {
+ unw_map_local_destroy();
+ unw_map_cursor_clear(&map_cursor_);
+ }
+}
+
+bool UnwindMapLocal::GenerateMap() {
+ // It's possible for the map to be regenerated while this loop is occurring.
+ // If that happens, get the map again, but only try at most three times
+ // before giving up.
+ for (int i = 0; i < 3; i++) {
+ maps_.clear();
+
+ unw_map_local_cursor_get(&map_cursor_);
+
+ unw_map_t unw_map;
+ int ret;
+ while ((ret = unw_map_local_cursor_get_next(&map_cursor_, &unw_map)) > 0) {
+ backtrace_map_t map;
+
+ map.start = unw_map.start;
+ map.end = unw_map.end;
+ map.offset = unw_map.offset;
+ map.load_base = unw_map.load_base;
+ map.flags = unw_map.flags;
+ map.name = unw_map.path;
+
+ free(unw_map.path);
+
+ // The maps are in descending order, but we want them in ascending order.
+ maps_.push_front(map);
+ }
+ // Check to see if the map changed while getting the data.
+ if (ret != -UNW_EINVAL) {
+ return true;
+ }
+ }
+
+ BACK_LOGW("Unable to generate the map.");
+ return false;
+}
+
+bool UnwindMapLocal::Build() {
+ return (map_created_ = (unw_map_local_create() == 0)) && GenerateMap();;
+}
+
+void UnwindMapLocal::FillIn(uintptr_t addr, backtrace_map_t* map) {
+ BacktraceMap::FillIn(addr, map);
+ if (!IsValid(*map)) {
+ // Check to see if the underlying map changed and regenerate the map
+ // if it did.
+ if (unw_map_local_cursor_valid(&map_cursor_) < 0) {
+ if (GenerateMap()) {
+ BacktraceMap::FillIn(addr, map);
+ }
+ }
+ }
+}
+
+//-------------------------------------------------------------------------
+// BacktraceMap create function.
+//-------------------------------------------------------------------------
+BacktraceMap* BacktraceMap::Create(pid_t pid, bool uncached) {
+ BacktraceMap* map;
+
+ if (uncached) {
+ // Force use of the base class to parse the maps when this call is made.
+ map = new BacktraceMap(pid);
+ } else if (pid == getpid()) {
+ map = new UnwindMapLocal();
+ } else {
+ map = new UnwindMap(pid);
+ }
+ if (!map->Build()) {
+ delete map;
+ return nullptr;
+ }
+ return map;
+}
diff --git a/libbacktrace/UnwindMap.h b/libbacktrace/UnwindMap.h
new file mode 100644
index 0000000..e292016
--- /dev/null
+++ b/libbacktrace/UnwindMap.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBBACKTRACE_UNWIND_MAP_H
+#define _LIBBACKTRACE_UNWIND_MAP_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <backtrace/BacktraceMap.h>
+
+// The unw_map_cursor_t structure is different depending on whether it is
+// the local or remote version. In order to get the correct version, include
+// libunwind.h first then this header.
+
+class UnwindMap : public BacktraceMap {
+public:
+ UnwindMap(pid_t pid);
+ virtual ~UnwindMap();
+
+ virtual bool Build();
+
+ unw_map_cursor_t* GetMapCursor() { return &map_cursor_; }
+
+protected:
+ virtual bool GenerateMap();
+
+ unw_map_cursor_t map_cursor_;
+};
+
+class UnwindMapLocal : public UnwindMap {
+public:
+ UnwindMapLocal();
+ virtual ~UnwindMapLocal();
+
+ virtual bool Build();
+
+ virtual void FillIn(uintptr_t addr, backtrace_map_t* map);
+
+protected:
+ virtual bool GenerateMap();
+
+ bool map_created_;
+};
+
+#endif // _LIBBACKTRACE_UNWIND_MAP_H
diff --git a/libbacktrace/UnwindPtrace.cpp b/libbacktrace/UnwindPtrace.cpp
new file mode 100644
index 0000000..07c2430
--- /dev/null
+++ b/libbacktrace/UnwindPtrace.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <ucontext.h>
+
+#include <libunwind.h>
+#include <libunwind-ptrace.h>
+
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+
+#include "BacktraceLog.h"
+#include "UnwindMap.h"
+#include "UnwindPtrace.h"
+
+UnwindPtrace::UnwindPtrace(pid_t pid, pid_t tid, BacktraceMap* map)
+ : BacktracePtrace(pid, tid, map), addr_space_(nullptr), upt_info_(nullptr) {
+}
+
+UnwindPtrace::~UnwindPtrace() {
+ if (upt_info_) {
+ _UPT_destroy(upt_info_);
+ upt_info_ = nullptr;
+ }
+ if (addr_space_) {
+ // Remove the map from the address space before destroying it.
+ // It will be freed in the UnwindMap destructor.
+ unw_map_set(addr_space_, nullptr);
+
+ unw_destroy_addr_space(addr_space_);
+ addr_space_ = nullptr;
+ }
+}
+
+bool UnwindPtrace::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) {
+ if (GetMap() == nullptr) {
+ // Without a map object, we can't do anything.
+ return false;
+ }
+
+ if (ucontext) {
+ BACK_LOGW("Unwinding from a specified context not supported yet.");
+ return false;
+ }
+
+ addr_space_ = unw_create_addr_space(&_UPT_accessors, 0);
+ if (!addr_space_) {
+ BACK_LOGW("unw_create_addr_space failed.");
+ return false;
+ }
+
+ UnwindMap* map = static_cast<UnwindMap*>(GetMap());
+ unw_map_set(addr_space_, map->GetMapCursor());
+
+ upt_info_ = reinterpret_cast<struct UPT_info*>(_UPT_create(Tid()));
+ if (!upt_info_) {
+ BACK_LOGW("Failed to create upt info.");
+ return false;
+ }
+
+ unw_cursor_t cursor;
+ int ret = unw_init_remote(&cursor, addr_space_, upt_info_);
+ if (ret < 0) {
+ BACK_LOGW("unw_init_remote failed %d", ret);
+ return false;
+ }
+
+ size_t num_frames = 0;
+ do {
+ unw_word_t pc;
+ ret = unw_get_reg(&cursor, UNW_REG_IP, &pc);
+ if (ret < 0) {
+ BACK_LOGW("Failed to read IP %d", ret);
+ break;
+ }
+ unw_word_t sp;
+ ret = unw_get_reg(&cursor, UNW_REG_SP, &sp);
+ if (ret < 0) {
+ BACK_LOGW("Failed to read SP %d", ret);
+ break;
+ }
+
+ if (num_ignore_frames == 0) {
+ frames_.resize(num_frames+1);
+ backtrace_frame_data_t* frame = &frames_.at(num_frames);
+ frame->num = num_frames;
+ frame->pc = static_cast<uintptr_t>(pc);
+ frame->sp = static_cast<uintptr_t>(sp);
+ frame->stack_size = 0;
+
+ if (num_frames > 0) {
+ backtrace_frame_data_t* prev = &frames_.at(num_frames-1);
+ prev->stack_size = frame->sp - prev->sp;
+ }
+
+ frame->func_name = GetFunctionName(frame->pc, &frame->func_offset);
+
+ FillInMap(frame->pc, &frame->map);
+
+ num_frames++;
+ } else {
+ num_ignore_frames--;
+ }
+ ret = unw_step (&cursor);
+ } while (ret > 0 && num_frames < MAX_BACKTRACE_FRAMES);
+
+ return true;
+}
+
+std::string UnwindPtrace::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) {
+ *offset = 0;
+ char buf[512];
+ unw_word_t value;
+ if (unw_get_proc_name_by_ip(addr_space_, pc, buf, sizeof(buf), &value,
+ upt_info_) >= 0 && buf[0] != '\0') {
+ *offset = static_cast<uintptr_t>(value);
+ return buf;
+ }
+ return "";
+}
diff --git a/libbacktrace/UnwindPtrace.h b/libbacktrace/UnwindPtrace.h
new file mode 100644
index 0000000..ab04abf
--- /dev/null
+++ b/libbacktrace/UnwindPtrace.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBBACKTRACE_UNWIND_PTRACE_H
+#define _LIBBACKTRACE_UNWIND_PTRACE_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <string>
+
+#ifdef UNW_LOCAL_ONLY
+#undef UNW_LOCAL_ONLY
+#endif
+#include <libunwind.h>
+
+#include "BacktracePtrace.h"
+
+class UnwindPtrace : public BacktracePtrace {
+public:
+ UnwindPtrace(pid_t pid, pid_t tid, BacktraceMap* map);
+ virtual ~UnwindPtrace();
+
+ bool Unwind(size_t num_ignore_frames, ucontext_t* ucontext) override;
+
+ std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) override;
+
+private:
+ unw_addr_space_t addr_space_;
+ struct UPT_info* upt_info_;
+};
+
+#endif // _LIBBACKTRACE_UNWIND_PTRACE_H
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
new file mode 100644
index 0000000..6bd7529
--- /dev/null
+++ b/libbacktrace/backtrace_test.cpp
@@ -0,0 +1,1442 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _GNU_SOURCE 1
+#include <dirent.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ptrace.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <list>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+
+#include <base/stringprintf.h>
+#include <cutils/atomic.h>
+#include <cutils/threads.h>
+
+#include <gtest/gtest.h>
+
+// For the THREAD_SIGNAL definition.
+#include "BacktraceCurrent.h"
+#include "thread_utils.h"
+
+// Number of microseconds per milliseconds.
+#define US_PER_MSEC 1000
+
+// Number of nanoseconds in a second.
+#define NS_PER_SEC 1000000000ULL
+
+// Number of simultaneous dumping operations to perform.
+#define NUM_THREADS 40
+
+// Number of simultaneous threads running in our forked process.
+#define NUM_PTRACE_THREADS 5
+
+struct thread_t {
+ pid_t tid;
+ int32_t state;
+ pthread_t threadId;
+ void* data;
+};
+
+struct dump_thread_t {
+ thread_t thread;
+ Backtrace* backtrace;
+ int32_t* now;
+ int32_t done;
+};
+
+extern "C" {
+// Prototypes for functions in the test library.
+int test_level_one(int, int, int, int, void (*)(void*), void*);
+
+int test_recursive_call(int, void (*)(void*), void*);
+}
+
+uint64_t NanoTime() {
+ struct timespec t = { 0, 0 };
+ clock_gettime(CLOCK_MONOTONIC, &t);
+ return static_cast<uint64_t>(t.tv_sec * NS_PER_SEC + t.tv_nsec);
+}
+
+std::string DumpFrames(Backtrace* backtrace) {
+ if (backtrace->NumFrames() == 0) {
+ return " No frames to dump.\n";
+ }
+
+ std::string frame;
+ for (size_t i = 0; i < backtrace->NumFrames(); i++) {
+ frame += " " + backtrace->FormatFrameData(i) + '\n';
+ }
+ return frame;
+}
+
+void WaitForStop(pid_t pid) {
+ uint64_t start = NanoTime();
+
+ siginfo_t si;
+ while (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) < 0 && (errno == EINTR || errno == ESRCH)) {
+ if ((NanoTime() - start) > NS_PER_SEC) {
+ printf("The process did not get to a stopping point in 1 second.\n");
+ break;
+ }
+ usleep(US_PER_MSEC);
+ }
+}
+
+bool ReadyLevelBacktrace(Backtrace* backtrace) {
+ // See if test_level_four is in the backtrace.
+ bool found = false;
+ for (Backtrace::const_iterator it = backtrace->begin(); it != backtrace->end(); ++it) {
+ if (it->func_name == "test_level_four") {
+ found = true;
+ break;
+ }
+ }
+
+ return found;
+}
+
+void VerifyLevelDump(Backtrace* backtrace) {
+ ASSERT_GT(backtrace->NumFrames(), static_cast<size_t>(0))
+ << DumpFrames(backtrace);
+ ASSERT_LT(backtrace->NumFrames(), static_cast<size_t>(MAX_BACKTRACE_FRAMES))
+ << DumpFrames(backtrace);
+
+ // Look through the frames starting at the highest to find the
+ // frame we want.
+ size_t frame_num = 0;
+ for (size_t i = backtrace->NumFrames()-1; i > 2; i--) {
+ if (backtrace->GetFrame(i)->func_name == "test_level_one") {
+ frame_num = i;
+ break;
+ }
+ }
+ ASSERT_LT(static_cast<size_t>(0), frame_num) << DumpFrames(backtrace);
+ ASSERT_LE(static_cast<size_t>(3), frame_num) << DumpFrames(backtrace);
+
+ ASSERT_EQ(backtrace->GetFrame(frame_num)->func_name, "test_level_one")
+ << DumpFrames(backtrace);
+ ASSERT_EQ(backtrace->GetFrame(frame_num-1)->func_name, "test_level_two")
+ << DumpFrames(backtrace);
+ ASSERT_EQ(backtrace->GetFrame(frame_num-2)->func_name, "test_level_three")
+ << DumpFrames(backtrace);
+ ASSERT_EQ(backtrace->GetFrame(frame_num-3)->func_name, "test_level_four")
+ << DumpFrames(backtrace);
+}
+
+void VerifyLevelBacktrace(void*) {
+ std::unique_ptr<Backtrace> backtrace(
+ Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
+ ASSERT_TRUE(backtrace.get() != nullptr);
+ ASSERT_TRUE(backtrace->Unwind(0));
+
+ VerifyLevelDump(backtrace.get());
+}
+
+bool ReadyMaxBacktrace(Backtrace* backtrace) {
+ return (backtrace->NumFrames() == MAX_BACKTRACE_FRAMES);
+}
+
+void VerifyMaxDump(Backtrace* backtrace) {
+ ASSERT_EQ(backtrace->NumFrames(), static_cast<size_t>(MAX_BACKTRACE_FRAMES))
+ << DumpFrames(backtrace);
+ // Verify that the last frame is our recursive call.
+ ASSERT_EQ(backtrace->GetFrame(MAX_BACKTRACE_FRAMES-1)->func_name, "test_recursive_call")
+ << DumpFrames(backtrace);
+}
+
+void VerifyMaxBacktrace(void*) {
+ std::unique_ptr<Backtrace> backtrace(
+ Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
+ ASSERT_TRUE(backtrace.get() != nullptr);
+ ASSERT_TRUE(backtrace->Unwind(0));
+
+ VerifyMaxDump(backtrace.get());
+}
+
+void ThreadSetState(void* data) {
+ thread_t* thread = reinterpret_cast<thread_t*>(data);
+ android_atomic_acquire_store(1, &thread->state);
+ volatile int i = 0;
+ while (thread->state) {
+ i++;
+ }
+}
+
+void VerifyThreadTest(pid_t tid, void (*VerifyFunc)(Backtrace*)) {
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), tid));
+ ASSERT_TRUE(backtrace.get() != nullptr);
+ ASSERT_TRUE(backtrace->Unwind(0));
+
+ VerifyFunc(backtrace.get());
+}
+
+bool WaitForNonZero(int32_t* value, uint64_t seconds) {
+ uint64_t start = NanoTime();
+ do {
+ if (android_atomic_acquire_load(value)) {
+ return true;
+ }
+ } while ((NanoTime() - start) < seconds * NS_PER_SEC);
+ return false;
+}
+
+TEST(libbacktrace, local_no_unwind_frames) {
+ // Verify that a local unwind does not include any frames within
+ // libunwind or libbacktrace.
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), getpid()));
+ ASSERT_TRUE(backtrace.get() != nullptr);
+ ASSERT_TRUE(backtrace->Unwind(0));
+
+ ASSERT_TRUE(backtrace->NumFrames() != 0);
+ for (const auto& frame : *backtrace ) {
+ if (BacktraceMap::IsValid(frame.map)) {
+ const std::string name = basename(frame.map.name.c_str());
+ ASSERT_TRUE(name != "libunwind.so" && name != "libbacktrace.so")
+ << DumpFrames(backtrace.get());
+ }
+ break;
+ }
+}
+
+TEST(libbacktrace, local_trace) {
+ ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelBacktrace, nullptr), 0);
+}
+
+void VerifyIgnoreFrames(
+ Backtrace* bt_all, Backtrace* bt_ign1,
+ Backtrace* bt_ign2, const char* cur_proc) {
+ EXPECT_EQ(bt_all->NumFrames(), bt_ign1->NumFrames() + 1)
+ << "All backtrace:\n" << DumpFrames(bt_all) << "Ignore 1 backtrace:\n" << DumpFrames(bt_ign1);
+ EXPECT_EQ(bt_all->NumFrames(), bt_ign2->NumFrames() + 2)
+ << "All backtrace:\n" << DumpFrames(bt_all) << "Ignore 2 backtrace:\n" << DumpFrames(bt_ign2);
+
+ // Check all of the frames are the same > the current frame.
+ bool check = (cur_proc == nullptr);
+ for (size_t i = 0; i < bt_ign2->NumFrames(); i++) {
+ if (check) {
+ EXPECT_EQ(bt_ign2->GetFrame(i)->pc, bt_ign1->GetFrame(i+1)->pc);
+ EXPECT_EQ(bt_ign2->GetFrame(i)->sp, bt_ign1->GetFrame(i+1)->sp);
+ EXPECT_EQ(bt_ign2->GetFrame(i)->stack_size, bt_ign1->GetFrame(i+1)->stack_size);
+
+ EXPECT_EQ(bt_ign2->GetFrame(i)->pc, bt_all->GetFrame(i+2)->pc);
+ EXPECT_EQ(bt_ign2->GetFrame(i)->sp, bt_all->GetFrame(i+2)->sp);
+ EXPECT_EQ(bt_ign2->GetFrame(i)->stack_size, bt_all->GetFrame(i+2)->stack_size);
+ }
+ if (!check && bt_ign2->GetFrame(i)->func_name == cur_proc) {
+ check = true;
+ }
+ }
+}
+
+void VerifyLevelIgnoreFrames(void*) {
+ std::unique_ptr<Backtrace> all(
+ Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
+ ASSERT_TRUE(all.get() != nullptr);
+ ASSERT_TRUE(all->Unwind(0));
+
+ std::unique_ptr<Backtrace> ign1(
+ Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
+ ASSERT_TRUE(ign1.get() != nullptr);
+ ASSERT_TRUE(ign1->Unwind(1));
+
+ std::unique_ptr<Backtrace> ign2(
+ Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
+ ASSERT_TRUE(ign2.get() != nullptr);
+ ASSERT_TRUE(ign2->Unwind(2));
+
+ VerifyIgnoreFrames(all.get(), ign1.get(), ign2.get(), "VerifyLevelIgnoreFrames");
+}
+
+TEST(libbacktrace, local_trace_ignore_frames) {
+ ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelIgnoreFrames, nullptr), 0);
+}
+
+TEST(libbacktrace, local_max_trace) {
+ ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, VerifyMaxBacktrace, nullptr), 0);
+}
+
+void VerifyProcTest(pid_t pid, pid_t tid, bool share_map,
+ bool (*ReadyFunc)(Backtrace*),
+ void (*VerifyFunc)(Backtrace*)) {
+ pid_t ptrace_tid;
+ if (tid < 0) {
+ ptrace_tid = pid;
+ } else {
+ ptrace_tid = tid;
+ }
+ uint64_t start = NanoTime();
+ bool verified = false;
+ std::string last_dump;
+ do {
+ usleep(US_PER_MSEC);
+ if (ptrace(PTRACE_ATTACH, ptrace_tid, 0, 0) == 0) {
+ // Wait for the process to get to a stopping point.
+ WaitForStop(ptrace_tid);
+
+ std::unique_ptr<BacktraceMap> map;
+ if (share_map) {
+ map.reset(BacktraceMap::Create(pid));
+ }
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid, map.get()));
+ ASSERT_TRUE(backtrace.get() != nullptr);
+ ASSERT_TRUE(backtrace->Unwind(0));
+ if (ReadyFunc(backtrace.get())) {
+ VerifyFunc(backtrace.get());
+ verified = true;
+ } else {
+ last_dump = DumpFrames(backtrace.get());
+ }
+
+ ASSERT_TRUE(ptrace(PTRACE_DETACH, ptrace_tid, 0, 0) == 0);
+ }
+ // If 5 seconds have passed, then we are done.
+ } while (!verified && (NanoTime() - start) <= 5 * NS_PER_SEC);
+ ASSERT_TRUE(verified) << "Last backtrace:\n" << last_dump;
+}
+
+TEST(libbacktrace, ptrace_trace) {
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ ASSERT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
+ _exit(1);
+ }
+ VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyLevelBacktrace, VerifyLevelDump);
+
+ kill(pid, SIGKILL);
+ int status;
+ ASSERT_EQ(waitpid(pid, &status, 0), pid);
+}
+
+TEST(libbacktrace, ptrace_trace_shared_map) {
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ ASSERT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
+ _exit(1);
+ }
+
+ VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, true, ReadyLevelBacktrace, VerifyLevelDump);
+
+ kill(pid, SIGKILL);
+ int status;
+ ASSERT_EQ(waitpid(pid, &status, 0), pid);
+}
+
+TEST(libbacktrace, ptrace_max_trace) {
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, nullptr, nullptr), 0);
+ _exit(1);
+ }
+ VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyMaxBacktrace, VerifyMaxDump);
+
+ kill(pid, SIGKILL);
+ int status;
+ ASSERT_EQ(waitpid(pid, &status, 0), pid);
+}
+
+void VerifyProcessIgnoreFrames(Backtrace* bt_all) {
+ std::unique_ptr<Backtrace> ign1(Backtrace::Create(bt_all->Pid(), BACKTRACE_CURRENT_THREAD));
+ ASSERT_TRUE(ign1.get() != nullptr);
+ ASSERT_TRUE(ign1->Unwind(1));
+
+ std::unique_ptr<Backtrace> ign2(Backtrace::Create(bt_all->Pid(), BACKTRACE_CURRENT_THREAD));
+ ASSERT_TRUE(ign2.get() != nullptr);
+ ASSERT_TRUE(ign2->Unwind(2));
+
+ VerifyIgnoreFrames(bt_all, ign1.get(), ign2.get(), nullptr);
+}
+
+TEST(libbacktrace, ptrace_ignore_frames) {
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ ASSERT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
+ _exit(1);
+ }
+ VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyLevelBacktrace, VerifyProcessIgnoreFrames);
+
+ kill(pid, SIGKILL);
+ int status;
+ ASSERT_EQ(waitpid(pid, &status, 0), pid);
+}
+
+// Create a process with multiple threads and dump all of the threads.
+void* PtraceThreadLevelRun(void*) {
+ EXPECT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
+ return nullptr;
+}
+
+void GetThreads(pid_t pid, std::vector<pid_t>* threads) {
+ // Get the list of tasks.
+ char task_path[128];
+ snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid);
+
+ DIR* tasks_dir = opendir(task_path);
+ ASSERT_TRUE(tasks_dir != nullptr);
+ struct dirent* entry;
+ while ((entry = readdir(tasks_dir)) != nullptr) {
+ char* end;
+ pid_t tid = strtoul(entry->d_name, &end, 10);
+ if (*end == '\0') {
+ threads->push_back(tid);
+ }
+ }
+ closedir(tasks_dir);
+}
+
+TEST(libbacktrace, ptrace_threads) {
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ for (size_t i = 0; i < NUM_PTRACE_THREADS; i++) {
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+ pthread_t thread;
+ ASSERT_TRUE(pthread_create(&thread, &attr, PtraceThreadLevelRun, nullptr) == 0);
+ }
+ ASSERT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
+ _exit(1);
+ }
+
+ // Check to see that all of the threads are running before unwinding.
+ std::vector<pid_t> threads;
+ uint64_t start = NanoTime();
+ do {
+ usleep(US_PER_MSEC);
+ threads.clear();
+ GetThreads(pid, &threads);
+ } while ((threads.size() != NUM_PTRACE_THREADS + 1) &&
+ ((NanoTime() - start) <= 5 * NS_PER_SEC));
+ ASSERT_EQ(threads.size(), static_cast<size_t>(NUM_PTRACE_THREADS + 1));
+
+ ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0);
+ WaitForStop(pid);
+ for (std::vector<int>::const_iterator it = threads.begin(); it != threads.end(); ++it) {
+ // Skip the current forked process, we only care about the threads.
+ if (pid == *it) {
+ continue;
+ }
+ VerifyProcTest(pid, *it, false, ReadyLevelBacktrace, VerifyLevelDump);
+ }
+ ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
+
+ kill(pid, SIGKILL);
+ int status;
+ ASSERT_EQ(waitpid(pid, &status, 0), pid);
+}
+
+void VerifyLevelThread(void*) {
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), gettid()));
+ ASSERT_TRUE(backtrace.get() != nullptr);
+ ASSERT_TRUE(backtrace->Unwind(0));
+
+ VerifyLevelDump(backtrace.get());
+}
+
+TEST(libbacktrace, thread_current_level) {
+ ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelThread, nullptr), 0);
+}
+
+void VerifyMaxThread(void*) {
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), gettid()));
+ ASSERT_TRUE(backtrace.get() != nullptr);
+ ASSERT_TRUE(backtrace->Unwind(0));
+
+ VerifyMaxDump(backtrace.get());
+}
+
+TEST(libbacktrace, thread_current_max) {
+ ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, VerifyMaxThread, nullptr), 0);
+}
+
+void* ThreadLevelRun(void* data) {
+ thread_t* thread = reinterpret_cast<thread_t*>(data);
+
+ thread->tid = gettid();
+ EXPECT_NE(test_level_one(1, 2, 3, 4, ThreadSetState, data), 0);
+ return nullptr;
+}
+
+TEST(libbacktrace, thread_level_trace) {
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+ thread_t thread_data = { 0, 0, 0, nullptr };
+ pthread_t thread;
+ ASSERT_TRUE(pthread_create(&thread, &attr, ThreadLevelRun, &thread_data) == 0);
+
+ // Wait up to 2 seconds for the tid to be set.
+ ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2));
+
+ // Make sure that the thread signal used is not visible when compiled for
+ // the target.
+#if !defined(__GLIBC__)
+ ASSERT_LT(THREAD_SIGNAL, SIGRTMIN);
+#endif
+
+ // Save the current signal action and make sure it is restored afterwards.
+ struct sigaction cur_action;
+ ASSERT_TRUE(sigaction(THREAD_SIGNAL, nullptr, &cur_action) == 0);
+
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), thread_data.tid));
+ ASSERT_TRUE(backtrace.get() != nullptr);
+ ASSERT_TRUE(backtrace->Unwind(0));
+
+ VerifyLevelDump(backtrace.get());
+
+ // Tell the thread to exit its infinite loop.
+ android_atomic_acquire_store(0, &thread_data.state);
+
+ // Verify that the old action was restored.
+ struct sigaction new_action;
+ ASSERT_TRUE(sigaction(THREAD_SIGNAL, nullptr, &new_action) == 0);
+ EXPECT_EQ(cur_action.sa_sigaction, new_action.sa_sigaction);
+ // The SA_RESTORER flag gets set behind our back, so a direct comparison
+ // doesn't work unless we mask the value off. Mips doesn't have this
+ // flag, so skip this on that platform.
+#if defined(SA_RESTORER)
+ cur_action.sa_flags &= ~SA_RESTORER;
+ new_action.sa_flags &= ~SA_RESTORER;
+#elif defined(__GLIBC__)
+ // Our host compiler doesn't appear to define this flag for some reason.
+ cur_action.sa_flags &= ~0x04000000;
+ new_action.sa_flags &= ~0x04000000;
+#endif
+ EXPECT_EQ(cur_action.sa_flags, new_action.sa_flags);
+}
+
+TEST(libbacktrace, thread_ignore_frames) {
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+ thread_t thread_data = { 0, 0, 0, nullptr };
+ pthread_t thread;
+ ASSERT_TRUE(pthread_create(&thread, &attr, ThreadLevelRun, &thread_data) == 0);
+
+ // Wait up to 2 seconds for the tid to be set.
+ ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2));
+
+ std::unique_ptr<Backtrace> all(Backtrace::Create(getpid(), thread_data.tid));
+ ASSERT_TRUE(all.get() != nullptr);
+ ASSERT_TRUE(all->Unwind(0));
+
+ std::unique_ptr<Backtrace> ign1(Backtrace::Create(getpid(), thread_data.tid));
+ ASSERT_TRUE(ign1.get() != nullptr);
+ ASSERT_TRUE(ign1->Unwind(1));
+
+ std::unique_ptr<Backtrace> ign2(Backtrace::Create(getpid(), thread_data.tid));
+ ASSERT_TRUE(ign2.get() != nullptr);
+ ASSERT_TRUE(ign2->Unwind(2));
+
+ VerifyIgnoreFrames(all.get(), ign1.get(), ign2.get(), nullptr);
+
+ // Tell the thread to exit its infinite loop.
+ android_atomic_acquire_store(0, &thread_data.state);
+}
+
+void* ThreadMaxRun(void* data) {
+ thread_t* thread = reinterpret_cast<thread_t*>(data);
+
+ thread->tid = gettid();
+ EXPECT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, ThreadSetState, data), 0);
+ return nullptr;
+}
+
+TEST(libbacktrace, thread_max_trace) {
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+ thread_t thread_data = { 0, 0, 0, nullptr };
+ pthread_t thread;
+ ASSERT_TRUE(pthread_create(&thread, &attr, ThreadMaxRun, &thread_data) == 0);
+
+ // Wait for the tid to be set.
+ ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2));
+
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), thread_data.tid));
+ ASSERT_TRUE(backtrace.get() != nullptr);
+ ASSERT_TRUE(backtrace->Unwind(0));
+
+ VerifyMaxDump(backtrace.get());
+
+ // Tell the thread to exit its infinite loop.
+ android_atomic_acquire_store(0, &thread_data.state);
+}
+
+void* ThreadDump(void* data) {
+ dump_thread_t* dump = reinterpret_cast<dump_thread_t*>(data);
+ while (true) {
+ if (android_atomic_acquire_load(dump->now)) {
+ break;
+ }
+ }
+
+ // The status of the actual unwind will be checked elsewhere.
+ dump->backtrace = Backtrace::Create(getpid(), dump->thread.tid);
+ dump->backtrace->Unwind(0);
+
+ android_atomic_acquire_store(1, &dump->done);
+
+ return nullptr;
+}
+
+TEST(libbacktrace, thread_multiple_dump) {
+ // Dump NUM_THREADS simultaneously.
+ std::vector<thread_t> runners(NUM_THREADS);
+ std::vector<dump_thread_t> dumpers(NUM_THREADS);
+
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ for (size_t i = 0; i < NUM_THREADS; i++) {
+ // Launch the runners, they will spin in hard loops doing nothing.
+ runners[i].tid = 0;
+ runners[i].state = 0;
+ ASSERT_TRUE(pthread_create(&runners[i].threadId, &attr, ThreadMaxRun, &runners[i]) == 0);
+ }
+
+ // Wait for tids to be set.
+ for (std::vector<thread_t>::iterator it = runners.begin(); it != runners.end(); ++it) {
+ ASSERT_TRUE(WaitForNonZero(&it->state, 30));
+ }
+
+ // Start all of the dumpers at once, they will spin until they are signalled
+ // to begin their dump run.
+ int32_t dump_now = 0;
+ for (size_t i = 0; i < NUM_THREADS; i++) {
+ dumpers[i].thread.tid = runners[i].tid;
+ dumpers[i].thread.state = 0;
+ dumpers[i].done = 0;
+ dumpers[i].now = &dump_now;
+
+ ASSERT_TRUE(pthread_create(&dumpers[i].thread.threadId, &attr, ThreadDump, &dumpers[i]) == 0);
+ }
+
+ // Start all of the dumpers going at once.
+ android_atomic_acquire_store(1, &dump_now);
+
+ for (size_t i = 0; i < NUM_THREADS; i++) {
+ ASSERT_TRUE(WaitForNonZero(&dumpers[i].done, 30));
+
+ // Tell the runner thread to exit its infinite loop.
+ android_atomic_acquire_store(0, &runners[i].state);
+
+ ASSERT_TRUE(dumpers[i].backtrace != nullptr);
+ VerifyMaxDump(dumpers[i].backtrace);
+
+ delete dumpers[i].backtrace;
+ dumpers[i].backtrace = nullptr;
+ }
+}
+
+TEST(libbacktrace, thread_multiple_dump_same_thread) {
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ thread_t runner;
+ runner.tid = 0;
+ runner.state = 0;
+ ASSERT_TRUE(pthread_create(&runner.threadId, &attr, ThreadMaxRun, &runner) == 0);
+
+ // Wait for tids to be set.
+ ASSERT_TRUE(WaitForNonZero(&runner.state, 30));
+
+ // Start all of the dumpers at once, they will spin until they are signalled
+ // to begin their dump run.
+ int32_t dump_now = 0;
+ // Dump the same thread NUM_THREADS simultaneously.
+ std::vector<dump_thread_t> dumpers(NUM_THREADS);
+ for (size_t i = 0; i < NUM_THREADS; i++) {
+ dumpers[i].thread.tid = runner.tid;
+ dumpers[i].thread.state = 0;
+ dumpers[i].done = 0;
+ dumpers[i].now = &dump_now;
+
+ ASSERT_TRUE(pthread_create(&dumpers[i].thread.threadId, &attr, ThreadDump, &dumpers[i]) == 0);
+ }
+
+ // Start all of the dumpers going at once.
+ android_atomic_acquire_store(1, &dump_now);
+
+ for (size_t i = 0; i < NUM_THREADS; i++) {
+ ASSERT_TRUE(WaitForNonZero(&dumpers[i].done, 30));
+
+ ASSERT_TRUE(dumpers[i].backtrace != nullptr);
+ VerifyMaxDump(dumpers[i].backtrace);
+
+ delete dumpers[i].backtrace;
+ dumpers[i].backtrace = nullptr;
+ }
+
+ // Tell the runner thread to exit its infinite loop.
+ android_atomic_acquire_store(0, &runner.state);
+}
+
+// This test is for UnwindMaps that should share the same map cursor when
+// multiple maps are created for the current process at the same time.
+TEST(libbacktrace, simultaneous_maps) {
+ BacktraceMap* map1 = BacktraceMap::Create(getpid());
+ BacktraceMap* map2 = BacktraceMap::Create(getpid());
+ BacktraceMap* map3 = BacktraceMap::Create(getpid());
+
+ Backtrace* back1 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map1);
+ ASSERT_TRUE(back1 != nullptr);
+ EXPECT_TRUE(back1->Unwind(0));
+ delete back1;
+ delete map1;
+
+ Backtrace* back2 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map2);
+ ASSERT_TRUE(back2 != nullptr);
+ EXPECT_TRUE(back2->Unwind(0));
+ delete back2;
+ delete map2;
+
+ Backtrace* back3 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map3);
+ ASSERT_TRUE(back3 != nullptr);
+ EXPECT_TRUE(back3->Unwind(0));
+ delete back3;
+ delete map3;
+}
+
+TEST(libbacktrace, fillin_erases) {
+ BacktraceMap* back_map = BacktraceMap::Create(getpid());
+
+ backtrace_map_t map;
+
+ map.start = 1;
+ map.end = 3;
+ map.flags = 1;
+ map.name = "Initialized";
+ back_map->FillIn(0, &map);
+ delete back_map;
+
+ ASSERT_FALSE(BacktraceMap::IsValid(map));
+ ASSERT_EQ(static_cast<uintptr_t>(0), map.start);
+ ASSERT_EQ(static_cast<uintptr_t>(0), map.end);
+ ASSERT_EQ(0, map.flags);
+ ASSERT_EQ("", map.name);
+}
+
+TEST(libbacktrace, format_test) {
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD));
+ ASSERT_TRUE(backtrace.get() != nullptr);
+
+ backtrace_frame_data_t frame;
+ frame.num = 1;
+ frame.pc = 2;
+ frame.sp = 0;
+ frame.stack_size = 0;
+ frame.func_offset = 0;
+
+ // Check no map set.
+ frame.num = 1;
+#if defined(__LP64__)
+ EXPECT_EQ("#01 pc 0000000000000002 <unknown>",
+#else
+ EXPECT_EQ("#01 pc 00000002 <unknown>",
+#endif
+ backtrace->FormatFrameData(&frame));
+
+ // Check map name empty, but exists.
+ frame.map.start = 1;
+ frame.map.end = 1;
+ frame.map.load_base = 0;
+#if defined(__LP64__)
+ EXPECT_EQ("#01 pc 0000000000000001 <unknown>",
+#else
+ EXPECT_EQ("#01 pc 00000001 <unknown>",
+#endif
+ backtrace->FormatFrameData(&frame));
+
+
+ // Check relative pc is set and map name is set.
+ frame.pc = 0x12345679;
+ frame.map.name = "MapFake";
+ frame.map.start = 1;
+ frame.map.end = 1;
+#if defined(__LP64__)
+ EXPECT_EQ("#01 pc 0000000012345678 MapFake",
+#else
+ EXPECT_EQ("#01 pc 12345678 MapFake",
+#endif
+ backtrace->FormatFrameData(&frame));
+
+ // Check func_name is set, but no func offset.
+ frame.func_name = "ProcFake";
+#if defined(__LP64__)
+ EXPECT_EQ("#01 pc 0000000012345678 MapFake (ProcFake)",
+#else
+ EXPECT_EQ("#01 pc 12345678 MapFake (ProcFake)",
+#endif
+ backtrace->FormatFrameData(&frame));
+
+ // Check func_name is set, and func offset is non-zero.
+ frame.func_offset = 645;
+#if defined(__LP64__)
+ EXPECT_EQ("#01 pc 0000000012345678 MapFake (ProcFake+645)",
+#else
+ EXPECT_EQ("#01 pc 12345678 MapFake (ProcFake+645)",
+#endif
+ backtrace->FormatFrameData(&frame));
+
+ // Check func_name is set, func offset is non-zero, and load_base is non-zero.
+ frame.func_offset = 645;
+ frame.map.load_base = 100;
+#if defined(__LP64__)
+ EXPECT_EQ("#01 pc 00000000123456dc MapFake (ProcFake+645)",
+#else
+ EXPECT_EQ("#01 pc 123456dc MapFake (ProcFake+645)",
+#endif
+ backtrace->FormatFrameData(&frame));
+}
+
+struct map_test_t {
+ uintptr_t start;
+ uintptr_t end;
+};
+
+bool map_sort(map_test_t i, map_test_t j) {
+ return i.start < j.start;
+}
+
+void VerifyMap(pid_t pid) {
+ char buffer[4096];
+ snprintf(buffer, sizeof(buffer), "/proc/%d/maps", pid);
+
+ FILE* map_file = fopen(buffer, "r");
+ ASSERT_TRUE(map_file != nullptr);
+ std::vector<map_test_t> test_maps;
+ while (fgets(buffer, sizeof(buffer), map_file)) {
+ map_test_t map;
+ ASSERT_EQ(2, sscanf(buffer, "%" SCNxPTR "-%" SCNxPTR " ", &map.start, &map.end));
+ test_maps.push_back(map);
+ }
+ fclose(map_file);
+ std::sort(test_maps.begin(), test_maps.end(), map_sort);
+
+ std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(pid));
+
+ // Basic test that verifies that the map is in the expected order.
+ std::vector<map_test_t>::const_iterator test_it = test_maps.begin();
+ for (BacktraceMap::const_iterator it = map->begin(); it != map->end(); ++it) {
+ ASSERT_TRUE(test_it != test_maps.end());
+ ASSERT_EQ(test_it->start, it->start);
+ ASSERT_EQ(test_it->end, it->end);
+ ++test_it;
+ }
+ ASSERT_TRUE(test_it == test_maps.end());
+}
+
+TEST(libbacktrace, verify_map_remote) {
+ pid_t pid;
+
+ if ((pid = fork()) == 0) {
+ while (true) {
+ }
+ _exit(0);
+ }
+ ASSERT_LT(0, pid);
+
+ ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0);
+
+ // Wait for the process to get to a stopping point.
+ WaitForStop(pid);
+
+ // The maps should match exactly since the forked process has been paused.
+ VerifyMap(pid);
+
+ ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
+
+ kill(pid, SIGKILL);
+ ASSERT_EQ(waitpid(pid, nullptr, 0), pid);
+}
+
+void InitMemory(uint8_t* memory, size_t bytes) {
+ for (size_t i = 0; i < bytes; i++) {
+ memory[i] = i;
+ if (memory[i] == '\0') {
+ // Don't use '\0' in our data so we can verify that an overread doesn't
+ // occur by using a '\0' as the character after the read data.
+ memory[i] = 23;
+ }
+ }
+}
+
+void* ThreadReadTest(void* data) {
+ thread_t* thread_data = reinterpret_cast<thread_t*>(data);
+
+ thread_data->tid = gettid();
+
+ // Create two map pages.
+ // Mark the second page as not-readable.
+ size_t pagesize = static_cast<size_t>(sysconf(_SC_PAGE_SIZE));
+ uint8_t* memory;
+ if (posix_memalign(reinterpret_cast<void**>(&memory), pagesize, 2 * pagesize) != 0) {
+ return reinterpret_cast<void*>(-1);
+ }
+
+ if (mprotect(&memory[pagesize], pagesize, PROT_NONE) != 0) {
+ return reinterpret_cast<void*>(-1);
+ }
+
+ // Set up a simple pattern in memory.
+ InitMemory(memory, pagesize);
+
+ thread_data->data = memory;
+
+ // Tell the caller it's okay to start reading memory.
+ android_atomic_acquire_store(1, &thread_data->state);
+
+ // Loop waiting for the caller to finish reading the memory.
+ while (thread_data->state) {
+ }
+
+ // Re-enable read-write on the page so that we don't crash if we try
+ // and access data on this page when freeing the memory.
+ if (mprotect(&memory[pagesize], pagesize, PROT_READ | PROT_WRITE) != 0) {
+ return reinterpret_cast<void*>(-1);
+ }
+ free(memory);
+
+ android_atomic_acquire_store(1, &thread_data->state);
+
+ return nullptr;
+}
+
+void RunReadTest(Backtrace* backtrace, uintptr_t read_addr) {
+ size_t pagesize = static_cast<size_t>(sysconf(_SC_PAGE_SIZE));
+
+ // Create a page of data to use to do quick compares.
+ uint8_t* expected = new uint8_t[pagesize];
+ InitMemory(expected, pagesize);
+
+ uint8_t* data = new uint8_t[2*pagesize];
+ // Verify that we can only read one page worth of data.
+ size_t bytes_read = backtrace->Read(read_addr, data, 2 * pagesize);
+ ASSERT_EQ(pagesize, bytes_read);
+ ASSERT_TRUE(memcmp(data, expected, pagesize) == 0);
+
+ // Verify unaligned reads.
+ for (size_t i = 1; i < sizeof(word_t); i++) {
+ bytes_read = backtrace->Read(read_addr + i, data, 2 * sizeof(word_t));
+ ASSERT_EQ(2 * sizeof(word_t), bytes_read);
+ ASSERT_TRUE(memcmp(data, &expected[i], 2 * sizeof(word_t)) == 0)
+ << "Offset at " << i << " failed";
+ }
+
+ // Verify small unaligned reads.
+ for (size_t i = 1; i < sizeof(word_t); i++) {
+ for (size_t j = 1; j < sizeof(word_t); j++) {
+ // Set one byte past what we expect to read, to guarantee we don't overread.
+ data[j] = '\0';
+ bytes_read = backtrace->Read(read_addr + i, data, j);
+ ASSERT_EQ(j, bytes_read);
+ ASSERT_TRUE(memcmp(data, &expected[i], j) == 0)
+ << "Offset at " << i << " length " << j << " miscompared";
+ ASSERT_EQ('\0', data[j])
+ << "Offset at " << i << " length " << j << " wrote too much data";
+ }
+ }
+ delete[] data;
+ delete[] expected;
+}
+
+TEST(libbacktrace, thread_read) {
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ pthread_t thread;
+ thread_t thread_data = { 0, 0, 0, nullptr };
+ ASSERT_TRUE(pthread_create(&thread, &attr, ThreadReadTest, &thread_data) == 0);
+
+ ASSERT_TRUE(WaitForNonZero(&thread_data.state, 10));
+
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), thread_data.tid));
+ ASSERT_TRUE(backtrace.get() != nullptr);
+
+ RunReadTest(backtrace.get(), reinterpret_cast<uintptr_t>(thread_data.data));
+
+ android_atomic_acquire_store(0, &thread_data.state);
+
+ ASSERT_TRUE(WaitForNonZero(&thread_data.state, 10));
+}
+
+volatile uintptr_t g_ready = 0;
+volatile uintptr_t g_addr = 0;
+
+void ForkedReadTest() {
+ // Create two map pages.
+ size_t pagesize = static_cast<size_t>(sysconf(_SC_PAGE_SIZE));
+ uint8_t* memory;
+ if (posix_memalign(reinterpret_cast<void**>(&memory), pagesize, 2 * pagesize) != 0) {
+ perror("Failed to allocate memory\n");
+ exit(1);
+ }
+
+ // Mark the second page as not-readable.
+ if (mprotect(&memory[pagesize], pagesize, PROT_NONE) != 0) {
+ perror("Failed to mprotect memory\n");
+ exit(1);
+ }
+
+ // Set up a simple pattern in memory.
+ InitMemory(memory, pagesize);
+
+ g_addr = reinterpret_cast<uintptr_t>(memory);
+ g_ready = 1;
+
+ while (1) {
+ usleep(US_PER_MSEC);
+ }
+}
+
+TEST(libbacktrace, process_read) {
+ g_ready = 0;
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ ForkedReadTest();
+ exit(0);
+ }
+ ASSERT_NE(-1, pid);
+
+ bool test_executed = false;
+ uint64_t start = NanoTime();
+ while (1) {
+ if (ptrace(PTRACE_ATTACH, pid, 0, 0) == 0) {
+ WaitForStop(pid);
+
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, pid));
+ ASSERT_TRUE(backtrace.get() != nullptr);
+
+ uintptr_t read_addr;
+ size_t bytes_read = backtrace->Read(reinterpret_cast<uintptr_t>(&g_ready),
+ reinterpret_cast<uint8_t*>(&read_addr),
+ sizeof(uintptr_t));
+ ASSERT_EQ(sizeof(uintptr_t), bytes_read);
+ if (read_addr) {
+ // The forked process is ready to be read.
+ bytes_read = backtrace->Read(reinterpret_cast<uintptr_t>(&g_addr),
+ reinterpret_cast<uint8_t*>(&read_addr),
+ sizeof(uintptr_t));
+ ASSERT_EQ(sizeof(uintptr_t), bytes_read);
+
+ RunReadTest(backtrace.get(), read_addr);
+
+ test_executed = true;
+ break;
+ }
+ ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
+ }
+ if ((NanoTime() - start) > 5 * NS_PER_SEC) {
+ break;
+ }
+ usleep(US_PER_MSEC);
+ }
+ kill(pid, SIGKILL);
+ ASSERT_EQ(waitpid(pid, nullptr, 0), pid);
+
+ ASSERT_TRUE(test_executed);
+}
+
+void VerifyFunctionsFound(const std::vector<std::string>& found_functions) {
+ // We expect to find these functions in libbacktrace_test. If we don't
+ // find them, that's a bug in the memory read handling code in libunwind.
+ std::list<std::string> expected_functions;
+ expected_functions.push_back("test_recursive_call");
+ expected_functions.push_back("test_level_one");
+ expected_functions.push_back("test_level_two");
+ expected_functions.push_back("test_level_three");
+ expected_functions.push_back("test_level_four");
+ for (const auto& found_function : found_functions) {
+ for (const auto& expected_function : expected_functions) {
+ if (found_function == expected_function) {
+ expected_functions.remove(found_function);
+ break;
+ }
+ }
+ }
+ ASSERT_TRUE(expected_functions.empty()) << "Not all functions found in shared library.";
+}
+
+const char* CopySharedLibrary() {
+#if defined(__LP64__)
+ const char* lib_name = "lib64";
+#else
+ const char* lib_name = "lib";
+#endif
+
+#if defined(__BIONIC__)
+ const char* tmp_so_name = "/data/local/tmp/libbacktrace_test.so";
+ std::string cp_cmd = android::base::StringPrintf("cp /system/%s/libbacktrace_test.so %s",
+ lib_name, tmp_so_name);
+#else
+ const char* tmp_so_name = "/tmp/libbacktrace_test.so";
+ if (getenv("ANDROID_HOST_OUT") == NULL) {
+ fprintf(stderr, "ANDROID_HOST_OUT not set, make sure you run lunch.");
+ return nullptr;
+ }
+ std::string cp_cmd = android::base::StringPrintf("cp %s/%s/libbacktrace_test.so %s",
+ getenv("ANDROID_HOST_OUT"), lib_name,
+ tmp_so_name);
+#endif
+
+ // Copy the shared so to a tempory directory.
+ system(cp_cmd.c_str());
+
+ return tmp_so_name;
+}
+
+TEST(libbacktrace, check_unreadable_elf_local) {
+ const char* tmp_so_name = CopySharedLibrary();
+ ASSERT_TRUE(tmp_so_name != nullptr);
+
+ struct stat buf;
+ ASSERT_TRUE(stat(tmp_so_name, &buf) != -1);
+ uintptr_t map_size = buf.st_size;
+
+ int fd = open(tmp_so_name, O_RDONLY);
+ ASSERT_TRUE(fd != -1);
+
+ void* map = mmap(NULL, map_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ ASSERT_TRUE(map != MAP_FAILED);
+ close(fd);
+ ASSERT_TRUE(unlink(tmp_so_name) != -1);
+
+ std::vector<std::string> found_functions;
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS,
+ BACKTRACE_CURRENT_THREAD));
+ ASSERT_TRUE(backtrace.get() != nullptr);
+
+ // Needed before GetFunctionName will work.
+ backtrace->Unwind(0);
+
+ // Loop through the entire map, and get every function we can find.
+ map_size += reinterpret_cast<uintptr_t>(map);
+ std::string last_func;
+ for (uintptr_t read_addr = reinterpret_cast<uintptr_t>(map);
+ read_addr < map_size; read_addr += 4) {
+ uintptr_t offset;
+ std::string func_name = backtrace->GetFunctionName(read_addr, &offset);
+ if (!func_name.empty() && last_func != func_name) {
+ found_functions.push_back(func_name);
+ }
+ last_func = func_name;
+ }
+
+ ASSERT_TRUE(munmap(map, map_size - reinterpret_cast<uintptr_t>(map)) == 0);
+
+ VerifyFunctionsFound(found_functions);
+}
+
+TEST(libbacktrace, check_unreadable_elf_remote) {
+ const char* tmp_so_name = CopySharedLibrary();
+ ASSERT_TRUE(tmp_so_name != nullptr);
+
+ g_ready = 0;
+
+ struct stat buf;
+ ASSERT_TRUE(stat(tmp_so_name, &buf) != -1);
+ uintptr_t map_size = buf.st_size;
+
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ int fd = open(tmp_so_name, O_RDONLY);
+ if (fd == -1) {
+ fprintf(stderr, "Failed to open file %s: %s\n", tmp_so_name, strerror(errno));
+ unlink(tmp_so_name);
+ exit(0);
+ }
+
+ void* map = mmap(NULL, map_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (map == MAP_FAILED) {
+ fprintf(stderr, "Failed to map in memory: %s\n", strerror(errno));
+ unlink(tmp_so_name);
+ exit(0);
+ }
+ close(fd);
+ if (unlink(tmp_so_name) == -1) {
+ fprintf(stderr, "Failed to unlink: %s\n", strerror(errno));
+ exit(0);
+ }
+
+ g_addr = reinterpret_cast<uintptr_t>(map);
+ g_ready = 1;
+ while (true) {
+ usleep(US_PER_MSEC);
+ }
+ exit(0);
+ }
+ ASSERT_TRUE(pid > 0);
+
+ std::vector<std::string> found_functions;
+ uint64_t start = NanoTime();
+ while (true) {
+ ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0);
+
+ // Wait for the process to get to a stopping point.
+ WaitForStop(pid);
+
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, BACKTRACE_CURRENT_THREAD));
+ ASSERT_TRUE(backtrace.get() != nullptr);
+
+ uintptr_t read_addr;
+ ASSERT_EQ(sizeof(uintptr_t), backtrace->Read(reinterpret_cast<uintptr_t>(&g_ready), reinterpret_cast<uint8_t*>(&read_addr), sizeof(uintptr_t)));
+ if (read_addr) {
+ ASSERT_EQ(sizeof(uintptr_t), backtrace->Read(reinterpret_cast<uintptr_t>(&g_addr), reinterpret_cast<uint8_t*>(&read_addr), sizeof(uintptr_t)));
+
+ // Needed before GetFunctionName will work.
+ backtrace->Unwind(0);
+
+ // Loop through the entire map, and get every function we can find.
+ map_size += read_addr;
+ std::string last_func;
+ for (; read_addr < map_size; read_addr += 4) {
+ uintptr_t offset;
+ std::string func_name = backtrace->GetFunctionName(read_addr, &offset);
+ if (!func_name.empty() && last_func != func_name) {
+ found_functions.push_back(func_name);
+ }
+ last_func = func_name;
+ }
+ break;
+ }
+ ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
+
+ if ((NanoTime() - start) > 5 * NS_PER_SEC) {
+ break;
+ }
+ usleep(US_PER_MSEC);
+ }
+
+ kill(pid, SIGKILL);
+ ASSERT_EQ(waitpid(pid, nullptr, 0), pid);
+
+ VerifyFunctionsFound(found_functions);
+}
+
+bool FindFuncFrameInBacktrace(Backtrace* backtrace, uintptr_t test_func, size_t* frame_num) {
+ backtrace_map_t map;
+ backtrace->FillInMap(test_func, &map);
+ if (!BacktraceMap::IsValid(map)) {
+ return false;
+ }
+
+ // Loop through the frames, and find the one that is in the map.
+ *frame_num = 0;
+ for (Backtrace::const_iterator it = backtrace->begin(); it != backtrace->end(); ++it) {
+ if (BacktraceMap::IsValid(it->map) && map.start == it->map.start &&
+ it->pc >= test_func) {
+ *frame_num = it->num;
+ return true;
+ }
+ }
+ return false;
+}
+
+void VerifyUnreadableElfFrame(Backtrace* backtrace, uintptr_t test_func, size_t frame_num) {
+ ASSERT_LT(backtrace->NumFrames(), static_cast<size_t>(MAX_BACKTRACE_FRAMES))
+ << DumpFrames(backtrace);
+
+ ASSERT_TRUE(frame_num != 0) << DumpFrames(backtrace);
+ // Make sure that there is at least one more frame above the test func call.
+ ASSERT_LT(frame_num, backtrace->NumFrames()) << DumpFrames(backtrace);
+
+ uintptr_t diff = backtrace->GetFrame(frame_num)->pc - test_func;
+ ASSERT_LT(diff, 200U) << DumpFrames(backtrace);
+}
+
+void VerifyUnreadableElfBacktrace(uintptr_t test_func) {
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS,
+ BACKTRACE_CURRENT_THREAD));
+ ASSERT_TRUE(backtrace.get() != nullptr);
+ ASSERT_TRUE(backtrace->Unwind(0));
+
+ size_t frame_num;
+ ASSERT_TRUE(FindFuncFrameInBacktrace(backtrace.get(), test_func, &frame_num));
+
+ VerifyUnreadableElfFrame(backtrace.get(), test_func, frame_num);
+}
+
+typedef int (*test_func_t)(int, int, int, int, void (*)(uintptr_t), uintptr_t);
+
+TEST(libbacktrace, unwind_through_unreadable_elf_local) {
+ const char* tmp_so_name = CopySharedLibrary();
+ ASSERT_TRUE(tmp_so_name != nullptr);
+ void* lib_handle = dlopen(tmp_so_name, RTLD_NOW);
+ ASSERT_TRUE(lib_handle != nullptr);
+ ASSERT_TRUE(unlink(tmp_so_name) != -1);
+
+ test_func_t test_func;
+ test_func = reinterpret_cast<test_func_t>(dlsym(lib_handle, "test_level_one"));
+ ASSERT_TRUE(test_func != nullptr);
+
+ ASSERT_NE(test_func(1, 2, 3, 4, VerifyUnreadableElfBacktrace,
+ reinterpret_cast<uintptr_t>(test_func)), 0);
+
+ ASSERT_TRUE(dlclose(lib_handle) == 0);
+}
+
+TEST(libbacktrace, unwind_through_unreadable_elf_remote) {
+ const char* tmp_so_name = CopySharedLibrary();
+ ASSERT_TRUE(tmp_so_name != nullptr);
+ void* lib_handle = dlopen(tmp_so_name, RTLD_NOW);
+ ASSERT_TRUE(lib_handle != nullptr);
+ ASSERT_TRUE(unlink(tmp_so_name) != -1);
+
+ test_func_t test_func;
+ test_func = reinterpret_cast<test_func_t>(dlsym(lib_handle, "test_level_one"));
+ ASSERT_TRUE(test_func != nullptr);
+
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ test_func(1, 2, 3, 4, 0, 0);
+ exit(0);
+ }
+ ASSERT_TRUE(pid > 0);
+ ASSERT_TRUE(dlclose(lib_handle) == 0);
+
+ uint64_t start = NanoTime();
+ bool done = false;
+ while (!done) {
+ ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0);
+
+ // Wait for the process to get to a stopping point.
+ WaitForStop(pid);
+
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, BACKTRACE_CURRENT_THREAD));
+ ASSERT_TRUE(backtrace.get() != nullptr);
+ ASSERT_TRUE(backtrace->Unwind(0));
+
+ size_t frame_num;
+ if (FindFuncFrameInBacktrace(backtrace.get(),
+ reinterpret_cast<uintptr_t>(test_func), &frame_num)) {
+
+ VerifyUnreadableElfFrame(backtrace.get(), reinterpret_cast<uintptr_t>(test_func), frame_num);
+ done = true;
+ }
+
+ ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
+
+ if ((NanoTime() - start) > 5 * NS_PER_SEC) {
+ break;
+ }
+ usleep(US_PER_MSEC);
+ }
+
+ kill(pid, SIGKILL);
+ ASSERT_EQ(waitpid(pid, nullptr, 0), pid);
+
+ ASSERT_TRUE(done) << "Test function never found in unwind.";
+}
+
+#if defined(ENABLE_PSS_TESTS)
+#include "GetPss.h"
+
+#define MAX_LEAK_BYTES 32*1024UL
+
+void CheckForLeak(pid_t pid, pid_t tid) {
+ // Do a few runs to get the PSS stable.
+ for (size_t i = 0; i < 100; i++) {
+ Backtrace* backtrace = Backtrace::Create(pid, tid);
+ ASSERT_TRUE(backtrace != nullptr);
+ ASSERT_TRUE(backtrace->Unwind(0));
+ delete backtrace;
+ }
+ size_t stable_pss = GetPssBytes();
+ ASSERT_TRUE(stable_pss != 0);
+
+ // Loop enough that even a small leak should be detectable.
+ for (size_t i = 0; i < 4096; i++) {
+ Backtrace* backtrace = Backtrace::Create(pid, tid);
+ ASSERT_TRUE(backtrace != nullptr);
+ ASSERT_TRUE(backtrace->Unwind(0));
+ delete backtrace;
+ }
+ size_t new_pss = GetPssBytes();
+ ASSERT_TRUE(new_pss != 0);
+ size_t abs_diff = (new_pss > stable_pss) ? new_pss - stable_pss : stable_pss - new_pss;
+ // As long as the new pss is within a certain amount, consider everything okay.
+ ASSERT_LE(abs_diff, MAX_LEAK_BYTES);
+}
+
+TEST(libbacktrace, check_for_leak_local) {
+ CheckForLeak(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD);
+}
+
+TEST(libbacktrace, check_for_leak_local_thread) {
+ thread_t thread_data = { 0, 0, 0, nullptr };
+ pthread_t thread;
+ ASSERT_TRUE(pthread_create(&thread, nullptr, ThreadLevelRun, &thread_data) == 0);
+
+ // Wait up to 2 seconds for the tid to be set.
+ ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2));
+
+ CheckForLeak(BACKTRACE_CURRENT_PROCESS, thread_data.tid);
+
+ // Tell the thread to exit its infinite loop.
+ android_atomic_acquire_store(0, &thread_data.state);
+
+ ASSERT_TRUE(pthread_join(thread, nullptr) == 0);
+}
+
+TEST(libbacktrace, check_for_leak_remote) {
+ pid_t pid;
+
+ if ((pid = fork()) == 0) {
+ while (true) {
+ }
+ _exit(0);
+ }
+ ASSERT_LT(0, pid);
+
+ ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0);
+
+ // Wait for the process to get to a stopping point.
+ WaitForStop(pid);
+
+ CheckForLeak(pid, BACKTRACE_CURRENT_THREAD);
+
+ ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
+
+ kill(pid, SIGKILL);
+ ASSERT_EQ(waitpid(pid, nullptr, 0), pid);
+}
+#endif
+
diff --git a/libbacktrace/backtrace_testlib.c b/libbacktrace/backtrace_testlib.c
new file mode 100644
index 0000000..d4d15db
--- /dev/null
+++ b/libbacktrace/backtrace_testlib.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+
+int test_level_four(int one, int two, int three, int four,
+ void (*callback_func)(void*), void* data) {
+ if (callback_func != NULL) {
+ callback_func(data);
+ } else {
+ while (1) {
+ }
+ }
+ return one + two + three + four;
+}
+
+int test_level_three(int one, int two, int three, int four,
+ void (*callback_func)(void*), void* data) {
+ return test_level_four(one+3, two+6, three+9, four+12, callback_func, data) + 3;
+}
+
+int test_level_two(int one, int two, int three, int four,
+ void (*callback_func)(void*), void* data) {
+ return test_level_three(one+2, two+4, three+6, four+8, callback_func, data) + 2;
+}
+
+int test_level_one(int one, int two, int three, int four,
+ void (*callback_func)(void*), void* data) {
+ return test_level_two(one+1, two+2, three+3, four+4, callback_func, data) + 1;
+}
+
+int test_recursive_call(int level, void (*callback_func)(void*), void* data) {
+ if (level > 0) {
+ return test_recursive_call(level - 1, callback_func, data) + level;
+ } else if (callback_func != NULL) {
+ callback_func(data);
+ } else {
+ while (1) {
+ }
+ }
+ return 0;
+}
diff --git a/libbacktrace/map_info.c b/libbacktrace/map_info.c
new file mode 100644
index 0000000..073b24a
--- /dev/null
+++ b/libbacktrace/map_info.c
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <log/log.h>
+#include <sys/time.h>
+
+#include <backtrace/backtrace.h>
+
+#if defined(__APPLE__)
+
+// Mac OS vmmap(1) output:
+// __TEXT 0009f000-000a1000 [ 8K 8K] r-x/rwx SM=COW /Volumes/android/dalvik-dev/out/host/darwin-x86/bin/libcorkscrew_test\n
+// 012345678901234567890123456789012345678901234567890123456789
+// 0 1 2 3 4 5
+static backtrace_map_info_t* parse_vmmap_line(const char* line) {
+ unsigned long int start;
+ unsigned long int end;
+ char permissions[4];
+ int name_pos;
+ if (sscanf(line, "%*21c %lx-%lx [%*13c] %3c/%*3c SM=%*3c %n",
+ &start, &end, permissions, &name_pos) != 3) {
+ return NULL;
+ }
+
+ const char* name = line + name_pos;
+ size_t name_len = strlen(name);
+
+ backtrace_map_info_t* mi = calloc(1, sizeof(backtrace_map_info_t) + name_len);
+ if (mi != NULL) {
+ mi->start = start;
+ mi->end = end;
+ mi->is_readable = permissions[0] == 'r';
+ mi->is_writable = permissions[1] == 'w';
+ mi->is_executable = permissions[2] == 'x';
+ memcpy(mi->name, name, name_len);
+ mi->name[name_len - 1] = '\0';
+ ALOGV("Parsed map: start=0x%08x, end=0x%08x, "
+ "is_readable=%d, is_writable=%d is_executable=%d, name=%s",
+ mi->start, mi->end,
+ mi->is_readable, mi->is_writable, mi->is_executable, mi->name);
+ }
+ return mi;
+}
+
+backtrace_map_info_t* backtrace_create_map_info_list(pid_t pid) {
+ char cmd[1024];
+ if (pid < 0) {
+ pid = getpid();
+ }
+ snprintf(cmd, sizeof(cmd), "vmmap -w -resident -submap -allSplitLibs -interleaved %d", pid);
+ FILE* fp = popen(cmd, "r");
+ if (fp == NULL) {
+ return NULL;
+ }
+
+ char line[1024];
+ backtrace_map_info_t* milist = NULL;
+ while (fgets(line, sizeof(line), fp) != NULL) {
+ backtrace_map_info_t* mi = parse_vmmap_line(line);
+ if (mi != NULL) {
+ mi->next = milist;
+ milist = mi;
+ }
+ }
+ pclose(fp);
+ return milist;
+}
+
+#else
+
+// Linux /proc/<pid>/maps lines:
+// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so\n
+// 012345678901234567890123456789012345678901234567890123456789
+// 0 1 2 3 4 5
+static backtrace_map_info_t* parse_maps_line(const char* line)
+{
+ unsigned long int start;
+ unsigned long int end;
+ char permissions[5];
+ int name_pos;
+ if (sscanf(line, "%lx-%lx %4s %*x %*x:%*x %*d%n", &start, &end,
+ permissions, &name_pos) != 3) {
+ return NULL;
+ }
+
+ while (isspace(line[name_pos])) {
+ name_pos += 1;
+ }
+ const char* name = line + name_pos;
+ size_t name_len = strlen(name);
+ if (name_len && name[name_len - 1] == '\n') {
+ name_len -= 1;
+ }
+
+ backtrace_map_info_t* mi = calloc(1, sizeof(backtrace_map_info_t) + name_len + 1);
+ if (mi) {
+ mi->start = start;
+ mi->end = end;
+ mi->is_readable = strlen(permissions) == 4 && permissions[0] == 'r';
+ mi->is_writable = strlen(permissions) == 4 && permissions[1] == 'w';
+ mi->is_executable = strlen(permissions) == 4 && permissions[2] == 'x';
+ memcpy(mi->name, name, name_len);
+ mi->name[name_len] = '\0';
+ ALOGV("Parsed map: start=0x%08x, end=0x%08x, "
+ "is_readable=%d, is_writable=%d, is_executable=%d, name=%s",
+ mi->start, mi->end,
+ mi->is_readable, mi->is_writable, mi->is_executable, mi->name);
+ }
+ return mi;
+}
+
+backtrace_map_info_t* backtrace_create_map_info_list(pid_t tid) {
+ char path[PATH_MAX];
+ char line[1024];
+ FILE* fp;
+ backtrace_map_info_t* milist = NULL;
+
+ if (tid < 0) {
+ tid = getpid();
+ }
+ snprintf(path, PATH_MAX, "/proc/%d/maps", tid);
+ fp = fopen(path, "r");
+ if (fp) {
+ while(fgets(line, sizeof(line), fp)) {
+ backtrace_map_info_t* mi = parse_maps_line(line);
+ if (mi) {
+ mi->next = milist;
+ milist = mi;
+ }
+ }
+ fclose(fp);
+ }
+ return milist;
+}
+
+#endif
+
+void backtrace_destroy_map_info_list(backtrace_map_info_t* milist) {
+ while (milist) {
+ backtrace_map_info_t* next = milist->next;
+ free(milist);
+ milist = next;
+ }
+}
+
+const backtrace_map_info_t* backtrace_find_map_info(
+ const backtrace_map_info_t* milist, uintptr_t addr) {
+ const backtrace_map_info_t* mi = milist;
+ while (mi && !(addr >= mi->start && addr < mi->end)) {
+ mi = mi->next;
+ }
+ return mi;
+}
diff --git a/libbacktrace/thread_utils.c b/libbacktrace/thread_utils.c
new file mode 100644
index 0000000..e75f56e
--- /dev/null
+++ b/libbacktrace/thread_utils.c
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "thread_utils.h"
+
+#if !defined(__BIONIC__)
+
+// glibc doesn't implement or export tgkill.
+#include <unistd.h>
+#include <sys/syscall.h>
+
+int tgkill(int tgid, int tid, int sig) {
+ return syscall(__NR_tgkill, tgid, tid, sig);
+}
+
+#endif
diff --git a/libbacktrace/thread_utils.h b/libbacktrace/thread_utils.h
new file mode 100644
index 0000000..df83581
--- /dev/null
+++ b/libbacktrace/thread_utils.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBBACKTRACE_THREAD_UTILS_H
+#define _LIBBACKTRACE_THREAD_UTILS_H
+
+#include <unistd.h>
+
+__BEGIN_DECLS
+
+int tgkill(int tgid, int tid, int sig);
+
+__END_DECLS
+
+#endif /* _LIBBACKTRACE_THREAD_UTILS_H */
diff --git a/libcutils/Android.mk b/libcutils/Android.mk
new file mode 100644
index 0000000..5330949
--- /dev/null
+++ b/libcutils/Android.mk
@@ -0,0 +1,138 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH := $(my-dir)
+include $(CLEAR_VARS)
+
+commonSources := \
+ hashmap.c \
+ atomic.c.arm \
+ native_handle.c \
+ config_utils.c \
+ load_file.c \
+ strlcpy.c \
+ open_memstream.c \
+ strdup16to8.c \
+ strdup8to16.c \
+ record_stream.c \
+ process_name.c \
+ threads.c \
+ sched_policy.c \
+ iosched_policy.c \
+ str_parms.c \
+ fs_config.c
+
+# some files must not be compiled when building against Mingw
+# they correspond to features not used by our host development tools
+# which are also hard or even impossible to port to native Win32
+WINDOWS_HOST_ONLY :=
+ifeq ($(HOST_OS),windows)
+ ifeq ($(strip $(USE_CYGWIN)),)
+ WINDOWS_HOST_ONLY := 1
+ endif
+endif
+# USE_MINGW is defined when we build against Mingw on Linux
+ifneq ($(strip $(USE_MINGW)),)
+ WINDOWS_HOST_ONLY := 1
+endif
+
+ifneq ($(WINDOWS_HOST_ONLY),1)
+ commonSources += \
+ fs.c \
+ multiuser.c \
+ socket_inaddr_any_server.c \
+ socket_local_client.c \
+ socket_local_server.c \
+ socket_loopback_client.c \
+ socket_loopback_server.c \
+ socket_network_client.c \
+ sockets.c \
+
+ commonHostSources += \
+ ashmem-host.c \
+ trace-host.c
+
+endif
+
+
+# Shared and static library for host
+# ========================================================
+LOCAL_MODULE := libcutils
+LOCAL_SRC_FILES := $(commonSources) $(commonHostSources) dlmalloc_stubs.c
+LOCAL_STATIC_LIBRARIES := liblog
+ifneq ($(HOST_OS),windows)
+LOCAL_CFLAGS += -Werror -Wall -Wextra
+endif
+LOCAL_MULTILIB := both
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libcutils
+LOCAL_SRC_FILES := $(commonSources) $(commonHostSources) dlmalloc_stubs.c
+LOCAL_SHARED_LIBRARIES := liblog
+ifneq ($(HOST_OS),windows)
+LOCAL_CFLAGS += -Werror -Wall -Wextra
+endif
+LOCAL_MULTILIB := both
+include $(BUILD_HOST_SHARED_LIBRARY)
+
+
+
+# Shared and static library for target
+# ========================================================
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libcutils
+LOCAL_SRC_FILES := $(commonSources) \
+ android_reboot.c \
+ ashmem-dev.c \
+ debugger.c \
+ klog.c \
+ partition_utils.c \
+ properties.c \
+ qtaguid.c \
+ trace-dev.c \
+ uevent.c \
+
+LOCAL_SRC_FILES_arm += arch-arm/memset32.S
+LOCAL_SRC_FILES_arm64 += arch-arm64/android_memset.S
+
+LOCAL_SRC_FILES_mips += arch-mips/android_memset.c
+LOCAL_SRC_FILES_mips64 += arch-mips/android_memset.c
+
+LOCAL_SRC_FILES_x86 += \
+ arch-x86/android_memset16.S \
+ arch-x86/android_memset32.S \
+
+LOCAL_SRC_FILES_x86_64 += \
+ arch-x86_64/android_memset16.S \
+ arch-x86_64/android_memset32.S \
+
+LOCAL_C_INCLUDES := $(libcutils_c_includes)
+LOCAL_STATIC_LIBRARIES := liblog
+LOCAL_CFLAGS += -Werror -Wall -Wextra -std=gnu90
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libcutils
+# TODO: remove liblog as whole static library, once we don't have prebuilt that requires
+# liblog symbols present in libcutils.
+LOCAL_WHOLE_STATIC_LIBRARIES := libcutils liblog
+LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_CFLAGS += -Werror -Wall -Wextra
+LOCAL_C_INCLUDES := $(libcutils_c_includes)
+include $(BUILD_SHARED_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/libcutils/MODULE_LICENSE_APACHE2 b/libcutils/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libcutils/MODULE_LICENSE_APACHE2
diff --git a/libcutils/NOTICE b/libcutils/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/libcutils/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/libcutils/android_reboot.c b/libcutils/android_reboot.c
new file mode 100644
index 0000000..af7e189
--- /dev/null
+++ b/libcutils/android_reboot.c
@@ -0,0 +1,242 @@
+/*
+ * Copyright 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 <errno.h>
+#include <fcntl.h>
+#include <mntent.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/cdefs.h>
+#include <sys/mount.h>
+#include <sys/reboot.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <cutils/android_reboot.h>
+#include <cutils/klog.h>
+#include <cutils/list.h>
+
+#define TAG "android_reboot"
+#define READONLY_CHECK_MS 5000
+#define READONLY_CHECK_TIMES 50
+
+typedef struct {
+ struct listnode list;
+ struct mntent entry;
+} mntent_list;
+
+static bool has_mount_option(const char* opts, const char* opt_to_find)
+{
+ bool ret = false;
+ char* copy = NULL;
+ char* opt;
+ char* rem;
+
+ while ((opt = strtok_r(copy ? NULL : (copy = strdup(opts)), ",", &rem))) {
+ if (!strcmp(opt, opt_to_find)) {
+ ret = true;
+ break;
+ }
+ }
+
+ free(copy);
+ return ret;
+}
+
+static bool is_block_device(const char* fsname)
+{
+ return !strncmp(fsname, "/dev/block", 10);
+}
+
+/* Find all read+write block devices in /proc/mounts and add them to
+ * |rw_entries|.
+ */
+static void find_rw(struct listnode* rw_entries)
+{
+ FILE* fp;
+ struct mntent* mentry;
+
+ if ((fp = setmntent("/proc/mounts", "r")) == NULL) {
+ KLOG_WARNING(TAG, "Failed to open /proc/mounts.\n");
+ return;
+ }
+ while ((mentry = getmntent(fp)) != NULL) {
+ if (is_block_device(mentry->mnt_fsname) &&
+ has_mount_option(mentry->mnt_opts, "rw")) {
+ mntent_list* item = (mntent_list*)calloc(1, sizeof(mntent_list));
+ item->entry = *mentry;
+ item->entry.mnt_fsname = strdup(mentry->mnt_fsname);
+ item->entry.mnt_dir = strdup(mentry->mnt_dir);
+ item->entry.mnt_type = strdup(mentry->mnt_type);
+ item->entry.mnt_opts = strdup(mentry->mnt_opts);
+ list_add_tail(rw_entries, &item->list);
+ }
+ }
+ endmntent(fp);
+}
+
+static void free_entries(struct listnode* entries)
+{
+ struct listnode* node;
+ struct listnode* n;
+ list_for_each_safe(node, n, entries) {
+ mntent_list* item = node_to_item(node, mntent_list, list);
+ free(item->entry.mnt_fsname);
+ free(item->entry.mnt_dir);
+ free(item->entry.mnt_type);
+ free(item->entry.mnt_opts);
+ free(item);
+ }
+}
+
+static mntent_list* find_item(struct listnode* rw_entries, const char* fsname_to_find)
+{
+ struct listnode* node;
+ list_for_each(node, rw_entries) {
+ mntent_list* item = node_to_item(node, mntent_list, list);
+ if (!strcmp(item->entry.mnt_fsname, fsname_to_find)) {
+ return item;
+ }
+ }
+ return NULL;
+}
+
+/* Remounting filesystems read-only is difficult when there are files
+ * opened for writing or pending deletes on the filesystem. There is
+ * no way to force the remount with the mount(2) syscall. The magic sysrq
+ * 'u' command does an emergency remount read-only on all writable filesystems
+ * that have a block device (i.e. not tmpfs filesystems) by calling
+ * emergency_remount(), which knows how to force the remount to read-only.
+ * Unfortunately, that is asynchronous, and just schedules the work and
+ * returns. The best way to determine if it is done is to read /proc/mounts
+ * repeatedly until there are no more writable filesystems mounted on
+ * block devices.
+ */
+static void remount_ro(void (*cb_on_remount)(const struct mntent*))
+{
+ int fd, cnt;
+ FILE* fp;
+ struct mntent* mentry;
+ struct listnode* node;
+
+ list_declare(rw_entries);
+ list_declare(ro_entries);
+
+ sync();
+ find_rw(&rw_entries);
+
+ /* Trigger the remount of the filesystems as read-only,
+ * which also marks them clean.
+ */
+ fd = TEMP_FAILURE_RETRY(open("/proc/sysrq-trigger", O_WRONLY));
+ if (fd < 0) {
+ KLOG_WARNING(TAG, "Failed to open sysrq-trigger.\n");
+ /* TODO: Try to remount each rw parition manually in readonly mode.
+ * This may succeed if no process is using the partition.
+ */
+ goto out;
+ }
+ if (TEMP_FAILURE_RETRY(write(fd, "u", 1)) != 1) {
+ close(fd);
+ KLOG_WARNING(TAG, "Failed to write to sysrq-trigger.\n");
+ /* TODO: The same. Manually remount the paritions. */
+ goto out;
+ }
+ close(fd);
+
+ /* Now poll /proc/mounts till it's done */
+ cnt = 0;
+ while (cnt < READONLY_CHECK_TIMES) {
+ if ((fp = setmntent("/proc/mounts", "r")) == NULL) {
+ /* If we can't read /proc/mounts, just give up. */
+ KLOG_WARNING(TAG, "Failed to open /proc/mounts.\n");
+ goto out;
+ }
+ while ((mentry = getmntent(fp)) != NULL) {
+ if (!is_block_device(mentry->mnt_fsname) ||
+ !has_mount_option(mentry->mnt_opts, "ro")) {
+ continue;
+ }
+ mntent_list* item = find_item(&rw_entries, mentry->mnt_fsname);
+ if (item) {
+ /* |item| has now been ro remounted. */
+ list_remove(&item->list);
+ list_add_tail(&ro_entries, &item->list);
+ }
+ }
+ endmntent(fp);
+ if (list_empty(&rw_entries)) {
+ /* All rw block devices are now readonly. */
+ break;
+ }
+ TEMP_FAILURE_RETRY(
+ usleep(READONLY_CHECK_MS * 1000 / READONLY_CHECK_TIMES));
+ cnt++;
+ }
+
+ list_for_each(node, &rw_entries) {
+ mntent_list* item = node_to_item(node, mntent_list, list);
+ KLOG_WARNING(TAG, "Failed to remount %s in readonly mode.\n",
+ item->entry.mnt_fsname);
+ }
+
+ if (cb_on_remount) {
+ list_for_each(node, &ro_entries) {
+ mntent_list* item = node_to_item(node, mntent_list, list);
+ cb_on_remount(&item->entry);
+ }
+ }
+
+out:
+ free_entries(&rw_entries);
+ free_entries(&ro_entries);
+}
+
+int android_reboot_with_callback(
+ int cmd, int flags __unused, const char *arg,
+ void (*cb_on_remount)(const struct mntent*))
+{
+ int ret;
+ remount_ro(cb_on_remount);
+ switch (cmd) {
+ case ANDROID_RB_RESTART:
+ ret = reboot(RB_AUTOBOOT);
+ break;
+
+ case ANDROID_RB_POWEROFF:
+ ret = reboot(RB_POWER_OFF);
+ break;
+
+ case ANDROID_RB_RESTART2:
+ ret = syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
+ LINUX_REBOOT_CMD_RESTART2, arg);
+ break;
+
+ default:
+ ret = -1;
+ }
+
+ return ret;
+}
+
+int android_reboot(int cmd, int flags, const char *arg)
+{
+ return android_reboot_with_callback(cmd, flags, arg, NULL);
+}
diff --git a/libcutils/arch-arm/memset32.S b/libcutils/arch-arm/memset32.S
new file mode 100644
index 0000000..1e89636
--- /dev/null
+++ b/libcutils/arch-arm/memset32.S
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2006 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.
+ */
+/*
+ * memset32.S
+ *
+ */
+
+ .syntax unified
+
+ .text
+ .align
+
+ .global android_memset32
+ .type android_memset32, %function
+ .global android_memset16
+ .type android_memset16, %function
+
+ /*
+ * Optimized memset32 and memset16 for ARM.
+ *
+ * void android_memset16(uint16_t* dst, uint16_t value, size_t size);
+ * void android_memset32(uint32_t* dst, uint32_t value, size_t size);
+ *
+ */
+
+android_memset16:
+ .fnstart
+ cmp r2, #1
+ bxle lr
+
+ /* expand the data to 32 bits */
+ mov r1, r1, lsl #16
+ orr r1, r1, r1, lsr #16
+
+ /* align to 32 bits */
+ tst r0, #2
+ strhne r1, [r0], #2
+ subne r2, r2, #2
+ .fnend
+
+android_memset32:
+ .fnstart
+ .cfi_startproc
+ str lr, [sp, #-4]!
+ .cfi_def_cfa_offset 4
+ .cfi_rel_offset lr, 0
+
+ /* align the destination to a cache-line */
+ mov r12, r1
+ mov lr, r1
+ rsb r3, r0, #0
+ ands r3, r3, #0x1C
+ beq .Laligned32
+ cmp r3, r2
+ andhi r3, r2, #0x1C
+ sub r2, r2, r3
+
+ /* conditionally writes 0 to 7 words (length in r3) */
+ movs r3, r3, lsl #28
+ stmiacs r0!, {r1, lr}
+ stmiacs r0!, {r1, lr}
+ stmiami r0!, {r1, lr}
+ movs r3, r3, lsl #2
+ strcs r1, [r0], #4
+
+.Laligned32:
+ mov r3, r1
+1: subs r2, r2, #32
+ stmiahs r0!, {r1,r3,r12,lr}
+ stmiahs r0!, {r1,r3,r12,lr}
+ bhs 1b
+ add r2, r2, #32
+
+ /* conditionally stores 0 to 30 bytes */
+ movs r2, r2, lsl #28
+ stmiacs r0!, {r1,r3,r12,lr}
+ stmiami r0!, {r1,lr}
+ movs r2, r2, lsl #2
+ strcs r1, [r0], #4
+ strhmi lr, [r0], #2
+
+ ldr lr, [sp], #4
+ .cfi_def_cfa_offset 0
+ .cfi_restore lr
+ bx lr
+ .cfi_endproc
+ .fnend
diff --git a/libcutils/arch-arm64/android_memset.S b/libcutils/arch-arm64/android_memset.S
new file mode 100644
index 0000000..9a83a68
--- /dev/null
+++ b/libcutils/arch-arm64/android_memset.S
@@ -0,0 +1,211 @@
+/* Copyright (c) 2012, Linaro Limited
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the Linaro nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* Assumptions:
+ *
+ * ARMv8-a, AArch64
+ * Unaligned accesses
+ *
+ */
+
+/* By default we assume that the DC instruction can be used to zero
+ data blocks more efficiently. In some circumstances this might be
+ unsafe, for example in an asymmetric multiprocessor environment with
+ different DC clear lengths (neither the upper nor lower lengths are
+ safe to use). */
+
+#define dst x0
+#define count x2
+#define tmp1 x3
+#define tmp1w w3
+#define tmp2 x4
+#define tmp2w w4
+#define zva_len_x x5
+#define zva_len w5
+#define zva_bits_x x6
+
+#define A_l x1
+#define A_lw w1
+#define tmp3w w9
+
+#define ENTRY(f) \
+ .text; \
+ .globl f; \
+ .align 0; \
+ .type f, %function; \
+ f: \
+ .cfi_startproc \
+
+#define END(f) \
+ .cfi_endproc; \
+ .size f, .-f; \
+
+ENTRY(android_memset16)
+ ands A_lw, A_lw, #0xffff
+ b.eq .Lzero_mem
+ orr A_lw, A_lw, A_lw, lsl #16
+ b .Lexpand_to_64
+END(android_memset16)
+
+ENTRY(android_memset32)
+ cmp A_lw, #0
+ b.eq .Lzero_mem
+.Lexpand_to_64:
+ orr A_l, A_l, A_l, lsl #32
+.Ltail_maybe_long:
+ cmp count, #64
+ b.ge .Lnot_short
+.Ltail_maybe_tiny:
+ cmp count, #15
+ b.le .Ltail15tiny
+.Ltail63:
+ ands tmp1, count, #0x30
+ b.eq .Ltail15
+ add dst, dst, tmp1
+ cmp tmp1w, #0x20
+ b.eq 1f
+ b.lt 2f
+ stp A_l, A_l, [dst, #-48]
+1:
+ stp A_l, A_l, [dst, #-32]
+2:
+ stp A_l, A_l, [dst, #-16]
+
+.Ltail15:
+ and count, count, #15
+ add dst, dst, count
+ stp A_l, A_l, [dst, #-16] /* Repeat some/all of last store. */
+ ret
+
+.Ltail15tiny:
+ /* Set up to 15 bytes. Does not assume earlier memory
+ being set. */
+ tbz count, #3, 1f
+ str A_l, [dst], #8
+1:
+ tbz count, #2, 1f
+ str A_lw, [dst], #4
+1:
+ tbz count, #1, 1f
+ strh A_lw, [dst], #2
+1:
+ ret
+
+ /* Critical loop. Start at a new cache line boundary. Assuming
+ * 64 bytes per line, this ensures the entire loop is in one line. */
+ .p2align 6
+.Lnot_short:
+ neg tmp2, dst
+ ands tmp2, tmp2, #15
+ b.eq 2f
+ /* Bring DST to 128-bit (16-byte) alignment. We know that there's
+ * more than that to set, so we simply store 16 bytes and advance by
+ * the amount required to reach alignment. */
+ sub count, count, tmp2
+ stp A_l, A_l, [dst]
+ add dst, dst, tmp2
+ /* There may be less than 63 bytes to go now. */
+ cmp count, #63
+ b.le .Ltail63
+2:
+ sub dst, dst, #16 /* Pre-bias. */
+ sub count, count, #64
+1:
+ stp A_l, A_l, [dst, #16]
+ stp A_l, A_l, [dst, #32]
+ stp A_l, A_l, [dst, #48]
+ stp A_l, A_l, [dst, #64]!
+ subs count, count, #64
+ b.ge 1b
+ tst count, #0x3f
+ add dst, dst, #16
+ b.ne .Ltail63
+ ret
+
+ /* For zeroing memory, check to see if we can use the ZVA feature to
+ * zero entire 'cache' lines. */
+.Lzero_mem:
+ mov A_l, #0
+ cmp count, #63
+ b.le .Ltail_maybe_tiny
+ neg tmp2, dst
+ ands tmp2, tmp2, #15
+ b.eq 1f
+ sub count, count, tmp2
+ stp A_l, A_l, [dst]
+ add dst, dst, tmp2
+ cmp count, #63
+ b.le .Ltail63
+1:
+ /* For zeroing small amounts of memory, it's not worth setting up
+ * the line-clear code. */
+ cmp count, #128
+ b.lt .Lnot_short
+ mrs tmp1, dczid_el0
+ tbnz tmp1, #4, .Lnot_short
+ mov tmp3w, #4
+ and zva_len, tmp1w, #15 /* Safety: other bits reserved. */
+ lsl zva_len, tmp3w, zva_len
+
+.Lzero_by_line:
+ /* Compute how far we need to go to become suitably aligned. We're
+ * already at quad-word alignment. */
+ cmp count, zva_len_x
+ b.lt .Lnot_short /* Not enough to reach alignment. */
+ sub zva_bits_x, zva_len_x, #1
+ neg tmp2, dst
+ ands tmp2, tmp2, zva_bits_x
+ b.eq 1f /* Already aligned. */
+ /* Not aligned, check that there's enough to copy after alignment. */
+ sub tmp1, count, tmp2
+ cmp tmp1, #64
+ ccmp tmp1, zva_len_x, #8, ge /* NZCV=0b1000 */
+ b.lt .Lnot_short
+ /* We know that there's at least 64 bytes to zero and that it's safe
+ * to overrun by 64 bytes. */
+ mov count, tmp1
+2:
+ stp A_l, A_l, [dst]
+ stp A_l, A_l, [dst, #16]
+ stp A_l, A_l, [dst, #32]
+ subs tmp2, tmp2, #64
+ stp A_l, A_l, [dst, #48]
+ add dst, dst, #64
+ b.ge 2b
+ /* We've overrun a bit, so adjust dst downwards. */
+ add dst, dst, tmp2
+1:
+ sub count, count, zva_len_x
+3:
+ dc zva, dst
+ add dst, dst, zva_len_x
+ subs count, count, zva_len_x
+ b.ge 3b
+ ands count, count, zva_bits_x
+ b.ne .Ltail_maybe_long
+ ret
+END(android_memset32)
diff --git a/libcutils/arch-mips/android_memset.c b/libcutils/arch-mips/android_memset.c
new file mode 100644
index 0000000..a6b7496
--- /dev/null
+++ b/libcutils/arch-mips/android_memset.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* generic C version for any machine */
+
+#include <cutils/memory.h>
+
+void android_memset16(uint16_t* dst, uint16_t value, size_t size)
+{
+ /* optimized version of
+ size >>= 1;
+ while (size--)
+ *dst++ = value;
+ */
+
+ size >>= 1;
+ if (((uintptr_t)dst & 2) && size) {
+ /* fill unpaired first elem separately */
+ *dst++ = value;
+ size--;
+ }
+ /* dst is now 32-bit-aligned */
+ /* fill body with 32-bit pairs */
+ uint32_t value32 = (value << 16) | value;
+ android_memset32((uint32_t*) dst, value32, size<<1);
+ if (size & 1) {
+ dst[size-1] = value; /* fill unpaired last elem */
+ }
+}
+
+
+void android_memset32(uint32_t* dst, uint32_t value, size_t size)
+{
+ /* optimized version of
+ size >>= 2;
+ while (size--)
+ *dst++ = value;
+ */
+
+ size >>= 2;
+ if (((uintptr_t)dst & 4) && size) {
+ /* fill unpaired first 32-bit elem separately */
+ *dst++ = value;
+ size--;
+ }
+ /* dst is now 64-bit aligned */
+ /* fill body with 64-bit pairs */
+ uint64_t value64 = (((uint64_t)value)<<32) | value;
+ uint64_t* dst64 = (uint64_t*)dst;
+
+ while (size >= 12) {
+ dst64[0] = value64;
+ dst64[1] = value64;
+ dst64[2] = value64;
+ dst64[3] = value64;
+ dst64[4] = value64;
+ dst64[5] = value64;
+ size -= 12;
+ dst64 += 6;
+ }
+
+ /* fill remainder with original 32-bit single-elem loop */
+ dst = (uint32_t*) dst64;
+ while (size--) {
+ *dst++ = value;
+ }
+
+}
diff --git a/libcutils/arch-x86/android_memset16.S b/libcutils/arch-x86/android_memset16.S
new file mode 100755
index 0000000..cb2ff14
--- /dev/null
+++ b/libcutils/arch-x86/android_memset16.S
@@ -0,0 +1,719 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "cache.h"
+
+#ifndef MEMSET
+# define MEMSET android_memset16
+#endif
+
+#ifndef L
+# define L(label) .L##label
+#endif
+
+#ifndef ALIGN
+# define ALIGN(n) .p2align n
+#endif
+
+#ifndef cfi_startproc
+# define cfi_startproc .cfi_startproc
+#endif
+
+#ifndef cfi_endproc
+# define cfi_endproc .cfi_endproc
+#endif
+
+#ifndef cfi_rel_offset
+# define cfi_rel_offset(reg, off) .cfi_rel_offset reg, off
+#endif
+
+#ifndef cfi_restore
+# define cfi_restore(reg) .cfi_restore reg
+#endif
+
+#ifndef cfi_adjust_cfa_offset
+# define cfi_adjust_cfa_offset(off) .cfi_adjust_cfa_offset off
+#endif
+
+#ifndef ENTRY
+# define ENTRY(name) \
+ .type name, @function; \
+ .globl name; \
+ .p2align 4; \
+name: \
+ cfi_startproc
+#endif
+
+#ifndef END
+# define END(name) \
+ cfi_endproc; \
+ .size name, .-name
+#endif
+
+#define CFI_PUSH(REG) \
+ cfi_adjust_cfa_offset (4); \
+ cfi_rel_offset (REG, 0)
+
+#define CFI_POP(REG) \
+ cfi_adjust_cfa_offset (-4); \
+ cfi_restore (REG)
+
+#define PUSH(REG) pushl REG; CFI_PUSH (REG)
+#define POP(REG) popl REG; CFI_POP (REG)
+
+#ifdef USE_AS_BZERO16
+# define DEST PARMS
+# define LEN DEST+4
+# define SETRTNVAL
+#else
+# define DEST PARMS
+# define CHR DEST+4
+# define LEN CHR+4
+# define SETRTNVAL movl DEST(%esp), %eax
+#endif
+
+#if (defined SHARED || defined __PIC__)
+# define ENTRANCE PUSH (%ebx);
+# define RETURN_END POP (%ebx); ret
+# define RETURN RETURN_END; CFI_PUSH (%ebx)
+# define PARMS 8 /* Preserve EBX. */
+# define JMPTBL(I, B) I - B
+
+/* Load an entry in a jump table into EBX and branch to it. TABLE is a
+ jump table with relative offsets. */
+# define BRANCH_TO_JMPTBL_ENTRY(TABLE) \
+ /* We first load PC into EBX. */ \
+ call __x86.get_pc_thunk.bx; \
+ /* Get the address of the jump table. */ \
+ add $(TABLE - .), %ebx; \
+ /* Get the entry and convert the relative offset to the \
+ absolute address. */ \
+ add (%ebx,%ecx,4), %ebx; \
+ /* We loaded the jump table and adjuested EDX. Go. */ \
+ jmp *%ebx
+
+ .section .gnu.linkonce.t.__x86.get_pc_thunk.bx,"ax",@progbits
+ .globl __x86.get_pc_thunk.bx
+ .hidden __x86.get_pc_thunk.bx
+ ALIGN (4)
+ .type __x86.get_pc_thunk.bx,@function
+__x86.get_pc_thunk.bx:
+ movl (%esp), %ebx
+ ret
+#else
+# define ENTRANCE
+# define RETURN_END ret
+# define RETURN RETURN_END
+# define PARMS 4
+# define JMPTBL(I, B) I
+
+/* Branch to an entry in a jump table. TABLE is a jump table with
+ absolute offsets. */
+# define BRANCH_TO_JMPTBL_ENTRY(TABLE) \
+ jmp *TABLE(,%ecx,4)
+#endif
+
+ .section .text.sse2,"ax",@progbits
+ ALIGN (4)
+ENTRY (MEMSET)
+ ENTRANCE
+
+ movl LEN(%esp), %ecx
+ shr $1, %ecx
+#ifdef USE_AS_BZERO16
+ xor %eax, %eax
+#else
+ movzwl CHR(%esp), %eax
+ mov %eax, %edx
+ shl $16, %eax
+ or %edx, %eax
+#endif
+ movl DEST(%esp), %edx
+ cmp $32, %ecx
+ jae L(32wordsormore)
+
+L(write_less32words):
+ lea (%edx, %ecx, 2), %edx
+ BRANCH_TO_JMPTBL_ENTRY (L(table_less32words))
+
+
+ .pushsection .rodata.sse2,"a",@progbits
+ ALIGN (2)
+L(table_less32words):
+ .int JMPTBL (L(write_0words), L(table_less32words))
+ .int JMPTBL (L(write_1words), L(table_less32words))
+ .int JMPTBL (L(write_2words), L(table_less32words))
+ .int JMPTBL (L(write_3words), L(table_less32words))
+ .int JMPTBL (L(write_4words), L(table_less32words))
+ .int JMPTBL (L(write_5words), L(table_less32words))
+ .int JMPTBL (L(write_6words), L(table_less32words))
+ .int JMPTBL (L(write_7words), L(table_less32words))
+ .int JMPTBL (L(write_8words), L(table_less32words))
+ .int JMPTBL (L(write_9words), L(table_less32words))
+ .int JMPTBL (L(write_10words), L(table_less32words))
+ .int JMPTBL (L(write_11words), L(table_less32words))
+ .int JMPTBL (L(write_12words), L(table_less32words))
+ .int JMPTBL (L(write_13words), L(table_less32words))
+ .int JMPTBL (L(write_14words), L(table_less32words))
+ .int JMPTBL (L(write_15words), L(table_less32words))
+ .int JMPTBL (L(write_16words), L(table_less32words))
+ .int JMPTBL (L(write_17words), L(table_less32words))
+ .int JMPTBL (L(write_18words), L(table_less32words))
+ .int JMPTBL (L(write_19words), L(table_less32words))
+ .int JMPTBL (L(write_20words), L(table_less32words))
+ .int JMPTBL (L(write_21words), L(table_less32words))
+ .int JMPTBL (L(write_22words), L(table_less32words))
+ .int JMPTBL (L(write_23words), L(table_less32words))
+ .int JMPTBL (L(write_24words), L(table_less32words))
+ .int JMPTBL (L(write_25words), L(table_less32words))
+ .int JMPTBL (L(write_26words), L(table_less32words))
+ .int JMPTBL (L(write_27words), L(table_less32words))
+ .int JMPTBL (L(write_28words), L(table_less32words))
+ .int JMPTBL (L(write_29words), L(table_less32words))
+ .int JMPTBL (L(write_30words), L(table_less32words))
+ .int JMPTBL (L(write_31words), L(table_less32words))
+ .popsection
+
+ ALIGN (4)
+L(write_28words):
+ movl %eax, -56(%edx)
+ movl %eax, -52(%edx)
+L(write_24words):
+ movl %eax, -48(%edx)
+ movl %eax, -44(%edx)
+L(write_20words):
+ movl %eax, -40(%edx)
+ movl %eax, -36(%edx)
+L(write_16words):
+ movl %eax, -32(%edx)
+ movl %eax, -28(%edx)
+L(write_12words):
+ movl %eax, -24(%edx)
+ movl %eax, -20(%edx)
+L(write_8words):
+ movl %eax, -16(%edx)
+ movl %eax, -12(%edx)
+L(write_4words):
+ movl %eax, -8(%edx)
+ movl %eax, -4(%edx)
+L(write_0words):
+ SETRTNVAL
+ RETURN
+
+ ALIGN (4)
+L(write_29words):
+ movl %eax, -58(%edx)
+ movl %eax, -54(%edx)
+L(write_25words):
+ movl %eax, -50(%edx)
+ movl %eax, -46(%edx)
+L(write_21words):
+ movl %eax, -42(%edx)
+ movl %eax, -38(%edx)
+L(write_17words):
+ movl %eax, -34(%edx)
+ movl %eax, -30(%edx)
+L(write_13words):
+ movl %eax, -26(%edx)
+ movl %eax, -22(%edx)
+L(write_9words):
+ movl %eax, -18(%edx)
+ movl %eax, -14(%edx)
+L(write_5words):
+ movl %eax, -10(%edx)
+ movl %eax, -6(%edx)
+L(write_1words):
+ mov %ax, -2(%edx)
+ SETRTNVAL
+ RETURN
+
+ ALIGN (4)
+L(write_30words):
+ movl %eax, -60(%edx)
+ movl %eax, -56(%edx)
+L(write_26words):
+ movl %eax, -52(%edx)
+ movl %eax, -48(%edx)
+L(write_22words):
+ movl %eax, -44(%edx)
+ movl %eax, -40(%edx)
+L(write_18words):
+ movl %eax, -36(%edx)
+ movl %eax, -32(%edx)
+L(write_14words):
+ movl %eax, -28(%edx)
+ movl %eax, -24(%edx)
+L(write_10words):
+ movl %eax, -20(%edx)
+ movl %eax, -16(%edx)
+L(write_6words):
+ movl %eax, -12(%edx)
+ movl %eax, -8(%edx)
+L(write_2words):
+ movl %eax, -4(%edx)
+ SETRTNVAL
+ RETURN
+
+ ALIGN (4)
+L(write_31words):
+ movl %eax, -62(%edx)
+ movl %eax, -58(%edx)
+L(write_27words):
+ movl %eax, -54(%edx)
+ movl %eax, -50(%edx)
+L(write_23words):
+ movl %eax, -46(%edx)
+ movl %eax, -42(%edx)
+L(write_19words):
+ movl %eax, -38(%edx)
+ movl %eax, -34(%edx)
+L(write_15words):
+ movl %eax, -30(%edx)
+ movl %eax, -26(%edx)
+L(write_11words):
+ movl %eax, -22(%edx)
+ movl %eax, -18(%edx)
+L(write_7words):
+ movl %eax, -14(%edx)
+ movl %eax, -10(%edx)
+L(write_3words):
+ movl %eax, -6(%edx)
+ movw %ax, -2(%edx)
+ SETRTNVAL
+ RETURN
+
+ ALIGN (4)
+
+L(32wordsormore):
+ shl $1, %ecx
+ test $0x01, %edx
+ jz L(aligned2bytes)
+ mov %eax, (%edx)
+ mov %eax, -4(%edx, %ecx)
+ sub $2, %ecx
+ add $1, %edx
+ rol $8, %eax
+L(aligned2bytes):
+#ifdef USE_AS_BZERO16
+ pxor %xmm0, %xmm0
+#else
+ movd %eax, %xmm0
+ pshufd $0, %xmm0, %xmm0
+#endif
+ testl $0xf, %edx
+ jz L(aligned_16)
+/* ECX > 32 and EDX is not 16 byte aligned. */
+L(not_aligned_16):
+ movdqu %xmm0, (%edx)
+ movl %edx, %eax
+ and $-16, %edx
+ add $16, %edx
+ sub %edx, %eax
+ add %eax, %ecx
+ movd %xmm0, %eax
+
+ ALIGN (4)
+L(aligned_16):
+ cmp $128, %ecx
+ jae L(128bytesormore)
+
+L(aligned_16_less128bytes):
+ add %ecx, %edx
+ shr $1, %ecx
+ BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes))
+
+ ALIGN (4)
+L(128bytesormore):
+#ifdef SHARED_CACHE_SIZE
+ PUSH (%ebx)
+ mov $SHARED_CACHE_SIZE, %ebx
+#else
+# if (defined SHARED || defined __PIC__)
+ call __x86.get_pc_thunk.bx
+ add $_GLOBAL_OFFSET_TABLE_, %ebx
+ mov __x86_shared_cache_size@GOTOFF(%ebx), %ebx
+# else
+ PUSH (%ebx)
+ mov __x86_shared_cache_size, %ebx
+# endif
+#endif
+ cmp %ebx, %ecx
+ jae L(128bytesormore_nt_start)
+
+
+#ifdef DATA_CACHE_SIZE
+ POP (%ebx)
+# define RESTORE_EBX_STATE CFI_PUSH (%ebx)
+ cmp $DATA_CACHE_SIZE, %ecx
+#else
+# if (defined SHARED || defined __PIC__)
+# define RESTORE_EBX_STATE
+ call __x86.get_pc_thunk.bx
+ add $_GLOBAL_OFFSET_TABLE_, %ebx
+ cmp __x86_data_cache_size@GOTOFF(%ebx), %ecx
+# else
+ POP (%ebx)
+# define RESTORE_EBX_STATE CFI_PUSH (%ebx)
+ cmp __x86_data_cache_size, %ecx
+# endif
+#endif
+
+ jae L(128bytes_L2_normal)
+ subl $128, %ecx
+L(128bytesormore_normal):
+ sub $128, %ecx
+ movdqa %xmm0, (%edx)
+ movdqa %xmm0, 0x10(%edx)
+ movdqa %xmm0, 0x20(%edx)
+ movdqa %xmm0, 0x30(%edx)
+ movdqa %xmm0, 0x40(%edx)
+ movdqa %xmm0, 0x50(%edx)
+ movdqa %xmm0, 0x60(%edx)
+ movdqa %xmm0, 0x70(%edx)
+ lea 128(%edx), %edx
+ jb L(128bytesless_normal)
+
+
+ sub $128, %ecx
+ movdqa %xmm0, (%edx)
+ movdqa %xmm0, 0x10(%edx)
+ movdqa %xmm0, 0x20(%edx)
+ movdqa %xmm0, 0x30(%edx)
+ movdqa %xmm0, 0x40(%edx)
+ movdqa %xmm0, 0x50(%edx)
+ movdqa %xmm0, 0x60(%edx)
+ movdqa %xmm0, 0x70(%edx)
+ lea 128(%edx), %edx
+ jae L(128bytesormore_normal)
+
+L(128bytesless_normal):
+ lea 128(%ecx), %ecx
+ add %ecx, %edx
+ shr $1, %ecx
+ BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes))
+
+ ALIGN (4)
+L(128bytes_L2_normal):
+ prefetcht0 0x380(%edx)
+ prefetcht0 0x3c0(%edx)
+ sub $128, %ecx
+ movdqa %xmm0, (%edx)
+ movaps %xmm0, 0x10(%edx)
+ movaps %xmm0, 0x20(%edx)
+ movaps %xmm0, 0x30(%edx)
+ movaps %xmm0, 0x40(%edx)
+ movaps %xmm0, 0x50(%edx)
+ movaps %xmm0, 0x60(%edx)
+ movaps %xmm0, 0x70(%edx)
+ add $128, %edx
+ cmp $128, %ecx
+ jae L(128bytes_L2_normal)
+
+L(128bytesless_L2_normal):
+ add %ecx, %edx
+ shr $1, %ecx
+ BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes))
+
+ RESTORE_EBX_STATE
+L(128bytesormore_nt_start):
+ sub %ebx, %ecx
+ mov %ebx, %eax
+ and $0x7f, %eax
+ add %eax, %ecx
+ movd %xmm0, %eax
+ ALIGN (4)
+L(128bytesormore_shared_cache_loop):
+ prefetcht0 0x3c0(%edx)
+ prefetcht0 0x380(%edx)
+ sub $0x80, %ebx
+ movdqa %xmm0, (%edx)
+ movdqa %xmm0, 0x10(%edx)
+ movdqa %xmm0, 0x20(%edx)
+ movdqa %xmm0, 0x30(%edx)
+ movdqa %xmm0, 0x40(%edx)
+ movdqa %xmm0, 0x50(%edx)
+ movdqa %xmm0, 0x60(%edx)
+ movdqa %xmm0, 0x70(%edx)
+ add $0x80, %edx
+ cmp $0x80, %ebx
+ jae L(128bytesormore_shared_cache_loop)
+ cmp $0x80, %ecx
+ jb L(shared_cache_loop_end)
+ ALIGN (4)
+L(128bytesormore_nt):
+ sub $0x80, %ecx
+ movntdq %xmm0, (%edx)
+ movntdq %xmm0, 0x10(%edx)
+ movntdq %xmm0, 0x20(%edx)
+ movntdq %xmm0, 0x30(%edx)
+ movntdq %xmm0, 0x40(%edx)
+ movntdq %xmm0, 0x50(%edx)
+ movntdq %xmm0, 0x60(%edx)
+ movntdq %xmm0, 0x70(%edx)
+ add $0x80, %edx
+ cmp $0x80, %ecx
+ jae L(128bytesormore_nt)
+ sfence
+L(shared_cache_loop_end):
+#if defined DATA_CACHE_SIZE || !(defined SHARED || defined __PIC__)
+ POP (%ebx)
+#endif
+ add %ecx, %edx
+ shr $1, %ecx
+ BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes))
+
+
+ .pushsection .rodata.sse2,"a",@progbits
+ ALIGN (2)
+L(table_16_128bytes):
+ .int JMPTBL (L(aligned_16_0bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_2bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_4bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_6bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_8bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_10bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_12bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_14bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_16bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_18bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_20bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_22bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_24bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_26bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_28bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_30bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_32bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_34bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_36bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_38bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_40bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_42bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_44bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_46bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_48bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_50bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_52bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_54bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_56bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_58bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_60bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_62bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_64bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_66bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_68bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_70bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_72bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_74bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_76bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_78bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_80bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_82bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_84bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_86bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_88bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_90bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_92bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_94bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_96bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_98bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_100bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_102bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_104bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_106bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_108bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_110bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_112bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_114bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_116bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_118bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_120bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_122bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_124bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_126bytes), L(table_16_128bytes))
+ .popsection
+
+
+ ALIGN (4)
+L(aligned_16_112bytes):
+ movdqa %xmm0, -112(%edx)
+L(aligned_16_96bytes):
+ movdqa %xmm0, -96(%edx)
+L(aligned_16_80bytes):
+ movdqa %xmm0, -80(%edx)
+L(aligned_16_64bytes):
+ movdqa %xmm0, -64(%edx)
+L(aligned_16_48bytes):
+ movdqa %xmm0, -48(%edx)
+L(aligned_16_32bytes):
+ movdqa %xmm0, -32(%edx)
+L(aligned_16_16bytes):
+ movdqa %xmm0, -16(%edx)
+L(aligned_16_0bytes):
+ SETRTNVAL
+ RETURN
+
+
+ ALIGN (4)
+L(aligned_16_114bytes):
+ movdqa %xmm0, -114(%edx)
+L(aligned_16_98bytes):
+ movdqa %xmm0, -98(%edx)
+L(aligned_16_82bytes):
+ movdqa %xmm0, -82(%edx)
+L(aligned_16_66bytes):
+ movdqa %xmm0, -66(%edx)
+L(aligned_16_50bytes):
+ movdqa %xmm0, -50(%edx)
+L(aligned_16_34bytes):
+ movdqa %xmm0, -34(%edx)
+L(aligned_16_18bytes):
+ movdqa %xmm0, -18(%edx)
+L(aligned_16_2bytes):
+ movw %ax, -2(%edx)
+ SETRTNVAL
+ RETURN
+
+ ALIGN (4)
+L(aligned_16_116bytes):
+ movdqa %xmm0, -116(%edx)
+L(aligned_16_100bytes):
+ movdqa %xmm0, -100(%edx)
+L(aligned_16_84bytes):
+ movdqa %xmm0, -84(%edx)
+L(aligned_16_68bytes):
+ movdqa %xmm0, -68(%edx)
+L(aligned_16_52bytes):
+ movdqa %xmm0, -52(%edx)
+L(aligned_16_36bytes):
+ movdqa %xmm0, -36(%edx)
+L(aligned_16_20bytes):
+ movdqa %xmm0, -20(%edx)
+L(aligned_16_4bytes):
+ movl %eax, -4(%edx)
+ SETRTNVAL
+ RETURN
+
+
+ ALIGN (4)
+L(aligned_16_118bytes):
+ movdqa %xmm0, -118(%edx)
+L(aligned_16_102bytes):
+ movdqa %xmm0, -102(%edx)
+L(aligned_16_86bytes):
+ movdqa %xmm0, -86(%edx)
+L(aligned_16_70bytes):
+ movdqa %xmm0, -70(%edx)
+L(aligned_16_54bytes):
+ movdqa %xmm0, -54(%edx)
+L(aligned_16_38bytes):
+ movdqa %xmm0, -38(%edx)
+L(aligned_16_22bytes):
+ movdqa %xmm0, -22(%edx)
+L(aligned_16_6bytes):
+ movl %eax, -6(%edx)
+ movw %ax, -2(%edx)
+ SETRTNVAL
+ RETURN
+
+
+ ALIGN (4)
+L(aligned_16_120bytes):
+ movdqa %xmm0, -120(%edx)
+L(aligned_16_104bytes):
+ movdqa %xmm0, -104(%edx)
+L(aligned_16_88bytes):
+ movdqa %xmm0, -88(%edx)
+L(aligned_16_72bytes):
+ movdqa %xmm0, -72(%edx)
+L(aligned_16_56bytes):
+ movdqa %xmm0, -56(%edx)
+L(aligned_16_40bytes):
+ movdqa %xmm0, -40(%edx)
+L(aligned_16_24bytes):
+ movdqa %xmm0, -24(%edx)
+L(aligned_16_8bytes):
+ movq %xmm0, -8(%edx)
+ SETRTNVAL
+ RETURN
+
+
+ ALIGN (4)
+L(aligned_16_122bytes):
+ movdqa %xmm0, -122(%edx)
+L(aligned_16_106bytes):
+ movdqa %xmm0, -106(%edx)
+L(aligned_16_90bytes):
+ movdqa %xmm0, -90(%edx)
+L(aligned_16_74bytes):
+ movdqa %xmm0, -74(%edx)
+L(aligned_16_58bytes):
+ movdqa %xmm0, -58(%edx)
+L(aligned_16_42bytes):
+ movdqa %xmm0, -42(%edx)
+L(aligned_16_26bytes):
+ movdqa %xmm0, -26(%edx)
+L(aligned_16_10bytes):
+ movq %xmm0, -10(%edx)
+ movw %ax, -2(%edx)
+ SETRTNVAL
+ RETURN
+
+
+ ALIGN (4)
+L(aligned_16_124bytes):
+ movdqa %xmm0, -124(%edx)
+L(aligned_16_108bytes):
+ movdqa %xmm0, -108(%edx)
+L(aligned_16_92bytes):
+ movdqa %xmm0, -92(%edx)
+L(aligned_16_76bytes):
+ movdqa %xmm0, -76(%edx)
+L(aligned_16_60bytes):
+ movdqa %xmm0, -60(%edx)
+L(aligned_16_44bytes):
+ movdqa %xmm0, -44(%edx)
+L(aligned_16_28bytes):
+ movdqa %xmm0, -28(%edx)
+L(aligned_16_12bytes):
+ movq %xmm0, -12(%edx)
+ movl %eax, -4(%edx)
+ SETRTNVAL
+ RETURN
+
+
+ ALIGN (4)
+L(aligned_16_126bytes):
+ movdqa %xmm0, -126(%edx)
+L(aligned_16_110bytes):
+ movdqa %xmm0, -110(%edx)
+L(aligned_16_94bytes):
+ movdqa %xmm0, -94(%edx)
+L(aligned_16_78bytes):
+ movdqa %xmm0, -78(%edx)
+L(aligned_16_62bytes):
+ movdqa %xmm0, -62(%edx)
+L(aligned_16_46bytes):
+ movdqa %xmm0, -46(%edx)
+L(aligned_16_30bytes):
+ movdqa %xmm0, -30(%edx)
+L(aligned_16_14bytes):
+ movq %xmm0, -14(%edx)
+ movl %eax, -6(%edx)
+ movw %ax, -2(%edx)
+ SETRTNVAL
+ RETURN
+
+END (MEMSET)
diff --git a/libcutils/arch-x86/android_memset32.S b/libcutils/arch-x86/android_memset32.S
new file mode 100755
index 0000000..f4326dc
--- /dev/null
+++ b/libcutils/arch-x86/android_memset32.S
@@ -0,0 +1,510 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "cache.h"
+
+#ifndef MEMSET
+# define MEMSET android_memset32
+#endif
+
+#ifndef L
+# define L(label) .L##label
+#endif
+
+#ifndef ALIGN
+# define ALIGN(n) .p2align n
+#endif
+
+#ifndef cfi_startproc
+# define cfi_startproc .cfi_startproc
+#endif
+
+#ifndef cfi_endproc
+# define cfi_endproc .cfi_endproc
+#endif
+
+#ifndef cfi_rel_offset
+# define cfi_rel_offset(reg, off) .cfi_rel_offset reg, off
+#endif
+
+#ifndef cfi_restore
+# define cfi_restore(reg) .cfi_restore reg
+#endif
+
+#ifndef cfi_adjust_cfa_offset
+# define cfi_adjust_cfa_offset(off) .cfi_adjust_cfa_offset off
+#endif
+
+#ifndef ENTRY
+# define ENTRY(name) \
+ .type name, @function; \
+ .globl name; \
+ .p2align 4; \
+name: \
+ cfi_startproc
+#endif
+
+#ifndef END
+# define END(name) \
+ cfi_endproc; \
+ .size name, .-name
+#endif
+
+#define CFI_PUSH(REG) \
+ cfi_adjust_cfa_offset (4); \
+ cfi_rel_offset (REG, 0)
+
+#define CFI_POP(REG) \
+ cfi_adjust_cfa_offset (-4); \
+ cfi_restore (REG)
+
+#define PUSH(REG) pushl REG; CFI_PUSH (REG)
+#define POP(REG) popl REG; CFI_POP (REG)
+
+#ifdef USE_AS_BZERO32
+# define DEST PARMS
+# define LEN DEST+4
+# define SETRTNVAL
+#else
+# define DEST PARMS
+# define DWDS DEST+4
+# define LEN DWDS+4
+# define SETRTNVAL movl DEST(%esp), %eax
+#endif
+
+#if (defined SHARED || defined __PIC__)
+# define ENTRANCE PUSH (%ebx);
+# define RETURN_END POP (%ebx); ret
+# define RETURN RETURN_END; CFI_PUSH (%ebx)
+# define PARMS 8 /* Preserve EBX. */
+# define JMPTBL(I, B) I - B
+
+/* Load an entry in a jump table into EBX and branch to it. TABLE is a
+ jump table with relative offsets. */
+# define BRANCH_TO_JMPTBL_ENTRY(TABLE) \
+ /* We first load PC into EBX. */ \
+ call __x86.get_pc_thunk.bx; \
+ /* Get the address of the jump table. */ \
+ add $(TABLE - .), %ebx; \
+ /* Get the entry and convert the relative offset to the \
+ absolute address. */ \
+ add (%ebx,%ecx,4), %ebx; \
+ /* We loaded the jump table and adjuested EDX. Go. */ \
+ jmp *%ebx
+
+ .section .gnu.linkonce.t.__x86.get_pc_thunk.bx,"ax",@progbits
+ .globl __x86.get_pc_thunk.bx
+ .hidden __x86.get_pc_thunk.bx
+ ALIGN (4)
+ .type __x86.get_pc_thunk.bx,@function
+__x86.get_pc_thunk.bx:
+ movl (%esp), %ebx
+ ret
+#else
+# define ENTRANCE
+# define RETURN_END ret
+# define RETURN RETURN_END
+# define PARMS 4
+# define JMPTBL(I, B) I
+
+/* Branch to an entry in a jump table. TABLE is a jump table with
+ absolute offsets. */
+# define BRANCH_TO_JMPTBL_ENTRY(TABLE) \
+ jmp *TABLE(,%ecx,4)
+#endif
+
+ .section .text.sse2,"ax",@progbits
+ ALIGN (4)
+ENTRY (MEMSET)
+ ENTRANCE
+
+ movl LEN(%esp), %ecx
+ shr $2, %ecx
+#ifdef USE_AS_BZERO32
+ xor %eax, %eax
+#else
+ mov DWDS(%esp), %eax
+ mov %eax, %edx
+#endif
+ movl DEST(%esp), %edx
+ cmp $16, %ecx
+ jae L(16dbwordsormore)
+
+L(write_less16dbwords):
+ lea (%edx, %ecx, 4), %edx
+ BRANCH_TO_JMPTBL_ENTRY (L(table_less16dbwords))
+
+ .pushsection .rodata.sse2,"a",@progbits
+ ALIGN (2)
+L(table_less16dbwords):
+ .int JMPTBL (L(write_0dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_1dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_2dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_3dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_4dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_5dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_6dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_7dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_8dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_9dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_10dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_11dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_12dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_13dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_14dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_15dbwords), L(table_less16dbwords))
+ .popsection
+
+ ALIGN (4)
+L(write_15dbwords):
+ movl %eax, -60(%edx)
+L(write_14dbwords):
+ movl %eax, -56(%edx)
+L(write_13dbwords):
+ movl %eax, -52(%edx)
+L(write_12dbwords):
+ movl %eax, -48(%edx)
+L(write_11dbwords):
+ movl %eax, -44(%edx)
+L(write_10dbwords):
+ movl %eax, -40(%edx)
+L(write_9dbwords):
+ movl %eax, -36(%edx)
+L(write_8dbwords):
+ movl %eax, -32(%edx)
+L(write_7dbwords):
+ movl %eax, -28(%edx)
+L(write_6dbwords):
+ movl %eax, -24(%edx)
+L(write_5dbwords):
+ movl %eax, -20(%edx)
+L(write_4dbwords):
+ movl %eax, -16(%edx)
+L(write_3dbwords):
+ movl %eax, -12(%edx)
+L(write_2dbwords):
+ movl %eax, -8(%edx)
+L(write_1dbwords):
+ movl %eax, -4(%edx)
+L(write_0dbwords):
+ SETRTNVAL
+ RETURN
+
+ ALIGN (4)
+L(16dbwordsormore):
+ test $3, %edx
+ jz L(aligned4bytes)
+ mov %eax, (%edx)
+ mov %eax, -4(%edx, %ecx, 4)
+ sub $1, %ecx
+ rol $24, %eax
+ add $1, %edx
+ test $3, %edx
+ jz L(aligned4bytes)
+ ror $8, %eax
+ add $1, %edx
+ test $3, %edx
+ jz L(aligned4bytes)
+ ror $8, %eax
+ add $1, %edx
+L(aligned4bytes):
+ shl $2, %ecx
+
+#ifdef USE_AS_BZERO32
+ pxor %xmm0, %xmm0
+#else
+ movd %eax, %xmm0
+ pshufd $0, %xmm0, %xmm0
+#endif
+ testl $0xf, %edx
+ jz L(aligned_16)
+/* ECX > 32 and EDX is not 16 byte aligned. */
+L(not_aligned_16):
+ movdqu %xmm0, (%edx)
+ movl %edx, %eax
+ and $-16, %edx
+ add $16, %edx
+ sub %edx, %eax
+ add %eax, %ecx
+ movd %xmm0, %eax
+ ALIGN (4)
+L(aligned_16):
+ cmp $128, %ecx
+ jae L(128bytesormore)
+
+L(aligned_16_less128bytes):
+ add %ecx, %edx
+ shr $2, %ecx
+ BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes))
+
+ ALIGN (4)
+L(128bytesormore):
+#ifdef SHARED_CACHE_SIZE
+ PUSH (%ebx)
+ mov $SHARED_CACHE_SIZE, %ebx
+#else
+# if (defined SHARED || defined __PIC__)
+ call __x86.get_pc_thunk.bx
+ add $_GLOBAL_OFFSET_TABLE_, %ebx
+ mov __x86_shared_cache_size@GOTOFF(%ebx), %ebx
+# else
+ PUSH (%ebx)
+ mov __x86_shared_cache_size, %ebx
+# endif
+#endif
+ cmp %ebx, %ecx
+ jae L(128bytesormore_nt_start)
+
+#ifdef DATA_CACHE_SIZE
+ POP (%ebx)
+# define RESTORE_EBX_STATE CFI_PUSH (%ebx)
+ cmp $DATA_CACHE_SIZE, %ecx
+#else
+# if (defined SHARED || defined __PIC__)
+# define RESTORE_EBX_STATE
+ call __x86.get_pc_thunk.bx
+ add $_GLOBAL_OFFSET_TABLE_, %ebx
+ cmp __x86_data_cache_size@GOTOFF(%ebx), %ecx
+# else
+ POP (%ebx)
+# define RESTORE_EBX_STATE CFI_PUSH (%ebx)
+ cmp __x86_data_cache_size, %ecx
+# endif
+#endif
+
+ jae L(128bytes_L2_normal)
+ subl $128, %ecx
+L(128bytesormore_normal):
+ sub $128, %ecx
+ movdqa %xmm0, (%edx)
+ movdqa %xmm0, 0x10(%edx)
+ movdqa %xmm0, 0x20(%edx)
+ movdqa %xmm0, 0x30(%edx)
+ movdqa %xmm0, 0x40(%edx)
+ movdqa %xmm0, 0x50(%edx)
+ movdqa %xmm0, 0x60(%edx)
+ movdqa %xmm0, 0x70(%edx)
+ lea 128(%edx), %edx
+ jb L(128bytesless_normal)
+
+
+ sub $128, %ecx
+ movdqa %xmm0, (%edx)
+ movdqa %xmm0, 0x10(%edx)
+ movdqa %xmm0, 0x20(%edx)
+ movdqa %xmm0, 0x30(%edx)
+ movdqa %xmm0, 0x40(%edx)
+ movdqa %xmm0, 0x50(%edx)
+ movdqa %xmm0, 0x60(%edx)
+ movdqa %xmm0, 0x70(%edx)
+ lea 128(%edx), %edx
+ jae L(128bytesormore_normal)
+
+L(128bytesless_normal):
+ lea 128(%ecx), %ecx
+ add %ecx, %edx
+ shr $2, %ecx
+ BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes))
+
+ ALIGN (4)
+L(128bytes_L2_normal):
+ prefetcht0 0x380(%edx)
+ prefetcht0 0x3c0(%edx)
+ sub $128, %ecx
+ movdqa %xmm0, (%edx)
+ movaps %xmm0, 0x10(%edx)
+ movaps %xmm0, 0x20(%edx)
+ movaps %xmm0, 0x30(%edx)
+ movaps %xmm0, 0x40(%edx)
+ movaps %xmm0, 0x50(%edx)
+ movaps %xmm0, 0x60(%edx)
+ movaps %xmm0, 0x70(%edx)
+ add $128, %edx
+ cmp $128, %ecx
+ jae L(128bytes_L2_normal)
+
+L(128bytesless_L2_normal):
+ add %ecx, %edx
+ shr $2, %ecx
+ BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes))
+
+ RESTORE_EBX_STATE
+L(128bytesormore_nt_start):
+ sub %ebx, %ecx
+ mov %ebx, %eax
+ and $0x7f, %eax
+ add %eax, %ecx
+ movd %xmm0, %eax
+ ALIGN (4)
+L(128bytesormore_shared_cache_loop):
+ prefetcht0 0x3c0(%edx)
+ prefetcht0 0x380(%edx)
+ sub $0x80, %ebx
+ movdqa %xmm0, (%edx)
+ movdqa %xmm0, 0x10(%edx)
+ movdqa %xmm0, 0x20(%edx)
+ movdqa %xmm0, 0x30(%edx)
+ movdqa %xmm0, 0x40(%edx)
+ movdqa %xmm0, 0x50(%edx)
+ movdqa %xmm0, 0x60(%edx)
+ movdqa %xmm0, 0x70(%edx)
+ add $0x80, %edx
+ cmp $0x80, %ebx
+ jae L(128bytesormore_shared_cache_loop)
+ cmp $0x80, %ecx
+ jb L(shared_cache_loop_end)
+
+ ALIGN (4)
+L(128bytesormore_nt):
+ sub $0x80, %ecx
+ movntdq %xmm0, (%edx)
+ movntdq %xmm0, 0x10(%edx)
+ movntdq %xmm0, 0x20(%edx)
+ movntdq %xmm0, 0x30(%edx)
+ movntdq %xmm0, 0x40(%edx)
+ movntdq %xmm0, 0x50(%edx)
+ movntdq %xmm0, 0x60(%edx)
+ movntdq %xmm0, 0x70(%edx)
+ add $0x80, %edx
+ cmp $0x80, %ecx
+ jae L(128bytesormore_nt)
+ sfence
+L(shared_cache_loop_end):
+#if defined DATA_CACHE_SIZE || !(defined SHARED || defined __PIC__)
+ POP (%ebx)
+#endif
+ add %ecx, %edx
+ shr $2, %ecx
+ BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes))
+
+ .pushsection .rodata.sse2,"a",@progbits
+ ALIGN (2)
+L(table_16_128bytes):
+ .int JMPTBL (L(aligned_16_0bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_4bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_8bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_12bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_16bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_20bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_24bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_28bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_32bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_36bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_40bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_44bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_48bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_52bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_56bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_60bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_64bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_68bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_72bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_76bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_80bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_84bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_88bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_92bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_96bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_100bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_104bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_108bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_112bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_116bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_120bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_124bytes), L(table_16_128bytes))
+ .popsection
+
+ ALIGN (4)
+L(aligned_16_112bytes):
+ movdqa %xmm0, -112(%edx)
+L(aligned_16_96bytes):
+ movdqa %xmm0, -96(%edx)
+L(aligned_16_80bytes):
+ movdqa %xmm0, -80(%edx)
+L(aligned_16_64bytes):
+ movdqa %xmm0, -64(%edx)
+L(aligned_16_48bytes):
+ movdqa %xmm0, -48(%edx)
+L(aligned_16_32bytes):
+ movdqa %xmm0, -32(%edx)
+L(aligned_16_16bytes):
+ movdqa %xmm0, -16(%edx)
+L(aligned_16_0bytes):
+ SETRTNVAL
+ RETURN
+
+ ALIGN (4)
+L(aligned_16_116bytes):
+ movdqa %xmm0, -116(%edx)
+L(aligned_16_100bytes):
+ movdqa %xmm0, -100(%edx)
+L(aligned_16_84bytes):
+ movdqa %xmm0, -84(%edx)
+L(aligned_16_68bytes):
+ movdqa %xmm0, -68(%edx)
+L(aligned_16_52bytes):
+ movdqa %xmm0, -52(%edx)
+L(aligned_16_36bytes):
+ movdqa %xmm0, -36(%edx)
+L(aligned_16_20bytes):
+ movdqa %xmm0, -20(%edx)
+L(aligned_16_4bytes):
+ movl %eax, -4(%edx)
+ SETRTNVAL
+ RETURN
+
+ ALIGN (4)
+L(aligned_16_120bytes):
+ movdqa %xmm0, -120(%edx)
+L(aligned_16_104bytes):
+ movdqa %xmm0, -104(%edx)
+L(aligned_16_88bytes):
+ movdqa %xmm0, -88(%edx)
+L(aligned_16_72bytes):
+ movdqa %xmm0, -72(%edx)
+L(aligned_16_56bytes):
+ movdqa %xmm0, -56(%edx)
+L(aligned_16_40bytes):
+ movdqa %xmm0, -40(%edx)
+L(aligned_16_24bytes):
+ movdqa %xmm0, -24(%edx)
+L(aligned_16_8bytes):
+ movq %xmm0, -8(%edx)
+ SETRTNVAL
+ RETURN
+
+ ALIGN (4)
+L(aligned_16_124bytes):
+ movdqa %xmm0, -124(%edx)
+L(aligned_16_108bytes):
+ movdqa %xmm0, -108(%edx)
+L(aligned_16_92bytes):
+ movdqa %xmm0, -92(%edx)
+L(aligned_16_76bytes):
+ movdqa %xmm0, -76(%edx)
+L(aligned_16_60bytes):
+ movdqa %xmm0, -60(%edx)
+L(aligned_16_44bytes):
+ movdqa %xmm0, -44(%edx)
+L(aligned_16_28bytes):
+ movdqa %xmm0, -28(%edx)
+L(aligned_16_12bytes):
+ movq %xmm0, -12(%edx)
+ movl %eax, -4(%edx)
+ SETRTNVAL
+ RETURN
+
+END (MEMSET)
diff --git a/libcutils/arch-x86/cache.h b/libcutils/arch-x86/cache.h
new file mode 100644
index 0000000..1c22fea
--- /dev/null
+++ b/libcutils/arch-x86/cache.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if defined(__slm__)
+/* Values are optimized for Silvermont */
+#define SHARED_CACHE_SIZE (1024*1024) /* Silvermont L2 Cache */
+#define DATA_CACHE_SIZE (24*1024) /* Silvermont L1 Data Cache */
+#else
+/* Values are optimized for Atom */
+#define SHARED_CACHE_SIZE (512*1024) /* Atom L2 Cache */
+#define DATA_CACHE_SIZE (24*1024) /* Atom L1 Data Cache */
+#endif
+
+#define SHARED_CACHE_SIZE_HALF (SHARED_CACHE_SIZE / 2)
+#define DATA_CACHE_SIZE_HALF (DATA_CACHE_SIZE / 2)
diff --git a/libcutils/arch-x86_64/android_memset16.S b/libcutils/arch-x86_64/android_memset16.S
new file mode 100644
index 0000000..cb6d4a3
--- /dev/null
+++ b/libcutils/arch-x86_64/android_memset16.S
@@ -0,0 +1,565 @@
+/*
+ * Copyright (C) 2014 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 "cache.h"
+
+#ifndef MEMSET
+# define MEMSET android_memset16
+#endif
+
+#ifndef L
+# define L(label) .L##label
+#endif
+
+#ifndef ALIGN
+# define ALIGN(n) .p2align n
+#endif
+
+#ifndef cfi_startproc
+# define cfi_startproc .cfi_startproc
+#endif
+
+#ifndef cfi_endproc
+# define cfi_endproc .cfi_endproc
+#endif
+
+#ifndef ENTRY
+# define ENTRY(name) \
+ .type name, @function; \
+ .globl name; \
+ .p2align 4; \
+name: \
+ cfi_startproc
+#endif
+
+#ifndef END
+# define END(name) \
+ cfi_endproc; \
+ .size name, .-name
+#endif
+
+#define JMPTBL(I, B) I - B
+
+/* Branch to an entry in a jump table. TABLE is a jump table with
+ relative offsets. INDEX is a register contains the index into the
+ jump table. SCALE is the scale of INDEX. */
+#define BRANCH_TO_JMPTBL_ENTRY(TABLE, INDEX, SCALE) \
+ lea TABLE(%rip), %r11; \
+ movslq (%r11, INDEX, SCALE), INDEX; \
+ lea (%r11, INDEX), INDEX; \
+ jmp *INDEX
+
+ .section .text.sse2,"ax",@progbits
+ ALIGN (4)
+ENTRY (MEMSET) // Address in rdi
+ shr $1, %rdx // Count in rdx
+ movzwl %si, %ecx
+ /* Fill the whole ECX with pattern. */
+ shl $16, %esi
+ or %esi, %ecx // Pattern in ecx
+
+ cmp $32, %rdx
+ jae L(32wordsormore)
+
+L(write_less32words):
+ lea (%rdi, %rdx, 2), %rdi
+ BRANCH_TO_JMPTBL_ENTRY (L(table_less32words), %rdx, 4)
+
+ .pushsection .rodata.sse2,"a",@progbits
+ ALIGN (2)
+L(table_less32words):
+ .int JMPTBL (L(write_0words), L(table_less32words))
+ .int JMPTBL (L(write_1words), L(table_less32words))
+ .int JMPTBL (L(write_2words), L(table_less32words))
+ .int JMPTBL (L(write_3words), L(table_less32words))
+ .int JMPTBL (L(write_4words), L(table_less32words))
+ .int JMPTBL (L(write_5words), L(table_less32words))
+ .int JMPTBL (L(write_6words), L(table_less32words))
+ .int JMPTBL (L(write_7words), L(table_less32words))
+ .int JMPTBL (L(write_8words), L(table_less32words))
+ .int JMPTBL (L(write_9words), L(table_less32words))
+ .int JMPTBL (L(write_10words), L(table_less32words))
+ .int JMPTBL (L(write_11words), L(table_less32words))
+ .int JMPTBL (L(write_12words), L(table_less32words))
+ .int JMPTBL (L(write_13words), L(table_less32words))
+ .int JMPTBL (L(write_14words), L(table_less32words))
+ .int JMPTBL (L(write_15words), L(table_less32words))
+ .int JMPTBL (L(write_16words), L(table_less32words))
+ .int JMPTBL (L(write_17words), L(table_less32words))
+ .int JMPTBL (L(write_18words), L(table_less32words))
+ .int JMPTBL (L(write_19words), L(table_less32words))
+ .int JMPTBL (L(write_20words), L(table_less32words))
+ .int JMPTBL (L(write_21words), L(table_less32words))
+ .int JMPTBL (L(write_22words), L(table_less32words))
+ .int JMPTBL (L(write_23words), L(table_less32words))
+ .int JMPTBL (L(write_24words), L(table_less32words))
+ .int JMPTBL (L(write_25words), L(table_less32words))
+ .int JMPTBL (L(write_26words), L(table_less32words))
+ .int JMPTBL (L(write_27words), L(table_less32words))
+ .int JMPTBL (L(write_28words), L(table_less32words))
+ .int JMPTBL (L(write_29words), L(table_less32words))
+ .int JMPTBL (L(write_30words), L(table_less32words))
+ .int JMPTBL (L(write_31words), L(table_less32words))
+ .popsection
+
+ ALIGN (4)
+L(write_28words):
+ movl %ecx, -56(%rdi)
+ movl %ecx, -52(%rdi)
+L(write_24words):
+ movl %ecx, -48(%rdi)
+ movl %ecx, -44(%rdi)
+L(write_20words):
+ movl %ecx, -40(%rdi)
+ movl %ecx, -36(%rdi)
+L(write_16words):
+ movl %ecx, -32(%rdi)
+ movl %ecx, -28(%rdi)
+L(write_12words):
+ movl %ecx, -24(%rdi)
+ movl %ecx, -20(%rdi)
+L(write_8words):
+ movl %ecx, -16(%rdi)
+ movl %ecx, -12(%rdi)
+L(write_4words):
+ movl %ecx, -8(%rdi)
+ movl %ecx, -4(%rdi)
+L(write_0words):
+ ret
+
+ ALIGN (4)
+L(write_29words):
+ movl %ecx, -58(%rdi)
+ movl %ecx, -54(%rdi)
+L(write_25words):
+ movl %ecx, -50(%rdi)
+ movl %ecx, -46(%rdi)
+L(write_21words):
+ movl %ecx, -42(%rdi)
+ movl %ecx, -38(%rdi)
+L(write_17words):
+ movl %ecx, -34(%rdi)
+ movl %ecx, -30(%rdi)
+L(write_13words):
+ movl %ecx, -26(%rdi)
+ movl %ecx, -22(%rdi)
+L(write_9words):
+ movl %ecx, -18(%rdi)
+ movl %ecx, -14(%rdi)
+L(write_5words):
+ movl %ecx, -10(%rdi)
+ movl %ecx, -6(%rdi)
+L(write_1words):
+ mov %cx, -2(%rdi)
+ ret
+
+ ALIGN (4)
+L(write_30words):
+ movl %ecx, -60(%rdi)
+ movl %ecx, -56(%rdi)
+L(write_26words):
+ movl %ecx, -52(%rdi)
+ movl %ecx, -48(%rdi)
+L(write_22words):
+ movl %ecx, -44(%rdi)
+ movl %ecx, -40(%rdi)
+L(write_18words):
+ movl %ecx, -36(%rdi)
+ movl %ecx, -32(%rdi)
+L(write_14words):
+ movl %ecx, -28(%rdi)
+ movl %ecx, -24(%rdi)
+L(write_10words):
+ movl %ecx, -20(%rdi)
+ movl %ecx, -16(%rdi)
+L(write_6words):
+ movl %ecx, -12(%rdi)
+ movl %ecx, -8(%rdi)
+L(write_2words):
+ movl %ecx, -4(%rdi)
+ ret
+
+ ALIGN (4)
+L(write_31words):
+ movl %ecx, -62(%rdi)
+ movl %ecx, -58(%rdi)
+L(write_27words):
+ movl %ecx, -54(%rdi)
+ movl %ecx, -50(%rdi)
+L(write_23words):
+ movl %ecx, -46(%rdi)
+ movl %ecx, -42(%rdi)
+L(write_19words):
+ movl %ecx, -38(%rdi)
+ movl %ecx, -34(%rdi)
+L(write_15words):
+ movl %ecx, -30(%rdi)
+ movl %ecx, -26(%rdi)
+L(write_11words):
+ movl %ecx, -22(%rdi)
+ movl %ecx, -18(%rdi)
+L(write_7words):
+ movl %ecx, -14(%rdi)
+ movl %ecx, -10(%rdi)
+L(write_3words):
+ movl %ecx, -6(%rdi)
+ movw %cx, -2(%rdi)
+ ret
+
+ ALIGN (4)
+L(32wordsormore):
+ shl $1, %rdx
+ test $0x01, %edi
+ jz L(aligned2bytes)
+ mov %ecx, (%rdi)
+ mov %ecx, -4(%rdi, %rdx)
+ sub $2, %rdx
+ add $1, %rdi
+ rol $8, %ecx
+L(aligned2bytes):
+ /* Fill xmm0 with the pattern. */
+ movd %ecx, %xmm0
+ pshufd $0, %xmm0, %xmm0
+
+ testl $0xf, %edi
+ jz L(aligned_16)
+/* RDX > 32 and RDI is not 16 byte aligned. */
+ movdqu %xmm0, (%rdi)
+ mov %rdi, %rsi
+ and $-16, %rdi
+ add $16, %rdi
+ sub %rdi, %rsi
+ add %rsi, %rdx
+
+ ALIGN (4)
+L(aligned_16):
+ cmp $128, %rdx
+ jge L(128bytesormore)
+
+L(aligned_16_less128bytes):
+ add %rdx, %rdi
+ shr $1, %rdx
+ BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes), %rdx, 4)
+
+ ALIGN (4)
+L(128bytesormore):
+ cmp $SHARED_CACHE_SIZE, %rdx
+ jg L(128bytesormore_nt)
+
+L(128bytesormore_normal):
+ sub $128, %rdx
+ movdqa %xmm0, (%rdi)
+ movdqa %xmm0, 0x10(%rdi)
+ movdqa %xmm0, 0x20(%rdi)
+ movdqa %xmm0, 0x30(%rdi)
+ movdqa %xmm0, 0x40(%rdi)
+ movdqa %xmm0, 0x50(%rdi)
+ movdqa %xmm0, 0x60(%rdi)
+ movdqa %xmm0, 0x70(%rdi)
+ lea 128(%rdi), %rdi
+ cmp $128, %rdx
+ jl L(128bytesless_normal)
+
+ sub $128, %rdx
+ movdqa %xmm0, (%rdi)
+ movdqa %xmm0, 0x10(%rdi)
+ movdqa %xmm0, 0x20(%rdi)
+ movdqa %xmm0, 0x30(%rdi)
+ movdqa %xmm0, 0x40(%rdi)
+ movdqa %xmm0, 0x50(%rdi)
+ movdqa %xmm0, 0x60(%rdi)
+ movdqa %xmm0, 0x70(%rdi)
+ lea 128(%rdi), %rdi
+ cmp $128, %rdx
+ jl L(128bytesless_normal)
+
+ sub $128, %rdx
+ movdqa %xmm0, (%rdi)
+ movdqa %xmm0, 0x10(%rdi)
+ movdqa %xmm0, 0x20(%rdi)
+ movdqa %xmm0, 0x30(%rdi)
+ movdqa %xmm0, 0x40(%rdi)
+ movdqa %xmm0, 0x50(%rdi)
+ movdqa %xmm0, 0x60(%rdi)
+ movdqa %xmm0, 0x70(%rdi)
+ lea 128(%rdi), %rdi
+ cmp $128, %rdx
+ jl L(128bytesless_normal)
+
+ sub $128, %rdx
+ movdqa %xmm0, (%rdi)
+ movdqa %xmm0, 0x10(%rdi)
+ movdqa %xmm0, 0x20(%rdi)
+ movdqa %xmm0, 0x30(%rdi)
+ movdqa %xmm0, 0x40(%rdi)
+ movdqa %xmm0, 0x50(%rdi)
+ movdqa %xmm0, 0x60(%rdi)
+ movdqa %xmm0, 0x70(%rdi)
+ lea 128(%rdi), %rdi
+ cmp $128, %rdx
+ jge L(128bytesormore_normal)
+
+L(128bytesless_normal):
+ add %rdx, %rdi
+ shr $1, %rdx
+ BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes), %rdx, 4)
+
+ ALIGN (4)
+L(128bytesormore_nt):
+ sub $128, %rdx
+ movntdq %xmm0, (%rdi)
+ movntdq %xmm0, 0x10(%rdi)
+ movntdq %xmm0, 0x20(%rdi)
+ movntdq %xmm0, 0x30(%rdi)
+ movntdq %xmm0, 0x40(%rdi)
+ movntdq %xmm0, 0x50(%rdi)
+ movntdq %xmm0, 0x60(%rdi)
+ movntdq %xmm0, 0x70(%rdi)
+ lea 128(%rdi), %rdi
+ cmp $128, %rdx
+ jge L(128bytesormore_nt)
+
+ sfence
+ add %rdx, %rdi
+ shr $1, %rdx
+ BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes), %rdx, 4)
+
+ .pushsection .rodata.sse2,"a",@progbits
+ ALIGN (2)
+L(table_16_128bytes):
+ .int JMPTBL (L(aligned_16_0bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_2bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_4bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_6bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_8bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_10bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_12bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_14bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_16bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_18bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_20bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_22bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_24bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_26bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_28bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_30bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_32bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_34bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_36bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_38bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_40bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_42bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_44bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_46bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_48bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_50bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_52bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_54bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_56bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_58bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_60bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_62bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_64bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_66bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_68bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_70bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_72bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_74bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_76bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_78bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_80bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_82bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_84bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_86bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_88bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_90bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_92bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_94bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_96bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_98bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_100bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_102bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_104bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_106bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_108bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_110bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_112bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_114bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_116bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_118bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_120bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_122bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_124bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_126bytes), L(table_16_128bytes))
+ .popsection
+
+ ALIGN (4)
+L(aligned_16_112bytes):
+ movdqa %xmm0, -112(%rdi)
+L(aligned_16_96bytes):
+ movdqa %xmm0, -96(%rdi)
+L(aligned_16_80bytes):
+ movdqa %xmm0, -80(%rdi)
+L(aligned_16_64bytes):
+ movdqa %xmm0, -64(%rdi)
+L(aligned_16_48bytes):
+ movdqa %xmm0, -48(%rdi)
+L(aligned_16_32bytes):
+ movdqa %xmm0, -32(%rdi)
+L(aligned_16_16bytes):
+ movdqa %xmm0, -16(%rdi)
+L(aligned_16_0bytes):
+ ret
+
+ ALIGN (4)
+L(aligned_16_114bytes):
+ movdqa %xmm0, -114(%rdi)
+L(aligned_16_98bytes):
+ movdqa %xmm0, -98(%rdi)
+L(aligned_16_82bytes):
+ movdqa %xmm0, -82(%rdi)
+L(aligned_16_66bytes):
+ movdqa %xmm0, -66(%rdi)
+L(aligned_16_50bytes):
+ movdqa %xmm0, -50(%rdi)
+L(aligned_16_34bytes):
+ movdqa %xmm0, -34(%rdi)
+L(aligned_16_18bytes):
+ movdqa %xmm0, -18(%rdi)
+L(aligned_16_2bytes):
+ movw %cx, -2(%rdi)
+ ret
+
+ ALIGN (4)
+L(aligned_16_116bytes):
+ movdqa %xmm0, -116(%rdi)
+L(aligned_16_100bytes):
+ movdqa %xmm0, -100(%rdi)
+L(aligned_16_84bytes):
+ movdqa %xmm0, -84(%rdi)
+L(aligned_16_68bytes):
+ movdqa %xmm0, -68(%rdi)
+L(aligned_16_52bytes):
+ movdqa %xmm0, -52(%rdi)
+L(aligned_16_36bytes):
+ movdqa %xmm0, -36(%rdi)
+L(aligned_16_20bytes):
+ movdqa %xmm0, -20(%rdi)
+L(aligned_16_4bytes):
+ movl %ecx, -4(%rdi)
+ ret
+
+ ALIGN (4)
+L(aligned_16_118bytes):
+ movdqa %xmm0, -118(%rdi)
+L(aligned_16_102bytes):
+ movdqa %xmm0, -102(%rdi)
+L(aligned_16_86bytes):
+ movdqa %xmm0, -86(%rdi)
+L(aligned_16_70bytes):
+ movdqa %xmm0, -70(%rdi)
+L(aligned_16_54bytes):
+ movdqa %xmm0, -54(%rdi)
+L(aligned_16_38bytes):
+ movdqa %xmm0, -38(%rdi)
+L(aligned_16_22bytes):
+ movdqa %xmm0, -22(%rdi)
+L(aligned_16_6bytes):
+ movl %ecx, -6(%rdi)
+ movw %cx, -2(%rdi)
+ ret
+
+ ALIGN (4)
+L(aligned_16_120bytes):
+ movdqa %xmm0, -120(%rdi)
+L(aligned_16_104bytes):
+ movdqa %xmm0, -104(%rdi)
+L(aligned_16_88bytes):
+ movdqa %xmm0, -88(%rdi)
+L(aligned_16_72bytes):
+ movdqa %xmm0, -72(%rdi)
+L(aligned_16_56bytes):
+ movdqa %xmm0, -56(%rdi)
+L(aligned_16_40bytes):
+ movdqa %xmm0, -40(%rdi)
+L(aligned_16_24bytes):
+ movdqa %xmm0, -24(%rdi)
+L(aligned_16_8bytes):
+ movq %xmm0, -8(%rdi)
+ ret
+
+ ALIGN (4)
+L(aligned_16_122bytes):
+ movdqa %xmm0, -122(%rdi)
+L(aligned_16_106bytes):
+ movdqa %xmm0, -106(%rdi)
+L(aligned_16_90bytes):
+ movdqa %xmm0, -90(%rdi)
+L(aligned_16_74bytes):
+ movdqa %xmm0, -74(%rdi)
+L(aligned_16_58bytes):
+ movdqa %xmm0, -58(%rdi)
+L(aligned_16_42bytes):
+ movdqa %xmm0, -42(%rdi)
+L(aligned_16_26bytes):
+ movdqa %xmm0, -26(%rdi)
+L(aligned_16_10bytes):
+ movq %xmm0, -10(%rdi)
+ movw %cx, -2(%rdi)
+ ret
+
+ ALIGN (4)
+L(aligned_16_124bytes):
+ movdqa %xmm0, -124(%rdi)
+L(aligned_16_108bytes):
+ movdqa %xmm0, -108(%rdi)
+L(aligned_16_92bytes):
+ movdqa %xmm0, -92(%rdi)
+L(aligned_16_76bytes):
+ movdqa %xmm0, -76(%rdi)
+L(aligned_16_60bytes):
+ movdqa %xmm0, -60(%rdi)
+L(aligned_16_44bytes):
+ movdqa %xmm0, -44(%rdi)
+L(aligned_16_28bytes):
+ movdqa %xmm0, -28(%rdi)
+L(aligned_16_12bytes):
+ movq %xmm0, -12(%rdi)
+ movl %ecx, -4(%rdi)
+ ret
+
+ ALIGN (4)
+L(aligned_16_126bytes):
+ movdqa %xmm0, -126(%rdi)
+L(aligned_16_110bytes):
+ movdqa %xmm0, -110(%rdi)
+L(aligned_16_94bytes):
+ movdqa %xmm0, -94(%rdi)
+L(aligned_16_78bytes):
+ movdqa %xmm0, -78(%rdi)
+L(aligned_16_62bytes):
+ movdqa %xmm0, -62(%rdi)
+L(aligned_16_46bytes):
+ movdqa %xmm0, -46(%rdi)
+L(aligned_16_30bytes):
+ movdqa %xmm0, -30(%rdi)
+L(aligned_16_14bytes):
+ movq %xmm0, -14(%rdi)
+ movl %ecx, -6(%rdi)
+ movw %cx, -2(%rdi)
+ ret
+
+END (MEMSET)
diff --git a/libcutils/arch-x86_64/android_memset32.S b/libcutils/arch-x86_64/android_memset32.S
new file mode 100644
index 0000000..1514aa2
--- /dev/null
+++ b/libcutils/arch-x86_64/android_memset32.S
@@ -0,0 +1,373 @@
+/*
+ * Copyright (C) 2014 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 "cache.h"
+
+#ifndef MEMSET
+# define MEMSET android_memset32
+#endif
+
+#ifndef L
+# define L(label) .L##label
+#endif
+
+#ifndef ALIGN
+# define ALIGN(n) .p2align n
+#endif
+
+#ifndef cfi_startproc
+# define cfi_startproc .cfi_startproc
+#endif
+
+#ifndef cfi_endproc
+# define cfi_endproc .cfi_endproc
+#endif
+
+#ifndef ENTRY
+# define ENTRY(name) \
+ .type name, @function; \
+ .globl name; \
+ .p2align 4; \
+name: \
+ cfi_startproc
+#endif
+
+#ifndef END
+# define END(name) \
+ cfi_endproc; \
+ .size name, .-name
+#endif
+
+#define JMPTBL(I, B) I - B
+
+/* Branch to an entry in a jump table. TABLE is a jump table with
+ relative offsets. INDEX is a register contains the index into the
+ jump table. SCALE is the scale of INDEX. */
+#define BRANCH_TO_JMPTBL_ENTRY(TABLE, INDEX, SCALE) \
+ lea TABLE(%rip), %r11; \
+ movslq (%r11, INDEX, SCALE), INDEX; \
+ lea (%r11, INDEX), INDEX; \
+ jmp *INDEX
+
+ .section .text.sse2,"ax",@progbits
+ ALIGN (4)
+ENTRY (MEMSET) // Address in rdi
+ shr $2, %rdx // Count in rdx
+ movl %esi, %ecx // Pattern in ecx
+
+ cmp $16, %rdx
+ jae L(16dbwordsormore)
+
+L(write_less16dbwords):
+ lea (%rdi, %rdx, 4), %rdi
+ BRANCH_TO_JMPTBL_ENTRY (L(table_less16dbwords), %rdx, 4)
+
+ .pushsection .rodata.sse2,"a",@progbits
+ ALIGN (2)
+L(table_less16dbwords):
+ .int JMPTBL (L(write_0dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_1dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_2dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_3dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_4dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_5dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_6dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_7dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_8dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_9dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_10dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_11dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_12dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_13dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_14dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_15dbwords), L(table_less16dbwords))
+ .popsection
+
+ ALIGN (4)
+L(write_15dbwords):
+ movl %ecx, -60(%rdi)
+L(write_14dbwords):
+ movl %ecx, -56(%rdi)
+L(write_13dbwords):
+ movl %ecx, -52(%rdi)
+L(write_12dbwords):
+ movl %ecx, -48(%rdi)
+L(write_11dbwords):
+ movl %ecx, -44(%rdi)
+L(write_10dbwords):
+ movl %ecx, -40(%rdi)
+L(write_9dbwords):
+ movl %ecx, -36(%rdi)
+L(write_8dbwords):
+ movl %ecx, -32(%rdi)
+L(write_7dbwords):
+ movl %ecx, -28(%rdi)
+L(write_6dbwords):
+ movl %ecx, -24(%rdi)
+L(write_5dbwords):
+ movl %ecx, -20(%rdi)
+L(write_4dbwords):
+ movl %ecx, -16(%rdi)
+L(write_3dbwords):
+ movl %ecx, -12(%rdi)
+L(write_2dbwords):
+ movl %ecx, -8(%rdi)
+L(write_1dbwords):
+ movl %ecx, -4(%rdi)
+L(write_0dbwords):
+ ret
+
+ ALIGN (4)
+L(16dbwordsormore):
+ test $3, %edi
+ jz L(aligned4bytes)
+ mov %ecx, (%rdi)
+ mov %ecx, -4(%rdi, %rdx, 4)
+ sub $1, %rdx
+ rol $24, %ecx
+ add $1, %rdi
+ test $3, %edi
+ jz L(aligned4bytes)
+ ror $8, %ecx
+ add $1, %rdi
+ test $3, %edi
+ jz L(aligned4bytes)
+ ror $8, %ecx
+ add $1, %rdi
+L(aligned4bytes):
+ shl $2, %rdx
+
+ /* Fill xmm0 with the pattern. */
+ movd %ecx, %xmm0
+ pshufd $0, %xmm0, %xmm0
+
+ testl $0xf, %edi
+ jz L(aligned_16)
+/* RDX > 32 and RDI is not 16 byte aligned. */
+ movdqu %xmm0, (%rdi)
+ mov %rdi, %rsi
+ and $-16, %rdi
+ add $16, %rdi
+ sub %rdi, %rsi
+ add %rsi, %rdx
+
+ ALIGN (4)
+L(aligned_16):
+ cmp $128, %rdx
+ jge L(128bytesormore)
+
+L(aligned_16_less128bytes):
+ add %rdx, %rdi
+ shr $2, %rdx
+ BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes), %rdx, 4)
+
+ ALIGN (4)
+L(128bytesormore):
+ cmp $SHARED_CACHE_SIZE, %rdx
+ jg L(128bytesormore_nt)
+
+L(128bytesormore_normal):
+ sub $128, %rdx
+ movdqa %xmm0, (%rdi)
+ movdqa %xmm0, 0x10(%rdi)
+ movdqa %xmm0, 0x20(%rdi)
+ movdqa %xmm0, 0x30(%rdi)
+ movdqa %xmm0, 0x40(%rdi)
+ movdqa %xmm0, 0x50(%rdi)
+ movdqa %xmm0, 0x60(%rdi)
+ movdqa %xmm0, 0x70(%rdi)
+ lea 128(%rdi), %rdi
+ cmp $128, %rdx
+ jl L(128bytesless_normal)
+
+ sub $128, %rdx
+ movdqa %xmm0, (%rdi)
+ movdqa %xmm0, 0x10(%rdi)
+ movdqa %xmm0, 0x20(%rdi)
+ movdqa %xmm0, 0x30(%rdi)
+ movdqa %xmm0, 0x40(%rdi)
+ movdqa %xmm0, 0x50(%rdi)
+ movdqa %xmm0, 0x60(%rdi)
+ movdqa %xmm0, 0x70(%rdi)
+ lea 128(%rdi), %rdi
+ cmp $128, %rdx
+ jl L(128bytesless_normal)
+
+ sub $128, %rdx
+ movdqa %xmm0, (%rdi)
+ movdqa %xmm0, 0x10(%rdi)
+ movdqa %xmm0, 0x20(%rdi)
+ movdqa %xmm0, 0x30(%rdi)
+ movdqa %xmm0, 0x40(%rdi)
+ movdqa %xmm0, 0x50(%rdi)
+ movdqa %xmm0, 0x60(%rdi)
+ movdqa %xmm0, 0x70(%rdi)
+ lea 128(%rdi), %rdi
+ cmp $128, %rdx
+ jl L(128bytesless_normal)
+
+ sub $128, %rdx
+ movdqa %xmm0, (%rdi)
+ movdqa %xmm0, 0x10(%rdi)
+ movdqa %xmm0, 0x20(%rdi)
+ movdqa %xmm0, 0x30(%rdi)
+ movdqa %xmm0, 0x40(%rdi)
+ movdqa %xmm0, 0x50(%rdi)
+ movdqa %xmm0, 0x60(%rdi)
+ movdqa %xmm0, 0x70(%rdi)
+ lea 128(%rdi), %rdi
+ cmp $128, %rdx
+ jge L(128bytesormore_normal)
+
+L(128bytesless_normal):
+ add %rdx, %rdi
+ shr $2, %rdx
+ BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes), %rdx, 4)
+
+ ALIGN (4)
+L(128bytesormore_nt):
+ sub $128, %rdx
+ movntdq %xmm0, (%rdi)
+ movntdq %xmm0, 0x10(%rdi)
+ movntdq %xmm0, 0x20(%rdi)
+ movntdq %xmm0, 0x30(%rdi)
+ movntdq %xmm0, 0x40(%rdi)
+ movntdq %xmm0, 0x50(%rdi)
+ movntdq %xmm0, 0x60(%rdi)
+ movntdq %xmm0, 0x70(%rdi)
+ lea 128(%rdi), %rdi
+ cmp $128, %rdx
+ jge L(128bytesormore_nt)
+
+ sfence
+ add %rdx, %rdi
+ shr $2, %rdx
+ BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes), %rdx, 4)
+
+ .pushsection .rodata.sse2,"a",@progbits
+ ALIGN (2)
+L(table_16_128bytes):
+ .int JMPTBL (L(aligned_16_0bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_4bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_8bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_12bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_16bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_20bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_24bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_28bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_32bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_36bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_40bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_44bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_48bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_52bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_56bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_60bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_64bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_68bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_72bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_76bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_80bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_84bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_88bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_92bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_96bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_100bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_104bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_108bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_112bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_116bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_120bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_124bytes), L(table_16_128bytes))
+ .popsection
+
+ ALIGN (4)
+L(aligned_16_112bytes):
+ movdqa %xmm0, -112(%rdi)
+L(aligned_16_96bytes):
+ movdqa %xmm0, -96(%rdi)
+L(aligned_16_80bytes):
+ movdqa %xmm0, -80(%rdi)
+L(aligned_16_64bytes):
+ movdqa %xmm0, -64(%rdi)
+L(aligned_16_48bytes):
+ movdqa %xmm0, -48(%rdi)
+L(aligned_16_32bytes):
+ movdqa %xmm0, -32(%rdi)
+L(aligned_16_16bytes):
+ movdqa %xmm0, -16(%rdi)
+L(aligned_16_0bytes):
+ ret
+
+ ALIGN (4)
+L(aligned_16_116bytes):
+ movdqa %xmm0, -116(%rdi)
+L(aligned_16_100bytes):
+ movdqa %xmm0, -100(%rdi)
+L(aligned_16_84bytes):
+ movdqa %xmm0, -84(%rdi)
+L(aligned_16_68bytes):
+ movdqa %xmm0, -68(%rdi)
+L(aligned_16_52bytes):
+ movdqa %xmm0, -52(%rdi)
+L(aligned_16_36bytes):
+ movdqa %xmm0, -36(%rdi)
+L(aligned_16_20bytes):
+ movdqa %xmm0, -20(%rdi)
+L(aligned_16_4bytes):
+ movl %ecx, -4(%rdi)
+ ret
+
+ ALIGN (4)
+L(aligned_16_120bytes):
+ movdqa %xmm0, -120(%rdi)
+L(aligned_16_104bytes):
+ movdqa %xmm0, -104(%rdi)
+L(aligned_16_88bytes):
+ movdqa %xmm0, -88(%rdi)
+L(aligned_16_72bytes):
+ movdqa %xmm0, -72(%rdi)
+L(aligned_16_56bytes):
+ movdqa %xmm0, -56(%rdi)
+L(aligned_16_40bytes):
+ movdqa %xmm0, -40(%rdi)
+L(aligned_16_24bytes):
+ movdqa %xmm0, -24(%rdi)
+L(aligned_16_8bytes):
+ movq %xmm0, -8(%rdi)
+ ret
+
+ ALIGN (4)
+L(aligned_16_124bytes):
+ movdqa %xmm0, -124(%rdi)
+L(aligned_16_108bytes):
+ movdqa %xmm0, -108(%rdi)
+L(aligned_16_92bytes):
+ movdqa %xmm0, -92(%rdi)
+L(aligned_16_76bytes):
+ movdqa %xmm0, -76(%rdi)
+L(aligned_16_60bytes):
+ movdqa %xmm0, -60(%rdi)
+L(aligned_16_44bytes):
+ movdqa %xmm0, -44(%rdi)
+L(aligned_16_28bytes):
+ movdqa %xmm0, -28(%rdi)
+L(aligned_16_12bytes):
+ movq %xmm0, -12(%rdi)
+ movl %ecx, -4(%rdi)
+ ret
+
+END (MEMSET)
diff --git a/libcutils/arch-x86_64/cache.h b/libcutils/arch-x86_64/cache.h
new file mode 100644
index 0000000..f144309
--- /dev/null
+++ b/libcutils/arch-x86_64/cache.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+/* Values are optimized for Silvermont */
+#define SHARED_CACHE_SIZE (1024*1024) /* Silvermont L2 Cache */
+#define DATA_CACHE_SIZE (24*1024) /* Silvermont L1 Data Cache */
+
+#define SHARED_CACHE_SIZE_HALF (SHARED_CACHE_SIZE / 2)
+#define DATA_CACHE_SIZE_HALF (DATA_CACHE_SIZE / 2)
diff --git a/libcutils/ashmem-dev.c b/libcutils/ashmem-dev.c
new file mode 100644
index 0000000..3089a94
--- /dev/null
+++ b/libcutils/ashmem-dev.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Implementation of the user-space ashmem API for devices, which have our
+ * ashmem-enabled kernel. See ashmem-sim.c for the "fake" tmp-based version,
+ * used by the simulator.
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+
+#include <linux/ashmem.h>
+#include <cutils/ashmem.h>
+
+#define ASHMEM_DEVICE "/dev/ashmem"
+
+/*
+ * ashmem_create_region - creates a new ashmem region and returns the file
+ * descriptor, or <0 on error
+ *
+ * `name' is an optional label to give the region (visible in /proc/pid/maps)
+ * `size' is the size of the region, in page-aligned bytes
+ */
+int ashmem_create_region(const char *name, size_t size)
+{
+ int fd, ret;
+
+ fd = open(ASHMEM_DEVICE, O_RDWR);
+ if (fd < 0)
+ return fd;
+
+ if (name) {
+ char buf[ASHMEM_NAME_LEN] = {0};
+
+ strlcpy(buf, name, sizeof(buf));
+ ret = ioctl(fd, ASHMEM_SET_NAME, buf);
+ if (ret < 0)
+ goto error;
+ }
+
+ ret = ioctl(fd, ASHMEM_SET_SIZE, size);
+ if (ret < 0)
+ goto error;
+
+ return fd;
+
+error:
+ close(fd);
+ return ret;
+}
+
+int ashmem_set_prot_region(int fd, int prot)
+{
+ return ioctl(fd, ASHMEM_SET_PROT_MASK, prot);
+}
+
+int ashmem_pin_region(int fd, size_t offset, size_t len)
+{
+ struct ashmem_pin pin = { offset, len };
+ return ioctl(fd, ASHMEM_PIN, &pin);
+}
+
+int ashmem_unpin_region(int fd, size_t offset, size_t len)
+{
+ struct ashmem_pin pin = { offset, len };
+ return ioctl(fd, ASHMEM_UNPIN, &pin);
+}
+
+int ashmem_get_size_region(int fd)
+{
+ return ioctl(fd, ASHMEM_GET_SIZE, NULL);
+}
diff --git a/libcutils/ashmem-host.c b/libcutils/ashmem-host.c
new file mode 100644
index 0000000..abc4f94
--- /dev/null
+++ b/libcutils/ashmem-host.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Implementation of the user-space ashmem API for the simulator, which lacks
+ * an ashmem-enabled kernel. See ashmem-dev.c for the real ashmem-based version.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <cutils/ashmem.h>
+#include <utils/Compat.h>
+
+#ifndef __unused
+#define __unused __attribute__((__unused__))
+#endif
+
+int ashmem_create_region(const char *ignored __unused, size_t size)
+{
+ char template[PATH_MAX];
+ snprintf(template, sizeof(template), "/tmp/android-ashmem-%d-XXXXXXXXX", getpid());
+ int fd = mkstemp(template);
+ if (fd != -1 && TEMP_FAILURE_RETRY(ftruncate(fd, size)) != -1 && unlink(template) != -1) {
+ return fd;
+ }
+ close(fd);
+ return -1;
+}
+
+int ashmem_set_prot_region(int fd __unused, int prot __unused)
+{
+ return 0;
+}
+
+int ashmem_pin_region(int fd __unused, size_t offset __unused, size_t len __unused)
+{
+ return ASHMEM_NOT_PURGED;
+}
+
+int ashmem_unpin_region(int fd __unused, size_t offset __unused, size_t len __unused)
+{
+ return ASHMEM_IS_UNPINNED;
+}
+
+int ashmem_get_size_region(int fd)
+{
+ struct stat buf;
+ int result = fstat(fd, &buf);
+ if (result == -1) {
+ return -1;
+ }
+
+ // Check if this is an "ashmem" region.
+ // TODO: This is very hacky, and can easily break. We need some reliable indicator.
+ if (!(buf.st_nlink == 0 && S_ISREG(buf.st_mode))) {
+ errno = ENOTTY;
+ return -1;
+ }
+
+ return buf.st_size;
+}
diff --git a/libcutils/atomic.c b/libcutils/atomic.c
new file mode 100644
index 0000000..d34aa00
--- /dev/null
+++ b/libcutils/atomic.c
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2007 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.
+ */
+
+/*
+ * Generate non-inlined versions of android_atomic functions.
+ * Nobody should be using these, but some binary blobs currently (late 2014)
+ * are.
+ * If you read this in 2015 or later, please try to delete this file.
+ */
+
+#define ANDROID_ATOMIC_INLINE
+
+#include <cutils/atomic.h>
diff --git a/libcutils/config_utils.c b/libcutils/config_utils.c
new file mode 100644
index 0000000..fc5ca78
--- /dev/null
+++ b/libcutils/config_utils.c
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2007 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 <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <cutils/config_utils.h>
+#include <cutils/misc.h>
+
+cnode* config_node(const char *name, const char *value)
+{
+ cnode *node;
+
+ node = calloc(sizeof(cnode), 1);
+ if(node) {
+ node->name = name ? name : "";
+ node->value = value ? value : "";
+ }
+
+ return node;
+}
+
+cnode* config_find(cnode *root, const char *name)
+{
+ cnode *node, *match = NULL;
+
+ /* we walk the whole list, as we need to return the last (newest) entry */
+ for(node = root->first_child; node; node = node->next)
+ if(!strcmp(node->name, name))
+ match = node;
+
+ return match;
+}
+
+static cnode* _config_create(cnode *root, const char *name)
+{
+ cnode *node;
+
+ node = config_node(name, NULL);
+
+ if(root->last_child)
+ root->last_child->next = node;
+ else
+ root->first_child = node;
+
+ root->last_child = node;
+
+ return node;
+}
+
+int config_bool(cnode *root, const char *name, int _default)
+{
+ cnode *node;
+
+ node = config_find(root, name);
+ if(!node)
+ return _default;
+
+ switch(node->value[0]) {
+ case 'y':
+ case 'Y':
+ case '1':
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+const char* config_str(cnode *root, const char *name, const char *_default)
+{
+ cnode *node;
+
+ node = config_find(root, name);
+ if(!node)
+ return _default;
+ return node->value;
+}
+
+void config_set(cnode *root, const char *name, const char *value)
+{
+ cnode *node;
+
+ node = config_find(root, name);
+ if(node)
+ node->value = value;
+ else {
+ node = _config_create(root, name);
+ node->value = value;
+ }
+}
+
+#define T_EOF 0
+#define T_TEXT 1
+#define T_DOT 2
+#define T_OBRACE 3
+#define T_CBRACE 4
+
+typedef struct
+{
+ char *data;
+ char *text;
+ int len;
+ char next;
+} cstate;
+
+static int _lex(cstate *cs, int value)
+{
+ char c;
+ char *s;
+ char *data;
+
+ data = cs->data;
+
+ if(cs->next != 0) {
+ c = cs->next;
+ cs->next = 0;
+ goto got_c;
+ }
+
+restart:
+ for(;;) {
+ c = *data++;
+ got_c:
+ if(isspace(c))
+ continue;
+
+ switch(c) {
+ case 0:
+ return T_EOF;
+
+ case '#':
+ for(;;) {
+ switch(*data) {
+ case 0:
+ cs->data = data;
+ return T_EOF;
+ case '\n':
+ cs->data = data + 1;
+ goto restart;
+ default:
+ data++;
+ }
+ }
+ break;
+
+ case '.':
+ cs->data = data;
+ return T_DOT;
+
+ case '{':
+ cs->data = data;
+ return T_OBRACE;
+
+ case '}':
+ cs->data = data;
+ return T_CBRACE;
+
+ default:
+ s = data - 1;
+
+ if(value) {
+ for(;;) {
+ if(*data == 0) {
+ cs->data = data;
+ break;
+ }
+ if(*data == '\n') {
+ cs->data = data + 1;
+ *data-- = 0;
+ break;
+ }
+ data++;
+ }
+
+ /* strip trailing whitespace */
+ while(data > s){
+ if(!isspace(*data)) break;
+ *data-- = 0;
+ }
+
+ goto got_text;
+ } else {
+ for(;;) {
+ if(isspace(*data)) {
+ *data = 0;
+ cs->data = data + 1;
+ goto got_text;
+ }
+ switch(*data) {
+ case 0:
+ cs->data = data;
+ goto got_text;
+ case '.':
+ case '{':
+ case '}':
+ cs->next = *data;
+ *data = 0;
+ cs->data = data + 1;
+ goto got_text;
+ default:
+ data++;
+ }
+ }
+ }
+ }
+ }
+
+got_text:
+ cs->text = s;
+ return T_TEXT;
+}
+
+#if 0
+char *TOKENNAMES[] = { "EOF", "TEXT", "DOT", "OBRACE", "CBRACE" };
+
+static int lex(cstate *cs, int value)
+{
+ int tok = _lex(cs, value);
+ printf("TOKEN(%d) %s %s\n", value, TOKENNAMES[tok],
+ tok == T_TEXT ? cs->text : "");
+ return tok;
+}
+#else
+#define lex(cs,v) _lex(cs,v)
+#endif
+
+static int parse_expr(cstate *cs, cnode *node);
+
+static int parse_block(cstate *cs, cnode *node)
+{
+ for(;;){
+ switch(lex(cs, 0)){
+ case T_TEXT:
+ if(parse_expr(cs, node)) return -1;
+ continue;
+
+ case T_CBRACE:
+ return 0;
+
+ default:
+ return -1;
+ }
+ }
+}
+
+static int parse_expr(cstate *cs, cnode *root)
+{
+ cnode *node;
+
+ /* last token was T_TEXT */
+ node = config_find(root, cs->text);
+ if(!node || *node->value)
+ node = _config_create(root, cs->text);
+
+ for(;;) {
+ switch(lex(cs, 1)) {
+ case T_DOT:
+ if(lex(cs, 0) != T_TEXT)
+ return -1;
+ node = _config_create(node, cs->text);
+ continue;
+
+ case T_TEXT:
+ node->value = cs->text;
+ return 0;
+
+ case T_OBRACE:
+ return parse_block(cs, node);
+
+ default:
+ return -1;
+ }
+ }
+}
+
+void config_load(cnode *root, char *data)
+{
+ if(data != 0) {
+ cstate cs;
+ cs.data = data;
+ cs.next = 0;
+
+ for(;;) {
+ switch(lex(&cs, 0)) {
+ case T_TEXT:
+ if(parse_expr(&cs, root))
+ return;
+ break;
+ default:
+ return;
+ }
+ }
+ }
+}
+
+void config_load_file(cnode *root, const char *fn)
+{
+ char *data;
+ data = load_file(fn, 0);
+ config_load(root, data);
+}
+
+void config_free(cnode *root)
+{
+ cnode *cur = root->first_child;
+
+ while (cur) {
+ cnode *prev = cur;
+ config_free(cur);
+ cur = cur->next;
+ free(prev);
+ }
+}
diff --git a/libcutils/debugger.c b/libcutils/debugger.c
new file mode 100644
index 0000000..3407ec3
--- /dev/null
+++ b/libcutils/debugger.c
@@ -0,0 +1,129 @@
+/*
+ * 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 <stdbool.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <cutils/debugger.h>
+#include <cutils/sockets.h>
+
+#define LOG_TAG "DEBUG"
+#include <log/log.h>
+
+static int send_request(int sock_fd, void* msg_ptr, size_t msg_len) {
+ int result = 0;
+ if (TEMP_FAILURE_RETRY(write(sock_fd, msg_ptr, msg_len)) != (ssize_t) msg_len) {
+ result = -1;
+ } else {
+ char ack;
+ if (TEMP_FAILURE_RETRY(read(sock_fd, &ack, 1)) != 1) {
+ result = -1;
+ }
+ }
+ return result;
+}
+
+static int make_dump_request(debugger_action_t action, pid_t tid, int timeout_secs) {
+ debugger_msg_t msg;
+ memset(&msg, 0, sizeof(msg));
+ msg.tid = tid;
+ msg.action = action;
+
+ int sock_fd = socket_local_client(DEBUGGER_SOCKET_NAME, ANDROID_SOCKET_NAMESPACE_ABSTRACT,
+ SOCK_STREAM | SOCK_CLOEXEC);
+ if (sock_fd < 0) {
+ return -1;
+ }
+
+ if (timeout_secs > 0) {
+ struct timeval tm;
+ tm.tv_sec = timeout_secs;
+ tm.tv_usec = 0;
+ if (setsockopt(sock_fd, SOL_SOCKET, SO_RCVTIMEO, &tm, sizeof(tm)) == -1) {
+ ALOGE("WARNING: Cannot set receive timeout value on socket: %s", strerror(errno));
+ }
+
+ if (setsockopt(sock_fd, SOL_SOCKET, SO_SNDTIMEO, &tm, sizeof(tm)) == -1) {
+ ALOGE("WARNING: Cannot set send timeout value on socket: %s", strerror(errno));
+ }
+ }
+
+ if (send_request(sock_fd, &msg, sizeof(msg)) < 0) {
+ close(sock_fd);
+ return -1;
+ }
+
+ return sock_fd;
+}
+
+int dump_backtrace_to_file(pid_t tid, int fd) {
+ return dump_backtrace_to_file_timeout(tid, fd, 0);
+}
+
+int dump_backtrace_to_file_timeout(pid_t tid, int fd, int timeout_secs) {
+ int sock_fd = make_dump_request(DEBUGGER_ACTION_DUMP_BACKTRACE, tid, timeout_secs);
+ if (sock_fd < 0) {
+ return -1;
+ }
+
+ /* Write the data read from the socket to the fd. */
+ int result = 0;
+ char buffer[1024];
+ ssize_t n;
+ while ((n = TEMP_FAILURE_RETRY(read(sock_fd, buffer, sizeof(buffer)))) > 0) {
+ if (TEMP_FAILURE_RETRY(write(fd, buffer, n)) != n) {
+ result = -1;
+ break;
+ }
+ }
+ close(sock_fd);
+ return result;
+}
+
+int dump_tombstone(pid_t tid, char* pathbuf, size_t pathlen) {
+ return dump_tombstone_timeout(tid, pathbuf, pathlen, 0);
+}
+
+int dump_tombstone_timeout(pid_t tid, char* pathbuf, size_t pathlen, int timeout_secs) {
+ int sock_fd = make_dump_request(DEBUGGER_ACTION_DUMP_TOMBSTONE, tid, timeout_secs);
+ if (sock_fd < 0) {
+ return -1;
+ }
+
+ /* Read the tombstone file name. */
+ char buffer[100]; /* This is larger than the largest tombstone path. */
+ int result = 0;
+ ssize_t n = TEMP_FAILURE_RETRY(read(sock_fd, buffer, sizeof(buffer) - 1));
+ if (n <= 0) {
+ result = -1;
+ } else {
+ if (pathbuf && pathlen) {
+ if (n >= (ssize_t) pathlen) {
+ n = pathlen - 1;
+ }
+ buffer[n] = '\0';
+ memcpy(pathbuf, buffer, n + 1);
+ }
+ }
+ close(sock_fd);
+ return result;
+}
diff --git a/libcutils/dlmalloc_stubs.c b/libcutils/dlmalloc_stubs.c
new file mode 100644
index 0000000..2db473d
--- /dev/null
+++ b/libcutils/dlmalloc_stubs.c
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2007 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 "log/log.h"
+
+#define UNUSED __attribute__((__unused__))
+
+/*
+ * Stubs for functions defined in bionic/libc/bionic/dlmalloc.c. These
+ * are used in host builds, as the host libc will not contain these
+ * functions.
+ */
+void dlmalloc_inspect_all(void(*handler)(void*, void *, size_t, void*) UNUSED,
+ void* arg UNUSED)
+{
+ ALOGW("Called host unimplemented stub: dlmalloc_inspect_all");
+}
+
+int dlmalloc_trim(size_t unused UNUSED)
+{
+ ALOGW("Called host unimplemented stub: dlmalloc_trim");
+ return 0;
+}
diff --git a/libcutils/fs.c b/libcutils/fs.c
new file mode 100644
index 0000000..45c7add
--- /dev/null
+++ b/libcutils/fs.c
@@ -0,0 +1,237 @@
+/*
+ * 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 LOG_TAG "cutils"
+
+/* These defines are only needed because prebuilt headers are out of date */
+#define __USE_XOPEN2K8 1
+#define _ATFILE_SOURCE 1
+#define _GNU_SOURCE 1
+
+#include <cutils/fs.h>
+#include <cutils/log.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <dirent.h>
+
+#define ALL_PERMS (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
+#define BUF_SIZE 64
+
+int fs_prepare_dir(const char* path, mode_t mode, uid_t uid, gid_t gid) {
+ // Check if path needs to be created
+ struct stat sb;
+ if (TEMP_FAILURE_RETRY(lstat(path, &sb)) == -1) {
+ if (errno == ENOENT) {
+ goto create;
+ } else {
+ ALOGE("Failed to lstat(%s): %s", path, strerror(errno));
+ return -1;
+ }
+ }
+
+ // Exists, verify status
+ if (!S_ISDIR(sb.st_mode)) {
+ ALOGE("Not a directory: %s", path);
+ return -1;
+ }
+ if (((sb.st_mode & ALL_PERMS) == mode) && (sb.st_uid == uid) && (sb.st_gid == gid)) {
+ return 0;
+ } else {
+ goto fixup;
+ }
+
+create:
+ if (TEMP_FAILURE_RETRY(mkdir(path, mode)) == -1) {
+ if (errno != EEXIST) {
+ ALOGE("Failed to mkdir(%s): %s", path, strerror(errno));
+ return -1;
+ }
+ }
+
+fixup:
+ if (TEMP_FAILURE_RETRY(chmod(path, mode)) == -1) {
+ ALOGE("Failed to chmod(%s, %d): %s", path, mode, strerror(errno));
+ return -1;
+ }
+ if (TEMP_FAILURE_RETRY(chown(path, uid, gid)) == -1) {
+ ALOGE("Failed to chown(%s, %d, %d): %s", path, uid, gid, strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+int fs_read_atomic_int(const char* path, int* out_value) {
+ int fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY));
+ if (fd == -1) {
+ ALOGE("Failed to read %s: %s", path, strerror(errno));
+ return -1;
+ }
+
+ char buf[BUF_SIZE];
+ if (TEMP_FAILURE_RETRY(read(fd, buf, BUF_SIZE)) == -1) {
+ ALOGE("Failed to read %s: %s", path, strerror(errno));
+ goto fail;
+ }
+ if (sscanf(buf, "%d", out_value) != 1) {
+ ALOGE("Failed to parse %s: %s", path, strerror(errno));
+ goto fail;
+ }
+ close(fd);
+ return 0;
+
+fail:
+ close(fd);
+ *out_value = -1;
+ return -1;
+}
+
+int fs_write_atomic_int(const char* path, int value) {
+ char temp[PATH_MAX];
+ if (snprintf(temp, PATH_MAX, "%s.XXXXXX", path) >= PATH_MAX) {
+ ALOGE("Path too long");
+ return -1;
+ }
+
+ int fd = TEMP_FAILURE_RETRY(mkstemp(temp));
+ if (fd == -1) {
+ ALOGE("Failed to open %s: %s", temp, strerror(errno));
+ return -1;
+ }
+
+ char buf[BUF_SIZE];
+ int len = snprintf(buf, BUF_SIZE, "%d", value) + 1;
+ if (len > BUF_SIZE) {
+ ALOGE("Value %d too large: %s", value, strerror(errno));
+ goto fail;
+ }
+ if (TEMP_FAILURE_RETRY(write(fd, buf, len)) < len) {
+ ALOGE("Failed to write %s: %s", temp, strerror(errno));
+ goto fail;
+ }
+ if (close(fd) == -1) {
+ ALOGE("Failed to close %s: %s", temp, strerror(errno));
+ goto fail_closed;
+ }
+
+ if (rename(temp, path) == -1) {
+ ALOGE("Failed to rename %s to %s: %s", temp, path, strerror(errno));
+ goto fail_closed;
+ }
+
+ return 0;
+
+fail:
+ close(fd);
+fail_closed:
+ unlink(temp);
+ return -1;
+}
+
+#ifndef __APPLE__
+
+int fs_mkdirs(const char* path, mode_t mode) {
+ int res = 0;
+ int fd = 0;
+ struct stat sb;
+ char* buf = strdup(path);
+
+ if (*buf != '/') {
+ ALOGE("Relative paths are not allowed: %s", buf);
+ res = -EINVAL;
+ goto done;
+ }
+
+ if ((fd = open("/", 0)) == -1) {
+ ALOGE("Failed to open(/): %s", strerror(errno));
+ res = -errno;
+ goto done;
+ }
+
+ char* segment = buf + 1;
+ char* p = segment;
+ while (*p != '\0') {
+ if (*p == '/') {
+ *p = '\0';
+
+ if (!strcmp(segment, "..") || !strcmp(segment, ".") || !strcmp(segment, "")) {
+ ALOGE("Invalid path: %s", buf);
+ res = -EINVAL;
+ goto done_close;
+ }
+
+ if (fstatat(fd, segment, &sb, AT_SYMLINK_NOFOLLOW) != 0) {
+ if (errno == ENOENT) {
+ /* Nothing there yet; let's create it! */
+ if (mkdirat(fd, segment, mode) != 0) {
+ if (errno == EEXIST) {
+ /* We raced with someone; ignore */
+ } else {
+ ALOGE("Failed to mkdirat(%s): %s", buf, strerror(errno));
+ res = -errno;
+ goto done_close;
+ }
+ }
+ } else {
+ ALOGE("Failed to fstatat(%s): %s", buf, strerror(errno));
+ res = -errno;
+ goto done_close;
+ }
+ } else {
+ if (S_ISLNK(sb.st_mode)) {
+ ALOGE("Symbolic links are not allowed: %s", buf);
+ res = -ELOOP;
+ goto done_close;
+ }
+ if (!S_ISDIR(sb.st_mode)) {
+ ALOGE("Existing segment not a directory: %s", buf);
+ res = -ENOTDIR;
+ goto done_close;
+ }
+ }
+
+ /* Yay, segment is ready for us to step into */
+ int next_fd;
+ if ((next_fd = openat(fd, segment, O_NOFOLLOW | O_CLOEXEC)) == -1) {
+ ALOGE("Failed to openat(%s): %s", buf, strerror(errno));
+ res = -errno;
+ goto done_close;
+ }
+
+ close(fd);
+ fd = next_fd;
+
+ *p = '/';
+ segment = p + 1;
+ }
+ p++;
+ }
+
+done_close:
+ close(fd);
+done:
+ free(buf);
+ return res;
+}
+
+#endif
diff --git a/libcutils/fs_config.c b/libcutils/fs_config.c
new file mode 100644
index 0000000..1316339
--- /dev/null
+++ b/libcutils/fs_config.c
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* This file is used to define the properties of the filesystem
+** images generated by build tools (mkbootfs and mkyaffs2image) and
+** by the device side of adb.
+*/
+
+#define LOG_TAG "fs_config"
+
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <log/log.h>
+#include <private/android_filesystem_config.h>
+#include <utils/Compat.h>
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+/* The following structure is stored little endian */
+struct fs_path_config_from_file {
+ uint16_t len;
+ uint16_t mode;
+ uint16_t uid;
+ uint16_t gid;
+ uint64_t capabilities;
+ char prefix[];
+} __attribute__((__aligned__(sizeof(uint64_t))));
+
+/* My kingdom for <endian.h> */
+static inline uint16_t get2LE(const uint8_t* src)
+{
+ return src[0] | (src[1] << 8);
+}
+
+static inline uint64_t get8LE(const uint8_t* src)
+{
+ uint32_t low, high;
+
+ low = src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+ high = src[4] | (src[5] << 8) | (src[6] << 16) | (src[7] << 24);
+ return ((uint64_t) high << 32) | (uint64_t) low;
+}
+
+#define ALIGN(x, alignment) ( ((x) + ((alignment) - 1)) & ~((alignment) - 1) )
+
+/* Rules for directories.
+** These rules are applied based on "first match", so they
+** should start with the most specific path and work their
+** way up to the root.
+*/
+
+static const struct fs_path_config android_dirs[] = {
+ { 00770, AID_SYSTEM, AID_CACHE, 0, "cache" },
+ { 00500, AID_ROOT, AID_ROOT, 0, "config" },
+ { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app" },
+ { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app-private" },
+ { 00771, AID_ROOT, AID_ROOT, 0, "data/dalvik-cache" },
+ { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/data" },
+ { 00771, AID_SHELL, AID_SHELL, 0, "data/local/tmp" },
+ { 00771, AID_SHELL, AID_SHELL, 0, "data/local" },
+ { 01771, AID_SYSTEM, AID_MISC, 0, "data/misc" },
+ { 00770, AID_DHCP, AID_DHCP, 0, "data/misc/dhcp" },
+ { 00771, AID_SHARED_RELRO, AID_SHARED_RELRO, 0, "data/misc/shared_relro" },
+ { 00775, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media" },
+ { 00775, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media/Music" },
+ { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data" },
+ { 00755, AID_ROOT, AID_SYSTEM, 0, "mnt" },
+ { 00755, AID_ROOT, AID_ROOT, 0, "root" },
+ { 00750, AID_ROOT, AID_SHELL, 0, "sbin" },
+ { 00751, AID_ROOT, AID_SDCARD_R, 0, "storage" },
+ { 00755, AID_ROOT, AID_SHELL, 0, "system/bin" },
+ { 00755, AID_ROOT, AID_SHELL, 0, "system/vendor" },
+ { 00755, AID_ROOT, AID_SHELL, 0, "system/xbin" },
+ { 00755, AID_ROOT, AID_ROOT, 0, "system/etc/ppp" },
+ { 00755, AID_ROOT, AID_SHELL, 0, "vendor" },
+ { 00777, AID_ROOT, AID_ROOT, 0, "sdcard" },
+ { 00755, AID_ROOT, AID_ROOT, 0, 0 },
+};
+
+/* Rules for files.
+** These rules are applied based on "first match", so they
+** should start with the most specific path and work their
+** way up to the root. Prefixes ending in * denotes wildcard
+** and will allow partial matches.
+*/
+static const char conf_dir[] = "/system/etc/fs_config_dirs";
+static const char conf_file[] = "/system/etc/fs_config_files";
+
+static const struct fs_path_config android_files[] = {
+ { 00440, AID_ROOT, AID_SHELL, 0, "system/etc/init.goldfish.rc" },
+ { 00550, AID_ROOT, AID_SHELL, 0, "system/etc/init.goldfish.sh" },
+ { 00550, AID_ROOT, AID_SHELL, 0, "system/etc/init.ril" },
+ { 00550, AID_DHCP, AID_SHELL, 0, "system/etc/dhcpcd/dhcpcd-run-hooks" },
+ { 00555, AID_ROOT, AID_ROOT, 0, "system/etc/ppp/*" },
+ { 00555, AID_ROOT, AID_ROOT, 0, "system/etc/rc.*" },
+ { 00444, AID_ROOT, AID_ROOT, 0, conf_dir + 1 },
+ { 00444, AID_ROOT, AID_ROOT, 0, conf_file + 1 },
+ { 00644, AID_SYSTEM, AID_SYSTEM, 0, "data/app/*" },
+ { 00644, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media/*" },
+ { 00644, AID_SYSTEM, AID_SYSTEM, 0, "data/app-private/*" },
+ { 00644, AID_APP, AID_APP, 0, "data/data/*" },
+
+ /* the following five files are INTENTIONALLY set-uid, but they
+ * are NOT included on user builds. */
+ { 04750, AID_ROOT, AID_SHELL, 0, "system/xbin/su" },
+ { 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/librank" },
+ { 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/procrank" },
+ { 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/procmem" },
+ { 04770, AID_ROOT, AID_RADIO, 0, "system/bin/pppd-ril" },
+
+ /* the following files have enhanced capabilities and ARE included in user builds. */
+ { 00750, AID_ROOT, AID_SHELL, (1ULL << CAP_SETUID) | (1ULL << CAP_SETGID), "system/bin/run-as" },
+ { 00700, AID_SYSTEM, AID_SHELL, (1ULL << CAP_BLOCK_SUSPEND), "system/bin/inputflinger" },
+
+ { 00750, AID_ROOT, AID_ROOT, 0, "system/bin/uncrypt" },
+ { 00750, AID_ROOT, AID_ROOT, 0, "system/bin/install-recovery.sh" },
+ { 00755, AID_ROOT, AID_SHELL, 0, "system/bin/*" },
+ { 00755, AID_ROOT, AID_ROOT, 0, "system/lib/valgrind/*" },
+ { 00755, AID_ROOT, AID_ROOT, 0, "system/lib64/valgrind/*" },
+ { 00755, AID_ROOT, AID_SHELL, 0, "system/xbin/*" },
+ { 00755, AID_ROOT, AID_SHELL, 0, "system/vendor/bin/*" },
+ { 00755, AID_ROOT, AID_SHELL, 0, "vendor/bin/*" },
+ { 00750, AID_ROOT, AID_SHELL, 0, "sbin/*" },
+ { 00755, AID_ROOT, AID_ROOT, 0, "bin/*" },
+ { 00750, AID_ROOT, AID_SHELL, 0, "init*" },
+ { 00750, AID_ROOT, AID_SHELL, 0, "sbin/fs_mgr" },
+ { 00640, AID_ROOT, AID_SHELL, 0, "fstab.*" },
+ { 00644, AID_ROOT, AID_ROOT, 0, 0 },
+};
+
+static int fs_config_open(int dir)
+{
+ int fd = -1;
+
+ const char *out = getenv("OUT");
+ if (out && *out) {
+ char *name = NULL;
+ if (asprintf(&name, "%s%s", out, dir ? conf_dir : conf_file) != -1) {
+ fd = TEMP_FAILURE_RETRY(open(name, O_RDONLY | O_BINARY));
+ free(name);
+ }
+ }
+ if (fd < 0) {
+ fd = TEMP_FAILURE_RETRY(open(dir ? conf_dir : conf_file, O_RDONLY | O_BINARY));
+ }
+ return fd;
+}
+
+static bool fs_config_cmp(bool dir, const char *prefix, size_t len,
+ const char *path, size_t plen)
+{
+ if (dir) {
+ if (plen < len) {
+ return false;
+ }
+ } else {
+ /* If name ends in * then allow partial matches. */
+ if (prefix[len - 1] == '*') {
+ return !strncmp(prefix, path, len - 1);
+ }
+ if (plen != len) {
+ return false;
+ }
+ }
+ return !strncmp(prefix, path, len);
+}
+
+void fs_config(const char *path, int dir,
+ unsigned *uid, unsigned *gid, unsigned *mode, uint64_t *capabilities)
+{
+ const struct fs_path_config *pc;
+ int fd, plen;
+
+ if (path[0] == '/') {
+ path++;
+ }
+
+ plen = strlen(path);
+
+ fd = fs_config_open(dir);
+ if (fd >= 0) {
+ struct fs_path_config_from_file header;
+
+ while (TEMP_FAILURE_RETRY(read(fd, &header, sizeof(header))) == sizeof(header)) {
+ char *prefix;
+ uint16_t host_len = get2LE((const uint8_t *)&header.len);
+ ssize_t len, remainder = host_len - sizeof(header);
+ if (remainder <= 0) {
+ ALOGE("%s len is corrupted", dir ? conf_dir : conf_file);
+ break;
+ }
+ prefix = calloc(1, remainder);
+ if (!prefix) {
+ ALOGE("%s out of memory", dir ? conf_dir : conf_file);
+ break;
+ }
+ if (TEMP_FAILURE_RETRY(read(fd, prefix, remainder)) != remainder) {
+ free(prefix);
+ ALOGE("%s prefix is truncated", dir ? conf_dir : conf_file);
+ break;
+ }
+ len = strnlen(prefix, remainder);
+ if (len >= remainder) { /* missing a terminating null */
+ free(prefix);
+ ALOGE("%s is corrupted", dir ? conf_dir : conf_file);
+ break;
+ }
+ if (fs_config_cmp(dir, prefix, len, path, plen)) {
+ free(prefix);
+ close(fd);
+ *uid = get2LE((const uint8_t *)&(header.uid));
+ *gid = get2LE((const uint8_t *)&(header.gid));
+ *mode = (*mode & (~07777)) | get2LE((const uint8_t *)&(header.mode));
+ *capabilities = get8LE((const uint8_t *)&(header.capabilities));
+ return;
+ }
+ free(prefix);
+ }
+ close(fd);
+ }
+
+ pc = dir ? android_dirs : android_files;
+ for(; pc->prefix; pc++){
+ if (fs_config_cmp(dir, pc->prefix, strlen(pc->prefix), path, plen)) {
+ break;
+ }
+ }
+ *uid = pc->uid;
+ *gid = pc->gid;
+ *mode = (*mode & (~07777)) | pc->mode;
+ *capabilities = pc->capabilities;
+}
+
+ssize_t fs_config_generate(char *buffer, size_t length, const struct fs_path_config *pc)
+{
+ struct fs_path_config_from_file *p = (struct fs_path_config_from_file *)buffer;
+ size_t len = ALIGN(sizeof(*p) + strlen(pc->prefix) + 1, sizeof(uint64_t));
+
+ if ((length < len) || (len > UINT16_MAX)) {
+ return -ENOSPC;
+ }
+ memset(p, 0, len);
+ uint16_t host_len = len;
+ p->len = get2LE((const uint8_t *)&host_len);
+ p->mode = get2LE((const uint8_t *)&(pc->mode));
+ p->uid = get2LE((const uint8_t *)&(pc->uid));
+ p->gid = get2LE((const uint8_t *)&(pc->gid));
+ p->capabilities = get8LE((const uint8_t *)&(pc->capabilities));
+ strcpy(p->prefix, pc->prefix);
+ return len;
+}
diff --git a/libcutils/hashmap.c b/libcutils/hashmap.c
new file mode 100644
index 0000000..65539ea
--- /dev/null
+++ b/libcutils/hashmap.c
@@ -0,0 +1,351 @@
+/*
+ * Copyright (C) 2007 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 <cutils/hashmap.h>
+#include <assert.h>
+#include <errno.h>
+#include <cutils/threads.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <sys/types.h>
+
+typedef struct Entry Entry;
+struct Entry {
+ void* key;
+ int hash;
+ void* value;
+ Entry* next;
+};
+
+struct Hashmap {
+ Entry** buckets;
+ size_t bucketCount;
+ int (*hash)(void* key);
+ bool (*equals)(void* keyA, void* keyB);
+ mutex_t lock;
+ size_t size;
+};
+
+Hashmap* hashmapCreate(size_t initialCapacity,
+ int (*hash)(void* key), bool (*equals)(void* keyA, void* keyB)) {
+ assert(hash != NULL);
+ assert(equals != NULL);
+
+ Hashmap* map = malloc(sizeof(Hashmap));
+ if (map == NULL) {
+ return NULL;
+ }
+
+ // 0.75 load factor.
+ size_t minimumBucketCount = initialCapacity * 4 / 3;
+ map->bucketCount = 1;
+ while (map->bucketCount <= minimumBucketCount) {
+ // Bucket count must be power of 2.
+ map->bucketCount <<= 1;
+ }
+
+ map->buckets = calloc(map->bucketCount, sizeof(Entry*));
+ if (map->buckets == NULL) {
+ free(map);
+ return NULL;
+ }
+
+ map->size = 0;
+
+ map->hash = hash;
+ map->equals = equals;
+
+ mutex_init(&map->lock);
+
+ return map;
+}
+
+/**
+ * Hashes the given key.
+ */
+static inline int hashKey(Hashmap* map, void* key) {
+ int h = map->hash(key);
+
+ // We apply this secondary hashing discovered by Doug Lea to defend
+ // against bad hashes.
+ h += ~(h << 9);
+ h ^= (((unsigned int) h) >> 14);
+ h += (h << 4);
+ h ^= (((unsigned int) h) >> 10);
+
+ return h;
+}
+
+size_t hashmapSize(Hashmap* map) {
+ return map->size;
+}
+
+static inline size_t calculateIndex(size_t bucketCount, int hash) {
+ return ((size_t) hash) & (bucketCount - 1);
+}
+
+static void expandIfNecessary(Hashmap* map) {
+ // If the load factor exceeds 0.75...
+ if (map->size > (map->bucketCount * 3 / 4)) {
+ // Start off with a 0.33 load factor.
+ size_t newBucketCount = map->bucketCount << 1;
+ Entry** newBuckets = calloc(newBucketCount, sizeof(Entry*));
+ if (newBuckets == NULL) {
+ // Abort expansion.
+ return;
+ }
+
+ // Move over existing entries.
+ size_t i;
+ for (i = 0; i < map->bucketCount; i++) {
+ Entry* entry = map->buckets[i];
+ while (entry != NULL) {
+ Entry* next = entry->next;
+ size_t index = calculateIndex(newBucketCount, entry->hash);
+ entry->next = newBuckets[index];
+ newBuckets[index] = entry;
+ entry = next;
+ }
+ }
+
+ // Copy over internals.
+ free(map->buckets);
+ map->buckets = newBuckets;
+ map->bucketCount = newBucketCount;
+ }
+}
+
+void hashmapLock(Hashmap* map) {
+ mutex_lock(&map->lock);
+}
+
+void hashmapUnlock(Hashmap* map) {
+ mutex_unlock(&map->lock);
+}
+
+void hashmapFree(Hashmap* map) {
+ size_t i;
+ for (i = 0; i < map->bucketCount; i++) {
+ Entry* entry = map->buckets[i];
+ while (entry != NULL) {
+ Entry* next = entry->next;
+ free(entry);
+ entry = next;
+ }
+ }
+ free(map->buckets);
+ mutex_destroy(&map->lock);
+ free(map);
+}
+
+int hashmapHash(void* key, size_t keySize) {
+ int h = keySize;
+ char* data = (char*) key;
+ size_t i;
+ for (i = 0; i < keySize; i++) {
+ h = h * 31 + *data;
+ data++;
+ }
+ return h;
+}
+
+static Entry* createEntry(void* key, int hash, void* value) {
+ Entry* entry = malloc(sizeof(Entry));
+ if (entry == NULL) {
+ return NULL;
+ }
+ entry->key = key;
+ entry->hash = hash;
+ entry->value = value;
+ entry->next = NULL;
+ return entry;
+}
+
+static inline bool equalKeys(void* keyA, int hashA, void* keyB, int hashB,
+ bool (*equals)(void*, void*)) {
+ if (keyA == keyB) {
+ return true;
+ }
+ if (hashA != hashB) {
+ return false;
+ }
+ return equals(keyA, keyB);
+}
+
+void* hashmapPut(Hashmap* map, void* key, void* value) {
+ int hash = hashKey(map, key);
+ size_t index = calculateIndex(map->bucketCount, hash);
+
+ Entry** p = &(map->buckets[index]);
+ while (true) {
+ Entry* current = *p;
+
+ // Add a new entry.
+ if (current == NULL) {
+ *p = createEntry(key, hash, value);
+ if (*p == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ map->size++;
+ expandIfNecessary(map);
+ return NULL;
+ }
+
+ // Replace existing entry.
+ if (equalKeys(current->key, current->hash, key, hash, map->equals)) {
+ void* oldValue = current->value;
+ current->value = value;
+ return oldValue;
+ }
+
+ // Move to next entry.
+ p = ¤t->next;
+ }
+}
+
+void* hashmapGet(Hashmap* map, void* key) {
+ int hash = hashKey(map, key);
+ size_t index = calculateIndex(map->bucketCount, hash);
+
+ Entry* entry = map->buckets[index];
+ while (entry != NULL) {
+ if (equalKeys(entry->key, entry->hash, key, hash, map->equals)) {
+ return entry->value;
+ }
+ entry = entry->next;
+ }
+
+ return NULL;
+}
+
+bool hashmapContainsKey(Hashmap* map, void* key) {
+ int hash = hashKey(map, key);
+ size_t index = calculateIndex(map->bucketCount, hash);
+
+ Entry* entry = map->buckets[index];
+ while (entry != NULL) {
+ if (equalKeys(entry->key, entry->hash, key, hash, map->equals)) {
+ return true;
+ }
+ entry = entry->next;
+ }
+
+ return false;
+}
+
+void* hashmapMemoize(Hashmap* map, void* key,
+ void* (*initialValue)(void* key, void* context), void* context) {
+ int hash = hashKey(map, key);
+ size_t index = calculateIndex(map->bucketCount, hash);
+
+ Entry** p = &(map->buckets[index]);
+ while (true) {
+ Entry* current = *p;
+
+ // Add a new entry.
+ if (current == NULL) {
+ *p = createEntry(key, hash, NULL);
+ if (*p == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ void* value = initialValue(key, context);
+ (*p)->value = value;
+ map->size++;
+ expandIfNecessary(map);
+ return value;
+ }
+
+ // Return existing value.
+ if (equalKeys(current->key, current->hash, key, hash, map->equals)) {
+ return current->value;
+ }
+
+ // Move to next entry.
+ p = ¤t->next;
+ }
+}
+
+void* hashmapRemove(Hashmap* map, void* key) {
+ int hash = hashKey(map, key);
+ size_t index = calculateIndex(map->bucketCount, hash);
+
+ // Pointer to the current entry.
+ Entry** p = &(map->buckets[index]);
+ Entry* current;
+ while ((current = *p) != NULL) {
+ if (equalKeys(current->key, current->hash, key, hash, map->equals)) {
+ void* value = current->value;
+ *p = current->next;
+ free(current);
+ map->size--;
+ return value;
+ }
+
+ p = ¤t->next;
+ }
+
+ return NULL;
+}
+
+void hashmapForEach(Hashmap* map,
+ bool (*callback)(void* key, void* value, void* context),
+ void* context) {
+ size_t i;
+ for (i = 0; i < map->bucketCount; i++) {
+ Entry* entry = map->buckets[i];
+ while (entry != NULL) {
+ Entry *next = entry->next;
+ if (!callback(entry->key, entry->value, context)) {
+ return;
+ }
+ entry = next;
+ }
+ }
+}
+
+size_t hashmapCurrentCapacity(Hashmap* map) {
+ size_t bucketCount = map->bucketCount;
+ return bucketCount * 3 / 4;
+}
+
+size_t hashmapCountCollisions(Hashmap* map) {
+ size_t collisions = 0;
+ size_t i;
+ for (i = 0; i < map->bucketCount; i++) {
+ Entry* entry = map->buckets[i];
+ while (entry != NULL) {
+ if (entry->next != NULL) {
+ collisions++;
+ }
+ entry = entry->next;
+ }
+ }
+ return collisions;
+}
+
+int hashmapIntHash(void* key) {
+ // Return the key value itself.
+ return *((int*) key);
+}
+
+bool hashmapIntEquals(void* keyA, void* keyB) {
+ int a = *((int*) keyA);
+ int b = *((int*) keyB);
+ return a == b;
+}
diff --git a/libcutils/iosched_policy.c b/libcutils/iosched_policy.c
new file mode 100644
index 0000000..8946d3c
--- /dev/null
+++ b/libcutils/iosched_policy.c
@@ -0,0 +1,58 @@
+/*
+** Copyright 2007, 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <cutils/iosched_policy.h>
+
+#ifdef HAVE_ANDROID_OS
+#include <linux/ioprio.h>
+#include <sys/syscall.h>
+#define __android_unused
+#else
+#define __android_unused __attribute__((__unused__))
+#endif
+
+int android_set_ioprio(int pid __android_unused, IoSchedClass clazz __android_unused, int ioprio __android_unused) {
+#ifdef HAVE_ANDROID_OS
+ if (syscall(SYS_ioprio_set, IOPRIO_WHO_PROCESS, pid, ioprio | (clazz << IOPRIO_CLASS_SHIFT))) {
+ return -1;
+ }
+#endif
+ return 0;
+}
+
+int android_get_ioprio(int pid __android_unused, IoSchedClass *clazz, int *ioprio) {
+#ifdef HAVE_ANDROID_OS
+ int rc;
+
+ if ((rc = syscall(SYS_ioprio_get, IOPRIO_WHO_PROCESS, pid)) < 0) {
+ return -1;
+ }
+
+ *clazz = (rc >> IOPRIO_CLASS_SHIFT);
+ *ioprio = (rc & 0xff);
+#else
+ *clazz = IoSchedClass_NONE;
+ *ioprio = 0;
+#endif
+ return 0;
+}
diff --git a/libcutils/klog.c b/libcutils/klog.c
new file mode 100644
index 0000000..710dc66
--- /dev/null
+++ b/libcutils/klog.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <cutils/klog.h>
+
+static int klog_fd = -1;
+static int klog_level = KLOG_DEFAULT_LEVEL;
+
+int klog_get_level(void) {
+ return klog_level;
+}
+
+void klog_set_level(int level) {
+ klog_level = level;
+}
+
+void klog_init(void) {
+ if (klog_fd >= 0) return; /* Already initialized */
+
+ klog_fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC);
+ if (klog_fd >= 0) {
+ return;
+ }
+
+ static const char* name = "/dev/__kmsg__";
+ if (mknod(name, S_IFCHR | 0600, (1 << 8) | 11) == 0) {
+ klog_fd = open(name, O_WRONLY | O_CLOEXEC);
+ unlink(name);
+ }
+}
+
+#define LOG_BUF_MAX 512
+
+void klog_writev(int level, const struct iovec* iov, int iov_count) {
+ if (level > klog_level) return;
+ if (klog_fd < 0) klog_init();
+ if (klog_fd < 0) return;
+ TEMP_FAILURE_RETRY(writev(klog_fd, iov, iov_count));
+}
+
+void klog_write(int level, const char* fmt, ...) {
+ char buf[LOG_BUF_MAX];
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ buf[LOG_BUF_MAX - 1] = 0;
+
+ struct iovec iov[1];
+ iov[0].iov_base = buf;
+ iov[0].iov_len = strlen(buf);
+ klog_writev(level, iov, 1);
+}
diff --git a/libcutils/load_file.c b/libcutils/load_file.c
new file mode 100644
index 0000000..99f2965
--- /dev/null
+++ b/libcutils/load_file.c
@@ -0,0 +1,51 @@
+/* libs/cutils/load_file.c
+**
+** Copyright 2006, 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 <unistd.h>
+#include <fcntl.h>
+
+void *load_file(const char *fn, unsigned *_sz)
+{
+ char *data;
+ int sz;
+ int fd;
+
+ data = 0;
+ fd = open(fn, O_RDONLY);
+ if(fd < 0) return 0;
+
+ sz = lseek(fd, 0, SEEK_END);
+ if(sz < 0) goto oops;
+
+ if(lseek(fd, 0, SEEK_SET) != 0) goto oops;
+
+ data = (char*) malloc(sz + 1);
+ if(data == 0) goto oops;
+
+ if(read(fd, data, sz) != sz) goto oops;
+ close(fd);
+ data[sz] = 0;
+
+ if(_sz) *_sz = sz;
+ return data;
+
+oops:
+ close(fd);
+ if(data != 0) free(data);
+ return 0;
+}
diff --git a/libcutils/multiuser.c b/libcutils/multiuser.c
new file mode 100644
index 0000000..7c74bb8
--- /dev/null
+++ b/libcutils/multiuser.c
@@ -0,0 +1,29 @@
+/*
+ * 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 <cutils/multiuser.h>
+
+userid_t multiuser_get_user_id(uid_t uid) {
+ return uid / MULTIUSER_APP_PER_USER_RANGE;
+}
+
+appid_t multiuser_get_app_id(uid_t uid) {
+ return uid % MULTIUSER_APP_PER_USER_RANGE;
+}
+
+uid_t multiuser_get_uid(userid_t userId, appid_t appId) {
+ return userId * MULTIUSER_APP_PER_USER_RANGE + (appId % MULTIUSER_APP_PER_USER_RANGE);
+}
diff --git a/libcutils/native_handle.c b/libcutils/native_handle.c
new file mode 100644
index 0000000..9a4a5bb
--- /dev/null
+++ b/libcutils/native_handle.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2007 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 "NativeHandle"
+
+#include <stdint.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <cutils/log.h>
+#include <cutils/native_handle.h>
+
+native_handle_t* native_handle_create(int numFds, int numInts)
+{
+ native_handle_t* h = malloc(
+ sizeof(native_handle_t) + sizeof(int)*(numFds+numInts));
+
+ if (h) {
+ h->version = sizeof(native_handle_t);
+ h->numFds = numFds;
+ h->numInts = numInts;
+ }
+ return h;
+}
+
+int native_handle_delete(native_handle_t* h)
+{
+ if (h) {
+ if (h->version != sizeof(native_handle_t))
+ return -EINVAL;
+ free(h);
+ }
+ return 0;
+}
+
+int native_handle_close(const native_handle_t* h)
+{
+ if (h->version != sizeof(native_handle_t))
+ return -EINVAL;
+
+ const int numFds = h->numFds;
+ int i;
+ for (i=0 ; i<numFds ; i++) {
+ close(h->data[i]);
+ }
+ return 0;
+}
diff --git a/libcutils/open_memstream.c b/libcutils/open_memstream.c
new file mode 100644
index 0000000..9183266
--- /dev/null
+++ b/libcutils/open_memstream.c
@@ -0,0 +1,373 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if defined(__APPLE__)
+
+/*
+ * Implementation of the POSIX open_memstream() function, which Linux has
+ * but BSD lacks.
+ *
+ * Summary:
+ * - Works like a file-backed FILE* opened with fopen(name, "w"), but the
+ * backing is a chunk of memory rather than a file.
+ * - The buffer expands as you write more data. Seeking past the end
+ * of the file and then writing to it zero-fills the gap.
+ * - The values at "*bufp" and "*sizep" should be considered read-only,
+ * and are only valid immediately after an fflush() or fclose().
+ * - A '\0' is maintained just past the end of the file. This is not included
+ * in "*sizep". (The behavior w.r.t. fseek() is not clearly defined.
+ * The spec says the null byte is written when a write() advances EOF,
+ * but it looks like glibc ensures the null byte is always found at EOF,
+ * even if you just seeked backwards. The example on the opengroup.org
+ * page suggests that this is the expected behavior. The null must be
+ * present after a no-op fflush(), which we can't see, so we have to save
+ * and restore it. Annoying, but allows file truncation.)
+ * - After fclose(), the caller must eventually free(*bufp).
+ *
+ * This is built out of funopen(), which BSD has but Linux lacks. There is
+ * no flush() operator, so we need to keep the user pointers up to date
+ * after each operation.
+ *
+ * I don't think Windows has any of the above, but we don't need to use
+ * them there, so we just supply a stub.
+ */
+#include <cutils/open_memstream.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#if 0
+# define DBUG(x) printf x
+#else
+# define DBUG(x) ((void)0)
+#endif
+
+/*
+ * Definition of a seekable, write-only memory stream.
+ */
+typedef struct {
+ char** bufp; /* pointer to buffer pointer */
+ size_t* sizep; /* pointer to eof */
+
+ size_t allocSize; /* size of buffer */
+ size_t eof; /* furthest point we've written to */
+ size_t offset; /* current write offset */
+ char saved; /* required by NUL handling */
+} MemStream;
+
+#define kInitialSize 1024
+
+/*
+ * Ensure that we have enough storage to write "size" bytes at the
+ * current offset. We also have to take into account the extra '\0'
+ * that we maintain just past EOF.
+ *
+ * Returns 0 on success.
+ */
+static int ensureCapacity(MemStream* stream, int writeSize)
+{
+ DBUG(("+++ ensureCap off=%d size=%d\n", stream->offset, writeSize));
+
+ size_t neededSize = stream->offset + writeSize + 1;
+ if (neededSize <= stream->allocSize)
+ return 0;
+
+ size_t newSize;
+
+ if (stream->allocSize == 0) {
+ newSize = kInitialSize;
+ } else {
+ newSize = stream->allocSize;
+ newSize += newSize / 2; /* expand by 3/2 */
+ }
+
+ if (newSize < neededSize)
+ newSize = neededSize;
+ DBUG(("+++ realloc %p->%p to size=%d\n",
+ stream->bufp, *stream->bufp, newSize));
+ char* newBuf = (char*) realloc(*stream->bufp, newSize);
+ if (newBuf == NULL)
+ return -1;
+
+ *stream->bufp = newBuf;
+ stream->allocSize = newSize;
+ return 0;
+}
+
+/*
+ * Write data to a memstream, expanding the buffer if necessary.
+ *
+ * If we previously seeked beyond EOF, zero-fill the gap.
+ *
+ * Returns the number of bytes written.
+ */
+static int write_memstream(void* cookie, const char* buf, int size)
+{
+ MemStream* stream = (MemStream*) cookie;
+
+ if (ensureCapacity(stream, size) < 0)
+ return -1;
+
+ /* seeked past EOF earlier? */
+ if (stream->eof < stream->offset) {
+ DBUG(("+++ zero-fill gap from %d to %d\n",
+ stream->eof, stream->offset-1));
+ memset(*stream->bufp + stream->eof, '\0',
+ stream->offset - stream->eof);
+ }
+
+ /* copy data, advance write pointer */
+ memcpy(*stream->bufp + stream->offset, buf, size);
+ stream->offset += size;
+
+ if (stream->offset > stream->eof) {
+ /* EOF has advanced, update it and append null byte */
+ DBUG(("+++ EOF advanced to %d, appending nul\n", stream->offset));
+ assert(stream->offset < stream->allocSize);
+ stream->eof = stream->offset;
+ } else {
+ /* within previously-written area; save char we're about to stomp */
+ DBUG(("+++ within written area, saving '%c' at %d\n",
+ *(*stream->bufp + stream->offset), stream->offset));
+ stream->saved = *(*stream->bufp + stream->offset);
+ }
+ *(*stream->bufp + stream->offset) = '\0';
+ *stream->sizep = stream->offset;
+
+ return size;
+}
+
+/*
+ * Seek within a memstream.
+ *
+ * Returns the new offset, or -1 on failure.
+ */
+static fpos_t seek_memstream(void* cookie, fpos_t offset, int whence)
+{
+ MemStream* stream = (MemStream*) cookie;
+ off_t newPosn = (off_t) offset;
+
+ if (whence == SEEK_CUR) {
+ newPosn += stream->offset;
+ } else if (whence == SEEK_END) {
+ newPosn += stream->eof;
+ }
+
+ if (newPosn < 0 || ((fpos_t)((size_t) newPosn)) != newPosn) {
+ /* bad offset - negative or huge */
+ DBUG(("+++ bogus seek offset %ld\n", (long) newPosn));
+ errno = EINVAL;
+ return (fpos_t) -1;
+ }
+
+ if (stream->offset < stream->eof) {
+ /*
+ * We were pointing to an area we'd already written to, which means
+ * we stomped on a character and must now restore it.
+ */
+ DBUG(("+++ restoring char '%c' at %d\n",
+ stream->saved, stream->offset));
+ *(*stream->bufp + stream->offset) = stream->saved;
+ }
+
+ stream->offset = (size_t) newPosn;
+
+ if (stream->offset < stream->eof) {
+ /*
+ * We're seeked backward into the stream. Preserve the character
+ * at EOF and stomp it with a NUL.
+ */
+ stream->saved = *(*stream->bufp + stream->offset);
+ *(*stream->bufp + stream->offset) = '\0';
+ *stream->sizep = stream->offset;
+ } else {
+ /*
+ * We're positioned at, or possibly beyond, the EOF. We want to
+ * publish the current EOF, not the current position.
+ */
+ *stream->sizep = stream->eof;
+ }
+
+ return newPosn;
+}
+
+/*
+ * Close the memstream. We free everything but the data buffer.
+ */
+static int close_memstream(void* cookie)
+{
+ free(cookie);
+ return 0;
+}
+
+/*
+ * Prepare a memstream.
+ */
+FILE* open_memstream(char** bufp, size_t* sizep)
+{
+ FILE* fp;
+ MemStream* stream;
+
+ if (bufp == NULL || sizep == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ stream = (MemStream*) calloc(1, sizeof(MemStream));
+ if (stream == NULL)
+ return NULL;
+
+ fp = funopen(stream,
+ NULL, write_memstream, seek_memstream, close_memstream);
+ if (fp == NULL) {
+ free(stream);
+ return NULL;
+ }
+
+ *sizep = 0;
+ *bufp = NULL;
+ stream->bufp = bufp;
+ stream->sizep = sizep;
+
+ return fp;
+}
+
+
+
+
+#if 0
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * Simple regression test.
+ *
+ * To test on desktop Linux with valgrind, it's possible to make a simple
+ * change to open_memstream() to use fopencookie instead:
+ *
+ * cookie_io_functions_t iofuncs =
+ * { NULL, write_memstream, seek_memstream, close_memstream };
+ * fp = fopencookie(stream, "w", iofuncs);
+ *
+ * (Some tweaks to seek_memstream are also required, as that takes a
+ * pointer to an offset rather than an offset, and returns 0 or -1.)
+ */
+int testMemStream(void)
+{
+ FILE *stream;
+ char *buf;
+ size_t len;
+ off_t eob;
+
+ printf("Test1\n");
+
+ /* std example */
+ stream = open_memstream(&buf, &len);
+ fprintf(stream, "hello my world");
+ fflush(stream);
+ printf("buf=%s, len=%zu\n", buf, len);
+ eob = ftello(stream);
+ fseeko(stream, 0, SEEK_SET);
+ fprintf(stream, "good-bye");
+ fseeko(stream, eob, SEEK_SET);
+ fclose(stream);
+ printf("buf=%s, len=%zu\n", buf, len);
+ free(buf);
+
+ printf("Test2\n");
+
+ /* std example without final seek-to-end */
+ stream = open_memstream(&buf, &len);
+ fprintf(stream, "hello my world");
+ fflush(stream);
+ printf("buf=%s, len=%zu\n", buf, len);
+ eob = ftello(stream);
+ fseeko(stream, 0, SEEK_SET);
+ fprintf(stream, "good-bye");
+ //fseeko(stream, eob, SEEK_SET);
+ fclose(stream);
+ printf("buf=%s, len=%zu\n", buf, len);
+ free(buf);
+
+ printf("Test3\n");
+
+ /* fancy example; should expand buffer with writes */
+ static const int kCmpLen = 1024 + 128;
+ char* cmp = malloc(kCmpLen);
+ memset(cmp, 0, 1024);
+ memset(cmp+1024, 0xff, kCmpLen-1024);
+ sprintf(cmp, "This-is-a-tes1234");
+ sprintf(cmp + 1022, "abcdef");
+
+ stream = open_memstream (&buf, &len);
+ setvbuf(stream, NULL, _IONBF, 0); /* note: crashes in glibc with this */
+ fprintf(stream, "This-is-a-test");
+ fseek(stream, -1, SEEK_CUR); /* broken in glibc; can use {13,SEEK_SET} */
+ fprintf(stream, "1234");
+ fseek(stream, 1022, SEEK_SET);
+ fputc('a', stream);
+ fputc('b', stream);
+ fputc('c', stream);
+ fputc('d', stream);
+ fputc('e', stream);
+ fputc('f', stream);
+ fflush(stream);
+
+ if (memcmp(buf, cmp, len+1) != 0) {
+ printf("mismatch\n");
+ } else {
+ printf("match\n");
+ }
+
+ printf("Test4\n");
+ stream = open_memstream (&buf, &len);
+ fseek(stream, 5000, SEEK_SET);
+ fseek(stream, 4096, SEEK_SET);
+ fseek(stream, -1, SEEK_SET); /* should have no effect */
+ fputc('x', stream);
+ if (ftell(stream) == 4097)
+ printf("good\n");
+ else
+ printf("BAD: offset is %ld\n", ftell(stream));
+
+ printf("DONE\n");
+
+ return 0;
+}
+
+/* expected output:
+Test1
+buf=hello my world, len=14
+buf=good-bye world, len=14
+Test2
+buf=hello my world, len=14
+buf=good-bye, len=8
+Test3
+match
+Test4
+good
+DONE
+*/
+
+#endif
+
+#endif /* __APPLE__ */
diff --git a/libcutils/partition_utils.c b/libcutils/partition_utils.c
new file mode 100644
index 0000000..823b162
--- /dev/null
+++ b/libcutils/partition_utils.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright 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 <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h> /* for BLKGETSIZE */
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <cutils/properties.h>
+
+static int only_one_char(char *buf, int len, char c)
+{
+ int i, ret;
+
+ ret = 1;
+ for (i=0; i<len; i++) {
+ if (buf[i] != c) {
+ ret = 0;
+ break;
+ }
+ }
+ return ret;
+}
+
+int partition_wiped(char *source)
+{
+ char buf[4096];
+ int fd, ret;
+
+ if ((fd = open(source, O_RDONLY)) < 0) {
+ return 0;
+ }
+
+ ret = read(fd, buf, sizeof(buf));
+ close(fd);
+
+ if (ret != sizeof(buf)) {
+ return 0;
+ }
+
+ /* Check for all zeros */
+ if (only_one_char(buf, sizeof(buf), 0)) {
+ return 1;
+ }
+
+ /* Check for all ones */
+ if (only_one_char(buf, sizeof(buf), 0xff)) {
+ return 1;
+ }
+
+ return 0;
+}
+
diff --git a/libcutils/process_name.c b/libcutils/process_name.c
new file mode 100644
index 0000000..cc931eb
--- /dev/null
+++ b/libcutils/process_name.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#if defined(__linux__)
+#include <sys/prctl.h>
+#endif
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <cutils/process_name.h>
+#ifdef HAVE_ANDROID_OS
+#include <cutils/properties.h>
+#endif
+
+#define PROCESS_NAME_DEVICE "/sys/qemu_trace/process_name"
+
+static const char* process_name = "unknown";
+#ifdef HAVE_ANDROID_OS
+static int running_in_emulator = -1;
+#endif
+
+void set_process_name(const char* new_name) {
+#ifdef HAVE_ANDROID_OS
+ char propBuf[PROPERTY_VALUE_MAX];
+#endif
+
+ if (new_name == NULL) {
+ return;
+ }
+
+ // We never free the old name. Someone else could be using it.
+ int len = strlen(new_name);
+ char* copy = (char*) malloc(len + 1);
+ strcpy(copy, new_name);
+ process_name = (const char*) copy;
+
+#if defined(__linux__)
+ if (len < 16) {
+ prctl(PR_SET_NAME, (unsigned long) new_name, 0, 0, 0);
+ } else {
+ prctl(PR_SET_NAME, (unsigned long) new_name + len - 15, 0, 0, 0);
+ }
+#endif
+
+#ifdef HAVE_ANDROID_OS
+ // If we know we are not running in the emulator, then return.
+ if (running_in_emulator == 0) {
+ return;
+ }
+
+ // If the "running_in_emulator" variable has not been initialized,
+ // then do it now.
+ if (running_in_emulator == -1) {
+ property_get("ro.kernel.qemu", propBuf, "");
+ if (propBuf[0] == '1') {
+ running_in_emulator = 1;
+ } else {
+ running_in_emulator = 0;
+ return;
+ }
+ }
+
+ // If the emulator was started with the "-trace file" command line option
+ // then we want to record the process name in the trace even if we are
+ // not currently tracing instructions (so that we will know the process
+ // name when we do start tracing instructions). We do not need to execute
+ // this code if we are just running in the emulator without the "-trace"
+ // command line option, but we don't know that here and this function
+ // isn't called frequently enough to bother optimizing that case.
+ int fd = open(PROCESS_NAME_DEVICE, O_RDWR);
+ if (fd < 0)
+ return;
+ write(fd, process_name, strlen(process_name) + 1);
+ close(fd);
+#endif
+}
+
+const char* get_process_name(void) {
+ return process_name;
+}
diff --git a/libcutils/properties.c b/libcutils/properties.c
new file mode 100644
index 0000000..4e46e02
--- /dev/null
+++ b/libcutils/properties.c
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2006 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 "properties"
+// #define LOG_NDEBUG 0
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <cutils/sockets.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <cutils/properties.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <log/log.h>
+
+int8_t property_get_bool(const char *key, int8_t default_value) {
+ if (!key) {
+ return default_value;
+ }
+
+ int8_t result = default_value;
+ char buf[PROPERTY_VALUE_MAX] = {'\0',};
+
+ int len = property_get(key, buf, "");
+ if (len == 1) {
+ char ch = buf[0];
+ if (ch == '0' || ch == 'n') {
+ result = false;
+ } else if (ch == '1' || ch == 'y') {
+ result = true;
+ }
+ } else if (len > 1) {
+ if (!strcmp(buf, "no") || !strcmp(buf, "false") || !strcmp(buf, "off")) {
+ result = false;
+ } else if (!strcmp(buf, "yes") || !strcmp(buf, "true") || !strcmp(buf, "on")) {
+ result = true;
+ }
+ }
+
+ return result;
+}
+
+// Convert string property to int (default if fails); return default value if out of bounds
+static intmax_t property_get_imax(const char *key, intmax_t lower_bound, intmax_t upper_bound,
+ intmax_t default_value) {
+ if (!key) {
+ return default_value;
+ }
+
+ intmax_t result = default_value;
+ char buf[PROPERTY_VALUE_MAX] = {'\0',};
+ char *end = NULL;
+
+ int len = property_get(key, buf, "");
+ if (len > 0) {
+ int tmp = errno;
+ errno = 0;
+
+ // Infer base automatically
+ result = strtoimax(buf, &end, /*base*/0);
+ if ((result == INTMAX_MIN || result == INTMAX_MAX) && errno == ERANGE) {
+ // Over or underflow
+ result = default_value;
+ ALOGV("%s(%s,%" PRIdMAX ") - overflow", __FUNCTION__, key, default_value);
+ } else if (result < lower_bound || result > upper_bound) {
+ // Out of range of requested bounds
+ result = default_value;
+ ALOGV("%s(%s,%" PRIdMAX ") - out of range", __FUNCTION__, key, default_value);
+ } else if (end == buf) {
+ // Numeric conversion failed
+ result = default_value;
+ ALOGV("%s(%s,%" PRIdMAX ") - numeric conversion failed",
+ __FUNCTION__, key, default_value);
+ }
+
+ errno = tmp;
+ }
+
+ return result;
+}
+
+int64_t property_get_int64(const char *key, int64_t default_value) {
+ return (int64_t)property_get_imax(key, INT64_MIN, INT64_MAX, default_value);
+}
+
+int32_t property_get_int32(const char *key, int32_t default_value) {
+ return (int32_t)property_get_imax(key, INT32_MIN, INT32_MAX, default_value);
+}
+
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+
+int property_set(const char *key, const char *value)
+{
+ return __system_property_set(key, value);
+}
+
+int property_get(const char *key, char *value, const char *default_value)
+{
+ int len;
+
+ len = __system_property_get(key, value);
+ if(len > 0) {
+ return len;
+ }
+ if(default_value) {
+ len = strlen(default_value);
+ if (len >= PROPERTY_VALUE_MAX) {
+ len = PROPERTY_VALUE_MAX - 1;
+ }
+ memcpy(value, default_value, len);
+ value[len] = '\0';
+ }
+ return len;
+}
+
+struct property_list_callback_data
+{
+ void (*propfn)(const char *key, const char *value, void *cookie);
+ void *cookie;
+};
+
+static void property_list_callback(const prop_info *pi, void *cookie)
+{
+ char name[PROP_NAME_MAX];
+ char value[PROP_VALUE_MAX];
+ struct property_list_callback_data *data = cookie;
+
+ __system_property_read(pi, name, value);
+ data->propfn(name, value, data->cookie);
+}
+
+int property_list(
+ void (*propfn)(const char *key, const char *value, void *cookie),
+ void *cookie)
+{
+ struct property_list_callback_data data = { propfn, cookie };
+ return __system_property_foreach(property_list_callback, &data);
+}
diff --git a/libcutils/qtaguid.c b/libcutils/qtaguid.c
new file mode 100644
index 0000000..00e211c
--- /dev/null
+++ b/libcutils/qtaguid.c
@@ -0,0 +1,176 @@
+/*
+** Copyright 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.
+*/
+
+// #define LOG_NDEBUG 0
+
+#define LOG_TAG "qtaguid"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <cutils/qtaguid.h>
+#include <log/log.h>
+
+static const char* CTRL_PROCPATH = "/proc/net/xt_qtaguid/ctrl";
+static const int CTRL_MAX_INPUT_LEN = 128;
+static const char *GLOBAL_PACIFIER_PARAM = "/sys/module/xt_qtaguid/parameters/passive";
+static const char *TAG_PACIFIER_PARAM = "/sys/module/xt_qtaguid/parameters/tag_tracking_passive";
+
+/*
+ * One per proccess.
+ * Once the device is open, this process will have its socket tags tracked.
+ * And on exit or untimely death, all socket tags will be removed.
+ * A process can only open /dev/xt_qtaguid once.
+ * It should not close it unless it is really done with all the socket tags.
+ * Failure to open it will be visible when socket tagging will be attempted.
+ */
+static int resTrackFd = -1;
+pthread_once_t resTrackInitDone = PTHREAD_ONCE_INIT;
+
+/* Only call once per process. */
+void qtaguid_resTrack(void) {
+ resTrackFd = TEMP_FAILURE_RETRY(open("/dev/xt_qtaguid", O_RDONLY));
+ if (resTrackFd >=0) {
+ TEMP_FAILURE_RETRY(fcntl(resTrackFd, F_SETFD, FD_CLOEXEC));
+ }
+}
+
+/*
+ * Returns:
+ * 0 on success.
+ * -errno on failure.
+ */
+static int write_ctrl(const char *cmd) {
+ int fd, res, savedErrno;
+
+ ALOGV("write_ctrl(%s)", cmd);
+
+ fd = TEMP_FAILURE_RETRY(open(CTRL_PROCPATH, O_WRONLY));
+ if (fd < 0) {
+ return -errno;
+ }
+
+ res = TEMP_FAILURE_RETRY(write(fd, cmd, strlen(cmd)));
+ if (res < 0) {
+ savedErrno = errno;
+ } else {
+ savedErrno = 0;
+ }
+ if (res < 0) {
+ ALOGI("Failed write_ctrl(%s) res=%d errno=%d", cmd, res, savedErrno);
+ }
+ close(fd);
+ return -savedErrno;
+}
+
+static int write_param(const char *param_path, const char *value) {
+ int param_fd;
+ int res;
+
+ param_fd = TEMP_FAILURE_RETRY(open(param_path, O_WRONLY));
+ if (param_fd < 0) {
+ return -errno;
+ }
+ res = TEMP_FAILURE_RETRY(write(param_fd, value, strlen(value)));
+ if (res < 0) {
+ return -errno;
+ }
+ close(param_fd);
+ return 0;
+}
+
+int qtaguid_tagSocket(int sockfd, int tag, uid_t uid) {
+ char lineBuf[CTRL_MAX_INPUT_LEN];
+ int res;
+ uint64_t kTag = ((uint64_t)tag << 32);
+
+ pthread_once(&resTrackInitDone, qtaguid_resTrack);
+
+ snprintf(lineBuf, sizeof(lineBuf), "t %d %" PRIu64 " %d", sockfd, kTag, uid);
+
+ ALOGV("Tagging socket %d with tag %" PRIx64 "{%u,0} for uid %d", sockfd, kTag, tag, uid);
+
+ res = write_ctrl(lineBuf);
+ if (res < 0) {
+ ALOGI("Tagging socket %d with tag %" PRIx64 "(%d) for uid %d failed errno=%d",
+ sockfd, kTag, tag, uid, res);
+ }
+
+ return res;
+}
+
+int qtaguid_untagSocket(int sockfd) {
+ char lineBuf[CTRL_MAX_INPUT_LEN];
+ int res;
+
+ ALOGV("Untagging socket %d", sockfd);
+
+ snprintf(lineBuf, sizeof(lineBuf), "u %d", sockfd);
+ res = write_ctrl(lineBuf);
+ if (res < 0) {
+ ALOGI("Untagging socket %d failed errno=%d", sockfd, res);
+ }
+
+ return res;
+}
+
+int qtaguid_setCounterSet(int counterSetNum, uid_t uid) {
+ char lineBuf[CTRL_MAX_INPUT_LEN];
+ int res;
+
+ ALOGV("Setting counters to set %d for uid %d", counterSetNum, uid);
+
+ snprintf(lineBuf, sizeof(lineBuf), "s %d %d", counterSetNum, uid);
+ res = write_ctrl(lineBuf);
+ return res;
+}
+
+int qtaguid_deleteTagData(int tag, uid_t uid) {
+ char lineBuf[CTRL_MAX_INPUT_LEN];
+ int cnt = 0, res = 0;
+ uint64_t kTag = (uint64_t)tag << 32;
+
+ ALOGV("Deleting tag data with tag %" PRIx64 "{%d,0} for uid %d", kTag, tag, uid);
+
+ pthread_once(&resTrackInitDone, qtaguid_resTrack);
+
+ snprintf(lineBuf, sizeof(lineBuf), "d %" PRIu64 " %d", kTag, uid);
+ res = write_ctrl(lineBuf);
+ if (res < 0) {
+ ALOGI("Deleting tag data with tag %" PRIx64 "/%d for uid %d failed with cnt=%d errno=%d",
+ kTag, tag, uid, cnt, errno);
+ }
+
+ return res;
+}
+
+int qtaguid_setPacifier(int on) {
+ const char *value;
+
+ value = on ? "Y" : "N";
+ if (write_param(GLOBAL_PACIFIER_PARAM, value) < 0) {
+ return -errno;
+ }
+ if (write_param(TAG_PACIFIER_PARAM, value) < 0) {
+ return -errno;
+ }
+ return 0;
+}
diff --git a/libcutils/record_stream.c b/libcutils/record_stream.c
new file mode 100644
index 0000000..6994904
--- /dev/null
+++ b/libcutils/record_stream.c
@@ -0,0 +1,186 @@
+/* libs/cutils/record_stream.c
+**
+** Copyright 2006, 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 <unistd.h>
+#include <assert.h>
+#include <errno.h>
+#include <cutils/record_stream.h>
+#include <string.h>
+#include <stdint.h>
+#ifdef HAVE_WINSOCK
+#include <winsock2.h> /* for ntohl */
+#else
+#include <netinet/in.h>
+#endif
+
+#define HEADER_SIZE 4
+
+struct RecordStream {
+ int fd;
+ size_t maxRecordLen;
+
+ unsigned char *buffer;
+
+ unsigned char *unconsumed;
+ unsigned char *read_end;
+ unsigned char *buffer_end;
+};
+
+
+extern RecordStream *record_stream_new(int fd, size_t maxRecordLen)
+{
+ RecordStream *ret;
+
+ assert (maxRecordLen <= 0xffff);
+
+ ret = (RecordStream *)calloc(1, sizeof(RecordStream));
+
+ ret->fd = fd;
+ ret->maxRecordLen = maxRecordLen;
+ ret->buffer = (unsigned char *)malloc (maxRecordLen + HEADER_SIZE);
+
+ ret->unconsumed = ret->buffer;
+ ret->read_end = ret->buffer;
+ ret->buffer_end = ret->buffer + maxRecordLen + HEADER_SIZE;
+
+ return ret;
+}
+
+
+extern void record_stream_free(RecordStream *rs)
+{
+ free(rs->buffer);
+ free(rs);
+}
+
+
+/* returns NULL; if there isn't a full record in the buffer */
+static unsigned char * getEndOfRecord (unsigned char *p_begin,
+ unsigned char *p_end)
+{
+ size_t len;
+ unsigned char * p_ret;
+
+ if (p_end < p_begin + HEADER_SIZE) {
+ return NULL;
+ }
+
+ //First four bytes are length
+ len = ntohl(*((uint32_t *)p_begin));
+
+ p_ret = p_begin + HEADER_SIZE + len;
+
+ if (p_end < p_ret) {
+ return NULL;
+ }
+
+ return p_ret;
+}
+
+static void *getNextRecord (RecordStream *p_rs, size_t *p_outRecordLen)
+{
+ unsigned char *record_start, *record_end;
+
+ record_end = getEndOfRecord (p_rs->unconsumed, p_rs->read_end);
+
+ if (record_end != NULL) {
+ /* one full line in the buffer */
+ record_start = p_rs->unconsumed + HEADER_SIZE;
+ p_rs->unconsumed = record_end;
+
+ *p_outRecordLen = record_end - record_start;
+
+ return record_start;
+ }
+
+ return NULL;
+}
+
+/**
+ * Reads the next record from stream fd
+ * Records are prefixed by a 16-bit big endian length value
+ * Records may not be larger than maxRecordLen
+ *
+ * Doesn't guard against EINTR
+ *
+ * p_outRecord and p_outRecordLen may not be NULL
+ *
+ * Return 0 on success, -1 on fail
+ * Returns 0 with *p_outRecord set to NULL on end of stream
+ * Returns -1 / errno = EAGAIN if it needs to read again
+ */
+int record_stream_get_next (RecordStream *p_rs, void ** p_outRecord,
+ size_t *p_outRecordLen)
+{
+ void *ret;
+
+ ssize_t countRead;
+
+ /* is there one record already in the buffer? */
+ ret = getNextRecord (p_rs, p_outRecordLen);
+
+ if (ret != NULL) {
+ *p_outRecord = ret;
+ return 0;
+ }
+
+ // if the buffer is full and we don't have a full record
+ if (p_rs->unconsumed == p_rs->buffer
+ && p_rs->read_end == p_rs->buffer_end
+ ) {
+ // this should never happen
+ //ALOGE("max record length exceeded\n");
+ assert (0);
+ errno = EFBIG;
+ return -1;
+ }
+
+ if (p_rs->unconsumed != p_rs->buffer) {
+ // move remainder to the beginning of the buffer
+ size_t toMove;
+
+ toMove = p_rs->read_end - p_rs->unconsumed;
+ if (toMove) {
+ memmove(p_rs->buffer, p_rs->unconsumed, toMove);
+ }
+
+ p_rs->read_end = p_rs->buffer + toMove;
+ p_rs->unconsumed = p_rs->buffer;
+ }
+
+ countRead = read (p_rs->fd, p_rs->read_end, p_rs->buffer_end - p_rs->read_end);
+
+ if (countRead <= 0) {
+ /* note: end-of-stream drops through here too */
+ *p_outRecord = NULL;
+ return countRead;
+ }
+
+ p_rs->read_end += countRead;
+
+ ret = getNextRecord (p_rs, p_outRecordLen);
+
+ if (ret == NULL) {
+ /* not enough of a buffer to for a whole command */
+ errno = EAGAIN;
+ return -1;
+ }
+
+ *p_outRecord = ret;
+ return 0;
+}
diff --git a/libcutils/sched_policy.c b/libcutils/sched_policy.c
new file mode 100644
index 0000000..dfc8777
--- /dev/null
+++ b/libcutils/sched_policy.c
@@ -0,0 +1,340 @@
+/*
+** Copyright 2007, 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 "SchedPolicy"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <cutils/sched_policy.h>
+#include <log/log.h>
+
+#define UNUSED __attribute__((__unused__))
+
+/* Re-map SP_DEFAULT to the system default policy, and leave other values unchanged.
+ * Call this any place a SchedPolicy is used as an input parameter.
+ * Returns the possibly re-mapped policy.
+ */
+static inline SchedPolicy _policy(SchedPolicy p)
+{
+ return p == SP_DEFAULT ? SP_SYSTEM_DEFAULT : p;
+}
+
+#if defined(HAVE_ANDROID_OS)
+
+#include <pthread.h>
+#include <sched.h>
+#include <sys/prctl.h>
+
+#define POLICY_DEBUG 0
+
+// This prctl is only available in Android kernels.
+#define PR_SET_TIMERSLACK_PID 41
+
+// timer slack value in nS enforced when the thread moves to background
+#define TIMER_SLACK_BG 40000000
+
+static pthread_once_t the_once = PTHREAD_ONCE_INIT;
+
+static int __sys_supports_schedgroups = -1;
+
+// File descriptors open to /dev/cpuctl/../tasks, setup by initialize, or -1 on error.
+static int bg_cgroup_fd = -1;
+static int fg_cgroup_fd = -1;
+
+/* Add tid to the scheduling group defined by the policy */
+static int add_tid_to_cgroup(int tid, SchedPolicy policy)
+{
+ int fd;
+
+ switch (policy) {
+ case SP_BACKGROUND:
+ fd = bg_cgroup_fd;
+ break;
+ case SP_FOREGROUND:
+ case SP_AUDIO_APP:
+ case SP_AUDIO_SYS:
+ fd = fg_cgroup_fd;
+ break;
+ default:
+ fd = -1;
+ break;
+ }
+
+ if (fd < 0) {
+ SLOGE("add_tid_to_cgroup failed; policy=%d\n", policy);
+ return -1;
+ }
+
+ // specialized itoa -- works for tid > 0
+ char text[22];
+ char *end = text + sizeof(text) - 1;
+ char *ptr = end;
+ *ptr = '\0';
+ while (tid > 0) {
+ *--ptr = '0' + (tid % 10);
+ tid = tid / 10;
+ }
+
+ if (write(fd, ptr, end - ptr) < 0) {
+ /*
+ * If the thread is in the process of exiting,
+ * don't flag an error
+ */
+ if (errno == ESRCH)
+ return 0;
+ SLOGW("add_tid_to_cgroup failed to write '%s' (%s); policy=%d\n",
+ ptr, strerror(errno), policy);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void __initialize(void) {
+ char* filename;
+ if (!access("/dev/cpuctl/tasks", F_OK)) {
+ __sys_supports_schedgroups = 1;
+
+ filename = "/dev/cpuctl/tasks";
+ fg_cgroup_fd = open(filename, O_WRONLY | O_CLOEXEC);
+ if (fg_cgroup_fd < 0) {
+ SLOGE("open of %s failed: %s\n", filename, strerror(errno));
+ }
+
+ filename = "/dev/cpuctl/bg_non_interactive/tasks";
+ bg_cgroup_fd = open(filename, O_WRONLY | O_CLOEXEC);
+ if (bg_cgroup_fd < 0) {
+ SLOGE("open of %s failed: %s\n", filename, strerror(errno));
+ }
+ } else {
+ __sys_supports_schedgroups = 0;
+ }
+}
+
+/*
+ * Try to get the scheduler group.
+ *
+ * The data from /proc/<pid>/cgroup looks (something) like:
+ * 2:cpu:/bg_non_interactive
+ * 1:cpuacct:/
+ *
+ * We return the part after the "/", which will be an empty string for
+ * the default cgroup. If the string is longer than "bufLen", the string
+ * will be truncated.
+ */
+static int getSchedulerGroup(int tid, char* buf, size_t bufLen)
+{
+#ifdef HAVE_ANDROID_OS
+ char pathBuf[32];
+ char lineBuf[256];
+ FILE *fp;
+
+ snprintf(pathBuf, sizeof(pathBuf), "/proc/%d/cgroup", tid);
+ if (!(fp = fopen(pathBuf, "r"))) {
+ return -1;
+ }
+
+ while(fgets(lineBuf, sizeof(lineBuf) -1, fp)) {
+ char *next = lineBuf;
+ char *subsys;
+ char *grp;
+ size_t len;
+
+ /* Junk the first field */
+ if (!strsep(&next, ":")) {
+ goto out_bad_data;
+ }
+
+ if (!(subsys = strsep(&next, ":"))) {
+ goto out_bad_data;
+ }
+
+ if (strcmp(subsys, "cpu")) {
+ /* Not the subsys we're looking for */
+ continue;
+ }
+
+ if (!(grp = strsep(&next, ":"))) {
+ goto out_bad_data;
+ }
+ grp++; /* Drop the leading '/' */
+ len = strlen(grp);
+ grp[len-1] = '\0'; /* Drop the trailing '\n' */
+
+ if (bufLen <= len) {
+ len = bufLen - 1;
+ }
+ strncpy(buf, grp, len);
+ buf[len] = '\0';
+ fclose(fp);
+ return 0;
+ }
+
+ SLOGE("Failed to find cpu subsys");
+ fclose(fp);
+ return -1;
+ out_bad_data:
+ SLOGE("Bad cgroup data {%s}", lineBuf);
+ fclose(fp);
+ return -1;
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+int get_sched_policy(int tid, SchedPolicy *policy)
+{
+ if (tid == 0) {
+ tid = gettid();
+ }
+ pthread_once(&the_once, __initialize);
+
+ if (__sys_supports_schedgroups) {
+ char grpBuf[32];
+ if (getSchedulerGroup(tid, grpBuf, sizeof(grpBuf)) < 0)
+ return -1;
+ if (grpBuf[0] == '\0') {
+ *policy = SP_FOREGROUND;
+ } else if (!strcmp(grpBuf, "bg_non_interactive")) {
+ *policy = SP_BACKGROUND;
+ } else {
+ errno = ERANGE;
+ return -1;
+ }
+ } else {
+ int rc = sched_getscheduler(tid);
+ if (rc < 0)
+ return -1;
+ else if (rc == SCHED_NORMAL)
+ *policy = SP_FOREGROUND;
+ else if (rc == SCHED_BATCH)
+ *policy = SP_BACKGROUND;
+ else {
+ errno = ERANGE;
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int set_sched_policy(int tid, SchedPolicy policy)
+{
+ if (tid == 0) {
+ tid = gettid();
+ }
+ policy = _policy(policy);
+ pthread_once(&the_once, __initialize);
+
+#if POLICY_DEBUG
+ char statfile[64];
+ char statline[1024];
+ char thread_name[255];
+ int fd;
+
+ sprintf(statfile, "/proc/%d/stat", tid);
+ memset(thread_name, 0, sizeof(thread_name));
+
+ fd = open(statfile, O_RDONLY);
+ if (fd >= 0) {
+ int rc = read(fd, statline, 1023);
+ close(fd);
+ statline[rc] = 0;
+ char *p = statline;
+ char *q;
+
+ for (p = statline; *p != '('; p++);
+ p++;
+ for (q = p; *q != ')'; q++);
+
+ strncpy(thread_name, p, (q-p));
+ }
+ switch (policy) {
+ case SP_BACKGROUND:
+ SLOGD("vvv tid %d (%s)", tid, thread_name);
+ break;
+ case SP_FOREGROUND:
+ case SP_AUDIO_APP:
+ case SP_AUDIO_SYS:
+ SLOGD("^^^ tid %d (%s)", tid, thread_name);
+ break;
+ case SP_SYSTEM:
+ SLOGD("/// tid %d (%s)", tid, thread_name);
+ break;
+ default:
+ SLOGD("??? tid %d (%s)", tid, thread_name);
+ break;
+ }
+#endif
+
+ if (__sys_supports_schedgroups) {
+ if (add_tid_to_cgroup(tid, policy)) {
+ if (errno != ESRCH && errno != ENOENT)
+ return -errno;
+ }
+ } else {
+ struct sched_param param;
+
+ param.sched_priority = 0;
+ sched_setscheduler(tid,
+ (policy == SP_BACKGROUND) ?
+ SCHED_BATCH : SCHED_NORMAL,
+ ¶m);
+ }
+
+ prctl(PR_SET_TIMERSLACK_PID, policy == SP_BACKGROUND ? TIMER_SLACK_BG : 0, tid);
+
+ return 0;
+}
+
+#else
+
+/* Stubs for non-Android targets. */
+
+int set_sched_policy(int tid UNUSED, SchedPolicy policy UNUSED)
+{
+ return 0;
+}
+
+int get_sched_policy(int tid UNUSED, SchedPolicy *policy)
+{
+ *policy = SP_SYSTEM_DEFAULT;
+ return 0;
+}
+
+#endif
+
+const char *get_sched_policy_name(SchedPolicy policy)
+{
+ policy = _policy(policy);
+ static const char * const strings[SP_CNT] = {
+ [SP_BACKGROUND] = "bg",
+ [SP_FOREGROUND] = "fg",
+ [SP_SYSTEM] = " ",
+ [SP_AUDIO_APP] = "aa",
+ [SP_AUDIO_SYS] = "as",
+ };
+ if ((policy < SP_CNT) && (strings[policy] != NULL))
+ return strings[policy];
+ else
+ return "error";
+}
+
diff --git a/libcutils/socket_inaddr_any_server.c b/libcutils/socket_inaddr_any_server.c
new file mode 100644
index 0000000..6c849de
--- /dev/null
+++ b/libcutils/socket_inaddr_any_server.c
@@ -0,0 +1,68 @@
+/*
+** Copyright 2006, 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 <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifndef HAVE_WINSOCK
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#endif
+
+#include <cutils/sockets.h>
+
+#define LISTEN_BACKLOG 4
+
+/* open listen() port on any interface */
+int socket_inaddr_any_server(int port, int type)
+{
+ struct sockaddr_in addr;
+ int s, n;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ s = socket(AF_INET, type, 0);
+ if(s < 0) return -1;
+
+ n = 1;
+ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *) &n, sizeof(n));
+
+ if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ close(s);
+ return -1;
+ }
+
+ if (type == SOCK_STREAM) {
+ int ret;
+
+ ret = listen(s, LISTEN_BACKLOG);
+
+ if (ret < 0) {
+ close(s);
+ return -1;
+ }
+ }
+
+ return s;
+}
diff --git a/libcutils/socket_local.h b/libcutils/socket_local.h
new file mode 100644
index 0000000..45b9856
--- /dev/null
+++ b/libcutils/socket_local.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __SOCKET_LOCAL_H
+#define __SOCKET_LOCAL_H
+
+#define FILESYSTEM_SOCKET_PREFIX "/tmp/"
+#define ANDROID_RESERVED_SOCKET_PREFIX "/dev/socket/"
+
+/*
+ * Set up a given sockaddr_un, to have it refer to the given
+ * name in the given namespace. The namespace must be one
+ * of <code>ANDROID_SOCKET_NAMESPACE_ABSTRACT</code>,
+ * <code>ANDROID_SOCKET_NAMESPACE_RESERVED</code>, or
+ * <code>ANDROID_SOCKET_NAMESPACE_FILESYSTEM</code>. Upon success,
+ * the pointed at sockaddr_un is filled in and the pointed at
+ * socklen_t is set to indicate the final length. This function
+ * will fail if the namespace is invalid (not one of the indicated
+ * constants) or if the name is too long.
+ *
+ * @return 0 on success or -1 on failure
+ */
+int socket_make_sockaddr_un(const char *name, int namespaceId,
+ struct sockaddr_un *p_addr, socklen_t *alen);
+
+#endif
diff --git a/libcutils/socket_local_client.c b/libcutils/socket_local_client.c
new file mode 100644
index 0000000..7b42daa
--- /dev/null
+++ b/libcutils/socket_local_client.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2006 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 <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <cutils/sockets.h>
+
+#ifdef HAVE_WINSOCK
+
+int socket_local_client(const char *name, int namespaceId, int type)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+#else /* !HAVE_WINSOCK */
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/select.h>
+#include <sys/types.h>
+
+#include "socket_local.h"
+
+#define UNUSED __attribute__((unused))
+
+#define LISTEN_BACKLOG 4
+
+/* Documented in header file. */
+int socket_make_sockaddr_un(const char *name, int namespaceId,
+ struct sockaddr_un *p_addr, socklen_t *alen)
+{
+ memset (p_addr, 0, sizeof (*p_addr));
+ size_t namelen;
+
+ switch (namespaceId) {
+ case ANDROID_SOCKET_NAMESPACE_ABSTRACT:
+#if defined(__linux__)
+ namelen = strlen(name);
+
+ // Test with length +1 for the *initial* '\0'.
+ if ((namelen + 1) > sizeof(p_addr->sun_path)) {
+ goto error;
+ }
+
+ /*
+ * Note: The path in this case is *not* supposed to be
+ * '\0'-terminated. ("man 7 unix" for the gory details.)
+ */
+
+ p_addr->sun_path[0] = 0;
+ memcpy(p_addr->sun_path + 1, name, namelen);
+#else
+ /* this OS doesn't have the Linux abstract namespace */
+
+ namelen = strlen(name) + strlen(FILESYSTEM_SOCKET_PREFIX);
+ /* unix_path_max appears to be missing on linux */
+ if (namelen > sizeof(*p_addr)
+ - offsetof(struct sockaddr_un, sun_path) - 1) {
+ goto error;
+ }
+
+ strcpy(p_addr->sun_path, FILESYSTEM_SOCKET_PREFIX);
+ strcat(p_addr->sun_path, name);
+#endif
+ break;
+
+ case ANDROID_SOCKET_NAMESPACE_RESERVED:
+ namelen = strlen(name) + strlen(ANDROID_RESERVED_SOCKET_PREFIX);
+ /* unix_path_max appears to be missing on linux */
+ if (namelen > sizeof(*p_addr)
+ - offsetof(struct sockaddr_un, sun_path) - 1) {
+ goto error;
+ }
+
+ strcpy(p_addr->sun_path, ANDROID_RESERVED_SOCKET_PREFIX);
+ strcat(p_addr->sun_path, name);
+ break;
+
+ case ANDROID_SOCKET_NAMESPACE_FILESYSTEM:
+ namelen = strlen(name);
+ /* unix_path_max appears to be missing on linux */
+ if (namelen > sizeof(*p_addr)
+ - offsetof(struct sockaddr_un, sun_path) - 1) {
+ goto error;
+ }
+
+ strcpy(p_addr->sun_path, name);
+ break;
+ default:
+ // invalid namespace id
+ return -1;
+ }
+
+ p_addr->sun_family = AF_LOCAL;
+ *alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1;
+ return 0;
+error:
+ return -1;
+}
+
+/**
+ * connect to peer named "name" on fd
+ * returns same fd or -1 on error.
+ * fd is not closed on error. that's your job.
+ *
+ * Used by AndroidSocketImpl
+ */
+int socket_local_client_connect(int fd, const char *name, int namespaceId,
+ int type UNUSED)
+{
+ struct sockaddr_un addr;
+ socklen_t alen;
+ int err;
+
+ err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen);
+
+ if (err < 0) {
+ goto error;
+ }
+
+ if(connect(fd, (struct sockaddr *) &addr, alen) < 0) {
+ goto error;
+ }
+
+ return fd;
+
+error:
+ return -1;
+}
+
+/**
+ * connect to peer named "name"
+ * returns fd or -1 on error
+ */
+int socket_local_client(const char *name, int namespaceId, int type)
+{
+ int s;
+
+ s = socket(AF_LOCAL, type, 0);
+ if(s < 0) return -1;
+
+ if ( 0 > socket_local_client_connect(s, name, namespaceId, type)) {
+ close(s);
+ return -1;
+ }
+
+ return s;
+}
+
+#endif /* !HAVE_WINSOCK */
diff --git a/libcutils/socket_local_server.c b/libcutils/socket_local_server.c
new file mode 100644
index 0000000..60eb86b
--- /dev/null
+++ b/libcutils/socket_local_server.c
@@ -0,0 +1,126 @@
+/* libs/cutils/socket_local_server.c
+**
+** Copyright 2006, 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 <cutils/sockets.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stddef.h>
+
+#ifdef HAVE_WINSOCK
+
+int socket_local_server(const char *name, int namespaceId, int type)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+#else /* !HAVE_WINSOCK */
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#include "socket_local.h"
+
+#define LISTEN_BACKLOG 4
+
+/* Only the bottom bits are really the socket type; there are flags too. */
+#define SOCK_TYPE_MASK 0xf
+
+/**
+ * Binds a pre-created socket(AF_LOCAL) 's' to 'name'
+ * returns 's' on success, -1 on fail
+ *
+ * Does not call listen()
+ */
+int socket_local_server_bind(int s, const char *name, int namespaceId)
+{
+ struct sockaddr_un addr;
+ socklen_t alen;
+ int n;
+ int err;
+
+ err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen);
+
+ if (err < 0) {
+ return -1;
+ }
+
+ /* basically: if this is a filesystem path, unlink first */
+#if !defined(__linux__)
+ if (1) {
+#else
+ if (namespaceId == ANDROID_SOCKET_NAMESPACE_RESERVED
+ || namespaceId == ANDROID_SOCKET_NAMESPACE_FILESYSTEM) {
+#endif
+ /*ignore ENOENT*/
+ unlink(addr.sun_path);
+ }
+
+ n = 1;
+ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n));
+
+ if(bind(s, (struct sockaddr *) &addr, alen) < 0) {
+ return -1;
+ }
+
+ return s;
+
+}
+
+
+/** Open a server-side UNIX domain datagram socket in the Linux non-filesystem
+ * namespace
+ *
+ * Returns fd on success, -1 on fail
+ */
+
+int socket_local_server(const char *name, int namespace, int type)
+{
+ int err;
+ int s;
+
+ s = socket(AF_LOCAL, type, 0);
+ if (s < 0) return -1;
+
+ err = socket_local_server_bind(s, name, namespace);
+
+ if (err < 0) {
+ close(s);
+ return -1;
+ }
+
+ if ((type & SOCK_TYPE_MASK) == SOCK_STREAM) {
+ int ret;
+
+ ret = listen(s, LISTEN_BACKLOG);
+
+ if (ret < 0) {
+ close(s);
+ return -1;
+ }
+ }
+
+ return s;
+}
+
+#endif /* !HAVE_WINSOCK */
diff --git a/libcutils/socket_loopback_client.c b/libcutils/socket_loopback_client.c
new file mode 100644
index 0000000..9aed7b7
--- /dev/null
+++ b/libcutils/socket_loopback_client.c
@@ -0,0 +1,57 @@
+/*
+** Copyright 2006, 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 <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifndef HAVE_WINSOCK
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#endif
+
+#include <cutils/sockets.h>
+
+/* Connect to port on the loopback IP interface. type is
+ * SOCK_STREAM or SOCK_DGRAM.
+ * return is a file descriptor or -1 on error
+ */
+int socket_loopback_client(int port, int type)
+{
+ struct sockaddr_in addr;
+ int s;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ s = socket(AF_INET, type, 0);
+ if(s < 0) return -1;
+
+ if(connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ close(s);
+ return -1;
+ }
+
+ return s;
+
+}
+
diff --git a/libcutils/socket_loopback_server.c b/libcutils/socket_loopback_server.c
new file mode 100644
index 0000000..71afce7
--- /dev/null
+++ b/libcutils/socket_loopback_server.c
@@ -0,0 +1,69 @@
+/*
+** Copyright 2006, 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 <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define LISTEN_BACKLOG 4
+
+#ifndef HAVE_WINSOCK
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#endif
+
+#include <cutils/sockets.h>
+
+/* open listen() port on loopback interface */
+int socket_loopback_server(int port, int type)
+{
+ struct sockaddr_in addr;
+ int s, n;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ s = socket(AF_INET, type, 0);
+ if(s < 0) return -1;
+
+ n = 1;
+ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *) &n, sizeof(n));
+
+ if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ close(s);
+ return -1;
+ }
+
+ if (type == SOCK_STREAM) {
+ int ret;
+
+ ret = listen(s, LISTEN_BACKLOG);
+
+ if (ret < 0) {
+ close(s);
+ return -1;
+ }
+ }
+
+ return s;
+}
+
diff --git a/libcutils/socket_network_client.c b/libcutils/socket_network_client.c
new file mode 100644
index 0000000..3300b8f
--- /dev/null
+++ b/libcutils/socket_network_client.c
@@ -0,0 +1,125 @@
+/*
+** Copyright 2006, 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 <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include <cutils/sockets.h>
+
+static int toggle_O_NONBLOCK(int s) {
+ int flags = fcntl(s, F_GETFL);
+ if (flags == -1 || fcntl(s, F_SETFL, flags ^ O_NONBLOCK) == -1) {
+ close(s);
+ return -1;
+ }
+ return s;
+}
+
+// Connect to the given host and port.
+// 'timeout' is in seconds (0 for no timeout).
+// Returns a file descriptor or -1 on error.
+// On error, check *getaddrinfo_error (for use with gai_strerror) first;
+// if that's 0, use errno instead.
+int socket_network_client_timeout(const char* host, int port, int type, int timeout,
+ int* getaddrinfo_error) {
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = type;
+
+ char port_str[16];
+ snprintf(port_str, sizeof(port_str), "%d", port);
+
+ struct addrinfo* addrs;
+ *getaddrinfo_error = getaddrinfo(host, port_str, &hints, &addrs);
+ if (*getaddrinfo_error != 0) {
+ return -1;
+ }
+
+ // TODO: try all the addresses if there's more than one?
+ int family = addrs[0].ai_family;
+ int protocol = addrs[0].ai_protocol;
+ socklen_t addr_len = addrs[0].ai_addrlen;
+ struct sockaddr_storage addr;
+ memcpy(&addr, addrs[0].ai_addr, addr_len);
+
+ freeaddrinfo(addrs);
+
+ // The Mac doesn't have SOCK_NONBLOCK.
+ int s = socket(family, type, protocol);
+ if (s == -1 || toggle_O_NONBLOCK(s) == -1) return -1;
+
+ int rc = connect(s, (const struct sockaddr*) &addr, addr_len);
+ if (rc == 0) {
+ return toggle_O_NONBLOCK(s);
+ } else if (rc == -1 && errno != EINPROGRESS) {
+ close(s);
+ return -1;
+ }
+
+ fd_set r_set;
+ FD_ZERO(&r_set);
+ FD_SET(s, &r_set);
+ fd_set w_set = r_set;
+
+ struct timeval ts;
+ ts.tv_sec = timeout;
+ ts.tv_usec = 0;
+ if ((rc = select(s + 1, &r_set, &w_set, NULL, (timeout != 0) ? &ts : NULL)) == -1) {
+ close(s);
+ return -1;
+ }
+ if (rc == 0) { // we had a timeout
+ errno = ETIMEDOUT;
+ close(s);
+ return -1;
+ }
+
+ int error = 0;
+ socklen_t len = sizeof(error);
+ if (FD_ISSET(s, &r_set) || FD_ISSET(s, &w_set)) {
+ if (getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
+ close(s);
+ return -1;
+ }
+ } else {
+ close(s);
+ return -1;
+ }
+
+ if (error) { // check if we had a socket error
+ errno = error;
+ close(s);
+ return -1;
+ }
+
+ return toggle_O_NONBLOCK(s);
+}
+
+int socket_network_client(const char* host, int port, int type) {
+ int getaddrinfo_error;
+ return socket_network_client_timeout(host, port, type, 0, &getaddrinfo_error);
+}
diff --git a/libcutils/sockets.c b/libcutils/sockets.c
new file mode 100644
index 0000000..15ede2b
--- /dev/null
+++ b/libcutils/sockets.c
@@ -0,0 +1,47 @@
+/*
+ * 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 <cutils/sockets.h>
+#include <log/log.h>
+
+#ifdef HAVE_ANDROID_OS
+/* For the socket trust (credentials) check */
+#include <private/android_filesystem_config.h>
+#define __android_unused
+#else
+#define __android_unused __attribute__((__unused__))
+#endif
+
+bool socket_peer_is_trusted(int fd __android_unused)
+{
+#ifdef HAVE_ANDROID_OS
+ struct ucred cr;
+ socklen_t len = sizeof(cr);
+ int n = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len);
+
+ if (n != 0) {
+ ALOGE("could not get socket credentials: %s\n", strerror(errno));
+ return false;
+ }
+
+ if ((cr.uid != AID_ROOT) && (cr.uid != AID_SHELL)) {
+ ALOGE("untrusted userid on other end of socket: userid %d\n", cr.uid);
+ return false;
+ }
+#endif
+
+ return true;
+}
diff --git a/libcutils/str_parms.c b/libcutils/str_parms.c
new file mode 100644
index 0000000..924289a
--- /dev/null
+++ b/libcutils/str_parms.c
@@ -0,0 +1,359 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "str_params"
+//#define LOG_NDEBUG 0
+
+#define _GNU_SOURCE 1
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <cutils/hashmap.h>
+#include <cutils/memory.h>
+#include <cutils/str_parms.h>
+#include <log/log.h>
+
+#define UNUSED __attribute__((unused))
+
+struct str_parms {
+ Hashmap *map;
+};
+
+
+static bool str_eq(void *key_a, void *key_b)
+{
+ return !strcmp((const char *)key_a, (const char *)key_b);
+}
+
+/* use djb hash unless we find it inadequate */
+static int str_hash_fn(void *str)
+{
+ uint32_t hash = 5381;
+ char *p;
+
+ for (p = str; p && *p; p++)
+ hash = ((hash << 5) + hash) + *p;
+ return (int)hash;
+}
+
+struct str_parms *str_parms_create(void)
+{
+ struct str_parms *str_parms;
+
+ str_parms = calloc(1, sizeof(struct str_parms));
+ if (!str_parms)
+ return NULL;
+
+ str_parms->map = hashmapCreate(5, str_hash_fn, str_eq);
+ if (!str_parms->map)
+ goto err;
+
+ return str_parms;
+
+err:
+ free(str_parms);
+ return NULL;
+}
+
+struct remove_ctxt {
+ struct str_parms *str_parms;
+ const char *key;
+};
+
+static bool remove_pair(void *key, void *value, void *context)
+{
+ struct remove_ctxt *ctxt = context;
+ bool should_continue;
+
+ /*
+ * - if key is not supplied, then we are removing all entries,
+ * so remove key and continue (i.e. return true)
+ * - if key is supplied and matches, then remove it and don't
+ * continue (return false). Otherwise, return true and keep searching
+ * for key.
+ *
+ */
+ if (!ctxt->key) {
+ should_continue = true;
+ goto do_remove;
+ } else if (!strcmp(ctxt->key, key)) {
+ should_continue = false;
+ goto do_remove;
+ }
+
+ return true;
+
+do_remove:
+ hashmapRemove(ctxt->str_parms->map, key);
+ free(key);
+ free(value);
+ return should_continue;
+}
+
+void str_parms_del(struct str_parms *str_parms, const char *key)
+{
+ struct remove_ctxt ctxt = {
+ .str_parms = str_parms,
+ .key = key,
+ };
+ hashmapForEach(str_parms->map, remove_pair, &ctxt);
+}
+
+void str_parms_destroy(struct str_parms *str_parms)
+{
+ struct remove_ctxt ctxt = {
+ .str_parms = str_parms,
+ };
+
+ hashmapForEach(str_parms->map, remove_pair, &ctxt);
+ hashmapFree(str_parms->map);
+ free(str_parms);
+}
+
+struct str_parms *str_parms_create_str(const char *_string)
+{
+ struct str_parms *str_parms;
+ char *str;
+ char *kvpair;
+ char *tmpstr;
+ int items = 0;
+
+ str_parms = str_parms_create();
+ if (!str_parms)
+ goto err_create_str_parms;
+
+ str = strdup(_string);
+ if (!str)
+ goto err_strdup;
+
+ ALOGV("%s: source string == '%s'\n", __func__, _string);
+
+ kvpair = strtok_r(str, ";", &tmpstr);
+ while (kvpair && *kvpair) {
+ char *eq = strchr(kvpair, '='); /* would love strchrnul */
+ char *value;
+ char *key;
+ void *old_val;
+
+ if (eq == kvpair)
+ goto next_pair;
+
+ if (eq) {
+ key = strndup(kvpair, eq - kvpair);
+ if (*(++eq))
+ value = strdup(eq);
+ else
+ value = strdup("");
+ } else {
+ key = strdup(kvpair);
+ value = strdup("");
+ }
+
+ /* if we replaced a value, free it */
+ old_val = hashmapPut(str_parms->map, key, value);
+ if (old_val) {
+ free(old_val);
+ free(key);
+ }
+
+ items++;
+next_pair:
+ kvpair = strtok_r(NULL, ";", &tmpstr);
+ }
+
+ if (!items)
+ ALOGV("%s: no items found in string\n", __func__);
+
+ free(str);
+
+ return str_parms;
+
+err_strdup:
+ str_parms_destroy(str_parms);
+err_create_str_parms:
+ return NULL;
+}
+
+int str_parms_add_str(struct str_parms *str_parms, const char *key,
+ const char *value)
+{
+ void *tmp_key = NULL;
+ void *tmp_val = NULL;
+ void *old_val = NULL;
+
+ // strdup and hashmapPut both set errno on failure.
+ // Set errno to 0 so we can recognize whether anything went wrong.
+ int saved_errno = errno;
+ errno = 0;
+
+ tmp_key = strdup(key);
+ if (tmp_key == NULL) {
+ goto clean_up;
+ }
+
+ tmp_val = strdup(value);
+ if (tmp_val == NULL) {
+ goto clean_up;
+ }
+
+ old_val = hashmapPut(str_parms->map, tmp_key, tmp_val);
+ if (old_val == NULL) {
+ // Did hashmapPut fail?
+ if (errno == ENOMEM) {
+ goto clean_up;
+ }
+ // For new keys, hashmap takes ownership of tmp_key and tmp_val.
+ tmp_key = tmp_val = NULL;
+ } else {
+ // For existing keys, hashmap takes ownership of tmp_val.
+ // (It also gives up ownership of old_val.)
+ tmp_val = NULL;
+ }
+
+clean_up:
+ free(tmp_key);
+ free(tmp_val);
+ free(old_val);
+ int result = -errno;
+ errno = saved_errno;
+ return result;
+}
+
+int str_parms_add_int(struct str_parms *str_parms, const char *key, int value)
+{
+ char val_str[12];
+ int ret;
+
+ ret = snprintf(val_str, sizeof(val_str), "%d", value);
+ if (ret < 0)
+ return -EINVAL;
+
+ ret = str_parms_add_str(str_parms, key, val_str);
+ return ret;
+}
+
+int str_parms_add_float(struct str_parms *str_parms, const char *key,
+ float value)
+{
+ char val_str[23];
+ int ret;
+
+ ret = snprintf(val_str, sizeof(val_str), "%.10f", value);
+ if (ret < 0)
+ return -EINVAL;
+
+ ret = str_parms_add_str(str_parms, key, val_str);
+ return ret;
+}
+
+int str_parms_has_key(struct str_parms *str_parms, const char *key) {
+ return hashmapGet(str_parms->map, (void *)key) != NULL;
+}
+
+int str_parms_get_str(struct str_parms *str_parms, const char *key, char *val,
+ int len)
+{
+ char *value;
+
+ value = hashmapGet(str_parms->map, (void *)key);
+ if (value)
+ return strlcpy(val, value, len);
+
+ return -ENOENT;
+}
+
+int str_parms_get_int(struct str_parms *str_parms, const char *key, int *val)
+{
+ char *value;
+ char *end;
+
+ value = hashmapGet(str_parms->map, (void *)key);
+ if (!value)
+ return -ENOENT;
+
+ *val = (int)strtol(value, &end, 0);
+ if (*value != '\0' && *end == '\0')
+ return 0;
+
+ return -EINVAL;
+}
+
+int str_parms_get_float(struct str_parms *str_parms, const char *key,
+ float *val)
+{
+ float out;
+ char *value;
+ char *end;
+
+ value = hashmapGet(str_parms->map, (void *)key);
+ if (!value)
+ return -ENOENT;
+
+ out = strtof(value, &end);
+ if (*value == '\0' || *end != '\0')
+ return -EINVAL;
+
+ *val = out;
+ return 0;
+}
+
+static bool combine_strings(void *key, void *value, void *context)
+{
+ char **old_str = context;
+ char *new_str;
+ int ret;
+
+ ret = asprintf(&new_str, "%s%s%s=%s",
+ *old_str ? *old_str : "",
+ *old_str ? ";" : "",
+ (char *)key,
+ (char *)value);
+ if (*old_str)
+ free(*old_str);
+
+ if (ret >= 0) {
+ *old_str = new_str;
+ return true;
+ }
+
+ *old_str = NULL;
+ return false;
+}
+
+char *str_parms_to_str(struct str_parms *str_parms)
+{
+ char *str = NULL;
+
+ if (hashmapSize(str_parms->map) > 0)
+ hashmapForEach(str_parms->map, combine_strings, &str);
+ else
+ str = strdup("");
+ return str;
+}
+
+static bool dump_entry(void *key, void *value, void *context UNUSED)
+{
+ ALOGI("key: '%s' value: '%s'\n", (char *)key, (char *)value);
+ return true;
+}
+
+void str_parms_dump(struct str_parms *str_parms)
+{
+ hashmapForEach(str_parms->map, dump_entry, str_parms);
+}
diff --git a/libcutils/strdup16to8.c b/libcutils/strdup16to8.c
new file mode 100644
index 0000000..1a8ba86
--- /dev/null
+++ b/libcutils/strdup16to8.c
@@ -0,0 +1,168 @@
+/* libs/cutils/strdup16to8.c
+**
+** Copyright 2006, 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 <limits.h> /* for SIZE_MAX */
+
+#include <cutils/jstring.h>
+#include <assert.h>
+#include <stdlib.h>
+
+
+/**
+ * Given a UTF-16 string, compute the length of the corresponding UTF-8
+ * string in bytes.
+ */
+extern size_t strnlen16to8(const char16_t* utf16Str, size_t len)
+{
+ size_t utf8Len = 0;
+
+ /* A small note on integer overflow. The result can
+ * potentially be as big as 3*len, which will overflow
+ * for len > SIZE_MAX/3.
+ *
+ * Moreover, the result of a strnlen16to8 is typically used
+ * to allocate a destination buffer to strncpy16to8 which
+ * requires one more byte to terminate the UTF-8 copy, and
+ * this is generally done by careless users by incrementing
+ * the result without checking for integer overflows, e.g.:
+ *
+ * dst = malloc(strnlen16to8(utf16,len)+1)
+ *
+ * Due to this, the following code will try to detect
+ * overflows, and never return more than (SIZE_MAX-1)
+ * when it detects one. A careless user will try to malloc
+ * SIZE_MAX bytes, which will return NULL which can at least
+ * be detected appropriately.
+ *
+ * As far as I know, this function is only used by strndup16(),
+ * but better be safe than sorry.
+ */
+
+ /* Fast path for the usual case where 3*len is < SIZE_MAX-1.
+ */
+ if (len < (SIZE_MAX-1)/3) {
+ while (len--) {
+ unsigned int uic = *utf16Str++;
+
+ if (uic > 0x07ff)
+ utf8Len += 3;
+ else if (uic > 0x7f || uic == 0)
+ utf8Len += 2;
+ else
+ utf8Len++;
+ }
+ return utf8Len;
+ }
+
+ /* The slower but paranoid version */
+ while (len--) {
+ unsigned int uic = *utf16Str++;
+ size_t utf8Cur = utf8Len;
+
+ if (uic > 0x07ff)
+ utf8Len += 3;
+ else if (uic > 0x7f || uic == 0)
+ utf8Len += 2;
+ else
+ utf8Len++;
+
+ if (utf8Len < utf8Cur) /* overflow detected */
+ return SIZE_MAX-1;
+ }
+
+ /* don't return SIZE_MAX to avoid common user bug */
+ if (utf8Len == SIZE_MAX)
+ utf8Len = SIZE_MAX-1;
+
+ return utf8Len;
+}
+
+
+/**
+ * Convert a Java-Style UTF-16 string + length to a JNI-Style UTF-8 string.
+ *
+ * This basically means: embedded \0's in the UTF-16 string are encoded
+ * as "0xc0 0x80"
+ *
+ * Make sure you allocate "utf8Str" with the result of strlen16to8() + 1,
+ * not just "len".
+ *
+ * Please note, a terminated \0 is always added, so your result will always
+ * be "strlen16to8() + 1" bytes long.
+ */
+extern char* strncpy16to8(char* utf8Str, const char16_t* utf16Str, size_t len)
+{
+ char* utf8cur = utf8Str;
+
+ /* Note on overflows: We assume the user did check the result of
+ * strnlen16to8() properly or at a minimum checked the result of
+ * its malloc(SIZE_MAX) in case of overflow.
+ */
+ while (len--) {
+ unsigned int uic = *utf16Str++;
+
+ if (uic > 0x07ff) {
+ *utf8cur++ = (uic >> 12) | 0xe0;
+ *utf8cur++ = ((uic >> 6) & 0x3f) | 0x80;
+ *utf8cur++ = (uic & 0x3f) | 0x80;
+ } else if (uic > 0x7f || uic == 0) {
+ *utf8cur++ = (uic >> 6) | 0xc0;
+ *utf8cur++ = (uic & 0x3f) | 0x80;
+ } else {
+ *utf8cur++ = uic;
+
+ if (uic == 0) {
+ break;
+ }
+ }
+ }
+
+ *utf8cur = '\0';
+
+ return utf8Str;
+}
+
+/**
+ * Convert a UTF-16 string to UTF-8.
+ *
+ */
+char * strndup16to8 (const char16_t* s, size_t n)
+{
+ char* ret;
+ size_t len;
+
+ if (s == NULL) {
+ return NULL;
+ }
+
+ len = strnlen16to8(s, n);
+
+ /* We are paranoid, and we check for SIZE_MAX-1
+ * too since it is an overflow value for our
+ * strnlen16to8 implementation.
+ */
+ if (len >= SIZE_MAX-1)
+ return NULL;
+
+ ret = malloc(len + 1);
+ if (ret == NULL)
+ return NULL;
+
+ strncpy16to8 (ret, s, n);
+
+ return ret;
+}
diff --git a/libcutils/strdup8to16.c b/libcutils/strdup8to16.c
new file mode 100644
index 0000000..63e5ca4
--- /dev/null
+++ b/libcutils/strdup8to16.c
@@ -0,0 +1,214 @@
+/* libs/cutils/strdup8to16.c
+**
+** Copyright 2006, 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 <cutils/jstring.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <limits.h>
+
+/* See http://www.unicode.org/reports/tr22/ for discussion
+ * on invalid sequences
+ */
+
+#define UTF16_REPLACEMENT_CHAR 0xfffd
+
+/* Clever trick from Dianne that returns 1-4 depending on leading bit sequence*/
+#define UTF8_SEQ_LENGTH(ch) (((0xe5000000 >> ((ch >> 3) & 0x1e)) & 3) + 1)
+
+/* note: macro expands to multiple lines */
+#define UTF8_SHIFT_AND_MASK(unicode, byte) \
+ (unicode)<<=6; (unicode) |= (0x3f & (byte));
+
+#define UNICODE_UPPER_LIMIT 0x10fffd
+
+/**
+ * out_len is an out parameter (which may not be null) containing the
+ * length of the UTF-16 string (which may contain embedded \0's)
+ */
+
+extern char16_t * strdup8to16 (const char* s, size_t *out_len)
+{
+ char16_t *ret;
+ size_t len;
+
+ if (s == NULL) return NULL;
+
+ len = strlen8to16(s);
+
+ // fail on overflow
+ if (len && SIZE_MAX/len < sizeof(char16_t))
+ return NULL;
+
+ // no plus-one here. UTF-16 strings are not null terminated
+ ret = (char16_t *) malloc (sizeof(char16_t) * len);
+
+ return strcpy8to16 (ret, s, out_len);
+}
+
+/**
+ * Like "strlen", but for strings encoded with Java's modified UTF-8.
+ *
+ * The value returned is the number of UTF-16 characters required
+ * to represent this string.
+ */
+extern size_t strlen8to16 (const char* utf8Str)
+{
+ size_t len = 0;
+ int ic;
+ int expected = 0;
+
+ while ((ic = *utf8Str++) != '\0') {
+ /* bytes that start 0? or 11 are lead bytes and count as characters.*/
+ /* bytes that start 10 are extention bytes and are not counted */
+
+ if ((ic & 0xc0) == 0x80) {
+ /* count the 0x80 extention bytes. if we have more than
+ * expected, then start counting them because strcpy8to16
+ * will insert UTF16_REPLACEMENT_CHAR's
+ */
+ expected--;
+ if (expected < 0) {
+ len++;
+ }
+ } else {
+ len++;
+ expected = UTF8_SEQ_LENGTH(ic) - 1;
+
+ /* this will result in a surrogate pair */
+ if (expected == 3) {
+ len++;
+ }
+ }
+ }
+
+ return len;
+}
+
+
+
+/*
+ * Retrieve the next UTF-32 character from a UTF-8 string.
+ *
+ * Stops at inner \0's
+ *
+ * Returns UTF16_REPLACEMENT_CHAR if an invalid sequence is encountered
+ *
+ * Advances "*pUtf8Ptr" to the start of the next character.
+ */
+static inline uint32_t getUtf32FromUtf8(const char** pUtf8Ptr)
+{
+ uint32_t ret;
+ int seq_len;
+ int i;
+
+ /* Mask for leader byte for lengths 1, 2, 3, and 4 respectively*/
+ static const char leaderMask[4] = {0xff, 0x1f, 0x0f, 0x07};
+
+ /* Bytes that start with bits "10" are not leading characters. */
+ if (((**pUtf8Ptr) & 0xc0) == 0x80) {
+ (*pUtf8Ptr)++;
+ return UTF16_REPLACEMENT_CHAR;
+ }
+
+ /* note we tolerate invalid leader 11111xxx here */
+ seq_len = UTF8_SEQ_LENGTH(**pUtf8Ptr);
+
+ ret = (**pUtf8Ptr) & leaderMask [seq_len - 1];
+
+ if (**pUtf8Ptr == '\0') return ret;
+
+ (*pUtf8Ptr)++;
+ for (i = 1; i < seq_len ; i++, (*pUtf8Ptr)++) {
+ if ((**pUtf8Ptr) == '\0') return UTF16_REPLACEMENT_CHAR;
+ if (((**pUtf8Ptr) & 0xc0) != 0x80) return UTF16_REPLACEMENT_CHAR;
+
+ UTF8_SHIFT_AND_MASK(ret, **pUtf8Ptr);
+ }
+
+ return ret;
+}
+
+
+/**
+ * out_len is an out parameter (which may not be null) containing the
+ * length of the UTF-16 string (which may contain embedded \0's)
+ */
+
+extern char16_t * strcpy8to16 (char16_t *utf16Str, const char*utf8Str,
+ size_t *out_len)
+{
+ char16_t *dest = utf16Str;
+
+ while (*utf8Str != '\0') {
+ uint32_t ret;
+
+ ret = getUtf32FromUtf8(&utf8Str);
+
+ if (ret <= 0xffff) {
+ *dest++ = (char16_t) ret;
+ } else if (ret <= UNICODE_UPPER_LIMIT) {
+ /* Create surrogate pairs */
+ /* See http://en.wikipedia.org/wiki/UTF-16/UCS-2#Method_for_code_points_in_Plane_1.2C_Plane_2 */
+
+ *dest++ = 0xd800 | ((ret - 0x10000) >> 10);
+ *dest++ = 0xdc00 | ((ret - 0x10000) & 0x3ff);
+ } else {
+ *dest++ = UTF16_REPLACEMENT_CHAR;
+ }
+ }
+
+ *out_len = dest - utf16Str;
+
+ return utf16Str;
+}
+
+/**
+ * length is the number of characters in the UTF-8 string.
+ * out_len is an out parameter (which may not be null) containing the
+ * length of the UTF-16 string (which may contain embedded \0's)
+ */
+
+extern char16_t * strcpylen8to16 (char16_t *utf16Str, const char*utf8Str,
+ int length, size_t *out_len)
+{
+ /* TODO: Share more of this code with the method above. Only 2 lines changed. */
+
+ char16_t *dest = utf16Str;
+
+ const char *end = utf8Str + length; /* This line */
+ while (utf8Str < end) { /* and this line changed. */
+ uint32_t ret;
+
+ ret = getUtf32FromUtf8(&utf8Str);
+
+ if (ret <= 0xffff) {
+ *dest++ = (char16_t) ret;
+ } else if (ret <= UNICODE_UPPER_LIMIT) {
+ /* Create surrogate pairs */
+ /* See http://en.wikipedia.org/wiki/UTF-16/UCS-2#Method_for_code_points_in_Plane_1.2C_Plane_2 */
+
+ *dest++ = 0xd800 | ((ret - 0x10000) >> 10);
+ *dest++ = 0xdc00 | ((ret - 0x10000) & 0x3ff);
+ } else {
+ *dest++ = UTF16_REPLACEMENT_CHAR;
+ }
+ }
+
+ *out_len = dest - utf16Str;
+
+ return utf16Str;
+}
diff --git a/libcutils/strlcpy.c b/libcutils/strlcpy.c
new file mode 100644
index 0000000..c66246c
--- /dev/null
+++ b/libcutils/strlcpy.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+
+#if defined(__GLIBC__) || defined(_WIN32)
+
+#include <string.h>
+
+#include <cutils/memory.h>
+
+/* Implementation of strlcpy() for platforms that don't already have it. */
+
+/*
+ * Copy src to string dst of size siz. At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+size_t
+strlcpy(char *dst, const char *src, size_t siz)
+{
+ char *d = dst;
+ const char *s = src;
+ size_t n = siz;
+
+ /* Copy as many bytes as will fit */
+ if (n != 0) {
+ while (--n != 0) {
+ if ((*d++ = *s++) == '\0')
+ break;
+ }
+ }
+
+ /* Not enough room in dst, add NUL and traverse rest of src */
+ if (n == 0) {
+ if (siz != 0)
+ *d = '\0'; /* NUL-terminate dst */
+ while (*s++)
+ ;
+ }
+
+ return(s - src - 1); /* count does not include NUL */
+}
+
+#endif
diff --git a/libcutils/tests/Android.mk b/libcutils/tests/Android.mk
new file mode 100644
index 0000000..cf70345
--- /dev/null
+++ b/libcutils/tests/Android.mk
@@ -0,0 +1,73 @@
+# Copyright (C) 2014 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)
+
+test_src_files := \
+ test_str_parms.cpp \
+
+test_target_only_src_files := \
+ MemsetTest.cpp \
+ PropertiesTest.cpp \
+
+test_libraries := libcutils liblog
+
+
+#
+# Target.
+#
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libcutils_test
+LOCAL_SRC_FILES := $(test_src_files) $(test_target_only_src_files)
+LOCAL_SHARED_LIBRARIES := $(test_libraries)
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+include $(BUILD_NATIVE_TEST)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libcutils_test_static
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_SRC_FILES := $(test_src_files) $(test_target_only_src_files)
+LOCAL_STATIC_LIBRARIES := libc $(test_libraries)
+LOCAL_CXX_STL := libc++_static
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+include $(BUILD_NATIVE_TEST)
+
+
+#
+# Host.
+#
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libcutils_test
+LOCAL_SRC_FILES := $(test_src_files)
+LOCAL_SHARED_LIBRARIES := $(test_libraries)
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+include $(BUILD_HOST_NATIVE_TEST)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libcutils_test_static
+LOCAL_SRC_FILES := $(test_src_files)
+LOCAL_STATIC_LIBRARIES := $(test_libraries)
+LOCAL_CXX_STL := libc++_static
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+include $(BUILD_HOST_NATIVE_TEST)
diff --git a/libcutils/tests/MemsetTest.cpp b/libcutils/tests/MemsetTest.cpp
new file mode 100644
index 0000000..45efc51
--- /dev/null
+++ b/libcutils/tests/MemsetTest.cpp
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2014 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 <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+
+#include <cutils/memory.h>
+#include <gtest/gtest.h>
+
+#define FENCEPOST_LENGTH 8
+
+#define MAX_TEST_SIZE (64*1024)
+// Choose values that have no repeating byte values.
+#define MEMSET16_PATTERN 0xb139
+#define MEMSET32_PATTERN 0x48193a27
+
+enum test_e {
+ MEMSET16 = 0,
+ MEMSET32,
+};
+
+static int g_memset16_aligns[][2] = {
+ { 2, 0 },
+ { 4, 0 },
+ { 8, 0 },
+ { 16, 0 },
+ { 32, 0 },
+ { 64, 0 },
+ { 128, 0 },
+
+ { 4, 2 },
+
+ { 8, 2 },
+ { 8, 4 },
+ { 8, 6 },
+
+ { 128, 2 },
+ { 128, 4 },
+ { 128, 6 },
+ { 128, 8 },
+ { 128, 10 },
+ { 128, 12 },
+ { 128, 14 },
+ { 128, 16 },
+};
+
+static int g_memset32_aligns[][2] = {
+ { 4, 0 },
+ { 8, 0 },
+ { 16, 0 },
+ { 32, 0 },
+ { 64, 0 },
+ { 128, 0 },
+
+ { 8, 4 },
+
+ { 128, 4 },
+ { 128, 8 },
+ { 128, 12 },
+ { 128, 16 },
+};
+
+static size_t GetIncrement(size_t len, size_t min_incr) {
+ if (len >= 4096) {
+ return 1024;
+ } else if (len >= 1024) {
+ return 256;
+ }
+ return min_incr;
+}
+
+// Return a pointer into the current buffer with the specified alignment.
+static void *GetAlignedPtr(void *orig_ptr, int alignment, int or_mask) {
+ uint64_t ptr = reinterpret_cast<uint64_t>(orig_ptr);
+ if (alignment > 0) {
+ // When setting the alignment, set it to exactly the alignment chosen.
+ // The pointer returned will be guaranteed not to be aligned to anything
+ // more than that.
+ ptr += alignment - (ptr & (alignment - 1));
+ ptr |= alignment | or_mask;
+ }
+
+ return reinterpret_cast<void*>(ptr);
+}
+
+static void SetFencepost(uint8_t *buffer) {
+ for (int i = 0; i < FENCEPOST_LENGTH; i += 2) {
+ buffer[i] = 0xde;
+ buffer[i+1] = 0xad;
+ }
+}
+
+static void VerifyFencepost(uint8_t *buffer) {
+ for (int i = 0; i < FENCEPOST_LENGTH; i += 2) {
+ if (buffer[i] != 0xde || buffer[i+1] != 0xad) {
+ uint8_t expected_value;
+ if (buffer[i] == 0xde) {
+ i++;
+ expected_value = 0xad;
+ } else {
+ expected_value = 0xde;
+ }
+ ASSERT_EQ(expected_value, buffer[i]);
+ }
+ }
+}
+
+void RunMemsetTests(test_e test_type, uint32_t value, int align[][2], size_t num_aligns) {
+ size_t min_incr = 4;
+ if (test_type == MEMSET16) {
+ min_incr = 2;
+ value |= value << 16;
+ }
+ uint32_t* expected_buf = new uint32_t[MAX_TEST_SIZE/sizeof(uint32_t)];
+ for (size_t i = 0; i < MAX_TEST_SIZE/sizeof(uint32_t); i++) {
+ expected_buf[i] = value;
+ }
+
+ // Allocate one large buffer with lots of extra space so that we can
+ // guarantee that all possible alignments will fit.
+ uint8_t *buf = new uint8_t[3*MAX_TEST_SIZE];
+ uint8_t *buf_align;
+ for (size_t i = 0; i < num_aligns; i++) {
+ size_t incr = min_incr;
+ for (size_t len = incr; len <= MAX_TEST_SIZE; len += incr) {
+ incr = GetIncrement(len, min_incr);
+
+ buf_align = reinterpret_cast<uint8_t*>(GetAlignedPtr(
+ buf+FENCEPOST_LENGTH, align[i][0], align[i][1]));
+
+ SetFencepost(&buf_align[-FENCEPOST_LENGTH]);
+ SetFencepost(&buf_align[len]);
+
+ memset(buf_align, 0xff, len);
+ if (test_type == MEMSET16) {
+ android_memset16(reinterpret_cast<uint16_t*>(buf_align), value, len);
+ } else {
+ android_memset32(reinterpret_cast<uint32_t*>(buf_align), value, len);
+ }
+ ASSERT_EQ(0, memcmp(expected_buf, buf_align, len))
+ << "Failed size " << len << " align " << align[i][0] << " " << align[i][1] << "\n";
+
+ VerifyFencepost(&buf_align[-FENCEPOST_LENGTH]);
+ VerifyFencepost(&buf_align[len]);
+ }
+ }
+ delete expected_buf;
+ delete buf;
+}
+
+TEST(libcutils, android_memset16_non_zero) {
+ RunMemsetTests(MEMSET16, MEMSET16_PATTERN, g_memset16_aligns, sizeof(g_memset16_aligns)/sizeof(int[2]));
+}
+
+TEST(libcutils, android_memset16_zero) {
+ RunMemsetTests(MEMSET16, 0, g_memset16_aligns, sizeof(g_memset16_aligns)/sizeof(int[2]));
+}
+
+TEST(libcutils, android_memset32_non_zero) {
+ RunMemsetTests(MEMSET32, MEMSET32_PATTERN, g_memset32_aligns, sizeof(g_memset32_aligns)/sizeof(int[2]));
+}
+
+TEST(libcutils, android_memset32_zero) {
+ RunMemsetTests(MEMSET32, 0, g_memset32_aligns, sizeof(g_memset32_aligns)/sizeof(int[2]));
+}
diff --git a/libcutils/tests/PropertiesTest.cpp b/libcutils/tests/PropertiesTest.cpp
new file mode 100644
index 0000000..659821c
--- /dev/null
+++ b/libcutils/tests/PropertiesTest.cpp
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2014 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 "Properties_test"
+#include <utils/Log.h>
+#include <gtest/gtest.h>
+
+#include <cutils/properties.h>
+#include <limits.h>
+#include <string>
+#include <sstream>
+#include <iostream>
+
+namespace android {
+
+#define STRINGIFY_INNER(x) #x
+#define STRINGIFY(x) STRINGIFY_INNER(x)
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
+#define ASSERT_OK(x) ASSERT_EQ(0, (x))
+#define EXPECT_OK(x) EXPECT_EQ(0, (x))
+
+#define PROPERTY_TEST_KEY "libcutils.test.key"
+#define PROPERTY_TEST_VALUE_DEFAULT "<<<default_value>>>"
+
+template <typename T>
+static std::string HexString(T value) {
+ std::stringstream ss;
+ ss << "0x" << std::hex << std::uppercase << value;
+ return ss.str();
+}
+
+template <typename T>
+static ::testing::AssertionResult AssertEqualHex(const char *mExpr,
+ const char *nExpr,
+ T m,
+ T n) {
+ if (m == n) {
+ return ::testing::AssertionSuccess();
+ }
+
+ return ::testing::AssertionFailure()
+ << mExpr << " and " << nExpr << " (expected: " << HexString(m) <<
+ ", actual: " << HexString(n) << ") are not equal";
+}
+
+class PropertiesTest : public testing::Test {
+public:
+ PropertiesTest() : mValue() {}
+protected:
+ virtual void SetUp() {
+ EXPECT_OK(property_set(PROPERTY_TEST_KEY, /*value*/NULL));
+ }
+
+ virtual void TearDown() {
+ EXPECT_OK(property_set(PROPERTY_TEST_KEY, /*value*/NULL));
+ }
+
+ char mValue[PROPERTY_VALUE_MAX];
+
+ template <typename T>
+ static std::string ToString(T value) {
+ std::stringstream ss;
+ ss << value;
+
+ return ss.str();
+ }
+
+ // Return length of property read; value is written into mValue
+ int SetAndGetProperty(const char* value, const char* defaultValue = PROPERTY_TEST_VALUE_DEFAULT) {
+ EXPECT_OK(property_set(PROPERTY_TEST_KEY, value)) << "value: '" << value << "'";
+ return property_get(PROPERTY_TEST_KEY, mValue, defaultValue);
+ }
+
+ void ResetValue(unsigned char c = 0xFF) {
+ for (size_t i = 0; i < ARRAY_SIZE(mValue); ++i) {
+ mValue[i] = (char) c;
+ }
+ }
+};
+
+TEST_F(PropertiesTest, SetString) {
+
+ // Null key -> unsuccessful set
+ {
+ // Null key -> fails
+ EXPECT_GT(0, property_set(/*key*/NULL, PROPERTY_TEST_VALUE_DEFAULT));
+ }
+
+ // Null value -> returns default value
+ {
+ // Null value -> OK , and it clears the value
+ EXPECT_OK(property_set(PROPERTY_TEST_KEY, /*value*/NULL));
+ ResetValue();
+
+ // Since the value is null, default value will be returned
+ int len = property_get(PROPERTY_TEST_KEY, mValue, PROPERTY_TEST_VALUE_DEFAULT);
+ EXPECT_EQ(strlen(PROPERTY_TEST_VALUE_DEFAULT), len);
+ EXPECT_STREQ(PROPERTY_TEST_VALUE_DEFAULT, mValue);
+ }
+
+ // Trivial case => get returns what was set
+ {
+ int len = SetAndGetProperty("hello_world");
+ EXPECT_EQ(strlen("hello_world"), len) << "hello_world key";
+ EXPECT_STREQ("hello_world", mValue);
+ ResetValue();
+ }
+
+ // Set to empty string => get returns default always
+ {
+ const char* EMPTY_STRING_DEFAULT = "EMPTY_STRING";
+ int len = SetAndGetProperty("", EMPTY_STRING_DEFAULT);
+ EXPECT_EQ(strlen(EMPTY_STRING_DEFAULT), len) << "empty key";
+ EXPECT_STREQ(EMPTY_STRING_DEFAULT, mValue);
+ ResetValue();
+ }
+
+ // Set to max length => get returns what was set
+ {
+ std::string maxLengthString = std::string(PROPERTY_VALUE_MAX-1, 'a');
+
+ int len = SetAndGetProperty(maxLengthString.c_str());
+ EXPECT_EQ(PROPERTY_VALUE_MAX-1, len) << "max length key";
+ EXPECT_STREQ(maxLengthString.c_str(), mValue);
+ ResetValue();
+ }
+
+ // Set to max length + 1 => set fails
+ {
+ const char* VALID_TEST_VALUE = "VALID_VALUE";
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, VALID_TEST_VALUE));
+
+ std::string oneLongerString = std::string(PROPERTY_VALUE_MAX, 'a');
+
+ // Expect that the value set fails since it's too long
+ EXPECT_GT(0, property_set(PROPERTY_TEST_KEY, oneLongerString.c_str()));
+ int len = property_get(PROPERTY_TEST_KEY, mValue, PROPERTY_TEST_VALUE_DEFAULT);
+
+ EXPECT_EQ(strlen(VALID_TEST_VALUE), len) << "set should've failed";
+ EXPECT_STREQ(VALID_TEST_VALUE, mValue);
+ ResetValue();
+ }
+}
+
+TEST_F(PropertiesTest, GetString) {
+
+ // Try to use a default value that's too long => set fails
+ {
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
+
+ std::string maxLengthString = std::string(PROPERTY_VALUE_MAX-1, 'a');
+ std::string oneLongerString = std::string(PROPERTY_VALUE_MAX, 'a');
+
+ // Expect that the value is truncated since it's too long (by 1)
+ int len = property_get(PROPERTY_TEST_KEY, mValue, oneLongerString.c_str());
+ EXPECT_EQ(PROPERTY_VALUE_MAX-1, len);
+ EXPECT_STREQ(maxLengthString.c_str(), mValue);
+ ResetValue();
+ }
+}
+
+TEST_F(PropertiesTest, GetBool) {
+ /**
+ * TRUE
+ */
+ const char *valuesTrue[] = { "1", "true", "y", "yes", "on", };
+ for (size_t i = 0; i < ARRAY_SIZE(valuesTrue); ++i) {
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, valuesTrue[i]));
+ bool val = property_get_bool(PROPERTY_TEST_KEY, /*default_value*/false);
+ EXPECT_TRUE(val) << "Property should've been TRUE for value: '" << valuesTrue[i] << "'";
+ }
+
+ /**
+ * FALSE
+ */
+ const char *valuesFalse[] = { "0", "false", "n", "no", "off", };
+ for (size_t i = 0; i < ARRAY_SIZE(valuesFalse); ++i) {
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, valuesFalse[i]));
+ bool val = property_get_bool(PROPERTY_TEST_KEY, /*default_value*/true);
+ EXPECT_FALSE(val) << "Property shoud've been FALSE For string value: '" << valuesFalse[i] << "'";
+ }
+
+ /**
+ * NEITHER
+ */
+ const char *valuesNeither[] = { "x0", "x1", "2", "-2", "True", "False", "garbage", "", " ",
+ "+1", " 1 ", " true", " true ", " y ", " yes", "yes ",
+ "+0", "-0", "00", " 00 ", " false", "false ",
+ };
+ for (size_t i = 0; i < ARRAY_SIZE(valuesNeither); ++i) {
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, valuesNeither[i]));
+
+ // The default value should always be used
+ bool val = property_get_bool(PROPERTY_TEST_KEY, /*default_value*/true);
+ EXPECT_TRUE(val) << "Property should've been NEITHER (true) for string value: '" << valuesNeither[i] << "'";
+
+ val = property_get_bool(PROPERTY_TEST_KEY, /*default_value*/false);
+ EXPECT_FALSE(val) << "Property should've been NEITHER (false) for string value: '" << valuesNeither[i] << "'";
+ }
+}
+
+TEST_F(PropertiesTest, GetInt64) {
+ const int64_t DEFAULT_VALUE = INT64_C(0xDEADBEEFBEEFDEAD);
+
+ const std::string longMaxString = ToString(INT64_MAX);
+ const std::string longStringOverflow = longMaxString + "0";
+
+ const std::string longMinString = ToString(INT64_MIN);
+ const std::string longStringUnderflow = longMinString + "0";
+
+ const char* setValues[] = {
+ // base 10
+ "1", "2", "12345", "-1", "-2", "-12345",
+ // base 16
+ "0xFF", "0x0FF", "0xC0FFEE",
+ // base 8
+ "0", "01234", "07",
+ // corner cases
+ " 2", "2 ", "+0", "-0", " +0 ", longMaxString.c_str(), longMinString.c_str(),
+ // failing cases
+ NULL, "", " ", " ", "hello", " true ", "y",
+ longStringOverflow.c_str(), longStringUnderflow.c_str(),
+ };
+
+ int64_t getValues[] = {
+ // base 10
+ 1, 2, 12345, -1, -2, -12345,
+ // base 16
+ 0xFF, 0x0FF, 0xC0FFEE,
+ // base 8
+ 0, 01234, 07,
+ // corner cases
+ 2, 2, 0, 0, 0, INT64_MAX, INT64_MIN,
+ // failing cases
+ DEFAULT_VALUE, DEFAULT_VALUE, DEFAULT_VALUE, DEFAULT_VALUE, DEFAULT_VALUE, DEFAULT_VALUE, DEFAULT_VALUE,
+ DEFAULT_VALUE, DEFAULT_VALUE,
+ };
+
+ ASSERT_EQ(ARRAY_SIZE(setValues), ARRAY_SIZE(getValues));
+
+ for (size_t i = 0; i < ARRAY_SIZE(setValues); ++i) {
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, setValues[i]));
+
+ int64_t val = property_get_int64(PROPERTY_TEST_KEY, DEFAULT_VALUE);
+ EXPECT_PRED_FORMAT2(AssertEqualHex, getValues[i], val) << "Property was set to '" << setValues[i] << "'";
+ }
+}
+
+TEST_F(PropertiesTest, GetInt32) {
+ const int32_t DEFAULT_VALUE = INT32_C(0xDEADBEEF);
+
+ const std::string intMaxString = ToString(INT32_MAX);
+ const std::string intStringOverflow = intMaxString + "0";
+
+ const std::string intMinString = ToString(INT32_MIN);
+ const std::string intStringUnderflow = intMinString + "0";
+
+ const char* setValues[] = {
+ // base 10
+ "1", "2", "12345", "-1", "-2", "-12345",
+ // base 16
+ "0xFF", "0x0FF", "0xC0FFEE", "0Xf00",
+ // base 8
+ "0", "01234", "07",
+ // corner cases
+ " 2", "2 ", "+0", "-0", " +0 ", intMaxString.c_str(), intMinString.c_str(),
+ // failing cases
+ NULL, "", " ", " ", "hello", " true ", "y",
+ intStringOverflow.c_str(), intStringUnderflow.c_str(),
+ };
+
+ int32_t getValues[] = {
+ // base 10
+ 1, 2, 12345, -1, -2, -12345,
+ // base 16
+ 0xFF, 0x0FF, 0xC0FFEE, 0Xf00,
+ // base 8
+ 0, 01234, 07,
+ // corner cases
+ 2, 2, 0, 0, 0, INT32_MAX, INT32_MIN,
+ // failing cases
+ DEFAULT_VALUE, DEFAULT_VALUE, DEFAULT_VALUE, DEFAULT_VALUE, DEFAULT_VALUE, DEFAULT_VALUE, DEFAULT_VALUE,
+ DEFAULT_VALUE, DEFAULT_VALUE,
+ };
+
+ ASSERT_EQ(ARRAY_SIZE(setValues), ARRAY_SIZE(getValues));
+
+ for (size_t i = 0; i < ARRAY_SIZE(setValues); ++i) {
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, setValues[i]));
+
+ int32_t val = property_get_int32(PROPERTY_TEST_KEY, DEFAULT_VALUE);
+ EXPECT_PRED_FORMAT2(AssertEqualHex, getValues[i], val) << "Property was set to '" << setValues[i] << "'";
+ }
+}
+
+} // namespace android
diff --git a/libcutils/tests/test_str_parms.cpp b/libcutils/tests/test_str_parms.cpp
new file mode 100644
index 0000000..d8f639b
--- /dev/null
+++ b/libcutils/tests/test_str_parms.cpp
@@ -0,0 +1,57 @@
+/*
+ * 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 <cutils/str_parms.h>
+#include <gtest/gtest.h>
+
+static void test_str_parms_str(const char* str, const char* expected) {
+ str_parms* str_parms = str_parms_create_str(str);
+ str_parms_add_str(str_parms, "dude", "woah");
+ str_parms_add_str(str_parms, "dude", "woah");
+ str_parms_del(str_parms, "dude");
+ str_parms_dump(str_parms);
+ char* out_str = str_parms_to_str(str_parms);
+ str_parms_destroy(str_parms);
+ ASSERT_STREQ(expected, out_str) << str;
+ free(out_str);
+}
+
+TEST(str_parms, smoke) {
+ test_str_parms_str("", "");
+ test_str_parms_str(";", "");
+ test_str_parms_str("=", "");
+ test_str_parms_str("=;", "");
+ test_str_parms_str("=bar", "");
+ test_str_parms_str("=bar;", "");
+ test_str_parms_str("foo=", "foo=");
+ test_str_parms_str("foo=;", "foo=");
+ test_str_parms_str("foo=bar", "foo=bar");
+ test_str_parms_str("foo=bar;", "foo=bar");
+ test_str_parms_str("foo=bar;baz", "foo=bar;baz=");
+ test_str_parms_str("foo=bar;baz=", "foo=bar;baz=");
+ test_str_parms_str("foo=bar;baz=bat", "foo=bar;baz=bat");
+ test_str_parms_str("foo=bar;baz=bat;", "foo=bar;baz=bat");
+ test_str_parms_str("foo=bar1;baz=bat;foo=bar2", "foo=bar2;baz=bat");
+}
+
+TEST(str_parms, put_ENOMEM) {
+ // hashmapPut reports errors by setting errno to ENOMEM.
+ // Test that we're not confused by running in an environment where this is already true.
+ errno = ENOMEM;
+ test_str_parms_str("foo=bar;baz=", "foo=bar;baz=");
+ ASSERT_EQ(ENOMEM, errno);
+ test_str_parms_str("foo=bar;baz=", "foo=bar;baz=");
+}
diff --git a/libcutils/threads.c b/libcutils/threads.c
new file mode 100644
index 0000000..036f8c5
--- /dev/null
+++ b/libcutils/threads.c
@@ -0,0 +1,109 @@
+/*
+** Copyright (C) 2007, 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 "cutils/threads.h"
+
+// For gettid.
+#if defined(__APPLE__)
+#include "AvailabilityMacros.h" // For MAC_OS_X_VERSION_MAX_ALLOWED
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <unistd.h>
+#elif defined(__linux__) && !defined(__ANDROID__)
+#include <syscall.h>
+#include <unistd.h>
+#elif defined(_WIN32)
+#include <windows.h>
+#endif
+
+// No definition needed for Android because we'll just pick up bionic's copy.
+#ifndef __ANDROID__
+pid_t gettid() {
+#if defined(__APPLE__)
+ return syscall(SYS_thread_selfid);
+#elif defined(__linux__)
+ return syscall(__NR_gettid);
+#elif defined(_WIN32)
+ return GetCurrentThreadId();
+#endif
+}
+#endif // __ANDROID__
+
+#if !defined(_WIN32)
+
+void* thread_store_get( thread_store_t* store )
+{
+ if (!store->has_tls)
+ return NULL;
+
+ return pthread_getspecific( store->tls );
+}
+
+extern void thread_store_set( thread_store_t* store,
+ void* value,
+ thread_store_destruct_t destroy)
+{
+ pthread_mutex_lock( &store->lock );
+ if (!store->has_tls) {
+ if (pthread_key_create( &store->tls, destroy) != 0) {
+ pthread_mutex_unlock(&store->lock);
+ return;
+ }
+ store->has_tls = 1;
+ }
+ pthread_mutex_unlock( &store->lock );
+
+ pthread_setspecific( store->tls, value );
+}
+
+#else /* !defined(_WIN32) */
+void* thread_store_get( thread_store_t* store )
+{
+ if (!store->has_tls)
+ return NULL;
+
+ return (void*) TlsGetValue( store->tls );
+}
+
+void thread_store_set( thread_store_t* store,
+ void* value,
+ thread_store_destruct_t destroy )
+{
+ /* XXX: can't use destructor on thread exit */
+ if (!store->lock_init) {
+ store->lock_init = -1;
+ InitializeCriticalSection( &store->lock );
+ store->lock_init = -2;
+ } else while (store->lock_init != -2) {
+ Sleep(10); /* 10ms */
+ }
+
+ EnterCriticalSection( &store->lock );
+ if (!store->has_tls) {
+ store->tls = TlsAlloc();
+ if (store->tls == TLS_OUT_OF_INDEXES) {
+ LeaveCriticalSection( &store->lock );
+ return;
+ }
+ store->has_tls = 1;
+ }
+ LeaveCriticalSection( &store->lock );
+
+ TlsSetValue( store->tls, value );
+}
+#endif /* !defined(_WIN32) */
diff --git a/libcutils/trace-dev.c b/libcutils/trace-dev.c
new file mode 100644
index 0000000..a06987e
--- /dev/null
+++ b/libcutils/trace-dev.c
@@ -0,0 +1,242 @@
+/*
+ * 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 <limits.h>
+#include <pthread.h>
+#include <stdatomic.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <cutils/compiler.h>
+#include <cutils/properties.h>
+#include <cutils/trace.h>
+
+#define LOG_TAG "cutils-trace"
+#include <log/log.h>
+
+/**
+ * Maximum size of a message that can be logged to the trace buffer.
+ * Note this message includes a tag, the pid, and the string given as the name.
+ * Names should be kept short to get the most use of the trace buffer.
+ */
+#define ATRACE_MESSAGE_LENGTH 1024
+
+atomic_bool atrace_is_ready = ATOMIC_VAR_INIT(false);
+int atrace_marker_fd = -1;
+uint64_t atrace_enabled_tags = ATRACE_TAG_NOT_READY;
+static bool atrace_is_debuggable = false;
+static atomic_bool atrace_is_enabled = ATOMIC_VAR_INIT(true);
+static pthread_once_t atrace_once_control = PTHREAD_ONCE_INIT;
+static pthread_mutex_t atrace_tags_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+// Set whether this process is debuggable, which determines whether
+// application-level tracing is allowed when the ro.debuggable system property
+// is not set to '1'.
+void atrace_set_debuggable(bool debuggable)
+{
+ atrace_is_debuggable = debuggable;
+ atrace_update_tags();
+}
+
+// Set whether tracing is enabled in this process. This is used to prevent
+// the Zygote process from tracing.
+void atrace_set_tracing_enabled(bool enabled)
+{
+ atomic_store_explicit(&atrace_is_enabled, enabled, memory_order_release);
+ atrace_update_tags();
+}
+
+// Check whether the given command line matches one of the comma-separated
+// values listed in the app_cmdlines property.
+static bool atrace_is_cmdline_match(const char* cmdline)
+{
+ char value[PROPERTY_VALUE_MAX];
+ char* start = value;
+
+ property_get("debug.atrace.app_cmdlines", value, "");
+
+ while (start != NULL) {
+ char* end = strchr(start, ',');
+
+ if (end != NULL) {
+ *end = '\0';
+ end++;
+ }
+
+ if (strcmp(cmdline, start) == 0) {
+ return true;
+ }
+
+ start = end;
+ }
+
+ return false;
+}
+
+// Determine whether application-level tracing is enabled for this process.
+static bool atrace_is_app_tracing_enabled()
+{
+ bool sys_debuggable = false;
+ char value[PROPERTY_VALUE_MAX];
+ bool result = false;
+
+ // Check whether the system is debuggable.
+ property_get("ro.debuggable", value, "0");
+ if (value[0] == '1') {
+ sys_debuggable = true;
+ }
+
+ if (sys_debuggable || atrace_is_debuggable) {
+ // Check whether tracing is enabled for this process.
+ FILE * file = fopen("/proc/self/cmdline", "r");
+ if (file) {
+ char cmdline[4096];
+ if (fgets(cmdline, sizeof(cmdline), file)) {
+ result = atrace_is_cmdline_match(cmdline);
+ } else {
+ ALOGE("Error reading cmdline: %s (%d)", strerror(errno), errno);
+ }
+ fclose(file);
+ } else {
+ ALOGE("Error opening /proc/self/cmdline: %s (%d)", strerror(errno),
+ errno);
+ }
+ }
+
+ return result;
+}
+
+// Read the sysprop and return the value tags should be set to
+static uint64_t atrace_get_property()
+{
+ char value[PROPERTY_VALUE_MAX];
+ char *endptr;
+ uint64_t tags;
+
+ property_get("debug.atrace.tags.enableflags", value, "0");
+ errno = 0;
+ tags = strtoull(value, &endptr, 0);
+ if (value[0] == '\0' || *endptr != '\0') {
+ ALOGE("Error parsing trace property: Not a number: %s", value);
+ return 0;
+ } else if (errno == ERANGE || tags == ULLONG_MAX) {
+ ALOGE("Error parsing trace property: Number too large: %s", value);
+ return 0;
+ }
+
+ // Only set the "app" tag if this process was selected for app-level debug
+ // tracing.
+ if (atrace_is_app_tracing_enabled()) {
+ tags |= ATRACE_TAG_APP;
+ } else {
+ tags &= ~ATRACE_TAG_APP;
+ }
+
+ return (tags | ATRACE_TAG_ALWAYS) & ATRACE_TAG_VALID_MASK;
+}
+
+// Update tags if tracing is ready. Useful as a sysprop change callback.
+void atrace_update_tags()
+{
+ uint64_t tags;
+ if (CC_UNLIKELY(atomic_load_explicit(&atrace_is_ready, memory_order_acquire))) {
+ if (atomic_load_explicit(&atrace_is_enabled, memory_order_acquire)) {
+ tags = atrace_get_property();
+ pthread_mutex_lock(&atrace_tags_mutex);
+ atrace_enabled_tags = tags;
+ pthread_mutex_unlock(&atrace_tags_mutex);
+ } else {
+ // Tracing is disabled for this process, so we simply don't
+ // initialize the tags.
+ pthread_mutex_lock(&atrace_tags_mutex);
+ atrace_enabled_tags = ATRACE_TAG_NOT_READY;
+ pthread_mutex_unlock(&atrace_tags_mutex);
+ }
+ }
+}
+
+static void atrace_init_once()
+{
+ atrace_marker_fd = open("/sys/kernel/debug/tracing/trace_marker", O_WRONLY);
+ if (atrace_marker_fd == -1) {
+ ALOGE("Error opening trace file: %s (%d)", strerror(errno), errno);
+ atrace_enabled_tags = 0;
+ goto done;
+ }
+
+ atrace_enabled_tags = atrace_get_property();
+
+done:
+ atomic_store_explicit(&atrace_is_ready, true, memory_order_release);
+}
+
+void atrace_setup()
+{
+ pthread_once(&atrace_once_control, atrace_init_once);
+}
+
+void atrace_begin_body(const char* name)
+{
+ char buf[ATRACE_MESSAGE_LENGTH];
+ size_t len;
+
+ len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "B|%d|%s", getpid(), name);
+ write(atrace_marker_fd, buf, len);
+}
+
+
+void atrace_async_begin_body(const char* name, int32_t cookie)
+{
+ char buf[ATRACE_MESSAGE_LENGTH];
+ size_t len;
+
+ len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "S|%d|%s|%" PRId32,
+ getpid(), name, cookie);
+ write(atrace_marker_fd, buf, len);
+}
+
+void atrace_async_end_body(const char* name, int32_t cookie)
+{
+ char buf[ATRACE_MESSAGE_LENGTH];
+ size_t len;
+
+ len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "F|%d|%s|%" PRId32,
+ getpid(), name, cookie);
+ write(atrace_marker_fd, buf, len);
+}
+
+void atrace_int_body(const char* name, int32_t value)
+{
+ char buf[ATRACE_MESSAGE_LENGTH];
+ size_t len;
+
+ len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "C|%d|%s|%" PRId32,
+ getpid(), name, value);
+ write(atrace_marker_fd, buf, len);
+}
+
+void atrace_int64_body(const char* name, int64_t value)
+{
+ char buf[ATRACE_MESSAGE_LENGTH];
+ size_t len;
+
+ len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "C|%d|%s|%" PRId64,
+ getpid(), name, value);
+ write(atrace_marker_fd, buf, len);
+}
diff --git a/libcutils/trace-host.c b/libcutils/trace-host.c
new file mode 100644
index 0000000..6478e3e
--- /dev/null
+++ b/libcutils/trace-host.c
@@ -0,0 +1,35 @@
+/*
+ * 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 <cutils/trace.h>
+
+#ifndef __unused
+#define __unused __attribute__((__unused__))
+#endif
+
+atomic_bool atrace_is_ready = ATOMIC_VAR_INIT(true);
+int atrace_marker_fd = -1;
+uint64_t atrace_enabled_tags = 0;
+
+void atrace_set_debuggable(bool debuggable __unused) { }
+void atrace_set_tracing_enabled(bool enabled __unused) { }
+void atrace_update_tags() { }
+void atrace_setup() { }
+void atrace_begin_body(const char* name __unused) { }
+void atrace_async_begin_body(const char* name __unused, int32_t cookie __unused) { }
+void atrace_async_end_body(const char* name __unused, int32_t cookie __unused) { }
+void atrace_int_body(const char* name __unused, int32_t value __unused) { }
+void atrace_int64_body(const char* name __unused, int64_t value __unused) { }
diff --git a/libcutils/uevent.c b/libcutils/uevent.c
new file mode 100644
index 0000000..de5d227
--- /dev/null
+++ b/libcutils/uevent.c
@@ -0,0 +1,128 @@
+/*
+ * 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 <cutils/uevent.h>
+
+#include <errno.h>
+#include <stdbool.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <linux/netlink.h>
+
+/**
+ * Like recv(), but checks that messages actually originate from the kernel.
+ */
+ssize_t uevent_kernel_multicast_recv(int socket, void *buffer, size_t length)
+{
+ uid_t uid = -1;
+ return uevent_kernel_multicast_uid_recv(socket, buffer, length, &uid);
+}
+
+/**
+ * Like the above, but passes a uid_t in by pointer. In the event that this
+ * fails due to a bad uid check, the uid_t will be set to the uid of the
+ * socket's peer.
+ *
+ * If this method rejects a netlink message from outside the kernel, it
+ * returns -1, sets errno to EIO, and sets "user" to the UID associated with the
+ * message. If the peer UID cannot be determined, "user" is set to -1."
+ */
+ssize_t uevent_kernel_multicast_uid_recv(int socket, void *buffer, size_t length, uid_t *uid)
+{
+ return uevent_kernel_recv(socket, buffer, length, true, uid);
+}
+
+ssize_t uevent_kernel_recv(int socket, void *buffer, size_t length, bool require_group, uid_t *uid)
+{
+ struct iovec iov = { buffer, length };
+ struct sockaddr_nl addr;
+ char control[CMSG_SPACE(sizeof(struct ucred))];
+ struct msghdr hdr = {
+ &addr,
+ sizeof(addr),
+ &iov,
+ 1,
+ control,
+ sizeof(control),
+ 0,
+ };
+
+ *uid = -1;
+ ssize_t n = recvmsg(socket, &hdr, 0);
+ if (n <= 0) {
+ return n;
+ }
+
+ struct cmsghdr *cmsg = CMSG_FIRSTHDR(&hdr);
+ if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {
+ /* ignoring netlink message with no sender credentials */
+ goto out;
+ }
+
+ struct ucred *cred = (struct ucred *)CMSG_DATA(cmsg);
+ *uid = cred->uid;
+ if (cred->uid != 0) {
+ /* ignoring netlink message from non-root user */
+ goto out;
+ }
+
+ if (addr.nl_pid != 0) {
+ /* ignore non-kernel */
+ goto out;
+ }
+ if (require_group && addr.nl_groups == 0) {
+ /* ignore unicast messages when requested */
+ goto out;
+ }
+
+ return n;
+
+out:
+ /* clear residual potentially malicious data */
+ bzero(buffer, length);
+ errno = EIO;
+ return -1;
+}
+
+int uevent_open_socket(int buf_sz, bool passcred)
+{
+ struct sockaddr_nl addr;
+ int on = passcred;
+ int s;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.nl_family = AF_NETLINK;
+ addr.nl_pid = getpid();
+ addr.nl_groups = 0xffffffff;
+
+ s = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT);
+ if(s < 0)
+ return -1;
+
+ setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &buf_sz, sizeof(buf_sz));
+ setsockopt(s, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
+
+ if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ close(s);
+ return -1;
+ }
+
+ return s;
+}
diff --git a/libdiskconfig/Android.mk b/libdiskconfig/Android.mk
new file mode 100644
index 0000000..624e385
--- /dev/null
+++ b/libdiskconfig/Android.mk
@@ -0,0 +1,25 @@
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+commonSources := \
+ diskconfig.c \
+ diskutils.c \
+ write_lst.c \
+ config_mbr.c
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(commonSources)
+LOCAL_MODULE := libdiskconfig
+LOCAL_MODULE_TAGS := optional
+LOCAL_SYSTEM_SHARED_LIBRARIES := libcutils liblog libc
+LOCAL_CFLAGS := -Werror
+include $(BUILD_SHARED_LIBRARY)
+
+ifeq ($(HOST_OS),linux)
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(commonSources)
+LOCAL_MODULE := libdiskconfig_host
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := -O2 -g -W -Wall -Werror -D_LARGEFILE64_SOURCE
+include $(BUILD_HOST_STATIC_LIBRARY)
+endif # HOST_OS == linux
diff --git a/libdiskconfig/config_mbr.c b/libdiskconfig/config_mbr.c
new file mode 100644
index 0000000..7b6ca1c
--- /dev/null
+++ b/libdiskconfig/config_mbr.c
@@ -0,0 +1,352 @@
+/* libs/diskconfig/diskconfig.c
+ *
+ * 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 "config_mbr"
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <cutils/log.h>
+
+#include <diskconfig/diskconfig.h>
+
+
+/* start and len are in LBA units */
+static void
+cfg_pentry(struct pc_partition *pentry, uint8_t status, uint8_t type,
+ uint32_t start, uint32_t len)
+{
+ if (len > 0) {
+ /* seems that somes BIOSens can get wedged on boot while verifying
+ * the mbr if these are 0 */
+ memset(&pentry->start, 0xff, sizeof(struct chs));
+ memset(&pentry->end, 0xff, sizeof(struct chs));
+ } else {
+ /* zero out the c/h/s entries.. they are not used */
+ memset(&pentry->start, 0, sizeof(struct chs));
+ memset(&pentry->end, 0, sizeof(struct chs));
+ }
+
+ pentry->status = status;
+ pentry->type = type;
+ pentry->start_lba = start;
+ pentry->len_lba = len;
+
+ ALOGI("Configuring pentry. status=0x%x type=0x%x start_lba=%u len_lba=%u",
+ pentry->status, pentry->type, pentry->start_lba, pentry->len_lba);
+}
+
+
+static inline uint32_t
+kb_to_lba(uint32_t len_kb, uint32_t sect_size)
+{
+ uint64_t lba;
+
+ lba = (uint64_t)len_kb * 1024;
+ /* bump it up to the next LBA boundary just in case */
+ lba = (lba + (uint64_t)sect_size - 1) & ~((uint64_t)sect_size - 1);
+ lba /= (uint64_t)sect_size;
+ if (lba >= 0xffffffffULL)
+ ALOGE("Error converting kb -> lba. 32bit overflow, expect weirdness");
+ return (uint32_t)(lba & 0xffffffffULL);
+}
+
+
+static struct write_list *
+mk_pri_pentry(struct disk_info *dinfo, struct part_info *pinfo, int pnum,
+ uint32_t *lba)
+{
+ struct write_list *item;
+ struct pc_partition *pentry;
+
+ if (pnum >= PC_NUM_BOOT_RECORD_PARTS) {
+ ALOGE("Maximum number of primary partition exceeded.");
+ return NULL;
+ }
+
+ if (!(item = alloc_wl(sizeof(struct pc_partition)))) {
+ ALOGE("Unable to allocate memory for partition entry.");
+ return NULL;
+ }
+
+ {
+ /* DO NOT DEREFERENCE */
+ struct pc_boot_record *mbr = (void *)PC_MBR_DISK_OFFSET;
+ /* grab the offset in mbr where to write this partition entry. */
+ item->offset = (loff_t)((uintptr_t)((uint8_t *)(&mbr->ptable[pnum])));
+ }
+
+ pentry = (struct pc_partition *) &item->data;
+
+ /* need a standard primary partition entry */
+ if (pinfo) {
+ /* need this to be 64 bit in case len_kb is large */
+ uint64_t len_lba;
+
+ if (pinfo->len_kb != (uint32_t)-1) {
+ /* bump it up to the next LBA boundary just in case */
+ len_lba = ((uint64_t)pinfo->len_kb * 1024);
+ len_lba += ((uint64_t)dinfo->sect_size - 1);
+ len_lba &= ~((uint64_t)dinfo->sect_size - 1);
+ len_lba /= (uint64_t)dinfo->sect_size;
+ } else {
+ /* make it fill the rest of disk */
+ len_lba = dinfo->num_lba - *lba;
+ }
+
+ cfg_pentry(pentry, ((pinfo->flags & PART_ACTIVE_FLAG) ?
+ PC_PART_ACTIVE : PC_PART_NORMAL),
+ pinfo->type, *lba, (uint32_t)len_lba);
+
+ pinfo->start_lba = *lba;
+ *lba += (uint32_t)len_lba;
+ } else {
+ /* this should be made an extended partition, and should take
+ * up the rest of the disk as a primary partition */
+ cfg_pentry(pentry, PC_PART_NORMAL, PC_PART_TYPE_EXTENDED,
+ *lba, dinfo->num_lba - *lba);
+
+ /* note that we do not update the *lba because we now have to
+ * create a chain of extended partition tables, and first one is at
+ * *lba */
+ }
+
+ return item;
+}
+
+
+/* This function configures an extended boot record at the beginning of an
+ * extended partition. This creates a logical partition and a pointer to
+ * the next EBR.
+ *
+ * ext_lba == The start of the toplevel extended partition (pointed to by the
+ * entry in the MBR).
+ */
+static struct write_list *
+mk_ext_pentry(struct disk_info *dinfo, struct part_info *pinfo, uint32_t *lba,
+ uint32_t ext_lba, struct part_info *pnext)
+{
+ struct write_list *item;
+ struct pc_boot_record *ebr;
+ uint32_t len; /* in lba units */
+
+ if (!(item = alloc_wl(sizeof(struct pc_boot_record)))) {
+ ALOGE("Unable to allocate memory for EBR.");
+ return NULL;
+ }
+
+ /* we are going to write the ebr at the current LBA, and then bump the
+ * lba counter since that is where the logical data partition will start */
+ item->offset = ((loff_t)(*lba)) * dinfo->sect_size;
+ (*lba)++;
+
+ ebr = (struct pc_boot_record *) &item->data;
+ memset(ebr, 0, sizeof(struct pc_boot_record));
+ ebr->mbr_sig = PC_BIOS_BOOT_SIG;
+
+ if (pinfo->len_kb != (uint32_t)-1)
+ len = kb_to_lba(pinfo->len_kb, dinfo->sect_size);
+ else {
+ if (pnext) {
+ ALOGE("Only the last partition can be specified to fill the disk "
+ "(name = '%s')", pinfo->name);
+ goto fail;
+ }
+ len = dinfo->num_lba - *lba;
+ /* update the pinfo structure to reflect the new size, for
+ * bookkeeping */
+ pinfo->len_kb =
+ (uint32_t)(((uint64_t)len * (uint64_t)dinfo->sect_size) /
+ ((uint64_t)1024));
+ }
+
+ cfg_pentry(&ebr->ptable[PC_EBR_LOGICAL_PART], PC_PART_NORMAL,
+ pinfo->type, 1, len);
+
+ pinfo->start_lba = *lba;
+ *lba += len;
+
+ /* If this is not the last partition, we have to create a link to the
+ * next extended partition.
+ *
+ * Otherwise, there's nothing to do since the "pointer entry" is
+ * already zero-filled.
+ */
+ if (pnext) {
+ /* The start lba for next partition is an offset from the beginning
+ * of the top-level extended partition */
+ uint32_t next_start_lba = *lba - ext_lba;
+ uint32_t next_len_lba;
+ if (pnext->len_kb != (uint32_t)-1)
+ next_len_lba = 1 + kb_to_lba(pnext->len_kb, dinfo->sect_size);
+ else
+ next_len_lba = dinfo->num_lba - *lba;
+ cfg_pentry(&ebr->ptable[PC_EBR_NEXT_PTR_PART], PC_PART_NORMAL,
+ PC_PART_TYPE_EXTENDED, next_start_lba, next_len_lba);
+ }
+
+ return item;
+
+fail:
+ free_wl(item);
+ return NULL;
+}
+
+
+static struct write_list *
+mk_mbr_sig()
+{
+ struct write_list *item;
+ if (!(item = alloc_wl(sizeof(uint16_t)))) {
+ ALOGE("Unable to allocate memory for MBR signature.");
+ return NULL;
+ }
+
+ {
+ /* DO NOT DEREFERENCE */
+ struct pc_boot_record *mbr = (void *)PC_MBR_DISK_OFFSET;
+ /* grab the offset in mbr where to write mbr signature. */
+ item->offset = (loff_t)((uintptr_t)((uint8_t *)(&mbr->mbr_sig)));
+ }
+
+ *((uint16_t*)item->data) = PC_BIOS_BOOT_SIG;
+ return item;
+}
+
+struct write_list *
+config_mbr(struct disk_info *dinfo)
+{
+ struct part_info *pinfo;
+ uint32_t cur_lba = dinfo->skip_lba;
+ uint32_t ext_lba = 0;
+ struct write_list *wr_list = NULL;
+ struct write_list *temp_wr = NULL;
+ int cnt = 0;
+ int extended = 0;
+
+ if (!dinfo->part_lst)
+ return NULL;
+
+ for (cnt = 0; cnt < dinfo->num_parts; ++cnt) {
+ pinfo = &dinfo->part_lst[cnt];
+
+ /* Should we create an extedned partition? */
+ if (cnt == (PC_NUM_BOOT_RECORD_PARTS - 1)) {
+ if (cnt + 1 < dinfo->num_parts) {
+ extended = 1;
+ ext_lba = cur_lba;
+ if ((temp_wr = mk_pri_pentry(dinfo, NULL, cnt, &cur_lba)))
+ wlist_add(&wr_list, temp_wr);
+ else {
+ ALOGE("Cannot create primary extended partition.");
+ goto fail;
+ }
+ }
+ }
+
+ /* if extended, need 1 lba for ebr */
+ if ((cur_lba + extended) >= dinfo->num_lba)
+ goto nospace;
+ else if (pinfo->len_kb != (uint32_t)-1) {
+ uint32_t sz_lba = (pinfo->len_kb / dinfo->sect_size) * 1024;
+ if ((cur_lba + sz_lba + extended) > dinfo->num_lba)
+ goto nospace;
+ }
+
+ if (!extended)
+ temp_wr = mk_pri_pentry(dinfo, pinfo, cnt, &cur_lba);
+ else {
+ struct part_info *pnext;
+ pnext = cnt + 1 < dinfo->num_parts ? &dinfo->part_lst[cnt+1] : NULL;
+ temp_wr = mk_ext_pentry(dinfo, pinfo, &cur_lba, ext_lba, pnext);
+ }
+
+ if (temp_wr)
+ wlist_add(&wr_list, temp_wr);
+ else {
+ ALOGE("Cannot create partition %d (%s).", cnt, pinfo->name);
+ goto fail;
+ }
+ }
+
+ /* fill in the rest of the MBR with empty parts (if needed). */
+ for (; cnt < PC_NUM_BOOT_RECORD_PARTS; ++cnt) {
+ struct part_info blank;
+ cur_lba = 0;
+ memset(&blank, 0, sizeof(struct part_info));
+ if (!(temp_wr = mk_pri_pentry(dinfo, &blank, cnt, &cur_lba))) {
+ ALOGE("Cannot create blank partition %d.", cnt);
+ goto fail;
+ }
+ wlist_add(&wr_list, temp_wr);
+ }
+
+ if ((temp_wr = mk_mbr_sig()))
+ wlist_add(&wr_list, temp_wr);
+ else {
+ ALOGE("Cannot set MBR signature");
+ goto fail;
+ }
+
+ return wr_list;
+
+nospace:
+ ALOGE("Not enough space to add parttion '%s'.", pinfo->name);
+
+fail:
+ wlist_free(wr_list);
+ return NULL;
+}
+
+
+/* Returns the device path of the partition referred to by 'name'
+ * Must be freed by the caller.
+ */
+char *
+find_mbr_part(struct disk_info *dinfo, const char *name)
+{
+ struct part_info *plist = dinfo->part_lst;
+ int num = 0;
+ char *dev_name = NULL;
+ int has_extended = (dinfo->num_parts > PC_NUM_BOOT_RECORD_PARTS);
+
+ for(num = 1; num <= dinfo->num_parts; ++num) {
+ if (!strcmp(plist[num-1].name, name))
+ break;
+ }
+
+ if (num > dinfo->num_parts)
+ return NULL;
+
+ if (has_extended && (num >= PC_NUM_BOOT_RECORD_PARTS))
+ num++;
+
+ if (!(dev_name = malloc(MAX_NAME_LEN))) {
+ ALOGE("Cannot allocate memory.");
+ return NULL;
+ }
+
+ num = snprintf(dev_name, MAX_NAME_LEN, "%s%d", dinfo->device, num);
+ if (num >= MAX_NAME_LEN) {
+ ALOGE("Device name is too long?!");
+ free(dev_name);
+ return NULL;
+ }
+
+ return dev_name;
+}
diff --git a/libdiskconfig/diskconfig.c b/libdiskconfig/diskconfig.c
new file mode 100644
index 0000000..1167d4b
--- /dev/null
+++ b/libdiskconfig/diskconfig.c
@@ -0,0 +1,537 @@
+/* libs/diskconfig/diskconfig.c
+ *
+ * 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 "diskconfig"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+
+#include <linux/fs.h>
+
+#include <cutils/config_utils.h>
+#include <log/log.h>
+
+#include <diskconfig/diskconfig.h>
+
+
+static int
+parse_len(const char *str, uint64_t *plen)
+{
+ char tmp[64];
+ int len_str;
+ uint32_t multiple = 1;
+
+ strncpy(tmp, str, sizeof(tmp));
+ tmp[sizeof(tmp)-1] = '\0';
+ len_str = strlen(tmp);
+ if (!len_str) {
+ ALOGE("Invalid disk length specified.");
+ return 1;
+ }
+
+ switch(tmp[len_str - 1]) {
+ case 'M': case 'm':
+ /* megabyte */
+ multiple <<= 10;
+ case 'K': case 'k':
+ /* kilobytes */
+ multiple <<= 10;
+ tmp[len_str - 1] = '\0';
+ break;
+ default:
+ break;
+ }
+
+ *plen = strtoull(tmp, NULL, 0);
+ if (!*plen) {
+ ALOGE("Invalid length specified: %s", str);
+ return 1;
+ }
+
+ if (*plen == (uint64_t)-1) {
+ if (multiple > 1) {
+ ALOGE("Size modifier illegal when len is -1");
+ return 1;
+ }
+ } else {
+ /* convert len to kilobytes */
+ if (multiple > 1024)
+ multiple >>= 10;
+ *plen *= multiple;
+
+ if (*plen > 0xffffffffULL) {
+ ALOGE("Length specified is too large!: %"PRIu64" KB", *plen);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+static int
+load_partitions(cnode *root, struct disk_info *dinfo)
+{
+ cnode *partnode;
+
+ dinfo->num_parts = 0;
+ for (partnode = root->first_child; partnode; partnode = partnode->next) {
+ struct part_info *pinfo = &dinfo->part_lst[dinfo->num_parts];
+ const char *tmp;
+
+ /* bleh, i will leak memory here, but i DONT CARE since
+ * the only right thing to do when this function fails
+ * is to quit */
+ pinfo->name = strdup(partnode->name);
+
+ if(config_bool(partnode, "active", 0))
+ pinfo->flags |= PART_ACTIVE_FLAG;
+
+ if (!(tmp = config_str(partnode, "type", NULL))) {
+ ALOGE("Partition type required: %s", pinfo->name);
+ return 1;
+ }
+
+ /* possible values are: linux, fat32 */
+ if (!strcmp(tmp, "linux")) {
+ pinfo->type = PC_PART_TYPE_LINUX;
+ } else if (!strcmp(tmp, "fat32")) {
+ pinfo->type = PC_PART_TYPE_FAT32;
+ } else {
+ ALOGE("Unsupported partition type found: %s", tmp);
+ return 1;
+ }
+
+ if ((tmp = config_str(partnode, "len", NULL)) != NULL) {
+ uint64_t len;
+ if (parse_len(tmp, &len))
+ return 1;
+ pinfo->len_kb = (uint32_t) len;
+ } else
+ pinfo->len_kb = 0;
+
+ ++dinfo->num_parts;
+ }
+
+ return 0;
+}
+
+struct disk_info *
+load_diskconfig(const char *fn, char *path_override)
+{
+ struct disk_info *dinfo;
+ cnode *devroot;
+ cnode *partnode;
+ cnode *root = config_node("", "");
+ const char *tmp;
+
+ if (!(dinfo = malloc(sizeof(struct disk_info)))) {
+ ALOGE("Could not malloc disk_info");
+ return NULL;
+ }
+ memset(dinfo, 0, sizeof(struct disk_info));
+
+ if (!(dinfo->part_lst = malloc(MAX_NUM_PARTS * sizeof(struct part_info)))) {
+ ALOGE("Could not malloc part_lst");
+ goto fail;
+ }
+ memset(dinfo->part_lst, 0,
+ (MAX_NUM_PARTS * sizeof(struct part_info)));
+
+ config_load_file(root, fn);
+ if (root->first_child == NULL) {
+ ALOGE("Could not read config file %s", fn);
+ goto fail;
+ }
+
+ if (!(devroot = config_find(root, "device"))) {
+ ALOGE("Could not find device section in config file '%s'", fn);
+ goto fail;
+ }
+
+
+ if (!(tmp = config_str(devroot, "path", path_override))) {
+ ALOGE("device path is requried");
+ goto fail;
+ }
+ dinfo->device = strdup(tmp);
+
+ /* find the partition scheme */
+ if (!(tmp = config_str(devroot, "scheme", NULL))) {
+ ALOGE("partition scheme is required");
+ goto fail;
+ } else if (!strcmp(tmp, "mbr")) {
+ dinfo->scheme = PART_SCHEME_MBR;
+ } else if (!strcmp(tmp, "gpt")) {
+ ALOGE("'gpt' partition scheme not supported yet.");
+ goto fail;
+ } else {
+ ALOGE("Unknown partition scheme specified: %s", tmp);
+ goto fail;
+ }
+
+ /* grab the sector size (in bytes) */
+ tmp = config_str(devroot, "sector_size", "512");
+ dinfo->sect_size = strtol(tmp, NULL, 0);
+ if (!dinfo->sect_size) {
+ ALOGE("Invalid sector size: %s", tmp);
+ goto fail;
+ }
+
+ /* first lba where the partitions will start on disk */
+ if (!(tmp = config_str(devroot, "start_lba", NULL))) {
+ ALOGE("start_lba must be provided");
+ goto fail;
+ }
+
+ if (!(dinfo->skip_lba = strtol(tmp, NULL, 0))) {
+ ALOGE("Invalid starting LBA (or zero): %s", tmp);
+ goto fail;
+ }
+
+ /* Number of LBAs on disk */
+ if (!(tmp = config_str(devroot, "num_lba", NULL))) {
+ ALOGE("num_lba is required");
+ goto fail;
+ }
+ dinfo->num_lba = strtoul(tmp, NULL, 0);
+
+ if (!(partnode = config_find(devroot, "partitions"))) {
+ ALOGE("Device must specify partition list");
+ goto fail;
+ }
+
+ if (load_partitions(partnode, dinfo))
+ goto fail;
+
+ return dinfo;
+
+fail:
+ if (dinfo->part_lst)
+ free(dinfo->part_lst);
+ if (dinfo->device)
+ free(dinfo->device);
+ free(dinfo);
+ return NULL;
+}
+
+static int
+sync_ptable(int fd)
+{
+ struct stat stat;
+ int rv;
+
+ sync();
+
+ if (fstat(fd, &stat)) {
+ ALOGE("Cannot stat, errno=%d.", errno);
+ return -1;
+ }
+
+ if (S_ISBLK(stat.st_mode) && ((rv = ioctl(fd, BLKRRPART, NULL)) < 0)) {
+ ALOGE("Could not re-read partition table. REBOOT!. (errno=%d)", errno);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* This function verifies that the disk info provided is valid, and if so,
+ * returns an open file descriptor.
+ *
+ * This does not necessarily mean that it will later be successfully written
+ * though. If we use the pc-bios partitioning scheme, we must use extended
+ * partitions, which eat up some hd space. If the user manually provisioned
+ * every single partition, but did not account for the extra needed space,
+ * then we will later fail.
+ *
+ * TODO: Make validation more complete.
+ */
+static int
+validate(struct disk_info *dinfo)
+{
+ int fd;
+ int sect_sz;
+ uint64_t disk_size;
+ uint64_t total_size;
+ int cnt;
+ struct stat stat;
+
+ if (!dinfo)
+ return -1;
+
+ if ((fd = open(dinfo->device, O_RDWR)) < 0) {
+ ALOGE("Cannot open device '%s' (errno=%d)", dinfo->device, errno);
+ return -1;
+ }
+
+ if (fstat(fd, &stat)) {
+ ALOGE("Cannot stat file '%s', errno=%d.", dinfo->device, errno);
+ goto fail;
+ }
+
+
+ /* XXX: Some of the code below is kind of redundant and should probably
+ * be refactored a little, but it will do for now. */
+
+ /* Verify that we can operate on the device that was requested.
+ * We presently only support block devices and regular file images. */
+ if (S_ISBLK(stat.st_mode)) {
+ /* get the sector size and make sure we agree */
+ if (ioctl(fd, BLKSSZGET, §_sz) < 0) {
+ ALOGE("Cannot get sector size (errno=%d)", errno);
+ goto fail;
+ }
+
+ if (!sect_sz || sect_sz != dinfo->sect_size) {
+ ALOGE("Device sector size is zero or sector sizes do not match!");
+ goto fail;
+ }
+
+ /* allow the user override the "disk size" if they provided num_lba */
+ if (!dinfo->num_lba) {
+ if (ioctl(fd, BLKGETSIZE64, &disk_size) < 0) {
+ ALOGE("Could not get block device size (errno=%d)", errno);
+ goto fail;
+ }
+ /* XXX: we assume that the disk has < 2^32 sectors :-) */
+ dinfo->num_lba = (uint32_t)(disk_size / (uint64_t)dinfo->sect_size);
+ } else
+ disk_size = (uint64_t)dinfo->num_lba * (uint64_t)dinfo->sect_size;
+ } else if (S_ISREG(stat.st_mode)) {
+ ALOGI("Requesting operation on a regular file, not block device.");
+ if (!dinfo->sect_size) {
+ ALOGE("Sector size for regular file images cannot be zero");
+ goto fail;
+ }
+ if (dinfo->num_lba)
+ disk_size = (uint64_t)dinfo->num_lba * (uint64_t)dinfo->sect_size;
+ else {
+ dinfo->num_lba = (uint32_t)(stat.st_size / dinfo->sect_size);
+ disk_size = (uint64_t)stat.st_size;
+ }
+ } else {
+ ALOGE("Device does not refer to a regular file or a block device!");
+ goto fail;
+ }
+
+#if 1
+ ALOGV("Device/file %s: size=%" PRIu64 " bytes, num_lba=%u, sect_size=%d",
+ dinfo->device, disk_size, dinfo->num_lba, dinfo->sect_size);
+#endif
+
+ /* since this is our offset into the disk, we start off with that as
+ * our size of needed partitions */
+ total_size = dinfo->skip_lba * dinfo->sect_size;
+
+ /* add up all the partition sizes and make sure it fits */
+ for (cnt = 0; cnt < dinfo->num_parts; ++cnt) {
+ struct part_info *part = &dinfo->part_lst[cnt];
+ if (part->len_kb != (uint32_t)-1) {
+ total_size += part->len_kb * 1024;
+ } else if (part->len_kb == 0) {
+ ALOGE("Zero-size partition '%s' is invalid.", part->name);
+ goto fail;
+ } else {
+ /* the partition requests the rest of the disk. */
+ if (cnt + 1 != dinfo->num_parts) {
+ ALOGE("Only the last partition in the list can request to fill "
+ "the rest of disk.");
+ goto fail;
+ }
+ }
+
+ if ((part->type != PC_PART_TYPE_LINUX) &&
+ (part->type != PC_PART_TYPE_FAT32)) {
+ ALOGE("Unknown partition type (0x%x) encountered for partition "
+ "'%s'\n", part->type, part->name);
+ goto fail;
+ }
+ }
+
+ /* only matters for disks, not files */
+ if (S_ISBLK(stat.st_mode) && total_size > disk_size) {
+ ALOGE("Total requested size of partitions (%"PRIu64") is greater than disk "
+ "size (%"PRIu64").", total_size, disk_size);
+ goto fail;
+ }
+
+ return fd;
+
+fail:
+ close(fd);
+ return -1;
+}
+
+static int
+validate_and_config(struct disk_info *dinfo, int *fd, struct write_list **lst)
+{
+ *lst = NULL;
+ *fd = -1;
+
+ if ((*fd = validate(dinfo)) < 0)
+ return 1;
+
+ switch (dinfo->scheme) {
+ case PART_SCHEME_MBR:
+ *lst = config_mbr(dinfo);
+ return *lst == NULL;
+ case PART_SCHEME_GPT:
+ /* not supported yet */
+ default:
+ ALOGE("Uknown partition scheme.");
+ break;
+ }
+
+ close(*fd);
+ *lst = NULL;
+ return 1;
+}
+
+/* validate and process the disk layout configuration.
+ * This will cause an update to the partitions' start lba.
+ *
+ * Basically, this does the same thing as apply_disk_config in test mode,
+ * except that wlist_commit is not called to print out the data to be
+ * written.
+ */
+int
+process_disk_config(struct disk_info *dinfo)
+{
+ struct write_list *lst;
+ int fd;
+
+ if (validate_and_config(dinfo, &fd, &lst) != 0)
+ return 1;
+
+ close(fd);
+ wlist_free(lst);
+ return 0;
+}
+
+
+int
+apply_disk_config(struct disk_info *dinfo, int test)
+{
+ int fd;
+ struct write_list *wr_lst = NULL;
+ int rv;
+
+ if (validate_and_config(dinfo, &fd, &wr_lst) != 0) {
+ ALOGE("Configuration is invalid.");
+ goto fail;
+ }
+
+ if ((rv = wlist_commit(fd, wr_lst, test)) >= 0)
+ rv = test ? 0 : sync_ptable(fd);
+
+ close(fd);
+ wlist_free(wr_lst);
+ return rv;
+
+fail:
+ close(fd);
+ if (wr_lst)
+ wlist_free(wr_lst);
+ return 1;
+}
+
+int
+dump_disk_config(struct disk_info *dinfo)
+{
+ int cnt;
+ struct part_info *part;
+
+ printf("Device: %s\n", dinfo->device);
+ printf("Scheme: ");
+ switch (dinfo->scheme) {
+ case PART_SCHEME_MBR:
+ printf("MBR");
+ break;
+ case PART_SCHEME_GPT:
+ printf("GPT (unsupported)");
+ break;
+ default:
+ printf("Unknown");
+ break;
+ }
+ printf ("\n");
+
+ printf("Sector size: %d\n", dinfo->sect_size);
+ printf("Skip leading LBAs: %u\n", dinfo->skip_lba);
+ printf("Number of LBAs: %u\n", dinfo->num_lba);
+ printf("Partitions:\n");
+
+ for (cnt = 0; cnt < dinfo->num_parts; ++cnt) {
+ part = &dinfo->part_lst[cnt];
+ printf("\tname = %s\n", part->name);
+ printf("\t\tflags = %s\n",
+ part->flags & PART_ACTIVE_FLAG ? "Active" : "None");
+ printf("\t\ttype = %s\n",
+ part->type == PC_PART_TYPE_LINUX ? "Linux" : "Unknown");
+ if (part->len_kb == (uint32_t)-1)
+ printf("\t\tlen = rest of disk\n");
+ else
+ printf("\t\tlen = %uKB\n", part->len_kb);
+ }
+ printf("Total number of partitions: %d\n", cnt);
+ printf("\n");
+
+ return 0;
+}
+
+struct part_info *
+find_part(struct disk_info *dinfo, const char *name)
+{
+ struct part_info *pinfo;
+ int cnt;
+
+ for (cnt = 0; cnt < dinfo->num_parts; ++cnt) {
+ pinfo = &dinfo->part_lst[cnt];
+ if (!strcmp(pinfo->name, name))
+ return pinfo;
+ }
+
+ return NULL;
+}
+
+/* NOTE: If the returned ptr is non-NULL, it must be freed by the caller. */
+char *
+find_part_device(struct disk_info *dinfo, const char *name)
+{
+ switch (dinfo->scheme) {
+ case PART_SCHEME_MBR:
+ return find_mbr_part(dinfo, name);
+ case PART_SCHEME_GPT:
+ ALOGE("GPT is presently not supported");
+ break;
+ default:
+ ALOGE("Unknown partition table scheme");
+ break;
+ }
+
+ return NULL;
+}
+
+
diff --git a/libdiskconfig/diskutils.c b/libdiskconfig/diskutils.c
new file mode 100644
index 0000000..5d0ee62
--- /dev/null
+++ b/libdiskconfig/diskutils.c
@@ -0,0 +1,118 @@
+/* libs/diskconfig/diskutils.c
+ *
+ * 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 "diskutils"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include <log/log.h>
+
+#include <diskconfig/diskconfig.h>
+
+int
+write_raw_image(const char *dst, const char *src, loff_t offset, int test)
+{
+ int dst_fd = -1;
+ int src_fd = -1;
+ uint8_t buffer[2048];
+ ssize_t nr_bytes;
+ ssize_t tmp;
+ int done = 0;
+ uint64_t total = 0;
+
+ ALOGI("Writing RAW image '%s' to '%s' (offset=%llu)", src, dst, (unsigned long long)offset);
+ if ((src_fd = open(src, O_RDONLY)) < 0) {
+ ALOGE("Could not open %s for reading (errno=%d).", src, errno);
+ goto fail;
+ }
+
+ if (!test) {
+ if ((dst_fd = open(dst, O_RDWR)) < 0) {
+ ALOGE("Could not open '%s' for read/write (errno=%d).", dst, errno);
+ goto fail;
+ }
+
+ if (lseek64(dst_fd, offset, SEEK_SET) != offset) {
+ ALOGE("Could not seek to offset %lld in %s.", (long long)offset, dst);
+ goto fail;
+ }
+ }
+
+ while (!done) {
+ if ((nr_bytes = read(src_fd, buffer, sizeof(buffer))) < 0) {
+ /* XXX: Should we not even bother with EINTR? */
+ if (errno == EINTR)
+ continue;
+ ALOGE("Error (%d) while reading from '%s'", errno, src);
+ goto fail;
+ }
+
+ if (!nr_bytes) {
+ /* we're done. */
+ done = 1;
+ break;
+ }
+
+ total += nr_bytes;
+
+ /* skip the write loop if we're testing */
+ if (test)
+ nr_bytes = 0;
+
+ while (nr_bytes > 0) {
+ if ((tmp = write(dst_fd, buffer, nr_bytes)) < 0) {
+ /* XXX: Should we not even bother with EINTR? */
+ if (errno == EINTR)
+ continue;
+ ALOGE("Error (%d) while writing to '%s'", errno, dst);
+ goto fail;
+ }
+ if (!tmp)
+ continue;
+ nr_bytes -= tmp;
+ }
+ }
+
+ if (!done) {
+ ALOGE("Exited read/write loop without setting flag! WTF?!");
+ goto fail;
+ }
+
+ if (dst_fd >= 0)
+ fsync(dst_fd);
+
+ ALOGI("Wrote %" PRIu64 " bytes to %s @ %lld", total, dst, (long long)offset);
+
+ close(src_fd);
+ if (dst_fd >= 0)
+ close(dst_fd);
+ return 0;
+
+fail:
+ if (dst_fd >= 0)
+ close(dst_fd);
+ if (src_fd >= 0)
+ close(src_fd);
+ return 1;
+}
diff --git a/libdiskconfig/dump_diskconfig.c b/libdiskconfig/dump_diskconfig.c
new file mode 100644
index 0000000..75256f6
--- /dev/null
+++ b/libdiskconfig/dump_diskconfig.c
@@ -0,0 +1,42 @@
+/* libs/diskconfig/dump_diskconfig.c
+ *
+ * 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 "dump_diskconfig"
+#include <stdio.h>
+
+#include <cutils/log.h>
+
+#include "diskconfig.h"
+
+int
+main(int argc, char *argv[])
+{
+ struct disk_info *dinfo;
+
+ if (argc < 2) {
+ ALOGE("usage: %s <conf file>", argv[0]);
+ return 1;
+ }
+
+ if (!(dinfo = load_diskconfig(argv[1], NULL)))
+ return 1;
+
+ dump_disk_config(dinfo);
+
+ return 0;
+}
+
diff --git a/libdiskconfig/write_lst.c b/libdiskconfig/write_lst.c
new file mode 100644
index 0000000..90b1c82
--- /dev/null
+++ b/libdiskconfig/write_lst.c
@@ -0,0 +1,92 @@
+/* libs/diskconfig/write_lst.c
+ *
+ * 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 "write_lst"
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <cutils/log.h>
+
+#include <diskconfig/diskconfig.h>
+
+struct write_list *
+alloc_wl(uint32_t data_len)
+{
+ struct write_list *item;
+
+ if (!(item = malloc(sizeof(struct write_list) + data_len))) {
+ ALOGE("Unable to allocate memory.");
+ return NULL;
+ }
+
+ item->len = data_len;
+ return item;
+}
+
+void
+free_wl(struct write_list *item)
+{
+ if (item)
+ free(item);
+}
+
+struct write_list *
+wlist_add(struct write_list **lst, struct write_list *item)
+{
+ item->next = (*lst);
+ *lst = item;
+ return item;
+}
+
+void
+wlist_free(struct write_list *lst)
+{
+ struct write_list *temp_wr;
+ while (lst) {
+ temp_wr = lst->next;
+ free_wl(lst);
+ lst = temp_wr;
+ }
+}
+
+int
+wlist_commit(int fd, struct write_list *lst, int test)
+{
+ for(; lst; lst = lst->next) {
+ if (lseek64(fd, lst->offset, SEEK_SET) != (loff_t)lst->offset) {
+ ALOGE("Cannot seek to the specified position (%lld).", (long long)lst->offset);
+ goto fail;
+ }
+
+ if (!test) {
+ if (write(fd, lst->data, lst->len) != (int)lst->len) {
+ ALOGE("Failed writing %u bytes at position %lld.", lst->len,
+ (long long)lst->offset);
+ goto fail;
+ }
+ } else
+ ALOGI("Would write %d bytes @ offset %lld.", lst->len, (long long)lst->offset);
+ }
+
+ return 0;
+
+fail:
+ return -1;
+}
diff --git a/libion/Android.mk b/libion/Android.mk
new file mode 100644
index 0000000..6562cd3
--- /dev/null
+++ b/libion/Android.mk
@@ -0,0 +1,22 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := ion.c
+LOCAL_MODULE := libion
+LOCAL_MODULE_TAGS := optional
+LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/kernel-headers
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include $(LOCAL_PATH)/kernel-headers
+LOCAL_CFLAGS := -Werror
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := ion.c ion_test.c
+LOCAL_MODULE := iontest
+LOCAL_MODULE_TAGS := optional tests
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/kernel-headers
+LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_CFLAGS := -Werror
+include $(BUILD_EXECUTABLE)
+
+include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/libion/include/ion/ion.h b/libion/include/ion/ion.h
new file mode 100644
index 0000000..f47793d
--- /dev/null
+++ b/libion/include/ion/ion.h
@@ -0,0 +1,46 @@
+/*
+ * ion.c
+ *
+ * Memory Allocator functions for ion
+ *
+ * Copyright 2011 Google, Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __SYS_CORE_ION_H
+#define __SYS_CORE_ION_H
+
+#include <sys/types.h>
+#include <linux/ion.h>
+
+__BEGIN_DECLS
+
+struct ion_handle;
+
+int ion_open();
+int ion_close(int fd);
+int ion_alloc(int fd, size_t len, size_t align, unsigned int heap_mask,
+ unsigned int flags, ion_user_handle_t *handle);
+int ion_alloc_fd(int fd, size_t len, size_t align, unsigned int heap_mask,
+ unsigned int flags, int *handle_fd);
+int ion_sync_fd(int fd, int handle_fd);
+int ion_free(int fd, ion_user_handle_t handle);
+int ion_map(int fd, ion_user_handle_t handle, size_t length, int prot,
+ int flags, off_t offset, unsigned char **ptr, int *map_fd);
+int ion_share(int fd, ion_user_handle_t handle, int *share_fd);
+int ion_import(int fd, int share_fd, ion_user_handle_t *handle);
+
+__END_DECLS
+
+#endif /* __SYS_CORE_ION_H */
diff --git a/libion/ion.c b/libion/ion.c
new file mode 100644
index 0000000..4908932
--- /dev/null
+++ b/libion/ion.c
@@ -0,0 +1,176 @@
+/*
+ * ion.c
+ *
+ * Memory Allocator functions for ion
+ *
+ * Copyright 2011 Google, Inc
+ *
+ * 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 "ion"
+
+#include <cutils/log.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+
+#include <linux/ion.h>
+#include <ion/ion.h>
+
+int ion_open()
+{
+ int fd = open("/dev/ion", O_RDWR);
+ if (fd < 0)
+ ALOGE("open /dev/ion failed!\n");
+ return fd;
+}
+
+int ion_close(int fd)
+{
+ int ret = close(fd);
+ if (ret < 0)
+ return -errno;
+ return ret;
+}
+
+static int ion_ioctl(int fd, int req, void *arg)
+{
+ int ret = ioctl(fd, req, arg);
+ if (ret < 0) {
+ ALOGE("ioctl %x failed with code %d: %s\n", req,
+ ret, strerror(errno));
+ return -errno;
+ }
+ return ret;
+}
+
+int ion_alloc(int fd, size_t len, size_t align, unsigned int heap_mask,
+ unsigned int flags, ion_user_handle_t *handle)
+{
+ int ret;
+ struct ion_allocation_data data = {
+ .len = len,
+ .align = align,
+ .heap_id_mask = heap_mask,
+ .flags = flags,
+ };
+
+ if (handle == NULL)
+ return -EINVAL;
+
+ ret = ion_ioctl(fd, ION_IOC_ALLOC, &data);
+ if (ret < 0)
+ return ret;
+ *handle = data.handle;
+ return ret;
+}
+
+int ion_free(int fd, ion_user_handle_t handle)
+{
+ struct ion_handle_data data = {
+ .handle = handle,
+ };
+ return ion_ioctl(fd, ION_IOC_FREE, &data);
+}
+
+int ion_map(int fd, ion_user_handle_t handle, size_t length, int prot,
+ int flags, off_t offset, unsigned char **ptr, int *map_fd)
+{
+ int ret;
+ struct ion_fd_data data = {
+ .handle = handle,
+ };
+
+ if (map_fd == NULL)
+ return -EINVAL;
+ if (ptr == NULL)
+ return -EINVAL;
+
+ ret = ion_ioctl(fd, ION_IOC_MAP, &data);
+ if (ret < 0)
+ return ret;
+ *map_fd = data.fd;
+ if (*map_fd < 0) {
+ ALOGE("map ioctl returned negative fd\n");
+ return -EINVAL;
+ }
+ *ptr = mmap(NULL, length, prot, flags, *map_fd, offset);
+ if (*ptr == MAP_FAILED) {
+ ALOGE("mmap failed: %s\n", strerror(errno));
+ return -errno;
+ }
+ return ret;
+}
+
+int ion_share(int fd, ion_user_handle_t handle, int *share_fd)
+{
+ int ret;
+ struct ion_fd_data data = {
+ .handle = handle,
+ };
+
+ if (share_fd == NULL)
+ return -EINVAL;
+
+ ret = ion_ioctl(fd, ION_IOC_SHARE, &data);
+ if (ret < 0)
+ return ret;
+ *share_fd = data.fd;
+ if (*share_fd < 0) {
+ ALOGE("share ioctl returned negative fd\n");
+ return -EINVAL;
+ }
+ return ret;
+}
+
+int ion_alloc_fd(int fd, size_t len, size_t align, unsigned int heap_mask,
+ unsigned int flags, int *handle_fd) {
+ ion_user_handle_t handle;
+ int ret;
+
+ ret = ion_alloc(fd, len, align, heap_mask, flags, &handle);
+ if (ret < 0)
+ return ret;
+ ret = ion_share(fd, handle, handle_fd);
+ ion_free(fd, handle);
+ return ret;
+}
+
+int ion_import(int fd, int share_fd, ion_user_handle_t *handle)
+{
+ int ret;
+ struct ion_fd_data data = {
+ .fd = share_fd,
+ };
+
+ if (handle == NULL)
+ return -EINVAL;
+
+ ret = ion_ioctl(fd, ION_IOC_IMPORT, &data);
+ if (ret < 0)
+ return ret;
+ *handle = data.handle;
+ return ret;
+}
+
+int ion_sync_fd(int fd, int handle_fd)
+{
+ struct ion_fd_data data = {
+ .fd = handle_fd,
+ };
+ return ion_ioctl(fd, ION_IOC_SYNC, &data);
+}
diff --git a/libion/ion_test.c b/libion/ion_test.c
new file mode 100644
index 0000000..b7d5583
--- /dev/null
+++ b/libion/ion_test.c
@@ -0,0 +1,289 @@
+/*
+ * Copyright 2013 Google, Inc
+ *
+ * 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 <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <ion/ion.h>
+#include <linux/ion.h>
+
+size_t len = 1024*1024, align = 0;
+int prot = PROT_READ | PROT_WRITE;
+int map_flags = MAP_SHARED;
+int alloc_flags = 0;
+int heap_mask = 1;
+int test = -1;
+size_t stride;
+
+int _ion_alloc_test(int *fd, ion_user_handle_t *handle)
+{
+ int ret;
+
+ *fd = ion_open();
+ if (*fd < 0)
+ return *fd;
+
+ ret = ion_alloc(*fd, len, align, heap_mask, alloc_flags, handle);
+
+ if (ret)
+ printf("%s failed: %s\n", __func__, strerror(ret));
+ return ret;
+}
+
+void ion_alloc_test()
+{
+ int fd, ret;
+ ion_user_handle_t handle;
+
+ if(_ion_alloc_test(&fd, &handle))
+ return;
+
+ ret = ion_free(fd, handle);
+ if (ret) {
+ printf("%s failed: %s %d\n", __func__, strerror(ret), handle);
+ return;
+ }
+ ion_close(fd);
+ printf("ion alloc test: passed\n");
+}
+
+void ion_map_test()
+{
+ int fd, map_fd, ret;
+ size_t i;
+ ion_user_handle_t handle;
+ unsigned char *ptr;
+
+ if(_ion_alloc_test(&fd, &handle))
+ return;
+
+ ret = ion_map(fd, handle, len, prot, map_flags, 0, &ptr, &map_fd);
+ if (ret)
+ return;
+
+ for (i = 0; i < len; i++) {
+ ptr[i] = (unsigned char)i;
+ }
+ for (i = 0; i < len; i++)
+ if (ptr[i] != (unsigned char)i)
+ printf("%s failed wrote %zu read %d from mapped "
+ "memory\n", __func__, i, ptr[i]);
+ /* clean up properly */
+ ret = ion_free(fd, handle);
+ ion_close(fd);
+ munmap(ptr, len);
+ close(map_fd);
+
+ _ion_alloc_test(&fd, &handle);
+ close(fd);
+
+#if 0
+ munmap(ptr, len);
+ close(map_fd);
+ ion_close(fd);
+
+ _ion_alloc_test(len, align, flags, &fd, &handle);
+ close(map_fd);
+ ret = ion_map(fd, handle, len, prot, flags, 0, &ptr, &map_fd);
+ /* don't clean up */
+#endif
+}
+
+void ion_share_test()
+
+{
+ ion_user_handle_t handle;
+ int sd[2];
+ int num_fd = 1;
+ struct iovec count_vec = {
+ .iov_base = &num_fd,
+ .iov_len = sizeof num_fd,
+ };
+ char buf[CMSG_SPACE(sizeof(int))];
+ socketpair(AF_UNIX, SOCK_STREAM, 0, sd);
+ if (fork()) {
+ struct msghdr msg = {
+ .msg_control = buf,
+ .msg_controllen = sizeof buf,
+ .msg_iov = &count_vec,
+ .msg_iovlen = 1,
+ };
+
+ struct cmsghdr *cmsg;
+ int fd, share_fd, ret;
+ char *ptr;
+ /* parent */
+ if(_ion_alloc_test(&fd, &handle))
+ return;
+ ret = ion_share(fd, handle, &share_fd);
+ if (ret)
+ printf("share failed %s\n", strerror(errno));
+ ptr = mmap(NULL, len, prot, map_flags, share_fd, 0);
+ if (ptr == MAP_FAILED) {
+ return;
+ }
+ strcpy(ptr, "master");
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ *(int *)CMSG_DATA(cmsg) = share_fd;
+ /* send the fd */
+ printf("master? [%10s] should be [master]\n", ptr);
+ printf("master sending msg 1\n");
+ sendmsg(sd[0], &msg, 0);
+ if (recvmsg(sd[0], &msg, 0) < 0)
+ perror("master recv msg 2");
+ printf("master? [%10s] should be [child]\n", ptr);
+
+ /* send ping */
+ sendmsg(sd[0], &msg, 0);
+ printf("master->master? [%10s]\n", ptr);
+ if (recvmsg(sd[0], &msg, 0) < 0)
+ perror("master recv 1");
+ close(fd);
+ _exit(0);
+ } else {
+ struct cmsghdr *cmsg;
+ char* ptr;
+ int fd, recv_fd;
+ char* child_buf[100];
+ /* child */
+ struct iovec count_vec = {
+ .iov_base = child_buf,
+ .iov_len = sizeof child_buf,
+ };
+
+ struct msghdr child_msg = {
+ .msg_control = buf,
+ .msg_controllen = sizeof buf,
+ .msg_iov = &count_vec,
+ .msg_iovlen = 1,
+ };
+
+ if (recvmsg(sd[1], &child_msg, 0) < 0)
+ perror("child recv msg 1");
+ cmsg = CMSG_FIRSTHDR(&child_msg);
+ if (cmsg == NULL) {
+ printf("no cmsg rcvd in child");
+ return;
+ }
+ recv_fd = *(int*)CMSG_DATA(cmsg);
+ if (recv_fd < 0) {
+ printf("could not get recv_fd from socket");
+ return;
+ }
+ printf("child %d\n", recv_fd);
+ fd = ion_open();
+ ptr = mmap(NULL, len, prot, map_flags, recv_fd, 0);
+ if (ptr == MAP_FAILED) {
+ return;
+ }
+ printf("child? [%10s] should be [master]\n", ptr);
+ strcpy(ptr, "child");
+ printf("child sending msg 2\n");
+ sendmsg(sd[1], &child_msg, 0);
+ close(fd);
+ }
+}
+
+int main(int argc, char* argv[]) {
+ int c;
+ enum tests {
+ ALLOC_TEST = 0, MAP_TEST, SHARE_TEST,
+ };
+
+ while (1) {
+ static struct option opts[] = {
+ {"alloc", no_argument, 0, 'a'},
+ {"alloc_flags", required_argument, 0, 'f'},
+ {"heap_mask", required_argument, 0, 'h'},
+ {"map", no_argument, 0, 'm'},
+ {"share", no_argument, 0, 's'},
+ {"len", required_argument, 0, 'l'},
+ {"align", required_argument, 0, 'g'},
+ {"map_flags", required_argument, 0, 'z'},
+ {"prot", required_argument, 0, 'p'},
+ };
+ int i = 0;
+ c = getopt_long(argc, argv, "af:h:l:mr:st", opts, &i);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'l':
+ len = atol(optarg);
+ break;
+ case 'g':
+ align = atol(optarg);
+ break;
+ case 'z':
+ map_flags = 0;
+ map_flags |= strstr(optarg, "PROT_EXEC") ? PROT_EXEC : 0;
+ map_flags |= strstr(optarg, "PROT_READ") ? PROT_READ: 0;
+ map_flags |= strstr(optarg, "PROT_WRITE") ? PROT_WRITE: 0;
+ map_flags |= strstr(optarg, "PROT_NONE") ? PROT_NONE: 0;
+ break;
+ case 'p':
+ prot = 0;
+ prot |= strstr(optarg, "MAP_PRIVATE") ? MAP_PRIVATE : 0;
+ prot |= strstr(optarg, "MAP_SHARED") ? MAP_PRIVATE : 0;
+ break;
+ case 'f':
+ alloc_flags = atol(optarg);
+ break;
+ case 'h':
+ heap_mask = atol(optarg);
+ break;
+ case 'a':
+ test = ALLOC_TEST;
+ break;
+ case 'm':
+ test = MAP_TEST;
+ break;
+ case 's':
+ test = SHARE_TEST;
+ break;
+ }
+ }
+ printf("test %d, len %zu, align %zu, map_flags %d, prot %d, heap_mask %d,"
+ " alloc_flags %d\n", test, len, align, map_flags, prot,
+ heap_mask, alloc_flags);
+ switch (test) {
+ case ALLOC_TEST:
+ ion_alloc_test();
+ break;
+ case MAP_TEST:
+ ion_map_test();
+ break;
+ case SHARE_TEST:
+ ion_share_test();
+ break;
+ default:
+ printf("must specify a test (alloc, map, share)\n");
+ }
+ return 0;
+}
diff --git a/libion/kernel-headers/linux/ion.h b/libion/kernel-headers/linux/ion.h
new file mode 100644
index 0000000..5af39d0
--- /dev/null
+++ b/libion/kernel-headers/linux/ion.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ *** This header was automatically generated from a Linux kernel header
+ *** of the same name, to make information necessary for userspace to
+ *** call into the kernel available to libc. It contains only constants,
+ *** structures, and macros generated from the original header, and thus,
+ *** contains no copyrightable information.
+ ***
+ *** To edit the content of this header, modify the corresponding
+ *** source file (e.g. under external/kernel-headers/original/) then
+ *** run bionic/libc/kernel/tools/update_all.py
+ ***
+ *** Any manual change here will be lost the next time this script will
+ *** be run. You've been warned!
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef _UAPI_LINUX_ION_H
+#define _UAPI_LINUX_ION_H
+#include <linux/ioctl.h>
+#include <linux/types.h>
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+typedef int ion_user_handle_t;
+enum ion_heap_type {
+ ION_HEAP_TYPE_SYSTEM,
+ ION_HEAP_TYPE_SYSTEM_CONTIG,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ ION_HEAP_TYPE_CARVEOUT,
+ ION_HEAP_TYPE_CHUNK,
+ ION_HEAP_TYPE_DMA,
+ ION_HEAP_TYPE_CUSTOM,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ ION_NUM_HEAPS = 16,
+};
+#define ION_HEAP_SYSTEM_MASK (1 << ION_HEAP_TYPE_SYSTEM)
+#define ION_HEAP_SYSTEM_CONTIG_MASK (1 << ION_HEAP_TYPE_SYSTEM_CONTIG)
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ION_HEAP_CARVEOUT_MASK (1 << ION_HEAP_TYPE_CARVEOUT)
+#define ION_HEAP_TYPE_DMA_MASK (1 << ION_HEAP_TYPE_DMA)
+#define ION_NUM_HEAP_IDS sizeof(unsigned int) * 8
+#define ION_FLAG_CACHED 1
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ION_FLAG_CACHED_NEEDS_SYNC 2
+struct ion_allocation_data {
+ size_t len;
+ size_t align;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ unsigned int heap_id_mask;
+ unsigned int flags;
+ ion_user_handle_t handle;
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct ion_fd_data {
+ ion_user_handle_t handle;
+ int fd;
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct ion_handle_data {
+ ion_user_handle_t handle;
+};
+struct ion_custom_data {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ unsigned int cmd;
+ unsigned long arg;
+};
+#define ION_IOC_MAGIC 'I'
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ION_IOC_ALLOC _IOWR(ION_IOC_MAGIC, 0, struct ion_allocation_data)
+#define ION_IOC_FREE _IOWR(ION_IOC_MAGIC, 1, struct ion_handle_data)
+#define ION_IOC_MAP _IOWR(ION_IOC_MAGIC, 2, struct ion_fd_data)
+#define ION_IOC_SHARE _IOWR(ION_IOC_MAGIC, 4, struct ion_fd_data)
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ION_IOC_IMPORT _IOWR(ION_IOC_MAGIC, 5, struct ion_fd_data)
+#define ION_IOC_SYNC _IOWR(ION_IOC_MAGIC, 7, struct ion_fd_data)
+#define ION_IOC_CUSTOM _IOWR(ION_IOC_MAGIC, 6, struct ion_custom_data)
+#endif
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
diff --git a/libion/kernel-headers/linux/ion_test.h b/libion/kernel-headers/linux/ion_test.h
new file mode 100644
index 0000000..6f3e2a7
--- /dev/null
+++ b/libion/kernel-headers/linux/ion_test.h
@@ -0,0 +1,38 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ *** This header was automatically generated from a Linux kernel header
+ *** of the same name, to make information necessary for userspace to
+ *** call into the kernel available to libc. It contains only constants,
+ *** structures, and macros generated from the original header, and thus,
+ *** contains no copyrightable information.
+ ***
+ *** To edit the content of this header, modify the corresponding
+ *** source file (e.g. under external/kernel-headers/original/) then
+ *** run bionic/libc/kernel/tools/update_all.py
+ ***
+ *** Any manual change here will be lost the next time this script will
+ *** be run. You've been warned!
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef _UAPI_LINUX_ION_TEST_H
+#define _UAPI_LINUX_ION_TEST_H
+#include <linux/ioctl.h>
+#include <linux/types.h>
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct ion_test_rw_data {
+ __u64 ptr;
+ __u64 offset;
+ __u64 size;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ int write;
+ int __padding;
+};
+#define ION_IOC_MAGIC 'I'
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ION_IOC_TEST_SET_FD _IO(ION_IOC_MAGIC, 0xf0)
+#define ION_IOC_TEST_DMA_MAPPING _IOW(ION_IOC_MAGIC, 0xf1, struct ion_test_rw_data)
+#define ION_IOC_TEST_KERNEL_MAPPING _IOW(ION_IOC_MAGIC, 0xf2, struct ion_test_rw_data)
+#endif
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
diff --git a/libion/original-kernel-headers/linux/ion.h b/libion/original-kernel-headers/linux/ion.h
new file mode 100644
index 0000000..f09e7c1
--- /dev/null
+++ b/libion/original-kernel-headers/linux/ion.h
@@ -0,0 +1,196 @@
+/*
+ * drivers/staging/android/uapi/ion.h
+ *
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _UAPI_LINUX_ION_H
+#define _UAPI_LINUX_ION_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+typedef int ion_user_handle_t;
+
+/**
+ * enum ion_heap_types - list of all possible types of heaps
+ * @ION_HEAP_TYPE_SYSTEM: memory allocated via vmalloc
+ * @ION_HEAP_TYPE_SYSTEM_CONTIG: memory allocated via kmalloc
+ * @ION_HEAP_TYPE_CARVEOUT: memory allocated from a prereserved
+ * carveout heap, allocations are physically
+ * contiguous
+ * @ION_HEAP_TYPE_DMA: memory allocated via DMA API
+ * @ION_NUM_HEAPS: helper for iterating over heaps, a bit mask
+ * is used to identify the heaps, so only 32
+ * total heap types are supported
+ */
+enum ion_heap_type {
+ ION_HEAP_TYPE_SYSTEM,
+ ION_HEAP_TYPE_SYSTEM_CONTIG,
+ ION_HEAP_TYPE_CARVEOUT,
+ ION_HEAP_TYPE_CHUNK,
+ ION_HEAP_TYPE_DMA,
+ ION_HEAP_TYPE_CUSTOM, /* must be last so device specific heaps always
+ are at the end of this enum */
+ ION_NUM_HEAPS = 16,
+};
+
+#define ION_HEAP_SYSTEM_MASK (1 << ION_HEAP_TYPE_SYSTEM)
+#define ION_HEAP_SYSTEM_CONTIG_MASK (1 << ION_HEAP_TYPE_SYSTEM_CONTIG)
+#define ION_HEAP_CARVEOUT_MASK (1 << ION_HEAP_TYPE_CARVEOUT)
+#define ION_HEAP_TYPE_DMA_MASK (1 << ION_HEAP_TYPE_DMA)
+
+#define ION_NUM_HEAP_IDS sizeof(unsigned int) * 8
+
+/**
+ * allocation flags - the lower 16 bits are used by core ion, the upper 16
+ * bits are reserved for use by the heaps themselves.
+ */
+#define ION_FLAG_CACHED 1 /* mappings of this buffer should be
+ cached, ion will do cache
+ maintenance when the buffer is
+ mapped for dma */
+#define ION_FLAG_CACHED_NEEDS_SYNC 2 /* mappings of this buffer will created
+ at mmap time, if this is set
+ caches must be managed manually */
+
+/**
+ * DOC: Ion Userspace API
+ *
+ * create a client by opening /dev/ion
+ * most operations handled via following ioctls
+ *
+ */
+
+/**
+ * struct ion_allocation_data - metadata passed from userspace for allocations
+ * @len: size of the allocation
+ * @align: required alignment of the allocation
+ * @heap_id_mask: mask of heap ids to allocate from
+ * @flags: flags passed to heap
+ * @handle: pointer that will be populated with a cookie to use to
+ * refer to this allocation
+ *
+ * Provided by userspace as an argument to the ioctl
+ */
+struct ion_allocation_data {
+ size_t len;
+ size_t align;
+ unsigned int heap_id_mask;
+ unsigned int flags;
+ ion_user_handle_t handle;
+};
+
+/**
+ * struct ion_fd_data - metadata passed to/from userspace for a handle/fd pair
+ * @handle: a handle
+ * @fd: a file descriptor representing that handle
+ *
+ * For ION_IOC_SHARE or ION_IOC_MAP userspace populates the handle field with
+ * the handle returned from ion alloc, and the kernel returns the file
+ * descriptor to share or map in the fd field. For ION_IOC_IMPORT, userspace
+ * provides the file descriptor and the kernel returns the handle.
+ */
+struct ion_fd_data {
+ ion_user_handle_t handle;
+ int fd;
+};
+
+/**
+ * struct ion_handle_data - a handle passed to/from the kernel
+ * @handle: a handle
+ */
+struct ion_handle_data {
+ ion_user_handle_t handle;
+};
+
+/**
+ * struct ion_custom_data - metadata passed to/from userspace for a custom ioctl
+ * @cmd: the custom ioctl function to call
+ * @arg: additional data to pass to the custom ioctl, typically a user
+ * pointer to a predefined structure
+ *
+ * This works just like the regular cmd and arg fields of an ioctl.
+ */
+struct ion_custom_data {
+ unsigned int cmd;
+ unsigned long arg;
+};
+
+#define ION_IOC_MAGIC 'I'
+
+/**
+ * DOC: ION_IOC_ALLOC - allocate memory
+ *
+ * Takes an ion_allocation_data struct and returns it with the handle field
+ * populated with the opaque handle for the allocation.
+ */
+#define ION_IOC_ALLOC _IOWR(ION_IOC_MAGIC, 0, \
+ struct ion_allocation_data)
+
+/**
+ * DOC: ION_IOC_FREE - free memory
+ *
+ * Takes an ion_handle_data struct and frees the handle.
+ */
+#define ION_IOC_FREE _IOWR(ION_IOC_MAGIC, 1, struct ion_handle_data)
+
+/**
+ * DOC: ION_IOC_MAP - get a file descriptor to mmap
+ *
+ * Takes an ion_fd_data struct with the handle field populated with a valid
+ * opaque handle. Returns the struct with the fd field set to a file
+ * descriptor open in the current address space. This file descriptor
+ * can then be used as an argument to mmap.
+ */
+#define ION_IOC_MAP _IOWR(ION_IOC_MAGIC, 2, struct ion_fd_data)
+
+/**
+ * DOC: ION_IOC_SHARE - creates a file descriptor to use to share an allocation
+ *
+ * Takes an ion_fd_data struct with the handle field populated with a valid
+ * opaque handle. Returns the struct with the fd field set to a file
+ * descriptor open in the current address space. This file descriptor
+ * can then be passed to another process. The corresponding opaque handle can
+ * be retrieved via ION_IOC_IMPORT.
+ */
+#define ION_IOC_SHARE _IOWR(ION_IOC_MAGIC, 4, struct ion_fd_data)
+
+/**
+ * DOC: ION_IOC_IMPORT - imports a shared file descriptor
+ *
+ * Takes an ion_fd_data struct with the fd field populated with a valid file
+ * descriptor obtained from ION_IOC_SHARE and returns the struct with the handle
+ * filed set to the corresponding opaque handle.
+ */
+#define ION_IOC_IMPORT _IOWR(ION_IOC_MAGIC, 5, struct ion_fd_data)
+
+/**
+ * DOC: ION_IOC_SYNC - syncs a shared file descriptors to memory
+ *
+ * Deprecated in favor of using the dma_buf api's correctly (syncing
+ * will happend automatically when the buffer is mapped to a device).
+ * If necessary should be used after touching a cached buffer from the cpu,
+ * this will make the buffer in memory coherent.
+ */
+#define ION_IOC_SYNC _IOWR(ION_IOC_MAGIC, 7, struct ion_fd_data)
+
+/**
+ * DOC: ION_IOC_CUSTOM - call architecture specific ion ioctl
+ *
+ * Takes the argument of the architecture specific ioctl to call and
+ * passes appropriate userdata for that ioctl
+ */
+#define ION_IOC_CUSTOM _IOWR(ION_IOC_MAGIC, 6, struct ion_custom_data)
+
+#endif /* _UAPI_LINUX_ION_H */
diff --git a/libion/original-kernel-headers/linux/ion_test.h b/libion/original-kernel-headers/linux/ion_test.h
new file mode 100644
index 0000000..ffef06f
--- /dev/null
+++ b/libion/original-kernel-headers/linux/ion_test.h
@@ -0,0 +1,70 @@
+/*
+ * drivers/staging/android/uapi/ion.h
+ *
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _UAPI_LINUX_ION_TEST_H
+#define _UAPI_LINUX_ION_TEST_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+/**
+ * struct ion_test_rw_data - metadata passed to the kernel to read handle
+ * @ptr: a pointer to an area at least as large as size
+ * @offset: offset into the ion buffer to start reading
+ * @size: size to read or write
+ * @write: 1 to write, 0 to read
+ */
+struct ion_test_rw_data {
+ __u64 ptr;
+ __u64 offset;
+ __u64 size;
+ int write;
+ int __padding;
+};
+
+#define ION_IOC_MAGIC 'I'
+
+/**
+ * DOC: ION_IOC_TEST_SET_DMA_BUF - attach a dma buf to the test driver
+ *
+ * Attaches a dma buf fd to the test driver. Passing a second fd or -1 will
+ * release the first fd.
+ */
+#define ION_IOC_TEST_SET_FD \
+ _IO(ION_IOC_MAGIC, 0xf0)
+
+/**
+ * DOC: ION_IOC_TEST_DMA_MAPPING - read or write memory from a handle as DMA
+ *
+ * Reads or writes the memory from a handle using an uncached mapping. Can be
+ * used by unit tests to emulate a DMA engine as close as possible. Only
+ * expected to be used for debugging and testing, may not always be available.
+ */
+#define ION_IOC_TEST_DMA_MAPPING \
+ _IOW(ION_IOC_MAGIC, 0xf1, struct ion_test_rw_data)
+
+/**
+ * DOC: ION_IOC_TEST_KERNEL_MAPPING - read or write memory from a handle
+ *
+ * Reads or writes the memory from a handle using a kernel mapping. Can be
+ * used by unit tests to test heap map_kernel functions. Only expected to be
+ * used for debugging and testing, may not always be available.
+ */
+#define ION_IOC_TEST_KERNEL_MAPPING \
+ _IOW(ION_IOC_MAGIC, 0xf2, struct ion_test_rw_data)
+
+
+#endif /* _UAPI_LINUX_ION_H */
diff --git a/libion/tests/Android.mk b/libion/tests/Android.mk
new file mode 100644
index 0000000..894f90e
--- /dev/null
+++ b/libion/tests/Android.mk
@@ -0,0 +1,32 @@
+#
+# Copyright (C) 2013 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := ion-unit-tests
+LOCAL_CFLAGS += -g -Wall -Werror -std=gnu++11 -Wno-missing-field-initializers
+LOCAL_SHARED_LIBRARIES += libion
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/../kernel-headers
+LOCAL_SRC_FILES := \
+ ion_test_fixture.cpp \
+ allocate_test.cpp \
+ formerly_valid_handle_test.cpp \
+ invalid_values_test.cpp \
+ map_test.cpp \
+ device_test.cpp \
+ exit_test.cpp
+include $(BUILD_NATIVE_TEST)
diff --git a/libion/tests/allocate_test.cpp b/libion/tests/allocate_test.cpp
new file mode 100644
index 0000000..e26b302
--- /dev/null
+++ b/libion/tests/allocate_test.cpp
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/mman.h>
+
+#include <gtest/gtest.h>
+
+#include <ion/ion.h>
+#include "ion_test_fixture.h"
+
+class Allocate : public IonAllHeapsTest {
+};
+
+TEST_F(Allocate, Allocate)
+{
+ static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+ for (unsigned int heapMask : m_allHeaps) {
+ for (size_t size : allocationSizes) {
+ SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ SCOPED_TRACE(::testing::Message() << "size " << size);
+ ion_user_handle_t handle = 0;
+ ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, 0, &handle));
+ ASSERT_TRUE(handle != 0);
+ ASSERT_EQ(0, ion_free(m_ionFd, handle));
+ }
+ }
+}
+
+TEST_F(Allocate, AllocateCached)
+{
+ static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+ for (unsigned int heapMask : m_allHeaps) {
+ for (size_t size : allocationSizes) {
+ SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ SCOPED_TRACE(::testing::Message() << "size " << size);
+ ion_user_handle_t handle = 0;
+ ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED, &handle));
+ ASSERT_TRUE(handle != 0);
+ ASSERT_EQ(0, ion_free(m_ionFd, handle));
+ }
+ }
+}
+
+TEST_F(Allocate, AllocateCachedNeedsSync)
+{
+ static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+ for (unsigned int heapMask : m_allHeaps) {
+ for (size_t size : allocationSizes) {
+ SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ SCOPED_TRACE(::testing::Message() << "size " << size);
+ ion_user_handle_t handle = 0;
+ ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED_NEEDS_SYNC, &handle));
+ ASSERT_TRUE(handle != 0);
+ ASSERT_EQ(0, ion_free(m_ionFd, handle));
+ }
+ }
+}
+
+TEST_F(Allocate, RepeatedAllocate)
+{
+ static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+ for (unsigned int heapMask : m_allHeaps) {
+ for (size_t size : allocationSizes) {
+ SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ SCOPED_TRACE(::testing::Message() << "size " << size);
+ ion_user_handle_t handle = 0;
+
+ for (unsigned int i = 0; i < 1024; i++) {
+ SCOPED_TRACE(::testing::Message() << "iteration " << i);
+ ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, 0, &handle));
+ ASSERT_TRUE(handle != 0);
+ ASSERT_EQ(0, ion_free(m_ionFd, handle));
+ }
+ }
+ }
+}
+
+TEST_F(Allocate, Zeroed)
+{
+ void *zeroes = calloc(4096, 1);
+
+ for (unsigned int heapMask : m_allHeaps) {
+ SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ int fds[16];
+ for (unsigned int i = 0; i < 16; i++) {
+ int map_fd = -1;
+
+ ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, 0, &map_fd));
+ ASSERT_GE(map_fd, 0);
+
+ void *ptr = NULL;
+ ptr = mmap(NULL, 4096, PROT_WRITE, MAP_SHARED, map_fd, 0);
+ ASSERT_TRUE(ptr != NULL);
+
+ memset(ptr, 0xaa, 4096);
+
+ ASSERT_EQ(0, munmap(ptr, 4096));
+ fds[i] = map_fd;
+ }
+
+ for (unsigned int i = 0; i < 16; i++) {
+ ASSERT_EQ(0, close(fds[i]));
+ }
+
+ int newIonFd = ion_open();
+ int map_fd = -1;
+
+ ASSERT_EQ(0, ion_alloc_fd(newIonFd, 4096, 0, heapMask, 0, &map_fd));
+ ASSERT_GE(map_fd, 0);
+
+ void *ptr = NULL;
+ ptr = mmap(NULL, 4096, PROT_READ, MAP_SHARED, map_fd, 0);
+ ASSERT_TRUE(ptr != NULL);
+
+ ASSERT_EQ(0, memcmp(ptr, zeroes, 4096));
+
+ ASSERT_EQ(0, munmap(ptr, 4096));
+ ASSERT_EQ(0, close(map_fd));
+ }
+
+ free(zeroes);
+
+}
+
+TEST_F(Allocate, Large)
+{
+ for (unsigned int heapMask : m_allHeaps) {
+ SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ ion_user_handle_t handle = 0;
+ ASSERT_EQ(-ENOMEM, ion_alloc(m_ionFd, 3UL*1024*1024*1024, 0, heapMask, 0, &handle));
+ }
+}
diff --git a/libion/tests/device_test.cpp b/libion/tests/device_test.cpp
new file mode 100644
index 0000000..6f6e1bd
--- /dev/null
+++ b/libion/tests/device_test.cpp
@@ -0,0 +1,568 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <linux/ion_test.h>
+
+#include <gtest/gtest.h>
+
+#include <ion/ion.h>
+
+#include "ion_test_fixture.h"
+
+#define ALIGN(x,y) (((x) + ((y) - 1)) & ~((y) - 1))
+
+class Device : public IonAllHeapsTest {
+ public:
+ virtual void SetUp();
+ virtual void TearDown();
+ int m_deviceFd;
+ void readDMA(int fd, void *buf, size_t size);
+ void writeDMA(int fd, void *buf, size_t size);
+ void readKernel(int fd, void *buf, size_t size);
+ void writeKernel(int fd, void *buf, size_t size);
+ void blowCache();
+ void dirtyCache(void *ptr, size_t size);
+};
+
+void Device::SetUp()
+{
+ IonAllHeapsTest::SetUp();
+ m_deviceFd = open("/dev/ion-test", O_RDWR);
+ ASSERT_GE(m_deviceFd, 0);
+}
+
+void Device::TearDown()
+{
+ ASSERT_EQ(0, close(m_deviceFd));
+ IonAllHeapsTest::TearDown();
+}
+
+void Device::readDMA(int fd, void *buf, size_t size)
+{
+ ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, fd));
+ struct ion_test_rw_data ion_test_rw_data = {
+ .ptr = (uint64_t)buf,
+ .offset = 0,
+ .size = size,
+ .write = 0,
+ };
+
+ ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_DMA_MAPPING, &ion_test_rw_data));
+ ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, -1));
+}
+
+void Device::writeDMA(int fd, void *buf, size_t size)
+{
+ ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, fd));
+ struct ion_test_rw_data ion_test_rw_data = {
+ .ptr = (uint64_t)buf,
+ .offset = 0,
+ .size = size,
+ .write = 1,
+ };
+
+ ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_DMA_MAPPING, &ion_test_rw_data));
+ ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, -1));
+}
+
+void Device::readKernel(int fd, void *buf, size_t size)
+{
+ ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, fd));
+ struct ion_test_rw_data ion_test_rw_data = {
+ .ptr = (uint64_t)buf,
+ .offset = 0,
+ .size = size,
+ .write = 0,
+ };
+
+ ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_KERNEL_MAPPING, &ion_test_rw_data));
+ ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, -1));
+}
+
+void Device::writeKernel(int fd, void *buf, size_t size)
+{
+ ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, fd));
+ struct ion_test_rw_data ion_test_rw_data = {
+ .ptr = (uint64_t)buf,
+ .offset = 0,
+ .size = size,
+ .write = 1,
+ };
+
+ ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_KERNEL_MAPPING, &ion_test_rw_data));
+ ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, -1));
+}
+
+void Device::blowCache()
+{
+ const size_t bigger_than_cache = 8*1024*1024;
+ void *buf1 = malloc(bigger_than_cache);
+ void *buf2 = malloc(bigger_than_cache);
+ memset(buf1, 0xaa, bigger_than_cache);
+ memcpy(buf2, buf1, bigger_than_cache);
+ free(buf1);
+ free(buf2);
+}
+
+void Device::dirtyCache(void *ptr, size_t size)
+{
+ /* try to dirty cache lines */
+ for (size_t i = size-1; i > 0; i--) {
+ ((volatile char *)ptr)[i];
+ ((char *)ptr)[i] = i;
+ }
+}
+
+TEST_F(Device, KernelReadCached)
+{
+ void *alloc = malloc(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+
+ for (unsigned int heapMask : m_allHeaps) {
+ SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ int map_fd = -1;
+ unsigned int flags = ION_FLAG_CACHED;
+
+ ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+ ASSERT_GE(map_fd, 0);
+
+ void *ptr;
+ ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+ ASSERT_TRUE(ptr != NULL);
+
+ for (int i = 0; i < 4096; i++)
+ ((char *)ptr)[i] = i;
+
+ ((char*)buf)[4096] = 0x12;
+ readKernel(map_fd, buf, 4096);
+ ASSERT_EQ(((char*)buf)[4096], 0x12);
+
+ for (int i = 0; i < 4096; i++)
+ ASSERT_EQ((char)i, ((char *)buf)[i]);
+
+ ASSERT_EQ(0, munmap(ptr, 4096));
+ ASSERT_EQ(0, close(map_fd));
+ }
+
+ free(alloc);
+}
+
+TEST_F(Device, KernelWriteCached)
+{
+ void *alloc = malloc(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+
+ for (int i = 0; i < 4096; i++)
+ ((char *)buf)[i] = i;
+
+ for (unsigned int heapMask : m_allHeaps) {
+ SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ int map_fd = -1;
+ unsigned int flags = ION_FLAG_CACHED;
+
+ ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+ ASSERT_GE(map_fd, 0);
+
+ void *ptr;
+ ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+ ASSERT_TRUE(ptr != NULL);
+
+ dirtyCache(ptr, 4096);
+
+ writeKernel(map_fd, buf, 4096);
+
+ for (int i = 0; i < 4096; i++)
+ ASSERT_EQ((char)i, ((char *)ptr)[i]) << i;
+
+ ASSERT_EQ(0, munmap(ptr, 4096));
+ ASSERT_EQ(0, close(map_fd));
+ }
+
+ free(alloc);
+}
+
+TEST_F(Device, DMAReadCached)
+{
+ void *alloc = malloc(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+
+ for (unsigned int heapMask : m_allHeaps) {
+ SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ int map_fd = -1;
+ unsigned int flags = ION_FLAG_CACHED;
+
+ ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+ ASSERT_GE(map_fd, 0);
+
+ void *ptr;
+ ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+ ASSERT_TRUE(ptr != NULL);
+
+ for (int i = 0; i < 4096; i++)
+ ((char *)ptr)[i] = i;
+
+ readDMA(map_fd, buf, 4096);
+
+ for (int i = 0; i < 4096; i++)
+ ASSERT_EQ((char)i, ((char *)buf)[i]);
+
+ ASSERT_EQ(0, munmap(ptr, 4096));
+ ASSERT_EQ(0, close(map_fd));
+ }
+
+ free(alloc);
+}
+
+TEST_F(Device, DMAWriteCached)
+{
+ void *alloc = malloc(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+
+ for (int i = 0; i < 4096; i++)
+ ((char *)buf)[i] = i;
+
+ for (unsigned int heapMask : m_allHeaps) {
+ SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ int map_fd = -1;
+ unsigned int flags = ION_FLAG_CACHED;
+
+ ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+ ASSERT_GE(map_fd, 0);
+
+ void *ptr;
+ ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+ ASSERT_TRUE(ptr != NULL);
+
+ dirtyCache(ptr, 4096);
+
+ writeDMA(map_fd, buf, 4096);
+
+ for (int i = 0; i < 4096; i++)
+ ASSERT_EQ((char)i, ((char *)ptr)[i]) << i;
+
+ ASSERT_EQ(0, munmap(ptr, 4096));
+ ASSERT_EQ(0, close(map_fd));
+ }
+
+ free(alloc);
+}
+
+TEST_F(Device, KernelReadCachedNeedsSync)
+{
+ void *alloc = malloc(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+
+ for (unsigned int heapMask : m_allHeaps) {
+ SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ int map_fd = -1;
+ unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
+
+ ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+ ASSERT_GE(map_fd, 0);
+
+ void *ptr;
+ ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+ ASSERT_TRUE(ptr != NULL);
+
+ for (int i = 0; i < 4096; i++)
+ ((char *)ptr)[i] = i;
+
+ ((char*)buf)[4096] = 0x12;
+ readKernel(map_fd, buf, 4096);
+ ASSERT_EQ(((char*)buf)[4096], 0x12);
+
+ for (int i = 0; i < 4096; i++)
+ ASSERT_EQ((char)i, ((char *)buf)[i]);
+
+ ASSERT_EQ(0, munmap(ptr, 4096));
+ ASSERT_EQ(0, close(map_fd));
+ }
+
+ free(alloc);
+}
+
+TEST_F(Device, KernelWriteCachedNeedsSync)
+{
+ void *alloc = malloc(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+
+ for (int i = 0; i < 4096; i++)
+ ((char *)buf)[i] = i;
+
+ for (unsigned int heapMask : m_allHeaps) {
+ SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ int map_fd = -1;
+ unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
+
+ ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+ ASSERT_GE(map_fd, 0);
+
+ void *ptr;
+ ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+ ASSERT_TRUE(ptr != NULL);
+
+ dirtyCache(ptr, 4096);
+
+ writeKernel(map_fd, buf, 4096);
+
+ for (int i = 0; i < 4096; i++)
+ ASSERT_EQ((char)i, ((char *)ptr)[i]) << i;
+
+ ASSERT_EQ(0, munmap(ptr, 4096));
+ ASSERT_EQ(0, close(map_fd));
+ }
+
+ free(alloc);
+}
+
+TEST_F(Device, DMAReadCachedNeedsSync)
+{
+ void *alloc = malloc(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+
+ for (unsigned int heapMask : m_allHeaps) {
+ SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ int map_fd = -1;
+ unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
+
+ ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+ ASSERT_GE(map_fd, 0);
+
+ void *ptr;
+ ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+ ASSERT_TRUE(ptr != NULL);
+
+ for (int i = 0; i < 4096; i++)
+ ((char *)ptr)[i] = i;
+
+ ion_sync_fd(m_ionFd, map_fd);
+
+ readDMA(map_fd, buf, 4096);
+
+ for (int i = 0; i < 4096; i++)
+ ASSERT_EQ((char)i, ((char *)buf)[i]);
+
+ ASSERT_EQ(0, munmap(ptr, 4096));
+ ASSERT_EQ(0, close(map_fd));
+ }
+
+ free(alloc);
+}
+
+TEST_F(Device, DMAWriteCachedNeedsSync)
+{
+ void *alloc = malloc(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+
+ for (int i = 0; i < 4096; i++)
+ ((char *)buf)[i] = i;
+
+ for (unsigned int heapMask : m_allHeaps) {
+ SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ int map_fd = -1;
+ unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
+
+ ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+ ASSERT_GE(map_fd, 0);
+
+ void *ptr;
+ ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+ ASSERT_TRUE(ptr != NULL);
+
+ dirtyCache(ptr, 4096);
+
+ writeDMA(map_fd, buf, 4096);
+
+ ion_sync_fd(m_ionFd, map_fd);
+
+ for (int i = 0; i < 4096; i++)
+ ASSERT_EQ((char)i, ((char *)ptr)[i]) << i;
+
+ ASSERT_EQ(0, munmap(ptr, 4096));
+ ASSERT_EQ(0, close(map_fd));
+ }
+
+ free(alloc);
+}
+TEST_F(Device, KernelRead)
+{
+ void *alloc = malloc(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+
+ for (unsigned int heapMask : m_allHeaps) {
+ SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ int map_fd = -1;
+ unsigned int flags = 0;
+
+ ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+ ASSERT_GE(map_fd, 0);
+
+ void *ptr;
+ ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+ ASSERT_TRUE(ptr != NULL);
+
+ for (int i = 0; i < 4096; i++)
+ ((char *)ptr)[i] = i;
+
+ ((char*)buf)[4096] = 0x12;
+ readKernel(map_fd, buf, 4096);
+ ASSERT_EQ(((char*)buf)[4096], 0x12);
+
+ for (int i = 0; i < 4096; i++)
+ ASSERT_EQ((char)i, ((char *)buf)[i]);
+
+ ASSERT_EQ(0, munmap(ptr, 4096));
+ ASSERT_EQ(0, close(map_fd));
+ }
+
+ free(alloc);
+}
+
+TEST_F(Device, KernelWrite)
+{
+ void *alloc = malloc(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+
+ for (int i = 0; i < 4096; i++)
+ ((char *)buf)[i] = i;
+
+ for (unsigned int heapMask : m_allHeaps) {
+ SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ int map_fd = -1;
+ unsigned int flags = 0;
+
+ ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+ ASSERT_GE(map_fd, 0);
+
+ void *ptr;
+ ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+ ASSERT_TRUE(ptr != NULL);
+
+ dirtyCache(ptr, 4096);
+
+ writeKernel(map_fd, buf, 4096);
+
+ for (int i = 0; i < 4096; i++)
+ ASSERT_EQ((char)i, ((char *)ptr)[i]) << i;
+
+ ASSERT_EQ(0, munmap(ptr, 4096));
+ ASSERT_EQ(0, close(map_fd));
+ }
+
+ free(alloc);
+}
+
+TEST_F(Device, DMARead)
+{
+ void *alloc = malloc(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+
+ for (unsigned int heapMask : m_allHeaps) {
+ SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ int map_fd = -1;
+ unsigned int flags = 0;
+
+ ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+ ASSERT_GE(map_fd, 0);
+
+ void *ptr;
+ ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+ ASSERT_TRUE(ptr != NULL);
+
+ for (int i = 0; i < 4096; i++)
+ ((char *)ptr)[i] = i;
+
+ readDMA(map_fd, buf, 4096);
+
+ for (int i = 0; i < 4096; i++)
+ ASSERT_EQ((char)i, ((char *)buf)[i]);
+
+ ASSERT_EQ(0, munmap(ptr, 4096));
+ ASSERT_EQ(0, close(map_fd));
+ }
+
+ free(alloc);
+}
+
+TEST_F(Device, DMAWrite)
+{
+ void *alloc = malloc(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+
+ for (int i = 0; i < 4096; i++)
+ ((char *)buf)[i] = i;
+
+ for (unsigned int heapMask : m_allHeaps) {
+ SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ int map_fd = -1;
+ unsigned int flags = 0;
+
+ ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+ ASSERT_GE(map_fd, 0);
+
+ void *ptr;
+ ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+ ASSERT_TRUE(ptr != NULL);
+
+ dirtyCache(ptr, 4096);
+
+ writeDMA(map_fd, buf, 4096);
+
+ for (int i = 0; i < 4096; i++)
+ ASSERT_EQ((char)i, ((char *)ptr)[i]) << i;
+
+ ASSERT_EQ(0, munmap(ptr, 4096));
+ ASSERT_EQ(0, close(map_fd));
+ }
+
+ free(alloc);
+}
+
+TEST_F(Device, IsCached)
+{
+ void *buf = malloc(4096);
+
+ for (unsigned int heapMask : m_allHeaps) {
+ SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ int map_fd = -1;
+ unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
+
+ ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+ ASSERT_GE(map_fd, 0);
+
+ void *ptr;
+ ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+ ASSERT_TRUE(ptr != NULL);
+
+ dirtyCache(ptr, 4096);
+
+ readDMA(map_fd, buf, 4096);
+
+ bool same = true;
+ for (int i = 4096-16; i >= 0; i -= 16)
+ if (((char *)buf)[i] != i)
+ same = false;
+ ASSERT_FALSE(same);
+
+ ASSERT_EQ(0, munmap(ptr, 4096));
+ ASSERT_EQ(0, close(map_fd));
+ }
+}
diff --git a/libion/tests/exit_test.cpp b/libion/tests/exit_test.cpp
new file mode 100644
index 0000000..cdd3e27
--- /dev/null
+++ b/libion/tests/exit_test.cpp
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/mman.h>
+
+#include <gtest/gtest.h>
+
+#include <ion/ion.h>
+
+#include "ion_test_fixture.h"
+
+class Exit : public IonAllHeapsTest {
+};
+
+TEST_F(Exit, WithAlloc)
+{
+ static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+ for (unsigned int heapMask : m_allHeaps) {
+ for (size_t size : allocationSizes) {
+ SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ SCOPED_TRACE(::testing::Message() << "size " << size);
+ EXPECT_EXIT({
+ ion_user_handle_t handle = 0;
+
+ ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, 0, &handle));
+ ASSERT_TRUE(handle != 0);
+ exit(0);
+ }, ::testing::ExitedWithCode(0), "");
+ }
+ }
+}
+
+TEST_F(Exit, WithAllocFd)
+{
+ static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+ for (unsigned int heapMask : m_allHeaps) {
+ for (size_t size : allocationSizes) {
+ SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ SCOPED_TRACE(::testing::Message() << "size " << size);
+ EXPECT_EXIT({
+ int handle_fd = -1;
+
+ ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, 0, &handle_fd));
+ ASSERT_NE(-1, handle_fd);
+ exit(0);
+ }, ::testing::ExitedWithCode(0), "");
+ }
+ }
+}
+
+TEST_F(Exit, WithRepeatedAllocFd)
+{
+ static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+ for (unsigned int heapMask : m_allHeaps) {
+ for (size_t size : allocationSizes) {
+ for (unsigned int i = 0; i < 1024; i++) {
+ SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ SCOPED_TRACE(::testing::Message() << "size " << size);
+ ASSERT_EXIT({
+ int handle_fd = -1;
+
+ ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, 0, &handle_fd));
+ ASSERT_NE(-1, handle_fd);
+ exit(0);
+ }, ::testing::ExitedWithCode(0), "")
+ << "failed on heap " << heapMask
+ << " and size " << size
+ << " on iteration " << i;
+ }
+ }
+ }
+}
+
+
+TEST_F(Exit, WithMapping)
+{
+ static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+ for (unsigned int heapMask : m_allHeaps) {
+ for (size_t size : allocationSizes) {
+ SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ SCOPED_TRACE(::testing::Message() << "size " << size);
+ EXPECT_EXIT({
+ int map_fd = -1;
+
+ ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, 0, &map_fd));
+ ASSERT_GE(map_fd, 0);
+
+ void *ptr;
+ ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+ ASSERT_TRUE(ptr != NULL);
+ exit(0);
+ }, ::testing::ExitedWithCode(0), "");
+ }
+ }
+
+}
+
+TEST_F(Exit, WithPartialMapping)
+{
+ static const size_t allocationSizes[] = {64*1024, 1024*1024, 2*1024*1024};
+ for (unsigned int heapMask : m_allHeaps) {
+ for (size_t size : allocationSizes) {
+ SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ SCOPED_TRACE(::testing::Message() << "size " << size);
+ EXPECT_EXIT({
+ int map_fd = -1;
+
+ ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, 0, &map_fd));
+ ASSERT_GE(map_fd, 0);
+
+ void *ptr;
+ ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+ ASSERT_TRUE(ptr != NULL);
+
+ ASSERT_EQ(0, munmap(ptr, size / 2));
+ exit(0);
+ }, ::testing::ExitedWithCode(0), "");
+ }
+ }
+}
+
+TEST_F(Exit, WithMappingCached)
+{
+ static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+ for (unsigned int heapMask : m_allHeaps) {
+ for (size_t size : allocationSizes) {
+ SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ SCOPED_TRACE(::testing::Message() << "size " << size);
+ EXPECT_EXIT({
+ int map_fd = -1;
+
+ ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED, &map_fd));
+ ASSERT_GE(map_fd, 0);
+
+ void *ptr;
+ ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+ ASSERT_TRUE(ptr != NULL);
+ exit(0);
+ }, ::testing::ExitedWithCode(0), "");
+ }
+ }
+
+}
+
+TEST_F(Exit, WithPartialMappingCached)
+{
+ static const size_t allocationSizes[] = {64*1024, 1024*1024, 2*1024*1024};
+ for (unsigned int heapMask : m_allHeaps) {
+ for (size_t size : allocationSizes) {
+ SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ SCOPED_TRACE(::testing::Message() << "size " << size);
+ EXPECT_EXIT({
+ int map_fd = -1;
+
+ ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED, &map_fd));
+ ASSERT_GE(map_fd, 0);
+
+ void *ptr;
+ ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+ ASSERT_TRUE(ptr != NULL);
+
+ ASSERT_EQ(0, munmap(ptr, size / 2));
+ exit(0);
+ }, ::testing::ExitedWithCode(0), "");
+ }
+ }
+}
+
+TEST_F(Exit, WithMappingNeedsSync)
+{
+ static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+ for (unsigned int heapMask : m_allHeaps) {
+ for (size_t size : allocationSizes) {
+ SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ SCOPED_TRACE(::testing::Message() << "size " << size);
+ EXPECT_EXIT({
+ int map_fd = -1;
+
+ ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC, &map_fd));
+ ASSERT_GE(map_fd, 0);
+
+ void *ptr;
+ ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+ ASSERT_TRUE(ptr != NULL);
+ exit(0);
+ }, ::testing::ExitedWithCode(0), "");
+ }
+ }
+
+}
+
+TEST_F(Exit, WithPartialMappingNeedsSync)
+{
+ static const size_t allocationSizes[] = {64*1024, 1024*1024, 2*1024*1024};
+ for (unsigned int heapMask : m_allHeaps) {
+ for (size_t size : allocationSizes) {
+ SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ SCOPED_TRACE(::testing::Message() << "size " << size);
+ EXPECT_EXIT({
+ int map_fd = -1;
+
+ ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC, &map_fd));
+ ASSERT_GE(map_fd, 0);
+
+ void *ptr;
+ ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+ ASSERT_TRUE(ptr != NULL);
+
+ ASSERT_EQ(0, munmap(ptr, size / 2));
+ exit(0);
+ }, ::testing::ExitedWithCode(0), "");
+ }
+ }
+}
diff --git a/libion/tests/formerly_valid_handle_test.cpp b/libion/tests/formerly_valid_handle_test.cpp
new file mode 100644
index 0000000..01ab8f3
--- /dev/null
+++ b/libion/tests/formerly_valid_handle_test.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/mman.h>
+
+#include <gtest/gtest.h>
+
+#include <ion/ion.h>
+
+#include "ion_test_fixture.h"
+
+class FormerlyValidHandle : public IonTest {
+ public:
+ virtual void SetUp();
+ virtual void TearDown();
+ ion_user_handle_t m_handle;
+};
+
+void FormerlyValidHandle::SetUp()
+{
+ IonTest::SetUp();
+ ASSERT_EQ(0, ion_alloc(m_ionFd, 4096, 0, 1/* ion_env->m_firstHeap */, 0, &m_handle));
+ ASSERT_TRUE(m_handle != 0);
+ ASSERT_EQ(0, ion_free(m_ionFd, m_handle));
+}
+
+void FormerlyValidHandle::TearDown()
+{
+ m_handle = 0;
+}
+
+TEST_F(FormerlyValidHandle, free)
+{
+ ASSERT_EQ(-EINVAL, ion_free(m_ionFd, m_handle));
+}
+
+TEST_F(FormerlyValidHandle, map)
+{
+ int map_fd;
+ unsigned char *ptr;
+
+ ASSERT_EQ(-EINVAL, ion_map(m_ionFd, m_handle, 4096, PROT_READ, 0, 0, &ptr, &map_fd));
+}
+
+TEST_F(FormerlyValidHandle, share)
+{
+ int share_fd;
+
+ ASSERT_EQ(-EINVAL, ion_share(m_ionFd, m_handle, &share_fd));
+}
diff --git a/libion/tests/invalid_values_test.cpp b/libion/tests/invalid_values_test.cpp
new file mode 100644
index 0000000..77fea17
--- /dev/null
+++ b/libion/tests/invalid_values_test.cpp
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/mman.h>
+
+#include <gtest/gtest.h>
+
+#include <ion/ion.h>
+
+#include "ion_test_fixture.h"
+
+class InvalidValues : public IonAllHeapsTest {
+ public:
+ virtual void SetUp();
+ virtual void TearDown();
+ ion_user_handle_t m_validHandle;
+ int m_validShareFd;
+ ion_user_handle_t const m_badHandle = -1;
+};
+
+void InvalidValues::SetUp()
+{
+ IonAllHeapsTest::SetUp();
+ ASSERT_EQ(0, ion_alloc(m_ionFd, 4096, 0, m_firstHeap, 0, &m_validHandle))
+ << m_ionFd << " " << m_firstHeap;
+ ASSERT_TRUE(m_validHandle != 0);
+ ASSERT_EQ(0, ion_share(m_ionFd, m_validHandle, &m_validShareFd));
+}
+
+void InvalidValues::TearDown()
+{
+ ASSERT_EQ(0, ion_free(m_ionFd, m_validHandle));
+ ASSERT_EQ(0, close(m_validShareFd));
+ m_validHandle = 0;
+ IonAllHeapsTest::TearDown();
+}
+
+TEST_F(InvalidValues, ion_close)
+{
+ EXPECT_EQ(-EBADF, ion_close(-1));
+}
+
+TEST_F(InvalidValues, ion_alloc)
+{
+ ion_user_handle_t handle;
+ /* invalid ion_fd */
+ int ret = ion_alloc(0, 4096, 0, m_firstHeap, 0, &handle);
+ EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
+ /* invalid ion_fd */
+ EXPECT_EQ(-EBADF, ion_alloc(-1, 4096, 0, m_firstHeap, 0, &handle));
+ /* no heaps */
+ EXPECT_EQ(-ENODEV, ion_alloc(m_ionFd, 4096, 0, 0, 0, &handle));
+ for (unsigned int heapMask : m_allHeaps) {
+ SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ /* zero size */
+ EXPECT_EQ(-EINVAL, ion_alloc(m_ionFd, 0, 0, heapMask, 0, &handle));
+ /* too large size */
+ EXPECT_EQ(-EINVAL, ion_alloc(m_ionFd, -1, 0, heapMask, 0, &handle));
+ /* bad alignment */
+ EXPECT_EQ(-EINVAL, ion_alloc(m_ionFd, 4096, -1, heapMask, 0, &handle));
+ /* NULL handle */
+ EXPECT_EQ(-EINVAL, ion_alloc(m_ionFd, 4096, 0, heapMask, 0, NULL));
+ }
+}
+
+TEST_F(InvalidValues, ion_alloc_fd)
+{
+ int fd;
+ /* invalid ion_fd */
+ int ret = ion_alloc_fd(0, 4096, 0, m_firstHeap, 0, &fd);
+ EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
+ /* invalid ion_fd */
+ EXPECT_EQ(-EBADF, ion_alloc_fd(-1, 4096, 0, m_firstHeap, 0, &fd));
+ /* no heaps */
+ EXPECT_EQ(-ENODEV, ion_alloc_fd(m_ionFd, 4096, 0, 0, 0, &fd));
+ for (unsigned int heapMask : m_allHeaps) {
+ SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ /* zero size */
+ EXPECT_EQ(-EINVAL, ion_alloc_fd(m_ionFd, 0, 0, heapMask, 0, &fd));
+ /* too large size */
+ EXPECT_EQ(-EINVAL, ion_alloc_fd(m_ionFd, -1, 0, heapMask, 0, &fd));
+ /* bad alignment */
+ EXPECT_EQ(-EINVAL, ion_alloc_fd(m_ionFd, 4096, -1, heapMask, 0, &fd));
+ /* NULL handle */
+ EXPECT_EQ(-EINVAL, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, 0, NULL));
+ }
+}
+
+TEST_F(InvalidValues, ion_free)
+{
+ /* invalid ion fd */
+ int ret = ion_free(0, m_validHandle);
+ EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
+ /* invalid ion fd */
+ EXPECT_EQ(-EBADF, ion_free(-1, m_validHandle));
+ /* zero handle */
+ EXPECT_EQ(-EINVAL, ion_free(m_ionFd, 0));
+ /* bad handle */
+ EXPECT_EQ(-EINVAL, ion_free(m_ionFd, m_badHandle));
+}
+
+TEST_F(InvalidValues, ion_map)
+{
+ int map_fd;
+ unsigned char *ptr;
+
+ /* invalid ion fd */
+ int ret = ion_map(0, m_validHandle, 4096, PROT_READ, 0, 0, &ptr, &map_fd);
+ EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
+ /* invalid ion fd */
+ EXPECT_EQ(-EBADF, ion_map(-1, m_validHandle, 4096, PROT_READ, 0, 0, &ptr, &map_fd));
+ /* zero handle */
+ EXPECT_EQ(-EINVAL, ion_map(m_ionFd, 0, 4096, PROT_READ, 0, 0, &ptr, &map_fd));
+ /* bad handle */
+ EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_badHandle, 4096, PROT_READ, 0, 0, &ptr, &map_fd));
+ /* zero length */
+ EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_validHandle, 0, PROT_READ, 0, 0, &ptr, &map_fd));
+ /* bad prot */
+ EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_validHandle, 4096, -1, 0, 0, &ptr, &map_fd));
+ /* bad offset */
+ EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_validHandle, 4096, PROT_READ, 0, -1, &ptr, &map_fd));
+ /* NULL ptr */
+ EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_validHandle, 4096, PROT_READ, 0, 0, NULL, &map_fd));
+ /* NULL map_fd */
+ EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_validHandle, 4096, PROT_READ, 0, 0, &ptr, NULL));
+}
+
+TEST_F(InvalidValues, ion_share)
+{
+ int share_fd;
+
+ /* invalid ion fd */
+ int ret = ion_share(0, m_validHandle, &share_fd);
+ EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
+ /* invalid ion fd */
+ EXPECT_EQ(-EBADF, ion_share(-1, m_validHandle, &share_fd));
+ /* zero handle */
+ EXPECT_EQ(-EINVAL, ion_share(m_ionFd, 0, &share_fd));
+ /* bad handle */
+ EXPECT_EQ(-EINVAL, ion_share(m_ionFd, m_badHandle, &share_fd));
+ /* NULL share_fd */
+ EXPECT_EQ(-EINVAL, ion_share(m_ionFd, m_validHandle, NULL));
+}
+
+TEST_F(InvalidValues, ion_import)
+{
+ ion_user_handle_t handle;
+
+ /* invalid ion fd */
+ int ret = ion_import(0, m_validShareFd, &handle);
+ EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
+ /* invalid ion fd */
+ EXPECT_EQ(-EBADF, ion_import(-1, m_validShareFd, &handle));
+ /* bad share_fd */
+ EXPECT_EQ(-EINVAL, ion_import(m_ionFd, 0, &handle));
+ /* invalid share_fd */
+ EXPECT_EQ(-EBADF, ion_import(m_ionFd, -1, &handle));
+ /* NULL handle */
+ EXPECT_EQ(-EINVAL, ion_import(m_ionFd, m_validShareFd, NULL));
+}
+
+TEST_F(InvalidValues, ion_sync_fd)
+{
+ /* invalid ion fd */
+ int ret = ion_sync_fd(0, m_validShareFd);
+ EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
+ /* invalid ion fd */
+ EXPECT_EQ(-EBADF, ion_sync_fd(-1, m_validShareFd));
+ /* bad share_fd */
+ EXPECT_EQ(-EINVAL, ion_sync_fd(m_ionFd, 0));
+ /* invalid share_fd */
+ EXPECT_EQ(-EBADF, ion_sync_fd(m_ionFd, -1));
+}
diff --git a/libion/tests/ion_test_fixture.cpp b/libion/tests/ion_test_fixture.cpp
new file mode 100644
index 0000000..e20c730
--- /dev/null
+++ b/libion/tests/ion_test_fixture.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <ion/ion.h>
+
+#include "ion_test_fixture.h"
+
+IonTest::IonTest() : m_ionFd(-1)
+{
+}
+
+void IonTest::SetUp() {
+ m_ionFd = ion_open();
+ ASSERT_GE(m_ionFd, 0);
+}
+
+void IonTest::TearDown() {
+ ion_close(m_ionFd);
+}
+
+IonAllHeapsTest::IonAllHeapsTest() :
+ m_firstHeap(0),
+ m_lastHeap(0),
+ m_allHeaps()
+{
+}
+
+void IonAllHeapsTest::SetUp() {
+ int fd = ion_open();
+ ASSERT_GE(fd, 0);
+
+ for (int i = 1; i != 0; i <<= 1) {
+ ion_user_handle_t handle = 0;
+ int ret;
+ ret = ion_alloc(fd, 4096, 0, i, 0, &handle);
+ if (ret == 0 && handle != 0) {
+ ion_free(fd, handle);
+ if (!m_firstHeap) {
+ m_firstHeap = i;
+ }
+ m_lastHeap = i;
+ m_allHeaps.push_back(i);
+ } else {
+ ASSERT_EQ(-ENODEV, ret);
+ }
+ }
+ ion_close(fd);
+
+ EXPECT_NE(0U, m_firstHeap);
+ EXPECT_NE(0U, m_lastHeap);
+
+ RecordProperty("Heaps", m_allHeaps.size());
+ IonTest::SetUp();
+}
+
+void IonAllHeapsTest::TearDown() {
+ IonTest::TearDown();
+}
diff --git a/libion/tests/ion_test_fixture.h b/libion/tests/ion_test_fixture.h
new file mode 100644
index 0000000..4098214
--- /dev/null
+++ b/libion/tests/ion_test_fixture.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ION_TEST_FIXTURE_H_
+#define ION_TEST_FIXTURE_H_
+
+#include <gtest/gtest.h>
+
+using ::testing::Test;
+
+class IonTest : public virtual Test {
+ public:
+ IonTest();
+ virtual ~IonTest() {};
+ virtual void SetUp();
+ virtual void TearDown();
+ int m_ionFd;
+};
+
+class IonAllHeapsTest : public IonTest {
+ public:
+ IonAllHeapsTest();
+ virtual ~IonAllHeapsTest() {};
+ virtual void SetUp();
+ virtual void TearDown();
+
+ unsigned int m_firstHeap;
+ unsigned int m_lastHeap;
+
+ std::vector<unsigned int> m_allHeaps;
+};
+
+#endif /* ION_TEST_FIXTURE_H_ */
diff --git a/libion/tests/map_test.cpp b/libion/tests/map_test.cpp
new file mode 100644
index 0000000..c006dc8
--- /dev/null
+++ b/libion/tests/map_test.cpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/mman.h>
+
+#include <gtest/gtest.h>
+
+#include <ion/ion.h>
+
+#include "ion_test_fixture.h"
+
+class Map : public IonAllHeapsTest {
+};
+
+TEST_F(Map, MapHandle)
+{
+ static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+ for (unsigned int heapMask : m_allHeaps) {
+ for (size_t size : allocationSizes) {
+ SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ SCOPED_TRACE(::testing::Message() << "size " << size);
+ ion_user_handle_t handle = 0;
+
+ ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, 0, &handle));
+ ASSERT_TRUE(handle != 0);
+
+ int map_fd = -1;
+ unsigned char *ptr = NULL;
+ ASSERT_EQ(0, ion_map(m_ionFd, handle, size, PROT_READ | PROT_WRITE, MAP_SHARED, 0, &ptr, &map_fd));
+ ASSERT_TRUE(ptr != NULL);
+ ASSERT_GE(map_fd, 0);
+
+ ASSERT_EQ(0, close(map_fd));
+
+ ASSERT_EQ(0, ion_free(m_ionFd, handle));
+
+ memset(ptr, 0xaa, size);
+
+ ASSERT_EQ(0, munmap(ptr, size));
+ }
+ }
+}
+
+TEST_F(Map, MapFd)
+{
+ static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+ for (unsigned int heapMask : m_allHeaps) {
+ for (size_t size : allocationSizes) {
+ SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ SCOPED_TRACE(::testing::Message() << "size " << size);
+ int map_fd = -1;
+
+ ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, 0, &map_fd));
+ ASSERT_GE(map_fd, 0);
+
+ void *ptr;
+ ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+ ASSERT_TRUE(ptr != NULL);
+
+ ASSERT_EQ(0, close(map_fd));
+
+ memset(ptr, 0xaa, size);
+
+ ASSERT_EQ(0, munmap(ptr, size));
+ }
+ }
+}
+
+TEST_F(Map, MapOffset)
+{
+ for (unsigned int heapMask : m_allHeaps) {
+ SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ int map_fd = -1;
+
+ ASSERT_EQ(0, ion_alloc_fd(m_ionFd, PAGE_SIZE * 2, 0, heapMask, 0, &map_fd));
+ ASSERT_GE(map_fd, 0);
+
+ unsigned char *ptr;
+ ptr = (unsigned char *)mmap(NULL, PAGE_SIZE * 2, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+ ASSERT_TRUE(ptr != NULL);
+
+ memset(ptr, 0, PAGE_SIZE);
+ memset(ptr + PAGE_SIZE, 0xaa, PAGE_SIZE);
+
+ ASSERT_EQ(0, munmap(ptr, PAGE_SIZE * 2));
+
+ ptr = (unsigned char *)mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, PAGE_SIZE);
+ ASSERT_TRUE(ptr != NULL);
+
+ ASSERT_EQ(ptr[0], 0xaa);
+ ASSERT_EQ(ptr[PAGE_SIZE - 1], 0xaa);
+
+ ASSERT_EQ(0, munmap(ptr, PAGE_SIZE));
+
+ ASSERT_EQ(0, close(map_fd));
+ }
+}
+
+TEST_F(Map, MapCached)
+{
+ static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+ for (unsigned int heapMask : m_allHeaps) {
+ for (size_t size : allocationSizes) {
+ SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ SCOPED_TRACE(::testing::Message() << "size " << size);
+ int map_fd = -1;
+ unsigned int flags = ION_FLAG_CACHED;
+
+ ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, flags, &map_fd));
+ ASSERT_GE(map_fd, 0);
+
+ void *ptr;
+ ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+ ASSERT_TRUE(ptr != NULL);
+
+ ASSERT_EQ(0, close(map_fd));
+
+ memset(ptr, 0xaa, size);
+
+ ASSERT_EQ(0, munmap(ptr, size));
+ }
+ }
+}
+
+TEST_F(Map, MapCachedNeedsSync)
+{
+ static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+ for (unsigned int heapMask : m_allHeaps) {
+ for (size_t size : allocationSizes) {
+ SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ SCOPED_TRACE(::testing::Message() << "size " << size);
+ int map_fd = -1;
+ unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
+
+ ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, flags, &map_fd));
+ ASSERT_GE(map_fd, 0);
+
+ void *ptr;
+ ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+ ASSERT_TRUE(ptr != NULL);
+
+ ASSERT_EQ(0, close(map_fd));
+
+ memset(ptr, 0xaa, size);
+
+ ASSERT_EQ(0, munmap(ptr, size));
+ }
+ }
+}
diff --git a/liblog/Android.mk b/liblog/Android.mk
new file mode 100644
index 0000000..ce282bd
--- /dev/null
+++ b/liblog/Android.mk
@@ -0,0 +1,98 @@
+#
+# Copyright (C) 2008-2014 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 := $(my-dir)
+include $(CLEAR_VARS)
+
+# This is what we want to do:
+# liblog_cflags := $(shell \
+# sed -n \
+# 's/^\([0-9]*\)[ \t]*liblog[ \t].*/-DLIBLOG_LOG_TAG=\1/p' \
+# $(LOCAL_PATH)/event.logtags)
+# so make sure we do not regret hard-coding it as follows:
+liblog_cflags := -DLIBLOG_LOG_TAG=1005
+
+ifneq ($(TARGET_USES_LOGD),false)
+liblog_sources := logd_write.c
+else
+liblog_sources := logd_write_kern.c
+endif
+
+# some files must not be compiled when building against Mingw
+# they correspond to features not used by our host development tools
+# which are also hard or even impossible to port to native Win32
+
+ifeq ($(strip $(USE_MINGW)),)
+ liblog_sources += \
+ event_tag_map.c
+else
+ liblog_sources += \
+ uio.c
+endif
+
+liblog_host_sources := $(liblog_sources) fake_log_device.c event.logtags
+liblog_target_sources := $(liblog_sources) log_time.cpp log_is_loggable.c
+ifeq ($(strip $(USE_MINGW)),)
+liblog_target_sources += logprint.c
+endif
+ifneq ($(TARGET_USES_LOGD),false)
+liblog_target_sources += log_read.c
+else
+liblog_target_sources += log_read_kern.c
+endif
+
+# Shared and static library for host
+# ========================================================
+LOCAL_MODULE := liblog
+LOCAL_SRC_FILES := $(liblog_host_sources)
+LOCAL_CFLAGS := -DFAKE_LOG_DEVICE=1 -Werror $(liblog_cflags)
+LOCAL_MULTILIB := both
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := liblog
+LOCAL_WHOLE_STATIC_LIBRARIES := liblog
+ifeq ($(strip $(HOST_OS)),linux)
+LOCAL_LDLIBS := -lrt
+endif
+LOCAL_MULTILIB := both
+LOCAL_CXX_STL := none
+include $(BUILD_HOST_SHARED_LIBRARY)
+
+
+# Shared and static library for target
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := liblog
+LOCAL_SRC_FILES := $(liblog_target_sources)
+LOCAL_CFLAGS := -Werror $(liblog_cflags)
+# AddressSanitizer runtime library depends on liblog.
+LOCAL_SANITIZE := never
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := liblog
+LOCAL_WHOLE_STATIC_LIBRARIES := liblog
+LOCAL_CFLAGS := -Werror $(liblog_cflags)
+
+# TODO: This is to work around b/19059885. Remove after root cause is fixed
+LOCAL_LDFLAGS_arm := -Wl,--hash-style=both
+
+LOCAL_SANITIZE := never
+LOCAL_CXX_STL := none
+
+include $(BUILD_SHARED_LIBRARY)
+
+include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/liblog/NOTICE b/liblog/NOTICE
new file mode 100644
index 0000000..06a9081
--- /dev/null
+++ b/liblog/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-2014, 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/liblog/README b/liblog/README
new file mode 100644
index 0000000..f29ac04
--- /dev/null
+++ b/liblog/README
@@ -0,0 +1,166 @@
+LIBLOG(3) Android NDK Programming Manual LIBLOG(3)
+
+
+
+NAME
+ liblog - Android NDK logger interfaces
+
+SYNOPSIS
+ #include <log/log.h>
+
+ ALOG(android_priority, tag, format, ...)
+ IF_ALOG(android_priority, tag)
+ LOG_PRI(priority, tag, format, ...)
+ LOG_PRI_VA(priority, tag, format, args)
+ #define LOG_TAG NULL
+ ALOGV(format, ...)
+ SLOGV(format, ...)
+ RLOGV(format, ...)
+ ALOGV_IF(cond, format, ...)
+ SLOGV_IF(cond, format, ...)
+ RLOGV_IF(cond, format, ...)
+ IF_ALOGC()
+ ALOGD(format, ...)
+ SLOGD(format, ...)
+ RLOGD(format, ...)
+ ALOGD_IF(cond, format, ...)
+ SLOGD_IF(cond, format, ...)
+ RLOGD_IF(cond, format, ...)
+ IF_ALOGD()
+ ALOGI(format, ...)
+ SLOGI(format, ...)
+ RLOGI(format, ...)
+ ALOGI_IF(cond, format, ...)
+ SLOGI_IF(cond, format, ...)
+ RLOGI_IF(cond, format, ...)
+ IF_ALOGI()
+ ALOGW(format, ...)
+ SLOGW(format, ...)
+ RLOGW(format, ...)
+ ALOGW_IF(cond, format, ...)
+ SLOGW_IF(cond, format, ...)
+ RLOGW_IF(cond, format, ...)
+ IF_ALOGW()
+ ALOGE(format, ...)
+ SLOGE(format, ...)
+ RLOGE(format, ...)
+ ALOGE_IF(cond, format, ...)
+ SLOGE_IF(cond, format, ...)
+ RLOGE_IF(cond, format, ...)
+ IF_ALOGE()
+ LOG_FATAL(format, ...)
+ LOG_ALWAYS_FATAL(format, ...)
+ LOG_FATAL_IF(cond, format, ...)
+ LOG_ALWAYS_FATAL_IF(cond, format, ...)
+ ALOG_ASSERT(cond, format, ...)
+ LOG_EVENT_INT(tag, value)
+ LOG_EVENT_LONG(tag, value)
+
+ Link with -llog
+
+ #include <log/logger.h>
+
+ log_id_t android_logger_get_id(struct logger *logger)
+ int android_logger_clear(struct logger *logger)
+ int android_logger_get_log_size(struct logger *logger)
+ int android_logger_get_log_readable_size(struct logger *logger)
+ int android_logger_get_log_version(struct logger *logger)
+
+ struct logger_list *android_logger_list_alloc(int mode, unsigned int
+ tail, pid_t pid)
+ struct logger *android_logger_open(struct logger_list *logger_list,
+ log_id_t id)
+ struct logger_list *android_logger_list_open(log_id_t id, int mode,
+ unsigned int tail, pid_t pid)
+
+ int android_logger_list_read(struct logger_list *logger_list, struct
+ log_msg *log_msg
+
+ void android_logger_list_free(struct logger_list *logger_list)
+
+ log_id_t android_name_to_log_id(const char *logName)
+ const char *android_log_id_to_name(log_id_t log_id)
+
+ Link with -llog
+
+DESCRIPTION
+ liblog represents an interface to the volatile Android Logging system
+ for NDK (Native) applications and libraries. Interfaces for either
+ writing or reading logs. The log buffers are divided up in Main, Sys‐
+ tem, Radio and Events sub-logs.
+
+ The logging interfaces are a series of macros, all of which can be
+ overridden individually in order to control the verbosity of the appli‐
+ cation or library. [ASR]LOG[VDIWE] calls are used to log to BAsic,
+ System or Radio sub-logs in either the Verbose, Debug, Info, Warning or
+ Error priorities. [ASR]LOG[VDIWE]_IF calls are used to perform thus
+ based on a condition being true. IF_ALOG[VDIWE] calls are true if the
+ current LOG_TAG is enabled at the specified priority. LOG_ALWAYS_FATAL
+ is used to ALOG a message, then kill the process. LOG_FATAL call is a
+ variant of LOG_ALWAYS_FATAL, only enabled in engineering, and not
+ release builds. ALOG_ASSERT is used to ALOG a message if the condition
+ is false; the condition is part of the logged message.
+ LOG_EVENT_(INT|LONG) is used to drop binary content into the Events
+ sub-log.
+
+ The log reading interfaces permit opening the logs either singly or
+ multiply, retrieving a log entry at a time in time sorted order,
+ optionally limited to a specific pid and tail of the log(s) and finally
+ a call closing the logs. A single log can be opened with android_log‐
+ ger_list_open; or multiple logs can be opened with android_log‐
+ ger_list_alloc, calling in turn the android_logger_open for each log
+ id. Each entry can be retrieved with android_logger_list_read. The
+ log(s) can be closed with android_logger_list_free. The logs should be
+ opened with an ANDROID_LOG_RDONLY mode. ANDROID_LOG_NONBLOCK mode
+ will report when the log reading is done with an EAGAIN error return
+ code, otherwise the android_logger_list_read call will block for new
+ entries.
+
+ The ANDROID_LOG_PSTORE mode flag to the android_logger_open is used to
+ switch from the active logs to the persistent logs from before the last
+ reboot.
+
+ The value returned by android_logger_open can be used as a parameter to
+ the android_logger_clear function to empty the sub-log. It is recom‐
+ mended to only open log ANDROID_LOG_WRONLY in that case.
+
+ The value returned by android_logger_open can be used as a parameter to
+ the android_logger_get_log_(size|readable_size|version) to retrieve the
+ sub-log maximum size, readable size and log buffer format protocol ver‐
+ sion respectively. android_logger_get_id returns the id that was used
+ when opening the sub-log. It is recommended to open the log
+ ANDROID_LOG_RDONLY in these cases.
+
+ERRORS
+ If messages fail, a negative error code will be returned to the caller.
+
+ The -ENOTCONN return code indicates that the logger daemon is stopped.
+
+ The -EBADF return code indicates that the log access point can not be
+ opened, or the log buffer id is out of range.
+
+ For the -EAGAIN return code, this means that the logging message was
+ temporarily backed-up either because of Denial Of Service (DOS) logging
+ pressure from some chatty application or service in the Android system,
+ or if too small of a value is set in /proc/sys/net/unix/max_dgram_qlen.
+ To aid in diagnosing the occurence of this, a binary event from liblog
+ will be sent to the log daemon once a new message can get through
+ indicating how many messages were dropped as a result. Please take
+ action to resolve the structural problems at the source.
+
+ It is generally not advised for the caller to retry the -EAGAIN return
+ code as this will only make the problem(s) worse and cause your
+ application to temporarily drop to the logger daemon priority, BATCH
+ scheduling policy and background task cgroup. If you require a group of
+ messages to be passed atomically, merge them into one message with
+ embedded newlines to the maximum length LOGGER_ENTRY_MAX_PAYLOAD.
+
+ Other return codes from writing operation can be returned. Since the
+ library retries on EINTR, -EINTR should never be returned.
+
+SEE ALSO
+ syslogd(8)
+
+
+
+ 24 Jan 2014 LIBLOG(3)
diff --git a/liblog/event.logtags b/liblog/event.logtags
new file mode 100644
index 0000000..72ecab1
--- /dev/null
+++ b/liblog/event.logtags
@@ -0,0 +1,36 @@
+# The entries in this file map a sparse set of log tag numbers to tag names.
+# This is installed on the device, in /system/etc, and parsed by logcat.
+#
+# Tag numbers are decimal integers, from 0 to 2^31. (Let's leave the
+# negative values alone for now.)
+#
+# Tag names are one or more ASCII letters and numbers or underscores, i.e.
+# "[A-Z][a-z][0-9]_". Do not include spaces or punctuation (the former
+# impacts log readability, the latter makes regex searches more annoying).
+#
+# Tag numbers and names are separated by whitespace. Blank lines and lines
+# starting with '#' are ignored.
+#
+# Optionally, after the tag names can be put a description for the value(s)
+# of the tag. Description are in the format
+# (<name>|data type[|data unit])
+# Multiple values are separated by commas.
+#
+# The data type is a number from the following values:
+# 1: int
+# 2: long
+# 3: string
+# 4: list
+#
+# The data unit is a number taken from the following list:
+# 1: Number of objects
+# 2: Number of bytes
+# 3: Number of milliseconds
+# 4: Number of allocations
+# 5: Id
+# 6: Percent
+# Default value for data of type int/long is 2 (bytes).
+#
+# TODO: generate ".java" and ".h" files with integer constants from this file.
+
+1005 liblog (dropped|1)
diff --git a/liblog/event_tag_map.c b/liblog/event_tag_map.c
new file mode 100644
index 0000000..bea99aa
--- /dev/null
+++ b/liblog/event_tag_map.c
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2007 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 <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+
+#include <log/event_tag_map.h>
+#include <log/log.h>
+
+#define OUT_TAG "EventTagMap"
+
+/*
+ * Single entry.
+ */
+typedef struct EventTag {
+ unsigned int tagIndex;
+ const char* tagStr;
+} EventTag;
+
+/*
+ * Map.
+ */
+struct EventTagMap {
+ /* memory-mapped source file; we get strings from here */
+ void* mapAddr;
+ size_t mapLen;
+
+ /* array of event tags, sorted numerically by tag index */
+ EventTag* tagArray;
+ int numTags;
+};
+
+/* fwd */
+static int processFile(EventTagMap* map);
+static int countMapLines(const EventTagMap* map);
+static int parseMapLines(EventTagMap* map);
+static int scanTagLine(char** pData, EventTag* tag, int lineNum);
+static int sortTags(EventTagMap* map);
+
+
+/*
+ * Open the map file and allocate a structure to manage it.
+ *
+ * We create a private mapping because we want to terminate the log tag
+ * strings with '\0'.
+ */
+EventTagMap* android_openEventTagMap(const char* fileName)
+{
+ EventTagMap* newTagMap;
+ off_t end;
+ int fd = -1;
+
+ newTagMap = calloc(1, sizeof(EventTagMap));
+ if (newTagMap == NULL)
+ return NULL;
+
+ fd = open(fileName, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "%s: unable to open map '%s': %s\n",
+ OUT_TAG, fileName, strerror(errno));
+ goto fail;
+ }
+
+ end = lseek(fd, 0L, SEEK_END);
+ (void) lseek(fd, 0L, SEEK_SET);
+ if (end < 0) {
+ fprintf(stderr, "%s: unable to seek map '%s'\n", OUT_TAG, fileName);
+ goto fail;
+ }
+
+ newTagMap->mapAddr = mmap(NULL, end, PROT_READ | PROT_WRITE, MAP_PRIVATE,
+ fd, 0);
+ if (newTagMap->mapAddr == MAP_FAILED) {
+ fprintf(stderr, "%s: mmap(%s) failed: %s\n",
+ OUT_TAG, fileName, strerror(errno));
+ goto fail;
+ }
+ newTagMap->mapLen = end;
+
+ if (processFile(newTagMap) != 0)
+ goto fail;
+
+ return newTagMap;
+
+fail:
+ android_closeEventTagMap(newTagMap);
+ if (fd >= 0)
+ close(fd);
+ return NULL;
+}
+
+/*
+ * Close the map.
+ */
+void android_closeEventTagMap(EventTagMap* map)
+{
+ if (map == NULL)
+ return;
+
+ munmap(map->mapAddr, map->mapLen);
+ free(map);
+}
+
+/*
+ * Look up an entry in the map.
+ *
+ * The entries are sorted by tag number, so we can do a binary search.
+ */
+const char* android_lookupEventTag(const EventTagMap* map, int tag)
+{
+ int hi, lo, mid;
+
+ lo = 0;
+ hi = map->numTags-1;
+
+ while (lo <= hi) {
+ int cmp;
+
+ mid = (lo+hi)/2;
+ cmp = map->tagArray[mid].tagIndex - tag;
+ if (cmp < 0) {
+ /* tag is bigger */
+ lo = mid + 1;
+ } else if (cmp > 0) {
+ /* tag is smaller */
+ hi = mid - 1;
+ } else {
+ /* found */
+ return map->tagArray[mid].tagStr;
+ }
+ }
+
+ return NULL;
+}
+
+
+
+/*
+ * Determine whether "c" is a whitespace char.
+ */
+static inline int isCharWhitespace(char c)
+{
+ return (c == ' ' || c == '\n' || c == '\r' || c == '\t');
+}
+
+/*
+ * Determine whether "c" is a valid tag char.
+ */
+static inline int isCharValidTag(char c)
+{
+ return ((c >= 'A' && c <= 'Z') ||
+ (c >= 'a' && c <= 'z') ||
+ (c >= '0' && c <= '9') ||
+ (c == '_'));
+}
+
+/*
+ * Determine whether "c" is a valid decimal digit.
+ */
+static inline int isCharDigit(char c)
+{
+ return (c >= '0' && c <= '9');
+}
+
+
+/*
+ * Crunch through the file, parsing the contents and creating a tag index.
+ */
+static int processFile(EventTagMap* map)
+{
+ /* get a tag count */
+ map->numTags = countMapLines(map);
+ if (map->numTags < 0)
+ return -1;
+
+ //printf("+++ found %d tags\n", map->numTags);
+
+ /* allocate storage for the tag index array */
+ map->tagArray = calloc(1, sizeof(EventTag) * map->numTags);
+ if (map->tagArray == NULL)
+ return -1;
+
+ /* parse the file, null-terminating tag strings */
+ if (parseMapLines(map) != 0) {
+ fprintf(stderr, "%s: file parse failed\n", OUT_TAG);
+ return -1;
+ }
+
+ /* sort the tags and check for duplicates */
+ if (sortTags(map) != 0)
+ return -1;
+
+ return 0;
+}
+
+/*
+ * Run through all lines in the file, determining whether they're blank,
+ * comments, or possibly have a tag entry.
+ *
+ * This is a very "loose" scan. We don't try to detect syntax errors here.
+ * The later pass is more careful, but the number of tags found there must
+ * match the number of tags found here.
+ *
+ * Returns the number of potential tag entries found.
+ */
+static int countMapLines(const EventTagMap* map)
+{
+ int numTags, unknown;
+ const char* cp;
+ const char* endp;
+
+ cp = (const char*) map->mapAddr;
+ endp = cp + map->mapLen;
+
+ numTags = 0;
+ unknown = 1;
+ while (cp < endp) {
+ if (*cp == '\n') {
+ unknown = 1;
+ } else if (unknown) {
+ if (isCharDigit(*cp)) {
+ /* looks like a tag to me */
+ numTags++;
+ unknown = 0;
+ } else if (isCharWhitespace(*cp)) {
+ /* might be leading whitespace before tag num, keep going */
+ } else {
+ /* assume comment; second pass can complain in detail */
+ unknown = 0;
+ }
+ } else {
+ /* we've made up our mind; just scan to end of line */
+ }
+ cp++;
+ }
+
+ return numTags;
+}
+
+/*
+ * Parse the tags out of the file.
+ */
+static int parseMapLines(EventTagMap* map)
+{
+ int tagNum, lineStart, lineNum;
+ char* cp;
+ char* endp;
+
+ cp = (char*) map->mapAddr;
+ endp = cp + map->mapLen;
+
+ /* insist on EOL at EOF; simplifies parsing and null-termination */
+ if (*(endp-1) != '\n') {
+ fprintf(stderr, "%s: map file missing EOL on last line\n", OUT_TAG);
+ return -1;
+ }
+
+ tagNum = 0;
+ lineStart = 1;
+ lineNum = 1;
+ while (cp < endp) {
+ //printf("{%02x}", *cp); fflush(stdout);
+ if (*cp == '\n') {
+ lineStart = 1;
+ lineNum++;
+ } else if (lineStart) {
+ if (*cp == '#') {
+ /* comment; just scan to end */
+ lineStart = 0;
+ } else if (isCharDigit(*cp)) {
+ /* looks like a tag; scan it out */
+ if (tagNum >= map->numTags) {
+ fprintf(stderr,
+ "%s: more tags than expected (%d)\n", OUT_TAG, tagNum);
+ return -1;
+ }
+ if (scanTagLine(&cp, &map->tagArray[tagNum], lineNum) != 0)
+ return -1;
+ tagNum++;
+ lineNum++; // we eat the '\n'
+ /* leave lineStart==1 */
+ } else if (isCharWhitespace(*cp)) {
+ /* looks like leading whitespace; keep scanning */
+ } else {
+ fprintf(stderr,
+ "%s: unexpected chars (0x%02x) in tag number on line %d\n",
+ OUT_TAG, *cp, lineNum);
+ return -1;
+ }
+ } else {
+ /* this is a blank or comment line */
+ }
+ cp++;
+ }
+
+ if (tagNum != map->numTags) {
+ fprintf(stderr, "%s: parsed %d tags, expected %d\n",
+ OUT_TAG, tagNum, map->numTags);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Scan one tag line.
+ *
+ * "*pData" should be pointing to the first digit in the tag number. On
+ * successful return, it will be pointing to the last character in the
+ * tag line (i.e. the character before the start of the next line).
+ *
+ * Returns 0 on success, nonzero on failure.
+ */
+static int scanTagLine(char** pData, EventTag* tag, int lineNum)
+{
+ char* cp = *pData;
+ char* startp;
+ char* endp;
+ unsigned long val;
+
+ startp = cp;
+ while (isCharDigit(*++cp))
+ ;
+ *cp = '\0';
+
+ val = strtoul(startp, &endp, 10);
+ assert(endp == cp);
+ if (endp != cp)
+ fprintf(stderr, "ARRRRGH\n");
+
+ tag->tagIndex = val;
+
+ while (*++cp != '\n' && isCharWhitespace(*cp))
+ ;
+
+ if (*cp == '\n') {
+ fprintf(stderr,
+ "%s: missing tag string on line %d\n", OUT_TAG, lineNum);
+ return -1;
+ }
+
+ tag->tagStr = cp;
+
+ while (isCharValidTag(*++cp))
+ ;
+
+ if (*cp == '\n') {
+ /* null terminate and return */
+ *cp = '\0';
+ } else if (isCharWhitespace(*cp)) {
+ /* CRLF or trailin spaces; zap this char, then scan for the '\n' */
+ *cp = '\0';
+
+ /* just ignore the rest of the line till \n
+ TODO: read the tag description that follows the tag name
+ */
+ while (*++cp != '\n') {
+ }
+ } else {
+ fprintf(stderr,
+ "%s: invalid tag chars on line %d\n", OUT_TAG, lineNum);
+ return -1;
+ }
+
+ *pData = cp;
+
+ //printf("+++ Line %d: got %d '%s'\n", lineNum, tag->tagIndex, tag->tagStr);
+ return 0;
+}
+
+/*
+ * Compare two EventTags.
+ */
+static int compareEventTags(const void* v1, const void* v2)
+{
+ const EventTag* tag1 = (const EventTag*) v1;
+ const EventTag* tag2 = (const EventTag*) v2;
+
+ return tag1->tagIndex - tag2->tagIndex;
+}
+
+/*
+ * Sort the EventTag array so we can do fast lookups by tag index. After
+ * the sort we do a quick check for duplicate tag indices.
+ *
+ * Returns 0 on success.
+ */
+static int sortTags(EventTagMap* map)
+{
+ int i;
+
+ qsort(map->tagArray, map->numTags, sizeof(EventTag), compareEventTags);
+
+ for (i = 1; i < map->numTags; i++) {
+ if (map->tagArray[i].tagIndex == map->tagArray[i-1].tagIndex) {
+ fprintf(stderr, "%s: duplicate tag entries (%d:%s and %d:%s)\n",
+ OUT_TAG,
+ map->tagArray[i].tagIndex, map->tagArray[i].tagStr,
+ map->tagArray[i-1].tagIndex, map->tagArray[i-1].tagStr);
+ return -1;
+ }
+ }
+
+ return 0;
+}
diff --git a/liblog/fake_log_device.c b/liblog/fake_log_device.c
new file mode 100644
index 0000000..8a8ece2
--- /dev/null
+++ b/liblog/fake_log_device.c
@@ -0,0 +1,697 @@
+/*
+ * Copyright (C) 2008-2014 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.
+ */
+/*
+ * Intercepts log messages intended for the Android log device.
+ * When running in the context of the simulator, the messages are
+ * passed on to the underlying (fake) log device. When not in the
+ * simulator, messages are printed to stderr.
+ */
+#include "fake_log_device.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <log/logd.h>
+
+#if !defined(_WIN32)
+#include <pthread.h>
+#endif
+
+#ifndef __unused
+#define __unused __attribute__((__unused__))
+#endif
+
+#define kMaxTagLen 16 /* from the long-dead utils/Log.cpp */
+
+#define kTagSetSize 16 /* arbitrary */
+
+#if 0
+#define TRACE(...) printf("fake_log_device: " __VA_ARGS__)
+#else
+#define TRACE(...) ((void)0)
+#endif
+
+/* from the long-dead utils/Log.cpp */
+typedef enum {
+ FORMAT_OFF = 0,
+ FORMAT_BRIEF,
+ FORMAT_PROCESS,
+ FORMAT_TAG,
+ FORMAT_THREAD,
+ FORMAT_RAW,
+ FORMAT_TIME,
+ FORMAT_THREADTIME,
+ FORMAT_LONG
+} LogFormat;
+
+
+/*
+ * Log driver state.
+ */
+typedef struct LogState {
+ /* the fake fd that's seen by the user */
+ int fakeFd;
+
+ /* a printable name for this fake device */
+ char *debugName;
+
+ /* nonzero if this is a binary log */
+ int isBinary;
+
+ /* global minimum priority */
+ int globalMinPriority;
+
+ /* output format */
+ LogFormat outputFormat;
+
+ /* tags and priorities */
+ struct {
+ char tag[kMaxTagLen];
+ int minPriority;
+ } tagSet[kTagSetSize];
+} LogState;
+
+
+#if !defined(_WIN32)
+/*
+ * Locking. Since we're emulating a device, we need to be prepared
+ * to have multiple callers at the same time. This lock is used
+ * to both protect the fd list and to prevent LogStates from being
+ * freed out from under a user.
+ */
+static pthread_mutex_t fakeLogDeviceLock = PTHREAD_MUTEX_INITIALIZER;
+
+static void lock()
+{
+ pthread_mutex_lock(&fakeLogDeviceLock);
+}
+
+static void unlock()
+{
+ pthread_mutex_unlock(&fakeLogDeviceLock);
+}
+#else // !defined(_WIN32)
+#define lock() ((void)0)
+#define unlock() ((void)0)
+#endif // !defined(_WIN32)
+
+
+/*
+ * File descriptor management.
+ */
+#define FAKE_FD_BASE 10000
+#define MAX_OPEN_LOGS 16
+static LogState *openLogTable[MAX_OPEN_LOGS];
+
+/*
+ * Allocate an fd and associate a new LogState with it.
+ * The fd is available via the fakeFd field of the return value.
+ */
+static LogState *createLogState()
+{
+ size_t i;
+
+ for (i = 0; i < sizeof(openLogTable); i++) {
+ if (openLogTable[i] == NULL) {
+ openLogTable[i] = calloc(1, sizeof(LogState));
+ openLogTable[i]->fakeFd = FAKE_FD_BASE + i;
+ return openLogTable[i];
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Translate an fd to a LogState.
+ */
+static LogState *fdToLogState(int fd)
+{
+ if (fd >= FAKE_FD_BASE && fd < FAKE_FD_BASE + MAX_OPEN_LOGS) {
+ return openLogTable[fd - FAKE_FD_BASE];
+ }
+ return NULL;
+}
+
+/*
+ * Unregister the fake fd and free the memory it pointed to.
+ */
+static void deleteFakeFd(int fd)
+{
+ LogState *ls;
+
+ lock();
+
+ ls = fdToLogState(fd);
+ if (ls != NULL) {
+ openLogTable[fd - FAKE_FD_BASE] = NULL;
+ free(ls->debugName);
+ free(ls);
+ }
+
+ unlock();
+}
+
+/*
+ * Configure logging based on ANDROID_LOG_TAGS environment variable. We
+ * need to parse a string that looks like
+ *
+ * *:v jdwp:d dalvikvm:d dalvikvm-gc:i dalvikvmi:i
+ *
+ * The tag (or '*' for the global level) comes first, followed by a colon
+ * and a letter indicating the minimum priority level we're expected to log.
+ * This can be used to reveal or conceal logs with specific tags.
+ *
+ * We also want to check ANDROID_PRINTF_LOG to determine how the output
+ * will look.
+ */
+static void configureInitialState(const char* pathName, LogState* logState)
+{
+ static const int kDevLogLen = sizeof("/dev/log/") - 1;
+
+ logState->debugName = strdup(pathName);
+
+ /* identify binary logs */
+ if (strcmp(pathName + kDevLogLen, "events") == 0) {
+ logState->isBinary = 1;
+ }
+
+ /* global min priority defaults to "info" level */
+ logState->globalMinPriority = ANDROID_LOG_INFO;
+
+ /*
+ * This is based on the the long-dead utils/Log.cpp code.
+ */
+ const char* tags = getenv("ANDROID_LOG_TAGS");
+ TRACE("Found ANDROID_LOG_TAGS='%s'\n", tags);
+ if (tags != NULL) {
+ int entry = 0;
+
+ while (*tags != '\0') {
+ char tagName[kMaxTagLen];
+ int i, minPrio;
+
+ while (isspace(*tags))
+ tags++;
+
+ i = 0;
+ while (*tags != '\0' && !isspace(*tags) && *tags != ':' &&
+ i < kMaxTagLen)
+ {
+ tagName[i++] = *tags++;
+ }
+ if (i == kMaxTagLen) {
+ TRACE("ERROR: env tag too long (%d chars max)\n", kMaxTagLen-1);
+ return;
+ }
+ tagName[i] = '\0';
+
+ /* default priority, if there's no ":" part; also zero out '*' */
+ minPrio = ANDROID_LOG_VERBOSE;
+ if (tagName[0] == '*' && tagName[1] == '\0') {
+ minPrio = ANDROID_LOG_DEBUG;
+ tagName[0] = '\0';
+ }
+
+ if (*tags == ':') {
+ tags++;
+ if (*tags >= '0' && *tags <= '9') {
+ if (*tags >= ('0' + ANDROID_LOG_SILENT))
+ minPrio = ANDROID_LOG_VERBOSE;
+ else
+ minPrio = *tags - '\0';
+ } else {
+ switch (*tags) {
+ case 'v': minPrio = ANDROID_LOG_VERBOSE; break;
+ case 'd': minPrio = ANDROID_LOG_DEBUG; break;
+ case 'i': minPrio = ANDROID_LOG_INFO; break;
+ case 'w': minPrio = ANDROID_LOG_WARN; break;
+ case 'e': minPrio = ANDROID_LOG_ERROR; break;
+ case 'f': minPrio = ANDROID_LOG_FATAL; break;
+ case 's': minPrio = ANDROID_LOG_SILENT; break;
+ default: minPrio = ANDROID_LOG_DEFAULT; break;
+ }
+ }
+
+ tags++;
+ if (*tags != '\0' && !isspace(*tags)) {
+ TRACE("ERROR: garbage in tag env; expected whitespace\n");
+ TRACE(" env='%s'\n", tags);
+ return;
+ }
+ }
+
+ if (tagName[0] == 0) {
+ logState->globalMinPriority = minPrio;
+ TRACE("+++ global min prio %d\n", logState->globalMinPriority);
+ } else {
+ logState->tagSet[entry].minPriority = minPrio;
+ strcpy(logState->tagSet[entry].tag, tagName);
+ TRACE("+++ entry %d: %s:%d\n",
+ entry,
+ logState->tagSet[entry].tag,
+ logState->tagSet[entry].minPriority);
+ entry++;
+ }
+ }
+ }
+
+
+ /*
+ * Taken from the long-dead utils/Log.cpp
+ */
+ const char* fstr = getenv("ANDROID_PRINTF_LOG");
+ LogFormat format;
+ if (fstr == NULL) {
+ format = FORMAT_BRIEF;
+ } else {
+ if (strcmp(fstr, "brief") == 0)
+ format = FORMAT_BRIEF;
+ else if (strcmp(fstr, "process") == 0)
+ format = FORMAT_PROCESS;
+ else if (strcmp(fstr, "tag") == 0)
+ format = FORMAT_PROCESS;
+ else if (strcmp(fstr, "thread") == 0)
+ format = FORMAT_PROCESS;
+ else if (strcmp(fstr, "raw") == 0)
+ format = FORMAT_PROCESS;
+ else if (strcmp(fstr, "time") == 0)
+ format = FORMAT_PROCESS;
+ else if (strcmp(fstr, "long") == 0)
+ format = FORMAT_PROCESS;
+ else
+ format = (LogFormat) atoi(fstr); // really?!
+ }
+
+ logState->outputFormat = format;
+}
+
+/*
+ * Return a human-readable string for the priority level. Always returns
+ * a valid string.
+ */
+static const char* getPriorityString(int priority)
+{
+ /* the first character of each string should be unique */
+ static const char* priorityStrings[] = {
+ "Verbose", "Debug", "Info", "Warn", "Error", "Assert"
+ };
+ int idx;
+
+ idx = (int) priority - (int) ANDROID_LOG_VERBOSE;
+ if (idx < 0 ||
+ idx >= (int) (sizeof(priorityStrings) / sizeof(priorityStrings[0])))
+ return "?unknown?";
+ return priorityStrings[idx];
+}
+
+#if defined(_WIN32)
+/*
+ * WIN32 does not have writev().
+ * Make up something to replace it.
+ */
+static ssize_t fake_writev(int fd, const struct iovec *iov, int iovcnt) {
+ ssize_t result = 0;
+ const struct iovec* end = iov + iovcnt;
+ for (; iov < end; iov++) {
+ ssize_t w = write(fd, iov->iov_base, iov->iov_len);
+ if (w != (ssize_t) iov->iov_len) {
+ if (w < 0)
+ return w;
+ return result + w;
+ }
+ result += w;
+ }
+ return result;
+}
+
+#define writev fake_writev
+#endif
+
+
+/*
+ * Write a filtered log message to stderr.
+ *
+ * Log format parsing taken from the long-dead utils/Log.cpp.
+ */
+static void showLog(LogState *state,
+ int logPrio, const char* tag, const char* msg)
+{
+#if !defined(_WIN32)
+ struct tm tmBuf;
+#endif
+ struct tm* ptm;
+ char timeBuf[32];
+ char prefixBuf[128], suffixBuf[128];
+ char priChar;
+ time_t when;
+ pid_t pid, tid;
+
+ TRACE("LOG %d: %s %s", logPrio, tag, msg);
+
+ priChar = getPriorityString(logPrio)[0];
+ when = time(NULL);
+ pid = tid = getpid(); // find gettid()?
+
+ /*
+ * Get the current date/time in pretty form
+ *
+ * It's often useful when examining a log with "less" to jump to
+ * a specific point in the file by searching for the date/time stamp.
+ * For this reason it's very annoying to have regexp meta characters
+ * in the time stamp. Don't use forward slashes, parenthesis,
+ * brackets, asterisks, or other special chars here.
+ */
+#if !defined(_WIN32)
+ ptm = localtime_r(&when, &tmBuf);
+#else
+ ptm = localtime(&when);
+#endif
+ //strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", ptm);
+ strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm);
+
+ /*
+ * Construct a buffer containing the log header and log message.
+ */
+ size_t prefixLen, suffixLen;
+
+ switch (state->outputFormat) {
+ case FORMAT_TAG:
+ prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
+ "%c/%-8s: ", priChar, tag);
+ strcpy(suffixBuf, "\n"); suffixLen = 1;
+ break;
+ case FORMAT_PROCESS:
+ prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
+ "%c(%5d) ", priChar, pid);
+ suffixLen = snprintf(suffixBuf, sizeof(suffixBuf),
+ " (%s)\n", tag);
+ break;
+ case FORMAT_THREAD:
+ prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
+ "%c(%5d:%5d) ", priChar, pid, tid);
+ strcpy(suffixBuf, "\n"); suffixLen = 1;
+ break;
+ case FORMAT_RAW:
+ prefixBuf[0] = 0; prefixLen = 0;
+ strcpy(suffixBuf, "\n"); suffixLen = 1;
+ break;
+ case FORMAT_TIME:
+ prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
+ "%s %-8s\n\t", timeBuf, tag);
+ strcpy(suffixBuf, "\n"); suffixLen = 1;
+ break;
+ case FORMAT_THREADTIME:
+ prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
+ "%s %5d %5d %c %-8s \n\t", timeBuf, pid, tid, priChar, tag);
+ strcpy(suffixBuf, "\n"); suffixLen = 1;
+ break;
+ case FORMAT_LONG:
+ prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
+ "[ %s %5d:%5d %c/%-8s ]\n",
+ timeBuf, pid, tid, priChar, tag);
+ strcpy(suffixBuf, "\n\n"); suffixLen = 2;
+ break;
+ default:
+ prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
+ "%c/%-8s(%5d): ", priChar, tag, pid);
+ strcpy(suffixBuf, "\n"); suffixLen = 1;
+ break;
+ }
+
+ /*
+ * Figure out how many lines there will be.
+ */
+ const char* end = msg + strlen(msg);
+ size_t numLines = 0;
+ const char* p = msg;
+ while (p < end) {
+ if (*p++ == '\n') numLines++;
+ }
+ if (p > msg && *(p-1) != '\n') numLines++;
+
+ /*
+ * Create an array of iovecs large enough to write all of
+ * the lines with a prefix and a suffix.
+ */
+ const size_t INLINE_VECS = 6;
+ const size_t MAX_LINES = ((size_t)~0)/(3*sizeof(struct iovec*));
+ struct iovec stackVec[INLINE_VECS];
+ struct iovec* vec = stackVec;
+ size_t numVecs;
+
+ if (numLines > MAX_LINES)
+ numLines = MAX_LINES;
+
+ numVecs = numLines*3; // 3 iovecs per line.
+ if (numVecs > INLINE_VECS) {
+ vec = (struct iovec*)malloc(sizeof(struct iovec)*numVecs);
+ if (vec == NULL) {
+ msg = "LOG: write failed, no memory";
+ numVecs = 3;
+ numLines = 1;
+ vec = stackVec;
+ }
+ }
+
+ /*
+ * Fill in the iovec pointers.
+ */
+ p = msg;
+ struct iovec* v = vec;
+ int totalLen = 0;
+ while (numLines > 0 && p < end) {
+ if (prefixLen > 0) {
+ v->iov_base = prefixBuf;
+ v->iov_len = prefixLen;
+ totalLen += prefixLen;
+ v++;
+ }
+ const char* start = p;
+ while (p < end && *p != '\n') p++;
+ if ((p-start) > 0) {
+ v->iov_base = (void*)start;
+ v->iov_len = p-start;
+ totalLen += p-start;
+ v++;
+ }
+ if (*p == '\n') p++;
+ if (suffixLen > 0) {
+ v->iov_base = suffixBuf;
+ v->iov_len = suffixLen;
+ totalLen += suffixLen;
+ v++;
+ }
+ numLines -= 1;
+ }
+
+ /*
+ * Write the entire message to the log file with a single writev() call.
+ * We need to use this rather than a collection of printf()s on a FILE*
+ * because of multi-threading and multi-process issues.
+ *
+ * If the file was not opened with O_APPEND, this will produce interleaved
+ * output when called on the same file from multiple processes.
+ *
+ * If the file descriptor is actually a network socket, the writev()
+ * call may return with a partial write. Putting the writev() call in
+ * a loop can result in interleaved data. This can be alleviated
+ * somewhat by wrapping the writev call in the Mutex.
+ */
+
+ for(;;) {
+ int cc = writev(fileno(stderr), vec, v-vec);
+
+ if (cc == totalLen) break;
+
+ if (cc < 0) {
+ if(errno == EINTR) continue;
+
+ /* can't really log the failure; for now, throw out a stderr */
+ fprintf(stderr, "+++ LOG: write failed (errno=%d)\n", errno);
+ break;
+ } else {
+ /* shouldn't happen when writing to file or tty */
+ fprintf(stderr, "+++ LOG: write partial (%d of %d)\n", cc, totalLen);
+ break;
+ }
+ }
+
+ /* if we allocated storage for the iovecs, free it */
+ if (vec != stackVec)
+ free(vec);
+}
+
+
+/*
+ * Receive a log message. We happen to know that "vector" has three parts:
+ *
+ * priority (1 byte)
+ * tag (N bytes -- null-terminated ASCII string)
+ * message (N bytes -- null-terminated ASCII string)
+ */
+static ssize_t logWritev(int fd, const struct iovec* vector, int count)
+{
+ LogState* state;
+
+ /* Make sure that no-one frees the LogState while we're using it.
+ * Also guarantees that only one thread is in showLog() at a given
+ * time (if it matters).
+ */
+ lock();
+
+ state = fdToLogState(fd);
+ if (state == NULL) {
+ errno = EBADF;
+ goto error;
+ }
+
+ if (state->isBinary) {
+ TRACE("%s: ignoring binary log\n", state->debugName);
+ goto bail;
+ }
+
+ if (count != 3) {
+ TRACE("%s: writevLog with count=%d not expected\n",
+ state->debugName, count);
+ goto error;
+ }
+
+ /* pull out the three fields */
+ int logPrio = *(const char*)vector[0].iov_base;
+ const char* tag = (const char*) vector[1].iov_base;
+ const char* msg = (const char*) vector[2].iov_base;
+
+ /* see if this log tag is configured */
+ int i;
+ int minPrio = state->globalMinPriority;
+ for (i = 0; i < kTagSetSize; i++) {
+ if (state->tagSet[i].minPriority == ANDROID_LOG_UNKNOWN)
+ break; /* reached end of configured values */
+
+ if (strcmp(state->tagSet[i].tag, tag) == 0) {
+ //TRACE("MATCH tag '%s'\n", tag);
+ minPrio = state->tagSet[i].minPriority;
+ break;
+ }
+ }
+
+ if (logPrio >= minPrio) {
+ showLog(state, logPrio, tag, msg);
+ } else {
+ //TRACE("+++ NOLOG(%d): %s %s", logPrio, tag, msg);
+ }
+
+bail:
+ unlock();
+ return vector[0].iov_len + vector[1].iov_len + vector[2].iov_len;
+error:
+ unlock();
+ return -1;
+}
+
+/*
+ * Free up our state and close the fake descriptor.
+ */
+static int logClose(int fd)
+{
+ deleteFakeFd(fd);
+ return 0;
+}
+
+/*
+ * Open a log output device and return a fake fd.
+ */
+static int logOpen(const char* pathName, int flags __unused)
+{
+ LogState *logState;
+ int fd = -1;
+
+ lock();
+
+ logState = createLogState();
+ if (logState != NULL) {
+ configureInitialState(pathName, logState);
+ fd = logState->fakeFd;
+ } else {
+ errno = ENFILE;
+ }
+
+ unlock();
+
+ return fd;
+}
+
+
+/*
+ * Runtime redirection. If this binary is running in the simulator,
+ * just pass log messages to the emulated device. If it's running
+ * outside of the simulator, write the log messages to stderr.
+ */
+
+static int (*redirectOpen)(const char *pathName, int flags) = NULL;
+static int (*redirectClose)(int fd) = NULL;
+static ssize_t (*redirectWritev)(int fd, const struct iovec* vector, int count)
+ = NULL;
+
+static void setRedirects()
+{
+ const char *ws;
+
+ /* Wrapsim sets this environment variable on children that it's
+ * created using its LD_PRELOAD wrapper.
+ */
+ ws = getenv("ANDROID_WRAPSIM");
+ if (ws != NULL && strcmp(ws, "1") == 0) {
+ /* We're running inside wrapsim, so we can just write to the device. */
+ redirectOpen = (int (*)(const char *pathName, int flags))open;
+ redirectClose = close;
+ redirectWritev = writev;
+ } else {
+ /* There's no device to delegate to; handle the logging ourselves. */
+ redirectOpen = logOpen;
+ redirectClose = logClose;
+ redirectWritev = logWritev;
+ }
+}
+
+int fakeLogOpen(const char *pathName, int flags)
+{
+ if (redirectOpen == NULL) {
+ setRedirects();
+ }
+ return redirectOpen(pathName, flags);
+}
+
+int fakeLogClose(int fd)
+{
+ /* Assume that open() was called first. */
+ return redirectClose(fd);
+}
+
+ssize_t fakeLogWritev(int fd, const struct iovec* vector, int count)
+{
+ /* Assume that open() was called first. */
+ return redirectWritev(fd, vector, count);
+}
+
+int __android_log_is_loggable(int prio, const char *tag __unused, int def)
+{
+ int logLevel = def;
+ return logLevel >= 0 && prio >= logLevel;
+}
diff --git a/liblog/fake_log_device.h b/liblog/fake_log_device.h
new file mode 100644
index 0000000..9d168cd
--- /dev/null
+++ b/liblog/fake_log_device.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBLOG_FAKE_LOG_DEVICE_H
+#define _LIBLOG_FAKE_LOG_DEVICE_H
+
+#include <sys/types.h>
+
+struct iovec;
+
+int fakeLogOpen(const char *pathName, int flags);
+int fakeLogClose(int fd);
+ssize_t fakeLogWritev(int fd, const struct iovec* vector, int count);
+
+#endif // _LIBLOG_FAKE_LOG_DEVICE_H
diff --git a/liblog/log_is_loggable.c b/liblog/log_is_loggable.c
new file mode 100644
index 0000000..7a8e33f
--- /dev/null
+++ b/liblog/log_is_loggable.c
@@ -0,0 +1,178 @@
+/*
+** Copyright 2014, 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 <ctype.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+
+#include <android/log.h>
+
+struct cache {
+ const prop_info *pinfo;
+ uint32_t serial;
+ char c;
+};
+
+static void refresh_cache(struct cache *cache, const char *key)
+{
+ uint32_t serial;
+ char buf[PROP_VALUE_MAX];
+
+ if (!cache->pinfo) {
+ cache->pinfo = __system_property_find(key);
+ if (!cache->pinfo) {
+ return;
+ }
+ }
+ serial = __system_property_serial(cache->pinfo);
+ if (serial == cache->serial) {
+ return;
+ }
+ cache->serial = serial;
+ __system_property_read(cache->pinfo, 0, buf);
+ cache->c = buf[0];
+}
+
+static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
+
+static int __android_log_level(const char *tag, int def)
+{
+ /* sizeof() is used on this array below */
+ static const char log_namespace[] = "persist.log.tag.";
+ static const size_t base_offset = 8; /* skip "persist." */
+ /* calculate the size of our key temporary buffer */
+ const size_t taglen = (tag && *tag) ? strlen(tag) : 0;
+ /* sizeof(log_namespace) = strlen(log_namespace) + 1 */
+ char key[sizeof(log_namespace) + taglen];
+ char *kp;
+ size_t i;
+ char c = 0;
+ /*
+ * Single layer cache of four properties. Priorities are:
+ * log.tag.<tag>
+ * persist.log.tag.<tag>
+ * log.tag
+ * persist.log.tag
+ * Where the missing tag matches all tags and becomes the
+ * system global default. We do not support ro.log.tag* .
+ */
+ static char *last_tag;
+ static uint32_t global_serial;
+ uint32_t current_global_serial;
+ static struct cache tag_cache[2] = {
+ { NULL, -1, 0 },
+ { NULL, -1, 0 }
+ };
+ static struct cache global_cache[2] = {
+ { NULL, -1, 0 },
+ { NULL, -1, 0 }
+ };
+
+ strcpy(key, log_namespace);
+
+ pthread_mutex_lock(&lock);
+
+ current_global_serial = __system_property_area_serial();
+
+ if (taglen) {
+ uint32_t current_local_serial = current_global_serial;
+
+ if (!last_tag || strcmp(last_tag, tag)) {
+ /* invalidate log.tag.<tag> cache */
+ for(i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
+ tag_cache[i].pinfo = NULL;
+ tag_cache[i].serial = -1;
+ tag_cache[i].c = '\0';
+ }
+ free(last_tag);
+ last_tag = NULL;
+ current_global_serial = -1;
+ }
+ if (!last_tag) {
+ last_tag = strdup(tag);
+ }
+ strcpy(key + sizeof(log_namespace) - 1, tag);
+
+ kp = key;
+ for(i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
+ if (current_local_serial != global_serial) {
+ refresh_cache(&tag_cache[i], kp);
+ }
+
+ if (tag_cache[i].c) {
+ c = tag_cache[i].c;
+ break;
+ }
+
+ kp = key + base_offset;
+ }
+ }
+
+ switch (toupper(c)) { /* if invalid, resort to global */
+ case 'V':
+ case 'D':
+ case 'I':
+ case 'W':
+ case 'E':
+ case 'F': /* Not officially supported */
+ case 'A':
+ case 'S':
+ break;
+ default:
+ /* clear '.' after log.tag */
+ key[sizeof(log_namespace) - 2] = '\0';
+
+ kp = key;
+ for(i = 0; i < (sizeof(global_cache) / sizeof(global_cache[0])); ++i) {
+ if (current_global_serial != global_serial) {
+ refresh_cache(&global_cache[i], kp);
+ }
+
+ if (global_cache[i].c) {
+ c = global_cache[i].c;
+ break;
+ }
+
+ kp = key + base_offset;
+ }
+ break;
+ }
+
+ global_serial = current_global_serial;
+
+ pthread_mutex_unlock(&lock);
+
+ switch (toupper(c)) {
+ case 'V': return ANDROID_LOG_VERBOSE;
+ case 'D': return ANDROID_LOG_DEBUG;
+ case 'I': return ANDROID_LOG_INFO;
+ case 'W': return ANDROID_LOG_WARN;
+ case 'E': return ANDROID_LOG_ERROR;
+ case 'F': /* FALLTHRU */ /* Not officially supported */
+ case 'A': return ANDROID_LOG_FATAL;
+ case 'S': return -1; /* ANDROID_LOG_SUPPRESS */
+ }
+ return def;
+}
+
+int __android_log_is_loggable(int prio, const char *tag, int def)
+{
+ int logLevel = __android_log_level(tag, def);
+ return logLevel >= 0 && prio >= logLevel;
+}
diff --git a/liblog/log_read.c b/liblog/log_read.c
new file mode 100644
index 0000000..9c4af30
--- /dev/null
+++ b/liblog/log_read.c
@@ -0,0 +1,900 @@
+/*
+** Copyright 2013-2014, 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 <inttypes.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stddef.h>
+#define NOMINMAX /* for windows to suppress definition of min in stdlib.h */
+#include <stdlib.h>
+#include <string.h>
+#include <sys/cdefs.h>
+#include <unistd.h>
+
+#include <cutils/list.h>
+#include <cutils/sockets.h>
+#include <log/log.h>
+#include <log/logger.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+
+/* branchless on many architectures. */
+#define min(x,y) ((y) ^ (((x) ^ (y)) & -((x) < (y))))
+
+#if (defined(USE_MINGW) || defined(HAVE_WINSOCK))
+#define WEAK static
+#else
+#define WEAK __attribute__((weak))
+#endif
+#ifndef __unused
+#define __unused __attribute__((unused))
+#endif
+
+/* Private copy of ../libcutils/socket_local_client.c prevent library loops */
+
+#ifdef HAVE_WINSOCK
+
+int WEAK socket_local_client(const char *name, int namespaceId, int type)
+{
+ errno = ENOSYS;
+ return -ENOSYS;
+}
+
+#else /* !HAVE_WINSOCK */
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/select.h>
+#include <sys/types.h>
+
+/* Private copy of ../libcutils/socket_local.h prevent library loops */
+#define FILESYSTEM_SOCKET_PREFIX "/tmp/"
+#define ANDROID_RESERVED_SOCKET_PREFIX "/dev/socket/"
+/* End of ../libcutils/socket_local.h */
+
+#define LISTEN_BACKLOG 4
+
+/* Documented in header file. */
+int WEAK socket_make_sockaddr_un(const char *name, int namespaceId,
+ struct sockaddr_un *p_addr, socklen_t *alen)
+{
+ memset (p_addr, 0, sizeof (*p_addr));
+ size_t namelen;
+
+ switch (namespaceId) {
+ case ANDROID_SOCKET_NAMESPACE_ABSTRACT:
+#if defined(__linux__)
+ namelen = strlen(name);
+
+ /* Test with length +1 for the *initial* '\0'. */
+ if ((namelen + 1) > sizeof(p_addr->sun_path)) {
+ goto error;
+ }
+
+ /*
+ * Note: The path in this case is *not* supposed to be
+ * '\0'-terminated. ("man 7 unix" for the gory details.)
+ */
+
+ p_addr->sun_path[0] = 0;
+ memcpy(p_addr->sun_path + 1, name, namelen);
+#else
+ /* this OS doesn't have the Linux abstract namespace */
+
+ namelen = strlen(name) + strlen(FILESYSTEM_SOCKET_PREFIX);
+ /* unix_path_max appears to be missing on linux */
+ if (namelen > sizeof(*p_addr)
+ - offsetof(struct sockaddr_un, sun_path) - 1) {
+ goto error;
+ }
+
+ strcpy(p_addr->sun_path, FILESYSTEM_SOCKET_PREFIX);
+ strcat(p_addr->sun_path, name);
+#endif
+ break;
+
+ case ANDROID_SOCKET_NAMESPACE_RESERVED:
+ namelen = strlen(name) + strlen(ANDROID_RESERVED_SOCKET_PREFIX);
+ /* unix_path_max appears to be missing on linux */
+ if (namelen > sizeof(*p_addr)
+ - offsetof(struct sockaddr_un, sun_path) - 1) {
+ goto error;
+ }
+
+ strcpy(p_addr->sun_path, ANDROID_RESERVED_SOCKET_PREFIX);
+ strcat(p_addr->sun_path, name);
+ break;
+
+ case ANDROID_SOCKET_NAMESPACE_FILESYSTEM:
+ namelen = strlen(name);
+ /* unix_path_max appears to be missing on linux */
+ if (namelen > sizeof(*p_addr)
+ - offsetof(struct sockaddr_un, sun_path) - 1) {
+ goto error;
+ }
+
+ strcpy(p_addr->sun_path, name);
+ break;
+
+ default:
+ /* invalid namespace id */
+ return -1;
+ }
+
+ p_addr->sun_family = AF_LOCAL;
+ *alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1;
+ return 0;
+error:
+ return -1;
+}
+
+/**
+ * connect to peer named "name" on fd
+ * returns same fd or -1 on error.
+ * fd is not closed on error. that's your job.
+ *
+ * Used by AndroidSocketImpl
+ */
+int WEAK socket_local_client_connect(int fd, const char *name, int namespaceId,
+ int type __unused)
+{
+ struct sockaddr_un addr;
+ socklen_t alen;
+ int err;
+
+ err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen);
+
+ if (err < 0) {
+ goto error;
+ }
+
+ if(connect(fd, (struct sockaddr *) &addr, alen) < 0) {
+ goto error;
+ }
+
+ return fd;
+
+error:
+ return -1;
+}
+
+/**
+ * connect to peer named "name"
+ * returns fd or -1 on error
+ */
+int WEAK socket_local_client(const char *name, int namespaceId, int type)
+{
+ int s;
+
+ s = socket(AF_LOCAL, type, 0);
+ if(s < 0) return -1;
+
+ if ( 0 > socket_local_client_connect(s, name, namespaceId, type)) {
+ close(s);
+ return -1;
+ }
+
+ return s;
+}
+
+#endif /* !HAVE_WINSOCK */
+/* End of ../libcutils/socket_local_client.c */
+
+#define logger_for_each(logger, logger_list) \
+ for (logger = node_to_item((logger_list)->node.next, struct logger, node); \
+ logger != node_to_item(&(logger_list)->node, struct logger, node); \
+ logger = node_to_item((logger)->node.next, struct logger, node))
+
+/* In the future, we would like to make this list extensible */
+static const char *LOG_NAME[LOG_ID_MAX] = {
+ [LOG_ID_MAIN] = "main",
+ [LOG_ID_RADIO] = "radio",
+ [LOG_ID_EVENTS] = "events",
+ [LOG_ID_SYSTEM] = "system",
+ [LOG_ID_CRASH] = "crash",
+ [LOG_ID_KERNEL] = "kernel",
+};
+
+const char *android_log_id_to_name(log_id_t log_id)
+{
+ if (log_id >= LOG_ID_MAX) {
+ log_id = LOG_ID_MAIN;
+ }
+ return LOG_NAME[log_id];
+}
+
+log_id_t android_name_to_log_id(const char *logName)
+{
+ const char *b;
+ int ret;
+
+ if (!logName) {
+ return -1; /* NB: log_id_t is unsigned */
+ }
+ b = strrchr(logName, '/');
+ if (!b) {
+ b = logName;
+ } else {
+ ++b;
+ }
+
+ for(ret = LOG_ID_MIN; ret < LOG_ID_MAX; ++ret) {
+ const char *l = LOG_NAME[ret];
+ if (l && !strcmp(b, l)) {
+ return ret;
+ }
+ }
+ return -1; /* should never happen */
+}
+
+struct logger_list {
+ struct listnode node;
+ int mode;
+ unsigned int tail;
+ log_time start;
+ pid_t pid;
+ int sock;
+};
+
+struct logger {
+ struct listnode node;
+ struct logger_list *top;
+ log_id_t id;
+};
+
+/* android_logger_alloc unimplemented, no use case */
+/* android_logger_free not exported */
+static void android_logger_free(struct logger *logger)
+{
+ if (!logger) {
+ return;
+ }
+
+ list_remove(&logger->node);
+
+ free(logger);
+}
+
+/* android_logger_alloc unimplemented, no use case */
+
+/* method for getting the associated sublog id */
+log_id_t android_logger_get_id(struct logger *logger)
+{
+ return logger->id;
+}
+
+/* worker for sending the command to the logger */
+static ssize_t send_log_msg(struct logger *logger,
+ const char *msg, char *buf, size_t buf_size)
+{
+ ssize_t ret;
+ size_t len;
+ char *cp;
+ int errno_save = 0;
+ int sock = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_RESERVED,
+ SOCK_STREAM);
+ if (sock < 0) {
+ return sock;
+ }
+
+ if (msg) {
+ snprintf(buf, buf_size, msg, logger ? logger->id : (unsigned) -1);
+ }
+
+ len = strlen(buf) + 1;
+ ret = TEMP_FAILURE_RETRY(write(sock, buf, len));
+ if (ret <= 0) {
+ goto done;
+ }
+
+ len = buf_size;
+ cp = buf;
+ while ((ret = TEMP_FAILURE_RETRY(read(sock, cp, len))) > 0) {
+ struct pollfd p;
+
+ if (((size_t)ret == len) || (buf_size < PAGE_SIZE)) {
+ break;
+ }
+
+ len -= ret;
+ cp += ret;
+
+ memset(&p, 0, sizeof(p));
+ p.fd = sock;
+ p.events = POLLIN;
+
+ /* Give other side 20ms to refill pipe */
+ ret = TEMP_FAILURE_RETRY(poll(&p, 1, 20));
+
+ if (ret <= 0) {
+ break;
+ }
+
+ if (!(p.revents & POLLIN)) {
+ ret = 0;
+ break;
+ }
+ }
+
+ if (ret >= 0) {
+ ret += buf_size - len;
+ }
+
+done:
+ if ((ret == -1) && errno) {
+ errno_save = errno;
+ }
+ close(sock);
+ if (errno_save) {
+ errno = errno_save;
+ }
+ return ret;
+}
+
+static int check_log_success(char *buf, ssize_t ret)
+{
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (strncmp(buf, "success", 7)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Determine the credentials of the caller */
+static bool uid_has_log_permission(uid_t uid)
+{
+ return (uid == AID_SYSTEM) || (uid == AID_LOG) || (uid == AID_ROOT);
+}
+
+static uid_t get_best_effective_uid()
+{
+ uid_t euid;
+ uid_t uid;
+ gid_t gid;
+ ssize_t i;
+ static uid_t last_uid = (uid_t) -1;
+
+ if (last_uid != (uid_t) -1) {
+ return last_uid;
+ }
+ uid = getuid();
+ if (uid_has_log_permission(uid)) {
+ return last_uid = uid;
+ }
+ euid = geteuid();
+ if (uid_has_log_permission(euid)) {
+ return last_uid = euid;
+ }
+ gid = getgid();
+ if (uid_has_log_permission(gid)) {
+ return last_uid = gid;
+ }
+ gid = getegid();
+ if (uid_has_log_permission(gid)) {
+ return last_uid = gid;
+ }
+ i = getgroups((size_t) 0, NULL);
+ if (i > 0) {
+ gid_t list[i];
+
+ getgroups(i, list);
+ while (--i >= 0) {
+ if (uid_has_log_permission(list[i])) {
+ return last_uid = list[i];
+ }
+ }
+ }
+ return last_uid = uid;
+}
+
+int android_logger_clear(struct logger *logger)
+{
+ char buf[512];
+
+ if (logger->top->mode & ANDROID_LOG_PSTORE) {
+ if (uid_has_log_permission(get_best_effective_uid())) {
+ return unlink("/sys/fs/pstore/pmsg-ramoops-0");
+ }
+ errno = EPERM;
+ return -1;
+ }
+ return check_log_success(buf,
+ send_log_msg(logger, "clear %d", buf, sizeof(buf)));
+}
+
+/* returns the total size of the log's ring buffer */
+long android_logger_get_log_size(struct logger *logger)
+{
+ char buf[512];
+
+ ssize_t ret = send_log_msg(logger, "getLogSize %d", buf, sizeof(buf));
+ if (ret < 0) {
+ return ret;
+ }
+
+ if ((buf[0] < '0') || ('9' < buf[0])) {
+ return -1;
+ }
+
+ return atol(buf);
+}
+
+int android_logger_set_log_size(struct logger *logger, unsigned long size)
+{
+ char buf[512];
+
+ snprintf(buf, sizeof(buf), "setLogSize %d %lu",
+ logger ? logger->id : (unsigned) -1, size);
+
+ return check_log_success(buf, send_log_msg(NULL, NULL, buf, sizeof(buf)));
+}
+
+/*
+ * returns the readable size of the log's ring buffer (that is, amount of the
+ * log consumed)
+ */
+long android_logger_get_log_readable_size(struct logger *logger)
+{
+ char buf[512];
+
+ ssize_t ret = send_log_msg(logger, "getLogSizeUsed %d", buf, sizeof(buf));
+ if (ret < 0) {
+ return ret;
+ }
+
+ if ((buf[0] < '0') || ('9' < buf[0])) {
+ return -1;
+ }
+
+ return atol(buf);
+}
+
+/*
+ * returns the logger version
+ */
+int android_logger_get_log_version(struct logger *logger __unused)
+{
+ return 3;
+}
+
+/*
+ * returns statistics
+ */
+ssize_t android_logger_get_statistics(struct logger_list *logger_list,
+ char *buf, size_t len)
+{
+ struct logger *logger;
+ char *cp = buf;
+ size_t remaining = len;
+ size_t n;
+
+ n = snprintf(cp, remaining, "getStatistics");
+ n = min(n, remaining);
+ remaining -= n;
+ cp += n;
+
+ logger_for_each(logger, logger_list) {
+ n = snprintf(cp, remaining, " %d", logger->id);
+ n = min(n, remaining);
+ remaining -= n;
+ cp += n;
+ }
+ return send_log_msg(NULL, NULL, buf, len);
+}
+
+ssize_t android_logger_get_prune_list(struct logger_list *logger_list __unused,
+ char *buf, size_t len)
+{
+ return send_log_msg(NULL, "getPruneList", buf, len);
+}
+
+int android_logger_set_prune_list(struct logger_list *logger_list __unused,
+ char *buf, size_t len)
+{
+ const char cmd[] = "setPruneList ";
+ const size_t cmdlen = sizeof(cmd) - 1;
+
+ if (strlen(buf) > (len - cmdlen)) {
+ return -ENOMEM; /* KISS */
+ }
+ memmove(buf + cmdlen, buf, len - cmdlen);
+ buf[len - 1] = '\0';
+ memcpy(buf, cmd, cmdlen);
+
+ return check_log_success(buf, send_log_msg(NULL, NULL, buf, len));
+}
+
+struct logger_list *android_logger_list_alloc(int mode,
+ unsigned int tail,
+ pid_t pid)
+{
+ struct logger_list *logger_list;
+
+ logger_list = calloc(1, sizeof(*logger_list));
+ if (!logger_list) {
+ return NULL;
+ }
+
+ list_init(&logger_list->node);
+ logger_list->mode = mode;
+ logger_list->start.tv_sec = 0;
+ logger_list->start.tv_nsec = 0;
+ logger_list->tail = tail;
+ logger_list->pid = pid;
+ logger_list->sock = -1;
+
+ return logger_list;
+}
+
+struct logger_list *android_logger_list_alloc_time(int mode,
+ log_time start,
+ pid_t pid)
+{
+ struct logger_list *logger_list;
+
+ logger_list = calloc(1, sizeof(*logger_list));
+ if (!logger_list) {
+ return NULL;
+ }
+
+ list_init(&logger_list->node);
+ logger_list->mode = mode;
+ logger_list->start = start;
+ logger_list->tail = 0;
+ logger_list->pid = pid;
+ logger_list->sock = -1;
+
+ return logger_list;
+}
+
+/* android_logger_list_register unimplemented, no use case */
+/* android_logger_list_unregister unimplemented, no use case */
+
+/* Open the named log and add it to the logger list */
+struct logger *android_logger_open(struct logger_list *logger_list,
+ log_id_t id)
+{
+ struct logger *logger;
+
+ if (!logger_list || (id >= LOG_ID_MAX)) {
+ goto err;
+ }
+
+ logger_for_each(logger, logger_list) {
+ if (logger->id == id) {
+ goto ok;
+ }
+ }
+
+ logger = calloc(1, sizeof(*logger));
+ if (!logger) {
+ goto err;
+ }
+
+ logger->id = id;
+ list_add_tail(&logger_list->node, &logger->node);
+ logger->top = logger_list;
+ goto ok;
+
+err:
+ logger = NULL;
+ok:
+ return logger;
+}
+
+/* Open the single named log and make it part of a new logger list */
+struct logger_list *android_logger_list_open(log_id_t id,
+ int mode,
+ unsigned int tail,
+ pid_t pid)
+{
+ struct logger_list *logger_list = android_logger_list_alloc(mode, tail, pid);
+ if (!logger_list) {
+ return NULL;
+ }
+
+ if (!android_logger_open(logger_list, id)) {
+ android_logger_list_free(logger_list);
+ return NULL;
+ }
+
+ return logger_list;
+}
+
+static int android_logger_list_read_pstore(struct logger_list *logger_list,
+ struct log_msg *log_msg)
+{
+ ssize_t ret;
+ off_t current, next;
+ uid_t uid;
+ struct logger *logger;
+ struct __attribute__((__packed__)) {
+ android_pmsg_log_header_t p;
+ android_log_header_t l;
+ } buf;
+ static uint8_t preread_count;
+
+ memset(log_msg, 0, sizeof(*log_msg));
+
+ if (logger_list->sock < 0) {
+ int fd = open("/sys/fs/pstore/pmsg-ramoops-0", O_RDONLY);
+
+ if (fd < 0) {
+ return -errno;
+ }
+ logger_list->sock = fd;
+ preread_count = 0;
+ }
+
+ ret = 0;
+ while(1) {
+ if (preread_count < sizeof(buf)) {
+ ret = TEMP_FAILURE_RETRY(read(logger_list->sock,
+ &buf.p.magic + preread_count,
+ sizeof(buf) - preread_count));
+ if (ret < 0) {
+ return -errno;
+ }
+ preread_count += ret;
+ }
+ if (preread_count != sizeof(buf)) {
+ return preread_count ? -EIO : -EAGAIN;
+ }
+ if ((buf.p.magic != LOGGER_MAGIC)
+ || (buf.p.len <= sizeof(buf))
+ || (buf.p.len > (sizeof(buf) + LOGGER_ENTRY_MAX_PAYLOAD))
+ || (buf.l.id >= LOG_ID_MAX)
+ || (buf.l.realtime.tv_nsec >= NS_PER_SEC)) {
+ do {
+ memmove(&buf.p.magic, &buf.p.magic + 1, --preread_count);
+ } while (preread_count && (buf.p.magic != LOGGER_MAGIC));
+ continue;
+ }
+ preread_count = 0;
+
+ logger_for_each(logger, logger_list) {
+ if (buf.l.id != logger->id) {
+ continue;
+ }
+
+ if ((logger_list->start.tv_sec || logger_list->start.tv_nsec)
+ && ((logger_list->start.tv_sec > buf.l.realtime.tv_sec)
+ || ((logger_list->start.tv_sec == buf.l.realtime.tv_sec)
+ && (logger_list->start.tv_nsec > buf.l.realtime.tv_nsec)))) {
+ break;
+ }
+
+ if (logger_list->pid && (logger_list->pid != buf.p.pid)) {
+ break;
+ }
+
+ uid = get_best_effective_uid();
+ if (!uid_has_log_permission(uid) && (uid != buf.p.uid)) {
+ break;
+ }
+
+ ret = TEMP_FAILURE_RETRY(read(logger_list->sock,
+ log_msg->entry_v3.msg,
+ buf.p.len - sizeof(buf)));
+ if (ret < 0) {
+ return -errno;
+ }
+ if (ret != (ssize_t)(buf.p.len - sizeof(buf))) {
+ return -EIO;
+ }
+
+ log_msg->entry_v3.len = buf.p.len - sizeof(buf);
+ log_msg->entry_v3.hdr_size = sizeof(log_msg->entry_v3);
+ log_msg->entry_v3.pid = buf.p.pid;
+ log_msg->entry_v3.tid = buf.l.tid;
+ log_msg->entry_v3.sec = buf.l.realtime.tv_sec;
+ log_msg->entry_v3.nsec = buf.l.realtime.tv_nsec;
+ log_msg->entry_v3.lid = buf.l.id;
+
+ return ret;
+ }
+
+ current = TEMP_FAILURE_RETRY(lseek(logger_list->sock,
+ (off_t)0, SEEK_CUR));
+ if (current < 0) {
+ return -errno;
+ }
+ next = TEMP_FAILURE_RETRY(lseek(logger_list->sock,
+ (off_t)(buf.p.len - sizeof(buf)),
+ SEEK_CUR));
+ if (next < 0) {
+ return -errno;
+ }
+ if ((next - current) != (ssize_t)(buf.p.len - sizeof(buf))) {
+ return -EIO;
+ }
+ }
+}
+
+static void caught_signal(int signum __unused)
+{
+}
+
+/* Read from the selected logs */
+int android_logger_list_read(struct logger_list *logger_list,
+ struct log_msg *log_msg)
+{
+ int ret, e;
+ struct logger *logger;
+ struct sigaction ignore;
+ struct sigaction old_sigaction;
+ unsigned int old_alarm = 0;
+
+ if (!logger_list) {
+ return -EINVAL;
+ }
+
+ if (logger_list->mode & ANDROID_LOG_PSTORE) {
+ return android_logger_list_read_pstore(logger_list, log_msg);
+ }
+
+ if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
+ memset(&ignore, 0, sizeof(ignore));
+ ignore.sa_handler = caught_signal;
+ sigemptyset(&ignore.sa_mask);
+ }
+
+ if (logger_list->sock < 0) {
+ char buffer[256], *cp, c;
+
+ int sock = socket_local_client("logdr",
+ ANDROID_SOCKET_NAMESPACE_RESERVED,
+ SOCK_SEQPACKET);
+ if (sock < 0) {
+ if ((sock == -1) && errno) {
+ return -errno;
+ }
+ return sock;
+ }
+
+ strcpy(buffer,
+ (logger_list->mode & ANDROID_LOG_NONBLOCK) ? "dumpAndClose" : "stream");
+ cp = buffer + strlen(buffer);
+
+ strcpy(cp, " lids");
+ cp += 5;
+ c = '=';
+ int remaining = sizeof(buffer) - (cp - buffer);
+ logger_for_each(logger, logger_list) {
+ ret = snprintf(cp, remaining, "%c%u", c, logger->id);
+ ret = min(ret, remaining);
+ remaining -= ret;
+ cp += ret;
+ c = ',';
+ }
+
+ if (logger_list->tail) {
+ ret = snprintf(cp, remaining, " tail=%u", logger_list->tail);
+ ret = min(ret, remaining);
+ remaining -= ret;
+ cp += ret;
+ }
+
+ if (logger_list->start.tv_sec || logger_list->start.tv_nsec) {
+ ret = snprintf(cp, remaining, " start=%" PRIu32 ".%09" PRIu32,
+ logger_list->start.tv_sec,
+ logger_list->start.tv_nsec);
+ ret = min(ret, remaining);
+ remaining -= ret;
+ cp += ret;
+ }
+
+ if (logger_list->pid) {
+ ret = snprintf(cp, remaining, " pid=%u", logger_list->pid);
+ ret = min(ret, remaining);
+ remaining -= ret;
+ cp += ret;
+ }
+
+ if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
+ /* Deal with an unresponsive logd */
+ sigaction(SIGALRM, &ignore, &old_sigaction);
+ old_alarm = alarm(30);
+ }
+ ret = write(sock, buffer, cp - buffer);
+ e = errno;
+ if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
+ if (e == EINTR) {
+ e = ETIMEDOUT;
+ }
+ alarm(old_alarm);
+ sigaction(SIGALRM, &old_sigaction, NULL);
+ }
+
+ if (ret <= 0) {
+ close(sock);
+ if ((ret == -1) && e) {
+ return -e;
+ }
+ if (ret == 0) {
+ return -EIO;
+ }
+ return ret;
+ }
+
+ logger_list->sock = sock;
+ }
+
+ ret = 0;
+ while(1) {
+ memset(log_msg, 0, sizeof(*log_msg));
+
+ if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
+ /* particularily useful if tombstone is reporting for logd */
+ sigaction(SIGALRM, &ignore, &old_sigaction);
+ old_alarm = alarm(30);
+ }
+ /* NOTE: SOCK_SEQPACKET guarantees we read exactly one full entry */
+ ret = recv(logger_list->sock, log_msg, LOGGER_ENTRY_MAX_LEN, 0);
+ e = errno;
+ if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
+ if ((ret == 0) || (e == EINTR)) {
+ e = EAGAIN;
+ ret = -1;
+ }
+ alarm(old_alarm);
+ sigaction(SIGALRM, &old_sigaction, NULL);
+ }
+
+ if (ret <= 0) {
+ if ((ret == -1) && e) {
+ return -e;
+ }
+ return ret;
+ }
+
+ logger_for_each(logger, logger_list) {
+ if (log_msg->entry.lid == logger->id) {
+ return ret;
+ }
+ }
+ }
+ /* NOTREACH */
+ return ret;
+}
+
+/* Close all the logs */
+void android_logger_list_free(struct logger_list *logger_list)
+{
+ if (logger_list == NULL) {
+ return;
+ }
+
+ while (!list_empty(&logger_list->node)) {
+ struct listnode *node = list_head(&logger_list->node);
+ struct logger *logger = node_to_item(node, struct logger, node);
+ android_logger_free(logger);
+ }
+
+ if (logger_list->sock >= 0) {
+ close (logger_list->sock);
+ }
+
+ free(logger_list);
+}
diff --git a/liblog/log_read_kern.c b/liblog/log_read_kern.c
new file mode 100644
index 0000000..69b405c
--- /dev/null
+++ b/liblog/log_read_kern.c
@@ -0,0 +1,742 @@
+/*
+** Copyright 2013-2014, 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 _GNU_SOURCE /* asprintf for x86 host */
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/cdefs.h>
+#include <sys/ioctl.h>
+
+#include <cutils/list.h>
+#include <log/log.h>
+#include <log/logger.h>
+
+#define __LOGGERIO 0xAE
+
+#define LOGGER_GET_LOG_BUF_SIZE _IO(__LOGGERIO, 1) /* size of log */
+#define LOGGER_GET_LOG_LEN _IO(__LOGGERIO, 2) /* used log len */
+#define LOGGER_GET_NEXT_ENTRY_LEN _IO(__LOGGERIO, 3) /* next entry len */
+#define LOGGER_FLUSH_LOG _IO(__LOGGERIO, 4) /* flush log */
+#define LOGGER_GET_VERSION _IO(__LOGGERIO, 5) /* abi version */
+#define LOGGER_SET_VERSION _IO(__LOGGERIO, 6) /* abi version */
+
+typedef char bool;
+#define false (const bool)0
+#define true (const bool)1
+
+#define LOG_FILE_DIR "/dev/log/"
+
+/* timeout in milliseconds */
+#define LOG_TIMEOUT_FLUSH 5
+#define LOG_TIMEOUT_NEVER -1
+
+#define logger_for_each(logger, logger_list) \
+ for (logger = node_to_item((logger_list)->node.next, struct logger, node); \
+ logger != node_to_item(&(logger_list)->node, struct logger, node); \
+ logger = node_to_item((logger)->node.next, struct logger, node))
+
+#ifndef __unused
+#define __unused __attribute__((unused))
+#endif
+
+/* In the future, we would like to make this list extensible */
+static const char *LOG_NAME[LOG_ID_MAX] = {
+ [LOG_ID_MAIN] = "main",
+ [LOG_ID_RADIO] = "radio",
+ [LOG_ID_EVENTS] = "events",
+ [LOG_ID_SYSTEM] = "system",
+ [LOG_ID_CRASH] = "crash",
+ [LOG_ID_KERNEL] = "kernel",
+};
+
+const char *android_log_id_to_name(log_id_t log_id)
+{
+ if (log_id >= LOG_ID_MAX) {
+ log_id = LOG_ID_MAIN;
+ }
+ return LOG_NAME[log_id];
+}
+
+static int accessmode(int mode)
+{
+ if ((mode & ANDROID_LOG_ACCMODE) == ANDROID_LOG_WRONLY) {
+ return W_OK;
+ }
+ if ((mode & ANDROID_LOG_ACCMODE) == ANDROID_LOG_RDWR) {
+ return R_OK | W_OK;
+ }
+ return R_OK;
+}
+
+/* repeated fragment */
+static int check_allocate_accessible(char **n, const char *b, int mode)
+{
+ *n = NULL;
+
+ if (!b) {
+ return -EINVAL;
+ }
+
+ asprintf(n, LOG_FILE_DIR "%s", b);
+ if (!*n) {
+ return -1;
+ }
+
+ return access(*n, accessmode(mode));
+}
+
+log_id_t android_name_to_log_id(const char *logName)
+{
+ const char *b;
+ char *n;
+ int ret;
+
+ if (!logName) {
+ return -1; /* NB: log_id_t is unsigned */
+ }
+ b = strrchr(logName, '/');
+ if (!b) {
+ b = logName;
+ } else {
+ ++b;
+ }
+
+ ret = check_allocate_accessible(&n, b, ANDROID_LOG_RDONLY);
+ free(n);
+ if (ret) {
+ return ret;
+ }
+
+ for(ret = LOG_ID_MIN; ret < LOG_ID_MAX; ++ret) {
+ const char *l = LOG_NAME[ret];
+ if (l && !strcmp(b, l)) {
+ return ret;
+ }
+ }
+ return -1; /* should never happen */
+}
+
+struct logger_list {
+ struct listnode node;
+ int mode;
+ unsigned int tail;
+ pid_t pid;
+ unsigned int queued_lines;
+ int timeout_ms;
+ int error;
+ bool flush;
+ bool valid_entry; /* valiant(?) effort to deal with memory starvation */
+ struct log_msg entry;
+};
+
+struct log_list {
+ struct listnode node;
+ struct log_msg entry; /* Truncated to event->len() + 1 to save space */
+};
+
+struct logger {
+ struct listnode node;
+ struct logger_list *top;
+ int fd;
+ log_id_t id;
+ short *revents;
+ struct listnode log_list;
+};
+
+/* android_logger_alloc unimplemented, no use case */
+/* android_logger_free not exported */
+static void android_logger_free(struct logger *logger)
+{
+ if (!logger) {
+ return;
+ }
+
+ while (!list_empty(&logger->log_list)) {
+ struct log_list *entry = node_to_item(
+ list_head(&logger->log_list), struct log_list, node);
+ list_remove(&entry->node);
+ free(entry);
+ if (logger->top->queued_lines) {
+ logger->top->queued_lines--;
+ }
+ }
+
+ if (logger->fd >= 0) {
+ close(logger->fd);
+ }
+
+ list_remove(&logger->node);
+
+ free(logger);
+}
+
+log_id_t android_logger_get_id(struct logger *logger)
+{
+ return logger->id;
+}
+
+/* worker for sending the command to the logger */
+static int logger_ioctl(struct logger *logger, int cmd, int mode)
+{
+ char *n;
+ int f, ret;
+
+ if (!logger || !logger->top) {
+ return -EFAULT;
+ }
+
+ if (((mode & ANDROID_LOG_ACCMODE) == ANDROID_LOG_RDWR)
+ || (((mode ^ logger->top->mode) & ANDROID_LOG_ACCMODE) == 0)) {
+ return ioctl(logger->fd, cmd);
+ }
+
+ /* We go here if android_logger_list_open got mode wrong for this ioctl */
+ ret = check_allocate_accessible(&n, android_log_id_to_name(logger->id), mode);
+ if (ret) {
+ free(n);
+ return ret;
+ }
+
+ f = open(n, mode);
+ free(n);
+ if (f < 0) {
+ return f;
+ }
+
+ ret = ioctl(f, cmd);
+ close (f);
+
+ return ret;
+}
+
+int android_logger_clear(struct logger *logger)
+{
+ return logger_ioctl(logger, LOGGER_FLUSH_LOG, ANDROID_LOG_WRONLY);
+}
+
+/* returns the total size of the log's ring buffer */
+long android_logger_get_log_size(struct logger *logger)
+{
+ return logger_ioctl(logger, LOGGER_GET_LOG_BUF_SIZE, ANDROID_LOG_RDWR);
+}
+
+int android_logger_set_log_size(struct logger *logger __unused,
+ unsigned long size __unused)
+{
+ return -ENOTSUP;
+}
+
+/*
+ * returns the readable size of the log's ring buffer (that is, amount of the
+ * log consumed)
+ */
+long android_logger_get_log_readable_size(struct logger *logger)
+{
+ return logger_ioctl(logger, LOGGER_GET_LOG_LEN, ANDROID_LOG_RDONLY);
+}
+
+/*
+ * returns the logger version
+ */
+int android_logger_get_log_version(struct logger *logger)
+{
+ int ret = logger_ioctl(logger, LOGGER_GET_VERSION, ANDROID_LOG_RDWR);
+ return (ret < 0) ? 1 : ret;
+}
+
+/*
+ * returns statistics
+ */
+static const char unsupported[] = "18\nNot Supported\n\f";
+
+ssize_t android_logger_get_statistics(struct logger_list *logger_list __unused,
+ char *buf, size_t len)
+{
+ strncpy(buf, unsupported, len);
+ return -ENOTSUP;
+}
+
+ssize_t android_logger_get_prune_list(struct logger_list *logger_list __unused,
+ char *buf, size_t len)
+{
+ strncpy(buf, unsupported, len);
+ return -ENOTSUP;
+}
+
+int android_logger_set_prune_list(struct logger_list *logger_list __unused,
+ char *buf, size_t len)
+{
+ static const char unsupported_error[] = "Unsupported";
+ strncpy(buf, unsupported, len);
+ return -ENOTSUP;
+}
+
+struct logger_list *android_logger_list_alloc(int mode,
+ unsigned int tail,
+ pid_t pid)
+{
+ struct logger_list *logger_list;
+
+ logger_list = calloc(1, sizeof(*logger_list));
+ if (!logger_list) {
+ return NULL;
+ }
+ list_init(&logger_list->node);
+ logger_list->mode = mode;
+ logger_list->tail = tail;
+ logger_list->pid = pid;
+ return logger_list;
+}
+
+struct logger_list *android_logger_list_alloc_time(int mode,
+ log_time start __unused,
+ pid_t pid)
+{
+ return android_logger_list_alloc(mode, 0, pid);
+}
+
+/* android_logger_list_register unimplemented, no use case */
+/* android_logger_list_unregister unimplemented, no use case */
+
+/* Open the named log and add it to the logger list */
+struct logger *android_logger_open(struct logger_list *logger_list,
+ log_id_t id)
+{
+ struct listnode *node;
+ struct logger *logger;
+ char *n;
+
+ if (!logger_list || (id >= LOG_ID_MAX)) {
+ goto err;
+ }
+
+ logger_for_each(logger, logger_list) {
+ if (logger->id == id) {
+ goto ok;
+ }
+ }
+
+ logger = calloc(1, sizeof(*logger));
+ if (!logger) {
+ goto err;
+ }
+
+ if (check_allocate_accessible(&n, android_log_id_to_name(id),
+ logger_list->mode)) {
+ goto err_name;
+ }
+
+ logger->fd = open(n, logger_list->mode & (ANDROID_LOG_ACCMODE | ANDROID_LOG_NONBLOCK));
+ if (logger->fd < 0) {
+ goto err_name;
+ }
+
+ free(n);
+ logger->id = id;
+ list_init(&logger->log_list);
+ list_add_tail(&logger_list->node, &logger->node);
+ logger->top = logger_list;
+ logger_list->timeout_ms = LOG_TIMEOUT_FLUSH;
+ goto ok;
+
+err_name:
+ free(n);
+err_logger:
+ free(logger);
+err:
+ logger = NULL;
+ok:
+ return logger;
+}
+
+/* Open the single named log and make it part of a new logger list */
+struct logger_list *android_logger_list_open(log_id_t id,
+ int mode,
+ unsigned int tail,
+ pid_t pid)
+{
+ struct logger_list *logger_list = android_logger_list_alloc(mode, tail, pid);
+ if (!logger_list) {
+ return NULL;
+ }
+
+ if (!android_logger_open(logger_list, id)) {
+ android_logger_list_free(logger_list);
+ return NULL;
+ }
+
+ return logger_list;
+}
+
+/* prevent memory starvation when backfilling */
+static unsigned int queue_threshold(struct logger_list *logger_list)
+{
+ return (logger_list->tail < 64) ? 64 : logger_list->tail;
+}
+
+static bool low_queue(struct listnode *node)
+{
+ /* low is considered less than 2 */
+ return list_head(node) == list_tail(node);
+}
+
+/* Flush queues in sequential order, one at a time */
+static int android_logger_list_flush(struct logger_list *logger_list,
+ struct log_msg *log_msg)
+{
+ int ret = 0;
+ struct log_list *firstentry = NULL;
+
+ while ((ret == 0)
+ && (logger_list->flush
+ || (logger_list->queued_lines > logger_list->tail))) {
+ struct logger *logger;
+
+ /* Merge sort */
+ bool at_least_one_is_low = false;
+ struct logger *firstlogger = NULL;
+ firstentry = NULL;
+
+ logger_for_each(logger, logger_list) {
+ struct listnode *node;
+ struct log_list *oldest = NULL;
+
+ /* kernel logger channels not necessarily time-sort order */
+ list_for_each(node, &logger->log_list) {
+ struct log_list *entry = node_to_item(node,
+ struct log_list, node);
+ if (!oldest
+ || (entry->entry.entry.sec < oldest->entry.entry.sec)
+ || ((entry->entry.entry.sec == oldest->entry.entry.sec)
+ && (entry->entry.entry.nsec < oldest->entry.entry.nsec))) {
+ oldest = entry;
+ }
+ }
+
+ if (!oldest) {
+ at_least_one_is_low = true;
+ continue;
+ } else if (low_queue(&logger->log_list)) {
+ at_least_one_is_low = true;
+ }
+
+ if (!firstentry
+ || (oldest->entry.entry.sec < firstentry->entry.entry.sec)
+ || ((oldest->entry.entry.sec == firstentry->entry.entry.sec)
+ && (oldest->entry.entry.nsec < firstentry->entry.entry.nsec))) {
+ firstentry = oldest;
+ firstlogger = logger;
+ }
+ }
+
+ if (!firstentry) {
+ break;
+ }
+
+ /* when trimming list, tries to keep one entry behind in each bucket */
+ if (!logger_list->flush
+ && at_least_one_is_low
+ && (logger_list->queued_lines < queue_threshold(logger_list))) {
+ break;
+ }
+
+ /* within tail?, send! */
+ if ((logger_list->tail == 0)
+ || (logger_list->queued_lines <= logger_list->tail)) {
+ int diff;
+ ret = firstentry->entry.entry.hdr_size;
+ if (!ret) {
+ ret = sizeof(firstentry->entry.entry_v1);
+ }
+
+ /* Promote entry to v3 format */
+ memcpy(log_msg->buf, firstentry->entry.buf, ret);
+ diff = sizeof(firstentry->entry.entry_v3) - ret;
+ if (diff < 0) {
+ diff = 0;
+ } else if (diff > 0) {
+ memset(log_msg->buf + ret, 0, diff);
+ }
+ memcpy(log_msg->buf + ret + diff, firstentry->entry.buf + ret,
+ firstentry->entry.entry.len + 1);
+ ret += diff;
+ log_msg->entry.hdr_size = ret;
+ log_msg->entry.lid = firstlogger->id;
+
+ ret += firstentry->entry.entry.len;
+ }
+
+ /* next entry */
+ list_remove(&firstentry->node);
+ free(firstentry);
+ if (logger_list->queued_lines) {
+ logger_list->queued_lines--;
+ }
+ }
+
+ /* Flushed the list, no longer in tail mode for continuing content */
+ if (logger_list->flush && !firstentry) {
+ logger_list->tail = 0;
+ }
+ return ret;
+}
+
+/* Read from the selected logs */
+int android_logger_list_read(struct logger_list *logger_list,
+ struct log_msg *log_msg)
+{
+ struct logger *logger;
+ nfds_t nfds;
+ struct pollfd *p, *pollfds = NULL;
+ int error = 0, ret = 0;
+
+ memset(log_msg, 0, sizeof(struct log_msg));
+
+ if (!logger_list) {
+ return -ENODEV;
+ }
+
+ if (!(accessmode(logger_list->mode) & R_OK)) {
+ logger_list->error = EPERM;
+ goto done;
+ }
+
+ nfds = 0;
+ logger_for_each(logger, logger_list) {
+ ++nfds;
+ }
+ if (nfds <= 0) {
+ error = ENODEV;
+ goto done;
+ }
+
+ /* Do we have anything to offer from the buffer or state? */
+ if (logger_list->valid_entry) { /* implies we are also in a flush state */
+ goto flush;
+ }
+
+ ret = android_logger_list_flush(logger_list, log_msg);
+ if (ret) {
+ goto done;
+ }
+
+ if (logger_list->error) { /* implies we are also in a flush state */
+ goto done;
+ }
+
+ /* Lets start grinding on metal */
+ pollfds = calloc(nfds, sizeof(struct pollfd));
+ if (!pollfds) {
+ error = ENOMEM;
+ goto flush;
+ }
+
+ p = pollfds;
+ logger_for_each(logger, logger_list) {
+ p->fd = logger->fd;
+ p->events = POLLIN;
+ logger->revents = &p->revents;
+ ++p;
+ }
+
+ while (!ret && !error) {
+ int result;
+
+ /* If we oversleep it's ok, i.e. ignore EINTR. */
+ result = TEMP_FAILURE_RETRY(
+ poll(pollfds, nfds, logger_list->timeout_ms));
+
+ if (result <= 0) {
+ if (result) {
+ error = errno;
+ } else if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
+ error = EAGAIN;
+ } else {
+ logger_list->timeout_ms = LOG_TIMEOUT_NEVER;
+ }
+
+ logger_list->flush = true;
+ goto try_flush;
+ }
+
+ logger_list->timeout_ms = LOG_TIMEOUT_FLUSH;
+
+ /* Anti starvation */
+ if (!logger_list->flush
+ && (logger_list->queued_lines > (queue_threshold(logger_list) / 2))) {
+ /* Any queues with input pending that is low? */
+ bool starving = false;
+ logger_for_each(logger, logger_list) {
+ if ((*(logger->revents) & POLLIN)
+ && low_queue(&logger->log_list)) {
+ starving = true;
+ break;
+ }
+ }
+
+ /* pushback on any queues that are not low */
+ if (starving) {
+ logger_for_each(logger, logger_list) {
+ if ((*(logger->revents) & POLLIN)
+ && !low_queue(&logger->log_list)) {
+ *(logger->revents) &= ~POLLIN;
+ }
+ }
+ }
+ }
+
+ logger_for_each(logger, logger_list) {
+ unsigned int hdr_size;
+ struct log_list *entry;
+ int diff;
+
+ if (!(*(logger->revents) & POLLIN)) {
+ continue;
+ }
+
+ memset(logger_list->entry.buf, 0, sizeof(struct log_msg));
+ /* NOTE: driver guarantees we read exactly one full entry */
+ result = read(logger->fd, logger_list->entry.buf,
+ LOGGER_ENTRY_MAX_LEN);
+ if (result <= 0) {
+ if (!result) {
+ error = EIO;
+ } else if (errno != EINTR) {
+ error = errno;
+ }
+ continue;
+ }
+
+ if (logger_list->pid
+ && (logger_list->pid != logger_list->entry.entry.pid)) {
+ continue;
+ }
+
+ hdr_size = logger_list->entry.entry.hdr_size;
+ if (!hdr_size) {
+ hdr_size = sizeof(logger_list->entry.entry_v1);
+ }
+
+ if ((hdr_size > sizeof(struct log_msg))
+ || (logger_list->entry.entry.len
+ > sizeof(logger_list->entry.buf) - hdr_size)
+ || (logger_list->entry.entry.len != result - hdr_size)) {
+ error = EINVAL;
+ continue;
+ }
+
+ /* Promote entry to v3 format */
+ diff = sizeof(logger_list->entry.entry_v3) - hdr_size;
+ if (diff > 0) {
+ if (logger_list->entry.entry.len
+ > sizeof(logger_list->entry.buf) - hdr_size - diff) {
+ error = EINVAL;
+ continue;
+ }
+ result += diff;
+ memmove(logger_list->entry.buf + hdr_size + diff,
+ logger_list->entry.buf + hdr_size,
+ logger_list->entry.entry.len + 1);
+ memset(logger_list->entry.buf + hdr_size, 0, diff);
+ logger_list->entry.entry.hdr_size = hdr_size + diff;
+ }
+ logger_list->entry.entry.lid = logger->id;
+
+ /* speedup: If not tail, and only one list, send directly */
+ if (!logger_list->tail
+ && (list_head(&logger_list->node)
+ == list_tail(&logger_list->node))) {
+ ret = result;
+ memcpy(log_msg->buf, logger_list->entry.buf, result + 1);
+ break;
+ }
+
+ entry = malloc(sizeof(*entry) - sizeof(entry->entry) + result + 1);
+
+ if (!entry) {
+ logger_list->valid_entry = true;
+ error = ENOMEM;
+ break;
+ }
+
+ logger_list->queued_lines++;
+
+ memcpy(entry->entry.buf, logger_list->entry.buf, result);
+ entry->entry.buf[result] = '\0';
+ list_add_tail(&logger->log_list, &entry->node);
+ }
+
+ if (ret <= 0) {
+try_flush:
+ ret = android_logger_list_flush(logger_list, log_msg);
+ }
+ }
+
+ free(pollfds);
+
+flush:
+ if (error) {
+ logger_list->flush = true;
+ }
+
+ if (ret <= 0) {
+ ret = android_logger_list_flush(logger_list, log_msg);
+
+ if (!ret && logger_list->valid_entry) {
+ ret = logger_list->entry.entry.hdr_size;
+ if (!ret) {
+ ret = sizeof(logger_list->entry.entry_v1);
+ }
+ ret += logger_list->entry.entry.len;
+
+ memcpy(log_msg->buf, logger_list->entry.buf,
+ sizeof(struct log_msg));
+ logger_list->valid_entry = false;
+ }
+ }
+
+done:
+ if (logger_list->error) {
+ error = logger_list->error;
+ }
+ if (error) {
+ logger_list->error = error;
+ if (!ret) {
+ ret = -error;
+ }
+ }
+ return ret;
+}
+
+/* Close all the logs */
+void android_logger_list_free(struct logger_list *logger_list)
+{
+ if (logger_list == NULL) {
+ return;
+ }
+
+ while (!list_empty(&logger_list->node)) {
+ struct listnode *node = list_head(&logger_list->node);
+ struct logger *logger = node_to_item(node, struct logger, node);
+ android_logger_free(logger);
+ }
+
+ free(logger_list);
+}
diff --git a/liblog/log_time.cpp b/liblog/log_time.cpp
new file mode 100644
index 0000000..9d5ea0e
--- /dev/null
+++ b/liblog/log_time.cpp
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2014 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 <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/cdefs.h>
+
+#include <log/log_read.h>
+
+const char log_time::default_format[] = "%m-%d %H:%M:%S.%q";
+const timespec log_time::EPOCH = { 0, 0 };
+
+// Add %#q for fractional seconds to standard strptime function
+
+char *log_time::strptime(const char *s, const char *format) {
+ time_t now;
+#ifdef __linux__
+ *this = log_time(CLOCK_REALTIME);
+ now = tv_sec;
+#else
+ time(&now);
+ tv_sec = now;
+ tv_nsec = 0;
+#endif
+
+ struct tm *ptm;
+#if !defined(_WIN32)
+ struct tm tmBuf;
+ ptm = localtime_r(&now, &tmBuf);
+#else
+ ptm = localtime(&now);
+#endif
+
+ char fmt[strlen(format) + 1];
+ strcpy(fmt, format);
+
+ char *ret = const_cast<char *> (s);
+ char *cp;
+ for (char *f = cp = fmt; ; ++cp) {
+ if (!*cp) {
+ if (f != cp) {
+ ret = ::strptime(ret, f, ptm);
+ }
+ break;
+ }
+ if (*cp != '%') {
+ continue;
+ }
+ char *e = cp;
+ ++e;
+#if (defined(__BIONIC__))
+ if (*e == 's') {
+ *cp = '\0';
+ if (*f) {
+ ret = ::strptime(ret, f, ptm);
+ if (!ret) {
+ break;
+ }
+ }
+ tv_sec = 0;
+ while (isdigit(*ret)) {
+ tv_sec = tv_sec * 10 + *ret - '0';
+ ++ret;
+ }
+ now = tv_sec;
+#if !defined(_WIN32)
+ ptm = localtime_r(&now, &tmBuf);
+#else
+ ptm = localtime(&now);
+#endif
+ } else
+#endif
+ {
+ unsigned num = 0;
+ while (isdigit(*e)) {
+ num = num * 10 + *e - '0';
+ ++e;
+ }
+ if (*e != 'q') {
+ continue;
+ }
+ *cp = '\0';
+ if (*f) {
+ ret = ::strptime(ret, f, ptm);
+ if (!ret) {
+ break;
+ }
+ }
+ unsigned long mul = NS_PER_SEC;
+ if (num == 0) {
+ num = INT_MAX;
+ }
+ tv_nsec = 0;
+ while (isdigit(*ret) && num && (mul > 1)) {
+ --num;
+ mul /= 10;
+ tv_nsec = tv_nsec + (*ret - '0') * mul;
+ ++ret;
+ }
+ }
+ f = cp = e;
+ ++f;
+ }
+
+ if (ret) {
+ tv_sec = mktime(ptm);
+ return ret;
+ }
+
+ // Upon error, place a known value into the class, the current time.
+#ifdef __linux__
+ *this = log_time(CLOCK_REALTIME);
+#else
+ time(&now);
+ tv_sec = now;
+ tv_nsec = 0;
+#endif
+ return ret;
+}
+
+log_time log_time::operator-= (const timespec &T) {
+ // No concept of negative time, clamp to EPOCH
+ if (*this <= T) {
+ return *this = EPOCH;
+ }
+
+ if (this->tv_nsec < (unsigned long int)T.tv_nsec) {
+ --this->tv_sec;
+ this->tv_nsec = NS_PER_SEC + this->tv_nsec - T.tv_nsec;
+ } else {
+ this->tv_nsec -= T.tv_nsec;
+ }
+ this->tv_sec -= T.tv_sec;
+
+ return *this;
+}
+
+log_time log_time::operator+= (const timespec &T) {
+ this->tv_nsec += (unsigned long int)T.tv_nsec;
+ if (this->tv_nsec >= NS_PER_SEC) {
+ this->tv_nsec -= NS_PER_SEC;
+ ++this->tv_sec;
+ }
+ this->tv_sec += T.tv_sec;
+
+ return *this;
+}
+
+log_time log_time::operator-= (const log_time &T) {
+ // No concept of negative time, clamp to EPOCH
+ if (*this <= T) {
+ return *this = EPOCH;
+ }
+
+ if (this->tv_nsec < T.tv_nsec) {
+ --this->tv_sec;
+ this->tv_nsec = NS_PER_SEC + this->tv_nsec - T.tv_nsec;
+ } else {
+ this->tv_nsec -= T.tv_nsec;
+ }
+ this->tv_sec -= T.tv_sec;
+
+ return *this;
+}
+
+log_time log_time::operator+= (const log_time &T) {
+ this->tv_nsec += T.tv_nsec;
+ if (this->tv_nsec >= NS_PER_SEC) {
+ this->tv_nsec -= NS_PER_SEC;
+ ++this->tv_sec;
+ }
+ this->tv_sec += T.tv_sec;
+
+ return *this;
+}
diff --git a/liblog/logd_write.c b/liblog/logd_write.c
new file mode 100644
index 0000000..bdee28f
--- /dev/null
+++ b/liblog/logd_write.c
@@ -0,0 +1,515 @@
+/*
+ * Copyright (C) 2007-2014 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.
+ */
+#if (FAKE_LOG_DEVICE == 0)
+#include <endian.h>
+#endif
+#include <errno.h>
+#include <fcntl.h>
+#if !defined(_WIN32)
+#include <pthread.h>
+#endif
+#include <stdarg.h>
+#include <stdatomic.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#if (FAKE_LOG_DEVICE == 0)
+#include <sys/socket.h>
+#include <sys/un.h>
+#endif
+#include <time.h>
+#include <unistd.h>
+
+#ifdef __BIONIC__
+#include <android/set_abort_message.h>
+#endif
+
+#include <log/logd.h>
+#include <log/logger.h>
+#include <log/log_read.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+
+#define LOG_BUF_SIZE 1024
+
+#if FAKE_LOG_DEVICE
+/* This will be defined when building for the host. */
+#include "fake_log_device.h"
+#endif
+
+static int __write_to_log_init(log_id_t, struct iovec *vec, size_t nr);
+static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = __write_to_log_init;
+#if !defined(_WIN32)
+static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
+#endif
+
+#ifndef __unused
+#define __unused __attribute__((__unused__))
+#endif
+
+#if FAKE_LOG_DEVICE
+static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1, -1, -1 };
+#else
+static int logd_fd = -1;
+static int pstore_fd = -1;
+#endif
+
+/*
+ * This is used by the C++ code to decide if it should write logs through
+ * the C code. Basically, if /dev/socket/logd is available, we're running in
+ * the simulator rather than a desktop tool and want to use the device.
+ */
+static enum {
+ kLogUninitialized, kLogNotAvailable, kLogAvailable
+} g_log_status = kLogUninitialized;
+
+int __android_log_dev_available(void)
+{
+ if (g_log_status == kLogUninitialized) {
+ if (access("/dev/socket/logdw", W_OK) == 0)
+ g_log_status = kLogAvailable;
+ else
+ g_log_status = kLogNotAvailable;
+ }
+
+ return (g_log_status == kLogAvailable);
+}
+
+/* log_init_lock assumed */
+static int __write_to_log_initialize()
+{
+ int i, ret = 0;
+
+#if FAKE_LOG_DEVICE
+ for (i = 0; i < LOG_ID_MAX; i++) {
+ char buf[sizeof("/dev/log_system")];
+ snprintf(buf, sizeof(buf), "/dev/log_%s", android_log_id_to_name(i));
+ log_fds[i] = fakeLogOpen(buf, O_WRONLY);
+ }
+#else
+ if (pstore_fd < 0) {
+ pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
+ }
+
+ if (logd_fd < 0) {
+ i = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0));
+ if (i < 0) {
+ ret = -errno;
+ } else if (TEMP_FAILURE_RETRY(fcntl(i, F_SETFL, O_NONBLOCK)) < 0) {
+ ret = -errno;
+ close(i);
+ } else {
+ struct sockaddr_un un;
+ memset(&un, 0, sizeof(struct sockaddr_un));
+ un.sun_family = AF_UNIX;
+ strcpy(un.sun_path, "/dev/socket/logdw");
+
+ if (TEMP_FAILURE_RETRY(connect(i, (struct sockaddr *)&un,
+ sizeof(struct sockaddr_un))) < 0) {
+ ret = -errno;
+ close(i);
+ } else {
+ logd_fd = i;
+ }
+ }
+ }
+#endif
+
+ return ret;
+}
+
+static int __write_to_log_daemon(log_id_t log_id, struct iovec *vec, size_t nr)
+{
+ ssize_t ret;
+#if FAKE_LOG_DEVICE
+ int log_fd;
+
+ if (/*(int)log_id >= 0 &&*/ (int)log_id < (int)LOG_ID_MAX) {
+ log_fd = log_fds[(int)log_id];
+ } else {
+ return -EBADF;
+ }
+ do {
+ ret = fakeLogWritev(log_fd, vec, nr);
+ if (ret < 0) {
+ ret = -errno;
+ }
+ } while (ret == -EINTR);
+#else
+ static const unsigned header_length = 2;
+ struct iovec newVec[nr + header_length];
+ android_log_header_t header;
+ android_pmsg_log_header_t pmsg_header;
+ struct timespec ts;
+ size_t i, payload_size;
+ static uid_t last_uid = AID_ROOT; /* logd *always* starts up as AID_ROOT */
+ static pid_t last_pid = (pid_t) -1;
+ static atomic_int_fast32_t dropped;
+
+ if (!nr) {
+ return -EINVAL;
+ }
+
+ if (last_uid == AID_ROOT) { /* have we called to get the UID yet? */
+ last_uid = getuid();
+ }
+ if (last_pid == (pid_t) -1) {
+ last_pid = getpid();
+ }
+ /*
+ * struct {
+ * // what we provide to pstore
+ * android_pmsg_log_header_t pmsg_header;
+ * // what we provide to socket
+ * android_log_header_t header;
+ * // caller provides
+ * union {
+ * struct {
+ * char prio;
+ * char payload[];
+ * } string;
+ * struct {
+ * uint32_t tag
+ * char payload[];
+ * } binary;
+ * };
+ * };
+ */
+
+ clock_gettime(CLOCK_REALTIME, &ts);
+
+ pmsg_header.magic = LOGGER_MAGIC;
+ pmsg_header.len = sizeof(pmsg_header) + sizeof(header);
+ pmsg_header.uid = last_uid;
+ pmsg_header.pid = last_pid;
+
+ header.tid = gettid();
+ header.realtime.tv_sec = ts.tv_sec;
+ header.realtime.tv_nsec = ts.tv_nsec;
+
+ newVec[0].iov_base = (unsigned char *) &pmsg_header;
+ newVec[0].iov_len = sizeof(pmsg_header);
+ newVec[1].iov_base = (unsigned char *) &header;
+ newVec[1].iov_len = sizeof(header);
+
+ if (logd_fd > 0) {
+ int32_t snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
+ if (snapshot) {
+ android_log_event_int_t buffer;
+
+ header.id = LOG_ID_EVENTS;
+ buffer.header.tag = htole32(LIBLOG_LOG_TAG);
+ buffer.payload.type = EVENT_TYPE_INT;
+ buffer.payload.data = htole32(snapshot);
+
+ newVec[2].iov_base = &buffer;
+ newVec[2].iov_len = sizeof(buffer);
+
+ ret = TEMP_FAILURE_RETRY(writev(logd_fd, newVec + 1, 2));
+ if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
+ atomic_fetch_add_explicit(&dropped, snapshot, memory_order_relaxed);
+ }
+ }
+ }
+
+ header.id = log_id;
+
+ for (payload_size = 0, i = header_length; i < nr + header_length; i++) {
+ newVec[i].iov_base = vec[i - header_length].iov_base;
+ payload_size += newVec[i].iov_len = vec[i - header_length].iov_len;
+
+ if (payload_size > LOGGER_ENTRY_MAX_PAYLOAD) {
+ newVec[i].iov_len -= payload_size - LOGGER_ENTRY_MAX_PAYLOAD;
+ if (newVec[i].iov_len) {
+ ++i;
+ }
+ payload_size = LOGGER_ENTRY_MAX_PAYLOAD;
+ break;
+ }
+ }
+ pmsg_header.len += payload_size;
+
+ if (pstore_fd >= 0) {
+ TEMP_FAILURE_RETRY(writev(pstore_fd, newVec, i));
+ }
+
+ if (last_uid == AID_LOGD) { /* logd, after initialization and priv drop */
+ /*
+ * ignore log messages we send to ourself (logd).
+ * Such log messages are often generated by libraries we depend on
+ * which use standard Android logging.
+ */
+ return 0;
+ }
+
+ if (logd_fd < 0) {
+ return -EBADF;
+ }
+
+ /*
+ * The write below could be lost, but will never block.
+ *
+ * To logd, we drop the pmsg_header
+ *
+ * ENOTCONN occurs if logd dies.
+ * EAGAIN occurs if logd is overloaded.
+ */
+ ret = TEMP_FAILURE_RETRY(writev(logd_fd, newVec + 1, i - 1));
+ if (ret < 0) {
+ ret = -errno;
+ if (ret == -ENOTCONN) {
+#if !defined(_WIN32)
+ pthread_mutex_lock(&log_init_lock);
+#endif
+ close(logd_fd);
+ logd_fd = -1;
+ ret = __write_to_log_initialize();
+#if !defined(_WIN32)
+ pthread_mutex_unlock(&log_init_lock);
+#endif
+
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = TEMP_FAILURE_RETRY(writev(logd_fd, newVec + 1, i - 1));
+ if (ret < 0) {
+ ret = -errno;
+ }
+ }
+ }
+
+ if (ret > (ssize_t)sizeof(header)) {
+ ret -= sizeof(header);
+ } else if (ret == -EAGAIN) {
+ atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
+ }
+#endif
+
+ return ret;
+}
+
+#if FAKE_LOG_DEVICE
+static const char *LOG_NAME[LOG_ID_MAX] = {
+ [LOG_ID_MAIN] = "main",
+ [LOG_ID_RADIO] = "radio",
+ [LOG_ID_EVENTS] = "events",
+ [LOG_ID_SYSTEM] = "system",
+ [LOG_ID_CRASH] = "crash",
+ [LOG_ID_KERNEL] = "kernel",
+};
+
+const char *android_log_id_to_name(log_id_t log_id)
+{
+ if (log_id >= LOG_ID_MAX) {
+ log_id = LOG_ID_MAIN;
+ }
+ return LOG_NAME[log_id];
+}
+#endif
+
+static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr)
+{
+#if !defined(_WIN32)
+ pthread_mutex_lock(&log_init_lock);
+#endif
+
+ if (write_to_log == __write_to_log_init) {
+ int ret;
+
+ ret = __write_to_log_initialize();
+ if (ret < 0) {
+#if !defined(_WIN32)
+ pthread_mutex_unlock(&log_init_lock);
+#endif
+#if (FAKE_LOG_DEVICE == 0)
+ if (pstore_fd >= 0) {
+ __write_to_log_daemon(log_id, vec, nr);
+ }
+#endif
+ return ret;
+ }
+
+ write_to_log = __write_to_log_daemon;
+ }
+
+#if !defined(_WIN32)
+ pthread_mutex_unlock(&log_init_lock);
+#endif
+
+ return write_to_log(log_id, vec, nr);
+}
+
+int __android_log_write(int prio, const char *tag, const char *msg)
+{
+ return __android_log_buf_write(LOG_ID_MAIN, prio, tag, msg);
+}
+
+int __android_log_buf_write(int bufID, int prio, const char *tag, const char *msg)
+{
+ struct iovec vec[3];
+ char tmp_tag[32];
+
+ if (!tag)
+ tag = "";
+
+ /* XXX: This needs to go! */
+ if ((bufID != LOG_ID_RADIO) &&
+ (!strcmp(tag, "HTC_RIL") ||
+ !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */
+ !strncmp(tag, "IMS", 3) || /* Any log tag with "IMS" as the prefix */
+ !strcmp(tag, "AT") ||
+ !strcmp(tag, "GSM") ||
+ !strcmp(tag, "STK") ||
+ !strcmp(tag, "CDMA") ||
+ !strcmp(tag, "PHONE") ||
+ !strcmp(tag, "SMS"))) {
+ bufID = LOG_ID_RADIO;
+ /* Inform third party apps/ril/radio.. to use Rlog or RLOG */
+ snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
+ tag = tmp_tag;
+ }
+
+#if __BIONIC__
+ if (prio == ANDROID_LOG_FATAL) {
+ android_set_abort_message(msg);
+ }
+#endif
+
+ vec[0].iov_base = (unsigned char *) &prio;
+ vec[0].iov_len = 1;
+ vec[1].iov_base = (void *) tag;
+ vec[1].iov_len = strlen(tag) + 1;
+ vec[2].iov_base = (void *) msg;
+ vec[2].iov_len = strlen(msg) + 1;
+
+ return write_to_log(bufID, vec, 3);
+}
+
+int __android_log_vprint(int prio, const char *tag, const char *fmt, va_list ap)
+{
+ char buf[LOG_BUF_SIZE];
+
+ vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
+
+ return __android_log_write(prio, tag, buf);
+}
+
+int __android_log_print(int prio, const char *tag, const char *fmt, ...)
+{
+ va_list ap;
+ char buf[LOG_BUF_SIZE];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
+ va_end(ap);
+
+ return __android_log_write(prio, tag, buf);
+}
+
+int __android_log_buf_print(int bufID, int prio, const char *tag, const char *fmt, ...)
+{
+ va_list ap;
+ char buf[LOG_BUF_SIZE];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
+ va_end(ap);
+
+ return __android_log_buf_write(bufID, prio, tag, buf);
+}
+
+void __android_log_assert(const char *cond, const char *tag,
+ const char *fmt, ...)
+{
+ char buf[LOG_BUF_SIZE];
+
+ if (fmt) {
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
+ va_end(ap);
+ } else {
+ /* Msg not provided, log condition. N.B. Do not use cond directly as
+ * format string as it could contain spurious '%' syntax (e.g.
+ * "%d" in "blocks%devs == 0").
+ */
+ if (cond)
+ snprintf(buf, LOG_BUF_SIZE, "Assertion failed: %s", cond);
+ else
+ strcpy(buf, "Unspecified assertion failed");
+ }
+
+ __android_log_write(ANDROID_LOG_FATAL, tag, buf);
+ abort(); /* abort so we have a chance to debug the situation */
+ /* NOTREACHED */
+}
+
+int __android_log_bwrite(int32_t tag, const void *payload, size_t len)
+{
+ struct iovec vec[2];
+
+ vec[0].iov_base = &tag;
+ vec[0].iov_len = sizeof(tag);
+ vec[1].iov_base = (void*)payload;
+ vec[1].iov_len = len;
+
+ return write_to_log(LOG_ID_EVENTS, vec, 2);
+}
+
+/*
+ * Like __android_log_bwrite, but takes the type as well. Doesn't work
+ * for the general case where we're generating lists of stuff, but very
+ * handy if we just want to dump an integer into the log.
+ */
+int __android_log_btwrite(int32_t tag, char type, const void *payload,
+ size_t len)
+{
+ struct iovec vec[3];
+
+ vec[0].iov_base = &tag;
+ vec[0].iov_len = sizeof(tag);
+ vec[1].iov_base = &type;
+ vec[1].iov_len = sizeof(type);
+ vec[2].iov_base = (void*)payload;
+ vec[2].iov_len = len;
+
+ return write_to_log(LOG_ID_EVENTS, vec, 3);
+}
+
+/*
+ * Like __android_log_bwrite, but used for writing strings to the
+ * event log.
+ */
+int __android_log_bswrite(int32_t tag, const char *payload)
+{
+ struct iovec vec[4];
+ char type = EVENT_TYPE_STRING;
+ uint32_t len = strlen(payload);
+
+ vec[0].iov_base = &tag;
+ vec[0].iov_len = sizeof(tag);
+ vec[1].iov_base = &type;
+ vec[1].iov_len = sizeof(type);
+ vec[2].iov_base = &len;
+ vec[2].iov_len = sizeof(len);
+ vec[3].iov_base = (void*)payload;
+ vec[3].iov_len = len;
+
+ return write_to_log(LOG_ID_EVENTS, vec, 4);
+}
diff --git a/liblog/logd_write_kern.c b/liblog/logd_write_kern.c
new file mode 100644
index 0000000..8742b34
--- /dev/null
+++ b/liblog/logd_write_kern.c
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2007-2014 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 <pthread.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <android/set_abort_message.h>
+
+#include <log/log.h>
+#include <log/logd.h>
+#include <log/logger.h>
+
+#define LOGGER_LOG_MAIN "log/main"
+#define LOGGER_LOG_RADIO "log/radio"
+#define LOGGER_LOG_EVENTS "log/events"
+#define LOGGER_LOG_SYSTEM "log/system"
+
+#define LOG_BUF_SIZE 1024
+
+#define log_open(pathname, flags) open(pathname, (flags) | O_CLOEXEC)
+#define log_writev(filedes, vector, count) writev(filedes, vector, count)
+#define log_close(filedes) close(filedes)
+
+static int __write_to_log_init(log_id_t, struct iovec *vec, size_t nr);
+static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = __write_to_log_init;
+
+static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
+
+#ifndef __unused
+#define __unused __attribute__((__unused__))
+#endif
+
+static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1, -1 };
+
+/*
+ * This is used by the C++ code to decide if it should write logs through
+ * the C code. Basically, if /dev/log/... is available, we're running in
+ * the simulator rather than a desktop tool and want to use the device.
+ */
+static enum {
+ kLogUninitialized, kLogNotAvailable, kLogAvailable
+} g_log_status = kLogUninitialized;
+int __android_log_dev_available(void)
+{
+ if (g_log_status == kLogUninitialized) {
+ if (access("/dev/"LOGGER_LOG_MAIN, W_OK) == 0)
+ g_log_status = kLogAvailable;
+ else
+ g_log_status = kLogNotAvailable;
+ }
+
+ return (g_log_status == kLogAvailable);
+}
+
+static int __write_to_log_null(log_id_t log_fd __unused, struct iovec *vec __unused,
+ size_t nr __unused)
+{
+ return -1;
+}
+
+static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr)
+{
+ ssize_t ret;
+ int log_fd;
+
+ if (/*(int)log_id >= 0 &&*/ (int)log_id < (int)LOG_ID_MAX) {
+ if (log_id == LOG_ID_CRASH) {
+ log_id = LOG_ID_MAIN;
+ }
+ log_fd = log_fds[(int)log_id];
+ } else {
+ return -EBADF;
+ }
+
+ do {
+ ret = log_writev(log_fd, vec, nr);
+ if (ret < 0) {
+ ret = -errno;
+ }
+ } while (ret == -EINTR);
+
+ return ret;
+}
+
+static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr)
+{
+ pthread_mutex_lock(&log_init_lock);
+
+ if (write_to_log == __write_to_log_init) {
+ log_fds[LOG_ID_MAIN] = log_open("/dev/"LOGGER_LOG_MAIN, O_WRONLY);
+ log_fds[LOG_ID_RADIO] = log_open("/dev/"LOGGER_LOG_RADIO, O_WRONLY);
+ log_fds[LOG_ID_EVENTS] = log_open("/dev/"LOGGER_LOG_EVENTS, O_WRONLY);
+ log_fds[LOG_ID_SYSTEM] = log_open("/dev/"LOGGER_LOG_SYSTEM, O_WRONLY);
+
+ write_to_log = __write_to_log_kernel;
+
+ if (log_fds[LOG_ID_MAIN] < 0 || log_fds[LOG_ID_RADIO] < 0 ||
+ log_fds[LOG_ID_EVENTS] < 0) {
+ log_close(log_fds[LOG_ID_MAIN]);
+ log_close(log_fds[LOG_ID_RADIO]);
+ log_close(log_fds[LOG_ID_EVENTS]);
+ log_fds[LOG_ID_MAIN] = -1;
+ log_fds[LOG_ID_RADIO] = -1;
+ log_fds[LOG_ID_EVENTS] = -1;
+ write_to_log = __write_to_log_null;
+ }
+
+ if (log_fds[LOG_ID_SYSTEM] < 0) {
+ log_fds[LOG_ID_SYSTEM] = log_fds[LOG_ID_MAIN];
+ }
+ }
+
+ pthread_mutex_unlock(&log_init_lock);
+
+ return write_to_log(log_id, vec, nr);
+}
+
+int __android_log_write(int prio, const char *tag, const char *msg)
+{
+ return __android_log_buf_write(LOG_ID_MAIN, prio, tag, msg);
+}
+
+int __android_log_buf_write(int bufID, int prio, const char *tag, const char *msg)
+{
+ struct iovec vec[3];
+ char tmp_tag[32];
+
+ if (!tag)
+ tag = "";
+
+ /* XXX: This needs to go! */
+ if ((bufID != LOG_ID_RADIO) &&
+ (!strcmp(tag, "HTC_RIL") ||
+ !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */
+ !strncmp(tag, "IMS", 3) || /* Any log tag with "IMS" as the prefix */
+ !strcmp(tag, "AT") ||
+ !strcmp(tag, "GSM") ||
+ !strcmp(tag, "STK") ||
+ !strcmp(tag, "CDMA") ||
+ !strcmp(tag, "PHONE") ||
+ !strcmp(tag, "SMS"))) {
+ bufID = LOG_ID_RADIO;
+ /* Inform third party apps/ril/radio.. to use Rlog or RLOG */
+ snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
+ tag = tmp_tag;
+ }
+
+ if (prio == ANDROID_LOG_FATAL) {
+ android_set_abort_message(msg);
+ }
+
+ vec[0].iov_base = (unsigned char *) &prio;
+ vec[0].iov_len = 1;
+ vec[1].iov_base = (void *) tag;
+ vec[1].iov_len = strlen(tag) + 1;
+ vec[2].iov_base = (void *) msg;
+ vec[2].iov_len = strlen(msg) + 1;
+
+ return write_to_log(bufID, vec, 3);
+}
+
+int __android_log_vprint(int prio, const char *tag, const char *fmt, va_list ap)
+{
+ char buf[LOG_BUF_SIZE];
+
+ vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
+
+ return __android_log_write(prio, tag, buf);
+}
+
+int __android_log_print(int prio, const char *tag, const char *fmt, ...)
+{
+ va_list ap;
+ char buf[LOG_BUF_SIZE];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
+ va_end(ap);
+
+ return __android_log_write(prio, tag, buf);
+}
+
+int __android_log_buf_print(int bufID, int prio, const char *tag, const char *fmt, ...)
+{
+ va_list ap;
+ char buf[LOG_BUF_SIZE];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
+ va_end(ap);
+
+ return __android_log_buf_write(bufID, prio, tag, buf);
+}
+
+void __android_log_assert(const char *cond, const char *tag,
+ const char *fmt, ...)
+{
+ char buf[LOG_BUF_SIZE];
+
+ if (fmt) {
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
+ va_end(ap);
+ } else {
+ /* Msg not provided, log condition. N.B. Do not use cond directly as
+ * format string as it could contain spurious '%' syntax (e.g.
+ * "%d" in "blocks%devs == 0").
+ */
+ if (cond)
+ snprintf(buf, LOG_BUF_SIZE, "Assertion failed: %s", cond);
+ else
+ strcpy(buf, "Unspecified assertion failed");
+ }
+
+ __android_log_write(ANDROID_LOG_FATAL, tag, buf);
+ abort(); /* abort so we have a chance to debug the situation */
+ /* NOTREACHED */
+}
+
+int __android_log_bwrite(int32_t tag, const void *payload, size_t len)
+{
+ struct iovec vec[2];
+
+ vec[0].iov_base = &tag;
+ vec[0].iov_len = sizeof(tag);
+ vec[1].iov_base = (void*)payload;
+ vec[1].iov_len = len;
+
+ return write_to_log(LOG_ID_EVENTS, vec, 2);
+}
+
+/*
+ * Like __android_log_bwrite, but takes the type as well. Doesn't work
+ * for the general case where we're generating lists of stuff, but very
+ * handy if we just want to dump an integer into the log.
+ */
+int __android_log_btwrite(int32_t tag, char type, const void *payload,
+ size_t len)
+{
+ struct iovec vec[3];
+
+ vec[0].iov_base = &tag;
+ vec[0].iov_len = sizeof(tag);
+ vec[1].iov_base = &type;
+ vec[1].iov_len = sizeof(type);
+ vec[2].iov_base = (void*)payload;
+ vec[2].iov_len = len;
+
+ return write_to_log(LOG_ID_EVENTS, vec, 3);
+}
+
+/*
+ * Like __android_log_bwrite, but used for writing strings to the
+ * event log.
+ */
+int __android_log_bswrite(int32_t tag, const char *payload)
+{
+ struct iovec vec[4];
+ char type = EVENT_TYPE_STRING;
+ uint32_t len = strlen(payload);
+
+ vec[0].iov_base = &tag;
+ vec[0].iov_len = sizeof(tag);
+ vec[1].iov_base = &type;
+ vec[1].iov_len = sizeof(type);
+ vec[2].iov_base = &len;
+ vec[2].iov_len = sizeof(len);
+ vec[3].iov_base = (void*)payload;
+ vec[3].iov_len = len;
+
+ return write_to_log(LOG_ID_EVENTS, vec, 4);
+}
diff --git a/liblog/logprint.c b/liblog/logprint.c
new file mode 100644
index 0000000..c2f1545
--- /dev/null
+++ b/liblog/logprint.c
@@ -0,0 +1,1141 @@
+/*
+**
+** Copyright 2006-2014, 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 _GNU_SOURCE /* for asprintf */
+
+#include <arpa/inet.h>
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include <sys/param.h>
+
+#include <log/logd.h>
+#include <log/logprint.h>
+
+/* open coded fragment, prevent circular dependencies */
+#define WEAK static
+
+typedef struct FilterInfo_t {
+ char *mTag;
+ android_LogPriority mPri;
+ struct FilterInfo_t *p_next;
+} FilterInfo;
+
+struct AndroidLogFormat_t {
+ android_LogPriority global_pri;
+ FilterInfo *filters;
+ AndroidLogPrintFormat format;
+ bool colored_output;
+ bool usec_time_output;
+ bool printable_output;
+};
+
+/*
+ * gnome-terminal color tags
+ * See http://misc.flogisoft.com/bash/tip_colors_and_formatting
+ * for ideas on how to set the forground color of the text for xterm.
+ * The color manipulation character stream is defined as:
+ * ESC [ 3 8 ; 5 ; <color#> m
+ */
+#define ANDROID_COLOR_BLUE 75
+#define ANDROID_COLOR_DEFAULT 231
+#define ANDROID_COLOR_GREEN 40
+#define ANDROID_COLOR_ORANGE 166
+#define ANDROID_COLOR_RED 196
+#define ANDROID_COLOR_YELLOW 226
+
+static FilterInfo * filterinfo_new(const char * tag, android_LogPriority pri)
+{
+ FilterInfo *p_ret;
+
+ p_ret = (FilterInfo *)calloc(1, sizeof(FilterInfo));
+ p_ret->mTag = strdup(tag);
+ p_ret->mPri = pri;
+
+ return p_ret;
+}
+
+/* balance to above, filterinfo_free left unimplemented */
+
+/*
+ * Note: also accepts 0-9 priorities
+ * returns ANDROID_LOG_UNKNOWN if the character is unrecognized
+ */
+static android_LogPriority filterCharToPri (char c)
+{
+ android_LogPriority pri;
+
+ c = tolower(c);
+
+ if (c >= '0' && c <= '9') {
+ if (c >= ('0'+ANDROID_LOG_SILENT)) {
+ pri = ANDROID_LOG_VERBOSE;
+ } else {
+ pri = (android_LogPriority)(c - '0');
+ }
+ } else if (c == 'v') {
+ pri = ANDROID_LOG_VERBOSE;
+ } else if (c == 'd') {
+ pri = ANDROID_LOG_DEBUG;
+ } else if (c == 'i') {
+ pri = ANDROID_LOG_INFO;
+ } else if (c == 'w') {
+ pri = ANDROID_LOG_WARN;
+ } else if (c == 'e') {
+ pri = ANDROID_LOG_ERROR;
+ } else if (c == 'f') {
+ pri = ANDROID_LOG_FATAL;
+ } else if (c == 's') {
+ pri = ANDROID_LOG_SILENT;
+ } else if (c == '*') {
+ pri = ANDROID_LOG_DEFAULT;
+ } else {
+ pri = ANDROID_LOG_UNKNOWN;
+ }
+
+ return pri;
+}
+
+static char filterPriToChar (android_LogPriority pri)
+{
+ switch (pri) {
+ case ANDROID_LOG_VERBOSE: return 'V';
+ case ANDROID_LOG_DEBUG: return 'D';
+ case ANDROID_LOG_INFO: return 'I';
+ case ANDROID_LOG_WARN: return 'W';
+ case ANDROID_LOG_ERROR: return 'E';
+ case ANDROID_LOG_FATAL: return 'F';
+ case ANDROID_LOG_SILENT: return 'S';
+
+ case ANDROID_LOG_DEFAULT:
+ case ANDROID_LOG_UNKNOWN:
+ default: return '?';
+ }
+}
+
+static int colorFromPri (android_LogPriority pri)
+{
+ switch (pri) {
+ case ANDROID_LOG_VERBOSE: return ANDROID_COLOR_DEFAULT;
+ case ANDROID_LOG_DEBUG: return ANDROID_COLOR_BLUE;
+ case ANDROID_LOG_INFO: return ANDROID_COLOR_GREEN;
+ case ANDROID_LOG_WARN: return ANDROID_COLOR_ORANGE;
+ case ANDROID_LOG_ERROR: return ANDROID_COLOR_RED;
+ case ANDROID_LOG_FATAL: return ANDROID_COLOR_RED;
+ case ANDROID_LOG_SILENT: return ANDROID_COLOR_DEFAULT;
+
+ case ANDROID_LOG_DEFAULT:
+ case ANDROID_LOG_UNKNOWN:
+ default: return ANDROID_COLOR_DEFAULT;
+ }
+}
+
+static android_LogPriority filterPriForTag(
+ AndroidLogFormat *p_format, const char *tag)
+{
+ FilterInfo *p_curFilter;
+
+ for (p_curFilter = p_format->filters
+ ; p_curFilter != NULL
+ ; p_curFilter = p_curFilter->p_next
+ ) {
+ if (0 == strcmp(tag, p_curFilter->mTag)) {
+ if (p_curFilter->mPri == ANDROID_LOG_DEFAULT) {
+ return p_format->global_pri;
+ } else {
+ return p_curFilter->mPri;
+ }
+ }
+ }
+
+ return p_format->global_pri;
+}
+
+/**
+ * returns 1 if this log line should be printed based on its priority
+ * and tag, and 0 if it should not
+ */
+int android_log_shouldPrintLine (
+ AndroidLogFormat *p_format, const char *tag, android_LogPriority pri)
+{
+ return pri >= filterPriForTag(p_format, tag);
+}
+
+AndroidLogFormat *android_log_format_new()
+{
+ AndroidLogFormat *p_ret;
+
+ p_ret = calloc(1, sizeof(AndroidLogFormat));
+
+ p_ret->global_pri = ANDROID_LOG_VERBOSE;
+ p_ret->format = FORMAT_BRIEF;
+ p_ret->colored_output = false;
+ p_ret->usec_time_output = false;
+ p_ret->printable_output = false;
+
+ return p_ret;
+}
+
+void android_log_format_free(AndroidLogFormat *p_format)
+{
+ FilterInfo *p_info, *p_info_old;
+
+ p_info = p_format->filters;
+
+ while (p_info != NULL) {
+ p_info_old = p_info;
+ p_info = p_info->p_next;
+
+ free(p_info_old);
+ }
+
+ free(p_format);
+}
+
+
+
+int android_log_setPrintFormat(AndroidLogFormat *p_format,
+ AndroidLogPrintFormat format)
+{
+ switch (format) {
+ case FORMAT_MODIFIER_COLOR:
+ p_format->colored_output = true;
+ return 0;
+ case FORMAT_MODIFIER_TIME_USEC:
+ p_format->usec_time_output = true;
+ return 0;
+ case FORMAT_MODIFIER_PRINTABLE:
+ p_format->printable_output = true;
+ return 0;
+ default:
+ break;
+ }
+ p_format->format = format;
+ return 1;
+}
+
+/**
+ * Returns FORMAT_OFF on invalid string
+ */
+AndroidLogPrintFormat android_log_formatFromString(const char * formatString)
+{
+ static AndroidLogPrintFormat format;
+
+ if (strcmp(formatString, "brief") == 0) format = FORMAT_BRIEF;
+ else if (strcmp(formatString, "process") == 0) format = FORMAT_PROCESS;
+ else if (strcmp(formatString, "tag") == 0) format = FORMAT_TAG;
+ else if (strcmp(formatString, "thread") == 0) format = FORMAT_THREAD;
+ else if (strcmp(formatString, "raw") == 0) format = FORMAT_RAW;
+ else if (strcmp(formatString, "time") == 0) format = FORMAT_TIME;
+ else if (strcmp(formatString, "threadtime") == 0) format = FORMAT_THREADTIME;
+ else if (strcmp(formatString, "long") == 0) format = FORMAT_LONG;
+ else if (strcmp(formatString, "color") == 0) format = FORMAT_MODIFIER_COLOR;
+ else if (strcmp(formatString, "usec") == 0) format = FORMAT_MODIFIER_TIME_USEC;
+ else if (strcmp(formatString, "printable") == 0) format = FORMAT_MODIFIER_PRINTABLE;
+ else format = FORMAT_OFF;
+
+ return format;
+}
+
+/**
+ * filterExpression: a single filter expression
+ * eg "AT:d"
+ *
+ * returns 0 on success and -1 on invalid expression
+ *
+ * Assumes single threaded execution
+ */
+
+int android_log_addFilterRule(AndroidLogFormat *p_format,
+ const char *filterExpression)
+{
+ size_t tagNameLength;
+ android_LogPriority pri = ANDROID_LOG_DEFAULT;
+
+ tagNameLength = strcspn(filterExpression, ":");
+
+ if (tagNameLength == 0) {
+ goto error;
+ }
+
+ if(filterExpression[tagNameLength] == ':') {
+ pri = filterCharToPri(filterExpression[tagNameLength+1]);
+
+ if (pri == ANDROID_LOG_UNKNOWN) {
+ goto error;
+ }
+ }
+
+ if(0 == strncmp("*", filterExpression, tagNameLength)) {
+ /*
+ * This filter expression refers to the global filter
+ * The default level for this is DEBUG if the priority
+ * is unspecified
+ */
+ if (pri == ANDROID_LOG_DEFAULT) {
+ pri = ANDROID_LOG_DEBUG;
+ }
+
+ p_format->global_pri = pri;
+ } else {
+ /*
+ * for filter expressions that don't refer to the global
+ * filter, the default is verbose if the priority is unspecified
+ */
+ if (pri == ANDROID_LOG_DEFAULT) {
+ pri = ANDROID_LOG_VERBOSE;
+ }
+
+ char *tagName;
+
+/*
+ * Presently HAVE_STRNDUP is never defined, so the second case is always taken
+ * Darwin doesn't have strnup, everything else does
+ */
+#ifdef HAVE_STRNDUP
+ tagName = strndup(filterExpression, tagNameLength);
+#else
+ /* a few extra bytes copied... */
+ tagName = strdup(filterExpression);
+ tagName[tagNameLength] = '\0';
+#endif /*HAVE_STRNDUP*/
+
+ FilterInfo *p_fi = filterinfo_new(tagName, pri);
+ free(tagName);
+
+ p_fi->p_next = p_format->filters;
+ p_format->filters = p_fi;
+ }
+
+ return 0;
+error:
+ return -1;
+}
+
+
+/**
+ * filterString: a comma/whitespace-separated set of filter expressions
+ *
+ * eg "AT:d *:i"
+ *
+ * returns 0 on success and -1 on invalid expression
+ *
+ * Assumes single threaded execution
+ *
+ */
+
+int android_log_addFilterString(AndroidLogFormat *p_format,
+ const char *filterString)
+{
+ char *filterStringCopy = strdup (filterString);
+ char *p_cur = filterStringCopy;
+ char *p_ret;
+ int err;
+
+ /* Yes, I'm using strsep */
+ while (NULL != (p_ret = strsep(&p_cur, " \t,"))) {
+ /* ignore whitespace-only entries */
+ if(p_ret[0] != '\0') {
+ err = android_log_addFilterRule(p_format, p_ret);
+
+ if (err < 0) {
+ goto error;
+ }
+ }
+ }
+
+ free (filterStringCopy);
+ return 0;
+error:
+ free (filterStringCopy);
+ return -1;
+}
+
+/**
+ * Splits a wire-format buffer into an AndroidLogEntry
+ * entry allocated by caller. Pointers will point directly into buf
+ *
+ * Returns 0 on success and -1 on invalid wire format (entry will be
+ * in unspecified state)
+ */
+int android_log_processLogBuffer(struct logger_entry *buf,
+ AndroidLogEntry *entry)
+{
+ entry->tv_sec = buf->sec;
+ entry->tv_nsec = buf->nsec;
+ entry->pid = buf->pid;
+ entry->tid = buf->tid;
+
+ /*
+ * format: <priority:1><tag:N>\0<message:N>\0
+ *
+ * tag str
+ * starts at buf->msg+1
+ * msg
+ * starts at buf->msg+1+len(tag)+1
+ *
+ * The message may have been truncated by the kernel log driver.
+ * When that happens, we must null-terminate the message ourselves.
+ */
+ if (buf->len < 3) {
+ /*
+ * An well-formed entry must consist of at least a priority
+ * and two null characters
+ */
+ fprintf(stderr, "+++ LOG: entry too small\n");
+ return -1;
+ }
+
+ int msgStart = -1;
+ int msgEnd = -1;
+
+ int i;
+ char *msg = buf->msg;
+ struct logger_entry_v2 *buf2 = (struct logger_entry_v2 *)buf;
+ if (buf2->hdr_size) {
+ msg = ((char *)buf2) + buf2->hdr_size;
+ }
+ for (i = 1; i < buf->len; i++) {
+ if (msg[i] == '\0') {
+ if (msgStart == -1) {
+ msgStart = i + 1;
+ } else {
+ msgEnd = i;
+ break;
+ }
+ }
+ }
+
+ if (msgStart == -1) {
+ fprintf(stderr, "+++ LOG: malformed log message\n");
+ return -1;
+ }
+ if (msgEnd == -1) {
+ /* incoming message not null-terminated; force it */
+ msgEnd = buf->len - 1;
+ msg[msgEnd] = '\0';
+ }
+
+ entry->priority = msg[0];
+ entry->tag = msg + 1;
+ entry->message = msg + msgStart;
+ entry->messageLen = msgEnd - msgStart;
+
+ return 0;
+}
+
+/*
+ * Extract a 4-byte value from a byte stream.
+ */
+static inline uint32_t get4LE(const uint8_t* src)
+{
+ return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+}
+
+/*
+ * Extract an 8-byte value from a byte stream.
+ */
+static inline uint64_t get8LE(const uint8_t* src)
+{
+ uint32_t low, high;
+
+ low = src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+ high = src[4] | (src[5] << 8) | (src[6] << 16) | (src[7] << 24);
+ return ((uint64_t) high << 32) | (uint64_t) low;
+}
+
+
+/*
+ * Recursively convert binary log data to printable form.
+ *
+ * This needs to be recursive because you can have lists of lists.
+ *
+ * If we run out of room, we stop processing immediately. It's important
+ * for us to check for space on every output element to avoid producing
+ * garbled output.
+ *
+ * Returns 0 on success, 1 on buffer full, -1 on failure.
+ */
+static int android_log_printBinaryEvent(const unsigned char** pEventData,
+ size_t* pEventDataLen, char** pOutBuf, size_t* pOutBufLen)
+{
+ const unsigned char* eventData = *pEventData;
+ size_t eventDataLen = *pEventDataLen;
+ char* outBuf = *pOutBuf;
+ size_t outBufLen = *pOutBufLen;
+ unsigned char type;
+ size_t outCount;
+ int result = 0;
+
+ if (eventDataLen < 1)
+ return -1;
+ type = *eventData++;
+ eventDataLen--;
+
+ switch (type) {
+ case EVENT_TYPE_INT:
+ /* 32-bit signed int */
+ {
+ int ival;
+
+ if (eventDataLen < 4)
+ return -1;
+ ival = get4LE(eventData);
+ eventData += 4;
+ eventDataLen -= 4;
+
+ outCount = snprintf(outBuf, outBufLen, "%d", ival);
+ if (outCount < outBufLen) {
+ outBuf += outCount;
+ outBufLen -= outCount;
+ } else {
+ /* halt output */
+ goto no_room;
+ }
+ }
+ break;
+ case EVENT_TYPE_LONG:
+ /* 64-bit signed long */
+ {
+ uint64_t lval;
+
+ if (eventDataLen < 8)
+ return -1;
+ lval = get8LE(eventData);
+ eventData += 8;
+ eventDataLen -= 8;
+
+ outCount = snprintf(outBuf, outBufLen, "%" PRId64, lval);
+ if (outCount < outBufLen) {
+ outBuf += outCount;
+ outBufLen -= outCount;
+ } else {
+ /* halt output */
+ goto no_room;
+ }
+ }
+ break;
+ case EVENT_TYPE_FLOAT:
+ /* float */
+ {
+ uint32_t ival;
+ float fval;
+
+ if (eventDataLen < 4)
+ return -1;
+ ival = get4LE(eventData);
+ fval = *(float*)&ival;
+ eventData += 4;
+ eventDataLen -= 4;
+
+ outCount = snprintf(outBuf, outBufLen, "%f", fval);
+ if (outCount < outBufLen) {
+ outBuf += outCount;
+ outBufLen -= outCount;
+ } else {
+ /* halt output */
+ goto no_room;
+ }
+ }
+ break;
+ case EVENT_TYPE_STRING:
+ /* UTF-8 chars, not NULL-terminated */
+ {
+ unsigned int strLen;
+
+ if (eventDataLen < 4)
+ return -1;
+ strLen = get4LE(eventData);
+ eventData += 4;
+ eventDataLen -= 4;
+
+ if (eventDataLen < strLen)
+ return -1;
+
+ if (strLen < outBufLen) {
+ memcpy(outBuf, eventData, strLen);
+ outBuf += strLen;
+ outBufLen -= strLen;
+ } else if (outBufLen > 0) {
+ /* copy what we can */
+ memcpy(outBuf, eventData, outBufLen);
+ outBuf += outBufLen;
+ outBufLen -= outBufLen;
+ goto no_room;
+ }
+ eventData += strLen;
+ eventDataLen -= strLen;
+ break;
+ }
+ case EVENT_TYPE_LIST:
+ /* N items, all different types */
+ {
+ unsigned char count;
+ int i;
+
+ if (eventDataLen < 1)
+ return -1;
+
+ count = *eventData++;
+ eventDataLen--;
+
+ if (outBufLen > 0) {
+ *outBuf++ = '[';
+ outBufLen--;
+ } else {
+ goto no_room;
+ }
+
+ for (i = 0; i < count; i++) {
+ result = android_log_printBinaryEvent(&eventData, &eventDataLen,
+ &outBuf, &outBufLen);
+ if (result != 0)
+ goto bail;
+
+ if (i < count-1) {
+ if (outBufLen > 0) {
+ *outBuf++ = ',';
+ outBufLen--;
+ } else {
+ goto no_room;
+ }
+ }
+ }
+
+ if (outBufLen > 0) {
+ *outBuf++ = ']';
+ outBufLen--;
+ } else {
+ goto no_room;
+ }
+ }
+ break;
+ default:
+ fprintf(stderr, "Unknown binary event type %d\n", type);
+ return -1;
+ }
+
+bail:
+ *pEventData = eventData;
+ *pEventDataLen = eventDataLen;
+ *pOutBuf = outBuf;
+ *pOutBufLen = outBufLen;
+ return result;
+
+no_room:
+ result = 1;
+ goto bail;
+}
+
+/**
+ * Convert a binary log entry to ASCII form.
+ *
+ * For convenience we mimic the processLogBuffer API. There is no
+ * pre-defined output length for the binary data, since we're free to format
+ * it however we choose, which means we can't really use a fixed-size buffer
+ * here.
+ */
+int android_log_processBinaryLogBuffer(struct logger_entry *buf,
+ AndroidLogEntry *entry, const EventTagMap* map, char* messageBuf,
+ int messageBufLen)
+{
+ size_t inCount;
+ unsigned int tagIndex;
+ const unsigned char* eventData;
+
+ entry->tv_sec = buf->sec;
+ entry->tv_nsec = buf->nsec;
+ entry->priority = ANDROID_LOG_INFO;
+ entry->pid = buf->pid;
+ entry->tid = buf->tid;
+
+ /*
+ * Pull the tag out.
+ */
+ eventData = (const unsigned char*) buf->msg;
+ struct logger_entry_v2 *buf2 = (struct logger_entry_v2 *)buf;
+ if (buf2->hdr_size) {
+ eventData = ((unsigned char *)buf2) + buf2->hdr_size;
+ }
+ inCount = buf->len;
+ if (inCount < 4)
+ return -1;
+ tagIndex = get4LE(eventData);
+ eventData += 4;
+ inCount -= 4;
+
+ if (map != NULL) {
+ entry->tag = android_lookupEventTag(map, tagIndex);
+ } else {
+ entry->tag = NULL;
+ }
+
+ /*
+ * If we don't have a map, or didn't find the tag number in the map,
+ * stuff a generated tag value into the start of the output buffer and
+ * shift the buffer pointers down.
+ */
+ if (entry->tag == NULL) {
+ int tagLen;
+
+ tagLen = snprintf(messageBuf, messageBufLen, "[%d]", tagIndex);
+ entry->tag = messageBuf;
+ messageBuf += tagLen+1;
+ messageBufLen -= tagLen+1;
+ }
+
+ /*
+ * Format the event log data into the buffer.
+ */
+ char* outBuf = messageBuf;
+ size_t outRemaining = messageBufLen-1; /* leave one for nul byte */
+ int result;
+ result = android_log_printBinaryEvent(&eventData, &inCount, &outBuf,
+ &outRemaining);
+ if (result < 0) {
+ fprintf(stderr, "Binary log entry conversion failed\n");
+ return -1;
+ } else if (result == 1) {
+ if (outBuf > messageBuf) {
+ /* leave an indicator */
+ *(outBuf-1) = '!';
+ } else {
+ /* no room to output anything at all */
+ *outBuf++ = '!';
+ outRemaining--;
+ }
+ /* pretend we ate all the data */
+ inCount = 0;
+ }
+
+ /* eat the silly terminating '\n' */
+ if (inCount == 1 && *eventData == '\n') {
+ eventData++;
+ inCount--;
+ }
+
+ if (inCount != 0) {
+ fprintf(stderr,
+ "Warning: leftover binary log data (%zu bytes)\n", inCount);
+ }
+
+ /*
+ * Terminate the buffer. The NUL byte does not count as part of
+ * entry->messageLen.
+ */
+ *outBuf = '\0';
+ entry->messageLen = outBuf - messageBuf;
+ assert(entry->messageLen == (messageBufLen-1) - outRemaining);
+
+ entry->message = messageBuf;
+
+ return 0;
+}
+
+/*
+ * One utf8 character at a time
+ *
+ * Returns the length of the utf8 character in the buffer,
+ * or -1 if illegal or truncated
+ *
+ * Open coded from libutils/Unicode.cpp, borrowed from utf8_length(),
+ * can not remove from here because of library circular dependencies.
+ * Expect one-day utf8_character_length with the same signature could
+ * _also_ be part of libutils/Unicode.cpp if its usefullness needs to
+ * propagate globally.
+ */
+WEAK ssize_t utf8_character_length(const char *src, size_t len)
+{
+ const char *cur = src;
+ const char first_char = *cur++;
+ static const uint32_t kUnicodeMaxCodepoint = 0x0010FFFF;
+ int32_t mask, to_ignore_mask;
+ size_t num_to_read;
+ uint32_t utf32;
+
+ if ((first_char & 0x80) == 0) { /* ASCII */
+ return 1;
+ }
+
+ /*
+ * (UTF-8's character must not be like 10xxxxxx,
+ * but 110xxxxx, 1110xxxx, ... or 1111110x)
+ */
+ if ((first_char & 0x40) == 0) {
+ return -1;
+ }
+
+ for (utf32 = 1, num_to_read = 1, mask = 0x40, to_ignore_mask = 0x80;
+ num_to_read < 5 && (first_char & mask);
+ num_to_read++, to_ignore_mask |= mask, mask >>= 1) {
+ if (num_to_read > len) {
+ return -1;
+ }
+ if ((*cur & 0xC0) != 0x80) { /* can not be 10xxxxxx? */
+ return -1;
+ }
+ utf32 = (utf32 << 6) + (*cur++ & 0b00111111);
+ }
+ /* "first_char" must be (110xxxxx - 11110xxx) */
+ if (num_to_read >= 5) {
+ return -1;
+ }
+ to_ignore_mask |= mask;
+ utf32 |= ((~to_ignore_mask) & first_char) << (6 * (num_to_read - 1));
+ if (utf32 > kUnicodeMaxCodepoint) {
+ return -1;
+ }
+ return num_to_read;
+}
+
+/*
+ * Convert to printable from message to p buffer, return string length. If p is
+ * NULL, do not copy, but still return the expected string length.
+ */
+static size_t convertPrintable(char *p, const char *message, size_t messageLen)
+{
+ char *begin = p;
+ bool print = p != NULL;
+
+ while (messageLen) {
+ char buf[6];
+ ssize_t len = sizeof(buf) - 1;
+ if ((size_t)len > messageLen) {
+ len = messageLen;
+ }
+ len = utf8_character_length(message, len);
+
+ if (len < 0) {
+ snprintf(buf, sizeof(buf),
+ ((messageLen > 1) && isdigit(message[1]))
+ ? "\\%03o"
+ : "\\%o",
+ *message & 0377);
+ len = 1;
+ } else {
+ buf[0] = '\0';
+ if (len == 1) {
+ if (*message == '\a') {
+ strcpy(buf, "\\a");
+ } else if (*message == '\b') {
+ strcpy(buf, "\\b");
+ } else if (*message == '\t') {
+ strcpy(buf, "\\t");
+ } else if (*message == '\v') {
+ strcpy(buf, "\\v");
+ } else if (*message == '\f') {
+ strcpy(buf, "\\f");
+ } else if (*message == '\r') {
+ strcpy(buf, "\\r");
+ } else if (*message == '\\') {
+ strcpy(buf, "\\\\");
+ } else if ((*message < ' ') || (*message & 0x80)) {
+ snprintf(buf, sizeof(buf), "\\%o", *message & 0377);
+ }
+ }
+ if (!buf[0]) {
+ strncpy(buf, message, len);
+ buf[len] = '\0';
+ }
+ }
+ if (print) {
+ strcpy(p, buf);
+ }
+ p += strlen(buf);
+ message += len;
+ messageLen -= len;
+ }
+ return p - begin;
+}
+
+/**
+ * Formats a log message into a buffer
+ *
+ * Uses defaultBuffer if it can, otherwise malloc()'s a new buffer
+ * If return value != defaultBuffer, caller must call free()
+ * Returns NULL on malloc error
+ */
+
+char *android_log_formatLogLine (
+ AndroidLogFormat *p_format,
+ char *defaultBuffer,
+ size_t defaultBufferSize,
+ const AndroidLogEntry *entry,
+ size_t *p_outLength)
+{
+#if !defined(_WIN32)
+ struct tm tmBuf;
+#endif
+ struct tm* ptm;
+ char timeBuf[32]; /* good margin, 23+nul for msec, 26+nul for usec */
+ char prefixBuf[128], suffixBuf[128];
+ char priChar;
+ int prefixSuffixIsHeaderFooter = 0;
+ char * ret = NULL;
+
+ priChar = filterPriToChar(entry->priority);
+ size_t prefixLen = 0, suffixLen = 0;
+ size_t len;
+
+ /*
+ * Get the current date/time in pretty form
+ *
+ * It's often useful when examining a log with "less" to jump to
+ * a specific point in the file by searching for the date/time stamp.
+ * For this reason it's very annoying to have regexp meta characters
+ * in the time stamp. Don't use forward slashes, parenthesis,
+ * brackets, asterisks, or other special chars here.
+ */
+#if !defined(_WIN32)
+ ptm = localtime_r(&(entry->tv_sec), &tmBuf);
+#else
+ ptm = localtime(&(entry->tv_sec));
+#endif
+ /* strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", ptm); */
+ strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm);
+ len = strlen(timeBuf);
+ if (p_format->usec_time_output) {
+ snprintf(timeBuf + len, sizeof(timeBuf) - len,
+ ".%06ld", entry->tv_nsec / 1000);
+ } else {
+ snprintf(timeBuf + len, sizeof(timeBuf) - len,
+ ".%03ld", entry->tv_nsec / 1000000);
+ }
+
+ /*
+ * Construct a buffer containing the log header and log message.
+ */
+ if (p_format->colored_output) {
+ prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "\x1B[38;5;%dm",
+ colorFromPri(entry->priority));
+ prefixLen = MIN(prefixLen, sizeof(prefixBuf));
+ suffixLen = snprintf(suffixBuf, sizeof(suffixBuf), "\x1B[0m");
+ suffixLen = MIN(suffixLen, sizeof(suffixBuf));
+ }
+
+ switch (p_format->format) {
+ case FORMAT_TAG:
+ len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
+ "%c/%-8s: ", priChar, entry->tag);
+ strcpy(suffixBuf + suffixLen, "\n");
+ ++suffixLen;
+ break;
+ case FORMAT_PROCESS:
+ len = snprintf(suffixBuf + suffixLen, sizeof(suffixBuf) - suffixLen,
+ " (%s)\n", entry->tag);
+ suffixLen += MIN(len, sizeof(suffixBuf) - suffixLen);
+ len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
+ "%c(%5d) ", priChar, entry->pid);
+ break;
+ case FORMAT_THREAD:
+ len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
+ "%c(%5d:%5d) ", priChar, entry->pid, entry->tid);
+ strcpy(suffixBuf + suffixLen, "\n");
+ ++suffixLen;
+ break;
+ case FORMAT_RAW:
+ prefixBuf[prefixLen] = 0;
+ len = 0;
+ strcpy(suffixBuf + suffixLen, "\n");
+ ++suffixLen;
+ break;
+ case FORMAT_TIME:
+ len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
+ "%s %c/%-8s(%5d): ", timeBuf, priChar, entry->tag, entry->pid);
+ strcpy(suffixBuf + suffixLen, "\n");
+ ++suffixLen;
+ break;
+ case FORMAT_THREADTIME:
+ len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
+ "%s %5d %5d %c %-8s: ", timeBuf,
+ entry->pid, entry->tid, priChar, entry->tag);
+ strcpy(suffixBuf + suffixLen, "\n");
+ ++suffixLen;
+ break;
+ case FORMAT_LONG:
+ len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
+ "[ %s %5d:%5d %c/%-8s ]\n",
+ timeBuf, entry->pid, entry->tid, priChar, entry->tag);
+ strcpy(suffixBuf + suffixLen, "\n\n");
+ suffixLen += 2;
+ prefixSuffixIsHeaderFooter = 1;
+ break;
+ case FORMAT_BRIEF:
+ default:
+ len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
+ "%c/%-8s(%5d): ", priChar, entry->tag, entry->pid);
+ strcpy(suffixBuf + suffixLen, "\n");
+ ++suffixLen;
+ break;
+ }
+
+ /* snprintf has a weird return value. It returns what would have been
+ * written given a large enough buffer. In the case that the prefix is
+ * longer then our buffer(128), it messes up the calculations below
+ * possibly causing heap corruption. To avoid this we double check and
+ * set the length at the maximum (size minus null byte)
+ */
+ prefixLen += MIN(len, sizeof(prefixBuf) - prefixLen);
+ suffixLen = MIN(suffixLen, sizeof(suffixBuf));
+
+ /* the following code is tragically unreadable */
+
+ size_t numLines;
+ char *p;
+ size_t bufferSize;
+ const char *pm;
+
+ if (prefixSuffixIsHeaderFooter) {
+ /* we're just wrapping message with a header/footer */
+ numLines = 1;
+ } else {
+ pm = entry->message;
+ numLines = 0;
+
+ /*
+ * The line-end finding here must match the line-end finding
+ * in for ( ... numLines...) loop below
+ */
+ while (pm < (entry->message + entry->messageLen)) {
+ if (*pm++ == '\n') numLines++;
+ }
+ /* plus one line for anything not newline-terminated at the end */
+ if (pm > entry->message && *(pm-1) != '\n') numLines++;
+ }
+
+ /*
+ * this is an upper bound--newlines in message may be counted
+ * extraneously
+ */
+ bufferSize = (numLines * (prefixLen + suffixLen)) + 1;
+ if (p_format->printable_output) {
+ /* Calculate extra length to convert non-printable to printable */
+ bufferSize += convertPrintable(NULL, entry->message, entry->messageLen);
+ } else {
+ bufferSize += entry->messageLen;
+ }
+
+ if (defaultBufferSize >= bufferSize) {
+ ret = defaultBuffer;
+ } else {
+ ret = (char *)malloc(bufferSize);
+
+ if (ret == NULL) {
+ return ret;
+ }
+ }
+
+ ret[0] = '\0'; /* to start strcat off */
+
+ p = ret;
+ pm = entry->message;
+
+ if (prefixSuffixIsHeaderFooter) {
+ strcat(p, prefixBuf);
+ p += prefixLen;
+ if (p_format->printable_output) {
+ p += convertPrintable(p, entry->message, entry->messageLen);
+ } else {
+ strncat(p, entry->message, entry->messageLen);
+ p += entry->messageLen;
+ }
+ strcat(p, suffixBuf);
+ p += suffixLen;
+ } else {
+ while(pm < (entry->message + entry->messageLen)) {
+ const char *lineStart;
+ size_t lineLen;
+ lineStart = pm;
+
+ /* Find the next end-of-line in message */
+ while (pm < (entry->message + entry->messageLen)
+ && *pm != '\n') pm++;
+ lineLen = pm - lineStart;
+
+ strcat(p, prefixBuf);
+ p += prefixLen;
+ if (p_format->printable_output) {
+ p += convertPrintable(p, lineStart, lineLen);
+ } else {
+ strncat(p, lineStart, lineLen);
+ p += lineLen;
+ }
+ strcat(p, suffixBuf);
+ p += suffixLen;
+
+ if (*pm == '\n') pm++;
+ }
+ }
+
+ if (p_outLength != NULL) {
+ *p_outLength = p - ret;
+ }
+
+ return ret;
+}
+
+/**
+ * Either print or do not print log line, based on filter
+ *
+ * Returns count bytes written
+ */
+
+int android_log_printLogLine(
+ AndroidLogFormat *p_format,
+ int fd,
+ const AndroidLogEntry *entry)
+{
+ int ret;
+ char defaultBuffer[512];
+ char *outBuffer = NULL;
+ size_t totalLen;
+
+ outBuffer = android_log_formatLogLine(p_format, defaultBuffer,
+ sizeof(defaultBuffer), entry, &totalLen);
+
+ if (!outBuffer)
+ return -1;
+
+ do {
+ ret = write(fd, outBuffer, totalLen);
+ } while (ret < 0 && errno == EINTR);
+
+ if (ret < 0) {
+ fprintf(stderr, "+++ LOG: write failed (errno=%d)\n", errno);
+ ret = 0;
+ goto done;
+ }
+
+ if (((size_t)ret) < totalLen) {
+ fprintf(stderr, "+++ LOG: write partial (%d of %d)\n", ret,
+ (int)totalLen);
+ goto done;
+ }
+
+done:
+ if (outBuffer != defaultBuffer) {
+ free(outBuffer);
+ }
+
+ return ret;
+}
diff --git a/liblog/tests/Android.mk b/liblog/tests/Android.mk
new file mode 100644
index 0000000..a407c50
--- /dev/null
+++ b/liblog/tests/Android.mk
@@ -0,0 +1,82 @@
+#
+# Copyright (C) 2013-2014 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)
+
+# -----------------------------------------------------------------------------
+# Benchmarks.
+# -----------------------------------------------------------------------------
+
+test_module_prefix := liblog-
+test_tags := tests
+
+benchmark_c_flags := \
+ -Ibionic/tests \
+ -Wall -Wextra \
+ -Werror \
+ -fno-builtin \
+ -std=gnu++11
+
+benchmark_src_files := \
+ benchmark_main.cpp \
+ liblog_benchmark.cpp
+
+# Build benchmarks for the device. Run with:
+# adb shell liblog-benchmarks
+include $(CLEAR_VARS)
+LOCAL_MODULE := $(test_module_prefix)benchmarks
+LOCAL_MODULE_TAGS := $(test_tags)
+LOCAL_CFLAGS += $(benchmark_c_flags)
+LOCAL_SHARED_LIBRARIES += liblog libm
+LOCAL_SRC_FILES := $(benchmark_src_files)
+include $(BUILD_NATIVE_TEST)
+
+# -----------------------------------------------------------------------------
+# Unit tests.
+# -----------------------------------------------------------------------------
+
+test_c_flags := \
+ -fstack-protector-all \
+ -g \
+ -Wall -Wextra \
+ -Werror \
+ -fno-builtin \
+ -std=gnu++11
+
+test_src_files := \
+ liblog_test.cpp
+
+# to prevent breaking the build if bionic not relatively visible to us
+ifneq ($(wildcard $(LOCAL_PATH)/../../../../bionic/libc/bionic/libc_logging.cpp),)
+
+test_src_files += \
+ libc_test.cpp
+
+ifneq ($(TARGET_USES_LOGD),false)
+test_c_flags += -DTARGET_USES_LOGD
+endif
+
+endif
+
+# Build tests for the device (with .so). Run with:
+# adb shell /data/nativetest/liblog-unit-tests/liblog-unit-tests
+include $(CLEAR_VARS)
+LOCAL_MODULE := $(test_module_prefix)unit-tests
+LOCAL_MODULE_TAGS := $(test_tags)
+LOCAL_CFLAGS += $(test_c_flags)
+LOCAL_SHARED_LIBRARIES := liblog libcutils
+LOCAL_SRC_FILES := $(test_src_files)
+include $(BUILD_NATIVE_TEST)
diff --git a/liblog/tests/benchmark.h b/liblog/tests/benchmark.h
new file mode 100644
index 0000000..7f96e6d
--- /dev/null
+++ b/liblog/tests/benchmark.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2012-2014 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <vector>
+
+#ifndef BIONIC_BENCHMARK_H_
+#define BIONIC_BENCHMARK_H_
+
+namespace testing {
+
+class Benchmark;
+template <typename T> class BenchmarkWantsArg;
+template <typename T> class BenchmarkWithArg;
+
+void BenchmarkRegister(Benchmark* bm);
+int PrettyPrintInt(char* str, int len, unsigned int arg);
+
+class Benchmark {
+ public:
+ Benchmark(const char* name, void (*fn)(int)) : name_(strdup(name)), fn_(fn) {
+ BenchmarkRegister(this);
+ }
+ Benchmark(const char* name) : name_(strdup(name)), fn_(NULL) {}
+
+ virtual ~Benchmark() {
+ free(name_);
+ }
+
+ const char* Name() { return name_; }
+ virtual const char* ArgName() { return NULL; }
+ virtual void RunFn(int iterations) { fn_(iterations); }
+
+ protected:
+ char* name_;
+
+ private:
+ void (*fn_)(int);
+};
+
+template <typename T>
+class BenchmarkWantsArgBase : public Benchmark {
+ public:
+ BenchmarkWantsArgBase(const char* name, void (*fn)(int, T)) : Benchmark(name) {
+ fn_arg_ = fn;
+ }
+
+ BenchmarkWantsArgBase<T>* Arg(const char* arg_name, T arg) {
+ BenchmarkRegister(new BenchmarkWithArg<T>(name_, fn_arg_, arg_name, arg));
+ return this;
+ }
+
+ protected:
+ virtual void RunFn(int) { printf("can't run arg benchmark %s without arg\n", Name()); }
+ void (*fn_arg_)(int, T);
+};
+
+template <typename T>
+class BenchmarkWithArg : public BenchmarkWantsArg<T> {
+ public:
+ BenchmarkWithArg(const char* name, void (*fn)(int, T), const char* arg_name, T arg) :
+ BenchmarkWantsArg<T>(name, fn), arg_(arg) {
+ arg_name_ = strdup(arg_name);
+ }
+
+ virtual ~BenchmarkWithArg() {
+ free(arg_name_);
+ }
+
+ virtual const char* ArgName() { return arg_name_; }
+
+ protected:
+ virtual void RunFn(int iterations) { BenchmarkWantsArg<T>::fn_arg_(iterations, arg_); }
+
+ private:
+ T arg_;
+ char* arg_name_;
+};
+
+template <typename T>
+class BenchmarkWantsArg : public BenchmarkWantsArgBase<T> {
+ public:
+ BenchmarkWantsArg<T>(const char* name, void (*fn)(int, T)) :
+ BenchmarkWantsArgBase<T>(name, fn) { }
+};
+
+template <>
+class BenchmarkWantsArg<int> : public BenchmarkWantsArgBase<int> {
+ public:
+ BenchmarkWantsArg<int>(const char* name, void (*fn)(int, int)) :
+ BenchmarkWantsArgBase<int>(name, fn) { }
+
+ BenchmarkWantsArg<int>* Arg(int arg) {
+ char arg_name[100];
+ PrettyPrintInt(arg_name, sizeof(arg_name), arg);
+ BenchmarkRegister(new BenchmarkWithArg<int>(name_, fn_arg_, arg_name, arg));
+ return this;
+ }
+};
+
+static inline Benchmark* BenchmarkFactory(const char* name, void (*fn)(int)) {
+ return new Benchmark(name, fn);
+}
+
+template <typename T>
+static inline BenchmarkWantsArg<T>* BenchmarkFactory(const char* name, void (*fn)(int, T)) {
+ return new BenchmarkWantsArg<T>(name, fn);
+}
+
+} // namespace testing
+
+template <typename T>
+static inline void BenchmarkAddArg(::testing::Benchmark* b, const char* name, T arg) {
+ ::testing::BenchmarkWantsArg<T>* ba;
+ ba = static_cast< ::testing::BenchmarkWantsArg<T>* >(b);
+ ba->Arg(name, arg);
+}
+
+void SetBenchmarkBytesProcessed(uint64_t);
+void ResetBenchmarkTiming(void);
+void StopBenchmarkTiming(void);
+void StartBenchmarkTiming(void);
+void StartBenchmarkTiming(uint64_t);
+void StopBenchmarkTiming(uint64_t);
+
+#define BENCHMARK(f) \
+ static ::testing::Benchmark* _benchmark_##f __attribute__((unused)) = \
+ (::testing::Benchmark*)::testing::BenchmarkFactory(#f, f)
+
+#endif // BIONIC_BENCHMARK_H_
diff --git a/liblog/tests/benchmark_main.cpp b/liblog/tests/benchmark_main.cpp
new file mode 100644
index 0000000..e5ef970
--- /dev/null
+++ b/liblog/tests/benchmark_main.cpp
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2012-2014 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 <benchmark.h>
+
+#include <inttypes.h>
+#include <math.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <string>
+#include <map>
+#include <vector>
+
+static uint64_t gBytesProcessed;
+static uint64_t gBenchmarkTotalTimeNs;
+static uint64_t gBenchmarkTotalTimeNsSquared;
+static uint64_t gBenchmarkNum;
+static uint64_t gBenchmarkStartTimeNs;
+
+typedef std::vector< ::testing::Benchmark* > BenchmarkList;
+static BenchmarkList* gBenchmarks;
+
+static int Round(int n) {
+ int base = 1;
+ while (base*10 < n) {
+ base *= 10;
+ }
+ if (n < 2*base) {
+ return 2*base;
+ }
+ if (n < 5*base) {
+ return 5*base;
+ }
+ return 10*base;
+}
+
+static uint64_t NanoTime() {
+ struct timespec t;
+ t.tv_sec = t.tv_nsec = 0;
+ clock_gettime(CLOCK_MONOTONIC, &t);
+ return static_cast<uint64_t>(t.tv_sec) * 1000000000ULL + t.tv_nsec;
+}
+
+namespace testing {
+
+int PrettyPrintInt(char* str, int len, unsigned int arg)
+{
+ if (arg >= (1<<30) && arg % (1<<30) == 0) {
+ return snprintf(str, len, "%uGi", arg/(1<<30));
+ } else if (arg >= (1<<20) && arg % (1<<20) == 0) {
+ return snprintf(str, len, "%uMi", arg/(1<<20));
+ } else if (arg >= (1<<10) && arg % (1<<10) == 0) {
+ return snprintf(str, len, "%uKi", arg/(1<<10));
+ } else if (arg >= 1000000000 && arg % 1000000000 == 0) {
+ return snprintf(str, len, "%uG", arg/1000000000);
+ } else if (arg >= 1000000 && arg % 1000000 == 0) {
+ return snprintf(str, len, "%uM", arg/1000000);
+ } else if (arg >= 1000 && arg % 1000 == 0) {
+ return snprintf(str, len, "%uK", arg/1000);
+ } else {
+ return snprintf(str, len, "%u", arg);
+ }
+}
+
+bool ShouldRun(Benchmark* b, int argc, char* argv[]) {
+ if (argc == 1) {
+ return true; // With no arguments, we run all benchmarks.
+ }
+ // Otherwise, we interpret each argument as a regular expression and
+ // see if any of our benchmarks match.
+ for (int i = 1; i < argc; i++) {
+ regex_t re;
+ if (regcomp(&re, argv[i], 0) != 0) {
+ fprintf(stderr, "couldn't compile \"%s\" as a regular expression!\n", argv[i]);
+ exit(EXIT_FAILURE);
+ }
+ int match = regexec(&re, b->Name(), 0, NULL, 0);
+ regfree(&re);
+ if (match != REG_NOMATCH) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void BenchmarkRegister(Benchmark* b) {
+ if (gBenchmarks == NULL) {
+ gBenchmarks = new BenchmarkList;
+ }
+ gBenchmarks->push_back(b);
+}
+
+void RunRepeatedly(Benchmark* b, int iterations) {
+ gBytesProcessed = 0;
+ ResetBenchmarkTiming();
+ uint64_t StartTimeNs = NanoTime();
+ b->RunFn(iterations);
+ // Catch us if we fail to log anything.
+ if ((gBenchmarkTotalTimeNs == 0)
+ && (StartTimeNs != 0)
+ && (gBenchmarkStartTimeNs == 0)) {
+ gBenchmarkTotalTimeNs = NanoTime() - StartTimeNs;
+ }
+}
+
+void Run(Benchmark* b) {
+ // run once in case it's expensive
+ unsigned iterations = 1;
+ uint64_t s = NanoTime();
+ RunRepeatedly(b, iterations);
+ s = NanoTime() - s;
+ while (s < 2e9 && gBenchmarkTotalTimeNs < 1e9 && iterations < 1e9) {
+ unsigned last = iterations;
+ if (gBenchmarkTotalTimeNs/iterations == 0) {
+ iterations = 1e9;
+ } else {
+ iterations = 1e9 / (gBenchmarkTotalTimeNs/iterations);
+ }
+ iterations = std::max(last + 1, std::min(iterations + iterations/2, 100*last));
+ iterations = Round(iterations);
+ s = NanoTime();
+ RunRepeatedly(b, iterations);
+ s = NanoTime() - s;
+ }
+
+ char throughput[100];
+ throughput[0] = '\0';
+ if (gBenchmarkTotalTimeNs > 0 && gBytesProcessed > 0) {
+ double mib_processed = static_cast<double>(gBytesProcessed)/1e6;
+ double seconds = static_cast<double>(gBenchmarkTotalTimeNs)/1e9;
+ snprintf(throughput, sizeof(throughput), " %8.2f MiB/s", mib_processed/seconds);
+ }
+
+ char full_name[100];
+ snprintf(full_name, sizeof(full_name), "%s%s%s", b->Name(),
+ b->ArgName() ? "/" : "",
+ b->ArgName() ? b->ArgName() : "");
+
+ uint64_t mean = gBenchmarkTotalTimeNs / iterations;
+ uint64_t sdev = 0;
+ if (gBenchmarkNum == iterations) {
+ mean = gBenchmarkTotalTimeNs / gBenchmarkNum;
+ uint64_t nXvariance = gBenchmarkTotalTimeNsSquared * gBenchmarkNum
+ - (gBenchmarkTotalTimeNs * gBenchmarkTotalTimeNs);
+ sdev = (sqrt((double)nXvariance) / gBenchmarkNum / gBenchmarkNum) + 0.5;
+ }
+ if (mean > (10000 * sdev)) {
+ printf("%-25s %10" PRIu64 " %10" PRIu64 "%s\n", full_name,
+ static_cast<uint64_t>(iterations), mean, throughput);
+ } else {
+ printf("%-25s %10" PRIu64 " %10" PRIu64 "(\317\203%" PRIu64 ")%s\n", full_name,
+ static_cast<uint64_t>(iterations), mean, sdev, throughput);
+ }
+ fflush(stdout);
+}
+
+} // namespace testing
+
+void SetBenchmarkBytesProcessed(uint64_t x) {
+ gBytesProcessed = x;
+}
+
+void ResetBenchmarkTiming() {
+ gBenchmarkStartTimeNs = 0;
+ gBenchmarkTotalTimeNs = 0;
+ gBenchmarkTotalTimeNsSquared = 0;
+ gBenchmarkNum = 0;
+}
+
+void StopBenchmarkTiming(void) {
+ if (gBenchmarkStartTimeNs != 0) {
+ int64_t diff = NanoTime() - gBenchmarkStartTimeNs;
+ gBenchmarkTotalTimeNs += diff;
+ gBenchmarkTotalTimeNsSquared += diff * diff;
+ ++gBenchmarkNum;
+ }
+ gBenchmarkStartTimeNs = 0;
+}
+
+void StartBenchmarkTiming(void) {
+ if (gBenchmarkStartTimeNs == 0) {
+ gBenchmarkStartTimeNs = NanoTime();
+ }
+}
+
+void StopBenchmarkTiming(uint64_t NanoTime) {
+ if (gBenchmarkStartTimeNs != 0) {
+ int64_t diff = NanoTime - gBenchmarkStartTimeNs;
+ gBenchmarkTotalTimeNs += diff;
+ gBenchmarkTotalTimeNsSquared += diff * diff;
+ if (NanoTime != 0) {
+ ++gBenchmarkNum;
+ }
+ }
+ gBenchmarkStartTimeNs = 0;
+}
+
+void StartBenchmarkTiming(uint64_t NanoTime) {
+ if (gBenchmarkStartTimeNs == 0) {
+ gBenchmarkStartTimeNs = NanoTime;
+ }
+}
+
+int main(int argc, char* argv[]) {
+ if (gBenchmarks->empty()) {
+ fprintf(stderr, "No benchmarks registered!\n");
+ exit(EXIT_FAILURE);
+ }
+
+ bool need_header = true;
+ for (auto b : *gBenchmarks) {
+ if (ShouldRun(b, argc, argv)) {
+ if (need_header) {
+ printf("%-25s %10s %10s\n", "", "iterations", "ns/op");
+ fflush(stdout);
+ need_header = false;
+ }
+ Run(b);
+ }
+ }
+
+ if (need_header) {
+ fprintf(stderr, "No matching benchmarks!\n");
+ fprintf(stderr, "Available benchmarks:\n");
+ for (auto b : *gBenchmarks) {
+ fprintf(stderr, " %s\n", b->Name());
+ }
+ exit(EXIT_FAILURE);
+ }
+
+ return 0;
+}
diff --git a/liblog/tests/libc_test.cpp b/liblog/tests/libc_test.cpp
new file mode 100644
index 0000000..0e84f4e
--- /dev/null
+++ b/liblog/tests/libc_test.cpp
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2014 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 <fcntl.h>
+#include <sys/cdefs.h>
+
+#include <gtest/gtest.h>
+
+// Should be in bionic test suite, *but* we are using liblog to confirm
+// end-to-end logging, so let the overly cute oedipus complex begin ...
+#include "../../../../bionic/libc/bionic/libc_logging.cpp" // not Standalone
+#define _ANDROID_LOG_H // Priorities redefined
+#define _LIBS_LOG_LOG_H // log ids redefined
+typedef unsigned char log_id_t; // log_id_t missing as a result
+#ifdef TARGET_USES_LOGD
+#define _LIBS_LOG_LOG_READ_H // log_time redefined
+#endif
+
+#include <log/log.h>
+#include <log/logger.h>
+#include <log/log_read.h>
+
+TEST(libc, __libc_android_log_event_int) {
+ struct logger_list *logger_list;
+
+ pid_t pid = getpid();
+
+ ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+ LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+
+ struct timespec ts;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ int value = ts.tv_nsec;
+
+ __libc_android_log_event_int(0, value);
+ usleep(1000000);
+
+ int count = 0;
+
+ for (;;) {
+ log_msg log_msg;
+ if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+ break;
+ }
+
+ ASSERT_EQ(log_msg.entry.pid, pid);
+
+ if ((log_msg.entry.len != (4 + 1 + 4))
+ || ((int)log_msg.id() != LOG_ID_EVENTS)) {
+ continue;
+ }
+
+ char *eventData = log_msg.msg();
+
+ int incoming = (eventData[0] & 0xFF) |
+ ((eventData[1] & 0xFF) << 8) |
+ ((eventData[2] & 0xFF) << 16) |
+ ((eventData[3] & 0xFF) << 24);
+
+ if (incoming != 0) {
+ continue;
+ }
+
+ if (eventData[4] != EVENT_TYPE_INT) {
+ continue;
+ }
+
+ incoming = (eventData[4 + 1 + 0] & 0xFF) |
+ ((eventData[4 + 1 + 1] & 0xFF) << 8) |
+ ((eventData[4 + 1 + 2] & 0xFF) << 16) |
+ ((eventData[4 + 1 + 3] & 0xFF) << 24);
+
+ if (incoming == value) {
+ ++count;
+ }
+ }
+
+ EXPECT_EQ(1, count);
+
+ android_logger_list_close(logger_list);
+}
+
+TEST(libc, __libc_fatal_no_abort) {
+ struct logger_list *logger_list;
+
+ pid_t pid = getpid();
+
+ ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+ (log_id_t)LOG_ID_CRASH, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+
+ char b[80];
+ struct timespec ts;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+
+ __libc_fatal_no_abort("%u.%09u", (unsigned)ts.tv_sec, (unsigned)ts.tv_nsec);
+ snprintf(b, sizeof(b),"%u.%09u", (unsigned)ts.tv_sec, (unsigned)ts.tv_nsec);
+ usleep(1000000);
+
+ int count = 0;
+
+ for (;;) {
+ log_msg log_msg;
+ if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+ break;
+ }
+
+ ASSERT_EQ(log_msg.entry.pid, pid);
+
+ if ((int)log_msg.id() != LOG_ID_CRASH) {
+ continue;
+ }
+
+ char *data = log_msg.msg();
+
+ if ((*data == ANDROID_LOG_FATAL)
+ && !strcmp(data + 1, "libc")
+ && !strcmp(data + 1 + strlen(data + 1) + 1, b)) {
+ ++count;
+ }
+ }
+
+ EXPECT_EQ(1, count);
+
+ android_logger_list_close(logger_list);
+}
+
+TEST(libc, __pstore_append) {
+ FILE *fp;
+ ASSERT_TRUE(NULL != (fp = fopen("/dev/pmsg0", "a")));
+ static const char message[] = "libc.__pstore_append\n";
+ ASSERT_EQ((size_t)1, fwrite(message, sizeof(message), 1, fp));
+ ASSERT_EQ(0, fclose(fp));
+ fprintf(stderr, "Reboot, ensure string libc.__pstore_append is in /sys/fs/pstore/pmsg-ramoops-0\n");
+}
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
new file mode 100644
index 0000000..b594634
--- /dev/null
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2013-2014 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/socket.h>
+#include <cutils/sockets.h>
+#include <log/log.h>
+#include <log/logger.h>
+#include <log/log_read.h>
+
+#include "benchmark.h"
+
+// enhanced version of LOG_FAILURE_RETRY to add support for EAGAIN and
+// non-syscall libs. Since we are benchmarking, or using this in the emergency
+// signal to stuff a terminating code, we do NOT want to introduce
+// a syscall or usleep on EAGAIN retry.
+#define LOG_FAILURE_RETRY(exp) ({ \
+ typeof (exp) _rc; \
+ do { \
+ _rc = (exp); \
+ } while (((_rc == -1) \
+ && ((errno == EINTR) \
+ || (errno == EAGAIN))) \
+ || (_rc == -EINTR) \
+ || (_rc == -EAGAIN)); \
+ _rc; })
+
+/*
+ * Measure the fastest rate we can reliabley stuff print messages into
+ * the log at high pressure. Expect this to be less than double the process
+ * wakeup time (2ms?)
+ */
+static void BM_log_maximum_retry(int iters) {
+ StartBenchmarkTiming();
+
+ for (int i = 0; i < iters; ++i) {
+ LOG_FAILURE_RETRY(
+ __android_log_print(ANDROID_LOG_INFO,
+ "BM_log_maximum_retry", "%d", i));
+ }
+
+ StopBenchmarkTiming();
+}
+BENCHMARK(BM_log_maximum_retry);
+
+/*
+ * Measure the fastest rate we can stuff print messages into the log
+ * at high pressure. Expect this to be less than double the process wakeup
+ * time (2ms?)
+ */
+static void BM_log_maximum(int iters) {
+ StartBenchmarkTiming();
+
+ for (int i = 0; i < iters; ++i) {
+ __android_log_print(ANDROID_LOG_INFO, "BM_log_maximum", "%d", i);
+ }
+
+ StopBenchmarkTiming();
+}
+BENCHMARK(BM_log_maximum);
+
+/*
+ * Measure the time it takes to submit the android logging call using
+ * discrete acquisition under light load. Expect this to be a pair of
+ * syscall periods (2us).
+ */
+static void BM_clock_overhead(int iters) {
+ for (int i = 0; i < iters; ++i) {
+ StartBenchmarkTiming();
+ StopBenchmarkTiming();
+ }
+}
+BENCHMARK(BM_clock_overhead);
+
+/*
+ * Measure the time it takes to submit the android logging call using
+ * discrete acquisition under light load. Expect this to be a dozen or so
+ * syscall periods (40us).
+ */
+static void BM_log_overhead(int iters) {
+ for (int i = 0; i < iters; ++i) {
+ StartBenchmarkTiming();
+ __android_log_print(ANDROID_LOG_INFO, "BM_log_overhead", "%d", i);
+ StopBenchmarkTiming();
+ usleep(1000);
+ }
+}
+BENCHMARK(BM_log_overhead);
+
+static void caught_latency(int /*signum*/)
+{
+ unsigned long long v = 0xDEADBEEFA55A5AA5ULL;
+
+ LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v)));
+}
+
+static unsigned long long caught_convert(char *cp)
+{
+ unsigned long long l = cp[0] & 0xFF;
+ l |= (unsigned long long) (cp[1] & 0xFF) << 8;
+ l |= (unsigned long long) (cp[2] & 0xFF) << 16;
+ l |= (unsigned long long) (cp[3] & 0xFF) << 24;
+ l |= (unsigned long long) (cp[4] & 0xFF) << 32;
+ l |= (unsigned long long) (cp[5] & 0xFF) << 40;
+ l |= (unsigned long long) (cp[6] & 0xFF) << 48;
+ l |= (unsigned long long) (cp[7] & 0xFF) << 56;
+ return l;
+}
+
+static const int alarm_time = 3;
+
+/*
+ * Measure the time it takes for the logd posting call to acquire the
+ * timestamp to place into the internal record. Expect this to be less than
+ * 4 syscalls (3us).
+ */
+static void BM_log_latency(int iters) {
+ pid_t pid = getpid();
+
+ struct logger_list * logger_list = android_logger_list_open(LOG_ID_EVENTS,
+ ANDROID_LOG_RDONLY, 0, pid);
+
+ if (!logger_list) {
+ fprintf(stderr, "Unable to open events log: %s\n", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ signal(SIGALRM, caught_latency);
+ alarm(alarm_time);
+
+ for (int j = 0, i = 0; i < iters && j < 10*iters; ++i, ++j) {
+ log_time ts;
+ LOG_FAILURE_RETRY((
+ ts = log_time(CLOCK_REALTIME),
+ android_btWriteLog(0, EVENT_TYPE_LONG, &ts, sizeof(ts))));
+
+ for (;;) {
+ log_msg log_msg;
+ int ret = android_logger_list_read(logger_list, &log_msg);
+ alarm(alarm_time);
+
+ if (ret <= 0) {
+ iters = i;
+ break;
+ }
+ if ((log_msg.entry.len != (4 + 1 + 8))
+ || (log_msg.id() != LOG_ID_EVENTS)) {
+ continue;
+ }
+
+ char* eventData = log_msg.msg();
+
+ if (eventData[4] != EVENT_TYPE_LONG) {
+ continue;
+ }
+ log_time tx(eventData + 4 + 1);
+ if (ts != tx) {
+ if (0xDEADBEEFA55A5AA5ULL == caught_convert(eventData + 4 + 1)) {
+ iters = i;
+ break;
+ }
+ continue;
+ }
+
+ uint64_t start = ts.nsec();
+ uint64_t end = log_msg.nsec();
+ if (end >= start) {
+ StartBenchmarkTiming(start);
+ StopBenchmarkTiming(end);
+ } else {
+ --i;
+ }
+ break;
+ }
+ }
+
+ signal(SIGALRM, SIG_DFL);
+ alarm(0);
+
+ android_logger_list_free(logger_list);
+}
+BENCHMARK(BM_log_latency);
+
+static void caught_delay(int /*signum*/)
+{
+ unsigned long long v = 0xDEADBEEFA55A5AA6ULL;
+
+ LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v)));
+}
+
+/*
+ * Measure the time it takes for the logd posting call to make it into
+ * the logs. Expect this to be less than double the process wakeup time (2ms).
+ */
+static void BM_log_delay(int iters) {
+ pid_t pid = getpid();
+
+ struct logger_list * logger_list = android_logger_list_open(LOG_ID_EVENTS,
+ ANDROID_LOG_RDONLY, 0, pid);
+
+ if (!logger_list) {
+ fprintf(stderr, "Unable to open events log: %s\n", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ signal(SIGALRM, caught_delay);
+ alarm(alarm_time);
+
+ StartBenchmarkTiming();
+
+ for (int i = 0; i < iters; ++i) {
+ log_time ts(CLOCK_REALTIME);
+
+ LOG_FAILURE_RETRY(
+ android_btWriteLog(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
+
+ for (;;) {
+ log_msg log_msg;
+ int ret = android_logger_list_read(logger_list, &log_msg);
+ alarm(alarm_time);
+
+ if (ret <= 0) {
+ iters = i;
+ break;
+ }
+ if ((log_msg.entry.len != (4 + 1 + 8))
+ || (log_msg.id() != LOG_ID_EVENTS)) {
+ continue;
+ }
+
+ char* eventData = log_msg.msg();
+
+ if (eventData[4] != EVENT_TYPE_LONG) {
+ continue;
+ }
+ log_time tx(eventData + 4 + 1);
+ if (ts != tx) {
+ if (0xDEADBEEFA55A5AA6ULL == caught_convert(eventData + 4 + 1)) {
+ iters = i;
+ break;
+ }
+ continue;
+ }
+
+ break;
+ }
+ }
+
+ signal(SIGALRM, SIG_DFL);
+ alarm(0);
+
+ StopBenchmarkTiming();
+
+ android_logger_list_free(logger_list);
+}
+BENCHMARK(BM_log_delay);
+
+/*
+ * Measure the time it takes for __android_log_is_loggable.
+ */
+static void BM_is_loggable(int iters) {
+ StartBenchmarkTiming();
+
+ for (int i = 0; i < iters; ++i) {
+ __android_log_is_loggable(ANDROID_LOG_WARN, "logd", ANDROID_LOG_VERBOSE);
+ }
+
+ StopBenchmarkTiming();
+}
+BENCHMARK(BM_is_loggable);
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
new file mode 100644
index 0000000..abe0239
--- /dev/null
+++ b/liblog/tests/liblog_test.cpp
@@ -0,0 +1,878 @@
+/*
+ * Copyright (C) 2013-2014 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 <fcntl.h>
+#include <inttypes.h>
+#include <signal.h>
+
+#include <cutils/properties.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+#include <log/logger.h>
+#include <log/log_read.h>
+#include <log/logprint.h>
+
+// enhanced version of LOG_FAILURE_RETRY to add support for EAGAIN and
+// non-syscall libs. Since we are only using this in the emergency of
+// a signal to stuff a terminating code into the logs, we will spin rather
+// than try a usleep.
+#define LOG_FAILURE_RETRY(exp) ({ \
+ typeof (exp) _rc; \
+ do { \
+ _rc = (exp); \
+ } while (((_rc == -1) \
+ && ((errno == EINTR) \
+ || (errno == EAGAIN))) \
+ || (_rc == -EINTR) \
+ || (_rc == -EAGAIN)); \
+ _rc; })
+
+TEST(liblog, __android_log_buf_print) {
+ EXPECT_LT(0, __android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO,
+ "TEST__android_log_buf_print",
+ "radio"));
+ usleep(1000);
+ EXPECT_LT(0, __android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO,
+ "TEST__android_log_buf_print",
+ "system"));
+ usleep(1000);
+ EXPECT_LT(0, __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO,
+ "TEST__android_log_buf_print",
+ "main"));
+ usleep(1000);
+}
+
+TEST(liblog, __android_log_buf_write) {
+ EXPECT_LT(0, __android_log_buf_write(LOG_ID_RADIO, ANDROID_LOG_INFO,
+ "TEST__android_log_buf_write",
+ "radio"));
+ usleep(1000);
+ EXPECT_LT(0, __android_log_buf_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO,
+ "TEST__android_log_buf_write",
+ "system"));
+ usleep(1000);
+ EXPECT_LT(0, __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_INFO,
+ "TEST__android_log_buf_write",
+ "main"));
+ usleep(1000);
+}
+
+TEST(liblog, __android_log_btwrite) {
+ int intBuf = 0xDEADBEEF;
+ EXPECT_LT(0, __android_log_btwrite(0,
+ EVENT_TYPE_INT,
+ &intBuf, sizeof(intBuf)));
+ long long longBuf = 0xDEADBEEFA55A5AA5;
+ EXPECT_LT(0, __android_log_btwrite(0,
+ EVENT_TYPE_LONG,
+ &longBuf, sizeof(longBuf)));
+ usleep(1000);
+ char Buf[] = "\20\0\0\0DeAdBeEfA55a5aA5";
+ EXPECT_LT(0, __android_log_btwrite(0,
+ EVENT_TYPE_STRING,
+ Buf, sizeof(Buf) - 1));
+ usleep(1000);
+}
+
+static void* ConcurrentPrintFn(void *arg) {
+ int ret = __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO,
+ "TEST__android_log_print", "Concurrent %" PRIuPTR,
+ reinterpret_cast<uintptr_t>(arg));
+ return reinterpret_cast<void*>(ret);
+}
+
+#define NUM_CONCURRENT 64
+#define _concurrent_name(a,n) a##__concurrent##n
+#define concurrent_name(a,n) _concurrent_name(a,n)
+
+TEST(liblog, concurrent_name(__android_log_buf_print, NUM_CONCURRENT)) {
+ pthread_t t[NUM_CONCURRENT];
+ int i;
+ for (i=0; i < NUM_CONCURRENT; i++) {
+ ASSERT_EQ(0, pthread_create(&t[i], NULL,
+ ConcurrentPrintFn,
+ reinterpret_cast<void *>(i)));
+ }
+ int ret = 0;
+ for (i=0; i < NUM_CONCURRENT; i++) {
+ void* result;
+ ASSERT_EQ(0, pthread_join(t[i], &result));
+ int this_result = reinterpret_cast<uintptr_t>(result);
+ if ((0 == ret) && (0 != this_result)) {
+ ret = this_result;
+ }
+ }
+ ASSERT_LT(0, ret);
+}
+
+TEST(liblog, __android_log_btwrite__android_logger_list_read) {
+ struct logger_list *logger_list;
+
+ pid_t pid = getpid();
+
+ ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+ LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+
+ log_time ts(CLOCK_MONOTONIC);
+
+ ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
+ usleep(1000000);
+
+ int count = 0;
+
+ for (;;) {
+ log_msg log_msg;
+ if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+ break;
+ }
+
+ ASSERT_EQ(log_msg.entry.pid, pid);
+
+ if ((log_msg.entry.len != (4 + 1 + 8))
+ || (log_msg.id() != LOG_ID_EVENTS)) {
+ continue;
+ }
+
+ char *eventData = log_msg.msg();
+
+ if (eventData[4] != EVENT_TYPE_LONG) {
+ continue;
+ }
+
+ log_time tx(eventData + 4 + 1);
+ if (ts == tx) {
+ ++count;
+ }
+ }
+
+ EXPECT_EQ(1, count);
+
+ android_logger_list_close(logger_list);
+}
+
+static unsigned signaled;
+log_time signal_time;
+
+static void caught_blocking(int /*signum*/)
+{
+ unsigned long long v = 0xDEADBEEFA55A0000ULL;
+
+ v += getpid() & 0xFFFF;
+
+ ++signaled;
+ if ((signal_time.tv_sec == 0) && (signal_time.tv_nsec == 0)) {
+ signal_time = log_time(CLOCK_MONOTONIC);
+ signal_time.tv_sec += 2;
+ }
+
+ LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v)));
+}
+
+// Fill in current process user and system time in 10ms increments
+static void get_ticks(unsigned long long *uticks, unsigned long long *sticks)
+{
+ *uticks = *sticks = 0;
+
+ pid_t pid = getpid();
+
+ char buffer[512];
+ snprintf(buffer, sizeof(buffer), "/proc/%u/stat", pid);
+
+ FILE *fp = fopen(buffer, "r");
+ if (!fp) {
+ return;
+ }
+
+ char *cp = fgets(buffer, sizeof(buffer), fp);
+ fclose(fp);
+ if (!cp) {
+ return;
+ }
+
+ pid_t d;
+ char s[sizeof(buffer)];
+ char c;
+ long long ll;
+ unsigned long long ull;
+
+ if (15 != sscanf(buffer,
+ "%d %s %c %lld %lld %lld %lld %lld %llu %llu %llu %llu %llu %llu %llu ",
+ &d, s, &c, &ll, &ll, &ll, &ll, &ll, &ull, &ull, &ull, &ull, &ull,
+ uticks, sticks)) {
+ *uticks = *sticks = 0;
+ }
+}
+
+TEST(liblog, android_logger_list_read__cpu) {
+ struct logger_list *logger_list;
+ unsigned long long v = 0xDEADBEEFA55A0000ULL;
+
+ pid_t pid = getpid();
+
+ v += pid & 0xFFFF;
+
+ ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+ LOG_ID_EVENTS, ANDROID_LOG_RDONLY, 1000, pid)));
+
+ int count = 0;
+
+ int signals = 0;
+
+ unsigned long long uticks_start;
+ unsigned long long sticks_start;
+ get_ticks(&uticks_start, &sticks_start);
+
+ const unsigned alarm_time = 10;
+
+ memset(&signal_time, 0, sizeof(signal_time));
+
+ signal(SIGALRM, caught_blocking);
+ alarm(alarm_time);
+
+ signaled = 0;
+
+ do {
+ log_msg log_msg;
+ if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+ break;
+ }
+
+ alarm(alarm_time);
+
+ ++count;
+
+ ASSERT_EQ(log_msg.entry.pid, pid);
+
+ if ((log_msg.entry.len != (4 + 1 + 8))
+ || (log_msg.id() != LOG_ID_EVENTS)) {
+ continue;
+ }
+
+ char *eventData = log_msg.msg();
+
+ if (eventData[4] != EVENT_TYPE_LONG) {
+ continue;
+ }
+
+ unsigned long long l = eventData[4 + 1 + 0] & 0xFF;
+ l |= (unsigned long long) (eventData[4 + 1 + 1] & 0xFF) << 8;
+ l |= (unsigned long long) (eventData[4 + 1 + 2] & 0xFF) << 16;
+ l |= (unsigned long long) (eventData[4 + 1 + 3] & 0xFF) << 24;
+ l |= (unsigned long long) (eventData[4 + 1 + 4] & 0xFF) << 32;
+ l |= (unsigned long long) (eventData[4 + 1 + 5] & 0xFF) << 40;
+ l |= (unsigned long long) (eventData[4 + 1 + 6] & 0xFF) << 48;
+ l |= (unsigned long long) (eventData[4 + 1 + 7] & 0xFF) << 56;
+
+ if (l == v) {
+ ++signals;
+ break;
+ }
+ } while (!signaled || (log_time(CLOCK_MONOTONIC) < signal_time));
+ alarm(0);
+ signal(SIGALRM, SIG_DFL);
+
+ EXPECT_LT(1, count);
+
+ EXPECT_EQ(1, signals);
+
+ android_logger_list_close(logger_list);
+
+ unsigned long long uticks_end;
+ unsigned long long sticks_end;
+ get_ticks(&uticks_end, &sticks_end);
+
+ // Less than 1% in either user or system time, or both
+ const unsigned long long one_percent_ticks = alarm_time;
+ unsigned long long user_ticks = uticks_end - uticks_start;
+ unsigned long long system_ticks = sticks_end - sticks_start;
+ EXPECT_GT(one_percent_ticks, user_ticks);
+ EXPECT_GT(one_percent_ticks, system_ticks);
+ EXPECT_GT(one_percent_ticks, user_ticks + system_ticks);
+}
+
+static const char max_payload_tag[] = "TEST_max_payload_XXXX";
+static const char max_payload_buf[LOGGER_ENTRY_MAX_PAYLOAD
+ - sizeof(max_payload_tag) - 1] = "LEONATO\n\
+I learn in this letter that Don Peter of Arragon\n\
+comes this night to Messina\n\
+MESSENGER\n\
+He is very near by this: he was not three leagues off\n\
+when I left him\n\
+LEONATO\n\
+How many gentlemen have you lost in this action?\n\
+MESSENGER\n\
+But few of any sort, and none of name\n\
+LEONATO\n\
+A victory is twice itself when the achiever brings\n\
+home full numbers. I find here that Don Peter hath\n\
+bestowed much honour on a young Florentine called Claudio\n\
+MESSENGER\n\
+Much deserved on his part and equally remembered by\n\
+Don Pedro: he hath borne himself beyond the\n\
+promise of his age, doing, in the figure of a lamb,\n\
+the feats of a lion: he hath indeed better\n\
+bettered expectation than you must expect of me to\n\
+tell you how\n\
+LEONATO\n\
+He hath an uncle here in Messina will be very much\n\
+glad of it.\n\
+MESSENGER\n\
+I have already delivered him letters, and there\n\
+appears much joy in him; even so much that joy could\n\
+not show itself modest enough without a badge of\n\
+bitterness.\n\
+LEONATO\n\
+Did he break out into tears?\n\
+MESSENGER\n\
+In great measure.\n\
+LEONATO\n\
+A kind overflow of kindness: there are no faces\n\
+truer than those that are so washed. How much\n\
+better is it to weep at joy than to joy at weeping!\n\
+BEATRICE\n\
+I pray you, is Signior Mountanto returned from the\n\
+wars or no?\n\
+MESSENGER\n\
+I know none of that name, lady: there was none such\n\
+in the army of any sort.\n\
+LEONATO\n\
+What is he that you ask for, niece?\n\
+HERO\n\
+My cousin means Signior Benedick of Padua.\n\
+MESSENGER\n\
+O, he's returned; and as pleasant as ever he was.\n\
+BEATRICE\n\
+He set up his bills here in Messina and challenged\n\
+Cupid at the flight; and my uncle's fool, reading\n\
+the challenge, subscribed for Cupid, and challenged\n\
+him at the bird-bolt. I pray you, how many hath he\n\
+killed and eaten in these wars? But how many hath\n\
+he killed? for indeed I promised to eat all of his killing.\n\
+LEONATO\n\
+Faith, niece, you tax Signior Benedick too much;\n\
+but he'll be meet with you, I doubt it not.\n\
+MESSENGER\n\
+He hath done good service, lady, in these wars.\n\
+BEATRICE\n\
+You had musty victual, and he hath holp to eat it:\n\
+he is a very valiant trencherman; he hath an\n\
+excellent stomach.\n\
+MESSENGER\n\
+And a good soldier too, lady.\n\
+BEATRICE\n\
+And a good soldier to a lady: but what is he to a lord?\n\
+MESSENGER\n\
+A lord to a lord, a man to a man; stuffed with all\n\
+honourable virtues.\n\
+BEATRICE\n\
+It is so, indeed; he is no less than a stuffed man:\n\
+but for the stuffing,--well, we are all mortal.\n\
+LEONATO\n\
+You must not, sir, mistake my niece. There is a\n\
+kind of merry war betwixt Signior Benedick and her:\n\
+they never meet but there's a skirmish of wit\n\
+between them.\n\
+BEATRICE\n\
+Alas! he gets nothing by that. In our last\n\
+conflict four of his five wits went halting off, and\n\
+now is the whole man governed with one: so that if\n\
+he have wit enough to keep himself warm, let him\n\
+bear it for a difference between himself and his\n\
+horse; for it is all the wealth that he hath left,\n\
+to be known a reasonable creature. Who is his\n\
+companion now? He hath every month a new sworn brother.\n\
+MESSENGER\n\
+Is't possible?\n\
+BEATRICE\n\
+Very easily possible: he wears his faith but as\n\
+the fashion of his hat; it ever changes with the\n\
+next block.\n\
+MESSENGER\n\
+I see, lady, the gentleman is not in your books.\n\
+BEATRICE\n\
+No; an he were, I would burn my study. But, I pray\n\
+you, who is his companion? Is there no young\n\
+squarer now that will make a voyage with him to the devil?\n\
+MESSENGER\n\
+He is most in the company of the right noble Claudio.\n\
+BEATRICE\n\
+O Lord, he will hang upon him like a disease: he\n\
+is sooner caught than the pestilence, and the taker\n\
+runs presently mad. God help the noble Claudio! if\n\
+he have caught the Benedick, it will cost him a\n\
+thousand pound ere a' be cured.\n\
+MESSENGER\n\
+I will hold friends with you, lady.\n\
+BEATRICE\n\
+Do, good friend.\n\
+LEONATO\n\
+You will never run mad, niece.\n\
+BEATRICE\n\
+No, not till a hot January.\n\
+MESSENGER\n\
+Don Pedro is approached.\n\
+Enter DON PEDRO, DON JOHN, CLAUDIO, BENEDICK, and BALTHASAR\n\
+\n\
+DON PEDRO\n\
+Good Signior Leonato, you are come to meet your\n\
+trouble: the fashion of the world is to avoid\n\
+cost, and you encounter it\n\
+LEONATO\n\
+Never came trouble to my house in the likeness";
+
+TEST(liblog, max_payload) {
+ pid_t pid = getpid();
+ char tag[sizeof(max_payload_tag)];
+ memcpy(tag, max_payload_tag, sizeof(tag));
+ snprintf(tag + sizeof(tag) - 5, 5, "%04X", pid & 0xFFFF);
+
+ LOG_FAILURE_RETRY(__android_log_buf_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO,
+ tag, max_payload_buf));
+ sleep(2);
+
+ struct logger_list *logger_list;
+
+ ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+ LOG_ID_SYSTEM, ANDROID_LOG_RDONLY, 100, 0)));
+
+ bool matches = false;
+ ssize_t max_len = 0;
+
+ for(;;) {
+ log_msg log_msg;
+ if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+ break;
+ }
+
+ if ((log_msg.entry.pid != pid) || (log_msg.id() != LOG_ID_SYSTEM)) {
+ continue;
+ }
+
+ char *data = log_msg.msg() + 1;
+
+ if (strcmp(data, tag)) {
+ continue;
+ }
+
+ data += strlen(data) + 1;
+
+ const char *left = data;
+ const char *right = max_payload_buf;
+ while (*left && *right && (*left == *right)) {
+ ++left;
+ ++right;
+ }
+
+ if (max_len <= (left - data)) {
+ max_len = left - data + 1;
+ }
+
+ if (max_len > 512) {
+ matches = true;
+ break;
+ }
+ }
+
+ android_logger_list_close(logger_list);
+
+ EXPECT_EQ(true, matches);
+
+ EXPECT_LE(sizeof(max_payload_buf), static_cast<size_t>(max_len));
+}
+
+TEST(liblog, too_big_payload) {
+ pid_t pid = getpid();
+ static const char big_payload_tag[] = "TEST_big_payload_XXXX";
+ char tag[sizeof(big_payload_tag)];
+ memcpy(tag, big_payload_tag, sizeof(tag));
+ snprintf(tag + sizeof(tag) - 5, 5, "%04X", pid & 0xFFFF);
+
+ std::string longString(3266519, 'x');
+
+ ssize_t ret = LOG_FAILURE_RETRY(__android_log_buf_write(LOG_ID_SYSTEM,
+ ANDROID_LOG_INFO, tag, longString.c_str()));
+
+ struct logger_list *logger_list;
+
+ ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+ LOG_ID_SYSTEM, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 100, 0)));
+
+ ssize_t max_len = 0;
+
+ for(;;) {
+ log_msg log_msg;
+ if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+ break;
+ }
+
+ if ((log_msg.entry.pid != pid) || (log_msg.id() != LOG_ID_SYSTEM)) {
+ continue;
+ }
+
+ char *data = log_msg.msg() + 1;
+
+ if (strcmp(data, tag)) {
+ continue;
+ }
+
+ data += strlen(data) + 1;
+
+ const char *left = data;
+ const char *right = longString.c_str();
+ while (*left && *right && (*left == *right)) {
+ ++left;
+ ++right;
+ }
+
+ if (max_len <= (left - data)) {
+ max_len = left - data + 1;
+ }
+ }
+
+ android_logger_list_close(logger_list);
+
+ EXPECT_LE(LOGGER_ENTRY_MAX_PAYLOAD - sizeof(big_payload_tag),
+ static_cast<size_t>(max_len));
+
+ EXPECT_EQ(ret, max_len + static_cast<ssize_t>(sizeof(big_payload_tag)));
+}
+
+TEST(liblog, dual_reader) {
+ struct logger_list *logger_list1;
+
+ // >25 messages due to liblog.__android_log_buf_print__concurrentXX above.
+ ASSERT_TRUE(NULL != (logger_list1 = android_logger_list_open(
+ LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 25, 0)));
+
+ struct logger_list *logger_list2;
+
+ if (NULL == (logger_list2 = android_logger_list_open(
+ LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 15, 0))) {
+ android_logger_list_close(logger_list1);
+ ASSERT_TRUE(NULL != logger_list2);
+ }
+
+ int count1 = 0;
+ bool done1 = false;
+ int count2 = 0;
+ bool done2 = false;
+
+ do {
+ log_msg log_msg;
+
+ if (!done1) {
+ if (android_logger_list_read(logger_list1, &log_msg) <= 0) {
+ done1 = true;
+ } else {
+ ++count1;
+ }
+ }
+
+ if (!done2) {
+ if (android_logger_list_read(logger_list2, &log_msg) <= 0) {
+ done2 = true;
+ } else {
+ ++count2;
+ }
+ }
+ } while ((!done1) || (!done2));
+
+ android_logger_list_close(logger_list1);
+ android_logger_list_close(logger_list2);
+
+ EXPECT_EQ(25, count1);
+ EXPECT_EQ(15, count2);
+}
+
+TEST(liblog, android_logger_get_) {
+ struct logger_list * logger_list = android_logger_list_alloc(ANDROID_LOG_WRONLY, 0, 0);
+
+ for(int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
+ log_id_t id = static_cast<log_id_t>(i);
+ const char *name = android_log_id_to_name(id);
+ if (id != android_name_to_log_id(name)) {
+ continue;
+ }
+ fprintf(stderr, "log buffer %s\r", name);
+ struct logger * logger;
+ EXPECT_TRUE(NULL != (logger = android_logger_open(logger_list, id)));
+ EXPECT_EQ(id, android_logger_get_id(logger));
+ /* crash buffer is allowed to be empty, that is actually healthy! */
+ if (android_logger_get_log_size(logger) || strcmp("crash", name)) {
+ EXPECT_LT(0, android_logger_get_log_size(logger));
+ }
+ EXPECT_LT(0, android_logger_get_log_readable_size(logger));
+ EXPECT_LT(0, android_logger_get_log_version(logger));
+ }
+
+ android_logger_list_close(logger_list);
+}
+
+static bool checkPriForTag(AndroidLogFormat *p_format, const char *tag, android_LogPriority pri) {
+ return android_log_shouldPrintLine(p_format, tag, pri)
+ && !android_log_shouldPrintLine(p_format, tag, (android_LogPriority)(pri - 1));
+}
+
+TEST(liblog, filterRule) {
+ static const char tag[] = "random";
+
+ AndroidLogFormat *p_format = android_log_format_new();
+
+ android_log_addFilterRule(p_format,"*:i");
+
+ EXPECT_TRUE(checkPriForTag(p_format, tag, ANDROID_LOG_INFO));
+ EXPECT_TRUE(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0);
+ android_log_addFilterRule(p_format, "*");
+ EXPECT_TRUE (checkPriForTag(p_format, tag, ANDROID_LOG_DEBUG));
+ EXPECT_TRUE(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0);
+ android_log_addFilterRule(p_format, "*:v");
+ EXPECT_TRUE (checkPriForTag(p_format, tag, ANDROID_LOG_VERBOSE));
+ EXPECT_TRUE(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0);
+ android_log_addFilterRule(p_format, "*:i");
+ EXPECT_TRUE (checkPriForTag(p_format, tag, ANDROID_LOG_INFO));
+ EXPECT_TRUE(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0);
+
+ android_log_addFilterRule(p_format, tag);
+ EXPECT_TRUE (checkPriForTag(p_format, tag, ANDROID_LOG_VERBOSE));
+ EXPECT_TRUE(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0);
+ android_log_addFilterRule(p_format, "random:v");
+ EXPECT_TRUE (checkPriForTag(p_format, tag, ANDROID_LOG_VERBOSE));
+ EXPECT_TRUE(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0);
+ android_log_addFilterRule(p_format, "random:d");
+ EXPECT_TRUE (checkPriForTag(p_format, tag, ANDROID_LOG_DEBUG));
+ EXPECT_TRUE(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0);
+ android_log_addFilterRule(p_format, "random:w");
+ EXPECT_TRUE (checkPriForTag(p_format, tag, ANDROID_LOG_WARN));
+ EXPECT_TRUE(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0);
+
+ android_log_addFilterRule(p_format, "crap:*");
+ EXPECT_TRUE (checkPriForTag(p_format, "crap", ANDROID_LOG_VERBOSE));
+ EXPECT_TRUE(android_log_shouldPrintLine(p_format, "crap", ANDROID_LOG_VERBOSE) > 0);
+
+ // invalid expression
+ EXPECT_TRUE (android_log_addFilterRule(p_format, "random:z") < 0);
+ EXPECT_TRUE (checkPriForTag(p_format, tag, ANDROID_LOG_WARN));
+ EXPECT_TRUE(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0);
+
+ // Issue #550946
+ EXPECT_TRUE(android_log_addFilterString(p_format, " ") == 0);
+ EXPECT_TRUE(checkPriForTag(p_format, tag, ANDROID_LOG_WARN));
+
+ // note trailing space
+ EXPECT_TRUE(android_log_addFilterString(p_format, "*:s random:d ") == 0);
+ EXPECT_TRUE(checkPriForTag(p_format, tag, ANDROID_LOG_DEBUG));
+
+ EXPECT_TRUE(android_log_addFilterString(p_format, "*:s random:z") < 0);
+
+#if 0 // bitrot, seek update
+ char defaultBuffer[512];
+
+ android_log_formatLogLine(p_format,
+ defaultBuffer, sizeof(defaultBuffer), 0, ANDROID_LOG_ERROR, 123,
+ 123, 123, tag, "nofile", strlen("Hello"), "Hello", NULL);
+
+ fprintf(stderr, "%s\n", defaultBuffer);
+#endif
+
+ android_log_format_free(p_format);
+}
+
+TEST(liblog, is_loggable) {
+ static const char tag[] = "is_loggable";
+ static const char log_namespace[] = "persist.log.tag.";
+ static const size_t base_offset = 8; /* skip "persist." */
+ // sizeof("string") = strlen("string") + 1
+ char key[sizeof(log_namespace) + sizeof(tag) - 1];
+ char hold[4][PROP_VALUE_MAX];
+ static const struct {
+ int level;
+ char type;
+ } levels[] = {
+ { ANDROID_LOG_VERBOSE, 'v' },
+ { ANDROID_LOG_DEBUG , 'd' },
+ { ANDROID_LOG_INFO , 'i' },
+ { ANDROID_LOG_WARN , 'w' },
+ { ANDROID_LOG_ERROR , 'e' },
+ { ANDROID_LOG_FATAL , 'a' },
+ { -1 , 's' },
+ { -2 , 'g' }, // Illegal value, resort to default
+ };
+
+ // Set up initial test condition
+ memset(hold, 0, sizeof(hold));
+ snprintf(key, sizeof(key), "%s%s", log_namespace, tag);
+ property_get(key, hold[0], "");
+ property_set(key, "");
+ property_get(key + base_offset, hold[1], "");
+ property_set(key + base_offset, "");
+ strcpy(key, log_namespace);
+ key[sizeof(log_namespace) - 2] = '\0';
+ property_get(key, hold[2], "");
+ property_set(key, "");
+ property_get(key, hold[3], "");
+ property_set(key + base_offset, "");
+
+ // All combinations of level and defaults
+ for(size_t i = 0; i < (sizeof(levels) / sizeof(levels[0])); ++i) {
+ if (levels[i].level == -2) {
+ continue;
+ }
+ for(size_t j = 0; j < (sizeof(levels) / sizeof(levels[0])); ++j) {
+ if (levels[j].level == -2) {
+ continue;
+ }
+ fprintf(stderr, "i=%zu j=%zu\r", i, j);
+ if ((levels[i].level < levels[j].level)
+ || (levels[j].level == -1)) {
+ EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
+ levels[j].level));
+ } else {
+ EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
+ levels[j].level));
+ }
+ }
+ }
+
+ // All combinations of level and tag and global properties
+ for(size_t i = 0; i < (sizeof(levels) / sizeof(levels[0])); ++i) {
+ if (levels[i].level == -2) {
+ continue;
+ }
+ for(size_t j = 0; j < (sizeof(levels) / sizeof(levels[0])); ++j) {
+ char buf[2];
+ buf[0] = levels[j].type;
+ buf[1] = '\0';
+
+ snprintf(key, sizeof(key), "%s%s", log_namespace, tag);
+ fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r",
+ i, j, key, buf);
+ property_set(key, buf);
+ if ((levels[i].level < levels[j].level)
+ || (levels[j].level == -1)
+ || ((levels[i].level < ANDROID_LOG_DEBUG)
+ && (levels[j].level == -2))) {
+ EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
+ ANDROID_LOG_DEBUG));
+ } else {
+ EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
+ ANDROID_LOG_DEBUG));
+ }
+ property_set(key, "");
+
+ fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r",
+ i, j, key + base_offset, buf);
+ property_set(key + base_offset, buf);
+ if ((levels[i].level < levels[j].level)
+ || (levels[j].level == -1)
+ || ((levels[i].level < ANDROID_LOG_DEBUG)
+ && (levels[j].level == -2))) {
+ EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
+ ANDROID_LOG_DEBUG));
+ } else {
+ EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
+ ANDROID_LOG_DEBUG));
+ }
+ property_set(key + base_offset, "");
+
+ strcpy(key, log_namespace);
+ key[sizeof(log_namespace) - 2] = '\0';
+ fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r",
+ i, j, key, buf);
+ property_set(key, buf);
+ if ((levels[i].level < levels[j].level)
+ || (levels[j].level == -1)
+ || ((levels[i].level < ANDROID_LOG_DEBUG)
+ && (levels[j].level == -2))) {
+ EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
+ ANDROID_LOG_DEBUG));
+ } else {
+ EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
+ ANDROID_LOG_DEBUG));
+ }
+ property_set(key, "");
+
+ fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r",
+ i, j, key + base_offset, buf);
+ property_set(key + base_offset, buf);
+ if ((levels[i].level < levels[j].level)
+ || (levels[j].level == -1)
+ || ((levels[i].level < ANDROID_LOG_DEBUG)
+ && (levels[j].level == -2))) {
+ EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
+ ANDROID_LOG_DEBUG));
+ } else {
+ EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
+ ANDROID_LOG_DEBUG));
+ }
+ property_set(key + base_offset, "");
+ }
+ }
+
+ // All combinations of level and tag properties, but with global set to INFO
+ strcpy(key, log_namespace);
+ key[sizeof(log_namespace) - 2] = '\0';
+ property_set(key, "I");
+ snprintf(key, sizeof(key), "%s%s", log_namespace, tag);
+ for(size_t i = 0; i < (sizeof(levels) / sizeof(levels[0])); ++i) {
+ if (levels[i].level == -2) {
+ continue;
+ }
+ for(size_t j = 0; j < (sizeof(levels) / sizeof(levels[0])); ++j) {
+ char buf[2];
+ buf[0] = levels[j].type;
+ buf[1] = '\0';
+
+ fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r",
+ i, j, key, buf);
+ property_set(key, buf);
+ if ((levels[i].level < levels[j].level)
+ || (levels[j].level == -1)
+ || ((levels[i].level < ANDROID_LOG_INFO) // Yes INFO
+ && (levels[j].level == -2))) {
+ EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
+ ANDROID_LOG_DEBUG));
+ } else {
+ EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
+ ANDROID_LOG_DEBUG));
+ }
+ property_set(key, "");
+
+ fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r",
+ i, j, key + base_offset, buf);
+ property_set(key + base_offset, buf);
+ if ((levels[i].level < levels[j].level)
+ || (levels[j].level == -1)
+ || ((levels[i].level < ANDROID_LOG_INFO) // Yes INFO
+ && (levels[j].level == -2))) {
+ EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
+ ANDROID_LOG_DEBUG));
+ } else {
+ EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
+ ANDROID_LOG_DEBUG));
+ }
+ property_set(key + base_offset, "");
+ }
+ }
+
+ // reset parms
+ snprintf(key, sizeof(key), "%s%s", log_namespace, tag);
+ property_set(key, hold[0]);
+ property_set(key + base_offset, hold[1]);
+ strcpy(key, log_namespace);
+ key[sizeof(log_namespace) - 2] = '\0';
+ property_set(key, hold[2]);
+ property_set(key + base_offset, hold[3]);
+}
diff --git a/liblog/uio.c b/liblog/uio.c
new file mode 100644
index 0000000..f77cc49
--- /dev/null
+++ b/liblog/uio.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2007-2014 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.
+ */
+
+#if defined(_WIN32)
+
+#include <log/uio.h>
+#include <unistd.h>
+
+int readv( int fd, struct iovec* vecs, int count )
+{
+ int total = 0;
+
+ for ( ; count > 0; count--, vecs++ ) {
+ char* buf = vecs->iov_base;
+ int len = vecs->iov_len;
+
+ while (len > 0) {
+ int ret = read( fd, buf, len );
+ if (ret < 0) {
+ if (total == 0)
+ total = -1;
+ goto Exit;
+ }
+ if (ret == 0)
+ goto Exit;
+
+ total += ret;
+ buf += ret;
+ len -= ret;
+ }
+ }
+Exit:
+ return total;
+}
+
+int writev( int fd, const struct iovec* vecs, int count )
+{
+ int total = 0;
+
+ for ( ; count > 0; count--, vecs++ ) {
+ const char* buf = vecs->iov_base;
+ int len = vecs->iov_len;
+
+ while (len > 0) {
+ int ret = write( fd, buf, len );
+ if (ret < 0) {
+ if (total == 0)
+ total = -1;
+ goto Exit;
+ }
+ if (ret == 0)
+ goto Exit;
+
+ total += ret;
+ buf += ret;
+ len -= ret;
+ }
+ }
+Exit:
+ return total;
+}
+
+#endif
diff --git a/libmemtrack/Android.mk b/libmemtrack/Android.mk
new file mode 100644
index 0000000..a8fb3eb
--- /dev/null
+++ b/libmemtrack/Android.mk
@@ -0,0 +1,19 @@
+# Copyright 2013 The Android Open Source Project
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := memtrack.c
+LOCAL_MODULE := libmemtrack
+LOCAL_C_INCLUDES += hardware/libhardware/include
+LOCAL_SHARED_LIBRARIES := libhardware liblog
+LOCAL_CFLAGS := -Wall -Werror
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := memtrack_test.c
+LOCAL_MODULE := memtrack_test
+LOCAL_C_INCLUDES := $(call include-path-for, libpagemap)
+LOCAL_SHARED_LIBRARIES := libmemtrack libpagemap
+LOCAL_CFLAGS := -Wall -Werror
+include $(BUILD_EXECUTABLE)
diff --git a/libmemtrack/memtrack.c b/libmemtrack/memtrack.c
new file mode 100644
index 0000000..21d9ebd
--- /dev/null
+++ b/libmemtrack/memtrack.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <memtrack/memtrack.h>
+
+#define LOG_TAG "memtrack"
+
+#include <log/log.h>
+
+#include <errno.h>
+#include <malloc.h>
+#include <string.h>
+
+#include <hardware/memtrack.h>
+
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
+
+static const memtrack_module_t *module;
+
+struct memtrack_proc {
+ pid_t pid;
+ struct memtrack_proc_type {
+ enum memtrack_type type;
+ size_t num_records;
+ size_t allocated_records;
+ struct memtrack_record *records;
+ } types[MEMTRACK_NUM_TYPES];
+};
+
+int memtrack_init(void)
+{
+ int err;
+
+ if (module) {
+ return 0;
+ }
+
+ err = hw_get_module(MEMTRACK_HARDWARE_MODULE_ID,
+ (hw_module_t const**)&module);
+ if (err) {
+ ALOGE("Couldn't load %s module (%s)", MEMTRACK_HARDWARE_MODULE_ID,
+ strerror(-err));
+ return err;
+ }
+
+ return module->init(module);
+}
+
+struct memtrack_proc *memtrack_proc_new(void)
+{
+ if (!module) {
+ return NULL;
+ }
+
+ return calloc(sizeof(struct memtrack_proc), 1);
+}
+
+void memtrack_proc_destroy(struct memtrack_proc *p)
+{
+ enum memtrack_type i;
+
+ if (p) {
+ for (i = 0; i < MEMTRACK_NUM_TYPES; i++) {
+ free(p->types[i].records);
+ }
+ }
+ free(p);
+}
+
+static int memtrack_proc_get_type(struct memtrack_proc_type *t,
+ pid_t pid, enum memtrack_type type)
+{
+ size_t num_records = t->num_records;
+ int ret;
+
+retry:
+ ret = module->getMemory(module, pid, type, t->records, &num_records);
+ if (ret) {
+ t->num_records = 0;
+ return ret;
+ }
+ if (num_records > t->allocated_records) {
+ /* Need more records than allocated */
+ free(t->records);
+ t->records = calloc(sizeof(*t->records), num_records);
+ if (!t->records) {
+ return -ENOMEM;
+ }
+ t->allocated_records = num_records;
+ goto retry;
+ }
+ t->num_records = num_records;
+
+ return 0;
+}
+
+/* TODO: sanity checks on return values from HALs:
+ * make sure no records have invalid flags set
+ * - unknown flags
+ * - too many flags of a single category
+ * - missing ACCOUNTED/UNACCOUNTED
+ * make sure there are not overlapping SHARED and SHARED_PSS records
+ */
+static int memtrack_proc_sanity_check(struct memtrack_proc *p)
+{
+ (void)p;
+ return 0;
+}
+
+int memtrack_proc_get(struct memtrack_proc *p, pid_t pid)
+{
+ enum memtrack_type i;
+
+ if (!module) {
+ return -EINVAL;
+ }
+
+ if (!p) {
+ return -EINVAL;
+ }
+
+ p->pid = pid;
+ for (i = 0; i < MEMTRACK_NUM_TYPES; i++) {
+ memtrack_proc_get_type(&p->types[i], pid, i);
+ }
+
+ return memtrack_proc_sanity_check(p);
+}
+
+static ssize_t memtrack_proc_sum(struct memtrack_proc *p,
+ enum memtrack_type types[], size_t num_types,
+ unsigned int flags)
+{
+ ssize_t sum = 0;
+ size_t i;
+ size_t j;
+
+ for (i = 0; i < num_types; i++) {
+ enum memtrack_type type = types[i];
+ for (j = 0; j < p->types[type].num_records; j++) {
+ if ((p->types[type].records[j].flags & flags) == flags) {
+ sum += p->types[type].records[j].size_in_bytes;
+ }
+ }
+ }
+
+ return sum;
+}
+
+ssize_t memtrack_proc_graphics_total(struct memtrack_proc *p)
+{
+ enum memtrack_type types[] = { MEMTRACK_TYPE_GRAPHICS };
+ return memtrack_proc_sum(p, types, ARRAY_SIZE(types), 0);
+}
+
+ssize_t memtrack_proc_graphics_pss(struct memtrack_proc *p)
+{
+ enum memtrack_type types[] = { MEMTRACK_TYPE_GRAPHICS };
+ return memtrack_proc_sum(p, types, ARRAY_SIZE(types),
+ MEMTRACK_FLAG_SMAPS_UNACCOUNTED);
+}
+
+ssize_t memtrack_proc_gl_total(struct memtrack_proc *p)
+{
+ enum memtrack_type types[] = { MEMTRACK_TYPE_GL };
+ return memtrack_proc_sum(p, types, ARRAY_SIZE(types), 0);
+}
+
+ssize_t memtrack_proc_gl_pss(struct memtrack_proc *p)
+{
+ enum memtrack_type types[] = { MEMTRACK_TYPE_GL };
+ return memtrack_proc_sum(p, types, ARRAY_SIZE(types),
+ MEMTRACK_FLAG_SMAPS_UNACCOUNTED);
+}
+
+ssize_t memtrack_proc_other_total(struct memtrack_proc *p)
+{
+ enum memtrack_type types[] = { MEMTRACK_TYPE_MULTIMEDIA,
+ MEMTRACK_TYPE_CAMERA,
+ MEMTRACK_TYPE_OTHER };
+ return memtrack_proc_sum(p, types, ARRAY_SIZE(types), 0);
+}
+
+ssize_t memtrack_proc_other_pss(struct memtrack_proc *p)
+{
+ enum memtrack_type types[] = { MEMTRACK_TYPE_MULTIMEDIA,
+ MEMTRACK_TYPE_CAMERA,
+ MEMTRACK_TYPE_OTHER };
+ return memtrack_proc_sum(p, types, ARRAY_SIZE(types),
+ MEMTRACK_FLAG_SMAPS_UNACCOUNTED);
+}
diff --git a/libmemtrack/memtrack_test.c b/libmemtrack/memtrack_test.c
new file mode 100644
index 0000000..eaadfa7
--- /dev/null
+++ b/libmemtrack/memtrack_test.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <memtrack/memtrack.h>
+
+#include <pagemap/pagemap.h>
+
+#define DIV_ROUND_UP(x,y) (((x) + (y) - 1) / (y))
+
+static int getprocname(pid_t pid, char *buf, int len) {
+ char *filename;
+ FILE *f;
+ int rc = 0;
+ static const char* unknown_cmdline = "<unknown>";
+
+ if (len <= 0) {
+ return -1;
+ }
+
+ if (asprintf(&filename, "/proc/%d/cmdline", pid) < 0) {
+ rc = 1;
+ goto exit;
+ }
+
+ f = fopen(filename, "r");
+ if (f == NULL) {
+ rc = 2;
+ goto releasefilename;
+ }
+
+ if (fgets(buf, len, f) == NULL) {
+ rc = 3;
+ goto closefile;
+ }
+
+closefile:
+ (void) fclose(f);
+releasefilename:
+ free(filename);
+exit:
+ if (rc != 0) {
+ /*
+ * The process went away before we could read its process name. Try
+ * to give the user "<unknown>" here, but otherwise they get to look
+ * at a blank.
+ */
+ if (strlcpy(buf, unknown_cmdline, (size_t)len) >= (size_t)len) {
+ rc = 4;
+ }
+ }
+
+ return rc;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+ pm_kernel_t *ker;
+ size_t num_procs;
+ pid_t *pids;
+ struct memtrack_proc *p;
+ size_t i;
+
+ (void)argc;
+ (void)argv;
+
+ ret = memtrack_init();
+ if (ret < 0) {
+ fprintf(stderr, "failed to initialize HAL: %s (%d)\n", strerror(-ret), ret);
+ exit(EXIT_FAILURE);
+ }
+
+ ret = pm_kernel_create(&ker);
+ if (ret) {
+ fprintf(stderr, "Error creating kernel interface -- "
+ "does this kernel have pagemap?\n");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = pm_kernel_pids(ker, &pids, &num_procs);
+ if (ret) {
+ fprintf(stderr, "Error listing processes.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ p = memtrack_proc_new();
+ if (ret) {
+ fprintf(stderr, "failed to create memtrack process handle\n");
+ exit(EXIT_FAILURE);
+ }
+
+ for (i = 0; i < num_procs; i++) {
+ pid_t pid = pids[i];
+ char cmdline[256];
+ size_t v1;
+ size_t v2;
+ size_t v3;
+ size_t v4;
+ size_t v5;
+ size_t v6;
+
+ getprocname(pid, cmdline, (int)sizeof(cmdline));
+
+ ret = memtrack_proc_get(p, pid);
+ if (ret) {
+ fprintf(stderr, "failed to get memory info for pid %d: %s (%d)\n",
+ pid, strerror(-ret), ret);
+ continue;
+ }
+
+ v1 = DIV_ROUND_UP(memtrack_proc_graphics_total(p), 1024);
+ v2 = DIV_ROUND_UP(memtrack_proc_graphics_pss(p), 1024);
+ v3 = DIV_ROUND_UP(memtrack_proc_gl_total(p), 1024);
+ v4 = DIV_ROUND_UP(memtrack_proc_gl_pss(p), 1024);
+ v5 = DIV_ROUND_UP(memtrack_proc_other_total(p), 1024);
+ v6 = DIV_ROUND_UP(memtrack_proc_other_pss(p), 1024);
+
+ if (v1 | v2 | v3 | v4 | v5 | v6) {
+ printf("%5d %6zu %6zu %6zu %6zu %6zu %6zu %s\n", pid,
+ v1, v2, v3, v4, v5, v6, cmdline);
+ }
+ }
+
+ memtrack_proc_destroy(p);
+
+ return 0;
+}
diff --git a/libmincrypt/Android.mk b/libmincrypt/Android.mk
new file mode 100644
index 0000000..7906986
--- /dev/null
+++ b/libmincrypt/Android.mk
@@ -0,0 +1,18 @@
+# Copyright 2008 The Android Open Source Project
+#
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libmincrypt
+LOCAL_SRC_FILES := dsa_sig.c p256.c p256_ec.c p256_ecdsa.c rsa.c sha.c sha256.c
+LOCAL_CFLAGS := -Wall -Werror
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libmincrypt
+LOCAL_SRC_FILES := dsa_sig.c p256.c p256_ec.c p256_ecdsa.c rsa.c sha.c sha256.c
+LOCAL_CFLAGS := -Wall -Werror
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+include $(LOCAL_PATH)/tools/Android.mk \
+ $(LOCAL_PATH)/test/Android.mk
diff --git a/libmincrypt/NOTICE b/libmincrypt/NOTICE
new file mode 100644
index 0000000..430d3d6
--- /dev/null
+++ b/libmincrypt/NOTICE
@@ -0,0 +1,23 @@
+ Copyright 2008, The Android Open Source Project
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of Google Inc. nor the names of its contributors may
+ be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/libmincrypt/dsa_sig.c b/libmincrypt/dsa_sig.c
new file mode 100644
index 0000000..101314b
--- /dev/null
+++ b/libmincrypt/dsa_sig.c
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Google Inc. nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string.h>
+
+#include "mincrypt/dsa_sig.h"
+#include "mincrypt/p256.h"
+
+/**
+ * Trims off the leading zero bytes and copy it to a buffer aligning it to the end.
+ */
+static inline int trim_to_p256_bytes(unsigned char dst[P256_NBYTES], unsigned char *src,
+ int src_len) {
+ int dst_offset;
+ while (*src == '\0' && src_len > 0) {
+ src++;
+ src_len--;
+ }
+ if (src_len > P256_NBYTES || src_len < 1) {
+ return 0;
+ }
+ dst_offset = P256_NBYTES - src_len;
+ memset(dst, 0, dst_offset);
+ memcpy(dst + dst_offset, src, src_len);
+ return 1;
+}
+
+/**
+ * Unpacks the ASN.1 DSA signature sequence.
+ */
+int dsa_sig_unpack(unsigned char* sig, int sig_len, p256_int* r_int, p256_int* s_int) {
+ /*
+ * Structure is:
+ * 0x30 0xNN SEQUENCE + s_length
+ * 0x02 0xNN INTEGER + r_length
+ * 0xAA 0xBB .. r_length bytes of "r" (offset 4)
+ * 0x02 0xNN INTEGER + s_length
+ * 0xMM 0xNN .. s_length bytes of "s" (offset 6 + r_len)
+ */
+ int seq_len;
+ unsigned char r_bytes[P256_NBYTES];
+ unsigned char s_bytes[P256_NBYTES];
+ int r_len;
+ int s_len;
+
+ memset(r_bytes, 0, sizeof(r_bytes));
+ memset(s_bytes, 0, sizeof(s_bytes));
+
+ /*
+ * Must have at least:
+ * 2 bytes sequence header and length
+ * 2 bytes R integer header and length
+ * 1 byte of R
+ * 2 bytes S integer header and length
+ * 1 byte of S
+ *
+ * 8 bytes total
+ */
+ if (sig_len < 8 || sig[0] != 0x30 || sig[2] != 0x02) {
+ return 0;
+ }
+
+ seq_len = sig[1];
+ if ((seq_len <= 0) || (seq_len + 2 != sig_len)) {
+ return 0;
+ }
+
+ r_len = sig[3];
+ /*
+ * Must have at least:
+ * 2 bytes for R header and length
+ * 2 bytes S integer header and length
+ * 1 byte of S
+ */
+ if ((r_len < 1) || (r_len > seq_len - 5) || (sig[4 + r_len] != 0x02)) {
+ return 0;
+ }
+ s_len = sig[5 + r_len];
+
+ /**
+ * Must have:
+ * 2 bytes for R header and length
+ * r_len bytes for R
+ * 2 bytes S integer header and length
+ */
+ if ((s_len < 1) || (s_len != seq_len - 4 - r_len)) {
+ return 0;
+ }
+
+ /*
+ * ASN.1 encoded integers are zero-padded for positive integers. Make sure we have
+ * a correctly-sized buffer and that the resulting integer isn't too large.
+ */
+ if (!trim_to_p256_bytes(r_bytes, &sig[4], r_len)
+ || !trim_to_p256_bytes(s_bytes, &sig[6 + r_len], s_len)) {
+ return 0;
+ }
+
+ p256_from_bin(r_bytes, r_int);
+ p256_from_bin(s_bytes, s_int);
+
+ return 1;
+}
diff --git a/libmincrypt/p256.c b/libmincrypt/p256.c
new file mode 100644
index 0000000..555a07a
--- /dev/null
+++ b/libmincrypt/p256.c
@@ -0,0 +1,373 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Google Inc. nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// This is an implementation of the P256 elliptic curve group. It's written to
+// be portable 32-bit, although it's still constant-time.
+//
+// WARNING: Implementing these functions in a constant-time manner is far from
+// obvious. Be careful when touching this code.
+//
+// See http://www.imperialviolet.org/2010/12/04/ecc.html ([1]) for background.
+
+#include <assert.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "mincrypt/p256.h"
+
+const p256_int SECP256r1_n = // curve order
+ {{0xfc632551, 0xf3b9cac2, 0xa7179e84, 0xbce6faad, -1, -1, 0, -1}};
+
+const p256_int SECP256r1_p = // curve field size
+ {{-1, -1, -1, 0, 0, 0, 1, -1 }};
+
+const p256_int SECP256r1_b = // curve b
+ {{0x27d2604b, 0x3bce3c3e, 0xcc53b0f6, 0x651d06b0,
+ 0x769886bc, 0xb3ebbd55, 0xaa3a93e7, 0x5ac635d8}};
+
+void p256_init(p256_int* a) {
+ memset(a, 0, sizeof(*a));
+}
+
+void p256_clear(p256_int* a) { p256_init(a); }
+
+int p256_get_bit(const p256_int* scalar, int bit) {
+ return (P256_DIGIT(scalar, bit / P256_BITSPERDIGIT)
+ >> (bit & (P256_BITSPERDIGIT - 1))) & 1;
+}
+
+int p256_is_zero(const p256_int* a) {
+ int i, result = 0;
+ for (i = 0; i < P256_NDIGITS; ++i) result |= P256_DIGIT(a, i);
+ return !result;
+}
+
+// top, c[] += a[] * b
+// Returns new top
+static p256_digit mulAdd(const p256_int* a,
+ p256_digit b,
+ p256_digit top,
+ p256_digit* c) {
+ int i;
+ p256_ddigit carry = 0;
+
+ for (i = 0; i < P256_NDIGITS; ++i) {
+ carry += *c;
+ carry += (p256_ddigit)P256_DIGIT(a, i) * b;
+ *c++ = (p256_digit)carry;
+ carry >>= P256_BITSPERDIGIT;
+ }
+ return top + (p256_digit)carry;
+}
+
+// top, c[] -= top_a, a[]
+static p256_digit subTop(p256_digit top_a,
+ const p256_digit* a,
+ p256_digit top_c,
+ p256_digit* c) {
+ int i;
+ p256_sddigit borrow = 0;
+
+ for (i = 0; i < P256_NDIGITS; ++i) {
+ borrow += *c;
+ borrow -= *a++;
+ *c++ = (p256_digit)borrow;
+ borrow >>= P256_BITSPERDIGIT;
+ }
+ borrow += top_c;
+ borrow -= top_a;
+ top_c = (p256_digit)borrow;
+ assert((borrow >> P256_BITSPERDIGIT) == 0);
+ return top_c;
+}
+
+// top, c[] -= MOD[] & mask (0 or -1)
+// returns new top.
+static p256_digit subM(const p256_int* MOD,
+ p256_digit top,
+ p256_digit* c,
+ p256_digit mask) {
+ int i;
+ p256_sddigit borrow = 0;
+ for (i = 0; i < P256_NDIGITS; ++i) {
+ borrow += *c;
+ borrow -= P256_DIGIT(MOD, i) & mask;
+ *c++ = (p256_digit)borrow;
+ borrow >>= P256_BITSPERDIGIT;
+ }
+ return top + (p256_digit)borrow;
+}
+
+// top, c[] += MOD[] & mask (0 or -1)
+// returns new top.
+static p256_digit addM(const p256_int* MOD,
+ p256_digit top,
+ p256_digit* c,
+ p256_digit mask) {
+ int i;
+ p256_ddigit carry = 0;
+ for (i = 0; i < P256_NDIGITS; ++i) {
+ carry += *c;
+ carry += P256_DIGIT(MOD, i) & mask;
+ *c++ = (p256_digit)carry;
+ carry >>= P256_BITSPERDIGIT;
+ }
+ return top + (p256_digit)carry;
+}
+
+// c = a * b mod MOD. c can be a and/or b.
+void p256_modmul(const p256_int* MOD,
+ const p256_int* a,
+ const p256_digit top_b,
+ const p256_int* b,
+ p256_int* c) {
+ p256_digit tmp[P256_NDIGITS * 2 + 1] = { 0 };
+ p256_digit top = 0;
+ int i;
+
+ // Multiply/add into tmp.
+ for (i = 0; i < P256_NDIGITS; ++i) {
+ if (i) tmp[i + P256_NDIGITS - 1] = top;
+ top = mulAdd(a, P256_DIGIT(b, i), 0, tmp + i);
+ }
+
+ // Multiply/add top digit
+ tmp[i + P256_NDIGITS - 1] = top;
+ top = mulAdd(a, top_b, 0, tmp + i);
+
+ // Reduce tmp, digit by digit.
+ for (; i >= 0; --i) {
+ p256_digit reducer[P256_NDIGITS] = { 0 };
+ p256_digit top_reducer;
+
+ // top can be any value at this point.
+ // Guestimate reducer as top * MOD, since msw of MOD is -1.
+ top_reducer = mulAdd(MOD, top, 0, reducer);
+
+ // Subtract reducer from top | tmp.
+ top = subTop(top_reducer, reducer, top, tmp + i);
+
+ // top is now either 0 or 1. Make it 0, fixed-timing.
+ assert(top <= 1);
+
+ top = subM(MOD, top, tmp + i, ~(top - 1));
+
+ assert(top == 0);
+
+ // We have now reduced the top digit off tmp. Fetch new top digit.
+ top = tmp[i + P256_NDIGITS - 1];
+ }
+
+ // tmp might still be larger than MOD, yet same bit length.
+ // Make sure it is less, fixed-timing.
+ addM(MOD, 0, tmp, subM(MOD, 0, tmp, -1));
+
+ memcpy(c, tmp, P256_NBYTES);
+}
+int p256_is_odd(const p256_int* a) { return P256_DIGIT(a, 0) & 1; }
+int p256_is_even(const p256_int* a) { return !(P256_DIGIT(a, 0) & 1); }
+
+p256_digit p256_shl(const p256_int* a, int n, p256_int* b) {
+ int i;
+ p256_digit top = P256_DIGIT(a, P256_NDIGITS - 1);
+
+ n %= P256_BITSPERDIGIT;
+ for (i = P256_NDIGITS - 1; i > 0; --i) {
+ p256_digit accu = (P256_DIGIT(a, i) << n);
+ accu |= (P256_DIGIT(a, i - 1) >> (P256_BITSPERDIGIT - n));
+ P256_DIGIT(b, i) = accu;
+ }
+ P256_DIGIT(b, i) = (P256_DIGIT(a, i) << n);
+
+ top = (p256_digit)((((p256_ddigit)top) << n) >> P256_BITSPERDIGIT);
+
+ return top;
+}
+
+void p256_shr(const p256_int* a, int n, p256_int* b) {
+ int i;
+
+ n %= P256_BITSPERDIGIT;
+ for (i = 0; i < P256_NDIGITS - 1; ++i) {
+ p256_digit accu = (P256_DIGIT(a, i) >> n);
+ accu |= (P256_DIGIT(a, i + 1) << (P256_BITSPERDIGIT - n));
+ P256_DIGIT(b, i) = accu;
+ }
+ P256_DIGIT(b, i) = (P256_DIGIT(a, i) >> n);
+}
+
+static void p256_shr1(const p256_int* a, int highbit, p256_int* b) {
+ int i;
+
+ for (i = 0; i < P256_NDIGITS - 1; ++i) {
+ p256_digit accu = (P256_DIGIT(a, i) >> 1);
+ accu |= (P256_DIGIT(a, i + 1) << (P256_BITSPERDIGIT - 1));
+ P256_DIGIT(b, i) = accu;
+ }
+ P256_DIGIT(b, i) = (P256_DIGIT(a, i) >> 1) |
+ (highbit << (P256_BITSPERDIGIT - 1));
+}
+
+// Return -1, 0, 1 for a < b, a == b or a > b respectively.
+int p256_cmp(const p256_int* a, const p256_int* b) {
+ int i;
+ p256_sddigit borrow = 0;
+ p256_digit notzero = 0;
+
+ for (i = 0; i < P256_NDIGITS; ++i) {
+ borrow += (p256_sddigit)P256_DIGIT(a, i) - P256_DIGIT(b, i);
+ // Track whether any result digit is ever not zero.
+ // Relies on !!(non-zero) evaluating to 1, e.g., !!(-1) evaluating to 1.
+ notzero |= !!((p256_digit)borrow);
+ borrow >>= P256_BITSPERDIGIT;
+ }
+ return (int)borrow | notzero;
+}
+
+// c = a - b. Returns borrow: 0 or -1.
+int p256_sub(const p256_int* a, const p256_int* b, p256_int* c) {
+ int i;
+ p256_sddigit borrow = 0;
+
+ for (i = 0; i < P256_NDIGITS; ++i) {
+ borrow += (p256_sddigit)P256_DIGIT(a, i) - P256_DIGIT(b, i);
+ if (c) P256_DIGIT(c, i) = (p256_digit)borrow;
+ borrow >>= P256_BITSPERDIGIT;
+ }
+ return (int)borrow;
+}
+
+// c = a + b. Returns carry: 0 or 1.
+int p256_add(const p256_int* a, const p256_int* b, p256_int* c) {
+ int i;
+ p256_ddigit carry = 0;
+
+ for (i = 0; i < P256_NDIGITS; ++i) {
+ carry += (p256_ddigit)P256_DIGIT(a, i) + P256_DIGIT(b, i);
+ if (c) P256_DIGIT(c, i) = (p256_digit)carry;
+ carry >>= P256_BITSPERDIGIT;
+ }
+ return (int)carry;
+}
+
+// b = a + d. Returns carry, 0 or 1.
+int p256_add_d(const p256_int* a, p256_digit d, p256_int* b) {
+ int i;
+ p256_ddigit carry = d;
+
+ for (i = 0; i < P256_NDIGITS; ++i) {
+ carry += (p256_ddigit)P256_DIGIT(a, i);
+ if (b) P256_DIGIT(b, i) = (p256_digit)carry;
+ carry >>= P256_BITSPERDIGIT;
+ }
+ return (int)carry;
+}
+
+// b = 1/a mod MOD, binary euclid.
+void p256_modinv_vartime(const p256_int* MOD,
+ const p256_int* a,
+ p256_int* b) {
+ p256_int R = P256_ZERO;
+ p256_int S = P256_ONE;
+ p256_int U = *MOD;
+ p256_int V = *a;
+
+ for (;;) {
+ if (p256_is_even(&U)) {
+ p256_shr1(&U, 0, &U);
+ if (p256_is_even(&R)) {
+ p256_shr1(&R, 0, &R);
+ } else {
+ // R = (R+MOD)/2
+ p256_shr1(&R, p256_add(&R, MOD, &R), &R);
+ }
+ } else if (p256_is_even(&V)) {
+ p256_shr1(&V, 0, &V);
+ if (p256_is_even(&S)) {
+ p256_shr1(&S, 0, &S);
+ } else {
+ // S = (S+MOD)/2
+ p256_shr1(&S, p256_add(&S, MOD, &S) , &S);
+ }
+ } else { // U,V both odd.
+ if (!p256_sub(&V, &U, NULL)) {
+ p256_sub(&V, &U, &V);
+ if (p256_sub(&S, &R, &S)) p256_add(&S, MOD, &S);
+ if (p256_is_zero(&V)) break; // done.
+ } else {
+ p256_sub(&U, &V, &U);
+ if (p256_sub(&R, &S, &R)) p256_add(&R, MOD, &R);
+ }
+ }
+ }
+
+ p256_mod(MOD, &R, b);
+}
+
+void p256_mod(const p256_int* MOD,
+ const p256_int* in,
+ p256_int* out) {
+ if (out != in) *out = *in;
+ addM(MOD, 0, P256_DIGITS(out), subM(MOD, 0, P256_DIGITS(out), -1));
+}
+
+// Verify y^2 == x^3 - 3x + b mod p
+// and 0 < x < p and 0 < y < p
+int p256_is_valid_point(const p256_int* x, const p256_int* y) {
+ p256_int y2, x3;
+
+ if (p256_cmp(&SECP256r1_p, x) <= 0 ||
+ p256_cmp(&SECP256r1_p, y) <= 0 ||
+ p256_is_zero(x) ||
+ p256_is_zero(y)) return 0;
+
+ p256_modmul(&SECP256r1_p, y, 0, y, &y2); // y^2
+
+ p256_modmul(&SECP256r1_p, x, 0, x, &x3); // x^2
+ p256_modmul(&SECP256r1_p, x, 0, &x3, &x3); // x^3
+ if (p256_sub(&x3, x, &x3)) p256_add(&x3, &SECP256r1_p, &x3); // x^3 - x
+ if (p256_sub(&x3, x, &x3)) p256_add(&x3, &SECP256r1_p, &x3); // x^3 - 2x
+ if (p256_sub(&x3, x, &x3)) p256_add(&x3, &SECP256r1_p, &x3); // x^3 - 3x
+ if (p256_add(&x3, &SECP256r1_b, &x3)) // x^3 - 3x + b
+ p256_sub(&x3, &SECP256r1_p, &x3);
+
+ return p256_cmp(&y2, &x3) == 0;
+}
+
+void p256_from_bin(const uint8_t src[P256_NBYTES], p256_int* dst) {
+ int i;
+ const uint8_t* p = &src[0];
+
+ for (i = P256_NDIGITS - 1; i >= 0; --i) {
+ P256_DIGIT(dst, i) =
+ (p[0] << 24) |
+ (p[1] << 16) |
+ (p[2] << 8) |
+ p[3];
+ p += 4;
+ }
+}
diff --git a/libmincrypt/p256_ec.c b/libmincrypt/p256_ec.c
new file mode 100644
index 0000000..90262cc
--- /dev/null
+++ b/libmincrypt/p256_ec.c
@@ -0,0 +1,1279 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Google Inc. nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// This is an implementation of the P256 elliptic curve group. It's written to
+// be portable 32-bit, although it's still constant-time.
+//
+// WARNING: Implementing these functions in a constant-time manner is far from
+// obvious. Be careful when touching this code.
+//
+// See http://www.imperialviolet.org/2010/12/04/ecc.html ([1]) for background.
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "mincrypt/p256.h"
+
+typedef uint8_t u8;
+typedef uint32_t u32;
+typedef int32_t s32;
+typedef uint64_t u64;
+
+/* Our field elements are represented as nine 32-bit limbs.
+ *
+ * The value of an felem (field element) is:
+ * x[0] + (x[1] * 2**29) + (x[2] * 2**57) + ... + (x[8] * 2**228)
+ *
+ * That is, each limb is alternately 29 or 28-bits wide in little-endian
+ * order.
+ *
+ * This means that an felem hits 2**257, rather than 2**256 as we would like. A
+ * 28, 29, ... pattern would cause us to hit 2**256, but that causes problems
+ * when multiplying as terms end up one bit short of a limb which would require
+ * much bit-shifting to correct.
+ *
+ * Finally, the values stored in an felem are in Montgomery form. So the value
+ * |y| is stored as (y*R) mod p, where p is the P-256 prime and R is 2**257.
+ */
+typedef u32 limb;
+#define NLIMBS 9
+typedef limb felem[NLIMBS];
+
+static const limb kBottom28Bits = 0xfffffff;
+static const limb kBottom29Bits = 0x1fffffff;
+
+/* kOne is the number 1 as an felem. It's 2**257 mod p split up into 29 and
+ * 28-bit words. */
+static const felem kOne = {
+ 2, 0, 0, 0xffff800,
+ 0x1fffffff, 0xfffffff, 0x1fbfffff, 0x1ffffff,
+ 0
+};
+static const felem kZero = {0};
+static const felem kP = {
+ 0x1fffffff, 0xfffffff, 0x1fffffff, 0x3ff,
+ 0, 0, 0x200000, 0xf000000,
+ 0xfffffff
+};
+static const felem k2P = {
+ 0x1ffffffe, 0xfffffff, 0x1fffffff, 0x7ff,
+ 0, 0, 0x400000, 0xe000000,
+ 0x1fffffff
+};
+/* kPrecomputed contains precomputed values to aid the calculation of scalar
+ * multiples of the base point, G. It's actually two, equal length, tables
+ * concatenated.
+ *
+ * The first table contains (x,y) felem pairs for 16 multiples of the base
+ * point, G.
+ *
+ * Index | Index (binary) | Value
+ * 0 | 0000 | 0G (all zeros, omitted)
+ * 1 | 0001 | G
+ * 2 | 0010 | 2**64G
+ * 3 | 0011 | 2**64G + G
+ * 4 | 0100 | 2**128G
+ * 5 | 0101 | 2**128G + G
+ * 6 | 0110 | 2**128G + 2**64G
+ * 7 | 0111 | 2**128G + 2**64G + G
+ * 8 | 1000 | 2**192G
+ * 9 | 1001 | 2**192G + G
+ * 10 | 1010 | 2**192G + 2**64G
+ * 11 | 1011 | 2**192G + 2**64G + G
+ * 12 | 1100 | 2**192G + 2**128G
+ * 13 | 1101 | 2**192G + 2**128G + G
+ * 14 | 1110 | 2**192G + 2**128G + 2**64G
+ * 15 | 1111 | 2**192G + 2**128G + 2**64G + G
+ *
+ * The second table follows the same style, but the terms are 2**32G,
+ * 2**96G, 2**160G, 2**224G.
+ *
+ * This is ~2KB of data. */
+static const limb kPrecomputed[NLIMBS * 2 * 15 * 2] = {
+ 0x11522878, 0xe730d41, 0xdb60179, 0x4afe2ff, 0x12883add, 0xcaddd88, 0x119e7edc, 0xd4a6eab, 0x3120bee,
+ 0x1d2aac15, 0xf25357c, 0x19e45cdd, 0x5c721d0, 0x1992c5a5, 0xa237487, 0x154ba21, 0x14b10bb, 0xae3fe3,
+ 0xd41a576, 0x922fc51, 0x234994f, 0x60b60d3, 0x164586ae, 0xce95f18, 0x1fe49073, 0x3fa36cc, 0x5ebcd2c,
+ 0xb402f2f, 0x15c70bf, 0x1561925c, 0x5a26704, 0xda91e90, 0xcdc1c7f, 0x1ea12446, 0xe1ade1e, 0xec91f22,
+ 0x26f7778, 0x566847e, 0xa0bec9e, 0x234f453, 0x1a31f21a, 0xd85e75c, 0x56c7109, 0xa267a00, 0xb57c050,
+ 0x98fb57, 0xaa837cc, 0x60c0792, 0xcfa5e19, 0x61bab9e, 0x589e39b, 0xa324c5, 0x7d6dee7, 0x2976e4b,
+ 0x1fc4124a, 0xa8c244b, 0x1ce86762, 0xcd61c7e, 0x1831c8e0, 0x75774e1, 0x1d96a5a9, 0x843a649, 0xc3ab0fa,
+ 0x6e2e7d5, 0x7673a2a, 0x178b65e8, 0x4003e9b, 0x1a1f11c2, 0x7816ea, 0xf643e11, 0x58c43df, 0xf423fc2,
+ 0x19633ffa, 0x891f2b2, 0x123c231c, 0x46add8c, 0x54700dd, 0x59e2b17, 0x172db40f, 0x83e277d, 0xb0dd609,
+ 0xfd1da12, 0x35c6e52, 0x19ede20c, 0xd19e0c0, 0x97d0f40, 0xb015b19, 0x449e3f5, 0xe10c9e, 0x33ab581,
+ 0x56a67ab, 0x577734d, 0x1dddc062, 0xc57b10d, 0x149b39d, 0x26a9e7b, 0xc35df9f, 0x48764cd, 0x76dbcca,
+ 0xca4b366, 0xe9303ab, 0x1a7480e7, 0x57e9e81, 0x1e13eb50, 0xf466cf3, 0x6f16b20, 0x4ba3173, 0xc168c33,
+ 0x15cb5439, 0x6a38e11, 0x73658bd, 0xb29564f, 0x3f6dc5b, 0x53b97e, 0x1322c4c0, 0x65dd7ff, 0x3a1e4f6,
+ 0x14e614aa, 0x9246317, 0x1bc83aca, 0xad97eed, 0xd38ce4a, 0xf82b006, 0x341f077, 0xa6add89, 0x4894acd,
+ 0x9f162d5, 0xf8410ef, 0x1b266a56, 0xd7f223, 0x3e0cb92, 0xe39b672, 0x6a2901a, 0x69a8556, 0x7e7c0,
+ 0x9b7d8d3, 0x309a80, 0x1ad05f7f, 0xc2fb5dd, 0xcbfd41d, 0x9ceb638, 0x1051825c, 0xda0cf5b, 0x812e881,
+ 0x6f35669, 0x6a56f2c, 0x1df8d184, 0x345820, 0x1477d477, 0x1645db1, 0xbe80c51, 0xc22be3e, 0xe35e65a,
+ 0x1aeb7aa0, 0xc375315, 0xf67bc99, 0x7fdd7b9, 0x191fc1be, 0x61235d, 0x2c184e9, 0x1c5a839, 0x47a1e26,
+ 0xb7cb456, 0x93e225d, 0x14f3c6ed, 0xccc1ac9, 0x17fe37f3, 0x4988989, 0x1a90c502, 0x2f32042, 0xa17769b,
+ 0xafd8c7c, 0x8191c6e, 0x1dcdb237, 0x16200c0, 0x107b32a1, 0x66c08db, 0x10d06a02, 0x3fc93, 0x5620023,
+ 0x16722b27, 0x68b5c59, 0x270fcfc, 0xfad0ecc, 0xe5de1c2, 0xeab466b, 0x2fc513c, 0x407f75c, 0xbaab133,
+ 0x9705fe9, 0xb88b8e7, 0x734c993, 0x1e1ff8f, 0x19156970, 0xabd0f00, 0x10469ea7, 0x3293ac0, 0xcdc98aa,
+ 0x1d843fd, 0xe14bfe8, 0x15be825f, 0x8b5212, 0xeb3fb67, 0x81cbd29, 0xbc62f16, 0x2b6fcc7, 0xf5a4e29,
+ 0x13560b66, 0xc0b6ac2, 0x51ae690, 0xd41e271, 0xf3e9bd4, 0x1d70aab, 0x1029f72, 0x73e1c35, 0xee70fbc,
+ 0xad81baf, 0x9ecc49a, 0x86c741e, 0xfe6be30, 0x176752e7, 0x23d416, 0x1f83de85, 0x27de188, 0x66f70b8,
+ 0x181cd51f, 0x96b6e4c, 0x188f2335, 0xa5df759, 0x17a77eb6, 0xfeb0e73, 0x154ae914, 0x2f3ec51, 0x3826b59,
+ 0xb91f17d, 0x1c72949, 0x1362bf0a, 0xe23fddf, 0xa5614b0, 0xf7d8f, 0x79061, 0x823d9d2, 0x8213f39,
+ 0x1128ae0b, 0xd095d05, 0xb85c0c2, 0x1ecb2ef, 0x24ddc84, 0xe35e901, 0x18411a4a, 0xf5ddc3d, 0x3786689,
+ 0x52260e8, 0x5ae3564, 0x542b10d, 0x8d93a45, 0x19952aa4, 0x996cc41, 0x1051a729, 0x4be3499, 0x52b23aa,
+ 0x109f307e, 0x6f5b6bb, 0x1f84e1e7, 0x77a0cfa, 0x10c4df3f, 0x25a02ea, 0xb048035, 0xe31de66, 0xc6ecaa3,
+ 0x28ea335, 0x2886024, 0x1372f020, 0xf55d35, 0x15e4684c, 0xf2a9e17, 0x1a4a7529, 0xcb7beb1, 0xb2a78a1,
+ 0x1ab21f1f, 0x6361ccf, 0x6c9179d, 0xb135627, 0x1267b974, 0x4408bad, 0x1cbff658, 0xe3d6511, 0xc7d76f,
+ 0x1cc7a69, 0xe7ee31b, 0x54fab4f, 0x2b914f, 0x1ad27a30, 0xcd3579e, 0xc50124c, 0x50daa90, 0xb13f72,
+ 0xb06aa75, 0x70f5cc6, 0x1649e5aa, 0x84a5312, 0x329043c, 0x41c4011, 0x13d32411, 0xb04a838, 0xd760d2d,
+ 0x1713b532, 0xbaa0c03, 0x84022ab, 0x6bcf5c1, 0x2f45379, 0x18ae070, 0x18c9e11e, 0x20bca9a, 0x66f496b,
+ 0x3eef294, 0x67500d2, 0xd7f613c, 0x2dbbeb, 0xb741038, 0xe04133f, 0x1582968d, 0xbe985f7, 0x1acbc1a,
+ 0x1a6a939f, 0x33e50f6, 0xd665ed4, 0xb4b7bd6, 0x1e5a3799, 0x6b33847, 0x17fa56ff, 0x65ef930, 0x21dc4a,
+ 0x2b37659, 0x450fe17, 0xb357b65, 0xdf5efac, 0x15397bef, 0x9d35a7f, 0x112ac15f, 0x624e62e, 0xa90ae2f,
+ 0x107eecd2, 0x1f69bbe, 0x77d6bce, 0x5741394, 0x13c684fc, 0x950c910, 0x725522b, 0xdc78583, 0x40eeabb,
+ 0x1fde328a, 0xbd61d96, 0xd28c387, 0x9e77d89, 0x12550c40, 0x759cb7d, 0x367ef34, 0xae2a960, 0x91b8bdc,
+ 0x93462a9, 0xf469ef, 0xb2e9aef, 0xd2ca771, 0x54e1f42, 0x7aaa49, 0x6316abb, 0x2413c8e, 0x5425bf9,
+ 0x1bed3e3a, 0xf272274, 0x1f5e7326, 0x6416517, 0xea27072, 0x9cedea7, 0x6e7633, 0x7c91952, 0xd806dce,
+ 0x8e2a7e1, 0xe421e1a, 0x418c9e1, 0x1dbc890, 0x1b395c36, 0xa1dc175, 0x1dc4ef73, 0x8956f34, 0xe4b5cf2,
+ 0x1b0d3a18, 0x3194a36, 0x6c2641f, 0xe44124c, 0xa2f4eaa, 0xa8c25ba, 0xf927ed7, 0x627b614, 0x7371cca,
+ 0xba16694, 0x417bc03, 0x7c0a7e3, 0x9c35c19, 0x1168a205, 0x8b6b00d, 0x10e3edc9, 0x9c19bf2, 0x5882229,
+ 0x1b2b4162, 0xa5cef1a, 0x1543622b, 0x9bd433e, 0x364e04d, 0x7480792, 0x5c9b5b3, 0xe85ff25, 0x408ef57,
+ 0x1814cfa4, 0x121b41b, 0xd248a0f, 0x3b05222, 0x39bb16a, 0xc75966d, 0xa038113, 0xa4a1769, 0x11fbc6c,
+ 0x917e50e, 0xeec3da8, 0x169d6eac, 0x10c1699, 0xa416153, 0xf724912, 0x15cd60b7, 0x4acbad9, 0x5efc5fa,
+ 0xf150ed7, 0x122b51, 0x1104b40a, 0xcb7f442, 0xfbb28ff, 0x6ac53ca, 0x196142cc, 0x7bf0fa9, 0x957651,
+ 0x4e0f215, 0xed439f8, 0x3f46bd5, 0x5ace82f, 0x110916b6, 0x6db078, 0xffd7d57, 0xf2ecaac, 0xca86dec,
+ 0x15d6b2da, 0x965ecc9, 0x1c92b4c2, 0x1f3811, 0x1cb080f5, 0x2d8b804, 0x19d1c12d, 0xf20bd46, 0x1951fa7,
+ 0xa3656c3, 0x523a425, 0xfcd0692, 0xd44ddc8, 0x131f0f5b, 0xaf80e4a, 0xcd9fc74, 0x99bb618, 0x2db944c,
+ 0xa673090, 0x1c210e1, 0x178c8d23, 0x1474383, 0x10b8743d, 0x985a55b, 0x2e74779, 0x576138, 0x9587927,
+ 0x133130fa, 0xbe05516, 0x9f4d619, 0xbb62570, 0x99ec591, 0xd9468fe, 0x1d07782d, 0xfc72e0b, 0x701b298,
+ 0x1863863b, 0x85954b8, 0x121a0c36, 0x9e7fedf, 0xf64b429, 0x9b9d71e, 0x14e2f5d8, 0xf858d3a, 0x942eea8,
+ 0xda5b765, 0x6edafff, 0xa9d18cc, 0xc65e4ba, 0x1c747e86, 0xe4ea915, 0x1981d7a1, 0x8395659, 0x52ed4e2,
+ 0x87d43b7, 0x37ab11b, 0x19d292ce, 0xf8d4692, 0x18c3053f, 0x8863e13, 0x4c146c0, 0x6bdf55a, 0x4e4457d,
+ 0x16152289, 0xac78ec2, 0x1a59c5a2, 0x2028b97, 0x71c2d01, 0x295851f, 0x404747b, 0x878558d, 0x7d29aa4,
+ 0x13d8341f, 0x8daefd7, 0x139c972d, 0x6b7ea75, 0xd4a9dde, 0xff163d8, 0x81d55d7, 0xa5bef68, 0xb7b30d8,
+ 0xbe73d6f, 0xaa88141, 0xd976c81, 0x7e7a9cc, 0x18beb771, 0xd773cbd, 0x13f51951, 0x9d0c177, 0x1c49a78,
+};
+
+
+/* Field element operations: */
+
+/* NON_ZERO_TO_ALL_ONES returns:
+ * 0xffffffff for 0 < x <= 2**31
+ * 0 for x == 0 or x > 2**31.
+ *
+ * x must be a u32 or an equivalent type such as limb. */
+#define NON_ZERO_TO_ALL_ONES(x) ((((u32)(x) - 1) >> 31) - 1)
+
+/* felem_reduce_carry adds a multiple of p in order to cancel |carry|,
+ * which is a term at 2**257.
+ *
+ * On entry: carry < 2**3, inout[0,2,...] < 2**29, inout[1,3,...] < 2**28.
+ * On exit: inout[0,2,..] < 2**30, inout[1,3,...] < 2**29. */
+static void felem_reduce_carry(felem inout, limb carry) {
+ const u32 carry_mask = NON_ZERO_TO_ALL_ONES(carry);
+
+ inout[0] += carry << 1;
+ inout[3] += 0x10000000 & carry_mask;
+ /* carry < 2**3 thus (carry << 11) < 2**14 and we added 2**28 in the
+ * previous line therefore this doesn't underflow. */
+ inout[3] -= carry << 11;
+ inout[4] += (0x20000000 - 1) & carry_mask;
+ inout[5] += (0x10000000 - 1) & carry_mask;
+ inout[6] += (0x20000000 - 1) & carry_mask;
+ inout[6] -= carry << 22;
+ /* This may underflow if carry is non-zero but, if so, we'll fix it in the
+ * next line. */
+ inout[7] -= 1 & carry_mask;
+ inout[7] += carry << 25;
+}
+
+/* felem_sum sets out = in+in2.
+ *
+ * On entry, in[i]+in2[i] must not overflow a 32-bit word.
+ * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29 */
+static void felem_sum(felem out, const felem in, const felem in2) {
+ limb carry = 0;
+ unsigned i;
+
+ for (i = 0;; i++) {
+ out[i] = in[i] + in2[i];
+ out[i] += carry;
+ carry = out[i] >> 29;
+ out[i] &= kBottom29Bits;
+
+ i++;
+ if (i == NLIMBS)
+ break;
+
+ out[i] = in[i] + in2[i];
+ out[i] += carry;
+ carry = out[i] >> 28;
+ out[i] &= kBottom28Bits;
+ }
+
+ felem_reduce_carry(out, carry);
+}
+
+#define two31m3 (((limb)1) << 31) - (((limb)1) << 3)
+#define two30m2 (((limb)1) << 30) - (((limb)1) << 2)
+#define two30p13m2 (((limb)1) << 30) + (((limb)1) << 13) - (((limb)1) << 2)
+#define two31m2 (((limb)1) << 31) - (((limb)1) << 2)
+#define two31p24m2 (((limb)1) << 31) + (((limb)1) << 24) - (((limb)1) << 2)
+#define two30m27m2 (((limb)1) << 30) - (((limb)1) << 27) - (((limb)1) << 2)
+
+/* zero31 is 0 mod p. */
+static const felem zero31 = { two31m3, two30m2, two31m2, two30p13m2, two31m2, two30m2, two31p24m2, two30m27m2, two31m2 };
+
+/* felem_diff sets out = in-in2.
+ *
+ * On entry: in[0,2,...] < 2**30, in[1,3,...] < 2**29 and
+ * in2[0,2,...] < 2**30, in2[1,3,...] < 2**29.
+ * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. */
+static void felem_diff(felem out, const felem in, const felem in2) {
+ limb carry = 0;
+ unsigned i;
+
+ for (i = 0;; i++) {
+ out[i] = in[i] - in2[i];
+ out[i] += zero31[i];
+ out[i] += carry;
+ carry = out[i] >> 29;
+ out[i] &= kBottom29Bits;
+
+ i++;
+ if (i == NLIMBS)
+ break;
+
+ out[i] = in[i] - in2[i];
+ out[i] += zero31[i];
+ out[i] += carry;
+ carry = out[i] >> 28;
+ out[i] &= kBottom28Bits;
+ }
+
+ felem_reduce_carry(out, carry);
+}
+
+/* felem_reduce_degree sets out = tmp/R mod p where tmp contains 64-bit words
+ * with the same 29,28,... bit positions as an felem.
+ *
+ * The values in felems are in Montgomery form: x*R mod p where R = 2**257.
+ * Since we just multiplied two Montgomery values together, the result is
+ * x*y*R*R mod p. We wish to divide by R in order for the result also to be
+ * in Montgomery form.
+ *
+ * On entry: tmp[i] < 2**64
+ * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29 */
+static void felem_reduce_degree(felem out, u64 tmp[17]) {
+ /* The following table may be helpful when reading this code:
+ *
+ * Limb number: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10...
+ * Width (bits): 29| 28| 29| 28| 29| 28| 29| 28| 29| 28| 29
+ * Start bit: 0 | 29| 57| 86|114|143|171|200|228|257|285
+ * (odd phase): 0 | 28| 57| 85|114|142|171|199|228|256|285 */
+ limb tmp2[18], carry, x, xMask;
+ unsigned i;
+
+ /* tmp contains 64-bit words with the same 29,28,29-bit positions as an
+ * felem. So the top of an element of tmp might overlap with another
+ * element two positions down. The following loop eliminates this
+ * overlap. */
+ tmp2[0] = (limb)(tmp[0] & kBottom29Bits);
+
+ /* In the following we use "(limb) tmp[x]" and "(limb) (tmp[x]>>32)" to try
+ * and hint to the compiler that it can do a single-word shift by selecting
+ * the right register rather than doing a double-word shift and truncating
+ * afterwards. */
+ tmp2[1] = ((limb) tmp[0]) >> 29;
+ tmp2[1] |= (((limb)(tmp[0] >> 32)) << 3) & kBottom28Bits;
+ tmp2[1] += ((limb) tmp[1]) & kBottom28Bits;
+ carry = tmp2[1] >> 28;
+ tmp2[1] &= kBottom28Bits;
+
+ for (i = 2; i < 17; i++) {
+ tmp2[i] = ((limb)(tmp[i - 2] >> 32)) >> 25;
+ tmp2[i] += ((limb)(tmp[i - 1])) >> 28;
+ tmp2[i] += (((limb)(tmp[i - 1] >> 32)) << 4) & kBottom29Bits;
+ tmp2[i] += ((limb) tmp[i]) & kBottom29Bits;
+ tmp2[i] += carry;
+ carry = tmp2[i] >> 29;
+ tmp2[i] &= kBottom29Bits;
+
+ i++;
+ if (i == 17)
+ break;
+ tmp2[i] = ((limb)(tmp[i - 2] >> 32)) >> 25;
+ tmp2[i] += ((limb)(tmp[i - 1])) >> 29;
+ tmp2[i] += (((limb)(tmp[i - 1] >> 32)) << 3) & kBottom28Bits;
+ tmp2[i] += ((limb) tmp[i]) & kBottom28Bits;
+ tmp2[i] += carry;
+ carry = tmp2[i] >> 28;
+ tmp2[i] &= kBottom28Bits;
+ }
+
+ tmp2[17] = ((limb)(tmp[15] >> 32)) >> 25;
+ tmp2[17] += ((limb)(tmp[16])) >> 29;
+ tmp2[17] += (((limb)(tmp[16] >> 32)) << 3);
+ tmp2[17] += carry;
+
+ /* Montgomery elimination of terms.
+ *
+ * Since R is 2**257, we can divide by R with a bitwise shift if we can
+ * ensure that the right-most 257 bits are all zero. We can make that true by
+ * adding multiplies of p without affecting the value.
+ *
+ * So we eliminate limbs from right to left. Since the bottom 29 bits of p
+ * are all ones, then by adding tmp2[0]*p to tmp2 we'll make tmp2[0] == 0.
+ * We can do that for 8 further limbs and then right shift to eliminate the
+ * extra factor of R. */
+ for (i = 0;; i += 2) {
+ tmp2[i + 1] += tmp2[i] >> 29;
+ x = tmp2[i] & kBottom29Bits;
+ xMask = NON_ZERO_TO_ALL_ONES(x);
+ tmp2[i] = 0;
+
+ /* The bounds calculations for this loop are tricky. Each iteration of
+ * the loop eliminates two words by adding values to words to their
+ * right.
+ *
+ * The following table contains the amounts added to each word (as an
+ * offset from the value of i at the top of the loop). The amounts are
+ * accounted for from the first and second half of the loop separately
+ * and are written as, for example, 28 to mean a value <2**28.
+ *
+ * Word: 3 4 5 6 7 8 9 10
+ * Added in top half: 28 11 29 21 29 28
+ * 28 29
+ * 29
+ * Added in bottom half: 29 10 28 21 28 28
+ * 29
+ *
+ * The value that is currently offset 7 will be offset 5 for the next
+ * iteration and then offset 3 for the iteration after that. Therefore
+ * the total value added will be the values added at 7, 5 and 3.
+ *
+ * The following table accumulates these values. The sums at the bottom
+ * are written as, for example, 29+28, to mean a value < 2**29+2**28.
+ *
+ * Word: 3 4 5 6 7 8 9 10 11 12 13
+ * 28 11 10 29 21 29 28 28 28 28 28
+ * 29 28 11 28 29 28 29 28 29 28
+ * 29 28 21 21 29 21 29 21
+ * 10 29 28 21 28 21 28
+ * 28 29 28 29 28 29 28
+ * 11 10 29 10 29 10
+ * 29 28 11 28 11
+ * 29 29
+ * --------------------------------------------
+ * 30+ 31+ 30+ 31+ 30+
+ * 28+ 29+ 28+ 29+ 21+
+ * 21+ 28+ 21+ 28+ 10
+ * 10 21+ 10 21+
+ * 11 11
+ *
+ * So the greatest amount is added to tmp2[10] and tmp2[12]. If
+ * tmp2[10/12] has an initial value of <2**29, then the maximum value
+ * will be < 2**31 + 2**30 + 2**28 + 2**21 + 2**11, which is < 2**32,
+ * as required. */
+ tmp2[i + 3] += (x << 10) & kBottom28Bits;
+ tmp2[i + 4] += (x >> 18);
+
+ tmp2[i + 6] += (x << 21) & kBottom29Bits;
+ tmp2[i + 7] += x >> 8;
+
+ /* At position 200, which is the starting bit position for word 7, we
+ * have a factor of 0xf000000 = 2**28 - 2**24. */
+ tmp2[i + 7] += 0x10000000 & xMask;
+ /* Word 7 is 28 bits wide, so the 2**28 term exactly hits word 8. */
+ tmp2[i + 8] += (x - 1) & xMask;
+ tmp2[i + 7] -= (x << 24) & kBottom28Bits;
+ tmp2[i + 8] -= x >> 4;
+
+ tmp2[i + 8] += 0x20000000 & xMask;
+ tmp2[i + 8] -= x;
+ tmp2[i + 8] += (x << 28) & kBottom29Bits;
+ tmp2[i + 9] += ((x >> 1) - 1) & xMask;
+
+ if (i+1 == NLIMBS)
+ break;
+ tmp2[i + 2] += tmp2[i + 1] >> 28;
+ x = tmp2[i + 1] & kBottom28Bits;
+ xMask = NON_ZERO_TO_ALL_ONES(x);
+ tmp2[i + 1] = 0;
+
+ tmp2[i + 4] += (x << 11) & kBottom29Bits;
+ tmp2[i + 5] += (x >> 18);
+
+ tmp2[i + 7] += (x << 21) & kBottom28Bits;
+ tmp2[i + 8] += x >> 7;
+
+ /* At position 199, which is the starting bit of the 8th word when
+ * dealing with a context starting on an odd word, we have a factor of
+ * 0x1e000000 = 2**29 - 2**25. Since we have not updated i, the 8th
+ * word from i+1 is i+8. */
+ tmp2[i + 8] += 0x20000000 & xMask;
+ tmp2[i + 9] += (x - 1) & xMask;
+ tmp2[i + 8] -= (x << 25) & kBottom29Bits;
+ tmp2[i + 9] -= x >> 4;
+
+ tmp2[i + 9] += 0x10000000 & xMask;
+ tmp2[i + 9] -= x;
+ tmp2[i + 10] += (x - 1) & xMask;
+ }
+
+ /* We merge the right shift with a carry chain. The words above 2**257 have
+ * widths of 28,29,... which we need to correct when copying them down. */
+ carry = 0;
+ for (i = 0; i < 8; i++) {
+ /* The maximum value of tmp2[i + 9] occurs on the first iteration and
+ * is < 2**30+2**29+2**28. Adding 2**29 (from tmp2[i + 10]) is
+ * therefore safe. */
+ out[i] = tmp2[i + 9];
+ out[i] += carry;
+ out[i] += (tmp2[i + 10] << 28) & kBottom29Bits;
+ carry = out[i] >> 29;
+ out[i] &= kBottom29Bits;
+
+ i++;
+ out[i] = tmp2[i + 9] >> 1;
+ out[i] += carry;
+ carry = out[i] >> 28;
+ out[i] &= kBottom28Bits;
+ }
+
+ out[8] = tmp2[17];
+ out[8] += carry;
+ carry = out[8] >> 29;
+ out[8] &= kBottom29Bits;
+
+ felem_reduce_carry(out, carry);
+}
+
+/* felem_square sets out=in*in.
+ *
+ * On entry: in[0,2,...] < 2**30, in[1,3,...] < 2**29.
+ * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. */
+static void felem_square(felem out, const felem in) {
+ u64 tmp[17];
+
+ tmp[0] = ((u64) in[0]) * in[0];
+ tmp[1] = ((u64) in[0]) * (in[1] << 1);
+ tmp[2] = ((u64) in[0]) * (in[2] << 1) +
+ ((u64) in[1]) * (in[1] << 1);
+ tmp[3] = ((u64) in[0]) * (in[3] << 1) +
+ ((u64) in[1]) * (in[2] << 1);
+ tmp[4] = ((u64) in[0]) * (in[4] << 1) +
+ ((u64) in[1]) * (in[3] << 2) + ((u64) in[2]) * in[2];
+ tmp[5] = ((u64) in[0]) * (in[5] << 1) + ((u64) in[1]) *
+ (in[4] << 1) + ((u64) in[2]) * (in[3] << 1);
+ tmp[6] = ((u64) in[0]) * (in[6] << 1) + ((u64) in[1]) *
+ (in[5] << 2) + ((u64) in[2]) * (in[4] << 1) +
+ ((u64) in[3]) * (in[3] << 1);
+ tmp[7] = ((u64) in[0]) * (in[7] << 1) + ((u64) in[1]) *
+ (in[6] << 1) + ((u64) in[2]) * (in[5] << 1) +
+ ((u64) in[3]) * (in[4] << 1);
+ /* tmp[8] has the greatest value of 2**61 + 2**60 + 2**61 + 2**60 + 2**60,
+ * which is < 2**64 as required. */
+ tmp[8] = ((u64) in[0]) * (in[8] << 1) + ((u64) in[1]) *
+ (in[7] << 2) + ((u64) in[2]) * (in[6] << 1) +
+ ((u64) in[3]) * (in[5] << 2) + ((u64) in[4]) * in[4];
+ tmp[9] = ((u64) in[1]) * (in[8] << 1) + ((u64) in[2]) *
+ (in[7] << 1) + ((u64) in[3]) * (in[6] << 1) +
+ ((u64) in[4]) * (in[5] << 1);
+ tmp[10] = ((u64) in[2]) * (in[8] << 1) + ((u64) in[3]) *
+ (in[7] << 2) + ((u64) in[4]) * (in[6] << 1) +
+ ((u64) in[5]) * (in[5] << 1);
+ tmp[11] = ((u64) in[3]) * (in[8] << 1) + ((u64) in[4]) *
+ (in[7] << 1) + ((u64) in[5]) * (in[6] << 1);
+ tmp[12] = ((u64) in[4]) * (in[8] << 1) +
+ ((u64) in[5]) * (in[7] << 2) + ((u64) in[6]) * in[6];
+ tmp[13] = ((u64) in[5]) * (in[8] << 1) +
+ ((u64) in[6]) * (in[7] << 1);
+ tmp[14] = ((u64) in[6]) * (in[8] << 1) +
+ ((u64) in[7]) * (in[7] << 1);
+ tmp[15] = ((u64) in[7]) * (in[8] << 1);
+ tmp[16] = ((u64) in[8]) * in[8];
+
+ felem_reduce_degree(out, tmp);
+}
+
+/* felem_mul sets out=in*in2.
+ *
+ * On entry: in[0,2,...] < 2**30, in[1,3,...] < 2**29 and
+ * in2[0,2,...] < 2**30, in2[1,3,...] < 2**29.
+ * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. */
+static void felem_mul(felem out, const felem in, const felem in2) {
+ u64 tmp[17];
+
+ tmp[0] = ((u64) in[0]) * in2[0];
+ tmp[1] = ((u64) in[0]) * (in2[1] << 0) +
+ ((u64) in[1]) * (in2[0] << 0);
+ tmp[2] = ((u64) in[0]) * (in2[2] << 0) + ((u64) in[1]) *
+ (in2[1] << 1) + ((u64) in[2]) * (in2[0] << 0);
+ tmp[3] = ((u64) in[0]) * (in2[3] << 0) + ((u64) in[1]) *
+ (in2[2] << 0) + ((u64) in[2]) * (in2[1] << 0) +
+ ((u64) in[3]) * (in2[0] << 0);
+ tmp[4] = ((u64) in[0]) * (in2[4] << 0) + ((u64) in[1]) *
+ (in2[3] << 1) + ((u64) in[2]) * (in2[2] << 0) +
+ ((u64) in[3]) * (in2[1] << 1) +
+ ((u64) in[4]) * (in2[0] << 0);
+ tmp[5] = ((u64) in[0]) * (in2[5] << 0) + ((u64) in[1]) *
+ (in2[4] << 0) + ((u64) in[2]) * (in2[3] << 0) +
+ ((u64) in[3]) * (in2[2] << 0) + ((u64) in[4]) *
+ (in2[1] << 0) + ((u64) in[5]) * (in2[0] << 0);
+ tmp[6] = ((u64) in[0]) * (in2[6] << 0) + ((u64) in[1]) *
+ (in2[5] << 1) + ((u64) in[2]) * (in2[4] << 0) +
+ ((u64) in[3]) * (in2[3] << 1) + ((u64) in[4]) *
+ (in2[2] << 0) + ((u64) in[5]) * (in2[1] << 1) +
+ ((u64) in[6]) * (in2[0] << 0);
+ tmp[7] = ((u64) in[0]) * (in2[7] << 0) + ((u64) in[1]) *
+ (in2[6] << 0) + ((u64) in[2]) * (in2[5] << 0) +
+ ((u64) in[3]) * (in2[4] << 0) + ((u64) in[4]) *
+ (in2[3] << 0) + ((u64) in[5]) * (in2[2] << 0) +
+ ((u64) in[6]) * (in2[1] << 0) +
+ ((u64) in[7]) * (in2[0] << 0);
+ /* tmp[8] has the greatest value but doesn't overflow. See logic in
+ * felem_square. */
+ tmp[8] = ((u64) in[0]) * (in2[8] << 0) + ((u64) in[1]) *
+ (in2[7] << 1) + ((u64) in[2]) * (in2[6] << 0) +
+ ((u64) in[3]) * (in2[5] << 1) + ((u64) in[4]) *
+ (in2[4] << 0) + ((u64) in[5]) * (in2[3] << 1) +
+ ((u64) in[6]) * (in2[2] << 0) + ((u64) in[7]) *
+ (in2[1] << 1) + ((u64) in[8]) * (in2[0] << 0);
+ tmp[9] = ((u64) in[1]) * (in2[8] << 0) + ((u64) in[2]) *
+ (in2[7] << 0) + ((u64) in[3]) * (in2[6] << 0) +
+ ((u64) in[4]) * (in2[5] << 0) + ((u64) in[5]) *
+ (in2[4] << 0) + ((u64) in[6]) * (in2[3] << 0) +
+ ((u64) in[7]) * (in2[2] << 0) +
+ ((u64) in[8]) * (in2[1] << 0);
+ tmp[10] = ((u64) in[2]) * (in2[8] << 0) + ((u64) in[3]) *
+ (in2[7] << 1) + ((u64) in[4]) * (in2[6] << 0) +
+ ((u64) in[5]) * (in2[5] << 1) + ((u64) in[6]) *
+ (in2[4] << 0) + ((u64) in[7]) * (in2[3] << 1) +
+ ((u64) in[8]) * (in2[2] << 0);
+ tmp[11] = ((u64) in[3]) * (in2[8] << 0) + ((u64) in[4]) *
+ (in2[7] << 0) + ((u64) in[5]) * (in2[6] << 0) +
+ ((u64) in[6]) * (in2[5] << 0) + ((u64) in[7]) *
+ (in2[4] << 0) + ((u64) in[8]) * (in2[3] << 0);
+ tmp[12] = ((u64) in[4]) * (in2[8] << 0) + ((u64) in[5]) *
+ (in2[7] << 1) + ((u64) in[6]) * (in2[6] << 0) +
+ ((u64) in[7]) * (in2[5] << 1) +
+ ((u64) in[8]) * (in2[4] << 0);
+ tmp[13] = ((u64) in[5]) * (in2[8] << 0) + ((u64) in[6]) *
+ (in2[7] << 0) + ((u64) in[7]) * (in2[6] << 0) +
+ ((u64) in[8]) * (in2[5] << 0);
+ tmp[14] = ((u64) in[6]) * (in2[8] << 0) + ((u64) in[7]) *
+ (in2[7] << 1) + ((u64) in[8]) * (in2[6] << 0);
+ tmp[15] = ((u64) in[7]) * (in2[8] << 0) +
+ ((u64) in[8]) * (in2[7] << 0);
+ tmp[16] = ((u64) in[8]) * (in2[8] << 0);
+
+ felem_reduce_degree(out, tmp);
+}
+
+static void felem_assign(felem out, const felem in) {
+ memcpy(out, in, sizeof(felem));
+}
+
+/* felem_inv calculates |out| = |in|^{-1}
+ *
+ * Based on Fermat's Little Theorem:
+ * a^p = a (mod p)
+ * a^{p-1} = 1 (mod p)
+ * a^{p-2} = a^{-1} (mod p)
+ */
+static void felem_inv(felem out, const felem in) {
+ felem ftmp, ftmp2;
+ /* each e_I will hold |in|^{2^I - 1} */
+ felem e2, e4, e8, e16, e32, e64;
+ unsigned i;
+
+ felem_square(ftmp, in); /* 2^1 */
+ felem_mul(ftmp, in, ftmp); /* 2^2 - 2^0 */
+ felem_assign(e2, ftmp);
+ felem_square(ftmp, ftmp); /* 2^3 - 2^1 */
+ felem_square(ftmp, ftmp); /* 2^4 - 2^2 */
+ felem_mul(ftmp, ftmp, e2); /* 2^4 - 2^0 */
+ felem_assign(e4, ftmp);
+ felem_square(ftmp, ftmp); /* 2^5 - 2^1 */
+ felem_square(ftmp, ftmp); /* 2^6 - 2^2 */
+ felem_square(ftmp, ftmp); /* 2^7 - 2^3 */
+ felem_square(ftmp, ftmp); /* 2^8 - 2^4 */
+ felem_mul(ftmp, ftmp, e4); /* 2^8 - 2^0 */
+ felem_assign(e8, ftmp);
+ for (i = 0; i < 8; i++) {
+ felem_square(ftmp, ftmp);
+ } /* 2^16 - 2^8 */
+ felem_mul(ftmp, ftmp, e8); /* 2^16 - 2^0 */
+ felem_assign(e16, ftmp);
+ for (i = 0; i < 16; i++) {
+ felem_square(ftmp, ftmp);
+ } /* 2^32 - 2^16 */
+ felem_mul(ftmp, ftmp, e16); /* 2^32 - 2^0 */
+ felem_assign(e32, ftmp);
+ for (i = 0; i < 32; i++) {
+ felem_square(ftmp, ftmp);
+ } /* 2^64 - 2^32 */
+ felem_assign(e64, ftmp);
+ felem_mul(ftmp, ftmp, in); /* 2^64 - 2^32 + 2^0 */
+ for (i = 0; i < 192; i++) {
+ felem_square(ftmp, ftmp);
+ } /* 2^256 - 2^224 + 2^192 */
+
+ felem_mul(ftmp2, e64, e32); /* 2^64 - 2^0 */
+ for (i = 0; i < 16; i++) {
+ felem_square(ftmp2, ftmp2);
+ } /* 2^80 - 2^16 */
+ felem_mul(ftmp2, ftmp2, e16); /* 2^80 - 2^0 */
+ for (i = 0; i < 8; i++) {
+ felem_square(ftmp2, ftmp2);
+ } /* 2^88 - 2^8 */
+ felem_mul(ftmp2, ftmp2, e8); /* 2^88 - 2^0 */
+ for (i = 0; i < 4; i++) {
+ felem_square(ftmp2, ftmp2);
+ } /* 2^92 - 2^4 */
+ felem_mul(ftmp2, ftmp2, e4); /* 2^92 - 2^0 */
+ felem_square(ftmp2, ftmp2); /* 2^93 - 2^1 */
+ felem_square(ftmp2, ftmp2); /* 2^94 - 2^2 */
+ felem_mul(ftmp2, ftmp2, e2); /* 2^94 - 2^0 */
+ felem_square(ftmp2, ftmp2); /* 2^95 - 2^1 */
+ felem_square(ftmp2, ftmp2); /* 2^96 - 2^2 */
+ felem_mul(ftmp2, ftmp2, in); /* 2^96 - 3 */
+
+ felem_mul(out, ftmp2, ftmp); /* 2^256 - 2^224 + 2^192 + 2^96 - 3 */
+}
+
+/* felem_scalar_3 sets out=3*out.
+ *
+ * On entry: out[0,2,...] < 2**30, out[1,3,...] < 2**29.
+ * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. */
+static void felem_scalar_3(felem out) {
+ limb carry = 0;
+ unsigned i;
+
+ for (i = 0;; i++) {
+ out[i] *= 3;
+ out[i] += carry;
+ carry = out[i] >> 29;
+ out[i] &= kBottom29Bits;
+
+ i++;
+ if (i == NLIMBS)
+ break;
+
+ out[i] *= 3;
+ out[i] += carry;
+ carry = out[i] >> 28;
+ out[i] &= kBottom28Bits;
+ }
+
+ felem_reduce_carry(out, carry);
+}
+
+/* felem_scalar_4 sets out=4*out.
+ *
+ * On entry: out[0,2,...] < 2**30, out[1,3,...] < 2**29.
+ * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. */
+static void felem_scalar_4(felem out) {
+ limb carry = 0, next_carry;
+ unsigned i;
+
+ for (i = 0;; i++) {
+ next_carry = out[i] >> 27;
+ out[i] <<= 2;
+ out[i] &= kBottom29Bits;
+ out[i] += carry;
+ carry = next_carry + (out[i] >> 29);
+ out[i] &= kBottom29Bits;
+
+ i++;
+ if (i == NLIMBS)
+ break;
+
+ next_carry = out[i] >> 26;
+ out[i] <<= 2;
+ out[i] &= kBottom28Bits;
+ out[i] += carry;
+ carry = next_carry + (out[i] >> 28);
+ out[i] &= kBottom28Bits;
+ }
+
+ felem_reduce_carry(out, carry);
+}
+
+/* felem_scalar_8 sets out=8*out.
+ *
+ * On entry: out[0,2,...] < 2**30, out[1,3,...] < 2**29.
+ * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. */
+static void felem_scalar_8(felem out) {
+ limb carry = 0, next_carry;
+ unsigned i;
+
+ for (i = 0;; i++) {
+ next_carry = out[i] >> 26;
+ out[i] <<= 3;
+ out[i] &= kBottom29Bits;
+ out[i] += carry;
+ carry = next_carry + (out[i] >> 29);
+ out[i] &= kBottom29Bits;
+
+ i++;
+ if (i == NLIMBS)
+ break;
+
+ next_carry = out[i] >> 25;
+ out[i] <<= 3;
+ out[i] &= kBottom28Bits;
+ out[i] += carry;
+ carry = next_carry + (out[i] >> 28);
+ out[i] &= kBottom28Bits;
+ }
+
+ felem_reduce_carry(out, carry);
+}
+
+/* felem_is_zero_vartime returns 1 iff |in| == 0. It takes a variable amount of
+ * time depending on the value of |in|. */
+static char felem_is_zero_vartime(const felem in) {
+ limb carry;
+ int i;
+ limb tmp[NLIMBS];
+
+ felem_assign(tmp, in);
+
+ /* First, reduce tmp to a minimal form. */
+ do {
+ carry = 0;
+ for (i = 0;; i++) {
+ tmp[i] += carry;
+ carry = tmp[i] >> 29;
+ tmp[i] &= kBottom29Bits;
+
+ i++;
+ if (i == NLIMBS)
+ break;
+
+ tmp[i] += carry;
+ carry = tmp[i] >> 28;
+ tmp[i] &= kBottom28Bits;
+ }
+
+ felem_reduce_carry(tmp, carry);
+ } while (carry);
+
+ /* tmp < 2**257, so the only possible zero values are 0, p and 2p. */
+ return memcmp(tmp, kZero, sizeof(tmp)) == 0 ||
+ memcmp(tmp, kP, sizeof(tmp)) == 0 ||
+ memcmp(tmp, k2P, sizeof(tmp)) == 0;
+}
+
+
+/* Group operations:
+ *
+ * Elements of the elliptic curve group are represented in Jacobian
+ * coordinates: (x, y, z). An affine point (x', y') is x'=x/z**2, y'=y/z**3 in
+ * Jacobian form. */
+
+/* point_double sets {x_out,y_out,z_out} = 2*{x,y,z}.
+ *
+ * See http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l */
+static void point_double(felem x_out, felem y_out, felem z_out, const felem x,
+ const felem y, const felem z) {
+ felem delta, gamma, alpha, beta, tmp, tmp2;
+
+ felem_square(delta, z);
+ felem_square(gamma, y);
+ felem_mul(beta, x, gamma);
+
+ felem_sum(tmp, x, delta);
+ felem_diff(tmp2, x, delta);
+ felem_mul(alpha, tmp, tmp2);
+ felem_scalar_3(alpha);
+
+ felem_sum(tmp, y, z);
+ felem_square(tmp, tmp);
+ felem_diff(tmp, tmp, gamma);
+ felem_diff(z_out, tmp, delta);
+
+ felem_scalar_4(beta);
+ felem_square(x_out, alpha);
+ felem_diff(x_out, x_out, beta);
+ felem_diff(x_out, x_out, beta);
+
+ felem_diff(tmp, beta, x_out);
+ felem_mul(tmp, alpha, tmp);
+ felem_square(tmp2, gamma);
+ felem_scalar_8(tmp2);
+ felem_diff(y_out, tmp, tmp2);
+}
+
+/* point_add_mixed sets {x_out,y_out,z_out} = {x1,y1,z1} + {x2,y2,1}.
+ * (i.e. the second point is affine.)
+ *
+ * See http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl
+ *
+ * Note that this function does not handle P+P, infinity+P nor P+infinity
+ * correctly. */
+static void point_add_mixed(felem x_out, felem y_out, felem z_out,
+ const felem x1, const felem y1, const felem z1,
+ const felem x2, const felem y2) {
+ felem z1z1, z1z1z1, s2, u2, h, i, j, r, rr, v, tmp;
+
+ felem_square(z1z1, z1);
+ felem_sum(tmp, z1, z1);
+
+ felem_mul(u2, x2, z1z1);
+ felem_mul(z1z1z1, z1, z1z1);
+ felem_mul(s2, y2, z1z1z1);
+ felem_diff(h, u2, x1);
+ felem_sum(i, h, h);
+ felem_square(i, i);
+ felem_mul(j, h, i);
+ felem_diff(r, s2, y1);
+ felem_sum(r, r, r);
+ felem_mul(v, x1, i);
+
+ felem_mul(z_out, tmp, h);
+ felem_square(rr, r);
+ felem_diff(x_out, rr, j);
+ felem_diff(x_out, x_out, v);
+ felem_diff(x_out, x_out, v);
+
+ felem_diff(tmp, v, x_out);
+ felem_mul(y_out, tmp, r);
+ felem_mul(tmp, y1, j);
+ felem_diff(y_out, y_out, tmp);
+ felem_diff(y_out, y_out, tmp);
+}
+
+/* point_add sets {x_out,y_out,z_out} = {x1,y1,z1} + {x2,y2,z2}.
+ *
+ * See http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl
+ *
+ * Note that this function does not handle P+P, infinity+P nor P+infinity
+ * correctly. */
+static void point_add(felem x_out, felem y_out, felem z_out, const felem x1,
+ const felem y1, const felem z1, const felem x2,
+ const felem y2, const felem z2) {
+ felem z1z1, z1z1z1, z2z2, z2z2z2, s1, s2, u1, u2, h, i, j, r, rr, v, tmp;
+
+ felem_square(z1z1, z1);
+ felem_square(z2z2, z2);
+ felem_mul(u1, x1, z2z2);
+
+ felem_sum(tmp, z1, z2);
+ felem_square(tmp, tmp);
+ felem_diff(tmp, tmp, z1z1);
+ felem_diff(tmp, tmp, z2z2);
+
+ felem_mul(z2z2z2, z2, z2z2);
+ felem_mul(s1, y1, z2z2z2);
+
+ felem_mul(u2, x2, z1z1);
+ felem_mul(z1z1z1, z1, z1z1);
+ felem_mul(s2, y2, z1z1z1);
+ felem_diff(h, u2, u1);
+ felem_sum(i, h, h);
+ felem_square(i, i);
+ felem_mul(j, h, i);
+ felem_diff(r, s2, s1);
+ felem_sum(r, r, r);
+ felem_mul(v, u1, i);
+
+ felem_mul(z_out, tmp, h);
+ felem_square(rr, r);
+ felem_diff(x_out, rr, j);
+ felem_diff(x_out, x_out, v);
+ felem_diff(x_out, x_out, v);
+
+ felem_diff(tmp, v, x_out);
+ felem_mul(y_out, tmp, r);
+ felem_mul(tmp, s1, j);
+ felem_diff(y_out, y_out, tmp);
+ felem_diff(y_out, y_out, tmp);
+}
+
+/* point_add_or_double_vartime sets {x_out,y_out,z_out} = {x1,y1,z1} +
+ * {x2,y2,z2}.
+ *
+ * See http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl
+ *
+ * This function handles the case where {x1,y1,z1}={x2,y2,z2}. */
+static void point_add_or_double_vartime(
+ felem x_out, felem y_out, felem z_out, const felem x1, const felem y1,
+ const felem z1, const felem x2, const felem y2, const felem z2) {
+ felem z1z1, z1z1z1, z2z2, z2z2z2, s1, s2, u1, u2, h, i, j, r, rr, v, tmp;
+ char x_equal, y_equal;
+
+ felem_square(z1z1, z1);
+ felem_square(z2z2, z2);
+ felem_mul(u1, x1, z2z2);
+
+ felem_sum(tmp, z1, z2);
+ felem_square(tmp, tmp);
+ felem_diff(tmp, tmp, z1z1);
+ felem_diff(tmp, tmp, z2z2);
+
+ felem_mul(z2z2z2, z2, z2z2);
+ felem_mul(s1, y1, z2z2z2);
+
+ felem_mul(u2, x2, z1z1);
+ felem_mul(z1z1z1, z1, z1z1);
+ felem_mul(s2, y2, z1z1z1);
+ felem_diff(h, u2, u1);
+ x_equal = felem_is_zero_vartime(h);
+ felem_sum(i, h, h);
+ felem_square(i, i);
+ felem_mul(j, h, i);
+ felem_diff(r, s2, s1);
+ y_equal = felem_is_zero_vartime(r);
+ if (x_equal && y_equal) {
+ point_double(x_out, y_out, z_out, x1, y1, z1);
+ return;
+ }
+ felem_sum(r, r, r);
+ felem_mul(v, u1, i);
+
+ felem_mul(z_out, tmp, h);
+ felem_square(rr, r);
+ felem_diff(x_out, rr, j);
+ felem_diff(x_out, x_out, v);
+ felem_diff(x_out, x_out, v);
+
+ felem_diff(tmp, v, x_out);
+ felem_mul(y_out, tmp, r);
+ felem_mul(tmp, s1, j);
+ felem_diff(y_out, y_out, tmp);
+ felem_diff(y_out, y_out, tmp);
+}
+
+/* copy_conditional sets out=in if mask = 0xffffffff in constant time.
+ *
+ * On entry: mask is either 0 or 0xffffffff. */
+static void copy_conditional(felem out, const felem in, limb mask) {
+ int i;
+
+ for (i = 0; i < NLIMBS; i++) {
+ const limb tmp = mask & (in[i] ^ out[i]);
+ out[i] ^= tmp;
+ }
+}
+
+/* select_affine_point sets {out_x,out_y} to the index'th entry of table.
+ * On entry: index < 16, table[0] must be zero. */
+static void select_affine_point(felem out_x, felem out_y, const limb* table,
+ limb index) {
+ limb i, j;
+
+ memset(out_x, 0, sizeof(felem));
+ memset(out_y, 0, sizeof(felem));
+
+ for (i = 1; i < 16; i++) {
+ limb mask = i ^ index;
+ mask |= mask >> 2;
+ mask |= mask >> 1;
+ mask &= 1;
+ mask--;
+ for (j = 0; j < NLIMBS; j++, table++) {
+ out_x[j] |= *table & mask;
+ }
+ for (j = 0; j < NLIMBS; j++, table++) {
+ out_y[j] |= *table & mask;
+ }
+ }
+}
+
+/* select_jacobian_point sets {out_x,out_y,out_z} to the index'th entry of
+ * table. On entry: index < 16, table[0] must be zero. */
+static void select_jacobian_point(felem out_x, felem out_y, felem out_z,
+ const limb* table, limb index) {
+ limb i, j;
+
+ memset(out_x, 0, sizeof(felem));
+ memset(out_y, 0, sizeof(felem));
+ memset(out_z, 0, sizeof(felem));
+
+ /* The implicit value at index 0 is all zero. We don't need to perform that
+ * iteration of the loop because we already set out_* to zero. */
+ table += 3 * NLIMBS;
+
+ // Hit all entries to obscure cache profiling.
+ for (i = 1; i < 16; i++) {
+ limb mask = i ^ index;
+ mask |= mask >> 2;
+ mask |= mask >> 1;
+ mask &= 1;
+ mask--;
+ for (j = 0; j < NLIMBS; j++, table++) {
+ out_x[j] |= *table & mask;
+ }
+ for (j = 0; j < NLIMBS; j++, table++) {
+ out_y[j] |= *table & mask;
+ }
+ for (j = 0; j < NLIMBS; j++, table++) {
+ out_z[j] |= *table & mask;
+ }
+ }
+}
+
+/* scalar_base_mult sets {nx,ny,nz} = scalar*G where scalar is a little-endian
+ * number. Note that the value of scalar must be less than the order of the
+ * group. */
+static void scalar_base_mult(felem nx, felem ny, felem nz,
+ const p256_int* scalar) {
+ int i, j;
+ limb n_is_infinity_mask = -1, p_is_noninfinite_mask, mask;
+ u32 table_offset;
+
+ felem px, py;
+ felem tx, ty, tz;
+
+ memset(nx, 0, sizeof(felem));
+ memset(ny, 0, sizeof(felem));
+ memset(nz, 0, sizeof(felem));
+
+ /* The loop adds bits at positions 0, 64, 128 and 192, followed by
+ * positions 32,96,160 and 224 and does this 32 times. */
+ for (i = 0; i < 32; i++) {
+ if (i) {
+ point_double(nx, ny, nz, nx, ny, nz);
+ }
+ table_offset = 0;
+ for (j = 0; j <= 32; j += 32) {
+ char bit0 = p256_get_bit(scalar, 31 - i + j);
+ char bit1 = p256_get_bit(scalar, 95 - i + j);
+ char bit2 = p256_get_bit(scalar, 159 - i + j);
+ char bit3 = p256_get_bit(scalar, 223 - i + j);
+ limb index = bit0 | (bit1 << 1) | (bit2 << 2) | (bit3 << 3);
+
+ select_affine_point(px, py, kPrecomputed + table_offset, index);
+ table_offset += 30 * NLIMBS;
+
+ /* Since scalar is less than the order of the group, we know that
+ * {nx,ny,nz} != {px,py,1}, unless both are zero, which we handle
+ * below. */
+ point_add_mixed(tx, ty, tz, nx, ny, nz, px, py);
+ /* The result of point_add_mixed is incorrect if {nx,ny,nz} is zero
+ * (a.k.a. the point at infinity). We handle that situation by
+ * copying the point from the table. */
+ copy_conditional(nx, px, n_is_infinity_mask);
+ copy_conditional(ny, py, n_is_infinity_mask);
+ copy_conditional(nz, kOne, n_is_infinity_mask);
+
+ /* Equally, the result is also wrong if the point from the table is
+ * zero, which happens when the index is zero. We handle that by
+ * only copying from {tx,ty,tz} to {nx,ny,nz} if index != 0. */
+ p_is_noninfinite_mask = NON_ZERO_TO_ALL_ONES(index);
+ mask = p_is_noninfinite_mask & ~n_is_infinity_mask;
+ copy_conditional(nx, tx, mask);
+ copy_conditional(ny, ty, mask);
+ copy_conditional(nz, tz, mask);
+ /* If p was not zero, then n is now non-zero. */
+ n_is_infinity_mask &= ~p_is_noninfinite_mask;
+ }
+ }
+}
+
+/* point_to_affine converts a Jacobian point to an affine point. If the input
+ * is the point at infinity then it returns (0, 0) in constant time. */
+static void point_to_affine(felem x_out, felem y_out, const felem nx,
+ const felem ny, const felem nz) {
+ felem z_inv, z_inv_sq;
+ felem_inv(z_inv, nz);
+ felem_square(z_inv_sq, z_inv);
+ felem_mul(x_out, nx, z_inv_sq);
+ felem_mul(z_inv, z_inv, z_inv_sq);
+ felem_mul(y_out, ny, z_inv);
+}
+
+/* scalar_base_mult sets {nx,ny,nz} = scalar*{x,y}. */
+static void scalar_mult(felem nx, felem ny, felem nz, const felem x,
+ const felem y, const p256_int* scalar) {
+ int i;
+ felem px, py, pz, tx, ty, tz;
+ felem precomp[16][3];
+ limb n_is_infinity_mask, index, p_is_noninfinite_mask, mask;
+
+ /* We precompute 0,1,2,... times {x,y}. */
+ memset(precomp, 0, sizeof(felem) * 3);
+ memcpy(&precomp[1][0], x, sizeof(felem));
+ memcpy(&precomp[1][1], y, sizeof(felem));
+ memcpy(&precomp[1][2], kOne, sizeof(felem));
+
+ for (i = 2; i < 16; i += 2) {
+ point_double(precomp[i][0], precomp[i][1], precomp[i][2],
+ precomp[i / 2][0], precomp[i / 2][1], precomp[i / 2][2]);
+
+ point_add_mixed(precomp[i + 1][0], precomp[i + 1][1], precomp[i + 1][2],
+ precomp[i][0], precomp[i][1], precomp[i][2], x, y);
+ }
+
+ memset(nx, 0, sizeof(felem));
+ memset(ny, 0, sizeof(felem));
+ memset(nz, 0, sizeof(felem));
+ n_is_infinity_mask = -1;
+
+ /* We add in a window of four bits each iteration and do this 64 times. */
+ for (i = 0; i < 256; i += 4) {
+ if (i) {
+ point_double(nx, ny, nz, nx, ny, nz);
+ point_double(nx, ny, nz, nx, ny, nz);
+ point_double(nx, ny, nz, nx, ny, nz);
+ point_double(nx, ny, nz, nx, ny, nz);
+ }
+
+ index = (p256_get_bit(scalar, 255 - i - 0) << 3) |
+ (p256_get_bit(scalar, 255 - i - 1) << 2) |
+ (p256_get_bit(scalar, 255 - i - 2) << 1) |
+ p256_get_bit(scalar, 255 - i - 3);
+
+ /* See the comments in scalar_base_mult about handling infinities. */
+ select_jacobian_point(px, py, pz, precomp[0][0], index);
+ point_add(tx, ty, tz, nx, ny, nz, px, py, pz);
+ copy_conditional(nx, px, n_is_infinity_mask);
+ copy_conditional(ny, py, n_is_infinity_mask);
+ copy_conditional(nz, pz, n_is_infinity_mask);
+
+ p_is_noninfinite_mask = NON_ZERO_TO_ALL_ONES(index);
+ mask = p_is_noninfinite_mask & ~n_is_infinity_mask;
+
+ copy_conditional(nx, tx, mask);
+ copy_conditional(ny, ty, mask);
+ copy_conditional(nz, tz, mask);
+ n_is_infinity_mask &= ~p_is_noninfinite_mask;
+ }
+}
+
+#define kRDigits {2, 0, 0, 0xfffffffe, 0xffffffff, 0xffffffff, 0xfffffffd, 1} // 2^257 mod p256.p
+
+#define kRInvDigits {0x80000000, 1, 0xffffffff, 0, 0x80000001, 0xfffffffe, 1, 0x7fffffff} // 1 / 2^257 mod p256.p
+
+static const p256_int kR = { kRDigits };
+static const p256_int kRInv = { kRInvDigits };
+
+/* to_montgomery sets out = R*in. */
+static void to_montgomery(felem out, const p256_int* in) {
+ p256_int in_shifted;
+ int i;
+
+ p256_init(&in_shifted);
+ p256_modmul(&SECP256r1_p, in, 0, &kR, &in_shifted);
+
+ for (i = 0; i < NLIMBS; i++) {
+ if ((i & 1) == 0) {
+ out[i] = P256_DIGIT(&in_shifted, 0) & kBottom29Bits;
+ p256_shr(&in_shifted, 29, &in_shifted);
+ } else {
+ out[i] = P256_DIGIT(&in_shifted, 0) & kBottom28Bits;
+ p256_shr(&in_shifted, 28, &in_shifted);
+ }
+ }
+
+ p256_clear(&in_shifted);
+}
+
+/* from_montgomery sets out=in/R. */
+static void from_montgomery(p256_int* out, const felem in) {
+ p256_int result, tmp;
+ int i, top;
+
+ p256_init(&result);
+ p256_init(&tmp);
+
+ p256_add_d(&tmp, in[NLIMBS - 1], &result);
+ for (i = NLIMBS - 2; i >= 0; i--) {
+ if ((i & 1) == 0) {
+ top = p256_shl(&result, 29, &tmp);
+ } else {
+ top = p256_shl(&result, 28, &tmp);
+ }
+ top |= p256_add_d(&tmp, in[i], &result);
+ }
+
+ p256_modmul(&SECP256r1_p, &kRInv, top, &result, out);
+
+ p256_clear(&result);
+ p256_clear(&tmp);
+}
+
+/* p256_base_point_mul sets {out_x,out_y} = nG, where n is < the
+ * order of the group. */
+void p256_base_point_mul(const p256_int* n, p256_int* out_x, p256_int* out_y) {
+ felem x, y, z;
+
+ scalar_base_mult(x, y, z, n);
+
+ {
+ felem x_affine, y_affine;
+
+ point_to_affine(x_affine, y_affine, x, y, z);
+ from_montgomery(out_x, x_affine);
+ from_montgomery(out_y, y_affine);
+ }
+}
+
+/* p256_points_mul_vartime sets {out_x,out_y} = n1*G + n2*{in_x,in_y}, where
+ * n1 and n2 are < the order of the group.
+ *
+ * As indicated by the name, this function operates in variable time. This
+ * is safe because it's used for signature validation which doesn't deal
+ * with secrets. */
+void p256_points_mul_vartime(
+ const p256_int* n1, const p256_int* n2, const p256_int* in_x,
+ const p256_int* in_y, p256_int* out_x, p256_int* out_y) {
+ felem x1, y1, z1, x2, y2, z2, px, py;
+
+ /* If both scalars are zero, then the result is the point at infinity. */
+ if (p256_is_zero(n1) != 0 && p256_is_zero(n2) != 0) {
+ p256_clear(out_x);
+ p256_clear(out_y);
+ return;
+ }
+
+ to_montgomery(px, in_x);
+ to_montgomery(py, in_y);
+ scalar_base_mult(x1, y1, z1, n1);
+ scalar_mult(x2, y2, z2, px, py, n2);
+
+ if (p256_is_zero(n2) != 0) {
+ /* If n2 == 0, then {x2,y2,z2} is zero and the result is just
+ * {x1,y1,z1}. */
+ } else if (p256_is_zero(n1) != 0) {
+ /* If n1 == 0, then {x1,y1,z1} is zero and the result is just
+ * {x2,y2,z2}. */
+ memcpy(x1, x2, sizeof(x2));
+ memcpy(y1, y2, sizeof(y2));
+ memcpy(z1, z2, sizeof(z2));
+ } else {
+ /* This function handles the case where {x1,y1,z1} == {x2,y2,z2}. */
+ point_add_or_double_vartime(x1, y1, z1, x1, y1, z1, x2, y2, z2);
+ }
+
+ point_to_affine(px, py, x1, y1, z1);
+ from_montgomery(out_x, px);
+ from_montgomery(out_y, py);
+}
diff --git a/libmincrypt/p256_ecdsa.c b/libmincrypt/p256_ecdsa.c
new file mode 100644
index 0000000..f2264b0
--- /dev/null
+++ b/libmincrypt/p256_ecdsa.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Google Inc. nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string.h>
+
+#include "mincrypt/p256_ecdsa.h"
+#include "mincrypt/p256.h"
+
+int p256_ecdsa_verify(const p256_int* key_x, const p256_int* key_y,
+ const p256_int* message,
+ const p256_int* r, const p256_int* s) {
+ p256_int u, v;
+
+ // Check public key.
+ if (!p256_is_valid_point(key_x, key_y)) return 0;
+
+ // Check r and s are != 0 % n.
+ p256_mod(&SECP256r1_n, r, &u);
+ p256_mod(&SECP256r1_n, s, &v);
+ if (p256_is_zero(&u) || p256_is_zero(&v)) return 0;
+
+ p256_modinv_vartime(&SECP256r1_n, s, &v);
+ p256_modmul(&SECP256r1_n, message, 0, &v, &u); // message / s % n
+ p256_modmul(&SECP256r1_n, r, 0, &v, &v); // r / s % n
+
+ p256_points_mul_vartime(&u, &v,
+ key_x, key_y,
+ &u, &v);
+
+ p256_mod(&SECP256r1_n, &u, &u); // (x coord % p) % n
+ return p256_cmp(r, &u) == 0;
+}
+
diff --git a/libmincrypt/rsa.c b/libmincrypt/rsa.c
new file mode 100644
index 0000000..9061b3a
--- /dev/null
+++ b/libmincrypt/rsa.c
@@ -0,0 +1,308 @@
+/* rsa.c
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+** * Neither the name of Google Inc. nor the names of its contributors may
+** be used to endorse or promote products derived from this software
+** without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR
+** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+** EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "mincrypt/rsa.h"
+#include "mincrypt/sha.h"
+#include "mincrypt/sha256.h"
+
+// a[] -= mod
+static void subM(const RSAPublicKey* key,
+ uint32_t* a) {
+ int64_t A = 0;
+ int i;
+ for (i = 0; i < key->len; ++i) {
+ A += (uint64_t)a[i] - key->n[i];
+ a[i] = (uint32_t)A;
+ A >>= 32;
+ }
+}
+
+// return a[] >= mod
+static int geM(const RSAPublicKey* key,
+ const uint32_t* a) {
+ int i;
+ for (i = key->len; i;) {
+ --i;
+ if (a[i] < key->n[i]) return 0;
+ if (a[i] > key->n[i]) return 1;
+ }
+ return 1; // equal
+}
+
+// montgomery c[] += a * b[] / R % mod
+static void montMulAdd(const RSAPublicKey* key,
+ uint32_t* c,
+ const uint32_t a,
+ const uint32_t* b) {
+ uint64_t A = (uint64_t)a * b[0] + c[0];
+ uint32_t d0 = (uint32_t)A * key->n0inv;
+ uint64_t B = (uint64_t)d0 * key->n[0] + (uint32_t)A;
+ int i;
+
+ for (i = 1; i < key->len; ++i) {
+ A = (A >> 32) + (uint64_t)a * b[i] + c[i];
+ B = (B >> 32) + (uint64_t)d0 * key->n[i] + (uint32_t)A;
+ c[i - 1] = (uint32_t)B;
+ }
+
+ A = (A >> 32) + (B >> 32);
+
+ c[i - 1] = (uint32_t)A;
+
+ if (A >> 32) {
+ subM(key, c);
+ }
+}
+
+// montgomery c[] = a[] * b[] / R % mod
+static void montMul(const RSAPublicKey* key,
+ uint32_t* c,
+ const uint32_t* a,
+ const uint32_t* b) {
+ int i;
+ for (i = 0; i < key->len; ++i) {
+ c[i] = 0;
+ }
+ for (i = 0; i < key->len; ++i) {
+ montMulAdd(key, c, a[i], b);
+ }
+}
+
+// In-place public exponentiation.
+// Input and output big-endian byte array in inout.
+static void modpow(const RSAPublicKey* key,
+ uint8_t* inout) {
+ uint32_t a[RSANUMWORDS];
+ uint32_t aR[RSANUMWORDS];
+ uint32_t aaR[RSANUMWORDS];
+ uint32_t* aaa = 0;
+ int i;
+
+ // Convert from big endian byte array to little endian word array.
+ for (i = 0; i < key->len; ++i) {
+ uint32_t tmp =
+ (inout[((key->len - 1 - i) * 4) + 0] << 24) |
+ (inout[((key->len - 1 - i) * 4) + 1] << 16) |
+ (inout[((key->len - 1 - i) * 4) + 2] << 8) |
+ (inout[((key->len - 1 - i) * 4) + 3] << 0);
+ a[i] = tmp;
+ }
+
+ if (key->exponent == 65537) {
+ aaa = aaR; // Re-use location.
+ montMul(key, aR, a, key->rr); // aR = a * RR / R mod M
+ for (i = 0; i < 16; i += 2) {
+ montMul(key, aaR, aR, aR); // aaR = aR * aR / R mod M
+ montMul(key, aR, aaR, aaR); // aR = aaR * aaR / R mod M
+ }
+ montMul(key, aaa, aR, a); // aaa = aR * a / R mod M
+ } else if (key->exponent == 3) {
+ aaa = aR; // Re-use location.
+ montMul(key, aR, a, key->rr); /* aR = a * RR / R mod M */
+ montMul(key, aaR, aR, aR); /* aaR = aR * aR / R mod M */
+ montMul(key, aaa, aaR, a); /* aaa = aaR * a / R mod M */
+ }
+
+ // Make sure aaa < mod; aaa is at most 1x mod too large.
+ if (geM(key, aaa)) {
+ subM(key, aaa);
+ }
+
+ // Convert to bigendian byte array
+ for (i = key->len - 1; i >= 0; --i) {
+ uint32_t tmp = aaa[i];
+ *inout++ = tmp >> 24;
+ *inout++ = tmp >> 16;
+ *inout++ = tmp >> 8;
+ *inout++ = tmp >> 0;
+ }
+}
+
+// Expected PKCS1.5 signature padding bytes, for a keytool RSA signature.
+// Has the 0-length optional parameter encoded in the ASN1 (as opposed to the
+// other flavor which omits the optional parameter entirely). This code does not
+// accept signatures without the optional parameter.
+
+/*
+static const uint8_t sha_padding[RSANUMBYTES] = {
+ 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0x00, 0x30, 0x21, 0x30,
+ 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a,
+ 0x05, 0x00, 0x04, 0x14,
+
+ // 20 bytes of hash go here.
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+};
+*/
+
+// SHA-1 of PKCS1.5 signature sha_padding for 2048 bit, as above.
+// At the location of the bytes of the hash all 00 are hashed.
+static const uint8_t kExpectedPadShaRsa2048[SHA_DIGEST_SIZE] = {
+ 0xdc, 0xbd, 0xbe, 0x42, 0xd5, 0xf5, 0xa7, 0x2e,
+ 0x6e, 0xfc, 0xf5, 0x5d, 0xaf, 0x9d, 0xea, 0x68,
+ 0x7c, 0xfb, 0xf1, 0x67
+};
+
+/*
+static const uint8_t sha256_padding[RSANUMBYTES] = {
+ 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0x00, 0x30, 0x31, 0x30,
+ 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65,
+ 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20,
+
+ // 32 bytes of hash go here.
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+};
+*/
+
+// SHA-256 of PKCS1.5 signature sha256_padding for 2048 bit, as above.
+// At the location of the bytes of the hash all 00 are hashed.
+static const uint8_t kExpectedPadSha256Rsa2048[SHA256_DIGEST_SIZE] = {
+ 0xab, 0x28, 0x8d, 0x8a, 0xd7, 0xd9, 0x59, 0x92,
+ 0xba, 0xcc, 0xf8, 0x67, 0x20, 0xe1, 0x15, 0x2e,
+ 0x39, 0x8d, 0x80, 0x36, 0xd6, 0x6f, 0xf0, 0xfd,
+ 0x90, 0xe8, 0x7d, 0x8b, 0xe1, 0x7c, 0x87, 0x59,
+};
+
+// Verify a 2048-bit RSA PKCS1.5 signature against an expected hash.
+// Both e=3 and e=65537 are supported. hash_len may be
+// SHA_DIGEST_SIZE (== 20) to indicate a SHA-1 hash, or
+// SHA256_DIGEST_SIZE (== 32) to indicate a SHA-256 hash. No other
+// values are supported.
+//
+// Returns 1 on successful verification, 0 on failure.
+int RSA_verify(const RSAPublicKey *key,
+ const uint8_t *signature,
+ const int len,
+ const uint8_t *hash,
+ const int hash_len) {
+ uint8_t buf[RSANUMBYTES];
+ int i;
+ const uint8_t* padding_hash;
+
+ if (key->len != RSANUMWORDS) {
+ return 0; // Wrong key passed in.
+ }
+
+ if (len != sizeof(buf)) {
+ return 0; // Wrong input length.
+ }
+
+ if (hash_len != SHA_DIGEST_SIZE &&
+ hash_len != SHA256_DIGEST_SIZE) {
+ return 0; // Unsupported hash.
+ }
+
+ if (key->exponent != 3 && key->exponent != 65537) {
+ return 0; // Unsupported exponent.
+ }
+
+ for (i = 0; i < len; ++i) { // Copy input to local workspace.
+ buf[i] = signature[i];
+ }
+
+ modpow(key, buf); // In-place exponentiation.
+
+ // Xor sha portion, so it all becomes 00 iff equal.
+ for (i = len - hash_len; i < len; ++i) {
+ buf[i] ^= *hash++;
+ }
+
+ // Hash resulting buf, in-place.
+ switch (hash_len) {
+ case SHA_DIGEST_SIZE:
+ padding_hash = kExpectedPadShaRsa2048;
+ SHA_hash(buf, len, buf);
+ break;
+ case SHA256_DIGEST_SIZE:
+ padding_hash = kExpectedPadSha256Rsa2048;
+ SHA256_hash(buf, len, buf);
+ break;
+ default:
+ return 0;
+ }
+
+ // Compare against expected hash value.
+ for (i = 0; i < hash_len; ++i) {
+ if (buf[i] != padding_hash[i]) {
+ return 0;
+ }
+ }
+
+ return 1; // All checked out OK.
+}
diff --git a/libmincrypt/sha.c b/libmincrypt/sha.c
new file mode 100644
index 0000000..5bef32e
--- /dev/null
+++ b/libmincrypt/sha.c
@@ -0,0 +1,155 @@
+/* sha.c
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+** * Neither the name of Google Inc. nor the names of its contributors may
+** be used to endorse or promote products derived from this software
+** without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR
+** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+** EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+// Optimized for minimal code size.
+
+#include "mincrypt/sha.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#define rol(bits, value) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+static void SHA1_Transform(SHA_CTX* ctx) {
+ uint32_t W[80];
+ uint32_t A, B, C, D, E;
+ uint8_t* p = ctx->buf;
+ int t;
+
+ for(t = 0; t < 16; ++t) {
+ uint32_t tmp = *p++ << 24;
+ tmp |= *p++ << 16;
+ tmp |= *p++ << 8;
+ tmp |= *p++;
+ W[t] = tmp;
+ }
+
+ for(; t < 80; t++) {
+ W[t] = rol(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
+ }
+
+ A = ctx->state[0];
+ B = ctx->state[1];
+ C = ctx->state[2];
+ D = ctx->state[3];
+ E = ctx->state[4];
+
+ for(t = 0; t < 80; t++) {
+ uint32_t tmp = rol(5,A) + E + W[t];
+
+ if (t < 20)
+ tmp += (D^(B&(C^D))) + 0x5A827999;
+ else if ( t < 40)
+ tmp += (B^C^D) + 0x6ED9EBA1;
+ else if ( t < 60)
+ tmp += ((B&C)|(D&(B|C))) + 0x8F1BBCDC;
+ else
+ tmp += (B^C^D) + 0xCA62C1D6;
+
+ E = D;
+ D = C;
+ C = rol(30,B);
+ B = A;
+ A = tmp;
+ }
+
+ ctx->state[0] += A;
+ ctx->state[1] += B;
+ ctx->state[2] += C;
+ ctx->state[3] += D;
+ ctx->state[4] += E;
+}
+
+static const HASH_VTAB SHA_VTAB = {
+ SHA_init,
+ SHA_update,
+ SHA_final,
+ SHA_hash,
+ SHA_DIGEST_SIZE
+};
+
+void SHA_init(SHA_CTX* ctx) {
+ ctx->f = &SHA_VTAB;
+ ctx->state[0] = 0x67452301;
+ ctx->state[1] = 0xEFCDAB89;
+ ctx->state[2] = 0x98BADCFE;
+ ctx->state[3] = 0x10325476;
+ ctx->state[4] = 0xC3D2E1F0;
+ ctx->count = 0;
+}
+
+
+void SHA_update(SHA_CTX* ctx, const void* data, int len) {
+ int i = (int) (ctx->count & 63);
+ const uint8_t* p = (const uint8_t*)data;
+
+ ctx->count += len;
+
+ while (len--) {
+ ctx->buf[i++] = *p++;
+ if (i == 64) {
+ SHA1_Transform(ctx);
+ i = 0;
+ }
+ }
+}
+
+
+const uint8_t* SHA_final(SHA_CTX* ctx) {
+ uint8_t *p = ctx->buf;
+ uint64_t cnt = ctx->count * 8;
+ int i;
+
+ SHA_update(ctx, (uint8_t*)"\x80", 1);
+ while ((ctx->count & 63) != 56) {
+ SHA_update(ctx, (uint8_t*)"\0", 1);
+ }
+ for (i = 0; i < 8; ++i) {
+ uint8_t tmp = (uint8_t) (cnt >> ((7 - i) * 8));
+ SHA_update(ctx, &tmp, 1);
+ }
+
+ for (i = 0; i < 5; i++) {
+ uint32_t tmp = ctx->state[i];
+ *p++ = tmp >> 24;
+ *p++ = tmp >> 16;
+ *p++ = tmp >> 8;
+ *p++ = tmp >> 0;
+ }
+
+ return ctx->buf;
+}
+
+/* Convenience function */
+const uint8_t* SHA_hash(const void* data, int len, uint8_t* digest) {
+ SHA_CTX ctx;
+ SHA_init(&ctx);
+ SHA_update(&ctx, data, len);
+ memcpy(digest, SHA_final(&ctx), SHA_DIGEST_SIZE);
+ return digest;
+}
diff --git a/libmincrypt/sha256.c b/libmincrypt/sha256.c
new file mode 100644
index 0000000..eb6e308
--- /dev/null
+++ b/libmincrypt/sha256.c
@@ -0,0 +1,184 @@
+/* sha256.c
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+** * Neither the name of Google Inc. nor the names of its contributors may
+** be used to endorse or promote products derived from this software
+** without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR
+** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+** EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+// Optimized for minimal code size.
+
+#include "mincrypt/sha256.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#define ror(value, bits) (((value) >> (bits)) | ((value) << (32 - (bits))))
+#define shr(value, bits) ((value) >> (bits))
+
+static const uint32_t K[64] = {
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+ 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+ 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+ 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+ 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+ 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+ 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+ 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+ 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 };
+
+static void SHA256_Transform(SHA256_CTX* ctx) {
+ uint32_t W[64];
+ uint32_t A, B, C, D, E, F, G, H;
+ uint8_t* p = ctx->buf;
+ int t;
+
+ for(t = 0; t < 16; ++t) {
+ uint32_t tmp = *p++ << 24;
+ tmp |= *p++ << 16;
+ tmp |= *p++ << 8;
+ tmp |= *p++;
+ W[t] = tmp;
+ }
+
+ for(; t < 64; t++) {
+ uint32_t s0 = ror(W[t-15], 7) ^ ror(W[t-15], 18) ^ shr(W[t-15], 3);
+ uint32_t s1 = ror(W[t-2], 17) ^ ror(W[t-2], 19) ^ shr(W[t-2], 10);
+ W[t] = W[t-16] + s0 + W[t-7] + s1;
+ }
+
+ A = ctx->state[0];
+ B = ctx->state[1];
+ C = ctx->state[2];
+ D = ctx->state[3];
+ E = ctx->state[4];
+ F = ctx->state[5];
+ G = ctx->state[6];
+ H = ctx->state[7];
+
+ for(t = 0; t < 64; t++) {
+ uint32_t s0 = ror(A, 2) ^ ror(A, 13) ^ ror(A, 22);
+ uint32_t maj = (A & B) ^ (A & C) ^ (B & C);
+ uint32_t t2 = s0 + maj;
+ uint32_t s1 = ror(E, 6) ^ ror(E, 11) ^ ror(E, 25);
+ uint32_t ch = (E & F) ^ ((~E) & G);
+ uint32_t t1 = H + s1 + ch + K[t] + W[t];
+
+ H = G;
+ G = F;
+ F = E;
+ E = D + t1;
+ D = C;
+ C = B;
+ B = A;
+ A = t1 + t2;
+ }
+
+ ctx->state[0] += A;
+ ctx->state[1] += B;
+ ctx->state[2] += C;
+ ctx->state[3] += D;
+ ctx->state[4] += E;
+ ctx->state[5] += F;
+ ctx->state[6] += G;
+ ctx->state[7] += H;
+}
+
+static const HASH_VTAB SHA256_VTAB = {
+ SHA256_init,
+ SHA256_update,
+ SHA256_final,
+ SHA256_hash,
+ SHA256_DIGEST_SIZE
+};
+
+void SHA256_init(SHA256_CTX* ctx) {
+ ctx->f = &SHA256_VTAB;
+ ctx->state[0] = 0x6a09e667;
+ ctx->state[1] = 0xbb67ae85;
+ ctx->state[2] = 0x3c6ef372;
+ ctx->state[3] = 0xa54ff53a;
+ ctx->state[4] = 0x510e527f;
+ ctx->state[5] = 0x9b05688c;
+ ctx->state[6] = 0x1f83d9ab;
+ ctx->state[7] = 0x5be0cd19;
+ ctx->count = 0;
+}
+
+
+void SHA256_update(SHA256_CTX* ctx, const void* data, int len) {
+ int i = (int) (ctx->count & 63);
+ const uint8_t* p = (const uint8_t*)data;
+
+ ctx->count += len;
+
+ while (len--) {
+ ctx->buf[i++] = *p++;
+ if (i == 64) {
+ SHA256_Transform(ctx);
+ i = 0;
+ }
+ }
+}
+
+
+const uint8_t* SHA256_final(SHA256_CTX* ctx) {
+ uint8_t *p = ctx->buf;
+ uint64_t cnt = ctx->count * 8;
+ int i;
+
+ SHA256_update(ctx, (uint8_t*)"\x80", 1);
+ while ((ctx->count & 63) != 56) {
+ SHA256_update(ctx, (uint8_t*)"\0", 1);
+ }
+ for (i = 0; i < 8; ++i) {
+ uint8_t tmp = (uint8_t) (cnt >> ((7 - i) * 8));
+ SHA256_update(ctx, &tmp, 1);
+ }
+
+ for (i = 0; i < 8; i++) {
+ uint32_t tmp = ctx->state[i];
+ *p++ = tmp >> 24;
+ *p++ = tmp >> 16;
+ *p++ = tmp >> 8;
+ *p++ = tmp >> 0;
+ }
+
+ return ctx->buf;
+}
+
+/* Convenience function */
+const uint8_t* SHA256_hash(const void* data, int len, uint8_t* digest) {
+ SHA256_CTX ctx;
+ SHA256_init(&ctx);
+ SHA256_update(&ctx, data, len);
+ memcpy(digest, SHA256_final(&ctx), SHA256_DIGEST_SIZE);
+ return digest;
+}
diff --git a/libmincrypt/test/Android.mk b/libmincrypt/test/Android.mk
new file mode 100644
index 0000000..73ff7d0
--- /dev/null
+++ b/libmincrypt/test/Android.mk
@@ -0,0 +1,15 @@
+# Copyright 2013 The Android Open Source Project
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := rsa_test
+LOCAL_SRC_FILES := rsa_test.c
+LOCAL_STATIC_LIBRARIES := libmincrypt
+include $(BUILD_HOST_NATIVE_TEST)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := ecdsa_test
+LOCAL_SRC_FILES := ecdsa_test.c
+LOCAL_STATIC_LIBRARIES := libmincrypt
+include $(BUILD_HOST_NATIVE_TEST)
diff --git a/libmincrypt/test/ecdsa_test.c b/libmincrypt/test/ecdsa_test.c
new file mode 100644
index 0000000..24ec013
--- /dev/null
+++ b/libmincrypt/test/ecdsa_test.c
@@ -0,0 +1,284 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Google Inc. nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/cdefs.h>
+
+#include "mincrypt/dsa_sig.h"
+#include "mincrypt/p256.h"
+#include "mincrypt/p256_ecdsa.h"
+#include "mincrypt/sha256.h"
+
+#ifndef __unused
+#define __unused __attribute__((__unused__))
+#endif
+
+/**
+ * Messages signed using:
+ *
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIDw6UiziVMbjlfSpOAIpA2tcL+v1OlznZLnpadO8BGi1oAoGCCqGSM49
+AwEHoUQDQgAEZw7VAOjAXYRFuhZWYBgjahdOvkwcAnjGkxQWytZW+iS1hI3ZGE24
+6XmNka9IGxAgj2n/ip+MuZJMFoJ9DRea3g==
+-----END EC PRIVATE KEY-----
+ */
+
+p256_int key_x = {
+ .a = {0xd656fa24u, 0x931416cau, 0x1c0278c6u, 0x174ebe4cu,
+ 0x6018236au, 0x45ba1656u, 0xe8c05d84u, 0x670ed500u}
+};
+p256_int key_y = {
+ .a = {0x0d179adeu, 0x4c16827du, 0x9f8cb992u, 0x8f69ff8au,
+ 0x481b1020u, 0x798d91afu, 0x184db8e9u, 0xb5848dd9u}
+};
+
+char* message_1 =
+ "f4 5d 55 f3 55 51 e9 75 d6 a8 dc 7e a9 f4 88 59"
+ "39 40 cc 75 69 4a 27 8f 27 e5 78 a1 63 d8 39 b3"
+ "40 40 84 18 08 cf 9c 58 c9 b8 72 8b f5 f9 ce 8e"
+ "e8 11 ea 91 71 4f 47 ba b9 2d 0f 6d 5a 26 fc fe"
+ "ea 6c d9 3b 91 0c 0a 2c 96 3e 64 eb 18 23 f1 02"
+ "75 3d 41 f0 33 59 10 ad 3a 97 71 04 f1 aa f6 c3"
+ "74 27 16 a9 75 5d 11 b8 ee d6 90 47 7f 44 5c 5d"
+ "27 20 8b 2e 28 43 30 fa 3d 30 14 23 fa 7f 2d 08"
+ "6e 0a d0 b8 92 b9 db 54 4e 45 6d 3f 0d ab 85 d9"
+ "53 c1 2d 34 0a a8 73 ed a7 27 c8 a6 49 db 7f a6"
+ "37 40 e2 5e 9a f1 53 3b 30 7e 61 32 99 93 11 0e"
+ "95 19 4e 03 93 99 c3 82 4d 24 c5 1f 22 b2 6b de"
+ "10 24 cd 39 59 58 a2 df eb 48 16 a6 e8 ad ed b5"
+ "0b 1f 6b 56 d0 b3 06 0f f0 f1 c4 cb 0d 0e 00 1d"
+ "d5 9d 73 be 12";
+
+char* signature_1 =
+ "30 44 02 20 43 18 fc eb 3b a8 3a a8 a3 cf 41 b7"
+ "81 4a f9 01 e1 8b 6e 95 c1 3a 83 25 9e a5 2e 66"
+ "7c 98 25 d9 02 20 54 f3 7f 5a e9 36 9c a2 f0 51"
+ "e0 6e 78 48 60 a3 f9 8a d5 2c 37 5a 0a 29 c9 f7"
+ "ea 57 7e 88 46 12";
+
+// Same as signature 1, but with leading zeroes.
+char* message_2 =
+ "f4 5d 55 f3 55 51 e9 75 d6 a8 dc 7e a9 f4 88 59"
+ "39 40 cc 75 69 4a 27 8f 27 e5 78 a1 63 d8 39 b3"
+ "40 40 84 18 08 cf 9c 58 c9 b8 72 8b f5 f9 ce 8e"
+ "e8 11 ea 91 71 4f 47 ba b9 2d 0f 6d 5a 26 fc fe"
+ "ea 6c d9 3b 91 0c 0a 2c 96 3e 64 eb 18 23 f1 02"
+ "75 3d 41 f0 33 59 10 ad 3a 97 71 04 f1 aa f6 c3"
+ "74 27 16 a9 75 5d 11 b8 ee d6 90 47 7f 44 5c 5d"
+ "27 20 8b 2e 28 43 30 fa 3d 30 14 23 fa 7f 2d 08"
+ "6e 0a d0 b8 92 b9 db 54 4e 45 6d 3f 0d ab 85 d9"
+ "53 c1 2d 34 0a a8 73 ed a7 27 c8 a6 49 db 7f a6"
+ "37 40 e2 5e 9a f1 53 3b 30 7e 61 32 99 93 11 0e"
+ "95 19 4e 03 93 99 c3 82 4d 24 c5 1f 22 b2 6b de"
+ "10 24 cd 39 59 58 a2 df eb 48 16 a6 e8 ad ed b5"
+ "0b 1f 6b 56 d0 b3 06 0f f0 f1 c4 cb 0d 0e 00 1d"
+ "d5 9d 73 be 12";
+
+char* signature_2 =
+ "30 46 02 21 00 43 18 fc eb 3b a8 3a a8 a3 cf 41 b7"
+ "81 4a f9 01 e1 8b 6e 95 c1 3a 83 25 9e a5 2e 66"
+ "7c 98 25 d9 02 21 00 54 f3 7f 5a e9 36 9c a2 f0 51"
+ "e0 6e 78 48 60 a3 f9 8a d5 2c 37 5a 0a 29 c9 f7"
+ "ea 57 7e 88 46 12";
+
+// Excessive zeroes on the signature
+char* message_3 =
+ "f4 5d 55 f3 55 51 e9 75 d6 a8 dc 7e a9 f4 88 59"
+ "39 40 cc 75 69 4a 27 8f 27 e5 78 a1 63 d8 39 b3"
+ "40 40 84 18 08 cf 9c 58 c9 b8 72 8b f5 f9 ce 8e"
+ "e8 11 ea 91 71 4f 47 ba b9 2d 0f 6d 5a 26 fc fe"
+ "ea 6c d9 3b 91 0c 0a 2c 96 3e 64 eb 18 23 f1 02"
+ "75 3d 41 f0 33 59 10 ad 3a 97 71 04 f1 aa f6 c3"
+ "74 27 16 a9 75 5d 11 b8 ee d6 90 47 7f 44 5c 5d"
+ "27 20 8b 2e 28 43 30 fa 3d 30 14 23 fa 7f 2d 08"
+ "6e 0a d0 b8 92 b9 db 54 4e 45 6d 3f 0d ab 85 d9"
+ "53 c1 2d 34 0a a8 73 ed a7 27 c8 a6 49 db 7f a6"
+ "37 40 e2 5e 9a f1 53 3b 30 7e 61 32 99 93 11 0e"
+ "95 19 4e 03 93 99 c3 82 4d 24 c5 1f 22 b2 6b de"
+ "10 24 cd 39 59 58 a2 df eb 48 16 a6 e8 ad ed b5"
+ "0b 1f 6b 56 d0 b3 06 0f f0 f1 c4 cb 0d 0e 00 1d"
+ "d5 9d 73 be 12";
+
+char* signature_3 =
+ "30 4c 02 24 00 00 00 00 43 18 fc eb 3b a8 3a a8 a3 cf 41 b7"
+ "81 4a f9 01 e1 8b 6e 95 c1 3a 83 25 9e a5 2e 66"
+ "7c 98 25 d9 02 24 00 00 00 00 54 f3 7f 5a e9 36 9c a2 f0 51"
+ "e0 6e 78 48 60 a3 f9 8a d5 2c 37 5a 0a 29 c9 f7"
+ "ea 57 7e 88 46 12";
+
+
+char* good_dsa_signature_1 =
+ "30 0D 02 01 01 02 08 00 A5 55 5A 01 FF A5 01";
+p256_int good_dsa_signature_1_r = {
+ .a = {0x00000001U, 0x00000000U, 0x00000000U, 0x00000000U,
+ 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U}
+};
+p256_int good_dsa_signature_1_s = {
+ .a = {0x01FFA501U, 0x00A5555AU, 0x00000000U, 0x00000000U,
+ 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U}
+};
+
+
+char* bad_dsa_signature_1 =
+ "a0 06 02 01 01 02 01 01";
+
+char* bad_dsa_signature_2 =
+ "30 07 02 01 01 02 01 01";
+
+char* bad_dsa_signature_3 =
+ "30 06 82 01 01 02 01 01";
+
+char* bad_dsa_signature_4 =
+ "30 06 02 00 01 02 01 01";
+
+char* bad_dsa_signature_5 =
+ "30 06 02 01 01 82 01 01";
+
+char* bad_dsa_signature_6 =
+ "30 05 02 01 01 02 00";
+
+char* bad_dsa_signature_7 =
+ "30 06 02 01 01 02 00 01";
+
+unsigned char* parsehex(char* str, int* len) {
+ // result can't be longer than input
+ unsigned char* result = malloc(strlen(str));
+
+ unsigned char* p = result;
+ *len = 0;
+
+ while (*str) {
+ int b;
+
+ while (isspace(*str)) str++;
+
+ switch (*str) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ b = (*str - '0') << 4; break;
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ b = (*str - 'a' + 10) << 4; break;
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ b = (*str - 'A' + 10) << 4; break;
+ case '\0':
+ return result;
+ default:
+ return NULL;
+ }
+ str++;
+
+ while (isspace(*str)) str++;
+
+ switch (*str) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ b |= *str - '0'; break;
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ b |= *str - 'a' + 10; break;
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ b |= *str - 'A' + 10; break;
+ default:
+ return NULL;
+ }
+ str++;
+
+ *p++ = b;
+ ++*len;
+ }
+
+ return result;
+}
+
+int main(int arg __unused, char** argv __unused) {
+
+ unsigned char hash_buf[SHA256_DIGEST_SIZE];
+
+ unsigned char* message;
+ int mlen;
+ unsigned char* signature;
+ int slen;
+
+ p256_int hash;
+ p256_int r;
+ p256_int s;
+
+ int success = 1;
+
+#define CHECK_DSA_SIG(sig, good) do {\
+ message = parsehex(sig, &mlen); \
+ int result = dsa_sig_unpack(message, mlen, &r, &s); \
+ printf(#sig ": %s\n", result ? "good" : "bad"); \
+ success = success && !(good ^ result); \
+ free(message); \
+ } while(0)
+#define CHECK_GOOD_DSA_SIG(n) do {\
+ CHECK_DSA_SIG(good_dsa_signature_##n, 1); \
+ int result = !memcmp(P256_DIGITS(&good_dsa_signature_##n##_r), P256_DIGITS(&r), \
+ P256_NBYTES); \
+ success = success && result; \
+ printf(" R value %s\n", result ? "good" : "bad"); \
+ result = !memcmp(P256_DIGITS(&good_dsa_signature_##n##_s), P256_DIGITS(&s), \
+ P256_NBYTES); \
+ success = success && result; \
+ printf(" S value %s\n", result ? "good" : "bad"); \
+ } while (0)
+#define CHECK_BAD_DSA_SIG(n) \
+ CHECK_DSA_SIG(bad_dsa_signature_##n, 0)
+
+ CHECK_GOOD_DSA_SIG(1);
+
+ CHECK_BAD_DSA_SIG(1);
+ CHECK_BAD_DSA_SIG(2);
+ CHECK_BAD_DSA_SIG(3);
+ CHECK_BAD_DSA_SIG(4);
+ CHECK_BAD_DSA_SIG(5);
+ CHECK_BAD_DSA_SIG(6);
+ CHECK_BAD_DSA_SIG(7);
+
+
+#define TEST_MESSAGE(n) do {\
+ message = parsehex(message_##n, &mlen); \
+ SHA256_hash(message, mlen, hash_buf); \
+ p256_from_bin(hash_buf, &hash); \
+ signature = parsehex(signature_##n, &slen); \
+ int result = dsa_sig_unpack(signature, slen, &r, &s); \
+ if (result) { result = p256_ecdsa_verify(&key_x, &key_y, &hash, &r, &s); } \
+ printf("message %d: %s\n", n, result ? "verified" : "not verified"); \
+ success = success && result; \
+ free(signature); \
+ } while(0)
+
+ TEST_MESSAGE(1);
+ TEST_MESSAGE(2);
+ TEST_MESSAGE(3);
+
+ printf("\n%s\n\n", success ? "PASS" : "FAIL");
+
+ return !success;
+}
diff --git a/libmincrypt/test/rsa_test.c b/libmincrypt/test/rsa_test.c
new file mode 100644
index 0000000..055138f
--- /dev/null
+++ b/libmincrypt/test/rsa_test.c
@@ -0,0 +1,842 @@
+/*
+** Copyright 2013, The Android Open Source Project
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+** * Neither the name of Google Inc. nor the names of its contributors may
+** be used to endorse or promote products derived from this software
+** without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR
+** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+** EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/cdefs.h>
+
+#include "mincrypt/rsa.h"
+#include "mincrypt/sha.h"
+
+#ifndef __unused
+#define __unused __attribute__((unused))
+#endif
+
+// RSA test data taken from:
+//
+// ftp://ftp.rsa.com/pub/rsalabs/tmp/pkcs1v15sign-vectors.txt
+
+// This is the result (reformatted) of running DumpPublicKey on:
+//
+// # Example 15: A 2048-bit RSA key pair
+// # -----------------------------------
+//
+//
+// # Public key
+// # ----------
+//
+// # Modulus:
+// df 27 1f d2 5f 86 44 49 6b 0c 81 be 4b d5 02 97
+// ef 09 9b 00 2a 6f d6 77 27 eb 44 9c ea 56 6e d6
+// a3 98 1a 71 31 2a 14 1c ab c9 81 5c 12 09 e3 20
+// a2 5b 32 46 4e 99 99 f1 8c a1 3a 9f d3 89 25 58
+// f9 e0 ad ef dd 36 50 dd 23 a3 f0 36 d6 0f e3 98
+// 84 37 06 a4 0b 0b 84 62 c8 be e3 bc e1 2f 1f 28
+// 60 c2 44 4c dc 6a 44 47 6a 75 ff 4a a2 42 73 cc
+// be 3b f8 02 48 46 5f 8f f8 c3 a7 f3 36 7d fc 0d
+// f5 b6 50 9a 4f 82 81 1c ed d8 1c da aa 73 c4 91
+// da 41 21 70 d5 44 d4 ba 96 b9 7f 0a fc 80 65 49
+// 8d 3a 49 fd 91 09 92 a1 f0 72 5b e2 4f 46 5c fe
+// 7e 0e ab f6 78 99 6c 50 bc 5e 75 24 ab f7 3f 15
+// e5 be f7 d5 18 39 4e 31 38 ce 49 44 50 6a aa af
+// 3f 9b 23 6d ca b8 fc 00 f8 7a f5 96 fd c3 d9 d6
+// c7 5c d5 08 36 2f ae 2c be dd cc 4c 74 50 b1 7b
+// 77 6c 07 9e cc a1 f2 56 35 1a 43 b9 7d be 21 53
+//
+// # Exponent:
+// 01 00 01
+
+RSAPublicKey key_15 = {
+ .len = 64,
+ .n0inv = 0xf0053525,
+ .n = {2109612371u,890913721u,3433165398u,2003568542u,
+ 1951445371u,3202206796u,909094444u,3344749832u,
+ 4257470934u,4168807830u,3401120768u,1067131757u,
+ 1349167791u,953043268u,406408753u,3854497749u,
+ 2885107477u,3160306980u,2023320656u,2114890742u,
+ 1330011390u,4034026466u,2433323681u,2369407485u,
+ 4236272969u,2528739082u,3578057914u,3661701488u,
+ 2859713681u,3990363354u,1333952796u,4122366106u,
+ 914226189u,4173572083u,1212571535u,3191601154u,
+ 2722264012u,1786117962u,3697951815u,1623344204u,
+ 3777961768u,3367953340u,185304162u,2218198692u,
+ 3591365528u,597946422u,3711324381u,4192251375u,
+ 3548980568u,2359376543u,1318689265u,2723885638u,
+ 302637856u,2882109788u,824841244u,2744654449u,
+ 3931533014u,669729948u,711972471u,4010384128u,
+ 1272251031u,1795981758u,1602634825u,3743883218u},
+ .rr = {820482522u,2494434288u,1082168230u,731376296u,
+ 1306039452u,3139792975u,2575869288u,3874938710u,
+ 3198185181u,153506080u,1236489694u,1061859740u,
+ 1174461268u,115279508u,1782749185u,238124145u,
+ 3587596076u,2259236093u,1112265915u,4048059865u,
+ 3890381098u,999426242u,794481771u,3804065613u,
+ 2786019148u,461403875u,3072256692u,4079652654u,
+ 3056719901u,1871565394u,212974856u,3359008174u,
+ 1397773937u,3796256698u,914342841u,1097174457u,
+ 3322220191u,3170814748u,2439215020u,618719336u,
+ 3629353460u,496817177u,317052742u,380264245u,
+ 1976007217u,2697736152u,312540864u,4291855337u,
+ 697006561u,4234182488u,3904590917u,2609582216u,
+ 451424084u,1805773827u,776344974u,1064489733u,
+ 2633377036u,1954826648u,3202815814u,2240368662u,
+ 2618582484u,2211196815u,4107362845u,3640258615u},
+ .exponent = 65537,
+};
+
+// PKCS#1 v1.5 Signature Example 15.1
+
+char* message_1 =
+ "f4 5d 55 f3 55 51 e9 75 d6 a8 dc 7e a9 f4 88 59"
+ "39 40 cc 75 69 4a 27 8f 27 e5 78 a1 63 d8 39 b3"
+ "40 40 84 18 08 cf 9c 58 c9 b8 72 8b f5 f9 ce 8e"
+ "e8 11 ea 91 71 4f 47 ba b9 2d 0f 6d 5a 26 fc fe"
+ "ea 6c d9 3b 91 0c 0a 2c 96 3e 64 eb 18 23 f1 02"
+ "75 3d 41 f0 33 59 10 ad 3a 97 71 04 f1 aa f6 c3"
+ "74 27 16 a9 75 5d 11 b8 ee d6 90 47 7f 44 5c 5d"
+ "27 20 8b 2e 28 43 30 fa 3d 30 14 23 fa 7f 2d 08"
+ "6e 0a d0 b8 92 b9 db 54 4e 45 6d 3f 0d ab 85 d9"
+ "53 c1 2d 34 0a a8 73 ed a7 27 c8 a6 49 db 7f a6"
+ "37 40 e2 5e 9a f1 53 3b 30 7e 61 32 99 93 11 0e"
+ "95 19 4e 03 93 99 c3 82 4d 24 c5 1f 22 b2 6b de"
+ "10 24 cd 39 59 58 a2 df eb 48 16 a6 e8 ad ed b5"
+ "0b 1f 6b 56 d0 b3 06 0f f0 f1 c4 cb 0d 0e 00 1d"
+ "d5 9d 73 be 12";
+
+char* signature_1 =
+ "b7 5a 54 66 b6 5d 0f 30 0e f5 38 33 f2 17 5c 8a"
+ "34 7a 38 04 fc 63 45 1d c9 02 f0 b7 1f 90 83 45"
+ "9e d3 7a 51 79 a3 b7 23 a5 3f 10 51 64 2d 77 37"
+ "4c 4c 6c 8d bb 1c a2 05 25 f5 c9 f3 2d b7 76 95"
+ "35 56 da 31 29 0e 22 19 74 82 ce b6 99 06 c4 6a"
+ "75 8f b0 e7 40 9b a8 01 07 7d 2a 0a 20 ea e7 d1"
+ "d6 d3 92 ab 49 57 e8 6b 76 f0 65 2d 68 b8 39 88"
+ "a7 8f 26 e1 11 72 ea 60 9b f8 49 fb bd 78 ad 7e"
+ "dc e2 1d e6 62 a0 81 36 8c 04 06 07 ce e2 9d b0"
+ "62 72 27 f4 49 63 ad 17 1d 22 93 b6 33 a3 92 e3"
+ "31 dc a5 4f e3 08 27 52 f4 3f 63 c1 61 b4 47 a4"
+ "c6 5a 68 75 67 0d 5f 66 00 fc c8 60 a1 ca eb 0a"
+ "88 f8 fd ec 4e 56 43 98 a5 c4 6c 87 f6 8c e0 70"
+ "01 f6 21 3a be 0a b5 62 5f 87 d1 90 25 f0 8d 81"
+ "da c7 bd 45 86 bc 93 82 19 1f 6d 28 80 f6 22 7e"
+ "5d f3 ee d2 1e 77 92 d2 49 48 04 87 f3 65 52 61";
+
+// PKCS#1 v1.5 Signature Example 15.2
+
+char *message_2 =
+ "c1 4b 4c 60 75 b2 f9 aa d6 61 de f4 ec fd 3c b9 "
+ "33 c6 23 f4 e6 3b f5 34 10 d2 f0 16 d1 ab 98 e2 "
+ "72 9e cc f8 00 6c d8 e0 80 50 73 7d 95 fd bf 29 "
+ "6b 66 f5 b9 79 2a 90 29 36 c4 f7 ac 69 f5 14 53 "
+ "ce 43 69 45 2d c2 2d 96 f0 37 74 81 14 66 20 00 "
+ "dd 9c d3 a5 e1 79 f4 e0 f8 1f a6 a0 31 1c a1 ae "
+ "e6 51 9a 0f 63 ce c7 8d 27 bb 72 63 93 fb 7f 1f "
+ "88 cd e7 c9 7f 8a 66 cd 66 30 12 81 da c3 f3 a4 "
+ "33 24 8c 75 d6 c2 dc d7 08 b6 a9 7b 0a 3f 32 5e "
+ "0b 29 64 f8 a5 81 9e 47 9b ";
+
+char* signature_2 =
+ "af a7 34 34 62 be a1 22 cc 14 9f ca 70 ab da e7"
+ "94 46 67 7d b5 37 36 66 af 7d c3 13 01 5f 4d e7"
+ "86 e6 e3 94 94 6f ad 3c c0 e2 b0 2b ed ba 50 47"
+ "fe 9e 2d 7d 09 97 05 e4 a3 9f 28 68 32 79 cf 0a"
+ "c8 5c 15 30 41 22 42 c0 e9 18 95 3b e0 00 e9 39"
+ "cf 3b f1 82 52 5e 19 93 70 fa 79 07 eb a6 9d 5d"
+ "b4 63 10 17 c0 e3 6d f7 03 79 b5 db 8d 4c 69 5a"
+ "97 9a 8e 61 73 22 40 65 d7 dc 15 13 2e f2 8c d8"
+ "22 79 51 63 06 3b 54 c6 51 14 1b e8 6d 36 e3 67"
+ "35 bc 61 f3 1f ca 57 4e 53 09 f3 a3 bb df 91 ef"
+ "f1 2b 99 e9 cc 17 44 f1 ee 9a 1b d2 2c 5b ad 96"
+ "ad 48 19 29 25 1f 03 43 fd 36 bc f0 ac de 7f 11"
+ "e5 ad 60 97 77 21 20 27 96 fe 06 1f 9a da 1f c4"
+ "c8 e0 0d 60 22 a8 35 75 85 ff e9 fd d5 93 31 a2"
+ "8c 4a a3 12 15 88 fb 6c f6 83 96 d8 ac 05 46 59"
+ "95 00 c9 70 85 00 a5 97 2b d5 4f 72 cf 8d b0 c8";
+
+// PKCS#1 v1.5 Signature Example 15.3
+
+char* message_3 =
+ "d0 23 71 ad 7e e4 8b bf db 27 63 de 7a 84 3b 94 "
+ "08 ce 5e b5 ab f8 47 ca 3d 73 59 86 df 84 e9 06 "
+ "0b db cd d3 a5 5b a5 5d de 20 d4 76 1e 1a 21 d2 "
+ "25 c1 a1 86 f4 ac 4b 30 19 d3 ad f7 8f e6 33 46 "
+ "67 f5 6f 70 c9 01 a0 a2 70 0c 6f 0d 56 ad d7 19 "
+ "59 2d c8 8f 6d 23 06 c7 00 9f 6e 7a 63 5b 4c b3 "
+ "a5 02 df e6 8d dc 58 d0 3b e1 0a 11 70 00 4f e7 "
+ "4d d3 e4 6b 82 59 1f f7 54 14 f0 c4 a0 3e 60 5e "
+ "20 52 4f 24 16 f1 2e ca 58 9f 11 1b 75 d6 39 c6 "
+ "1b aa 80 ca fd 05 cf 35 00 24 4a 21 9e d9 ce d9 "
+ "f0 b1 02 97 18 2b 65 3b 52 6f 40 0f 29 53 ba 21 "
+ "4d 5b cd 47 88 41 32 87 2a e9 0d 4d 6b 1f 42 15 "
+ "39 f9 f3 46 62 a5 6d c0 e7 b4 b9 23 b6 23 1e 30 "
+ "d2 67 67 97 81 7f 7c 33 7b 5a c8 24 ba 93 14 3b "
+ "33 81 fa 3d ce 0e 6a eb d3 8e 67 73 51 87 b1 eb "
+ "d9 5c 02 ";
+
+char* signature_3 =
+ "3b ac 63 f8 6e 3b 70 27 12 03 10 6b 9c 79 aa bd"
+ "9f 47 7c 56 e4 ee 58 a4 fc e5 ba f2 ca b4 96 0f"
+ "88 39 1c 9c 23 69 8b e7 5c 99 ae df 9e 1a bf 17"
+ "05 be 1d ac 33 14 0a db 48 eb 31 f4 50 bb 9e fe"
+ "83 b7 b9 0d b7 f1 57 6d 33 f4 0c 1c ba 4b 8d 6b"
+ "1d 33 23 56 4b 0f 17 74 11 4f a7 c0 8e 6d 1e 20"
+ "dd 8f bb a9 b6 ac 7a d4 1e 26 b4 56 8f 4a 8a ac"
+ "bf d1 78 a8 f8 d2 c9 d5 f5 b8 81 12 93 5a 8b c9"
+ "ae 32 cd a4 0b 8d 20 37 55 10 73 50 96 53 68 18"
+ "ce 2b 2d b7 1a 97 72 c9 b0 dd a0 9a e1 01 52 fa"
+ "11 46 62 18 d0 91 b5 3d 92 54 30 61 b7 29 4a 55"
+ "be 82 ff 35 d5 c3 2f a2 33 f0 5a aa c7 58 50 30"
+ "7e cf 81 38 3c 11 16 74 39 7b 1a 1b 9d 3b f7 61"
+ "2c cb e5 ba cd 2b 38 f0 a9 83 97 b2 4c 83 65 8f"
+ "b6 c0 b4 14 0e f1 19 70 c4 63 0d 44 34 4e 76 ea"
+ "ed 74 dc be e8 11 db f6 57 59 41 f0 8a 65 23 b8";
+
+// PKCS#1 v1.5 Signature Example 15.4
+
+char* message_4 =
+ "29 03 55 84 ab 7e 02 26 a9 ec 4b 02 e8 dc f1 27 "
+ "2d c9 a4 1d 73 e2 82 00 07 b0 f6 e2 1f ec cd 5b "
+ "d9 db b9 ef 88 cd 67 58 76 9e e1 f9 56 da 7a d1 "
+ "84 41 de 6f ab 83 86 db c6 93 ";
+
+char* signature_4 =
+ "28 d8 e3 fc d5 dd db 21 ff bd 8d f1 63 0d 73 77"
+ "aa 26 51 e1 4c ad 1c 0e 43 cc c5 2f 90 7f 94 6d"
+ "66 de 72 54 e2 7a 6c 19 0e b0 22 ee 89 ec f6 22"
+ "4b 09 7b 71 06 8c d6 07 28 a1 ae d6 4b 80 e5 45"
+ "7b d3 10 6d d9 17 06 c9 37 c9 79 5f 2b 36 36 7f"
+ "f1 53 dc 25 19 a8 db 9b df 2c 80 74 30 c4 51 de"
+ "17 bb cd 0c e7 82 b3 e8 f1 02 4d 90 62 4d ea 7f"
+ "1e ed c7 42 0b 7e 7c aa 65 77 ce f4 31 41 a7 26"
+ "42 06 58 0e 44 a1 67 df 5e 41 ee a0 e6 9a 80 54"
+ "54 c4 0e ef c1 3f 48 e4 23 d7 a3 2d 02 ed 42 c0"
+ "ab 03 d0 a7 cf 70 c5 86 0a c9 2e 03 ee 00 5b 60"
+ "ff 35 03 42 4b 98 cc 89 45 68 c7 c5 6a 02 33 55"
+ "1c eb e5 88 cf 8b 01 67 b7 df 13 ad ca d8 28 67"
+ "68 10 49 9c 70 4d a7 ae 23 41 4d 69 e3 c0 d2 db"
+ "5d cb c2 61 3b c1 20 42 1f 9e 36 53 c5 a8 76 72"
+ "97 64 3c 7e 07 40 de 01 63 55 45 3d 6c 95 ae 72";
+
+// PKCS#1 v1.5 Signature Example 15.5
+
+char* message_5 =
+ "bd a3 a1 c7 90 59 ea e5 98 30 8d 3d f6 09 ";
+
+char* signature_5 =
+ "a1 56 17 6c b9 67 77 c7 fb 96 10 5d bd 91 3b c4"
+ "f7 40 54 f6 80 7c 60 08 a1 a9 56 ea 92 c1 f8 1c"
+ "b8 97 dc 4b 92 ef 9f 4e 40 66 8d c7 c5 56 90 1a"
+ "cb 6c f2 69 fe 61 5b 0f b7 2b 30 a5 13 38 69 23"
+ "14 b0 e5 87 8a 88 c2 c7 77 4b d1 69 39 b5 ab d8"
+ "2b 44 29 d6 7b d7 ac 8e 5e a7 fe 92 4e 20 a6 ec"
+ "66 22 91 f2 54 8d 73 4f 66 34 86 8b 03 9a a5 f9"
+ "d4 d9 06 b2 d0 cb 85 85 bf 42 85 47 af c9 1c 6e"
+ "20 52 dd cd 00 1c 3e f8 c8 ee fc 3b 6b 2a 82 b6"
+ "f9 c8 8c 56 f2 e2 c3 cb 0b e4 b8 0d a9 5e ba 37"
+ "1d 8b 5f 60 f9 25 38 74 3d db b5 da 29 72 c7 1f"
+ "e7 b9 f1 b7 90 26 8a 0e 77 0f c5 eb 4d 5d d8 52"
+ "47 d4 8a e2 ec 3f 26 25 5a 39 85 52 02 06 a1 f2"
+ "68 e4 83 e9 db b1 d5 ca b1 90 91 76 06 de 31 e7"
+ "c5 18 2d 8f 15 1b f4 1d fe cc ae d7 cd e6 90 b2"
+ "16 47 10 6b 49 0c 72 9d 54 a8 fe 28 02 a6 d1 26";
+
+// PKCS#1 v1.5 Signature Example 15.6
+
+char* message_6 =
+ "c1 87 91 5e 4e 87 da 81 c0 8e d4 35 6a 0c ce ac "
+ "1c 4f b5 c0 46 b4 52 81 b3 87 ec 28 f1 ab fd 56 "
+ "7e 54 6b 23 6b 37 d0 1a e7 1d 3b 28 34 36 5d 3d "
+ "f3 80 b7 50 61 b7 36 b0 13 0b 07 0b e5 8a e8 a4 "
+ "6d 12 16 63 61 b6 13 db c4 7d fa eb 4c a7 46 45 "
+ "6c 2e 88 83 85 52 5c ca 9d d1 c3 c7 a9 ad a7 6d "
+ "6c ";;
+
+char* signature_6 =
+ "9c ab 74 16 36 08 66 9f 75 55 a3 33 cf 19 6f e3"
+ "a0 e9 e5 eb 1a 32 d3 4b b5 c8 5f f6 89 aa ab 0e"
+ "3e 65 66 8e d3 b1 15 3f 94 eb 3d 8b e3 79 b8 ee"
+ "f0 07 c4 a0 2c 70 71 ce 30 d8 bb 34 1e 58 c6 20"
+ "f7 3d 37 b4 ec bf 48 be 29 4f 6c 9e 0e cb 5e 63"
+ "fe c4 1f 12 0e 55 53 df a0 eb eb bb 72 64 0a 95"
+ "37 ba dc b4 51 33 02 29 d9 f7 10 f6 2e 3e d8 ec"
+ "78 4e 50 ee 1d 92 62 b4 26 71 34 00 11 d7 d0 98"
+ "c6 f2 55 7b 21 31 fa 9b d0 25 46 36 59 7e 88 ec"
+ "b3 5a 24 0e f0 fd 85 95 71 24 df 80 80 fe e1 e1"
+ "49 af 93 99 89 e8 6b 26 c8 5a 58 81 fa e8 67 3d"
+ "9f d4 08 00 dd 13 4e b9 bd b6 41 0f 42 0b 0a a9"
+ "7b 20 ef cf 2e b0 c8 07 fa eb 83 a3 cc d9 b5 1d"
+ "45 53 e4 1d fc 0d f6 ca 80 a1 e8 1d c2 34 bb 83"
+ "89 dd 19 5a 38 b4 2d e4 ed c4 9d 34 64 78 b9 f1"
+ "1f 05 57 20 5f 5b 0b d7 ff e9 c8 50 f3 96 d7 c4";;
+
+// PKCS#1 v1.5 Signature Example 15.7
+
+char* message_7 =
+ "ab fa 2e cb 7d 29 bd 5b cb 99 31 ce 2b ad 2f 74 "
+ "38 3e 95 68 3c ee 11 02 2f 08 e8 e7 d0 b8 fa 05 "
+ "8b f9 eb 7e b5 f9 88 68 b5 bb 1f b5 c3 1c ed a3 "
+ "a6 4f 1a 12 cd f2 0f cd 0e 5a 24 6d 7a 17 73 d8 "
+ "db a0 e3 b2 77 54 5b ab e5 8f 2b 96 e3 f4 ed c1 "
+ "8e ab f5 cd 2a 56 0f ca 75 fe 96 e0 7d 85 9d ef "
+ "b2 56 4f 3a 34 f1 6f 11 e9 1b 3a 71 7b 41 af 53 "
+ "f6 60 53 23 00 1a a4 06 c6 ";
+
+char* signature_7 =
+ "c4 b4 37 bc f7 03 f3 52 e1 fa f7 4e b9 62 20 39"
+ "42 6b 56 72 ca f2 a7 b3 81 c6 c4 f0 19 1e 7e 4a"
+ "98 f0 ee bc d6 f4 17 84 c2 53 7f f0 f9 9e 74 98"
+ "2c 87 20 1b fb c6 5e ae 83 2d b7 1d 16 da ca db"
+ "09 77 e5 c5 04 67 9e 40 be 0f 9d b0 6f fd 84 8d"
+ "d2 e5 c3 8a 7e c0 21 e7 f6 8c 47 df d3 8c c3 54"
+ "49 3d 53 39 b4 59 5a 5b f3 1e 3f 8f 13 81 68 07"
+ "37 3d f6 ad 0d c7 e7 31 e5 1a d1 9e b4 75 4b 13"
+ "44 85 84 2f e7 09 d3 78 44 4d 8e 36 b1 72 4a 4f"
+ "da 21 ca fe e6 53 ab 80 74 7f 79 52 ee 80 4d ea"
+ "b1 03 9d 84 13 99 45 bb f4 be 82 00 87 53 f3 c5"
+ "4c 78 21 a1 d2 41 f4 21 79 c7 94 ef 70 42 bb f9"
+ "95 56 56 22 2e 45 c3 43 69 a3 84 69 7b 6a e7 42"
+ "e1 8f a5 ca 7a ba d2 7d 9f e7 10 52 e3 31 0d 0f"
+ "52 c8 d1 2e a3 3b f0 53 a3 00 f4 af c4 f0 98 df"
+ "4e 6d 88 67 79 d6 45 94 d3 69 15 8f db c1 f6 94";
+
+// PKCS#1 v1.5 Signature Example 15.8
+
+char* message_8 =
+ "df 40 44 a8 9a 83 e9 fc bf 12 62 54 0a e3 03 8b "
+ "bc 90 f2 b2 62 8b f2 a4 46 7a c6 77 22 d8 54 6b "
+ "3a 71 cb 0e a4 16 69 d5 b4 d6 18 59 c1 b4 e4 7c "
+ "ec c5 93 3f 75 7e c8 6d b0 64 4e 31 18 12 d0 0f "
+ "b8 02 f0 34 00 63 9c 0e 36 4d ae 5a eb c5 79 1b "
+ "c6 55 76 23 61 bc 43 c5 3d 3c 78 86 76 8f 79 68 "
+ "c1 c5 44 c6 f7 9f 7b e8 20 c7 e2 bd 2f 9d 73 e6 "
+ "2d ed 6d 2e 93 7e 6a 6d ae f9 0e e3 7a 1a 52 a5 "
+ "4f 00 e3 1a dd d6 48 94 cf 4c 02 e1 60 99 e2 9f "
+ "9e b7 f1 a7 bb 7f 84 c4 7a 2b 59 48 13 be 02 a1 "
+ "7b 7f c4 3b 34 c2 2c 91 92 52 64 12 6c 89 f8 6b "
+ "b4 d8 7f 3e f1 31 29 6c 53 a3 08 e0 33 1d ac 8b "
+ "af 3b 63 42 22 66 ec ef 2b 90 78 15 35 db da 41 "
+ "cb d0 cf 22 a8 cb fb 53 2e c6 8f c6 af b2 ac 06 ";
+
+char* signature_8 =
+ "14 14 b3 85 67 ae 6d 97 3e de 4a 06 84 2d cc 0e"
+ "05 59 b1 9e 65 a4 88 9b db ab d0 fd 02 80 68 29"
+ "13 ba cd 5d c2 f0 1b 30 bb 19 eb 81 0b 7d 9d ed"
+ "32 b2 84 f1 47 bb e7 71 c9 30 c6 05 2a a7 34 13"
+ "90 a8 49 f8 1d a9 cd 11 e5 ec cf 24 6d ba e9 5f"
+ "a9 58 28 e9 ae 0c a3 55 03 25 32 6d ee f9 f4 95"
+ "30 ba 44 1b ed 4a c2 9c 02 9c 9a 27 36 b1 a4 19"
+ "0b 85 08 4a d1 50 42 6b 46 d7 f8 5b d7 02 f4 8d"
+ "ac 5f 71 33 0b c4 23 a7 66 c6 5c c1 dc ab 20 d3"
+ "d3 bb a7 2b 63 b3 ef 82 44 d4 2f 15 7c b7 e3 a8"
+ "ba 5c 05 27 2c 64 cc 1a d2 1a 13 49 3c 39 11 f6"
+ "0b 4e 9f 4e cc 99 00 eb 05 6e e5 9d 6f e4 b8 ff"
+ "6e 80 48 cc c0 f3 8f 28 36 fd 3d fe 91 bf 4a 38"
+ "6e 1e cc 2c 32 83 9f 0c a4 d1 b2 7a 56 8f a9 40"
+ "dd 64 ad 16 bd 01 25 d0 34 8e 38 30 85 f0 88 94"
+ "86 1c a1 89 87 22 7d 37 b4 2b 58 4a 83 57 cb 04";
+
+// PKCS#1 v1.5 Signature Example 15.9
+
+char* message_9 =
+ "ea 94 1f f0 6f 86 c2 26 92 7f cf 0e 3b 11 b0 87 "
+ "26 76 17 0c 1b fc 33 bd a8 e2 65 c7 77 71 f9 d0 "
+ "85 01 64 a5 ee cb cc 5c e8 27 fb fa 07 c8 52 14 "
+ "79 6d 81 27 e8 ca a8 18 94 ea 61 ce b1 44 9e 72 "
+ "fe a0 a4 c9 43 b2 da 6d 9b 10 5f e0 53 b9 03 9a "
+ "9c c5 3d 42 0b 75 39 fa b2 23 9c 6b 51 d1 7e 69 "
+ "4c 95 7d 4b 0f 09 84 46 18 79 a0 75 9c 44 01 be "
+ "ec d4 c6 06 a0 af bd 7a 07 6f 50 a2 df c2 80 7f "
+ "24 f1 91 9b aa 77 46 d3 a6 4e 26 8e d3 f5 f8 e6 "
+ "da 83 a2 a5 c9 15 2f 83 7c b0 78 12 bd 5b a7 d3 "
+ "a0 79 85 de 88 11 3c 17 96 e9 b4 66 ec 29 9c 5a "
+ "c1 05 9e 27 f0 94 15 ";
+
+char* signature_9 =
+ "ce eb 84 cc b4 e9 09 92 65 65 07 21 ee a0 e8 ec"
+ "89 ca 25 bd 35 4d 4f 64 56 49 67 be 9d 4b 08 b3"
+ "f1 c0 18 53 9c 9d 37 1c f8 96 1f 22 91 fb e0 dc"
+ "2f 2f 95 fe a4 7b 63 9f 1e 12 f4 bc 38 1c ef 0c"
+ "2b 7a 7b 95 c3 ad f2 76 05 b7 f6 39 98 c3 cb ad"
+ "54 28 08 c3 82 2e 06 4d 4a d1 40 93 67 9e 6e 01"
+ "41 8a 6d 5c 05 96 84 cd 56 e3 4e d6 5a b6 05 b8"
+ "de 4f cf a6 40 47 4a 54 a8 25 1b bb 73 26 a4 2d"
+ "08 58 5c fc fc 95 67 69 b1 5b 6d 7f df 7d a8 4f"
+ "81 97 6e aa 41 d6 92 38 0f f1 0e ae cf e0 a5 79"
+ "68 29 09 b5 52 1f ad e8 54 d7 97 b8 a0 34 5b 9a"
+ "86 4e 05 88 f6 ca dd bf 65 f1 77 99 8e 18 0d 1f"
+ "10 24 43 e6 dc a5 3a 94 82 3c aa 9c 3b 35 f3 22"
+ "58 3c 70 3a f6 74 76 15 9e c7 ec 93 d1 76 9b 30"
+ "0a f0 e7 15 7d c2 98 c6 cd 2d ee 22 62 f8 cd dc"
+ "10 f1 1e 01 74 14 71 bb fd 65 18 a1 75 73 45 75";
+
+// PKCS#1 v1.5 Signature Example 15.10
+
+char* message_10 =
+ "d8 b8 16 45 c1 3c d7 ec f5 d0 0e d2 c9 1b 9a cd "
+ "46 c1 55 68 e5 30 3c 4a 97 75 ed e7 6b 48 40 3d "
+ "6b e5 6c 05 b6 b1 cf 77 c6 e7 5d e0 96 c5 cb 35 "
+ "51 cb 6f a9 64 f3 c8 79 cf 58 9d 28 e1 da 2f 9d "
+ "ec ";
+
+char* signature_10 =
+ "27 45 07 4c a9 71 75 d9 92 e2 b4 47 91 c3 23 c5"
+ "71 67 16 5c dd 8d a5 79 cd ef 46 86 b9 bb 40 4b"
+ "d3 6a 56 50 4e b1 fd 77 0f 60 bf a1 88 a7 b2 4b"
+ "0c 91 e8 81 c2 4e 35 b0 4d c4 dd 4c e3 85 66 bc"
+ "c9 ce 54 f4 9a 17 5f c9 d0 b2 25 22 d9 57 90 47"
+ "f9 ed 42 ec a8 3f 76 4a 10 16 39 97 94 7e 7d 2b"
+ "52 ff 08 98 0e 7e 7c 22 57 93 7b 23 f3 d2 79 d4"
+ "cd 17 d6 f4 95 54 63 73 d9 83 d5 36 ef d7 d1 b6"
+ "71 81 ca 2c b5 0a c6 16 c5 c7 ab fb b9 26 0b 91"
+ "b1 a3 8e 47 24 20 01 ff 45 2f 8d e1 0c a6 ea ea"
+ "dc af 9e dc 28 95 6f 28 a7 11 29 1f c9 a8 08 78"
+ "b8 ba 4c fe 25 b8 28 1c b8 0b c9 cd 6d 2b d1 82"
+ "52 46 ee be 25 2d 99 57 ef 93 70 73 52 08 4e 6d"
+ "36 d4 23 55 1b f2 66 a8 53 40 fb 4a 6a f3 70 88"
+ "0a ab 07 15 3d 01 f4 8d 08 6d f0 bf be c0 5e 7b"
+ "44 3b 97 e7 17 18 97 0e 2f 4b f6 20 23 e9 5b 67";
+
+// PKCS#1 v1.5 Signature Example 15.11
+
+char* message_11 =
+ "e5 73 9b 6c 14 c9 2d 51 0d 95 b8 26 93 33 37 ff "
+ "0d 24 ef 72 1a c4 ef 64 c2 ba d2 64 be 8b 44 ef "
+ "a1 51 6e 08 a2 7e b6 b6 11 d3 30 1d f0 06 2d ae "
+ "fc 73 a8 c0 d9 2e 2c 52 1f ac bc 7b 26 47 38 76 "
+ "7e a6 fc 97 d5 88 a0 ba f6 ce 50 ad f7 9e 60 0b "
+ "d2 9e 34 5f cb 1d ba 71 ac 5c 02 89 02 3f e4 a8 "
+ "2b 46 a5 40 77 19 19 7d 2e 95 8e 35 31 fd 54 ae "
+ "f9 03 aa bb 43 55 f8 83 18 99 4e d3 c3 dd 62 f4 "
+ "20 a7 ";
+
+char* signature_11 =
+ "be 40 a5 fb 94 f1 13 e1 b3 ef f6 b6 a3 39 86 f2"
+ "02 e3 63 f0 74 83 b7 92 e6 8d fa 55 54 df 04 66"
+ "cc 32 15 09 50 78 3b 4d 96 8b 63 9a 04 fd 2f b9"
+ "7f 6e b9 67 02 1f 5a dc cb 9f ca 95 ac c8 f2 cd"
+ "88 5a 38 0b 0a 4e 82 bc 76 07 64 db ab 88 c1 e6"
+ "c0 25 5c aa 94 f2 32 19 9d 6f 59 7c c9 14 5b 00"
+ "e3 d4 ba 34 6b 55 9a 88 33 ad 15 16 ad 51 63 f0"
+ "16 af 6a 59 83 1c 82 ea 13 c8 22 4d 84 d0 76 5a"
+ "9d 12 38 4d a4 60 a8 53 1b 4c 40 7e 04 f4 f3 50"
+ "70 9e b9 f0 8f 5b 22 0f fb 45 ab f6 b7 5d 15 79"
+ "fd 3f 1e b5 5f c7 5b 00 af 8b a3 b0 87 82 7f e9"
+ "ae 9f b4 f6 c5 fa 63 03 1f e5 82 85 2f e2 83 4f"
+ "9c 89 bf f5 3e 25 52 21 6b c7 c1 d4 a3 d5 dc 2b"
+ "a6 95 5c d9 b1 7d 13 63 e7 fe e8 ed 76 29 75 3f"
+ "f3 12 5e dd 48 52 1a e3 b9 b0 32 17 f4 49 6d 0d"
+ "8e de 57 ac bc 5b d4 de ae 74 a5 6f 86 67 1d e2";
+
+// PKCS#1 v1.5 Signature Example 15.12
+
+char* message_12 =
+ "7a f4 28 35 91 7a 88 d6 b3 c6 71 6b a2 f5 b0 d5 "
+ "b2 0b d4 e2 e6 e5 74 e0 6a f1 ee f7 c8 11 31 be "
+ "22 bf 81 28 b9 cb c6 ec 00 27 5b a8 02 94 a5 d1 "
+ "17 2d 08 24 a7 9e 8f dd 83 01 83 e4 c0 0b 96 78 "
+ "28 67 b1 22 7f ea 24 9a ad 32 ff c5 fe 00 7b c5 "
+ "1f 21 79 2f 72 8d ed a8 b5 70 8a a9 9c ab ab 20 "
+ "a4 aa 78 3e d8 6f 0f 27 b5 d5 63 f4 2e 07 15 8c "
+ "ea 72 d0 97 aa 68 87 ec 41 1d d0 12 91 2a 5e 03 "
+ "2b bf a6 78 50 71 44 bc c9 5f 39 b5 8b e7 bf d1 "
+ "75 9a db 9a 91 fa 1d 6d 82 26 a8 34 3a 8b 84 9d "
+ "ae 76 f7 b9 82 24 d5 9e 28 f7 81 f1 3e ce 60 5f "
+ "84 f6 c9 0b ae 5f 8c f3 78 81 6f 40 20 a7 dd a1 "
+ "be d9 0c 92 a2 36 34 d2 03 fa c3 fc d8 6d 68 d3 "
+ "18 2a 7d 9c ca be 7b 07 95 f5 c6 55 e9 ac c4 e3 "
+ "ec 18 51 40 d1 0c ef 05 34 64 ab 17 5c 83 bd 83 "
+ "93 5e 3d ab af 34 62 ee be 63 d1 5f 57 3d 26 9a ";
+
+char* signature_12 =
+ "4e 78 c5 90 2b 80 79 14 d1 2f a5 37 ae 68 71 c8"
+ "6d b8 02 1e 55 d1 ad b8 eb 0c cf 1b 8f 36 ab 7d"
+ "ad 1f 68 2e 94 7a 62 70 72 f0 3e 62 73 71 78 1d"
+ "33 22 1d 17 4a be 46 0d bd 88 56 0c 22 f6 90 11"
+ "6e 2f bb e6 e9 64 36 3a 3e 52 83 bb 5d 94 6e f1"
+ "c0 04 7e ba 03 8c 75 6c 40 be 79 23 05 58 09 b0"
+ "e9 f3 4a 03 a5 88 15 eb dd e7 67 93 1f 01 8f 6f"
+ "18 78 f2 ef 4f 47 dd 37 40 51 dd 48 68 5d ed 6e"
+ "fb 3e a8 02 1f 44 be 1d 7d 14 93 98 f9 8e a9 c0"
+ "8d 62 88 8e bb 56 19 2d 17 74 7b 6b 8e 17 09 54"
+ "31 f1 25 a8 a8 e9 96 2a a3 1c 28 52 64 e0 8f b2"
+ "1a ac 33 6c e6 c3 8a a3 75 e4 2b c9 2a b0 ab 91"
+ "03 84 31 e1 f9 2c 39 d2 af 5d ed 7e 43 bc 15 1e"
+ "6e be a4 c3 e2 58 3a f3 43 7e 82 c4 3c 5e 3b 5b"
+ "07 cf 03 59 68 3d 22 98 e3 59 48 ed 80 6c 06 3c"
+ "60 6e a1 78 15 0b 1e fc 15 85 69 34 c7 25 5c fe";
+
+// PKCS#1 v1.5 Signature Example 15.13
+
+char* message_13 =
+ "eb ae f3 f9 f2 3b df e5 fa 6b 8a f4 c2 08 c1 89 "
+ "f2 25 1b f3 2f 5f 13 7b 9d e4 40 63 78 68 6b 3f "
+ "07 21 f6 2d 24 cb 86 88 d6 fc 41 a2 7c ba e2 1d "
+ "30 e4 29 fe ac c7 11 19 41 c2 77 ";
+
+char* signature_13 =
+ "c4 8d be f5 07 11 4f 03 c9 5f af be b4 df 1b fa"
+ "88 e0 18 4a 33 cc 4f 8a 9a 10 35 ff 7f 82 2a 5e"
+ "38 cd a1 87 23 91 5f f0 78 24 44 29 e0 f6 08 1c"
+ "14 fd 83 33 1f a6 5c 6b a7 bb 9a 12 db f6 62 23"
+ "74 cd 0c a5 7d e3 77 4e 2b d7 ae 82 36 77 d0 61"
+ "d5 3a e9 c4 04 0d 2d a7 ef 70 14 f3 bb dc 95 a3"
+ "61 a4 38 55 c8 ce 9b 97 ec ab ce 17 4d 92 62 85"
+ "14 2b 53 4a 30 87 f9 f4 ef 74 51 1e c7 42 b0 d5"
+ "68 56 03 fa f4 03 b5 07 2b 98 5d f4 6a df 2d 25"
+ "29 a0 2d 40 71 1e 21 90 91 70 52 37 1b 79 b7 49"
+ "b8 3a bf 0a e2 94 86 c3 f2 f6 24 77 b2 bd 36 2b"
+ "03 9c 01 3c 0c 50 76 ef 52 0d bb 40 5f 42 ce e9"
+ "54 25 c3 73 a9 75 e1 cd d0 32 c4 96 22 c8 50 79"
+ "b0 9e 88 da b2 b1 39 69 ef 7a 72 39 73 78 10 40"
+ "45 9f 57 d5 01 36 38 48 3d e2 d9 1c b3 c4 90 da"
+ "81 c4 6d e6 cd 76 ea 8a 0c 8f 6f e3 31 71 2d 24";
+
+// PKCS#1 v1.5 Signature Example 15.14
+
+char* message_14 =
+ "c5 a2 71 12 78 76 1d fc dd 4f 0c 99 e6 f5 61 9d "
+ "6c 48 b5 d4 c1 a8 09 82 fa a6 b4 cf 1c f7 a6 0f "
+ "f3 27 ab ef 93 c8 01 42 9e fd e0 86 40 85 81 46 "
+ "10 56 ac c3 3f 3d 04 f5 ad a2 12 16 ca cd 5f d1 "
+ "f9 ed 83 20 3e 0e 2f e6 13 8e 3e ae 84 24 e5 91 "
+ "5a 08 3f 3f 7a b7 60 52 c8 be 55 ae 88 2d 6e c1 "
+ "48 2b 1e 45 c5 da e9 f4 10 15 40 53 27 02 2e c3 "
+ "2f 0e a2 42 97 63 b2 55 04 3b 19 58 ee 3c f6 d6 "
+ "39 83 59 6e b3 85 84 4f 85 28 cc 9a 98 65 83 5d "
+ "c5 11 3c 02 b8 0d 0f ca 68 aa 25 e7 2b ca ae b3 "
+ "cf 9d 79 d8 4f 98 4f d4 17 ";
+
+char* signature_14 =
+ "6b d5 25 7a a0 66 11 fb 46 60 08 7c b4 bc 4a 9e"
+ "44 91 59 d3 16 52 bd 98 08 44 da f3 b1 c7 b3 53"
+ "f8 e5 61 42 f7 ea 98 57 43 3b 18 57 3b 4d ee de"
+ "81 8a 93 b0 29 02 97 78 3f 1a 2f 23 cb c7 27 97"
+ "a6 72 53 7f 01 f6 24 84 cd 41 62 c3 21 4b 9a c6"
+ "28 22 4c 5d e0 1f 32 bb 9b 76 b2 73 54 f2 b1 51"
+ "d0 e8 c4 21 3e 46 15 ad 0b c7 1f 51 5e 30 0d 6a"
+ "64 c6 74 34 11 ff fd e8 e5 ff 19 0e 54 92 30 43"
+ "12 6e cf c4 c4 53 90 22 66 8f b6 75 f2 5c 07 e2"
+ "00 99 ee 31 5b 98 d6 af ec 4b 1a 9a 93 dc 33 49"
+ "6a 15 bd 6f de 16 63 a7 d4 9b 9f 1e 63 9d 38 66"
+ "4b 37 a0 10 b1 f3 5e 65 86 82 d9 cd 63 e5 7d e0"
+ "f1 5e 8b dd 09 65 58 f0 7e c0 ca a2 18 a8 c0 6f"
+ "47 88 45 39 40 28 7c 9d 34 b6 d4 0a 3f 09 bf 77"
+ "99 fe 98 ae 4e b4 9f 3f f4 1c 50 40 a5 0c ef c9"
+ "bd f2 39 4b 74 9c f1 64 48 0d f1 ab 68 80 27 3b";
+
+// PKCS#1 v1.5 Signature Example 15.15
+
+char* message_15 =
+ "9b f8 aa 25 3b 87 2e a7 7a 7e 23 47 6b e2 6b 23 "
+ "29 57 8c f6 ac 9e a2 80 5b 35 7f 6f c3 ad 13 0d "
+ "ba eb 3d 86 9a 13 cc e7 a8 08 bb bb c9 69 85 7e "
+ "03 94 5c 7b b6 1d f1 b5 c2 58 9b 8e 04 6c 2a 5d "
+ "7e 40 57 b1 a7 4f 24 c7 11 21 63 64 28 85 29 ec "
+ "95 70 f2 51 97 21 3b e1 f5 c2 e5 96 f8 bf 8b 2c "
+ "f3 cb 38 aa 56 ff e5 e3 1d f7 39 58 20 e9 4e cf "
+ "3b 11 89 a9 65 dc f9 a9 cb 42 98 d3 c8 8b 29 23 "
+ "c1 9f c6 bc 34 aa ce ca d4 e0 93 1a 7c 4e 5d 73 "
+ "dc 86 df a7 98 a8 47 6d 82 46 3e ef aa 90 a8 a9 "
+ "19 2a b0 8b 23 08 8d d5 8e 12 80 f7 d7 2e 45 48 "
+ "39 6b aa c1 12 25 2d d5 c5 34 6a db 20 04 a2 f7 "
+ "10 1c cc 89 9c c7 fa fa e8 bb e2 95 73 88 96 a5 "
+ "b2 01 22 85 01 4e f6 ";
+
+char* signature_15 =
+ "27 f7 f4 da 9b d6 10 10 6e f5 7d 32 38 3a 44 8a"
+ "8a 62 45 c8 3d c1 30 9c 6d 77 0d 35 7b a8 9e 73"
+ "f2 ad 08 32 06 2e b0 fe 0a c9 15 57 5b cd 6b 8b"
+ "ca db 4e 2b a6 fa 9d a7 3a 59 17 51 52 b2 d4 fe"
+ "72 b0 70 c9 b7 37 9e 50 00 0e 55 e6 c2 69 f6 65"
+ "8c 93 79 72 79 7d 3a dd 69 f1 30 e3 4b 85 bd ec"
+ "9f 3a 9b 39 22 02 d6 f3 e4 30 d0 9c ac a8 22 77"
+ "59 ab 82 5f 70 12 d2 ff 4b 5b 62 c8 50 4d ba d8"
+ "55 c0 5e dd 5c ab 5a 4c cc dc 67 f0 1d d6 51 7c"
+ "7d 41 c4 3e 2a 49 57 af f1 9d b6 f1 8b 17 85 9a"
+ "f0 bc 84 ab 67 14 6e c1 a4 a6 0a 17 d7 e0 5f 8b"
+ "4f 9c ed 6a d1 09 08 d8 d7 8f 7f c8 8b 76 ad c8"
+ "29 0f 87 da f2 a7 be 10 ae 40 85 21 39 5d 54 ed"
+ "25 56 fb 76 61 85 4a 73 0c e3 d8 2c 71 a8 d4 93"
+ "ec 49 a3 78 ac 8a 3c 74 43 9f 7c c5 55 ba 13 f8"
+ "59 07 08 90 ee 18 ff 65 8f a4 d7 41 96 9d 70 a5";
+
+// PKCS#1 v1.5 Signature Example 15.16
+
+char* message_16 =
+ "32 47 48 30 e2 20 37 54 c8 bf 06 81 dc 4f 84 2a "
+ "fe 36 09 30 37 86 16 c1 08 e8 33 65 6e 56 40 c8 "
+ "68 56 88 5b b0 5d 1e b9 43 8e fe de 67 92 63 de "
+ "07 cb 39 55 3f 6a 25 e0 06 b0 a5 23 11 a0 63 ca "
+ "08 82 66 d2 56 4f f6 49 0c 46 b5 60 98 18 54 8f "
+ "88 76 4d ad 34 a2 5e 3a 85 d5 75 02 3f 0b 9e 66 "
+ "50 48 a0 3c 35 05 79 a9 d3 24 46 c7 bb 96 cc 92 "
+ "e0 65 ab 94 d3 c8 95 2e 8d f6 8e f0 d9 fa 45 6b "
+ "3a 06 bb 80 e3 bb c4 b2 8e 6a 94 b6 d0 ff 76 96 "
+ "a6 4e fe 05 e7 35 fe a0 25 d7 bd bc 41 39 f3 a3 "
+ "b5 46 07 5c ba 7e fa 94 73 74 d3 f0 ac 80 a6 8d "
+ "76 5f 5d f6 21 0b ca 06 9a 2d 88 64 7a f7 ea 04 "
+ "2d ac 69 0c b5 73 78 ec 07 77 61 4f b8 b6 5f f4 "
+ "53 ca 6b 7d ce 60 98 45 1a 2f 8c 0d a9 bf ec f1 "
+ "fd f3 91 bb aa 4e 2a 91 ca 18 a1 12 1a 75 23 a2 "
+ "ab d4 25 14 f4 89 e8 ";
+
+char* signature_16 =
+ "69 17 43 72 57 c2 2c cb 54 03 29 0c 3d ee 82 d9"
+ "cf 75 50 b3 1b d3 1c 51 bd 57 bf d3 5d 45 2a b4"
+ "db 7c 4b e6 b2 e2 5a c9 a5 9a 1d 2a 7f eb 62 7f"
+ "0a fd 49 76 b3 00 3c c9 cf fd 88 96 50 5e c3 82"
+ "f2 65 10 4d 4c f8 c9 32 fa 9f e8 6e 00 87 07 95"
+ "99 12 38 9d a4 b2 d6 b3 69 b3 6a 5e 72 e2 9d 24"
+ "c9 a9 8c 9d 31 a3 ab 44 e6 43 e6 94 12 66 a4 7a"
+ "45 e3 44 6c e8 77 6a be 24 1a 8f 5f c6 42 3b 24"
+ "b1 ff 25 0d c2 c3 a8 17 23 53 56 10 77 e8 50 a7"
+ "69 b2 5f 03 25 da c8 89 65 a3 b9 b4 72 c4 94 e9"
+ "5f 71 9b 4e ac 33 2c aa 7a 65 c7 df e4 6d 9a a7"
+ "e6 e0 0f 52 5f 30 3d d6 3a b7 91 92 18 90 18 68"
+ "f9 33 7f 8c d2 6a af e6 f3 3b 7f b2 c9 88 10 af"
+ "19 f7 fc b2 82 ba 15 77 91 2c 1d 36 89 75 fd 5d"
+ "44 0b 86 e1 0c 19 97 15 fa 0b 6f 42 50 b5 33 73"
+ "2d 0b ef e1 54 51 50 fc 47 b8 76 de 09 b0 0a 94";
+
+// PKCS#1 v1.5 Signature Example 15.17
+
+char* message_17 =
+ "00 8e 59 50 5e af b5 50 aa e5 e8 45 58 4c eb b0 "
+ "0b 6d e1 73 3e 9f 95 d4 2c 88 2a 5b be b5 ce 1c "
+ "57 e1 19 e7 c0 d4 da ca 9f 1f f7 87 02 17 f7 cf "
+ "d8 a6 b3 73 97 7c ac 9c ab 8e 71 e4 20 ";
+
+char* signature_17 =
+ "92 25 03 b6 73 ee 5f 3e 69 1e 1c a8 5e 9f f4 17"
+ "3c f7 2b 05 ac 2c 13 1d a5 60 35 93 e3 bc 25 9c"
+ "94 c1 f7 d3 a0 6a 5b 98 91 bf 11 3f a3 9e 59 ff"
+ "7c 1e d6 46 5e 90 80 49 cb 89 e4 e1 25 cd 37 d2"
+ "ff d9 22 7a 41 b4 a0 a1 9c 0a 44 fb bf 3d e5 5b"
+ "ab 80 20 87 a3 bb 8d 4f f6 68 ee 6b bb 8a d8 9e"
+ "68 57 a7 9a 9c 72 78 19 90 df cf 92 cd 51 94 04"
+ "c9 50 f1 3d 11 43 c3 18 4f 1d 25 0c 90 e1 7a c6"
+ "ce 36 16 3b 98 95 62 7a d6 ff ec 14 22 44 1f 55"
+ "e4 49 9d ba 9b e8 95 46 ae 8b c6 3c ca 01 dd 08"
+ "46 3a e7 f1 fc e3 d8 93 99 69 38 77 8c 18 12 e6"
+ "74 ad 9c 30 9c 5a cc a3 fd e4 4e 7d d8 69 59 93"
+ "e9 c1 fa 87 ac da 99 ec e5 c8 49 9e 46 89 57 ad"
+ "66 35 9b f1 2a 51 ad be 78 d3 a2 13 b4 49 bf 0b"
+ "5f 8d 4d 49 6a cf 03 d3 03 3b 7c cd 19 6b c2 2f"
+ "68 fb 7b ef 4f 69 7c 5e a2 b3 50 62 f4 8a 36 dd";
+
+// PKCS#1 v1.5 Signature Example 15.18
+
+char* message_18 =
+ "6a bc 54 cf 8d 1d ff 1f 53 b1 7d 81 60 36 88 78 "
+ "a8 78 8c c6 d2 2f a5 c2 25 8c 88 e6 60 b0 9a 89 "
+ "33 f9 f2 c0 50 4d da dc 21 f6 e7 5e 0b 83 3b eb "
+ "55 52 29 de e6 56 b9 04 7b 92 f6 2e 76 b8 ff cc "
+ "60 da b0 6b 80 ";
+
+char* signature_18 =
+ "0b 6d af 42 f7 a8 62 14 7e 41 74 93 c2 c4 01 ef"
+ "ae 32 63 6a b4 cb d4 41 92 bb f5 f1 95 b5 0a e0"
+ "96 a4 75 a1 61 4f 0a 9f a8 f7 a0 26 cb 46 c6 50"
+ "6e 51 8e 33 d8 3e 56 47 7a 87 5a ca 8c 7e 71 4c"
+ "e1 bd bd 61 ef 5d 53 52 39 b3 3f 2b fd d6 17 71"
+ "ba b6 27 76 d7 81 71 a1 42 3c ea 87 31 f8 2e 60"
+ "76 6d 64 54 26 56 20 b1 5f 5c 5a 58 4f 55 f9 5b"
+ "80 2f e7 8c 57 4e d5 da cf c8 31 f3 cf 2b 05 02"
+ "c0 b2 98 f2 5c cf 11 f9 73 b3 1f 85 e4 74 42 19"
+ "85 f3 cf f7 02 df 39 46 ef 0a 66 05 68 21 11 b2"
+ "f5 5b 1f 8a b0 d2 ea 3a 68 3c 69 98 5e ad 93 ed"
+ "44 9e a4 8f 03 58 dd f7 08 02 cb 41 de 2f d8 3f"
+ "3c 80 80 82 d8 49 36 94 8e 0c 84 a1 31 b4 92 78"
+ "27 46 05 27 bb 5c d2 4b fa b7 b4 8e 07 1b 24 17"
+ "19 30 f9 97 63 27 2f 97 97 bc b7 6f 1d 24 81 57"
+ "55 58 fc f2 60 b1 f0 e5 54 eb b3 df 3c fc b9 58";
+
+// PKCS#1 v1.5 Signature Example 15.19
+
+char* message_19 =
+ "af 2d 78 15 2c f1 0e fe 01 d2 74 f2 17 b1 77 f6 "
+ "b0 1b 5e 74 9f 15 67 71 5d a3 24 85 9c d3 dd 88 "
+ "db 84 8e c7 9f 48 db ba 7b 6f 1d 33 11 1e f3 1b "
+ "64 89 9e 73 91 c2 bf fd 69 f4 90 25 cf 20 1f c5 "
+ "85 db d1 54 2c 1c 77 8a 2c e7 a7 ee 10 8a 30 9f "
+ "ec a2 6d 13 3a 5f fe dc 4e 86 9d cd 76 56 59 6a "
+ "c8 42 7e a3 ef 6e 3f d7 8f e9 9d 8d dc 71 d8 39 "
+ "f6 78 6e 0d a6 e7 86 bd 62 b3 a4 f1 9b 89 1a 56 "
+ "15 7a 55 4e c2 a2 b3 9e 25 a1 d7 c7 d3 73 21 c7 "
+ "a1 d9 46 cf 4f be 75 8d 92 76 f0 85 63 44 9d 67 "
+ "41 4a 2c 03 0f 42 51 cf e2 21 3d 04 a5 41 06 37 "
+ "87 ";
+
+char* signature_19 =
+ "20 9c 61 15 78 57 38 7b 71 e2 4b f3 dd 56 41 45"
+ "50 50 3b ec 18 0f f5 3b dd 9b ac 06 2a 2d 49 95"
+ "09 bf 99 12 81 b7 95 27 df 91 36 61 5b 7a 6d 9d"
+ "b3 a1 03 b5 35 e0 20 2a 2c ac a1 97 a7 b7 4e 53"
+ "56 f3 dd 59 5b 49 ac fd 9d 30 04 9a 98 ca 88 f6"
+ "25 bc a1 d5 f2 2a 39 2d 8a 74 9e fb 6e ed 9b 78"
+ "21 d3 11 0a c0 d2 44 19 9e cb 4a a3 d7 35 a8 3a"
+ "2e 88 93 c6 bf 85 81 38 3c ca ee 83 46 35 b7 fa"
+ "1f af fa 45 b1 3d 15 c1 da 33 af 71 e8 93 03 d6"
+ "80 90 ff 62 ee 61 5f df 5a 84 d1 20 71 1d a5 3c"
+ "28 89 19 8a b3 83 17 a9 73 4a b2 7d 67 92 4c ea"
+ "74 15 6f f9 9b ef 98 76 bb 5c 33 9e 93 74 52 83"
+ "e1 b3 4e 07 22 26 b8 80 45 e0 17 e9 f0 5b 2a 8c"
+ "41 67 40 25 8e 22 3b 26 90 02 74 91 73 22 73 f3"
+ "22 9d 9e f2 b1 b3 80 7e 32 10 18 92 0a d3 e5 3d"
+ "ae 47 e6 d9 39 5c 18 4b 93 a3 74 c6 71 fa a2 ce";
+
+// PKCS#1 v1.5 Signature Example 15.20
+
+char* message_20 =
+ "40 ee 99 24 58 d6 f6 14 86 d2 56 76 a9 6d d2 cb "
+ "93 a3 7f 04 b1 78 48 2f 2b 18 6c f8 82 15 27 0d "
+ "ba 29 d7 86 d7 74 b0 c5 e7 8c 7f 6e 56 a9 56 e7 "
+ "f7 39 50 a2 b0 c0 c1 0a 08 db cd 67 e5 b2 10 bb "
+ "21 c5 8e 27 67 d4 4f 7d d4 01 4e 39 66 14 3b f7 "
+ "e3 d6 6f f0 c0 9b e4 c5 5f 93 b3 99 94 b8 51 8d "
+ "9c 1d 76 d5 b4 73 74 de a0 8f 15 7d 57 d7 06 34 "
+ "97 8f 38 56 e0 e5 b4 81 af bb db 5a 3a c4 8d 48 "
+ "4b e9 2c 93 de 22 91 78 35 4c 2d e5 26 e9 c6 5a "
+ "31 ed e1 ef 68 cb 63 98 d7 91 16 84 fe c0 ba bc "
+ "3a 78 1a 66 66 07 83 50 69 74 d0 e1 48 25 10 1c "
+ "3b fa ea ";
+
+char* signature_20 =
+ "92 75 02 b8 24 af c4 25 13 ca 65 70 de 33 8b 8a"
+ "64 c3 a8 5e b8 28 d3 19 36 24 f2 7e 8b 10 29 c5"
+ "5c 11 9c 97 33 b1 8f 58 49 b3 50 09 18 bc c0 05"
+ "51 d9 a8 fd f5 3a 97 74 9f a8 dc 48 0d 6f e9 74"
+ "2a 58 71 f9 73 92 65 28 97 2a 1a f4 9e 39 25 b0"
+ "ad f1 4a 84 27 19 b4 a5 a2 d8 9f a9 c0 b6 60 5d"
+ "21 2b ed 1e 67 23 b9 34 06 ad 30 e8 68 29 a5 c7"
+ "19 b8 90 b3 89 30 6d c5 50 64 86 ee 2f 36 a8 df"
+ "e0 a9 6a f6 78 c9 cb d6 af f3 97 ca 20 0e 3e dc"
+ "1e 36 bd 2f 08 b3 1d 54 0c 0c b2 82 a9 55 9e 4a"
+ "dd 4f c9 e6 49 2e ed 0c cb d3 a6 98 2e 5f aa 2d"
+ "dd 17 be 47 41 7c 80 b4 e5 45 2d 31 f7 24 01 a0"
+ "42 32 51 09 54 4d 95 4c 01 93 90 79 d4 09 a5 c3"
+ "78 d7 51 2d fc 2d 2a 71 ef cc 34 32 a7 65 d1 c6"
+ "a5 2c fc e8 99 cd 79 b1 5b 4f c3 72 36 41 ef 6b"
+ "d0 0a cc 10 40 7e 5d f5 8d d1 c3 c5 c5 59 a5 06";
+
+
+unsigned char* parsehex(char* str, int* len) {
+ // result can't be longer than input
+ unsigned char* result = malloc(strlen(str));
+
+ unsigned char* p = result;
+ *len = 0;
+
+ while (*str) {
+ int b;
+
+ while (isspace(*str)) str++;
+
+ switch (*str) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ b = (*str - '0') << 4; break;
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ b = (*str - 'a' + 10) << 4; break;
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ b = (*str - 'A' + 10) << 4; break;
+ case '\0':
+ return result;
+ default:
+ return NULL;
+ }
+ str++;
+
+ while (isspace(*str)) str++;
+
+ switch (*str) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ b |= *str - '0'; break;
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ b |= *str - 'a' + 10; break;
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ b |= *str - 'A' + 10; break;
+ default:
+ return NULL;
+ }
+ str++;
+
+ *p++ = b;
+ ++*len;
+ }
+
+ return result;
+}
+
+
+int main(int arg __unused, char** argv __unused) {
+
+ unsigned char hash[SHA_DIGEST_SIZE];
+
+ unsigned char* message;
+ int mlen;
+ unsigned char* signature;
+ int slen;
+
+#define TEST_MESSAGE(n) do {\
+ message = parsehex(message_##n, &mlen); \
+ SHA_hash(message, mlen, hash); \
+ signature = parsehex(signature_##n, &slen); \
+ int result = RSA_verify(&key_15, signature, slen, hash, sizeof(hash)); \
+ printf("message %d: %s\n", n, result ? "verified" : "not verified"); \
+ success = success && result; \
+ } while(0)
+
+ int success = 1;
+
+ TEST_MESSAGE(1);
+ TEST_MESSAGE(2);
+ TEST_MESSAGE(3);
+ TEST_MESSAGE(4);
+ TEST_MESSAGE(5);
+ TEST_MESSAGE(6);
+ TEST_MESSAGE(7);
+ TEST_MESSAGE(8);
+ TEST_MESSAGE(9);
+ TEST_MESSAGE(10);
+ TEST_MESSAGE(11);
+ TEST_MESSAGE(12);
+ TEST_MESSAGE(13);
+ TEST_MESSAGE(14);
+ TEST_MESSAGE(15);
+ TEST_MESSAGE(16);
+ TEST_MESSAGE(17);
+ TEST_MESSAGE(18);
+ TEST_MESSAGE(19);
+ TEST_MESSAGE(20);
+
+ printf("\n%s\n\n", success ? "PASS" : "FAIL");
+
+ return !success;
+}
diff --git a/libmincrypt/tools/Android.mk b/libmincrypt/tools/Android.mk
new file mode 100644
index 0000000..3154914
--- /dev/null
+++ b/libmincrypt/tools/Android.mk
@@ -0,0 +1,22 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := dumpkey
+LOCAL_SRC_FILES := DumpPublicKey.java
+LOCAL_JAR_MANIFEST := DumpPublicKey.mf
+LOCAL_STATIC_JAVA_LIBRARIES := bouncycastle-host
+include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/libmincrypt/tools/DumpPublicKey.java b/libmincrypt/tools/DumpPublicKey.java
new file mode 100644
index 0000000..3eb1398
--- /dev/null
+++ b/libmincrypt/tools/DumpPublicKey.java
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dumpkey;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+import java.io.FileInputStream;
+import java.math.BigInteger;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.KeyStore;
+import java.security.Key;
+import java.security.PublicKey;
+import java.security.Security;
+import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.ECPoint;
+
+/**
+ * Command line tool to extract RSA public keys from X.509 certificates
+ * and output source code with data initializers for the keys.
+ * @hide
+ */
+class DumpPublicKey {
+ /**
+ * @param key to perform sanity checks on
+ * @return version number of key. Supported versions are:
+ * 1: 2048-bit RSA key with e=3 and SHA-1 hash
+ * 2: 2048-bit RSA key with e=65537 and SHA-1 hash
+ * 3: 2048-bit RSA key with e=3 and SHA-256 hash
+ * 4: 2048-bit RSA key with e=65537 and SHA-256 hash
+ * @throws Exception if the key has the wrong size or public exponent
+ */
+ static int checkRSA(RSAPublicKey key, boolean useSHA256) throws Exception {
+ BigInteger pubexp = key.getPublicExponent();
+ BigInteger modulus = key.getModulus();
+ int version;
+
+ if (pubexp.equals(BigInteger.valueOf(3))) {
+ version = useSHA256 ? 3 : 1;
+ } else if (pubexp.equals(BigInteger.valueOf(65537))) {
+ version = useSHA256 ? 4 : 2;
+ } else {
+ throw new Exception("Public exponent should be 3 or 65537 but is " +
+ pubexp.toString(10) + ".");
+ }
+
+ if (modulus.bitLength() != 2048) {
+ throw new Exception("Modulus should be 2048 bits long but is " +
+ modulus.bitLength() + " bits.");
+ }
+
+ return version;
+ }
+
+ /**
+ * @param key to perform sanity checks on
+ * @return version number of key. Supported versions are:
+ * 5: 256-bit EC key with curve NIST P-256
+ * @throws Exception if the key has the wrong size or public exponent
+ */
+ static int checkEC(ECPublicKey key) throws Exception {
+ if (key.getParams().getCurve().getField().getFieldSize() != 256) {
+ throw new Exception("Curve must be NIST P-256");
+ }
+
+ return 5;
+ }
+
+ /**
+ * Perform sanity check on public key.
+ */
+ static int check(PublicKey key, boolean useSHA256) throws Exception {
+ if (key instanceof RSAPublicKey) {
+ return checkRSA((RSAPublicKey) key, useSHA256);
+ } else if (key instanceof ECPublicKey) {
+ if (!useSHA256) {
+ throw new Exception("Must use SHA-256 with EC keys!");
+ }
+ return checkEC((ECPublicKey) key);
+ } else {
+ throw new Exception("Unsupported key class: " + key.getClass().getName());
+ }
+ }
+
+ /**
+ * @param key to output
+ * @return a String representing this public key. If the key is a
+ * version 1 key, the string will be a C initializer; this is
+ * not true for newer key versions.
+ */
+ static String printRSA(RSAPublicKey key, boolean useSHA256) throws Exception {
+ int version = check(key, useSHA256);
+
+ BigInteger N = key.getModulus();
+
+ StringBuilder result = new StringBuilder();
+
+ int nwords = N.bitLength() / 32; // # of 32 bit integers in modulus
+
+ if (version > 1) {
+ result.append("v");
+ result.append(Integer.toString(version));
+ result.append(" ");
+ }
+
+ result.append("{");
+ result.append(nwords);
+
+ BigInteger B = BigInteger.valueOf(0x100000000L); // 2^32
+ BigInteger N0inv = B.subtract(N.modInverse(B)); // -1 / N[0] mod 2^32
+
+ result.append(",0x");
+ result.append(N0inv.toString(16));
+
+ BigInteger R = BigInteger.valueOf(2).pow(N.bitLength());
+ BigInteger RR = R.multiply(R).mod(N); // 2^4096 mod N
+
+ // Write out modulus as little endian array of integers.
+ result.append(",{");
+ for (int i = 0; i < nwords; ++i) {
+ long n = N.mod(B).longValue();
+ result.append(n);
+
+ if (i != nwords - 1) {
+ result.append(",");
+ }
+
+ N = N.divide(B);
+ }
+ result.append("}");
+
+ // Write R^2 as little endian array of integers.
+ result.append(",{");
+ for (int i = 0; i < nwords; ++i) {
+ long rr = RR.mod(B).longValue();
+ result.append(rr);
+
+ if (i != nwords - 1) {
+ result.append(",");
+ }
+
+ RR = RR.divide(B);
+ }
+ result.append("}");
+
+ result.append("}");
+ return result.toString();
+ }
+
+ /**
+ * @param key to output
+ * @return a String representing this public key. If the key is a
+ * version 1 key, the string will be a C initializer; this is
+ * not true for newer key versions.
+ */
+ static String printEC(ECPublicKey key) throws Exception {
+ int version = checkEC(key);
+
+ StringBuilder result = new StringBuilder();
+
+ result.append("v");
+ result.append(Integer.toString(version));
+ result.append(" ");
+
+ BigInteger X = key.getW().getAffineX();
+ BigInteger Y = key.getW().getAffineY();
+ int nbytes = key.getParams().getCurve().getField().getFieldSize() / 8; // # of 32 bit integers in X coordinate
+
+ result.append("{");
+ result.append(nbytes);
+
+ BigInteger B = BigInteger.valueOf(0x100L); // 2^8
+
+ // Write out Y coordinate as array of characters.
+ result.append(",{");
+ for (int i = 0; i < nbytes; ++i) {
+ long n = X.mod(B).longValue();
+ result.append(n);
+
+ if (i != nbytes - 1) {
+ result.append(",");
+ }
+
+ X = X.divide(B);
+ }
+ result.append("}");
+
+ // Write out Y coordinate as array of characters.
+ result.append(",{");
+ for (int i = 0; i < nbytes; ++i) {
+ long n = Y.mod(B).longValue();
+ result.append(n);
+
+ if (i != nbytes - 1) {
+ result.append(",");
+ }
+
+ Y = Y.divide(B);
+ }
+ result.append("}");
+
+ result.append("}");
+ return result.toString();
+ }
+
+ static String print(PublicKey key, boolean useSHA256) throws Exception {
+ if (key instanceof RSAPublicKey) {
+ return printRSA((RSAPublicKey) key, useSHA256);
+ } else if (key instanceof ECPublicKey) {
+ return printEC((ECPublicKey) key);
+ } else {
+ throw new Exception("Unsupported key class: " + key.getClass().getName());
+ }
+ }
+
+ public static void main(String[] args) {
+ if (args.length < 1) {
+ System.err.println("Usage: DumpPublicKey certfile ... > source.c");
+ System.exit(1);
+ }
+ Security.addProvider(new BouncyCastleProvider());
+ try {
+ for (int i = 0; i < args.length; i++) {
+ FileInputStream input = new FileInputStream(args[i]);
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ X509Certificate cert = (X509Certificate) cf.generateCertificate(input);
+
+ boolean useSHA256 = false;
+ String sigAlg = cert.getSigAlgName();
+ if ("SHA1withRSA".equals(sigAlg) || "MD5withRSA".equals(sigAlg)) {
+ // SignApk has historically accepted "MD5withRSA"
+ // certificates, but treated them as "SHA1withRSA"
+ // anyway. Continue to do so for backwards
+ // compatibility.
+ useSHA256 = false;
+ } else if ("SHA256withRSA".equals(sigAlg) || "SHA256withECDSA".equals(sigAlg)) {
+ useSHA256 = true;
+ } else {
+ System.err.println(args[i] + ": unsupported signature algorithm \"" +
+ sigAlg + "\"");
+ System.exit(1);
+ }
+
+ PublicKey key = cert.getPublicKey();
+ check(key, useSHA256);
+ System.out.print(print(key, useSHA256));
+ System.out.println(i < args.length - 1 ? "," : "");
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ System.exit(1);
+ }
+ System.exit(0);
+ }
+}
diff --git a/libmincrypt/tools/DumpPublicKey.mf b/libmincrypt/tools/DumpPublicKey.mf
new file mode 100644
index 0000000..7bb3bc8
--- /dev/null
+++ b/libmincrypt/tools/DumpPublicKey.mf
@@ -0,0 +1 @@
+Main-Class: com.android.dumpkey.DumpPublicKey
diff --git a/libnativebridge/Android.mk b/libnativebridge/Android.mk
new file mode 100644
index 0000000..d20d44c
--- /dev/null
+++ b/libnativebridge/Android.mk
@@ -0,0 +1,58 @@
+LOCAL_PATH:= $(call my-dir)
+
+NATIVE_BRIDGE_COMMON_SRC_FILES := \
+ native_bridge.cc
+
+# Shared library for target
+# ========================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE:= libnativebridge
+
+LOCAL_SRC_FILES:= $(NATIVE_BRIDGE_COMMON_SRC_FILES)
+LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_CLANG := true
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CPPFLAGS := -std=gnu++11 -fvisibility=protected
+LOCAL_LDFLAGS := -ldl
+LOCAL_MULTILIB := both
+
+include $(BUILD_SHARED_LIBRARY)
+
+# Shared library for host
+# ========================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE:= libnativebridge
+
+LOCAL_SRC_FILES:= $(NATIVE_BRIDGE_COMMON_SRC_FILES)
+LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_CLANG := true
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CPPFLAGS := -std=gnu++11 -fvisibility=protected
+LOCAL_LDFLAGS := -ldl
+LOCAL_MULTILIB := both
+
+include $(BUILD_HOST_SHARED_LIBRARY)
+
+# Static library for host
+# ========================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE:= libnativebridge
+
+LOCAL_SRC_FILES:= $(NATIVE_BRIDGE_COMMON_SRC_FILES)
+LOCAL_STATIC_LIBRARIES := liblog
+LOCAL_CLANG := true
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CPPFLAGS := -std=gnu++11 -fvisibility=protected
+LOCAL_LDFLAGS := -ldl
+LOCAL_MULTILIB := both
+
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+
+include $(LOCAL_PATH)/tests/Android.mk
diff --git a/libnativebridge/native_bridge.cc b/libnativebridge/native_bridge.cc
new file mode 100644
index 0000000..a9671a9
--- /dev/null
+++ b/libnativebridge/native_bridge.cc
@@ -0,0 +1,523 @@
+/*
+ * Copyright (C) 2014 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 "nativebridge/native_bridge.h"
+
+#include <cstring>
+#include <cutils/log.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+
+namespace android {
+
+// Environment values required by the apps running with native bridge.
+struct NativeBridgeRuntimeValues {
+ const char* os_arch;
+ const char* cpu_abi;
+ const char* cpu_abi2;
+ const char* *supported_abis;
+ int32_t abi_count;
+};
+
+// The symbol name exposed by native-bridge with the type of NativeBridgeCallbacks.
+static constexpr const char* kNativeBridgeInterfaceSymbol = "NativeBridgeItf";
+
+enum class NativeBridgeState {
+ kNotSetup, // Initial state.
+ kOpened, // After successful dlopen.
+ kPreInitialized, // After successful pre-initialization.
+ kInitialized, // After successful initialization.
+ kClosed // Closed or errors.
+};
+
+static constexpr const char* kNotSetupString = "kNotSetup";
+static constexpr const char* kOpenedString = "kOpened";
+static constexpr const char* kPreInitializedString = "kPreInitialized";
+static constexpr const char* kInitializedString = "kInitialized";
+static constexpr const char* kClosedString = "kClosed";
+
+static const char* GetNativeBridgeStateString(NativeBridgeState state) {
+ switch (state) {
+ case NativeBridgeState::kNotSetup:
+ return kNotSetupString;
+
+ case NativeBridgeState::kOpened:
+ return kOpenedString;
+
+ case NativeBridgeState::kPreInitialized:
+ return kPreInitializedString;
+
+ case NativeBridgeState::kInitialized:
+ return kInitializedString;
+
+ case NativeBridgeState::kClosed:
+ return kClosedString;
+ }
+}
+
+// Current state of the native bridge.
+static NativeBridgeState state = NativeBridgeState::kNotSetup;
+
+// Whether we had an error at some point.
+static bool had_error = false;
+
+// Handle of the loaded library.
+static void* native_bridge_handle = nullptr;
+// Pointer to the callbacks. Available as soon as LoadNativeBridge succeeds, but only initialized
+// later.
+static const NativeBridgeCallbacks* callbacks = nullptr;
+// Callbacks provided by the environment to the bridge. Passed to LoadNativeBridge.
+static const NativeBridgeRuntimeCallbacks* runtime_callbacks = nullptr;
+
+// The app's code cache directory.
+static char* app_code_cache_dir = nullptr;
+
+// Code cache directory (relative to the application private directory)
+// Ideally we'd like to call into framework to retrieve this name. However that's considered an
+// implementation detail and will require either hacks or consistent refactorings. We compromise
+// and hard code the directory name again here.
+static constexpr const char* kCodeCacheDir = "code_cache";
+
+static constexpr uint32_t kLibNativeBridgeVersion = 2;
+
+// Characters allowed in a native bridge filename. The first character must
+// be in [a-zA-Z] (expected 'l' for "libx"). The rest must be in [a-zA-Z0-9._-].
+static bool CharacterAllowed(char c, bool first) {
+ if (first) {
+ return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z');
+ } else {
+ return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') ||
+ (c == '.') || (c == '_') || (c == '-');
+ }
+}
+
+static void ReleaseAppCodeCacheDir() {
+ if (app_code_cache_dir != nullptr) {
+ delete[] app_code_cache_dir;
+ app_code_cache_dir = nullptr;
+ }
+}
+
+// We only allow simple names for the library. It is supposed to be a file in
+// /system/lib or /vendor/lib. Only allow a small range of characters, that is
+// names consisting of [a-zA-Z0-9._-] and starting with [a-zA-Z].
+bool NativeBridgeNameAcceptable(const char* nb_library_filename) {
+ const char* ptr = nb_library_filename;
+ if (*ptr == 0) {
+ // Emptry string. Allowed, means no native bridge.
+ return true;
+ } else {
+ // First character must be [a-zA-Z].
+ if (!CharacterAllowed(*ptr, true)) {
+ // Found an invalid fist character, don't accept.
+ ALOGE("Native bridge library %s has been rejected for first character %c",
+ nb_library_filename,
+ *ptr);
+ return false;
+ } else {
+ // For the rest, be more liberal.
+ ptr++;
+ while (*ptr != 0) {
+ if (!CharacterAllowed(*ptr, false)) {
+ // Found an invalid character, don't accept.
+ ALOGE("Native bridge library %s has been rejected for %c", nb_library_filename, *ptr);
+ return false;
+ }
+ ptr++;
+ }
+ }
+ return true;
+ }
+}
+
+static bool VersionCheck(const NativeBridgeCallbacks* cb) {
+ // Libnativebridge is now designed to be forward-compatible. So only "0" is an unsupported
+ // version.
+ if (cb == nullptr || cb->version == 0) {
+ return false;
+ }
+
+ // If this is a v2+ bridge, it may not be forwards- or backwards-compatible. Check.
+ if (cb->version >= 2) {
+ if (!callbacks->isCompatibleWith(kLibNativeBridgeVersion)) {
+ // TODO: Scan which version is supported, and fall back to handle it.
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static void CloseNativeBridge(bool with_error) {
+ state = NativeBridgeState::kClosed;
+ had_error |= with_error;
+ ReleaseAppCodeCacheDir();
+}
+
+bool LoadNativeBridge(const char* nb_library_filename,
+ const NativeBridgeRuntimeCallbacks* runtime_cbs) {
+ // We expect only one place that calls LoadNativeBridge: Runtime::Init. At that point we are not
+ // multi-threaded, so we do not need locking here.
+
+ if (state != NativeBridgeState::kNotSetup) {
+ // Setup has been called before. Ignore this call.
+ if (nb_library_filename != nullptr) { // Avoids some log-spam for dalvikvm.
+ ALOGW("Called LoadNativeBridge for an already set up native bridge. State is %s.",
+ GetNativeBridgeStateString(state));
+ }
+ // Note: counts as an error, even though the bridge may be functional.
+ had_error = true;
+ return false;
+ }
+
+ if (nb_library_filename == nullptr || *nb_library_filename == 0) {
+ CloseNativeBridge(false);
+ return false;
+ } else {
+ if (!NativeBridgeNameAcceptable(nb_library_filename)) {
+ CloseNativeBridge(true);
+ } else {
+ // Try to open the library.
+ void* handle = dlopen(nb_library_filename, RTLD_LAZY);
+ if (handle != nullptr) {
+ callbacks = reinterpret_cast<NativeBridgeCallbacks*>(dlsym(handle,
+ kNativeBridgeInterfaceSymbol));
+ if (callbacks != nullptr) {
+ if (VersionCheck(callbacks)) {
+ // Store the handle for later.
+ native_bridge_handle = handle;
+ } else {
+ callbacks = nullptr;
+ dlclose(handle);
+ ALOGW("Unsupported native bridge interface.");
+ }
+ } else {
+ dlclose(handle);
+ }
+ }
+
+ // Two failure conditions: could not find library (dlopen failed), or could not find native
+ // bridge interface (dlsym failed). Both are an error and close the native bridge.
+ if (callbacks == nullptr) {
+ CloseNativeBridge(true);
+ } else {
+ runtime_callbacks = runtime_cbs;
+ state = NativeBridgeState::kOpened;
+ }
+ }
+ return state == NativeBridgeState::kOpened;
+ }
+}
+
+#if defined(__arm__)
+static const char* kRuntimeISA = "arm";
+#elif defined(__aarch64__)
+static const char* kRuntimeISA = "arm64";
+#elif defined(__mips__)
+static const char* kRuntimeISA = "mips";
+#elif defined(__i386__)
+static const char* kRuntimeISA = "x86";
+#elif defined(__x86_64__)
+static const char* kRuntimeISA = "x86_64";
+#else
+static const char* kRuntimeISA = "unknown";
+#endif
+
+
+bool NeedsNativeBridge(const char* instruction_set) {
+ if (instruction_set == nullptr) {
+ ALOGE("Null instruction set in NeedsNativeBridge.");
+ return false;
+ }
+ return strncmp(instruction_set, kRuntimeISA, strlen(kRuntimeISA) + 1) != 0;
+}
+
+#ifdef __APPLE__
+template<typename T> void UNUSED(const T&) {}
+#endif
+
+bool PreInitializeNativeBridge(const char* app_data_dir_in, const char* instruction_set) {
+ if (state != NativeBridgeState::kOpened) {
+ ALOGE("Invalid state: native bridge is expected to be opened.");
+ CloseNativeBridge(true);
+ return false;
+ }
+
+ if (app_data_dir_in == nullptr) {
+ ALOGE("Application private directory cannot be null.");
+ CloseNativeBridge(true);
+ return false;
+ }
+
+ // Create the path to the application code cache directory.
+ // The memory will be release after Initialization or when the native bridge is closed.
+ const size_t len = strlen(app_data_dir_in) + strlen(kCodeCacheDir) + 2; // '\0' + '/'
+ app_code_cache_dir = new char[len];
+ snprintf(app_code_cache_dir, len, "%s/%s", app_data_dir_in, kCodeCacheDir);
+
+ // Bind-mount /system/lib{,64}/<isa>/cpuinfo to /proc/cpuinfo.
+ // Failure is not fatal and will keep the native bridge in kPreInitialized.
+ state = NativeBridgeState::kPreInitialized;
+
+#ifndef __APPLE__
+ if (instruction_set == nullptr) {
+ return true;
+ }
+ size_t isa_len = strlen(instruction_set);
+ if (isa_len > 10) {
+ // 10 is a loose upper bound on the currently known instruction sets (a tight bound is 7 for
+ // x86_64 [including the trailing \0]). This is so we don't have to change here if there will
+ // be another instruction set in the future.
+ ALOGW("Instruction set %s is malformed, must be less than or equal to 10 characters.",
+ instruction_set);
+ return true;
+ }
+
+ // If the file does not exist, the mount command will fail,
+ // so we save the extra file existence check.
+ char cpuinfo_path[1024];
+
+#ifdef HAVE_ANDROID_OS
+ snprintf(cpuinfo_path, sizeof(cpuinfo_path), "/system/lib"
+#ifdef __LP64__
+ "64"
+#endif // __LP64__
+ "/%s/cpuinfo", instruction_set);
+#else // !HAVE_ANDROID_OS
+ // To be able to test on the host, we hardwire a relative path.
+ snprintf(cpuinfo_path, sizeof(cpuinfo_path), "./cpuinfo");
+#endif
+
+ // Bind-mount.
+ if (TEMP_FAILURE_RETRY(mount(cpuinfo_path, // Source.
+ "/proc/cpuinfo", // Target.
+ nullptr, // FS type.
+ MS_BIND, // Mount flags: bind mount.
+ nullptr)) == -1) { // "Data."
+ ALOGW("Failed to bind-mount %s as /proc/cpuinfo: %s", cpuinfo_path, strerror(errno));
+ }
+#else // __APPLE__
+ UNUSED(instruction_set);
+ ALOGW("Mac OS does not support bind-mounting. Host simulation of native bridge impossible.");
+#endif
+
+ return true;
+}
+
+static void SetCpuAbi(JNIEnv* env, jclass build_class, const char* field, const char* value) {
+ if (value != nullptr) {
+ jfieldID field_id = env->GetStaticFieldID(build_class, field, "Ljava/lang/String;");
+ if (field_id == nullptr) {
+ env->ExceptionClear();
+ ALOGW("Could not find %s field.", field);
+ return;
+ }
+
+ jstring str = env->NewStringUTF(value);
+ if (str == nullptr) {
+ env->ExceptionClear();
+ ALOGW("Could not create string %s.", value);
+ return;
+ }
+
+ env->SetStaticObjectField(build_class, field_id, str);
+ }
+}
+
+// Set up the environment for the bridged app.
+static void SetupEnvironment(const NativeBridgeCallbacks* callbacks, JNIEnv* env, const char* isa) {
+ // Need a JNIEnv* to do anything.
+ if (env == nullptr) {
+ ALOGW("No JNIEnv* to set up app environment.");
+ return;
+ }
+
+ // Query the bridge for environment values.
+ const struct NativeBridgeRuntimeValues* env_values = callbacks->getAppEnv(isa);
+ if (env_values == nullptr) {
+ return;
+ }
+
+ // Keep the JNIEnv clean.
+ jint success = env->PushLocalFrame(16); // That should be small and large enough.
+ if (success < 0) {
+ // Out of memory, really borked.
+ ALOGW("Out of memory while setting up app environment.");
+ env->ExceptionClear();
+ return;
+ }
+
+ // Reset CPU_ABI & CPU_ABI2 to values required by the apps running with native bridge.
+ if (env_values->cpu_abi != nullptr || env_values->cpu_abi2 != nullptr ||
+ env_values->abi_count >= 0) {
+ jclass bclass_id = env->FindClass("android/os/Build");
+ if (bclass_id != nullptr) {
+ SetCpuAbi(env, bclass_id, "CPU_ABI", env_values->cpu_abi);
+ SetCpuAbi(env, bclass_id, "CPU_ABI2", env_values->cpu_abi2);
+ } else {
+ // For example in a host test environment.
+ env->ExceptionClear();
+ ALOGW("Could not find Build class.");
+ }
+ }
+
+ if (env_values->os_arch != nullptr) {
+ jclass sclass_id = env->FindClass("java/lang/System");
+ if (sclass_id != nullptr) {
+ jmethodID set_prop_id = env->GetStaticMethodID(sclass_id, "setUnchangeableSystemProperty",
+ "(Ljava/lang/String;Ljava/lang/String;)V");
+ if (set_prop_id != nullptr) {
+ // Init os.arch to the value reqired by the apps running with native bridge.
+ env->CallStaticVoidMethod(sclass_id, set_prop_id, env->NewStringUTF("os.arch"),
+ env->NewStringUTF(env_values->os_arch));
+ } else {
+ env->ExceptionClear();
+ ALOGW("Could not find System#setUnchangeableSystemProperty.");
+ }
+ } else {
+ env->ExceptionClear();
+ ALOGW("Could not find System class.");
+ }
+ }
+
+ // Make it pristine again.
+ env->PopLocalFrame(nullptr);
+}
+
+bool InitializeNativeBridge(JNIEnv* env, const char* instruction_set) {
+ // We expect only one place that calls InitializeNativeBridge: Runtime::DidForkFromZygote. At that
+ // point we are not multi-threaded, so we do not need locking here.
+
+ if (state == NativeBridgeState::kPreInitialized) {
+ // Check for code cache: if it doesn't exist try to create it.
+ struct stat st;
+ if (stat(app_code_cache_dir, &st) == -1) {
+ if (errno == ENOENT) {
+ if (mkdir(app_code_cache_dir, S_IRWXU | S_IRWXG | S_IXOTH) == -1) {
+ ALOGW("Cannot create code cache directory %s: %s.", app_code_cache_dir, strerror(errno));
+ ReleaseAppCodeCacheDir();
+ }
+ } else {
+ ALOGW("Cannot stat code cache directory %s: %s.", app_code_cache_dir, strerror(errno));
+ ReleaseAppCodeCacheDir();
+ }
+ } else if (!S_ISDIR(st.st_mode)) {
+ ALOGW("Code cache is not a directory %s.", app_code_cache_dir);
+ ReleaseAppCodeCacheDir();
+ }
+
+ // If we're still PreInitialized (dind't fail the code cache checks) try to initialize.
+ if (state == NativeBridgeState::kPreInitialized) {
+ if (callbacks->initialize(runtime_callbacks, app_code_cache_dir, instruction_set)) {
+ SetupEnvironment(callbacks, env, instruction_set);
+ state = NativeBridgeState::kInitialized;
+ // We no longer need the code cache path, release the memory.
+ ReleaseAppCodeCacheDir();
+ } else {
+ // Unload the library.
+ dlclose(native_bridge_handle);
+ CloseNativeBridge(true);
+ }
+ }
+ } else {
+ CloseNativeBridge(true);
+ }
+
+ return state == NativeBridgeState::kInitialized;
+}
+
+void UnloadNativeBridge() {
+ // We expect only one place that calls UnloadNativeBridge: Runtime::DidForkFromZygote. At that
+ // point we are not multi-threaded, so we do not need locking here.
+
+ switch(state) {
+ case NativeBridgeState::kOpened:
+ case NativeBridgeState::kPreInitialized:
+ case NativeBridgeState::kInitialized:
+ // Unload.
+ dlclose(native_bridge_handle);
+ CloseNativeBridge(false);
+ break;
+
+ case NativeBridgeState::kNotSetup:
+ // Not even set up. Error.
+ CloseNativeBridge(true);
+ break;
+
+ case NativeBridgeState::kClosed:
+ // Ignore.
+ break;
+ }
+}
+
+bool NativeBridgeError() {
+ return had_error;
+}
+
+bool NativeBridgeAvailable() {
+ return state == NativeBridgeState::kOpened
+ || state == NativeBridgeState::kPreInitialized
+ || state == NativeBridgeState::kInitialized;
+}
+
+bool NativeBridgeInitialized() {
+ // Calls of this are supposed to happen in a state where the native bridge is stable, i.e., after
+ // Runtime::DidForkFromZygote. In that case we do not need a lock.
+ return state == NativeBridgeState::kInitialized;
+}
+
+void* NativeBridgeLoadLibrary(const char* libpath, int flag) {
+ if (NativeBridgeInitialized()) {
+ return callbacks->loadLibrary(libpath, flag);
+ }
+ return nullptr;
+}
+
+void* NativeBridgeGetTrampoline(void* handle, const char* name, const char* shorty,
+ uint32_t len) {
+ if (NativeBridgeInitialized()) {
+ return callbacks->getTrampoline(handle, name, shorty, len);
+ }
+ return nullptr;
+}
+
+bool NativeBridgeIsSupported(const char* libpath) {
+ if (NativeBridgeInitialized()) {
+ return callbacks->isSupported(libpath);
+ }
+ return false;
+}
+
+uint32_t NativeBridgeGetVersion() {
+ if (NativeBridgeAvailable()) {
+ return callbacks->version;
+ }
+ return 0;
+}
+
+NativeBridgeSignalHandlerFn NativeBridgeGetSignalHandler(int signal) {
+ if (NativeBridgeInitialized() && callbacks->version >= 2) {
+ return callbacks->getSignalHandler(signal);
+ }
+ return nullptr;
+}
+
+}; // namespace android
diff --git a/libnativebridge/tests/Android.mk b/libnativebridge/tests/Android.mk
new file mode 100644
index 0000000..7265939
--- /dev/null
+++ b/libnativebridge/tests/Android.mk
@@ -0,0 +1,49 @@
+# Build the unit tests.
+LOCAL_PATH := $(call my-dir)
+
+include $(LOCAL_PATH)/Android.nativebridge-dummy.mk
+
+include $(CLEAR_VARS)
+
+# Build the unit tests.
+test_src_files := \
+ CodeCacheCreate_test.cpp \
+ CodeCacheExists_test.cpp \
+ CodeCacheStatFail_test.cpp \
+ CompleteFlow_test.cpp \
+ InvalidCharsNativeBridge_test.cpp \
+ NativeBridge2Signal_test.cpp \
+ NativeBridgeVersion_test.cpp \
+ NeedsNativeBridge_test.cpp \
+ PreInitializeNativeBridge_test.cpp \
+ PreInitializeNativeBridgeFail1_test.cpp \
+ PreInitializeNativeBridgeFail2_test.cpp \
+ ReSetupNativeBridge_test.cpp \
+ UnavailableNativeBridge_test.cpp \
+ ValidNameNativeBridge_test.cpp
+
+
+shared_libraries := \
+ liblog \
+ libnativebridge \
+ libnativebridge-dummy
+
+$(foreach file,$(test_src_files), \
+ $(eval include $(CLEAR_VARS)) \
+ $(eval LOCAL_CLANG := true) \
+ $(eval LOCAL_CPPFLAGS := -std=gnu++11) \
+ $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
+ $(eval LOCAL_SRC_FILES := $(file)) \
+ $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
+ $(eval include $(BUILD_NATIVE_TEST)) \
+)
+
+$(foreach file,$(test_src_files), \
+ $(eval include $(CLEAR_VARS)) \
+ $(eval LOCAL_CLANG := true) \
+ $(eval LOCAL_CPPFLAGS := -std=gnu++11) \
+ $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
+ $(eval LOCAL_SRC_FILES := $(file)) \
+ $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
+ $(eval include $(BUILD_HOST_NATIVE_TEST)) \
+)
diff --git a/libnativebridge/tests/Android.nativebridge-dummy.mk b/libnativebridge/tests/Android.nativebridge-dummy.mk
new file mode 100644
index 0000000..2efc176
--- /dev/null
+++ b/libnativebridge/tests/Android.nativebridge-dummy.mk
@@ -0,0 +1,70 @@
+LOCAL_PATH:= $(call my-dir)
+
+NATIVE_BRIDGE_COMMON_SRC_FILES := \
+ DummyNativeBridge.cpp
+
+# Shared library for target
+# ========================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE:= libnativebridge-dummy
+
+LOCAL_SRC_FILES:= $(NATIVE_BRIDGE_COMMON_SRC_FILES)
+LOCAL_CLANG := true
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CPPFLAGS := -std=gnu++11 -fvisibility=protected
+LOCAL_LDFLAGS := -ldl
+LOCAL_MULTILIB := both
+
+include $(BUILD_SHARED_LIBRARY)
+
+# Shared library for host
+# ========================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE:= libnativebridge-dummy
+
+LOCAL_SRC_FILES:= $(NATIVE_BRIDGE_COMMON_SRC_FILES)
+LOCAL_CLANG := true
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CPPFLAGS := -std=gnu++11 -fvisibility=protected
+LOCAL_LDFLAGS := -ldl
+LOCAL_MULTILIB := both
+
+include $(BUILD_HOST_SHARED_LIBRARY)
+
+
+# v2.
+
+NATIVE_BRIDGE2_COMMON_SRC_FILES := \
+ DummyNativeBridge2.cpp
+
+# Shared library for target
+# ========================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE:= libnativebridge2-dummy
+
+LOCAL_SRC_FILES:= $(NATIVE_BRIDGE2_COMMON_SRC_FILES)
+LOCAL_CLANG := true
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CPPFLAGS := -std=gnu++11 -fvisibility=protected
+LOCAL_LDFLAGS := -ldl
+LOCAL_MULTILIB := both
+
+include $(BUILD_SHARED_LIBRARY)
+
+# Shared library for host
+# ========================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE:= libnativebridge2-dummy
+
+LOCAL_SRC_FILES:= $(NATIVE_BRIDGE2_COMMON_SRC_FILES)
+LOCAL_CLANG := true
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CPPFLAGS := -std=gnu++11 -fvisibility=protected
+LOCAL_LDFLAGS := -ldl
+LOCAL_MULTILIB := both
+
+include $(BUILD_HOST_SHARED_LIBRARY)
diff --git a/libnativebridge/tests/CodeCacheCreate_test.cpp b/libnativebridge/tests/CodeCacheCreate_test.cpp
new file mode 100644
index 0000000..58270c4
--- /dev/null
+++ b/libnativebridge/tests/CodeCacheCreate_test.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014 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 "NativeBridgeTest.h"
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+namespace android {
+
+// Tests that the bridge initialization creates the code_cache if it doesn't
+// exists.
+TEST_F(NativeBridgeTest, CodeCacheCreate) {
+ // Make sure that code_cache does not exists
+ struct stat st;
+ ASSERT_EQ(-1, stat(kCodeCache, &st));
+ ASSERT_EQ(ENOENT, errno);
+
+ // Init
+ ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr));
+ ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
+ ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
+ ASSERT_TRUE(NativeBridgeAvailable());
+ ASSERT_FALSE(NativeBridgeError());
+
+ // Check that code_cache was created
+ ASSERT_EQ(0, stat(kCodeCache, &st));
+ ASSERT_TRUE(S_ISDIR(st.st_mode));
+
+ // Clean up
+ UnloadNativeBridge();
+ ASSERT_EQ(0, rmdir(kCodeCache));
+
+ ASSERT_FALSE(NativeBridgeError());
+}
+
+} // namespace android
diff --git a/libnativebridge/tests/CodeCacheExists_test.cpp b/libnativebridge/tests/CodeCacheExists_test.cpp
new file mode 100644
index 0000000..8ba0158
--- /dev/null
+++ b/libnativebridge/tests/CodeCacheExists_test.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014 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 "NativeBridgeTest.h"
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+namespace android {
+
+// Tests that the bridge is initialized without errors if the code_cache already
+// exists.
+TEST_F(NativeBridgeTest, CodeCacheExists) {
+ // Make sure that code_cache does not exists
+ struct stat st;
+ ASSERT_EQ(-1, stat(kCodeCache, &st));
+ ASSERT_EQ(ENOENT, errno);
+
+ // Create the code_cache
+ ASSERT_EQ(0, mkdir(kCodeCache, S_IRWXU | S_IRWXG | S_IXOTH));
+
+ // Init
+ ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr));
+ ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
+ ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
+ ASSERT_TRUE(NativeBridgeAvailable());
+ ASSERT_FALSE(NativeBridgeError());
+
+ // Check that the code cache is still there
+ ASSERT_EQ(0, stat(kCodeCache, &st));
+ ASSERT_TRUE(S_ISDIR(st.st_mode));
+
+ // Clean up
+ UnloadNativeBridge();
+ ASSERT_EQ(0, rmdir(kCodeCache));
+
+ ASSERT_FALSE(NativeBridgeError());
+}
+
+} // namespace android
diff --git a/libnativebridge/tests/CodeCacheStatFail_test.cpp b/libnativebridge/tests/CodeCacheStatFail_test.cpp
new file mode 100644
index 0000000..4ea519e
--- /dev/null
+++ b/libnativebridge/tests/CodeCacheStatFail_test.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014 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 "NativeBridgeTest.h"
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+namespace android {
+
+// Tests that the bridge is initialized without errors if the code_cache is
+// existed as a file.
+TEST_F(NativeBridgeTest, CodeCacheStatFail) {
+ int fd = creat(kCodeCache, O_RDWR);
+ ASSERT_NE(-1, fd);
+ close(fd);
+
+ struct stat st;
+ ASSERT_EQ(-1, stat(kCodeCacheStatFail, &st));
+ ASSERT_EQ(ENOTDIR, errno);
+
+ // Init
+ ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr));
+ ASSERT_TRUE(PreInitializeNativeBridge(kCodeCacheStatFail, "isa"));
+ ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
+ ASSERT_TRUE(NativeBridgeAvailable());
+ ASSERT_FALSE(NativeBridgeError());
+
+ // Clean up
+ UnloadNativeBridge();
+
+ ASSERT_FALSE(NativeBridgeError());
+ unlink(kCodeCache);
+}
+
+} // namespace android
diff --git a/libnativebridge/tests/CompleteFlow_test.cpp b/libnativebridge/tests/CompleteFlow_test.cpp
new file mode 100644
index 0000000..b033792
--- /dev/null
+++ b/libnativebridge/tests/CompleteFlow_test.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2014 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 "NativeBridgeTest.h"
+
+#include <unistd.h>
+
+namespace android {
+
+TEST_F(NativeBridgeTest, CompleteFlow) {
+ // Init
+ ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr));
+ ASSERT_TRUE(NativeBridgeAvailable());
+ ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
+ ASSERT_TRUE(NativeBridgeAvailable());
+ ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
+ ASSERT_TRUE(NativeBridgeAvailable());
+
+ // Basic calls to check that nothing crashes
+ ASSERT_FALSE(NativeBridgeIsSupported(nullptr));
+ ASSERT_EQ(nullptr, NativeBridgeLoadLibrary(nullptr, 0));
+ ASSERT_EQ(nullptr, NativeBridgeGetTrampoline(nullptr, nullptr, nullptr, 0));
+
+ // Unload
+ UnloadNativeBridge();
+
+ ASSERT_FALSE(NativeBridgeAvailable());
+ ASSERT_FALSE(NativeBridgeError());
+
+ // Clean-up code_cache
+ ASSERT_EQ(0, rmdir(kCodeCache));
+}
+
+} // namespace android
diff --git a/libnativebridge/tests/DummyNativeBridge.cpp b/libnativebridge/tests/DummyNativeBridge.cpp
new file mode 100644
index 0000000..b9894f6
--- /dev/null
+++ b/libnativebridge/tests/DummyNativeBridge.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+// A dummy implementation of the native-bridge interface.
+
+#include "nativebridge/native_bridge.h"
+
+// NativeBridgeCallbacks implementations
+extern "C" bool native_bridge_initialize(const android::NativeBridgeRuntimeCallbacks* /* art_cbs */,
+ const char* /* app_code_cache_dir */,
+ const char* /* isa */) {
+ return true;
+}
+
+extern "C" void* native_bridge_loadLibrary(const char* /* libpath */, int /* flag */) {
+ return nullptr;
+}
+
+extern "C" void* native_bridge_getTrampoline(void* /* handle */, const char* /* name */,
+ const char* /* shorty */, uint32_t /* len */) {
+ return nullptr;
+}
+
+extern "C" bool native_bridge_isSupported(const char* /* libpath */) {
+ return false;
+}
+
+extern "C" const struct android::NativeBridgeRuntimeValues* native_bridge_getAppEnv(
+ const char* /* abi */) {
+ return nullptr;
+}
+
+android::NativeBridgeCallbacks NativeBridgeItf {
+ .version = 1,
+ .initialize = &native_bridge_initialize,
+ .loadLibrary = &native_bridge_loadLibrary,
+ .getTrampoline = &native_bridge_getTrampoline,
+ .isSupported = &native_bridge_isSupported,
+ .getAppEnv = &native_bridge_getAppEnv
+};
diff --git a/libnativebridge/tests/DummyNativeBridge2.cpp b/libnativebridge/tests/DummyNativeBridge2.cpp
new file mode 100644
index 0000000..6920c74
--- /dev/null
+++ b/libnativebridge/tests/DummyNativeBridge2.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+// A dummy implementation of the native-bridge interface.
+
+#include "nativebridge/native_bridge.h"
+
+#include <signal.h>
+
+// NativeBridgeCallbacks implementations
+extern "C" bool native_bridge2_initialize(const android::NativeBridgeRuntimeCallbacks* /* art_cbs */,
+ const char* /* app_code_cache_dir */,
+ const char* /* isa */) {
+ return true;
+}
+
+extern "C" void* native_bridge2_loadLibrary(const char* /* libpath */, int /* flag */) {
+ return nullptr;
+}
+
+extern "C" void* native_bridge2_getTrampoline(void* /* handle */, const char* /* name */,
+ const char* /* shorty */, uint32_t /* len */) {
+ return nullptr;
+}
+
+extern "C" bool native_bridge2_isSupported(const char* /* libpath */) {
+ return false;
+}
+
+extern "C" const struct android::NativeBridgeRuntimeValues* native_bridge2_getAppEnv(
+ const char* /* abi */) {
+ return nullptr;
+}
+
+extern "C" bool native_bridge2_is_compatible_compatible_with(uint32_t version) {
+ // For testing, allow 1 and 2, but disallow 3+.
+ return version <= 2;
+}
+
+static bool native_bridge2_dummy_signal_handler(int, siginfo_t*, void*) {
+ // TODO: Implement something here. We'd either have to have a death test with a log here, or
+ // we'd have to be able to resume after the faulting instruction...
+ return true;
+}
+
+extern "C" android::NativeBridgeSignalHandlerFn native_bridge2_get_signal_handler(int signal) {
+ if (signal == SIGSEGV) {
+ return &native_bridge2_dummy_signal_handler;
+ }
+ return nullptr;
+}
+
+android::NativeBridgeCallbacks NativeBridgeItf {
+ .version = 2,
+ .initialize = &native_bridge2_initialize,
+ .loadLibrary = &native_bridge2_loadLibrary,
+ .getTrampoline = &native_bridge2_getTrampoline,
+ .isSupported = &native_bridge2_isSupported,
+ .getAppEnv = &native_bridge2_getAppEnv,
+ .isCompatibleWith = &native_bridge2_is_compatible_compatible_with,
+ .getSignalHandler = &native_bridge2_get_signal_handler
+};
+
diff --git a/libnativebridge/tests/InvalidCharsNativeBridge_test.cpp b/libnativebridge/tests/InvalidCharsNativeBridge_test.cpp
new file mode 100644
index 0000000..8f7973d
--- /dev/null
+++ b/libnativebridge/tests/InvalidCharsNativeBridge_test.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014 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 "NativeBridgeTest.h"
+
+namespace android {
+
+static const char* kTestName = "../librandom$@-bridge_not.existing.so";
+
+TEST_F(NativeBridgeTest, InvalidChars) {
+ // Do one test actually calling setup.
+ EXPECT_EQ(false, NativeBridgeError());
+ LoadNativeBridge(kTestName, nullptr);
+ // This should lead to an error for invalid characters.
+ EXPECT_EQ(true, NativeBridgeError());
+
+ // Further tests need to use NativeBridgeNameAcceptable, as the error
+ // state can't be changed back.
+ EXPECT_EQ(false, NativeBridgeNameAcceptable("."));
+ EXPECT_EQ(false, NativeBridgeNameAcceptable(".."));
+ EXPECT_EQ(false, NativeBridgeNameAcceptable("_"));
+ EXPECT_EQ(false, NativeBridgeNameAcceptable("-"));
+ EXPECT_EQ(false, NativeBridgeNameAcceptable("lib@.so"));
+ EXPECT_EQ(false, NativeBridgeNameAcceptable("lib$.so"));
+}
+
+} // namespace android
diff --git a/libnativebridge/tests/NativeBridge2Signal_test.cpp b/libnativebridge/tests/NativeBridge2Signal_test.cpp
new file mode 100644
index 0000000..44e45e3
--- /dev/null
+++ b/libnativebridge/tests/NativeBridge2Signal_test.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2014 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 "NativeBridgeTest.h"
+
+#include <signal.h>
+#include <unistd.h>
+
+namespace android {
+
+constexpr const char* kNativeBridgeLibrary2 = "libnativebridge2-dummy.so";
+
+TEST_F(NativeBridgeTest, V2_Signal) {
+ // Init
+ ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary2, nullptr));
+ ASSERT_TRUE(NativeBridgeAvailable());
+ ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
+ ASSERT_TRUE(NativeBridgeAvailable());
+ ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
+ ASSERT_TRUE(NativeBridgeAvailable());
+
+ ASSERT_EQ(2U, NativeBridgeGetVersion());
+ ASSERT_NE(nullptr, NativeBridgeGetSignalHandler(SIGSEGV));
+
+ // Clean-up code_cache
+ ASSERT_EQ(0, rmdir(kCodeCache));
+}
+
+} // namespace android
diff --git a/libnativebridge/tests/NativeBridgeTest.h b/libnativebridge/tests/NativeBridgeTest.h
new file mode 100644
index 0000000..d489420
--- /dev/null
+++ b/libnativebridge/tests/NativeBridgeTest.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef NATIVE_BRIDGE_TEST_H_
+#define NATIVE_BRIDGE_TEST_H_
+
+#define LOG_TAG "NativeBridge_test"
+
+#include <nativebridge/native_bridge.h>
+#include <gtest/gtest.h>
+
+constexpr const char* kNativeBridgeLibrary = "libnativebridge-dummy.so";
+constexpr const char* kCodeCache = "./code_cache";
+constexpr const char* kCodeCacheStatFail = "./code_cache/temp";
+
+namespace android {
+
+class NativeBridgeTest : public testing::Test {
+};
+
+}; // namespace android
+
+#endif // NATIVE_BRIDGE_H_
+
diff --git a/libnativebridge/tests/NativeBridgeVersion_test.cpp b/libnativebridge/tests/NativeBridgeVersion_test.cpp
new file mode 100644
index 0000000..d3f9a80
--- /dev/null
+++ b/libnativebridge/tests/NativeBridgeVersion_test.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "NativeBridgeTest.h"
+
+#include <unistd.h>
+
+namespace android {
+
+TEST_F(NativeBridgeTest, Version) {
+ // When a bridge isn't loaded, we expect 0.
+ EXPECT_EQ(NativeBridgeGetVersion(), 0U);
+
+ // After our dummy bridge has been loaded, we expect 1.
+ ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr));
+ EXPECT_EQ(NativeBridgeGetVersion(), 1U);
+
+ // Unload
+ UnloadNativeBridge();
+
+ // Version information is gone.
+ EXPECT_EQ(NativeBridgeGetVersion(), 0U);
+}
+
+} // namespace android
diff --git a/libnativebridge/tests/NeedsNativeBridge_test.cpp b/libnativebridge/tests/NeedsNativeBridge_test.cpp
new file mode 100644
index 0000000..e1c0876
--- /dev/null
+++ b/libnativebridge/tests/NeedsNativeBridge_test.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014 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 "NativeBridgeTest.h"
+
+namespace android {
+
+static const char* kISAs[] = { "arm", "arm64", "mips", "x86", "x86_64", "random", "64arm", "64_x86",
+ "64_x86_64", "", "reallylongstringabcd", nullptr };
+
+#if defined(__arm__)
+static const char* kRuntimeISA = "arm";
+#elif defined(__aarch64__)
+static const char* kRuntimeISA = "arm64";
+#elif defined(__mips__)
+static const char* kRuntimeISA = "mips";
+#elif defined(__i386__)
+static const char* kRuntimeISA = "x86";
+#elif defined(__x86_64__)
+static const char* kRuntimeISA = "x86_64";
+#else
+static const char* kRuntimeISA = "unknown";
+#endif
+
+TEST_F(NativeBridgeTest, NeedsNativeBridge) {
+ EXPECT_EQ(false, NeedsNativeBridge(kRuntimeISA));
+
+ const size_t kISACount = sizeof(kISAs)/sizeof(kISAs[0]);
+ for (size_t i = 0; i < kISACount; i++) {
+ EXPECT_EQ(kISAs[i] == nullptr ? false : strcmp(kISAs[i], kRuntimeISA) != 0,
+ NeedsNativeBridge(kISAs[i]));
+ }
+}
+
+} // namespace android
diff --git a/libnativebridge/tests/PreInitializeNativeBridgeFail1_test.cpp b/libnativebridge/tests/PreInitializeNativeBridgeFail1_test.cpp
new file mode 100644
index 0000000..69c30a1
--- /dev/null
+++ b/libnativebridge/tests/PreInitializeNativeBridgeFail1_test.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 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 "NativeBridgeTest.h"
+
+#include <cstdio>
+#include <cstring>
+#include <cutils/log.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+namespace android {
+
+TEST_F(NativeBridgeTest, PreInitializeNativeBridgeFail1) {
+ // Needs a valid application directory.
+ ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr));
+ ASSERT_FALSE(PreInitializeNativeBridge(nullptr, "isa"));
+ ASSERT_TRUE(NativeBridgeError());
+}
+
+} // namespace android
diff --git a/libnativebridge/tests/PreInitializeNativeBridgeFail2_test.cpp b/libnativebridge/tests/PreInitializeNativeBridgeFail2_test.cpp
new file mode 100644
index 0000000..74e96e0
--- /dev/null
+++ b/libnativebridge/tests/PreInitializeNativeBridgeFail2_test.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 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 "NativeBridgeTest.h"
+
+#include <cstdio>
+#include <cstring>
+#include <cutils/log.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+namespace android {
+
+TEST_F(NativeBridgeTest, PreInitializeNativeBridgeFail2) {
+ // Needs LoadNativeBridge() first
+ ASSERT_FALSE(PreInitializeNativeBridge(nullptr, "isa"));
+ ASSERT_TRUE(NativeBridgeError());
+}
+
+} // namespace android
diff --git a/libnativebridge/tests/PreInitializeNativeBridge_test.cpp b/libnativebridge/tests/PreInitializeNativeBridge_test.cpp
new file mode 100644
index 0000000..cec26ce
--- /dev/null
+++ b/libnativebridge/tests/PreInitializeNativeBridge_test.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2014 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 "NativeBridgeTest.h"
+
+#include <cstdio>
+#include <cstring>
+#include <cutils/log.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+namespace android {
+
+static constexpr const char* kTestData = "PreInitializeNativeBridge test.";
+
+TEST_F(NativeBridgeTest, PreInitializeNativeBridge) {
+ ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr));
+#ifndef __APPLE__ // Mac OS does not support bind-mount.
+#ifndef HAVE_ANDROID_OS // Cannot write into the hard-wired location.
+ // Try to create our mount namespace.
+ if (unshare(CLONE_NEWNS) != -1) {
+ // Create a dummy file.
+ FILE* cpuinfo = fopen("./cpuinfo", "w");
+ ASSERT_NE(nullptr, cpuinfo) << strerror(errno);
+ fprintf(cpuinfo, kTestData);
+ fclose(cpuinfo);
+
+ ASSERT_TRUE(PreInitializeNativeBridge("does not matter 1", "short 2"));
+
+ // Read /proc/cpuinfo
+ FILE* proc_cpuinfo = fopen("/proc/cpuinfo", "r");
+ ASSERT_NE(nullptr, proc_cpuinfo) << strerror(errno);
+ char buf[1024];
+ EXPECT_NE(nullptr, fgets(buf, sizeof(buf), proc_cpuinfo)) << "Error reading.";
+ fclose(proc_cpuinfo);
+
+ EXPECT_EQ(0, strcmp(buf, kTestData));
+
+ // Delete the file.
+ ASSERT_EQ(0, unlink("./cpuinfo")) << "Error unlinking temporary file.";
+ // Ending the test will tear down the mount namespace.
+ } else {
+ GTEST_LOG_(WARNING) << "Could not create mount namespace. Are you running this as root?";
+ }
+#endif
+#endif
+}
+
+} // namespace android
diff --git a/libnativebridge/tests/ReSetupNativeBridge_test.cpp b/libnativebridge/tests/ReSetupNativeBridge_test.cpp
new file mode 100644
index 0000000..944e5d7
--- /dev/null
+++ b/libnativebridge/tests/ReSetupNativeBridge_test.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2014 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 "NativeBridgeTest.h"
+
+namespace android {
+
+TEST_F(NativeBridgeTest, ReSetup) {
+ EXPECT_EQ(false, NativeBridgeError());
+ LoadNativeBridge("", nullptr);
+ EXPECT_EQ(false, NativeBridgeError());
+ LoadNativeBridge("", nullptr);
+ // This should lead to an error for trying to re-setup a native bridge.
+ EXPECT_EQ(true, NativeBridgeError());
+}
+
+} // namespace android
diff --git a/libnativebridge/tests/UnavailableNativeBridge_test.cpp b/libnativebridge/tests/UnavailableNativeBridge_test.cpp
new file mode 100644
index 0000000..ad374a5
--- /dev/null
+++ b/libnativebridge/tests/UnavailableNativeBridge_test.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "NativeBridgeTest.h"
+
+namespace android {
+
+TEST_F(NativeBridgeTest, NoNativeBridge) {
+ EXPECT_EQ(false, NativeBridgeAvailable());
+ // Try to initialize. This should fail as we are not set up.
+ EXPECT_EQ(false, InitializeNativeBridge(nullptr, nullptr));
+ EXPECT_EQ(true, NativeBridgeError());
+ EXPECT_EQ(false, NativeBridgeAvailable());
+}
+
+} // namespace android
diff --git a/libnativebridge/tests/ValidNameNativeBridge_test.cpp b/libnativebridge/tests/ValidNameNativeBridge_test.cpp
new file mode 100644
index 0000000..690be4a
--- /dev/null
+++ b/libnativebridge/tests/ValidNameNativeBridge_test.cpp
@@ -0,0 +1,35 @@
+/*
+ * 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 <NativeBridgeTest.h>
+
+namespace android {
+
+static const char* kTestName = "librandom-bridge_not.existing.so";
+
+TEST_F(NativeBridgeTest, ValidName) {
+ // Check that the name is acceptable.
+ EXPECT_EQ(true, NativeBridgeNameAcceptable(kTestName));
+
+ // Now check what happens on LoadNativeBridge.
+ EXPECT_EQ(false, NativeBridgeError());
+ LoadNativeBridge(kTestName, nullptr);
+ // This will lead to an error as the library doesn't exist.
+ EXPECT_EQ(true, NativeBridgeError());
+ EXPECT_EQ(false, NativeBridgeAvailable());
+}
+
+} // namespace android
diff --git a/libnetutils/Android.mk b/libnetutils/Android.mk
new file mode 100644
index 0000000..2060df4
--- /dev/null
+++ b/libnetutils/Android.mk
@@ -0,0 +1,26 @@
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ dhcpclient.c \
+ dhcpmsg.c \
+ dhcp_utils.c \
+ ifc_utils.c \
+ packet.c
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ liblog
+
+LOCAL_MODULE := libnetutils
+
+LOCAL_CFLAGS := -Werror
+
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := dhcptool.c
+LOCAL_SHARED_LIBRARIES := libnetutils
+LOCAL_MODULE := dhcptool
+LOCAL_MODULE_TAGS := debug
+include $(BUILD_EXECUTABLE)
diff --git a/libnetutils/NOTICE b/libnetutils/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/libnetutils/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/libnetutils/dhcp_utils.c b/libnetutils/dhcp_utils.c
new file mode 100644
index 0000000..70e37c6
--- /dev/null
+++ b/libnetutils/dhcp_utils.c
@@ -0,0 +1,374 @@
+/*
+ * 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.
+ */
+
+/* Utilities for managing the dhcpcd DHCP client daemon */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#include <cutils/properties.h>
+
+static const char DAEMON_NAME[] = "dhcpcd";
+static const char DAEMON_PROP_NAME[] = "init.svc.dhcpcd";
+static const char HOSTNAME_PROP_NAME[] = "net.hostname";
+static const char DHCP_PROP_NAME_PREFIX[] = "dhcp";
+static const char DHCP_CONFIG_PATH[] = "/system/etc/dhcpcd/dhcpcd.conf";
+static const int NAP_TIME = 200; /* wait for 200ms at a time */
+ /* when polling for property values */
+static const char DAEMON_NAME_RENEW[] = "iprenew";
+static char errmsg[100];
+/* interface length for dhcpcd daemon start (dhcpcd_<interface> as defined in init.rc file)
+ * or for filling up system properties dhcpcd.<interface>.ipaddress, dhcpcd.<interface>.dns1
+ * and other properties on a successful bind
+ */
+#define MAX_INTERFACE_LENGTH 25
+
+/*
+ * P2p interface names increase sequentially p2p-p2p0-1, p2p-p2p0-2.. after
+ * group formation. This does not work well with system properties which can quickly
+ * exhaust or for specifiying a dhcp start target in init which requires
+ * interface to be pre-defined in init.rc file.
+ *
+ * This function returns a common string p2p for all p2p interfaces.
+ */
+void get_p2p_interface_replacement(const char *interface, char *p2p_interface) {
+ /* Use p2p for any interface starting with p2p. */
+ if (strncmp(interface, "p2p",3) == 0) {
+ strncpy(p2p_interface, "p2p", MAX_INTERFACE_LENGTH);
+ } else {
+ strncpy(p2p_interface, interface, MAX_INTERFACE_LENGTH);
+ }
+}
+
+/*
+ * Wait for a system property to be assigned a specified value.
+ * If desired_value is NULL, then just wait for the property to
+ * be created with any value. maxwait is the maximum amount of
+ * time in seconds to wait before giving up.
+ */
+static int wait_for_property(const char *name, const char *desired_value, int maxwait)
+{
+ char value[PROPERTY_VALUE_MAX] = {'\0'};
+ int maxnaps = (maxwait * 1000) / NAP_TIME;
+
+ if (maxnaps < 1) {
+ maxnaps = 1;
+ }
+
+ while (maxnaps-- >= 0) {
+ if (property_get(name, value, NULL)) {
+ if (desired_value == NULL ||
+ strcmp(value, desired_value) == 0) {
+ return 0;
+ }
+ }
+ if (maxnaps >= 0) {
+ usleep(NAP_TIME * 1000);
+ }
+ }
+ return -1; /* failure */
+}
+
+static int fill_ip_info(const char *interface,
+ char *ipaddr,
+ char *gateway,
+ uint32_t *prefixLength,
+ char *dns[],
+ char *server,
+ uint32_t *lease,
+ char *vendorInfo,
+ char *domain,
+ char *mtu)
+{
+ char prop_name[PROPERTY_KEY_MAX];
+ char prop_value[PROPERTY_VALUE_MAX];
+ /* Interface name after converting p2p0-p2p0-X to p2p to reuse system properties */
+ char p2p_interface[MAX_INTERFACE_LENGTH];
+ int x;
+
+ get_p2p_interface_replacement(interface, p2p_interface);
+
+ snprintf(prop_name, sizeof(prop_name), "%s.%s.ipaddress", DHCP_PROP_NAME_PREFIX, p2p_interface);
+ property_get(prop_name, ipaddr, NULL);
+
+ snprintf(prop_name, sizeof(prop_name), "%s.%s.gateway", DHCP_PROP_NAME_PREFIX, p2p_interface);
+ property_get(prop_name, gateway, NULL);
+
+ snprintf(prop_name, sizeof(prop_name), "%s.%s.server", DHCP_PROP_NAME_PREFIX, p2p_interface);
+ property_get(prop_name, server, NULL);
+
+ //TODO: Handle IPv6 when we change system property usage
+ if (gateway[0] == '\0' || strncmp(gateway, "0.0.0.0", 7) == 0) {
+ //DHCP server is our best bet as gateway
+ strncpy(gateway, server, PROPERTY_VALUE_MAX);
+ }
+
+ snprintf(prop_name, sizeof(prop_name), "%s.%s.mask", DHCP_PROP_NAME_PREFIX, p2p_interface);
+ if (property_get(prop_name, prop_value, NULL)) {
+ int p;
+ // this conversion is v4 only, but this dhcp client is v4 only anyway
+ in_addr_t mask = ntohl(inet_addr(prop_value));
+ // Check netmask is a valid IP address. ntohl gives NONE response (all 1's) for
+ // non 255.255.255.255 inputs. if we get that value check if it is legit..
+ if (mask == INADDR_NONE && strcmp(prop_value, "255.255.255.255") != 0) {
+ snprintf(errmsg, sizeof(errmsg), "DHCP gave invalid net mask %s", prop_value);
+ return -1;
+ }
+ for (p = 0; p < 32; p++) {
+ if (mask == 0) break;
+ // check for non-contiguous netmask, e.g., 255.254.255.0
+ if ((mask & 0x80000000) == 0) {
+ snprintf(errmsg, sizeof(errmsg), "DHCP gave invalid net mask %s", prop_value);
+ return -1;
+ }
+ mask = mask << 1;
+ }
+ *prefixLength = p;
+ }
+
+ for (x=0; dns[x] != NULL; x++) {
+ snprintf(prop_name, sizeof(prop_name), "%s.%s.dns%d", DHCP_PROP_NAME_PREFIX, p2p_interface, x+1);
+ property_get(prop_name, dns[x], NULL);
+ }
+
+ snprintf(prop_name, sizeof(prop_name), "%s.%s.leasetime", DHCP_PROP_NAME_PREFIX, p2p_interface);
+ if (property_get(prop_name, prop_value, NULL)) {
+ *lease = atol(prop_value);
+ }
+
+ snprintf(prop_name, sizeof(prop_name), "%s.%s.vendorInfo", DHCP_PROP_NAME_PREFIX,
+ p2p_interface);
+ property_get(prop_name, vendorInfo, NULL);
+
+ snprintf(prop_name, sizeof(prop_name), "%s.%s.domain", DHCP_PROP_NAME_PREFIX,
+ p2p_interface);
+ property_get(prop_name, domain, NULL);
+
+ snprintf(prop_name, sizeof(prop_name), "%s.%s.mtu", DHCP_PROP_NAME_PREFIX,
+ p2p_interface);
+ property_get(prop_name, mtu, NULL);
+
+ return 0;
+}
+
+/*
+ * Start the dhcp client daemon, and wait for it to finish
+ * configuring the interface.
+ *
+ * The device init.rc file needs a corresponding entry for this work.
+ *
+ * Example:
+ * service dhcpcd_<interface> /system/bin/dhcpcd -ABKL -f dhcpcd.conf
+ */
+int dhcp_do_request(const char *interface,
+ char *ipaddr,
+ char *gateway,
+ uint32_t *prefixLength,
+ char *dns[],
+ char *server,
+ uint32_t *lease,
+ char *vendorInfo,
+ char *domain,
+ char *mtu)
+{
+ char result_prop_name[PROPERTY_KEY_MAX];
+ char daemon_prop_name[PROPERTY_KEY_MAX];
+ char prop_value[PROPERTY_VALUE_MAX] = {'\0'};
+ char daemon_cmd[PROPERTY_VALUE_MAX * 2 + sizeof(DHCP_CONFIG_PATH)];
+ const char *ctrl_prop = "ctl.start";
+ const char *desired_status = "running";
+ /* Interface name after converting p2p0-p2p0-X to p2p to reuse system properties */
+ char p2p_interface[MAX_INTERFACE_LENGTH];
+
+ get_p2p_interface_replacement(interface, p2p_interface);
+
+ snprintf(result_prop_name, sizeof(result_prop_name), "%s.%s.result",
+ DHCP_PROP_NAME_PREFIX,
+ p2p_interface);
+
+ snprintf(daemon_prop_name, sizeof(daemon_prop_name), "%s_%s",
+ DAEMON_PROP_NAME,
+ p2p_interface);
+
+ /* Erase any previous setting of the dhcp result property */
+ property_set(result_prop_name, "");
+
+ /* Start the daemon and wait until it's ready */
+ if (property_get(HOSTNAME_PROP_NAME, prop_value, NULL) && (prop_value[0] != '\0'))
+ snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:-f %s -h %s %s", DAEMON_NAME,
+ p2p_interface, DHCP_CONFIG_PATH, prop_value, interface);
+ else
+ snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:-f %s %s", DAEMON_NAME,
+ p2p_interface, DHCP_CONFIG_PATH, interface);
+ memset(prop_value, '\0', PROPERTY_VALUE_MAX);
+ property_set(ctrl_prop, daemon_cmd);
+ if (wait_for_property(daemon_prop_name, desired_status, 10) < 0) {
+ snprintf(errmsg, sizeof(errmsg), "%s", "Timed out waiting for dhcpcd to start");
+ return -1;
+ }
+
+ /* Wait for the daemon to return a result */
+ if (wait_for_property(result_prop_name, NULL, 30) < 0) {
+ snprintf(errmsg, sizeof(errmsg), "%s", "Timed out waiting for DHCP to finish");
+ return -1;
+ }
+
+ if (!property_get(result_prop_name, prop_value, NULL)) {
+ /* shouldn't ever happen, given the success of wait_for_property() */
+ snprintf(errmsg, sizeof(errmsg), "%s", "DHCP result property was not set");
+ return -1;
+ }
+ if (strcmp(prop_value, "ok") == 0) {
+ if (fill_ip_info(interface, ipaddr, gateway, prefixLength, dns,
+ server, lease, vendorInfo, domain, mtu) == -1) {
+ return -1;
+ }
+ return 0;
+ } else {
+ snprintf(errmsg, sizeof(errmsg), "DHCP result was %s", prop_value);
+ return -1;
+ }
+}
+
+/**
+ * Stop the DHCP client daemon.
+ */
+int dhcp_stop(const char *interface)
+{
+ char result_prop_name[PROPERTY_KEY_MAX];
+ char daemon_prop_name[PROPERTY_KEY_MAX];
+ char daemon_cmd[PROPERTY_VALUE_MAX * 2];
+ const char *ctrl_prop = "ctl.stop";
+ const char *desired_status = "stopped";
+
+ char p2p_interface[MAX_INTERFACE_LENGTH];
+
+ get_p2p_interface_replacement(interface, p2p_interface);
+
+ snprintf(result_prop_name, sizeof(result_prop_name), "%s.%s.result",
+ DHCP_PROP_NAME_PREFIX,
+ p2p_interface);
+
+ snprintf(daemon_prop_name, sizeof(daemon_prop_name), "%s_%s",
+ DAEMON_PROP_NAME,
+ p2p_interface);
+
+ snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s", DAEMON_NAME, p2p_interface);
+
+ /* Stop the daemon and wait until it's reported to be stopped */
+ property_set(ctrl_prop, daemon_cmd);
+ if (wait_for_property(daemon_prop_name, desired_status, 5) < 0) {
+ return -1;
+ }
+ property_set(result_prop_name, "failed");
+ return 0;
+}
+
+/**
+ * Release the current DHCP client lease.
+ */
+int dhcp_release_lease(const char *interface)
+{
+ char daemon_prop_name[PROPERTY_KEY_MAX];
+ char daemon_cmd[PROPERTY_VALUE_MAX * 2];
+ const char *ctrl_prop = "ctl.stop";
+ const char *desired_status = "stopped";
+
+ char p2p_interface[MAX_INTERFACE_LENGTH];
+
+ get_p2p_interface_replacement(interface, p2p_interface);
+
+ snprintf(daemon_prop_name, sizeof(daemon_prop_name), "%s_%s",
+ DAEMON_PROP_NAME,
+ p2p_interface);
+
+ snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s", DAEMON_NAME, p2p_interface);
+
+ /* Stop the daemon and wait until it's reported to be stopped */
+ property_set(ctrl_prop, daemon_cmd);
+ if (wait_for_property(daemon_prop_name, desired_status, 5) < 0) {
+ return -1;
+ }
+ return 0;
+}
+
+char *dhcp_get_errmsg() {
+ return errmsg;
+}
+
+/**
+ * The device init.rc file needs a corresponding entry.
+ *
+ * Example:
+ * service iprenew_<interface> /system/bin/dhcpcd -n
+ *
+ */
+int dhcp_do_request_renew(const char *interface,
+ char *ipaddr,
+ char *gateway,
+ uint32_t *prefixLength,
+ char *dns[],
+ char *server,
+ uint32_t *lease,
+ char *vendorInfo,
+ char *domain,
+ char *mtu)
+{
+ char result_prop_name[PROPERTY_KEY_MAX];
+ char prop_value[PROPERTY_VALUE_MAX] = {'\0'};
+ char daemon_cmd[PROPERTY_VALUE_MAX * 2];
+ const char *ctrl_prop = "ctl.start";
+
+ char p2p_interface[MAX_INTERFACE_LENGTH];
+
+ get_p2p_interface_replacement(interface, p2p_interface);
+
+ snprintf(result_prop_name, sizeof(result_prop_name), "%s.%s.result",
+ DHCP_PROP_NAME_PREFIX,
+ p2p_interface);
+
+ /* Erase any previous setting of the dhcp result property */
+ property_set(result_prop_name, "");
+
+ /* Start the renew daemon and wait until it's ready */
+ snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:%s", DAEMON_NAME_RENEW,
+ p2p_interface, interface);
+ memset(prop_value, '\0', PROPERTY_VALUE_MAX);
+ property_set(ctrl_prop, daemon_cmd);
+
+ /* Wait for the daemon to return a result */
+ if (wait_for_property(result_prop_name, NULL, 30) < 0) {
+ snprintf(errmsg, sizeof(errmsg), "%s", "Timed out waiting for DHCP Renew to finish");
+ return -1;
+ }
+
+ if (!property_get(result_prop_name, prop_value, NULL)) {
+ /* shouldn't ever happen, given the success of wait_for_property() */
+ snprintf(errmsg, sizeof(errmsg), "%s", "DHCP Renew result property was not set");
+ return -1;
+ }
+ if (strcmp(prop_value, "ok") == 0) {
+ return fill_ip_info(interface, ipaddr, gateway, prefixLength, dns,
+ server, lease, vendorInfo, domain, mtu);
+ } else {
+ snprintf(errmsg, sizeof(errmsg), "DHCP Renew result was %s", prop_value);
+ return -1;
+ }
+}
diff --git a/libnetutils/dhcpclient.c b/libnetutils/dhcpclient.c
new file mode 100644
index 0000000..240a789
--- /dev/null
+++ b/libnetutils/dhcpclient.c
@@ -0,0 +1,545 @@
+/*
+ * 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 <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+#include <time.h>
+#include <sys/time.h>
+#include <poll.h>
+
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#include <cutils/properties.h>
+#define LOG_TAG "DHCP"
+#include <cutils/log.h>
+
+#include <dirent.h>
+
+#include <netutils/ifc.h>
+#include "dhcpmsg.h"
+#include "packet.h"
+
+#define VERBOSE 2
+
+static int verbose = 1;
+static char errmsg[2048];
+
+typedef unsigned long long msecs_t;
+#if VERBOSE
+void dump_dhcp_msg();
+#endif
+
+msecs_t get_msecs(void)
+{
+ struct timespec ts;
+
+ if (clock_gettime(CLOCK_MONOTONIC, &ts)) {
+ return 0;
+ } else {
+ return (((msecs_t) ts.tv_sec) * ((msecs_t) 1000)) +
+ (((msecs_t) ts.tv_nsec) / ((msecs_t) 1000000));
+ }
+}
+
+void printerr(char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(errmsg, sizeof(errmsg), fmt, ap);
+ va_end(ap);
+
+ ALOGD("%s", errmsg);
+}
+
+const char *dhcp_lasterror()
+{
+ return errmsg;
+}
+
+int fatal(const char *reason)
+{
+ printerr("%s: %s\n", reason, strerror(errno));
+ return -1;
+// exit(1);
+}
+
+const char *ipaddr(in_addr_t addr)
+{
+ struct in_addr in_addr;
+
+ in_addr.s_addr = addr;
+ return inet_ntoa(in_addr);
+}
+
+extern int ipv4NetmaskToPrefixLength(in_addr_t mask);
+
+typedef struct dhcp_info dhcp_info;
+
+struct dhcp_info {
+ uint32_t type;
+
+ uint32_t ipaddr;
+ uint32_t gateway;
+ uint32_t prefixLength;
+
+ uint32_t dns1;
+ uint32_t dns2;
+
+ uint32_t serveraddr;
+ uint32_t lease;
+};
+
+dhcp_info last_good_info;
+
+void get_dhcp_info(uint32_t *ipaddr, uint32_t *gateway, uint32_t *prefixLength,
+ uint32_t *dns1, uint32_t *dns2, uint32_t *server,
+ uint32_t *lease)
+{
+ *ipaddr = last_good_info.ipaddr;
+ *gateway = last_good_info.gateway;
+ *prefixLength = last_good_info.prefixLength;
+ *dns1 = last_good_info.dns1;
+ *dns2 = last_good_info.dns2;
+ *server = last_good_info.serveraddr;
+ *lease = last_good_info.lease;
+}
+
+static int dhcp_configure(const char *ifname, dhcp_info *info)
+{
+ last_good_info = *info;
+ return ifc_configure(ifname, info->ipaddr, info->prefixLength, info->gateway,
+ info->dns1, info->dns2);
+}
+
+static const char *dhcp_type_to_name(uint32_t type)
+{
+ switch(type) {
+ case DHCPDISCOVER: return "discover";
+ case DHCPOFFER: return "offer";
+ case DHCPREQUEST: return "request";
+ case DHCPDECLINE: return "decline";
+ case DHCPACK: return "ack";
+ case DHCPNAK: return "nak";
+ case DHCPRELEASE: return "release";
+ case DHCPINFORM: return "inform";
+ default: return "???";
+ }
+}
+
+void dump_dhcp_info(dhcp_info *info)
+{
+ char addr[20], gway[20];
+ ALOGD("--- dhcp %s (%d) ---",
+ dhcp_type_to_name(info->type), info->type);
+ strcpy(addr, ipaddr(info->ipaddr));
+ strcpy(gway, ipaddr(info->gateway));
+ ALOGD("ip %s gw %s prefixLength %d", addr, gway, info->prefixLength);
+ if (info->dns1) ALOGD("dns1: %s", ipaddr(info->dns1));
+ if (info->dns2) ALOGD("dns2: %s", ipaddr(info->dns2));
+ ALOGD("server %s, lease %d seconds",
+ ipaddr(info->serveraddr), info->lease);
+}
+
+
+int decode_dhcp_msg(dhcp_msg *msg, int len, dhcp_info *info)
+{
+ uint8_t *x;
+ unsigned int opt;
+ int optlen;
+
+ memset(info, 0, sizeof(dhcp_info));
+ if (len < (DHCP_MSG_FIXED_SIZE + 4)) return -1;
+
+ len -= (DHCP_MSG_FIXED_SIZE + 4);
+
+ if (msg->options[0] != OPT_COOKIE1) return -1;
+ if (msg->options[1] != OPT_COOKIE2) return -1;
+ if (msg->options[2] != OPT_COOKIE3) return -1;
+ if (msg->options[3] != OPT_COOKIE4) return -1;
+
+ x = msg->options + 4;
+
+ while (len > 2) {
+ opt = *x++;
+ if (opt == OPT_PAD) {
+ len--;
+ continue;
+ }
+ if (opt == OPT_END) {
+ break;
+ }
+ optlen = *x++;
+ len -= 2;
+ if (optlen > len) {
+ break;
+ }
+ switch(opt) {
+ case OPT_SUBNET_MASK:
+ if (optlen >= 4) {
+ in_addr_t mask;
+ memcpy(&mask, x, 4);
+ info->prefixLength = ipv4NetmaskToPrefixLength(mask);
+ }
+ break;
+ case OPT_GATEWAY:
+ if (optlen >= 4) memcpy(&info->gateway, x, 4);
+ break;
+ case OPT_DNS:
+ if (optlen >= 4) memcpy(&info->dns1, x + 0, 4);
+ if (optlen >= 8) memcpy(&info->dns2, x + 4, 4);
+ break;
+ case OPT_LEASE_TIME:
+ if (optlen >= 4) {
+ memcpy(&info->lease, x, 4);
+ info->lease = ntohl(info->lease);
+ }
+ break;
+ case OPT_SERVER_ID:
+ if (optlen >= 4) memcpy(&info->serveraddr, x, 4);
+ break;
+ case OPT_MESSAGE_TYPE:
+ info->type = *x;
+ break;
+ default:
+ break;
+ }
+ x += optlen;
+ len -= optlen;
+ }
+
+ info->ipaddr = msg->yiaddr;
+
+ return 0;
+}
+
+#if VERBOSE
+
+static void hex2str(char *buf, size_t buf_size, const unsigned char *array, int len)
+{
+ int i;
+ char *cp = buf;
+ char *buf_end = buf + buf_size;
+ for (i = 0; i < len; i++) {
+ cp += snprintf(cp, buf_end - cp, " %02x ", array[i]);
+ }
+}
+
+void dump_dhcp_msg(dhcp_msg *msg, int len)
+{
+ unsigned char *x;
+ unsigned int n,c;
+ int optsz;
+ const char *name;
+ char buf[2048];
+
+ ALOGD("===== DHCP message:");
+ if (len < DHCP_MSG_FIXED_SIZE) {
+ ALOGD("Invalid length %d, should be %d", len, DHCP_MSG_FIXED_SIZE);
+ return;
+ }
+
+ len -= DHCP_MSG_FIXED_SIZE;
+
+ if (msg->op == OP_BOOTREQUEST)
+ name = "BOOTREQUEST";
+ else if (msg->op == OP_BOOTREPLY)
+ name = "BOOTREPLY";
+ else
+ name = "????";
+ ALOGD("op = %s (%d), htype = %d, hlen = %d, hops = %d",
+ name, msg->op, msg->htype, msg->hlen, msg->hops);
+ ALOGD("xid = 0x%08x secs = %d, flags = 0x%04x optlen = %d",
+ ntohl(msg->xid), ntohs(msg->secs), ntohs(msg->flags), len);
+ ALOGD("ciaddr = %s", ipaddr(msg->ciaddr));
+ ALOGD("yiaddr = %s", ipaddr(msg->yiaddr));
+ ALOGD("siaddr = %s", ipaddr(msg->siaddr));
+ ALOGD("giaddr = %s", ipaddr(msg->giaddr));
+
+ c = msg->hlen > 16 ? 16 : msg->hlen;
+ hex2str(buf, sizeof(buf), msg->chaddr, c);
+ ALOGD("chaddr = {%s}", buf);
+
+ for (n = 0; n < 64; n++) {
+ unsigned char x = msg->sname[n];
+ if ((x < ' ') || (x > 127)) {
+ if (x == 0) break;
+ msg->sname[n] = '.';
+ }
+ }
+ msg->sname[63] = 0;
+
+ for (n = 0; n < 128; n++) {
+ unsigned char x = msg->file[n];
+ if ((x < ' ') || (x > 127)) {
+ if (x == 0) break;
+ msg->file[n] = '.';
+ }
+ }
+ msg->file[127] = 0;
+
+ ALOGD("sname = '%s'", msg->sname);
+ ALOGD("file = '%s'", msg->file);
+
+ if (len < 4) return;
+ len -= 4;
+ x = msg->options + 4;
+
+ while (len > 2) {
+ if (*x == 0) {
+ x++;
+ len--;
+ continue;
+ }
+ if (*x == OPT_END) {
+ break;
+ }
+ len -= 2;
+ optsz = x[1];
+ if (optsz > len) break;
+ if (x[0] == OPT_DOMAIN_NAME || x[0] == OPT_MESSAGE) {
+ if ((unsigned int)optsz < sizeof(buf) - 1) {
+ n = optsz;
+ } else {
+ n = sizeof(buf) - 1;
+ }
+ memcpy(buf, &x[2], n);
+ buf[n] = '\0';
+ } else {
+ hex2str(buf, sizeof(buf), &x[2], optsz);
+ }
+ if (x[0] == OPT_MESSAGE_TYPE)
+ name = dhcp_type_to_name(x[2]);
+ else
+ name = NULL;
+ ALOGD("op %d len %d {%s} %s", x[0], optsz, buf, name == NULL ? "" : name);
+ len -= optsz;
+ x = x + optsz + 2;
+ }
+}
+
+#endif
+
+static int send_message(int sock, int if_index, dhcp_msg *msg, int size)
+{
+#if VERBOSE > 1
+ dump_dhcp_msg(msg, size);
+#endif
+ return send_packet(sock, if_index, msg, size, INADDR_ANY, INADDR_BROADCAST,
+ PORT_BOOTP_CLIENT, PORT_BOOTP_SERVER);
+}
+
+static int is_valid_reply(dhcp_msg *msg, dhcp_msg *reply, int sz)
+{
+ if (sz < DHCP_MSG_FIXED_SIZE) {
+ if (verbose) ALOGD("Wrong size %d != %d\n", sz, DHCP_MSG_FIXED_SIZE);
+ return 0;
+ }
+ if (reply->op != OP_BOOTREPLY) {
+ if (verbose) ALOGD("Wrong Op %d != %d\n", reply->op, OP_BOOTREPLY);
+ return 0;
+ }
+ if (reply->xid != msg->xid) {
+ if (verbose) ALOGD("Wrong Xid 0x%x != 0x%x\n", ntohl(reply->xid),
+ ntohl(msg->xid));
+ return 0;
+ }
+ if (reply->htype != msg->htype) {
+ if (verbose) ALOGD("Wrong Htype %d != %d\n", reply->htype, msg->htype);
+ return 0;
+ }
+ if (reply->hlen != msg->hlen) {
+ if (verbose) ALOGD("Wrong Hlen %d != %d\n", reply->hlen, msg->hlen);
+ return 0;
+ }
+ if (memcmp(msg->chaddr, reply->chaddr, msg->hlen)) {
+ if (verbose) ALOGD("Wrong chaddr %x != %x\n", *(reply->chaddr),*(msg->chaddr));
+ return 0;
+ }
+ return 1;
+}
+
+#define STATE_SELECTING 1
+#define STATE_REQUESTING 2
+
+#define TIMEOUT_INITIAL 4000
+#define TIMEOUT_MAX 32000
+
+int dhcp_init_ifc(const char *ifname)
+{
+ dhcp_msg discover_msg;
+ dhcp_msg request_msg;
+ dhcp_msg reply;
+ dhcp_msg *msg;
+ dhcp_info info;
+ int s, r, size;
+ int valid_reply;
+ uint32_t xid;
+ unsigned char hwaddr[6];
+ struct pollfd pfd;
+ unsigned int state;
+ unsigned int timeout;
+ int if_index;
+
+ xid = (uint32_t) get_msecs();
+
+ if (ifc_get_hwaddr(ifname, hwaddr)) {
+ return fatal("cannot obtain interface address");
+ }
+ if (ifc_get_ifindex(ifname, &if_index)) {
+ return fatal("cannot obtain interface index");
+ }
+
+ s = open_raw_socket(ifname, hwaddr, if_index);
+
+ timeout = TIMEOUT_INITIAL;
+ state = STATE_SELECTING;
+ info.type = 0;
+ goto transmit;
+
+ for (;;) {
+ pfd.fd = s;
+ pfd.events = POLLIN;
+ pfd.revents = 0;
+ r = poll(&pfd, 1, timeout);
+
+ if (r == 0) {
+#if VERBOSE
+ printerr("TIMEOUT\n");
+#endif
+ if (timeout >= TIMEOUT_MAX) {
+ printerr("timed out\n");
+ if ( info.type == DHCPOFFER ) {
+ printerr("no acknowledgement from DHCP server\nconfiguring %s with offered parameters\n", ifname);
+ return dhcp_configure(ifname, &info);
+ }
+ errno = ETIME;
+ close(s);
+ return -1;
+ }
+ timeout = timeout * 2;
+
+ transmit:
+ size = 0;
+ msg = NULL;
+ switch(state) {
+ case STATE_SELECTING:
+ msg = &discover_msg;
+ size = init_dhcp_discover_msg(msg, hwaddr, xid);
+ break;
+ case STATE_REQUESTING:
+ msg = &request_msg;
+ size = init_dhcp_request_msg(msg, hwaddr, xid, info.ipaddr, info.serveraddr);
+ break;
+ default:
+ r = 0;
+ }
+ if (size != 0) {
+ r = send_message(s, if_index, msg, size);
+ if (r < 0) {
+ printerr("error sending dhcp msg: %s\n", strerror(errno));
+ }
+ }
+ continue;
+ }
+
+ if (r < 0) {
+ if ((errno == EAGAIN) || (errno == EINTR)) {
+ continue;
+ }
+ return fatal("poll failed");
+ }
+
+ errno = 0;
+ r = receive_packet(s, &reply);
+ if (r < 0) {
+ if (errno != 0) {
+ ALOGD("receive_packet failed (%d): %s", r, strerror(errno));
+ if (errno == ENETDOWN || errno == ENXIO) {
+ return -1;
+ }
+ }
+ continue;
+ }
+
+#if VERBOSE > 1
+ dump_dhcp_msg(&reply, r);
+#endif
+ decode_dhcp_msg(&reply, r, &info);
+
+ if (state == STATE_SELECTING) {
+ valid_reply = is_valid_reply(&discover_msg, &reply, r);
+ } else {
+ valid_reply = is_valid_reply(&request_msg, &reply, r);
+ }
+ if (!valid_reply) {
+ printerr("invalid reply\n");
+ continue;
+ }
+
+ if (verbose) dump_dhcp_info(&info);
+
+ switch(state) {
+ case STATE_SELECTING:
+ if (info.type == DHCPOFFER) {
+ state = STATE_REQUESTING;
+ timeout = TIMEOUT_INITIAL;
+ xid++;
+ goto transmit;
+ }
+ break;
+ case STATE_REQUESTING:
+ if (info.type == DHCPACK) {
+ printerr("configuring %s\n", ifname);
+ close(s);
+ return dhcp_configure(ifname, &info);
+ } else if (info.type == DHCPNAK) {
+ printerr("configuration request denied\n");
+ close(s);
+ return -1;
+ } else {
+ printerr("ignoring %s message in state %d\n",
+ dhcp_type_to_name(info.type), state);
+ }
+ break;
+ }
+ }
+ close(s);
+ return 0;
+}
+
+int do_dhcp(char *iname)
+{
+ if (ifc_set_addr(iname, 0)) {
+ printerr("failed to set ip addr for %s to 0.0.0.0: %s\n", iname, strerror(errno));
+ return -1;
+ }
+
+ if (ifc_up(iname)) {
+ printerr("failed to bring up interface %s: %s\n", iname, strerror(errno));
+ return -1;
+ }
+
+ return dhcp_init_ifc(iname);
+}
diff --git a/libnetutils/dhcpmsg.c b/libnetutils/dhcpmsg.c
new file mode 100644
index 0000000..1e0a233
--- /dev/null
+++ b/libnetutils/dhcpmsg.c
@@ -0,0 +1,100 @@
+/*
+ * 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 <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <netinet/in.h>
+
+#include "dhcpmsg.h"
+
+static void *init_dhcp_msg(dhcp_msg *msg, int type, void *hwaddr, uint32_t xid)
+{
+ uint8_t *x;
+
+ memset(msg, 0, sizeof(dhcp_msg));
+
+ msg->op = OP_BOOTREQUEST;
+ msg->htype = HTYPE_ETHER;
+ msg->hlen = 6;
+ msg->hops = 0;
+
+ msg->flags = htons(FLAGS_BROADCAST);
+
+ msg->xid = xid;
+
+ memcpy(msg->chaddr, hwaddr, 6);
+
+ x = msg->options;
+
+ *x++ = OPT_COOKIE1;
+ *x++ = OPT_COOKIE2;
+ *x++ = OPT_COOKIE3;
+ *x++ = OPT_COOKIE4;
+
+ *x++ = OPT_MESSAGE_TYPE;
+ *x++ = 1;
+ *x++ = type;
+
+ return x;
+}
+
+int init_dhcp_discover_msg(dhcp_msg *msg, void *hwaddr, uint32_t xid)
+{
+ uint8_t *x;
+
+ x = init_dhcp_msg(msg, DHCPDISCOVER, hwaddr, xid);
+
+ *x++ = OPT_PARAMETER_LIST;
+ *x++ = 4;
+ *x++ = OPT_SUBNET_MASK;
+ *x++ = OPT_GATEWAY;
+ *x++ = OPT_DNS;
+ *x++ = OPT_BROADCAST_ADDR;
+
+ *x++ = OPT_END;
+
+ return DHCP_MSG_FIXED_SIZE + (x - msg->options);
+}
+
+int init_dhcp_request_msg(dhcp_msg *msg, void *hwaddr, uint32_t xid,
+ uint32_t ipaddr, uint32_t serveraddr)
+{
+ uint8_t *x;
+
+ x = init_dhcp_msg(msg, DHCPREQUEST, hwaddr, xid);
+
+ *x++ = OPT_PARAMETER_LIST;
+ *x++ = 4;
+ *x++ = OPT_SUBNET_MASK;
+ *x++ = OPT_GATEWAY;
+ *x++ = OPT_DNS;
+ *x++ = OPT_BROADCAST_ADDR;
+
+ *x++ = OPT_REQUESTED_IP;
+ *x++ = 4;
+ memcpy(x, &ipaddr, 4);
+ x += 4;
+
+ *x++ = OPT_SERVER_ID;
+ *x++ = 4;
+ memcpy(x, &serveraddr, 4);
+ x += 4;
+
+ *x++ = OPT_END;
+
+ return DHCP_MSG_FIXED_SIZE + (x - msg->options);
+}
diff --git a/libnetutils/dhcpmsg.h b/libnetutils/dhcpmsg.h
new file mode 100644
index 0000000..c86e400
--- /dev/null
+++ b/libnetutils/dhcpmsg.h
@@ -0,0 +1,106 @@
+/*
+ * 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.
+ */
+
+#ifndef _WIFI_DHCP_H_
+#define _WIFI_DHCP_H_
+
+#include <sys/types.h>
+
+#define PORT_BOOTP_SERVER 67
+#define PORT_BOOTP_CLIENT 68
+
+/* RFC 2131 p 9 */
+typedef struct dhcp_msg dhcp_msg;
+
+#define OP_BOOTREQUEST 1
+#define OP_BOOTREPLY 2
+
+#define FLAGS_BROADCAST 0x8000
+
+#define HTYPE_ETHER 1
+
+struct dhcp_msg
+{
+ uint8_t op; /* BOOTREQUEST / BOOTREPLY */
+ uint8_t htype; /* hw addr type */
+ uint8_t hlen; /* hw addr len */
+ uint8_t hops; /* client set to 0 */
+
+ uint32_t xid; /* transaction id */
+
+ uint16_t secs; /* seconds since start of acq */
+ uint16_t flags;
+
+ uint32_t ciaddr; /* client IP addr */
+ uint32_t yiaddr; /* your (client) IP addr */
+ uint32_t siaddr; /* ip addr of next server */
+ /* (DHCPOFFER and DHCPACK) */
+ uint32_t giaddr; /* relay agent IP addr */
+
+ uint8_t chaddr[16]; /* client hw addr */
+ char sname[64]; /* asciiz server hostname */
+ char file[128]; /* asciiz boot file name */
+
+ uint8_t options[1024]; /* optional parameters */
+};
+
+#define DHCP_MSG_FIXED_SIZE 236
+
+/* first four bytes of options are a cookie to indicate that
+** the payload are DHCP options as opposed to some other BOOTP
+** extension.
+*/
+#define OPT_COOKIE1 0x63
+#define OPT_COOKIE2 0x82
+#define OPT_COOKIE3 0x53
+#define OPT_COOKIE4 0x63
+
+/* BOOTP/DHCP options - see RFC 2132 */
+#define OPT_PAD 0
+
+#define OPT_SUBNET_MASK 1 /* 4 <ipaddr> */
+#define OPT_TIME_OFFSET 2 /* 4 <seconds> */
+#define OPT_GATEWAY 3 /* 4*n <ipaddr> * n */
+#define OPT_DNS 6 /* 4*n <ipaddr> * n */
+#define OPT_DOMAIN_NAME 15 /* n <domainnamestring> */
+#define OPT_BROADCAST_ADDR 28 /* 4 <ipaddr> */
+
+#define OPT_REQUESTED_IP 50 /* 4 <ipaddr> */
+#define OPT_LEASE_TIME 51 /* 4 <seconds> */
+#define OPT_MESSAGE_TYPE 53 /* 1 <msgtype> */
+#define OPT_SERVER_ID 54 /* 4 <ipaddr> */
+#define OPT_PARAMETER_LIST 55 /* n <optcode> * n */
+#define OPT_MESSAGE 56 /* n <errorstring> */
+#define OPT_CLASS_ID 60 /* n <opaque> */
+#define OPT_CLIENT_ID 61 /* n <opaque> */
+#define OPT_END 255
+
+/* DHCP message types */
+#define DHCPDISCOVER 1
+#define DHCPOFFER 2
+#define DHCPREQUEST 3
+#define DHCPDECLINE 4
+#define DHCPACK 5
+#define DHCPNAK 6
+#define DHCPRELEASE 7
+#define DHCPINFORM 8
+
+int init_dhcp_discover_msg(dhcp_msg *msg, void *hwaddr, uint32_t xid);
+
+int init_dhcp_request_msg(dhcp_msg *msg, void *hwaddr, uint32_t xid,
+ uint32_t ipaddr, uint32_t serveraddr);
+
+#endif
diff --git a/libnetutils/dhcptool.c b/libnetutils/dhcptool.c
new file mode 100644
index 0000000..352ac5e
--- /dev/null
+++ b/libnetutils/dhcptool.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2015, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <error.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include <netutils/dhcp.h>
+#include <netutils/ifc.h>
+
+int main(int argc, char* argv[]) {
+ if (argc != 2) {
+ error(EXIT_FAILURE, 0, "usage: %s INTERFACE", argv[0]);
+ }
+
+ char* interface = argv[1];
+ if (ifc_init()) {
+ error(EXIT_FAILURE, errno, "dhcptool %s: ifc_init failed", interface);
+ }
+
+ int rc = do_dhcp(interface);
+ if (rc) {
+ error(0, errno, "dhcptool %s: do_dhcp failed", interface);
+ }
+
+ ifc_close();
+
+ return rc ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/libnetutils/ifc_utils.c b/libnetutils/ifc_utils.c
new file mode 100644
index 0000000..7d2a5fb
--- /dev/null
+++ b/libnetutils/ifc_utils.c
@@ -0,0 +1,703 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <netdb.h>
+
+#include <linux/if.h>
+#include <linux/if_ether.h>
+#include <linux/if_arp.h>
+#include <linux/netlink.h>
+#include <linux/route.h>
+#include <linux/ipv6_route.h>
+#include <linux/rtnetlink.h>
+#include <linux/sockios.h>
+
+#include "netutils/ifc.h"
+
+#ifdef ANDROID
+#define LOG_TAG "NetUtils"
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#else
+#include <stdio.h>
+#include <string.h>
+#define ALOGD printf
+#define ALOGW printf
+#endif
+
+#ifdef HAVE_ANDROID_OS
+/* SIOCKILLADDR is an Android extension. */
+#define SIOCKILLADDR 0x8939
+#endif
+
+static int ifc_ctl_sock = -1;
+static int ifc_ctl_sock6 = -1;
+void printerr(char *fmt, ...);
+
+#define DBG 0
+#define INET_ADDRLEN 4
+#define INET6_ADDRLEN 16
+
+in_addr_t prefixLengthToIpv4Netmask(int prefix_length)
+{
+ in_addr_t mask = 0;
+
+ // C99 (6.5.7): shifts of 32 bits have undefined results
+ if (prefix_length <= 0 || prefix_length > 32) {
+ return 0;
+ }
+
+ mask = ~mask << (32 - prefix_length);
+ mask = htonl(mask);
+
+ return mask;
+}
+
+int ipv4NetmaskToPrefixLength(in_addr_t mask)
+{
+ int prefixLength = 0;
+ uint32_t m = (uint32_t)ntohl(mask);
+ while (m & 0x80000000) {
+ prefixLength++;
+ m = m << 1;
+ }
+ return prefixLength;
+}
+
+static const char *ipaddr_to_string(in_addr_t addr)
+{
+ struct in_addr in_addr;
+
+ in_addr.s_addr = addr;
+ return inet_ntoa(in_addr);
+}
+
+int string_to_ip(const char *string, struct sockaddr_storage *ss) {
+ struct addrinfo hints, *ai;
+ int ret;
+
+ if (ss == NULL) {
+ return -EFAULT;
+ }
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_flags = AI_NUMERICHOST;
+ hints.ai_socktype = SOCK_DGRAM;
+
+ ret = getaddrinfo(string, NULL, &hints, &ai);
+ if (ret == 0) {
+ memcpy(ss, ai->ai_addr, ai->ai_addrlen);
+ freeaddrinfo(ai);
+ }
+
+ return ret;
+}
+
+int ifc_init(void)
+{
+ int ret;
+ if (ifc_ctl_sock == -1) {
+ ifc_ctl_sock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+ if (ifc_ctl_sock < 0) {
+ printerr("socket() failed: %s\n", strerror(errno));
+ }
+ }
+
+ ret = ifc_ctl_sock < 0 ? -1 : 0;
+ if (DBG) printerr("ifc_init_returning %d", ret);
+ return ret;
+}
+
+int ifc_init6(void)
+{
+ if (ifc_ctl_sock6 == -1) {
+ ifc_ctl_sock6 = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+ if (ifc_ctl_sock6 < 0) {
+ printerr("socket() failed: %s\n", strerror(errno));
+ }
+ }
+ return ifc_ctl_sock6 < 0 ? -1 : 0;
+}
+
+void ifc_close(void)
+{
+ if (DBG) printerr("ifc_close");
+ if (ifc_ctl_sock != -1) {
+ (void)close(ifc_ctl_sock);
+ ifc_ctl_sock = -1;
+ }
+}
+
+void ifc_close6(void)
+{
+ if (ifc_ctl_sock6 != -1) {
+ (void)close(ifc_ctl_sock6);
+ ifc_ctl_sock6 = -1;
+ }
+}
+
+static void ifc_init_ifr(const char *name, struct ifreq *ifr)
+{
+ memset(ifr, 0, sizeof(struct ifreq));
+ strncpy(ifr->ifr_name, name, IFNAMSIZ);
+ ifr->ifr_name[IFNAMSIZ - 1] = 0;
+}
+
+int ifc_get_hwaddr(const char *name, void *ptr)
+{
+ int r;
+ struct ifreq ifr;
+ ifc_init_ifr(name, &ifr);
+
+ r = ioctl(ifc_ctl_sock, SIOCGIFHWADDR, &ifr);
+ if(r < 0) return -1;
+
+ memcpy(ptr, &ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+ return 0;
+}
+
+int ifc_get_ifindex(const char *name, int *if_indexp)
+{
+ int r;
+ struct ifreq ifr;
+ ifc_init_ifr(name, &ifr);
+
+ r = ioctl(ifc_ctl_sock, SIOCGIFINDEX, &ifr);
+ if(r < 0) return -1;
+
+ *if_indexp = ifr.ifr_ifindex;
+ return 0;
+}
+
+static int ifc_set_flags(const char *name, unsigned set, unsigned clr)
+{
+ struct ifreq ifr;
+ ifc_init_ifr(name, &ifr);
+
+ if(ioctl(ifc_ctl_sock, SIOCGIFFLAGS, &ifr) < 0) return -1;
+ ifr.ifr_flags = (ifr.ifr_flags & (~clr)) | set;
+ return ioctl(ifc_ctl_sock, SIOCSIFFLAGS, &ifr);
+}
+
+int ifc_up(const char *name)
+{
+ int ret = ifc_set_flags(name, IFF_UP, 0);
+ if (DBG) printerr("ifc_up(%s) = %d", name, ret);
+ return ret;
+}
+
+int ifc_down(const char *name)
+{
+ int ret = ifc_set_flags(name, 0, IFF_UP);
+ if (DBG) printerr("ifc_down(%s) = %d", name, ret);
+ return ret;
+}
+
+static void init_sockaddr_in(struct sockaddr *sa, in_addr_t addr)
+{
+ struct sockaddr_in *sin = (struct sockaddr_in *) sa;
+ sin->sin_family = AF_INET;
+ sin->sin_port = 0;
+ sin->sin_addr.s_addr = addr;
+}
+
+int ifc_set_addr(const char *name, in_addr_t addr)
+{
+ struct ifreq ifr;
+ int ret;
+
+ ifc_init_ifr(name, &ifr);
+ init_sockaddr_in(&ifr.ifr_addr, addr);
+
+ ret = ioctl(ifc_ctl_sock, SIOCSIFADDR, &ifr);
+ if (DBG) printerr("ifc_set_addr(%s, xx) = %d", name, ret);
+ return ret;
+}
+
+/*
+ * Adds or deletes an IP address on an interface.
+ *
+ * Action is one of:
+ * - RTM_NEWADDR (to add a new address)
+ * - RTM_DELADDR (to delete an existing address)
+ *
+ * Returns zero on success and negative errno on failure.
+ */
+int ifc_act_on_address(int action, const char *name, const char *address,
+ int prefixlen) {
+ int ifindex, s, len, ret;
+ struct sockaddr_storage ss;
+ void *addr;
+ size_t addrlen;
+ struct {
+ struct nlmsghdr n;
+ struct ifaddrmsg r;
+ // Allow for IPv6 address, headers, and padding.
+ char attrbuf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
+ NLMSG_ALIGN(sizeof(struct rtattr)) +
+ NLMSG_ALIGN(INET6_ADDRLEN)];
+ } req;
+ struct rtattr *rta;
+ struct nlmsghdr *nh;
+ struct nlmsgerr *err;
+ char buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
+ NLMSG_ALIGN(sizeof(struct nlmsgerr)) +
+ NLMSG_ALIGN(sizeof(struct nlmsghdr))];
+
+ // Get interface ID.
+ ifindex = if_nametoindex(name);
+ if (ifindex == 0) {
+ return -errno;
+ }
+
+ // Convert string representation to sockaddr_storage.
+ ret = string_to_ip(address, &ss);
+ if (ret) {
+ return ret;
+ }
+
+ // Determine address type and length.
+ if (ss.ss_family == AF_INET) {
+ struct sockaddr_in *sin = (struct sockaddr_in *) &ss;
+ addr = &sin->sin_addr;
+ addrlen = INET_ADDRLEN;
+ } else if (ss.ss_family == AF_INET6) {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &ss;
+ addr = &sin6->sin6_addr;
+ addrlen = INET6_ADDRLEN;
+ } else {
+ return -EAFNOSUPPORT;
+ }
+
+ // Fill in netlink structures.
+ memset(&req, 0, sizeof(req));
+
+ // Netlink message header.
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.r));
+ req.n.nlmsg_type = action;
+ req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ req.n.nlmsg_pid = getpid();
+
+ // Interface address message header.
+ req.r.ifa_family = ss.ss_family;
+ req.r.ifa_prefixlen = prefixlen;
+ req.r.ifa_index = ifindex;
+
+ // Routing attribute. Contains the actual IP address.
+ rta = (struct rtattr *) (((char *) &req) + NLMSG_ALIGN(req.n.nlmsg_len));
+ rta->rta_type = IFA_LOCAL;
+ rta->rta_len = RTA_LENGTH(addrlen);
+ req.n.nlmsg_len = NLMSG_ALIGN(req.n.nlmsg_len) + RTA_LENGTH(addrlen);
+ memcpy(RTA_DATA(rta), addr, addrlen);
+
+ s = socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
+ if (send(s, &req, req.n.nlmsg_len, 0) < 0) {
+ close(s);
+ return -errno;
+ }
+
+ len = recv(s, buf, sizeof(buf), 0);
+ close(s);
+ if (len < 0) {
+ return -errno;
+ }
+
+ // Parse the acknowledgement to find the return code.
+ nh = (struct nlmsghdr *) buf;
+ if (!NLMSG_OK(nh, (unsigned) len) || nh->nlmsg_type != NLMSG_ERROR) {
+ return -EINVAL;
+ }
+ err = NLMSG_DATA(nh);
+
+ // Return code is negative errno.
+ return err->error;
+}
+
+int ifc_add_address(const char *name, const char *address, int prefixlen) {
+ return ifc_act_on_address(RTM_NEWADDR, name, address, prefixlen);
+}
+
+int ifc_del_address(const char *name, const char * address, int prefixlen) {
+ return ifc_act_on_address(RTM_DELADDR, name, address, prefixlen);
+}
+
+/*
+ * Clears IPv6 addresses on the specified interface.
+ */
+int ifc_clear_ipv6_addresses(const char *name) {
+ char rawaddrstr[INET6_ADDRSTRLEN], addrstr[INET6_ADDRSTRLEN];
+ unsigned int prefixlen;
+ int lasterror = 0, i, j, ret;
+ char ifname[64]; // Currently, IFNAMSIZ = 16.
+ FILE *f = fopen("/proc/net/if_inet6", "r");
+ if (!f) {
+ return -errno;
+ }
+
+ // Format:
+ // 20010db8000a0001fc446aa4b5b347ed 03 40 00 01 wlan0
+ while (fscanf(f, "%32s %*02x %02x %*02x %*02x %63s\n",
+ rawaddrstr, &prefixlen, ifname) == 3) {
+ // Is this the interface we're looking for?
+ if (strcmp(name, ifname)) {
+ continue;
+ }
+
+ // Put the colons back into the address.
+ for (i = 0, j = 0; i < 32; i++, j++) {
+ addrstr[j] = rawaddrstr[i];
+ if (i % 4 == 3) {
+ addrstr[++j] = ':';
+ }
+ }
+ addrstr[j - 1] = '\0';
+
+ // Don't delete the link-local address as well, or it will disable IPv6
+ // on the interface.
+ if (strncmp(addrstr, "fe80:", 5) == 0) {
+ continue;
+ }
+
+ ret = ifc_del_address(ifname, addrstr, prefixlen);
+ if (ret) {
+ ALOGE("Deleting address %s/%d on %s: %s", addrstr, prefixlen, ifname,
+ strerror(-ret));
+ lasterror = ret;
+ }
+ }
+
+ fclose(f);
+ return lasterror;
+}
+
+/*
+ * Clears IPv4 addresses on the specified interface.
+ */
+void ifc_clear_ipv4_addresses(const char *name) {
+ unsigned count, addr;
+ ifc_init();
+ for (count=0, addr=1;((addr != 0) && (count < 255)); count++) {
+ if (ifc_get_addr(name, &addr) < 0)
+ break;
+ if (addr)
+ ifc_set_addr(name, 0);
+ }
+ ifc_close();
+}
+
+/*
+ * Clears all IP addresses on the specified interface.
+ */
+int ifc_clear_addresses(const char *name) {
+ ifc_clear_ipv4_addresses(name);
+ return ifc_clear_ipv6_addresses(name);
+}
+
+int ifc_set_hwaddr(const char *name, const void *ptr)
+{
+ struct ifreq ifr;
+ ifc_init_ifr(name, &ifr);
+
+ ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER;
+ memcpy(&ifr.ifr_hwaddr.sa_data, ptr, ETH_ALEN);
+ return ioctl(ifc_ctl_sock, SIOCSIFHWADDR, &ifr);
+}
+
+int ifc_set_mask(const char *name, in_addr_t mask)
+{
+ struct ifreq ifr;
+ int ret;
+
+ ifc_init_ifr(name, &ifr);
+ init_sockaddr_in(&ifr.ifr_addr, mask);
+
+ ret = ioctl(ifc_ctl_sock, SIOCSIFNETMASK, &ifr);
+ if (DBG) printerr("ifc_set_mask(%s, xx) = %d", name, ret);
+ return ret;
+}
+
+int ifc_set_prefixLength(const char *name, int prefixLength)
+{
+ struct ifreq ifr;
+ // TODO - support ipv6
+ if (prefixLength > 32 || prefixLength < 0) return -1;
+
+ in_addr_t mask = prefixLengthToIpv4Netmask(prefixLength);
+ ifc_init_ifr(name, &ifr);
+ init_sockaddr_in(&ifr.ifr_addr, mask);
+
+ return ioctl(ifc_ctl_sock, SIOCSIFNETMASK, &ifr);
+}
+
+int ifc_get_addr(const char *name, in_addr_t *addr)
+{
+ struct ifreq ifr;
+ int ret = 0;
+
+ ifc_init_ifr(name, &ifr);
+ if (addr != NULL) {
+ ret = ioctl(ifc_ctl_sock, SIOCGIFADDR, &ifr);
+ if (ret < 0) {
+ *addr = 0;
+ } else {
+ *addr = ((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr.s_addr;
+ }
+ }
+ return ret;
+}
+
+int ifc_get_info(const char *name, in_addr_t *addr, int *prefixLength, unsigned *flags)
+{
+ struct ifreq ifr;
+ ifc_init_ifr(name, &ifr);
+
+ if (addr != NULL) {
+ if(ioctl(ifc_ctl_sock, SIOCGIFADDR, &ifr) < 0) {
+ *addr = 0;
+ } else {
+ *addr = ((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr.s_addr;
+ }
+ }
+
+ if (prefixLength != NULL) {
+ if(ioctl(ifc_ctl_sock, SIOCGIFNETMASK, &ifr) < 0) {
+ *prefixLength = 0;
+ } else {
+ *prefixLength = ipv4NetmaskToPrefixLength(
+ ((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr.s_addr);
+ }
+ }
+
+ if (flags != NULL) {
+ if(ioctl(ifc_ctl_sock, SIOCGIFFLAGS, &ifr) < 0) {
+ *flags = 0;
+ } else {
+ *flags = ifr.ifr_flags;
+ }
+ }
+
+ return 0;
+}
+
+int ifc_act_on_ipv4_route(int action, const char *ifname, struct in_addr dst, int prefix_length,
+ struct in_addr gw)
+{
+ struct rtentry rt;
+ int result;
+ in_addr_t netmask;
+
+ memset(&rt, 0, sizeof(rt));
+
+ rt.rt_dst.sa_family = AF_INET;
+ rt.rt_dev = (void*) ifname;
+
+ netmask = prefixLengthToIpv4Netmask(prefix_length);
+ init_sockaddr_in(&rt.rt_genmask, netmask);
+ init_sockaddr_in(&rt.rt_dst, dst.s_addr);
+ rt.rt_flags = RTF_UP;
+
+ if (prefix_length == 32) {
+ rt.rt_flags |= RTF_HOST;
+ }
+
+ if (gw.s_addr != 0) {
+ rt.rt_flags |= RTF_GATEWAY;
+ init_sockaddr_in(&rt.rt_gateway, gw.s_addr);
+ }
+
+ ifc_init();
+
+ if (ifc_ctl_sock < 0) {
+ return -errno;
+ }
+
+ result = ioctl(ifc_ctl_sock, action, &rt);
+ if (result < 0) {
+ if (errno == EEXIST) {
+ result = 0;
+ } else {
+ result = -errno;
+ }
+ }
+ ifc_close();
+ return result;
+}
+
+/* deprecated - v4 only */
+int ifc_create_default_route(const char *name, in_addr_t gw)
+{
+ struct in_addr in_dst, in_gw;
+
+ in_dst.s_addr = 0;
+ in_gw.s_addr = gw;
+
+ int ret = ifc_act_on_ipv4_route(SIOCADDRT, name, in_dst, 0, in_gw);
+ if (DBG) printerr("ifc_create_default_route(%s, %d) = %d", name, gw, ret);
+ return ret;
+}
+
+// Needed by code in hidden partner repositories / branches, so don't delete.
+int ifc_enable(const char *ifname)
+{
+ int result;
+
+ ifc_init();
+ result = ifc_up(ifname);
+ ifc_close();
+ return result;
+}
+
+// Needed by code in hidden partner repositories / branches, so don't delete.
+int ifc_disable(const char *ifname)
+{
+ unsigned addr, count;
+ int result;
+
+ ifc_init();
+ result = ifc_down(ifname);
+
+ ifc_set_addr(ifname, 0);
+ for (count=0, addr=1;((addr != 0) && (count < 255)); count++) {
+ if (ifc_get_addr(ifname, &addr) < 0)
+ break;
+ if (addr)
+ ifc_set_addr(ifname, 0);
+ }
+
+ ifc_close();
+ return result;
+}
+
+int ifc_reset_connections(const char *ifname, const int reset_mask)
+{
+#ifdef HAVE_ANDROID_OS
+ int result, success;
+ in_addr_t myaddr = 0;
+ struct ifreq ifr;
+ struct in6_ifreq ifr6;
+
+ if (reset_mask & RESET_IPV4_ADDRESSES) {
+ /* IPv4. Clear connections on the IP address. */
+ ifc_init();
+ if (!(reset_mask & RESET_IGNORE_INTERFACE_ADDRESS)) {
+ ifc_get_info(ifname, &myaddr, NULL, NULL);
+ }
+ ifc_init_ifr(ifname, &ifr);
+ init_sockaddr_in(&ifr.ifr_addr, myaddr);
+ result = ioctl(ifc_ctl_sock, SIOCKILLADDR, &ifr);
+ ifc_close();
+ } else {
+ result = 0;
+ }
+
+ if (reset_mask & RESET_IPV6_ADDRESSES) {
+ /*
+ * IPv6. On Linux, when an interface goes down it loses all its IPv6
+ * addresses, so we don't know which connections belonged to that interface
+ * So we clear all unused IPv6 connections on the device by specifying an
+ * empty IPv6 address.
+ */
+ ifc_init6();
+ // This implicitly specifies an address of ::, i.e., kill all IPv6 sockets.
+ memset(&ifr6, 0, sizeof(ifr6));
+ success = ioctl(ifc_ctl_sock6, SIOCKILLADDR, &ifr6);
+ if (result == 0) {
+ result = success;
+ }
+ ifc_close6();
+ }
+
+ return result;
+#else
+ return 0;
+#endif
+}
+
+/*
+ * Removes the default route for the named interface.
+ */
+int ifc_remove_default_route(const char *ifname)
+{
+ struct rtentry rt;
+ int result;
+
+ ifc_init();
+ memset(&rt, 0, sizeof(rt));
+ rt.rt_dev = (void *)ifname;
+ rt.rt_flags = RTF_UP|RTF_GATEWAY;
+ init_sockaddr_in(&rt.rt_dst, 0);
+ if ((result = ioctl(ifc_ctl_sock, SIOCDELRT, &rt)) < 0) {
+ ALOGD("failed to remove default route for %s: %s", ifname, strerror(errno));
+ }
+ ifc_close();
+ return result;
+}
+
+int
+ifc_configure(const char *ifname,
+ in_addr_t address,
+ uint32_t prefixLength,
+ in_addr_t gateway,
+ in_addr_t dns1,
+ in_addr_t dns2) {
+
+ char dns_prop_name[PROPERTY_KEY_MAX];
+
+ ifc_init();
+
+ if (ifc_up(ifname)) {
+ printerr("failed to turn on interface %s: %s\n", ifname, strerror(errno));
+ ifc_close();
+ return -1;
+ }
+ if (ifc_set_addr(ifname, address)) {
+ printerr("failed to set ipaddr %s: %s\n", ipaddr_to_string(address), strerror(errno));
+ ifc_close();
+ return -1;
+ }
+ if (ifc_set_prefixLength(ifname, prefixLength)) {
+ printerr("failed to set prefixLength %d: %s\n", prefixLength, strerror(errno));
+ ifc_close();
+ return -1;
+ }
+ if (ifc_create_default_route(ifname, gateway)) {
+ printerr("failed to set default route %s: %s\n", ipaddr_to_string(gateway), strerror(errno));
+ ifc_close();
+ return -1;
+ }
+
+ ifc_close();
+
+ snprintf(dns_prop_name, sizeof(dns_prop_name), "net.%s.dns1", ifname);
+ property_set(dns_prop_name, dns1 ? ipaddr_to_string(dns1) : "");
+ snprintf(dns_prop_name, sizeof(dns_prop_name), "net.%s.dns2", ifname);
+ property_set(dns_prop_name, dns2 ? ipaddr_to_string(dns2) : "");
+
+ return 0;
+}
diff --git a/libnetutils/packet.c b/libnetutils/packet.c
new file mode 100644
index 0000000..cd26d05
--- /dev/null
+++ b/libnetutils/packet.c
@@ -0,0 +1,242 @@
+/*
+ * 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 <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/uio.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include <linux/if_packet.h>
+#include <linux/if_ether.h>
+#include <errno.h>
+
+#ifdef ANDROID
+#define LOG_TAG "DHCP"
+#include <cutils/log.h>
+#else
+#include <stdio.h>
+#include <string.h>
+#define ALOGD printf
+#define ALOGW printf
+#endif
+
+#include "dhcpmsg.h"
+
+int fatal();
+
+int open_raw_socket(const char *ifname __attribute__((unused)), uint8_t *hwaddr, int if_index)
+{
+ int s;
+ struct sockaddr_ll bindaddr;
+
+ if((s = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) {
+ return fatal("socket(PF_PACKET)");
+ }
+
+ memset(&bindaddr, 0, sizeof(bindaddr));
+ bindaddr.sll_family = AF_PACKET;
+ bindaddr.sll_protocol = htons(ETH_P_IP);
+ bindaddr.sll_halen = ETH_ALEN;
+ memcpy(bindaddr.sll_addr, hwaddr, ETH_ALEN);
+ bindaddr.sll_ifindex = if_index;
+
+ if (bind(s, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) < 0) {
+ return fatal("Cannot bind raw socket to interface");
+ }
+
+ return s;
+}
+
+static uint32_t checksum(void *buffer, unsigned int count, uint32_t startsum)
+{
+ uint16_t *up = (uint16_t *)buffer;
+ uint32_t sum = startsum;
+ uint32_t upper16;
+
+ while (count > 1) {
+ sum += *up++;
+ count -= 2;
+ }
+ if (count > 0) {
+ sum += (uint16_t) *(uint8_t *)up;
+ }
+ while ((upper16 = (sum >> 16)) != 0) {
+ sum = (sum & 0xffff) + upper16;
+ }
+ return sum;
+}
+
+static uint32_t finish_sum(uint32_t sum)
+{
+ return ~sum & 0xffff;
+}
+
+int send_packet(int s, int if_index, struct dhcp_msg *msg, int size,
+ uint32_t saddr, uint32_t daddr, uint32_t sport, uint32_t dport)
+{
+ struct iphdr ip;
+ struct udphdr udp;
+ struct iovec iov[3];
+ uint32_t udpsum;
+ uint16_t temp;
+ struct msghdr msghdr;
+ struct sockaddr_ll destaddr;
+
+ ip.version = IPVERSION;
+ ip.ihl = sizeof(ip) >> 2;
+ ip.tos = 0;
+ ip.tot_len = htons(sizeof(ip) + sizeof(udp) + size);
+ ip.id = 0;
+ ip.frag_off = 0;
+ ip.ttl = IPDEFTTL;
+ ip.protocol = IPPROTO_UDP;
+ ip.check = 0;
+ ip.saddr = saddr;
+ ip.daddr = daddr;
+ ip.check = finish_sum(checksum(&ip, sizeof(ip), 0));
+
+ udp.source = htons(sport);
+ udp.dest = htons(dport);
+ udp.len = htons(sizeof(udp) + size);
+ udp.check = 0;
+
+ /* Calculate checksum for pseudo header */
+ udpsum = checksum(&ip.saddr, sizeof(ip.saddr), 0);
+ udpsum = checksum(&ip.daddr, sizeof(ip.daddr), udpsum);
+ temp = htons(IPPROTO_UDP);
+ udpsum = checksum(&temp, sizeof(temp), udpsum);
+ temp = udp.len;
+ udpsum = checksum(&temp, sizeof(temp), udpsum);
+
+ /* Add in the checksum for the udp header */
+ udpsum = checksum(&udp, sizeof(udp), udpsum);
+
+ /* Add in the checksum for the data */
+ udpsum = checksum(msg, size, udpsum);
+ udp.check = finish_sum(udpsum);
+
+ iov[0].iov_base = (char *)&ip;
+ iov[0].iov_len = sizeof(ip);
+ iov[1].iov_base = (char *)&udp;
+ iov[1].iov_len = sizeof(udp);
+ iov[2].iov_base = (char *)msg;
+ iov[2].iov_len = size;
+ memset(&destaddr, 0, sizeof(destaddr));
+ destaddr.sll_family = AF_PACKET;
+ destaddr.sll_protocol = htons(ETH_P_IP);
+ destaddr.sll_ifindex = if_index;
+ destaddr.sll_halen = ETH_ALEN;
+ memcpy(destaddr.sll_addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN);
+
+ msghdr.msg_name = &destaddr;
+ msghdr.msg_namelen = sizeof(destaddr);
+ msghdr.msg_iov = iov;
+ msghdr.msg_iovlen = sizeof(iov) / sizeof(struct iovec);
+ msghdr.msg_flags = 0;
+ msghdr.msg_control = 0;
+ msghdr.msg_controllen = 0;
+ return sendmsg(s, &msghdr, 0);
+}
+
+int receive_packet(int s, struct dhcp_msg *msg)
+{
+ int nread;
+ int is_valid;
+ struct dhcp_packet {
+ struct iphdr ip;
+ struct udphdr udp;
+ struct dhcp_msg dhcp;
+ } packet;
+ int dhcp_size;
+ uint32_t sum;
+ uint16_t temp;
+ uint32_t saddr, daddr;
+
+ nread = read(s, &packet, sizeof(packet));
+ if (nread < 0) {
+ return -1;
+ }
+ /*
+ * The raw packet interface gives us all packets received by the
+ * network interface. We need to filter out all packets that are
+ * not meant for us.
+ */
+ is_valid = 0;
+ if (nread < (int)(sizeof(struct iphdr) + sizeof(struct udphdr))) {
+#if VERBOSE
+ ALOGD("Packet is too small (%d) to be a UDP datagram", nread);
+#endif
+ } else if (packet.ip.version != IPVERSION || packet.ip.ihl != (sizeof(packet.ip) >> 2)) {
+#if VERBOSE
+ ALOGD("Not a valid IP packet");
+#endif
+ } else if (nread < ntohs(packet.ip.tot_len)) {
+#if VERBOSE
+ ALOGD("Packet was truncated (read %d, needed %d)", nread, ntohs(packet.ip.tot_len));
+#endif
+ } else if (packet.ip.protocol != IPPROTO_UDP) {
+#if VERBOSE
+ ALOGD("IP protocol (%d) is not UDP", packet.ip.protocol);
+#endif
+ } else if (packet.udp.dest != htons(PORT_BOOTP_CLIENT)) {
+#if VERBOSE
+ ALOGD("UDP dest port (%d) is not DHCP client", ntohs(packet.udp.dest));
+#endif
+ } else {
+ is_valid = 1;
+ }
+
+ if (!is_valid) {
+ return -1;
+ }
+
+ /* Seems like it's probably a valid DHCP packet */
+ /* validate IP header checksum */
+ sum = finish_sum(checksum(&packet.ip, sizeof(packet.ip), 0));
+ if (sum != 0) {
+ ALOGW("IP header checksum failure (0x%x)", packet.ip.check);
+ return -1;
+ }
+ /*
+ * Validate the UDP checksum.
+ * Since we don't need the IP header anymore, we "borrow" it
+ * to construct the pseudo header used in the checksum calculation.
+ */
+ dhcp_size = ntohs(packet.udp.len) - sizeof(packet.udp);
+ saddr = packet.ip.saddr;
+ daddr = packet.ip.daddr;
+ nread = ntohs(packet.ip.tot_len);
+ memset(&packet.ip, 0, sizeof(packet.ip));
+ packet.ip.saddr = saddr;
+ packet.ip.daddr = daddr;
+ packet.ip.protocol = IPPROTO_UDP;
+ packet.ip.tot_len = packet.udp.len;
+ temp = packet.udp.check;
+ packet.udp.check = 0;
+ sum = finish_sum(checksum(&packet, nread, 0));
+ packet.udp.check = temp;
+ if (!sum)
+ sum = finish_sum(sum);
+ if (temp != sum) {
+ ALOGW("UDP header checksum failure (0x%x should be 0x%x)", sum, temp);
+ return -1;
+ }
+ memcpy(msg, &packet.dhcp, dhcp_size);
+ return dhcp_size;
+}
diff --git a/libnetutils/packet.h b/libnetutils/packet.h
new file mode 100644
index 0000000..aade392
--- /dev/null
+++ b/libnetutils/packet.h
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+#ifndef _WIFI_PACKET_H_
+#define _WIFI_PACKET_H_
+
+int open_raw_socket(const char *ifname, uint8_t *hwaddr, int if_index);
+int send_packet(int s, int if_index, struct dhcp_msg *msg, int size,
+ uint32_t saddr, uint32_t daddr, uint32_t sport, uint32_t dport);
+int receive_packet(int s, struct dhcp_msg *msg);
+
+#endif
diff --git a/libpixelflinger/Android.mk b/libpixelflinger/Android.mk
new file mode 100644
index 0000000..697db25
--- /dev/null
+++ b/libpixelflinger/Android.mk
@@ -0,0 +1,79 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+#
+# C/C++ and ARMv5 objects
+#
+
+include $(CLEAR_VARS)
+PIXELFLINGER_SRC_FILES:= \
+ codeflinger/ARMAssemblerInterface.cpp \
+ codeflinger/ARMAssemblerProxy.cpp \
+ codeflinger/CodeCache.cpp \
+ codeflinger/GGLAssembler.cpp \
+ codeflinger/load_store.cpp \
+ codeflinger/blending.cpp \
+ codeflinger/texturing.cpp \
+ codeflinger/tinyutils/SharedBuffer.cpp \
+ codeflinger/tinyutils/VectorImpl.cpp \
+ fixed.cpp.arm \
+ picker.cpp.arm \
+ pixelflinger.cpp.arm \
+ trap.cpp.arm \
+ scanline.cpp.arm \
+ format.cpp \
+ clear.cpp \
+ raster.cpp \
+ buffer.cpp
+
+PIXELFLINGER_CFLAGS := -fstrict-aliasing -fomit-frame-pointer
+
+PIXELFLINGER_SRC_FILES_arm := \
+ codeflinger/ARMAssembler.cpp \
+ codeflinger/disassem.c \
+ col32cb16blend.S \
+ t32cb16blend.S \
+
+ifeq ($(ARCH_ARM_HAVE_NEON),true)
+PIXELFLINGER_SRC_FILES_arm += col32cb16blend_neon.S
+PIXELFLINGER_CFLAGS_arm += -D__ARM_HAVE_NEON
+endif
+
+PIXELFLINGER_SRC_FILES_arm64 := \
+ codeflinger/Arm64Assembler.cpp \
+ codeflinger/Arm64Disassembler.cpp \
+ arch-arm64/col32cb16blend.S \
+ arch-arm64/t32cb16blend.S \
+
+ifndef ARCH_MIPS_REV6
+PIXELFLINGER_SRC_FILES_mips := \
+ codeflinger/MIPSAssembler.cpp \
+ codeflinger/mips_disassem.c \
+ arch-mips/t32cb16blend.S \
+
+endif
+#
+# Shared library
+#
+
+LOCAL_MODULE:= libpixelflinger
+LOCAL_SRC_FILES := $(PIXELFLINGER_SRC_FILES)
+LOCAL_SRC_FILES_arm := $(PIXELFLINGER_SRC_FILES_arm)
+LOCAL_SRC_FILES_arm64 := $(PIXELFLINGER_SRC_FILES_arm64)
+LOCAL_SRC_FILES_mips := $(PIXELFLINGER_SRC_FILES_mips)
+LOCAL_CFLAGS := $(PIXELFLINGER_CFLAGS)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES += $(LOCAL_EXPORT_C_INCLUDE_DIRS)
+LOCAL_SHARED_LIBRARIES := libcutils liblog
+
+# Really this should go away entirely or at least not depend on
+# libhardware, but this at least gets us built.
+LOCAL_SHARED_LIBRARIES += libhardware_legacy
+LOCAL_CFLAGS += -DWITH_LIB_HARDWARE
+# t32cb16blend.S does not compile with Clang.
+LOCAL_CLANG_ASFLAGS_arm += -no-integrated-as
+# arch-arm64/col32cb16blend.S does not compile with Clang.
+LOCAL_CLANG_ASFLAGS_arm64 += -no-integrated-as
+include $(BUILD_SHARED_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/libpixelflinger/MODULE_LICENSE_APACHE2 b/libpixelflinger/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libpixelflinger/MODULE_LICENSE_APACHE2
diff --git a/libpixelflinger/NOTICE b/libpixelflinger/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/libpixelflinger/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/libpixelflinger/arch-arm64/col32cb16blend.S b/libpixelflinger/arch-arm64/col32cb16blend.S
new file mode 100644
index 0000000..18a01fd
--- /dev/null
+++ b/libpixelflinger/arch-arm64/col32cb16blend.S
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+ .text
+ .align
+
+ .global scanline_col32cb16blend_arm64
+
+//
+// This function alpha blends a fixed color into a destination scanline, using
+// the formula:
+//
+// d = s + (((a + (a >> 7)) * d) >> 8)
+//
+// where d is the destination pixel,
+// s is the source color,
+// a is the alpha channel of the source color.
+//
+
+// x0 = destination buffer pointer
+// w1 = color value
+// w2 = count
+
+
+scanline_col32cb16blend_arm64:
+
+ lsr w5, w1, #24 // shift down alpha
+ mov w9, #0xff // create mask
+ add w5, w5, w5, lsr #7 // add in top bit
+ mov w4, #256 // create #0x100
+ sub w5, w4, w5 // invert alpha
+ and w10, w1, #0xff // extract red
+ and w12, w9, w1, lsr #8 // extract green
+ and w4, w9, w1, lsr #16 // extract blue
+ lsl w10, w10, #5 // prescale red
+ lsl w12, w12, #6 // prescale green
+ lsl w4, w4, #5 // prescale blue
+ lsr w9, w9, #2 // create dest green mask
+
+1:
+ ldrh w8, [x0] // load dest pixel
+ subs w2, w2, #1 // decrement loop counter
+ lsr w6, w8, #11 // extract dest red
+ and w7, w9, w8, lsr #5 // extract dest green
+ and w8, w8, #0x1f // extract dest blue
+
+ madd w6, w6, w5, w10 // dest red * alpha + src red
+ madd w7, w7, w5, w12 // dest green * alpha + src green
+ madd w8, w8, w5, w4 // dest blue * alpha + src blue
+
+ lsr w6, w6, #8 // shift down red
+ lsr w7, w7, #8 // shift down green
+ lsl w6, w6, #11 // shift red into 565
+ orr w6, w6, w7, lsl #5 // shift green into 565
+ orr w6, w6, w8, lsr #8 // shift blue into 565
+
+ strh w6, [x0], #2 // store pixel to dest, update ptr
+ b.ne 1b // if count != 0, loop
+
+ ret
+
+
+
diff --git a/libpixelflinger/arch-arm64/t32cb16blend.S b/libpixelflinger/arch-arm64/t32cb16blend.S
new file mode 100644
index 0000000..7da8cf5
--- /dev/null
+++ b/libpixelflinger/arch-arm64/t32cb16blend.S
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+ .text
+ .align
+
+ .global scanline_t32cb16blend_arm64
+
+/*
+ * .macro pixel
+ *
+ * This macro alpha blends RGB565 original pixel located in either
+ * top or bottom 16 bits of DREG register with SRC 32 bit pixel value
+ * and writes the result to FB register
+ *
+ * \DREG is a 32-bit register containing *two* original destination RGB565
+ * pixels, with the even one in the low-16 bits, and the odd one in the
+ * high 16 bits.
+ *
+ * \SRC is a 32-bit 0xAABBGGRR pixel value, with pre-multiplied colors.
+ *
+ * \FB is a target register that will contain the blended pixel values.
+ *
+ * \ODD is either 0 or 1 and indicates if we're blending the lower or
+ * upper 16-bit pixels in DREG into FB
+ *
+ *
+ * clobbered: w6, w7, w16, w17, w18
+ *
+ */
+
+.macro pixel, DREG, SRC, FB, ODD
+
+ // SRC = 0xAABBGGRR
+ lsr w7, \SRC, #24 // sA
+ add w7, w7, w7, lsr #7 // sA + (sA >> 7)
+ mov w6, #0x100
+ sub w7, w6, w7 // sA = 0x100 - (sA+(sA>>7))
+
+1:
+
+.if \ODD //Blending odd pixel present in top 16 bits of DREG register
+
+ // red
+ lsr w16, \DREG, #(16 + 11)
+ mul w16, w7, w16
+ lsr w6, \SRC, #3
+ and w6, w6, #0x1F
+ add w16, w6, w16, lsr #8
+ cmp w16, #0x1F
+ orr w17, \FB, #(0x1F<<(16 + 11))
+ orr w18, \FB, w16, lsl #(16 + 11)
+ csel \FB, w17, w18, hi
+ // green
+ and w6, \DREG, #(0x3F<<(16 + 5))
+ lsr w17,w6,#(16+5)
+ mul w6, w7, w17
+ lsr w16, \SRC, #(8+2)
+ and w16, w16, #0x3F
+ add w6, w16, w6, lsr #8
+ cmp w6, #0x3F
+ orr w17, \FB, #(0x3F<<(16 + 5))
+ orr w18, \FB, w6, lsl #(16 + 5)
+ csel \FB, w17, w18, hi
+ // blue
+ and w16, \DREG, #(0x1F << 16)
+ lsr w17,w16,#16
+ mul w16, w7, w17
+ lsr w6, \SRC, #(8+8+3)
+ and w6, w6, #0x1F
+ add w16, w6, w16, lsr #8
+ cmp w16, #0x1F
+ orr w17, \FB, #(0x1F << 16)
+ orr w18, \FB, w16, lsl #16
+ csel \FB, w17, w18, hi
+
+.else //Blending even pixel present in bottom 16 bits of DREG register
+
+ // red
+ lsr w16, \DREG, #11
+ and w16, w16, #0x1F
+ mul w16, w7, w16
+ lsr w6, \SRC, #3
+ and w6, w6, #0x1F
+ add w16, w6, w16, lsr #8
+ cmp w16, #0x1F
+ mov w17, #(0x1F<<11)
+ lsl w18, w16, #11
+ csel \FB, w17, w18, hi
+
+
+ // green
+ and w6, \DREG, #(0x3F<<5)
+ mul w6, w7, w6
+ lsr w16, \SRC, #(8+2)
+ and w16, w16, #0x3F
+ add w6, w16, w6, lsr #(5+8)
+ cmp w6, #0x3F
+ orr w17, \FB, #(0x3F<<5)
+ orr w18, \FB, w6, lsl #5
+ csel \FB, w17, w18, hi
+
+ // blue
+ and w16, \DREG, #0x1F
+ mul w16, w7, w16
+ lsr w6, \SRC, #(8+8+3)
+ and w6, w6, #0x1F
+ add w16, w6, w16, lsr #8
+ cmp w16, #0x1F
+ orr w17, \FB, #0x1F
+ orr w18, \FB, w16
+ csel \FB, w17, w18, hi
+
+.endif // End of blending even pixel
+
+.endm // End of pixel macro
+
+
+// x0: dst ptr
+// x1: src ptr
+// w2: count
+// w3: d
+// w4: s0
+// w5: s1
+// w6: pixel
+// w7: pixel
+// w8: free
+// w9: free
+// w10: free
+// w11: free
+// w12: scratch
+// w14: pixel
+
+scanline_t32cb16blend_arm64:
+
+ // align DST to 32 bits
+ tst x0, #0x3
+ b.eq aligned
+ subs w2, w2, #1
+ b.lo return
+
+last:
+ ldr w4, [x1], #4
+ ldrh w3, [x0]
+ pixel w3, w4, w12, 0
+ strh w12, [x0], #2
+
+aligned:
+ subs w2, w2, #2
+ b.lo 9f
+
+ // The main loop is unrolled twice and processes 4 pixels
+8:
+ ldp w4,w5, [x1], #8
+ add x0, x0, #4
+ // it's all zero, skip this pixel
+ orr w3, w4, w5
+ cbz w3, 7f
+
+ // load the destination
+ ldr w3, [x0, #-4]
+ // stream the destination
+ pixel w3, w4, w12, 0
+ pixel w3, w5, w12, 1
+ str w12, [x0, #-4]
+
+ // 2nd iteration of the loop, don't stream anything
+ subs w2, w2, #2
+ csel w4, w5, w4, lt
+ blt 9f
+ ldp w4,w5, [x1], #8
+ add x0, x0, #4
+ orr w3, w4, w5
+ cbz w3, 7f
+ ldr w3, [x0, #-4]
+ pixel w3, w4, w12, 0
+ pixel w3, w5, w12, 1
+ str w12, [x0, #-4]
+
+7: subs w2, w2, #2
+ bhs 8b
+ mov w4, w5
+
+9: adds w2, w2, #1
+ b.lo return
+ b last
+
+return:
+ ret
diff --git a/libpixelflinger/arch-mips/t32cb16blend.S b/libpixelflinger/arch-mips/t32cb16blend.S
new file mode 100644
index 0000000..c911fbb
--- /dev/null
+++ b/libpixelflinger/arch-mips/t32cb16blend.S
@@ -0,0 +1,264 @@
+/* libs/pixelflinger/t32cb16blend.S
+**
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifdef DEBUG
+#define DBG
+#else
+#define DBG #
+#endif
+
+/*
+ * blend one of 2 16bpp RGB pixels held in dreg selected by shift
+ * with the 32bpp ABGR pixel held in src and store the result in fb
+ *
+ * Assumes that the dreg data is little endian and that
+ * the the second pixel (shift==16) will be merged into
+ * the fb result
+ *
+ * Uses $t0,$t6,$t7,$t8
+ */
+
+#if __mips==32 && __mips_isa_rev>=2
+ .macro pixel dreg src fb shift
+ /*
+ * sA = s >> 24
+ * f = 0x100 - (sA + (sA>>7))
+ */
+DBG .set noat
+DBG rdhwr $at,$2
+DBG .set at
+
+ srl $t7,\src,24
+ srl $t6,$t7,7
+ addu $t7,$t6
+ li $t6,0x100
+ subu $t7,$t6,$t7
+
+ /* red */
+ ext $t8,\dreg,\shift+6+5,5 # dst[\shift:15..11]
+ mul $t6,$t8,$t7
+ ext $t0,\dreg,\shift+5,6 # start green extraction dst[\shift:10..5]
+ ext $t8,\src,3,5 # src[7..3]
+ srl $t6,8
+ addu $t8,$t6
+ ins \fb,$t8,\shift+6+5,5 # dst[\shift:15..11]
+
+ /* green */
+ mul $t8,$t0,$t7
+ ext $t0,\dreg,\shift,5 # start blue extraction dst[\shift:4..0]
+ ext $t6,\src,2+8,6 # src[15..10]
+ srl $t8,8
+ addu $t8,$t6
+
+ /* blue */
+ mul $t0,$t0,$t7
+ ins \fb,$t8,\shift+5,6 # finish green insertion dst[\shift:10..5]
+ ext $t6,\src,(3+8+8),5
+ srl $t8,$t0,8
+ addu $t8,$t6
+ ins \fb,$t8,\shift,5
+
+DBG .set noat
+DBG rdhwr $t8,$2
+DBG subu $t8,$at
+DBG sltu $at,$t8,$v0
+DBG movn $v0,$t8,$at
+DBG sgtu $at,$t8,$v1
+DBG movn $v1,$t8,$at
+DBG .set at
+ .endm
+
+#else
+
+ .macro pixel dreg src fb shift
+ /*
+ * sA = s >> 24
+ * f = 0x100 - (sA + (sA>>7))
+ */
+DBG .set push
+DBG .set noat
+DBG .set mips32r2
+DBG rdhwr $at,$2
+DBG .set pop
+
+ srl $t7,\src,24
+ srl $t6,$t7,7
+ addu $t7,$t6
+ li $t6,0x100
+ subu $t7,$t6,$t7
+
+ /*
+ * red
+ * dR = (d >> (6 + 5)) & 0x1f;
+ * dR = (f*dR)>>8
+ * sR = (s >> ( 3)) & 0x1f;
+ * sR += dR
+ * fb |= sR << 11
+ */
+ srl $t8,\dreg,\shift+6+5
+.if \shift==0
+ and $t8,0x1f
+.endif
+ mul $t8,$t8,$t7
+ srl $t6,\src,3
+ and $t6,0x1f
+ srl $t8,8
+ addu $t8,$t6
+.if \shift!=0
+ sll $t8,\shift+11
+ or \fb,$t8
+.else
+ sll \fb,$t8,11
+.endif
+
+ /*
+ * green
+ * dG = (d >> 5) & 0x3f
+ * dG = (f*dG) >> 8
+ * sG = (s >> ( 8+2))&0x3F;
+ */
+ srl $t8,\dreg,\shift+5
+ and $t8,0x3f
+ mul $t8,$t8,$t7
+ srl $t6,\src,8+2
+ and $t6,0x3f
+ srl $t8,8
+ addu $t8,$t6
+ sll $t8,\shift + 5
+ or \fb,$t8
+
+ /* blue */
+.if \shift!=0
+ srl $t8,\dreg,\shift
+ and $t8,0x1f
+.else
+ and $t8,\dreg,0x1f
+.endif
+ mul $t8,$t8,$t7
+ srl $t6,\src,(8+8+3)
+ and $t6,0x1f
+ srl $t8,8
+ addu $t8,$t6
+.if \shift!=0
+ sll $t8,\shift
+.endif
+ or \fb,$t8
+DBG .set push
+DBG .set noat
+DBG .set mips32r2
+DBG rdhwr $t8,$2
+DBG subu $t8,$at
+DBG sltu $at,$t8,$v0
+DBG movn $v0,$t8,$at
+DBG sgtu $at,$t8,$v1
+DBG movn $v1,$t8,$at
+DBG .set pop
+ .endm
+#endif
+
+ .text
+ .align
+
+ .global scanline_t32cb16blend_mips
+ .ent scanline_t32cb16blend_mips
+scanline_t32cb16blend_mips:
+DBG li $v0,0xffffffff
+DBG li $v1,0
+ /* Align the destination if necessary */
+ and $t0,$a0,3
+ beqz $t0,aligned
+
+ /* as long as there is at least one pixel */
+ beqz $a2,done
+
+ lw $t4,($a1)
+ addu $a0,2
+ addu $a1,4
+ beqz $t4,1f
+ lhu $t3,-2($a0)
+ pixel $t3,$t4,$t1,0
+ sh $t1,-2($a0)
+1: subu $a2,1
+
+aligned:
+ /* Check to see if its worth unrolling the loop */
+ subu $a2,4
+ bltz $a2,tail
+
+ /* Process 4 pixels at a time */
+fourpixels:
+ /* 1st pair of pixels */
+ lw $t4,0($a1)
+ lw $t5,4($a1)
+ addu $a0,8
+ addu $a1,16
+
+ /* both are zero, skip this pair */
+ or $t3,$t4,$t5
+ beqz $t3,1f
+
+ /* load the destination */
+ lw $t3,-8($a0)
+
+ pixel $t3,$t4,$t1,0
+ pixel $t3,$t5,$t1,16
+ sw $t1,-8($a0)
+
+1:
+ /* 2nd pair of pixels */
+ lw $t4,-8($a1)
+ lw $t5,-4($a1)
+
+ /* both are zero, skip this pair */
+ or $t3,$t4,$t5
+ beqz $t3,1f
+
+ /* load the destination */
+ lw $t3,-4($a0)
+
+ pixel $t3,$t4,$t1,0
+ pixel $t3,$t5,$t1,16
+ sw $t1,-4($a0)
+
+1: subu $a2,4
+ bgtz $a2,fourpixels
+
+tail:
+ /* the pixel count underran, restore it now */
+ addu $a2,4
+
+ /* handle the last 0..3 pixels */
+ beqz $a2,done
+onepixel:
+ lw $t4,($a1)
+ addu $a0,2
+ addu $a1,4
+ beqz $t4,1f
+ lhu $t3,-2($a0)
+ pixel $t3,$t4,$t1,0
+ sh $t1,-2($a0)
+1: subu $a2,1
+ bnez $a2,onepixel
+done:
+DBG .set push
+DBG .set mips32r2
+DBG rdhwr $a0,$3
+DBG mul $v0,$a0
+DBG mul $v1,$a0
+DBG .set pop
+ j $ra
+ .end scanline_t32cb16blend_mips
diff --git a/libpixelflinger/buffer.cpp b/libpixelflinger/buffer.cpp
new file mode 100644
index 0000000..dcb95c5
--- /dev/null
+++ b/libpixelflinger/buffer.cpp
@@ -0,0 +1,384 @@
+/* libs/pixelflinger/buffer.cpp
+**
+** Copyright 2006, 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 <assert.h>
+
+#include "buffer.h"
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+static void read_pixel(const surface_t* s, context_t* c,
+ uint32_t x, uint32_t y, pixel_t* pixel);
+static void write_pixel(const surface_t* s, context_t* c,
+ uint32_t x, uint32_t y, const pixel_t* pixel);
+static void readRGB565(const surface_t* s, context_t* c,
+ uint32_t x, uint32_t y, pixel_t* pixel);
+static void readABGR8888(const surface_t* s, context_t* c,
+ uint32_t x, uint32_t y, pixel_t* pixel);
+
+static uint32_t logic_op(int op, uint32_t s, uint32_t d);
+static uint32_t extract(uint32_t v, int h, int l, int bits);
+static uint32_t expand(uint32_t v, int sbits, int dbits);
+static uint32_t downshift_component(uint32_t in, uint32_t v,
+ int sh, int sl, int dh, int dl, int ch, int cl, int dither);
+
+// ----------------------------------------------------------------------------
+
+void ggl_init_texture(context_t* c)
+{
+ for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
+ texture_t& t = c->state.texture[i];
+ t.s_coord = GGL_ONE_TO_ONE;
+ t.t_coord = GGL_ONE_TO_ONE;
+ t.s_wrap = GGL_REPEAT;
+ t.t_wrap = GGL_REPEAT;
+ t.min_filter = GGL_NEAREST;
+ t.mag_filter = GGL_NEAREST;
+ t.env = GGL_MODULATE;
+ }
+ c->activeTMU = &(c->state.texture[0]);
+}
+
+void ggl_set_surface(context_t* c, surface_t* dst, const GGLSurface* src)
+{
+ dst->width = src->width;
+ dst->height = src->height;
+ dst->stride = src->stride;
+ dst->data = src->data;
+ dst->format = src->format;
+ dst->dirty = 1;
+ if (__builtin_expect(dst->stride < 0, false)) {
+ const GGLFormat& pixelFormat(c->formats[dst->format]);
+ const int32_t bpr = -dst->stride * pixelFormat.size;
+ dst->data += bpr * (dst->height-1);
+ }
+}
+
+static void pick_read_write(surface_t* s)
+{
+ // Choose best reader/writers.
+ switch (s->format) {
+ case GGL_PIXEL_FORMAT_RGBA_8888: s->read = readABGR8888; break;
+ case GGL_PIXEL_FORMAT_RGB_565: s->read = readRGB565; break;
+ default: s->read = read_pixel; break;
+ }
+ s->write = write_pixel;
+}
+
+void ggl_pick_texture(context_t* c)
+{
+ for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; ++i) {
+ surface_t& s = c->state.texture[i].surface;
+ if ((!c->state.texture[i].enable) || (!s.dirty))
+ continue;
+ s.dirty = 0;
+ pick_read_write(&s);
+ generated_tex_vars_t& gen = c->generated_vars.texture[i];
+ gen.width = s.width;
+ gen.height = s.height;
+ gen.stride = s.stride;
+ gen.data = uintptr_t(s.data);
+ }
+}
+
+void ggl_pick_cb(context_t* c)
+{
+ surface_t& s = c->state.buffers.color;
+ if (s.dirty) {
+ s.dirty = 0;
+ pick_read_write(&s);
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+void read_pixel(const surface_t* s, context_t* c,
+ uint32_t x, uint32_t y, pixel_t* pixel)
+{
+ assert((x < s->width) && (y < s->height));
+
+ const GGLFormat* f = &(c->formats[s->format]);
+ int32_t index = x + (s->stride * y);
+ uint8_t* const data = s->data + index * f->size;
+ uint32_t v = 0;
+ switch (f->size) {
+ case 1: v = *data; break;
+ case 2: v = *(uint16_t*)data; break;
+ case 3: v = (data[2]<<16)|(data[1]<<8)|data[0]; break;
+ case 4: v = GGL_RGBA_TO_HOST(*(uint32_t*)data); break;
+ }
+ for (int i=0 ; i<4 ; i++) {
+ pixel->s[i] = f->c[i].h - f->c[i].l;
+ if (pixel->s[i])
+ pixel->c[i] = extract(v, f->c[i].h, f->c[i].l, f->size*8);
+ }
+}
+
+void readRGB565(const surface_t* s, context_t* /*c*/,
+ uint32_t x, uint32_t y, pixel_t* pixel)
+{
+ uint16_t v = *(reinterpret_cast<uint16_t*>(s->data) + (x + (s->stride * y)));
+ pixel->c[0] = 0;
+ pixel->c[1] = v>>11;
+ pixel->c[2] = (v>>5)&0x3F;
+ pixel->c[3] = v&0x1F;
+ pixel->s[0] = 0;
+ pixel->s[1] = 5;
+ pixel->s[2] = 6;
+ pixel->s[3] = 5;
+}
+
+void readABGR8888(const surface_t* s, context_t* /*c*/,
+ uint32_t x, uint32_t y, pixel_t* pixel)
+{
+ uint32_t v = *(reinterpret_cast<uint32_t*>(s->data) + (x + (s->stride * y)));
+ v = GGL_RGBA_TO_HOST(v);
+ pixel->c[0] = v>>24; // A
+ pixel->c[1] = v&0xFF; // R
+ pixel->c[2] = (v>>8)&0xFF; // G
+ pixel->c[3] = (v>>16)&0xFF; // B
+ pixel->s[0] =
+ pixel->s[1] =
+ pixel->s[2] =
+ pixel->s[3] = 8;
+}
+
+void write_pixel(const surface_t* s, context_t* c,
+ uint32_t x, uint32_t y, const pixel_t* pixel)
+{
+ assert((x < s->width) && (y < s->height));
+
+ int dither = -1;
+ if (c->state.enables & GGL_ENABLE_DITHER) {
+ dither = c->ditherMatrix[ (x & GGL_DITHER_MASK) +
+ ((y & GGL_DITHER_MASK)<<GGL_DITHER_ORDER_SHIFT) ];
+ }
+
+ const GGLFormat* f = &(c->formats[s->format]);
+ int32_t index = x + (s->stride * y);
+ uint8_t* const data = s->data + index * f->size;
+
+ uint32_t mask = 0;
+ uint32_t v = 0;
+ for (int i=0 ; i<4 ; i++) {
+ const int component_mask = 1 << i;
+ if (f->components>=GGL_LUMINANCE &&
+ (i==GGLFormat::GREEN || i==GGLFormat::BLUE)) {
+ // destinations L formats don't have G or B
+ continue;
+ }
+ const int l = f->c[i].l;
+ const int h = f->c[i].h;
+ if (h && (c->state.mask.color & component_mask)) {
+ mask |= (((1<<(h-l))-1)<<l);
+ uint32_t u = pixel->c[i];
+ int32_t pixelSize = pixel->s[i];
+ if (pixelSize < (h-l)) {
+ u = expand(u, pixelSize, h-l);
+ pixelSize = h-l;
+ }
+ v = downshift_component(v, u, pixelSize, 0, h, l, 0, 0, dither);
+ }
+ }
+
+ if ((c->state.mask.color != 0xF) ||
+ (c->state.enables & GGL_ENABLE_LOGIC_OP)) {
+ uint32_t d = 0;
+ switch (f->size) {
+ case 1: d = *data; break;
+ case 2: d = *(uint16_t*)data; break;
+ case 3: d = (data[2]<<16)|(data[1]<<8)|data[0]; break;
+ case 4: d = GGL_RGBA_TO_HOST(*(uint32_t*)data); break;
+ }
+ if (c->state.enables & GGL_ENABLE_LOGIC_OP) {
+ v = logic_op(c->state.logic_op.opcode, v, d);
+ v &= mask;
+ }
+ v |= (d & ~mask);
+ }
+
+ switch (f->size) {
+ case 1: *data = v; break;
+ case 2: *(uint16_t*)data = v; break;
+ case 3:
+ data[0] = v;
+ data[1] = v>>8;
+ data[2] = v>>16;
+ break;
+ case 4: *(uint32_t*)data = GGL_HOST_TO_RGBA(v); break;
+ }
+}
+
+static uint32_t logic_op(int op, uint32_t s, uint32_t d)
+{
+ switch(op) {
+ case GGL_CLEAR: return 0;
+ case GGL_AND: return s & d;
+ case GGL_AND_REVERSE: return s & ~d;
+ case GGL_COPY: return s;
+ case GGL_AND_INVERTED: return ~s & d;
+ case GGL_NOOP: return d;
+ case GGL_XOR: return s ^ d;
+ case GGL_OR: return s | d;
+ case GGL_NOR: return ~(s | d);
+ case GGL_EQUIV: return ~(s ^ d);
+ case GGL_INVERT: return ~d;
+ case GGL_OR_REVERSE: return s | ~d;
+ case GGL_COPY_INVERTED: return ~s;
+ case GGL_OR_INVERTED: return ~s | d;
+ case GGL_NAND: return ~(s & d);
+ case GGL_SET: return ~0;
+ };
+ return s;
+}
+
+
+uint32_t ggl_expand(uint32_t v, int sbits, int dbits)
+{
+ return expand(v, sbits, dbits);
+}
+
+uint32_t ggl_pack_color(context_t* c, int32_t format,
+ GGLcolor r, GGLcolor g, GGLcolor b, GGLcolor a)
+{
+ const GGLFormat* f = &(c->formats[format]);
+ uint32_t p = 0;
+ const int32_t hbits = GGL_COLOR_BITS;
+ const int32_t lbits = GGL_COLOR_BITS - 8;
+ p = downshift_component(p, r, hbits, lbits, f->rh, f->rl, 0, 1, -1);
+ p = downshift_component(p, g, hbits, lbits, f->gh, f->gl, 0, 1, -1);
+ p = downshift_component(p, b, hbits, lbits, f->bh, f->bl, 0, 1, -1);
+ p = downshift_component(p, a, hbits, lbits, f->ah, f->al, 0, 1, -1);
+ switch (f->size) {
+ case 1: p |= p << 8; // fallthrough
+ case 2: p |= p << 16;
+ }
+ return p;
+}
+
+// ----------------------------------------------------------------------------
+
+// extract a component from a word
+uint32_t extract(uint32_t v, int h, int l, int bits)
+{
+ assert(h);
+ if (l) {
+ v >>= l;
+ }
+ if (h != bits) {
+ v &= (1<<(h-l))-1;
+ }
+ return v;
+}
+
+// expand a component from sbits to dbits
+uint32_t expand(uint32_t v, int sbits, int dbits)
+{
+ if (dbits > sbits) {
+ assert(sbits);
+ if (sbits==1) {
+ v = (v<<dbits) - v;
+ } else {
+ if (dbits % sbits) {
+ v <<= (dbits-sbits);
+ dbits -= sbits;
+ do {
+ v |= v>>sbits;
+ dbits -= sbits;
+ sbits *= 2;
+ } while (dbits>0);
+ } else {
+ dbits -= sbits;
+ do {
+ v |= v<<sbits;
+ dbits -= sbits;
+ if (sbits*2 < dbits) {
+ sbits *= 2;
+ }
+ } while (dbits > 0);
+ }
+ }
+ }
+ return v;
+}
+
+// downsample a component from sbits to dbits
+// and shift / construct the pixel
+uint32_t downshift_component( uint32_t in, uint32_t v,
+ int sh, int sl, // src
+ int dh, int dl, // dst
+ int ch, int cl, // clear
+ int dither)
+{
+ const int sbits = sh-sl;
+ const int dbits = dh-dl;
+
+ assert(sbits>=dbits);
+
+
+ if (sbits>dbits) {
+ if (dither>=0) {
+ v -= (v>>dbits); // fix up
+ const int shift = (GGL_DITHER_BITS - (sbits-dbits));
+ if (shift >= 0) v += (dither >> shift) << sl;
+ else v += (dither << (-shift)) << sl;
+ } else {
+ // don't do that right now, so we can reproduce the same
+ // artifacts we get on ARM (Where we don't do this)
+ // -> this is not really needed if we don't dither
+ //if (dBits > 1) { // result already OK if dBits==1
+ // v -= (v>>dbits); // fix up
+ // v += 1 << ((sbits-dbits)-1); // rounding
+ //}
+ }
+ }
+
+
+ // we need to clear the high bits of the source
+ if (ch) {
+ v <<= 32-sh;
+ sl += 32-sh;
+ sh = 32;
+ }
+
+ if (dl) {
+ if (cl || (sbits>dbits)) {
+ v >>= sh-dbits;
+ sl = 0;
+ sh = dbits;
+ in |= v<<dl;
+ } else {
+ // sbits==dbits and we don't need to clean the lower bits
+ // so we just have to shift the component to the right location
+ int shift = dh-sh;
+ in |= v<<shift;
+ }
+ } else {
+ // destination starts at bit 0
+ // ie: sh-dh == sh-dbits
+ int shift = sh-dh;
+ if (shift > 0) in |= v>>shift;
+ else if (shift < 0) in |= v<<shift;
+ else in |= v;
+ }
+ return in;
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libpixelflinger/buffer.h b/libpixelflinger/buffer.h
new file mode 100644
index 0000000..9c9e4bc
--- /dev/null
+++ b/libpixelflinger/buffer.h
@@ -0,0 +1,39 @@
+/* libs/pixelflinger/buffer.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+
+#ifndef ANDROID_GGL_TEXTURE_H
+#define ANDROID_GGL_TEXTURE_H
+
+#include <private/pixelflinger/ggl_context.h>
+
+namespace android {
+
+void ggl_init_texture(context_t* c);
+
+void ggl_set_surface(context_t* c, surface_t* dst, const GGLSurface* src);
+
+void ggl_pick_texture(context_t* c);
+void ggl_pick_cb(context_t* c);
+
+uint32_t ggl_expand(uint32_t v, int sbits, int dbits);
+uint32_t ggl_pack_color(context_t* c, int32_t format,
+ GGLcolor r, GGLcolor g, GGLcolor b, GGLcolor a);
+
+}; // namespace android
+
+#endif // ANDROID_GGL_TEXTURE_H
diff --git a/libpixelflinger/clear.cpp b/libpixelflinger/clear.cpp
new file mode 100644
index 0000000..b962456
--- /dev/null
+++ b/libpixelflinger/clear.cpp
@@ -0,0 +1,171 @@
+/* libs/pixelflinger/clear.cpp
+**
+** Copyright 2006, 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 <cutils/memory.h>
+
+#include "clear.h"
+#include "buffer.h"
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+static void ggl_clear(void* c, GGLbitfield mask);
+static void ggl_clearColorx(void* c,
+ GGLclampx r, GGLclampx g, GGLclampx b, GGLclampx a);
+static void ggl_clearDepthx(void* c, GGLclampx depth);
+static void ggl_clearStencil(void* c, GGLint s);
+
+// ----------------------------------------------------------------------------
+
+void ggl_init_clear(context_t* c)
+{
+ GGLContext& procs = *(GGLContext*)c;
+ GGL_INIT_PROC(procs, clear);
+ GGL_INIT_PROC(procs, clearColorx);
+ GGL_INIT_PROC(procs, clearDepthx);
+ GGL_INIT_PROC(procs, clearStencil);
+ c->state.clear.dirty = GGL_STENCIL_BUFFER_BIT |
+ GGL_COLOR_BUFFER_BIT |
+ GGL_DEPTH_BUFFER_BIT;
+ c->state.clear.depth = FIXED_ONE;
+}
+
+// ----------------------------------------------------------------------------
+
+static void memset2d(context_t* c, const surface_t& s, uint32_t packed,
+ uint32_t l, uint32_t t, uint32_t w, uint32_t h)
+{
+ const uint32_t size = c->formats[s.format].size;
+ const int32_t stride = s.stride * size;
+ uint8_t* dst = (uint8_t*)s.data + (l + t*s.stride)*size;
+ w *= size;
+
+ if (ggl_likely(int32_t(w) == stride)) {
+ // clear the whole thing in one call
+ w *= h;
+ h = 1;
+ }
+
+ switch (size) {
+ case 1:
+ do {
+ memset(dst, packed, w);
+ dst += stride;
+ } while(--h);
+ break;
+ case 2:
+ do {
+ android_memset16((uint16_t*)dst, packed, w);
+ dst += stride;
+ } while(--h);
+ break;
+ case 3: // XXX: 24-bit clear.
+ break;
+ case 4:
+ do {
+ android_memset32((uint32_t*)dst, packed, w);
+ dst += stride;
+ } while(--h);
+ break;
+ }
+}
+
+static inline GGLfixed fixedToZ(GGLfixed z) {
+ return GGLfixed(((int64_t(z) << 16) - z) >> 16);
+}
+
+static void ggl_clear(void* con, GGLbitfield mask)
+{
+ GGL_CONTEXT(c, con);
+
+ // XXX: rgba-dithering, rgba-masking
+ // XXX: handle all formats of Z and S
+
+ const uint32_t l = c->state.scissor.left;
+ const uint32_t t = c->state.scissor.top;
+ uint32_t w = c->state.scissor.right - l;
+ uint32_t h = c->state.scissor.bottom - t;
+
+ if (!w || !h)
+ return;
+
+ // unexsiting buffers have no effect...
+ if (c->state.buffers.color.format == 0)
+ mask &= ~GGL_COLOR_BUFFER_BIT;
+
+ if (c->state.buffers.depth.format == 0)
+ mask &= ~GGL_DEPTH_BUFFER_BIT;
+
+ if (c->state.buffers.stencil.format == 0)
+ mask &= ~GGL_STENCIL_BUFFER_BIT;
+
+ if (mask & GGL_COLOR_BUFFER_BIT) {
+ if (c->state.clear.dirty & GGL_COLOR_BUFFER_BIT) {
+ c->state.clear.dirty &= ~GGL_COLOR_BUFFER_BIT;
+
+ uint32_t colorPacked = ggl_pack_color(c,
+ c->state.buffers.color.format,
+ gglFixedToIteratedColor(c->state.clear.r),
+ gglFixedToIteratedColor(c->state.clear.g),
+ gglFixedToIteratedColor(c->state.clear.b),
+ gglFixedToIteratedColor(c->state.clear.a));
+
+ c->state.clear.colorPacked = GGL_HOST_TO_RGBA(colorPacked);
+ }
+ const uint32_t packed = c->state.clear.colorPacked;
+ memset2d(c, c->state.buffers.color, packed, l, t, w, h);
+ }
+ if (mask & GGL_DEPTH_BUFFER_BIT) {
+ if (c->state.clear.dirty & GGL_DEPTH_BUFFER_BIT) {
+ c->state.clear.dirty &= ~GGL_DEPTH_BUFFER_BIT;
+ uint32_t depth = fixedToZ(c->state.clear.depth);
+ c->state.clear.depthPacked = (depth<<16)|depth;
+ }
+ const uint32_t packed = c->state.clear.depthPacked;
+ memset2d(c, c->state.buffers.depth, packed, l, t, w, h);
+ }
+
+ // XXX: do stencil buffer
+}
+
+static void ggl_clearColorx(void* con,
+ GGLclampx r, GGLclampx g, GGLclampx b, GGLclampx a)
+{
+ GGL_CONTEXT(c, con);
+ c->state.clear.r = gglClampx(r);
+ c->state.clear.g = gglClampx(g);
+ c->state.clear.b = gglClampx(b);
+ c->state.clear.a = gglClampx(a);
+ c->state.clear.dirty |= GGL_COLOR_BUFFER_BIT;
+}
+
+static void ggl_clearDepthx(void* con, GGLclampx depth)
+{
+ GGL_CONTEXT(c, con);
+ c->state.clear.depth = gglClampx(depth);
+ c->state.clear.dirty |= GGL_DEPTH_BUFFER_BIT;
+}
+
+static void ggl_clearStencil(void* con, GGLint s)
+{
+ GGL_CONTEXT(c, con);
+ c->state.clear.stencil = s;
+ c->state.clear.dirty |= GGL_STENCIL_BUFFER_BIT;
+}
+
+}; // namespace android
diff --git a/libpixelflinger/clear.h b/libpixelflinger/clear.h
new file mode 100644
index 0000000..b071df0
--- /dev/null
+++ b/libpixelflinger/clear.h
@@ -0,0 +1,30 @@
+/* libs/pixelflinger/clear.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef ANDROID_GGL_CLEAR_H
+#define ANDROID_GGL_CLEAR_H
+
+#include <pixelflinger/pixelflinger.h>
+#include <private/pixelflinger/ggl_context.h>
+
+namespace android {
+
+void ggl_init_clear(context_t* c);
+
+}; // namespace android
+
+#endif // ANDROID_GGL_CLEAR_H
diff --git a/libpixelflinger/codeflinger/ARMAssembler.cpp b/libpixelflinger/codeflinger/ARMAssembler.cpp
new file mode 100644
index 0000000..92243da
--- /dev/null
+++ b/libpixelflinger/codeflinger/ARMAssembler.cpp
@@ -0,0 +1,594 @@
+/* libs/pixelflinger/codeflinger/ARMAssembler.cpp
+**
+** Copyright 2006, 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 "ARMAssembler"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <cutils/log.h>
+#include <cutils/properties.h>
+
+#if defined(WITH_LIB_HARDWARE)
+#include <hardware_legacy/qemu_tracing.h>
+#endif
+
+#include <private/pixelflinger/ggl_context.h>
+
+#include "ARMAssembler.h"
+#include "CodeCache.h"
+#include "disassem.h"
+
+// ----------------------------------------------------------------------------
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark ARMAssembler...
+#endif
+
+ARMAssembler::ARMAssembler(const sp<Assembly>& assembly)
+ : ARMAssemblerInterface(),
+ mAssembly(assembly)
+{
+ mBase = mPC = (uint32_t *)assembly->base();
+ mDuration = ggl_system_time();
+#if defined(WITH_LIB_HARDWARE)
+ mQemuTracing = true;
+#endif
+}
+
+ARMAssembler::~ARMAssembler()
+{
+}
+
+uint32_t* ARMAssembler::pc() const
+{
+ return mPC;
+}
+
+uint32_t* ARMAssembler::base() const
+{
+ return mBase;
+}
+
+void ARMAssembler::reset()
+{
+ mBase = mPC = (uint32_t *)mAssembly->base();
+ mBranchTargets.clear();
+ mLabels.clear();
+ mLabelsInverseMapping.clear();
+ mComments.clear();
+}
+
+int ARMAssembler::getCodegenArch()
+{
+ return CODEGEN_ARCH_ARM;
+}
+
+// ----------------------------------------------------------------------------
+
+void ARMAssembler::disassemble(const char* name)
+{
+ if (name) {
+ printf("%s:\n", name);
+ }
+ size_t count = pc()-base();
+ uint32_t* i = base();
+ while (count--) {
+ ssize_t label = mLabelsInverseMapping.indexOfKey(i);
+ if (label >= 0) {
+ printf("%s:\n", mLabelsInverseMapping.valueAt(label));
+ }
+ ssize_t comment = mComments.indexOfKey(i);
+ if (comment >= 0) {
+ printf("; %s\n", mComments.valueAt(comment));
+ }
+ printf("%08x: %08x ", uintptr_t(i), int(i[0]));
+ ::disassemble((uintptr_t)i);
+ i++;
+ }
+}
+
+void ARMAssembler::comment(const char* string)
+{
+ mComments.add(mPC, string);
+}
+
+void ARMAssembler::label(const char* theLabel)
+{
+ mLabels.add(theLabel, mPC);
+ mLabelsInverseMapping.add(mPC, theLabel);
+}
+
+void ARMAssembler::B(int cc, const char* label)
+{
+ mBranchTargets.add(branch_target_t(label, mPC));
+ *mPC++ = (cc<<28) | (0xA<<24) | 0;
+}
+
+void ARMAssembler::BL(int cc, const char* label)
+{
+ mBranchTargets.add(branch_target_t(label, mPC));
+ *mPC++ = (cc<<28) | (0xB<<24) | 0;
+}
+
+#if 0
+#pragma mark -
+#pragma mark Prolog/Epilog & Generate...
+#endif
+
+
+void ARMAssembler::prolog()
+{
+ // write dummy prolog code
+ mPrologPC = mPC;
+ STM(AL, FD, SP, 1, LSAVED);
+}
+
+void ARMAssembler::epilog(uint32_t touched)
+{
+ touched &= LSAVED;
+ if (touched) {
+ // write prolog code
+ uint32_t* pc = mPC;
+ mPC = mPrologPC;
+ STM(AL, FD, SP, 1, touched | LLR);
+ mPC = pc;
+ // write epilog code
+ LDM(AL, FD, SP, 1, touched | LLR);
+ BX(AL, LR);
+ } else { // heh, no registers to save!
+ // write prolog code
+ uint32_t* pc = mPC;
+ mPC = mPrologPC;
+ MOV(AL, 0, R0, R0); // NOP
+ mPC = pc;
+ // write epilog code
+ BX(AL, LR);
+ }
+}
+
+int ARMAssembler::generate(const char* name)
+{
+ // fixup all the branches
+ size_t count = mBranchTargets.size();
+ while (count--) {
+ const branch_target_t& bt = mBranchTargets[count];
+ uint32_t* target_pc = mLabels.valueFor(bt.label);
+ LOG_ALWAYS_FATAL_IF(!target_pc,
+ "error resolving branch targets, target_pc is null");
+ int32_t offset = int32_t(target_pc - (bt.pc+2));
+ *bt.pc |= offset & 0xFFFFFF;
+ }
+
+ mAssembly->resize( int(pc()-base())*4 );
+
+ // the instruction cache is flushed by CodeCache
+ const int64_t duration = ggl_system_time() - mDuration;
+ const char * const format = "generated %s (%d ins) at [%p:%p] in %lld ns\n";
+ ALOGI(format, name, int(pc()-base()), base(), pc(), duration);
+
+#if defined(WITH_LIB_HARDWARE)
+ if (__builtin_expect(mQemuTracing, 0)) {
+ int err = qemu_add_mapping(uintptr_t(base()), name);
+ mQemuTracing = (err >= 0);
+ }
+#endif
+
+ char value[PROPERTY_VALUE_MAX];
+ property_get("debug.pf.disasm", value, "0");
+ if (atoi(value) != 0) {
+ printf(format, name, int(pc()-base()), base(), pc(), duration);
+ disassemble(name);
+ }
+
+ return NO_ERROR;
+}
+
+uint32_t* ARMAssembler::pcForLabel(const char* label)
+{
+ return mLabels.valueFor(label);
+}
+
+// ----------------------------------------------------------------------------
+
+#if 0
+#pragma mark -
+#pragma mark Data Processing...
+#endif
+
+void ARMAssembler::dataProcessing(int opcode, int cc,
+ int s, int Rd, int Rn, uint32_t Op2)
+{
+ *mPC++ = (cc<<28) | (opcode<<21) | (s<<20) | (Rn<<16) | (Rd<<12) | Op2;
+}
+
+#if 0
+#pragma mark -
+#pragma mark Multiply...
+#endif
+
+// multiply...
+void ARMAssembler::MLA(int cc, int s,
+ int Rd, int Rm, int Rs, int Rn) {
+ if (Rd == Rm) { int t = Rm; Rm=Rs; Rs=t; }
+ LOG_FATAL_IF(Rd==Rm, "MLA(r%u,r%u,r%u,r%u)", Rd,Rm,Rs,Rn);
+ *mPC++ = (cc<<28) | (1<<21) | (s<<20) |
+ (Rd<<16) | (Rn<<12) | (Rs<<8) | 0x90 | Rm;
+}
+void ARMAssembler::MUL(int cc, int s,
+ int Rd, int Rm, int Rs) {
+ if (Rd == Rm) { int t = Rm; Rm=Rs; Rs=t; }
+ LOG_FATAL_IF(Rd==Rm, "MUL(r%u,r%u,r%u)", Rd,Rm,Rs);
+ *mPC++ = (cc<<28) | (s<<20) | (Rd<<16) | (Rs<<8) | 0x90 | Rm;
+}
+void ARMAssembler::UMULL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs) {
+ LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi,
+ "UMULL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs);
+ *mPC++ = (cc<<28) | (1<<23) | (s<<20) |
+ (RdHi<<16) | (RdLo<<12) | (Rs<<8) | 0x90 | Rm;
+}
+void ARMAssembler::UMUAL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs) {
+ LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi,
+ "UMUAL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs);
+ *mPC++ = (cc<<28) | (1<<23) | (1<<21) | (s<<20) |
+ (RdHi<<16) | (RdLo<<12) | (Rs<<8) | 0x90 | Rm;
+}
+void ARMAssembler::SMULL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs) {
+ LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi,
+ "SMULL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs);
+ *mPC++ = (cc<<28) | (1<<23) | (1<<22) | (s<<20) |
+ (RdHi<<16) | (RdLo<<12) | (Rs<<8) | 0x90 | Rm;
+}
+void ARMAssembler::SMUAL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs) {
+ LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi,
+ "SMUAL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs);
+ *mPC++ = (cc<<28) | (1<<23) | (1<<22) | (1<<21) | (s<<20) |
+ (RdHi<<16) | (RdLo<<12) | (Rs<<8) | 0x90 | Rm;
+}
+
+#if 0
+#pragma mark -
+#pragma mark Branches...
+#endif
+
+// branches...
+void ARMAssembler::B(int cc, uint32_t* pc)
+{
+ int32_t offset = int32_t(pc - (mPC+2));
+ *mPC++ = (cc<<28) | (0xA<<24) | (offset & 0xFFFFFF);
+}
+
+void ARMAssembler::BL(int cc, uint32_t* pc)
+{
+ int32_t offset = int32_t(pc - (mPC+2));
+ *mPC++ = (cc<<28) | (0xB<<24) | (offset & 0xFFFFFF);
+}
+
+void ARMAssembler::BX(int cc, int Rn)
+{
+ *mPC++ = (cc<<28) | 0x12FFF10 | Rn;
+}
+
+#if 0
+#pragma mark -
+#pragma mark Data Transfer...
+#endif
+
+// data transfert...
+void ARMAssembler::LDR(int cc, int Rd, int Rn, uint32_t offset) {
+ *mPC++ = (cc<<28) | (1<<26) | (1<<20) | (Rn<<16) | (Rd<<12) | offset;
+}
+void ARMAssembler::LDRB(int cc, int Rd, int Rn, uint32_t offset) {
+ *mPC++ = (cc<<28) | (1<<26) | (1<<22) | (1<<20) | (Rn<<16) | (Rd<<12) | offset;
+}
+void ARMAssembler::STR(int cc, int Rd, int Rn, uint32_t offset) {
+ *mPC++ = (cc<<28) | (1<<26) | (Rn<<16) | (Rd<<12) | offset;
+}
+void ARMAssembler::STRB(int cc, int Rd, int Rn, uint32_t offset) {
+ *mPC++ = (cc<<28) | (1<<26) | (1<<22) | (Rn<<16) | (Rd<<12) | offset;
+}
+
+void ARMAssembler::LDRH(int cc, int Rd, int Rn, uint32_t offset) {
+ *mPC++ = (cc<<28) | (1<<20) | (Rn<<16) | (Rd<<12) | 0xB0 | offset;
+}
+void ARMAssembler::LDRSB(int cc, int Rd, int Rn, uint32_t offset) {
+ *mPC++ = (cc<<28) | (1<<20) | (Rn<<16) | (Rd<<12) | 0xD0 | offset;
+}
+void ARMAssembler::LDRSH(int cc, int Rd, int Rn, uint32_t offset) {
+ *mPC++ = (cc<<28) | (1<<20) | (Rn<<16) | (Rd<<12) | 0xF0 | offset;
+}
+void ARMAssembler::STRH(int cc, int Rd, int Rn, uint32_t offset) {
+ *mPC++ = (cc<<28) | (Rn<<16) | (Rd<<12) | 0xB0 | offset;
+}
+
+#if 0
+#pragma mark -
+#pragma mark Block Data Transfer...
+#endif
+
+// block data transfer...
+void ARMAssembler::LDM(int cc, int dir,
+ int Rn, int W, uint32_t reg_list)
+{ // ED FD EA FA IB IA DB DA
+ const uint8_t P[8] = { 1, 0, 1, 0, 1, 0, 1, 0 };
+ const uint8_t U[8] = { 1, 1, 0, 0, 1, 1, 0, 0 };
+ *mPC++ = (cc<<28) | (4<<25) | (uint32_t(P[dir])<<24) |
+ (uint32_t(U[dir])<<23) | (1<<20) | (W<<21) | (Rn<<16) | reg_list;
+}
+
+void ARMAssembler::STM(int cc, int dir,
+ int Rn, int W, uint32_t reg_list)
+{ // ED FD EA FA IB IA DB DA
+ const uint8_t P[8] = { 0, 1, 0, 1, 1, 0, 1, 0 };
+ const uint8_t U[8] = { 0, 0, 1, 1, 1, 1, 0, 0 };
+ *mPC++ = (cc<<28) | (4<<25) | (uint32_t(P[dir])<<24) |
+ (uint32_t(U[dir])<<23) | (0<<20) | (W<<21) | (Rn<<16) | reg_list;
+}
+
+#if 0
+#pragma mark -
+#pragma mark Special...
+#endif
+
+// special...
+void ARMAssembler::SWP(int cc, int Rn, int Rd, int Rm) {
+ *mPC++ = (cc<<28) | (2<<23) | (Rn<<16) | (Rd << 12) | 0x90 | Rm;
+}
+void ARMAssembler::SWPB(int cc, int Rn, int Rd, int Rm) {
+ *mPC++ = (cc<<28) | (2<<23) | (1<<22) | (Rn<<16) | (Rd << 12) | 0x90 | Rm;
+}
+void ARMAssembler::SWI(int cc, uint32_t comment) {
+ *mPC++ = (cc<<28) | (0xF<<24) | comment;
+}
+
+#if 0
+#pragma mark -
+#pragma mark DSP instructions...
+#endif
+
+// DSP instructions...
+void ARMAssembler::PLD(int Rn, uint32_t offset) {
+ LOG_ALWAYS_FATAL_IF(!((offset&(1<<24)) && !(offset&(1<<21))),
+ "PLD only P=1, W=0");
+ *mPC++ = 0xF550F000 | (Rn<<16) | offset;
+}
+
+void ARMAssembler::CLZ(int cc, int Rd, int Rm)
+{
+ *mPC++ = (cc<<28) | 0x16F0F10| (Rd<<12) | Rm;
+}
+
+void ARMAssembler::QADD(int cc, int Rd, int Rm, int Rn)
+{
+ *mPC++ = (cc<<28) | 0x1000050 | (Rn<<16) | (Rd<<12) | Rm;
+}
+
+void ARMAssembler::QDADD(int cc, int Rd, int Rm, int Rn)
+{
+ *mPC++ = (cc<<28) | 0x1400050 | (Rn<<16) | (Rd<<12) | Rm;
+}
+
+void ARMAssembler::QSUB(int cc, int Rd, int Rm, int Rn)
+{
+ *mPC++ = (cc<<28) | 0x1200050 | (Rn<<16) | (Rd<<12) | Rm;
+}
+
+void ARMAssembler::QDSUB(int cc, int Rd, int Rm, int Rn)
+{
+ *mPC++ = (cc<<28) | 0x1600050 | (Rn<<16) | (Rd<<12) | Rm;
+}
+
+void ARMAssembler::SMUL(int cc, int xy,
+ int Rd, int Rm, int Rs)
+{
+ *mPC++ = (cc<<28) | 0x1600080 | (Rd<<16) | (Rs<<8) | (xy<<4) | Rm;
+}
+
+void ARMAssembler::SMULW(int cc, int y,
+ int Rd, int Rm, int Rs)
+{
+ *mPC++ = (cc<<28) | 0x12000A0 | (Rd<<16) | (Rs<<8) | (y<<4) | Rm;
+}
+
+void ARMAssembler::SMLA(int cc, int xy,
+ int Rd, int Rm, int Rs, int Rn)
+{
+ *mPC++ = (cc<<28) | 0x1000080 | (Rd<<16) | (Rn<<12) | (Rs<<8) | (xy<<4) | Rm;
+}
+
+void ARMAssembler::SMLAL(int cc, int xy,
+ int RdHi, int RdLo, int Rs, int Rm)
+{
+ *mPC++ = (cc<<28) | 0x1400080 | (RdHi<<16) | (RdLo<<12) | (Rs<<8) | (xy<<4) | Rm;
+}
+
+void ARMAssembler::SMLAW(int cc, int y,
+ int Rd, int Rm, int Rs, int Rn)
+{
+ *mPC++ = (cc<<28) | 0x1200080 | (Rd<<16) | (Rn<<12) | (Rs<<8) | (y<<4) | Rm;
+}
+
+#if 0
+#pragma mark -
+#pragma mark Byte/half word extract and extend (ARMv6+ only)...
+#endif
+
+void ARMAssembler::UXTB16(int cc, int Rd, int Rm, int rotate)
+{
+ *mPC++ = (cc<<28) | 0x6CF0070 | (Rd<<12) | ((rotate >> 3) << 10) | Rm;
+}
+#if 0
+#pragma mark -
+#pragma mark Bit manipulation (ARMv7+ only)...
+#endif
+
+// Bit manipulation (ARMv7+ only)...
+void ARMAssembler::UBFX(int cc, int Rd, int Rn, int lsb, int width)
+{
+ *mPC++ = (cc<<28) | 0x7E00000 | ((width-1)<<16) | (Rd<<12) | (lsb<<7) | 0x50 | Rn;
+}
+
+#if 0
+#pragma mark -
+#pragma mark Addressing modes...
+#endif
+
+int ARMAssembler::buildImmediate(
+ uint32_t immediate, uint32_t& rot, uint32_t& imm)
+{
+ rot = 0;
+ imm = immediate;
+ if (imm > 0x7F) { // skip the easy cases
+ while (!(imm&3) || (imm&0xFC000000)) {
+ uint32_t newval;
+ newval = imm >> 2;
+ newval |= (imm&3) << 30;
+ imm = newval;
+ rot += 2;
+ if (rot == 32) {
+ rot = 0;
+ break;
+ }
+ }
+ }
+ rot = (16 - (rot>>1)) & 0xF;
+
+ if (imm>=0x100)
+ return -EINVAL;
+
+ if (((imm>>(rot<<1)) | (imm<<(32-(rot<<1)))) != immediate)
+ return -1;
+
+ return 0;
+}
+
+// shifters...
+
+bool ARMAssembler::isValidImmediate(uint32_t immediate)
+{
+ uint32_t rot, imm;
+ return buildImmediate(immediate, rot, imm) == 0;
+}
+
+uint32_t ARMAssembler::imm(uint32_t immediate)
+{
+ uint32_t rot, imm;
+ int err = buildImmediate(immediate, rot, imm);
+
+ LOG_ALWAYS_FATAL_IF(err==-EINVAL,
+ "immediate %08x cannot be encoded",
+ immediate);
+
+ LOG_ALWAYS_FATAL_IF(err,
+ "immediate (%08x) encoding bogus!",
+ immediate);
+
+ return (1<<25) | (rot<<8) | imm;
+}
+
+uint32_t ARMAssembler::reg_imm(int Rm, int type, uint32_t shift)
+{
+ return ((shift&0x1F)<<7) | ((type&0x3)<<5) | (Rm&0xF);
+}
+
+uint32_t ARMAssembler::reg_rrx(int Rm)
+{
+ return (ROR<<5) | (Rm&0xF);
+}
+
+uint32_t ARMAssembler::reg_reg(int Rm, int type, int Rs)
+{
+ return ((Rs&0xF)<<8) | ((type&0x3)<<5) | (1<<4) | (Rm&0xF);
+}
+
+// addressing modes...
+// LDR(B)/STR(B)/PLD (immediate and Rm can be negative, which indicate U=0)
+uint32_t ARMAssembler::immed12_pre(int32_t immed12, int W)
+{
+ LOG_ALWAYS_FATAL_IF(abs(immed12) >= 0x800,
+ "LDR(B)/STR(B)/PLD immediate too big (%08x)",
+ immed12);
+ return (1<<24) | (((uint32_t(immed12)>>31)^1)<<23) |
+ ((W&1)<<21) | (abs(immed12)&0x7FF);
+}
+
+uint32_t ARMAssembler::immed12_post(int32_t immed12)
+{
+ LOG_ALWAYS_FATAL_IF(abs(immed12) >= 0x800,
+ "LDR(B)/STR(B)/PLD immediate too big (%08x)",
+ immed12);
+
+ return (((uint32_t(immed12)>>31)^1)<<23) | (abs(immed12)&0x7FF);
+}
+
+uint32_t ARMAssembler::reg_scale_pre(int Rm, int type,
+ uint32_t shift, int W)
+{
+ return (1<<25) | (1<<24) |
+ (((uint32_t(Rm)>>31)^1)<<23) | ((W&1)<<21) |
+ reg_imm(abs(Rm), type, shift);
+}
+
+uint32_t ARMAssembler::reg_scale_post(int Rm, int type, uint32_t shift)
+{
+ return (1<<25) | (((uint32_t(Rm)>>31)^1)<<23) | reg_imm(abs(Rm), type, shift);
+}
+
+// LDRH/LDRSB/LDRSH/STRH (immediate and Rm can be negative, which indicate U=0)
+uint32_t ARMAssembler::immed8_pre(int32_t immed8, int W)
+{
+ uint32_t offset = abs(immed8);
+
+ LOG_ALWAYS_FATAL_IF(abs(immed8) >= 0x100,
+ "LDRH/LDRSB/LDRSH/STRH immediate too big (%08x)",
+ immed8);
+
+ return (1<<24) | (1<<22) | (((uint32_t(immed8)>>31)^1)<<23) |
+ ((W&1)<<21) | (((offset&0xF0)<<4)|(offset&0xF));
+}
+
+uint32_t ARMAssembler::immed8_post(int32_t immed8)
+{
+ uint32_t offset = abs(immed8);
+
+ LOG_ALWAYS_FATAL_IF(abs(immed8) >= 0x100,
+ "LDRH/LDRSB/LDRSH/STRH immediate too big (%08x)",
+ immed8);
+
+ return (1<<22) | (((uint32_t(immed8)>>31)^1)<<23) |
+ (((offset&0xF0)<<4) | (offset&0xF));
+}
+
+uint32_t ARMAssembler::reg_pre(int Rm, int W)
+{
+ return (1<<24) | (((uint32_t(Rm)>>31)^1)<<23) | ((W&1)<<21) | (abs(Rm)&0xF);
+}
+
+uint32_t ARMAssembler::reg_post(int Rm)
+{
+ return (((uint32_t(Rm)>>31)^1)<<23) | (abs(Rm)&0xF);
+}
+
+}; // namespace android
+
diff --git a/libpixelflinger/codeflinger/ARMAssembler.h b/libpixelflinger/codeflinger/ARMAssembler.h
new file mode 100644
index 0000000..c03dd9a
--- /dev/null
+++ b/libpixelflinger/codeflinger/ARMAssembler.h
@@ -0,0 +1,190 @@
+/* libs/pixelflinger/codeflinger/ARMAssembler.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef ANDROID_ARMASSEMBLER_H
+#define ANDROID_ARMASSEMBLER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "tinyutils/Vector.h"
+#include "tinyutils/KeyedVector.h"
+#include "tinyutils/smartpointer.h"
+
+#include "ARMAssemblerInterface.h"
+#include "CodeCache.h"
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+class ARMAssembler : public ARMAssemblerInterface
+{
+public:
+ ARMAssembler(const sp<Assembly>& assembly);
+ virtual ~ARMAssembler();
+
+ uint32_t* base() const;
+ uint32_t* pc() const;
+
+
+ void disassemble(const char* name);
+
+ // ------------------------------------------------------------------------
+ // ARMAssemblerInterface...
+ // ------------------------------------------------------------------------
+
+ virtual void reset();
+
+ virtual int generate(const char* name);
+ virtual int getCodegenArch();
+
+ virtual void prolog();
+ virtual void epilog(uint32_t touched);
+ virtual void comment(const char* string);
+
+
+ // -----------------------------------------------------------------------
+ // shifters and addressing modes
+ // -----------------------------------------------------------------------
+
+ // shifters...
+ virtual bool isValidImmediate(uint32_t immed);
+ virtual int buildImmediate(uint32_t i, uint32_t& rot, uint32_t& imm);
+
+ virtual uint32_t imm(uint32_t immediate);
+ virtual uint32_t reg_imm(int Rm, int type, uint32_t shift);
+ virtual uint32_t reg_rrx(int Rm);
+ virtual uint32_t reg_reg(int Rm, int type, int Rs);
+
+ // addressing modes...
+ // LDR(B)/STR(B)/PLD
+ // (immediate and Rm can be negative, which indicates U=0)
+ virtual uint32_t immed12_pre(int32_t immed12, int W=0);
+ virtual uint32_t immed12_post(int32_t immed12);
+ virtual uint32_t reg_scale_pre(int Rm, int type=0, uint32_t shift=0, int W=0);
+ virtual uint32_t reg_scale_post(int Rm, int type=0, uint32_t shift=0);
+
+ // LDRH/LDRSB/LDRSH/STRH
+ // (immediate and Rm can be negative, which indicates U=0)
+ virtual uint32_t immed8_pre(int32_t immed8, int W=0);
+ virtual uint32_t immed8_post(int32_t immed8);
+ virtual uint32_t reg_pre(int Rm, int W=0);
+ virtual uint32_t reg_post(int Rm);
+
+
+ virtual void dataProcessing(int opcode, int cc, int s,
+ int Rd, int Rn,
+ uint32_t Op2);
+ virtual void MLA(int cc, int s,
+ int Rd, int Rm, int Rs, int Rn);
+ virtual void MUL(int cc, int s,
+ int Rd, int Rm, int Rs);
+ virtual void UMULL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs);
+ virtual void UMUAL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs);
+ virtual void SMULL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs);
+ virtual void SMUAL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs);
+
+ virtual void B(int cc, uint32_t* pc);
+ virtual void BL(int cc, uint32_t* pc);
+ virtual void BX(int cc, int Rn);
+ virtual void label(const char* theLabel);
+ virtual void B(int cc, const char* label);
+ virtual void BL(int cc, const char* label);
+
+ virtual uint32_t* pcForLabel(const char* label);
+
+ virtual void LDR (int cc, int Rd,
+ int Rn, uint32_t offset = __immed12_pre(0));
+ virtual void LDRB(int cc, int Rd,
+ int Rn, uint32_t offset = __immed12_pre(0));
+ virtual void STR (int cc, int Rd,
+ int Rn, uint32_t offset = __immed12_pre(0));
+ virtual void STRB(int cc, int Rd,
+ int Rn, uint32_t offset = __immed12_pre(0));
+ virtual void LDRH (int cc, int Rd,
+ int Rn, uint32_t offset = __immed8_pre(0));
+ virtual void LDRSB(int cc, int Rd,
+ int Rn, uint32_t offset = __immed8_pre(0));
+ virtual void LDRSH(int cc, int Rd,
+ int Rn, uint32_t offset = __immed8_pre(0));
+ virtual void STRH (int cc, int Rd,
+ int Rn, uint32_t offset = __immed8_pre(0));
+
+
+ virtual void LDM(int cc, int dir,
+ int Rn, int W, uint32_t reg_list);
+ virtual void STM(int cc, int dir,
+ int Rn, int W, uint32_t reg_list);
+
+ virtual void SWP(int cc, int Rn, int Rd, int Rm);
+ virtual void SWPB(int cc, int Rn, int Rd, int Rm);
+ virtual void SWI(int cc, uint32_t comment);
+
+ virtual void PLD(int Rn, uint32_t offset);
+ virtual void CLZ(int cc, int Rd, int Rm);
+ virtual void QADD(int cc, int Rd, int Rm, int Rn);
+ virtual void QDADD(int cc, int Rd, int Rm, int Rn);
+ virtual void QSUB(int cc, int Rd, int Rm, int Rn);
+ virtual void QDSUB(int cc, int Rd, int Rm, int Rn);
+ virtual void SMUL(int cc, int xy,
+ int Rd, int Rm, int Rs);
+ virtual void SMULW(int cc, int y,
+ int Rd, int Rm, int Rs);
+ virtual void SMLA(int cc, int xy,
+ int Rd, int Rm, int Rs, int Rn);
+ virtual void SMLAL(int cc, int xy,
+ int RdHi, int RdLo, int Rs, int Rm);
+ virtual void SMLAW(int cc, int y,
+ int Rd, int Rm, int Rs, int Rn);
+ virtual void UXTB16(int cc, int Rd, int Rm, int rotate);
+ virtual void UBFX(int cc, int Rd, int Rn, int lsb, int width);
+
+private:
+ ARMAssembler(const ARMAssembler& rhs);
+ ARMAssembler& operator = (const ARMAssembler& rhs);
+
+ sp<Assembly> mAssembly;
+ uint32_t* mBase;
+ uint32_t* mPC;
+ uint32_t* mPrologPC;
+ int64_t mDuration;
+#if defined(WITH_LIB_HARDWARE)
+ bool mQemuTracing;
+#endif
+
+ struct branch_target_t {
+ inline branch_target_t() : label(0), pc(0) { }
+ inline branch_target_t(const char* l, uint32_t* p)
+ : label(l), pc(p) { }
+ const char* label;
+ uint32_t* pc;
+ };
+
+ Vector<branch_target_t> mBranchTargets;
+ KeyedVector< const char*, uint32_t* > mLabels;
+ KeyedVector< uint32_t*, const char* > mLabelsInverseMapping;
+ KeyedVector< uint32_t*, const char* > mComments;
+};
+
+}; // namespace android
+
+#endif //ANDROID_ARMASSEMBLER_H
diff --git a/libpixelflinger/codeflinger/ARMAssemblerInterface.cpp b/libpixelflinger/codeflinger/ARMAssemblerInterface.cpp
new file mode 100644
index 0000000..5041999
--- /dev/null
+++ b/libpixelflinger/codeflinger/ARMAssemblerInterface.cpp
@@ -0,0 +1,89 @@
+/* libs/pixelflinger/codeflinger/ARMAssemblerInterface.cpp
+**
+** Copyright 2006, 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 <stdlib.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <cutils/log.h>
+#include "ARMAssemblerInterface.h"
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+ARMAssemblerInterface::~ARMAssemblerInterface()
+{
+}
+
+// --------------------------------------------------------------------
+
+// The following two functions are static and used for initializers
+// in the original ARM code. The above versions (without __), are now
+// virtual, and can be overridden in the MIPS code. But since these are
+// needed at initialization time, they must be static. Not thrilled with
+// this implementation, but it works...
+
+uint32_t ARMAssemblerInterface::__immed12_pre(int32_t immed12, int W)
+{
+ LOG_ALWAYS_FATAL_IF(abs(immed12) >= 0x800,
+ "LDR(B)/STR(B)/PLD immediate too big (%08x)",
+ immed12);
+ return (1<<24) | (((uint32_t(immed12)>>31)^1)<<23) |
+ ((W&1)<<21) | (abs(immed12)&0x7FF);
+}
+
+uint32_t ARMAssemblerInterface::__immed8_pre(int32_t immed8, int W)
+{
+ uint32_t offset = abs(immed8);
+
+ LOG_ALWAYS_FATAL_IF(abs(immed8) >= 0x100,
+ "LDRH/LDRSB/LDRSH/STRH immediate too big (%08x)",
+ immed8);
+
+ return (1<<24) | (1<<22) | (((uint32_t(immed8)>>31)^1)<<23) |
+ ((W&1)<<21) | (((offset&0xF0)<<4)|(offset&0xF));
+}
+
+// The following four functions are required for address manipulation
+// These are virtual functions, which can be overridden by architectures
+// that need special handling of address values (e.g. 64-bit arch)
+
+void ARMAssemblerInterface::ADDR_LDR(int cc, int Rd,
+ int Rn, uint32_t offset)
+{
+ LDR(cc, Rd, Rn, offset);
+}
+void ARMAssemblerInterface::ADDR_STR(int cc, int Rd,
+ int Rn, uint32_t offset)
+{
+ STR(cc, Rd, Rn, offset);
+}
+void ARMAssemblerInterface::ADDR_ADD(int cc, int s,
+ int Rd, int Rn, uint32_t Op2)
+{
+ dataProcessing(opADD, cc, s, Rd, Rn, Op2);
+}
+void ARMAssemblerInterface::ADDR_SUB(int cc, int s,
+ int Rd, int Rn, uint32_t Op2)
+{
+ dataProcessing(opSUB, cc, s, Rd, Rn, Op2);
+}
+}; // namespace android
+
diff --git a/libpixelflinger/codeflinger/ARMAssemblerInterface.h b/libpixelflinger/codeflinger/ARMAssemblerInterface.h
new file mode 100644
index 0000000..40cbfcf
--- /dev/null
+++ b/libpixelflinger/codeflinger/ARMAssemblerInterface.h
@@ -0,0 +1,348 @@
+/* libs/pixelflinger/codeflinger/ARMAssemblerInterface.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+
+#ifndef ANDROID_ARMASSEMBLER_INTERFACE_H
+#define ANDROID_ARMASSEMBLER_INTERFACE_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+class ARMAssemblerInterface
+{
+public:
+ virtual ~ARMAssemblerInterface();
+
+ enum {
+ EQ, NE, CS, CC, MI, PL, VS, VC, HI, LS, GE, LT, GT, LE, AL, NV,
+ HS = CS,
+ LO = CC
+ };
+ enum {
+ S = 1
+ };
+ enum {
+ LSL, LSR, ASR, ROR
+ };
+ enum {
+ ED, FD, EA, FA,
+ IB, IA, DB, DA
+ };
+ enum {
+ R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R15,
+ SP = R13,
+ LR = R14,
+ PC = R15
+ };
+ enum {
+ #define LIST(rr) L##rr=1<<rr
+ LIST(R0), LIST(R1), LIST(R2), LIST(R3), LIST(R4), LIST(R5), LIST(R6),
+ LIST(R7), LIST(R8), LIST(R9), LIST(R10), LIST(R11), LIST(R12),
+ LIST(R13), LIST(R14), LIST(R15),
+ LIST(SP), LIST(LR), LIST(PC),
+ #undef LIST
+ LSAVED = LR4|LR5|LR6|LR7|LR8|LR9|LR10|LR11 | LLR
+ };
+
+ enum {
+ CODEGEN_ARCH_ARM = 1, CODEGEN_ARCH_MIPS, CODEGEN_ARCH_ARM64
+ };
+
+ // -----------------------------------------------------------------------
+ // shifters and addressing modes
+ // -----------------------------------------------------------------------
+
+ // these static versions are used for initializers on LDxx/STxx below
+ static uint32_t __immed12_pre(int32_t immed12, int W=0);
+ static uint32_t __immed8_pre(int32_t immed12, int W=0);
+
+ virtual bool isValidImmediate(uint32_t immed) = 0;
+ virtual int buildImmediate(uint32_t i, uint32_t& rot, uint32_t& imm) = 0;
+
+ virtual uint32_t imm(uint32_t immediate) = 0;
+ virtual uint32_t reg_imm(int Rm, int type, uint32_t shift) = 0;
+ virtual uint32_t reg_rrx(int Rm) = 0;
+ virtual uint32_t reg_reg(int Rm, int type, int Rs) = 0;
+
+ // addressing modes...
+ // LDR(B)/STR(B)/PLD
+ // (immediate and Rm can be negative, which indicates U=0)
+ virtual uint32_t immed12_pre(int32_t immed12, int W=0) = 0;
+ virtual uint32_t immed12_post(int32_t immed12) = 0;
+ virtual uint32_t reg_scale_pre(int Rm, int type=0, uint32_t shift=0, int W=0) = 0;
+ virtual uint32_t reg_scale_post(int Rm, int type=0, uint32_t shift=0) = 0;
+
+ // LDRH/LDRSB/LDRSH/STRH
+ // (immediate and Rm can be negative, which indicates U=0)
+ virtual uint32_t immed8_pre(int32_t immed8, int W=0) = 0;
+ virtual uint32_t immed8_post(int32_t immed8) = 0;
+ virtual uint32_t reg_pre(int Rm, int W=0) = 0;
+ virtual uint32_t reg_post(int Rm) = 0;
+
+ // -----------------------------------------------------------------------
+ // basic instructions & code generation
+ // -----------------------------------------------------------------------
+
+ // generate the code
+ virtual void reset() = 0;
+ virtual int generate(const char* name) = 0;
+ virtual void disassemble(const char* name) = 0;
+ virtual int getCodegenArch() = 0;
+
+ // construct prolog and epilog
+ virtual void prolog() = 0;
+ virtual void epilog(uint32_t touched) = 0;
+ virtual void comment(const char* string) = 0;
+
+ // data processing...
+ enum {
+ opAND, opEOR, opSUB, opRSB, opADD, opADC, opSBC, opRSC,
+ opTST, opTEQ, opCMP, opCMN, opORR, opMOV, opBIC, opMVN
+ };
+
+ virtual void
+ dataProcessing( int opcode, int cc, int s,
+ int Rd, int Rn,
+ uint32_t Op2) = 0;
+
+ // multiply...
+ virtual void MLA(int cc, int s,
+ int Rd, int Rm, int Rs, int Rn) = 0;
+ virtual void MUL(int cc, int s,
+ int Rd, int Rm, int Rs) = 0;
+ virtual void UMULL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs) = 0;
+ virtual void UMUAL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs) = 0;
+ virtual void SMULL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs) = 0;
+ virtual void SMUAL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs) = 0;
+
+ // branches...
+ virtual void B(int cc, uint32_t* pc) = 0;
+ virtual void BL(int cc, uint32_t* pc) = 0;
+ virtual void BX(int cc, int Rn) = 0;
+
+ virtual void label(const char* theLabel) = 0;
+ virtual void B(int cc, const char* label) = 0;
+ virtual void BL(int cc, const char* label) = 0;
+
+ // valid only after generate() has been called
+ virtual uint32_t* pcForLabel(const char* label) = 0;
+
+ // data transfer...
+ virtual void LDR (int cc, int Rd,
+ int Rn, uint32_t offset = __immed12_pre(0)) = 0;
+ virtual void LDRB(int cc, int Rd,
+ int Rn, uint32_t offset = __immed12_pre(0)) = 0;
+ virtual void STR (int cc, int Rd,
+ int Rn, uint32_t offset = __immed12_pre(0)) = 0;
+ virtual void STRB(int cc, int Rd,
+ int Rn, uint32_t offset = __immed12_pre(0)) = 0;
+
+ virtual void LDRH (int cc, int Rd,
+ int Rn, uint32_t offset = __immed8_pre(0)) = 0;
+ virtual void LDRSB(int cc, int Rd,
+ int Rn, uint32_t offset = __immed8_pre(0)) = 0;
+ virtual void LDRSH(int cc, int Rd,
+ int Rn, uint32_t offset = __immed8_pre(0)) = 0;
+ virtual void STRH (int cc, int Rd,
+ int Rn, uint32_t offset = __immed8_pre(0)) = 0;
+
+ // block data transfer...
+ virtual void LDM(int cc, int dir,
+ int Rn, int W, uint32_t reg_list) = 0;
+ virtual void STM(int cc, int dir,
+ int Rn, int W, uint32_t reg_list) = 0;
+
+ // special...
+ virtual void SWP(int cc, int Rn, int Rd, int Rm) = 0;
+ virtual void SWPB(int cc, int Rn, int Rd, int Rm) = 0;
+ virtual void SWI(int cc, uint32_t comment) = 0;
+
+ // DSP instructions...
+ enum {
+ // B=0, T=1
+ // yx
+ xyBB = 0, // 0000,
+ xyTB = 2, // 0010,
+ xyBT = 4, // 0100,
+ xyTT = 6, // 0110,
+ yB = 0, // 0000,
+ yT = 4, // 0100
+ };
+
+ virtual void PLD(int Rn, uint32_t offset) = 0;
+
+ virtual void CLZ(int cc, int Rd, int Rm) = 0;
+
+ virtual void QADD(int cc, int Rd, int Rm, int Rn) = 0;
+ virtual void QDADD(int cc, int Rd, int Rm, int Rn) = 0;
+ virtual void QSUB(int cc, int Rd, int Rm, int Rn) = 0;
+ virtual void QDSUB(int cc, int Rd, int Rm, int Rn) = 0;
+
+ virtual void SMUL(int cc, int xy,
+ int Rd, int Rm, int Rs) = 0;
+ virtual void SMULW(int cc, int y,
+ int Rd, int Rm, int Rs) = 0;
+ virtual void SMLA(int cc, int xy,
+ int Rd, int Rm, int Rs, int Rn) = 0;
+ virtual void SMLAL(int cc, int xy,
+ int RdHi, int RdLo, int Rs, int Rm) = 0;
+ virtual void SMLAW(int cc, int y,
+ int Rd, int Rm, int Rs, int Rn) = 0;
+
+ // byte/half word extract...
+ virtual void UXTB16(int cc, int Rd, int Rm, int rotate) = 0;
+
+ // bit manipulation...
+ virtual void UBFX(int cc, int Rd, int Rn, int lsb, int width) = 0;
+
+ // -----------------------------------------------------------------------
+ // convenience...
+ // -----------------------------------------------------------------------
+ inline void
+ ADC(int cc, int s, int Rd, int Rn, uint32_t Op2) {
+ dataProcessing(opADC, cc, s, Rd, Rn, Op2);
+ }
+ inline void
+ ADD(int cc, int s, int Rd, int Rn, uint32_t Op2) {
+ dataProcessing(opADD, cc, s, Rd, Rn, Op2);
+ }
+ inline void
+ AND(int cc, int s, int Rd, int Rn, uint32_t Op2) {
+ dataProcessing(opAND, cc, s, Rd, Rn, Op2);
+ }
+ inline void
+ BIC(int cc, int s, int Rd, int Rn, uint32_t Op2) {
+ dataProcessing(opBIC, cc, s, Rd, Rn, Op2);
+ }
+ inline void
+ EOR(int cc, int s, int Rd, int Rn, uint32_t Op2) {
+ dataProcessing(opEOR, cc, s, Rd, Rn, Op2);
+ }
+ inline void
+ MOV(int cc, int s, int Rd, uint32_t Op2) {
+ dataProcessing(opMOV, cc, s, Rd, 0, Op2);
+ }
+ inline void
+ MVN(int cc, int s, int Rd, uint32_t Op2) {
+ dataProcessing(opMVN, cc, s, Rd, 0, Op2);
+ }
+ inline void
+ ORR(int cc, int s, int Rd, int Rn, uint32_t Op2) {
+ dataProcessing(opORR, cc, s, Rd, Rn, Op2);
+ }
+ inline void
+ RSB(int cc, int s, int Rd, int Rn, uint32_t Op2) {
+ dataProcessing(opRSB, cc, s, Rd, Rn, Op2);
+ }
+ inline void
+ RSC(int cc, int s, int Rd, int Rn, uint32_t Op2) {
+ dataProcessing(opRSC, cc, s, Rd, Rn, Op2);
+ }
+ inline void
+ SBC(int cc, int s, int Rd, int Rn, uint32_t Op2) {
+ dataProcessing(opSBC, cc, s, Rd, Rn, Op2);
+ }
+ inline void
+ SUB(int cc, int s, int Rd, int Rn, uint32_t Op2) {
+ dataProcessing(opSUB, cc, s, Rd, Rn, Op2);
+ }
+ inline void
+ TEQ(int cc, int Rn, uint32_t Op2) {
+ dataProcessing(opTEQ, cc, 1, 0, Rn, Op2);
+ }
+ inline void
+ TST(int cc, int Rn, uint32_t Op2) {
+ dataProcessing(opTST, cc, 1, 0, Rn, Op2);
+ }
+ inline void
+ CMP(int cc, int Rn, uint32_t Op2) {
+ dataProcessing(opCMP, cc, 1, 0, Rn, Op2);
+ }
+ inline void
+ CMN(int cc, int Rn, uint32_t Op2) {
+ dataProcessing(opCMN, cc, 1, 0, Rn, Op2);
+ }
+
+ inline void SMULBB(int cc, int Rd, int Rm, int Rs) {
+ SMUL(cc, xyBB, Rd, Rm, Rs); }
+ inline void SMULTB(int cc, int Rd, int Rm, int Rs) {
+ SMUL(cc, xyTB, Rd, Rm, Rs); }
+ inline void SMULBT(int cc, int Rd, int Rm, int Rs) {
+ SMUL(cc, xyBT, Rd, Rm, Rs); }
+ inline void SMULTT(int cc, int Rd, int Rm, int Rs) {
+ SMUL(cc, xyTT, Rd, Rm, Rs); }
+
+ inline void SMULWB(int cc, int Rd, int Rm, int Rs) {
+ SMULW(cc, yB, Rd, Rm, Rs); }
+ inline void SMULWT(int cc, int Rd, int Rm, int Rs) {
+ SMULW(cc, yT, Rd, Rm, Rs); }
+
+ inline void
+ SMLABB(int cc, int Rd, int Rm, int Rs, int Rn) {
+ SMLA(cc, xyBB, Rd, Rm, Rs, Rn); }
+ inline void
+ SMLATB(int cc, int Rd, int Rm, int Rs, int Rn) {
+ SMLA(cc, xyTB, Rd, Rm, Rs, Rn); }
+ inline void
+ SMLABT(int cc, int Rd, int Rm, int Rs, int Rn) {
+ SMLA(cc, xyBT, Rd, Rm, Rs, Rn); }
+ inline void
+ SMLATT(int cc, int Rd, int Rm, int Rs, int Rn) {
+ SMLA(cc, xyTT, Rd, Rm, Rs, Rn); }
+
+ inline void
+ SMLALBB(int cc, int RdHi, int RdLo, int Rs, int Rm) {
+ SMLAL(cc, xyBB, RdHi, RdLo, Rs, Rm); }
+ inline void
+ SMLALTB(int cc, int RdHi, int RdLo, int Rs, int Rm) {
+ SMLAL(cc, xyTB, RdHi, RdLo, Rs, Rm); }
+ inline void
+ SMLALBT(int cc, int RdHi, int RdLo, int Rs, int Rm) {
+ SMLAL(cc, xyBT, RdHi, RdLo, Rs, Rm); }
+ inline void
+ SMLALTT(int cc, int RdHi, int RdLo, int Rs, int Rm) {
+ SMLAL(cc, xyTT, RdHi, RdLo, Rs, Rm); }
+
+ inline void
+ SMLAWB(int cc, int Rd, int Rm, int Rs, int Rn) {
+ SMLAW(cc, yB, Rd, Rm, Rs, Rn); }
+ inline void
+ SMLAWT(int cc, int Rd, int Rm, int Rs, int Rn) {
+ SMLAW(cc, yT, Rd, Rm, Rs, Rn); }
+
+ // Address loading/storing/manipulation
+ virtual void ADDR_LDR(int cc, int Rd,
+ int Rn, uint32_t offset = __immed12_pre(0));
+ virtual void ADDR_STR (int cc, int Rd,
+ int Rn, uint32_t offset = __immed12_pre(0));
+ virtual void ADDR_ADD(int cc, int s, int Rd,
+ int Rn, uint32_t Op2);
+ virtual void ADDR_SUB(int cc, int s, int Rd,
+ int Rn, uint32_t Op2);
+};
+
+}; // namespace android
+
+#endif //ANDROID_ARMASSEMBLER_INTERFACE_H
diff --git a/libpixelflinger/codeflinger/ARMAssemblerProxy.cpp b/libpixelflinger/codeflinger/ARMAssemblerProxy.cpp
new file mode 100644
index 0000000..816de48
--- /dev/null
+++ b/libpixelflinger/codeflinger/ARMAssemblerProxy.cpp
@@ -0,0 +1,311 @@
+/* libs/pixelflinger/codeflinger/ARMAssemblerProxy.cpp
+**
+** Copyright 2006, 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 <sys/types.h>
+
+#include "ARMAssemblerProxy.h"
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+ARMAssemblerProxy::ARMAssemblerProxy()
+ : mTarget(0)
+{
+}
+
+ARMAssemblerProxy::ARMAssemblerProxy(ARMAssemblerInterface* target)
+ : mTarget(target)
+{
+}
+
+ARMAssemblerProxy::~ARMAssemblerProxy()
+{
+ delete mTarget;
+}
+
+void ARMAssemblerProxy::setTarget(ARMAssemblerInterface* target)
+{
+ delete mTarget;
+ mTarget = target;
+}
+
+void ARMAssemblerProxy::reset() {
+ mTarget->reset();
+}
+int ARMAssemblerProxy::generate(const char* name) {
+ return mTarget->generate(name);
+}
+void ARMAssemblerProxy::disassemble(const char* name) {
+ return mTarget->disassemble(name);
+}
+int ARMAssemblerProxy::getCodegenArch()
+{
+ return mTarget->getCodegenArch();
+}
+void ARMAssemblerProxy::prolog() {
+ mTarget->prolog();
+}
+void ARMAssemblerProxy::epilog(uint32_t touched) {
+ mTarget->epilog(touched);
+}
+void ARMAssemblerProxy::comment(const char* string) {
+ mTarget->comment(string);
+}
+
+
+
+// addressing modes
+
+bool ARMAssemblerProxy::isValidImmediate(uint32_t immed)
+{
+ return mTarget->isValidImmediate(immed);
+}
+
+int ARMAssemblerProxy::buildImmediate(uint32_t i, uint32_t& rot, uint32_t& imm)
+{
+ return mTarget->buildImmediate(i, rot, imm);
+}
+
+
+
+uint32_t ARMAssemblerProxy::imm(uint32_t immediate)
+{
+ return mTarget->imm(immediate);
+}
+
+uint32_t ARMAssemblerProxy::reg_imm(int Rm, int type, uint32_t shift)
+{
+ return mTarget->reg_imm(Rm, type, shift);
+}
+
+uint32_t ARMAssemblerProxy::reg_rrx(int Rm)
+{
+ return mTarget->reg_rrx(Rm);
+}
+
+uint32_t ARMAssemblerProxy::reg_reg(int Rm, int type, int Rs)
+{
+ return mTarget->reg_reg(Rm, type, Rs);
+}
+
+
+// addressing modes...
+// LDR(B)/STR(B)/PLD
+// (immediate and Rm can be negative, which indicates U=0)
+uint32_t ARMAssemblerProxy::immed12_pre(int32_t immed12, int W)
+{
+ return mTarget->immed12_pre(immed12, W);
+}
+
+uint32_t ARMAssemblerProxy::immed12_post(int32_t immed12)
+{
+ return mTarget->immed12_post(immed12);
+}
+
+uint32_t ARMAssemblerProxy::reg_scale_pre(int Rm, int type, uint32_t shift, int W)
+{
+ return mTarget->reg_scale_pre(Rm, type, shift, W);
+}
+
+uint32_t ARMAssemblerProxy::reg_scale_post(int Rm, int type, uint32_t shift)
+{
+ return mTarget->reg_scale_post(Rm, type, shift);
+}
+
+
+// LDRH/LDRSB/LDRSH/STRH
+// (immediate and Rm can be negative, which indicates U=0)
+uint32_t ARMAssemblerProxy::immed8_pre(int32_t immed8, int W)
+{
+ return mTarget->immed8_pre(immed8, W);
+}
+
+uint32_t ARMAssemblerProxy::immed8_post(int32_t immed8)
+{
+ return mTarget->immed8_post(immed8);
+}
+
+uint32_t ARMAssemblerProxy::reg_pre(int Rm, int W)
+{
+ return mTarget->reg_pre(Rm, W);
+}
+
+uint32_t ARMAssemblerProxy::reg_post(int Rm)
+{
+ return mTarget->reg_post(Rm);
+}
+
+
+//------------------------------------------------------------------------
+
+
+
+void ARMAssemblerProxy::dataProcessing( int opcode, int cc, int s,
+ int Rd, int Rn, uint32_t Op2)
+{
+ mTarget->dataProcessing(opcode, cc, s, Rd, Rn, Op2);
+}
+
+void ARMAssemblerProxy::MLA(int cc, int s, int Rd, int Rm, int Rs, int Rn) {
+ mTarget->MLA(cc, s, Rd, Rm, Rs, Rn);
+}
+void ARMAssemblerProxy::MUL(int cc, int s, int Rd, int Rm, int Rs) {
+ mTarget->MUL(cc, s, Rd, Rm, Rs);
+}
+void ARMAssemblerProxy::UMULL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs) {
+ mTarget->UMULL(cc, s, RdLo, RdHi, Rm, Rs);
+}
+void ARMAssemblerProxy::UMUAL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs) {
+ mTarget->UMUAL(cc, s, RdLo, RdHi, Rm, Rs);
+}
+void ARMAssemblerProxy::SMULL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs) {
+ mTarget->SMULL(cc, s, RdLo, RdHi, Rm, Rs);
+}
+void ARMAssemblerProxy::SMUAL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs) {
+ mTarget->SMUAL(cc, s, RdLo, RdHi, Rm, Rs);
+}
+
+void ARMAssemblerProxy::B(int cc, uint32_t* pc) {
+ mTarget->B(cc, pc);
+}
+void ARMAssemblerProxy::BL(int cc, uint32_t* pc) {
+ mTarget->BL(cc, pc);
+}
+void ARMAssemblerProxy::BX(int cc, int Rn) {
+ mTarget->BX(cc, Rn);
+}
+void ARMAssemblerProxy::label(const char* theLabel) {
+ mTarget->label(theLabel);
+}
+void ARMAssemblerProxy::B(int cc, const char* label) {
+ mTarget->B(cc, label);
+}
+void ARMAssemblerProxy::BL(int cc, const char* label) {
+ mTarget->BL(cc, label);
+}
+
+uint32_t* ARMAssemblerProxy::pcForLabel(const char* label) {
+ return mTarget->pcForLabel(label);
+}
+
+void ARMAssemblerProxy::LDR(int cc, int Rd, int Rn, uint32_t offset) {
+ mTarget->LDR(cc, Rd, Rn, offset);
+}
+void ARMAssemblerProxy::LDRB(int cc, int Rd, int Rn, uint32_t offset) {
+ mTarget->LDRB(cc, Rd, Rn, offset);
+}
+void ARMAssemblerProxy::STR(int cc, int Rd, int Rn, uint32_t offset) {
+ mTarget->STR(cc, Rd, Rn, offset);
+}
+void ARMAssemblerProxy::STRB(int cc, int Rd, int Rn, uint32_t offset) {
+ mTarget->STRB(cc, Rd, Rn, offset);
+}
+void ARMAssemblerProxy::LDRH(int cc, int Rd, int Rn, uint32_t offset) {
+ mTarget->LDRH(cc, Rd, Rn, offset);
+}
+void ARMAssemblerProxy::LDRSB(int cc, int Rd, int Rn, uint32_t offset) {
+ mTarget->LDRSB(cc, Rd, Rn, offset);
+}
+void ARMAssemblerProxy::LDRSH(int cc, int Rd, int Rn, uint32_t offset) {
+ mTarget->LDRSH(cc, Rd, Rn, offset);
+}
+void ARMAssemblerProxy::STRH(int cc, int Rd, int Rn, uint32_t offset) {
+ mTarget->STRH(cc, Rd, Rn, offset);
+}
+void ARMAssemblerProxy::LDM(int cc, int dir, int Rn, int W, uint32_t reg_list) {
+ mTarget->LDM(cc, dir, Rn, W, reg_list);
+}
+void ARMAssemblerProxy::STM(int cc, int dir, int Rn, int W, uint32_t reg_list) {
+ mTarget->STM(cc, dir, Rn, W, reg_list);
+}
+
+void ARMAssemblerProxy::SWP(int cc, int Rn, int Rd, int Rm) {
+ mTarget->SWP(cc, Rn, Rd, Rm);
+}
+void ARMAssemblerProxy::SWPB(int cc, int Rn, int Rd, int Rm) {
+ mTarget->SWPB(cc, Rn, Rd, Rm);
+}
+void ARMAssemblerProxy::SWI(int cc, uint32_t comment) {
+ mTarget->SWI(cc, comment);
+}
+
+
+void ARMAssemblerProxy::PLD(int Rn, uint32_t offset) {
+ mTarget->PLD(Rn, offset);
+}
+void ARMAssemblerProxy::CLZ(int cc, int Rd, int Rm) {
+ mTarget->CLZ(cc, Rd, Rm);
+}
+void ARMAssemblerProxy::QADD(int cc, int Rd, int Rm, int Rn) {
+ mTarget->QADD(cc, Rd, Rm, Rn);
+}
+void ARMAssemblerProxy::QDADD(int cc, int Rd, int Rm, int Rn) {
+ mTarget->QDADD(cc, Rd, Rm, Rn);
+}
+void ARMAssemblerProxy::QSUB(int cc, int Rd, int Rm, int Rn) {
+ mTarget->QSUB(cc, Rd, Rm, Rn);
+}
+void ARMAssemblerProxy::QDSUB(int cc, int Rd, int Rm, int Rn) {
+ mTarget->QDSUB(cc, Rd, Rm, Rn);
+}
+void ARMAssemblerProxy::SMUL(int cc, int xy, int Rd, int Rm, int Rs) {
+ mTarget->SMUL(cc, xy, Rd, Rm, Rs);
+}
+void ARMAssemblerProxy::SMULW(int cc, int y, int Rd, int Rm, int Rs) {
+ mTarget->SMULW(cc, y, Rd, Rm, Rs);
+}
+void ARMAssemblerProxy::SMLA(int cc, int xy, int Rd, int Rm, int Rs, int Rn) {
+ mTarget->SMLA(cc, xy, Rd, Rm, Rs, Rn);
+}
+void ARMAssemblerProxy::SMLAL( int cc, int xy,
+ int RdHi, int RdLo, int Rs, int Rm) {
+ mTarget->SMLAL(cc, xy, RdHi, RdLo, Rs, Rm);
+}
+void ARMAssemblerProxy::SMLAW(int cc, int y, int Rd, int Rm, int Rs, int Rn) {
+ mTarget->SMLAW(cc, y, Rd, Rm, Rs, Rn);
+}
+
+void ARMAssemblerProxy::UXTB16(int cc, int Rd, int Rm, int rotate) {
+ mTarget->UXTB16(cc, Rd, Rm, rotate);
+}
+
+void ARMAssemblerProxy::UBFX(int cc, int Rd, int Rn, int lsb, int width) {
+ mTarget->UBFX(cc, Rd, Rn, lsb, width);
+}
+
+void ARMAssemblerProxy::ADDR_LDR(int cc, int Rd, int Rn, uint32_t offset) {
+ mTarget->ADDR_LDR(cc, Rd, Rn, offset);
+}
+void ARMAssemblerProxy::ADDR_STR(int cc, int Rd, int Rn, uint32_t offset) {
+ mTarget->ADDR_STR(cc, Rd, Rn, offset);
+}
+void ARMAssemblerProxy::ADDR_ADD(int cc, int s, int Rd, int Rn, uint32_t Op2){
+ mTarget->ADDR_ADD(cc, s, Rd, Rn, Op2);
+}
+void ARMAssemblerProxy::ADDR_SUB(int cc, int s, int Rd, int Rn, uint32_t Op2){
+ mTarget->ADDR_SUB(cc, s, Rd, Rn, Op2);
+}
+
+}; // namespace android
+
diff --git a/libpixelflinger/codeflinger/ARMAssemblerProxy.h b/libpixelflinger/codeflinger/ARMAssemblerProxy.h
new file mode 100644
index 0000000..b852794
--- /dev/null
+++ b/libpixelflinger/codeflinger/ARMAssemblerProxy.h
@@ -0,0 +1,164 @@
+/* libs/pixelflinger/codeflinger/ARMAssemblerProxy.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+
+#ifndef ANDROID_ARMASSEMBLER_PROXY_H
+#define ANDROID_ARMASSEMBLER_PROXY_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "ARMAssemblerInterface.h"
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+class ARMAssemblerProxy : public ARMAssemblerInterface
+{
+public:
+ // ARMAssemblerProxy take ownership of the target
+
+ ARMAssemblerProxy();
+ ARMAssemblerProxy(ARMAssemblerInterface* target);
+ virtual ~ARMAssemblerProxy();
+
+ void setTarget(ARMAssemblerInterface* target);
+
+ virtual void reset();
+ virtual int generate(const char* name);
+ virtual void disassemble(const char* name);
+ virtual int getCodegenArch();
+
+ virtual void prolog();
+ virtual void epilog(uint32_t touched);
+ virtual void comment(const char* string);
+
+ // -----------------------------------------------------------------------
+ // shifters and addressing modes
+ // -----------------------------------------------------------------------
+
+ virtual bool isValidImmediate(uint32_t immed);
+ virtual int buildImmediate(uint32_t i, uint32_t& rot, uint32_t& imm);
+
+ virtual uint32_t imm(uint32_t immediate);
+ virtual uint32_t reg_imm(int Rm, int type, uint32_t shift);
+ virtual uint32_t reg_rrx(int Rm);
+ virtual uint32_t reg_reg(int Rm, int type, int Rs);
+
+ // addressing modes...
+ // LDR(B)/STR(B)/PLD
+ // (immediate and Rm can be negative, which indicates U=0)
+ virtual uint32_t immed12_pre(int32_t immed12, int W=0);
+ virtual uint32_t immed12_post(int32_t immed12);
+ virtual uint32_t reg_scale_pre(int Rm, int type=0, uint32_t shift=0, int W=0);
+ virtual uint32_t reg_scale_post(int Rm, int type=0, uint32_t shift=0);
+
+ // LDRH/LDRSB/LDRSH/STRH
+ // (immediate and Rm can be negative, which indicates U=0)
+ virtual uint32_t immed8_pre(int32_t immed8, int W=0);
+ virtual uint32_t immed8_post(int32_t immed8);
+ virtual uint32_t reg_pre(int Rm, int W=0);
+ virtual uint32_t reg_post(int Rm);
+
+
+ virtual void dataProcessing(int opcode, int cc, int s,
+ int Rd, int Rn,
+ uint32_t Op2);
+ virtual void MLA(int cc, int s,
+ int Rd, int Rm, int Rs, int Rn);
+ virtual void MUL(int cc, int s,
+ int Rd, int Rm, int Rs);
+ virtual void UMULL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs);
+ virtual void UMUAL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs);
+ virtual void SMULL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs);
+ virtual void SMUAL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs);
+
+ virtual void B(int cc, uint32_t* pc);
+ virtual void BL(int cc, uint32_t* pc);
+ virtual void BX(int cc, int Rn);
+ virtual void label(const char* theLabel);
+ virtual void B(int cc, const char* label);
+ virtual void BL(int cc, const char* label);
+
+ uint32_t* pcForLabel(const char* label);
+
+ virtual void LDR (int cc, int Rd,
+ int Rn, uint32_t offset = __immed12_pre(0));
+ virtual void LDRB(int cc, int Rd,
+ int Rn, uint32_t offset = __immed12_pre(0));
+ virtual void STR (int cc, int Rd,
+ int Rn, uint32_t offset = __immed12_pre(0));
+ virtual void STRB(int cc, int Rd,
+ int Rn, uint32_t offset = __immed12_pre(0));
+ virtual void LDRH (int cc, int Rd,
+ int Rn, uint32_t offset = __immed8_pre(0));
+ virtual void LDRSB(int cc, int Rd,
+ int Rn, uint32_t offset = __immed8_pre(0));
+ virtual void LDRSH(int cc, int Rd,
+ int Rn, uint32_t offset = __immed8_pre(0));
+ virtual void STRH (int cc, int Rd,
+ int Rn, uint32_t offset = __immed8_pre(0));
+ virtual void LDM(int cc, int dir,
+ int Rn, int W, uint32_t reg_list);
+ virtual void STM(int cc, int dir,
+ int Rn, int W, uint32_t reg_list);
+
+ virtual void SWP(int cc, int Rn, int Rd, int Rm);
+ virtual void SWPB(int cc, int Rn, int Rd, int Rm);
+ virtual void SWI(int cc, uint32_t comment);
+
+ virtual void PLD(int Rn, uint32_t offset);
+ virtual void CLZ(int cc, int Rd, int Rm);
+ virtual void QADD(int cc, int Rd, int Rm, int Rn);
+ virtual void QDADD(int cc, int Rd, int Rm, int Rn);
+ virtual void QSUB(int cc, int Rd, int Rm, int Rn);
+ virtual void QDSUB(int cc, int Rd, int Rm, int Rn);
+ virtual void SMUL(int cc, int xy,
+ int Rd, int Rm, int Rs);
+ virtual void SMULW(int cc, int y,
+ int Rd, int Rm, int Rs);
+ virtual void SMLA(int cc, int xy,
+ int Rd, int Rm, int Rs, int Rn);
+ virtual void SMLAL(int cc, int xy,
+ int RdHi, int RdLo, int Rs, int Rm);
+ virtual void SMLAW(int cc, int y,
+ int Rd, int Rm, int Rs, int Rn);
+
+ virtual void UXTB16(int cc, int Rd, int Rm, int rotate);
+ virtual void UBFX(int cc, int Rd, int Rn, int lsb, int width);
+
+ virtual void ADDR_LDR(int cc, int Rd,
+ int Rn, uint32_t offset = __immed12_pre(0));
+ virtual void ADDR_STR (int cc, int Rd,
+ int Rn, uint32_t offset = __immed12_pre(0));
+ virtual void ADDR_ADD(int cc, int s, int Rd,
+ int Rn, uint32_t Op2);
+ virtual void ADDR_SUB(int cc, int s, int Rd,
+ int Rn, uint32_t Op2);
+
+private:
+ ARMAssemblerInterface* mTarget;
+};
+
+}; // namespace android
+
+#endif //ANDROID_ARMASSEMBLER_PROXY_H
diff --git a/libpixelflinger/codeflinger/Arm64Assembler.cpp b/libpixelflinger/codeflinger/Arm64Assembler.cpp
new file mode 100644
index 0000000..bd11818
--- /dev/null
+++ b/libpixelflinger/codeflinger/Arm64Assembler.cpp
@@ -0,0 +1,1242 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define LOG_TAG "ArmToArm64Assembler"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include <private/pixelflinger/ggl_context.h>
+
+#include "codeflinger/Arm64Assembler.h"
+#include "codeflinger/CodeCache.h"
+#include "codeflinger/Arm64Disassembler.h"
+
+
+/*
+** --------------------------------------------
+** Support for Arm64 in GGLAssembler JIT
+** --------------------------------------------
+**
+** Approach
+** - GGLAssembler and associated files are largely un-changed.
+** - A translator class maps ArmAssemblerInterface calls to
+** generate Arm64 instructions.
+**
+** ----------------------
+** ArmToArm64Assembler
+** ----------------------
+**
+** - Subclassed from ArmAssemblerInterface
+**
+** - Translates each ArmAssemblerInterface call to generate
+** one or more Arm64 instructions as necessary.
+**
+** - Does not implement ArmAssemblerInterface portions unused by GGLAssembler
+** It calls NOT_IMPLEMENTED() for such cases, which in turn logs
+** a fatal message.
+**
+** - Uses A64_.. series of functions to generate instruction machine code
+** for Arm64 instructions. These functions also log the instruction
+** to LOG, if ARM64_ASM_DEBUG define is set to 1
+**
+** - Dumps machine code and eqvt assembly if "debug.pf.disasm" option is set
+** It uses arm64_disassemble to perform disassembly
+**
+** - Uses register 13 (SP in ARM), 15 (PC in ARM), 16, 17 for storing
+** intermediate results. GGLAssembler does not use SP and PC as these
+** registers are marked as reserved. The temporary registers are not
+** saved/restored on stack as these are caller-saved registers in Arm64
+**
+** - Uses CSEL instruction to support conditional execution. The result is
+** stored in a temporary register and then copied to the target register
+** if the condition is true.
+**
+** - In the case of conditional data transfer instructions, conditional
+** branch is used to skip over instruction, if the condition is false
+**
+** - Wherever possible, immediate values are transferred to temporary
+** register prior to processing. This simplifies overall implementation
+** as instructions requiring immediate values are converted to
+** move immediate instructions followed by register-register instruction.
+**
+** --------------------------------------------
+** ArmToArm64Assembler unit test bench
+** --------------------------------------------
+**
+** - Tests ArmToArm64Assembler interface for all the possible
+** ways in which GGLAssembler uses ArmAssemblerInterface interface.
+**
+** - Uses test jacket (written in assembly) to set the registers,
+** condition flags prior to calling generated instruction. It also
+** copies registers and flags at the end of execution. Caller then
+** checks if generated code performed correct operation based on
+** output registers and flags.
+**
+** - Broadly contains three type of tests, (i) data operation tests
+** (ii) data transfer tests and (iii) LDM/STM tests.
+**
+** ----------------------
+** Arm64 disassembler
+** ----------------------
+** - This disassembler disassembles only those machine codes which can be
+** generated by ArmToArm64Assembler. It has a unit testbench which
+** tests all the instructions supported by the disassembler.
+**
+** ------------------------------------------------------------------
+** ARMAssembler/ARMAssemblerInterface/ARMAssemblerProxy changes
+** ------------------------------------------------------------------
+**
+** - In existing code, addresses were being handled as 32 bit values at
+** certain places.
+**
+** - Added a new set of functions for address load/store/manipulation.
+** These are ADDR_LDR, ADDR_STR, ADDR_ADD, ADDR_SUB and they map to
+** default 32 bit implementations in ARMAssemblerInterface.
+**
+** - ArmToArm64Assembler maps these functions to appropriate 64 bit
+** functions.
+**
+** ----------------------
+** GGLAssembler changes
+** ----------------------
+** - Since ArmToArm64Assembler can generate 4 Arm64 instructions for
+** each call in worst case, the memory required is set to 4 times
+** ARM memory
+**
+** - Address load/store/manipulation were changed to use new functions
+** added in the ARMAssemblerInterface.
+**
+*/
+
+
+#define NOT_IMPLEMENTED() LOG_FATAL("Arm instruction %s not yet implemented\n", __func__)
+
+#define ARM64_ASM_DEBUG 0
+
+#if ARM64_ASM_DEBUG
+ #define LOG_INSTR(...) ALOGD("\t" __VA_ARGS__)
+ #define LOG_LABEL(...) ALOGD(__VA_ARGS__)
+#else
+ #define LOG_INSTR(...) ((void)0)
+ #define LOG_LABEL(...) ((void)0)
+#endif
+
+namespace android {
+
+static const char* shift_codes[] =
+{
+ "LSL", "LSR", "ASR", "ROR"
+};
+static const char *cc_codes[] =
+{
+ "EQ", "NE", "CS", "CC", "MI",
+ "PL", "VS", "VC", "HI", "LS",
+ "GE", "LT", "GT", "LE", "AL", "NV"
+};
+
+ArmToArm64Assembler::ArmToArm64Assembler(const sp<Assembly>& assembly)
+ : ARMAssemblerInterface(),
+ mAssembly(assembly)
+{
+ mBase = mPC = (uint32_t *)assembly->base();
+ mDuration = ggl_system_time();
+ mZeroReg = 13;
+ mTmpReg1 = 15;
+ mTmpReg2 = 16;
+ mTmpReg3 = 17;
+}
+
+ArmToArm64Assembler::ArmToArm64Assembler(void *base)
+ : ARMAssemblerInterface(), mAssembly(NULL)
+{
+ mBase = mPC = (uint32_t *)base;
+ mDuration = ggl_system_time();
+ // Regs 13, 15, 16, 17 are used as temporary registers
+ mZeroReg = 13;
+ mTmpReg1 = 15;
+ mTmpReg2 = 16;
+ mTmpReg3 = 17;
+}
+
+ArmToArm64Assembler::~ArmToArm64Assembler()
+{
+}
+
+uint32_t* ArmToArm64Assembler::pc() const
+{
+ return mPC;
+}
+
+uint32_t* ArmToArm64Assembler::base() const
+{
+ return mBase;
+}
+
+void ArmToArm64Assembler::reset()
+{
+ if(mAssembly == NULL)
+ mPC = mBase;
+ else
+ mBase = mPC = (uint32_t *)mAssembly->base();
+ mBranchTargets.clear();
+ mLabels.clear();
+ mLabelsInverseMapping.clear();
+ mComments.clear();
+#if ARM64_ASM_DEBUG
+ ALOGI("RESET\n");
+#endif
+}
+
+int ArmToArm64Assembler::getCodegenArch()
+{
+ return CODEGEN_ARCH_ARM64;
+}
+
+// ----------------------------------------------------------------------------
+
+void ArmToArm64Assembler::disassemble(const char* name)
+{
+ if(name)
+ {
+ printf("%s:\n", name);
+ }
+ size_t count = pc()-base();
+ uint32_t* i = base();
+ while (count--)
+ {
+ ssize_t label = mLabelsInverseMapping.indexOfKey(i);
+ if (label >= 0)
+ {
+ printf("%s:\n", mLabelsInverseMapping.valueAt(label));
+ }
+ ssize_t comment = mComments.indexOfKey(i);
+ if (comment >= 0)
+ {
+ printf("; %s\n", mComments.valueAt(comment));
+ }
+ printf("%p: %08x ", i, uint32_t(i[0]));
+ {
+ char instr[256];
+ ::arm64_disassemble(*i, instr);
+ printf("%s\n", instr);
+ }
+ i++;
+ }
+}
+
+void ArmToArm64Assembler::comment(const char* string)
+{
+ mComments.add(mPC, string);
+ LOG_INSTR("//%s\n", string);
+}
+
+void ArmToArm64Assembler::label(const char* theLabel)
+{
+ mLabels.add(theLabel, mPC);
+ mLabelsInverseMapping.add(mPC, theLabel);
+ LOG_LABEL("%s:\n", theLabel);
+}
+
+void ArmToArm64Assembler::B(int cc, const char* label)
+{
+ mBranchTargets.add(branch_target_t(label, mPC));
+ LOG_INSTR("B%s %s\n", cc_codes[cc], label );
+ *mPC++ = (0x54 << 24) | cc;
+}
+
+void ArmToArm64Assembler::BL(int /*cc*/, const char* /*label*/)
+{
+ NOT_IMPLEMENTED(); //Not Required
+}
+
+// ----------------------------------------------------------------------------
+//Prolog/Epilog & Generate...
+// ----------------------------------------------------------------------------
+
+void ArmToArm64Assembler::prolog()
+{
+ // write prolog code
+ mPrologPC = mPC;
+ *mPC++ = A64_MOVZ_X(mZeroReg,0,0);
+}
+
+void ArmToArm64Assembler::epilog(uint32_t /*touched*/)
+{
+ // write epilog code
+ static const int XLR = 30;
+ *mPC++ = A64_RET(XLR);
+}
+
+int ArmToArm64Assembler::generate(const char* name)
+{
+ // fixup all the branches
+ size_t count = mBranchTargets.size();
+ while (count--)
+ {
+ const branch_target_t& bt = mBranchTargets[count];
+ uint32_t* target_pc = mLabels.valueFor(bt.label);
+ LOG_ALWAYS_FATAL_IF(!target_pc,
+ "error resolving branch targets, target_pc is null");
+ int32_t offset = int32_t(target_pc - bt.pc);
+ *bt.pc |= (offset & 0x7FFFF) << 5;
+ }
+
+ if(mAssembly != NULL)
+ mAssembly->resize( int(pc()-base())*4 );
+
+ // the instruction cache is flushed by CodeCache
+ const int64_t duration = ggl_system_time() - mDuration;
+ const char * const format = "generated %s (%d ins) at [%p:%p] in %ld ns\n";
+ ALOGI(format, name, int(pc()-base()), base(), pc(), duration);
+
+
+ char value[PROPERTY_VALUE_MAX];
+ property_get("debug.pf.disasm", value, "0");
+ if (atoi(value) != 0)
+ {
+ printf(format, name, int(pc()-base()), base(), pc(), duration);
+ disassemble(name);
+ }
+ return NO_ERROR;
+}
+
+uint32_t* ArmToArm64Assembler::pcForLabel(const char* label)
+{
+ return mLabels.valueFor(label);
+}
+
+// ----------------------------------------------------------------------------
+// Data Processing...
+// ----------------------------------------------------------------------------
+void ArmToArm64Assembler::dataProcessingCommon(int opcode,
+ int s, int Rd, int Rn, uint32_t Op2)
+{
+ if(opcode != opSUB && s == 1)
+ {
+ NOT_IMPLEMENTED(); //Not required
+ return;
+ }
+
+ if(opcode != opSUB && opcode != opADD && opcode != opAND &&
+ opcode != opORR && opcode != opMVN)
+ {
+ NOT_IMPLEMENTED(); //Not required
+ return;
+ }
+
+ if(Op2 == OPERAND_REG_IMM && mAddrMode.reg_imm_shift > 31)
+ {
+ NOT_IMPLEMENTED();
+ return;
+ }
+
+ //Store immediate in temporary register and convert
+ //immediate operation into register operation
+ if(Op2 == OPERAND_IMM)
+ {
+ int imm = mAddrMode.immediate;
+ *mPC++ = A64_MOVZ_W(mTmpReg2, imm & 0x0000FFFF, 0);
+ *mPC++ = A64_MOVK_W(mTmpReg2, (imm >> 16) & 0x0000FFFF, 16);
+ Op2 = mTmpReg2;
+ }
+
+
+ {
+ uint32_t shift;
+ uint32_t amount;
+ uint32_t Rm;
+
+ if(Op2 == OPERAND_REG_IMM)
+ {
+ shift = mAddrMode.reg_imm_type;
+ amount = mAddrMode.reg_imm_shift;
+ Rm = mAddrMode.reg_imm_Rm;
+ }
+ else if(Op2 < OPERAND_REG)
+ {
+ shift = 0;
+ amount = 0;
+ Rm = Op2;
+ }
+ else
+ {
+ NOT_IMPLEMENTED(); //Not required
+ return;
+ }
+
+ switch(opcode)
+ {
+ case opADD: *mPC++ = A64_ADD_W(Rd, Rn, Rm, shift, amount); break;
+ case opAND: *mPC++ = A64_AND_W(Rd, Rn, Rm, shift, amount); break;
+ case opORR: *mPC++ = A64_ORR_W(Rd, Rn, Rm, shift, amount); break;
+ case opMVN: *mPC++ = A64_ORN_W(Rd, Rn, Rm, shift, amount); break;
+ case opSUB: *mPC++ = A64_SUB_W(Rd, Rn, Rm, shift, amount, s);break;
+ };
+
+ }
+}
+
+void ArmToArm64Assembler::dataProcessing(int opcode, int cc,
+ int s, int Rd, int Rn, uint32_t Op2)
+{
+ uint32_t Wd;
+
+ if(cc != AL)
+ Wd = mTmpReg1;
+ else
+ Wd = Rd;
+
+ if(opcode == opADD || opcode == opAND || opcode == opORR ||opcode == opSUB)
+ {
+ dataProcessingCommon(opcode, s, Wd, Rn, Op2);
+ }
+ else if(opcode == opCMP)
+ {
+ dataProcessingCommon(opSUB, 1, mTmpReg3, Rn, Op2);
+ }
+ else if(opcode == opRSB)
+ {
+ dataProcessingCommon(opSUB, s, Wd, Rn, Op2);
+ dataProcessingCommon(opSUB, s, Wd, mZeroReg, Wd);
+ }
+ else if(opcode == opMOV)
+ {
+ dataProcessingCommon(opORR, 0, Wd, mZeroReg, Op2);
+ if(s == 1)
+ {
+ dataProcessingCommon(opSUB, 1, mTmpReg3, Wd, mZeroReg);
+ }
+ }
+ else if(opcode == opMVN)
+ {
+ dataProcessingCommon(opMVN, s, Wd, mZeroReg, Op2);
+ }
+ else if(opcode == opBIC)
+ {
+ dataProcessingCommon(opMVN, s, mTmpReg3, mZeroReg, Op2);
+ dataProcessingCommon(opAND, s, Wd, Rn, mTmpReg3);
+ }
+ else
+ {
+ NOT_IMPLEMENTED();
+ return;
+ }
+
+ if(cc != AL)
+ {
+ *mPC++ = A64_CSEL_W(Rd, mTmpReg1, Rd, cc);
+ }
+}
+// ----------------------------------------------------------------------------
+// Address Processing...
+// ----------------------------------------------------------------------------
+
+void ArmToArm64Assembler::ADDR_ADD(int cc,
+ int s, int Rd, int Rn, uint32_t Op2)
+{
+ if(cc != AL){ NOT_IMPLEMENTED(); return;} //Not required
+ if(s != 0) { NOT_IMPLEMENTED(); return;} //Not required
+
+
+ if(Op2 == OPERAND_REG_IMM && mAddrMode.reg_imm_type == LSL)
+ {
+ int Rm = mAddrMode.reg_imm_Rm;
+ int amount = mAddrMode.reg_imm_shift;
+ *mPC++ = A64_ADD_X_Wm_SXTW(Rd, Rn, Rm, amount);
+ }
+ else if(Op2 < OPERAND_REG)
+ {
+ int Rm = Op2;
+ int amount = 0;
+ *mPC++ = A64_ADD_X_Wm_SXTW(Rd, Rn, Rm, amount);
+ }
+ else if(Op2 == OPERAND_IMM)
+ {
+ int imm = mAddrMode.immediate;
+ *mPC++ = A64_MOVZ_W(mTmpReg1, imm & 0x0000FFFF, 0);
+ *mPC++ = A64_MOVK_W(mTmpReg1, (imm >> 16) & 0x0000FFFF, 16);
+
+ int Rm = mTmpReg1;
+ int amount = 0;
+ *mPC++ = A64_ADD_X_Wm_SXTW(Rd, Rn, Rm, amount);
+ }
+ else
+ {
+ NOT_IMPLEMENTED(); //Not required
+ }
+}
+
+void ArmToArm64Assembler::ADDR_SUB(int cc,
+ int s, int Rd, int Rn, uint32_t Op2)
+{
+ if(cc != AL){ NOT_IMPLEMENTED(); return;} //Not required
+ if(s != 0) { NOT_IMPLEMENTED(); return;} //Not required
+
+ if(Op2 == OPERAND_REG_IMM && mAddrMode.reg_imm_type == LSR)
+ {
+ *mPC++ = A64_ADD_W(mTmpReg1, mZeroReg, mAddrMode.reg_imm_Rm,
+ LSR, mAddrMode.reg_imm_shift);
+ *mPC++ = A64_SUB_X_Wm_SXTW(Rd, Rn, mTmpReg1, 0);
+ }
+ else
+ {
+ NOT_IMPLEMENTED(); //Not required
+ }
+}
+
+// ----------------------------------------------------------------------------
+// multiply...
+// ----------------------------------------------------------------------------
+void ArmToArm64Assembler::MLA(int cc, int s,int Rd, int Rm, int Rs, int Rn)
+{
+ if(cc != AL){ NOT_IMPLEMENTED(); return;} //Not required
+
+ *mPC++ = A64_MADD_W(Rd, Rm, Rs, Rn);
+ if(s == 1)
+ dataProcessingCommon(opSUB, 1, mTmpReg1, Rd, mZeroReg);
+}
+void ArmToArm64Assembler::MUL(int cc, int s, int Rd, int Rm, int Rs)
+{
+ if(cc != AL){ NOT_IMPLEMENTED(); return;} //Not required
+ if(s != 0) { NOT_IMPLEMENTED(); return;} //Not required
+ *mPC++ = A64_MADD_W(Rd, Rm, Rs, mZeroReg);
+}
+void ArmToArm64Assembler::UMULL(int /*cc*/, int /*s*/,
+ int /*RdLo*/, int /*RdHi*/, int /*Rm*/, int /*Rs*/)
+{
+ NOT_IMPLEMENTED(); //Not required
+}
+void ArmToArm64Assembler::UMUAL(int /*cc*/, int /*s*/,
+ int /*RdLo*/, int /*RdHi*/, int /*Rm*/, int /*Rs*/)
+{
+ NOT_IMPLEMENTED(); //Not required
+}
+void ArmToArm64Assembler::SMULL(int /*cc*/, int /*s*/,
+ int /*RdLo*/, int /*RdHi*/, int /*Rm*/, int /*Rs*/)
+{
+ NOT_IMPLEMENTED(); //Not required
+}
+void ArmToArm64Assembler::SMUAL(int /*cc*/, int /*s*/,
+ int /*RdLo*/, int /*RdHi*/, int /*Rm*/, int /*Rs*/)
+{
+ NOT_IMPLEMENTED(); //Not required
+}
+
+// ----------------------------------------------------------------------------
+// branches relative to PC...
+// ----------------------------------------------------------------------------
+void ArmToArm64Assembler::B(int /*cc*/, uint32_t* /*pc*/){
+ NOT_IMPLEMENTED(); //Not required
+}
+
+void ArmToArm64Assembler::BL(int /*cc*/, uint32_t* /*pc*/){
+ NOT_IMPLEMENTED(); //Not required
+}
+
+void ArmToArm64Assembler::BX(int /*cc*/, int /*Rn*/){
+ NOT_IMPLEMENTED(); //Not required
+}
+
+// ----------------------------------------------------------------------------
+// data transfer...
+// ----------------------------------------------------------------------------
+enum dataTransferOp
+{
+ opLDR,opLDRB,opLDRH,opSTR,opSTRB,opSTRH
+};
+
+void ArmToArm64Assembler::dataTransfer(int op, int cc,
+ int Rd, int Rn, uint32_t op_type, uint32_t size)
+{
+ const int XSP = 31;
+ if(Rn == SP)
+ Rn = XSP;
+
+ if(op_type == OPERAND_IMM)
+ {
+ int addrReg;
+ int imm = mAddrMode.immediate;
+ if(imm >= 0 && imm < (1<<12))
+ *mPC++ = A64_ADD_IMM_X(mTmpReg1, mZeroReg, imm, 0);
+ else if(imm < 0 && -imm < (1<<12))
+ *mPC++ = A64_SUB_IMM_X(mTmpReg1, mZeroReg, -imm, 0);
+ else
+ {
+ NOT_IMPLEMENTED();
+ return;
+ }
+
+ addrReg = Rn;
+ if(mAddrMode.preindex == true || mAddrMode.postindex == true)
+ {
+ *mPC++ = A64_ADD_X(mTmpReg2, addrReg, mTmpReg1);
+ if(mAddrMode.preindex == true)
+ addrReg = mTmpReg2;
+ }
+
+ if(cc != AL)
+ *mPC++ = A64_B_COND(cc^1, 8);
+
+ *mPC++ = A64_LDRSTR_Wm_SXTW_0(op, size, Rd, addrReg, mZeroReg);
+
+ if(mAddrMode.writeback == true)
+ *mPC++ = A64_CSEL_X(Rn, mTmpReg2, Rn, cc);
+ }
+ else if(op_type == OPERAND_REG_OFFSET)
+ {
+ if(cc != AL)
+ *mPC++ = A64_B_COND(cc^1, 8);
+ *mPC++ = A64_LDRSTR_Wm_SXTW_0(op, size, Rd, Rn, mAddrMode.reg_offset);
+
+ }
+ else if(op_type > OPERAND_UNSUPPORTED)
+ {
+ if(cc != AL)
+ *mPC++ = A64_B_COND(cc^1, 8);
+ *mPC++ = A64_LDRSTR_Wm_SXTW_0(op, size, Rd, Rn, mZeroReg);
+ }
+ else
+ {
+ NOT_IMPLEMENTED(); // Not required
+ }
+ return;
+
+}
+void ArmToArm64Assembler::ADDR_LDR(int cc, int Rd, int Rn, uint32_t op_type)
+{
+ return dataTransfer(opLDR, cc, Rd, Rn, op_type, 64);
+}
+void ArmToArm64Assembler::ADDR_STR(int cc, int Rd, int Rn, uint32_t op_type)
+{
+ return dataTransfer(opSTR, cc, Rd, Rn, op_type, 64);
+}
+void ArmToArm64Assembler::LDR(int cc, int Rd, int Rn, uint32_t op_type)
+{
+ return dataTransfer(opLDR, cc, Rd, Rn, op_type);
+}
+void ArmToArm64Assembler::LDRB(int cc, int Rd, int Rn, uint32_t op_type)
+{
+ return dataTransfer(opLDRB, cc, Rd, Rn, op_type);
+}
+void ArmToArm64Assembler::STR(int cc, int Rd, int Rn, uint32_t op_type)
+{
+ return dataTransfer(opSTR, cc, Rd, Rn, op_type);
+}
+
+void ArmToArm64Assembler::STRB(int cc, int Rd, int Rn, uint32_t op_type)
+{
+ return dataTransfer(opSTRB, cc, Rd, Rn, op_type);
+}
+
+void ArmToArm64Assembler::LDRH(int cc, int Rd, int Rn, uint32_t op_type)
+{
+ return dataTransfer(opLDRH, cc, Rd, Rn, op_type);
+}
+void ArmToArm64Assembler::LDRSB(int /*cc*/, int /*Rd*/, int /*Rn*/, uint32_t /*offset*/)
+{
+ NOT_IMPLEMENTED(); //Not required
+}
+void ArmToArm64Assembler::LDRSH(int /*cc*/, int /*Rd*/, int /*Rn*/, uint32_t /*offset*/)
+{
+ NOT_IMPLEMENTED(); //Not required
+}
+
+void ArmToArm64Assembler::STRH(int cc, int Rd, int Rn, uint32_t op_type)
+{
+ return dataTransfer(opSTRH, cc, Rd, Rn, op_type);
+}
+
+// ----------------------------------------------------------------------------
+// block data transfer...
+// ----------------------------------------------------------------------------
+void ArmToArm64Assembler::LDM(int cc, int dir,
+ int Rn, int W, uint32_t reg_list)
+{
+ const int XSP = 31;
+ if(cc != AL || dir != IA || W == 0 || Rn != SP)
+ {
+ NOT_IMPLEMENTED();
+ return;
+ }
+
+ for(int i = 0; i < 32; ++i)
+ {
+ if((reg_list & (1 << i)))
+ {
+ int reg = i;
+ int size = 16;
+ *mPC++ = A64_LDR_IMM_PostIndex(reg, XSP, size);
+ }
+ }
+}
+
+void ArmToArm64Assembler::STM(int cc, int dir,
+ int Rn, int W, uint32_t reg_list)
+{
+ const int XSP = 31;
+ if(cc != AL || dir != DB || W == 0 || Rn != SP)
+ {
+ NOT_IMPLEMENTED();
+ return;
+ }
+
+ for(int i = 31; i >= 0; --i)
+ {
+ if((reg_list & (1 << i)))
+ {
+ int size = -16;
+ int reg = i;
+ *mPC++ = A64_STR_IMM_PreIndex(reg, XSP, size);
+ }
+ }
+}
+
+// ----------------------------------------------------------------------------
+// special...
+// ----------------------------------------------------------------------------
+void ArmToArm64Assembler::SWP(int /*cc*/, int /*Rn*/, int /*Rd*/, int /*Rm*/)
+{
+ NOT_IMPLEMENTED(); //Not required
+}
+void ArmToArm64Assembler::SWPB(int /*cc*/, int /*Rn*/, int /*Rd*/, int /*Rm*/)
+{
+ NOT_IMPLEMENTED(); //Not required
+}
+void ArmToArm64Assembler::SWI(int /*cc*/, uint32_t /*comment*/)
+{
+ NOT_IMPLEMENTED(); //Not required
+}
+
+// ----------------------------------------------------------------------------
+// DSP instructions...
+// ----------------------------------------------------------------------------
+void ArmToArm64Assembler::PLD(int /*Rn*/, uint32_t /*offset*/) {
+ NOT_IMPLEMENTED(); //Not required
+}
+
+void ArmToArm64Assembler::CLZ(int /*cc*/, int /*Rd*/, int /*Rm*/)
+{
+ NOT_IMPLEMENTED(); //Not required
+}
+
+void ArmToArm64Assembler::QADD(int /*cc*/, int /*Rd*/, int /*Rm*/, int /*Rn*/)
+{
+ NOT_IMPLEMENTED(); //Not required
+}
+
+void ArmToArm64Assembler::QDADD(int /*cc*/, int /*Rd*/, int /*Rm*/, int /*Rn*/)
+{
+ NOT_IMPLEMENTED(); //Not required
+}
+
+void ArmToArm64Assembler::QSUB(int /*cc*/, int /*Rd*/, int /*Rm*/, int /*Rn*/)
+{
+ NOT_IMPLEMENTED(); //Not required
+}
+
+void ArmToArm64Assembler::QDSUB(int /*cc*/, int /*Rd*/, int /*Rm*/, int /*Rn*/)
+{
+ NOT_IMPLEMENTED(); //Not required
+}
+
+// ----------------------------------------------------------------------------
+// 16 x 16 multiplication
+// ----------------------------------------------------------------------------
+void ArmToArm64Assembler::SMUL(int cc, int xy,
+ int Rd, int Rm, int Rs)
+{
+ if(cc != AL){ NOT_IMPLEMENTED(); return;} //Not required
+
+ if (xy & xyTB)
+ *mPC++ = A64_SBFM_W(mTmpReg1, Rm, 16, 31);
+ else
+ *mPC++ = A64_SBFM_W(mTmpReg1, Rm, 0, 15);
+
+ if (xy & xyBT)
+ *mPC++ = A64_SBFM_W(mTmpReg2, Rs, 16, 31);
+ else
+ *mPC++ = A64_SBFM_W(mTmpReg2, Rs, 0, 15);
+
+ *mPC++ = A64_MADD_W(Rd,mTmpReg1,mTmpReg2, mZeroReg);
+}
+// ----------------------------------------------------------------------------
+// 32 x 16 multiplication
+// ----------------------------------------------------------------------------
+void ArmToArm64Assembler::SMULW(int cc, int y, int Rd, int Rm, int Rs)
+{
+ if(cc != AL){ NOT_IMPLEMENTED(); return;} //Not required
+
+ if (y & yT)
+ *mPC++ = A64_SBFM_W(mTmpReg1, Rs, 16, 31);
+ else
+ *mPC++ = A64_SBFM_W(mTmpReg1, Rs, 0, 15);
+
+ *mPC++ = A64_SBFM_W(mTmpReg2, Rm, 0, 31);
+ *mPC++ = A64_SMADDL(mTmpReg3,mTmpReg1,mTmpReg2, mZeroReg);
+ *mPC++ = A64_UBFM_X(Rd,mTmpReg3, 16, 47);
+}
+// ----------------------------------------------------------------------------
+// 16 x 16 multiplication and accumulate
+// ----------------------------------------------------------------------------
+void ArmToArm64Assembler::SMLA(int cc, int xy, int Rd, int Rm, int Rs, int Rn)
+{
+ if(cc != AL){ NOT_IMPLEMENTED(); return;} //Not required
+ if(xy != xyBB) { NOT_IMPLEMENTED(); return;} //Not required
+
+ *mPC++ = A64_SBFM_W(mTmpReg1, Rm, 0, 15);
+ *mPC++ = A64_SBFM_W(mTmpReg2, Rs, 0, 15);
+ *mPC++ = A64_MADD_W(Rd, mTmpReg1, mTmpReg2, Rn);
+}
+
+void ArmToArm64Assembler::SMLAL(int /*cc*/, int /*xy*/,
+ int /*RdHi*/, int /*RdLo*/, int /*Rs*/, int /*Rm*/)
+{
+ NOT_IMPLEMENTED(); //Not required
+ return;
+}
+
+void ArmToArm64Assembler::SMLAW(int /*cc*/, int /*y*/,
+ int /*Rd*/, int /*Rm*/, int /*Rs*/, int /*Rn*/)
+{
+ NOT_IMPLEMENTED(); //Not required
+ return;
+}
+
+// ----------------------------------------------------------------------------
+// Byte/half word extract and extend
+// ----------------------------------------------------------------------------
+void ArmToArm64Assembler::UXTB16(int cc, int Rd, int Rm, int rotate)
+{
+ if(cc != AL){ NOT_IMPLEMENTED(); return;} //Not required
+
+ *mPC++ = A64_EXTR_W(mTmpReg1, Rm, Rm, rotate * 8);
+
+ uint32_t imm = 0x00FF00FF;
+ *mPC++ = A64_MOVZ_W(mTmpReg2, imm & 0xFFFF, 0);
+ *mPC++ = A64_MOVK_W(mTmpReg2, (imm >> 16) & 0x0000FFFF, 16);
+ *mPC++ = A64_AND_W(Rd,mTmpReg1, mTmpReg2);
+}
+
+// ----------------------------------------------------------------------------
+// Bit manipulation
+// ----------------------------------------------------------------------------
+void ArmToArm64Assembler::UBFX(int cc, int Rd, int Rn, int lsb, int width)
+{
+ if(cc != AL){ NOT_IMPLEMENTED(); return;} //Not required
+ *mPC++ = A64_UBFM_W(Rd, Rn, lsb, lsb + width - 1);
+}
+// ----------------------------------------------------------------------------
+// Shifters...
+// ----------------------------------------------------------------------------
+int ArmToArm64Assembler::buildImmediate(
+ uint32_t immediate, uint32_t& rot, uint32_t& imm)
+{
+ rot = 0;
+ imm = immediate;
+ return 0; // Always true
+}
+
+
+bool ArmToArm64Assembler::isValidImmediate(uint32_t immediate)
+{
+ uint32_t rot, imm;
+ return buildImmediate(immediate, rot, imm) == 0;
+}
+
+uint32_t ArmToArm64Assembler::imm(uint32_t immediate)
+{
+ mAddrMode.immediate = immediate;
+ mAddrMode.writeback = false;
+ mAddrMode.preindex = false;
+ mAddrMode.postindex = false;
+ return OPERAND_IMM;
+
+}
+
+uint32_t ArmToArm64Assembler::reg_imm(int Rm, int type, uint32_t shift)
+{
+ mAddrMode.reg_imm_Rm = Rm;
+ mAddrMode.reg_imm_type = type;
+ mAddrMode.reg_imm_shift = shift;
+ return OPERAND_REG_IMM;
+}
+
+uint32_t ArmToArm64Assembler::reg_rrx(int /*Rm*/)
+{
+ NOT_IMPLEMENTED();
+ return OPERAND_UNSUPPORTED;
+}
+
+uint32_t ArmToArm64Assembler::reg_reg(int /*Rm*/, int /*type*/, int /*Rs*/)
+{
+ NOT_IMPLEMENTED(); //Not required
+ return OPERAND_UNSUPPORTED;
+}
+// ----------------------------------------------------------------------------
+// Addressing modes...
+// ----------------------------------------------------------------------------
+uint32_t ArmToArm64Assembler::immed12_pre(int32_t immed12, int W)
+{
+ mAddrMode.immediate = immed12;
+ mAddrMode.writeback = W;
+ mAddrMode.preindex = true;
+ mAddrMode.postindex = false;
+ return OPERAND_IMM;
+}
+
+uint32_t ArmToArm64Assembler::immed12_post(int32_t immed12)
+{
+ mAddrMode.immediate = immed12;
+ mAddrMode.writeback = true;
+ mAddrMode.preindex = false;
+ mAddrMode.postindex = true;
+ return OPERAND_IMM;
+}
+
+uint32_t ArmToArm64Assembler::reg_scale_pre(int Rm, int type,
+ uint32_t shift, int W)
+{
+ if(type != 0 || shift != 0 || W != 0)
+ {
+ NOT_IMPLEMENTED(); //Not required
+ return OPERAND_UNSUPPORTED;
+ }
+ else
+ {
+ mAddrMode.reg_offset = Rm;
+ return OPERAND_REG_OFFSET;
+ }
+}
+
+uint32_t ArmToArm64Assembler::reg_scale_post(int /*Rm*/, int /*type*/, uint32_t /*shift*/)
+{
+ NOT_IMPLEMENTED(); //Not required
+ return OPERAND_UNSUPPORTED;
+}
+
+uint32_t ArmToArm64Assembler::immed8_pre(int32_t immed8, int W)
+{
+ mAddrMode.immediate = immed8;
+ mAddrMode.writeback = W;
+ mAddrMode.preindex = true;
+ mAddrMode.postindex = false;
+ return OPERAND_IMM;
+}
+
+uint32_t ArmToArm64Assembler::immed8_post(int32_t immed8)
+{
+ mAddrMode.immediate = immed8;
+ mAddrMode.writeback = true;
+ mAddrMode.preindex = false;
+ mAddrMode.postindex = true;
+ return OPERAND_IMM;
+}
+
+uint32_t ArmToArm64Assembler::reg_pre(int Rm, int W)
+{
+ if(W != 0)
+ {
+ NOT_IMPLEMENTED(); //Not required
+ return OPERAND_UNSUPPORTED;
+ }
+ else
+ {
+ mAddrMode.reg_offset = Rm;
+ return OPERAND_REG_OFFSET;
+ }
+}
+
+uint32_t ArmToArm64Assembler::reg_post(int /*Rm*/)
+{
+ NOT_IMPLEMENTED(); //Not required
+ return OPERAND_UNSUPPORTED;
+}
+
+// ----------------------------------------------------------------------------
+// A64 instructions
+// ----------------------------------------------------------------------------
+
+static const char * dataTransferOpName[] =
+{
+ "LDR","LDRB","LDRH","STR","STRB","STRH"
+};
+
+static const uint32_t dataTransferOpCode [] =
+{
+ ((0xB8u << 24) | (0x3 << 21) | (0x6 << 13) | (0x0 << 12) |(0x1 << 11)),
+ ((0x38u << 24) | (0x3 << 21) | (0x6 << 13) | (0x1 << 12) |(0x1 << 11)),
+ ((0x78u << 24) | (0x3 << 21) | (0x6 << 13) | (0x0 << 12) |(0x1 << 11)),
+ ((0xB8u << 24) | (0x1 << 21) | (0x6 << 13) | (0x0 << 12) |(0x1 << 11)),
+ ((0x38u << 24) | (0x1 << 21) | (0x6 << 13) | (0x1 << 12) |(0x1 << 11)),
+ ((0x78u << 24) | (0x1 << 21) | (0x6 << 13) | (0x0 << 12) |(0x1 << 11))
+};
+uint32_t ArmToArm64Assembler::A64_LDRSTR_Wm_SXTW_0(uint32_t op,
+ uint32_t size, uint32_t Rt,
+ uint32_t Rn, uint32_t Rm)
+{
+ if(size == 32)
+ {
+ LOG_INSTR("%s W%d, [X%d, W%d, SXTW #0]\n",
+ dataTransferOpName[op], Rt, Rn, Rm);
+ return(dataTransferOpCode[op] | (Rm << 16) | (Rn << 5) | Rt);
+ }
+ else
+ {
+ LOG_INSTR("%s X%d, [X%d, W%d, SXTW #0]\n",
+ dataTransferOpName[op], Rt, Rn, Rm);
+ return(dataTransferOpCode[op] | (0x1<<30) | (Rm<<16) | (Rn<<5)|Rt);
+ }
+}
+
+uint32_t ArmToArm64Assembler::A64_STR_IMM_PreIndex(uint32_t Rt,
+ uint32_t Rn, int32_t simm)
+{
+ if(Rn == 31)
+ LOG_INSTR("STR W%d, [SP, #%d]!\n", Rt, simm);
+ else
+ LOG_INSTR("STR W%d, [X%d, #%d]!\n", Rt, Rn, simm);
+
+ uint32_t imm9 = (unsigned)(simm) & 0x01FF;
+ return (0xB8 << 24) | (imm9 << 12) | (0x3 << 10) | (Rn << 5) | Rt;
+}
+
+uint32_t ArmToArm64Assembler::A64_LDR_IMM_PostIndex(uint32_t Rt,
+ uint32_t Rn, int32_t simm)
+{
+ if(Rn == 31)
+ LOG_INSTR("LDR W%d, [SP], #%d\n",Rt,simm);
+ else
+ LOG_INSTR("LDR W%d, [X%d], #%d\n",Rt, Rn, simm);
+
+ uint32_t imm9 = (unsigned)(simm) & 0x01FF;
+ return (0xB8 << 24) | (0x1 << 22) |
+ (imm9 << 12) | (0x1 << 10) | (Rn << 5) | Rt;
+
+}
+uint32_t ArmToArm64Assembler::A64_ADD_X_Wm_SXTW(uint32_t Rd,
+ uint32_t Rn,
+ uint32_t Rm,
+ uint32_t amount)
+{
+ LOG_INSTR("ADD X%d, X%d, W%d, SXTW #%d\n", Rd, Rn, Rm, amount);
+ return ((0x8B << 24) | (0x1 << 21) |(Rm << 16) |
+ (0x6 << 13) | (amount << 10) | (Rn << 5) | Rd);
+
+}
+
+uint32_t ArmToArm64Assembler::A64_SUB_X_Wm_SXTW(uint32_t Rd,
+ uint32_t Rn,
+ uint32_t Rm,
+ uint32_t amount)
+{
+ LOG_INSTR("SUB X%d, X%d, W%d, SXTW #%d\n", Rd, Rn, Rm, amount);
+ return ((0xCB << 24) | (0x1 << 21) |(Rm << 16) |
+ (0x6 << 13) | (amount << 10) | (Rn << 5) | Rd);
+
+}
+
+uint32_t ArmToArm64Assembler::A64_B_COND(uint32_t cc, uint32_t offset)
+{
+ LOG_INSTR("B.%s #.+%d\n", cc_codes[cc], offset);
+ return (0x54 << 24) | ((offset/4) << 5) | (cc);
+
+}
+uint32_t ArmToArm64Assembler::A64_ADD_X(uint32_t Rd, uint32_t Rn,
+ uint32_t Rm, uint32_t shift,
+ uint32_t amount)
+{
+ LOG_INSTR("ADD X%d, X%d, X%d, %s #%d\n",
+ Rd, Rn, Rm, shift_codes[shift], amount);
+ return ((0x8B << 24) | (shift << 22) | ( Rm << 16) |
+ (amount << 10) |(Rn << 5) | Rd);
+}
+uint32_t ArmToArm64Assembler::A64_ADD_IMM_X(uint32_t Rd, uint32_t Rn,
+ uint32_t imm, uint32_t shift)
+{
+ LOG_INSTR("ADD X%d, X%d, #%d, LSL #%d\n", Rd, Rn, imm, shift);
+ return (0x91 << 24) | ((shift/12) << 22) | (imm << 10) | (Rn << 5) | Rd;
+}
+
+uint32_t ArmToArm64Assembler::A64_SUB_IMM_X(uint32_t Rd, uint32_t Rn,
+ uint32_t imm, uint32_t shift)
+{
+ LOG_INSTR("SUB X%d, X%d, #%d, LSL #%d\n", Rd, Rn, imm, shift);
+ return (0xD1 << 24) | ((shift/12) << 22) | (imm << 10) | (Rn << 5) | Rd;
+}
+
+uint32_t ArmToArm64Assembler::A64_ADD_W(uint32_t Rd, uint32_t Rn,
+ uint32_t Rm, uint32_t shift,
+ uint32_t amount)
+{
+ LOG_INSTR("ADD W%d, W%d, W%d, %s #%d\n",
+ Rd, Rn, Rm, shift_codes[shift], amount);
+ return ((0x0B << 24) | (shift << 22) | ( Rm << 16) |
+ (amount << 10) |(Rn << 5) | Rd);
+}
+
+uint32_t ArmToArm64Assembler::A64_SUB_W(uint32_t Rd, uint32_t Rn,
+ uint32_t Rm, uint32_t shift,
+ uint32_t amount,
+ uint32_t setflag)
+{
+ if(setflag == 0)
+ {
+ LOG_INSTR("SUB W%d, W%d, W%d, %s #%d\n",
+ Rd, Rn, Rm, shift_codes[shift], amount);
+ return ((0x4B << 24) | (shift << 22) | ( Rm << 16) |
+ (amount << 10) |(Rn << 5) | Rd);
+ }
+ else
+ {
+ LOG_INSTR("SUBS W%d, W%d, W%d, %s #%d\n",
+ Rd, Rn, Rm, shift_codes[shift], amount);
+ return ((0x6B << 24) | (shift << 22) | ( Rm << 16) |
+ (amount << 10) |(Rn << 5) | Rd);
+ }
+}
+
+uint32_t ArmToArm64Assembler::A64_AND_W(uint32_t Rd, uint32_t Rn,
+ uint32_t Rm, uint32_t shift,
+ uint32_t amount)
+{
+ LOG_INSTR("AND W%d, W%d, W%d, %s #%d\n",
+ Rd, Rn, Rm, shift_codes[shift], amount);
+ return ((0x0A << 24) | (shift << 22) | ( Rm << 16) |
+ (amount << 10) |(Rn << 5) | Rd);
+}
+
+uint32_t ArmToArm64Assembler::A64_ORR_W(uint32_t Rd, uint32_t Rn,
+ uint32_t Rm, uint32_t shift,
+ uint32_t amount)
+{
+ LOG_INSTR("ORR W%d, W%d, W%d, %s #%d\n",
+ Rd, Rn, Rm, shift_codes[shift], amount);
+ return ((0x2A << 24) | (shift << 22) | ( Rm << 16) |
+ (amount << 10) |(Rn << 5) | Rd);
+}
+
+uint32_t ArmToArm64Assembler::A64_ORN_W(uint32_t Rd, uint32_t Rn,
+ uint32_t Rm, uint32_t shift,
+ uint32_t amount)
+{
+ LOG_INSTR("ORN W%d, W%d, W%d, %s #%d\n",
+ Rd, Rn, Rm, shift_codes[shift], amount);
+ return ((0x2A << 24) | (shift << 22) | (0x1 << 21) | ( Rm << 16) |
+ (amount << 10) |(Rn << 5) | Rd);
+}
+
+uint32_t ArmToArm64Assembler::A64_CSEL_X(uint32_t Rd, uint32_t Rn,
+ uint32_t Rm, uint32_t cond)
+{
+ LOG_INSTR("CSEL X%d, X%d, X%d, %s\n", Rd, Rn, Rm, cc_codes[cond]);
+ return ((0x9A << 24)|(0x1 << 23)|(Rm << 16) |(cond << 12)| (Rn << 5) | Rd);
+}
+
+uint32_t ArmToArm64Assembler::A64_CSEL_W(uint32_t Rd, uint32_t Rn,
+ uint32_t Rm, uint32_t cond)
+{
+ LOG_INSTR("CSEL W%d, W%d, W%d, %s\n", Rd, Rn, Rm, cc_codes[cond]);
+ return ((0x1A << 24)|(0x1 << 23)|(Rm << 16) |(cond << 12)| (Rn << 5) | Rd);
+}
+
+uint32_t ArmToArm64Assembler::A64_RET(uint32_t Rn)
+{
+ LOG_INSTR("RET X%d\n", Rn);
+ return ((0xD6 << 24) | (0x1 << 22) | (0x1F << 16) | (Rn << 5));
+}
+
+uint32_t ArmToArm64Assembler::A64_MOVZ_X(uint32_t Rd, uint32_t imm,
+ uint32_t shift)
+{
+ LOG_INSTR("MOVZ X%d, #0x%x, LSL #%d\n", Rd, imm, shift);
+ return(0xD2 << 24) | (0x1 << 23) | ((shift/16) << 21) | (imm << 5) | Rd;
+}
+
+uint32_t ArmToArm64Assembler::A64_MOVK_W(uint32_t Rd, uint32_t imm,
+ uint32_t shift)
+{
+ LOG_INSTR("MOVK W%d, #0x%x, LSL #%d\n", Rd, imm, shift);
+ return (0x72 << 24) | (0x1 << 23) | ((shift/16) << 21) | (imm << 5) | Rd;
+}
+
+uint32_t ArmToArm64Assembler::A64_MOVZ_W(uint32_t Rd, uint32_t imm,
+ uint32_t shift)
+{
+ LOG_INSTR("MOVZ W%d, #0x%x, LSL #%d\n", Rd, imm, shift);
+ return(0x52 << 24) | (0x1 << 23) | ((shift/16) << 21) | (imm << 5) | Rd;
+}
+
+uint32_t ArmToArm64Assembler::A64_SMADDL(uint32_t Rd, uint32_t Rn,
+ uint32_t Rm, uint32_t Ra)
+{
+ LOG_INSTR("SMADDL X%d, W%d, W%d, X%d\n",Rd, Rn, Rm, Ra);
+ return ((0x9B << 24) | (0x1 << 21) | (Rm << 16)|(Ra << 10)|(Rn << 5) | Rd);
+}
+
+uint32_t ArmToArm64Assembler::A64_MADD_W(uint32_t Rd, uint32_t Rn,
+ uint32_t Rm, uint32_t Ra)
+{
+ LOG_INSTR("MADD W%d, W%d, W%d, W%d\n",Rd, Rn, Rm, Ra);
+ return ((0x1B << 24) | (Rm << 16) | (Ra << 10) |(Rn << 5) | Rd);
+}
+
+uint32_t ArmToArm64Assembler::A64_SBFM_W(uint32_t Rd, uint32_t Rn,
+ uint32_t immr, uint32_t imms)
+{
+ LOG_INSTR("SBFM W%d, W%d, #%d, #%d\n", Rd, Rn, immr, imms);
+ return ((0x13 << 24) | (immr << 16) | (imms << 10) | (Rn << 5) | Rd);
+
+}
+uint32_t ArmToArm64Assembler::A64_UBFM_W(uint32_t Rd, uint32_t Rn,
+ uint32_t immr, uint32_t imms)
+{
+ LOG_INSTR("UBFM W%d, W%d, #%d, #%d\n", Rd, Rn, immr, imms);
+ return ((0x53 << 24) | (immr << 16) | (imms << 10) | (Rn << 5) | Rd);
+
+}
+uint32_t ArmToArm64Assembler::A64_UBFM_X(uint32_t Rd, uint32_t Rn,
+ uint32_t immr, uint32_t imms)
+{
+ LOG_INSTR("UBFM X%d, X%d, #%d, #%d\n", Rd, Rn, immr, imms);
+ return ((0xD3 << 24) | (0x1 << 22) |
+ (immr << 16) | (imms << 10) | (Rn << 5) | Rd);
+
+}
+uint32_t ArmToArm64Assembler::A64_EXTR_W(uint32_t Rd, uint32_t Rn,
+ uint32_t Rm, uint32_t lsb)
+{
+ LOG_INSTR("EXTR W%d, W%d, W%d, #%d\n", Rd, Rn, Rm, lsb);
+ return (0x13 << 24)|(0x1 << 23) | (Rm << 16) | (lsb << 10)|(Rn << 5) | Rd;
+}
+
+}; // namespace android
+
diff --git a/libpixelflinger/codeflinger/Arm64Assembler.h b/libpixelflinger/codeflinger/Arm64Assembler.h
new file mode 100644
index 0000000..8479270
--- /dev/null
+++ b/libpixelflinger/codeflinger/Arm64Assembler.h
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef ANDROID_ARMTOARM64ASSEMBLER_H
+#define ANDROID_ARMTOARM64ASSEMBLER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "tinyutils/Vector.h"
+#include "tinyutils/KeyedVector.h"
+#include "tinyutils/smartpointer.h"
+
+#include "tinyutils/smartpointer.h"
+#include "codeflinger/ARMAssemblerInterface.h"
+#include "codeflinger/CodeCache.h"
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+class ArmToArm64Assembler : public ARMAssemblerInterface
+{
+public:
+ ArmToArm64Assembler(const sp<Assembly>& assembly);
+ ArmToArm64Assembler(void *base);
+ virtual ~ArmToArm64Assembler();
+
+ uint32_t* base() const;
+ uint32_t* pc() const;
+
+
+ void disassemble(const char* name);
+
+ // ------------------------------------------------------------------------
+ // ARMAssemblerInterface...
+ // ------------------------------------------------------------------------
+
+ virtual void reset();
+
+ virtual int generate(const char* name);
+ virtual int getCodegenArch();
+
+ virtual void prolog();
+ virtual void epilog(uint32_t touched);
+ virtual void comment(const char* string);
+
+
+ // -----------------------------------------------------------------------
+ // shifters and addressing modes
+ // -----------------------------------------------------------------------
+
+ // shifters...
+ virtual bool isValidImmediate(uint32_t immed);
+ virtual int buildImmediate(uint32_t i, uint32_t& rot, uint32_t& imm);
+
+ virtual uint32_t imm(uint32_t immediate);
+ virtual uint32_t reg_imm(int Rm, int type, uint32_t shift);
+ virtual uint32_t reg_rrx(int Rm);
+ virtual uint32_t reg_reg(int Rm, int type, int Rs);
+
+ // addressing modes...
+ virtual uint32_t immed12_pre(int32_t immed12, int W=0);
+ virtual uint32_t immed12_post(int32_t immed12);
+ virtual uint32_t reg_scale_pre(int Rm, int type=0, uint32_t shift=0, int W=0);
+ virtual uint32_t reg_scale_post(int Rm, int type=0, uint32_t shift=0);
+ virtual uint32_t immed8_pre(int32_t immed8, int W=0);
+ virtual uint32_t immed8_post(int32_t immed8);
+ virtual uint32_t reg_pre(int Rm, int W=0);
+ virtual uint32_t reg_post(int Rm);
+
+
+ virtual void dataProcessing(int opcode, int cc, int s,
+ int Rd, int Rn,
+ uint32_t Op2);
+ virtual void MLA(int cc, int s,
+ int Rd, int Rm, int Rs, int Rn);
+ virtual void MUL(int cc, int s,
+ int Rd, int Rm, int Rs);
+ virtual void UMULL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs);
+ virtual void UMUAL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs);
+ virtual void SMULL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs);
+ virtual void SMUAL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs);
+
+ virtual void B(int cc, uint32_t* pc);
+ virtual void BL(int cc, uint32_t* pc);
+ virtual void BX(int cc, int Rn);
+ virtual void label(const char* theLabel);
+ virtual void B(int cc, const char* label);
+ virtual void BL(int cc, const char* label);
+
+ virtual uint32_t* pcForLabel(const char* label);
+
+ virtual void ADDR_LDR(int cc, int Rd,
+ int Rn, uint32_t offset = 0);
+ virtual void ADDR_ADD(int cc, int s, int Rd,
+ int Rn, uint32_t Op2);
+ virtual void ADDR_SUB(int cc, int s, int Rd,
+ int Rn, uint32_t Op2);
+ virtual void ADDR_STR (int cc, int Rd,
+ int Rn, uint32_t offset = 0);
+
+ virtual void LDR (int cc, int Rd,
+ int Rn, uint32_t offset = 0);
+ virtual void LDRB(int cc, int Rd,
+ int Rn, uint32_t offset = 0);
+ virtual void STR (int cc, int Rd,
+ int Rn, uint32_t offset = 0);
+ virtual void STRB(int cc, int Rd,
+ int Rn, uint32_t offset = 0);
+ virtual void LDRH (int cc, int Rd,
+ int Rn, uint32_t offset = 0);
+ virtual void LDRSB(int cc, int Rd,
+ int Rn, uint32_t offset = 0);
+ virtual void LDRSH(int cc, int Rd,
+ int Rn, uint32_t offset = 0);
+ virtual void STRH (int cc, int Rd,
+ int Rn, uint32_t offset = 0);
+
+
+ virtual void LDM(int cc, int dir,
+ int Rn, int W, uint32_t reg_list);
+ virtual void STM(int cc, int dir,
+ int Rn, int W, uint32_t reg_list);
+
+ virtual void SWP(int cc, int Rn, int Rd, int Rm);
+ virtual void SWPB(int cc, int Rn, int Rd, int Rm);
+ virtual void SWI(int cc, uint32_t comment);
+
+ virtual void PLD(int Rn, uint32_t offset);
+ virtual void CLZ(int cc, int Rd, int Rm);
+ virtual void QADD(int cc, int Rd, int Rm, int Rn);
+ virtual void QDADD(int cc, int Rd, int Rm, int Rn);
+ virtual void QSUB(int cc, int Rd, int Rm, int Rn);
+ virtual void QDSUB(int cc, int Rd, int Rm, int Rn);
+ virtual void SMUL(int cc, int xy,
+ int Rd, int Rm, int Rs);
+ virtual void SMULW(int cc, int y,
+ int Rd, int Rm, int Rs);
+ virtual void SMLA(int cc, int xy,
+ int Rd, int Rm, int Rs, int Rn);
+ virtual void SMLAL(int cc, int xy,
+ int RdHi, int RdLo, int Rs, int Rm);
+ virtual void SMLAW(int cc, int y,
+ int Rd, int Rm, int Rs, int Rn);
+ virtual void UXTB16(int cc, int Rd, int Rm, int rotate);
+ virtual void UBFX(int cc, int Rd, int Rn, int lsb, int width);
+
+private:
+ ArmToArm64Assembler(const ArmToArm64Assembler& rhs);
+ ArmToArm64Assembler& operator = (const ArmToArm64Assembler& rhs);
+
+ // -----------------------------------------------------------------------
+ // helper functions
+ // -----------------------------------------------------------------------
+
+ void dataTransfer(int operation, int cc, int Rd, int Rn,
+ uint32_t operand_type, uint32_t size = 32);
+ void dataProcessingCommon(int opcode, int s,
+ int Rd, int Rn, uint32_t Op2);
+
+ // -----------------------------------------------------------------------
+ // Arm64 instructions
+ // -----------------------------------------------------------------------
+ uint32_t A64_B_COND(uint32_t cc, uint32_t offset);
+ uint32_t A64_RET(uint32_t Rn);
+
+ uint32_t A64_LDRSTR_Wm_SXTW_0(uint32_t operation,
+ uint32_t size, uint32_t Rt,
+ uint32_t Rn, uint32_t Rm);
+
+ uint32_t A64_STR_IMM_PreIndex(uint32_t Rt, uint32_t Rn, int32_t simm);
+ uint32_t A64_LDR_IMM_PostIndex(uint32_t Rt,uint32_t Rn, int32_t simm);
+
+ uint32_t A64_ADD_X_Wm_SXTW(uint32_t Rd, uint32_t Rn, uint32_t Rm,
+ uint32_t amount);
+ uint32_t A64_SUB_X_Wm_SXTW(uint32_t Rd, uint32_t Rn, uint32_t Rm,
+ uint32_t amount);
+
+ uint32_t A64_ADD_IMM_X(uint32_t Rd, uint32_t Rn,
+ uint32_t imm, uint32_t shift = 0);
+ uint32_t A64_SUB_IMM_X(uint32_t Rd, uint32_t Rn,
+ uint32_t imm, uint32_t shift = 0);
+
+ uint32_t A64_ADD_X(uint32_t Rd, uint32_t Rn,
+ uint32_t Rm, uint32_t shift = 0, uint32_t amount = 0);
+ uint32_t A64_ADD_W(uint32_t Rd, uint32_t Rn, uint32_t Rm,
+ uint32_t shift = 0, uint32_t amount = 0);
+ uint32_t A64_SUB_W(uint32_t Rd, uint32_t Rn, uint32_t Rm,
+ uint32_t shift = 0, uint32_t amount = 0,
+ uint32_t setflag = 0);
+ uint32_t A64_AND_W(uint32_t Rd, uint32_t Rn,
+ uint32_t Rm, uint32_t shift = 0, uint32_t amount = 0);
+ uint32_t A64_ORR_W(uint32_t Rd, uint32_t Rn,
+ uint32_t Rm, uint32_t shift = 0, uint32_t amount = 0);
+ uint32_t A64_ORN_W(uint32_t Rd, uint32_t Rn,
+ uint32_t Rm, uint32_t shift = 0, uint32_t amount = 0);
+
+ uint32_t A64_MOVZ_W(uint32_t Rd, uint32_t imm, uint32_t shift);
+ uint32_t A64_MOVZ_X(uint32_t Rd, uint32_t imm, uint32_t shift);
+ uint32_t A64_MOVK_W(uint32_t Rd, uint32_t imm, uint32_t shift);
+
+ uint32_t A64_SMADDL(uint32_t Rd, uint32_t Rn, uint32_t Rm, uint32_t Ra);
+ uint32_t A64_MADD_W(uint32_t Rd, uint32_t Rn, uint32_t Rm, uint32_t Ra);
+
+ uint32_t A64_SBFM_W(uint32_t Rd, uint32_t Rn,
+ uint32_t immr, uint32_t imms);
+ uint32_t A64_UBFM_W(uint32_t Rd, uint32_t Rn,
+ uint32_t immr, uint32_t imms);
+ uint32_t A64_UBFM_X(uint32_t Rd, uint32_t Rn,
+ uint32_t immr, uint32_t imms);
+
+ uint32_t A64_EXTR_W(uint32_t Rd, uint32_t Rn, uint32_t Rm, uint32_t lsb);
+ uint32_t A64_CSEL_X(uint32_t Rd, uint32_t Rn, uint32_t Rm, uint32_t cond);
+ uint32_t A64_CSEL_W(uint32_t Rd, uint32_t Rn, uint32_t Rm, uint32_t cond);
+
+ uint32_t* mBase;
+ uint32_t* mPC;
+ uint32_t* mPrologPC;
+ int64_t mDuration;
+ uint32_t mTmpReg1, mTmpReg2, mTmpReg3, mZeroReg;
+
+ struct branch_target_t {
+ inline branch_target_t() : label(0), pc(0) { }
+ inline branch_target_t(const char* l, uint32_t* p)
+ : label(l), pc(p) { }
+ const char* label;
+ uint32_t* pc;
+ };
+
+ sp<Assembly> mAssembly;
+ Vector<branch_target_t> mBranchTargets;
+ KeyedVector< const char*, uint32_t* > mLabels;
+ KeyedVector< uint32_t*, const char* > mLabelsInverseMapping;
+ KeyedVector< uint32_t*, const char* > mComments;
+
+ enum operand_type_t
+ {
+ OPERAND_REG = 0x20,
+ OPERAND_IMM,
+ OPERAND_REG_IMM,
+ OPERAND_REG_OFFSET,
+ OPERAND_UNSUPPORTED
+ };
+
+ struct addr_mode_t {
+ int32_t immediate;
+ bool writeback;
+ bool preindex;
+ bool postindex;
+ int32_t reg_imm_Rm;
+ int32_t reg_imm_type;
+ uint32_t reg_imm_shift;
+ int32_t reg_offset;
+ } mAddrMode;
+
+};
+
+}; // namespace android
+
+#endif //ANDROID_ARM64ASSEMBLER_H
diff --git a/libpixelflinger/codeflinger/Arm64Disassembler.cpp b/libpixelflinger/codeflinger/Arm64Disassembler.cpp
new file mode 100644
index 0000000..70f1ff1
--- /dev/null
+++ b/libpixelflinger/codeflinger/Arm64Disassembler.cpp
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <inttypes.h>
+#include <string.h>
+
+struct disasm_table_entry_t
+{
+ uint32_t mask;
+ uint32_t value;
+ const char* instr_template;
+};
+
+
+static disasm_table_entry_t disasm_table[] =
+{
+ {0xff000000, 0x91000000, "add <xd|sp>, <xn|sp>, #<imm1>, <shift1>"},
+ {0xff000000, 0xd1000000, "sub <xd|sp>, <xn|sp>, #<imm1>, <shift1>"},
+ {0xff200000, 0x8b000000, "add <xd>, <xn>, <xm>, <shift2> #<amt1>"},
+ {0xff200000, 0x0b000000, "add <wd>, <wn>, <wm>, <shift2> #<amt1>"},
+ {0xff200000, 0x4b000000, "sub <wd>, <wn>, <wm>, <shift2> #<amt1>"},
+ {0xff200000, 0x6b000000, "subs <wd>, <wn>, <wm>, <shift2> #<amt1>"},
+ {0xff200000, 0x0a000000, "and <wd>, <wn>, <wm>, <shift2> #<amt1>"},
+ {0xff200000, 0x2a000000, "orr <wd>, <wn>, <wm>, <shift2> #<amt1>"},
+ {0xff200000, 0x2a200000, "orn <wd>, <wn>, <wm>, <shift2> #<amt1>"},
+ {0xff800000, 0x72800000, "movk <wd>, #<imm2>, lsl #<shift3>"},
+ {0xff800000, 0x52800000, "movz <wd>, #<imm2>, lsl #<shift3>"},
+ {0xff800000, 0xd2800000, "movz <xd>, #<imm2>, lsl #<shift3>"},
+ {0xffe00c00, 0x1a800000, "csel <wd>, <wn>, <wm>, <cond1>"},
+ {0xffe00c00, 0x9a800000, "csel <xd>, <xn>, <xm>, <cond1>"},
+ {0xffe00c00, 0x5a800000, "csinv <wd>, <wn>, <wm>, <cond1>"},
+ {0xffe08000, 0x1b000000, "madd <wd>, <wn>, <wm>, <wa>"},
+ {0xffe08000, 0x9b200000, "smaddl <xd>, <wn>, <wm>, <xa>"},
+ {0xffe04c00, 0xb8604800, "ldr <wt>, [<xn|sp>, <r1><m1>, <ext1> #<amt2>]"},
+ {0xffe04c00, 0xb8204800, "str <wt>, [<xn|sp>, <r1><m1>, <ext1> #<amt2>]"},
+ {0xffe04c00, 0xf8604800, "ldr <xt>, [<xn|sp>, <r1><m1>, <ext1> #<amt3>]"},
+ {0xffe04c00, 0xf8204800, "str <xt>, [<xn|sp>, <r1><m1>, <ext1> #<amt3>]"},
+ {0xffe04c00, 0x38604800, "ldrb <wt>, [<xn|sp>, <r1><m1>, <ext1> <amt5>]"},
+ {0xffe04c00, 0x38204800, "strb <wt>, [<xn|sp>, <r1><m1>, <ext1> <amt5>]"},
+ {0xffe04c00, 0x78604800, "ldrh <wt>, [<xn|sp>, <r1><m1>, <ext1> #<amt6>]"},
+ {0xffe04c00, 0x78204800, "strh <wt>, [<xn|sp>, <r1><m1>, <ext1> #<amt6>]"},
+ {0xffe00c00, 0xb8400400, "ldr <wt>, [<xn|sp>], #<simm1>"},
+ {0xffe00c00, 0xb8000c00, "str <wt>, [<xn|sp>, #<simm1>]!"},
+ {0xffc00000, 0x13000000, "sbfm <wd>, <wn>, #<immr1>, #<imms1>"},
+ {0xffc00000, 0x53000000, "ubfm <wd>, <wn>, #<immr1>, #<imms1>"},
+ {0xffc00000, 0xd3400000, "ubfm <xd>, <xn>, #<immr1>, #<imms1>"},
+ {0xffe00000, 0x13800000, "extr <wd>, <wn>, <wm>, #<lsb1>"},
+ {0xff000000, 0x54000000, "b.<cond2> <label1>"},
+ {0xfffffc1f, 0xd65f0000, "ret <xn>"},
+ {0xffe00000, 0x8b200000, "add <xd|sp>, <xn|sp>, <r2><m1>, <ext2> #<amt4>"},
+ {0xffe00000, 0xcb200000, "sub <xd|sp>, <xn|sp>, <r2><m1>, <ext2> #<amt4>"}
+};
+
+static int32_t bits_signed(uint32_t instr, uint32_t msb, uint32_t lsb)
+{
+ int32_t value;
+ value = ((int32_t)instr) << (31 - msb);
+ value >>= (31 - msb);
+ value >>= lsb;
+ return value;
+}
+static uint32_t bits_unsigned(uint32_t instr, uint32_t msb, uint32_t lsb)
+{
+ uint32_t width = msb - lsb + 1;
+ uint32_t mask = (1 << width) - 1;
+ return ((instr >> lsb) & mask);
+}
+
+static void get_token(const char *instr, uint32_t index, char *token)
+{
+ uint32_t i, j;
+ for(i = index, j = 0; i < strlen(instr); ++i)
+ {
+ if(instr[index] == '<' && instr[i] == '>')
+ {
+ token[j++] = instr[i];
+ break;
+ }
+ else if(instr[index] != '<' && instr[i] == '<')
+ {
+ break;
+ }
+ else
+ {
+ token[j++] = instr[i];
+ }
+ }
+ token[j] = '\0';
+ return;
+}
+
+
+static const char * token_cc_table[] =
+{
+ "eq", "ne", "cs", "cc", "mi",
+ "pl", "vs", "vc", "hi", "ls",
+ "ge", "lt", "gt", "le", "al", "nv"
+};
+
+static void decode_rx_zr_token(uint32_t reg, const char *prefix, char *instr_part)
+{
+ if(reg == 31)
+ sprintf(instr_part, "%s%s", prefix, "zr");
+ else
+ sprintf(instr_part, "%s%d", prefix, reg);
+}
+
+static void decode_token(uint32_t code, char *token, char *instr_part)
+{
+ if(strcmp(token, "<imm1>") == 0)
+ sprintf(instr_part, "0x%x", bits_unsigned(code, 21,10));
+ else if(strcmp(token, "<imm2>") == 0)
+ sprintf(instr_part, "0x%x", bits_unsigned(code, 20,5));
+ else if(strcmp(token, "<shift1>") == 0)
+ sprintf(instr_part, "lsl #%d", bits_unsigned(code, 23,22) * 12);
+ else if(strcmp(token, "<shift2>") == 0)
+ {
+ static const char * shift2_table[] = { "lsl", "lsr", "asr", "ror"};
+ sprintf(instr_part, "%s", shift2_table[bits_unsigned(code, 23,22)]);
+ }
+ else if(strcmp(token, "<shift3>") == 0)
+ sprintf(instr_part, "%d", bits_unsigned(code, 22,21) * 16);
+ else if(strcmp(token, "<amt1>") == 0)
+ sprintf(instr_part, "%d", bits_unsigned(code, 15,10));
+ else if(strcmp(token, "<amt2>") == 0)
+ sprintf(instr_part, "%d", bits_unsigned(code, 12,12) * 2);
+ else if(strcmp(token, "<amt3>") == 0)
+ sprintf(instr_part, "%d", bits_unsigned(code, 12,12) * 3);
+ else if(strcmp(token, "<amt4>") == 0)
+ sprintf(instr_part, "%d", bits_unsigned(code, 12,10));
+ else if(strcmp(token, "<amt5>") == 0)
+ {
+ static const char * amt5_table[] = {"", "#0"};
+ sprintf(instr_part, "%s", amt5_table[bits_unsigned(code, 12,12)]);
+ }
+ else if(strcmp(token, "<amt6>") == 0)
+ sprintf(instr_part, "%d", bits_unsigned(code, 12,12));
+ else if(strcmp(token, "<simm1>") == 0)
+ sprintf(instr_part, "%d", bits_signed(code, 20,12));
+ else if(strcmp(token, "<immr1>") == 0)
+ sprintf(instr_part, "%d", bits_unsigned(code, 21,16));
+ else if(strcmp(token, "<imms1>") == 0)
+ sprintf(instr_part, "%d", bits_unsigned(code, 15,10));
+ else if(strcmp(token, "<lsb1>") == 0)
+ sprintf(instr_part, "%d", bits_unsigned(code, 15,10));
+ else if(strcmp(token, "<cond1>") == 0)
+ sprintf(instr_part, "%s", token_cc_table[bits_unsigned(code, 15,12)]);
+ else if(strcmp(token, "<cond2>") == 0)
+ sprintf(instr_part, "%s", token_cc_table[bits_unsigned(code, 4,0)]);
+ else if(strcmp(token, "<r1>") == 0)
+ {
+ const char * token_r1_table[] =
+ {
+ "reserved", "reserved", "w", "x",
+ "reserved", "reserved", "w", "x"
+ };
+ sprintf(instr_part, "%s", token_r1_table[bits_unsigned(code, 15,13)]);
+ }
+ else if(strcmp(token, "<r2>") == 0)
+ {
+ static const char * token_r2_table[] =
+ {
+ "w","w","w", "x", "w", "w", "w", "x"
+ };
+ sprintf(instr_part, "%s", token_r2_table[bits_unsigned(code, 15,13)]);
+ }
+ else if(strcmp(token, "<m1>") == 0)
+ {
+ uint32_t reg = bits_unsigned(code, 20,16);
+ if(reg == 31)
+ sprintf(instr_part, "%s", "zr");
+ else
+ sprintf(instr_part, "%d", reg);
+ }
+ else if(strcmp(token, "<ext1>") == 0)
+ {
+ static const char * token_ext1_table[] =
+ {
+ "reserved","reserved","uxtw", "lsl",
+ "reserved","reserved", "sxtw", "sxtx"
+ };
+ sprintf(instr_part, "%s", token_ext1_table[bits_unsigned(code, 15,13)]);
+ }
+ else if(strcmp(token, "<ext2>") == 0)
+ {
+ static const char * token_ext2_table[] =
+ {
+ "uxtb","uxth","uxtw","uxtx",
+ "sxtb","sxth","sxtw","sxtx"
+ };
+ sprintf(instr_part, "%s", token_ext2_table[bits_unsigned(code, 15,13)]);
+ }
+ else if (strcmp(token, "<label1>") == 0)
+ {
+ int32_t offset = bits_signed(code, 23,5) * 4;
+ if(offset > 0)
+ sprintf(instr_part, "#.+%d", offset);
+ else
+ sprintf(instr_part, "#.-%d", -offset);
+ }
+ else if (strcmp(token, "<xn|sp>") == 0)
+ {
+ uint32_t reg = bits_unsigned(code, 9, 5);
+ if(reg == 31)
+ sprintf(instr_part, "%s", "sp");
+ else
+ sprintf(instr_part, "x%d", reg);
+ }
+ else if (strcmp(token, "<xd|sp>") == 0)
+ {
+ uint32_t reg = bits_unsigned(code, 4, 0);
+ if(reg == 31)
+ sprintf(instr_part, "%s", "sp");
+ else
+ sprintf(instr_part, "x%d", reg);
+ }
+ else if (strcmp(token, "<xn>") == 0)
+ decode_rx_zr_token(bits_unsigned(code, 9, 5), "x", instr_part);
+ else if (strcmp(token, "<xd>") == 0)
+ decode_rx_zr_token(bits_unsigned(code, 4, 0), "x", instr_part);
+ else if (strcmp(token, "<xm>") == 0)
+ decode_rx_zr_token(bits_unsigned(code, 20, 16), "x", instr_part);
+ else if (strcmp(token, "<xa>") == 0)
+ decode_rx_zr_token(bits_unsigned(code, 14, 10), "x", instr_part);
+ else if (strcmp(token, "<xt>") == 0)
+ decode_rx_zr_token(bits_unsigned(code, 4, 0), "x", instr_part);
+ else if (strcmp(token, "<wn>") == 0)
+ decode_rx_zr_token(bits_unsigned(code, 9, 5), "w", instr_part);
+ else if (strcmp(token, "<wd>") == 0)
+ decode_rx_zr_token(bits_unsigned(code, 4, 0), "w", instr_part);
+ else if (strcmp(token, "<wm>") == 0)
+ decode_rx_zr_token(bits_unsigned(code, 20, 16), "w", instr_part);
+ else if (strcmp(token, "<wa>") == 0)
+ decode_rx_zr_token(bits_unsigned(code, 14, 10), "w", instr_part);
+ else if (strcmp(token, "<wt>") == 0)
+ decode_rx_zr_token(bits_unsigned(code, 4, 0), "w", instr_part);
+ else
+ {
+ sprintf(instr_part, "error");
+ }
+ return;
+}
+
+int arm64_disassemble(uint32_t code, char* instr)
+{
+ uint32_t i;
+ char token[256];
+ char instr_part[256];
+
+ if(instr == NULL)
+ return -1;
+
+ bool matched = false;
+ disasm_table_entry_t *entry = NULL;
+ for(i = 0; i < sizeof(disasm_table)/sizeof(disasm_table_entry_t); ++i)
+ {
+ entry = &disasm_table[i];
+ if((code & entry->mask) == entry->value)
+ {
+ matched = true;
+ break;
+ }
+ }
+ if(matched == false)
+ {
+ strcpy(instr, "Unknown Instruction");
+ return -1;
+ }
+ else
+ {
+ uint32_t index = 0;
+ uint32_t length = strlen(entry->instr_template);
+ instr[0] = '\0';
+ do
+ {
+ get_token(entry->instr_template, index, token);
+ if(token[0] == '<')
+ {
+ decode_token(code, token, instr_part);
+ strcat(instr, instr_part);
+ }
+ else
+ {
+ strcat(instr, token);
+ }
+ index += strlen(token);
+ }while(index < length);
+ return 0;
+ }
+}
diff --git a/libpixelflinger/codeflinger/Arm64Disassembler.h b/libpixelflinger/codeflinger/Arm64Disassembler.h
new file mode 100644
index 0000000..86f3aba
--- /dev/null
+++ b/libpixelflinger/codeflinger/Arm64Disassembler.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef ANDROID_ARM64DISASSEMBLER_H
+#define ANDROID_ARM64DISASSEMBLER_H
+
+#include <inttypes.h>
+int arm64_disassemble(uint32_t code, char* instr);
+
+#endif //ANDROID_ARM64ASSEMBLER_H
diff --git a/libpixelflinger/codeflinger/CodeCache.cpp b/libpixelflinger/codeflinger/CodeCache.cpp
new file mode 100644
index 0000000..d770302
--- /dev/null
+++ b/libpixelflinger/codeflinger/CodeCache.cpp
@@ -0,0 +1,215 @@
+/* libs/pixelflinger/codeflinger/CodeCache.cpp
+**
+** Copyright 2006, 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 <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+#include <cutils/ashmem.h>
+#include <cutils/atomic.h>
+#define LOG_TAG "CodeCache"
+#include <cutils/log.h>
+
+
+#include "CodeCache.h"
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+#if defined(__arm__) || defined(__aarch64__)
+#include <unistd.h>
+#include <errno.h>
+#endif
+
+#if defined(__mips__)
+#include <asm/cachectl.h>
+#include <errno.h>
+#endif
+
+// ----------------------------------------------------------------------------
+// ----------------------------------------------------------------------------
+
+// A dlmalloc mspace is used to manage the code cache over a mmaped region.
+#define HAVE_MMAP 0
+#define HAVE_MREMAP 0
+#define HAVE_MORECORE 0
+#define MALLOC_ALIGNMENT 16
+#define MSPACES 1
+#define NO_MALLINFO 1
+#define ONLY_MSPACES 1
+// Custom heap error handling.
+#define PROCEED_ON_ERROR 0
+static void heap_error(const char* msg, const char* function, void* p);
+#define CORRUPTION_ERROR_ACTION(m) \
+ heap_error("HEAP MEMORY CORRUPTION", __FUNCTION__, NULL)
+#define USAGE_ERROR_ACTION(m,p) \
+ heap_error("ARGUMENT IS INVALID HEAP ADDRESS", __FUNCTION__, p)
+
+#include "../../../../bionic/libc/upstream-dlmalloc/malloc.c"
+
+static void heap_error(const char* msg, const char* function, void* p) {
+ ALOG(LOG_FATAL, LOG_TAG, "@@@ ABORTING: CODE FLINGER: %s IN %s addr=%p",
+ msg, function, p);
+ /* So that we can get a memory dump around p */
+ *((int **) 0xdeadbaad) = (int *) p;
+}
+
+// ----------------------------------------------------------------------------
+
+static void* gExecutableStore = NULL;
+static mspace gMspace = NULL;
+const size_t kMaxCodeCacheCapacity = 1024 * 1024;
+
+static mspace getMspace()
+{
+ if (gExecutableStore == NULL) {
+ int fd = ashmem_create_region("CodeFlinger code cache",
+ kMaxCodeCacheCapacity);
+ LOG_ALWAYS_FATAL_IF(fd < 0,
+ "Creating code cache, ashmem_create_region "
+ "failed with error '%s'", strerror(errno));
+ gExecutableStore = mmap(NULL, kMaxCodeCacheCapacity,
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_PRIVATE, fd, 0);
+ LOG_ALWAYS_FATAL_IF(gExecutableStore == MAP_FAILED,
+ "Creating code cache, mmap failed with error "
+ "'%s'", strerror(errno));
+ close(fd);
+ gMspace = create_mspace_with_base(gExecutableStore, kMaxCodeCacheCapacity,
+ /*locked=*/ false);
+ mspace_set_footprint_limit(gMspace, kMaxCodeCacheCapacity);
+ }
+ return gMspace;
+}
+
+Assembly::Assembly(size_t size)
+ : mCount(1), mSize(0)
+{
+ mBase = (uint32_t*)mspace_malloc(getMspace(), size);
+ LOG_ALWAYS_FATAL_IF(mBase == NULL,
+ "Failed to create Assembly of size %zd in executable "
+ "store of size %zd", size, kMaxCodeCacheCapacity);
+ mSize = size;
+}
+
+Assembly::~Assembly()
+{
+ mspace_free(getMspace(), mBase);
+}
+
+void Assembly::incStrong(const void*) const
+{
+ android_atomic_inc(&mCount);
+}
+
+void Assembly::decStrong(const void*) const
+{
+ if (android_atomic_dec(&mCount) == 1) {
+ delete this;
+ }
+}
+
+ssize_t Assembly::size() const
+{
+ if (!mBase) return NO_MEMORY;
+ return mSize;
+}
+
+uint32_t* Assembly::base() const
+{
+ return mBase;
+}
+
+ssize_t Assembly::resize(size_t newSize)
+{
+ mBase = (uint32_t*)mspace_realloc(getMspace(), mBase, newSize);
+ LOG_ALWAYS_FATAL_IF(mBase == NULL,
+ "Failed to resize Assembly to %zd in code cache "
+ "of size %zd", newSize, kMaxCodeCacheCapacity);
+ mSize = newSize;
+ return size();
+}
+
+// ----------------------------------------------------------------------------
+
+CodeCache::CodeCache(size_t size)
+ : mCacheSize(size), mCacheInUse(0)
+{
+ pthread_mutex_init(&mLock, 0);
+}
+
+CodeCache::~CodeCache()
+{
+ pthread_mutex_destroy(&mLock);
+}
+
+sp<Assembly> CodeCache::lookup(const AssemblyKeyBase& keyBase) const
+{
+ pthread_mutex_lock(&mLock);
+ sp<Assembly> r;
+ ssize_t index = mCacheData.indexOfKey(key_t(keyBase));
+ if (index >= 0) {
+ const cache_entry_t& e = mCacheData.valueAt(index);
+ e.when = mWhen++;
+ r = e.entry;
+ }
+ pthread_mutex_unlock(&mLock);
+ return r;
+}
+
+int CodeCache::cache( const AssemblyKeyBase& keyBase,
+ const sp<Assembly>& assembly)
+{
+ pthread_mutex_lock(&mLock);
+
+ const ssize_t assemblySize = assembly->size();
+ while (mCacheInUse + assemblySize > mCacheSize) {
+ // evict the LRU
+ size_t lru = 0;
+ size_t count = mCacheData.size();
+ for (size_t i=0 ; i<count ; i++) {
+ const cache_entry_t& e = mCacheData.valueAt(i);
+ if (e.when < mCacheData.valueAt(lru).when) {
+ lru = i;
+ }
+ }
+ const cache_entry_t& e = mCacheData.valueAt(lru);
+ mCacheInUse -= e.entry->size();
+ mCacheData.removeItemsAt(lru);
+ }
+
+ ssize_t err = mCacheData.add(key_t(keyBase), cache_entry_t(assembly, mWhen));
+ if (err >= 0) {
+ mCacheInUse += assemblySize;
+ mWhen++;
+ // synchronize caches...
+ char* base = reinterpret_cast<char*>(assembly->base());
+ char* curr = reinterpret_cast<char*>(base + assembly->size());
+ __builtin___clear_cache(base, curr);
+ }
+
+ pthread_mutex_unlock(&mLock);
+ return err;
+}
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/libpixelflinger/codeflinger/CodeCache.h b/libpixelflinger/codeflinger/CodeCache.h
new file mode 100644
index 0000000..fa67dd0
--- /dev/null
+++ b/libpixelflinger/codeflinger/CodeCache.h
@@ -0,0 +1,135 @@
+/* libs/pixelflinger/codeflinger/CodeCache.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+
+#ifndef ANDROID_CODECACHE_H
+#define ANDROID_CODECACHE_H
+
+#include <stdint.h>
+#include <pthread.h>
+#include <sys/types.h>
+
+#include "tinyutils/KeyedVector.h"
+#include "tinyutils/smartpointer.h"
+
+namespace android {
+
+using namespace tinyutils;
+
+// ----------------------------------------------------------------------------
+
+class AssemblyKeyBase {
+public:
+ virtual ~AssemblyKeyBase() { }
+ virtual int compare_type(const AssemblyKeyBase& key) const = 0;
+};
+
+template <typename T>
+class AssemblyKey : public AssemblyKeyBase
+{
+public:
+ AssemblyKey(const T& rhs) : mKey(rhs) { }
+ virtual int compare_type(const AssemblyKeyBase& key) const {
+ const T& rhs = static_cast<const AssemblyKey&>(key).mKey;
+ return android::compare_type(mKey, rhs);
+ }
+private:
+ T mKey;
+};
+
+// ----------------------------------------------------------------------------
+
+class Assembly
+{
+public:
+ Assembly(size_t size);
+ virtual ~Assembly();
+
+ ssize_t size() const;
+ uint32_t* base() const;
+ ssize_t resize(size_t size);
+
+ // protocol for sp<>
+ void incStrong(const void* id) const;
+ void decStrong(const void* id) const;
+ typedef void weakref_type;
+
+private:
+ mutable int32_t mCount;
+ uint32_t* mBase;
+ size_t mSize;
+};
+
+// ----------------------------------------------------------------------------
+
+class CodeCache
+{
+public:
+// pretty simple cache API...
+ CodeCache(size_t size);
+ ~CodeCache();
+
+ sp<Assembly> lookup(const AssemblyKeyBase& key) const;
+
+ int cache( const AssemblyKeyBase& key,
+ const sp<Assembly>& assembly);
+
+private:
+ // nothing to see here...
+ struct cache_entry_t {
+ inline cache_entry_t() { }
+ inline cache_entry_t(const sp<Assembly>& a, int64_t w)
+ : entry(a), when(w) { }
+ sp<Assembly> entry;
+ mutable int64_t when;
+ };
+
+ class key_t {
+ friend int compare_type(
+ const key_value_pair_t<key_t, cache_entry_t>&,
+ const key_value_pair_t<key_t, cache_entry_t>&);
+ const AssemblyKeyBase* mKey;
+ public:
+ key_t() { };
+ key_t(const AssemblyKeyBase& k) : mKey(&k) { }
+ };
+
+ mutable pthread_mutex_t mLock;
+ mutable int64_t mWhen;
+ size_t mCacheSize;
+ size_t mCacheInUse;
+ KeyedVector<key_t, cache_entry_t> mCacheData;
+
+ friend int compare_type(
+ const key_value_pair_t<key_t, cache_entry_t>&,
+ const key_value_pair_t<key_t, cache_entry_t>&);
+};
+
+// KeyedVector uses compare_type(), which is more efficient, than
+// just using operator < ()
+inline int compare_type(
+ const key_value_pair_t<CodeCache::key_t, CodeCache::cache_entry_t>& lhs,
+ const key_value_pair_t<CodeCache::key_t, CodeCache::cache_entry_t>& rhs)
+{
+ return lhs.key.mKey->compare_type(*(rhs.key.mKey));
+}
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif //ANDROID_CODECACHE_H
diff --git a/libpixelflinger/codeflinger/GGLAssembler.cpp b/libpixelflinger/codeflinger/GGLAssembler.cpp
new file mode 100644
index 0000000..325caba
--- /dev/null
+++ b/libpixelflinger/codeflinger/GGLAssembler.cpp
@@ -0,0 +1,1194 @@
+/* libs/pixelflinger/codeflinger/GGLAssembler.cpp
+**
+** Copyright 2006, 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 "GGLAssembler"
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <cutils/log.h>
+
+#include "GGLAssembler.h"
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+GGLAssembler::GGLAssembler(ARMAssemblerInterface* target)
+ : ARMAssemblerProxy(target),
+ RegisterAllocator(ARMAssemblerProxy::getCodegenArch()), mOptLevel(7)
+{
+}
+
+GGLAssembler::~GGLAssembler()
+{
+}
+
+void GGLAssembler::prolog()
+{
+ ARMAssemblerProxy::prolog();
+}
+
+void GGLAssembler::epilog(uint32_t touched)
+{
+ ARMAssemblerProxy::epilog(touched);
+}
+
+void GGLAssembler::reset(int opt_level)
+{
+ ARMAssemblerProxy::reset();
+ RegisterAllocator::reset();
+ mOptLevel = opt_level;
+}
+
+// ---------------------------------------------------------------------------
+
+int GGLAssembler::scanline(const needs_t& needs, context_t const* c)
+{
+ int err = 0;
+ int opt_level = mOptLevel;
+ while (opt_level >= 0) {
+ reset(opt_level);
+ err = scanline_core(needs, c);
+ if (err == 0)
+ break;
+ opt_level--;
+ }
+
+ // XXX: in theory, pcForLabel is not valid before generate()
+ uint32_t* fragment_start_pc = pcForLabel("fragment_loop");
+ uint32_t* fragment_end_pc = pcForLabel("epilog");
+ const int per_fragment_ops = int(fragment_end_pc - fragment_start_pc);
+
+ // build a name for our pipeline
+ char name[64];
+ sprintf(name,
+ "scanline__%08X:%08X_%08X_%08X [%3d ipp]",
+ needs.p, needs.n, needs.t[0], needs.t[1], per_fragment_ops);
+
+ if (err) {
+ ALOGE("Error while generating ""%s""\n", name);
+ disassemble(name);
+ return -1;
+ }
+
+ return generate(name);
+}
+
+int GGLAssembler::scanline_core(const needs_t& needs, context_t const* c)
+{
+ int64_t duration = ggl_system_time();
+
+ mBlendFactorCached = 0;
+ mBlending = 0;
+ mMasking = 0;
+ mAA = GGL_READ_NEEDS(P_AA, needs.p);
+ mDithering = GGL_READ_NEEDS(P_DITHER, needs.p);
+ mAlphaTest = GGL_READ_NEEDS(P_ALPHA_TEST, needs.p) + GGL_NEVER;
+ mDepthTest = GGL_READ_NEEDS(P_DEPTH_TEST, needs.p) + GGL_NEVER;
+ mFog = GGL_READ_NEEDS(P_FOG, needs.p) != 0;
+ mSmooth = GGL_READ_NEEDS(SHADE, needs.n) != 0;
+ mBuilderContext.needs = needs;
+ mBuilderContext.c = c;
+ mBuilderContext.Rctx = reserveReg(R0); // context always in R0
+ mCbFormat = c->formats[ GGL_READ_NEEDS(CB_FORMAT, needs.n) ];
+
+ // ------------------------------------------------------------------------
+
+ decodeLogicOpNeeds(needs);
+
+ decodeTMUNeeds(needs, c);
+
+ mBlendSrc = ggl_needs_to_blendfactor(GGL_READ_NEEDS(BLEND_SRC, needs.n));
+ mBlendDst = ggl_needs_to_blendfactor(GGL_READ_NEEDS(BLEND_DST, needs.n));
+ mBlendSrcA = ggl_needs_to_blendfactor(GGL_READ_NEEDS(BLEND_SRCA, needs.n));
+ mBlendDstA = ggl_needs_to_blendfactor(GGL_READ_NEEDS(BLEND_DSTA, needs.n));
+
+ if (!mCbFormat.c[GGLFormat::ALPHA].h) {
+ if ((mBlendSrc == GGL_ONE_MINUS_DST_ALPHA) ||
+ (mBlendSrc == GGL_DST_ALPHA)) {
+ mBlendSrc = GGL_ONE;
+ }
+ if ((mBlendSrcA == GGL_ONE_MINUS_DST_ALPHA) ||
+ (mBlendSrcA == GGL_DST_ALPHA)) {
+ mBlendSrcA = GGL_ONE;
+ }
+ if ((mBlendDst == GGL_ONE_MINUS_DST_ALPHA) ||
+ (mBlendDst == GGL_DST_ALPHA)) {
+ mBlendDst = GGL_ONE;
+ }
+ if ((mBlendDstA == GGL_ONE_MINUS_DST_ALPHA) ||
+ (mBlendDstA == GGL_DST_ALPHA)) {
+ mBlendDstA = GGL_ONE;
+ }
+ }
+
+ // if we need the framebuffer, read it now
+ const int blending = blending_codes(mBlendSrc, mBlendDst) |
+ blending_codes(mBlendSrcA, mBlendDstA);
+
+ // XXX: handle special cases, destination not modified...
+ if ((mBlendSrc==GGL_ZERO) && (mBlendSrcA==GGL_ZERO) &&
+ (mBlendDst==GGL_ONE) && (mBlendDstA==GGL_ONE)) {
+ // Destination unmodified (beware of logic ops)
+ } else if ((mBlendSrc==GGL_ZERO) && (mBlendSrcA==GGL_ZERO) &&
+ (mBlendDst==GGL_ZERO) && (mBlendDstA==GGL_ZERO)) {
+ // Destination is zero (beware of logic ops)
+ }
+
+ int fbComponents = 0;
+ const int masking = GGL_READ_NEEDS(MASK_ARGB, needs.n);
+ for (int i=0 ; i<4 ; i++) {
+ const int mask = 1<<i;
+ component_info_t& info = mInfo[i];
+ int fs = i==GGLFormat::ALPHA ? mBlendSrcA : mBlendSrc;
+ int fd = i==GGLFormat::ALPHA ? mBlendDstA : mBlendDst;
+ if (fs==GGL_SRC_ALPHA_SATURATE && i==GGLFormat::ALPHA)
+ fs = GGL_ONE;
+ info.masked = !!(masking & mask);
+ info.inDest = !info.masked && mCbFormat.c[i].h &&
+ ((mLogicOp & LOGIC_OP_SRC) || (!mLogicOp));
+ if (mCbFormat.components >= GGL_LUMINANCE &&
+ (i==GGLFormat::GREEN || i==GGLFormat::BLUE)) {
+ info.inDest = false;
+ }
+ info.needed = (i==GGLFormat::ALPHA) &&
+ (isAlphaSourceNeeded() || mAlphaTest != GGL_ALWAYS);
+ info.replaced = !!(mTextureMachine.replaced & mask);
+ info.iterated = (!info.replaced && (info.inDest || info.needed));
+ info.smooth = mSmooth && info.iterated;
+ info.fog = mFog && info.inDest && (i != GGLFormat::ALPHA);
+ info.blend = (fs != int(GGL_ONE)) || (fd > int(GGL_ZERO));
+
+ mBlending |= (info.blend ? mask : 0);
+ mMasking |= (mCbFormat.c[i].h && info.masked) ? mask : 0;
+ fbComponents |= mCbFormat.c[i].h ? mask : 0;
+ }
+
+ mAllMasked = (mMasking == fbComponents);
+ if (mAllMasked) {
+ mDithering = 0;
+ }
+
+ fragment_parts_t parts;
+
+ // ------------------------------------------------------------------------
+ prolog();
+ // ------------------------------------------------------------------------
+
+ build_scanline_prolog(parts, needs);
+
+ if (registerFile().status())
+ return registerFile().status();
+
+ // ------------------------------------------------------------------------
+ label("fragment_loop");
+ // ------------------------------------------------------------------------
+ {
+ Scratch regs(registerFile());
+
+ if (mDithering) {
+ // update the dither index.
+ MOV(AL, 0, parts.count.reg,
+ reg_imm(parts.count.reg, ROR, GGL_DITHER_ORDER_SHIFT));
+ ADD(AL, 0, parts.count.reg, parts.count.reg,
+ imm( 1 << (32 - GGL_DITHER_ORDER_SHIFT)));
+ MOV(AL, 0, parts.count.reg,
+ reg_imm(parts.count.reg, ROR, 32 - GGL_DITHER_ORDER_SHIFT));
+ }
+
+ // XXX: could we do an early alpha-test here in some cases?
+ // It would probaly be used only with smooth-alpha and no texture
+ // (or no alpha component in the texture).
+
+ // Early z-test
+ if (mAlphaTest==GGL_ALWAYS) {
+ build_depth_test(parts, Z_TEST|Z_WRITE);
+ } else {
+ // we cannot do the z-write here, because
+ // it might be killed by the alpha-test later
+ build_depth_test(parts, Z_TEST);
+ }
+
+ { // texture coordinates
+ Scratch scratches(registerFile());
+
+ // texel generation
+ build_textures(parts, regs);
+ if (registerFile().status())
+ return registerFile().status();
+ }
+
+ if ((blending & (FACTOR_DST|BLEND_DST)) ||
+ (mMasking && !mAllMasked) ||
+ (mLogicOp & LOGIC_OP_DST))
+ {
+ // blending / logic_op / masking need the framebuffer
+ mDstPixel.setTo(regs.obtain(), &mCbFormat);
+
+ // load the framebuffer pixel
+ comment("fetch color-buffer");
+ load(parts.cbPtr, mDstPixel);
+ }
+
+ if (registerFile().status())
+ return registerFile().status();
+
+ pixel_t pixel;
+ int directTex = mTextureMachine.directTexture;
+ if (directTex | parts.packed) {
+ // note: we can't have both here
+ // iterated color or direct texture
+ pixel = directTex ? parts.texel[directTex-1] : parts.iterated;
+ pixel.flags &= ~CORRUPTIBLE;
+ } else {
+ if (mDithering) {
+ const int ctxtReg = mBuilderContext.Rctx;
+ const int mask = GGL_DITHER_SIZE-1;
+ parts.dither = reg_t(regs.obtain());
+ AND(AL, 0, parts.dither.reg, parts.count.reg, imm(mask));
+ ADDR_ADD(AL, 0, parts.dither.reg, ctxtReg, parts.dither.reg);
+ LDRB(AL, parts.dither.reg, parts.dither.reg,
+ immed12_pre(GGL_OFFSETOF(ditherMatrix)));
+ }
+
+ // allocate a register for the resulting pixel
+ pixel.setTo(regs.obtain(), &mCbFormat, FIRST);
+
+ build_component(pixel, parts, GGLFormat::ALPHA, regs);
+
+ if (mAlphaTest!=GGL_ALWAYS) {
+ // only handle the z-write part here. We know z-test
+ // was successful, as well as alpha-test.
+ build_depth_test(parts, Z_WRITE);
+ }
+
+ build_component(pixel, parts, GGLFormat::RED, regs);
+ build_component(pixel, parts, GGLFormat::GREEN, regs);
+ build_component(pixel, parts, GGLFormat::BLUE, regs);
+
+ pixel.flags |= CORRUPTIBLE;
+ }
+
+ if (registerFile().status())
+ return registerFile().status();
+
+ if (pixel.reg == -1) {
+ // be defensive here. if we're here it's probably
+ // that this whole fragment is a no-op.
+ pixel = mDstPixel;
+ }
+
+ if (!mAllMasked) {
+ // logic operation
+ build_logic_op(pixel, regs);
+
+ // masking
+ build_masking(pixel, regs);
+
+ comment("store");
+ store(parts.cbPtr, pixel, WRITE_BACK);
+ }
+ }
+
+ if (registerFile().status())
+ return registerFile().status();
+
+ // update the iterated color...
+ if (parts.reload != 3) {
+ build_smooth_shade(parts);
+ }
+
+ // update iterated z
+ build_iterate_z(parts);
+
+ // update iterated fog
+ build_iterate_f(parts);
+
+ SUB(AL, S, parts.count.reg, parts.count.reg, imm(1<<16));
+ B(PL, "fragment_loop");
+ label("epilog");
+ epilog(registerFile().touched());
+
+ if ((mAlphaTest!=GGL_ALWAYS) || (mDepthTest!=GGL_ALWAYS)) {
+ if (mDepthTest!=GGL_ALWAYS) {
+ label("discard_before_textures");
+ build_iterate_texture_coordinates(parts);
+ }
+ label("discard_after_textures");
+ build_smooth_shade(parts);
+ build_iterate_z(parts);
+ build_iterate_f(parts);
+ if (!mAllMasked) {
+ ADDR_ADD(AL, 0, parts.cbPtr.reg, parts.cbPtr.reg, imm(parts.cbPtr.size>>3));
+ }
+ SUB(AL, S, parts.count.reg, parts.count.reg, imm(1<<16));
+ B(PL, "fragment_loop");
+ epilog(registerFile().touched());
+ }
+
+ return registerFile().status();
+}
+
+// ---------------------------------------------------------------------------
+
+void GGLAssembler::build_scanline_prolog(
+ fragment_parts_t& parts, const needs_t& needs)
+{
+ Scratch scratches(registerFile());
+ int Rctx = mBuilderContext.Rctx;
+
+ // compute count
+ comment("compute ct (# of pixels to process)");
+ parts.count.setTo(obtainReg());
+ int Rx = scratches.obtain();
+ int Ry = scratches.obtain();
+ CONTEXT_LOAD(Rx, iterators.xl);
+ CONTEXT_LOAD(parts.count.reg, iterators.xr);
+ CONTEXT_LOAD(Ry, iterators.y);
+
+ // parts.count = iterators.xr - Rx
+ SUB(AL, 0, parts.count.reg, parts.count.reg, Rx);
+ SUB(AL, 0, parts.count.reg, parts.count.reg, imm(1));
+
+ if (mDithering) {
+ // parts.count.reg = 0xNNNNXXDD
+ // NNNN = count-1
+ // DD = dither offset
+ // XX = 0xxxxxxx (x = garbage)
+ Scratch scratches(registerFile());
+ int tx = scratches.obtain();
+ int ty = scratches.obtain();
+ AND(AL, 0, tx, Rx, imm(GGL_DITHER_MASK));
+ AND(AL, 0, ty, Ry, imm(GGL_DITHER_MASK));
+ ADD(AL, 0, tx, tx, reg_imm(ty, LSL, GGL_DITHER_ORDER_SHIFT));
+ ORR(AL, 0, parts.count.reg, tx, reg_imm(parts.count.reg, LSL, 16));
+ } else {
+ // parts.count.reg = 0xNNNN0000
+ // NNNN = count-1
+ MOV(AL, 0, parts.count.reg, reg_imm(parts.count.reg, LSL, 16));
+ }
+
+ if (!mAllMasked) {
+ // compute dst ptr
+ comment("compute color-buffer pointer");
+ const int cb_bits = mCbFormat.size*8;
+ int Rs = scratches.obtain();
+ parts.cbPtr.setTo(obtainReg(), cb_bits);
+ CONTEXT_LOAD(Rs, state.buffers.color.stride);
+ CONTEXT_ADDR_LOAD(parts.cbPtr.reg, state.buffers.color.data);
+ SMLABB(AL, Rs, Ry, Rs, Rx); // Rs = Rx + Ry*Rs
+ base_offset(parts.cbPtr, parts.cbPtr, Rs);
+ scratches.recycle(Rs);
+ }
+
+ // init fog
+ const int need_fog = GGL_READ_NEEDS(P_FOG, needs.p);
+ if (need_fog) {
+ comment("compute initial fog coordinate");
+ Scratch scratches(registerFile());
+ int dfdx = scratches.obtain();
+ int ydfdy = scratches.obtain();
+ int f = ydfdy;
+ CONTEXT_LOAD(dfdx, generated_vars.dfdx);
+ CONTEXT_LOAD(ydfdy, iterators.ydfdy);
+ MLA(AL, 0, f, Rx, dfdx, ydfdy);
+ CONTEXT_STORE(f, generated_vars.f);
+ }
+
+ // init Z coordinate
+ if ((mDepthTest != GGL_ALWAYS) || GGL_READ_NEEDS(P_MASK_Z, needs.p)) {
+ parts.z = reg_t(obtainReg());
+ comment("compute initial Z coordinate");
+ Scratch scratches(registerFile());
+ int dzdx = scratches.obtain();
+ int ydzdy = parts.z.reg;
+ CONTEXT_LOAD(dzdx, generated_vars.dzdx); // 1.31 fixed-point
+ CONTEXT_LOAD(ydzdy, iterators.ydzdy); // 1.31 fixed-point
+ MLA(AL, 0, parts.z.reg, Rx, dzdx, ydzdy);
+
+ // we're going to index zbase of parts.count
+ // zbase = base + (xl-count + stride*y)*2
+ int Rs = dzdx;
+ int zbase = scratches.obtain();
+ CONTEXT_LOAD(Rs, state.buffers.depth.stride);
+ CONTEXT_ADDR_LOAD(zbase, state.buffers.depth.data);
+ SMLABB(AL, Rs, Ry, Rs, Rx);
+ ADD(AL, 0, Rs, Rs, reg_imm(parts.count.reg, LSR, 16));
+ ADDR_ADD(AL, 0, zbase, zbase, reg_imm(Rs, LSL, 1));
+ CONTEXT_ADDR_STORE(zbase, generated_vars.zbase);
+ }
+
+ // init texture coordinates
+ init_textures(parts.coords, reg_t(Rx), reg_t(Ry));
+ scratches.recycle(Ry);
+
+ // iterated color
+ init_iterated_color(parts, reg_t(Rx));
+
+ // init coverage factor application (anti-aliasing)
+ if (mAA) {
+ parts.covPtr.setTo(obtainReg(), 16);
+ CONTEXT_ADDR_LOAD(parts.covPtr.reg, state.buffers.coverage);
+ ADDR_ADD(AL, 0, parts.covPtr.reg, parts.covPtr.reg, reg_imm(Rx, LSL, 1));
+ }
+}
+
+// ---------------------------------------------------------------------------
+
+void GGLAssembler::build_component( pixel_t& pixel,
+ const fragment_parts_t& parts,
+ int component,
+ Scratch& regs)
+{
+ static char const * comments[] = {"alpha", "red", "green", "blue"};
+ comment(comments[component]);
+
+ // local register file
+ Scratch scratches(registerFile());
+ const int dst_component_size = pixel.component_size(component);
+
+ component_t temp(-1);
+ build_incoming_component( temp, dst_component_size,
+ parts, component, scratches, regs);
+
+ if (mInfo[component].inDest) {
+
+ // blending...
+ build_blending( temp, mDstPixel, component, scratches );
+
+ // downshift component and rebuild pixel...
+ downshift(pixel, component, temp, parts.dither);
+ }
+}
+
+void GGLAssembler::build_incoming_component(
+ component_t& temp,
+ int dst_size,
+ const fragment_parts_t& parts,
+ int component,
+ Scratch& scratches,
+ Scratch& global_regs)
+{
+ const uint32_t component_mask = 1<<component;
+
+ // Figure out what we need for the blending stage...
+ int fs = component==GGLFormat::ALPHA ? mBlendSrcA : mBlendSrc;
+ int fd = component==GGLFormat::ALPHA ? mBlendDstA : mBlendDst;
+ if (fs==GGL_SRC_ALPHA_SATURATE && component==GGLFormat::ALPHA) {
+ fs = GGL_ONE;
+ }
+
+ // Figure out what we need to extract and for what reason
+ const int blending = blending_codes(fs, fd);
+
+ // Are we actually going to blend?
+ const int need_blending = (fs != int(GGL_ONE)) || (fd > int(GGL_ZERO));
+
+ // expand the source if the destination has more bits
+ int need_expander = false;
+ for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT-1 ; i++) {
+ texture_unit_t& tmu = mTextureMachine.tmu[i];
+ if ((tmu.format_idx) &&
+ (parts.texel[i].component_size(component) < dst_size)) {
+ need_expander = true;
+ }
+ }
+
+ // do we need to extract this component?
+ const bool multiTexture = mTextureMachine.activeUnits > 1;
+ const int blend_needs_alpha_source = (component==GGLFormat::ALPHA) &&
+ (isAlphaSourceNeeded());
+ int need_extract = mInfo[component].needed;
+ if (mInfo[component].inDest)
+ {
+ need_extract |= ((need_blending ?
+ (blending & (BLEND_SRC|FACTOR_SRC)) : need_expander));
+ need_extract |= (mTextureMachine.mask != mTextureMachine.replaced);
+ need_extract |= mInfo[component].smooth;
+ need_extract |= mInfo[component].fog;
+ need_extract |= mDithering;
+ need_extract |= multiTexture;
+ }
+
+ if (need_extract) {
+ Scratch& regs = blend_needs_alpha_source ? global_regs : scratches;
+ component_t fragment;
+
+ // iterated color
+ build_iterated_color(fragment, parts, component, regs);
+
+ // texture environement (decal, modulate, replace)
+ build_texture_environment(fragment, parts, component, regs);
+
+ // expand the source if the destination has more bits
+ if (need_expander && (fragment.size() < dst_size)) {
+ // we're here only if we fetched a texel
+ // (so we know for sure fragment is CORRUPTIBLE)
+ expand(fragment, fragment, dst_size);
+ }
+
+ // We have a few specific things to do for the alpha-channel
+ if ((component==GGLFormat::ALPHA) &&
+ (mInfo[component].needed || fragment.size()<dst_size))
+ {
+ // convert to integer_t first and make sure
+ // we don't corrupt a needed register
+ if (fragment.l) {
+ component_t incoming(fragment);
+ modify(fragment, regs);
+ MOV(AL, 0, fragment.reg, reg_imm(incoming.reg, LSR, incoming.l));
+ fragment.h -= fragment.l;
+ fragment.l = 0;
+ }
+
+ // coverage factor application
+ build_coverage_application(fragment, parts, regs);
+
+ // alpha-test
+ build_alpha_test(fragment, parts);
+
+ if (blend_needs_alpha_source) {
+ // We keep only 8 bits for the blending stage
+ const int shift = fragment.h <= 8 ? 0 : fragment.h-8;
+ if (fragment.flags & CORRUPTIBLE) {
+ fragment.flags &= ~CORRUPTIBLE;
+ mAlphaSource.setTo(fragment.reg,
+ fragment.size(), fragment.flags);
+ if (shift) {
+ MOV(AL, 0, mAlphaSource.reg,
+ reg_imm(mAlphaSource.reg, LSR, shift));
+ }
+ } else {
+ // XXX: it would better to do this in build_blend_factor()
+ // so we can avoid the extra MOV below.
+ mAlphaSource.setTo(regs.obtain(),
+ fragment.size(), CORRUPTIBLE);
+ if (shift) {
+ MOV(AL, 0, mAlphaSource.reg,
+ reg_imm(fragment.reg, LSR, shift));
+ } else {
+ MOV(AL, 0, mAlphaSource.reg, fragment.reg);
+ }
+ }
+ mAlphaSource.s -= shift;
+ }
+ }
+
+ // fog...
+ build_fog( fragment, component, regs );
+
+ temp = fragment;
+ } else {
+ if (mInfo[component].inDest) {
+ // extraction not needed and replace
+ // we just select the right component
+ if ((mTextureMachine.replaced & component_mask) == 0) {
+ // component wasn't replaced, so use it!
+ temp = component_t(parts.iterated, component);
+ }
+ for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
+ const texture_unit_t& tmu = mTextureMachine.tmu[i];
+ if ((tmu.mask & component_mask) &&
+ ((tmu.replaced & component_mask) == 0)) {
+ temp = component_t(parts.texel[i], component);
+ }
+ }
+ }
+ }
+}
+
+bool GGLAssembler::isAlphaSourceNeeded() const
+{
+ // XXX: also needed for alpha-test
+ const int bs = mBlendSrc;
+ const int bd = mBlendDst;
+ return bs==GGL_SRC_ALPHA_SATURATE ||
+ bs==GGL_SRC_ALPHA || bs==GGL_ONE_MINUS_SRC_ALPHA ||
+ bd==GGL_SRC_ALPHA || bd==GGL_ONE_MINUS_SRC_ALPHA ;
+}
+
+// ---------------------------------------------------------------------------
+
+void GGLAssembler::build_smooth_shade(const fragment_parts_t& parts)
+{
+ if (mSmooth && !parts.iterated_packed) {
+ // update the iterated color in a pipelined way...
+ comment("update iterated color");
+ Scratch scratches(registerFile());
+
+ const int reload = parts.reload;
+ for (int i=0 ; i<4 ; i++) {
+ if (!mInfo[i].iterated)
+ continue;
+
+ int c = parts.argb[i].reg;
+ int dx = parts.argb_dx[i].reg;
+
+ if (reload & 1) {
+ c = scratches.obtain();
+ CONTEXT_LOAD(c, generated_vars.argb[i].c);
+ }
+ if (reload & 2) {
+ dx = scratches.obtain();
+ CONTEXT_LOAD(dx, generated_vars.argb[i].dx);
+ }
+
+ if (mSmooth) {
+ ADD(AL, 0, c, c, dx);
+ }
+
+ if (reload & 1) {
+ CONTEXT_STORE(c, generated_vars.argb[i].c);
+ scratches.recycle(c);
+ }
+ if (reload & 2) {
+ scratches.recycle(dx);
+ }
+ }
+ }
+}
+
+// ---------------------------------------------------------------------------
+
+void GGLAssembler::build_coverage_application(component_t& fragment,
+ const fragment_parts_t& parts, Scratch& regs)
+{
+ // here fragment.l is guarenteed to be 0
+ if (mAA) {
+ // coverages are 1.15 fixed-point numbers
+ comment("coverage application");
+
+ component_t incoming(fragment);
+ modify(fragment, regs);
+
+ Scratch scratches(registerFile());
+ int cf = scratches.obtain();
+ LDRH(AL, cf, parts.covPtr.reg, immed8_post(2));
+ if (fragment.h > 31) {
+ fragment.h--;
+ SMULWB(AL, fragment.reg, incoming.reg, cf);
+ } else {
+ MOV(AL, 0, fragment.reg, reg_imm(incoming.reg, LSL, 1));
+ SMULWB(AL, fragment.reg, fragment.reg, cf);
+ }
+ }
+}
+
+// ---------------------------------------------------------------------------
+
+void GGLAssembler::build_alpha_test(component_t& fragment,
+ const fragment_parts_t& /*parts*/)
+{
+ if (mAlphaTest != GGL_ALWAYS) {
+ comment("Alpha Test");
+ Scratch scratches(registerFile());
+ int ref = scratches.obtain();
+ const int shift = GGL_COLOR_BITS-fragment.size();
+ CONTEXT_LOAD(ref, state.alpha_test.ref);
+ if (shift) CMP(AL, fragment.reg, reg_imm(ref, LSR, shift));
+ else CMP(AL, fragment.reg, ref);
+ int cc = NV;
+ switch (mAlphaTest) {
+ case GGL_NEVER: cc = NV; break;
+ case GGL_LESS: cc = LT; break;
+ case GGL_EQUAL: cc = EQ; break;
+ case GGL_LEQUAL: cc = LS; break;
+ case GGL_GREATER: cc = HI; break;
+ case GGL_NOTEQUAL: cc = NE; break;
+ case GGL_GEQUAL: cc = HS; break;
+ }
+ B(cc^1, "discard_after_textures");
+ }
+}
+
+// ---------------------------------------------------------------------------
+
+void GGLAssembler::build_depth_test(
+ const fragment_parts_t& parts, uint32_t mask)
+{
+ mask &= Z_TEST|Z_WRITE;
+ const needs_t& needs = mBuilderContext.needs;
+ const int zmask = GGL_READ_NEEDS(P_MASK_Z, needs.p);
+ Scratch scratches(registerFile());
+
+ if (mDepthTest != GGL_ALWAYS || zmask) {
+ int cc=AL, ic=AL;
+ switch (mDepthTest) {
+ case GGL_LESS: ic = HI; break;
+ case GGL_EQUAL: ic = EQ; break;
+ case GGL_LEQUAL: ic = HS; break;
+ case GGL_GREATER: ic = LT; break;
+ case GGL_NOTEQUAL: ic = NE; break;
+ case GGL_GEQUAL: ic = LS; break;
+ case GGL_NEVER:
+ // this never happens, because it's taken care of when
+ // computing the needs. but we keep it for completness.
+ comment("Depth Test (NEVER)");
+ B(AL, "discard_before_textures");
+ return;
+ case GGL_ALWAYS:
+ // we're here because zmask is enabled
+ mask &= ~Z_TEST; // test always passes.
+ break;
+ }
+
+ // inverse the condition
+ cc = ic^1;
+
+ if ((mask & Z_WRITE) && !zmask) {
+ mask &= ~Z_WRITE;
+ }
+
+ if (!mask)
+ return;
+
+ comment("Depth Test");
+
+ int zbase = scratches.obtain();
+ int depth = scratches.obtain();
+ int z = parts.z.reg;
+
+ CONTEXT_ADDR_LOAD(zbase, generated_vars.zbase); // stall
+ ADDR_SUB(AL, 0, zbase, zbase, reg_imm(parts.count.reg, LSR, 15));
+ // above does zbase = zbase + ((count >> 16) << 1)
+
+ if (mask & Z_TEST) {
+ LDRH(AL, depth, zbase); // stall
+ CMP(AL, depth, reg_imm(z, LSR, 16));
+ B(cc, "discard_before_textures");
+ }
+ if (mask & Z_WRITE) {
+ if (mask == Z_WRITE) {
+ // only z-write asked, cc is meaningless
+ ic = AL;
+ }
+ MOV(AL, 0, depth, reg_imm(z, LSR, 16));
+ STRH(ic, depth, zbase);
+ }
+ }
+}
+
+void GGLAssembler::build_iterate_z(const fragment_parts_t& parts)
+{
+ const needs_t& needs = mBuilderContext.needs;
+ if ((mDepthTest != GGL_ALWAYS) || GGL_READ_NEEDS(P_MASK_Z, needs.p)) {
+ Scratch scratches(registerFile());
+ int dzdx = scratches.obtain();
+ CONTEXT_LOAD(dzdx, generated_vars.dzdx); // stall
+ ADD(AL, 0, parts.z.reg, parts.z.reg, dzdx);
+ }
+}
+
+void GGLAssembler::build_iterate_f(const fragment_parts_t& /*parts*/)
+{
+ const needs_t& needs = mBuilderContext.needs;
+ if (GGL_READ_NEEDS(P_FOG, needs.p)) {
+ Scratch scratches(registerFile());
+ int dfdx = scratches.obtain();
+ int f = scratches.obtain();
+ CONTEXT_LOAD(f, generated_vars.f);
+ CONTEXT_LOAD(dfdx, generated_vars.dfdx); // stall
+ ADD(AL, 0, f, f, dfdx);
+ CONTEXT_STORE(f, generated_vars.f);
+ }
+}
+
+// ---------------------------------------------------------------------------
+
+void GGLAssembler::build_logic_op(pixel_t& pixel, Scratch& regs)
+{
+ const needs_t& needs = mBuilderContext.needs;
+ const int opcode = GGL_READ_NEEDS(LOGIC_OP, needs.n) | GGL_CLEAR;
+ if (opcode == GGL_COPY)
+ return;
+
+ comment("logic operation");
+
+ pixel_t s(pixel);
+ if (!(pixel.flags & CORRUPTIBLE)) {
+ pixel.reg = regs.obtain();
+ pixel.flags |= CORRUPTIBLE;
+ }
+
+ pixel_t d(mDstPixel);
+ switch(opcode) {
+ case GGL_CLEAR: MOV(AL, 0, pixel.reg, imm(0)); break;
+ case GGL_AND: AND(AL, 0, pixel.reg, s.reg, d.reg); break;
+ case GGL_AND_REVERSE: BIC(AL, 0, pixel.reg, s.reg, d.reg); break;
+ case GGL_COPY: break;
+ case GGL_AND_INVERTED: BIC(AL, 0, pixel.reg, d.reg, s.reg); break;
+ case GGL_NOOP: MOV(AL, 0, pixel.reg, d.reg); break;
+ case GGL_XOR: EOR(AL, 0, pixel.reg, s.reg, d.reg); break;
+ case GGL_OR: ORR(AL, 0, pixel.reg, s.reg, d.reg); break;
+ case GGL_NOR: ORR(AL, 0, pixel.reg, s.reg, d.reg);
+ MVN(AL, 0, pixel.reg, pixel.reg); break;
+ case GGL_EQUIV: EOR(AL, 0, pixel.reg, s.reg, d.reg);
+ MVN(AL, 0, pixel.reg, pixel.reg); break;
+ case GGL_INVERT: MVN(AL, 0, pixel.reg, d.reg); break;
+ case GGL_OR_REVERSE: // s | ~d == ~(~s & d)
+ BIC(AL, 0, pixel.reg, d.reg, s.reg);
+ MVN(AL, 0, pixel.reg, pixel.reg); break;
+ case GGL_COPY_INVERTED: MVN(AL, 0, pixel.reg, s.reg); break;
+ case GGL_OR_INVERTED: // ~s | d == ~(s & ~d)
+ BIC(AL, 0, pixel.reg, s.reg, d.reg);
+ MVN(AL, 0, pixel.reg, pixel.reg); break;
+ case GGL_NAND: AND(AL, 0, pixel.reg, s.reg, d.reg);
+ MVN(AL, 0, pixel.reg, pixel.reg); break;
+ case GGL_SET: MVN(AL, 0, pixel.reg, imm(0)); break;
+ };
+}
+
+// ---------------------------------------------------------------------------
+
+static uint32_t find_bottom(uint32_t val)
+{
+ uint32_t i = 0;
+ while (!(val & (3<<i)))
+ i+= 2;
+ return i;
+}
+
+static void normalize(uint32_t& val, uint32_t& rot)
+{
+ rot = 0;
+ while (!(val&3) || (val & 0xFC000000)) {
+ uint32_t newval;
+ newval = val >> 2;
+ newval |= (val&3) << 30;
+ val = newval;
+ rot += 2;
+ if (rot == 32) {
+ rot = 0;
+ break;
+ }
+ }
+}
+
+void GGLAssembler::build_and_immediate(int d, int s, uint32_t mask, int bits)
+{
+ uint32_t rot;
+ uint32_t size = ((bits>=32) ? 0 : (1LU << bits)) - 1;
+ mask &= size;
+
+ if (mask == size) {
+ if (d != s)
+ MOV( AL, 0, d, s);
+ return;
+ }
+
+ if (getCodegenArch() == CODEGEN_ARCH_MIPS) {
+ // MIPS can do 16-bit imm in 1 instr, 32-bit in 3 instr
+ // the below ' while (mask)' code is buggy on mips
+ // since mips returns true on isValidImmediate()
+ // then we get multiple AND instr (positive logic)
+ AND( AL, 0, d, s, imm(mask) );
+ return;
+ }
+ else if (getCodegenArch() == CODEGEN_ARCH_ARM64) {
+ AND( AL, 0, d, s, imm(mask) );
+ return;
+ }
+
+ int negative_logic = !isValidImmediate(mask);
+ if (negative_logic) {
+ mask = ~mask & size;
+ }
+ normalize(mask, rot);
+
+ if (mask) {
+ while (mask) {
+ uint32_t bitpos = find_bottom(mask);
+ int shift = rot + bitpos;
+ uint32_t m = mask & (0xff << bitpos);
+ mask &= ~m;
+ m >>= bitpos;
+ int32_t newMask = (m<<shift) | (m>>(32-shift));
+ if (!negative_logic) {
+ AND( AL, 0, d, s, imm(newMask) );
+ } else {
+ BIC( AL, 0, d, s, imm(newMask) );
+ }
+ s = d;
+ }
+ } else {
+ MOV( AL, 0, d, imm(0));
+ }
+}
+
+void GGLAssembler::build_masking(pixel_t& pixel, Scratch& regs)
+{
+ if (!mMasking || mAllMasked) {
+ return;
+ }
+
+ comment("color mask");
+
+ pixel_t fb(mDstPixel);
+ pixel_t s(pixel);
+ if (!(pixel.flags & CORRUPTIBLE)) {
+ pixel.reg = regs.obtain();
+ pixel.flags |= CORRUPTIBLE;
+ }
+
+ int mask = 0;
+ for (int i=0 ; i<4 ; i++) {
+ const int component_mask = 1<<i;
+ const int h = fb.format.c[i].h;
+ const int l = fb.format.c[i].l;
+ if (h && (!(mMasking & component_mask))) {
+ mask |= ((1<<(h-l))-1) << l;
+ }
+ }
+
+ // There is no need to clear the masked components of the source
+ // (unless we applied a logic op), because they're already zeroed
+ // by construction (masked components are not computed)
+
+ if (mLogicOp) {
+ const needs_t& needs = mBuilderContext.needs;
+ const int opcode = GGL_READ_NEEDS(LOGIC_OP, needs.n) | GGL_CLEAR;
+ if (opcode != GGL_CLEAR) {
+ // clear masked component of source
+ build_and_immediate(pixel.reg, s.reg, mask, fb.size());
+ s = pixel;
+ }
+ }
+
+ // clear non masked components of destination
+ build_and_immediate(fb.reg, fb.reg, ~mask, fb.size());
+
+ // or back the channels that were masked
+ if (s.reg == fb.reg) {
+ // this is in fact a MOV
+ if (s.reg == pixel.reg) {
+ // ugh. this in in fact a nop
+ } else {
+ MOV(AL, 0, pixel.reg, fb.reg);
+ }
+ } else {
+ ORR(AL, 0, pixel.reg, s.reg, fb.reg);
+ }
+}
+
+// ---------------------------------------------------------------------------
+
+void GGLAssembler::base_offset(
+ const pointer_t& d, const pointer_t& b, const reg_t& o)
+{
+ switch (b.size) {
+ case 32:
+ ADDR_ADD(AL, 0, d.reg, b.reg, reg_imm(o.reg, LSL, 2));
+ break;
+ case 24:
+ if (d.reg == b.reg) {
+ ADDR_ADD(AL, 0, d.reg, b.reg, reg_imm(o.reg, LSL, 1));
+ ADDR_ADD(AL, 0, d.reg, d.reg, o.reg);
+ } else {
+ ADDR_ADD(AL, 0, d.reg, o.reg, reg_imm(o.reg, LSL, 1));
+ ADDR_ADD(AL, 0, d.reg, d.reg, b.reg);
+ }
+ break;
+ case 16:
+ ADDR_ADD(AL, 0, d.reg, b.reg, reg_imm(o.reg, LSL, 1));
+ break;
+ case 8:
+ ADDR_ADD(AL, 0, d.reg, b.reg, o.reg);
+ break;
+ }
+}
+
+// ----------------------------------------------------------------------------
+// cheezy register allocator...
+// ----------------------------------------------------------------------------
+
+// Modified to support MIPS processors, in a very simple way. We retain the
+// (Arm) limit of 16 total registers, but shift the mapping of those registers
+// from 0-15, to 2-17. Register 0 on Mips cannot be used as GP registers, and
+// register 1 has a traditional use as a temp).
+
+RegisterAllocator::RegisterAllocator(int arch) : mRegs(arch)
+{
+}
+
+void RegisterAllocator::reset()
+{
+ mRegs.reset();
+}
+
+int RegisterAllocator::reserveReg(int reg)
+{
+ return mRegs.reserve(reg);
+}
+
+int RegisterAllocator::obtainReg()
+{
+ return mRegs.obtain();
+}
+
+void RegisterAllocator::recycleReg(int reg)
+{
+ mRegs.recycle(reg);
+}
+
+RegisterAllocator::RegisterFile& RegisterAllocator::registerFile()
+{
+ return mRegs;
+}
+
+// ----------------------------------------------------------------------------
+
+RegisterAllocator::RegisterFile::RegisterFile(int codegen_arch)
+ : mRegs(0), mTouched(0), mStatus(0), mArch(codegen_arch), mRegisterOffset(0)
+{
+ if (mArch == ARMAssemblerInterface::CODEGEN_ARCH_MIPS) {
+ mRegisterOffset = 2; // ARM has regs 0..15, MIPS offset to 2..17
+ }
+ reserve(ARMAssemblerInterface::SP);
+ reserve(ARMAssemblerInterface::PC);
+}
+
+RegisterAllocator::RegisterFile::RegisterFile(const RegisterFile& rhs, int codegen_arch)
+ : mRegs(rhs.mRegs), mTouched(rhs.mTouched), mArch(codegen_arch), mRegisterOffset(0)
+{
+ if (mArch == ARMAssemblerInterface::CODEGEN_ARCH_MIPS) {
+ mRegisterOffset = 2; // ARM has regs 0..15, MIPS offset to 2..17
+ }
+}
+
+RegisterAllocator::RegisterFile::~RegisterFile()
+{
+}
+
+bool RegisterAllocator::RegisterFile::operator == (const RegisterFile& rhs) const
+{
+ return (mRegs == rhs.mRegs);
+}
+
+void RegisterAllocator::RegisterFile::reset()
+{
+ mRegs = mTouched = mStatus = 0;
+ reserve(ARMAssemblerInterface::SP);
+ reserve(ARMAssemblerInterface::PC);
+}
+
+// RegisterFile::reserve() take a register parameter in the
+// range 0-15 (Arm compatible), but on a Mips processor, will
+// return the actual allocated register in the range 2-17.
+int RegisterAllocator::RegisterFile::reserve(int reg)
+{
+ reg += mRegisterOffset;
+ LOG_ALWAYS_FATAL_IF(isUsed(reg),
+ "reserving register %d, but already in use",
+ reg);
+ mRegs |= (1<<reg);
+ mTouched |= mRegs;
+ return reg;
+}
+
+// This interface uses regMask in range 2-17 on MIPS, no translation.
+void RegisterAllocator::RegisterFile::reserveSeveral(uint32_t regMask)
+{
+ mRegs |= regMask;
+ mTouched |= regMask;
+}
+
+int RegisterAllocator::RegisterFile::isUsed(int reg) const
+{
+ LOG_ALWAYS_FATAL_IF(reg>=16+(int)mRegisterOffset, "invalid register %d", reg);
+ return mRegs & (1<<reg);
+}
+
+int RegisterAllocator::RegisterFile::obtain()
+{
+ const char priorityList[14] = { 0, 1, 2, 3,
+ 12, 14, 4, 5,
+ 6, 7, 8, 9,
+ 10, 11 };
+ const int nbreg = sizeof(priorityList);
+ int i, r, reg;
+ for (i=0 ; i<nbreg ; i++) {
+ r = priorityList[i];
+ if (!isUsed(r + mRegisterOffset)) {
+ break;
+ }
+ }
+ // this is not an error anymore because, we'll try again with
+ // a lower optimization level.
+ //ALOGE_IF(i >= nbreg, "pixelflinger ran out of registers\n");
+ if (i >= nbreg) {
+ mStatus |= OUT_OF_REGISTERS;
+ // we return SP so we can more easily debug things
+ // the code will never be run anyway.
+ return ARMAssemblerInterface::SP;
+ }
+ reg = reserve(r); // Param in Arm range 0-15, returns range 2-17 on Mips.
+ return reg;
+}
+
+bool RegisterAllocator::RegisterFile::hasFreeRegs() const
+{
+ uint32_t regs = mRegs >> mRegisterOffset; // MIPS fix.
+ return ((regs & 0xFFFF) == 0xFFFF) ? false : true;
+}
+
+int RegisterAllocator::RegisterFile::countFreeRegs() const
+{
+ uint32_t regs = mRegs >> mRegisterOffset; // MIPS fix.
+ int f = ~regs & 0xFFFF;
+ // now count number of 1
+ f = (f & 0x5555) + ((f>>1) & 0x5555);
+ f = (f & 0x3333) + ((f>>2) & 0x3333);
+ f = (f & 0x0F0F) + ((f>>4) & 0x0F0F);
+ f = (f & 0x00FF) + ((f>>8) & 0x00FF);
+ return f;
+}
+
+void RegisterAllocator::RegisterFile::recycle(int reg)
+{
+ // commented out, since common failure of running out of regs
+ // triggers this assertion. Since the code is not execectued
+ // in that case, it does not matter. No reason to FATAL err.
+ // LOG_FATAL_IF(!isUsed(reg),
+ // "recycling unallocated register %d",
+ // reg);
+ mRegs &= ~(1<<reg);
+}
+
+void RegisterAllocator::RegisterFile::recycleSeveral(uint32_t regMask)
+{
+ // commented out, since common failure of running out of regs
+ // triggers this assertion. Since the code is not execectued
+ // in that case, it does not matter. No reason to FATAL err.
+ // LOG_FATAL_IF((mRegs & regMask)!=regMask,
+ // "recycling unallocated registers "
+ // "(recycle=%08x, allocated=%08x, unallocated=%08x)",
+ // regMask, mRegs, mRegs®Mask);
+ mRegs &= ~regMask;
+}
+
+uint32_t RegisterAllocator::RegisterFile::touched() const
+{
+ return mTouched;
+}
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
diff --git a/libpixelflinger/codeflinger/GGLAssembler.h b/libpixelflinger/codeflinger/GGLAssembler.h
new file mode 100644
index 0000000..9db20df
--- /dev/null
+++ b/libpixelflinger/codeflinger/GGLAssembler.h
@@ -0,0 +1,564 @@
+/* libs/pixelflinger/codeflinger/GGLAssembler.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+
+#ifndef ANDROID_GGLASSEMBLER_H
+#define ANDROID_GGLASSEMBLER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <private/pixelflinger/ggl_context.h>
+
+#include "ARMAssemblerProxy.h"
+
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+#define CONTEXT_ADDR_LOAD(REG, FIELD) \
+ ADDR_LDR(AL, REG, mBuilderContext.Rctx, immed12_pre(GGL_OFFSETOF(FIELD)))
+
+#define CONTEXT_ADDR_STORE(REG, FIELD) \
+ ADDR_STR(AL, REG, mBuilderContext.Rctx, immed12_pre(GGL_OFFSETOF(FIELD)))
+
+#define CONTEXT_LOAD(REG, FIELD) \
+ LDR(AL, REG, mBuilderContext.Rctx, immed12_pre(GGL_OFFSETOF(FIELD)))
+
+#define CONTEXT_STORE(REG, FIELD) \
+ STR(AL, REG, mBuilderContext.Rctx, immed12_pre(GGL_OFFSETOF(FIELD)))
+
+
+class RegisterAllocator
+{
+public:
+ class RegisterFile;
+
+ RegisterAllocator(int arch);
+ RegisterFile& registerFile();
+ int reserveReg(int reg);
+ int obtainReg();
+ void recycleReg(int reg);
+ void reset();
+
+ class RegisterFile
+ {
+ public:
+ RegisterFile(int arch);
+ RegisterFile(const RegisterFile& rhs, int arch);
+ ~RegisterFile();
+
+ void reset();
+
+ bool operator == (const RegisterFile& rhs) const;
+ bool operator != (const RegisterFile& rhs) const {
+ return !operator == (rhs);
+ }
+
+ int reserve(int reg);
+ void reserveSeveral(uint32_t regMask);
+
+ void recycle(int reg);
+ void recycleSeveral(uint32_t regMask);
+
+ int obtain();
+ inline int isUsed(int reg) const;
+
+ bool hasFreeRegs() const;
+ int countFreeRegs() const;
+
+ uint32_t touched() const;
+ inline uint32_t status() const { return mStatus; }
+
+ enum {
+ OUT_OF_REGISTERS = 0x1
+ };
+
+ private:
+ uint32_t mRegs;
+ uint32_t mTouched;
+ uint32_t mStatus;
+ int mArch;
+ uint32_t mRegisterOffset; // lets reg alloc use 2..17 for mips
+ // while arm uses 0..15
+ };
+
+ class Scratch
+ {
+ public:
+ Scratch(RegisterFile& regFile)
+ : mRegFile(regFile), mScratch(0) {
+ }
+ ~Scratch() {
+ mRegFile.recycleSeveral(mScratch);
+ }
+ int obtain() {
+ int reg = mRegFile.obtain();
+ mScratch |= 1<<reg;
+ return reg;
+ }
+ void recycle(int reg) {
+ mRegFile.recycle(reg);
+ mScratch &= ~(1<<reg);
+ }
+ bool isUsed(int reg) {
+ return (mScratch & (1<<reg));
+ }
+ int countFreeRegs() {
+ return mRegFile.countFreeRegs();
+ }
+ private:
+ RegisterFile& mRegFile;
+ uint32_t mScratch;
+ };
+
+ class Spill
+ {
+ public:
+ Spill(RegisterFile& regFile, ARMAssemblerInterface& gen, uint32_t reglist)
+ : mRegFile(regFile), mGen(gen), mRegList(reglist), mCount(0)
+ {
+ if (reglist) {
+ int count = 0;
+ while (reglist) {
+ count++;
+ reglist &= ~(1 << (31 - __builtin_clz(reglist)));
+ }
+ if (count == 1) {
+ int reg = 31 - __builtin_clz(mRegList);
+ mGen.STR(mGen.AL, reg, mGen.SP, mGen.immed12_pre(-4, 1));
+ } else {
+ mGen.STM(mGen.AL, mGen.DB, mGen.SP, 1, mRegList);
+ }
+ mRegFile.recycleSeveral(mRegList);
+ mCount = count;
+ }
+ }
+ ~Spill() {
+ if (mRegList) {
+ if (mCount == 1) {
+ int reg = 31 - __builtin_clz(mRegList);
+ mGen.LDR(mGen.AL, reg, mGen.SP, mGen.immed12_post(4));
+ } else {
+ mGen.LDM(mGen.AL, mGen.IA, mGen.SP, 1, mRegList);
+ }
+ mRegFile.reserveSeveral(mRegList);
+ }
+ }
+ private:
+ RegisterFile& mRegFile;
+ ARMAssemblerInterface& mGen;
+ uint32_t mRegList;
+ int mCount;
+ };
+
+private:
+ RegisterFile mRegs;
+};
+
+// ----------------------------------------------------------------------------
+
+class GGLAssembler : public ARMAssemblerProxy, public RegisterAllocator
+{
+public:
+
+ GGLAssembler(ARMAssemblerInterface* target);
+ virtual ~GGLAssembler();
+
+ uint32_t* base() const { return 0; } // XXX
+ uint32_t* pc() const { return 0; } // XXX
+
+ void reset(int opt_level);
+
+ virtual void prolog();
+ virtual void epilog(uint32_t touched);
+
+ // generate scanline code for given needs
+ int scanline(const needs_t& needs, context_t const* c);
+ int scanline_core(const needs_t& needs, context_t const* c);
+
+ enum {
+ CLEAR_LO = 0x0001,
+ CLEAR_HI = 0x0002,
+ CORRUPTIBLE = 0x0004,
+ FIRST = 0x0008
+ };
+
+ enum { //load/store flags
+ WRITE_BACK = 0x0001
+ };
+
+ struct reg_t {
+ reg_t() : reg(-1), flags(0) {
+ }
+ reg_t(int r, int f=0)
+ : reg(r), flags(f) {
+ }
+ void setTo(int r, int f=0) {
+ reg=r; flags=f;
+ }
+ int reg;
+ uint16_t flags;
+ };
+
+ struct integer_t : public reg_t {
+ integer_t() : reg_t(), s(0) {
+ }
+ integer_t(int r, int sz=32, int f=0)
+ : reg_t(r, f), s(sz) {
+ }
+ void setTo(int r, int sz=32, int f=0) {
+ reg_t::setTo(r, f); s=sz;
+ }
+ int8_t s;
+ inline int size() const { return s; }
+ };
+
+ struct pixel_t : public reg_t {
+ pixel_t() : reg_t() {
+ memset(&format, 0, sizeof(GGLFormat));
+ }
+ pixel_t(int r, const GGLFormat* fmt, int f=0)
+ : reg_t(r, f), format(*fmt) {
+ }
+ void setTo(int r, const GGLFormat* fmt, int f=0) {
+ reg_t::setTo(r, f); format = *fmt;
+ }
+ GGLFormat format;
+ inline int hi(int c) const { return format.c[c].h; }
+ inline int low(int c) const { return format.c[c].l; }
+ inline int mask(int c) const { return ((1<<size(c))-1) << low(c); }
+ inline int size() const { return format.size*8; }
+ inline int size(int c) const { return component_size(c); }
+ inline int component_size(int c) const { return hi(c) - low(c); }
+ };
+
+ struct component_t : public reg_t {
+ component_t() : reg_t(), h(0), l(0) {
+ }
+ component_t(int r, int f=0)
+ : reg_t(r, f), h(0), l(0) {
+ }
+ component_t(int r, int lo, int hi, int f=0)
+ : reg_t(r, f), h(hi), l(lo) {
+ }
+ explicit component_t(const integer_t& rhs)
+ : reg_t(rhs.reg, rhs.flags), h(rhs.s), l(0) {
+ }
+ explicit component_t(const pixel_t& rhs, int component) {
+ setTo( rhs.reg,
+ rhs.format.c[component].l,
+ rhs.format.c[component].h,
+ rhs.flags|CLEAR_LO|CLEAR_HI);
+ }
+ void setTo(int r, int lo=0, int hi=0, int f=0) {
+ reg_t::setTo(r, f); h=hi; l=lo;
+ }
+ int8_t h;
+ int8_t l;
+ inline int size() const { return h-l; }
+ };
+
+ struct pointer_t : public reg_t {
+ pointer_t() : reg_t(), size(0) {
+ }
+ pointer_t(int r, int s, int f=0)
+ : reg_t(r, f), size(s) {
+ }
+ void setTo(int r, int s, int f=0) {
+ reg_t::setTo(r, f); size=s;
+ }
+ int8_t size;
+ };
+
+
+private:
+ struct tex_coord_t {
+ reg_t s;
+ reg_t t;
+ pointer_t ptr;
+ };
+
+ struct fragment_parts_t {
+ uint32_t packed : 1;
+ uint32_t reload : 2;
+ uint32_t iterated_packed : 1;
+ pixel_t iterated;
+ pointer_t cbPtr;
+ pointer_t covPtr;
+ reg_t count;
+ reg_t argb[4];
+ reg_t argb_dx[4];
+ reg_t z;
+ reg_t dither;
+ pixel_t texel[GGL_TEXTURE_UNIT_COUNT];
+ tex_coord_t coords[GGL_TEXTURE_UNIT_COUNT];
+ };
+
+ struct texture_unit_t {
+ int format_idx;
+ GGLFormat format;
+ int bits;
+ int swrap;
+ int twrap;
+ int env;
+ int pot;
+ int linear;
+ uint8_t mask;
+ uint8_t replaced;
+ };
+
+ struct texture_machine_t {
+ texture_unit_t tmu[GGL_TEXTURE_UNIT_COUNT];
+ uint8_t mask;
+ uint8_t replaced;
+ uint8_t directTexture;
+ uint8_t activeUnits;
+ };
+
+ struct component_info_t {
+ bool masked : 1;
+ bool inDest : 1;
+ bool needed : 1;
+ bool replaced : 1;
+ bool iterated : 1;
+ bool smooth : 1;
+ bool blend : 1;
+ bool fog : 1;
+ };
+
+ struct builder_context_t {
+ context_t const* c;
+ needs_t needs;
+ int Rctx;
+ };
+
+ template <typename T>
+ void modify(T& r, Scratch& regs)
+ {
+ if (!(r.flags & CORRUPTIBLE)) {
+ r.reg = regs.obtain();
+ r.flags |= CORRUPTIBLE;
+ }
+ }
+
+ // helpers
+ void base_offset(const pointer_t& d, const pointer_t& b, const reg_t& o);
+
+ // texture environement
+ void modulate( component_t& dest,
+ const component_t& incoming,
+ const pixel_t& texel, int component);
+
+ void decal( component_t& dest,
+ const component_t& incoming,
+ const pixel_t& texel, int component);
+
+ void blend( component_t& dest,
+ const component_t& incoming,
+ const pixel_t& texel, int component, int tmu);
+
+ void add( component_t& dest,
+ const component_t& incoming,
+ const pixel_t& texel, int component);
+
+ // load/store stuff
+ void store(const pointer_t& addr, const pixel_t& src, uint32_t flags=0);
+ void load(const pointer_t& addr, const pixel_t& dest, uint32_t flags=0);
+ void extract(integer_t& d, const pixel_t& s, int component);
+ void extract(component_t& d, const pixel_t& s, int component);
+ void extract(integer_t& d, int s, int h, int l, int bits=32);
+ void expand(integer_t& d, const integer_t& s, int dbits);
+ void expand(integer_t& d, const component_t& s, int dbits);
+ void expand(component_t& d, const component_t& s, int dbits);
+ void downshift(pixel_t& d, int component, component_t s, const reg_t& dither);
+
+
+ void mul_factor( component_t& d,
+ const integer_t& v,
+ const integer_t& f);
+
+ void mul_factor_add( component_t& d,
+ const integer_t& v,
+ const integer_t& f,
+ const component_t& a);
+
+ void component_add( component_t& d,
+ const integer_t& dst,
+ const integer_t& src);
+
+ void component_sat( const component_t& v);
+
+
+ void build_scanline_prolog( fragment_parts_t& parts,
+ const needs_t& needs);
+
+ void build_smooth_shade(const fragment_parts_t& parts);
+
+ void build_component( pixel_t& pixel,
+ const fragment_parts_t& parts,
+ int component,
+ Scratch& global_scratches);
+
+ void build_incoming_component(
+ component_t& temp,
+ int dst_size,
+ const fragment_parts_t& parts,
+ int component,
+ Scratch& scratches,
+ Scratch& global_scratches);
+
+ void init_iterated_color(fragment_parts_t& parts, const reg_t& x);
+
+ void build_iterated_color( component_t& fragment,
+ const fragment_parts_t& parts,
+ int component,
+ Scratch& regs);
+
+ void decodeLogicOpNeeds(const needs_t& needs);
+
+ void decodeTMUNeeds(const needs_t& needs, context_t const* c);
+
+ void init_textures( tex_coord_t* coords,
+ const reg_t& x,
+ const reg_t& y);
+
+ void build_textures( fragment_parts_t& parts,
+ Scratch& regs);
+
+ void filter8( const fragment_parts_t& parts,
+ pixel_t& texel, const texture_unit_t& tmu,
+ int U, int V, pointer_t& txPtr,
+ int FRAC_BITS);
+
+ void filter16( const fragment_parts_t& parts,
+ pixel_t& texel, const texture_unit_t& tmu,
+ int U, int V, pointer_t& txPtr,
+ int FRAC_BITS);
+
+ void filter24( const fragment_parts_t& parts,
+ pixel_t& texel, const texture_unit_t& tmu,
+ int U, int V, pointer_t& txPtr,
+ int FRAC_BITS);
+
+ void filter32( const fragment_parts_t& parts,
+ pixel_t& texel, const texture_unit_t& tmu,
+ int U, int V, pointer_t& txPtr,
+ int FRAC_BITS);
+
+ void build_texture_environment( component_t& fragment,
+ const fragment_parts_t& parts,
+ int component,
+ Scratch& regs);
+
+ void wrapping( int d,
+ int coord, int size,
+ int tx_wrap, int tx_linear);
+
+ void build_fog( component_t& temp,
+ int component,
+ Scratch& parent_scratches);
+
+ void build_blending( component_t& in_out,
+ const pixel_t& pixel,
+ int component,
+ Scratch& parent_scratches);
+
+ void build_blend_factor(
+ integer_t& factor, int f, int component,
+ const pixel_t& dst_pixel,
+ integer_t& fragment,
+ integer_t& fb,
+ Scratch& scratches);
+
+ void build_blendFOneMinusF( component_t& temp,
+ const integer_t& factor,
+ const integer_t& fragment,
+ const integer_t& fb);
+
+ void build_blendOneMinusFF( component_t& temp,
+ const integer_t& factor,
+ const integer_t& fragment,
+ const integer_t& fb);
+
+ void build_coverage_application(component_t& fragment,
+ const fragment_parts_t& parts,
+ Scratch& regs);
+
+ void build_alpha_test(component_t& fragment, const fragment_parts_t& parts);
+
+ enum { Z_TEST=1, Z_WRITE=2 };
+ void build_depth_test(const fragment_parts_t& parts, uint32_t mask);
+ void build_iterate_z(const fragment_parts_t& parts);
+ void build_iterate_f(const fragment_parts_t& parts);
+ void build_iterate_texture_coordinates(const fragment_parts_t& parts);
+
+ void build_logic_op(pixel_t& pixel, Scratch& regs);
+
+ void build_masking(pixel_t& pixel, Scratch& regs);
+
+ void build_and_immediate(int d, int s, uint32_t mask, int bits);
+
+ bool isAlphaSourceNeeded() const;
+
+ enum {
+ FACTOR_SRC=1, FACTOR_DST=2, BLEND_SRC=4, BLEND_DST=8
+ };
+
+ enum {
+ LOGIC_OP=1, LOGIC_OP_SRC=2, LOGIC_OP_DST=4
+ };
+
+ static int blending_codes(int fs, int fd);
+
+ builder_context_t mBuilderContext;
+ texture_machine_t mTextureMachine;
+ component_info_t mInfo[4];
+ int mBlending;
+ int mMasking;
+ int mAllMasked;
+ int mLogicOp;
+ int mAlphaTest;
+ int mAA;
+ int mDithering;
+ int mDepthTest;
+
+ int mSmooth;
+ int mFog;
+ pixel_t mDstPixel;
+
+ GGLFormat mCbFormat;
+
+ int mBlendFactorCached;
+ integer_t mAlphaSource;
+
+ int mBaseRegister;
+
+ int mBlendSrc;
+ int mBlendDst;
+ int mBlendSrcA;
+ int mBlendDstA;
+
+ int mOptLevel;
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_GGLASSEMBLER_H
diff --git a/libpixelflinger/codeflinger/MIPSAssembler.cpp b/libpixelflinger/codeflinger/MIPSAssembler.cpp
new file mode 100644
index 0000000..a88d2fe
--- /dev/null
+++ b/libpixelflinger/codeflinger/MIPSAssembler.cpp
@@ -0,0 +1,1957 @@
+/* libs/pixelflinger/codeflinger/MIPSAssembler.cpp
+**
+** Copyright 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.
+*/
+
+
+/* MIPS assembler and ARM->MIPS assembly translator
+**
+** The approach is to leave the GGLAssembler and associated files largely
+** un-changed, still utilizing all Arm instruction generation. Via the
+** ArmToMipsAssembler (subclassed from ArmAssemblerInterface) each Arm
+** instruction is translated to one or more Mips instructions as necessary. This
+** is clearly less efficient than a direct implementation within the
+** GGLAssembler, but is far cleaner, more maintainable, and has yielded very
+** significant performance gains on Mips compared to the generic pixel pipeline.
+**
+**
+** GGLAssembler changes
+**
+** - The register allocator has been modified to re-map Arm registers 0-15 to mips
+** registers 2-17. Mips register 0 cannot be used as general-purpose register,
+** and register 1 has traditional uses as a short-term temporary.
+**
+** - Added some early bailouts for OUT_OF_REGISTERS in texturing.cpp and
+** GGLAssembler.cpp, since this is not fatal, and can be retried at lower
+** optimization level.
+**
+**
+** ARMAssembler and ARMAssemblerInterface changes
+**
+** Refactored ARM address-mode static functions (imm(), reg_imm(), imm12_pre(), etc.)
+** to virtual, so they can be overridden in MIPSAssembler. The implementation of these
+** functions on ARM is moved from ARMAssemblerInterface.cpp to ARMAssembler.cpp, and
+** is unchanged from the original. (This required duplicating 2 of these as static
+** functions in ARMAssemblerInterface.cpp so they could be used as static initializers).
+*/
+
+
+#define LOG_TAG "MIPSAssembler"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <cutils/log.h>
+#include <cutils/properties.h>
+
+#if defined(WITH_LIB_HARDWARE)
+#include <hardware_legacy/qemu_tracing.h>
+#endif
+
+#include <private/pixelflinger/ggl_context.h>
+
+#include "MIPSAssembler.h"
+#include "CodeCache.h"
+#include "mips_disassem.h"
+
+// Choose MIPS arch variant following gcc flags
+#if defined(__mips__) && __mips==32 && __mips_isa_rev>=2
+#define mips32r2 1
+#else
+#define mips32r2 0
+#endif
+
+
+#define NOT_IMPLEMENTED() LOG_ALWAYS_FATAL("Arm instruction %s not yet implemented\n", __func__)
+
+
+
+// ----------------------------------------------------------------------------
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark ArmToMipsAssembler...
+#endif
+
+ArmToMipsAssembler::ArmToMipsAssembler(const sp<Assembly>& assembly,
+ char *abuf, int linesz, int instr_count)
+ : ARMAssemblerInterface(),
+ mArmDisassemblyBuffer(abuf),
+ mArmLineLength(linesz),
+ mArmInstrCount(instr_count),
+ mInum(0),
+ mAssembly(assembly)
+{
+ mMips = new MIPSAssembler(assembly, this);
+ mArmPC = (uint32_t **) malloc(ARM_MAX_INSTUCTIONS * sizeof(uint32_t *));
+ init_conditional_labels();
+}
+
+ArmToMipsAssembler::~ArmToMipsAssembler()
+{
+ delete mMips;
+ free((void *) mArmPC);
+}
+
+uint32_t* ArmToMipsAssembler::pc() const
+{
+ return mMips->pc();
+}
+
+uint32_t* ArmToMipsAssembler::base() const
+{
+ return mMips->base();
+}
+
+void ArmToMipsAssembler::reset()
+{
+ cond.labelnum = 0;
+ mInum = 0;
+ mMips->reset();
+}
+
+int ArmToMipsAssembler::getCodegenArch()
+{
+ return CODEGEN_ARCH_MIPS;
+}
+
+void ArmToMipsAssembler::comment(const char* string)
+{
+ mMips->comment(string);
+}
+
+void ArmToMipsAssembler::label(const char* theLabel)
+{
+ mMips->label(theLabel);
+}
+
+void ArmToMipsAssembler::disassemble(const char* name)
+{
+ mMips->disassemble(name);
+}
+
+void ArmToMipsAssembler::init_conditional_labels()
+{
+ int i;
+ for (i=0;i<99; ++i) {
+ sprintf(cond.label[i], "cond_%d", i);
+ }
+}
+
+
+
+#if 0
+#pragma mark -
+#pragma mark Prolog/Epilog & Generate...
+#endif
+
+void ArmToMipsAssembler::prolog()
+{
+ mArmPC[mInum++] = pc(); // save starting PC for this instr
+
+ mMips->ADDIU(R_sp, R_sp, -(5 * 4));
+ mMips->SW(R_s0, R_sp, 0);
+ mMips->SW(R_s1, R_sp, 4);
+ mMips->SW(R_s2, R_sp, 8);
+ mMips->SW(R_s3, R_sp, 12);
+ mMips->SW(R_s4, R_sp, 16);
+ mMips->MOVE(R_v0, R_a0); // move context * passed in a0 to v0 (arm r0)
+}
+
+void ArmToMipsAssembler::epilog(uint32_t touched)
+{
+ mArmPC[mInum++] = pc(); // save starting PC for this instr
+
+ mMips->LW(R_s0, R_sp, 0);
+ mMips->LW(R_s1, R_sp, 4);
+ mMips->LW(R_s2, R_sp, 8);
+ mMips->LW(R_s3, R_sp, 12);
+ mMips->LW(R_s4, R_sp, 16);
+ mMips->ADDIU(R_sp, R_sp, (5 * 4));
+ mMips->JR(R_ra);
+
+}
+
+int ArmToMipsAssembler::generate(const char* name)
+{
+ return mMips->generate(name);
+}
+
+uint32_t* ArmToMipsAssembler::pcForLabel(const char* label)
+{
+ return mMips->pcForLabel(label);
+}
+
+
+
+//----------------------------------------------------------
+
+#if 0
+#pragma mark -
+#pragma mark Addressing modes & shifters...
+#endif
+
+
+// do not need this for MIPS, but it is in the Interface (virtual)
+int ArmToMipsAssembler::buildImmediate(
+ uint32_t immediate, uint32_t& rot, uint32_t& imm)
+{
+ // for MIPS, any 32-bit immediate is OK
+ rot = 0;
+ imm = immediate;
+ return 0;
+}
+
+// shifters...
+
+bool ArmToMipsAssembler::isValidImmediate(uint32_t immediate)
+{
+ // for MIPS, any 32-bit immediate is OK
+ return true;
+}
+
+uint32_t ArmToMipsAssembler::imm(uint32_t immediate)
+{
+ // ALOGW("immediate value %08x at pc %08x\n", immediate, (int)pc());
+ amode.value = immediate;
+ return AMODE_IMM;
+}
+
+uint32_t ArmToMipsAssembler::reg_imm(int Rm, int type, uint32_t shift)
+{
+ amode.reg = Rm;
+ amode.stype = type;
+ amode.value = shift;
+ return AMODE_REG_IMM;
+}
+
+uint32_t ArmToMipsAssembler::reg_rrx(int Rm)
+{
+ // reg_rrx mode is not used in the GLLAssember code at this time
+ return AMODE_UNSUPPORTED;
+}
+
+uint32_t ArmToMipsAssembler::reg_reg(int Rm, int type, int Rs)
+{
+ // reg_reg mode is not used in the GLLAssember code at this time
+ return AMODE_UNSUPPORTED;
+}
+
+
+// addressing modes...
+// LDR(B)/STR(B)/PLD (immediate and Rm can be negative, which indicate U=0)
+uint32_t ArmToMipsAssembler::immed12_pre(int32_t immed12, int W)
+{
+ LOG_ALWAYS_FATAL_IF(abs(immed12) >= 0x800,
+ "LDR(B)/STR(B)/PLD immediate too big (%08x)",
+ immed12);
+ amode.value = immed12;
+ amode.writeback = W;
+ return AMODE_IMM_12_PRE;
+}
+
+uint32_t ArmToMipsAssembler::immed12_post(int32_t immed12)
+{
+ LOG_ALWAYS_FATAL_IF(abs(immed12) >= 0x800,
+ "LDR(B)/STR(B)/PLD immediate too big (%08x)",
+ immed12);
+
+ amode.value = immed12;
+ return AMODE_IMM_12_POST;
+}
+
+uint32_t ArmToMipsAssembler::reg_scale_pre(int Rm, int type,
+ uint32_t shift, int W)
+{
+ LOG_ALWAYS_FATAL_IF(W | type | shift, "reg_scale_pre adv modes not yet implemented");
+
+ amode.reg = Rm;
+ // amode.stype = type; // more advanced modes not used in GGLAssembler yet
+ // amode.value = shift;
+ // amode.writeback = W;
+ return AMODE_REG_SCALE_PRE;
+}
+
+uint32_t ArmToMipsAssembler::reg_scale_post(int Rm, int type, uint32_t shift)
+{
+ LOG_ALWAYS_FATAL("adr mode reg_scale_post not yet implemented\n");
+ return AMODE_UNSUPPORTED;
+}
+
+// LDRH/LDRSB/LDRSH/STRH (immediate and Rm can be negative, which indicate U=0)
+uint32_t ArmToMipsAssembler::immed8_pre(int32_t immed8, int W)
+{
+ // uint32_t offset = abs(immed8);
+
+ LOG_ALWAYS_FATAL("adr mode immed8_pre not yet implemented\n");
+
+ LOG_ALWAYS_FATAL_IF(abs(immed8) >= 0x100,
+ "LDRH/LDRSB/LDRSH/STRH immediate too big (%08x)",
+ immed8);
+ return AMODE_IMM_8_PRE;
+}
+
+uint32_t ArmToMipsAssembler::immed8_post(int32_t immed8)
+{
+ // uint32_t offset = abs(immed8);
+
+ LOG_ALWAYS_FATAL_IF(abs(immed8) >= 0x100,
+ "LDRH/LDRSB/LDRSH/STRH immediate too big (%08x)",
+ immed8);
+ amode.value = immed8;
+ return AMODE_IMM_8_POST;
+}
+
+uint32_t ArmToMipsAssembler::reg_pre(int Rm, int W)
+{
+ LOG_ALWAYS_FATAL_IF(W, "reg_pre writeback not yet implemented");
+ amode.reg = Rm;
+ return AMODE_REG_PRE;
+}
+
+uint32_t ArmToMipsAssembler::reg_post(int Rm)
+{
+ LOG_ALWAYS_FATAL("adr mode reg_post not yet implemented\n");
+ return AMODE_UNSUPPORTED;
+}
+
+
+
+// ----------------------------------------------------------------------------
+
+#if 0
+#pragma mark -
+#pragma mark Data Processing...
+#endif
+
+
+static const char * const dpOpNames[] = {
+ "AND", "EOR", "SUB", "RSB", "ADD", "ADC", "SBC", "RSC",
+ "TST", "TEQ", "CMP", "CMN", "ORR", "MOV", "BIC", "MVN"
+};
+
+// check if the operand registers from a previous CMP or S-bit instruction
+// would be overwritten by this instruction. If so, move the value to a
+// safe register.
+// Note that we cannot tell at _this_ instruction time if a future (conditional)
+// instruction will _also_ use this value (a defect of the simple 1-pass, one-
+// instruction-at-a-time translation). Therefore we must be conservative and
+// save the value before it is overwritten. This costs an extra MOVE instr.
+
+void ArmToMipsAssembler::protectConditionalOperands(int Rd)
+{
+ if (Rd == cond.r1) {
+ mMips->MOVE(R_cmp, cond.r1);
+ cond.r1 = R_cmp;
+ }
+ if (cond.type == CMP_COND && Rd == cond.r2) {
+ mMips->MOVE(R_cmp2, cond.r2);
+ cond.r2 = R_cmp2;
+ }
+}
+
+
+// interprets the addressing mode, and generates the common code
+// used by the majority of data-processing ops. Many MIPS instructions
+// have a register-based form and a different immediate form. See
+// opAND below for an example. (this could be inlined)
+//
+// this works with the imm(), reg_imm() methods above, which are directly
+// called by the GLLAssembler.
+// note: _signed parameter defaults to false (un-signed)
+// note: tmpReg parameter defaults to 1, MIPS register AT
+int ArmToMipsAssembler::dataProcAdrModes(int op, int& source, bool _signed, int tmpReg)
+{
+ if (op < AMODE_REG) {
+ source = op;
+ return SRC_REG;
+ } else if (op == AMODE_IMM) {
+ if ((!_signed && amode.value > 0xffff)
+ || (_signed && ((int)amode.value < -32768 || (int)amode.value > 32767) )) {
+ mMips->LUI(tmpReg, (amode.value >> 16));
+ if (amode.value & 0x0000ffff) {
+ mMips->ORI(tmpReg, tmpReg, (amode.value & 0x0000ffff));
+ }
+ source = tmpReg;
+ return SRC_REG;
+ } else {
+ source = amode.value;
+ return SRC_IMM;
+ }
+ } else if (op == AMODE_REG_IMM) {
+ switch (amode.stype) {
+ case LSL: mMips->SLL(tmpReg, amode.reg, amode.value); break;
+ case LSR: mMips->SRL(tmpReg, amode.reg, amode.value); break;
+ case ASR: mMips->SRA(tmpReg, amode.reg, amode.value); break;
+ case ROR: if (mips32r2) {
+ mMips->ROTR(tmpReg, amode.reg, amode.value);
+ } else {
+ mMips->RORIsyn(tmpReg, amode.reg, amode.value);
+ }
+ break;
+ }
+ source = tmpReg;
+ return SRC_REG;
+ } else { // adr mode RRX is not used in GGL Assembler at this time
+ // we are screwed, this should be exception, assert-fail or something
+ LOG_ALWAYS_FATAL("adr mode reg_rrx not yet implemented\n");
+ return SRC_ERROR;
+ }
+}
+
+
+void ArmToMipsAssembler::dataProcessing(int opcode, int cc,
+ int s, int Rd, int Rn, uint32_t Op2)
+{
+ int src; // src is modified by dataProcAdrModes() - passed as int&
+
+
+ if (cc != AL) {
+ protectConditionalOperands(Rd);
+ // the branch tests register(s) set by prev CMP or instr with 'S' bit set
+ // inverse the condition to jump past this conditional instruction
+ ArmToMipsAssembler::B(cc^1, cond.label[++cond.labelnum]);
+ } else {
+ mArmPC[mInum++] = pc(); // save starting PC for this instr
+ }
+
+ switch (opcode) {
+ case opAND:
+ if (dataProcAdrModes(Op2, src) == SRC_REG) {
+ mMips->AND(Rd, Rn, src);
+ } else { // adr mode was SRC_IMM
+ mMips->ANDI(Rd, Rn, src);
+ }
+ break;
+
+ case opADD:
+ // set "signed" to true for adr modes
+ if (dataProcAdrModes(Op2, src, true) == SRC_REG) {
+ mMips->ADDU(Rd, Rn, src);
+ } else { // adr mode was SRC_IMM
+ mMips->ADDIU(Rd, Rn, src);
+ }
+ break;
+
+ case opSUB:
+ // set "signed" to true for adr modes
+ if (dataProcAdrModes(Op2, src, true) == SRC_REG) {
+ mMips->SUBU(Rd, Rn, src);
+ } else { // adr mode was SRC_IMM
+ mMips->SUBIU(Rd, Rn, src);
+ }
+ break;
+
+ case opEOR:
+ if (dataProcAdrModes(Op2, src) == SRC_REG) {
+ mMips->XOR(Rd, Rn, src);
+ } else { // adr mode was SRC_IMM
+ mMips->XORI(Rd, Rn, src);
+ }
+ break;
+
+ case opORR:
+ if (dataProcAdrModes(Op2, src) == SRC_REG) {
+ mMips->OR(Rd, Rn, src);
+ } else { // adr mode was SRC_IMM
+ mMips->ORI(Rd, Rn, src);
+ }
+ break;
+
+ case opBIC:
+ if (dataProcAdrModes(Op2, src) == SRC_IMM) {
+ // if we are 16-bit imnmediate, load to AT reg
+ mMips->ORI(R_at, 0, src);
+ src = R_at;
+ }
+ mMips->NOT(R_at, src);
+ mMips->AND(Rd, Rn, R_at);
+ break;
+
+ case opRSB:
+ if (dataProcAdrModes(Op2, src) == SRC_IMM) {
+ // if we are 16-bit imnmediate, load to AT reg
+ mMips->ORI(R_at, 0, src);
+ src = R_at;
+ }
+ mMips->SUBU(Rd, src, Rn); // subu with the parameters reversed
+ break;
+
+ case opMOV:
+ if (Op2 < AMODE_REG) { // op2 is reg # in this case
+ mMips->MOVE(Rd, Op2);
+ } else if (Op2 == AMODE_IMM) {
+ if (amode.value > 0xffff) {
+ mMips->LUI(Rd, (amode.value >> 16));
+ if (amode.value & 0x0000ffff) {
+ mMips->ORI(Rd, Rd, (amode.value & 0x0000ffff));
+ }
+ } else {
+ mMips->ORI(Rd, 0, amode.value);
+ }
+ } else if (Op2 == AMODE_REG_IMM) {
+ switch (amode.stype) {
+ case LSL: mMips->SLL(Rd, amode.reg, amode.value); break;
+ case LSR: mMips->SRL(Rd, amode.reg, amode.value); break;
+ case ASR: mMips->SRA(Rd, amode.reg, amode.value); break;
+ case ROR: if (mips32r2) {
+ mMips->ROTR(Rd, amode.reg, amode.value);
+ } else {
+ mMips->RORIsyn(Rd, amode.reg, amode.value);
+ }
+ break;
+ }
+ }
+ else {
+ // adr mode RRX is not used in GGL Assembler at this time
+ mMips->UNIMPL();
+ }
+ break;
+
+ case opMVN: // this is a 1's complement: NOT
+ if (Op2 < AMODE_REG) { // op2 is reg # in this case
+ mMips->NOR(Rd, Op2, 0); // NOT is NOR with 0
+ break;
+ } else if (Op2 == AMODE_IMM) {
+ if (amode.value > 0xffff) {
+ mMips->LUI(Rd, (amode.value >> 16));
+ if (amode.value & 0x0000ffff) {
+ mMips->ORI(Rd, Rd, (amode.value & 0x0000ffff));
+ }
+ } else {
+ mMips->ORI(Rd, 0, amode.value);
+ }
+ } else if (Op2 == AMODE_REG_IMM) {
+ switch (amode.stype) {
+ case LSL: mMips->SLL(Rd, amode.reg, amode.value); break;
+ case LSR: mMips->SRL(Rd, amode.reg, amode.value); break;
+ case ASR: mMips->SRA(Rd, amode.reg, amode.value); break;
+ case ROR: if (mips32r2) {
+ mMips->ROTR(Rd, amode.reg, amode.value);
+ } else {
+ mMips->RORIsyn(Rd, amode.reg, amode.value);
+ }
+ break;
+ }
+ }
+ else {
+ // adr mode RRX is not used in GGL Assembler at this time
+ mMips->UNIMPL();
+ }
+ mMips->NOR(Rd, Rd, 0); // NOT is NOR with 0
+ break;
+
+ case opCMP:
+ // Either operand of a CMP instr could get overwritten by a subsequent
+ // conditional instruction, which is ok, _UNLESS_ there is a _second_
+ // conditional instruction. Under MIPS, this requires doing the comparison
+ // again (SLT), and the original operands must be available. (and this
+ // pattern of multiple conditional instructions from same CMP _is_ used
+ // in GGL-Assembler)
+ //
+ // For now, if a conditional instr overwrites the operands, we will
+ // move them to dedicated temp regs. This is ugly, and inefficient,
+ // and should be optimized.
+ //
+ // WARNING: making an _Assumption_ that CMP operand regs will NOT be
+ // trashed by intervening NON-conditional instructions. In the general
+ // case this is legal, but it is NOT currently done in GGL-Assembler.
+
+ cond.type = CMP_COND;
+ cond.r1 = Rn;
+ if (dataProcAdrModes(Op2, src, false, R_cmp2) == SRC_REG) {
+ cond.r2 = src;
+ } else { // adr mode was SRC_IMM
+ mMips->ORI(R_cmp2, R_zero, src);
+ cond.r2 = R_cmp2;
+ }
+
+ break;
+
+
+ case opTST:
+ case opTEQ:
+ case opCMN:
+ case opADC:
+ case opSBC:
+ case opRSC:
+ mMips->UNIMPL(); // currently unused in GGL Assembler code
+ break;
+ }
+
+ if (cc != AL) {
+ mMips->label(cond.label[cond.labelnum]);
+ }
+ if (s && opcode != opCMP) {
+ cond.type = SBIT_COND;
+ cond.r1 = Rd;
+ }
+}
+
+
+
+#if 0
+#pragma mark -
+#pragma mark Multiply...
+#endif
+
+// multiply, accumulate
+void ArmToMipsAssembler::MLA(int cc, int s,
+ int Rd, int Rm, int Rs, int Rn) {
+
+ mArmPC[mInum++] = pc(); // save starting PC for this instr
+
+ mMips->MUL(R_at, Rm, Rs);
+ mMips->ADDU(Rd, R_at, Rn);
+ if (s) {
+ cond.type = SBIT_COND;
+ cond.r1 = Rd;
+ }
+}
+
+void ArmToMipsAssembler::MUL(int cc, int s,
+ int Rd, int Rm, int Rs) {
+ mArmPC[mInum++] = pc();
+ mMips->MUL(Rd, Rm, Rs);
+ if (s) {
+ cond.type = SBIT_COND;
+ cond.r1 = Rd;
+ }
+}
+
+void ArmToMipsAssembler::UMULL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs) {
+ mArmPC[mInum++] = pc();
+ mMips->MULT(Rm, Rs);
+ mMips->MFHI(RdHi);
+ mMips->MFLO(RdLo);
+ if (s) {
+ cond.type = SBIT_COND;
+ cond.r1 = RdHi; // BUG...
+ LOG_ALWAYS_FATAL("Condition on UMULL must be on 64-bit result\n");
+ }
+}
+
+void ArmToMipsAssembler::UMUAL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs) {
+ LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi,
+ "UMUAL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs);
+ // *mPC++ = (cc<<28) | (1<<23) | (1<<21) | (s<<20) |
+ // (RdHi<<16) | (RdLo<<12) | (Rs<<8) | 0x90 | Rm;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+ if (s) {
+ cond.type = SBIT_COND;
+ cond.r1 = RdHi; // BUG...
+ LOG_ALWAYS_FATAL("Condition on UMULL must be on 64-bit result\n");
+ }
+}
+
+void ArmToMipsAssembler::SMULL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs) {
+ LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi,
+ "SMULL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs);
+ // *mPC++ = (cc<<28) | (1<<23) | (1<<22) | (s<<20) |
+ // (RdHi<<16) | (RdLo<<12) | (Rs<<8) | 0x90 | Rm;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+ if (s) {
+ cond.type = SBIT_COND;
+ cond.r1 = RdHi; // BUG...
+ LOG_ALWAYS_FATAL("Condition on SMULL must be on 64-bit result\n");
+ }
+}
+void ArmToMipsAssembler::SMUAL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs) {
+ LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi,
+ "SMUAL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs);
+ // *mPC++ = (cc<<28) | (1<<23) | (1<<22) | (1<<21) | (s<<20) |
+ // (RdHi<<16) | (RdLo<<12) | (Rs<<8) | 0x90 | Rm;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+ if (s) {
+ cond.type = SBIT_COND;
+ cond.r1 = RdHi; // BUG...
+ LOG_ALWAYS_FATAL("Condition on SMUAL must be on 64-bit result\n");
+ }
+}
+
+
+
+#if 0
+#pragma mark -
+#pragma mark Branches...
+#endif
+
+// branches...
+
+void ArmToMipsAssembler::B(int cc, const char* label)
+{
+ mArmPC[mInum++] = pc();
+ if (cond.type == SBIT_COND) { cond.r2 = R_zero; }
+
+ switch(cc) {
+ case EQ: mMips->BEQ(cond.r1, cond.r2, label); break;
+ case NE: mMips->BNE(cond.r1, cond.r2, label); break;
+ case HS: mMips->BGEU(cond.r1, cond.r2, label); break;
+ case LO: mMips->BLTU(cond.r1, cond.r2, label); break;
+ case MI: mMips->BLT(cond.r1, cond.r2, label); break;
+ case PL: mMips->BGE(cond.r1, cond.r2, label); break;
+
+ case HI: mMips->BGTU(cond.r1, cond.r2, label); break;
+ case LS: mMips->BLEU(cond.r1, cond.r2, label); break;
+ case GE: mMips->BGE(cond.r1, cond.r2, label); break;
+ case LT: mMips->BLT(cond.r1, cond.r2, label); break;
+ case GT: mMips->BGT(cond.r1, cond.r2, label); break;
+ case LE: mMips->BLE(cond.r1, cond.r2, label); break;
+ case AL: mMips->B(label); break;
+ case NV: /* B Never - no instruction */ break;
+
+ case VS:
+ case VC:
+ default:
+ LOG_ALWAYS_FATAL("Unsupported cc: %02x\n", cc);
+ break;
+ }
+}
+
+void ArmToMipsAssembler::BL(int cc, const char* label)
+{
+ LOG_ALWAYS_FATAL("branch-and-link not supported yet\n");
+ mArmPC[mInum++] = pc();
+}
+
+// no use for Branches with integer PC, but they're in the Interface class ....
+void ArmToMipsAssembler::B(int cc, uint32_t* to_pc)
+{
+ LOG_ALWAYS_FATAL("branch to absolute PC not supported, use Label\n");
+ mArmPC[mInum++] = pc();
+}
+
+void ArmToMipsAssembler::BL(int cc, uint32_t* to_pc)
+{
+ LOG_ALWAYS_FATAL("branch to absolute PC not supported, use Label\n");
+ mArmPC[mInum++] = pc();
+}
+
+void ArmToMipsAssembler::BX(int cc, int Rn)
+{
+ LOG_ALWAYS_FATAL("branch to absolute PC not supported, use Label\n");
+ mArmPC[mInum++] = pc();
+}
+
+
+
+#if 0
+#pragma mark -
+#pragma mark Data Transfer...
+#endif
+
+// data transfer...
+void ArmToMipsAssembler::LDR(int cc, int Rd, int Rn, uint32_t offset)
+{
+ mArmPC[mInum++] = pc();
+ // work-around for ARM default address mode of immed12_pre(0)
+ if (offset > AMODE_UNSUPPORTED) offset = 0;
+ switch (offset) {
+ case 0:
+ amode.value = 0;
+ amode.writeback = 0;
+ // fall thru to next case ....
+ case AMODE_IMM_12_PRE:
+ if (Rn == ARMAssemblerInterface::SP) {
+ Rn = R_sp; // convert LDR via Arm SP to LW via Mips SP
+ }
+ mMips->LW(Rd, Rn, amode.value);
+ if (amode.writeback) { // OPTIONAL writeback on pre-index mode
+ mMips->ADDIU(Rn, Rn, amode.value);
+ }
+ break;
+ case AMODE_IMM_12_POST:
+ if (Rn == ARMAssemblerInterface::SP) {
+ Rn = R_sp; // convert STR thru Arm SP to STR thru Mips SP
+ }
+ mMips->LW(Rd, Rn, 0);
+ mMips->ADDIU(Rn, Rn, amode.value);
+ break;
+ case AMODE_REG_SCALE_PRE:
+ // we only support simple base + index, no advanced modes for this one yet
+ mMips->ADDU(R_at, Rn, amode.reg);
+ mMips->LW(Rd, R_at, 0);
+ break;
+ }
+}
+
+void ArmToMipsAssembler::LDRB(int cc, int Rd, int Rn, uint32_t offset)
+{
+ mArmPC[mInum++] = pc();
+ // work-around for ARM default address mode of immed12_pre(0)
+ if (offset > AMODE_UNSUPPORTED) offset = 0;
+ switch (offset) {
+ case 0:
+ amode.value = 0;
+ amode.writeback = 0;
+ // fall thru to next case ....
+ case AMODE_IMM_12_PRE:
+ mMips->LBU(Rd, Rn, amode.value);
+ if (amode.writeback) { // OPTIONAL writeback on pre-index mode
+ mMips->ADDIU(Rn, Rn, amode.value);
+ }
+ break;
+ case AMODE_IMM_12_POST:
+ mMips->LBU(Rd, Rn, 0);
+ mMips->ADDIU(Rn, Rn, amode.value);
+ break;
+ case AMODE_REG_SCALE_PRE:
+ // we only support simple base + index, no advanced modes for this one yet
+ mMips->ADDU(R_at, Rn, amode.reg);
+ mMips->LBU(Rd, R_at, 0);
+ break;
+ }
+
+}
+
+void ArmToMipsAssembler::STR(int cc, int Rd, int Rn, uint32_t offset)
+{
+ mArmPC[mInum++] = pc();
+ // work-around for ARM default address mode of immed12_pre(0)
+ if (offset > AMODE_UNSUPPORTED) offset = 0;
+ switch (offset) {
+ case 0:
+ amode.value = 0;
+ amode.writeback = 0;
+ // fall thru to next case ....
+ case AMODE_IMM_12_PRE:
+ if (Rn == ARMAssemblerInterface::SP) {
+ Rn = R_sp; // convert STR thru Arm SP to SW thru Mips SP
+ }
+ if (amode.writeback) { // OPTIONAL writeback on pre-index mode
+ // If we will writeback, then update the index reg, then store.
+ // This correctly handles stack-push case.
+ mMips->ADDIU(Rn, Rn, amode.value);
+ mMips->SW(Rd, Rn, 0);
+ } else {
+ // No writeback so store offset by value
+ mMips->SW(Rd, Rn, amode.value);
+ }
+ break;
+ case AMODE_IMM_12_POST:
+ mMips->SW(Rd, Rn, 0);
+ mMips->ADDIU(Rn, Rn, amode.value); // post index always writes back
+ break;
+ case AMODE_REG_SCALE_PRE:
+ // we only support simple base + index, no advanced modes for this one yet
+ mMips->ADDU(R_at, Rn, amode.reg);
+ mMips->SW(Rd, R_at, 0);
+ break;
+ }
+}
+
+void ArmToMipsAssembler::STRB(int cc, int Rd, int Rn, uint32_t offset)
+{
+ mArmPC[mInum++] = pc();
+ // work-around for ARM default address mode of immed12_pre(0)
+ if (offset > AMODE_UNSUPPORTED) offset = 0;
+ switch (offset) {
+ case 0:
+ amode.value = 0;
+ amode.writeback = 0;
+ // fall thru to next case ....
+ case AMODE_IMM_12_PRE:
+ mMips->SB(Rd, Rn, amode.value);
+ if (amode.writeback) { // OPTIONAL writeback on pre-index mode
+ mMips->ADDIU(Rn, Rn, amode.value);
+ }
+ break;
+ case AMODE_IMM_12_POST:
+ mMips->SB(Rd, Rn, 0);
+ mMips->ADDIU(Rn, Rn, amode.value);
+ break;
+ case AMODE_REG_SCALE_PRE:
+ // we only support simple base + index, no advanced modes for this one yet
+ mMips->ADDU(R_at, Rn, amode.reg);
+ mMips->SB(Rd, R_at, 0);
+ break;
+ }
+}
+
+void ArmToMipsAssembler::LDRH(int cc, int Rd, int Rn, uint32_t offset)
+{
+ mArmPC[mInum++] = pc();
+ // work-around for ARM default address mode of immed8_pre(0)
+ if (offset > AMODE_UNSUPPORTED) offset = 0;
+ switch (offset) {
+ case 0:
+ amode.value = 0;
+ // fall thru to next case ....
+ case AMODE_IMM_8_PRE: // no support yet for writeback
+ mMips->LHU(Rd, Rn, amode.value);
+ break;
+ case AMODE_IMM_8_POST:
+ mMips->LHU(Rd, Rn, 0);
+ mMips->ADDIU(Rn, Rn, amode.value);
+ break;
+ case AMODE_REG_PRE:
+ // we only support simple base +/- index
+ if (amode.reg >= 0) {
+ mMips->ADDU(R_at, Rn, amode.reg);
+ } else {
+ mMips->SUBU(R_at, Rn, abs(amode.reg));
+ }
+ mMips->LHU(Rd, R_at, 0);
+ break;
+ }
+}
+
+void ArmToMipsAssembler::LDRSB(int cc, int Rd, int Rn, uint32_t offset)
+{
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+void ArmToMipsAssembler::LDRSH(int cc, int Rd, int Rn, uint32_t offset)
+{
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+void ArmToMipsAssembler::STRH(int cc, int Rd, int Rn, uint32_t offset)
+{
+ mArmPC[mInum++] = pc();
+ // work-around for ARM default address mode of immed8_pre(0)
+ if (offset > AMODE_UNSUPPORTED) offset = 0;
+ switch (offset) {
+ case 0:
+ amode.value = 0;
+ // fall thru to next case ....
+ case AMODE_IMM_8_PRE: // no support yet for writeback
+ mMips->SH(Rd, Rn, amode.value);
+ break;
+ case AMODE_IMM_8_POST:
+ mMips->SH(Rd, Rn, 0);
+ mMips->ADDIU(Rn, Rn, amode.value);
+ break;
+ case AMODE_REG_PRE:
+ // we only support simple base +/- index
+ if (amode.reg >= 0) {
+ mMips->ADDU(R_at, Rn, amode.reg);
+ } else {
+ mMips->SUBU(R_at, Rn, abs(amode.reg));
+ }
+ mMips->SH(Rd, R_at, 0);
+ break;
+ }
+}
+
+
+
+#if 0
+#pragma mark -
+#pragma mark Block Data Transfer...
+#endif
+
+// block data transfer...
+void ArmToMipsAssembler::LDM(int cc, int dir,
+ int Rn, int W, uint32_t reg_list)
+{ // ED FD EA FA IB IA DB DA
+ // const uint8_t P[8] = { 1, 0, 1, 0, 1, 0, 1, 0 };
+ // const uint8_t U[8] = { 1, 1, 0, 0, 1, 1, 0, 0 };
+ // *mPC++ = (cc<<28) | (4<<25) | (uint32_t(P[dir])<<24) |
+ // (uint32_t(U[dir])<<23) | (1<<20) | (W<<21) | (Rn<<16) | reg_list;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+void ArmToMipsAssembler::STM(int cc, int dir,
+ int Rn, int W, uint32_t reg_list)
+{ // FA EA FD ED IB IA DB DA
+ // const uint8_t P[8] = { 0, 1, 0, 1, 1, 0, 1, 0 };
+ // const uint8_t U[8] = { 0, 0, 1, 1, 1, 1, 0, 0 };
+ // *mPC++ = (cc<<28) | (4<<25) | (uint32_t(P[dir])<<24) |
+ // (uint32_t(U[dir])<<23) | (0<<20) | (W<<21) | (Rn<<16) | reg_list;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+
+
+#if 0
+#pragma mark -
+#pragma mark Special...
+#endif
+
+// special...
+void ArmToMipsAssembler::SWP(int cc, int Rn, int Rd, int Rm) {
+ // *mPC++ = (cc<<28) | (2<<23) | (Rn<<16) | (Rd << 12) | 0x90 | Rm;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+void ArmToMipsAssembler::SWPB(int cc, int Rn, int Rd, int Rm) {
+ // *mPC++ = (cc<<28) | (2<<23) | (1<<22) | (Rn<<16) | (Rd << 12) | 0x90 | Rm;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+void ArmToMipsAssembler::SWI(int cc, uint32_t comment) {
+ // *mPC++ = (cc<<28) | (0xF<<24) | comment;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+
+#if 0
+#pragma mark -
+#pragma mark DSP instructions...
+#endif
+
+// DSP instructions...
+void ArmToMipsAssembler::PLD(int Rn, uint32_t offset) {
+ LOG_ALWAYS_FATAL_IF(!((offset&(1<<24)) && !(offset&(1<<21))),
+ "PLD only P=1, W=0");
+ // *mPC++ = 0xF550F000 | (Rn<<16) | offset;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+void ArmToMipsAssembler::CLZ(int cc, int Rd, int Rm)
+{
+ mArmPC[mInum++] = pc();
+ mMips->CLZ(Rd, Rm);
+}
+
+void ArmToMipsAssembler::QADD(int cc, int Rd, int Rm, int Rn)
+{
+ // *mPC++ = (cc<<28) | 0x1000050 | (Rn<<16) | (Rd<<12) | Rm;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+void ArmToMipsAssembler::QDADD(int cc, int Rd, int Rm, int Rn)
+{
+ // *mPC++ = (cc<<28) | 0x1400050 | (Rn<<16) | (Rd<<12) | Rm;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+void ArmToMipsAssembler::QSUB(int cc, int Rd, int Rm, int Rn)
+{
+ // *mPC++ = (cc<<28) | 0x1200050 | (Rn<<16) | (Rd<<12) | Rm;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+void ArmToMipsAssembler::QDSUB(int cc, int Rd, int Rm, int Rn)
+{
+ // *mPC++ = (cc<<28) | 0x1600050 | (Rn<<16) | (Rd<<12) | Rm;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+// 16 x 16 signed multiply (like SMLAxx without the accumulate)
+void ArmToMipsAssembler::SMUL(int cc, int xy,
+ int Rd, int Rm, int Rs)
+{
+ mArmPC[mInum++] = pc();
+
+ // the 16 bits may be in the top or bottom half of 32-bit source reg,
+ // as defined by the codes BB, BT, TB, TT (compressed param xy)
+ // where x corresponds to Rm and y to Rs
+
+ // select half-reg for Rm
+ if (xy & xyTB) {
+ // use top 16-bits
+ mMips->SRA(R_at, Rm, 16);
+ } else {
+ // use bottom 16, but sign-extend to 32
+ if (mips32r2) {
+ mMips->SEH(R_at, Rm);
+ } else {
+ mMips->SLL(R_at, Rm, 16);
+ mMips->SRA(R_at, R_at, 16);
+ }
+ }
+ // select half-reg for Rs
+ if (xy & xyBT) {
+ // use top 16-bits
+ mMips->SRA(R_at2, Rs, 16);
+ } else {
+ // use bottom 16, but sign-extend to 32
+ if (mips32r2) {
+ mMips->SEH(R_at2, Rs);
+ } else {
+ mMips->SLL(R_at2, Rs, 16);
+ mMips->SRA(R_at2, R_at2, 16);
+ }
+ }
+ mMips->MUL(Rd, R_at, R_at2);
+}
+
+// signed 32b x 16b multiple, save top 32-bits of 48-bit result
+void ArmToMipsAssembler::SMULW(int cc, int y,
+ int Rd, int Rm, int Rs)
+{
+ mArmPC[mInum++] = pc();
+
+ // the selector yT or yB refers to reg Rs
+ if (y & yT) {
+ // zero the bottom 16-bits, with 2 shifts, it can affect result
+ mMips->SRL(R_at, Rs, 16);
+ mMips->SLL(R_at, R_at, 16);
+
+ } else {
+ // move low 16-bit half, to high half
+ mMips->SLL(R_at, Rs, 16);
+ }
+ mMips->MULT(Rm, R_at);
+ mMips->MFHI(Rd);
+}
+
+// 16 x 16 signed multiply, accumulate: Rd = Rm{16} * Rs{16} + Rn
+void ArmToMipsAssembler::SMLA(int cc, int xy,
+ int Rd, int Rm, int Rs, int Rn)
+{
+ mArmPC[mInum++] = pc();
+
+ // the 16 bits may be in the top or bottom half of 32-bit source reg,
+ // as defined by the codes BB, BT, TB, TT (compressed param xy)
+ // where x corresponds to Rm and y to Rs
+
+ // select half-reg for Rm
+ if (xy & xyTB) {
+ // use top 16-bits
+ mMips->SRA(R_at, Rm, 16);
+ } else {
+ // use bottom 16, but sign-extend to 32
+ if (mips32r2) {
+ mMips->SEH(R_at, Rm);
+ } else {
+ mMips->SLL(R_at, Rm, 16);
+ mMips->SRA(R_at, R_at, 16);
+ }
+ }
+ // select half-reg for Rs
+ if (xy & xyBT) {
+ // use top 16-bits
+ mMips->SRA(R_at2, Rs, 16);
+ } else {
+ // use bottom 16, but sign-extend to 32
+ if (mips32r2) {
+ mMips->SEH(R_at2, Rs);
+ } else {
+ mMips->SLL(R_at2, Rs, 16);
+ mMips->SRA(R_at2, R_at2, 16);
+ }
+ }
+
+ mMips->MUL(R_at, R_at, R_at2);
+ mMips->ADDU(Rd, R_at, Rn);
+}
+
+void ArmToMipsAssembler::SMLAL(int cc, int xy,
+ int RdHi, int RdLo, int Rs, int Rm)
+{
+ // *mPC++ = (cc<<28) | 0x1400080 | (RdHi<<16) | (RdLo<<12) | (Rs<<8) | (xy<<4) | Rm;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+void ArmToMipsAssembler::SMLAW(int cc, int y,
+ int Rd, int Rm, int Rs, int Rn)
+{
+ // *mPC++ = (cc<<28) | 0x1200080 | (Rd<<16) | (Rn<<12) | (Rs<<8) | (y<<4) | Rm;
+ mArmPC[mInum++] = pc();
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+// used by ARMv6 version of GGLAssembler::filter32
+void ArmToMipsAssembler::UXTB16(int cc, int Rd, int Rm, int rotate)
+{
+ mArmPC[mInum++] = pc();
+
+ //Rd[31:16] := ZeroExtend((Rm ROR (8 * sh))[23:16]),
+ //Rd[15:0] := ZeroExtend((Rm ROR (8 * sh))[7:0]). sh 0-3.
+
+ mMips->ROTR(Rm, Rm, rotate * 8);
+ mMips->AND(Rd, Rm, 0x00FF00FF);
+}
+
+void ArmToMipsAssembler::UBFX(int cc, int Rd, int Rn, int lsb, int width)
+{
+ /* Placeholder for UBFX */
+ mArmPC[mInum++] = pc();
+
+ mMips->NOP2();
+ NOT_IMPLEMENTED();
+}
+
+
+
+
+
+#if 0
+#pragma mark -
+#pragma mark MIPS Assembler...
+#endif
+
+
+//**************************************************************************
+//**************************************************************************
+//**************************************************************************
+
+
+/* mips assembler
+** this is a subset of mips32r2, targeted specifically at ARM instruction
+** replacement in the pixelflinger/codeflinger code.
+**
+** To that end, there is no need for floating point, or priviledged
+** instructions. This all runs in user space, no float.
+**
+** The syntax makes no attempt to be as complete as the assember, with
+** synthetic instructions, and automatic recognition of immedate operands
+** (use the immediate form of the instruction), etc.
+**
+** We start with mips32r1, and may add r2 and dsp extensions if cpu
+** supports. Decision will be made at compile time, based on gcc
+** options. (makes sense since android will be built for a a specific
+** device)
+*/
+
+MIPSAssembler::MIPSAssembler(const sp<Assembly>& assembly, ArmToMipsAssembler *parent)
+ : mParent(parent),
+ mAssembly(assembly)
+{
+ mBase = mPC = (uint32_t *)assembly->base();
+ mDuration = ggl_system_time();
+}
+
+MIPSAssembler::~MIPSAssembler()
+{
+}
+
+
+uint32_t* MIPSAssembler::pc() const
+{
+ return mPC;
+}
+
+uint32_t* MIPSAssembler::base() const
+{
+ return mBase;
+}
+
+void MIPSAssembler::reset()
+{
+ mBase = mPC = (uint32_t *)mAssembly->base();
+ mBranchTargets.clear();
+ mLabels.clear();
+ mLabelsInverseMapping.clear();
+ mComments.clear();
+}
+
+
+// convert tabs to spaces, and remove any newline
+// works with strings of limited size (makes a temp copy)
+#define TABSTOP 8
+void MIPSAssembler::string_detab(char *s)
+{
+ char *os = s;
+ char temp[100];
+ char *t = temp;
+ int len = 99;
+ int i = TABSTOP;
+
+ while (*s && len-- > 0) {
+ if (*s == '\n') { s++; continue; }
+ if (*s == '\t') {
+ s++;
+ for ( ; i>0; i--) {*t++ = ' '; len--; }
+ } else {
+ *t++ = *s++;
+ }
+ if (i <= 0) i = TABSTOP;
+ i--;
+ }
+ *t = '\0';
+ strcpy(os, temp);
+}
+
+void MIPSAssembler::string_pad(char *s, int padded_len)
+{
+ int len = strlen(s);
+ s += len;
+ for (int i = padded_len - len; i > 0; --i) {
+ *s++ = ' ';
+ }
+ *s = '\0';
+}
+
+// ----------------------------------------------------------------------------
+
+void MIPSAssembler::disassemble(const char* name)
+{
+ char di_buf[140];
+
+ if (name) {
+ ALOGW("%s:\n", name);
+ }
+
+ bool arm_disasm_fmt = (mParent->mArmDisassemblyBuffer == NULL) ? false : true;
+
+ typedef char dstr[40];
+ dstr *lines = (dstr *)mParent->mArmDisassemblyBuffer;
+
+ if (mParent->mArmDisassemblyBuffer != NULL) {
+ for (int i=0; i<mParent->mArmInstrCount; ++i) {
+ string_detab(lines[i]);
+ }
+ }
+
+ // iArm is an index to Arm instructions 1...n for this assembly sequence
+ // mArmPC[iArm] holds the value of the Mips-PC for the first MIPS
+ // instruction corresponding to that Arm instruction number
+
+ int iArm = 0;
+ size_t count = pc()-base();
+ uint32_t* mipsPC = base();
+ while (count--) {
+ ssize_t label = mLabelsInverseMapping.indexOfKey(mipsPC);
+ if (label >= 0) {
+ ALOGW("%s:\n", mLabelsInverseMapping.valueAt(label));
+ }
+ ssize_t comment = mComments.indexOfKey(mipsPC);
+ if (comment >= 0) {
+ ALOGW("; %s\n", mComments.valueAt(comment));
+ }
+ // ALOGW("%08x: %08x ", int(i), int(i[0]));
+ ::mips_disassem(mipsPC, di_buf, arm_disasm_fmt);
+ string_detab(di_buf);
+ string_pad(di_buf, 30);
+ ALOGW("%08x: %08x %s", uint32_t(mipsPC), uint32_t(*mipsPC), di_buf);
+ mipsPC++;
+ }
+}
+
+void MIPSAssembler::comment(const char* string)
+{
+ mComments.add(pc(), string);
+}
+
+void MIPSAssembler::label(const char* theLabel)
+{
+ mLabels.add(theLabel, pc());
+ mLabelsInverseMapping.add(pc(), theLabel);
+}
+
+
+void MIPSAssembler::prolog()
+{
+ // empty - done in ArmToMipsAssembler
+}
+
+void MIPSAssembler::epilog(uint32_t touched)
+{
+ // empty - done in ArmToMipsAssembler
+}
+
+int MIPSAssembler::generate(const char* name)
+{
+ // fixup all the branches
+ size_t count = mBranchTargets.size();
+ while (count--) {
+ const branch_target_t& bt = mBranchTargets[count];
+ uint32_t* target_pc = mLabels.valueFor(bt.label);
+ LOG_ALWAYS_FATAL_IF(!target_pc,
+ "error resolving branch targets, target_pc is null");
+ int32_t offset = int32_t(target_pc - (bt.pc+1));
+ *bt.pc |= offset & 0x00FFFF;
+ }
+
+ mAssembly->resize( int(pc()-base())*4 );
+
+ // the instruction & data caches are flushed by CodeCache
+ const int64_t duration = ggl_system_time() - mDuration;
+ const char * const format = "generated %s (%d ins) at [%p:%p] in %lld ns\n";
+ ALOGI(format, name, int(pc()-base()), base(), pc(), duration);
+
+#if defined(WITH_LIB_HARDWARE)
+ if (__builtin_expect(mQemuTracing, 0)) {
+ int err = qemu_add_mapping(int(base()), name);
+ mQemuTracing = (err >= 0);
+ }
+#endif
+
+ char value[PROPERTY_VALUE_MAX];
+ value[0] = '\0';
+
+ property_get("debug.pf.disasm", value, "0");
+
+ if (atoi(value) != 0) {
+ disassemble(name);
+ }
+
+ return NO_ERROR;
+}
+
+uint32_t* MIPSAssembler::pcForLabel(const char* label)
+{
+ return mLabels.valueFor(label);
+}
+
+
+
+#if 0
+#pragma mark -
+#pragma mark Arithmetic...
+#endif
+
+void MIPSAssembler::ADDU(int Rd, int Rs, int Rt)
+{
+ *mPC++ = (spec_op<<OP_SHF) | (addu_fn<<FUNC_SHF)
+ | (Rs<<RS_SHF) | (Rt<<RT_SHF) | (Rd<<RD_SHF);
+}
+
+// MD00086 pdf says this is: ADDIU rt, rs, imm -- they do not use Rd
+void MIPSAssembler::ADDIU(int Rt, int Rs, int16_t imm)
+{
+ *mPC++ = (addiu_op<<OP_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF) | (imm & MSK_16);
+}
+
+
+void MIPSAssembler::SUBU(int Rd, int Rs, int Rt)
+{
+ *mPC++ = (spec_op<<OP_SHF) | (subu_fn<<FUNC_SHF) |
+ (Rs<<RS_SHF) | (Rt<<RT_SHF) | (Rd<<RD_SHF) ;
+}
+
+
+void MIPSAssembler::SUBIU(int Rt, int Rs, int16_t imm) // really addiu(d, s, -j)
+{
+ *mPC++ = (addiu_op<<OP_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF) | ((-imm) & MSK_16);
+}
+
+
+void MIPSAssembler::NEGU(int Rd, int Rs) // really subu(d, zero, s)
+{
+ MIPSAssembler::SUBU(Rd, 0, Rs);
+}
+
+void MIPSAssembler::MUL(int Rd, int Rs, int Rt)
+{
+ *mPC++ = (spec2_op<<OP_SHF) | (mul_fn<<FUNC_SHF) |
+ (Rs<<RS_SHF) | (Rt<<RT_SHF) | (Rd<<RD_SHF) ;
+}
+
+void MIPSAssembler::MULT(int Rs, int Rt) // dest is hi,lo
+{
+ *mPC++ = (spec_op<<OP_SHF) | (mult_fn<<FUNC_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF);
+}
+
+void MIPSAssembler::MULTU(int Rs, int Rt) // dest is hi,lo
+{
+ *mPC++ = (spec_op<<OP_SHF) | (multu_fn<<FUNC_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF);
+}
+
+void MIPSAssembler::MADD(int Rs, int Rt) // hi,lo = hi,lo + Rs * Rt
+{
+ *mPC++ = (spec2_op<<OP_SHF) | (madd_fn<<FUNC_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF);
+}
+
+void MIPSAssembler::MADDU(int Rs, int Rt) // hi,lo = hi,lo + Rs * Rt
+{
+ *mPC++ = (spec2_op<<OP_SHF) | (maddu_fn<<FUNC_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF);
+}
+
+
+void MIPSAssembler::MSUB(int Rs, int Rt) // hi,lo = hi,lo - Rs * Rt
+{
+ *mPC++ = (spec2_op<<OP_SHF) | (msub_fn<<FUNC_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF);
+}
+
+void MIPSAssembler::MSUBU(int Rs, int Rt) // hi,lo = hi,lo - Rs * Rt
+{
+ *mPC++ = (spec2_op<<OP_SHF) | (msubu_fn<<FUNC_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF);
+}
+
+
+void MIPSAssembler::SEB(int Rd, int Rt) // sign-extend byte (mips32r2)
+{
+ *mPC++ = (spec3_op<<OP_SHF) | (bshfl_fn<<FUNC_SHF) | (seb_fn << SA_SHF) |
+ (Rt<<RT_SHF) | (Rd<<RD_SHF);
+}
+
+void MIPSAssembler::SEH(int Rd, int Rt) // sign-extend half-word (mips32r2)
+{
+ *mPC++ = (spec3_op<<OP_SHF) | (bshfl_fn<<FUNC_SHF) | (seh_fn << SA_SHF) |
+ (Rt<<RT_SHF) | (Rd<<RD_SHF);
+}
+
+
+
+#if 0
+#pragma mark -
+#pragma mark Comparisons...
+#endif
+
+void MIPSAssembler::SLT(int Rd, int Rs, int Rt)
+{
+ *mPC++ = (spec_op<<OP_SHF) | (slt_fn<<FUNC_SHF) |
+ (Rd<<RD_SHF) | (Rs<<RS_SHF) | (Rt<<RT_SHF);
+}
+
+void MIPSAssembler::SLTI(int Rt, int Rs, int16_t imm)
+{
+ *mPC++ = (slti_op<<OP_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF) | (imm & MSK_16);
+}
+
+
+void MIPSAssembler::SLTU(int Rd, int Rs, int Rt)
+{
+ *mPC++ = (spec_op<<OP_SHF) | (sltu_fn<<FUNC_SHF) |
+ (Rd<<RD_SHF) | (Rs<<RS_SHF) | (Rt<<RT_SHF);
+}
+
+void MIPSAssembler::SLTIU(int Rt, int Rs, int16_t imm)
+{
+ *mPC++ = (sltiu_op<<OP_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF) | (imm & MSK_16);
+}
+
+
+
+#if 0
+#pragma mark -
+#pragma mark Logical...
+#endif
+
+void MIPSAssembler::AND(int Rd, int Rs, int Rt)
+{
+ *mPC++ = (spec_op<<OP_SHF) | (and_fn<<FUNC_SHF) |
+ (Rd<<RD_SHF) | (Rs<<RS_SHF) | (Rt<<RT_SHF);
+}
+
+void MIPSAssembler::ANDI(int Rt, int Rs, uint16_t imm) // todo: support larger immediate
+{
+ *mPC++ = (andi_op<<OP_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF) | (imm & MSK_16);
+}
+
+
+void MIPSAssembler::OR(int Rd, int Rs, int Rt)
+{
+ *mPC++ = (spec_op<<OP_SHF) | (or_fn<<FUNC_SHF) |
+ (Rd<<RD_SHF) | (Rs<<RS_SHF) | (Rt<<RT_SHF);
+}
+
+void MIPSAssembler::ORI(int Rt, int Rs, uint16_t imm)
+{
+ *mPC++ = (ori_op<<OP_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF) | (imm & MSK_16);
+}
+
+void MIPSAssembler::NOR(int Rd, int Rs, int Rt)
+{
+ *mPC++ = (spec_op<<OP_SHF) | (nor_fn<<FUNC_SHF) |
+ (Rd<<RD_SHF) | (Rs<<RS_SHF) | (Rt<<RT_SHF);
+}
+
+void MIPSAssembler::NOT(int Rd, int Rs)
+{
+ MIPSAssembler::NOR(Rd, Rs, 0); // NOT(d,s) = NOR(d,s,zero)
+}
+
+void MIPSAssembler::XOR(int Rd, int Rs, int Rt)
+{
+ *mPC++ = (spec_op<<OP_SHF) | (xor_fn<<FUNC_SHF) |
+ (Rd<<RD_SHF) | (Rs<<RS_SHF) | (Rt<<RT_SHF);
+}
+
+void MIPSAssembler::XORI(int Rt, int Rs, uint16_t imm) // todo: support larger immediate
+{
+ *mPC++ = (xori_op<<OP_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF) | (imm & MSK_16);
+}
+
+void MIPSAssembler::SLL(int Rd, int Rt, int shft)
+{
+ *mPC++ = (spec_op<<OP_SHF) | (sll_fn<<FUNC_SHF) |
+ (Rd<<RD_SHF) | (Rt<<RT_SHF) | (shft<<RE_SHF);
+}
+
+void MIPSAssembler::SLLV(int Rd, int Rt, int Rs)
+{
+ *mPC++ = (spec_op<<OP_SHF) | (sllv_fn<<FUNC_SHF) |
+ (Rd<<RD_SHF) | (Rs<<RS_SHF) | (Rt<<RT_SHF);
+}
+
+void MIPSAssembler::SRL(int Rd, int Rt, int shft)
+{
+ *mPC++ = (spec_op<<OP_SHF) | (srl_fn<<FUNC_SHF) |
+ (Rd<<RD_SHF) | (Rt<<RT_SHF) | (shft<<RE_SHF);
+}
+
+void MIPSAssembler::SRLV(int Rd, int Rt, int Rs)
+{
+ *mPC++ = (spec_op<<OP_SHF) | (srlv_fn<<FUNC_SHF) |
+ (Rd<<RD_SHF) | (Rs<<RS_SHF) | (Rt<<RT_SHF);
+}
+
+void MIPSAssembler::SRA(int Rd, int Rt, int shft)
+{
+ *mPC++ = (spec_op<<OP_SHF) | (sra_fn<<FUNC_SHF) |
+ (Rd<<RD_SHF) | (Rt<<RT_SHF) | (shft<<RE_SHF);
+}
+
+void MIPSAssembler::SRAV(int Rd, int Rt, int Rs)
+{
+ *mPC++ = (spec_op<<OP_SHF) | (srav_fn<<FUNC_SHF) |
+ (Rd<<RD_SHF) | (Rs<<RS_SHF) | (Rt<<RT_SHF);
+}
+
+void MIPSAssembler::ROTR(int Rd, int Rt, int shft) // mips32r2
+{
+ // note weird encoding (SRL + 1)
+ *mPC++ = (spec_op<<OP_SHF) | (srl_fn<<FUNC_SHF) |
+ (1<<RS_SHF) | (Rd<<RD_SHF) | (Rt<<RT_SHF) | (shft<<RE_SHF);
+}
+
+void MIPSAssembler::ROTRV(int Rd, int Rt, int Rs) // mips32r2
+{
+ // note weird encoding (SRLV + 1)
+ *mPC++ = (spec_op<<OP_SHF) | (srlv_fn<<FUNC_SHF) |
+ (Rd<<RD_SHF) | (Rs<<RS_SHF) | (Rt<<RT_SHF) | (1<<RE_SHF);
+}
+
+// uses at2 register (mapped to some appropriate mips reg)
+void MIPSAssembler::RORsyn(int Rd, int Rt, int Rs)
+{
+ // synthetic: d = t rotated by s
+ MIPSAssembler::NEGU(R_at2, Rs);
+ MIPSAssembler::SLLV(R_at2, Rt, R_at2);
+ MIPSAssembler::SRLV(Rd, Rt, Rs);
+ MIPSAssembler::OR(Rd, Rd, R_at2);
+}
+
+// immediate version - uses at2 register (mapped to some appropriate mips reg)
+void MIPSAssembler::RORIsyn(int Rd, int Rt, int rot)
+{
+ // synthetic: d = t rotated by immed rot
+ // d = s >> rot | s << (32-rot)
+ MIPSAssembler::SLL(R_at2, Rt, 32-rot);
+ MIPSAssembler::SRL(Rd, Rt, rot);
+ MIPSAssembler::OR(Rd, Rd, R_at2);
+}
+
+void MIPSAssembler::CLO(int Rd, int Rs)
+{
+ // Rt field must have same gpr # as Rd
+ *mPC++ = (spec2_op<<OP_SHF) | (clo_fn<<FUNC_SHF) |
+ (Rd<<RD_SHF) | (Rs<<RS_SHF) | (Rd<<RT_SHF);
+}
+
+void MIPSAssembler::CLZ(int Rd, int Rs)
+{
+ // Rt field must have same gpr # as Rd
+ *mPC++ = (spec2_op<<OP_SHF) | (clz_fn<<FUNC_SHF) |
+ (Rd<<RD_SHF) | (Rs<<RS_SHF) | (Rd<<RT_SHF);
+}
+
+void MIPSAssembler::WSBH(int Rd, int Rt) // mips32r2
+{
+ *mPC++ = (spec3_op<<OP_SHF) | (bshfl_fn<<FUNC_SHF) | (wsbh_fn << SA_SHF) |
+ (Rt<<RT_SHF) | (Rd<<RD_SHF);
+}
+
+
+
+#if 0
+#pragma mark -
+#pragma mark Load/store...
+#endif
+
+void MIPSAssembler::LW(int Rt, int Rbase, int16_t offset)
+{
+ *mPC++ = (lw_op<<OP_SHF) | (Rbase<<RS_SHF) | (Rt<<RT_SHF) | (offset & MSK_16);
+}
+
+void MIPSAssembler::SW(int Rt, int Rbase, int16_t offset)
+{
+ *mPC++ = (sw_op<<OP_SHF) | (Rbase<<RS_SHF) | (Rt<<RT_SHF) | (offset & MSK_16);
+}
+
+// lb is sign-extended
+void MIPSAssembler::LB(int Rt, int Rbase, int16_t offset)
+{
+ *mPC++ = (lb_op<<OP_SHF) | (Rbase<<RS_SHF) | (Rt<<RT_SHF) | (offset & MSK_16);
+}
+
+void MIPSAssembler::LBU(int Rt, int Rbase, int16_t offset)
+{
+ *mPC++ = (lbu_op<<OP_SHF) | (Rbase<<RS_SHF) | (Rt<<RT_SHF) | (offset & MSK_16);
+}
+
+void MIPSAssembler::SB(int Rt, int Rbase, int16_t offset)
+{
+ *mPC++ = (sb_op<<OP_SHF) | (Rbase<<RS_SHF) | (Rt<<RT_SHF) | (offset & MSK_16);
+}
+
+// lh is sign-extended
+void MIPSAssembler::LH(int Rt, int Rbase, int16_t offset)
+{
+ *mPC++ = (lh_op<<OP_SHF) | (Rbase<<RS_SHF) | (Rt<<RT_SHF) | (offset & MSK_16);
+}
+
+void MIPSAssembler::LHU(int Rt, int Rbase, int16_t offset)
+{
+ *mPC++ = (lhu_op<<OP_SHF) | (Rbase<<RS_SHF) | (Rt<<RT_SHF) | (offset & MSK_16);
+}
+
+void MIPSAssembler::SH(int Rt, int Rbase, int16_t offset)
+{
+ *mPC++ = (sh_op<<OP_SHF) | (Rbase<<RS_SHF) | (Rt<<RT_SHF) | (offset & MSK_16);
+}
+
+void MIPSAssembler::LUI(int Rt, int16_t offset)
+{
+ *mPC++ = (lui_op<<OP_SHF) | (Rt<<RT_SHF) | (offset & MSK_16);
+}
+
+
+
+#if 0
+#pragma mark -
+#pragma mark Register move...
+#endif
+
+void MIPSAssembler::MOVE(int Rd, int Rs)
+{
+ // encoded as "or rd, rs, zero"
+ *mPC++ = (spec_op<<OP_SHF) | (or_fn<<FUNC_SHF) |
+ (Rd<<RD_SHF) | (Rs<<RS_SHF) | (0<<RT_SHF);
+}
+
+void MIPSAssembler::MOVN(int Rd, int Rs, int Rt)
+{
+ *mPC++ = (spec_op<<OP_SHF) | (movn_fn<<FUNC_SHF) |
+ (Rd<<RD_SHF) | (Rs<<RS_SHF) | (Rt<<RT_SHF);
+}
+
+void MIPSAssembler::MOVZ(int Rd, int Rs, int Rt)
+{
+ *mPC++ = (spec_op<<OP_SHF) | (movz_fn<<FUNC_SHF) |
+ (Rd<<RD_SHF) | (Rs<<RS_SHF) | (Rt<<RT_SHF);
+}
+
+void MIPSAssembler::MFHI(int Rd)
+{
+ *mPC++ = (spec_op<<OP_SHF) | (mfhi_fn<<FUNC_SHF) | (Rd<<RD_SHF);
+}
+
+void MIPSAssembler::MFLO(int Rd)
+{
+ *mPC++ = (spec_op<<OP_SHF) | (mflo_fn<<FUNC_SHF) | (Rd<<RD_SHF);
+}
+
+void MIPSAssembler::MTHI(int Rs)
+{
+ *mPC++ = (spec_op<<OP_SHF) | (mthi_fn<<FUNC_SHF) | (Rs<<RS_SHF);
+}
+
+void MIPSAssembler::MTLO(int Rs)
+{
+ *mPC++ = (spec_op<<OP_SHF) | (mtlo_fn<<FUNC_SHF) | (Rs<<RS_SHF);
+}
+
+
+
+#if 0
+#pragma mark -
+#pragma mark Branch...
+#endif
+
+// temporarily forcing a NOP into branch-delay slot, just to be safe
+// todo: remove NOP, optimze use of delay slots
+void MIPSAssembler::B(const char* label)
+{
+ mBranchTargets.add(branch_target_t(label, mPC));
+
+ // encoded as BEQ zero, zero, offset
+ *mPC++ = (beq_op<<OP_SHF) | (0<<RT_SHF)
+ | (0<<RS_SHF) | 0; // offset filled in later
+
+ MIPSAssembler::NOP();
+}
+
+void MIPSAssembler::BEQ(int Rs, int Rt, const char* label)
+{
+ mBranchTargets.add(branch_target_t(label, mPC));
+ *mPC++ = (beq_op<<OP_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF) | 0;
+ MIPSAssembler::NOP();
+}
+
+void MIPSAssembler::BNE(int Rs, int Rt, const char* label)
+{
+ mBranchTargets.add(branch_target_t(label, mPC));
+ *mPC++ = (bne_op<<OP_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF) | 0;
+ MIPSAssembler::NOP();
+}
+
+void MIPSAssembler::BLEZ(int Rs, const char* label)
+{
+ mBranchTargets.add(branch_target_t(label, mPC));
+ *mPC++ = (blez_op<<OP_SHF) | (0<<RT_SHF) | (Rs<<RS_SHF) | 0;
+ MIPSAssembler::NOP();
+}
+
+void MIPSAssembler::BLTZ(int Rs, const char* label)
+{
+ mBranchTargets.add(branch_target_t(label, mPC));
+ *mPC++ = (regimm_op<<OP_SHF) | (bltz_fn<<RT_SHF) | (Rs<<RS_SHF) | 0;
+ MIPSAssembler::NOP();
+}
+
+void MIPSAssembler::BGTZ(int Rs, const char* label)
+{
+ mBranchTargets.add(branch_target_t(label, mPC));
+ *mPC++ = (bgtz_op<<OP_SHF) | (0<<RT_SHF) | (Rs<<RS_SHF) | 0;
+ MIPSAssembler::NOP();
+}
+
+
+void MIPSAssembler::BGEZ(int Rs, const char* label)
+{
+ mBranchTargets.add(branch_target_t(label, mPC));
+ *mPC++ = (regimm_op<<OP_SHF) | (bgez_fn<<RT_SHF) | (Rs<<RS_SHF) | 0;
+ MIPSAssembler::NOP();
+}
+
+void MIPSAssembler::JR(int Rs)
+{
+ *mPC++ = (spec_op<<OP_SHF) | (Rs<<RS_SHF) | (jr_fn << FUNC_SHF);
+ MIPSAssembler::NOP();
+}
+
+
+#if 0
+#pragma mark -
+#pragma mark Synthesized Branch...
+#endif
+
+// synthetic variants of branches (using slt & friends)
+void MIPSAssembler::BEQZ(int Rs, const char* label)
+{
+ BEQ(Rs, R_zero, label);
+}
+
+void MIPSAssembler::BNEZ(int Rs, const char* label)
+{
+ BNE(R_at, R_zero, label);
+}
+
+void MIPSAssembler::BGE(int Rs, int Rt, const char* label)
+{
+ SLT(R_at, Rs, Rt);
+ BEQ(R_at, R_zero, label);
+}
+
+void MIPSAssembler::BGEU(int Rs, int Rt, const char* label)
+{
+ SLTU(R_at, Rs, Rt);
+ BEQ(R_at, R_zero, label);
+}
+
+void MIPSAssembler::BGT(int Rs, int Rt, const char* label)
+{
+ SLT(R_at, Rt, Rs); // rev
+ BNE(R_at, R_zero, label);
+}
+
+void MIPSAssembler::BGTU(int Rs, int Rt, const char* label)
+{
+ SLTU(R_at, Rt, Rs); // rev
+ BNE(R_at, R_zero, label);
+}
+
+void MIPSAssembler::BLE(int Rs, int Rt, const char* label)
+{
+ SLT(R_at, Rt, Rs); // rev
+ BEQ(R_at, R_zero, label);
+}
+
+void MIPSAssembler::BLEU(int Rs, int Rt, const char* label)
+{
+ SLTU(R_at, Rt, Rs); // rev
+ BEQ(R_at, R_zero, label);
+}
+
+void MIPSAssembler::BLT(int Rs, int Rt, const char* label)
+{
+ SLT(R_at, Rs, Rt);
+ BNE(R_at, R_zero, label);
+}
+
+void MIPSAssembler::BLTU(int Rs, int Rt, const char* label)
+{
+ SLTU(R_at, Rs, Rt);
+ BNE(R_at, R_zero, label);
+}
+
+
+
+
+#if 0
+#pragma mark -
+#pragma mark Misc...
+#endif
+
+void MIPSAssembler::NOP(void)
+{
+ // encoded as "sll zero, zero, 0", which is all zero
+ *mPC++ = (spec_op<<OP_SHF) | (sll_fn<<FUNC_SHF);
+}
+
+// using this as special opcode for not-yet-implemented ARM instruction
+void MIPSAssembler::NOP2(void)
+{
+ // encoded as "sll zero, zero, 2", still a nop, but a unique code
+ *mPC++ = (spec_op<<OP_SHF) | (sll_fn<<FUNC_SHF) | (2 << RE_SHF);
+}
+
+// using this as special opcode for purposefully NOT implemented ARM instruction
+void MIPSAssembler::UNIMPL(void)
+{
+ // encoded as "sll zero, zero, 3", still a nop, but a unique code
+ *mPC++ = (spec_op<<OP_SHF) | (sll_fn<<FUNC_SHF) | (3 << RE_SHF);
+}
+
+
+}; // namespace android:
+
+
diff --git a/libpixelflinger/codeflinger/MIPSAssembler.h b/libpixelflinger/codeflinger/MIPSAssembler.h
new file mode 100644
index 0000000..430ab06
--- /dev/null
+++ b/libpixelflinger/codeflinger/MIPSAssembler.h
@@ -0,0 +1,555 @@
+/* libs/pixelflinger/codeflinger/MIPSAssembler.h
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef ANDROID_MIPSASSEMBLER_H
+#define ANDROID_MIPSASSEMBLER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "tinyutils/KeyedVector.h"
+#include "tinyutils/Vector.h"
+#include "tinyutils/smartpointer.h"
+
+#include "ARMAssemblerInterface.h"
+#include "CodeCache.h"
+
+namespace android {
+
+class MIPSAssembler; // forward reference
+
+// this class mimics ARMAssembler interface
+// intent is to translate each ARM instruction to 1 or more MIPS instr
+// implementation calls MIPSAssembler class to generate mips code
+class ArmToMipsAssembler : public ARMAssemblerInterface
+{
+public:
+ ArmToMipsAssembler(const sp<Assembly>& assembly,
+ char *abuf = 0, int linesz = 0, int instr_count = 0);
+ virtual ~ArmToMipsAssembler();
+
+ uint32_t* base() const;
+ uint32_t* pc() const;
+ void disassemble(const char* name);
+
+ virtual void reset();
+
+ virtual int generate(const char* name);
+ virtual int getCodegenArch();
+
+ virtual void prolog();
+ virtual void epilog(uint32_t touched);
+ virtual void comment(const char* string);
+
+
+ // -----------------------------------------------------------------------
+ // shifters and addressing modes
+ // -----------------------------------------------------------------------
+
+ // shifters...
+ virtual bool isValidImmediate(uint32_t immed);
+ virtual int buildImmediate(uint32_t i, uint32_t& rot, uint32_t& imm);
+
+ virtual uint32_t imm(uint32_t immediate);
+ virtual uint32_t reg_imm(int Rm, int type, uint32_t shift);
+ virtual uint32_t reg_rrx(int Rm);
+ virtual uint32_t reg_reg(int Rm, int type, int Rs);
+
+ // addressing modes...
+ // LDR(B)/STR(B)/PLD
+ // (immediate and Rm can be negative, which indicates U=0)
+ virtual uint32_t immed12_pre(int32_t immed12, int W=0);
+ virtual uint32_t immed12_post(int32_t immed12);
+ virtual uint32_t reg_scale_pre(int Rm, int type=0, uint32_t shift=0, int W=0);
+ virtual uint32_t reg_scale_post(int Rm, int type=0, uint32_t shift=0);
+
+ // LDRH/LDRSB/LDRSH/STRH
+ // (immediate and Rm can be negative, which indicates U=0)
+ virtual uint32_t immed8_pre(int32_t immed8, int W=0);
+ virtual uint32_t immed8_post(int32_t immed8);
+ virtual uint32_t reg_pre(int Rm, int W=0);
+ virtual uint32_t reg_post(int Rm);
+
+
+
+
+ virtual void dataProcessing(int opcode, int cc, int s,
+ int Rd, int Rn,
+ uint32_t Op2);
+ virtual void MLA(int cc, int s,
+ int Rd, int Rm, int Rs, int Rn);
+ virtual void MUL(int cc, int s,
+ int Rd, int Rm, int Rs);
+ virtual void UMULL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs);
+ virtual void UMUAL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs);
+ virtual void SMULL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs);
+ virtual void SMUAL(int cc, int s,
+ int RdLo, int RdHi, int Rm, int Rs);
+
+ virtual void B(int cc, uint32_t* pc);
+ virtual void BL(int cc, uint32_t* pc);
+ virtual void BX(int cc, int Rn);
+ virtual void label(const char* theLabel);
+ virtual void B(int cc, const char* label);
+ virtual void BL(int cc, const char* label);
+
+ virtual uint32_t* pcForLabel(const char* label);
+
+ virtual void LDR (int cc, int Rd,
+ int Rn, uint32_t offset = 0);
+ virtual void LDRB(int cc, int Rd,
+ int Rn, uint32_t offset = 0);
+ virtual void STR (int cc, int Rd,
+ int Rn, uint32_t offset = 0);
+ virtual void STRB(int cc, int Rd,
+ int Rn, uint32_t offset = 0);
+ virtual void LDRH (int cc, int Rd,
+ int Rn, uint32_t offset = 0);
+ virtual void LDRSB(int cc, int Rd,
+ int Rn, uint32_t offset = 0);
+ virtual void LDRSH(int cc, int Rd,
+ int Rn, uint32_t offset = 0);
+ virtual void STRH (int cc, int Rd,
+ int Rn, uint32_t offset = 0);
+
+ virtual void LDM(int cc, int dir,
+ int Rn, int W, uint32_t reg_list);
+ virtual void STM(int cc, int dir,
+ int Rn, int W, uint32_t reg_list);
+
+ virtual void SWP(int cc, int Rn, int Rd, int Rm);
+ virtual void SWPB(int cc, int Rn, int Rd, int Rm);
+ virtual void SWI(int cc, uint32_t comment);
+
+ virtual void PLD(int Rn, uint32_t offset);
+ virtual void CLZ(int cc, int Rd, int Rm);
+ virtual void QADD(int cc, int Rd, int Rm, int Rn);
+ virtual void QDADD(int cc, int Rd, int Rm, int Rn);
+ virtual void QSUB(int cc, int Rd, int Rm, int Rn);
+ virtual void QDSUB(int cc, int Rd, int Rm, int Rn);
+ virtual void SMUL(int cc, int xy,
+ int Rd, int Rm, int Rs);
+ virtual void SMULW(int cc, int y,
+ int Rd, int Rm, int Rs);
+ virtual void SMLA(int cc, int xy,
+ int Rd, int Rm, int Rs, int Rn);
+ virtual void SMLAL(int cc, int xy,
+ int RdHi, int RdLo, int Rs, int Rm);
+ virtual void SMLAW(int cc, int y,
+ int Rd, int Rm, int Rs, int Rn);
+
+ // byte/half word extract...
+ virtual void UXTB16(int cc, int Rd, int Rm, int rotate);
+
+ // bit manipulation...
+ virtual void UBFX(int cc, int Rd, int Rn, int lsb, int width);
+
+ // this is some crap to share is MIPSAssembler class for debug
+ char * mArmDisassemblyBuffer;
+ int mArmLineLength;
+ int mArmInstrCount;
+
+ int mInum; // current arm instuction number (0..n)
+ uint32_t** mArmPC; // array: PC for 1st mips instr of
+ // each translated ARM instr
+
+
+private:
+ ArmToMipsAssembler(const ArmToMipsAssembler& rhs);
+ ArmToMipsAssembler& operator = (const ArmToMipsAssembler& rhs);
+
+ void init_conditional_labels(void);
+
+ void protectConditionalOperands(int Rd);
+
+ // reg__tmp set to MIPS AT, reg 1
+ int dataProcAdrModes(int op, int& source, bool sign = false, int reg_tmp = 1);
+
+ sp<Assembly> mAssembly;
+ MIPSAssembler* mMips;
+
+
+ enum misc_constants_t {
+ ARM_MAX_INSTUCTIONS = 512 // based on ASSEMBLY_SCRATCH_SIZE
+ };
+
+ enum {
+ SRC_REG = 0,
+ SRC_IMM,
+ SRC_ERROR = -1
+ };
+
+ enum addr_modes {
+ // start above the range of legal mips reg #'s (0-31)
+ AMODE_REG = 0x20,
+ AMODE_IMM, AMODE_REG_IMM, // for data processing
+ AMODE_IMM_12_PRE, AMODE_IMM_12_POST, // for load/store
+ AMODE_REG_SCALE_PRE, AMODE_IMM_8_PRE,
+ AMODE_IMM_8_POST, AMODE_REG_PRE,
+ AMODE_UNSUPPORTED
+ };
+
+ struct addr_mode_t { // address modes for current ARM instruction
+ int reg;
+ int stype;
+ uint32_t value;
+ bool writeback; // writeback the adr reg after modification
+ } amode;
+
+ enum cond_types {
+ CMP_COND = 1,
+ SBIT_COND
+ };
+
+ struct cond_mode_t { // conditional-execution info for current ARM instruction
+ cond_types type;
+ int r1;
+ int r2;
+ int labelnum;
+ char label[100][10];
+ } cond;
+
+};
+
+
+
+
+// ----------------------------------------------------------------------------
+// ----------------------------------------------------------------------------
+// ----------------------------------------------------------------------------
+
+// This is the basic MIPS assembler, which just creates the opcodes in memory.
+// All the more complicated work is done in ArmToMipsAssember above.
+
+class MIPSAssembler
+{
+public:
+ MIPSAssembler(const sp<Assembly>& assembly, ArmToMipsAssembler *parent);
+ virtual ~MIPSAssembler();
+
+ uint32_t* base() const;
+ uint32_t* pc() const;
+ void reset();
+
+ void disassemble(const char* name);
+
+ void prolog();
+ void epilog(uint32_t touched);
+ int generate(const char* name);
+ void comment(const char* string);
+ void label(const char* string);
+
+ // valid only after generate() has been called
+ uint32_t* pcForLabel(const char* label);
+
+
+ // ------------------------------------------------------------------------
+ // MIPSAssemblerInterface...
+ // ------------------------------------------------------------------------
+
+#if 0
+#pragma mark -
+#pragma mark Arithmetic...
+#endif
+
+ void ADDU(int Rd, int Rs, int Rt);
+ void ADDIU(int Rt, int Rs, int16_t imm);
+ void SUBU(int Rd, int Rs, int Rt);
+ void SUBIU(int Rt, int Rs, int16_t imm);
+ void NEGU(int Rd, int Rs);
+ void MUL(int Rd, int Rs, int Rt);
+ void MULT(int Rs, int Rt); // dest is hi,lo
+ void MULTU(int Rs, int Rt); // dest is hi,lo
+ void MADD(int Rs, int Rt); // hi,lo = hi,lo + Rs * Rt
+ void MADDU(int Rs, int Rt); // hi,lo = hi,lo + Rs * Rt
+ void MSUB(int Rs, int Rt); // hi,lo = hi,lo - Rs * Rt
+ void MSUBU(int Rs, int Rt); // hi,lo = hi,lo - Rs * Rt
+ void SEB(int Rd, int Rt); // sign-extend byte (mips32r2)
+ void SEH(int Rd, int Rt); // sign-extend half-word (mips32r2)
+
+
+#if 0
+#pragma mark -
+#pragma mark Comparisons...
+#endif
+
+ void SLT(int Rd, int Rs, int Rt);
+ void SLTI(int Rt, int Rs, int16_t imm);
+ void SLTU(int Rd, int Rs, int Rt);
+ void SLTIU(int Rt, int Rs, int16_t imm);
+
+
+#if 0
+#pragma mark -
+#pragma mark Logical...
+#endif
+
+ void AND(int Rd, int Rs, int Rt);
+ void ANDI(int Rd, int Rs, uint16_t imm);
+ void OR(int Rd, int Rs, int Rt);
+ void ORI(int Rt, int Rs, uint16_t imm);
+ void NOR(int Rd, int Rs, int Rt);
+ void NOT(int Rd, int Rs);
+ void XOR(int Rd, int Rs, int Rt);
+ void XORI(int Rt, int Rs, uint16_t imm);
+
+ void SLL(int Rd, int Rt, int shft);
+ void SLLV(int Rd, int Rt, int Rs);
+ void SRL(int Rd, int Rt, int shft);
+ void SRLV(int Rd, int Rt, int Rs);
+ void SRA(int Rd, int Rt, int shft);
+ void SRAV(int Rd, int Rt, int Rs);
+ void ROTR(int Rd, int Rt, int shft); // mips32r2
+ void ROTRV(int Rd, int Rt, int Rs); // mips32r2
+ void RORsyn(int Rd, int Rs, int Rt); // synthetic: d = s rotated by t
+ void RORIsyn(int Rd, int Rt, int rot); // synthetic: d = s rotated by immed
+
+ void CLO(int Rd, int Rs);
+ void CLZ(int Rd, int Rs);
+ void WSBH(int Rd, int Rt);
+
+
+#if 0
+#pragma mark -
+#pragma mark Load/store...
+#endif
+
+ void LW(int Rt, int Rbase, int16_t offset);
+ void SW(int Rt, int Rbase, int16_t offset);
+ void LB(int Rt, int Rbase, int16_t offset);
+ void LBU(int Rt, int Rbase, int16_t offset);
+ void SB(int Rt, int Rbase, int16_t offset);
+ void LH(int Rt, int Rbase, int16_t offset);
+ void LHU(int Rt, int Rbase, int16_t offset);
+ void SH(int Rt, int Rbase, int16_t offset);
+ void LUI(int Rt, int16_t offset);
+
+#if 0
+#pragma mark -
+#pragma mark Register moves...
+#endif
+
+ void MOVE(int Rd, int Rs);
+ void MOVN(int Rd, int Rs, int Rt);
+ void MOVZ(int Rd, int Rs, int Rt);
+ void MFHI(int Rd);
+ void MFLO(int Rd);
+ void MTHI(int Rs);
+ void MTLO(int Rs);
+
+#if 0
+#pragma mark -
+#pragma mark Branch...
+#endif
+
+ void B(const char* label);
+ void BEQ(int Rs, int Rt, const char* label);
+ void BNE(int Rs, int Rt, const char* label);
+ void BGEZ(int Rs, const char* label);
+ void BGTZ(int Rs, const char* label);
+ void BLEZ(int Rs, const char* label);
+ void BLTZ(int Rs, const char* label);
+ void JR(int Rs);
+
+
+#if 0
+#pragma mark -
+#pragma mark Synthesized Branch...
+#endif
+
+ // synthetic variants of above (using slt & friends)
+ void BEQZ(int Rs, const char* label);
+ void BNEZ(int Rs, const char* label);
+ void BGE(int Rs, int Rt, const char* label);
+ void BGEU(int Rs, int Rt, const char* label);
+ void BGT(int Rs, int Rt, const char* label);
+ void BGTU(int Rs, int Rt, const char* label);
+ void BLE(int Rs, int Rt, const char* label);
+ void BLEU(int Rs, int Rt, const char* label);
+ void BLT(int Rs, int Rt, const char* label);
+ void BLTU(int Rs, int Rt, const char* label);
+
+#if 0
+#pragma mark -
+#pragma mark Misc...
+#endif
+
+ void NOP(void);
+ void NOP2(void);
+ void UNIMPL(void);
+
+
+
+
+
+private:
+ void string_detab(char *s);
+ void string_pad(char *s, int padded_len);
+
+ ArmToMipsAssembler *mParent;
+ sp<Assembly> mAssembly;
+ uint32_t* mBase;
+ uint32_t* mPC;
+ uint32_t* mPrologPC;
+ int64_t mDuration;
+#if defined(WITH_LIB_HARDWARE)
+ bool mQemuTracing;
+#endif
+
+ struct branch_target_t {
+ inline branch_target_t() : label(0), pc(0) { }
+ inline branch_target_t(const char* l, uint32_t* p)
+ : label(l), pc(p) { }
+ const char* label;
+ uint32_t* pc;
+ };
+
+ Vector<branch_target_t> mBranchTargets;
+ KeyedVector< const char*, uint32_t* > mLabels;
+ KeyedVector< uint32_t*, const char* > mLabelsInverseMapping;
+ KeyedVector< uint32_t*, const char* > mComments;
+
+
+
+
+ // opcode field of all instructions
+ enum opcode_field {
+ spec_op, regimm_op, j_op, jal_op, // 00
+ beq_op, bne_op, blez_op, bgtz_op,
+ addi_op, addiu_op, slti_op, sltiu_op, // 08
+ andi_op, ori_op, xori_op, lui_op,
+ cop0_op, cop1_op, cop2_op, cop1x_op, // 10
+ beql_op, bnel_op, blezl_op, bgtzl_op,
+ daddi_op, daddiu_op, ldl_op, ldr_op, // 18
+ spec2_op, jalx_op, mdmx_op, spec3_op,
+ lb_op, lh_op, lwl_op, lw_op, // 20
+ lbu_op, lhu_op, lwr_op, lwu_op,
+ sb_op, sh_op, swl_op, sw_op, // 28
+ sdl_op, sdr_op, swr_op, cache_op,
+ ll_op, lwc1_op, lwc2_op, pref_op, // 30
+ lld_op, ldc1_op, ldc2_op, ld_op,
+ sc_op, swc1_op, swc2_op, rsrv_3b_op, // 38
+ scd_op, sdc1_op, sdc2_op, sd_op
+ };
+
+
+ // func field for special opcode
+ enum func_spec_op {
+ sll_fn, movc_fn, srl_fn, sra_fn, // 00
+ sllv_fn, pmon_fn, srlv_fn, srav_fn,
+ jr_fn, jalr_fn, movz_fn, movn_fn, // 08
+ syscall_fn, break_fn, spim_fn, sync_fn,
+ mfhi_fn, mthi_fn, mflo_fn, mtlo_fn, // 10
+ dsllv_fn, rsrv_spec_2, dsrlv_fn, dsrav_fn,
+ mult_fn, multu_fn, div_fn, divu_fn, // 18
+ dmult_fn, dmultu_fn, ddiv_fn, ddivu_fn,
+ add_fn, addu_fn, sub_fn, subu_fn, // 20
+ and_fn, or_fn, xor_fn, nor_fn,
+ rsrv_spec_3, rsrv_spec_4, slt_fn, sltu_fn, // 28
+ dadd_fn, daddu_fn, dsub_fn, dsubu_fn,
+ tge_fn, tgeu_fn, tlt_fn, tltu_fn, // 30
+ teq_fn, rsrv_spec_5, tne_fn, rsrv_spec_6,
+ dsll_fn, rsrv_spec_7, dsrl_fn, dsra_fn, // 38
+ dsll32_fn, rsrv_spec_8, dsrl32_fn, dsra32_fn
+ };
+
+ // func field for spec2 opcode
+ enum func_spec2_op {
+ madd_fn, maddu_fn, mul_fn, rsrv_spec2_3,
+ msub_fn, msubu_fn,
+ clz_fn = 0x20, clo_fn,
+ dclz_fn = 0x24, dclo_fn,
+ sdbbp_fn = 0x3f
+ };
+
+ // func field for spec3 opcode
+ enum func_spec3_op {
+ ext_fn, dextm_fn, dextu_fn, dext_fn,
+ ins_fn, dinsm_fn, dinsu_fn, dins_fn,
+ bshfl_fn = 0x20,
+ dbshfl_fn = 0x24,
+ rdhwr_fn = 0x3b
+ };
+
+ // sa field for spec3 opcodes, with BSHFL function
+ enum func_spec3_bshfl {
+ wsbh_fn = 0x02,
+ seb_fn = 0x10,
+ seh_fn = 0x18
+ };
+
+ // rt field of regimm opcodes.
+ enum regimm_fn {
+ bltz_fn, bgez_fn, bltzl_fn, bgezl_fn,
+ rsrv_ri_fn4, rsrv_ri_fn5, rsrv_ri_fn6, rsrv_ri_fn7,
+ tgei_fn, tgeiu_fn, tlti_fn, tltiu_fn,
+ teqi_fn, rsrv_ri_fn_0d, tnei_fn, rsrv_ri_fn0f,
+ bltzal_fn, bgezal_fn, bltzall_fn, bgezall_fn,
+ bposge32_fn= 0x1c,
+ synci_fn = 0x1f
+ };
+
+
+ // func field for mad opcodes (MIPS IV).
+ enum mad_func {
+ madd_fp_op = 0x08, msub_fp_op = 0x0a,
+ nmadd_fp_op = 0x0c, nmsub_fp_op = 0x0e
+ };
+
+
+ enum mips_inst_shifts {
+ OP_SHF = 26,
+ JTARGET_SHF = 0,
+ RS_SHF = 21,
+ RT_SHF = 16,
+ RD_SHF = 11,
+ RE_SHF = 6,
+ SA_SHF = RE_SHF, // synonym
+ IMM_SHF = 0,
+ FUNC_SHF = 0,
+
+ // mask values
+ MSK_16 = 0xffff,
+
+
+ CACHEOP_SHF = 18,
+ CACHESEL_SHF = 16,
+ };
+};
+
+enum mips_regnames {
+ R_zero = 0,
+ R_at, R_v0, R_v1, R_a0, R_a1, R_a2, R_a3,
+ R_t0, R_t1, R_t2, R_t3, R_t4, R_t5, R_t6, R_t7,
+ R_s0, R_s1, R_s2, R_s3, R_s4, R_s5, R_s6, R_s7,
+ R_t8, R_t9, R_k0, R_k1, R_gp, R_sp, R_s8, R_ra,
+ R_lr = R_s8,
+
+ // arm regs 0-15 are mips regs 2-17 (meaning s0 & s1 are used)
+ R_at2 = R_s2, // R_at2 = 18 = s2
+ R_cmp = R_s3, // R_cmp = 19 = s3
+ R_cmp2 = R_s4 // R_cmp2 = 20 = s4
+};
+
+
+
+}; // namespace android
+
+#endif //ANDROID_MIPSASSEMBLER_H
diff --git a/libpixelflinger/codeflinger/armreg.h b/libpixelflinger/codeflinger/armreg.h
new file mode 100644
index 0000000..fde81ba
--- /dev/null
+++ b/libpixelflinger/codeflinger/armreg.h
@@ -0,0 +1,300 @@
+/* $NetBSD: armreg.h,v 1.28 2003/10/31 16:30:15 scw Exp $ */
+
+/*-
+ * Copyright (c) 1998, 2001 Ben Harris
+ * Copyright (c) 1994-1996 Mark Brinicombe.
+ * Copyright (c) 1994 Brini.
+ * All rights reserved.
+ *
+ * This code is derived from software written for Brini by Mark Brinicombe
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Brini.
+ * 4. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: /repoman/r/ncvs/src/sys/arm/include/armreg.h,v 1.3 2005/11/21 19:06:25 cognet Exp $
+ */
+
+#ifndef MACHINE_ARMREG_H
+#define MACHINE_ARMREG_H
+#define INSN_SIZE 4
+#define INSN_COND_MASK 0xf0000000 /* Condition mask */
+#define PSR_MODE 0x0000001f /* mode mask */
+#define PSR_USR26_MODE 0x00000000
+#define PSR_FIQ26_MODE 0x00000001
+#define PSR_IRQ26_MODE 0x00000002
+#define PSR_SVC26_MODE 0x00000003
+#define PSR_USR32_MODE 0x00000010
+#define PSR_FIQ32_MODE 0x00000011
+#define PSR_IRQ32_MODE 0x00000012
+#define PSR_SVC32_MODE 0x00000013
+#define PSR_ABT32_MODE 0x00000017
+#define PSR_UND32_MODE 0x0000001b
+#define PSR_SYS32_MODE 0x0000001f
+#define PSR_32_MODE 0x00000010
+#define PSR_FLAGS 0xf0000000 /* flags */
+
+#define PSR_C_bit (1 << 29) /* carry */
+
+/* The high-order byte is always the implementor */
+#define CPU_ID_IMPLEMENTOR_MASK 0xff000000
+#define CPU_ID_ARM_LTD 0x41000000 /* 'A' */
+#define CPU_ID_DEC 0x44000000 /* 'D' */
+#define CPU_ID_INTEL 0x69000000 /* 'i' */
+#define CPU_ID_TI 0x54000000 /* 'T' */
+
+/* How to decide what format the CPUID is in. */
+#define CPU_ID_ISOLD(x) (((x) & 0x0000f000) == 0x00000000)
+#define CPU_ID_IS7(x) (((x) & 0x0000f000) == 0x00007000)
+#define CPU_ID_ISNEW(x) (!CPU_ID_ISOLD(x) && !CPU_ID_IS7(x))
+
+/* On ARM3 and ARM6, this byte holds the foundry ID. */
+#define CPU_ID_FOUNDRY_MASK 0x00ff0000
+#define CPU_ID_FOUNDRY_VLSI 0x00560000
+
+/* On ARM7 it holds the architecture and variant (sub-model) */
+#define CPU_ID_7ARCH_MASK 0x00800000
+#define CPU_ID_7ARCH_V3 0x00000000
+#define CPU_ID_7ARCH_V4T 0x00800000
+#define CPU_ID_7VARIANT_MASK 0x007f0000
+
+/* On more recent ARMs, it does the same, but in a different format */
+#define CPU_ID_ARCH_MASK 0x000f0000
+#define CPU_ID_ARCH_V3 0x00000000
+#define CPU_ID_ARCH_V4 0x00010000
+#define CPU_ID_ARCH_V4T 0x00020000
+#define CPU_ID_ARCH_V5 0x00030000
+#define CPU_ID_ARCH_V5T 0x00040000
+#define CPU_ID_ARCH_V5TE 0x00050000
+#define CPU_ID_VARIANT_MASK 0x00f00000
+
+/* Next three nybbles are part number */
+#define CPU_ID_PARTNO_MASK 0x0000fff0
+
+/* Intel XScale has sub fields in part number */
+#define CPU_ID_XSCALE_COREGEN_MASK 0x0000e000 /* core generation */
+#define CPU_ID_XSCALE_COREREV_MASK 0x00001c00 /* core revision */
+#define CPU_ID_XSCALE_PRODUCT_MASK 0x000003f0 /* product number */
+
+/* And finally, the revision number. */
+#define CPU_ID_REVISION_MASK 0x0000000f
+
+/* Individual CPUs are probably best IDed by everything but the revision. */
+#define CPU_ID_CPU_MASK 0xfffffff0
+
+/* Fake CPU IDs for ARMs without CP15 */
+#define CPU_ID_ARM2 0x41560200
+#define CPU_ID_ARM250 0x41560250
+
+/* Pre-ARM7 CPUs -- [15:12] == 0 */
+#define CPU_ID_ARM3 0x41560300
+#define CPU_ID_ARM600 0x41560600
+#define CPU_ID_ARM610 0x41560610
+#define CPU_ID_ARM620 0x41560620
+
+/* ARM7 CPUs -- [15:12] == 7 */
+#define CPU_ID_ARM700 0x41007000 /* XXX This is a guess. */
+#define CPU_ID_ARM710 0x41007100
+#define CPU_ID_ARM7500 0x41027100 /* XXX This is a guess. */
+#define CPU_ID_ARM710A 0x41047100 /* inc ARM7100 */
+#define CPU_ID_ARM7500FE 0x41077100
+#define CPU_ID_ARM710T 0x41807100
+#define CPU_ID_ARM720T 0x41807200
+#define CPU_ID_ARM740T8K 0x41807400 /* XXX no MMU, 8KB cache */
+#define CPU_ID_ARM740T4K 0x41817400 /* XXX no MMU, 4KB cache */
+
+/* Post-ARM7 CPUs */
+#define CPU_ID_ARM810 0x41018100
+#define CPU_ID_ARM920T 0x41129200
+#define CPU_ID_ARM920T_ALT 0x41009200
+#define CPU_ID_ARM922T 0x41029220
+#define CPU_ID_ARM940T 0x41029400 /* XXX no MMU */
+#define CPU_ID_ARM946ES 0x41049460 /* XXX no MMU */
+#define CPU_ID_ARM966ES 0x41049660 /* XXX no MMU */
+#define CPU_ID_ARM966ESR1 0x41059660 /* XXX no MMU */
+#define CPU_ID_ARM1020E 0x4115a200 /* (AKA arm10 rev 1) */
+#define CPU_ID_ARM1022ES 0x4105a220
+#define CPU_ID_SA110 0x4401a100
+#define CPU_ID_SA1100 0x4401a110
+#define CPU_ID_TI925T 0x54029250
+#define CPU_ID_SA1110 0x6901b110
+#define CPU_ID_IXP1200 0x6901c120
+#define CPU_ID_80200 0x69052000
+#define CPU_ID_PXA250 0x69052100 /* sans core revision */
+#define CPU_ID_PXA210 0x69052120
+#define CPU_ID_PXA250A 0x69052100 /* 1st version Core */
+#define CPU_ID_PXA210A 0x69052120 /* 1st version Core */
+#define CPU_ID_PXA250B 0x69052900 /* 3rd version Core */
+#define CPU_ID_PXA210B 0x69052920 /* 3rd version Core */
+#define CPU_ID_PXA250C 0x69052d00 /* 4th version Core */
+#define CPU_ID_PXA210C 0x69052d20 /* 4th version Core */
+#define CPU_ID_80321_400 0x69052420
+#define CPU_ID_80321_600 0x69052430
+#define CPU_ID_80321_400_B0 0x69052c20
+#define CPU_ID_80321_600_B0 0x69052c30
+#define CPU_ID_IXP425_533 0x690541c0
+#define CPU_ID_IXP425_400 0x690541d0
+#define CPU_ID_IXP425_266 0x690541f0
+
+/* ARM3-specific coprocessor 15 registers */
+#define ARM3_CP15_FLUSH 1
+#define ARM3_CP15_CONTROL 2
+#define ARM3_CP15_CACHEABLE 3
+#define ARM3_CP15_UPDATEABLE 4
+#define ARM3_CP15_DISRUPTIVE 5
+
+/* ARM3 Control register bits */
+#define ARM3_CTL_CACHE_ON 0x00000001
+#define ARM3_CTL_SHARED 0x00000002
+#define ARM3_CTL_MONITOR 0x00000004
+
+/*
+ * Post-ARM3 CP15 registers:
+ *
+ * 1 Control register
+ *
+ * 2 Translation Table Base
+ *
+ * 3 Domain Access Control
+ *
+ * 4 Reserved
+ *
+ * 5 Fault Status
+ *
+ * 6 Fault Address
+ *
+ * 7 Cache/write-buffer Control
+ *
+ * 8 TLB Control
+ *
+ * 9 Cache Lockdown
+ *
+ * 10 TLB Lockdown
+ *
+ * 11 Reserved
+ *
+ * 12 Reserved
+ *
+ * 13 Process ID (for FCSE)
+ *
+ * 14 Reserved
+ *
+ * 15 Implementation Dependent
+ */
+
+/* Some of the definitions below need cleaning up for V3/V4 architectures */
+
+/* CPU control register (CP15 register 1) */
+#define CPU_CONTROL_MMU_ENABLE 0x00000001 /* M: MMU/Protection unit enable */
+#define CPU_CONTROL_AFLT_ENABLE 0x00000002 /* A: Alignment fault enable */
+#define CPU_CONTROL_DC_ENABLE 0x00000004 /* C: IDC/DC enable */
+#define CPU_CONTROL_WBUF_ENABLE 0x00000008 /* W: Write buffer enable */
+#define CPU_CONTROL_32BP_ENABLE 0x00000010 /* P: 32-bit exception handlers */
+#define CPU_CONTROL_32BD_ENABLE 0x00000020 /* D: 32-bit addressing */
+#define CPU_CONTROL_LABT_ENABLE 0x00000040 /* L: Late abort enable */
+#define CPU_CONTROL_BEND_ENABLE 0x00000080 /* B: Big-endian mode */
+#define CPU_CONTROL_SYST_ENABLE 0x00000100 /* S: System protection bit */
+#define CPU_CONTROL_ROM_ENABLE 0x00000200 /* R: ROM protection bit */
+#define CPU_CONTROL_CPCLK 0x00000400 /* F: Implementation defined */
+#define CPU_CONTROL_BPRD_ENABLE 0x00000800 /* Z: Branch prediction enable */
+#define CPU_CONTROL_IC_ENABLE 0x00001000 /* I: IC enable */
+#define CPU_CONTROL_VECRELOC 0x00002000 /* V: Vector relocation */
+#define CPU_CONTROL_ROUNDROBIN 0x00004000 /* RR: Predictable replacement */
+#define CPU_CONTROL_V4COMPAT 0x00008000 /* L4: ARMv4 compat LDR R15 etc */
+
+#define CPU_CONTROL_IDC_ENABLE CPU_CONTROL_DC_ENABLE
+
+/* XScale Auxillary Control Register (CP15 register 1, opcode2 1) */
+#define XSCALE_AUXCTL_K 0x00000001 /* dis. write buffer coalescing */
+#define XSCALE_AUXCTL_P 0x00000002 /* ECC protect page table access */
+#define XSCALE_AUXCTL_MD_WB_RA 0x00000000 /* mini-D$ wb, read-allocate */
+#define XSCALE_AUXCTL_MD_WB_RWA 0x00000010 /* mini-D$ wb, read/write-allocate */
+#define XSCALE_AUXCTL_MD_WT 0x00000020 /* mini-D$ wt, read-allocate */
+#define XSCALE_AUXCTL_MD_MASK 0x00000030
+
+/* Cache type register definitions */
+#define CPU_CT_ISIZE(x) ((x) & 0xfff) /* I$ info */
+#define CPU_CT_DSIZE(x) (((x) >> 12) & 0xfff) /* D$ info */
+#define CPU_CT_S (1U << 24) /* split cache */
+#define CPU_CT_CTYPE(x) (((x) >> 25) & 0xf) /* cache type */
+
+#define CPU_CT_CTYPE_WT 0 /* write-through */
+#define CPU_CT_CTYPE_WB1 1 /* write-back, clean w/ read */
+#define CPU_CT_CTYPE_WB2 2 /* w/b, clean w/ cp15,7 */
+#define CPU_CT_CTYPE_WB6 6 /* w/b, cp15,7, lockdown fmt A */
+#define CPU_CT_CTYPE_WB7 7 /* w/b, cp15,7, lockdown fmt B */
+
+#define CPU_CT_xSIZE_LEN(x) ((x) & 0x3) /* line size */
+#define CPU_CT_xSIZE_M (1U << 2) /* multiplier */
+#define CPU_CT_xSIZE_ASSOC(x) (((x) >> 3) & 0x7) /* associativity */
+#define CPU_CT_xSIZE_SIZE(x) (((x) >> 6) & 0x7) /* size */
+
+/* Fault status register definitions */
+
+#define FAULT_TYPE_MASK 0x0f
+#define FAULT_USER 0x10
+
+#define FAULT_WRTBUF_0 0x00 /* Vector Exception */
+#define FAULT_WRTBUF_1 0x02 /* Terminal Exception */
+#define FAULT_BUSERR_0 0x04 /* External Abort on Linefetch -- Section */
+#define FAULT_BUSERR_1 0x06 /* External Abort on Linefetch -- Page */
+#define FAULT_BUSERR_2 0x08 /* External Abort on Non-linefetch -- Section */
+#define FAULT_BUSERR_3 0x0a /* External Abort on Non-linefetch -- Page */
+#define FAULT_BUSTRNL1 0x0c /* External abort on Translation -- Level 1 */
+#define FAULT_BUSTRNL2 0x0e /* External abort on Translation -- Level 2 */
+#define FAULT_ALIGN_0 0x01 /* Alignment */
+#define FAULT_ALIGN_1 0x03 /* Alignment */
+#define FAULT_TRANS_S 0x05 /* Translation -- Section */
+#define FAULT_TRANS_P 0x07 /* Translation -- Page */
+#define FAULT_DOMAIN_S 0x09 /* Domain -- Section */
+#define FAULT_DOMAIN_P 0x0b /* Domain -- Page */
+#define FAULT_PERM_S 0x0d /* Permission -- Section */
+#define FAULT_PERM_P 0x0f /* Permission -- Page */
+
+#define FAULT_IMPRECISE 0x400 /* Imprecise exception (XSCALE) */
+
+/*
+ * Address of the vector page, low and high versions.
+ */
+#define ARM_VECTORS_LOW 0x00000000U
+#define ARM_VECTORS_HIGH 0xffff0000U
+
+/*
+ * ARM Instructions
+ *
+ * 3 3 2 2 2
+ * 1 0 9 8 7 0
+ * +-------+-------------------------------------------------------+
+ * | cond | instruction dependant |
+ * |c c c c| |
+ * +-------+-------------------------------------------------------+
+ */
+
+#define INSN_SIZE 4 /* Always 4 bytes */
+#define INSN_COND_MASK 0xf0000000 /* Condition mask */
+#define INSN_COND_AL 0xe0000000 /* Always condition */
+
+#endif /* !MACHINE_ARMREG_H */
diff --git a/libpixelflinger/codeflinger/blending.cpp b/libpixelflinger/codeflinger/blending.cpp
new file mode 100644
index 0000000..b20219c
--- /dev/null
+++ b/libpixelflinger/codeflinger/blending.cpp
@@ -0,0 +1,672 @@
+/* libs/pixelflinger/codeflinger/blending.cpp
+**
+** Copyright 2006, 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 <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+#include <cutils/log.h>
+
+#include "GGLAssembler.h"
+
+
+namespace android {
+
+void GGLAssembler::build_fog(
+ component_t& temp, // incomming fragment / output
+ int component,
+ Scratch& regs)
+{
+ if (mInfo[component].fog) {
+ Scratch scratches(registerFile());
+ comment("fog");
+
+ integer_t fragment(temp.reg, temp.h, temp.flags);
+ if (!(temp.flags & CORRUPTIBLE)) {
+ temp.reg = regs.obtain();
+ temp.flags |= CORRUPTIBLE;
+ }
+
+ integer_t fogColor(scratches.obtain(), 8, CORRUPTIBLE);
+ LDRB(AL, fogColor.reg, mBuilderContext.Rctx,
+ immed12_pre(GGL_OFFSETOF(state.fog.color[component])));
+
+ integer_t factor(scratches.obtain(), 16, CORRUPTIBLE);
+ CONTEXT_LOAD(factor.reg, generated_vars.f);
+
+ // clamp fog factor (TODO: see if there is a way to guarantee
+ // we won't overflow, when setting the iterators)
+ BIC(AL, 0, factor.reg, factor.reg, reg_imm(factor.reg, ASR, 31));
+ CMP(AL, factor.reg, imm( 0x10000 ));
+ MOV(HS, 0, factor.reg, imm( 0x10000 ));
+
+ build_blendFOneMinusF(temp, factor, fragment, fogColor);
+ }
+}
+
+void GGLAssembler::build_blending(
+ component_t& temp, // incomming fragment / output
+ const pixel_t& pixel, // framebuffer
+ int component,
+ Scratch& regs)
+{
+ if (!mInfo[component].blend)
+ return;
+
+ int fs = component==GGLFormat::ALPHA ? mBlendSrcA : mBlendSrc;
+ int fd = component==GGLFormat::ALPHA ? mBlendDstA : mBlendDst;
+ if (fs==GGL_SRC_ALPHA_SATURATE && component==GGLFormat::ALPHA)
+ fs = GGL_ONE;
+ const int blending = blending_codes(fs, fd);
+ if (!temp.size()) {
+ // here, blending will produce something which doesn't depend on
+ // that component (eg: GL_ZERO:GL_*), so the register has not been
+ // allocated yet. Will never be used as a source.
+ temp = component_t(regs.obtain(), CORRUPTIBLE);
+ }
+
+ // we are doing real blending...
+ // fb: extracted dst
+ // fragment: extracted src
+ // temp: component_t(fragment) and result
+
+ // scoped register allocator
+ Scratch scratches(registerFile());
+ comment("blending");
+
+ // we can optimize these cases a bit...
+ // (1) saturation is not needed
+ // (2) we can use only one multiply instead of 2
+ // (3) we can reduce the register pressure
+ // R = S*f + D*(1-f) = (S-D)*f + D
+ // R = S*(1-f) + D*f = (D-S)*f + S
+
+ const bool same_factor_opt1 =
+ (fs==GGL_DST_COLOR && fd==GGL_ONE_MINUS_DST_COLOR) ||
+ (fs==GGL_SRC_COLOR && fd==GGL_ONE_MINUS_SRC_COLOR) ||
+ (fs==GGL_DST_ALPHA && fd==GGL_ONE_MINUS_DST_ALPHA) ||
+ (fs==GGL_SRC_ALPHA && fd==GGL_ONE_MINUS_SRC_ALPHA);
+
+ const bool same_factor_opt2 =
+ (fs==GGL_ONE_MINUS_DST_COLOR && fd==GGL_DST_COLOR) ||
+ (fs==GGL_ONE_MINUS_SRC_COLOR && fd==GGL_SRC_COLOR) ||
+ (fs==GGL_ONE_MINUS_DST_ALPHA && fd==GGL_DST_ALPHA) ||
+ (fs==GGL_ONE_MINUS_SRC_ALPHA && fd==GGL_SRC_ALPHA);
+
+
+ // XXX: we could also optimize these cases:
+ // R = S*f + D*f = (S+D)*f
+ // R = S*(1-f) + D*(1-f) = (S+D)*(1-f)
+ // R = S*D + D*S = 2*S*D
+
+
+ // see if we need to extract 'component' from the destination (fb)
+ integer_t fb;
+ if (blending & (BLEND_DST|FACTOR_DST)) {
+ fb.setTo(scratches.obtain(), 32);
+ extract(fb, pixel, component);
+ if (mDithering) {
+ // XXX: maybe what we should do instead, is simply
+ // expand fb -or- fragment to the larger of the two
+ if (fb.size() < temp.size()) {
+ // for now we expand 'fb' to min(fragment, 8)
+ int new_size = temp.size() < 8 ? temp.size() : 8;
+ expand(fb, fb, new_size);
+ }
+ }
+ }
+
+
+ // convert input fragment to integer_t
+ if (temp.l && (temp.flags & CORRUPTIBLE)) {
+ MOV(AL, 0, temp.reg, reg_imm(temp.reg, LSR, temp.l));
+ temp.h -= temp.l;
+ temp.l = 0;
+ }
+ integer_t fragment(temp.reg, temp.size(), temp.flags);
+
+ // if not done yet, convert input fragment to integer_t
+ if (temp.l) {
+ // here we know temp is not CORRUPTIBLE
+ fragment.reg = scratches.obtain();
+ MOV(AL, 0, fragment.reg, reg_imm(temp.reg, LSR, temp.l));
+ fragment.flags |= CORRUPTIBLE;
+ }
+
+ if (!(temp.flags & CORRUPTIBLE)) {
+ // temp is not corruptible, but since it's the destination it
+ // will be modified, so we need to allocate a new register.
+ temp.reg = regs.obtain();
+ temp.flags &= ~CORRUPTIBLE;
+ fragment.flags &= ~CORRUPTIBLE;
+ }
+
+ if ((blending & BLEND_SRC) && !same_factor_opt1) {
+ // source (fragment) is needed for the blending stage
+ // so it's not CORRUPTIBLE (unless we're doing same_factor_opt1)
+ fragment.flags &= ~CORRUPTIBLE;
+ }
+
+
+ if (same_factor_opt1) {
+ // R = S*f + D*(1-f) = (S-D)*f + D
+ integer_t factor;
+ build_blend_factor(factor, fs,
+ component, pixel, fragment, fb, scratches);
+ // fb is always corruptible from this point
+ fb.flags |= CORRUPTIBLE;
+ build_blendFOneMinusF(temp, factor, fragment, fb);
+ } else if (same_factor_opt2) {
+ // R = S*(1-f) + D*f = (D-S)*f + S
+ integer_t factor;
+ // fb is always corrruptible here
+ fb.flags |= CORRUPTIBLE;
+ build_blend_factor(factor, fd,
+ component, pixel, fragment, fb, scratches);
+ build_blendOneMinusFF(temp, factor, fragment, fb);
+ } else {
+ integer_t src_factor;
+ integer_t dst_factor;
+
+ // if destination (fb) is not needed for the blending stage,
+ // then it can be marked as CORRUPTIBLE
+ if (!(blending & BLEND_DST)) {
+ fb.flags |= CORRUPTIBLE;
+ }
+
+ // XXX: try to mark some registers as CORRUPTIBLE
+ // in most case we could make those corruptible
+ // when we're processing the last component
+ // but not always, for instance
+ // when fragment is constant and not reloaded
+ // when fb is needed for logic-ops or masking
+ // when a register is aliased (for instance with mAlphaSource)
+
+ // blend away...
+ if (fs==GGL_ZERO) {
+ if (fd==GGL_ZERO) { // R = 0
+ // already taken care of
+ } else if (fd==GGL_ONE) { // R = D
+ // already taken care of
+ } else { // R = D*fd
+ // compute fd
+ build_blend_factor(dst_factor, fd,
+ component, pixel, fragment, fb, scratches);
+ mul_factor(temp, fb, dst_factor);
+ }
+ } else if (fs==GGL_ONE) {
+ if (fd==GGL_ZERO) { // R = S
+ // NOP, taken care of
+ } else if (fd==GGL_ONE) { // R = S + D
+ component_add(temp, fb, fragment); // args order matters
+ component_sat(temp);
+ } else { // R = S + D*fd
+ // compute fd
+ build_blend_factor(dst_factor, fd,
+ component, pixel, fragment, fb, scratches);
+ mul_factor_add(temp, fb, dst_factor, component_t(fragment));
+ component_sat(temp);
+ }
+ } else {
+ // compute fs
+ build_blend_factor(src_factor, fs,
+ component, pixel, fragment, fb, scratches);
+ if (fd==GGL_ZERO) { // R = S*fs
+ mul_factor(temp, fragment, src_factor);
+ } else if (fd==GGL_ONE) { // R = S*fs + D
+ mul_factor_add(temp, fragment, src_factor, component_t(fb));
+ component_sat(temp);
+ } else { // R = S*fs + D*fd
+ mul_factor(temp, fragment, src_factor);
+ if (scratches.isUsed(src_factor.reg))
+ scratches.recycle(src_factor.reg);
+ // compute fd
+ build_blend_factor(dst_factor, fd,
+ component, pixel, fragment, fb, scratches);
+ mul_factor_add(temp, fb, dst_factor, temp);
+ if (!same_factor_opt1 && !same_factor_opt2) {
+ component_sat(temp);
+ }
+ }
+ }
+ }
+
+ // now we can be corrupted (it's the dest)
+ temp.flags |= CORRUPTIBLE;
+}
+
+void GGLAssembler::build_blend_factor(
+ integer_t& factor, int f, int component,
+ const pixel_t& dst_pixel,
+ integer_t& fragment,
+ integer_t& fb,
+ Scratch& scratches)
+{
+ integer_t src_alpha(fragment);
+
+ // src_factor/dst_factor won't be used after blending,
+ // so it's fine to mark them as CORRUPTIBLE (if not aliased)
+ factor.flags |= CORRUPTIBLE;
+
+ switch(f) {
+ case GGL_ONE_MINUS_SRC_ALPHA:
+ case GGL_SRC_ALPHA:
+ if (component==GGLFormat::ALPHA && !isAlphaSourceNeeded()) {
+ // we're processing alpha, so we already have
+ // src-alpha in fragment, and we need src-alpha just this time.
+ } else {
+ // alpha-src will be needed for other components
+ if (!mBlendFactorCached || mBlendFactorCached==f) {
+ src_alpha = mAlphaSource;
+ factor = mAlphaSource;
+ factor.flags &= ~CORRUPTIBLE;
+ // we already computed the blend factor before, nothing to do.
+ if (mBlendFactorCached)
+ return;
+ // this is the first time, make sure to compute the blend
+ // factor properly.
+ mBlendFactorCached = f;
+ break;
+ } else {
+ // we have a cached alpha blend factor, but we want another one,
+ // this should really not happen because by construction,
+ // we cannot have BOTH source and destination
+ // blend factors use ALPHA *and* ONE_MINUS_ALPHA (because
+ // the blending stage uses the f/(1-f) optimization
+
+ // for completeness, we handle this case though. Since there
+ // are only 2 choices, this meens we want "the other one"
+ // (1-factor)
+ factor = mAlphaSource;
+ factor.flags &= ~CORRUPTIBLE;
+ RSB(AL, 0, factor.reg, factor.reg, imm((1<<factor.s)));
+ mBlendFactorCached = f;
+ return;
+ }
+ }
+ // fall-through...
+ case GGL_ONE_MINUS_DST_COLOR:
+ case GGL_DST_COLOR:
+ case GGL_ONE_MINUS_SRC_COLOR:
+ case GGL_SRC_COLOR:
+ case GGL_ONE_MINUS_DST_ALPHA:
+ case GGL_DST_ALPHA:
+ case GGL_SRC_ALPHA_SATURATE:
+ // help us find out what register we can use for the blend-factor
+ // CORRUPTIBLE registers are chosen first, or a new one is allocated.
+ if (fragment.flags & CORRUPTIBLE) {
+ factor.setTo(fragment.reg, 32, CORRUPTIBLE);
+ fragment.flags &= ~CORRUPTIBLE;
+ } else if (fb.flags & CORRUPTIBLE) {
+ factor.setTo(fb.reg, 32, CORRUPTIBLE);
+ fb.flags &= ~CORRUPTIBLE;
+ } else {
+ factor.setTo(scratches.obtain(), 32, CORRUPTIBLE);
+ }
+ break;
+ }
+
+ // XXX: doesn't work if size==1
+
+ switch(f) {
+ case GGL_ONE_MINUS_DST_COLOR:
+ case GGL_DST_COLOR:
+ factor.s = fb.s;
+ ADD(AL, 0, factor.reg, fb.reg, reg_imm(fb.reg, LSR, fb.s-1));
+ break;
+ case GGL_ONE_MINUS_SRC_COLOR:
+ case GGL_SRC_COLOR:
+ factor.s = fragment.s;
+ ADD(AL, 0, factor.reg, fragment.reg,
+ reg_imm(fragment.reg, LSR, fragment.s-1));
+ break;
+ case GGL_ONE_MINUS_SRC_ALPHA:
+ case GGL_SRC_ALPHA:
+ factor.s = src_alpha.s;
+ ADD(AL, 0, factor.reg, src_alpha.reg,
+ reg_imm(src_alpha.reg, LSR, src_alpha.s-1));
+ break;
+ case GGL_ONE_MINUS_DST_ALPHA:
+ case GGL_DST_ALPHA:
+ // XXX: should be precomputed
+ extract(factor, dst_pixel, GGLFormat::ALPHA);
+ ADD(AL, 0, factor.reg, factor.reg,
+ reg_imm(factor.reg, LSR, factor.s-1));
+ break;
+ case GGL_SRC_ALPHA_SATURATE:
+ // XXX: should be precomputed
+ // XXX: f = min(As, 1-Ad)
+ // btw, we're guaranteed that Ad's size is <= 8, because
+ // it's extracted from the framebuffer
+ break;
+ }
+
+ switch(f) {
+ case GGL_ONE_MINUS_DST_COLOR:
+ case GGL_ONE_MINUS_SRC_COLOR:
+ case GGL_ONE_MINUS_DST_ALPHA:
+ case GGL_ONE_MINUS_SRC_ALPHA:
+ RSB(AL, 0, factor.reg, factor.reg, imm((1<<factor.s)));
+ }
+
+ // don't need more than 8-bits for the blend factor
+ // and this will prevent overflows in the multiplies later
+ if (factor.s > 8) {
+ MOV(AL, 0, factor.reg, reg_imm(factor.reg, LSR, factor.s-8));
+ factor.s = 8;
+ }
+}
+
+int GGLAssembler::blending_codes(int fs, int fd)
+{
+ int blending = 0;
+ switch(fs) {
+ case GGL_ONE:
+ blending |= BLEND_SRC;
+ break;
+
+ case GGL_ONE_MINUS_DST_COLOR:
+ case GGL_DST_COLOR:
+ blending |= FACTOR_DST|BLEND_SRC;
+ break;
+ case GGL_ONE_MINUS_DST_ALPHA:
+ case GGL_DST_ALPHA:
+ // no need to extract 'component' from the destination
+ // for the blend factor, because we need ALPHA only.
+ blending |= BLEND_SRC;
+ break;
+
+ case GGL_ONE_MINUS_SRC_COLOR:
+ case GGL_SRC_COLOR:
+ blending |= FACTOR_SRC|BLEND_SRC;
+ break;
+ case GGL_ONE_MINUS_SRC_ALPHA:
+ case GGL_SRC_ALPHA:
+ case GGL_SRC_ALPHA_SATURATE:
+ blending |= FACTOR_SRC|BLEND_SRC;
+ break;
+ }
+ switch(fd) {
+ case GGL_ONE:
+ blending |= BLEND_DST;
+ break;
+
+ case GGL_ONE_MINUS_DST_COLOR:
+ case GGL_DST_COLOR:
+ blending |= FACTOR_DST|BLEND_DST;
+ break;
+ case GGL_ONE_MINUS_DST_ALPHA:
+ case GGL_DST_ALPHA:
+ blending |= FACTOR_DST|BLEND_DST;
+ break;
+
+ case GGL_ONE_MINUS_SRC_COLOR:
+ case GGL_SRC_COLOR:
+ blending |= FACTOR_SRC|BLEND_DST;
+ break;
+ case GGL_ONE_MINUS_SRC_ALPHA:
+ case GGL_SRC_ALPHA:
+ // no need to extract 'component' from the source
+ // for the blend factor, because we need ALPHA only.
+ blending |= BLEND_DST;
+ break;
+ }
+ return blending;
+}
+
+// ---------------------------------------------------------------------------
+
+void GGLAssembler::build_blendFOneMinusF(
+ component_t& temp,
+ const integer_t& factor,
+ const integer_t& fragment,
+ const integer_t& fb)
+{
+ // R = S*f + D*(1-f) = (S-D)*f + D
+ Scratch scratches(registerFile());
+ // compute S-D
+ integer_t diff(fragment.flags & CORRUPTIBLE ?
+ fragment.reg : scratches.obtain(), fb.size(), CORRUPTIBLE);
+ const int shift = fragment.size() - fb.size();
+ if (shift>0) RSB(AL, 0, diff.reg, fb.reg, reg_imm(fragment.reg, LSR, shift));
+ else if (shift<0) RSB(AL, 0, diff.reg, fb.reg, reg_imm(fragment.reg, LSL,-shift));
+ else RSB(AL, 0, diff.reg, fb.reg, fragment.reg);
+ mul_factor_add(temp, diff, factor, component_t(fb));
+}
+
+void GGLAssembler::build_blendOneMinusFF(
+ component_t& temp,
+ const integer_t& factor,
+ const integer_t& fragment,
+ const integer_t& fb)
+{
+ // R = S*f + D*(1-f) = (S-D)*f + D
+ Scratch scratches(registerFile());
+ // compute D-S
+ integer_t diff(fb.flags & CORRUPTIBLE ?
+ fb.reg : scratches.obtain(), fb.size(), CORRUPTIBLE);
+ const int shift = fragment.size() - fb.size();
+ if (shift>0) SUB(AL, 0, diff.reg, fb.reg, reg_imm(fragment.reg, LSR, shift));
+ else if (shift<0) SUB(AL, 0, diff.reg, fb.reg, reg_imm(fragment.reg, LSL,-shift));
+ else SUB(AL, 0, diff.reg, fb.reg, fragment.reg);
+ mul_factor_add(temp, diff, factor, component_t(fragment));
+}
+
+// ---------------------------------------------------------------------------
+
+void GGLAssembler::mul_factor( component_t& d,
+ const integer_t& v,
+ const integer_t& f)
+{
+ int vs = v.size();
+ int fs = f.size();
+ int ms = vs+fs;
+
+ // XXX: we could have special cases for 1 bit mul
+
+ // all this code below to use the best multiply instruction
+ // wrt the parameters size. We take advantage of the fact
+ // that the 16-bits multiplies allow a 16-bit shift
+ // The trick is that we just make sure that we have at least 8-bits
+ // per component (which is enough for a 8 bits display).
+
+ int xy;
+ int vshift = 0;
+ int fshift = 0;
+ int smulw = 0;
+
+ if (vs<16) {
+ if (fs<16) {
+ xy = xyBB;
+ } else if (GGL_BETWEEN(fs, 24, 31)) {
+ ms -= 16;
+ xy = xyTB;
+ } else {
+ // eg: 15 * 18 -> 15 * 15
+ fshift = fs - 15;
+ ms -= fshift;
+ xy = xyBB;
+ }
+ } else if (GGL_BETWEEN(vs, 24, 31)) {
+ if (fs<16) {
+ ms -= 16;
+ xy = xyTB;
+ } else if (GGL_BETWEEN(fs, 24, 31)) {
+ ms -= 32;
+ xy = xyTT;
+ } else {
+ // eg: 24 * 18 -> 8 * 18
+ fshift = fs - 15;
+ ms -= 16 + fshift;
+ xy = xyTB;
+ }
+ } else {
+ if (fs<16) {
+ // eg: 18 * 15 -> 15 * 15
+ vshift = vs - 15;
+ ms -= vshift;
+ xy = xyBB;
+ } else if (GGL_BETWEEN(fs, 24, 31)) {
+ // eg: 18 * 24 -> 15 * 8
+ vshift = vs - 15;
+ ms -= 16 + vshift;
+ xy = xyBT;
+ } else {
+ // eg: 18 * 18 -> (15 * 18)>>16
+ fshift = fs - 15;
+ ms -= 16 + fshift;
+ xy = yB; //XXX SMULWB
+ smulw = 1;
+ }
+ }
+
+ ALOGE_IF(ms>=32, "mul_factor overflow vs=%d, fs=%d", vs, fs);
+
+ int vreg = v.reg;
+ int freg = f.reg;
+ if (vshift) {
+ MOV(AL, 0, d.reg, reg_imm(vreg, LSR, vshift));
+ vreg = d.reg;
+ }
+ if (fshift) {
+ MOV(AL, 0, d.reg, reg_imm(vreg, LSR, fshift));
+ freg = d.reg;
+ }
+ if (smulw) SMULW(AL, xy, d.reg, vreg, freg);
+ else SMUL(AL, xy, d.reg, vreg, freg);
+
+
+ d.h = ms;
+ if (mDithering) {
+ d.l = 0;
+ } else {
+ d.l = fs;
+ d.flags |= CLEAR_LO;
+ }
+}
+
+void GGLAssembler::mul_factor_add( component_t& d,
+ const integer_t& v,
+ const integer_t& f,
+ const component_t& a)
+{
+ // XXX: we could have special cases for 1 bit mul
+ Scratch scratches(registerFile());
+
+ int vs = v.size();
+ int fs = f.size();
+ int as = a.h;
+ int ms = vs+fs;
+
+ ALOGE_IF(ms>=32, "mul_factor_add overflow vs=%d, fs=%d, as=%d", vs, fs, as);
+
+ integer_t add(a.reg, a.h, a.flags);
+
+ // 'a' is a component_t but it is guaranteed to have
+ // its high bits set to 0. However in the dithering case,
+ // we can't get away with truncating the potentially bad bits
+ // so extraction is needed.
+
+ if ((mDithering) && (a.size() < ms)) {
+ // we need to expand a
+ if (!(a.flags & CORRUPTIBLE)) {
+ // ... but it's not corruptible, so we need to pick a
+ // temporary register.
+ // Try to uses the destination register first (it's likely
+ // to be usable, unless it aliases an input).
+ if (d.reg!=a.reg && d.reg!=v.reg && d.reg!=f.reg) {
+ add.reg = d.reg;
+ } else {
+ add.reg = scratches.obtain();
+ }
+ }
+ expand(add, a, ms); // extracts and expands
+ as = ms;
+ }
+
+ if (ms == as) {
+ if (vs<16 && fs<16) SMLABB(AL, d.reg, v.reg, f.reg, add.reg);
+ else MLA(AL, 0, d.reg, v.reg, f.reg, add.reg);
+ } else {
+ int temp = d.reg;
+ if (temp == add.reg) {
+ // the mul will modify add.reg, we need an intermediary reg
+ if (v.flags & CORRUPTIBLE) temp = v.reg;
+ else if (f.flags & CORRUPTIBLE) temp = f.reg;
+ else temp = scratches.obtain();
+ }
+
+ if (vs<16 && fs<16) SMULBB(AL, temp, v.reg, f.reg);
+ else MUL(AL, 0, temp, v.reg, f.reg);
+
+ if (ms>as) {
+ ADD(AL, 0, d.reg, temp, reg_imm(add.reg, LSL, ms-as));
+ } else if (ms<as) {
+ // not sure if we should expand the mul instead?
+ ADD(AL, 0, d.reg, temp, reg_imm(add.reg, LSR, as-ms));
+ }
+ }
+
+ d.h = ms;
+ if (mDithering) {
+ d.l = a.l;
+ } else {
+ d.l = fs>a.l ? fs : a.l;
+ d.flags |= CLEAR_LO;
+ }
+}
+
+void GGLAssembler::component_add(component_t& d,
+ const integer_t& dst, const integer_t& src)
+{
+ // here we're guaranteed that fragment.size() >= fb.size()
+ const int shift = src.size() - dst.size();
+ if (!shift) {
+ ADD(AL, 0, d.reg, src.reg, dst.reg);
+ } else {
+ ADD(AL, 0, d.reg, src.reg, reg_imm(dst.reg, LSL, shift));
+ }
+
+ d.h = src.size();
+ if (mDithering) {
+ d.l = 0;
+ } else {
+ d.l = shift;
+ d.flags |= CLEAR_LO;
+ }
+}
+
+void GGLAssembler::component_sat(const component_t& v)
+{
+ const int one = ((1<<v.size())-1)<<v.l;
+ CMP(AL, v.reg, imm( 1<<v.h ));
+ if (isValidImmediate(one)) {
+ MOV(HS, 0, v.reg, imm( one ));
+ } else if (isValidImmediate(~one)) {
+ MVN(HS, 0, v.reg, imm( ~one ));
+ } else {
+ MOV(HS, 0, v.reg, imm( 1<<v.h ));
+ SUB(HS, 0, v.reg, v.reg, imm( 1<<v.l ));
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
diff --git a/libpixelflinger/codeflinger/disassem.c b/libpixelflinger/codeflinger/disassem.c
new file mode 100644
index 0000000..39dd614
--- /dev/null
+++ b/libpixelflinger/codeflinger/disassem.c
@@ -0,0 +1,714 @@
+/* $NetBSD: disassem.c,v 1.14 2003/03/27 16:58:36 mycroft Exp $ */
+
+/*-
+ * Copyright (c) 1996 Mark Brinicombe.
+ * Copyright (c) 1996 Brini.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Brini.
+ * 4. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * RiscBSD kernel project
+ *
+ * db_disasm.c
+ *
+ * Kernel disassembler
+ *
+ * Created : 10/02/96
+ *
+ * Structured after the sparc/sparc/db_disasm.c by David S. Miller &
+ * Paul Kranenburg
+ *
+ * This code is not complete. Not all instructions are disassembled.
+ */
+
+#include <sys/cdefs.h>
+//__FBSDID("$FreeBSD: /repoman/r/ncvs/src/sys/arm/arm/disassem.c,v 1.2 2005/01/05 21:58:47 imp Exp $");
+#include <sys/param.h>
+#include <stdio.h>
+
+#include "disassem.h"
+#include "armreg.h"
+//#include <ddb/ddb.h>
+
+/*
+ * General instruction format
+ *
+ * insn[cc][mod] [operands]
+ *
+ * Those fields with an uppercase format code indicate that the field
+ * follows directly after the instruction before the separator i.e.
+ * they modify the instruction rather than just being an operand to
+ * the instruction. The only exception is the writeback flag which
+ * follows a operand.
+ *
+ *
+ * 2 - print Operand 2 of a data processing instruction
+ * d - destination register (bits 12-15)
+ * n - n register (bits 16-19)
+ * s - s register (bits 8-11)
+ * o - indirect register rn (bits 16-19) (used by swap)
+ * m - m register (bits 0-3)
+ * a - address operand of ldr/str instruction
+ * e - address operand of ldrh/strh instruction
+ * l - register list for ldm/stm instruction
+ * f - 1st fp operand (register) (bits 12-14)
+ * g - 2nd fp operand (register) (bits 16-18)
+ * h - 3rd fp operand (register/immediate) (bits 0-4)
+ * j - xtb rotate literal (bits 10-11)
+ * i - bfx lsb literal (bits 7-11)
+ * w - bfx width literal (bits 16-20)
+ * b - branch address
+ * t - thumb branch address (bits 24, 0-23)
+ * k - breakpoint comment (bits 0-3, 8-19)
+ * X - block transfer type
+ * Y - block transfer type (r13 base)
+ * c - comment field bits(0-23)
+ * p - saved or current status register
+ * F - PSR transfer fields
+ * D - destination-is-r15 (P) flag on TST, TEQ, CMP, CMN
+ * L - co-processor transfer size
+ * S - set status flag
+ * P - fp precision
+ * Q - fp precision (for ldf/stf)
+ * R - fp rounding
+ * v - co-processor data transfer registers + addressing mode
+ * W - writeback flag
+ * x - instruction in hex
+ * # - co-processor number
+ * y - co-processor data processing registers
+ * z - co-processor register transfer registers
+ */
+
+struct arm32_insn {
+ u_int mask;
+ u_int pattern;
+ char* name;
+ char* format;
+};
+
+static const struct arm32_insn arm32_i[] = {
+ { 0x0fffffff, 0x0ff00000, "imb", "c" }, /* Before swi */
+ { 0x0fffffff, 0x0ff00001, "imbrange", "c" }, /* Before swi */
+ { 0x0f000000, 0x0f000000, "swi", "c" },
+ { 0xfe000000, 0xfa000000, "blx", "t" }, /* Before b and bl */
+ { 0x0f000000, 0x0a000000, "b", "b" },
+ { 0x0f000000, 0x0b000000, "bl", "b" },
+ { 0x0fe000f0, 0x00000090, "mul", "Snms" },
+ { 0x0fe000f0, 0x00200090, "mla", "Snmsd" },
+ { 0x0fe000f0, 0x00800090, "umull", "Sdnms" },
+ { 0x0fe000f0, 0x00c00090, "smull", "Sdnms" },
+ { 0x0fe000f0, 0x00a00090, "umlal", "Sdnms" },
+ { 0x0fe000f0, 0x00e00090, "smlal", "Sdnms" },
+ { 0x0fff03f0, 0x06cf0070, "uxtb16", "dmj" },
+ { 0x0fe00070, 0x07e00050, "ubfx", "dmiw" },
+ { 0x0d700000, 0x04200000, "strt", "daW" },
+ { 0x0d700000, 0x04300000, "ldrt", "daW" },
+ { 0x0d700000, 0x04600000, "strbt", "daW" },
+ { 0x0d700000, 0x04700000, "ldrbt", "daW" },
+ { 0x0c500000, 0x04000000, "str", "daW" },
+ { 0x0c500000, 0x04100000, "ldr", "daW" },
+ { 0x0c500000, 0x04400000, "strb", "daW" },
+ { 0x0c500000, 0x04500000, "ldrb", "daW" },
+ { 0x0e1f0000, 0x080d0000, "stm", "YnWl" },/* separate out r13 base */
+ { 0x0e1f0000, 0x081d0000, "ldm", "YnWl" },/* separate out r13 base */
+ { 0x0e100000, 0x08000000, "stm", "XnWl" },
+ { 0x0e100000, 0x08100000, "ldm", "XnWl" },
+ { 0x0e1000f0, 0x00100090, "ldrb", "deW" },
+ { 0x0e1000f0, 0x00000090, "strb", "deW" },
+ { 0x0e1000f0, 0x001000d0, "ldrsb", "deW" },
+ { 0x0e1000f0, 0x001000b0, "ldrh", "deW" },
+ { 0x0e1000f0, 0x000000b0, "strh", "deW" },
+ { 0x0e1000f0, 0x001000f0, "ldrsh", "deW" },
+ { 0x0f200090, 0x00200090, "und", "x" }, /* Before data processing */
+ { 0x0e1000d0, 0x000000d0, "und", "x" }, /* Before data processing */
+ { 0x0ff00ff0, 0x01000090, "swp", "dmo" },
+ { 0x0ff00ff0, 0x01400090, "swpb", "dmo" },
+ { 0x0fbf0fff, 0x010f0000, "mrs", "dp" }, /* Before data processing */
+ { 0x0fb0fff0, 0x0120f000, "msr", "pFm" },/* Before data processing */
+ { 0x0fb0f000, 0x0320f000, "msr", "pF2" },/* Before data processing */
+ { 0x0ffffff0, 0x012fff10, "bx", "m" },
+ { 0x0fff0ff0, 0x016f0f10, "clz", "dm" },
+ { 0x0ffffff0, 0x012fff30, "blx", "m" },
+ { 0xfff000f0, 0xe1200070, "bkpt", "k" },
+ { 0x0de00000, 0x00000000, "and", "Sdn2" },
+ { 0x0de00000, 0x00200000, "eor", "Sdn2" },
+ { 0x0de00000, 0x00400000, "sub", "Sdn2" },
+ { 0x0de00000, 0x00600000, "rsb", "Sdn2" },
+ { 0x0de00000, 0x00800000, "add", "Sdn2" },
+ { 0x0de00000, 0x00a00000, "adc", "Sdn2" },
+ { 0x0de00000, 0x00c00000, "sbc", "Sdn2" },
+ { 0x0de00000, 0x00e00000, "rsc", "Sdn2" },
+ { 0x0df00000, 0x01100000, "tst", "Dn2" },
+ { 0x0df00000, 0x01300000, "teq", "Dn2" },
+ { 0x0df00000, 0x01500000, "cmp", "Dn2" },
+ { 0x0df00000, 0x01700000, "cmn", "Dn2" },
+ { 0x0de00000, 0x01800000, "orr", "Sdn2" },
+ { 0x0de00000, 0x01a00000, "mov", "Sd2" },
+ { 0x0de00000, 0x01c00000, "bic", "Sdn2" },
+ { 0x0de00000, 0x01e00000, "mvn", "Sd2" },
+ { 0x0ff08f10, 0x0e000100, "adf", "PRfgh" },
+ { 0x0ff08f10, 0x0e100100, "muf", "PRfgh" },
+ { 0x0ff08f10, 0x0e200100, "suf", "PRfgh" },
+ { 0x0ff08f10, 0x0e300100, "rsf", "PRfgh" },
+ { 0x0ff08f10, 0x0e400100, "dvf", "PRfgh" },
+ { 0x0ff08f10, 0x0e500100, "rdf", "PRfgh" },
+ { 0x0ff08f10, 0x0e600100, "pow", "PRfgh" },
+ { 0x0ff08f10, 0x0e700100, "rpw", "PRfgh" },
+ { 0x0ff08f10, 0x0e800100, "rmf", "PRfgh" },
+ { 0x0ff08f10, 0x0e900100, "fml", "PRfgh" },
+ { 0x0ff08f10, 0x0ea00100, "fdv", "PRfgh" },
+ { 0x0ff08f10, 0x0eb00100, "frd", "PRfgh" },
+ { 0x0ff08f10, 0x0ec00100, "pol", "PRfgh" },
+ { 0x0f008f10, 0x0e000100, "fpbop", "PRfgh" },
+ { 0x0ff08f10, 0x0e008100, "mvf", "PRfh" },
+ { 0x0ff08f10, 0x0e108100, "mnf", "PRfh" },
+ { 0x0ff08f10, 0x0e208100, "abs", "PRfh" },
+ { 0x0ff08f10, 0x0e308100, "rnd", "PRfh" },
+ { 0x0ff08f10, 0x0e408100, "sqt", "PRfh" },
+ { 0x0ff08f10, 0x0e508100, "log", "PRfh" },
+ { 0x0ff08f10, 0x0e608100, "lgn", "PRfh" },
+ { 0x0ff08f10, 0x0e708100, "exp", "PRfh" },
+ { 0x0ff08f10, 0x0e808100, "sin", "PRfh" },
+ { 0x0ff08f10, 0x0e908100, "cos", "PRfh" },
+ { 0x0ff08f10, 0x0ea08100, "tan", "PRfh" },
+ { 0x0ff08f10, 0x0eb08100, "asn", "PRfh" },
+ { 0x0ff08f10, 0x0ec08100, "acs", "PRfh" },
+ { 0x0ff08f10, 0x0ed08100, "atn", "PRfh" },
+ { 0x0f008f10, 0x0e008100, "fpuop", "PRfh" },
+ { 0x0e100f00, 0x0c000100, "stf", "QLv" },
+ { 0x0e100f00, 0x0c100100, "ldf", "QLv" },
+ { 0x0ff00f10, 0x0e000110, "flt", "PRgd" },
+ { 0x0ff00f10, 0x0e100110, "fix", "PRdh" },
+ { 0x0ff00f10, 0x0e200110, "wfs", "d" },
+ { 0x0ff00f10, 0x0e300110, "rfs", "d" },
+ { 0x0ff00f10, 0x0e400110, "wfc", "d" },
+ { 0x0ff00f10, 0x0e500110, "rfc", "d" },
+ { 0x0ff0ff10, 0x0e90f110, "cmf", "PRgh" },
+ { 0x0ff0ff10, 0x0eb0f110, "cnf", "PRgh" },
+ { 0x0ff0ff10, 0x0ed0f110, "cmfe", "PRgh" },
+ { 0x0ff0ff10, 0x0ef0f110, "cnfe", "PRgh" },
+ { 0xff100010, 0xfe000010, "mcr2", "#z" },
+ { 0x0f100010, 0x0e000010, "mcr", "#z" },
+ { 0xff100010, 0xfe100010, "mrc2", "#z" },
+ { 0x0f100010, 0x0e100010, "mrc", "#z" },
+ { 0xff000010, 0xfe000000, "cdp2", "#y" },
+ { 0x0f000010, 0x0e000000, "cdp", "#y" },
+ { 0xfe100090, 0xfc100000, "ldc2", "L#v" },
+ { 0x0e100090, 0x0c100000, "ldc", "L#v" },
+ { 0xfe100090, 0xfc000000, "stc2", "L#v" },
+ { 0x0e100090, 0x0c000000, "stc", "L#v" },
+ { 0xf550f000, 0xf550f000, "pld", "ne" },
+ { 0x0ff00ff0, 0x01000050, "qaad", "dmn" },
+ { 0x0ff00ff0, 0x01400050, "qdaad", "dmn" },
+ { 0x0ff00ff0, 0x01600050, "qdsub", "dmn" },
+ { 0x0ff00ff0, 0x01200050, "dsub", "dmn" },
+ { 0x0ff000f0, 0x01000080, "smlabb", "nmsd" }, // d & n inverted!!
+ { 0x0ff000f0, 0x010000a0, "smlatb", "nmsd" }, // d & n inverted!!
+ { 0x0ff000f0, 0x010000c0, "smlabt", "nmsd" }, // d & n inverted!!
+ { 0x0ff000f0, 0x010000e0, "smlatt", "nmsd" }, // d & n inverted!!
+ { 0x0ff000f0, 0x01400080, "smlalbb","ndms" }, // d & n inverted!!
+ { 0x0ff000f0, 0x014000a0, "smlaltb","ndms" }, // d & n inverted!!
+ { 0x0ff000f0, 0x014000c0, "smlalbt","ndms" }, // d & n inverted!!
+ { 0x0ff000f0, 0x014000e0, "smlaltt","ndms" }, // d & n inverted!!
+ { 0x0ff000f0, 0x01200080, "smlawb", "nmsd" }, // d & n inverted!!
+ { 0x0ff0f0f0, 0x012000a0, "smulwb","nms" }, // d & n inverted!!
+ { 0x0ff000f0, 0x012000c0, "smlawt", "nmsd" }, // d & n inverted!!
+ { 0x0ff0f0f0, 0x012000e0, "smulwt","nms" }, // d & n inverted!!
+ { 0x0ff0f0f0, 0x01600080, "smulbb","nms" }, // d & n inverted!!
+ { 0x0ff0f0f0, 0x016000a0, "smultb","nms" }, // d & n inverted!!
+ { 0x0ff0f0f0, 0x016000c0, "smulbt","nms" }, // d & n inverted!!
+ { 0x0ff0f0f0, 0x016000e0, "smultt","nms" }, // d & n inverted!!
+ { 0x00000000, 0x00000000, NULL, NULL }
+};
+
+static char const arm32_insn_conditions[][4] = {
+ "eq", "ne", "cs", "cc",
+ "mi", "pl", "vs", "vc",
+ "hi", "ls", "ge", "lt",
+ "gt", "le", "", "nv"
+};
+
+static char const insn_block_transfers[][4] = {
+ "da", "ia", "db", "ib"
+};
+
+static char const insn_stack_block_transfers[][4] = {
+ "ed", "ea", "fd", "fa"
+};
+
+static char const op_shifts[][4] = {
+ "lsl", "lsr", "asr", "ror"
+};
+
+static char const insn_fpa_rounding[][2] = {
+ "", "p", "m", "z"
+};
+
+static char const insn_fpa_precision[][2] = {
+ "s", "d", "e", "p"
+};
+
+static char const insn_fpaconstants[][8] = {
+ "0.0", "1.0", "2.0", "3.0",
+ "4.0", "5.0", "0.5", "10.0"
+};
+
+#define insn_condition(x) arm32_insn_conditions[(x >> 28) & 0x0f]
+#define insn_blktrans(x) insn_block_transfers[(x >> 23) & 3]
+#define insn_stkblktrans(x) insn_stack_block_transfers[(3*((x >> 20)&1))^((x >> 23)&3)]
+#define op2_shift(x) op_shifts[(x >> 5) & 3]
+#define insn_fparnd(x) insn_fpa_rounding[(x >> 5) & 0x03]
+#define insn_fpaprec(x) insn_fpa_precision[(((x >> 18) & 2)|(x >> 7)) & 1]
+#define insn_fpaprect(x) insn_fpa_precision[(((x >> 21) & 2)|(x >> 15)) & 1]
+#define insn_fpaimm(x) insn_fpaconstants[x & 0x07]
+
+/* Local prototypes */
+static void disasm_register_shift(const disasm_interface_t *di, u_int insn);
+static void disasm_print_reglist(const disasm_interface_t *di, u_int insn);
+static void disasm_insn_ldrstr(const disasm_interface_t *di, u_int insn,
+ u_int loc);
+static void disasm_insn_ldrhstrh(const disasm_interface_t *di, u_int insn,
+ u_int loc);
+static void disasm_insn_ldcstc(const disasm_interface_t *di, u_int insn,
+ u_int loc);
+static u_int disassemble_readword(u_int address);
+static void disassemble_printaddr(u_int address);
+
+u_int
+disasm(const disasm_interface_t *di, u_int loc, int __unused altfmt)
+{
+ const struct arm32_insn *i_ptr = &arm32_i[0];
+ u_int insn = di->di_readword(loc);
+ int matchp = 0;
+ int branch;
+ char* f_ptr;
+ int fmt = 0;
+
+/* di->di_printf("loc=%08x insn=%08x : ", loc, insn);*/
+
+ while (i_ptr->name) {
+ if ((insn & i_ptr->mask) == i_ptr->pattern) {
+ matchp = 1;
+ break;
+ }
+ i_ptr++;
+ }
+
+ if (!matchp) {
+ di->di_printf("und%s\t%08x\n", insn_condition(insn), insn);
+ return(loc + INSN_SIZE);
+ }
+
+ /* If instruction forces condition code, don't print it. */
+ if ((i_ptr->mask & 0xf0000000) == 0xf0000000)
+ di->di_printf("%s", i_ptr->name);
+ else
+ di->di_printf("%s%s", i_ptr->name, insn_condition(insn));
+
+ f_ptr = i_ptr->format;
+
+ /* Insert tab if there are no instruction modifiers */
+
+ if (*(f_ptr) < 'A' || *(f_ptr) > 'Z') {
+ ++fmt;
+ di->di_printf("\t");
+ }
+
+ while (*f_ptr) {
+ switch (*f_ptr) {
+ /* 2 - print Operand 2 of a data processing instruction */
+ case '2':
+ if (insn & 0x02000000) {
+ int rotate= ((insn >> 7) & 0x1e);
+
+ di->di_printf("#0x%08x",
+ (insn & 0xff) << (32 - rotate) |
+ (insn & 0xff) >> rotate);
+ } else {
+ disasm_register_shift(di, insn);
+ }
+ break;
+ /* d - destination register (bits 12-15) */
+ case 'd':
+ di->di_printf("r%d", ((insn >> 12) & 0x0f));
+ break;
+ /* D - insert 'p' if Rd is R15 */
+ case 'D':
+ if (((insn >> 12) & 0x0f) == 15)
+ di->di_printf("p");
+ break;
+ /* n - n register (bits 16-19) */
+ case 'n':
+ di->di_printf("r%d", ((insn >> 16) & 0x0f));
+ break;
+ /* s - s register (bits 8-11) */
+ case 's':
+ di->di_printf("r%d", ((insn >> 8) & 0x0f));
+ break;
+ /* o - indirect register rn (bits 16-19) (used by swap) */
+ case 'o':
+ di->di_printf("[r%d]", ((insn >> 16) & 0x0f));
+ break;
+ /* m - m register (bits 0-4) */
+ case 'm':
+ di->di_printf("r%d", ((insn >> 0) & 0x0f));
+ break;
+ /* a - address operand of ldr/str instruction */
+ case 'a':
+ disasm_insn_ldrstr(di, insn, loc);
+ break;
+ /* e - address operand of ldrh/strh instruction */
+ case 'e':
+ disasm_insn_ldrhstrh(di, insn, loc);
+ break;
+ /* l - register list for ldm/stm instruction */
+ case 'l':
+ disasm_print_reglist(di, insn);
+ break;
+ /* f - 1st fp operand (register) (bits 12-14) */
+ case 'f':
+ di->di_printf("f%d", (insn >> 12) & 7);
+ break;
+ /* g - 2nd fp operand (register) (bits 16-18) */
+ case 'g':
+ di->di_printf("f%d", (insn >> 16) & 7);
+ break;
+ /* h - 3rd fp operand (register/immediate) (bits 0-4) */
+ case 'h':
+ if (insn & (1 << 3))
+ di->di_printf("#%s", insn_fpaimm(insn));
+ else
+ di->di_printf("f%d", insn & 7);
+ break;
+ /* j - xtb rotate literal (bits 10-11) */
+ case 'j':
+ di->di_printf("ror #%d", ((insn >> 10) & 3) << 3);
+ break;
+ /* i - bfx lsb literal (bits 7-11) */
+ case 'i':
+ di->di_printf("#%d", (insn >> 7) & 31);
+ break;
+ /* w - bfx width literal (bits 16-20) */
+ case 'w':
+ di->di_printf("#%d", 1 + ((insn >> 16) & 31));
+ break;
+ /* b - branch address */
+ case 'b':
+ branch = ((insn << 2) & 0x03ffffff);
+ if (branch & 0x02000000)
+ branch |= 0xfc000000;
+ di->di_printaddr(loc + 8 + branch);
+ break;
+ /* t - blx address */
+ case 't':
+ branch = ((insn << 2) & 0x03ffffff) |
+ (insn >> 23 & 0x00000002);
+ if (branch & 0x02000000)
+ branch |= 0xfc000000;
+ di->di_printaddr(loc + 8 + branch);
+ break;
+ /* X - block transfer type */
+ case 'X':
+ di->di_printf("%s", insn_blktrans(insn));
+ break;
+ /* Y - block transfer type (r13 base) */
+ case 'Y':
+ di->di_printf("%s", insn_stkblktrans(insn));
+ break;
+ /* c - comment field bits(0-23) */
+ case 'c':
+ di->di_printf("0x%08x", (insn & 0x00ffffff));
+ break;
+ /* k - breakpoint comment (bits 0-3, 8-19) */
+ case 'k':
+ di->di_printf("0x%04x",
+ (insn & 0x000fff00) >> 4 | (insn & 0x0000000f));
+ break;
+ /* p - saved or current status register */
+ case 'p':
+ if (insn & 0x00400000)
+ di->di_printf("spsr");
+ else
+ di->di_printf("cpsr");
+ break;
+ /* F - PSR transfer fields */
+ case 'F':
+ di->di_printf("_");
+ if (insn & (1 << 16))
+ di->di_printf("c");
+ if (insn & (1 << 17))
+ di->di_printf("x");
+ if (insn & (1 << 18))
+ di->di_printf("s");
+ if (insn & (1 << 19))
+ di->di_printf("f");
+ break;
+ /* B - byte transfer flag */
+ case 'B':
+ if (insn & 0x00400000)
+ di->di_printf("b");
+ break;
+ /* L - co-processor transfer size */
+ case 'L':
+ if (insn & (1 << 22))
+ di->di_printf("l");
+ break;
+ /* S - set status flag */
+ case 'S':
+ if (insn & 0x00100000)
+ di->di_printf("s");
+ break;
+ /* P - fp precision */
+ case 'P':
+ di->di_printf("%s", insn_fpaprec(insn));
+ break;
+ /* Q - fp precision (for ldf/stf) */
+ case 'Q':
+ break;
+ /* R - fp rounding */
+ case 'R':
+ di->di_printf("%s", insn_fparnd(insn));
+ break;
+ /* W - writeback flag */
+ case 'W':
+ if (insn & (1 << 21))
+ di->di_printf("!");
+ break;
+ /* # - co-processor number */
+ case '#':
+ di->di_printf("p%d", (insn >> 8) & 0x0f);
+ break;
+ /* v - co-processor data transfer registers+addressing mode */
+ case 'v':
+ disasm_insn_ldcstc(di, insn, loc);
+ break;
+ /* x - instruction in hex */
+ case 'x':
+ di->di_printf("0x%08x", insn);
+ break;
+ /* y - co-processor data processing registers */
+ case 'y':
+ di->di_printf("%d, ", (insn >> 20) & 0x0f);
+
+ di->di_printf("c%d, c%d, c%d", (insn >> 12) & 0x0f,
+ (insn >> 16) & 0x0f, insn & 0x0f);
+
+ di->di_printf(", %d", (insn >> 5) & 0x07);
+ break;
+ /* z - co-processor register transfer registers */
+ case 'z':
+ di->di_printf("%d, ", (insn >> 21) & 0x07);
+ di->di_printf("r%d, c%d, c%d, %d",
+ (insn >> 12) & 0x0f, (insn >> 16) & 0x0f,
+ insn & 0x0f, (insn >> 5) & 0x07);
+
+/* if (((insn >> 5) & 0x07) != 0)
+ di->di_printf(", %d", (insn >> 5) & 0x07);*/
+ break;
+ default:
+ di->di_printf("[%c - unknown]", *f_ptr);
+ break;
+ }
+ if (*(f_ptr+1) >= 'A' && *(f_ptr+1) <= 'Z')
+ ++f_ptr;
+ else if (*(++f_ptr)) {
+ ++fmt;
+ if (fmt == 1)
+ di->di_printf("\t");
+ else
+ di->di_printf(", ");
+ }
+ };
+
+ di->di_printf("\n");
+
+ return(loc + INSN_SIZE);
+}
+
+
+static void
+disasm_register_shift(const disasm_interface_t *di, u_int insn)
+{
+ di->di_printf("r%d", (insn & 0x0f));
+ if ((insn & 0x00000ff0) == 0)
+ ;
+ else if ((insn & 0x00000ff0) == 0x00000060)
+ di->di_printf(", rrx");
+ else {
+ if (insn & 0x10)
+ di->di_printf(", %s r%d", op2_shift(insn),
+ (insn >> 8) & 0x0f);
+ else
+ di->di_printf(", %s #%d", op2_shift(insn),
+ (insn >> 7) & 0x1f);
+ }
+}
+
+
+static void
+disasm_print_reglist(const disasm_interface_t *di, u_int insn)
+{
+ int loop;
+ int start;
+ int comma;
+
+ di->di_printf("{");
+ start = -1;
+ comma = 0;
+
+ for (loop = 0; loop < 17; ++loop) {
+ if (start != -1) {
+ if (loop == 16 || !(insn & (1 << loop))) {
+ if (comma)
+ di->di_printf(", ");
+ else
+ comma = 1;
+ if (start == loop - 1)
+ di->di_printf("r%d", start);
+ else
+ di->di_printf("r%d-r%d", start, loop - 1);
+ start = -1;
+ }
+ } else {
+ if (insn & (1 << loop))
+ start = loop;
+ }
+ }
+ di->di_printf("}");
+
+ if (insn & (1 << 22))
+ di->di_printf("^");
+}
+
+static void
+disasm_insn_ldrstr(const disasm_interface_t *di, u_int insn, u_int loc)
+{
+ int offset;
+
+ offset = insn & 0xfff;
+ if ((insn & 0x032f0000) == 0x010f0000) {
+ /* rA = pc, immediate index */
+ if (insn & 0x00800000)
+ loc += offset;
+ else
+ loc -= offset;
+ di->di_printaddr(loc + 8);
+ } else {
+ di->di_printf("[r%d", (insn >> 16) & 0x0f);
+ if ((insn & 0x03000fff) != 0x01000000) {
+ di->di_printf("%s, ", (insn & (1 << 24)) ? "" : "]");
+ if (!(insn & 0x00800000))
+ di->di_printf("-");
+ if (insn & (1 << 25))
+ disasm_register_shift(di, insn);
+ else
+ di->di_printf("#0x%03x", offset);
+ }
+ if (insn & (1 << 24))
+ di->di_printf("]");
+ }
+}
+
+static void
+disasm_insn_ldrhstrh(const disasm_interface_t *di, u_int insn, u_int loc)
+{
+ int offset;
+
+ offset = ((insn & 0xf00) >> 4) | (insn & 0xf);
+ if ((insn & 0x004f0000) == 0x004f0000) {
+ /* rA = pc, immediate index */
+ if (insn & 0x00800000)
+ loc += offset;
+ else
+ loc -= offset;
+ di->di_printaddr(loc + 8);
+ } else {
+ di->di_printf("[r%d", (insn >> 16) & 0x0f);
+ if ((insn & 0x01400f0f) != 0x01400000) {
+ di->di_printf("%s, ", (insn & (1 << 24)) ? "" : "]");
+ if (!(insn & 0x00800000))
+ di->di_printf("-");
+ if (insn & (1 << 22))
+ di->di_printf("#0x%02x", offset);
+ else
+ di->di_printf("r%d", (insn & 0x0f));
+ }
+ if (insn & (1 << 24))
+ di->di_printf("]");
+ }
+}
+
+static void
+disasm_insn_ldcstc(const disasm_interface_t *di, u_int insn, u_int __unused loc)
+{
+ if (((insn >> 8) & 0xf) == 1)
+ di->di_printf("f%d, ", (insn >> 12) & 0x07);
+ else
+ di->di_printf("c%d, ", (insn >> 12) & 0x0f);
+
+ di->di_printf("[r%d", (insn >> 16) & 0x0f);
+
+ di->di_printf("%s, ", (insn & (1 << 24)) ? "" : "]");
+
+ if (!(insn & (1 << 23)))
+ di->di_printf("-");
+
+ di->di_printf("#0x%03x", (insn & 0xff) << 2);
+
+ if (insn & (1 << 24))
+ di->di_printf("]");
+
+ if (insn & (1 << 21))
+ di->di_printf("!");
+}
+
+static u_int
+disassemble_readword(u_int address)
+{
+ return(*((u_int *)address));
+}
+
+static void
+disassemble_printaddr(u_int address)
+{
+ printf("0x%08x", address);
+}
+
+static const disasm_interface_t disassemble_di = {
+ disassemble_readword, disassemble_printaddr, printf
+};
+
+void
+disassemble(u_int address)
+{
+
+ (void)disasm(&disassemble_di, address, 0);
+}
+
+/* End of disassem.c */
diff --git a/libpixelflinger/codeflinger/disassem.h b/libpixelflinger/codeflinger/disassem.h
new file mode 100644
index 0000000..c7c60b6
--- /dev/null
+++ b/libpixelflinger/codeflinger/disassem.h
@@ -0,0 +1,65 @@
+/* $NetBSD: disassem.h,v 1.4 2001/03/04 04:15:58 matt Exp $ */
+
+/*-
+ * Copyright (c) 1997 Mark Brinicombe.
+ * Copyright (c) 1997 Causality Limited.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Mark Brinicombe.
+ * 4. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Define the interface structure required by the disassembler.
+ *
+ * $FreeBSD: /repoman/r/ncvs/src/sys/arm/include/disassem.h,v 1.2 2005/01/05 21:58:48 imp Exp $
+ */
+
+#ifndef ANDROID_MACHINE_DISASSEM_H
+#define ANDROID_MACHINE_DISASSEM_H
+
+#include <sys/types.h>
+
+#if __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+ u_int (*di_readword)(u_int);
+ void (*di_printaddr)(u_int);
+ int (*di_printf)(const char *, ...);
+} disasm_interface_t;
+
+/* Prototypes for callable functions */
+
+u_int disasm(const disasm_interface_t *, u_int, int);
+void disassemble(u_int);
+
+#if __cplusplus
+}
+#endif
+
+#endif /* !ANDROID_MACHINE_DISASSEM_H */
diff --git a/libpixelflinger/codeflinger/load_store.cpp b/libpixelflinger/codeflinger/load_store.cpp
new file mode 100644
index 0000000..e5a1ae0
--- /dev/null
+++ b/libpixelflinger/codeflinger/load_store.cpp
@@ -0,0 +1,381 @@
+/* libs/pixelflinger/codeflinger/load_store.cpp
+**
+** Copyright 2006, 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 <assert.h>
+#include <stdio.h>
+#include <cutils/log.h>
+#include "GGLAssembler.h"
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+void GGLAssembler::store(const pointer_t& addr, const pixel_t& s, uint32_t flags)
+{
+ const int bits = addr.size;
+ const int inc = (flags & WRITE_BACK)?1:0;
+ switch (bits) {
+ case 32:
+ if (inc) STR(AL, s.reg, addr.reg, immed12_post(4));
+ else STR(AL, s.reg, addr.reg);
+ break;
+ case 24:
+ // 24 bits formats are a little special and used only for RGB
+ // 0x00BBGGRR is unpacked as R,G,B
+ STRB(AL, s.reg, addr.reg, immed12_pre(0));
+ MOV(AL, 0, s.reg, reg_imm(s.reg, ROR, 8));
+ STRB(AL, s.reg, addr.reg, immed12_pre(1));
+ MOV(AL, 0, s.reg, reg_imm(s.reg, ROR, 8));
+ STRB(AL, s.reg, addr.reg, immed12_pre(2));
+ if (!(s.flags & CORRUPTIBLE)) {
+ MOV(AL, 0, s.reg, reg_imm(s.reg, ROR, 16));
+ }
+ if (inc)
+ ADD(AL, 0, addr.reg, addr.reg, imm(3));
+ break;
+ case 16:
+ if (inc) STRH(AL, s.reg, addr.reg, immed8_post(2));
+ else STRH(AL, s.reg, addr.reg);
+ break;
+ case 8:
+ if (inc) STRB(AL, s.reg, addr.reg, immed12_post(1));
+ else STRB(AL, s.reg, addr.reg);
+ break;
+ }
+}
+
+void GGLAssembler::load(const pointer_t& addr, const pixel_t& s, uint32_t flags)
+{
+ Scratch scratches(registerFile());
+ int s0;
+
+ const int bits = addr.size;
+ const int inc = (flags & WRITE_BACK)?1:0;
+ switch (bits) {
+ case 32:
+ if (inc) LDR(AL, s.reg, addr.reg, immed12_post(4));
+ else LDR(AL, s.reg, addr.reg);
+ break;
+ case 24:
+ // 24 bits formats are a little special and used only for RGB
+ // R,G,B is packed as 0x00BBGGRR
+ s0 = scratches.obtain();
+ if (s.reg != addr.reg) {
+ LDRB(AL, s.reg, addr.reg, immed12_pre(0)); // R
+ LDRB(AL, s0, addr.reg, immed12_pre(1)); // G
+ ORR(AL, 0, s.reg, s.reg, reg_imm(s0, LSL, 8));
+ LDRB(AL, s0, addr.reg, immed12_pre(2)); // B
+ ORR(AL, 0, s.reg, s.reg, reg_imm(s0, LSL, 16));
+ } else {
+ int s1 = scratches.obtain();
+ LDRB(AL, s1, addr.reg, immed12_pre(0)); // R
+ LDRB(AL, s0, addr.reg, immed12_pre(1)); // G
+ ORR(AL, 0, s1, s1, reg_imm(s0, LSL, 8));
+ LDRB(AL, s0, addr.reg, immed12_pre(2)); // B
+ ORR(AL, 0, s.reg, s1, reg_imm(s0, LSL, 16));
+ }
+ if (inc)
+ ADD(AL, 0, addr.reg, addr.reg, imm(3));
+ break;
+ case 16:
+ if (inc) LDRH(AL, s.reg, addr.reg, immed8_post(2));
+ else LDRH(AL, s.reg, addr.reg);
+ break;
+ case 8:
+ if (inc) LDRB(AL, s.reg, addr.reg, immed12_post(1));
+ else LDRB(AL, s.reg, addr.reg);
+ break;
+ }
+}
+
+void GGLAssembler::extract(integer_t& d, int s, int h, int l, int bits)
+{
+ const int maskLen = h-l;
+
+#ifdef __mips__
+ assert(maskLen<=11);
+#else
+ assert(maskLen<=8);
+#endif
+ assert(h);
+
+ if (h != bits) {
+ const int mask = ((1<<maskLen)-1) << l;
+ if (isValidImmediate(mask)) {
+ AND(AL, 0, d.reg, s, imm(mask)); // component = packed & mask;
+ } else if (isValidImmediate(~mask)) {
+ BIC(AL, 0, d.reg, s, imm(~mask)); // component = packed & mask;
+ } else {
+ MOV(AL, 0, d.reg, reg_imm(s, LSL, 32-h));
+ l += 32-h;
+ h = 32;
+ }
+ s = d.reg;
+ }
+
+ if (l) {
+ MOV(AL, 0, d.reg, reg_imm(s, LSR, l)); // component = packed >> l;
+ s = d.reg;
+ }
+
+ if (s != d.reg) {
+ MOV(AL, 0, d.reg, s);
+ }
+
+ d.s = maskLen;
+}
+
+void GGLAssembler::extract(integer_t& d, const pixel_t& s, int component)
+{
+ extract(d, s.reg,
+ s.format.c[component].h,
+ s.format.c[component].l,
+ s.size());
+}
+
+void GGLAssembler::extract(component_t& d, const pixel_t& s, int component)
+{
+ integer_t r(d.reg, 32, d.flags);
+ extract(r, s.reg,
+ s.format.c[component].h,
+ s.format.c[component].l,
+ s.size());
+ d = component_t(r);
+}
+
+
+void GGLAssembler::expand(integer_t& d, const component_t& s, int dbits)
+{
+ if (s.l || (s.flags & CLEAR_HI)) {
+ extract(d, s.reg, s.h, s.l, 32);
+ expand(d, d, dbits);
+ } else {
+ expand(d, integer_t(s.reg, s.size(), s.flags), dbits);
+ }
+}
+
+void GGLAssembler::expand(component_t& d, const component_t& s, int dbits)
+{
+ integer_t r(d.reg, 32, d.flags);
+ expand(r, s, dbits);
+ d = component_t(r);
+}
+
+void GGLAssembler::expand(integer_t& dst, const integer_t& src, int dbits)
+{
+ assert(src.size());
+
+ int sbits = src.size();
+ int s = src.reg;
+ int d = dst.reg;
+
+ // be sure to set 'dst' after we read 'src' as they may be identical
+ dst.s = dbits;
+ dst.flags = 0;
+
+ if (dbits<=sbits) {
+ if (s != d) {
+ MOV(AL, 0, d, s);
+ }
+ return;
+ }
+
+ if (sbits == 1) {
+ RSB(AL, 0, d, s, reg_imm(s, LSL, dbits));
+ // d = (s<<dbits) - s;
+ return;
+ }
+
+ if (dbits % sbits) {
+ MOV(AL, 0, d, reg_imm(s, LSL, dbits-sbits));
+ // d = s << (dbits-sbits);
+ dbits -= sbits;
+ do {
+ ORR(AL, 0, d, d, reg_imm(d, LSR, sbits));
+ // d |= d >> sbits;
+ dbits -= sbits;
+ sbits *= 2;
+ } while(dbits>0);
+ return;
+ }
+
+ dbits -= sbits;
+ do {
+ ORR(AL, 0, d, s, reg_imm(s, LSL, sbits));
+ // d |= d<<sbits;
+ s = d;
+ dbits -= sbits;
+ if (sbits*2 < dbits) {
+ sbits *= 2;
+ }
+ } while(dbits>0);
+}
+
+void GGLAssembler::downshift(
+ pixel_t& d, int component, component_t s, const reg_t& dither)
+{
+ const needs_t& needs = mBuilderContext.needs;
+ Scratch scratches(registerFile());
+
+ int sh = s.h;
+ int sl = s.l;
+ int maskHiBits = (sh!=32) ? ((s.flags & CLEAR_HI)?1:0) : 0;
+ int maskLoBits = (sl!=0) ? ((s.flags & CLEAR_LO)?1:0) : 0;
+ int sbits = sh - sl;
+
+ int dh = d.format.c[component].h;
+ int dl = d.format.c[component].l;
+ int dbits = dh - dl;
+ int dithering = 0;
+
+ ALOGE_IF(sbits<dbits, "sbits (%d) < dbits (%d) in downshift", sbits, dbits);
+
+ if (sbits>dbits) {
+ // see if we need to dither
+ dithering = mDithering;
+ }
+
+ int ireg = d.reg;
+ if (!(d.flags & FIRST)) {
+ if (s.flags & CORRUPTIBLE) {
+ ireg = s.reg;
+ } else {
+ ireg = scratches.obtain();
+ }
+ }
+ d.flags &= ~FIRST;
+
+ if (maskHiBits) {
+ // we need to mask the high bits (and possibly the lowbits too)
+ // and we might be able to use immediate mask.
+ if (!dithering) {
+ // we don't do this if we only have maskLoBits because we can
+ // do it more efficiently below (in the case where dl=0)
+ const int offset = sh - dbits;
+ if (dbits<=8 && offset >= 0) {
+ const uint32_t mask = ((1<<dbits)-1) << offset;
+ if (isValidImmediate(mask) || isValidImmediate(~mask)) {
+ build_and_immediate(ireg, s.reg, mask, 32);
+ sl = offset;
+ s.reg = ireg;
+ sbits = dbits;
+ maskLoBits = maskHiBits = 0;
+ }
+ }
+ } else {
+ // in the dithering case though, we need to preserve the lower bits
+ const uint32_t mask = ((1<<sbits)-1) << sl;
+ if (isValidImmediate(mask) || isValidImmediate(~mask)) {
+ build_and_immediate(ireg, s.reg, mask, 32);
+ s.reg = ireg;
+ maskLoBits = maskHiBits = 0;
+ }
+ }
+ }
+
+ // XXX: we could special case (maskHiBits & !maskLoBits)
+ // like we do for maskLoBits below, but it happens very rarely
+ // that we have maskHiBits only and the conditions necessary to lead
+ // to better code (like doing d |= s << 24)
+
+ if (maskHiBits) {
+ MOV(AL, 0, ireg, reg_imm(s.reg, LSL, 32-sh));
+ sl += 32-sh;
+ sh = 32;
+ s.reg = ireg;
+ maskHiBits = 0;
+ }
+
+ // Downsampling should be performed as follows:
+ // V * ((1<<dbits)-1) / ((1<<sbits)-1)
+ // V * [(1<<dbits)/((1<<sbits)-1) - 1/((1<<sbits)-1)]
+ // V * [1/((1<<sbits)-1)>>dbits - 1/((1<<sbits)-1)]
+ // V/((1<<(sbits-dbits))-(1>>dbits)) - (V>>sbits)/((1<<sbits)-1)>>sbits
+ // V/((1<<(sbits-dbits))-(1>>dbits)) - (V>>sbits)/(1-(1>>sbits))
+ //
+ // By approximating (1>>dbits) and (1>>sbits) to 0:
+ //
+ // V>>(sbits-dbits) - V>>sbits
+ //
+ // A good approximation is V>>(sbits-dbits),
+ // but better one (needed for dithering) is:
+ //
+ // (V>>(sbits-dbits)<<sbits - V)>>sbits
+ // (V<<dbits - V)>>sbits
+ // (V - V>>dbits)>>(sbits-dbits)
+
+ // Dithering is done here
+ if (dithering) {
+ comment("dithering");
+ if (sl) {
+ MOV(AL, 0, ireg, reg_imm(s.reg, LSR, sl));
+ sh -= sl;
+ sl = 0;
+ s.reg = ireg;
+ }
+ // scaling (V-V>>dbits)
+ SUB(AL, 0, ireg, s.reg, reg_imm(s.reg, LSR, dbits));
+ const int shift = (GGL_DITHER_BITS - (sbits-dbits));
+ if (shift>0) ADD(AL, 0, ireg, ireg, reg_imm(dither.reg, LSR, shift));
+ else if (shift<0) ADD(AL, 0, ireg, ireg, reg_imm(dither.reg, LSL,-shift));
+ else ADD(AL, 0, ireg, ireg, dither.reg);
+ s.reg = ireg;
+ }
+
+ if ((maskLoBits|dithering) && (sh > dbits)) {
+ int shift = sh-dbits;
+ if (dl) {
+ MOV(AL, 0, ireg, reg_imm(s.reg, LSR, shift));
+ if (ireg == d.reg) {
+ MOV(AL, 0, d.reg, reg_imm(ireg, LSL, dl));
+ } else {
+ ORR(AL, 0, d.reg, d.reg, reg_imm(ireg, LSL, dl));
+ }
+ } else {
+ if (ireg == d.reg) {
+ MOV(AL, 0, d.reg, reg_imm(s.reg, LSR, shift));
+ } else {
+ ORR(AL, 0, d.reg, d.reg, reg_imm(s.reg, LSR, shift));
+ }
+ }
+ } else {
+ int shift = sh-dh;
+ if (shift>0) {
+ if (ireg == d.reg) {
+ MOV(AL, 0, d.reg, reg_imm(s.reg, LSR, shift));
+ } else {
+ ORR(AL, 0, d.reg, d.reg, reg_imm(s.reg, LSR, shift));
+ }
+ } else if (shift<0) {
+ if (ireg == d.reg) {
+ MOV(AL, 0, d.reg, reg_imm(s.reg, LSL, -shift));
+ } else {
+ ORR(AL, 0, d.reg, d.reg, reg_imm(s.reg, LSL, -shift));
+ }
+ } else {
+ if (ireg == d.reg) {
+ if (s.reg != d.reg) {
+ MOV(AL, 0, d.reg, s.reg);
+ }
+ } else {
+ ORR(AL, 0, d.reg, d.reg, s.reg);
+ }
+ }
+ }
+}
+
+}; // namespace android
diff --git a/libpixelflinger/codeflinger/mips_disassem.c b/libpixelflinger/codeflinger/mips_disassem.c
new file mode 100644
index 0000000..4ab9bd3
--- /dev/null
+++ b/libpixelflinger/codeflinger/mips_disassem.c
@@ -0,0 +1,590 @@
+/* $NetBSD: db_disasm.c,v 1.19 2007/02/28 04:21:53 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Ralph Campbell.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)kadb.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <sys/cdefs.h>
+
+#include <sys/types.h>
+#include "mips_opcode.h"
+
+
+// #include <sys/systm.h>
+// #include <sys/param.h>
+
+// #include <machine/reg.h>
+// #include <machine/cpu.h>
+/*#include <machine/param.h>*/
+// #include <machine/db_machdep.h>
+
+// #include <ddb/db_interface.h>
+// #include <ddb/db_output.h>
+// #include <ddb/db_extern.h>
+// #include <ddb/db_sym.h>
+
+
+static char *sprintf_buffer;
+static int sprintf_buf_len;
+
+
+typedef uint32_t db_addr_t;
+static void db_printf(const char* fmt, ...);
+
+static const char * const op_name[64] = {
+/* 0 */ "spec", "bcond","j ", "jal", "beq", "bne", "blez", "bgtz",
+/* 8 */ "addi", "addiu","slti", "sltiu","andi", "ori", "xori", "lui",
+/*16 */ "cop0", "cop1", "cop2", "cop3", "beql", "bnel", "blezl","bgtzl",
+/*24 */ "daddi","daddiu","ldl", "ldr", "op34", "op35", "op36", "op37",
+/*32 */ "lb ", "lh ", "lwl", "lw ", "lbu", "lhu", "lwr", "lwu",
+/*40 */ "sb ", "sh ", "swl", "sw ", "sdl", "sdr", "swr", "cache",
+/*48 */ "ll ", "lwc1", "lwc2", "lwc3", "lld", "ldc1", "ldc2", "ld ",
+/*56 */ "sc ", "swc1", "swc2", "swc3", "scd", "sdc1", "sdc2", "sd "
+};
+
+static const char * const spec_name[64] = {
+/* 0 */ "sll", "spec01","srl", "sra", "sllv", "spec05","srlv","srav",
+/* 8 */ "jr", "jalr", "movz","movn","syscall","break","spec16","sync",
+/*16 */ "mfhi", "mthi", "mflo", "mtlo", "dsllv","spec25","dsrlv","dsrav",
+/*24 */ "mult", "multu","div", "divu", "dmult","dmultu","ddiv","ddivu",
+/*32 */ "add", "addu", "sub", "subu", "and", "or ", "xor", "nor",
+/*40 */ "spec50","spec51","slt","sltu", "dadd","daddu","dsub","dsubu",
+/*48 */ "tge","tgeu","tlt","tltu","teq","spec65","tne","spec67",
+/*56 */ "dsll","spec71","dsrl","dsra","dsll32","spec75","dsrl32","dsra32"
+};
+
+static const char * const spec2_name[64] = { /* QED RM4650, R5000, etc. */
+/* 0x00 */ "madd", "maddu", "mul", "spec3", "msub", "msubu", "rsrv6", "rsrv7",
+/* 0x08 */ "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv",
+/* 0x10 */ "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv",
+/* 0x18 */ "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv",
+/* 0x20 */ "clz", "clo", "rsrv", "rsrv", "dclz", "dclo", "rsrv", "rsrv",
+/* 0x28 */ "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv",
+/* 0x30 */ "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv",
+/* 0x38 */ "rsrv", "rsrv", "rsrv", "resv", "rsrv", "rsrv", "rsrv", "sdbbp"
+};
+
+static const char * const bcond_name[32] = {
+/* 0 */ "bltz", "bgez", "bltzl", "bgezl", "?", "?", "?", "?",
+/* 8 */ "tgei", "tgeiu", "tlti", "tltiu", "teqi", "?", "tnei", "?",
+/*16 */ "bltzal", "bgezal", "bltzall", "bgezall", "?", "?", "?", "?",
+/*24 */ "?", "?", "?", "?", "?", "?", "?", "?",
+};
+
+static const char * const cop1_name[64] = {
+/* 0 */ "fadd", "fsub", "fmpy", "fdiv", "fsqrt","fabs", "fmov", "fneg",
+/* 8 */ "fop08","fop09","fop0a","fop0b","fop0c","fop0d","fop0e","fop0f",
+/*16 */ "fop10","fop11","fop12","fop13","fop14","fop15","fop16","fop17",
+/*24 */ "fop18","fop19","fop1a","fop1b","fop1c","fop1d","fop1e","fop1f",
+/*32 */ "fcvts","fcvtd","fcvte","fop23","fcvtw","fop25","fop26","fop27",
+/*40 */ "fop28","fop29","fop2a","fop2b","fop2c","fop2d","fop2e","fop2f",
+/*48 */ "fcmp.f","fcmp.un","fcmp.eq","fcmp.ueq","fcmp.olt","fcmp.ult",
+ "fcmp.ole","fcmp.ule",
+/*56 */ "fcmp.sf","fcmp.ngle","fcmp.seq","fcmp.ngl","fcmp.lt","fcmp.nge",
+ "fcmp.le","fcmp.ngt"
+};
+
+static const char * const fmt_name[16] = {
+ "s", "d", "e", "fmt3",
+ "w", "fmt5", "fmt6", "fmt7",
+ "fmt8", "fmt9", "fmta", "fmtb",
+ "fmtc", "fmtd", "fmte", "fmtf"
+};
+
+#if defined(__mips_n32) || defined(__mips_n64)
+static char * const reg_name[32] = {
+ "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3",
+ "a4", "a5", "a6", "a7", "t0", "t1", "t2", "t3",
+ "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
+ "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra"
+};
+#else
+
+static char * alt_arm_reg_name[32] = { // hacked names for comparison with ARM code
+ "zero", "at", "r0", "r1", "r2", "r3", "r4", "r5",
+ "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r13",
+ "r14", "r15", "at2", "cmp", "s4", "s5", "s6", "s7",
+ "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra"
+};
+
+static char * mips_reg_name[32] = {
+ "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3",
+ "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
+ "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
+ "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra"
+};
+
+static char ** reg_name = &mips_reg_name[0];
+
+#endif /* __mips_n32 || __mips_n64 */
+
+static const char * const c0_opname[64] = {
+ "c0op00","tlbr", "tlbwi", "c0op03","c0op04","c0op05","tlbwr", "c0op07",
+ "tlbp", "c0op11","c0op12","c0op13","c0op14","c0op15","c0op16","c0op17",
+ "rfe", "c0op21","c0op22","c0op23","c0op24","c0op25","c0op26","c0op27",
+ "eret", "c0op31","c0op32","c0op33","c0op34","c0op35","c0op36","c0op37",
+ "c0op40","c0op41","c0op42","c0op43","c0op44","c0op45","c0op46","c0op47",
+ "c0op50","c0op51","c0op52","c0op53","c0op54","c0op55","c0op56","c0op57",
+ "c0op60","c0op61","c0op62","c0op63","c0op64","c0op65","c0op66","c0op67",
+ "c0op70","c0op71","c0op72","c0op73","c0op74","c0op75","c0op77","c0op77",
+};
+
+static const char * const c0_reg[32] = {
+ "index", "random", "tlblo0", "tlblo1",
+ "context", "pagemask", "wired", "cp0r7",
+ "badvaddr", "count", "tlbhi", "compare",
+ "status", "cause", "epc", "prid",
+ "config", "lladdr", "watchlo", "watchhi",
+ "xcontext", "cp0r21", "cp0r22", "debug",
+ "depc", "perfcnt", "ecc", "cacheerr",
+ "taglo", "taghi", "errepc", "desave"
+};
+
+static void print_addr(db_addr_t);
+db_addr_t mips_disassem(db_addr_t loc, char *di_buffer, int alt_dis_format);
+
+
+/*
+ * Disassemble instruction 'insn' nominally at 'loc'.
+ * 'loc' may in fact contain a breakpoint instruction.
+ */
+static db_addr_t
+db_disasm_insn(int insn, db_addr_t loc, bool altfmt)
+{
+ bool bdslot = false;
+ InstFmt i;
+
+ i.word = insn;
+
+ switch (i.JType.op) {
+ case OP_SPECIAL:
+ if (i.word == 0) {
+ db_printf("nop");
+ break;
+ }
+ if (i.word == 0x0080) {
+ db_printf("NIY");
+ break;
+ }
+ if (i.word == 0x00c0) {
+ db_printf("NOT IMPL");
+ break;
+ }
+ /* Special cases --------------------------------------------------
+ * "addu" is a "move" only in 32-bit mode. What's the correct
+ * answer - never decode addu/daddu as "move"?
+ */
+ if ( (i.RType.func == OP_ADDU && i.RType.rt == 0) ||
+ (i.RType.func == OP_OR && i.RType.rt == 0) ) {
+ db_printf("move\t%s,%s",
+ reg_name[i.RType.rd],
+ reg_name[i.RType.rs]);
+ break;
+ }
+ // mips32r2, rotr & rotrv
+ if (i.RType.func == OP_SRL && (i.RType.rs & 1) == 1) {
+ db_printf("rotr\t%s,%s,%d", reg_name[i.RType.rd],
+ reg_name[i.RType.rt], i.RType.shamt);
+ break;
+ }
+ if (i.RType.func == OP_SRLV && (i.RType.shamt & 1) == 1) {
+ db_printf("rotrv\t%s,%s,%s", reg_name[i.RType.rd],
+ reg_name[i.RType.rt], reg_name[i.RType.rs]);
+ break;
+ }
+
+
+ db_printf("%s", spec_name[i.RType.func]);
+ switch (i.RType.func) {
+ case OP_SLL:
+ case OP_SRL:
+ case OP_SRA:
+ case OP_DSLL:
+
+ case OP_DSRL:
+ case OP_DSRA:
+ case OP_DSLL32:
+ case OP_DSRL32:
+ case OP_DSRA32:
+ db_printf("\t%s,%s,%d",
+ reg_name[i.RType.rd],
+ reg_name[i.RType.rt],
+ i.RType.shamt);
+ break;
+
+ case OP_SLLV:
+ case OP_SRLV:
+ case OP_SRAV:
+ case OP_DSLLV:
+ case OP_DSRLV:
+ case OP_DSRAV:
+ db_printf("\t%s,%s,%s",
+ reg_name[i.RType.rd],
+ reg_name[i.RType.rt],
+ reg_name[i.RType.rs]);
+ break;
+
+ case OP_MFHI:
+ case OP_MFLO:
+ db_printf("\t%s", reg_name[i.RType.rd]);
+ break;
+
+ case OP_JR:
+ case OP_JALR:
+ db_printf("\t%s", reg_name[i.RType.rs]);
+ bdslot = true;
+ break;
+ case OP_MTLO:
+ case OP_MTHI:
+ db_printf("\t%s", reg_name[i.RType.rs]);
+ break;
+
+ case OP_MULT:
+ case OP_MULTU:
+ case OP_DMULT:
+ case OP_DMULTU:
+ case OP_DIV:
+ case OP_DIVU:
+ case OP_DDIV:
+ case OP_DDIVU:
+ db_printf("\t%s,%s",
+ reg_name[i.RType.rs],
+ reg_name[i.RType.rt]);
+ break;
+
+
+ case OP_SYSCALL:
+ case OP_SYNC:
+ break;
+
+ case OP_BREAK:
+ db_printf("\t%d", (i.RType.rs << 5) | i.RType.rt);
+ break;
+
+ default:
+ db_printf("\t%s,%s,%s",
+ reg_name[i.RType.rd],
+ reg_name[i.RType.rs],
+ reg_name[i.RType.rt]);
+ }
+ break;
+
+ case OP_SPECIAL2:
+ if (i.RType.func == OP_MUL)
+ db_printf("%s\t%s,%s,%s",
+ spec2_name[i.RType.func & 0x3f],
+ reg_name[i.RType.rd],
+ reg_name[i.RType.rs],
+ reg_name[i.RType.rt]);
+ else
+ db_printf("%s\t%s,%s",
+ spec2_name[i.RType.func & 0x3f],
+ reg_name[i.RType.rs],
+ reg_name[i.RType.rt]);
+
+ break;
+
+ case OP_SPECIAL3:
+ if (i.RType.func == OP_EXT)
+ db_printf("ext\t%s,%s,%d,%d",
+ reg_name[i.RType.rt],
+ reg_name[i.RType.rs],
+ i.RType.rd+1,
+ i.RType.shamt);
+ else if (i.RType.func == OP_INS)
+ db_printf("ins\t%s,%s,%d,%d",
+ reg_name[i.RType.rt],
+ reg_name[i.RType.rs],
+ i.RType.rd+1,
+ i.RType.shamt);
+ else if (i.RType.func == OP_BSHFL && i.RType.shamt == OP_WSBH)
+ db_printf("wsbh\t%s,%s",
+ reg_name[i.RType.rd],
+ reg_name[i.RType.rt]);
+ else if (i.RType.func == OP_BSHFL && i.RType.shamt == OP_SEB)
+ db_printf("seb\t%s,%s",
+ reg_name[i.RType.rd],
+ reg_name[i.RType.rt]);
+ else if (i.RType.func == OP_BSHFL && i.RType.shamt == OP_SEH)
+ db_printf("seh\t%s,%s",
+ reg_name[i.RType.rd],
+ reg_name[i.RType.rt]);
+ else
+ db_printf("Unknown");
+ break;
+
+ case OP_BCOND:
+ db_printf("%s\t%s,", bcond_name[i.IType.rt],
+ reg_name[i.IType.rs]);
+ goto pr_displ;
+
+ case OP_BLEZ:
+ case OP_BLEZL:
+ case OP_BGTZ:
+ case OP_BGTZL:
+ db_printf("%s\t%s,", op_name[i.IType.op],
+ reg_name[i.IType.rs]);
+ goto pr_displ;
+
+ case OP_BEQ:
+ case OP_BEQL:
+ if (i.IType.rs == 0 && i.IType.rt == 0) {
+ db_printf("b \t");
+ goto pr_displ;
+ }
+ /* FALLTHROUGH */
+ case OP_BNE:
+ case OP_BNEL:
+ db_printf("%s\t%s,%s,", op_name[i.IType.op],
+ reg_name[i.IType.rs],
+ reg_name[i.IType.rt]);
+ pr_displ:
+ print_addr(loc + 4 + ((short)i.IType.imm << 2));
+ bdslot = true;
+ break;
+
+ case OP_COP0:
+ switch (i.RType.rs) {
+ case OP_BCx:
+ case OP_BCy:
+
+ db_printf("bc0%c\t",
+ "ft"[i.RType.rt & COPz_BC_TF_MASK]);
+ goto pr_displ;
+
+ case OP_MT:
+ db_printf("mtc0\t%s,%s",
+ reg_name[i.RType.rt],
+ c0_reg[i.RType.rd]);
+ break;
+
+ case OP_DMT:
+ db_printf("dmtc0\t%s,%s",
+ reg_name[i.RType.rt],
+ c0_reg[i.RType.rd]);
+ break;
+
+ case OP_MF:
+ db_printf("mfc0\t%s,%s",
+ reg_name[i.RType.rt],
+ c0_reg[i.RType.rd]);
+ break;
+
+ case OP_DMF:
+ db_printf("dmfc0\t%s,%s",
+ reg_name[i.RType.rt],
+ c0_reg[i.RType.rd]);
+ break;
+
+ default:
+ db_printf("%s", c0_opname[i.FRType.func]);
+ }
+ break;
+
+ case OP_COP1:
+ switch (i.RType.rs) {
+ case OP_BCx:
+ case OP_BCy:
+ db_printf("bc1%c\t",
+ "ft"[i.RType.rt & COPz_BC_TF_MASK]);
+ goto pr_displ;
+
+ case OP_MT:
+ db_printf("mtc1\t%s,f%d",
+ reg_name[i.RType.rt],
+ i.RType.rd);
+ break;
+
+ case OP_MF:
+ db_printf("mfc1\t%s,f%d",
+ reg_name[i.RType.rt],
+ i.RType.rd);
+ break;
+
+ case OP_CT:
+ db_printf("ctc1\t%s,f%d",
+ reg_name[i.RType.rt],
+ i.RType.rd);
+ break;
+
+ case OP_CF:
+ db_printf("cfc1\t%s,f%d",
+ reg_name[i.RType.rt],
+ i.RType.rd);
+ break;
+
+ default:
+ db_printf("%s.%s\tf%d,f%d,f%d",
+ cop1_name[i.FRType.func],
+ fmt_name[i.FRType.fmt],
+ i.FRType.fd, i.FRType.fs, i.FRType.ft);
+ }
+ break;
+
+ case OP_J:
+ case OP_JAL:
+ db_printf("%s\t", op_name[i.JType.op]);
+ print_addr((loc & 0xF0000000) | (i.JType.target << 2));
+ bdslot = true;
+ break;
+
+ case OP_LWC1:
+ case OP_SWC1:
+ db_printf("%s\tf%d,", op_name[i.IType.op],
+ i.IType.rt);
+ goto loadstore;
+
+ case OP_LB:
+ case OP_LH:
+ case OP_LW:
+ case OP_LD:
+ case OP_LBU:
+ case OP_LHU:
+ case OP_LWU:
+ case OP_SB:
+ case OP_SH:
+ case OP_SW:
+ case OP_SD:
+ db_printf("%s\t%s,", op_name[i.IType.op],
+ reg_name[i.IType.rt]);
+ loadstore:
+ db_printf("%d(%s)", (short)i.IType.imm,
+ reg_name[i.IType.rs]);
+ break;
+
+ case OP_ORI:
+ case OP_XORI:
+ if (i.IType.rs == 0) {
+ db_printf("li\t%s,0x%x",
+ reg_name[i.IType.rt],
+ i.IType.imm);
+ break;
+ }
+ /* FALLTHROUGH */
+ case OP_ANDI:
+ db_printf("%s\t%s,%s,0x%x", op_name[i.IType.op],
+ reg_name[i.IType.rt],
+ reg_name[i.IType.rs],
+ i.IType.imm);
+ break;
+
+ case OP_LUI:
+ db_printf("%s\t%s,0x%x", op_name[i.IType.op],
+ reg_name[i.IType.rt],
+ i.IType.imm);
+ break;
+
+ case OP_CACHE:
+ db_printf("%s\t0x%x,0x%x(%s)",
+ op_name[i.IType.op],
+ i.IType.rt,
+ i.IType.imm,
+ reg_name[i.IType.rs]);
+ break;
+
+ case OP_ADDI:
+ case OP_DADDI:
+ case OP_ADDIU:
+ case OP_DADDIU:
+ if (i.IType.rs == 0) {
+ db_printf("li\t%s,%d",
+ reg_name[i.IType.rt],
+ (short)i.IType.imm);
+ break;
+ }
+ /* FALLTHROUGH */
+ default:
+ db_printf("%s\t%s,%s,%d", op_name[i.IType.op],
+ reg_name[i.IType.rt],
+ reg_name[i.IType.rs],
+ (short)i.IType.imm);
+ }
+ // db_printf("\n");
+ // if (bdslot) {
+ // db_printf(" bd: ");
+ // mips_disassem(loc+4);
+ // return (loc + 8);
+ // }
+ return (loc + 4);
+}
+
+static void
+print_addr(db_addr_t loc)
+{
+ db_printf("0x%08x", loc);
+}
+
+
+
+static void db_printf(const char* fmt, ...)
+{
+ int cnt;
+ va_list argp;
+ va_start(argp, fmt);
+ if (sprintf_buffer) {
+ cnt = vsnprintf(sprintf_buffer, sprintf_buf_len, fmt, argp);
+ sprintf_buffer += cnt;
+ sprintf_buf_len -= cnt;
+ } else {
+ vprintf(fmt, argp);
+ }
+}
+
+
+/*
+ * Disassemble instruction at 'loc'.
+ * Return address of start of next instruction.
+ * Since this function is used by 'examine' and by 'step'
+ * "next instruction" does NOT mean the next instruction to
+ * be executed but the 'linear' next instruction.
+ */
+db_addr_t
+mips_disassem(db_addr_t loc, char *di_buffer, int alt_dis_format)
+{
+ u_int32_t instr;
+
+ if (alt_dis_format) { // use ARM register names for disassembly
+ reg_name = &alt_arm_reg_name[0];
+ }
+
+ sprintf_buffer = di_buffer; // quick 'n' dirty printf() vs sprintf()
+ sprintf_buf_len = 39; // should be passed in
+
+ instr = *(u_int32_t *)loc;
+ return (db_disasm_insn(instr, loc, false));
+}
+
diff --git a/libpixelflinger/codeflinger/mips_disassem.h b/libpixelflinger/codeflinger/mips_disassem.h
new file mode 100644
index 0000000..2d5b7f5
--- /dev/null
+++ b/libpixelflinger/codeflinger/mips_disassem.h
@@ -0,0 +1,66 @@
+/* $NetBSD: db_disasm.c,v 1.19 2007/02/28 04:21:53 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Ralph Campbell.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)kadb.c 8.1 (Berkeley) 6/10/93
+ */
+
+
+
+#ifndef ANDROID_MIPS_DISASSEM_H
+#define ANDROID_MIPS_DISASSEM_H
+
+#include <sys/types.h>
+
+#if __cplusplus
+extern "C" {
+#endif
+
+
+// could add an interface like this, but I have not
+// typedef struct {
+// u_int (*di_readword)(u_int);
+// void (*di_printaddr)(u_int);
+// void (*di_printf)(const char *, ...);
+// } disasm_interface_t;
+
+/* Prototypes for callable functions */
+
+// u_int disasm(const disasm_interface_t *, u_int, int);
+
+void mips_disassem(uint32_t *location, char *di_buffer, int alt_fmt);
+
+#if __cplusplus
+}
+#endif
+
+#endif /* !ANDROID_MIPS_DISASSEM_H */
diff --git a/libpixelflinger/codeflinger/mips_opcode.h b/libpixelflinger/codeflinger/mips_opcode.h
new file mode 100644
index 0000000..7ed5ef5
--- /dev/null
+++ b/libpixelflinger/codeflinger/mips_opcode.h
@@ -0,0 +1,316 @@
+/* $NetBSD: mips_opcode.h,v 1.12 2005/12/11 12:18:09 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Ralph Campbell.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)mips_opcode.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Define the instruction formats and opcode values for the
+ * MIPS instruction set.
+ */
+
+#include <endian.h>
+
+/*
+ * Define the instruction formats.
+ */
+typedef union {
+ unsigned word;
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+ struct {
+ unsigned imm: 16;
+ unsigned rt: 5;
+ unsigned rs: 5;
+ unsigned op: 6;
+ } IType;
+
+ struct {
+ unsigned target: 26;
+ unsigned op: 6;
+ } JType;
+
+ struct {
+ unsigned func: 6;
+ unsigned shamt: 5;
+ unsigned rd: 5;
+ unsigned rt: 5;
+ unsigned rs: 5;
+ unsigned op: 6;
+ } RType;
+
+ struct {
+ unsigned func: 6;
+ unsigned fd: 5;
+ unsigned fs: 5;
+ unsigned ft: 5;
+ unsigned fmt: 4;
+ unsigned : 1; /* always '1' */
+ unsigned op: 6; /* always '0x11' */
+ } FRType;
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+ struct {
+ unsigned op: 6;
+ unsigned rs: 5;
+ unsigned rt: 5;
+ unsigned imm: 16;
+ } IType;
+
+ struct {
+ unsigned op: 6;
+ unsigned target: 26;
+ } JType;
+
+ struct {
+ unsigned op: 6;
+ unsigned rs: 5;
+ unsigned rt: 5;
+ unsigned rd: 5;
+ unsigned shamt: 5;
+ unsigned func: 6;
+ } RType;
+
+ struct {
+ unsigned op: 6; /* always '0x11' */
+ unsigned : 1; /* always '1' */
+ unsigned fmt: 4;
+ unsigned ft: 5;
+ unsigned fs: 5;
+ unsigned fd: 5;
+ unsigned func: 6;
+ } FRType;
+#endif
+} InstFmt;
+
+/*
+ * Values for the 'op' field.
+ */
+#define OP_SPECIAL 000
+#define OP_BCOND 001
+#define OP_J 002
+#define OP_JAL 003
+#define OP_BEQ 004
+#define OP_BNE 005
+#define OP_BLEZ 006
+#define OP_BGTZ 007
+
+#define OP_ADDI 010
+#define OP_ADDIU 011
+#define OP_SLTI 012
+#define OP_SLTIU 013
+#define OP_ANDI 014
+#define OP_ORI 015
+#define OP_XORI 016
+#define OP_LUI 017
+
+#define OP_COP0 020
+#define OP_COP1 021
+#define OP_COP2 022
+#define OP_COP3 023
+#define OP_BEQL 024 /* MIPS-II, for r4000 port */
+#define OP_BNEL 025 /* MIPS-II, for r4000 port */
+#define OP_BLEZL 026 /* MIPS-II, for r4000 port */
+#define OP_BGTZL 027 /* MIPS-II, for r4000 port */
+
+#define OP_DADDI 030 /* MIPS-II, for r4000 port */
+#define OP_DADDIU 031 /* MIPS-II, for r4000 port */
+#define OP_LDL 032 /* MIPS-II, for r4000 port */
+#define OP_LDR 033 /* MIPS-II, for r4000 port */
+
+#define OP_SPECIAL2 034 /* QED opcodes */
+#define OP_SPECIAL3 037 /* mips32r2 opcodes */
+
+#define OP_LB 040
+#define OP_LH 041
+#define OP_LWL 042
+#define OP_LW 043
+#define OP_LBU 044
+#define OP_LHU 045
+#define OP_LWR 046
+#define OP_LHU 045
+#define OP_LWR 046
+#define OP_LWU 047 /* MIPS-II, for r4000 port */
+
+#define OP_SB 050
+#define OP_SH 051
+#define OP_SWL 052
+#define OP_SW 053
+#define OP_SDL 054 /* MIPS-II, for r4000 port */
+#define OP_SDR 055 /* MIPS-II, for r4000 port */
+#define OP_SWR 056
+#define OP_CACHE 057 /* MIPS-II, for r4000 port */
+
+#define OP_LL 060
+#define OP_LWC0 OP_LL /* backwards source compatibility */
+#define OP_LWC1 061
+#define OP_LWC2 062
+#define OP_LWC3 063
+#define OP_LLD 064 /* MIPS-II, for r4000 port */
+#define OP_LDC1 065
+#define OP_LD 067 /* MIPS-II, for r4000 port */
+
+#define OP_SC 070
+#define OP_SWC0 OP_SC /* backwards source compatibility */
+#define OP_SWC1 071
+#define OP_SWC2 072
+#define OP_SWC3 073
+#define OP_SCD 074 /* MIPS-II, for r4000 port */
+#define OP_SDC1 075
+#define OP_SD 077 /* MIPS-II, for r4000 port */
+
+/*
+ * Values for the 'func' field when 'op' == OP_SPECIAL.
+ */
+#define OP_SLL 000
+#define OP_SRL 002
+#define OP_SRA 003
+#define OP_SLLV 004
+#define OP_SRLV 006
+#define OP_SRAV 007
+
+#define OP_JR 010
+#define OP_JALR 011
+#define OP_SYSCALL 014
+#define OP_BREAK 015
+#define OP_SYNC 017 /* MIPS-II, for r4000 port */
+
+#define OP_MFHI 020
+#define OP_MTHI 021
+#define OP_MFLO 022
+#define OP_MTLO 023
+#define OP_DSLLV 024 /* MIPS-II, for r4000 port */
+#define OP_DSRLV 026 /* MIPS-II, for r4000 port */
+#define OP_DSRAV 027 /* MIPS-II, for r4000 port */
+
+#define OP_MULT 030
+#define OP_MULTU 031
+#define OP_DIV 032
+#define OP_DIVU 033
+#define OP_DMULT 034 /* MIPS-II, for r4000 port */
+#define OP_DMULTU 035 /* MIPS-II, for r4000 port */
+#define OP_DDIV 036 /* MIPS-II, for r4000 port */
+#define OP_DDIVU 037 /* MIPS-II, for r4000 port */
+
+#define OP_ADD 040
+#define OP_ADDU 041
+#define OP_SUB 042
+#define OP_SUBU 043
+#define OP_AND 044
+#define OP_OR 045
+#define OP_XOR 046
+#define OP_NOR 047
+
+#define OP_SLT 052
+#define OP_SLTU 053
+#define OP_DADD 054 /* MIPS-II, for r4000 port */
+#define OP_DADDU 055 /* MIPS-II, for r4000 port */
+#define OP_DSUB 056 /* MIPS-II, for r4000 port */
+#define OP_DSUBU 057 /* MIPS-II, for r4000 port */
+
+#define OP_TGE 060 /* MIPS-II, for r4000 port */
+#define OP_TGEU 061 /* MIPS-II, for r4000 port */
+#define OP_TLT 062 /* MIPS-II, for r4000 port */
+#define OP_TLTU 063 /* MIPS-II, for r4000 port */
+#define OP_TEQ 064 /* MIPS-II, for r4000 port */
+#define OP_TNE 066 /* MIPS-II, for r4000 port */
+
+#define OP_DSLL 070 /* MIPS-II, for r4000 port */
+#define OP_DSRL 072 /* MIPS-II, for r4000 port */
+#define OP_DSRA 073 /* MIPS-II, for r4000 port */
+#define OP_DSLL32 074 /* MIPS-II, for r4000 port */
+#define OP_DSRL32 076 /* MIPS-II, for r4000 port */
+#define OP_DSRA32 077 /* MIPS-II, for r4000 port */
+
+/*
+ * Values for the 'func' field when 'op' == OP_SPECIAL2.
+ */
+#define OP_MAD 000 /* QED */
+#define OP_MADU 001 /* QED */
+#define OP_MUL 002 /* QED */
+
+/*
+ * Values for the 'func' field when 'op' == OP_SPECIAL3.
+ */
+#define OP_EXT 000
+#define OP_INS 004
+#define OP_BSHFL 040
+
+/*
+ * Values for the 'shamt' field when OP_SPECIAL3 && func OP_BSHFL.
+ */
+#define OP_WSBH 002
+#define OP_SEB 020
+#define OP_SEH 030
+
+/*
+ * Values for the 'func' field when 'op' == OP_BCOND.
+ */
+#define OP_BLTZ 000
+#define OP_BGEZ 001
+#define OP_BLTZL 002 /* MIPS-II, for r4000 port */
+#define OP_BGEZL 003 /* MIPS-II, for r4000 port */
+
+#define OP_TGEI 010 /* MIPS-II, for r4000 port */
+#define OP_TGEIU 011 /* MIPS-II, for r4000 port */
+#define OP_TLTI 012 /* MIPS-II, for r4000 port */
+#define OP_TLTIU 013 /* MIPS-II, for r4000 port */
+#define OP_TEQI 014 /* MIPS-II, for r4000 port */
+#define OP_TNEI 016 /* MIPS-II, for r4000 port */
+
+#define OP_BLTZAL 020 /* MIPS-II, for r4000 port */
+#define OP_BGEZAL 021
+#define OP_BLTZALL 022
+#define OP_BGEZALL 023
+
+/*
+ * Values for the 'rs' field when 'op' == OP_COPz.
+ */
+#define OP_MF 000
+#define OP_DMF 001 /* MIPS-II, for r4000 port */
+#define OP_MT 004
+#define OP_DMT 005 /* MIPS-II, for r4000 port */
+#define OP_BCx 010
+#define OP_BCy 014
+#define OP_CF 002
+#define OP_CT 006
+
+/*
+ * Values for the 'rt' field when 'op' == OP_COPz.
+ */
+#define COPz_BC_TF_MASK 0x01
+#define COPz_BC_TRUE 0x01
+#define COPz_BC_FALSE 0x00
+#define COPz_BCL_TF_MASK 0x02 /* MIPS-II, for r4000 port */
+#define COPz_BCL_TRUE 0x02 /* MIPS-II, for r4000 port */
+#define COPz_BCL_FALSE 0x00 /* MIPS-II, for r4000 port */
diff --git a/libpixelflinger/codeflinger/texturing.cpp b/libpixelflinger/codeflinger/texturing.cpp
new file mode 100644
index 0000000..29a3742
--- /dev/null
+++ b/libpixelflinger/codeflinger/texturing.cpp
@@ -0,0 +1,1268 @@
+/* libs/pixelflinger/codeflinger/texturing.cpp
+**
+** Copyright 2006, 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 <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+#include <cutils/log.h>
+
+#include "GGLAssembler.h"
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+// iterators are initialized like this:
+// (intToFixedCenter(x) * dx)>>16 + x0
+// ((x<<16 + 0x8000) * dx)>>16 + x0
+// ((x<<16)*dx + (0x8000*dx))>>16 + x0
+// ( (x*dx) + dx>>1 ) + x0
+// (x*dx) + (dx>>1 + x0)
+
+void GGLAssembler::init_iterated_color(fragment_parts_t& parts, const reg_t& x)
+{
+ context_t const* c = mBuilderContext.c;
+ const needs_t& needs = mBuilderContext.needs;
+
+ if (mSmooth) {
+ // NOTE: we could take this case in the mDithering + !mSmooth case,
+ // but this would use up to 4 more registers for the color components
+ // for only a little added quality.
+ // Currently, this causes the system to run out of registers in
+ // some case (see issue #719496)
+
+ comment("compute initial iterated color (smooth and/or dither case)");
+
+ parts.iterated_packed = 0;
+ parts.packed = 0;
+
+ // 0x1: color component
+ // 0x2: iterators
+ const int optReload = mOptLevel >> 1;
+ if (optReload >= 3) parts.reload = 0; // reload nothing
+ else if (optReload == 2) parts.reload = 2; // reload iterators
+ else if (optReload == 1) parts.reload = 1; // reload colors
+ else if (optReload <= 0) parts.reload = 3; // reload both
+
+ if (!mSmooth) {
+ // we're not smoothing (just dithering), we never have to
+ // reload the iterators
+ parts.reload &= ~2;
+ }
+
+ Scratch scratches(registerFile());
+ const int t0 = (parts.reload & 1) ? scratches.obtain() : 0;
+ const int t1 = (parts.reload & 2) ? scratches.obtain() : 0;
+ for (int i=0 ; i<4 ; i++) {
+ if (!mInfo[i].iterated)
+ continue;
+
+ // this component exists in the destination and is not replaced
+ // by a texture unit.
+ const int c = (parts.reload & 1) ? t0 : obtainReg();
+ if (i==0) CONTEXT_LOAD(c, iterators.ydady);
+ if (i==1) CONTEXT_LOAD(c, iterators.ydrdy);
+ if (i==2) CONTEXT_LOAD(c, iterators.ydgdy);
+ if (i==3) CONTEXT_LOAD(c, iterators.ydbdy);
+ parts.argb[i].reg = c;
+
+ if (mInfo[i].smooth) {
+ parts.argb_dx[i].reg = (parts.reload & 2) ? t1 : obtainReg();
+ const int dvdx = parts.argb_dx[i].reg;
+ CONTEXT_LOAD(dvdx, generated_vars.argb[i].dx);
+ MLA(AL, 0, c, x.reg, dvdx, c);
+
+ // adjust the color iterator to make sure it won't overflow
+ if (!mAA) {
+ // this is not needed when we're using anti-aliasing
+ // because we will (have to) clamp the components
+ // anyway.
+ int end = scratches.obtain();
+ MOV(AL, 0, end, reg_imm(parts.count.reg, LSR, 16));
+ MLA(AL, 1, end, dvdx, end, c);
+ SUB(MI, 0, c, c, end);
+ BIC(AL, 0, c, c, reg_imm(c, ASR, 31));
+ scratches.recycle(end);
+ }
+ }
+
+ if (parts.reload & 1) {
+ CONTEXT_STORE(c, generated_vars.argb[i].c);
+ }
+ }
+ } else {
+ // We're not smoothed, so we can
+ // just use a packed version of the color and extract the
+ // components as needed (or not at all if we don't blend)
+
+ // figure out if we need the iterated color
+ int load = 0;
+ for (int i=0 ; i<4 ; i++) {
+ component_info_t& info = mInfo[i];
+ if ((info.inDest || info.needed) && !info.replaced)
+ load |= 1;
+ }
+
+ parts.iterated_packed = 1;
+ parts.packed = (!mTextureMachine.mask && !mBlending
+ && !mFog && !mDithering);
+ parts.reload = 0;
+ if (load || parts.packed) {
+ if (mBlending || mDithering || mInfo[GGLFormat::ALPHA].needed) {
+ comment("load initial iterated color (8888 packed)");
+ parts.iterated.setTo(obtainReg(),
+ &(c->formats[GGL_PIXEL_FORMAT_RGBA_8888]));
+ CONTEXT_LOAD(parts.iterated.reg, packed8888);
+ } else {
+ comment("load initial iterated color (dest format packed)");
+
+ parts.iterated.setTo(obtainReg(), &mCbFormat);
+
+ // pre-mask the iterated color
+ const int bits = parts.iterated.size();
+ const uint32_t size = ((bits>=32) ? 0 : (1LU << bits)) - 1;
+ uint32_t mask = 0;
+ if (mMasking) {
+ for (int i=0 ; i<4 ; i++) {
+ const int component_mask = 1<<i;
+ const int h = parts.iterated.format.c[i].h;
+ const int l = parts.iterated.format.c[i].l;
+ if (h && (!(mMasking & component_mask))) {
+ mask |= ((1<<(h-l))-1) << l;
+ }
+ }
+ }
+
+ if (mMasking && ((mask & size)==0)) {
+ // none of the components are present in the mask
+ } else {
+ CONTEXT_LOAD(parts.iterated.reg, packed);
+ if (mCbFormat.size == 1) {
+ AND(AL, 0, parts.iterated.reg,
+ parts.iterated.reg, imm(0xFF));
+ } else if (mCbFormat.size == 2) {
+ MOV(AL, 0, parts.iterated.reg,
+ reg_imm(parts.iterated.reg, LSR, 16));
+ }
+ }
+
+ // pre-mask the iterated color
+ if (mMasking) {
+ build_and_immediate(parts.iterated.reg, parts.iterated.reg,
+ mask, bits);
+ }
+ }
+ }
+ }
+}
+
+void GGLAssembler::build_iterated_color(
+ component_t& fragment,
+ const fragment_parts_t& parts,
+ int component,
+ Scratch& regs)
+{
+ fragment.setTo( regs.obtain(), 0, 32, CORRUPTIBLE);
+
+ if (!mInfo[component].iterated)
+ return;
+
+ if (parts.iterated_packed) {
+ // iterated colors are packed, extract the one we need
+ extract(fragment, parts.iterated, component);
+ } else {
+ fragment.h = GGL_COLOR_BITS;
+ fragment.l = GGL_COLOR_BITS - 8;
+ fragment.flags |= CLEAR_LO;
+ // iterated colors are held in their own register,
+ // (smooth and/or dithering case)
+ if (parts.reload==3) {
+ // this implies mSmooth
+ Scratch scratches(registerFile());
+ int dx = scratches.obtain();
+ CONTEXT_LOAD(fragment.reg, generated_vars.argb[component].c);
+ CONTEXT_LOAD(dx, generated_vars.argb[component].dx);
+ ADD(AL, 0, dx, fragment.reg, dx);
+ CONTEXT_STORE(dx, generated_vars.argb[component].c);
+ } else if (parts.reload & 1) {
+ CONTEXT_LOAD(fragment.reg, generated_vars.argb[component].c);
+ } else {
+ // we don't reload, so simply rename the register and mark as
+ // non CORRUPTIBLE so that the texture env or blending code
+ // won't modify this (renamed) register
+ regs.recycle(fragment.reg);
+ fragment.reg = parts.argb[component].reg;
+ fragment.flags &= ~CORRUPTIBLE;
+ }
+ if (mInfo[component].smooth && mAA) {
+ // when using smooth shading AND anti-aliasing, we need to clamp
+ // the iterators because there is always an extra pixel on the
+ // edges, which most of the time will cause an overflow
+ // (since technically its outside of the domain).
+ BIC(AL, 0, fragment.reg, fragment.reg,
+ reg_imm(fragment.reg, ASR, 31));
+ component_sat(fragment);
+ }
+ }
+}
+
+// ---------------------------------------------------------------------------
+
+void GGLAssembler::decodeLogicOpNeeds(const needs_t& needs)
+{
+ // gather some informations about the components we need to process...
+ const int opcode = GGL_READ_NEEDS(LOGIC_OP, needs.n) | GGL_CLEAR;
+ switch(opcode) {
+ case GGL_COPY:
+ mLogicOp = 0;
+ break;
+ case GGL_CLEAR:
+ case GGL_SET:
+ mLogicOp = LOGIC_OP;
+ break;
+ case GGL_AND:
+ case GGL_AND_REVERSE:
+ case GGL_AND_INVERTED:
+ case GGL_XOR:
+ case GGL_OR:
+ case GGL_NOR:
+ case GGL_EQUIV:
+ case GGL_OR_REVERSE:
+ case GGL_OR_INVERTED:
+ case GGL_NAND:
+ mLogicOp = LOGIC_OP|LOGIC_OP_SRC|LOGIC_OP_DST;
+ break;
+ case GGL_NOOP:
+ case GGL_INVERT:
+ mLogicOp = LOGIC_OP|LOGIC_OP_DST;
+ break;
+ case GGL_COPY_INVERTED:
+ mLogicOp = LOGIC_OP|LOGIC_OP_SRC;
+ break;
+ };
+}
+
+void GGLAssembler::decodeTMUNeeds(const needs_t& needs, context_t const* c)
+{
+ uint8_t replaced=0;
+ mTextureMachine.mask = 0;
+ mTextureMachine.activeUnits = 0;
+ for (int i=GGL_TEXTURE_UNIT_COUNT-1 ; i>=0 ; i--) {
+ texture_unit_t& tmu = mTextureMachine.tmu[i];
+ if (replaced == 0xF) {
+ // all components are replaced, skip this TMU.
+ tmu.format_idx = 0;
+ tmu.mask = 0;
+ tmu.replaced = replaced;
+ continue;
+ }
+ tmu.format_idx = GGL_READ_NEEDS(T_FORMAT, needs.t[i]);
+ tmu.format = c->formats[tmu.format_idx];
+ tmu.bits = tmu.format.size*8;
+ tmu.swrap = GGL_READ_NEEDS(T_S_WRAP, needs.t[i]);
+ tmu.twrap = GGL_READ_NEEDS(T_T_WRAP, needs.t[i]);
+ tmu.env = ggl_needs_to_env(GGL_READ_NEEDS(T_ENV, needs.t[i]));
+ tmu.pot = GGL_READ_NEEDS(T_POT, needs.t[i]);
+ tmu.linear = GGL_READ_NEEDS(T_LINEAR, needs.t[i])
+ && tmu.format.size!=3; // XXX: only 8, 16 and 32 modes for now
+
+ // 5551 linear filtering is not supported
+ if (tmu.format_idx == GGL_PIXEL_FORMAT_RGBA_5551)
+ tmu.linear = 0;
+
+ tmu.mask = 0;
+ tmu.replaced = replaced;
+
+ if (tmu.format_idx) {
+ mTextureMachine.activeUnits++;
+ if (tmu.format.c[0].h) tmu.mask |= 0x1;
+ if (tmu.format.c[1].h) tmu.mask |= 0x2;
+ if (tmu.format.c[2].h) tmu.mask |= 0x4;
+ if (tmu.format.c[3].h) tmu.mask |= 0x8;
+ if (tmu.env == GGL_REPLACE) {
+ replaced |= tmu.mask;
+ } else if (tmu.env == GGL_DECAL) {
+ if (!tmu.format.c[GGLFormat::ALPHA].h) {
+ // if we don't have alpha, decal does nothing
+ tmu.mask = 0;
+ } else {
+ // decal always ignores At
+ tmu.mask &= ~(1<<GGLFormat::ALPHA);
+ }
+ }
+ }
+ mTextureMachine.mask |= tmu.mask;
+ //printf("%d: mask=%08lx, replaced=%08lx\n",
+ // i, int(tmu.mask), int(tmu.replaced));
+ }
+ mTextureMachine.replaced = replaced;
+ mTextureMachine.directTexture = 0;
+ //printf("replaced=%08lx\n", mTextureMachine.replaced);
+}
+
+
+void GGLAssembler::init_textures(
+ tex_coord_t* coords,
+ const reg_t& x, const reg_t& y)
+{
+ context_t const* c = mBuilderContext.c;
+ const needs_t& needs = mBuilderContext.needs;
+ int Rctx = mBuilderContext.Rctx;
+ int Rx = x.reg;
+ int Ry = y.reg;
+
+ if (mTextureMachine.mask) {
+ comment("compute texture coordinates");
+ }
+
+ // init texture coordinates for each tmu
+ const int cb_format_idx = GGL_READ_NEEDS(CB_FORMAT, needs.n);
+ const bool multiTexture = mTextureMachine.activeUnits > 1;
+ for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT; i++) {
+ const texture_unit_t& tmu = mTextureMachine.tmu[i];
+ if (tmu.format_idx == 0)
+ continue;
+ if ((tmu.swrap == GGL_NEEDS_WRAP_11) &&
+ (tmu.twrap == GGL_NEEDS_WRAP_11))
+ {
+ // 1:1 texture
+ pointer_t& txPtr = coords[i].ptr;
+ txPtr.setTo(obtainReg(), tmu.bits);
+ CONTEXT_LOAD(txPtr.reg, state.texture[i].iterators.ydsdy);
+ ADD(AL, 0, Rx, Rx, reg_imm(txPtr.reg, ASR, 16)); // x += (s>>16)
+ CONTEXT_LOAD(txPtr.reg, state.texture[i].iterators.ydtdy);
+ ADD(AL, 0, Ry, Ry, reg_imm(txPtr.reg, ASR, 16)); // y += (t>>16)
+ // merge base & offset
+ CONTEXT_LOAD(txPtr.reg, generated_vars.texture[i].stride);
+ SMLABB(AL, Rx, Ry, txPtr.reg, Rx); // x+y*stride
+ CONTEXT_ADDR_LOAD(txPtr.reg, generated_vars.texture[i].data);
+ base_offset(txPtr, txPtr, Rx);
+ } else {
+ Scratch scratches(registerFile());
+ reg_t& s = coords[i].s;
+ reg_t& t = coords[i].t;
+ // s = (x * dsdx)>>16 + ydsdy
+ // s = (x * dsdx)>>16 + (y*dsdy)>>16 + s0
+ // t = (x * dtdx)>>16 + ydtdy
+ // t = (x * dtdx)>>16 + (y*dtdy)>>16 + t0
+ s.setTo(obtainReg());
+ t.setTo(obtainReg());
+ const int need_w = GGL_READ_NEEDS(W, needs.n);
+ if (need_w) {
+ CONTEXT_LOAD(s.reg, state.texture[i].iterators.ydsdy);
+ CONTEXT_LOAD(t.reg, state.texture[i].iterators.ydtdy);
+ } else {
+ int ydsdy = scratches.obtain();
+ int ydtdy = scratches.obtain();
+ CONTEXT_LOAD(s.reg, generated_vars.texture[i].dsdx);
+ CONTEXT_LOAD(ydsdy, state.texture[i].iterators.ydsdy);
+ CONTEXT_LOAD(t.reg, generated_vars.texture[i].dtdx);
+ CONTEXT_LOAD(ydtdy, state.texture[i].iterators.ydtdy);
+ MLA(AL, 0, s.reg, Rx, s.reg, ydsdy);
+ MLA(AL, 0, t.reg, Rx, t.reg, ydtdy);
+ }
+
+ if ((mOptLevel&1)==0) {
+ CONTEXT_STORE(s.reg, generated_vars.texture[i].spill[0]);
+ CONTEXT_STORE(t.reg, generated_vars.texture[i].spill[1]);
+ recycleReg(s.reg);
+ recycleReg(t.reg);
+ }
+ }
+
+ // direct texture?
+ if (!multiTexture && !mBlending && !mDithering && !mFog &&
+ cb_format_idx == tmu.format_idx && !tmu.linear &&
+ mTextureMachine.replaced == tmu.mask)
+ {
+ mTextureMachine.directTexture = i + 1;
+ }
+ }
+}
+
+void GGLAssembler::build_textures( fragment_parts_t& parts,
+ Scratch& regs)
+{
+ context_t const* c = mBuilderContext.c;
+ const needs_t& needs = mBuilderContext.needs;
+ int Rctx = mBuilderContext.Rctx;
+
+ // We don't have a way to spill registers automatically
+ // spill depth and AA regs, when we know we may have to.
+ // build the spill list...
+ uint32_t spill_list = 0;
+ for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT; i++) {
+ const texture_unit_t& tmu = mTextureMachine.tmu[i];
+ if (tmu.format_idx == 0)
+ continue;
+ if (tmu.linear) {
+ // we may run out of register if we have linear filtering
+ // at 1 or 4 bytes / pixel on any texture unit.
+ if (tmu.format.size == 1) {
+ // if depth and AA enabled, we'll run out of 1 register
+ if (parts.z.reg > 0 && parts.covPtr.reg > 0)
+ spill_list |= 1<<parts.covPtr.reg;
+ }
+ if (tmu.format.size == 4) {
+ // if depth or AA enabled, we'll run out of 1 or 2 registers
+ if (parts.z.reg > 0)
+ spill_list |= 1<<parts.z.reg;
+ if (parts.covPtr.reg > 0)
+ spill_list |= 1<<parts.covPtr.reg;
+ }
+ }
+ }
+
+ Spill spill(registerFile(), *this, spill_list);
+
+ const bool multiTexture = mTextureMachine.activeUnits > 1;
+ for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT; i++) {
+ const texture_unit_t& tmu = mTextureMachine.tmu[i];
+ if (tmu.format_idx == 0)
+ continue;
+
+ pointer_t& txPtr = parts.coords[i].ptr;
+ pixel_t& texel = parts.texel[i];
+
+ // repeat...
+ if ((tmu.swrap == GGL_NEEDS_WRAP_11) &&
+ (tmu.twrap == GGL_NEEDS_WRAP_11))
+ { // 1:1 textures
+ comment("fetch texel");
+ texel.setTo(regs.obtain(), &tmu.format);
+ load(txPtr, texel, WRITE_BACK);
+ } else {
+ Scratch scratches(registerFile());
+ reg_t& s = parts.coords[i].s;
+ reg_t& t = parts.coords[i].t;
+ if ((mOptLevel&1)==0) {
+ comment("reload s/t (multitexture or linear filtering)");
+ s.reg = scratches.obtain();
+ t.reg = scratches.obtain();
+ CONTEXT_LOAD(s.reg, generated_vars.texture[i].spill[0]);
+ CONTEXT_LOAD(t.reg, generated_vars.texture[i].spill[1]);
+ }
+
+ if (registerFile().status() & RegisterFile::OUT_OF_REGISTERS)
+ return;
+
+ comment("compute repeat/clamp");
+ int u = scratches.obtain();
+ int v = scratches.obtain();
+ int width = scratches.obtain();
+ int height = scratches.obtain();
+ int U = 0;
+ int V = 0;
+
+ if (registerFile().status() & RegisterFile::OUT_OF_REGISTERS)
+ return;
+
+ CONTEXT_LOAD(width, generated_vars.texture[i].width);
+ CONTEXT_LOAD(height, generated_vars.texture[i].height);
+
+ int FRAC_BITS = 0;
+ if (tmu.linear) {
+ // linear interpolation
+ if (tmu.format.size == 1) {
+ // for 8-bits textures, we can afford
+ // 7 bits of fractional precision at no
+ // additional cost (we can't do 8 bits
+ // because filter8 uses signed 16 bits muls)
+ FRAC_BITS = 7;
+ } else if (tmu.format.size == 2) {
+ // filter16() is internally limited to 4 bits, so:
+ // FRAC_BITS=2 generates less instructions,
+ // FRAC_BITS=3,4,5 creates unpleasant artifacts,
+ // FRAC_BITS=6+ looks good
+ FRAC_BITS = 6;
+ } else if (tmu.format.size == 4) {
+ // filter32() is internally limited to 8 bits, so:
+ // FRAC_BITS=4 looks good
+ // FRAC_BITS=5+ looks better, but generates 3 extra ipp
+ FRAC_BITS = 6;
+ } else {
+ // for all other cases we use 4 bits.
+ FRAC_BITS = 4;
+ }
+ }
+ wrapping(u, s.reg, width, tmu.swrap, FRAC_BITS);
+ wrapping(v, t.reg, height, tmu.twrap, FRAC_BITS);
+
+ if (tmu.linear) {
+ comment("compute linear filtering offsets");
+ // pixel size scale
+ const int shift = 31 - gglClz(tmu.format.size);
+ U = scratches.obtain();
+ V = scratches.obtain();
+
+ if (registerFile().status() & RegisterFile::OUT_OF_REGISTERS)
+ return;
+
+ // sample the texel center
+ SUB(AL, 0, u, u, imm(1<<(FRAC_BITS-1)));
+ SUB(AL, 0, v, v, imm(1<<(FRAC_BITS-1)));
+
+ // get the fractionnal part of U,V
+ AND(AL, 0, U, u, imm((1<<FRAC_BITS)-1));
+ AND(AL, 0, V, v, imm((1<<FRAC_BITS)-1));
+
+ // compute width-1 and height-1
+ SUB(AL, 0, width, width, imm(1));
+ SUB(AL, 0, height, height, imm(1));
+
+ // get the integer part of U,V and clamp/wrap
+ // and compute offset to the next texel
+ if (tmu.swrap == GGL_NEEDS_WRAP_REPEAT) {
+ // u has already been REPEATed
+ MOV(AL, 1, u, reg_imm(u, ASR, FRAC_BITS));
+ MOV(MI, 0, u, width);
+ CMP(AL, u, width);
+ MOV(LT, 0, width, imm(1 << shift));
+ if (shift)
+ MOV(GE, 0, width, reg_imm(width, LSL, shift));
+ RSB(GE, 0, width, width, imm(0));
+ } else {
+ // u has not been CLAMPed yet
+ // algorithm:
+ // if ((u>>4) >= width)
+ // u = width<<4
+ // width = 0
+ // else
+ // width = 1<<shift
+ // u = u>>4; // get integer part
+ // if (u<0)
+ // u = 0
+ // width = 0
+ // generated_vars.rt = width
+
+ CMP(AL, width, reg_imm(u, ASR, FRAC_BITS));
+ MOV(LE, 0, u, reg_imm(width, LSL, FRAC_BITS));
+ MOV(LE, 0, width, imm(0));
+ MOV(GT, 0, width, imm(1 << shift));
+ MOV(AL, 1, u, reg_imm(u, ASR, FRAC_BITS));
+ MOV(MI, 0, u, imm(0));
+ MOV(MI, 0, width, imm(0));
+ }
+ CONTEXT_STORE(width, generated_vars.rt);
+
+ const int stride = width;
+ CONTEXT_LOAD(stride, generated_vars.texture[i].stride);
+ if (tmu.twrap == GGL_NEEDS_WRAP_REPEAT) {
+ // v has already been REPEATed
+ MOV(AL, 1, v, reg_imm(v, ASR, FRAC_BITS));
+ MOV(MI, 0, v, height);
+ CMP(AL, v, height);
+ MOV(LT, 0, height, imm(1 << shift));
+ if (shift)
+ MOV(GE, 0, height, reg_imm(height, LSL, shift));
+ RSB(GE, 0, height, height, imm(0));
+ MUL(AL, 0, height, stride, height);
+ } else {
+ // v has not been CLAMPed yet
+ CMP(AL, height, reg_imm(v, ASR, FRAC_BITS));
+ MOV(LE, 0, v, reg_imm(height, LSL, FRAC_BITS));
+ MOV(LE, 0, height, imm(0));
+ if (shift) {
+ MOV(GT, 0, height, reg_imm(stride, LSL, shift));
+ } else {
+ MOV(GT, 0, height, stride);
+ }
+ MOV(AL, 1, v, reg_imm(v, ASR, FRAC_BITS));
+ MOV(MI, 0, v, imm(0));
+ MOV(MI, 0, height, imm(0));
+ }
+ CONTEXT_STORE(height, generated_vars.lb);
+ }
+
+ scratches.recycle(width);
+ scratches.recycle(height);
+
+ // iterate texture coordinates...
+ comment("iterate s,t");
+ int dsdx = scratches.obtain();
+ int dtdx = scratches.obtain();
+
+ if (registerFile().status() & RegisterFile::OUT_OF_REGISTERS)
+ return;
+
+ CONTEXT_LOAD(dsdx, generated_vars.texture[i].dsdx);
+ CONTEXT_LOAD(dtdx, generated_vars.texture[i].dtdx);
+ ADD(AL, 0, s.reg, s.reg, dsdx);
+ ADD(AL, 0, t.reg, t.reg, dtdx);
+ if ((mOptLevel&1)==0) {
+ CONTEXT_STORE(s.reg, generated_vars.texture[i].spill[0]);
+ CONTEXT_STORE(t.reg, generated_vars.texture[i].spill[1]);
+ scratches.recycle(s.reg);
+ scratches.recycle(t.reg);
+ }
+ scratches.recycle(dsdx);
+ scratches.recycle(dtdx);
+
+ // merge base & offset...
+ comment("merge base & offset");
+ texel.setTo(regs.obtain(), &tmu.format);
+ txPtr.setTo(texel.reg, tmu.bits);
+ int stride = scratches.obtain();
+
+ if (registerFile().status() & RegisterFile::OUT_OF_REGISTERS)
+ return;
+
+ CONTEXT_LOAD(stride, generated_vars.texture[i].stride);
+ CONTEXT_ADDR_LOAD(txPtr.reg, generated_vars.texture[i].data);
+ SMLABB(AL, u, v, stride, u); // u+v*stride
+ base_offset(txPtr, txPtr, u);
+
+ // load texel
+ if (!tmu.linear) {
+ comment("fetch texel");
+ load(txPtr, texel, 0);
+ } else {
+ // recycle registers we don't need anymore
+ scratches.recycle(u);
+ scratches.recycle(v);
+ scratches.recycle(stride);
+
+ comment("fetch texel, bilinear");
+ switch (tmu.format.size) {
+ case 1: filter8(parts, texel, tmu, U, V, txPtr, FRAC_BITS); break;
+ case 2: filter16(parts, texel, tmu, U, V, txPtr, FRAC_BITS); break;
+ case 3: filter24(parts, texel, tmu, U, V, txPtr, FRAC_BITS); break;
+ case 4: filter32(parts, texel, tmu, U, V, txPtr, FRAC_BITS); break;
+ }
+ }
+ }
+ }
+}
+
+void GGLAssembler::build_iterate_texture_coordinates(
+ const fragment_parts_t& parts)
+{
+ const bool multiTexture = mTextureMachine.activeUnits > 1;
+ for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT; i++) {
+ const texture_unit_t& tmu = mTextureMachine.tmu[i];
+ if (tmu.format_idx == 0)
+ continue;
+
+ if ((tmu.swrap == GGL_NEEDS_WRAP_11) &&
+ (tmu.twrap == GGL_NEEDS_WRAP_11))
+ { // 1:1 textures
+ const pointer_t& txPtr = parts.coords[i].ptr;
+ ADD(AL, 0, txPtr.reg, txPtr.reg, imm(txPtr.size>>3));
+ } else {
+ Scratch scratches(registerFile());
+ int s = parts.coords[i].s.reg;
+ int t = parts.coords[i].t.reg;
+ if ((mOptLevel&1)==0) {
+ s = scratches.obtain();
+ t = scratches.obtain();
+ CONTEXT_LOAD(s, generated_vars.texture[i].spill[0]);
+ CONTEXT_LOAD(t, generated_vars.texture[i].spill[1]);
+ }
+ int dsdx = scratches.obtain();
+ int dtdx = scratches.obtain();
+ CONTEXT_LOAD(dsdx, generated_vars.texture[i].dsdx);
+ CONTEXT_LOAD(dtdx, generated_vars.texture[i].dtdx);
+ ADD(AL, 0, s, s, dsdx);
+ ADD(AL, 0, t, t, dtdx);
+ if ((mOptLevel&1)==0) {
+ CONTEXT_STORE(s, generated_vars.texture[i].spill[0]);
+ CONTEXT_STORE(t, generated_vars.texture[i].spill[1]);
+ }
+ }
+ }
+}
+
+void GGLAssembler::filter8(
+ const fragment_parts_t& /*parts*/,
+ pixel_t& texel, const texture_unit_t& tmu,
+ int U, int V, pointer_t& txPtr,
+ int FRAC_BITS)
+{
+ if (tmu.format.components != GGL_ALPHA &&
+ tmu.format.components != GGL_LUMINANCE)
+ {
+ // this is a packed format, and we don't support
+ // linear filtering (it's probably RGB 332)
+ // Should not happen with OpenGL|ES
+ LDRB(AL, texel.reg, txPtr.reg);
+ return;
+ }
+
+ // ------------------------
+ // about ~22 cycles / pixel
+ Scratch scratches(registerFile());
+
+ int pixel= scratches.obtain();
+ int d = scratches.obtain();
+ int u = scratches.obtain();
+ int k = scratches.obtain();
+ int rt = scratches.obtain();
+ int lb = scratches.obtain();
+
+ // RB -> U * V
+
+ CONTEXT_LOAD(rt, generated_vars.rt);
+ CONTEXT_LOAD(lb, generated_vars.lb);
+
+ int offset = pixel;
+ ADD(AL, 0, offset, lb, rt);
+ LDRB(AL, pixel, txPtr.reg, reg_scale_pre(offset));
+ SMULBB(AL, u, U, V);
+ SMULBB(AL, d, pixel, u);
+ RSB(AL, 0, k, u, imm(1<<(FRAC_BITS*2)));
+
+ // LB -> (1-U) * V
+ RSB(AL, 0, U, U, imm(1<<FRAC_BITS));
+ LDRB(AL, pixel, txPtr.reg, reg_scale_pre(lb));
+ SMULBB(AL, u, U, V);
+ SMLABB(AL, d, pixel, u, d);
+ SUB(AL, 0, k, k, u);
+
+ // LT -> (1-U)*(1-V)
+ RSB(AL, 0, V, V, imm(1<<FRAC_BITS));
+ LDRB(AL, pixel, txPtr.reg);
+ SMULBB(AL, u, U, V);
+ SMLABB(AL, d, pixel, u, d);
+
+ // RT -> U*(1-V)
+ LDRB(AL, pixel, txPtr.reg, reg_scale_pre(rt));
+ SUB(AL, 0, u, k, u);
+ SMLABB(AL, texel.reg, pixel, u, d);
+
+ for (int i=0 ; i<4 ; i++) {
+ if (!texel.format.c[i].h) continue;
+ texel.format.c[i].h = FRAC_BITS*2+8;
+ texel.format.c[i].l = FRAC_BITS*2; // keeping 8 bits in enough
+ }
+ texel.format.size = 4;
+ texel.format.bitsPerPixel = 32;
+ texel.flags |= CLEAR_LO;
+}
+
+void GGLAssembler::filter16(
+ const fragment_parts_t& /*parts*/,
+ pixel_t& texel, const texture_unit_t& tmu,
+ int U, int V, pointer_t& txPtr,
+ int FRAC_BITS)
+{
+ // compute the mask
+ // XXX: it would be nice if the mask below could be computed
+ // automatically.
+ uint32_t mask = 0;
+ int shift = 0;
+ int prec = 0;
+ switch (tmu.format_idx) {
+ case GGL_PIXEL_FORMAT_RGB_565:
+ // source: 00000ggg.ggg00000 | rrrrr000.000bbbbb
+ // result: gggggggg.gggrrrrr | rrrrr0bb.bbbbbbbb
+ mask = 0x07E0F81F;
+ shift = 16;
+ prec = 5;
+ break;
+ case GGL_PIXEL_FORMAT_RGBA_4444:
+ // 0000,1111,0000,1111 | 0000,1111,0000,1111
+ mask = 0x0F0F0F0F;
+ shift = 12;
+ prec = 4;
+ break;
+ case GGL_PIXEL_FORMAT_LA_88:
+ // 0000,0000,1111,1111 | 0000,0000,1111,1111
+ // AALL -> 00AA | 00LL
+ mask = 0x00FF00FF;
+ shift = 8;
+ prec = 8;
+ break;
+ default:
+ // unsupported format, do something sensical...
+ ALOGE("Unsupported 16-bits texture format (%d)", tmu.format_idx);
+ LDRH(AL, texel.reg, txPtr.reg);
+ return;
+ }
+
+ const int adjust = FRAC_BITS*2 - prec;
+ const int round = 0;
+
+ // update the texel format
+ texel.format.size = 4;
+ texel.format.bitsPerPixel = 32;
+ texel.flags |= CLEAR_HI|CLEAR_LO;
+ for (int i=0 ; i<4 ; i++) {
+ if (!texel.format.c[i].h) continue;
+ const uint32_t offset = (mask & tmu.format.mask(i)) ? 0 : shift;
+ texel.format.c[i].h = tmu.format.c[i].h + offset + prec;
+ texel.format.c[i].l = texel.format.c[i].h - (tmu.format.bits(i) + prec);
+ }
+
+ // ------------------------
+ // about ~40 cycles / pixel
+ Scratch scratches(registerFile());
+
+ int pixel= scratches.obtain();
+ int d = scratches.obtain();
+ int u = scratches.obtain();
+ int k = scratches.obtain();
+
+ // RB -> U * V
+ int offset = pixel;
+ CONTEXT_LOAD(offset, generated_vars.rt);
+ CONTEXT_LOAD(u, generated_vars.lb);
+ ADD(AL, 0, offset, offset, u);
+
+ LDRH(AL, pixel, txPtr.reg, reg_pre(offset));
+ SMULBB(AL, u, U, V);
+ ORR(AL, 0, pixel, pixel, reg_imm(pixel, LSL, shift));
+ build_and_immediate(pixel, pixel, mask, 32);
+ if (adjust) {
+ if (round)
+ ADD(AL, 0, u, u, imm(1<<(adjust-1)));
+ MOV(AL, 0, u, reg_imm(u, LSR, adjust));
+ }
+ MUL(AL, 0, d, pixel, u);
+ RSB(AL, 0, k, u, imm(1<<prec));
+
+ // LB -> (1-U) * V
+ CONTEXT_LOAD(offset, generated_vars.lb);
+ RSB(AL, 0, U, U, imm(1<<FRAC_BITS));
+ LDRH(AL, pixel, txPtr.reg, reg_pre(offset));
+ SMULBB(AL, u, U, V);
+ ORR(AL, 0, pixel, pixel, reg_imm(pixel, LSL, shift));
+ build_and_immediate(pixel, pixel, mask, 32);
+ if (adjust) {
+ if (round)
+ ADD(AL, 0, u, u, imm(1<<(adjust-1)));
+ MOV(AL, 0, u, reg_imm(u, LSR, adjust));
+ }
+ MLA(AL, 0, d, pixel, u, d);
+ SUB(AL, 0, k, k, u);
+
+ // LT -> (1-U)*(1-V)
+ RSB(AL, 0, V, V, imm(1<<FRAC_BITS));
+ LDRH(AL, pixel, txPtr.reg);
+ SMULBB(AL, u, U, V);
+ ORR(AL, 0, pixel, pixel, reg_imm(pixel, LSL, shift));
+ build_and_immediate(pixel, pixel, mask, 32);
+ if (adjust) {
+ if (round)
+ ADD(AL, 0, u, u, imm(1<<(adjust-1)));
+ MOV(AL, 0, u, reg_imm(u, LSR, adjust));
+ }
+ MLA(AL, 0, d, pixel, u, d);
+
+ // RT -> U*(1-V)
+ CONTEXT_LOAD(offset, generated_vars.rt);
+ LDRH(AL, pixel, txPtr.reg, reg_pre(offset));
+ SUB(AL, 0, u, k, u);
+ ORR(AL, 0, pixel, pixel, reg_imm(pixel, LSL, shift));
+ build_and_immediate(pixel, pixel, mask, 32);
+ MLA(AL, 0, texel.reg, pixel, u, d);
+}
+
+void GGLAssembler::filter24(
+ const fragment_parts_t& /*parts*/,
+ pixel_t& texel, const texture_unit_t& /*tmu*/,
+ int /*U*/, int /*V*/, pointer_t& txPtr,
+ int /*FRAC_BITS*/)
+{
+ // not supported yet (currently disabled)
+ load(txPtr, texel, 0);
+}
+
+void GGLAssembler::filter32(
+ const fragment_parts_t& /*parts*/,
+ pixel_t& texel, const texture_unit_t& /*tmu*/,
+ int U, int V, pointer_t& txPtr,
+ int FRAC_BITS)
+{
+ const int adjust = FRAC_BITS*2 - 8;
+ const int round = 0;
+
+ // ------------------------
+ // about ~38 cycles / pixel
+ Scratch scratches(registerFile());
+
+ int pixel= scratches.obtain();
+ int dh = scratches.obtain();
+ int u = scratches.obtain();
+ int k = scratches.obtain();
+
+ int temp = scratches.obtain();
+ int dl = scratches.obtain();
+ int mask = scratches.obtain();
+
+ MOV(AL, 0, mask, imm(0xFF));
+ ORR(AL, 0, mask, mask, imm(0xFF0000));
+
+ // RB -> U * V
+ int offset = pixel;
+ CONTEXT_LOAD(offset, generated_vars.rt);
+ CONTEXT_LOAD(u, generated_vars.lb);
+ ADD(AL, 0, offset, offset, u);
+
+ LDR(AL, pixel, txPtr.reg, reg_scale_pre(offset));
+ SMULBB(AL, u, U, V);
+ AND(AL, 0, temp, mask, pixel);
+ if (adjust) {
+ if (round)
+ ADD(AL, 0, u, u, imm(1<<(adjust-1)));
+ MOV(AL, 0, u, reg_imm(u, LSR, adjust));
+ }
+ MUL(AL, 0, dh, temp, u);
+ AND(AL, 0, temp, mask, reg_imm(pixel, LSR, 8));
+ MUL(AL, 0, dl, temp, u);
+ RSB(AL, 0, k, u, imm(0x100));
+
+ // LB -> (1-U) * V
+ CONTEXT_LOAD(offset, generated_vars.lb);
+ RSB(AL, 0, U, U, imm(1<<FRAC_BITS));
+ LDR(AL, pixel, txPtr.reg, reg_scale_pre(offset));
+ SMULBB(AL, u, U, V);
+ AND(AL, 0, temp, mask, pixel);
+ if (adjust) {
+ if (round)
+ ADD(AL, 0, u, u, imm(1<<(adjust-1)));
+ MOV(AL, 0, u, reg_imm(u, LSR, adjust));
+ }
+ MLA(AL, 0, dh, temp, u, dh);
+ AND(AL, 0, temp, mask, reg_imm(pixel, LSR, 8));
+ MLA(AL, 0, dl, temp, u, dl);
+ SUB(AL, 0, k, k, u);
+
+ // LT -> (1-U)*(1-V)
+ RSB(AL, 0, V, V, imm(1<<FRAC_BITS));
+ LDR(AL, pixel, txPtr.reg);
+ SMULBB(AL, u, U, V);
+ AND(AL, 0, temp, mask, pixel);
+ if (adjust) {
+ if (round)
+ ADD(AL, 0, u, u, imm(1<<(adjust-1)));
+ MOV(AL, 0, u, reg_imm(u, LSR, adjust));
+ }
+ MLA(AL, 0, dh, temp, u, dh);
+ AND(AL, 0, temp, mask, reg_imm(pixel, LSR, 8));
+ MLA(AL, 0, dl, temp, u, dl);
+
+ // RT -> U*(1-V)
+ CONTEXT_LOAD(offset, generated_vars.rt);
+ LDR(AL, pixel, txPtr.reg, reg_scale_pre(offset));
+ SUB(AL, 0, u, k, u);
+ AND(AL, 0, temp, mask, pixel);
+ MLA(AL, 0, dh, temp, u, dh);
+ AND(AL, 0, temp, mask, reg_imm(pixel, LSR, 8));
+ MLA(AL, 0, dl, temp, u, dl);
+
+ AND(AL, 0, dh, mask, reg_imm(dh, LSR, 8));
+ AND(AL, 0, dl, dl, reg_imm(mask, LSL, 8));
+ ORR(AL, 0, texel.reg, dh, dl);
+}
+
+void GGLAssembler::build_texture_environment(
+ component_t& fragment,
+ const fragment_parts_t& parts,
+ int component,
+ Scratch& regs)
+{
+ const uint32_t component_mask = 1<<component;
+ const bool multiTexture = mTextureMachine.activeUnits > 1;
+ for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
+ texture_unit_t& tmu = mTextureMachine.tmu[i];
+
+ if (tmu.mask & component_mask) {
+ // replace or modulate with this texture
+ if ((tmu.replaced & component_mask) == 0) {
+ // not replaced by a later tmu...
+
+ Scratch scratches(registerFile());
+ pixel_t texel(parts.texel[i]);
+
+ if (multiTexture &&
+ tmu.swrap == GGL_NEEDS_WRAP_11 &&
+ tmu.twrap == GGL_NEEDS_WRAP_11)
+ {
+ texel.reg = scratches.obtain();
+ texel.flags |= CORRUPTIBLE;
+ comment("fetch texel (multitexture 1:1)");
+ load(parts.coords[i].ptr, texel, WRITE_BACK);
+ }
+
+ component_t incoming(fragment);
+ modify(fragment, regs);
+
+ switch (tmu.env) {
+ case GGL_REPLACE:
+ extract(fragment, texel, component);
+ break;
+ case GGL_MODULATE:
+ modulate(fragment, incoming, texel, component);
+ break;
+ case GGL_DECAL:
+ decal(fragment, incoming, texel, component);
+ break;
+ case GGL_BLEND:
+ blend(fragment, incoming, texel, component, i);
+ break;
+ case GGL_ADD:
+ add(fragment, incoming, texel, component);
+ break;
+ }
+ }
+ }
+ }
+}
+
+// ---------------------------------------------------------------------------
+
+void GGLAssembler::wrapping(
+ int d,
+ int coord, int size,
+ int tx_wrap, int tx_linear)
+{
+ // notes:
+ // if tx_linear is set, we need 4 extra bits of precision on the result
+ // SMULL/UMULL is 3 cycles
+ Scratch scratches(registerFile());
+ int c = coord;
+ if (tx_wrap == GGL_NEEDS_WRAP_REPEAT) {
+ // UMULL takes 4 cycles (interlocked), and we can get away with
+ // 2 cycles using SMULWB, but we're loosing 16 bits of precision
+ // out of 32 (this is not a problem because the iterator keeps
+ // its full precision)
+ // UMULL(AL, 0, size, d, c, size);
+ // note: we can't use SMULTB because it's signed.
+ MOV(AL, 0, d, reg_imm(c, LSR, 16-tx_linear));
+ SMULWB(AL, d, d, size);
+ } else if (tx_wrap == GGL_NEEDS_WRAP_CLAMP_TO_EDGE) {
+ if (tx_linear) {
+ // 1 cycle
+ MOV(AL, 0, d, reg_imm(coord, ASR, 16-tx_linear));
+ } else {
+ // 4 cycles (common case)
+ MOV(AL, 0, d, reg_imm(coord, ASR, 16));
+ BIC(AL, 0, d, d, reg_imm(d, ASR, 31));
+ CMP(AL, d, size);
+ SUB(GE, 0, d, size, imm(1));
+ }
+ }
+}
+
+// ---------------------------------------------------------------------------
+
+void GGLAssembler::modulate(
+ component_t& dest,
+ const component_t& incoming,
+ const pixel_t& incomingTexel, int component)
+{
+ Scratch locals(registerFile());
+ integer_t texel(locals.obtain(), 32, CORRUPTIBLE);
+ extract(texel, incomingTexel, component);
+
+ const int Nt = texel.size();
+ // Nt should always be less than 10 bits because it comes
+ // from the TMU.
+
+ int Ni = incoming.size();
+ // Ni could be big because it comes from previous MODULATEs
+
+ if (Nt == 1) {
+ // texel acts as a bit-mask
+ // dest = incoming & ((texel << incoming.h)-texel)
+ RSB(AL, 0, dest.reg, texel.reg, reg_imm(texel.reg, LSL, incoming.h));
+ AND(AL, 0, dest.reg, dest.reg, incoming.reg);
+ dest.l = incoming.l;
+ dest.h = incoming.h;
+ dest.flags |= (incoming.flags & CLEAR_LO);
+ } else if (Ni == 1) {
+ MOV(AL, 0, dest.reg, reg_imm(incoming.reg, LSL, 31-incoming.h));
+ AND(AL, 0, dest.reg, texel.reg, reg_imm(dest.reg, ASR, 31));
+ dest.l = 0;
+ dest.h = Nt;
+ } else {
+ int inReg = incoming.reg;
+ int shift = incoming.l;
+ if ((Nt + Ni) > 32) {
+ // we will overflow, reduce the precision of Ni to 8 bits
+ // (Note Nt cannot be more than 10 bits which happens with
+ // 565 textures and GGL_LINEAR)
+ shift += Ni-8;
+ Ni = 8;
+ }
+
+ // modulate by the component with the lowest precision
+ if (Nt >= Ni) {
+ if (shift) {
+ // XXX: we should be able to avoid this shift
+ // when shift==16 && Nt<16 && Ni<16, in which
+ // we could use SMULBT below.
+ MOV(AL, 0, dest.reg, reg_imm(inReg, LSR, shift));
+ inReg = dest.reg;
+ shift = 0;
+ }
+ // operation: (Cf*Ct)/((1<<Ni)-1)
+ // approximated with: Cf*(Ct + Ct>>(Ni-1))>>Ni
+ // this operation doesn't change texel's size
+ ADD(AL, 0, dest.reg, inReg, reg_imm(inReg, LSR, Ni-1));
+ if (Nt<16 && Ni<16) SMULBB(AL, dest.reg, texel.reg, dest.reg);
+ else MUL(AL, 0, dest.reg, texel.reg, dest.reg);
+ dest.l = Ni;
+ dest.h = Nt + Ni;
+ } else {
+ if (shift && (shift != 16)) {
+ // if shift==16, we can use 16-bits mul instructions later
+ MOV(AL, 0, dest.reg, reg_imm(inReg, LSR, shift));
+ inReg = dest.reg;
+ shift = 0;
+ }
+ // operation: (Cf*Ct)/((1<<Nt)-1)
+ // approximated with: Ct*(Cf + Cf>>(Nt-1))>>Nt
+ // this operation doesn't change incoming's size
+ Scratch scratches(registerFile());
+ int t = (texel.flags & CORRUPTIBLE) ? texel.reg : dest.reg;
+ if (t == inReg)
+ t = scratches.obtain();
+ ADD(AL, 0, t, texel.reg, reg_imm(texel.reg, LSR, Nt-1));
+ if (Nt<16 && Ni<16) {
+ if (shift==16) SMULBT(AL, dest.reg, t, inReg);
+ else SMULBB(AL, dest.reg, t, inReg);
+ } else MUL(AL, 0, dest.reg, t, inReg);
+ dest.l = Nt;
+ dest.h = Nt + Ni;
+ }
+
+ // low bits are not valid
+ dest.flags |= CLEAR_LO;
+
+ // no need to keep more than 8 bits/component
+ if (dest.size() > 8)
+ dest.l = dest.h-8;
+ }
+}
+
+void GGLAssembler::decal(
+ component_t& dest,
+ const component_t& incoming,
+ const pixel_t& incomingTexel, int component)
+{
+ // RGBA:
+ // Cv = Cf*(1 - At) + Ct*At = Cf + (Ct - Cf)*At
+ // Av = Af
+ Scratch locals(registerFile());
+ integer_t texel(locals.obtain(), 32, CORRUPTIBLE);
+ integer_t factor(locals.obtain(), 32, CORRUPTIBLE);
+ extract(texel, incomingTexel, component);
+ extract(factor, incomingTexel, GGLFormat::ALPHA);
+
+ // no need to keep more than 8-bits for decal
+ int Ni = incoming.size();
+ int shift = incoming.l;
+ if (Ni > 8) {
+ shift += Ni-8;
+ Ni = 8;
+ }
+ integer_t incomingNorm(incoming.reg, Ni, incoming.flags);
+ if (shift) {
+ MOV(AL, 0, dest.reg, reg_imm(incomingNorm.reg, LSR, shift));
+ incomingNorm.reg = dest.reg;
+ incomingNorm.flags |= CORRUPTIBLE;
+ }
+ ADD(AL, 0, factor.reg, factor.reg, reg_imm(factor.reg, LSR, factor.s-1));
+ build_blendOneMinusFF(dest, factor, incomingNorm, texel);
+}
+
+void GGLAssembler::blend(
+ component_t& dest,
+ const component_t& incoming,
+ const pixel_t& incomingTexel, int component, int tmu)
+{
+ // RGBA:
+ // Cv = (1 - Ct)*Cf + Ct*Cc = Cf + (Cc - Cf)*Ct
+ // Av = At*Af
+
+ if (component == GGLFormat::ALPHA) {
+ modulate(dest, incoming, incomingTexel, component);
+ return;
+ }
+
+ Scratch locals(registerFile());
+ integer_t color(locals.obtain(), 8, CORRUPTIBLE);
+ integer_t factor(locals.obtain(), 32, CORRUPTIBLE);
+ LDRB(AL, color.reg, mBuilderContext.Rctx,
+ immed12_pre(GGL_OFFSETOF(state.texture[tmu].env_color[component])));
+ extract(factor, incomingTexel, component);
+
+ // no need to keep more than 8-bits for blend
+ int Ni = incoming.size();
+ int shift = incoming.l;
+ if (Ni > 8) {
+ shift += Ni-8;
+ Ni = 8;
+ }
+ integer_t incomingNorm(incoming.reg, Ni, incoming.flags);
+ if (shift) {
+ MOV(AL, 0, dest.reg, reg_imm(incomingNorm.reg, LSR, shift));
+ incomingNorm.reg = dest.reg;
+ incomingNorm.flags |= CORRUPTIBLE;
+ }
+ ADD(AL, 0, factor.reg, factor.reg, reg_imm(factor.reg, LSR, factor.s-1));
+ build_blendOneMinusFF(dest, factor, incomingNorm, color);
+}
+
+void GGLAssembler::add(
+ component_t& dest,
+ const component_t& incoming,
+ const pixel_t& incomingTexel, int component)
+{
+ // RGBA:
+ // Cv = Cf + Ct;
+ Scratch locals(registerFile());
+
+ component_t incomingTemp(incoming);
+
+ // use "dest" as a temporary for extracting the texel, unless "dest"
+ // overlaps "incoming".
+ integer_t texel(dest.reg, 32, CORRUPTIBLE);
+ if (dest.reg == incomingTemp.reg)
+ texel.reg = locals.obtain();
+ extract(texel, incomingTexel, component);
+
+ if (texel.s < incomingTemp.size()) {
+ expand(texel, texel, incomingTemp.size());
+ } else if (texel.s > incomingTemp.size()) {
+ if (incomingTemp.flags & CORRUPTIBLE) {
+ expand(incomingTemp, incomingTemp, texel.s);
+ } else {
+ incomingTemp.reg = locals.obtain();
+ expand(incomingTemp, incoming, texel.s);
+ }
+ }
+
+ if (incomingTemp.l) {
+ ADD(AL, 0, dest.reg, texel.reg,
+ reg_imm(incomingTemp.reg, LSR, incomingTemp.l));
+ } else {
+ ADD(AL, 0, dest.reg, texel.reg, incomingTemp.reg);
+ }
+ dest.l = 0;
+ dest.h = texel.size();
+ component_sat(dest);
+}
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
diff --git a/libpixelflinger/codeflinger/tinyutils/Errors.h b/libpixelflinger/codeflinger/tinyutils/Errors.h
new file mode 100644
index 0000000..47ae9d7
--- /dev/null
+++ b/libpixelflinger/codeflinger/tinyutils/Errors.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PIXELFLINGER_ERRORS_H
+#define ANDROID_PIXELFLINGER_ERRORS_H
+
+#include <sys/types.h>
+#include <errno.h>
+
+namespace android {
+namespace tinyutils {
+
+// use this type to return error codes
+typedef int32_t status_t;
+
+/*
+ * Error codes.
+ * All error codes are negative values.
+ */
+
+enum {
+ NO_ERROR = 0, // No errors.
+ NO_MEMORY = -ENOMEM,
+ BAD_VALUE = -EINVAL,
+ BAD_INDEX = -EOVERFLOW,
+ NAME_NOT_FOUND = -ENOENT,
+};
+
+
+} // namespace tinyutils
+} // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_PIXELFLINGER_ERRORS_H
diff --git a/libpixelflinger/codeflinger/tinyutils/KeyedVector.h b/libpixelflinger/codeflinger/tinyutils/KeyedVector.h
new file mode 100644
index 0000000..9d8668b
--- /dev/null
+++ b/libpixelflinger/codeflinger/tinyutils/KeyedVector.h
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PIXELFLINGER_KEYED_VECTOR_H
+#define ANDROID_PIXELFLINGER_KEYED_VECTOR_H
+
+#include <assert.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "Errors.h"
+#include "SortedVector.h"
+#include "TypeHelpers.h"
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+namespace tinyutils {
+
+template <typename KEY, typename VALUE>
+class KeyedVector
+{
+public:
+ typedef KEY key_type;
+ typedef VALUE value_type;
+
+ inline KeyedVector();
+
+ /*
+ * empty the vector
+ */
+
+ inline void clear() { mVector.clear(); }
+
+ /*!
+ * vector stats
+ */
+
+ //! returns number of items in the vector
+ inline size_t size() const { return mVector.size(); }
+ //! returns wether or not the vector is empty
+ inline bool isEmpty() const { return mVector.isEmpty(); }
+ //! returns how many items can be stored without reallocating the backing store
+ inline size_t capacity() const { return mVector.capacity(); }
+ //! setst the capacity. capacity can never be reduced less than size()
+ inline ssize_t setCapacity(size_t size) { return mVector.setCapacity(size); }
+
+ /*!
+ * accessors
+ */
+ const VALUE& valueFor(const KEY& key) const;
+ const VALUE& valueAt(size_t index) const;
+ const KEY& keyAt(size_t index) const;
+ ssize_t indexOfKey(const KEY& key) const;
+
+ /*!
+ * modifing the array
+ */
+
+ VALUE& editValueFor(const KEY& key);
+ VALUE& editValueAt(size_t index);
+
+ /*!
+ * add/insert/replace items
+ */
+
+ ssize_t add(const KEY& key, const VALUE& item);
+ ssize_t replaceValueFor(const KEY& key, const VALUE& item);
+ ssize_t replaceValueAt(size_t index, const VALUE& item);
+
+ /*!
+ * remove items
+ */
+
+ ssize_t removeItem(const KEY& key);
+ ssize_t removeItemsAt(size_t index, size_t count = 1);
+
+private:
+ SortedVector< key_value_pair_t<KEY, VALUE> > mVector;
+};
+
+// ---------------------------------------------------------------------------
+
+/**
+ * Variation of KeyedVector that holds a default value to return when
+ * valueFor() is called with a key that doesn't exist.
+ */
+template <typename KEY, typename VALUE>
+class DefaultKeyedVector : public KeyedVector<KEY, VALUE>
+{
+public:
+ inline DefaultKeyedVector(const VALUE& defValue = VALUE());
+ const VALUE& valueFor(const KEY& key) const;
+
+private:
+ VALUE mDefault;
+};
+
+// ---------------------------------------------------------------------------
+
+template<typename KEY, typename VALUE> inline
+KeyedVector<KEY,VALUE>::KeyedVector()
+{
+}
+
+template<typename KEY, typename VALUE> inline
+ssize_t KeyedVector<KEY,VALUE>::indexOfKey(const KEY& key) const {
+ return mVector.indexOf( key_value_pair_t<KEY,VALUE>(key) );
+}
+
+template<typename KEY, typename VALUE> inline
+const VALUE& KeyedVector<KEY,VALUE>::valueFor(const KEY& key) const {
+ ssize_t i = indexOfKey(key);
+ assert(i>=0);
+ return mVector.itemAt(i).value;
+}
+
+template<typename KEY, typename VALUE> inline
+const VALUE& KeyedVector<KEY,VALUE>::valueAt(size_t index) const {
+ return mVector.itemAt(index).value;
+}
+
+template<typename KEY, typename VALUE> inline
+const KEY& KeyedVector<KEY,VALUE>::keyAt(size_t index) const {
+ return mVector.itemAt(index).key;
+}
+
+template<typename KEY, typename VALUE> inline
+VALUE& KeyedVector<KEY,VALUE>::editValueFor(const KEY& key) {
+ ssize_t i = indexOfKey(key);
+ assert(i>=0);
+ return mVector.editItemAt(i).value;
+}
+
+template<typename KEY, typename VALUE> inline
+VALUE& KeyedVector<KEY,VALUE>::editValueAt(size_t index) {
+ return mVector.editItemAt(index).value;
+}
+
+template<typename KEY, typename VALUE> inline
+ssize_t KeyedVector<KEY,VALUE>::add(const KEY& key, const VALUE& value) {
+ return mVector.add( key_value_pair_t<KEY,VALUE>(key, value) );
+}
+
+template<typename KEY, typename VALUE> inline
+ssize_t KeyedVector<KEY,VALUE>::replaceValueFor(const KEY& key, const VALUE& value) {
+ key_value_pair_t<KEY,VALUE> pair(key, value);
+ mVector.remove(pair);
+ return mVector.add(pair);
+}
+
+template<typename KEY, typename VALUE> inline
+ssize_t KeyedVector<KEY,VALUE>::replaceValueAt(size_t index, const VALUE& item) {
+ if (index<size()) {
+ mVector.editValueAt(index).value = item;
+ return index;
+ }
+ return BAD_INDEX;
+}
+
+template<typename KEY, typename VALUE> inline
+ssize_t KeyedVector<KEY,VALUE>::removeItem(const KEY& key) {
+ return mVector.remove(key_value_pair_t<KEY,VALUE>(key));
+}
+
+template<typename KEY, typename VALUE> inline
+ssize_t KeyedVector<KEY, VALUE>::removeItemsAt(size_t index, size_t count) {
+ return mVector.removeItemsAt(index, count);
+}
+
+// ---------------------------------------------------------------------------
+
+template<typename KEY, typename VALUE> inline
+DefaultKeyedVector<KEY,VALUE>::DefaultKeyedVector(const VALUE& defValue)
+ : mDefault(defValue)
+{
+}
+
+template<typename KEY, typename VALUE> inline
+const VALUE& DefaultKeyedVector<KEY,VALUE>::valueFor(const KEY& key) const {
+ ssize_t i = indexOfKey(key);
+ return i >= 0 ? KeyedVector<KEY,VALUE>::valueAt(i) : mDefault;
+}
+
+} // namespace tinyutils
+} // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_PIXELFLINGER_KEYED_VECTOR_H
diff --git a/libpixelflinger/codeflinger/tinyutils/SharedBuffer.cpp b/libpixelflinger/codeflinger/tinyutils/SharedBuffer.cpp
new file mode 100644
index 0000000..ef453fa
--- /dev/null
+++ b/libpixelflinger/codeflinger/tinyutils/SharedBuffer.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2005 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>
+
+#include <cutils/atomic.h>
+
+#include "SharedBuffer.h"
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+namespace tinyutils {
+
+SharedBuffer* SharedBuffer::alloc(size_t size)
+{
+ SharedBuffer* sb = static_cast<SharedBuffer *>(malloc(sizeof(SharedBuffer) + size));
+ if (sb) {
+ sb->mRefs = 1;
+ sb->mSize = size;
+ }
+ return sb;
+}
+
+
+ssize_t SharedBuffer::dealloc(const SharedBuffer* released)
+{
+ if (released->mRefs != 0) return -1; // XXX: invalid operation
+ free(const_cast<SharedBuffer*>(released));
+ return 0;
+}
+
+SharedBuffer* SharedBuffer::edit() const
+{
+ if (onlyOwner()) {
+ return const_cast<SharedBuffer*>(this);
+ }
+ SharedBuffer* sb = alloc(mSize);
+ if (sb) {
+ memcpy(sb->data(), data(), size());
+ release();
+ }
+ return sb;
+}
+
+SharedBuffer* SharedBuffer::editResize(size_t newSize) const
+{
+ if (onlyOwner()) {
+ SharedBuffer* buf = const_cast<SharedBuffer*>(this);
+ if (buf->mSize == newSize) return buf;
+ buf = (SharedBuffer*)realloc(buf, sizeof(SharedBuffer) + newSize);
+ if (buf != NULL) {
+ buf->mSize = newSize;
+ return buf;
+ }
+ }
+ SharedBuffer* sb = alloc(newSize);
+ if (sb) {
+ const size_t mySize = mSize;
+ memcpy(sb->data(), data(), newSize < mySize ? newSize : mySize);
+ release();
+ }
+ return sb;
+}
+
+SharedBuffer* SharedBuffer::attemptEdit() const
+{
+ if (onlyOwner()) {
+ return const_cast<SharedBuffer*>(this);
+ }
+ return 0;
+}
+
+SharedBuffer* SharedBuffer::reset(size_t new_size) const
+{
+ // cheap-o-reset.
+ SharedBuffer* sb = alloc(new_size);
+ if (sb) {
+ release();
+ }
+ return sb;
+}
+
+void SharedBuffer::acquire() const {
+ android_atomic_inc(&mRefs);
+}
+
+int32_t SharedBuffer::release(uint32_t flags) const
+{
+ int32_t prev = 1;
+ if (onlyOwner() || ((prev = android_atomic_dec(&mRefs)) == 1)) {
+ mRefs = 0;
+ if ((flags & eKeepStorage) == 0) {
+ free(const_cast<SharedBuffer*>(this));
+ }
+ }
+ return prev;
+}
+
+} // namespace tinyutils
+} // namespace android
diff --git a/libpixelflinger/codeflinger/tinyutils/SharedBuffer.h b/libpixelflinger/codeflinger/tinyutils/SharedBuffer.h
new file mode 100644
index 0000000..d69b417
--- /dev/null
+++ b/libpixelflinger/codeflinger/tinyutils/SharedBuffer.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PIXELFLINGER_SHARED_BUFFER_H
+#define ANDROID_PIXELFLINGER_SHARED_BUFFER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+namespace tinyutils {
+
+class SharedBuffer
+{
+public:
+
+ /* flags to use with release() */
+ enum {
+ eKeepStorage = 0x00000001
+ };
+
+ /*! allocate a buffer of size 'size' and acquire() it.
+ * call release() to free it.
+ */
+ static SharedBuffer* alloc(size_t size);
+
+ /*! free the memory associated with the SharedBuffer.
+ * Fails if there are any users associated with this SharedBuffer.
+ * In other words, the buffer must have been release by all its
+ * users.
+ */
+ static ssize_t dealloc(const SharedBuffer* released);
+
+ //! get the SharedBuffer from the data pointer
+ static inline const SharedBuffer* sharedBuffer(const void* data);
+
+ //! access the data for read
+ inline const void* data() const;
+
+ //! access the data for read/write
+ inline void* data();
+
+ //! get size of the buffer
+ inline size_t size() const;
+
+ //! get back a SharedBuffer object from its data
+ static inline SharedBuffer* bufferFromData(void* data);
+
+ //! get back a SharedBuffer object from its data
+ static inline const SharedBuffer* bufferFromData(const void* data);
+
+ //! get the size of a SharedBuffer object from its data
+ static inline size_t sizeFromData(const void* data);
+
+ //! edit the buffer (get a writtable, or non-const, version of it)
+ SharedBuffer* edit() const;
+
+ //! edit the buffer, resizing if needed
+ SharedBuffer* editResize(size_t size) const;
+
+ //! like edit() but fails if a copy is required
+ SharedBuffer* attemptEdit() const;
+
+ //! resize and edit the buffer, loose it's content.
+ SharedBuffer* reset(size_t size) const;
+
+ //! acquire/release a reference on this buffer
+ void acquire() const;
+
+ /*! release a reference on this buffer, with the option of not
+ * freeing the memory associated with it if it was the last reference
+ * returns the previous reference count
+ */
+ int32_t release(uint32_t flags = 0) const;
+
+ //! returns wether or not we're the only owner
+ inline bool onlyOwner() const;
+
+
+private:
+ inline SharedBuffer() { }
+ inline ~SharedBuffer() { }
+ inline SharedBuffer(const SharedBuffer&);
+
+ // 16 bytes. must be sized to preserve correct alingment.
+ mutable int32_t mRefs;
+ size_t mSize;
+ uint32_t mReserved[2];
+};
+
+// ---------------------------------------------------------------------------
+
+const SharedBuffer* SharedBuffer::sharedBuffer(const void* data) {
+ return data ? reinterpret_cast<const SharedBuffer *>(data)-1 : 0;
+}
+
+const void* SharedBuffer::data() const {
+ return this + 1;
+}
+
+void* SharedBuffer::data() {
+ return this + 1;
+}
+
+size_t SharedBuffer::size() const {
+ return mSize;
+}
+
+SharedBuffer* SharedBuffer::bufferFromData(void* data)
+{
+ return ((SharedBuffer*)data)-1;
+}
+
+const SharedBuffer* SharedBuffer::bufferFromData(const void* data)
+{
+ return ((const SharedBuffer*)data)-1;
+}
+
+size_t SharedBuffer::sizeFromData(const void* data)
+{
+ return (((const SharedBuffer*)data)-1)->mSize;
+}
+
+bool SharedBuffer::onlyOwner() const {
+ return (mRefs == 1);
+}
+
+} // namespace tinyutils
+} // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_PIXELFLINGER_SHARED_BUFFER_H
diff --git a/libpixelflinger/codeflinger/tinyutils/SortedVector.h b/libpixelflinger/codeflinger/tinyutils/SortedVector.h
new file mode 100644
index 0000000..a2b7005
--- /dev/null
+++ b/libpixelflinger/codeflinger/tinyutils/SortedVector.h
@@ -0,0 +1,284 @@
+/*
+ * Copyright 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PIXELFLINGER_SORTED_VECTOR_H
+#define ANDROID_PIXELFLINGER_SORTED_VECTOR_H
+
+#include <assert.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "Vector.h"
+#include "VectorImpl.h"
+#include "TypeHelpers.h"
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+namespace tinyutils {
+
+template <class TYPE>
+class SortedVector : private SortedVectorImpl
+{
+public:
+ typedef TYPE value_type;
+
+ /*!
+ * Constructors and destructors
+ */
+
+ SortedVector();
+ SortedVector(const SortedVector<TYPE>& rhs);
+ virtual ~SortedVector();
+
+ /*! copy operator */
+ const SortedVector<TYPE>& operator = (const SortedVector<TYPE>& rhs) const;
+ SortedVector<TYPE>& operator = (const SortedVector<TYPE>& rhs);
+
+ /*
+ * empty the vector
+ */
+
+ inline void clear() { VectorImpl::clear(); }
+
+ /*!
+ * vector stats
+ */
+
+ //! returns number of items in the vector
+ inline size_t size() const { return VectorImpl::size(); }
+ //! returns wether or not the vector is empty
+ inline bool isEmpty() const { return VectorImpl::isEmpty(); }
+ //! returns how many items can be stored without reallocating the backing store
+ inline size_t capacity() const { return VectorImpl::capacity(); }
+ //! setst the capacity. capacity can never be reduced less than size()
+ inline ssize_t setCapacity(size_t size) { return VectorImpl::setCapacity(size); }
+
+ /*!
+ * C-style array access
+ */
+
+ //! read-only C-style access
+ inline const TYPE* array() const;
+
+ //! read-write C-style access. BE VERY CAREFUL when modifying the array
+ //! you ust keep it sorted! You usually don't use this function.
+ TYPE* editArray();
+
+ //! finds the index of an item
+ ssize_t indexOf(const TYPE& item) const;
+
+ //! finds where this item should be inserted
+ size_t orderOf(const TYPE& item) const;
+
+
+ /*!
+ * accessors
+ */
+
+ //! read-only access to an item at a given index
+ inline const TYPE& operator [] (size_t index) const;
+ //! alternate name for operator []
+ inline const TYPE& itemAt(size_t index) const;
+ //! stack-usage of the vector. returns the top of the stack (last element)
+ const TYPE& top() const;
+ //! same as operator [], but allows to access the vector backward (from the end) with a negative index
+ const TYPE& mirrorItemAt(ssize_t index) const;
+
+ /*!
+ * modifing the array
+ */
+
+ //! add an item in the right place (and replace the one that is there)
+ ssize_t add(const TYPE& item);
+
+ //! editItemAt() MUST NOT change the order of this item
+ TYPE& editItemAt(size_t index) {
+ return *( static_cast<TYPE *>(VectorImpl::editItemLocation(index)) );
+ }
+
+ //! merges a vector into this one
+ ssize_t merge(const Vector<TYPE>& vector);
+ ssize_t merge(const SortedVector<TYPE>& vector);
+
+ //! removes an item
+ ssize_t remove(const TYPE&);
+
+ //! remove several items
+ inline ssize_t removeItemsAt(size_t index, size_t count = 1);
+ //! remove one item
+ inline ssize_t removeAt(size_t index) { return removeItemsAt(index); }
+
+protected:
+ virtual void do_construct(void* storage, size_t num) const;
+ virtual void do_destroy(void* storage, size_t num) const;
+ virtual void do_copy(void* dest, const void* from, size_t num) const;
+ virtual void do_splat(void* dest, const void* item, size_t num) const;
+ virtual void do_move_forward(void* dest, const void* from, size_t num) const;
+ virtual void do_move_backward(void* dest, const void* from, size_t num) const;
+ virtual int do_compare(const void* lhs, const void* rhs) const;
+};
+
+
+// ---------------------------------------------------------------------------
+// No user serviceable parts from here...
+// ---------------------------------------------------------------------------
+
+template<class TYPE> inline
+SortedVector<TYPE>::SortedVector()
+ : SortedVectorImpl(sizeof(TYPE),
+ ((traits<TYPE>::has_trivial_ctor ? HAS_TRIVIAL_CTOR : 0)
+ |(traits<TYPE>::has_trivial_dtor ? HAS_TRIVIAL_DTOR : 0)
+ |(traits<TYPE>::has_trivial_copy ? HAS_TRIVIAL_COPY : 0)
+ |(traits<TYPE>::has_trivial_assign ? HAS_TRIVIAL_ASSIGN : 0))
+ )
+{
+}
+
+template<class TYPE> inline
+SortedVector<TYPE>::SortedVector(const SortedVector<TYPE>& rhs)
+ : SortedVectorImpl(rhs) {
+}
+
+template<class TYPE> inline
+SortedVector<TYPE>::~SortedVector() {
+ finish_vector();
+}
+
+template<class TYPE> inline
+SortedVector<TYPE>& SortedVector<TYPE>::operator = (const SortedVector<TYPE>& rhs) {
+ SortedVectorImpl::operator = (rhs);
+ return *this;
+}
+
+template<class TYPE> inline
+const SortedVector<TYPE>& SortedVector<TYPE>::operator = (const SortedVector<TYPE>& rhs) const {
+ SortedVectorImpl::operator = (rhs);
+ return *this;
+}
+
+template<class TYPE> inline
+const TYPE* SortedVector<TYPE>::array() const {
+ return static_cast<const TYPE *>(arrayImpl());
+}
+
+template<class TYPE> inline
+TYPE* SortedVector<TYPE>::editArray() {
+ return static_cast<TYPE *>(editArrayImpl());
+}
+
+
+template<class TYPE> inline
+const TYPE& SortedVector<TYPE>::operator[](size_t index) const {
+ assert( index<size() );
+ return *(array() + index);
+}
+
+template<class TYPE> inline
+const TYPE& SortedVector<TYPE>::itemAt(size_t index) const {
+ return operator[](index);
+}
+
+template<class TYPE> inline
+const TYPE& SortedVector<TYPE>::mirrorItemAt(ssize_t index) const {
+ assert( (index>0 ? index : -index)<size() );
+ return *(array() + ((index<0) ? (size()-index) : index));
+}
+
+template<class TYPE> inline
+const TYPE& SortedVector<TYPE>::top() const {
+ return *(array() + size() - 1);
+}
+
+template<class TYPE> inline
+ssize_t SortedVector<TYPE>::add(const TYPE& item) {
+ return SortedVectorImpl::add(&item);
+}
+
+template<class TYPE> inline
+ssize_t SortedVector<TYPE>::indexOf(const TYPE& item) const {
+ return SortedVectorImpl::indexOf(&item);
+}
+
+template<class TYPE> inline
+size_t SortedVector<TYPE>::orderOf(const TYPE& item) const {
+ return SortedVectorImpl::orderOf(&item);
+}
+
+template<class TYPE> inline
+ssize_t SortedVector<TYPE>::merge(const Vector<TYPE>& vector) {
+ return SortedVectorImpl::merge(reinterpret_cast<const VectorImpl&>(vector));
+}
+
+template<class TYPE> inline
+ssize_t SortedVector<TYPE>::merge(const SortedVector<TYPE>& vector) {
+ return SortedVectorImpl::merge(reinterpret_cast<const SortedVectorImpl&>(vector));
+}
+
+template<class TYPE> inline
+ssize_t SortedVector<TYPE>::remove(const TYPE& item) {
+ return SortedVectorImpl::remove(&item);
+}
+
+template<class TYPE> inline
+ssize_t SortedVector<TYPE>::removeItemsAt(size_t index, size_t count) {
+ return VectorImpl::removeItemsAt(index, count);
+}
+
+// ---------------------------------------------------------------------------
+
+template<class TYPE>
+void SortedVector<TYPE>::do_construct(void* storage, size_t num) const {
+ construct_type( reinterpret_cast<TYPE*>(storage), num );
+}
+
+template<class TYPE>
+void SortedVector<TYPE>::do_destroy(void* storage, size_t num) const {
+ destroy_type( reinterpret_cast<TYPE*>(storage), num );
+}
+
+template<class TYPE>
+void SortedVector<TYPE>::do_copy(void* dest, const void* from, size_t num) const {
+ copy_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
+}
+
+template<class TYPE>
+void SortedVector<TYPE>::do_splat(void* dest, const void* item, size_t num) const {
+ splat_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(item), num );
+}
+
+template<class TYPE>
+void SortedVector<TYPE>::do_move_forward(void* dest, const void* from, size_t num) const {
+ move_forward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
+}
+
+template<class TYPE>
+void SortedVector<TYPE>::do_move_backward(void* dest, const void* from, size_t num) const {
+ move_backward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
+}
+
+template<class TYPE>
+int SortedVector<TYPE>::do_compare(const void* lhs, const void* rhs) const {
+ return compare_type( *reinterpret_cast<const TYPE*>(lhs), *reinterpret_cast<const TYPE*>(rhs) );
+}
+
+} // namespace tinyutils
+} // namespace android
+
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_PIXELFLINGER_SORTED_VECTOR_H
diff --git a/libpixelflinger/codeflinger/tinyutils/TypeHelpers.h b/libpixelflinger/codeflinger/tinyutils/TypeHelpers.h
new file mode 100644
index 0000000..7abff07
--- /dev/null
+++ b/libpixelflinger/codeflinger/tinyutils/TypeHelpers.h
@@ -0,0 +1,256 @@
+/*
+ * Copyright 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PIXELFLINGER_TYPE_HELPERS_H
+#define ANDROID_PIXELFLINGER_TYPE_HELPERS_H
+
+#include <new>
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+namespace tinyutils {
+
+/*
+ * Types traits
+ */
+
+template <typename T> struct trait_trivial_ctor { enum { value = false }; };
+template <typename T> struct trait_trivial_dtor { enum { value = false }; };
+template <typename T> struct trait_trivial_copy { enum { value = false }; };
+template <typename T> struct trait_trivial_assign{ enum { value = false }; };
+
+template <typename T> struct trait_pointer { enum { value = false }; };
+template <typename T> struct trait_pointer<T*> { enum { value = true }; };
+
+#define ANDROID_BASIC_TYPES_TRAITS( T ) \
+ template<> struct trait_trivial_ctor< T > { enum { value = true }; }; \
+ template<> struct trait_trivial_dtor< T > { enum { value = true }; }; \
+ template<> struct trait_trivial_copy< T > { enum { value = true }; }; \
+ template<> struct trait_trivial_assign< T >{ enum { value = true }; };
+
+#define ANDROID_TYPE_TRAITS( T, ctor, dtor, copy, assign ) \
+ template<> struct trait_trivial_ctor< T > { enum { value = ctor }; }; \
+ template<> struct trait_trivial_dtor< T > { enum { value = dtor }; }; \
+ template<> struct trait_trivial_copy< T > { enum { value = copy }; }; \
+ template<> struct trait_trivial_assign< T >{ enum { value = assign }; };
+
+template <typename TYPE>
+struct traits {
+ enum {
+ is_pointer = trait_pointer<TYPE>::value,
+ has_trivial_ctor = is_pointer || trait_trivial_ctor<TYPE>::value,
+ has_trivial_dtor = is_pointer || trait_trivial_dtor<TYPE>::value,
+ has_trivial_copy = is_pointer || trait_trivial_copy<TYPE>::value,
+ has_trivial_assign = is_pointer || trait_trivial_assign<TYPE>::value
+ };
+};
+
+template <typename T, typename U>
+struct aggregate_traits {
+ enum {
+ is_pointer = false,
+ has_trivial_ctor = traits<T>::has_trivial_ctor && traits<U>::has_trivial_ctor,
+ has_trivial_dtor = traits<T>::has_trivial_dtor && traits<U>::has_trivial_dtor,
+ has_trivial_copy = traits<T>::has_trivial_copy && traits<U>::has_trivial_copy,
+ has_trivial_assign = traits<T>::has_trivial_assign && traits<U>::has_trivial_assign
+ };
+};
+
+// ---------------------------------------------------------------------------
+
+/*
+ * basic types traits
+ */
+
+ANDROID_BASIC_TYPES_TRAITS( void );
+ANDROID_BASIC_TYPES_TRAITS( bool );
+ANDROID_BASIC_TYPES_TRAITS( char );
+ANDROID_BASIC_TYPES_TRAITS( unsigned char );
+ANDROID_BASIC_TYPES_TRAITS( short );
+ANDROID_BASIC_TYPES_TRAITS( unsigned short );
+ANDROID_BASIC_TYPES_TRAITS( int );
+ANDROID_BASIC_TYPES_TRAITS( unsigned int );
+ANDROID_BASIC_TYPES_TRAITS( long );
+ANDROID_BASIC_TYPES_TRAITS( unsigned long );
+ANDROID_BASIC_TYPES_TRAITS( long long );
+ANDROID_BASIC_TYPES_TRAITS( unsigned long long );
+ANDROID_BASIC_TYPES_TRAITS( float );
+ANDROID_BASIC_TYPES_TRAITS( double );
+
+// ---------------------------------------------------------------------------
+
+
+/*
+ * compare and order types
+ */
+
+template<typename TYPE> inline
+int strictly_order_type(const TYPE& lhs, const TYPE& rhs) {
+ return (lhs < rhs) ? 1 : 0;
+}
+
+template<typename TYPE> inline
+int compare_type(const TYPE& lhs, const TYPE& rhs) {
+ return strictly_order_type(rhs, lhs) - strictly_order_type(lhs, rhs);
+}
+
+/*
+ * create, destroy, copy and assign types...
+ */
+
+template<typename TYPE> inline
+void construct_type(TYPE* p, size_t n) {
+ if (!traits<TYPE>::has_trivial_ctor) {
+ while (n--) {
+ new(p++) TYPE;
+ }
+ }
+}
+
+template<typename TYPE> inline
+void destroy_type(TYPE* p, size_t n) {
+ if (!traits<TYPE>::has_trivial_dtor) {
+ while (n--) {
+ p->~TYPE();
+ p++;
+ }
+ }
+}
+
+template<typename TYPE> inline
+void copy_type(TYPE* d, const TYPE* s, size_t n) {
+ if (!traits<TYPE>::has_trivial_copy) {
+ while (n--) {
+ new(d) TYPE(*s);
+ d++, s++;
+ }
+ } else {
+ memcpy(d,s,n*sizeof(TYPE));
+ }
+}
+
+template<typename TYPE> inline
+void assign_type(TYPE* d, const TYPE* s, size_t n) {
+ if (!traits<TYPE>::has_trivial_assign) {
+ while (n--) {
+ *d++ = *s++;
+ }
+ } else {
+ memcpy(d,s,n*sizeof(TYPE));
+ }
+}
+
+template<typename TYPE> inline
+void splat_type(TYPE* where, const TYPE* what, size_t n) {
+ if (!traits<TYPE>::has_trivial_copy) {
+ while (n--) {
+ new(where) TYPE(*what);
+ where++;
+ }
+ } else {
+ while (n--) {
+ *where++ = *what;
+ }
+ }
+}
+
+template<typename TYPE> inline
+void move_forward_type(TYPE* d, const TYPE* s, size_t n = 1) {
+ if (!traits<TYPE>::has_trivial_copy || !traits<TYPE>::has_trivial_dtor) {
+ d += n;
+ s += n;
+ while (n--) {
+ --d, --s;
+ if (!traits<TYPE>::has_trivial_copy) {
+ new(d) TYPE(*s);
+ } else {
+ *d = *s;
+ }
+ if (!traits<TYPE>::has_trivial_dtor) {
+ s->~TYPE();
+ }
+ }
+ } else {
+ memmove(d,s,n*sizeof(TYPE));
+ }
+}
+
+template<typename TYPE> inline
+void move_backward_type(TYPE* d, const TYPE* s, size_t n = 1) {
+ if (!traits<TYPE>::has_trivial_copy || !traits<TYPE>::has_trivial_dtor) {
+ while (n--) {
+ if (!traits<TYPE>::has_trivial_copy) {
+ new(d) TYPE(*s);
+ } else {
+ *d = *s;
+ }
+ if (!traits<TYPE>::has_trivial_dtor) {
+ s->~TYPE();
+ }
+ d++, s++;
+ }
+ } else {
+ memmove(d,s,n*sizeof(TYPE));
+ }
+}
+// ---------------------------------------------------------------------------
+
+/*
+ * a key/value pair
+ */
+
+template <typename KEY, typename VALUE>
+struct key_value_pair_t {
+ KEY key;
+ VALUE value;
+ key_value_pair_t() { }
+ key_value_pair_t(const key_value_pair_t& o) : key(o.key), value(o.value) { }
+ key_value_pair_t(const KEY& k, const VALUE& v) : key(k), value(v) { }
+ key_value_pair_t(const KEY& k) : key(k) { }
+ inline bool operator < (const key_value_pair_t& o) const {
+ return strictly_order_type(key, o.key);
+ }
+};
+
+template<>
+template <typename K, typename V>
+struct trait_trivial_ctor< key_value_pair_t<K, V> >
+{ enum { value = aggregate_traits<K,V>::has_trivial_ctor }; };
+template<>
+template <typename K, typename V>
+struct trait_trivial_dtor< key_value_pair_t<K, V> >
+{ enum { value = aggregate_traits<K,V>::has_trivial_dtor }; };
+template<>
+template <typename K, typename V>
+struct trait_trivial_copy< key_value_pair_t<K, V> >
+{ enum { value = aggregate_traits<K,V>::has_trivial_copy }; };
+template<>
+template <typename K, typename V>
+struct trait_trivial_assign< key_value_pair_t<K, V> >
+{ enum { value = aggregate_traits<K,V>::has_trivial_assign};};
+
+// ---------------------------------------------------------------------------
+
+} // namespace tinyutils
+} // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_PIXELFLINGER_TYPE_HELPERS_H
diff --git a/libpixelflinger/codeflinger/tinyutils/Vector.h b/libpixelflinger/codeflinger/tinyutils/Vector.h
new file mode 100644
index 0000000..c07a17a
--- /dev/null
+++ b/libpixelflinger/codeflinger/tinyutils/Vector.h
@@ -0,0 +1,353 @@
+/*
+ * Copyright 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PIXELFLINGER_VECTOR_H
+#define ANDROID_PIXELFLINGER_VECTOR_H
+
+#include <new>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <cutils/log.h>
+
+#include "Errors.h"
+#include "VectorImpl.h"
+#include "TypeHelpers.h"
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+namespace tinyutils {
+
+/*!
+ * The main templated vector class ensuring type safety
+ * while making use of VectorImpl.
+ * This is the class users want to use.
+ */
+
+template <class TYPE>
+class Vector : private VectorImpl
+{
+public:
+ typedef TYPE value_type;
+
+ /*!
+ * Constructors and destructors
+ */
+
+ Vector();
+ Vector(const Vector<TYPE>& rhs);
+ virtual ~Vector();
+
+ /*! copy operator */
+ const Vector<TYPE>& operator = (const Vector<TYPE>& rhs) const;
+ Vector<TYPE>& operator = (const Vector<TYPE>& rhs);
+
+ /*
+ * empty the vector
+ */
+
+ inline void clear() { VectorImpl::clear(); }
+
+ /*!
+ * vector stats
+ */
+
+ //! returns number of items in the vector
+ inline size_t size() const { return VectorImpl::size(); }
+ //! returns wether or not the vector is empty
+ inline bool isEmpty() const { return VectorImpl::isEmpty(); }
+ //! returns how many items can be stored without reallocating the backing store
+ inline size_t capacity() const { return VectorImpl::capacity(); }
+ //! setst the capacity. capacity can never be reduced less than size()
+ inline ssize_t setCapacity(size_t size) { return VectorImpl::setCapacity(size); }
+
+ /*!
+ * C-style array access
+ */
+
+ //! read-only C-style access
+ inline const TYPE* array() const;
+ //! read-write C-style access
+ TYPE* editArray();
+
+ /*!
+ * accessors
+ */
+
+ //! read-only access to an item at a given index
+ inline const TYPE& operator [] (size_t index) const;
+ //! alternate name for operator []
+ inline const TYPE& itemAt(size_t index) const;
+ //! stack-usage of the vector. returns the top of the stack (last element)
+ const TYPE& top() const;
+ //! same as operator [], but allows to access the vector backward (from the end) with a negative index
+ const TYPE& mirrorItemAt(ssize_t index) const;
+
+ /*!
+ * modifing the array
+ */
+
+ //! copy-on write support, grants write access to an item
+ TYPE& editItemAt(size_t index);
+ //! grants right acces to the top of the stack (last element)
+ TYPE& editTop();
+
+ /*!
+ * append/insert another vector
+ */
+
+ //! insert another vector at a given index
+ ssize_t insertVectorAt(const Vector<TYPE>& vector, size_t index);
+
+ //! append another vector at the end of this one
+ ssize_t appendVector(const Vector<TYPE>& vector);
+
+
+ /*!
+ * add/insert/replace items
+ */
+
+ //! insert one or several items initialized with their default constructor
+ inline ssize_t insertAt(size_t index, size_t numItems = 1);
+ //! insert on onr several items initialized from a prototype item
+ ssize_t insertAt(const TYPE& prototype_item, size_t index, size_t numItems = 1);
+ //! pop the top of the stack (removes the last element). No-op if the stack's empty
+ inline void pop();
+ //! pushes an item initialized with its default constructor
+ inline void push();
+ //! pushes an item on the top of the stack
+ void push(const TYPE& item);
+ //! same as push() but returns the index the item was added at (or an error)
+ inline ssize_t add();
+ //! same as push() but returns the index the item was added at (or an error)
+ ssize_t add(const TYPE& item);
+ //! replace an item with a new one initialized with its default constructor
+ inline ssize_t replaceAt(size_t index);
+ //! replace an item with a new one
+ ssize_t replaceAt(const TYPE& item, size_t index);
+
+ /*!
+ * remove items
+ */
+
+ //! remove several items
+ inline ssize_t removeItemsAt(size_t index, size_t count = 1);
+ //! remove one item
+ inline ssize_t removeAt(size_t index) { return removeItemsAt(index); }
+
+ /*!
+ * sort (stable) the array
+ */
+
+ typedef int (*compar_t)(const TYPE* lhs, const TYPE* rhs);
+ typedef int (*compar_r_t)(const TYPE* lhs, const TYPE* rhs, void* state);
+
+ inline status_t sort(compar_t cmp);
+ inline status_t sort(compar_r_t cmp, void* state);
+
+protected:
+ virtual void do_construct(void* storage, size_t num) const;
+ virtual void do_destroy(void* storage, size_t num) const;
+ virtual void do_copy(void* dest, const void* from, size_t num) const;
+ virtual void do_splat(void* dest, const void* item, size_t num) const;
+ virtual void do_move_forward(void* dest, const void* from, size_t num) const;
+ virtual void do_move_backward(void* dest, const void* from, size_t num) const;
+};
+
+
+// ---------------------------------------------------------------------------
+// No user serviceable parts from here...
+// ---------------------------------------------------------------------------
+
+template<class TYPE> inline
+Vector<TYPE>::Vector()
+ : VectorImpl(sizeof(TYPE),
+ ((traits<TYPE>::has_trivial_ctor ? HAS_TRIVIAL_CTOR : 0)
+ |(traits<TYPE>::has_trivial_dtor ? HAS_TRIVIAL_DTOR : 0)
+ |(traits<TYPE>::has_trivial_copy ? HAS_TRIVIAL_COPY : 0)
+ |(traits<TYPE>::has_trivial_assign ? HAS_TRIVIAL_ASSIGN : 0))
+ )
+{
+}
+
+template<class TYPE> inline
+Vector<TYPE>::Vector(const Vector<TYPE>& rhs)
+ : VectorImpl(rhs) {
+}
+
+template<class TYPE> inline
+Vector<TYPE>::~Vector() {
+ finish_vector();
+}
+
+template<class TYPE> inline
+Vector<TYPE>& Vector<TYPE>::operator = (const Vector<TYPE>& rhs) {
+ VectorImpl::operator = (rhs);
+ return *this;
+}
+
+template<class TYPE> inline
+const Vector<TYPE>& Vector<TYPE>::operator = (const Vector<TYPE>& rhs) const {
+ VectorImpl::operator = (rhs);
+ return *this;
+}
+
+template<class TYPE> inline
+const TYPE* Vector<TYPE>::array() const {
+ return static_cast<const TYPE *>(arrayImpl());
+}
+
+template<class TYPE> inline
+TYPE* Vector<TYPE>::editArray() {
+ return static_cast<TYPE *>(editArrayImpl());
+}
+
+
+template<class TYPE> inline
+const TYPE& Vector<TYPE>::operator[](size_t index) const {
+ LOG_FATAL_IF( index>=size(),
+ "itemAt: index %d is past size %d", (int)index, (int)size() );
+ return *(array() + index);
+}
+
+template<class TYPE> inline
+const TYPE& Vector<TYPE>::itemAt(size_t index) const {
+ return operator[](index);
+}
+
+template<class TYPE> inline
+const TYPE& Vector<TYPE>::mirrorItemAt(ssize_t index) const {
+ LOG_FATAL_IF( (index>0 ? index : -index)>=size(),
+ "mirrorItemAt: index %d is past size %d",
+ (int)index, (int)size() );
+ return *(array() + ((index<0) ? (size()-index) : index));
+}
+
+template<class TYPE> inline
+const TYPE& Vector<TYPE>::top() const {
+ return *(array() + size() - 1);
+}
+
+template<class TYPE> inline
+TYPE& Vector<TYPE>::editItemAt(size_t index) {
+ return *( static_cast<TYPE *>(editItemLocation(index)) );
+}
+
+template<class TYPE> inline
+TYPE& Vector<TYPE>::editTop() {
+ return *( static_cast<TYPE *>(editItemLocation(size()-1)) );
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::insertVectorAt(const Vector<TYPE>& vector, size_t index) {
+ return VectorImpl::insertVectorAt(reinterpret_cast<const VectorImpl&>(vector), index);
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::appendVector(const Vector<TYPE>& vector) {
+ return VectorImpl::appendVector(reinterpret_cast<const VectorImpl&>(vector));
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::insertAt(const TYPE& item, size_t index, size_t numItems) {
+ return VectorImpl::insertAt(&item, index, numItems);
+}
+
+template<class TYPE> inline
+void Vector<TYPE>::push(const TYPE& item) {
+ return VectorImpl::push(&item);
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::add(const TYPE& item) {
+ return VectorImpl::add(&item);
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::replaceAt(const TYPE& item, size_t index) {
+ return VectorImpl::replaceAt(&item, index);
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::insertAt(size_t index, size_t numItems) {
+ return VectorImpl::insertAt(index, numItems);
+}
+
+template<class TYPE> inline
+void Vector<TYPE>::pop() {
+ VectorImpl::pop();
+}
+
+template<class TYPE> inline
+void Vector<TYPE>::push() {
+ VectorImpl::push();
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::add() {
+ return VectorImpl::add();
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::replaceAt(size_t index) {
+ return VectorImpl::replaceAt(index);
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::removeItemsAt(size_t index, size_t count) {
+ return VectorImpl::removeItemsAt(index, count);
+}
+
+// ---------------------------------------------------------------------------
+
+template<class TYPE>
+void Vector<TYPE>::do_construct(void* storage, size_t num) const {
+ construct_type( reinterpret_cast<TYPE*>(storage), num );
+}
+
+template<class TYPE>
+void Vector<TYPE>::do_destroy(void* storage, size_t num) const {
+ destroy_type( reinterpret_cast<TYPE*>(storage), num );
+}
+
+template<class TYPE>
+void Vector<TYPE>::do_copy(void* dest, const void* from, size_t num) const {
+ copy_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
+}
+
+template<class TYPE>
+void Vector<TYPE>::do_splat(void* dest, const void* item, size_t num) const {
+ splat_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(item), num );
+}
+
+template<class TYPE>
+void Vector<TYPE>::do_move_forward(void* dest, const void* from, size_t num) const {
+ move_forward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
+}
+
+template<class TYPE>
+void Vector<TYPE>::do_move_backward(void* dest, const void* from, size_t num) const {
+ move_backward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
+}
+
+} // namespace tinyutils
+} // namespace android
+
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_PIXELFLINGER_VECTOR_H
diff --git a/libpixelflinger/codeflinger/tinyutils/VectorImpl.cpp b/libpixelflinger/codeflinger/tinyutils/VectorImpl.cpp
new file mode 100644
index 0000000..689129a
--- /dev/null
+++ b/libpixelflinger/codeflinger/tinyutils/VectorImpl.cpp
@@ -0,0 +1,555 @@
+/*
+ * Copyright 2005 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 "Vector"
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <cutils/log.h>
+
+#include "Errors.h"
+#include "SharedBuffer.h"
+#include "VectorImpl.h"
+
+/*****************************************************************************/
+
+
+namespace android {
+namespace tinyutils {
+
+// ----------------------------------------------------------------------------
+
+const size_t kMinVectorCapacity = 4;
+
+static inline size_t max(size_t a, size_t b) {
+ return a>b ? a : b;
+}
+
+// ----------------------------------------------------------------------------
+
+VectorImpl::VectorImpl(size_t itemSize, uint32_t flags)
+ : mStorage(0), mCount(0), mFlags(flags), mItemSize(itemSize)
+{
+}
+
+VectorImpl::VectorImpl(const VectorImpl& rhs)
+ : mStorage(rhs.mStorage), mCount(rhs.mCount),
+ mFlags(rhs.mFlags), mItemSize(rhs.mItemSize)
+{
+ if (mStorage) {
+ SharedBuffer::sharedBuffer(mStorage)->acquire();
+ }
+}
+
+VectorImpl::~VectorImpl()
+{
+ ALOG_ASSERT(!mCount,
+ "[%p] "
+ "subclasses of VectorImpl must call finish_vector()"
+ " in their destructor. Leaking %d bytes.",
+ this, (int)(mCount*mItemSize));
+ // We can't call _do_destroy() here because the vtable is already gone.
+}
+
+VectorImpl& VectorImpl::operator = (const VectorImpl& rhs)
+{
+ ALOG_ASSERT(mItemSize == rhs.mItemSize,
+ "Vector<> have different types (this=%p, rhs=%p)", this, &rhs);
+ if (this != &rhs) {
+ release_storage();
+ if (rhs.mCount) {
+ mStorage = rhs.mStorage;
+ mCount = rhs.mCount;
+ SharedBuffer::sharedBuffer(mStorage)->acquire();
+ } else {
+ mStorage = 0;
+ mCount = 0;
+ }
+ }
+ return *this;
+}
+
+void* VectorImpl::editArrayImpl()
+{
+ if (mStorage) {
+ SharedBuffer* sb = SharedBuffer::sharedBuffer(mStorage)->attemptEdit();
+ if (sb == 0) {
+ sb = SharedBuffer::alloc(capacity() * mItemSize);
+ if (sb) {
+ _do_copy(sb->data(), mStorage, mCount);
+ release_storage();
+ mStorage = sb->data();
+ }
+ }
+ }
+ return mStorage;
+}
+
+size_t VectorImpl::capacity() const
+{
+ if (mStorage) {
+ return SharedBuffer::sharedBuffer(mStorage)->size() / mItemSize;
+ }
+ return 0;
+}
+
+ssize_t VectorImpl::insertVectorAt(const VectorImpl& vector, size_t index)
+{
+ if (index > size())
+ return BAD_INDEX;
+ void* where = _grow(index, vector.size());
+ if (where) {
+ _do_copy(where, vector.arrayImpl(), vector.size());
+ }
+ return where ? index : (ssize_t)NO_MEMORY;
+}
+
+ssize_t VectorImpl::appendVector(const VectorImpl& vector)
+{
+ return insertVectorAt(vector, size());
+}
+
+ssize_t VectorImpl::insertAt(size_t index, size_t numItems)
+{
+ return insertAt(0, index, numItems);
+}
+
+ssize_t VectorImpl::insertAt(const void* item, size_t index, size_t numItems)
+{
+ if (index > size())
+ return BAD_INDEX;
+ void* where = _grow(index, numItems);
+ if (where) {
+ if (item) {
+ _do_splat(where, item, numItems);
+ } else {
+ _do_construct(where, numItems);
+ }
+ }
+ return where ? index : (ssize_t)NO_MEMORY;
+}
+
+void VectorImpl::pop()
+{
+ if (size())
+ removeItemsAt(size()-1, 1);
+}
+
+void VectorImpl::push()
+{
+ push(0);
+}
+
+void VectorImpl::push(const void* item)
+{
+ insertAt(item, size());
+}
+
+ssize_t VectorImpl::add()
+{
+ return add(0);
+}
+
+ssize_t VectorImpl::add(const void* item)
+{
+ return insertAt(item, size());
+}
+
+ssize_t VectorImpl::replaceAt(size_t index)
+{
+ return replaceAt(0, index);
+}
+
+ssize_t VectorImpl::replaceAt(const void* prototype, size_t index)
+{
+ ALOG_ASSERT(index<size(),
+ "[%p] replace: index=%d, size=%d", this, (int)index, (int)size());
+
+ void* item = editItemLocation(index);
+ if (item == 0)
+ return NO_MEMORY;
+ _do_destroy(item, 1);
+ if (prototype == 0) {
+ _do_construct(item, 1);
+ } else {
+ _do_copy(item, prototype, 1);
+ }
+ return ssize_t(index);
+}
+
+ssize_t VectorImpl::removeItemsAt(size_t index, size_t count)
+{
+ ALOG_ASSERT((index+count)<=size(),
+ "[%p] remove: index=%d, count=%d, size=%d",
+ this, (int)index, (int)count, (int)size());
+
+ if ((index+count) > size())
+ return BAD_VALUE;
+ _shrink(index, count);
+ return index;
+}
+
+void VectorImpl::finish_vector()
+{
+ release_storage();
+ mStorage = 0;
+ mCount = 0;
+}
+
+void VectorImpl::clear()
+{
+ _shrink(0, mCount);
+}
+
+void* VectorImpl::editItemLocation(size_t index)
+{
+ ALOG_ASSERT(index<capacity(),
+ "[%p] itemLocation: index=%d, capacity=%d, count=%d",
+ this, (int)index, (int)capacity(), (int)mCount);
+
+ void* buffer = editArrayImpl();
+ if (buffer)
+ return reinterpret_cast<char*>(buffer) + index*mItemSize;
+ return 0;
+}
+
+const void* VectorImpl::itemLocation(size_t index) const
+{
+ ALOG_ASSERT(index<capacity(),
+ "[%p] editItemLocation: index=%d, capacity=%d, count=%d",
+ this, (int)index, (int)capacity(), (int)mCount);
+
+ const void* buffer = arrayImpl();
+ if (buffer)
+ return reinterpret_cast<const char*>(buffer) + index*mItemSize;
+ return 0;
+}
+
+ssize_t VectorImpl::setCapacity(size_t new_capacity)
+{
+ size_t current_capacity = capacity();
+ ssize_t amount = new_capacity - size();
+ if (amount <= 0) {
+ // we can't reduce the capacity
+ return current_capacity;
+ }
+ SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize);
+ if (sb) {
+ void* array = sb->data();
+ _do_copy(array, mStorage, size());
+ release_storage();
+ mStorage = const_cast<void*>(array);
+ } else {
+ return NO_MEMORY;
+ }
+ return new_capacity;
+}
+
+void VectorImpl::release_storage()
+{
+ if (mStorage) {
+ const SharedBuffer* sb = SharedBuffer::sharedBuffer(mStorage);
+ if (sb->release(SharedBuffer::eKeepStorage) == 1) {
+ _do_destroy(mStorage, mCount);
+ SharedBuffer::dealloc(sb);
+ }
+ }
+}
+
+void* VectorImpl::_grow(size_t where, size_t amount)
+{
+// ALOGV("_grow(this=%p, where=%d, amount=%d) count=%d, capacity=%d",
+// this, (int)where, (int)amount, (int)mCount, (int)capacity());
+
+ if (where > mCount)
+ where = mCount;
+
+ const size_t new_size = mCount + amount;
+ if (capacity() < new_size) {
+ const size_t new_capacity = max(kMinVectorCapacity, ((new_size*3)+1)/2);
+// ALOGV("grow vector %p, new_capacity=%d", this, (int)new_capacity);
+ if ((mStorage) &&
+ (mCount==where) &&
+ (mFlags & HAS_TRIVIAL_COPY) &&
+ (mFlags & HAS_TRIVIAL_DTOR))
+ {
+ const SharedBuffer* cur_sb = SharedBuffer::sharedBuffer(mStorage);
+ SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize);
+ mStorage = sb->data();
+ } else {
+ SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize);
+ if (sb) {
+ void* array = sb->data();
+ if (where>0) {
+ _do_copy(array, mStorage, where);
+ }
+ if (mCount>where) {
+ const void* from = reinterpret_cast<const uint8_t *>(mStorage) + where*mItemSize;
+ void* dest = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize;
+ _do_copy(dest, from, mCount-where);
+ }
+ release_storage();
+ mStorage = const_cast<void*>(array);
+ }
+ }
+ } else {
+ ssize_t s = mCount-where;
+ if (s>0) {
+ void* array = editArrayImpl();
+ void* to = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize;
+ const void* from = reinterpret_cast<const uint8_t *>(array) + where*mItemSize;
+ _do_move_forward(to, from, s);
+ }
+ }
+ mCount += amount;
+ void* free_space = const_cast<void*>(itemLocation(where));
+ return free_space;
+}
+
+void VectorImpl::_shrink(size_t where, size_t amount)
+{
+ if (!mStorage)
+ return;
+
+// ALOGV("_shrink(this=%p, where=%d, amount=%d) count=%d, capacity=%d",
+// this, (int)where, (int)amount, (int)mCount, (int)capacity());
+
+ if (where >= mCount)
+ where = mCount - amount;
+
+ const size_t new_size = mCount - amount;
+ if (new_size*3 < capacity()) {
+ const size_t new_capacity = max(kMinVectorCapacity, new_size*2);
+// ALOGV("shrink vector %p, new_capacity=%d", this, (int)new_capacity);
+ if ((where == mCount-amount) &&
+ (mFlags & HAS_TRIVIAL_COPY) &&
+ (mFlags & HAS_TRIVIAL_DTOR))
+ {
+ const SharedBuffer* cur_sb = SharedBuffer::sharedBuffer(mStorage);
+ SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize);
+ mStorage = sb->data();
+ } else {
+ SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize);
+ if (sb) {
+ void* array = sb->data();
+ if (where>0) {
+ _do_copy(array, mStorage, where);
+ }
+ if (mCount > where+amount) {
+ const void* from = reinterpret_cast<const uint8_t *>(mStorage) + (where+amount)*mItemSize;
+ void* dest = reinterpret_cast<uint8_t *>(array) + where*mItemSize;
+ _do_copy(dest, from, mCount-(where+amount));
+ }
+ release_storage();
+ mStorage = const_cast<void*>(array);
+ }
+ }
+ } else {
+ void* array = editArrayImpl();
+ void* to = reinterpret_cast<uint8_t *>(array) + where*mItemSize;
+ _do_destroy(to, amount);
+ ssize_t s = mCount-(where+amount);
+ if (s>0) {
+ const void* from = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize;
+ _do_move_backward(to, from, s);
+ }
+ }
+
+ // adjust the number of items...
+ mCount -= amount;
+}
+
+size_t VectorImpl::itemSize() const {
+ return mItemSize;
+}
+
+void VectorImpl::_do_construct(void* storage, size_t num) const
+{
+ if (!(mFlags & HAS_TRIVIAL_CTOR)) {
+ do_construct(storage, num);
+ }
+}
+
+void VectorImpl::_do_destroy(void* storage, size_t num) const
+{
+ if (!(mFlags & HAS_TRIVIAL_DTOR)) {
+ do_destroy(storage, num);
+ }
+}
+
+void VectorImpl::_do_copy(void* dest, const void* from, size_t num) const
+{
+ if (!(mFlags & HAS_TRIVIAL_COPY)) {
+ do_copy(dest, from, num);
+ } else {
+ memcpy(dest, from, num*itemSize());
+ }
+}
+
+void VectorImpl::_do_splat(void* dest, const void* item, size_t num) const {
+ do_splat(dest, item, num);
+}
+
+void VectorImpl::_do_move_forward(void* dest, const void* from, size_t num) const {
+ do_move_forward(dest, from, num);
+}
+
+void VectorImpl::_do_move_backward(void* dest, const void* from, size_t num) const {
+ do_move_backward(dest, from, num);
+}
+
+void VectorImpl::reservedVectorImpl1() { }
+void VectorImpl::reservedVectorImpl2() { }
+void VectorImpl::reservedVectorImpl3() { }
+void VectorImpl::reservedVectorImpl4() { }
+void VectorImpl::reservedVectorImpl5() { }
+void VectorImpl::reservedVectorImpl6() { }
+void VectorImpl::reservedVectorImpl7() { }
+void VectorImpl::reservedVectorImpl8() { }
+
+/*****************************************************************************/
+
+SortedVectorImpl::SortedVectorImpl(size_t itemSize, uint32_t flags)
+ : VectorImpl(itemSize, flags)
+{
+}
+
+SortedVectorImpl::SortedVectorImpl(const VectorImpl& rhs)
+: VectorImpl(rhs)
+{
+}
+
+SortedVectorImpl::~SortedVectorImpl()
+{
+}
+
+SortedVectorImpl& SortedVectorImpl::operator = (const SortedVectorImpl& rhs)
+{
+ return static_cast<SortedVectorImpl&>( VectorImpl::operator = (static_cast<const VectorImpl&>(rhs)) );
+}
+
+ssize_t SortedVectorImpl::indexOf(const void* item) const
+{
+ return _indexOrderOf(item);
+}
+
+size_t SortedVectorImpl::orderOf(const void* item) const
+{
+ size_t o;
+ _indexOrderOf(item, &o);
+ return o;
+}
+
+ssize_t SortedVectorImpl::_indexOrderOf(const void* item, size_t* order) const
+{
+ // binary search
+ ssize_t err = NAME_NOT_FOUND;
+ ssize_t l = 0;
+ ssize_t h = size()-1;
+ ssize_t mid;
+ const void* a = arrayImpl();
+ const size_t s = itemSize();
+ while (l <= h) {
+ mid = l + (h - l)/2;
+ const void* const curr = reinterpret_cast<const char *>(a) + (mid*s);
+ const int c = do_compare(curr, item);
+ if (c == 0) {
+ err = l = mid;
+ break;
+ } else if (c < 0) {
+ l = mid + 1;
+ } else {
+ h = mid - 1;
+ }
+ }
+ if (order) *order = l;
+ return err;
+}
+
+ssize_t SortedVectorImpl::add(const void* item)
+{
+ size_t order;
+ ssize_t index = _indexOrderOf(item, &order);
+ if (index < 0) {
+ index = VectorImpl::insertAt(item, order, 1);
+ } else {
+ index = VectorImpl::replaceAt(item, index);
+ }
+ return index;
+}
+
+ssize_t SortedVectorImpl::merge(const VectorImpl& vector)
+{
+ // naive merge...
+ if (!vector.isEmpty()) {
+ const void* buffer = vector.arrayImpl();
+ const size_t is = itemSize();
+ size_t s = vector.size();
+ for (size_t i=0 ; i<s ; i++) {
+ ssize_t err = add( reinterpret_cast<const char*>(buffer) + i*is );
+ if (err<0) {
+ return err;
+ }
+ }
+ }
+ return NO_ERROR;
+}
+
+ssize_t SortedVectorImpl::merge(const SortedVectorImpl& vector)
+{
+ // we've merging a sorted vector... nice!
+ ssize_t err = NO_ERROR;
+ if (!vector.isEmpty()) {
+ // first take care of the case where the vectors are sorted together
+ if (do_compare(vector.itemLocation(vector.size()-1), arrayImpl()) <= 0) {
+ err = VectorImpl::insertVectorAt(static_cast<const VectorImpl&>(vector), 0);
+ } else if (do_compare(vector.arrayImpl(), itemLocation(size()-1)) >= 0) {
+ err = VectorImpl::appendVector(static_cast<const VectorImpl&>(vector));
+ } else {
+ // this could be made a little better
+ err = merge(static_cast<const VectorImpl&>(vector));
+ }
+ }
+ return err;
+}
+
+ssize_t SortedVectorImpl::remove(const void* item)
+{
+ ssize_t i = indexOf(item);
+ if (i>=0) {
+ VectorImpl::removeItemsAt(i, 1);
+ }
+ return i;
+}
+
+void SortedVectorImpl::reservedSortedVectorImpl1() { };
+void SortedVectorImpl::reservedSortedVectorImpl2() { };
+void SortedVectorImpl::reservedSortedVectorImpl3() { };
+void SortedVectorImpl::reservedSortedVectorImpl4() { };
+void SortedVectorImpl::reservedSortedVectorImpl5() { };
+void SortedVectorImpl::reservedSortedVectorImpl6() { };
+void SortedVectorImpl::reservedSortedVectorImpl7() { };
+void SortedVectorImpl::reservedSortedVectorImpl8() { };
+
+
+/*****************************************************************************/
+
+} // namespace tinyutils
+} // namespace android
+
diff --git a/libpixelflinger/codeflinger/tinyutils/VectorImpl.h b/libpixelflinger/codeflinger/tinyutils/VectorImpl.h
new file mode 100644
index 0000000..56089b3
--- /dev/null
+++ b/libpixelflinger/codeflinger/tinyutils/VectorImpl.h
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PIXELFLINGER_VECTOR_IMPL_H
+#define ANDROID_PIXELFLINGER_VECTOR_IMPL_H
+
+#include <assert.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+// ---------------------------------------------------------------------------
+// No user serviceable parts in here...
+// ---------------------------------------------------------------------------
+
+namespace android {
+namespace tinyutils {
+
+/*!
+ * Implementation of the guts of the vector<> class
+ * this ensures backward binary compatibility and
+ * reduces code size.
+ * For performance reasons, we expose mStorage and mCount
+ * so these fields are set in stone.
+ *
+ */
+
+class VectorImpl
+{
+public:
+ enum { // flags passed to the ctor
+ HAS_TRIVIAL_CTOR = 0x00000001,
+ HAS_TRIVIAL_DTOR = 0x00000002,
+ HAS_TRIVIAL_COPY = 0x00000004,
+ HAS_TRIVIAL_ASSIGN = 0x00000008
+ };
+
+ VectorImpl(size_t itemSize, uint32_t flags);
+ VectorImpl(const VectorImpl& rhs);
+ virtual ~VectorImpl();
+
+ /*! must be called from subclasses destructor */
+ void finish_vector();
+
+ VectorImpl& operator = (const VectorImpl& rhs);
+
+ /*! C-style array access */
+ inline const void* arrayImpl() const { return mStorage; }
+ void* editArrayImpl();
+
+ /*! vector stats */
+ inline size_t size() const { return mCount; }
+ inline bool isEmpty() const { return mCount == 0; }
+ size_t capacity() const;
+ ssize_t setCapacity(size_t size);
+
+ /*! append/insert another vector */
+ ssize_t insertVectorAt(const VectorImpl& vector, size_t index);
+ ssize_t appendVector(const VectorImpl& vector);
+
+ /*! add/insert/replace items */
+ ssize_t insertAt(size_t where, size_t numItems = 1);
+ ssize_t insertAt(const void* item, size_t where, size_t numItems = 1);
+ void pop();
+ void push();
+ void push(const void* item);
+ ssize_t add();
+ ssize_t add(const void* item);
+ ssize_t replaceAt(size_t index);
+ ssize_t replaceAt(const void* item, size_t index);
+
+ /*! remove items */
+ ssize_t removeItemsAt(size_t index, size_t count = 1);
+ void clear();
+
+ const void* itemLocation(size_t index) const;
+ void* editItemLocation(size_t index);
+
+protected:
+ size_t itemSize() const;
+ void release_storage();
+
+ virtual void do_construct(void* storage, size_t num) const = 0;
+ virtual void do_destroy(void* storage, size_t num) const = 0;
+ virtual void do_copy(void* dest, const void* from, size_t num) const = 0;
+ virtual void do_splat(void* dest, const void* item, size_t num) const = 0;
+ virtual void do_move_forward(void* dest, const void* from, size_t num) const = 0;
+ virtual void do_move_backward(void* dest, const void* from, size_t num) const = 0;
+
+ // take care of FBC...
+ virtual void reservedVectorImpl1();
+ virtual void reservedVectorImpl2();
+ virtual void reservedVectorImpl3();
+ virtual void reservedVectorImpl4();
+ virtual void reservedVectorImpl5();
+ virtual void reservedVectorImpl6();
+ virtual void reservedVectorImpl7();
+ virtual void reservedVectorImpl8();
+
+private:
+ void* _grow(size_t where, size_t amount);
+ void _shrink(size_t where, size_t amount);
+
+ inline void _do_construct(void* storage, size_t num) const;
+ inline void _do_destroy(void* storage, size_t num) const;
+ inline void _do_copy(void* dest, const void* from, size_t num) const;
+ inline void _do_splat(void* dest, const void* item, size_t num) const;
+ inline void _do_move_forward(void* dest, const void* from, size_t num) const;
+ inline void _do_move_backward(void* dest, const void* from, size_t num) const;
+
+ // These 2 fields are exposed in the inlines below,
+ // so they're set in stone.
+ void * mStorage; // base address of the vector
+ size_t mCount; // number of items
+
+ const uint32_t mFlags;
+ const size_t mItemSize;
+};
+
+
+
+class SortedVectorImpl : public VectorImpl
+{
+public:
+ SortedVectorImpl(size_t itemSize, uint32_t flags);
+ SortedVectorImpl(const VectorImpl& rhs);
+ virtual ~SortedVectorImpl();
+
+ SortedVectorImpl& operator = (const SortedVectorImpl& rhs);
+
+ //! finds the index of an item
+ ssize_t indexOf(const void* item) const;
+
+ //! finds where this item should be inserted
+ size_t orderOf(const void* item) const;
+
+ //! add an item in the right place (or replaces it if there is one)
+ ssize_t add(const void* item);
+
+ //! merges a vector into this one
+ ssize_t merge(const VectorImpl& vector);
+ ssize_t merge(const SortedVectorImpl& vector);
+
+ //! removes an item
+ ssize_t remove(const void* item);
+
+protected:
+ virtual int do_compare(const void* lhs, const void* rhs) const = 0;
+
+ // take care of FBC...
+ virtual void reservedSortedVectorImpl1();
+ virtual void reservedSortedVectorImpl2();
+ virtual void reservedSortedVectorImpl3();
+ virtual void reservedSortedVectorImpl4();
+ virtual void reservedSortedVectorImpl5();
+ virtual void reservedSortedVectorImpl6();
+ virtual void reservedSortedVectorImpl7();
+ virtual void reservedSortedVectorImpl8();
+
+private:
+ ssize_t _indexOrderOf(const void* item, size_t* order = 0) const;
+
+ // these are made private, because they can't be used on a SortedVector
+ // (they don't have an implementation either)
+ ssize_t add();
+ void pop();
+ void push();
+ void push(const void* item);
+ ssize_t insertVectorAt(const VectorImpl& vector, size_t index);
+ ssize_t appendVector(const VectorImpl& vector);
+ ssize_t insertAt(size_t where, size_t numItems = 1);
+ ssize_t insertAt(const void* item, size_t where, size_t numItems = 1);
+ ssize_t replaceAt(size_t index);
+ ssize_t replaceAt(const void* item, size_t index);
+};
+
+} // namespace tinyutils
+} // namespace android
+
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_PIXELFLINGER_VECTOR_IMPL_H
diff --git a/libpixelflinger/codeflinger/tinyutils/smartpointer.h b/libpixelflinger/codeflinger/tinyutils/smartpointer.h
new file mode 100644
index 0000000..9d0a16e
--- /dev/null
+++ b/libpixelflinger/codeflinger/tinyutils/smartpointer.h
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PIXELFLINGER_SMART_POINTER_H
+#define ANDROID_PIXELFLINGER_SMART_POINTER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <stdlib.h>
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace tinyutils {
+
+// ---------------------------------------------------------------------------
+
+#define COMPARE(_op_) \
+inline bool operator _op_ (const sp<T>& o) const { \
+ return m_ptr _op_ o.m_ptr; \
+} \
+inline bool operator _op_ (const T* o) const { \
+ return m_ptr _op_ o; \
+} \
+template<typename U> \
+inline bool operator _op_ (const sp<U>& o) const { \
+ return m_ptr _op_ o.m_ptr; \
+} \
+template<typename U> \
+inline bool operator _op_ (const U* o) const { \
+ return m_ptr _op_ o; \
+}
+
+// ---------------------------------------------------------------------------
+
+template <typename T>
+class sp
+{
+public:
+ inline sp() : m_ptr(0) { }
+
+ sp(T* other);
+ sp(const sp<T>& other);
+ template<typename U> sp(U* other);
+ template<typename U> sp(const sp<U>& other);
+
+ ~sp();
+
+ // Assignment
+
+ sp& operator = (T* other);
+ sp& operator = (const sp<T>& other);
+
+ template<typename U> sp& operator = (const sp<U>& other);
+ template<typename U> sp& operator = (U* other);
+
+ // Reset
+ void clear();
+
+ // Accessors
+
+ inline T& operator* () const { return *m_ptr; }
+ inline T* operator-> () const { return m_ptr; }
+ inline T* get() const { return m_ptr; }
+
+ // Operators
+
+ COMPARE(==)
+ COMPARE(!=)
+ COMPARE(>)
+ COMPARE(<)
+ COMPARE(<=)
+ COMPARE(>=)
+
+private:
+ template<typename Y> friend class sp;
+
+ T* m_ptr;
+};
+
+// ---------------------------------------------------------------------------
+// No user serviceable parts below here.
+
+template<typename T>
+sp<T>::sp(T* other)
+ : m_ptr(other)
+{
+ if (other) other->incStrong(this);
+}
+
+template<typename T>
+sp<T>::sp(const sp<T>& other)
+ : m_ptr(other.m_ptr)
+{
+ if (m_ptr) m_ptr->incStrong(this);
+}
+
+template<typename T> template<typename U>
+sp<T>::sp(U* other) : m_ptr(other)
+{
+ if (other) other->incStrong(this);
+}
+
+template<typename T> template<typename U>
+sp<T>::sp(const sp<U>& other)
+ : m_ptr(other.m_ptr)
+{
+ if (m_ptr) m_ptr->incStrong(this);
+}
+
+template<typename T>
+sp<T>::~sp()
+{
+ if (m_ptr) m_ptr->decStrong(this);
+}
+
+template<typename T>
+sp<T>& sp<T>::operator = (const sp<T>& other) {
+ if (other.m_ptr) other.m_ptr->incStrong(this);
+ if (m_ptr) m_ptr->decStrong(this);
+ m_ptr = other.m_ptr;
+ return *this;
+}
+
+template<typename T>
+sp<T>& sp<T>::operator = (T* other)
+{
+ if (other) other->incStrong(this);
+ if (m_ptr) m_ptr->decStrong(this);
+ m_ptr = other;
+ return *this;
+}
+
+template<typename T> template<typename U>
+sp<T>& sp<T>::operator = (const sp<U>& other)
+{
+ if (other.m_ptr) other.m_ptr->incStrong(this);
+ if (m_ptr) m_ptr->decStrong(this);
+ m_ptr = other.m_ptr;
+ return *this;
+}
+
+template<typename T> template<typename U>
+sp<T>& sp<T>::operator = (U* other)
+{
+ if (other) other->incStrong(this);
+ if (m_ptr) m_ptr->decStrong(this);
+ m_ptr = other;
+ return *this;
+}
+
+template<typename T>
+void sp<T>::clear()
+{
+ if (m_ptr) {
+ m_ptr->decStrong(this);
+ m_ptr = 0;
+ }
+}
+
+// ---------------------------------------------------------------------------
+
+} // namespace tinyutils
+} // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_PIXELFLINGER_SMART_POINTER_H
diff --git a/libpixelflinger/col32cb16blend.S b/libpixelflinger/col32cb16blend.S
new file mode 100644
index 0000000..1831255
--- /dev/null
+++ b/libpixelflinger/col32cb16blend.S
@@ -0,0 +1,77 @@
+/* libs/pixelflinger/col32cb16blend.S
+ *
+ * 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.
+ */
+
+ .text
+ .align
+
+ .global scanline_col32cb16blend_arm
+
+//
+// This function alpha blends a fixed color into a destination scanline, using
+// the formula:
+//
+// d = s + (((a + (a >> 7)) * d) >> 8)
+//
+// where d is the destination pixel,
+// s is the source color,
+// a is the alpha channel of the source color.
+//
+
+// r0 = destination buffer pointer
+// r1 = color value
+// r2 = count
+
+
+scanline_col32cb16blend_arm:
+ push {r4-r10, lr} // stack ARM regs
+
+ mov r5, r1, lsr #24 // shift down alpha
+ mov r9, #0xff // create mask
+ add r5, r5, r5, lsr #7 // add in top bit
+ rsb r5, r5, #256 // invert alpha
+ and r10, r1, #0xff // extract red
+ and r12, r9, r1, lsr #8 // extract green
+ and r4, r9, r1, lsr #16 // extract blue
+ mov r10, r10, lsl #5 // prescale red
+ mov r12, r12, lsl #6 // prescale green
+ mov r4, r4, lsl #5 // prescale blue
+ mov r9, r9, lsr #2 // create dest green mask
+
+1:
+ ldrh r8, [r0] // load dest pixel
+ subs r2, r2, #1 // decrement loop counter
+ mov r6, r8, lsr #11 // extract dest red
+ and r7, r9, r8, lsr #5 // extract dest green
+ and r8, r8, #0x1f // extract dest blue
+
+ smlabb r6, r6, r5, r10 // dest red * alpha + src red
+ smlabb r7, r7, r5, r12 // dest green * alpha + src green
+ smlabb r8, r8, r5, r4 // dest blue * alpha + src blue
+
+ mov r6, r6, lsr #8 // shift down red
+ mov r7, r7, lsr #8 // shift down green
+ mov r6, r6, lsl #11 // shift red into 565
+ orr r6, r7, lsl #5 // shift green into 565
+ orr r6, r8, lsr #8 // shift blue into 565
+
+ strh r6, [r0], #2 // store pixel to dest, update ptr
+ bne 1b // if count != 0, loop
+
+ pop {r4-r10, pc} // return
+
+
+
diff --git a/libpixelflinger/col32cb16blend_neon.S b/libpixelflinger/col32cb16blend_neon.S
new file mode 100644
index 0000000..cbd54d1
--- /dev/null
+++ b/libpixelflinger/col32cb16blend_neon.S
@@ -0,0 +1,153 @@
+/* libs/pixelflinger/col32cb16blend_neon.S
+ *
+ * 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.
+ */
+
+
+ .text
+ .align
+
+ .global scanline_col32cb16blend_neon
+
+//
+// This function alpha blends a fixed color into a destination scanline, using
+// the formula:
+//
+// d = s + (((a + (a >> 7)) * d) >> 8)
+//
+// where d is the destination pixel,
+// s is the source color,
+// a is the alpha channel of the source color.
+//
+// The NEON implementation processes 16 pixels per iteration. The remaining 0 - 15
+// pixels are processed in ARM code.
+//
+
+// r0 = destination buffer pointer
+// r1 = color pointer
+// r2 = count
+
+
+scanline_col32cb16blend_neon:
+ push {r4-r11, lr} // stack ARM regs
+
+ vmov.u16 q15, #256 // create alpha constant
+ movs r3, r2, lsr #4 // calc. sixteens iterations
+ vmov.u16 q14, #0x1f // create blue mask
+
+ beq 2f // if r3 == 0, branch to singles
+
+ vld4.8 {d0[], d2[], d4[], d6[]}, [r1] // load color into four registers
+ // split and duplicate them, such that
+ // d0 = 8 equal red values
+ // d2 = 8 equal green values
+ // d4 = 8 equal blue values
+ // d6 = 8 equal alpha values
+ vshll.u8 q0, d0, #5 // shift up red and widen
+ vshll.u8 q1, d2, #6 // shift up green and widen
+ vshll.u8 q2, d4, #5 // shift up blue and widen
+
+ vshr.u8 d7, d6, #7 // extract top bit of alpha
+ vaddl.u8 q3, d6, d7 // add top bit into alpha
+ vsub.u16 q3, q15, q3 // invert alpha
+
+1:
+ // This loop processes 16 pixels per iteration. In the comments, references to
+ // the first eight pixels are suffixed with "0" (red0, green0, blue0),
+ // the second eight are suffixed "1".
+ // q8 = dst red0
+ // q9 = dst green0
+ // q10 = dst blue0
+ // q13 = dst red1
+ // q12 = dst green1
+ // q11 = dst blue1
+
+ vld1.16 {d20, d21, d22, d23}, [r0] // load 16 dest pixels
+ vshr.u16 q8, q10, #11 // shift dst red0 to low 5 bits
+ pld [r0, #63] // preload next dest pixels
+ vshl.u16 q9, q10, #5 // shift dst green0 to top 6 bits
+ vand q10, q10, q14 // extract dst blue0
+ vshr.u16 q9, q9, #10 // shift dst green0 to low 6 bits
+ vmul.u16 q8, q8, q3 // multiply dst red0 by src alpha
+ vshl.u16 q12, q11, #5 // shift dst green1 to top 6 bits
+ vmul.u16 q9, q9, q3 // multiply dst green0 by src alpha
+ vshr.u16 q13, q11, #11 // shift dst red1 to low 5 bits
+ vmul.u16 q10, q10, q3 // multiply dst blue0 by src alpha
+ vshr.u16 q12, q12, #10 // shift dst green1 to low 6 bits
+ vand q11, q11, q14 // extract dst blue1
+ vadd.u16 q8, q8, q0 // add src red to dst red0
+ vmul.u16 q13, q13, q3 // multiply dst red1 by src alpha
+ vadd.u16 q9, q9, q1 // add src green to dst green0
+ vmul.u16 q12, q12, q3 // multiply dst green1 by src alpha
+ vadd.u16 q10, q10, q2 // add src blue to dst blue0
+ vmul.u16 q11, q11, q3 // multiply dst blue1 by src alpha
+ vshr.u16 q8, q8, #8 // shift down red0
+ vadd.u16 q13, q13, q0 // add src red to dst red1
+ vshr.u16 q9, q9, #8 // shift down green0
+ vadd.u16 q12, q12, q1 // add src green to dst green1
+ vshr.u16 q10, q10, #8 // shift down blue0
+ vadd.u16 q11, q11, q2 // add src blue to dst blue1
+ vsli.u16 q10, q9, #5 // shift & insert green0 into blue0
+ vshr.u16 q13, q13, #8 // shift down red1
+ vsli.u16 q10, q8, #11 // shift & insert red0 into blue0
+ vshr.u16 q12, q12, #8 // shift down green1
+ vshr.u16 q11, q11, #8 // shift down blue1
+ subs r3, r3, #1 // decrement loop counter
+ vsli.u16 q11, q12, #5 // shift & insert green1 into blue1
+ vsli.u16 q11, q13, #11 // shift & insert red1 into blue1
+
+ vst1.16 {d20, d21, d22, d23}, [r0]! // write 16 pixels back to dst
+ bne 1b // if count != 0, loop
+
+2:
+ ands r3, r2, #15 // calc. single iterations
+ beq 4f // if r3 == 0, exit
+
+ ldr r4, [r1] // load source color
+ mov r5, r4, lsr #24 // shift down alpha
+ add r5, r5, r5, lsr #7 // add in top bit
+ rsb r5, r5, #256 // invert alpha
+ and r11, r4, #0xff // extract red
+ ubfx r12, r4, #8, #8 // extract green
+ ubfx r4, r4, #16, #8 // extract blue
+ mov r11, r11, lsl #5 // prescale red
+ mov r12, r12, lsl #6 // prescale green
+ mov r4, r4, lsl #5 // prescale blue
+
+3:
+ ldrh r8, [r0] // load dest pixel
+ subs r3, r3, #1 // decrement loop counter
+ mov r6, r8, lsr #11 // extract dest red
+ ubfx r7, r8, #5, #6 // extract dest green
+ and r8, r8, #0x1f // extract dest blue
+
+ smlabb r6, r6, r5, r11 // dest red * alpha + src red
+ smlabb r7, r7, r5, r12 // dest green * alpha + src green
+ smlabb r8, r8, r5, r4 // dest blue * alpha + src blue
+
+ mov r6, r6, lsr #8 // shift down red
+ mov r7, r7, lsr #8 // shift down green
+ mov r6, r6, lsl #11 // shift red into 565
+ orr r6, r7, lsl #5 // shift green into 565
+ orr r6, r8, lsr #8 // shift blue into 565
+
+ strh r6, [r0], #2 // store pixel to dest, update ptr
+ bne 3b // if count != 0, loop
+4:
+
+ pop {r4-r11, pc} // return
+
+
+
diff --git a/libpixelflinger/fixed.cpp b/libpixelflinger/fixed.cpp
new file mode 100644
index 0000000..5094537
--- /dev/null
+++ b/libpixelflinger/fixed.cpp
@@ -0,0 +1,340 @@
+/* libs/pixelflinger/fixed.cpp
+**
+** Copyright 2006, 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 <private/pixelflinger/ggl_context.h>
+#include <private/pixelflinger/ggl_fixed.h>
+
+
+// ------------------------------------------------------------------------
+
+int32_t gglRecipQNormalized(int32_t x, int* exponent)
+{
+ const int32_t s = x>>31;
+ uint32_t a = s ? -x : x;
+
+ // the result will overflow, so just set it to the biggest/inf value
+ if (ggl_unlikely(a <= 2LU)) {
+ *exponent = 0;
+ return s ? FIXED_MIN : FIXED_MAX;
+ }
+
+ // Newton-Raphson iteration:
+ // x = r*(2 - a*r)
+
+ const int32_t lz = gglClz(a);
+ a <<= lz; // 0.32
+ uint32_t r = a;
+ // note: if a == 0x80000000, this means x was a power-of-2, in this
+ // case we don't need to compute anything. We get the reciprocal for
+ // (almost) free.
+ if (a != 0x80000000) {
+ r = (0x2E800 << (30-16)) - (r>>(2-1)); // 2.30, r = 2.90625 - 2*a
+ // 0.32 + 2.30 = 2.62 -> 2.30
+ // 2.30 + 2.30 = 4.60 -> 2.30
+ r = (((2LU<<30) - uint32_t((uint64_t(a)*r) >> 32)) * uint64_t(r)) >> 30;
+ r = (((2LU<<30) - uint32_t((uint64_t(a)*r) >> 32)) * uint64_t(r)) >> 30;
+ }
+
+ // shift right 1-bit to make room for the sign bit
+ *exponent = 30-lz-1;
+ r >>= 1;
+ return s ? -r : r;
+}
+
+int32_t gglRecipQ(GGLfixed x, int q)
+{
+ int shift;
+ x = gglRecipQNormalized(x, &shift);
+ shift += 16-q;
+ if (shift > 0)
+ x += 1L << (shift-1); // rounding
+ x >>= shift;
+ return x;
+}
+
+// ------------------------------------------------------------------------
+
+GGLfixed gglFastDivx(GGLfixed n, GGLfixed d)
+{
+ if ((d>>24) && ((d>>24)+1)) {
+ n >>= 8;
+ d >>= 8;
+ }
+ return gglMulx(n, gglRecip(d));
+}
+
+// ------------------------------------------------------------------------
+
+static const GGLfixed ggl_sqrt_reciproc_approx_tab[8] = {
+ // 1/sqrt(x) with x = 1-N/16, N=[8...1]
+ 0x16A09, 0x15555, 0x143D1, 0x134BF, 0x1279A, 0x11C01, 0x111AC, 0x10865
+};
+
+GGLfixed gglSqrtRecipx(GGLfixed x)
+{
+ if (x == 0) return FIXED_MAX;
+ if (x == FIXED_ONE) return x;
+ const GGLfixed a = x;
+ const int32_t lz = gglClz(x);
+ x = ggl_sqrt_reciproc_approx_tab[(a>>(28-lz))&0x7];
+ const int32_t exp = lz - 16;
+ if (exp <= 0) x >>= -exp>>1;
+ else x <<= (exp>>1) + (exp & 1);
+ if (exp & 1) {
+ x = gglMulx(x, ggl_sqrt_reciproc_approx_tab[0])>>1;
+ }
+ // 2 Newton-Raphson iterations: x = x/2*(3-(a*x)*x)
+ x = gglMulx((x>>1),(0x30000 - gglMulx(gglMulx(a,x),x)));
+ x = gglMulx((x>>1),(0x30000 - gglMulx(gglMulx(a,x),x)));
+ return x;
+}
+
+GGLfixed gglSqrtx(GGLfixed a)
+{
+ // Compute a full precision square-root (24 bits accuracy)
+ GGLfixed r = 0;
+ GGLfixed bit = 0x800000;
+ int32_t bshift = 15;
+ do {
+ GGLfixed temp = bit + (r<<1);
+ if (bshift >= 8) temp <<= (bshift-8);
+ else temp >>= (8-bshift);
+ if (a >= temp) {
+ r += bit;
+ a -= temp;
+ }
+ bshift--;
+ } while (bit>>=1);
+ return r;
+}
+
+// ------------------------------------------------------------------------
+
+static const GGLfixed ggl_log_approx_tab[] = {
+ // -ln(x)/ln(2) with x = N/16, N=[8...16]
+ 0xFFFF, 0xd47f, 0xad96, 0x8a62, 0x6a3f, 0x4caf, 0x3151, 0x17d6, 0x0000
+};
+
+static const GGLfixed ggl_alog_approx_tab[] = { // domain [0 - 1.0]
+ 0xffff, 0xeac0, 0xd744, 0xc567, 0xb504, 0xa5fe, 0x9837, 0x8b95, 0x8000
+};
+
+GGLfixed gglPowx(GGLfixed x, GGLfixed y)
+{
+ // prerequisite: 0 <= x <= 1, and y >=0
+
+ // pow(x,y) = 2^(y*log2(x))
+ // = 2^(y*log2(x*(2^exp)*(2^-exp))))
+ // = 2^(y*(log2(X)-exp))
+ // = 2^(log2(X)*y - y*exp)
+ // = 2^( - (-log2(X)*y + y*exp) )
+
+ int32_t exp = gglClz(x) - 16;
+ GGLfixed f = x << exp;
+ x = (f & 0x0FFF)<<4;
+ f = (f >> 12) & 0x7;
+ GGLfixed p = gglMulAddx(
+ ggl_log_approx_tab[f+1] - ggl_log_approx_tab[f], x,
+ ggl_log_approx_tab[f]);
+ p = gglMulAddx(p, y, y*exp);
+ exp = gglFixedToIntFloor(p);
+ if (exp < 31) {
+ p = gglFracx(p);
+ x = (p & 0x1FFF)<<3;
+ p >>= 13;
+ p = gglMulAddx(
+ ggl_alog_approx_tab[p+1] - ggl_alog_approx_tab[p], x,
+ ggl_alog_approx_tab[p]);
+ p >>= exp;
+ } else {
+ p = 0;
+ }
+ return p;
+ // ( powf((a*65536.0f), (b*65536.0f)) ) * 65536.0f;
+}
+
+// ------------------------------------------------------------------------
+
+int32_t gglDivQ(GGLfixed n, GGLfixed d, int32_t i)
+{
+ //int32_t r =int32_t((int64_t(n)<<i)/d);
+ const int32_t ds = n^d;
+ if (n<0) n = -n;
+ if (d<0) d = -d;
+ int nd = gglClz(d) - gglClz(n);
+ i += nd + 1;
+ if (nd > 0) d <<= nd;
+ else n <<= -nd;
+ uint32_t q = 0;
+
+ int j = i & 7;
+ i >>= 3;
+
+ // gcc deals with the code below pretty well.
+ // we get 3.75 cycles per bit in the main loop
+ // and 8 cycles per bit in the termination loop
+ if (ggl_likely(i)) {
+ n -= d;
+ do {
+ q <<= 8;
+ if (n>=0) q |= 128;
+ else n += d;
+ n = n*2 - d;
+ if (n>=0) q |= 64;
+ else n += d;
+ n = n*2 - d;
+ if (n>=0) q |= 32;
+ else n += d;
+ n = n*2 - d;
+ if (n>=0) q |= 16;
+ else n += d;
+ n = n*2 - d;
+ if (n>=0) q |= 8;
+ else n += d;
+ n = n*2 - d;
+ if (n>=0) q |= 4;
+ else n += d;
+ n = n*2 - d;
+ if (n>=0) q |= 2;
+ else n += d;
+ n = n*2 - d;
+ if (n>=0) q |= 1;
+ else n += d;
+
+ if (--i == 0)
+ goto finish;
+
+ n = n*2 - d;
+ } while(true);
+ do {
+ q <<= 1;
+ n = n*2 - d;
+ if (n>=0) q |= 1;
+ else n += d;
+ finish: ;
+ } while (j--);
+ return (ds<0) ? -q : q;
+ }
+
+ n -= d;
+ if (n>=0) q |= 1;
+ else n += d;
+ j--;
+ goto finish;
+}
+
+// ------------------------------------------------------------------------
+
+// assumes that the int32_t values of a, b, and c are all positive
+// use when both a and b are larger than c
+
+template <typename T>
+static inline void swap(T& a, T& b) {
+ T t(a);
+ a = b;
+ b = t;
+}
+
+static __attribute__((noinline))
+int32_t slow_muldiv(uint32_t a, uint32_t b, uint32_t c)
+{
+ // first we compute a*b as a 64-bit integer
+ // (GCC generates umull with the code below)
+ uint64_t ab = uint64_t(a)*b;
+ uint32_t hi = ab>>32;
+ uint32_t lo = ab;
+ uint32_t result;
+
+ // now perform the division
+ if (hi >= c) {
+ overflow:
+ result = 0x7fffffff; // basic overflow
+ } else if (hi == 0) {
+ result = lo/c; // note: c can't be 0
+ if ((result >> 31) != 0) // result must fit in 31 bits
+ goto overflow;
+ } else {
+ uint32_t r = hi;
+ int bits = 31;
+ result = 0;
+ do {
+ r = (r << 1) | (lo >> 31);
+ lo <<= 1;
+ result <<= 1;
+ if (r >= c) {
+ r -= c;
+ result |= 1;
+ }
+ } while (bits--);
+ }
+ return int32_t(result);
+}
+
+// assumes a >= 0 and c >= b >= 0
+static inline
+int32_t quick_muldiv(int32_t a, int32_t b, int32_t c)
+{
+ int32_t r = 0, q = 0, i;
+ int leading = gglClz(a);
+ i = 32 - leading;
+ a <<= leading;
+ do {
+ r <<= 1;
+ if (a < 0)
+ r += b;
+ a <<= 1;
+ q <<= 1;
+ if (r >= c) {
+ r -= c;
+ q++;
+ }
+ asm(""::); // gcc generates better code this way
+ if (r >= c) {
+ r -= c;
+ q++;
+ }
+ }
+ while (--i);
+ return q;
+}
+
+// this function computes a*b/c with 64-bit intermediate accuracy
+// overflows (e.g. division by 0) are handled and return INT_MAX
+
+int32_t gglMulDivi(int32_t a, int32_t b, int32_t c)
+{
+ int32_t result;
+ int32_t sign = a^b^c;
+
+ if (a < 0) a = -a;
+ if (b < 0) b = -b;
+ if (c < 0) c = -c;
+
+ if (a < b) {
+ swap(a, b);
+ }
+
+ if (b <= c) result = quick_muldiv(a, b, c);
+ else result = slow_muldiv((uint32_t)a, (uint32_t)b, (uint32_t)c);
+
+ if (sign < 0)
+ result = -result;
+
+ return result;
+}
diff --git a/libpixelflinger/format.cpp b/libpixelflinger/format.cpp
new file mode 100644
index 0000000..6546e8c
--- /dev/null
+++ b/libpixelflinger/format.cpp
@@ -0,0 +1,76 @@
+/* libs/pixelflinger/format.cpp
+**
+** Copyright 2006, 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 <pixelflinger/format.h>
+
+namespace android {
+
+static GGLFormat const gPixelFormatInfos[] =
+{ // Alpha Red Green Blue
+ { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE
+ { 4, 32, {{32,24, 8, 0, 16, 8, 24,16 }}, GGL_RGBA }, // PIXEL_FORMAT_RGBA_8888
+ { 4, 24, {{ 0, 0, 8, 0, 16, 8, 24,16 }}, GGL_RGB }, // PIXEL_FORMAT_RGBX_8888
+ { 3, 24, {{ 0, 0, 8, 0, 16, 8, 24,16 }}, GGL_RGB }, // PIXEL_FORMAT_RGB_888
+ { 2, 16, {{ 0, 0, 16,11, 11, 5, 5, 0 }}, GGL_RGB }, // PIXEL_FORMAT_RGB_565
+ { 4, 32, {{32,24, 24,16, 16, 8, 8, 0 }}, GGL_RGBA }, // PIXEL_FORMAT_BGRA_8888
+ { 2, 16, {{ 1, 0, 16,11, 11, 6, 6, 1 }}, GGL_RGBA }, // PIXEL_FORMAT_RGBA_5551
+ { 2, 16, {{ 4, 0, 16,12, 12, 8, 8, 4 }}, GGL_RGBA }, // PIXEL_FORMAT_RGBA_4444
+ { 1, 8, {{ 8, 0, 0, 0, 0, 0, 0, 0 }}, GGL_ALPHA}, // PIXEL_FORMAT_A8
+ { 1, 8, {{ 0, 0, 8, 0, 8, 0, 8, 0 }}, GGL_LUMINANCE},//PIXEL_FORMAT_L8
+ { 2, 16, {{16, 8, 8, 0, 8, 0, 8, 0 }}, GGL_LUMINANCE_ALPHA},// PIXEL_FORMAT_LA_88
+ { 1, 8, {{ 0, 0, 8, 5, 5, 2, 2, 0 }}, GGL_RGB }, // PIXEL_FORMAT_RGB_332
+
+ { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE
+ { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE
+ { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE
+ { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE
+
+ { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE
+ { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE
+ { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE
+ { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE
+ { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE
+ { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE
+ { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE
+ { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE
+
+ { 2, 16, {{ 0, 0, 16, 0, 0, 0, 0, 0 }}, GGL_DEPTH_COMPONENT},
+ { 1, 8, {{ 8, 0, 0, 0, 0, 0, 0, 0 }}, GGL_STENCIL_INDEX },
+ { 4, 24, {{ 0, 0, 24, 0, 0, 0, 0, 0 }}, GGL_DEPTH_COMPONENT},
+ { 4, 8, {{ 32,24, 0, 0, 0, 0, 0, 0 }}, GGL_STENCIL_INDEX },
+
+ { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE
+ { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE
+ { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE
+ { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE
+
+ { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE
+ { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE
+
+};
+
+}; // namespace android
+
+
+const GGLFormat* gglGetPixelFormatTable(size_t* numEntries)
+{
+ if (numEntries) {
+ *numEntries = sizeof(android::gPixelFormatInfos)/sizeof(GGLFormat);
+ }
+ return android::gPixelFormatInfos;
+}
diff --git a/libpixelflinger/include/pixelflinger/format.h b/libpixelflinger/include/pixelflinger/format.h
new file mode 100644
index 0000000..82eeca4
--- /dev/null
+++ b/libpixelflinger/include/pixelflinger/format.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PIXELFLINGER_FORMAT_H
+#define ANDROID_PIXELFLINGER_FORMAT_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+enum GGLPixelFormat {
+ // these constants need to match those
+ // in graphics/PixelFormat.java, ui/PixelFormat.h, BlitHardware.h
+ GGL_PIXEL_FORMAT_UNKNOWN = 0,
+ GGL_PIXEL_FORMAT_NONE = 0,
+
+ GGL_PIXEL_FORMAT_RGBA_8888 = 1, // 4x8-bit ARGB
+ GGL_PIXEL_FORMAT_RGBX_8888 = 2, // 3x8-bit RGB stored in 32-bit chunks
+ GGL_PIXEL_FORMAT_RGB_888 = 3, // 3x8-bit RGB
+ GGL_PIXEL_FORMAT_RGB_565 = 4, // 16-bit RGB
+ GGL_PIXEL_FORMAT_BGRA_8888 = 5, // 4x8-bit BGRA
+ GGL_PIXEL_FORMAT_RGBA_5551 = 6, // 16-bit RGBA
+ GGL_PIXEL_FORMAT_RGBA_4444 = 7, // 16-bit RGBA
+
+ GGL_PIXEL_FORMAT_A_8 = 8, // 8-bit A
+ GGL_PIXEL_FORMAT_L_8 = 9, // 8-bit L (R=G=B = L)
+ GGL_PIXEL_FORMAT_LA_88 = 0xA, // 16-bit LA
+ GGL_PIXEL_FORMAT_RGB_332 = 0xB, // 8-bit RGB (non paletted)
+
+ // reserved range. don't use.
+ GGL_PIXEL_FORMAT_RESERVED_10 = 0x10,
+ GGL_PIXEL_FORMAT_RESERVED_11 = 0x11,
+ GGL_PIXEL_FORMAT_RESERVED_12 = 0x12,
+ GGL_PIXEL_FORMAT_RESERVED_13 = 0x13,
+ GGL_PIXEL_FORMAT_RESERVED_14 = 0x14,
+ GGL_PIXEL_FORMAT_RESERVED_15 = 0x15,
+ GGL_PIXEL_FORMAT_RESERVED_16 = 0x16,
+ GGL_PIXEL_FORMAT_RESERVED_17 = 0x17,
+
+ // reserved/special formats
+ GGL_PIXEL_FORMAT_Z_16 = 0x18,
+ GGL_PIXEL_FORMAT_S_8 = 0x19,
+ GGL_PIXEL_FORMAT_SZ_24 = 0x1A,
+ GGL_PIXEL_FORMAT_SZ_8 = 0x1B,
+
+ // reserved range. don't use.
+ GGL_PIXEL_FORMAT_RESERVED_20 = 0x20,
+ GGL_PIXEL_FORMAT_RESERVED_21 = 0x21,
+};
+
+enum GGLFormatComponents {
+ GGL_STENCIL_INDEX = 0x1901,
+ GGL_DEPTH_COMPONENT = 0x1902,
+ GGL_ALPHA = 0x1906,
+ GGL_RGB = 0x1907,
+ GGL_RGBA = 0x1908,
+ GGL_LUMINANCE = 0x1909,
+ GGL_LUMINANCE_ALPHA = 0x190A,
+};
+
+enum GGLFormatComponentIndex {
+ GGL_INDEX_ALPHA = 0,
+ GGL_INDEX_RED = 1,
+ GGL_INDEX_GREEN = 2,
+ GGL_INDEX_BLUE = 3,
+ GGL_INDEX_STENCIL = 0,
+ GGL_INDEX_DEPTH = 1,
+ GGL_INDEX_Y = 0,
+ GGL_INDEX_CB = 1,
+ GGL_INDEX_CR = 2,
+};
+
+typedef struct {
+#ifdef __cplusplus
+ enum {
+ ALPHA = GGL_INDEX_ALPHA,
+ RED = GGL_INDEX_RED,
+ GREEN = GGL_INDEX_GREEN,
+ BLUE = GGL_INDEX_BLUE,
+ STENCIL = GGL_INDEX_STENCIL,
+ DEPTH = GGL_INDEX_DEPTH,
+ LUMA = GGL_INDEX_Y,
+ CHROMAB = GGL_INDEX_CB,
+ CHROMAR = GGL_INDEX_CR,
+ };
+ inline uint32_t mask(int i) const {
+ return ((1<<(c[i].h-c[i].l))-1)<<c[i].l;
+ }
+ inline uint32_t bits(int i) const {
+ return c[i].h - c[i].l;
+ }
+#endif
+ uint8_t size; // bytes per pixel
+ uint8_t bitsPerPixel;
+ union {
+ struct {
+ uint8_t ah; // alpha high bit position + 1
+ uint8_t al; // alpha low bit position
+ uint8_t rh; // red high bit position + 1
+ uint8_t rl; // red low bit position
+ uint8_t gh; // green high bit position + 1
+ uint8_t gl; // green low bit position
+ uint8_t bh; // blue high bit position + 1
+ uint8_t bl; // blue low bit position
+ };
+ struct {
+ uint8_t h;
+ uint8_t l;
+ } __attribute__((__packed__)) c[4];
+ } __attribute__((__packed__));
+ uint16_t components; // GGLFormatComponents
+} GGLFormat;
+
+
+#ifdef __cplusplus
+extern "C" const GGLFormat* gglGetPixelFormatTable(size_t* numEntries = 0);
+#else
+const GGLFormat* gglGetPixelFormatTable(size_t* numEntries);
+#endif
+
+
+// ----------------------------------------------------------------------------
+
+#endif // ANDROID_PIXELFLINGER_FORMAT_H
diff --git a/libpixelflinger/include/pixelflinger/pixelflinger.h b/libpixelflinger/include/pixelflinger/pixelflinger.h
new file mode 100644
index 0000000..8a2b442
--- /dev/null
+++ b/libpixelflinger/include/pixelflinger/pixelflinger.h
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PIXELFLINGER_H
+#define ANDROID_PIXELFLINGER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <pixelflinger/format.h>
+
+// GGL types
+
+typedef int8_t GGLbyte; // b
+typedef int16_t GGLshort; // s
+typedef int32_t GGLint; // i
+typedef ssize_t GGLsizei; // i
+typedef int32_t GGLfixed; // x
+typedef int32_t GGLclampx; // x
+typedef float GGLfloat; // f
+typedef float GGLclampf; // f
+typedef double GGLdouble; // d
+typedef double GGLclampd; // d
+typedef uint8_t GGLubyte; // ub
+typedef uint8_t GGLboolean; // ub
+typedef uint16_t GGLushort; // us
+typedef uint32_t GGLuint; // ui
+typedef unsigned int GGLenum; // ui
+typedef unsigned int GGLbitfield; // ui
+typedef void GGLvoid;
+typedef int32_t GGLfixed32;
+typedef int32_t GGLcolor;
+typedef int32_t GGLcoord;
+
+// ----------------------------------------------------------------------------
+
+#define GGL_MAX_VIEWPORT_DIMS 4096
+#define GGL_MAX_TEXTURE_SIZE 4096
+#define GGL_MAX_ALIASED_POINT_SIZE 0x7FFFFFF
+#define GGL_MAX_SMOOTH_POINT_SIZE 2048
+#define GGL_MAX_SMOOTH_LINE_WIDTH 2048
+
+// ----------------------------------------------------------------------------
+
+// All these names are compatible with their OpenGL equivalents
+// some of them are listed only for completeness
+enum GGLNames {
+ GGL_FALSE = 0,
+ GGL_TRUE = 1,
+
+ // enable/disable
+ GGL_SCISSOR_TEST = 0x0C11,
+ GGL_TEXTURE_2D = 0x0DE1,
+ GGL_ALPHA_TEST = 0x0BC0,
+ GGL_BLEND = 0x0BE2,
+ GGL_COLOR_LOGIC_OP = 0x0BF2,
+ GGL_DITHER = 0x0BD0,
+ GGL_STENCIL_TEST = 0x0B90,
+ GGL_DEPTH_TEST = 0x0B71,
+ GGL_AA = 0x80000001,
+ GGL_W_LERP = 0x80000004,
+ GGL_POINT_SMOOTH_NICE = 0x80000005,
+
+ // buffers, pixel drawing/reading
+ GGL_COLOR = 0x1800,
+
+ // fog
+ GGL_FOG = 0x0B60,
+
+ // shade model
+ GGL_FLAT = 0x1D00,
+ GGL_SMOOTH = 0x1D01,
+
+ // Texture parameter name
+ GGL_TEXTURE_MIN_FILTER = 0x2801,
+ GGL_TEXTURE_MAG_FILTER = 0x2800,
+ GGL_TEXTURE_WRAP_S = 0x2802,
+ GGL_TEXTURE_WRAP_T = 0x2803,
+ GGL_TEXTURE_WRAP_R = 0x2804,
+
+ // Texture Filter
+ GGL_NEAREST = 0x2600,
+ GGL_LINEAR = 0x2601,
+ GGL_NEAREST_MIPMAP_NEAREST = 0x2700,
+ GGL_LINEAR_MIPMAP_NEAREST = 0x2701,
+ GGL_NEAREST_MIPMAP_LINEAR = 0x2702,
+ GGL_LINEAR_MIPMAP_LINEAR = 0x2703,
+
+ // Texture Wrap Mode
+ GGL_CLAMP = 0x2900,
+ GGL_REPEAT = 0x2901,
+ GGL_CLAMP_TO_EDGE = 0x812F,
+
+ // Texture Env Mode
+ GGL_REPLACE = 0x1E01,
+ GGL_MODULATE = 0x2100,
+ GGL_DECAL = 0x2101,
+ GGL_ADD = 0x0104,
+
+ // Texture Env Parameter
+ GGL_TEXTURE_ENV_MODE = 0x2200,
+ GGL_TEXTURE_ENV_COLOR = 0x2201,
+
+ // Texture Env Target
+ GGL_TEXTURE_ENV = 0x2300,
+
+ // Texture coord generation
+ GGL_TEXTURE_GEN_MODE = 0x2500,
+ GGL_S = 0x2000,
+ GGL_T = 0x2001,
+ GGL_R = 0x2002,
+ GGL_Q = 0x2003,
+ GGL_ONE_TO_ONE = 0x80000002,
+ GGL_AUTOMATIC = 0x80000003,
+
+ // AlphaFunction
+ GGL_NEVER = 0x0200,
+ GGL_LESS = 0x0201,
+ GGL_EQUAL = 0x0202,
+ GGL_LEQUAL = 0x0203,
+ GGL_GREATER = 0x0204,
+ GGL_NOTEQUAL = 0x0205,
+ GGL_GEQUAL = 0x0206,
+ GGL_ALWAYS = 0x0207,
+
+ // LogicOp
+ GGL_CLEAR = 0x1500, // 0
+ GGL_AND = 0x1501, // s & d
+ GGL_AND_REVERSE = 0x1502, // s & ~d
+ GGL_COPY = 0x1503, // s
+ GGL_AND_INVERTED = 0x1504, // ~s & d
+ GGL_NOOP = 0x1505, // d
+ GGL_XOR = 0x1506, // s ^ d
+ GGL_OR = 0x1507, // s | d
+ GGL_NOR = 0x1508, // ~(s | d)
+ GGL_EQUIV = 0x1509, // ~(s ^ d)
+ GGL_INVERT = 0x150A, // ~d
+ GGL_OR_REVERSE = 0x150B, // s | ~d
+ GGL_COPY_INVERTED = 0x150C, // ~s
+ GGL_OR_INVERTED = 0x150D, // ~s | d
+ GGL_NAND = 0x150E, // ~(s & d)
+ GGL_SET = 0x150F, // 1
+
+ // blending equation & function
+ GGL_ZERO = 0, // SD
+ GGL_ONE = 1, // SD
+ GGL_SRC_COLOR = 0x0300, // D
+ GGL_ONE_MINUS_SRC_COLOR = 0x0301, // D
+ GGL_SRC_ALPHA = 0x0302, // SD
+ GGL_ONE_MINUS_SRC_ALPHA = 0x0303, // SD
+ GGL_DST_ALPHA = 0x0304, // SD
+ GGL_ONE_MINUS_DST_ALPHA = 0x0305, // SD
+ GGL_DST_COLOR = 0x0306, // S
+ GGL_ONE_MINUS_DST_COLOR = 0x0307, // S
+ GGL_SRC_ALPHA_SATURATE = 0x0308, // S
+
+ // clear bits
+ GGL_DEPTH_BUFFER_BIT = 0x00000100,
+ GGL_STENCIL_BUFFER_BIT = 0x00000400,
+ GGL_COLOR_BUFFER_BIT = 0x00004000,
+
+ // errors
+ GGL_NO_ERROR = 0,
+ GGL_INVALID_ENUM = 0x0500,
+ GGL_INVALID_VALUE = 0x0501,
+ GGL_INVALID_OPERATION = 0x0502,
+ GGL_STACK_OVERFLOW = 0x0503,
+ GGL_STACK_UNDERFLOW = 0x0504,
+ GGL_OUT_OF_MEMORY = 0x0505
+};
+
+// ----------------------------------------------------------------------------
+
+typedef struct {
+ GGLsizei version; // always set to sizeof(GGLSurface)
+ GGLuint width; // width in pixels
+ GGLuint height; // height in pixels
+ GGLint stride; // stride in pixels
+ GGLubyte* data; // pointer to the bits
+ GGLubyte format; // pixel format
+ GGLubyte rfu[3]; // must be zero
+ // these values are dependent on the used format
+ union {
+ GGLint compressedFormat;
+ GGLint vstride;
+ };
+ void* reserved;
+} GGLSurface;
+
+
+typedef struct {
+ // immediate rendering
+ void (*pointx)(void *con, const GGLcoord* v, GGLcoord r);
+ void (*linex)(void *con,
+ const GGLcoord* v0, const GGLcoord* v1, GGLcoord width);
+ void (*recti)(void* c, GGLint l, GGLint t, GGLint r, GGLint b);
+ void (*trianglex)(void* c,
+ GGLcoord const* v0, GGLcoord const* v1, GGLcoord const* v2);
+
+ // scissor
+ void (*scissor)(void* c, GGLint x, GGLint y, GGLsizei width, GGLsizei height);
+
+ // Set the textures and color buffers
+ void (*activeTexture)(void* c, GGLuint tmu);
+ void (*bindTexture)(void* c, const GGLSurface* surface);
+ void (*colorBuffer)(void* c, const GGLSurface* surface);
+ void (*readBuffer)(void* c, const GGLSurface* surface);
+ void (*depthBuffer)(void* c, const GGLSurface* surface);
+ void (*bindTextureLod)(void* c, GGLuint tmu, const GGLSurface* surface);
+
+ // enable/disable features
+ void (*enable)(void* c, GGLenum name);
+ void (*disable)(void* c, GGLenum name);
+ void (*enableDisable)(void* c, GGLenum name, GGLboolean en);
+
+ // specify the fragment's color
+ void (*shadeModel)(void* c, GGLenum mode);
+ void (*color4xv)(void* c, const GGLclampx* color);
+ // specify color iterators (16.16)
+ void (*colorGrad12xv)(void* c, const GGLcolor* grad);
+
+ // specify Z coordinate iterators (0.32)
+ void (*zGrad3xv)(void* c, const GGLfixed32* grad);
+
+ // specify W coordinate iterators (16.16)
+ void (*wGrad3xv)(void* c, const GGLfixed* grad);
+
+ // specify fog iterator & color (16.16)
+ void (*fogGrad3xv)(void* c, const GGLfixed* grad);
+ void (*fogColor3xv)(void* c, const GGLclampx* color);
+
+ // specify blending parameters
+ void (*blendFunc)(void* c, GGLenum src, GGLenum dst);
+ void (*blendFuncSeparate)(void* c, GGLenum src, GGLenum dst,
+ GGLenum srcAlpha, GGLenum dstAplha);
+
+ // texture environnement (REPLACE / MODULATE / DECAL / BLEND)
+ void (*texEnvi)(void* c, GGLenum target,
+ GGLenum pname,
+ GGLint param);
+
+ void (*texEnvxv)(void* c, GGLenum target,
+ GGLenum pname, const GGLfixed* params);
+
+ // texture parameters (Wrapping, filter)
+ void (*texParameteri)(void* c, GGLenum target,
+ GGLenum pname,
+ GGLint param);
+
+ // texture iterators (16.16)
+ void (*texCoord2i)(void* c, GGLint s, GGLint t);
+ void (*texCoord2x)(void* c, GGLfixed s, GGLfixed t);
+
+ // s, dsdx, dsdy, scale, t, dtdx, dtdy, tscale
+ // This api uses block floating-point for S and T texture coordinates.
+ // All values are given in 16.16, scaled by 'scale'. In other words,
+ // set scale to 0, for 16.16 values.
+ void (*texCoordGradScale8xv)(void* c, GGLint tmu, const int32_t* grad8);
+
+ void (*texGeni)(void* c, GGLenum coord, GGLenum pname, GGLint param);
+
+ // masking
+ void (*colorMask)(void* c, GGLboolean red,
+ GGLboolean green,
+ GGLboolean blue,
+ GGLboolean alpha);
+
+ void (*depthMask)(void* c, GGLboolean flag);
+
+ void (*stencilMask)(void* c, GGLuint mask);
+
+ // alpha func
+ void (*alphaFuncx)(void* c, GGLenum func, GGLclampx ref);
+
+ // depth func
+ void (*depthFunc)(void* c, GGLenum func);
+
+ // logic op
+ void (*logicOp)(void* c, GGLenum opcode);
+
+ // clear
+ void (*clear)(void* c, GGLbitfield mask);
+ void (*clearColorx)(void* c,
+ GGLclampx r, GGLclampx g, GGLclampx b, GGLclampx a);
+ void (*clearDepthx)(void* c, GGLclampx depth);
+ void (*clearStencil)(void* c, GGLint s);
+
+ // framebuffer operations
+ void (*copyPixels)(void* c, GGLint x, GGLint y,
+ GGLsizei width, GGLsizei height, GGLenum type);
+ void (*rasterPos2x)(void* c, GGLfixed x, GGLfixed y);
+ void (*rasterPos2i)(void* c, GGLint x, GGLint y);
+} GGLContext;
+
+// ----------------------------------------------------------------------------
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// construct / destroy the context
+ssize_t gglInit(GGLContext** context);
+ssize_t gglUninit(GGLContext* context);
+
+GGLint gglBitBlit(
+ GGLContext* c,
+ int tmu,
+ GGLint crop[4],
+ GGLint where[4]);
+
+#ifdef __cplusplus
+};
+#endif
+
+// ----------------------------------------------------------------------------
+
+#endif // ANDROID_PIXELFLINGER_H
diff --git a/libpixelflinger/include/private/pixelflinger/ggl_context.h b/libpixelflinger/include/private/pixelflinger/ggl_context.h
new file mode 100644
index 0000000..d43655c
--- /dev/null
+++ b/libpixelflinger/include/private/pixelflinger/ggl_context.h
@@ -0,0 +1,565 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_GGL_CONTEXT_H
+#define ANDROID_GGL_CONTEXT_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include <string.h>
+#include <sys/types.h>
+#include <endian.h>
+
+#include <pixelflinger/pixelflinger.h>
+#include <private/pixelflinger/ggl_fixed.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+
+inline uint32_t GGL_RGBA_TO_HOST(uint32_t v) {
+ return v;
+}
+inline uint32_t GGL_HOST_TO_RGBA(uint32_t v) {
+ return v;
+}
+
+#else
+
+inline uint32_t GGL_RGBA_TO_HOST(uint32_t v) {
+#if defined(__mips__) && __mips==32 && __mips_isa_rev>=2
+ uint32_t r;
+ __asm__("wsbh %0, %1;"
+ "rotr %0, %0, 16"
+ : "=r" (r)
+ : "r" (v)
+ );
+ return r;
+#else
+ return (v<<24) | (v>>24) | ((v<<8)&0xff0000) | ((v>>8)&0xff00);
+#endif
+}
+inline uint32_t GGL_HOST_TO_RGBA(uint32_t v) {
+#if defined(__mips__) && __mips==32 && __mips_isa_rev>=2
+ uint32_t r;
+ __asm__("wsbh %0, %1;"
+ "rotr %0, %0, 16"
+ : "=r" (r)
+ : "r" (v)
+ );
+ return r;
+#else
+ return (v<<24) | (v>>24) | ((v<<8)&0xff0000) | ((v>>8)&0xff00);
+#endif
+}
+
+#endif
+
+// ----------------------------------------------------------------------------
+
+const int GGL_DITHER_BITS = 6; // dither weights stored on 6 bits
+const int GGL_DITHER_ORDER_SHIFT= 3;
+const int GGL_DITHER_ORDER = (1<<GGL_DITHER_ORDER_SHIFT);
+const int GGL_DITHER_SIZE = GGL_DITHER_ORDER * GGL_DITHER_ORDER;
+const int GGL_DITHER_MASK = GGL_DITHER_ORDER-1;
+
+// ----------------------------------------------------------------------------
+
+const int GGL_SUBPIXEL_BITS = 4;
+
+// TRI_FRACTION_BITS defines the number of bits we want to use
+// for the sub-pixel coordinates during the edge stepping, the
+// value shouldn't be more than 7, or bad things are going to
+// happen when drawing large triangles (8 doesn't work because
+// 32 bit muls will loose the sign bit)
+
+#define TRI_FRACTION_BITS (GGL_SUBPIXEL_BITS)
+#define TRI_ONE (1 << TRI_FRACTION_BITS)
+#define TRI_HALF (1 << (TRI_FRACTION_BITS-1))
+#define TRI_FROM_INT(x) ((x) << TRI_FRACTION_BITS)
+#define TRI_FRAC(x) ((x) & (TRI_ONE-1))
+#define TRI_FLOOR(x) ((x) & ~(TRI_ONE-1))
+#define TRI_CEIL(x) (((x) + (TRI_ONE-1)) & ~(TRI_ONE-1))
+#define TRI_ROUND(x) (((x) + TRI_HALF ) & ~(TRI_ONE-1))
+
+#define TRI_ROUDNING (1 << (16 - TRI_FRACTION_BITS - 1))
+#define TRI_FROM_FIXED(x) (((x)+TRI_ROUDNING) >> (16-TRI_FRACTION_BITS))
+
+#define TRI_SNAP_NEXT_HALF(x) (TRI_CEIL((x)+TRI_HALF) - TRI_HALF)
+#define TRI_SNAP_PREV_HALF(x) (TRI_CEIL((x)-TRI_HALF) - TRI_HALF)
+
+// ----------------------------------------------------------------------------
+
+const int GGL_COLOR_BITS = 24;
+
+// To maintain 8-bits color chanels, with a maximum GGLSurface
+// size of 4096 and GGL_SUBPIXEL_BITS=4, we need 8 + 12 + 4 = 24 bits
+// for encoding the color iterators
+
+inline GGLcolor gglFixedToIteratedColor(GGLfixed c) {
+ return (c << 8) - c;
+}
+
+// ----------------------------------------------------------------------------
+
+template<bool> struct CTA;
+template<> struct CTA<true> { };
+
+#define GGL_CONTEXT(con, c) context_t *con = static_cast<context_t *>(c)
+#define GGL_OFFSETOF(field) uintptr_t(&(((context_t*)0)->field))
+#define GGL_INIT_PROC(p, f) p.f = ggl_ ## f;
+#define GGL_BETWEEN(x, L, H) (uint32_t((x)-(L)) <= ((H)-(L)))
+
+#define ggl_likely(x) __builtin_expect(!!(x), 1)
+#define ggl_unlikely(x) __builtin_expect(!!(x), 0)
+
+const int GGL_TEXTURE_UNIT_COUNT = 2;
+const int GGL_TMU_STATE = 0x00000001;
+const int GGL_CB_STATE = 0x00000002;
+const int GGL_PIXEL_PIPELINE_STATE = 0x00000004;
+
+// ----------------------------------------------------------------------------
+
+#define GGL_RESERVE_NEEDS(name, l, s) \
+ const uint32_t GGL_NEEDS_##name##_MASK = (((1LU<<(s))-1)<<l); \
+ const uint32_t GGL_NEEDS_##name##_SHIFT = (l);
+
+#define GGL_BUILD_NEEDS(val, name) \
+ (((val)<<(GGL_NEEDS_##name##_SHIFT)) & GGL_NEEDS_##name##_MASK)
+
+#define GGL_READ_NEEDS(name, n) \
+ (uint32_t(n & GGL_NEEDS_##name##_MASK) >> GGL_NEEDS_##name##_SHIFT)
+
+#define GGL_NEED_MASK(name) (uint32_t(GGL_NEEDS_##name##_MASK))
+#define GGL_NEED(name, val) GGL_BUILD_NEEDS(val, name)
+
+GGL_RESERVE_NEEDS( CB_FORMAT, 0, 6 )
+GGL_RESERVE_NEEDS( SHADE, 6, 1 )
+GGL_RESERVE_NEEDS( W, 7, 1 )
+GGL_RESERVE_NEEDS( BLEND_SRC, 8, 4 )
+GGL_RESERVE_NEEDS( BLEND_DST, 12, 4 )
+GGL_RESERVE_NEEDS( BLEND_SRCA, 16, 4 )
+GGL_RESERVE_NEEDS( BLEND_DSTA, 20, 4 )
+GGL_RESERVE_NEEDS( LOGIC_OP, 24, 4 )
+GGL_RESERVE_NEEDS( MASK_ARGB, 28, 4 )
+
+GGL_RESERVE_NEEDS( P_ALPHA_TEST, 0, 3 )
+GGL_RESERVE_NEEDS( P_AA, 3, 1 )
+GGL_RESERVE_NEEDS( P_DEPTH_TEST, 4, 3 )
+GGL_RESERVE_NEEDS( P_MASK_Z, 7, 1 )
+GGL_RESERVE_NEEDS( P_DITHER, 8, 1 )
+GGL_RESERVE_NEEDS( P_FOG, 9, 1 )
+GGL_RESERVE_NEEDS( P_RESERVED1, 10,22 )
+
+GGL_RESERVE_NEEDS( T_FORMAT, 0, 6 )
+GGL_RESERVE_NEEDS( T_RESERVED0, 6, 1 )
+GGL_RESERVE_NEEDS( T_POT, 7, 1 )
+GGL_RESERVE_NEEDS( T_S_WRAP, 8, 2 )
+GGL_RESERVE_NEEDS( T_T_WRAP, 10, 2 )
+GGL_RESERVE_NEEDS( T_ENV, 12, 3 )
+GGL_RESERVE_NEEDS( T_LINEAR, 15, 1 )
+
+const int GGL_NEEDS_WRAP_CLAMP_TO_EDGE = 0;
+const int GGL_NEEDS_WRAP_REPEAT = 1;
+const int GGL_NEEDS_WRAP_11 = 2;
+
+inline uint32_t ggl_wrap_to_needs(uint32_t e) {
+ switch (e) {
+ case GGL_CLAMP: return GGL_NEEDS_WRAP_CLAMP_TO_EDGE;
+ case GGL_REPEAT: return GGL_NEEDS_WRAP_REPEAT;
+ }
+ return 0;
+}
+
+inline uint32_t ggl_blendfactor_to_needs(uint32_t b) {
+ if (b <= 1) return b;
+ return (b & 0xF)+2;
+}
+
+inline uint32_t ggl_needs_to_blendfactor(uint32_t n) {
+ if (n <= 1) return n;
+ return (n - 2) + 0x300;
+}
+
+inline uint32_t ggl_env_to_needs(uint32_t e) {
+ switch (e) {
+ case GGL_REPLACE: return 0;
+ case GGL_MODULATE: return 1;
+ case GGL_DECAL: return 2;
+ case GGL_BLEND: return 3;
+ case GGL_ADD: return 4;
+ }
+ return 0;
+}
+
+inline uint32_t ggl_needs_to_env(uint32_t n) {
+ const uint32_t envs[] = { GGL_REPLACE, GGL_MODULATE,
+ GGL_DECAL, GGL_BLEND, GGL_ADD };
+ return envs[n];
+
+}
+
+// ----------------------------------------------------------------------------
+
+enum {
+ GGL_ENABLE_BLENDING = 0x00000001,
+ GGL_ENABLE_SMOOTH = 0x00000002,
+ GGL_ENABLE_AA = 0x00000004,
+ GGL_ENABLE_LOGIC_OP = 0x00000008,
+ GGL_ENABLE_ALPHA_TEST = 0x00000010,
+ GGL_ENABLE_SCISSOR_TEST = 0x00000020,
+ GGL_ENABLE_TMUS = 0x00000040,
+ GGL_ENABLE_DEPTH_TEST = 0x00000080,
+ GGL_ENABLE_STENCIL_TEST = 0x00000100,
+ GGL_ENABLE_W = 0x00000200,
+ GGL_ENABLE_DITHER = 0x00000400,
+ GGL_ENABLE_FOG = 0x00000800,
+ GGL_ENABLE_POINT_AA_NICE= 0x00001000
+};
+
+// ----------------------------------------------------------------------------
+
+class needs_filter_t;
+struct needs_t {
+ inline int match(const needs_filter_t& filter);
+ inline bool operator == (const needs_t& rhs) const {
+ return (n==rhs.n) &&
+ (p==rhs.p) &&
+ (t[0]==rhs.t[0]) &&
+ (t[1]==rhs.t[1]);
+ }
+ inline bool operator != (const needs_t& rhs) const {
+ return !operator == (rhs);
+ }
+ uint32_t n;
+ uint32_t p;
+ uint32_t t[GGL_TEXTURE_UNIT_COUNT];
+};
+
+inline int compare_type(const needs_t& lhs, const needs_t& rhs) {
+ return memcmp(&lhs, &rhs, sizeof(needs_t));
+}
+
+struct needs_filter_t {
+ needs_t value;
+ needs_t mask;
+};
+
+int needs_t::match(const needs_filter_t& filter) {
+ uint32_t result =
+ ((filter.value.n ^ n) & filter.mask.n) |
+ ((filter.value.p ^ p) & filter.mask.p) |
+ ((filter.value.t[0] ^ t[0]) & filter.mask.t[0]) |
+ ((filter.value.t[1] ^ t[1]) & filter.mask.t[1]);
+ return (result == 0);
+}
+
+// ----------------------------------------------------------------------------
+
+struct context_t;
+class Assembly;
+
+struct blend_state_t {
+ uint32_t src;
+ uint32_t dst;
+ uint32_t src_alpha;
+ uint32_t dst_alpha;
+ uint8_t reserved;
+ uint8_t alpha_separate;
+ uint8_t operation;
+ uint8_t equation;
+};
+
+struct mask_state_t {
+ uint8_t color;
+ uint8_t depth;
+ uint32_t stencil;
+};
+
+struct clear_state_t {
+ GGLclampx r;
+ GGLclampx g;
+ GGLclampx b;
+ GGLclampx a;
+ GGLclampx depth;
+ GGLint stencil;
+ uint32_t colorPacked;
+ uint32_t depthPacked;
+ uint32_t stencilPacked;
+ uint32_t dirty;
+};
+
+struct fog_state_t {
+ uint8_t color[4];
+};
+
+struct logic_op_state_t {
+ uint16_t opcode;
+};
+
+struct alpha_test_state_t {
+ uint16_t func;
+ GGLcolor ref;
+};
+
+struct depth_test_state_t {
+ uint16_t func;
+ GGLclampx clearValue;
+};
+
+struct scissor_t {
+ uint32_t user_left;
+ uint32_t user_right;
+ uint32_t user_top;
+ uint32_t user_bottom;
+ uint32_t left;
+ uint32_t right;
+ uint32_t top;
+ uint32_t bottom;
+};
+
+struct pixel_t {
+ uint32_t c[4];
+ uint8_t s[4];
+};
+
+struct surface_t {
+ union {
+ GGLSurface s;
+ // Keep the following struct field types in line with the corresponding
+ // GGLSurface fields to avoid mismatches leading to errors.
+ struct {
+ GGLsizei reserved;
+ GGLuint width;
+ GGLuint height;
+ GGLint stride;
+ GGLubyte* data;
+ GGLubyte format;
+ GGLubyte dirty;
+ GGLubyte pad[2];
+ };
+ };
+ void (*read) (const surface_t* s, context_t* c,
+ uint32_t x, uint32_t y, pixel_t* pixel);
+ void (*write)(const surface_t* s, context_t* c,
+ uint32_t x, uint32_t y, const pixel_t* pixel);
+};
+
+// ----------------------------------------------------------------------------
+
+struct texture_shade_t {
+ union {
+ struct {
+ int32_t is0;
+ int32_t idsdx;
+ int32_t idsdy;
+ int sscale;
+ int32_t it0;
+ int32_t idtdx;
+ int32_t idtdy;
+ int tscale;
+ };
+ struct {
+ int32_t v;
+ int32_t dx;
+ int32_t dy;
+ int scale;
+ } st[2];
+ };
+};
+
+struct texture_iterators_t {
+ // these are not encoded in the same way than in the
+ // texture_shade_t structure
+ union {
+ struct {
+ GGLfixed ydsdy;
+ GGLfixed dsdx;
+ GGLfixed dsdy;
+ int sscale;
+ GGLfixed ydtdy;
+ GGLfixed dtdx;
+ GGLfixed dtdy;
+ int tscale;
+ };
+ struct {
+ GGLfixed ydvdy;
+ GGLfixed dvdx;
+ GGLfixed dvdy;
+ int scale;
+ } st[2];
+ };
+};
+
+struct texture_t {
+ surface_t surface;
+ texture_iterators_t iterators;
+ texture_shade_t shade;
+ uint32_t s_coord;
+ uint32_t t_coord;
+ uint16_t s_wrap;
+ uint16_t t_wrap;
+ uint16_t min_filter;
+ uint16_t mag_filter;
+ uint16_t env;
+ uint8_t env_color[4];
+ uint8_t enable;
+ uint8_t dirty;
+};
+
+struct raster_t {
+ GGLfixed x;
+ GGLfixed y;
+};
+
+struct framebuffer_t {
+ surface_t color;
+ surface_t read;
+ surface_t depth;
+ surface_t stencil;
+ int16_t *coverage;
+ size_t coverageBufferSize;
+};
+
+// ----------------------------------------------------------------------------
+
+struct iterators_t {
+ int32_t xl;
+ int32_t xr;
+ int32_t y;
+ GGLcolor ydady;
+ GGLcolor ydrdy;
+ GGLcolor ydgdy;
+ GGLcolor ydbdy;
+ GGLfixed ydzdy;
+ GGLfixed ydwdy;
+ GGLfixed ydfdy;
+};
+
+struct shade_t {
+ GGLcolor a0;
+ GGLcolor dadx;
+ GGLcolor dady;
+ GGLcolor r0;
+ GGLcolor drdx;
+ GGLcolor drdy;
+ GGLcolor g0;
+ GGLcolor dgdx;
+ GGLcolor dgdy;
+ GGLcolor b0;
+ GGLcolor dbdx;
+ GGLcolor dbdy;
+ uint32_t z0;
+ GGLfixed32 dzdx;
+ GGLfixed32 dzdy;
+ GGLfixed w0;
+ GGLfixed dwdx;
+ GGLfixed dwdy;
+ uint32_t f0;
+ GGLfixed dfdx;
+ GGLfixed dfdy;
+};
+
+// these are used in the generated code
+// we use this mirror structure to improve
+// data locality in the pixel pipeline
+struct generated_tex_vars_t {
+ uint32_t width;
+ uint32_t height;
+ uint32_t stride;
+ uintptr_t data;
+ int32_t dsdx;
+ int32_t dtdx;
+ int32_t spill[2];
+};
+
+struct generated_vars_t {
+ struct {
+ int32_t c;
+ int32_t dx;
+ } argb[4];
+ int32_t aref;
+ int32_t dzdx;
+ int32_t zbase;
+ int32_t f;
+ int32_t dfdx;
+ int32_t spill[3];
+ generated_tex_vars_t texture[GGL_TEXTURE_UNIT_COUNT];
+ int32_t rt;
+ int32_t lb;
+};
+
+// ----------------------------------------------------------------------------
+
+struct state_t {
+ framebuffer_t buffers;
+ texture_t texture[GGL_TEXTURE_UNIT_COUNT];
+ scissor_t scissor;
+ raster_t raster;
+ blend_state_t blend;
+ alpha_test_state_t alpha_test;
+ depth_test_state_t depth_test;
+ mask_state_t mask;
+ clear_state_t clear;
+ fog_state_t fog;
+ logic_op_state_t logic_op;
+ uint32_t enables;
+ uint32_t enabled_tmu;
+ needs_t needs;
+};
+
+// ----------------------------------------------------------------------------
+
+struct context_t {
+ GGLContext procs;
+ state_t state;
+ shade_t shade;
+ iterators_t iterators;
+ generated_vars_t generated_vars __attribute__((aligned(32)));
+ uint8_t ditherMatrix[GGL_DITHER_SIZE] __attribute__((aligned(32)));
+ uint32_t packed;
+ uint32_t packed8888;
+ const GGLFormat* formats;
+ uint32_t dirty;
+ texture_t* activeTMU;
+ uint32_t activeTMUIndex;
+
+ void (*init_y)(context_t* c, int32_t y);
+ void (*step_y)(context_t* c);
+ void (*scanline)(context_t* c);
+ void (*span)(context_t* c);
+ void (*rect)(context_t* c, size_t yc);
+
+ void* base;
+ Assembly* scanline_as;
+ GGLenum error;
+};
+
+// ----------------------------------------------------------------------------
+
+void ggl_init_context(context_t* context);
+void ggl_uninit_context(context_t* context);
+void ggl_error(context_t* c, GGLenum error);
+int64_t ggl_system_time();
+
+// ----------------------------------------------------------------------------
+
+};
+
+#endif // ANDROID_GGL_CONTEXT_H
+
diff --git a/libpixelflinger/include/private/pixelflinger/ggl_fixed.h b/libpixelflinger/include/private/pixelflinger/ggl_fixed.h
new file mode 100644
index 0000000..787f620
--- /dev/null
+++ b/libpixelflinger/include/private/pixelflinger/ggl_fixed.h
@@ -0,0 +1,633 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_GGL_FIXED_H
+#define ANDROID_GGL_FIXED_H
+
+#include <math.h>
+#include <pixelflinger/pixelflinger.h>
+
+// ----------------------------------------------------------------------------
+
+#define CONST __attribute__((const))
+#define ALWAYS_INLINE __attribute__((always_inline))
+
+const GGLfixed FIXED_BITS = 16;
+const GGLfixed FIXED_EPSILON = 1;
+const GGLfixed FIXED_ONE = 1L<<FIXED_BITS;
+const GGLfixed FIXED_HALF = 1L<<(FIXED_BITS-1);
+const GGLfixed FIXED_MIN = 0x80000000L;
+const GGLfixed FIXED_MAX = 0x7FFFFFFFL;
+
+inline GGLfixed gglIntToFixed(GGLfixed i) ALWAYS_INLINE ;
+inline GGLfixed gglFixedToIntRound(GGLfixed f) ALWAYS_INLINE ;
+inline GGLfixed gglFixedToIntFloor(GGLfixed f) ALWAYS_INLINE ;
+inline GGLfixed gglFixedToIntCeil(GGLfixed f) ALWAYS_INLINE ;
+inline GGLfixed gglFracx(GGLfixed v) ALWAYS_INLINE ;
+inline GGLfixed gglFloorx(GGLfixed v) ALWAYS_INLINE ;
+inline GGLfixed gglCeilx(GGLfixed v) ALWAYS_INLINE ;
+inline GGLfixed gglCenterx(GGLfixed v) ALWAYS_INLINE ;
+inline GGLfixed gglRoundx(GGLfixed v) ALWAYS_INLINE ;
+
+GGLfixed gglIntToFixed(GGLfixed i) {
+ return i<<FIXED_BITS;
+}
+GGLfixed gglFixedToIntRound(GGLfixed f) {
+ return (f + FIXED_HALF)>>FIXED_BITS;
+}
+GGLfixed gglFixedToIntFloor(GGLfixed f) {
+ return f>>FIXED_BITS;
+}
+GGLfixed gglFixedToIntCeil(GGLfixed f) {
+ return (f + ((1<<FIXED_BITS) - 1))>>FIXED_BITS;
+}
+
+GGLfixed gglFracx(GGLfixed v) {
+ return v & ((1<<FIXED_BITS)-1);
+}
+GGLfixed gglFloorx(GGLfixed v) {
+ return gglFixedToIntFloor(v)<<FIXED_BITS;
+}
+GGLfixed gglCeilx(GGLfixed v) {
+ return gglFixedToIntCeil(v)<<FIXED_BITS;
+}
+GGLfixed gglCenterx(GGLfixed v) {
+ return gglFloorx(v + FIXED_HALF) | FIXED_HALF;
+}
+GGLfixed gglRoundx(GGLfixed v) {
+ return gglFixedToIntRound(v)<<FIXED_BITS;
+}
+
+// conversion from (unsigned) int, short, byte to fixed...
+#define GGL_B_TO_X(_x) GGLfixed( ((int32_t(_x)+1)>>1)<<10 )
+#define GGL_S_TO_X(_x) GGLfixed( ((int32_t(_x)+1)>>1)<<2 )
+#define GGL_I_TO_X(_x) GGLfixed( ((int32_t(_x)>>1)+1)>>14 )
+#define GGL_UB_TO_X(_x) GGLfixed( uint32_t(_x) + \
+ (uint32_t(_x)<<8) + \
+ (uint32_t(_x)>>7) )
+#define GGL_US_TO_X(_x) GGLfixed( (_x) + ((_x)>>15) )
+#define GGL_UI_TO_X(_x) GGLfixed( (((_x)>>1)+1)>>15 )
+
+// ----------------------------------------------------------------------------
+
+GGLfixed gglPowx(GGLfixed x, GGLfixed y) CONST;
+GGLfixed gglSqrtx(GGLfixed a) CONST;
+GGLfixed gglSqrtRecipx(GGLfixed x) CONST;
+GGLfixed gglFastDivx(GGLfixed n, GGLfixed d) CONST;
+int32_t gglMulDivi(int32_t a, int32_t b, int32_t c);
+
+int32_t gglRecipQNormalized(int32_t x, int* exponent);
+int32_t gglRecipQ(GGLfixed x, int q) CONST;
+
+inline GGLfixed gglRecip(GGLfixed x) CONST;
+inline GGLfixed gglRecip(GGLfixed x) {
+ return gglRecipQ(x, 16);
+}
+
+inline GGLfixed gglRecip28(GGLfixed x) CONST;
+int32_t gglRecip28(GGLfixed x) {
+ return gglRecipQ(x, 28);
+}
+
+// ----------------------------------------------------------------------------
+
+#if defined(__arm__) && !defined(__thumb__)
+
+// inline ARM implementations
+inline GGLfixed gglMulx(GGLfixed x, GGLfixed y, int shift) CONST;
+inline GGLfixed gglMulx(GGLfixed x, GGLfixed y, int shift) {
+ GGLfixed result, t;
+ if (__builtin_constant_p(shift)) {
+ asm("smull %[lo], %[hi], %[x], %[y] \n"
+ "movs %[lo], %[lo], lsr %[rshift] \n"
+ "adc %[lo], %[lo], %[hi], lsl %[lshift] \n"
+ : [lo]"=r"(result), [hi]"=r"(t), [x]"=r"(x)
+ : "%[x]"(x), [y]"r"(y), [lshift] "I"(32-shift), [rshift] "I"(shift)
+ : "cc"
+ );
+ } else {
+ asm("smull %[lo], %[hi], %[x], %[y] \n"
+ "movs %[lo], %[lo], lsr %[rshift] \n"
+ "adc %[lo], %[lo], %[hi], lsl %[lshift] \n"
+ : [lo]"=&r"(result), [hi]"=&r"(t), [x]"=&r"(x)
+ : "%[x]"(x), [y]"r"(y), [lshift] "r"(32-shift), [rshift] "r"(shift)
+ : "cc"
+ );
+ }
+ return result;
+}
+
+inline GGLfixed gglMulAddx(GGLfixed x, GGLfixed y, GGLfixed a, int shift) CONST;
+inline GGLfixed gglMulAddx(GGLfixed x, GGLfixed y, GGLfixed a, int shift) {
+ GGLfixed result, t;
+ if (__builtin_constant_p(shift)) {
+ asm("smull %[lo], %[hi], %[x], %[y] \n"
+ "add %[lo], %[a], %[lo], lsr %[rshift] \n"
+ "add %[lo], %[lo], %[hi], lsl %[lshift] \n"
+ : [lo]"=&r"(result), [hi]"=&r"(t), [x]"=&r"(x)
+ : "%[x]"(x), [y]"r"(y), [a]"r"(a), [lshift] "I"(32-shift), [rshift] "I"(shift)
+ );
+ } else {
+ asm("smull %[lo], %[hi], %[x], %[y] \n"
+ "add %[lo], %[a], %[lo], lsr %[rshift] \n"
+ "add %[lo], %[lo], %[hi], lsl %[lshift] \n"
+ : [lo]"=&r"(result), [hi]"=&r"(t), [x]"=&r"(x)
+ : "%[x]"(x), [y]"r"(y), [a]"r"(a), [lshift] "r"(32-shift), [rshift] "r"(shift)
+ );
+ }
+ return result;
+}
+
+inline GGLfixed gglMulSubx(GGLfixed x, GGLfixed y, GGLfixed a, int shift) CONST;
+inline GGLfixed gglMulSubx(GGLfixed x, GGLfixed y, GGLfixed a, int shift) {
+ GGLfixed result, t;
+ if (__builtin_constant_p(shift)) {
+ asm("smull %[lo], %[hi], %[x], %[y] \n"
+ "rsb %[lo], %[a], %[lo], lsr %[rshift] \n"
+ "add %[lo], %[lo], %[hi], lsl %[lshift] \n"
+ : [lo]"=&r"(result), [hi]"=&r"(t), [x]"=&r"(x)
+ : "%[x]"(x), [y]"r"(y), [a]"r"(a), [lshift] "I"(32-shift), [rshift] "I"(shift)
+ );
+ } else {
+ asm("smull %[lo], %[hi], %[x], %[y] \n"
+ "rsb %[lo], %[a], %[lo], lsr %[rshift] \n"
+ "add %[lo], %[lo], %[hi], lsl %[lshift] \n"
+ : [lo]"=&r"(result), [hi]"=&r"(t), [x]"=&r"(x)
+ : "%[x]"(x), [y]"r"(y), [a]"r"(a), [lshift] "r"(32-shift), [rshift] "r"(shift)
+ );
+ }
+ return result;
+}
+
+inline int64_t gglMulii(int32_t x, int32_t y) CONST;
+inline int64_t gglMulii(int32_t x, int32_t y)
+{
+ // 64-bits result: r0=low, r1=high
+ union {
+ struct {
+ int32_t lo;
+ int32_t hi;
+ } s;
+ int64_t res;
+ };
+ asm("smull %0, %1, %2, %3 \n"
+ : "=r"(s.lo), "=&r"(s.hi)
+ : "%r"(x), "r"(y)
+ :
+ );
+ return res;
+}
+#elif defined(__mips__) && __mips_isa_rev < 6
+
+/*inline MIPS implementations*/
+inline GGLfixed gglMulx(GGLfixed a, GGLfixed b, int shift) CONST;
+inline GGLfixed gglMulx(GGLfixed a, GGLfixed b, int shift) {
+ GGLfixed result,tmp,tmp1,tmp2;
+
+ if (__builtin_constant_p(shift)) {
+ if (shift == 0) {
+ asm ("mult %[a], %[b] \t\n"
+ "mflo %[res] \t\n"
+ : [res]"=&r"(result),[tmp]"=&r"(tmp)
+ : [a]"r"(a),[b]"r"(b)
+ : "%hi","%lo"
+ );
+ } else if (shift == 32)
+ {
+ asm ("mult %[a], %[b] \t\n"
+ "li %[tmp],1\t\n"
+ "sll %[tmp],%[tmp],0x1f\t\n"
+ "mflo %[res] \t\n"
+ "addu %[tmp1],%[tmp],%[res] \t\n"
+ "sltu %[tmp1],%[tmp1],%[tmp]\t\n" /*obit*/
+ "sra %[tmp],%[tmp],0x1f \t\n"
+ "mfhi %[res] \t\n"
+ "addu %[res],%[res],%[tmp]\t\n"
+ "addu %[res],%[res],%[tmp1]\t\n"
+ : [res]"=&r"(result),[tmp]"=&r"(tmp),[tmp1]"=&r"(tmp1)
+ : [a]"r"(a),[b]"r"(b),[shift]"I"(shift)
+ : "%hi","%lo"
+ );
+ } else if ((shift >0) && (shift < 32))
+ {
+ asm ("mult %[a], %[b] \t\n"
+ "li %[tmp],1 \t\n"
+ "sll %[tmp],%[tmp],%[shiftm1] \t\n"
+ "mflo %[res] \t\n"
+ "addu %[tmp1],%[tmp],%[res] \t\n"
+ "sltu %[tmp1],%[tmp1],%[tmp] \t\n" /*obit?*/
+ "addu %[res],%[res],%[tmp] \t\n"
+ "mfhi %[tmp] \t\n"
+ "addu %[tmp],%[tmp],%[tmp1] \t\n"
+ "sll %[tmp],%[tmp],%[lshift] \t\n"
+ "srl %[res],%[res],%[rshift] \t\n"
+ "or %[res],%[res],%[tmp] \t\n"
+ : [res]"=&r"(result),[tmp]"=&r"(tmp),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2)
+ : [a]"r"(a),[b]"r"(b),[lshift]"I"(32-shift),[rshift]"I"(shift),[shiftm1]"I"(shift-1)
+ : "%hi","%lo"
+ );
+ } else {
+ asm ("mult %[a], %[b] \t\n"
+ "li %[tmp],1 \t\n"
+ "sll %[tmp],%[tmp],%[shiftm1] \t\n"
+ "mflo %[res] \t\n"
+ "addu %[tmp1],%[tmp],%[res] \t\n"
+ "sltu %[tmp1],%[tmp1],%[tmp] \t\n" /*obit?*/
+ "sra %[tmp2],%[tmp],0x1f \t\n"
+ "addu %[res],%[res],%[tmp] \t\n"
+ "mfhi %[tmp] \t\n"
+ "addu %[tmp],%[tmp],%[tmp2] \t\n"
+ "addu %[tmp],%[tmp],%[tmp1] \t\n" /*tmp=hi*/
+ "srl %[tmp2],%[res],%[rshift] \t\n"
+ "srav %[res], %[tmp],%[rshift]\t\n"
+ "sll %[tmp],%[tmp],1 \t\n"
+ "sll %[tmp],%[tmp],%[norbits] \t\n"
+ "or %[tmp],%[tmp],%[tmp2] \t\n"
+ "movz %[res],%[tmp],%[bit5] \t\n"
+ : [res]"=&r"(result),[tmp]"=&r"(tmp),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2)
+ : [a]"r"(a),[b]"r"(b),[norbits]"I"(~(shift)),[rshift]"I"(shift),[shiftm1] "I"(shift-1),[bit5]"I"(shift & 0x20)
+ : "%hi","%lo"
+ );
+ }
+ } else {
+ asm ("mult %[a], %[b] \t\n"
+ "li %[tmp],1 \t\n"
+ "sll %[tmp],%[tmp],%[shiftm1] \t\n"
+ "mflo %[res] \t\n"
+ "addu %[tmp1],%[tmp],%[res] \t\n"
+ "sltu %[tmp1],%[tmp1],%[tmp] \t\n" /*obit?*/
+ "sra %[tmp2],%[tmp],0x1f \t\n"
+ "addu %[res],%[res],%[tmp] \t\n"
+ "mfhi %[tmp] \t\n"
+ "addu %[tmp],%[tmp],%[tmp2] \t\n"
+ "addu %[tmp],%[tmp],%[tmp1] \t\n" /*tmp=hi*/
+ "srl %[tmp2],%[res],%[rshift] \t\n"
+ "srav %[res], %[tmp],%[rshift]\t\n"
+ "sll %[tmp],%[tmp],1 \t\n"
+ "sll %[tmp],%[tmp],%[norbits] \t\n"
+ "or %[tmp],%[tmp],%[tmp2] \t\n"
+ "movz %[res],%[tmp],%[bit5] \t\n"
+ : [res]"=&r"(result),[tmp]"=&r"(tmp),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2)
+ : [a]"r"(a),[b]"r"(b),[norbits]"r"(~(shift)),[rshift] "r"(shift),[shiftm1]"r"(shift-1),[bit5] "r"(shift & 0x20)
+ : "%hi","%lo"
+ );
+ }
+
+ return result;
+}
+
+inline GGLfixed gglMulAddx(GGLfixed a, GGLfixed b, GGLfixed c, int shift) CONST;
+inline GGLfixed gglMulAddx(GGLfixed a, GGLfixed b, GGLfixed c, int shift) {
+ GGLfixed result,t,tmp1,tmp2;
+
+ if (__builtin_constant_p(shift)) {
+ if (shift == 0) {
+ asm ("mult %[a], %[b] \t\n"
+ "mflo %[lo] \t\n"
+ "addu %[lo],%[lo],%[c] \t\n"
+ : [lo]"=&r"(result)
+ : [a]"r"(a),[b]"r"(b),[c]"r"(c)
+ : "%hi","%lo"
+ );
+ } else if (shift == 32) {
+ asm ("mult %[a], %[b] \t\n"
+ "mfhi %[lo] \t\n"
+ "addu %[lo],%[lo],%[c] \t\n"
+ : [lo]"=&r"(result)
+ : [a]"r"(a),[b]"r"(b),[c]"r"(c)
+ : "%hi","%lo"
+ );
+ } else if ((shift>0) && (shift<32)) {
+ asm ("mult %[a], %[b] \t\n"
+ "mflo %[res] \t\n"
+ "mfhi %[t] \t\n"
+ "srl %[res],%[res],%[rshift] \t\n"
+ "sll %[t],%[t],%[lshift] \t\n"
+ "or %[res],%[res],%[t] \t\n"
+ "addu %[res],%[res],%[c] \t\n"
+ : [res]"=&r"(result),[t]"=&r"(t)
+ : [a]"r"(a),[b]"r"(b),[c]"r"(c),[lshift]"I"(32-shift),[rshift]"I"(shift)
+ : "%hi","%lo"
+ );
+ } else {
+ asm ("mult %[a], %[b] \t\n"
+ "nor %[tmp1],$zero,%[shift]\t\n"
+ "mflo %[res] \t\n"
+ "mfhi %[t] \t\n"
+ "srl %[res],%[res],%[shift] \t\n"
+ "sll %[tmp2],%[t],1 \t\n"
+ "sllv %[tmp2],%[tmp2],%[tmp1] \t\n"
+ "or %[tmp1],%[tmp2],%[res] \t\n"
+ "srav %[res],%[t],%[shift] \t\n"
+ "andi %[tmp2],%[shift],0x20\t\n"
+ "movz %[res],%[tmp1],%[tmp2]\t\n"
+ "addu %[res],%[res],%[c] \t\n"
+ : [res]"=&r"(result),[t]"=&r"(t),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2)
+ : [a]"r"(a),[b]"r"(b),[c]"r"(c),[shift]"I"(shift)
+ : "%hi","%lo"
+ );
+ }
+ } else {
+ asm ("mult %[a], %[b] \t\n"
+ "nor %[tmp1],$zero,%[shift]\t\n"
+ "mflo %[res] \t\n"
+ "mfhi %[t] \t\n"
+ "srl %[res],%[res],%[shift] \t\n"
+ "sll %[tmp2],%[t],1 \t\n"
+ "sllv %[tmp2],%[tmp2],%[tmp1] \t\n"
+ "or %[tmp1],%[tmp2],%[res] \t\n"
+ "srav %[res],%[t],%[shift] \t\n"
+ "andi %[tmp2],%[shift],0x20\t\n"
+ "movz %[res],%[tmp1],%[tmp2]\t\n"
+ "addu %[res],%[res],%[c] \t\n"
+ : [res]"=&r"(result),[t]"=&r"(t),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2)
+ : [a]"r"(a),[b]"r"(b),[c]"r"(c),[shift]"r"(shift)
+ : "%hi","%lo"
+ );
+ }
+ return result;
+}
+
+inline GGLfixed gglMulSubx(GGLfixed a, GGLfixed b, GGLfixed c, int shift) CONST;
+inline GGLfixed gglMulSubx(GGLfixed a, GGLfixed b, GGLfixed c, int shift) {
+ GGLfixed result,t,tmp1,tmp2;
+
+ if (__builtin_constant_p(shift)) {
+ if (shift == 0) {
+ asm ("mult %[a], %[b] \t\n"
+ "mflo %[lo] \t\n"
+ "subu %[lo],%[lo],%[c] \t\n"
+ : [lo]"=&r"(result)
+ : [a]"r"(a),[b]"r"(b),[c]"r"(c)
+ : "%hi","%lo"
+ );
+ } else if (shift == 32) {
+ asm ("mult %[a], %[b] \t\n"
+ "mfhi %[lo] \t\n"
+ "subu %[lo],%[lo],%[c] \t\n"
+ : [lo]"=&r"(result)
+ : [a]"r"(a),[b]"r"(b),[c]"r"(c)
+ : "%hi","%lo"
+ );
+ } else if ((shift>0) && (shift<32)) {
+ asm ("mult %[a], %[b] \t\n"
+ "mflo %[res] \t\n"
+ "mfhi %[t] \t\n"
+ "srl %[res],%[res],%[rshift] \t\n"
+ "sll %[t],%[t],%[lshift] \t\n"
+ "or %[res],%[res],%[t] \t\n"
+ "subu %[res],%[res],%[c] \t\n"
+ : [res]"=&r"(result),[t]"=&r"(t)
+ : [a]"r"(a),[b]"r"(b),[c]"r"(c),[lshift]"I"(32-shift),[rshift]"I"(shift)
+ : "%hi","%lo"
+ );
+ } else {
+ asm ("mult %[a], %[b] \t\n"
+ "nor %[tmp1],$zero,%[shift]\t\n"
+ "mflo %[res] \t\n"
+ "mfhi %[t] \t\n"
+ "srl %[res],%[res],%[shift] \t\n"
+ "sll %[tmp2],%[t],1 \t\n"
+ "sllv %[tmp2],%[tmp2],%[tmp1] \t\n"
+ "or %[tmp1],%[tmp2],%[res] \t\n"
+ "srav %[res],%[t],%[shift] \t\n"
+ "andi %[tmp2],%[shift],0x20\t\n"
+ "movz %[res],%[tmp1],%[tmp2]\t\n"
+ "subu %[res],%[res],%[c] \t\n"
+ : [res]"=&r"(result),[t]"=&r"(t),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2)
+ : [a]"r"(a),[b]"r"(b),[c]"r"(c),[shift]"I"(shift)
+ : "%hi","%lo"
+ );
+ }
+ } else {
+ asm ("mult %[a], %[b] \t\n"
+ "nor %[tmp1],$zero,%[shift]\t\n"
+ "mflo %[res] \t\n"
+ "mfhi %[t] \t\n"
+ "srl %[res],%[res],%[shift] \t\n"
+ "sll %[tmp2],%[t],1 \t\n"
+ "sllv %[tmp2],%[tmp2],%[tmp1] \t\n"
+ "or %[tmp1],%[tmp2],%[res] \t\n"
+ "srav %[res],%[t],%[shift] \t\n"
+ "andi %[tmp2],%[shift],0x20\t\n"
+ "movz %[res],%[tmp1],%[tmp2]\t\n"
+ "subu %[res],%[res],%[c] \t\n"
+ : [res]"=&r"(result),[t]"=&r"(t),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2)
+ : [a]"r"(a),[b]"r"(b),[c]"r"(c),[shift]"r"(shift)
+ : "%hi","%lo"
+ );
+ }
+ return result;
+}
+
+inline int64_t gglMulii(int32_t x, int32_t y) CONST;
+inline int64_t gglMulii(int32_t x, int32_t y) {
+ union {
+ struct {
+#if defined(__MIPSEL__)
+ int32_t lo;
+ int32_t hi;
+#elif defined(__MIPSEB__)
+ int32_t hi;
+ int32_t lo;
+#endif
+ } s;
+ int64_t res;
+ }u;
+ asm("mult %2, %3 \t\n"
+ "mfhi %1 \t\n"
+ "mflo %0 \t\n"
+ : "=r"(u.s.lo), "=&r"(u.s.hi)
+ : "%r"(x), "r"(y)
+ : "%hi","%lo"
+ );
+ return u.res;
+}
+
+#elif defined(__aarch64__)
+
+// inline AArch64 implementations
+
+inline GGLfixed gglMulx(GGLfixed x, GGLfixed y, int shift) CONST;
+inline GGLfixed gglMulx(GGLfixed x, GGLfixed y, int shift)
+{
+ GGLfixed result;
+ GGLfixed round;
+
+ asm("mov %x[round], #1 \n"
+ "lsl %x[round], %x[round], %x[shift] \n"
+ "lsr %x[round], %x[round], #1 \n"
+ "smaddl %x[result], %w[x], %w[y],%x[round] \n"
+ "lsr %x[result], %x[result], %x[shift] \n"
+ : [round]"=&r"(round), [result]"=&r"(result) \
+ : [x]"r"(x), [y]"r"(y), [shift] "r"(shift) \
+ :
+ );
+ return result;
+}
+inline GGLfixed gglMulAddx(GGLfixed x, GGLfixed y, GGLfixed a, int shift) CONST;
+inline GGLfixed gglMulAddx(GGLfixed x, GGLfixed y, GGLfixed a, int shift)
+{
+ GGLfixed result;
+ asm("smull %x[result], %w[x], %w[y] \n"
+ "lsr %x[result], %x[result], %x[shift] \n"
+ "add %w[result], %w[result], %w[a] \n"
+ : [result]"=&r"(result) \
+ : [x]"r"(x), [y]"r"(y), [a]"r"(a), [shift] "r"(shift) \
+ :
+ );
+ return result;
+}
+
+inline GGLfixed gglMulSubx(GGLfixed x, GGLfixed y, GGLfixed a, int shift) CONST;
+inline GGLfixed gglMulSubx(GGLfixed x, GGLfixed y, GGLfixed a, int shift)
+{
+
+ GGLfixed result;
+ int rshift;
+
+ asm("smull %x[result], %w[x], %w[y] \n"
+ "lsr %x[result], %x[result], %x[shift] \n"
+ "sub %w[result], %w[result], %w[a] \n"
+ : [result]"=&r"(result) \
+ : [x]"r"(x), [y]"r"(y), [a]"r"(a), [shift] "r"(shift) \
+ :
+ );
+ return result;
+}
+inline int64_t gglMulii(int32_t x, int32_t y) CONST;
+inline int64_t gglMulii(int32_t x, int32_t y)
+{
+ int64_t res;
+ asm("smull %x0, %w1, %w2 \n"
+ : "=r"(res)
+ : "%r"(x), "r"(y)
+ :
+ );
+ return res;
+}
+
+#else // ----------------------------------------------------------------------
+
+inline GGLfixed gglMulx(GGLfixed a, GGLfixed b, int shift) CONST;
+inline GGLfixed gglMulx(GGLfixed a, GGLfixed b, int shift) {
+ return GGLfixed((int64_t(a)*b + (1<<(shift-1)))>>shift);
+}
+inline GGLfixed gglMulAddx(GGLfixed a, GGLfixed b, GGLfixed c, int shift) CONST;
+inline GGLfixed gglMulAddx(GGLfixed a, GGLfixed b, GGLfixed c, int shift) {
+ return GGLfixed((int64_t(a)*b)>>shift) + c;
+}
+inline GGLfixed gglMulSubx(GGLfixed a, GGLfixed b, GGLfixed c, int shift) CONST;
+inline GGLfixed gglMulSubx(GGLfixed a, GGLfixed b, GGLfixed c, int shift) {
+ return GGLfixed((int64_t(a)*b)>>shift) - c;
+}
+inline int64_t gglMulii(int32_t a, int32_t b) CONST;
+inline int64_t gglMulii(int32_t a, int32_t b) {
+ return int64_t(a)*b;
+}
+
+#endif
+
+// ------------------------------------------------------------------------
+
+inline GGLfixed gglMulx(GGLfixed a, GGLfixed b) CONST;
+inline GGLfixed gglMulx(GGLfixed a, GGLfixed b) {
+ return gglMulx(a, b, 16);
+}
+inline GGLfixed gglMulAddx(GGLfixed a, GGLfixed b, GGLfixed c) CONST;
+inline GGLfixed gglMulAddx(GGLfixed a, GGLfixed b, GGLfixed c) {
+ return gglMulAddx(a, b, c, 16);
+}
+inline GGLfixed gglMulSubx(GGLfixed a, GGLfixed b, GGLfixed c) CONST;
+inline GGLfixed gglMulSubx(GGLfixed a, GGLfixed b, GGLfixed c) {
+ return gglMulSubx(a, b, c, 16);
+}
+
+// ------------------------------------------------------------------------
+
+inline int32_t gglClz(int32_t x) CONST;
+inline int32_t gglClz(int32_t x)
+{
+#if (defined(__arm__) && !defined(__thumb__)) || defined(__mips__) || defined(__aarch64__)
+ return __builtin_clz(x);
+#else
+ if (!x) return 32;
+ int32_t exp = 31;
+ if (x & 0xFFFF0000) { exp -=16; x >>= 16; }
+ if (x & 0x0000ff00) { exp -= 8; x >>= 8; }
+ if (x & 0x000000f0) { exp -= 4; x >>= 4; }
+ if (x & 0x0000000c) { exp -= 2; x >>= 2; }
+ if (x & 0x00000002) { exp -= 1; }
+ return exp;
+#endif
+}
+
+// ------------------------------------------------------------------------
+
+int32_t gglDivQ(GGLfixed n, GGLfixed d, int32_t i) CONST;
+
+inline int32_t gglDivQ16(GGLfixed n, GGLfixed d) CONST;
+inline int32_t gglDivQ16(GGLfixed n, GGLfixed d) {
+ return gglDivQ(n, d, 16);
+}
+
+inline int32_t gglDivx(GGLfixed n, GGLfixed d) CONST;
+inline int32_t gglDivx(GGLfixed n, GGLfixed d) {
+ return gglDivQ(n, d, 16);
+}
+
+// ------------------------------------------------------------------------
+
+inline GGLfixed gglRecipFast(GGLfixed x) CONST;
+inline GGLfixed gglRecipFast(GGLfixed x)
+{
+ // This is a really bad approximation of 1/x, but it's also
+ // very fast. x must be strictly positive.
+ // if x between [0.5, 1[ , then 1/x = 3-2*x
+ // (we use 2.30 fixed-point)
+ const int32_t lz = gglClz(x);
+ return (0xC0000000 - (x << (lz - 1))) >> (30-lz);
+}
+
+// ------------------------------------------------------------------------
+
+inline GGLfixed gglClampx(GGLfixed c) CONST;
+inline GGLfixed gglClampx(GGLfixed c)
+{
+#if defined(__thumb__)
+ // clamp without branches
+ c &= ~(c>>31); c = FIXED_ONE - c;
+ c &= ~(c>>31); c = FIXED_ONE - c;
+#else
+#if defined(__arm__)
+ // I don't know why gcc thinks its smarter than me! The code below
+ // clamps to zero in one instruction, but gcc won't generate it and
+ // replace it by a cmp + movlt (it's quite amazing actually).
+ asm("bic %0, %1, %1, asr #31\n" : "=r"(c) : "r"(c));
+#elif defined(__aarch64__)
+ asm("bic %w0, %w1, %w1, asr #31\n" : "=r"(c) : "r"(c));
+#else
+ c &= ~(c>>31);
+#endif
+ if (c>FIXED_ONE)
+ c = FIXED_ONE;
+#endif
+ return c;
+}
+
+// ------------------------------------------------------------------------
+
+#endif // ANDROID_GGL_FIXED_H
diff --git a/libpixelflinger/picker.cpp b/libpixelflinger/picker.cpp
new file mode 100644
index 0000000..aa55229
--- /dev/null
+++ b/libpixelflinger/picker.cpp
@@ -0,0 +1,173 @@
+/* libs/pixelflinger/picker.cpp
+**
+** Copyright 2006, 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 "buffer.h"
+#include "scanline.h"
+#include "picker.h"
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+void ggl_init_picker(context_t* /*c*/)
+{
+}
+
+void ggl_pick(context_t* c)
+{
+ if (ggl_likely(!c->dirty))
+ return;
+
+ // compute needs, see if they changed...
+ const uint32_t enables = c->state.enables;
+ needs_t new_needs(c->state.needs);
+
+ if (c->dirty & GGL_CB_STATE) {
+ new_needs.n &= ~GGL_NEEDS_CB_FORMAT_MASK;
+ new_needs.n |= GGL_BUILD_NEEDS(c->state.buffers.color.format, CB_FORMAT);
+ if (enables & GGL_ENABLE_BLENDING)
+ c->dirty |= GGL_PIXEL_PIPELINE_STATE;
+ }
+
+ if (c->dirty & GGL_PIXEL_PIPELINE_STATE) {
+ uint32_t n = GGL_BUILD_NEEDS(c->state.buffers.color.format, CB_FORMAT);
+ uint32_t p = 0;
+ if (enables & GGL_ENABLE_BLENDING) {
+ uint32_t src = c->state.blend.src;
+ uint32_t dst = c->state.blend.dst;
+ uint32_t src_alpha = c->state.blend.src_alpha;
+ uint32_t dst_alpha = c->state.blend.dst_alpha;
+ const GGLFormat& cbf = c->formats[ c->state.buffers.color.format ];
+ if (!cbf.c[GGLFormat::ALPHA].h) {
+ if ((src == GGL_ONE_MINUS_DST_ALPHA) ||
+ (src == GGL_DST_ALPHA)) {
+ src = GGL_ONE;
+ }
+ if ((src_alpha == GGL_ONE_MINUS_DST_ALPHA) ||
+ (src_alpha == GGL_DST_ALPHA)) {
+ src_alpha = GGL_ONE;
+ }
+ if ((dst == GGL_ONE_MINUS_DST_ALPHA) ||
+ (dst == GGL_DST_ALPHA)) {
+ dst = GGL_ONE;
+ }
+ if ((dst_alpha == GGL_ONE_MINUS_DST_ALPHA) ||
+ (dst_alpha == GGL_DST_ALPHA)) {
+ dst_alpha = GGL_ONE;
+ }
+ }
+
+ src = ggl_blendfactor_to_needs(src);
+ dst = ggl_blendfactor_to_needs(dst);
+ src_alpha = ggl_blendfactor_to_needs(src_alpha);
+ dst_alpha = ggl_blendfactor_to_needs(dst_alpha);
+
+ n |= GGL_BUILD_NEEDS( src, BLEND_SRC );
+ n |= GGL_BUILD_NEEDS( dst, BLEND_DST );
+ if (c->state.blend.alpha_separate) {
+ n |= GGL_BUILD_NEEDS( src_alpha, BLEND_SRCA );
+ n |= GGL_BUILD_NEEDS( dst_alpha, BLEND_DSTA );
+ } else {
+ n |= GGL_BUILD_NEEDS( src, BLEND_SRCA );
+ n |= GGL_BUILD_NEEDS( dst, BLEND_DSTA );
+ }
+ } else {
+ n |= GGL_BUILD_NEEDS( GGL_ONE, BLEND_SRC );
+ n |= GGL_BUILD_NEEDS( GGL_ZERO, BLEND_DST );
+ n |= GGL_BUILD_NEEDS( GGL_ONE, BLEND_SRCA );
+ n |= GGL_BUILD_NEEDS( GGL_ZERO, BLEND_DSTA );
+ }
+
+
+ n |= GGL_BUILD_NEEDS(c->state.mask.color^0xF, MASK_ARGB);
+ n |= GGL_BUILD_NEEDS((enables & GGL_ENABLE_SMOOTH) ?1:0, SHADE);
+ if (enables & GGL_ENABLE_TMUS) {
+ n |= GGL_BUILD_NEEDS((enables & GGL_ENABLE_W) ?1:0, W);
+ }
+ p |= GGL_BUILD_NEEDS((enables & GGL_ENABLE_DITHER) ?1:0, P_DITHER);
+ p |= GGL_BUILD_NEEDS((enables & GGL_ENABLE_AA) ?1:0, P_AA);
+ p |= GGL_BUILD_NEEDS((enables & GGL_ENABLE_FOG) ?1:0, P_FOG);
+
+ if (enables & GGL_ENABLE_LOGIC_OP) {
+ n |= GGL_BUILD_NEEDS(c->state.logic_op.opcode, LOGIC_OP);
+ } else {
+ n |= GGL_BUILD_NEEDS(GGL_COPY, LOGIC_OP);
+ }
+
+ if (enables & GGL_ENABLE_ALPHA_TEST) {
+ p |= GGL_BUILD_NEEDS(c->state.alpha_test.func, P_ALPHA_TEST);
+ } else {
+ p |= GGL_BUILD_NEEDS(GGL_ALWAYS, P_ALPHA_TEST);
+ }
+
+ if (enables & GGL_ENABLE_DEPTH_TEST) {
+ p |= GGL_BUILD_NEEDS(c->state.depth_test.func, P_DEPTH_TEST);
+ p |= GGL_BUILD_NEEDS(c->state.mask.depth&1, P_MASK_Z);
+ } else {
+ p |= GGL_BUILD_NEEDS(GGL_ALWAYS, P_DEPTH_TEST);
+ // writing to the z-buffer is always disabled if depth-test
+ // is disabled.
+ }
+ new_needs.n = n;
+ new_needs.p = p;
+ }
+
+ if (c->dirty & GGL_TMU_STATE) {
+ int idx = 0;
+ for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; ++i) {
+ const texture_t& tx = c->state.texture[i];
+ if (tx.enable) {
+ uint32_t t = 0;
+ t |= GGL_BUILD_NEEDS(tx.surface.format, T_FORMAT);
+ t |= GGL_BUILD_NEEDS(ggl_env_to_needs(tx.env), T_ENV);
+ t |= GGL_BUILD_NEEDS(0, T_POT); // XXX: not used yet
+ if (tx.s_coord==GGL_ONE_TO_ONE && tx.t_coord==GGL_ONE_TO_ONE) {
+ // we encode 1-to-1 into the wrap mode
+ t |= GGL_BUILD_NEEDS(GGL_NEEDS_WRAP_11, T_S_WRAP);
+ t |= GGL_BUILD_NEEDS(GGL_NEEDS_WRAP_11, T_T_WRAP);
+ } else {
+ t |= GGL_BUILD_NEEDS(ggl_wrap_to_needs(tx.s_wrap), T_S_WRAP);
+ t |= GGL_BUILD_NEEDS(ggl_wrap_to_needs(tx.t_wrap), T_T_WRAP);
+ }
+ if (tx.mag_filter == GGL_LINEAR) {
+ t |= GGL_BUILD_NEEDS(1, T_LINEAR);
+ }
+ if (tx.min_filter == GGL_LINEAR) {
+ t |= GGL_BUILD_NEEDS(1, T_LINEAR);
+ }
+ new_needs.t[idx++] = t;
+ } else {
+ new_needs.t[i] = 0;
+ }
+ }
+ }
+
+ if (new_needs != c->state.needs) {
+ c->state.needs = new_needs;
+ ggl_pick_texture(c);
+ ggl_pick_cb(c);
+ ggl_pick_scanline(c);
+ }
+ c->dirty = 0;
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
diff --git a/libpixelflinger/picker.h b/libpixelflinger/picker.h
new file mode 100644
index 0000000..9cdbc3c
--- /dev/null
+++ b/libpixelflinger/picker.h
@@ -0,0 +1,31 @@
+/* libs/pixelflinger/picker.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+
+#ifndef ANDROID_PICKER_H
+#define ANDROID_PICKER_H
+
+#include <private/pixelflinger/ggl_context.h>
+
+namespace android {
+
+void ggl_init_picker(context_t* c);
+void ggl_pick(context_t* c);
+
+}; // namespace android
+
+#endif
diff --git a/libpixelflinger/pixelflinger.cpp b/libpixelflinger/pixelflinger.cpp
new file mode 100644
index 0000000..fd449b2
--- /dev/null
+++ b/libpixelflinger/pixelflinger.cpp
@@ -0,0 +1,836 @@
+/* libs/pixelflinger/pixelflinger.cpp
+**
+** Copyright 2006, 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>
+#include <assert.h>
+
+#include <sys/time.h>
+
+#include <pixelflinger/pixelflinger.h>
+#include <private/pixelflinger/ggl_context.h>
+
+#include "buffer.h"
+#include "clear.h"
+#include "picker.h"
+#include "raster.h"
+#include "scanline.h"
+#include "trap.h"
+
+#include "codeflinger/GGLAssembler.h"
+#include "codeflinger/CodeCache.h"
+
+#include <stdio.h>
+
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+// 8x8 Bayer dither matrix
+static const uint8_t gDitherMatrix[GGL_DITHER_SIZE] = {
+ 0, 32, 8, 40, 2, 34, 10, 42,
+ 48, 16, 56, 24, 50, 18, 58, 26,
+ 12, 44, 4, 36, 14, 46, 6, 38,
+ 60, 28, 52, 20, 62, 30, 54, 22,
+ 3, 35, 11, 43, 1, 33, 9, 41,
+ 51, 19, 59, 27, 49, 17, 57, 25,
+ 15, 47, 7, 39, 13, 45, 5, 37,
+ 63, 31, 55, 23, 61, 29, 53, 21
+};
+
+static void ggl_init_procs(context_t* c);
+static void ggl_set_scissor(context_t* c);
+
+static void ggl_enable_blending(context_t* c, int enable);
+static void ggl_enable_scissor_test(context_t* c, int enable);
+static void ggl_enable_alpha_test(context_t* c, int enable);
+static void ggl_enable_logic_op(context_t* c, int enable);
+static void ggl_enable_dither(context_t* c, int enable);
+static void ggl_enable_stencil_test(context_t* c, int enable);
+static void ggl_enable_depth_test(context_t* c, int enable);
+static void ggl_enable_aa(context_t* c, int enable);
+static void ggl_enable_point_aa_nice(context_t* c, int enable);
+static void ggl_enable_texture2d(context_t* c, int enable);
+static void ggl_enable_w_lerp(context_t* c, int enable);
+static void ggl_enable_fog(context_t* c, int enable);
+
+static inline int min(int a, int b) CONST;
+static inline int min(int a, int b) {
+ return a < b ? a : b;
+}
+
+static inline int max(int a, int b) CONST;
+static inline int max(int a, int b) {
+ return a < b ? b : a;
+}
+
+// ----------------------------------------------------------------------------
+
+void ggl_error(context_t* c, GGLenum error)
+{
+ if (c->error == GGL_NO_ERROR)
+ c->error = error;
+}
+
+// ----------------------------------------------------------------------------
+
+static void ggl_bindTexture(void* con, const GGLSurface* surface)
+{
+ GGL_CONTEXT(c, con);
+ if (surface->format != c->activeTMU->surface.format)
+ ggl_state_changed(c, GGL_TMU_STATE);
+ ggl_set_surface(c, &(c->activeTMU->surface), surface);
+}
+
+
+static void ggl_bindTextureLod(void* con, GGLuint tmu,const GGLSurface* surface)
+{
+ GGL_CONTEXT(c, con);
+ // All LODs must have the same format
+ ggl_set_surface(c, &c->state.texture[tmu].surface, surface);
+}
+
+static void ggl_colorBuffer(void* con, const GGLSurface* surface)
+{
+ GGL_CONTEXT(c, con);
+ if (surface->format != c->state.buffers.color.format)
+ ggl_state_changed(c, GGL_CB_STATE);
+
+ if (surface->width > c->state.buffers.coverageBufferSize) {
+ // allocate the coverage factor buffer
+ free(c->state.buffers.coverage);
+ c->state.buffers.coverage = (int16_t*)malloc(surface->width * 2);
+ c->state.buffers.coverageBufferSize =
+ c->state.buffers.coverage ? surface->width : 0;
+ }
+ ggl_set_surface(c, &(c->state.buffers.color), surface);
+ if (c->state.buffers.read.format == 0) {
+ ggl_set_surface(c, &(c->state.buffers.read), surface);
+ }
+ ggl_set_scissor(c);
+}
+
+static void ggl_readBuffer(void* con, const GGLSurface* surface)
+{
+ GGL_CONTEXT(c, con);
+ ggl_set_surface(c, &(c->state.buffers.read), surface);
+}
+
+static void ggl_depthBuffer(void* con, const GGLSurface* surface)
+{
+ GGL_CONTEXT(c, con);
+ if (surface->format == GGL_PIXEL_FORMAT_Z_16) {
+ ggl_set_surface(c, &(c->state.buffers.depth), surface);
+ } else {
+ c->state.buffers.depth.format = GGL_PIXEL_FORMAT_NONE;
+ ggl_enable_depth_test(c, 0);
+ }
+}
+
+static void ggl_scissor(void* con, GGLint x, GGLint y,
+ GGLsizei width, GGLsizei height)
+{
+ GGL_CONTEXT(c, con);
+ c->state.scissor.user_left = x;
+ c->state.scissor.user_top = y;
+ c->state.scissor.user_right = x + width;
+ c->state.scissor.user_bottom = y + height;
+ ggl_set_scissor(c);
+}
+
+// ----------------------------------------------------------------------------
+
+static void enable_disable(context_t* c, GGLenum name, int en)
+{
+ switch (name) {
+ case GGL_BLEND: ggl_enable_blending(c, en); break;
+ case GGL_SCISSOR_TEST: ggl_enable_scissor_test(c, en); break;
+ case GGL_ALPHA_TEST: ggl_enable_alpha_test(c, en); break;
+ case GGL_COLOR_LOGIC_OP: ggl_enable_logic_op(c, en); break;
+ case GGL_DITHER: ggl_enable_dither(c, en); break;
+ case GGL_STENCIL_TEST: ggl_enable_stencil_test(c, en); break;
+ case GGL_DEPTH_TEST: ggl_enable_depth_test(c, en); break;
+ case GGL_AA: ggl_enable_aa(c, en); break;
+ case GGL_TEXTURE_2D: ggl_enable_texture2d(c, en); break;
+ case GGL_W_LERP: ggl_enable_w_lerp(c, en); break;
+ case GGL_FOG: ggl_enable_fog(c, en); break;
+ case GGL_POINT_SMOOTH_NICE: ggl_enable_point_aa_nice(c, en); break;
+ }
+}
+
+static void ggl_enable(void* con, GGLenum name)
+{
+ GGL_CONTEXT(c, con);
+ enable_disable(c, name, 1);
+}
+
+static void ggl_disable(void* con, GGLenum name)
+{
+ GGL_CONTEXT(c, con);
+ enable_disable(c, name, 0);
+}
+
+static void ggl_enableDisable(void* con, GGLenum name, GGLboolean en)
+{
+ GGL_CONTEXT(c, con);
+ enable_disable(c, name, en ? 1 : 0);
+}
+
+// ----------------------------------------------------------------------------
+
+static void ggl_shadeModel(void* con, GGLenum mode)
+{
+ GGL_CONTEXT(c, con);
+ switch (mode) {
+ case GGL_FLAT:
+ if (c->state.enables & GGL_ENABLE_SMOOTH) {
+ c->state.enables &= ~GGL_ENABLE_SMOOTH;
+ ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE);
+ }
+ break;
+ case GGL_SMOOTH:
+ if (!(c->state.enables & GGL_ENABLE_SMOOTH)) {
+ c->state.enables |= GGL_ENABLE_SMOOTH;
+ ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE);
+ }
+ break;
+ default:
+ ggl_error(c, GGL_INVALID_ENUM);
+ }
+}
+
+static void ggl_color4xv(void* con, const GGLclampx* color)
+{
+ GGL_CONTEXT(c, con);
+ c->shade.r0 = gglFixedToIteratedColor(color[0]);
+ c->shade.g0 = gglFixedToIteratedColor(color[1]);
+ c->shade.b0 = gglFixedToIteratedColor(color[2]);
+ c->shade.a0 = gglFixedToIteratedColor(color[3]);
+}
+
+static void ggl_colorGrad12xv(void* con, const GGLcolor* grad)
+{
+ GGL_CONTEXT(c, con);
+ // it is very important to round the iterated value here because
+ // the rasterizer doesn't clamp them, therefore the iterated value
+ //must absolutely be correct.
+ // GGLColor is encoded as 8.16 value
+ const int32_t round = 0x8000;
+ c->shade.r0 = grad[ 0] + round;
+ c->shade.drdx = grad[ 1];
+ c->shade.drdy = grad[ 2];
+ c->shade.g0 = grad[ 3] + round;
+ c->shade.dgdx = grad[ 4];
+ c->shade.dgdy = grad[ 5];
+ c->shade.b0 = grad[ 6] + round;
+ c->shade.dbdx = grad[ 7];
+ c->shade.dbdy = grad[ 8];
+ c->shade.a0 = grad[ 9] + round;
+ c->shade.dadx = grad[10];
+ c->shade.dady = grad[11];
+}
+
+static void ggl_zGrad3xv(void* con, const GGLfixed32* grad)
+{
+ GGL_CONTEXT(c, con);
+ // z iterators are encoded as 0.32 fixed point and the z-buffer
+ // holds 16 bits, the rounding value is 0x8000.
+ const uint32_t round = 0x8000;
+ c->shade.z0 = grad[0] + round;
+ c->shade.dzdx = grad[1];
+ c->shade.dzdy = grad[2];
+}
+
+static void ggl_wGrad3xv(void* con, const GGLfixed* grad)
+{
+ GGL_CONTEXT(c, con);
+ c->shade.w0 = grad[0];
+ c->shade.dwdx = grad[1];
+ c->shade.dwdy = grad[2];
+}
+
+// ----------------------------------------------------------------------------
+
+static void ggl_fogGrad3xv(void* con, const GGLfixed* grad)
+{
+ GGL_CONTEXT(c, con);
+ c->shade.f0 = grad[0];
+ c->shade.dfdx = grad[1];
+ c->shade.dfdy = grad[2];
+}
+
+static void ggl_fogColor3xv(void* con, const GGLclampx* color)
+{
+ GGL_CONTEXT(c, con);
+ const int32_t r = gglClampx(color[0]);
+ const int32_t g = gglClampx(color[1]);
+ const int32_t b = gglClampx(color[2]);
+ c->state.fog.color[GGLFormat::ALPHA]= 0xFF; // unused
+ c->state.fog.color[GGLFormat::RED] = (r - (r>>8))>>8;
+ c->state.fog.color[GGLFormat::GREEN]= (g - (g>>8))>>8;
+ c->state.fog.color[GGLFormat::BLUE] = (b - (b>>8))>>8;
+}
+
+static void ggl_enable_fog(context_t* c, int enable)
+{
+ const int e = (c->state.enables & GGL_ENABLE_FOG)?1:0;
+ if (e != enable) {
+ if (enable) c->state.enables |= GGL_ENABLE_FOG;
+ else c->state.enables &= ~GGL_ENABLE_FOG;
+ ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE);
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+static void ggl_blendFunc(void* con, GGLenum src, GGLenum dst)
+{
+ GGL_CONTEXT(c, con);
+ c->state.blend.src = src;
+ c->state.blend.src_alpha = src;
+ c->state.blend.dst = dst;
+ c->state.blend.dst_alpha = dst;
+ c->state.blend.alpha_separate = 0;
+ if (c->state.enables & GGL_ENABLE_BLENDING) {
+ ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE);
+ }
+}
+
+static void ggl_blendFuncSeparate(void* con,
+ GGLenum src, GGLenum dst,
+ GGLenum srcAlpha, GGLenum dstAplha)
+{
+ GGL_CONTEXT(c, con);
+ c->state.blend.src = src;
+ c->state.blend.src_alpha = srcAlpha;
+ c->state.blend.dst = dst;
+ c->state.blend.dst_alpha = dstAplha;
+ c->state.blend.alpha_separate = 1;
+ if (c->state.enables & GGL_ENABLE_BLENDING) {
+ ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE);
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+static void ggl_texEnvi(void* con, GGLenum target,
+ GGLenum pname,
+ GGLint param)
+{
+ GGL_CONTEXT(c, con);
+ if (target != GGL_TEXTURE_ENV || pname != GGL_TEXTURE_ENV_MODE) {
+ ggl_error(c, GGL_INVALID_ENUM);
+ return;
+ }
+ switch (param) {
+ case GGL_REPLACE:
+ case GGL_MODULATE:
+ case GGL_DECAL:
+ case GGL_BLEND:
+ case GGL_ADD:
+ if (c->activeTMU->env != param) {
+ c->activeTMU->env = param;
+ ggl_state_changed(c, GGL_TMU_STATE);
+ }
+ break;
+ default:
+ ggl_error(c, GGL_INVALID_ENUM);
+ }
+}
+
+static void ggl_texEnvxv(void* con, GGLenum target,
+ GGLenum pname, const GGLfixed* params)
+{
+ GGL_CONTEXT(c, con);
+ if (target != GGL_TEXTURE_ENV) {
+ ggl_error(c, GGL_INVALID_ENUM);
+ return;
+ }
+ switch (pname) {
+ case GGL_TEXTURE_ENV_MODE:
+ ggl_texEnvi(con, target, pname, params[0]);
+ break;
+ case GGL_TEXTURE_ENV_COLOR: {
+ uint8_t* const color = c->activeTMU->env_color;
+ const GGLclampx r = gglClampx(params[0]);
+ const GGLclampx g = gglClampx(params[1]);
+ const GGLclampx b = gglClampx(params[2]);
+ const GGLclampx a = gglClampx(params[3]);
+ color[0] = (a-(a>>8))>>8;
+ color[1] = (r-(r>>8))>>8;
+ color[2] = (g-(g>>8))>>8;
+ color[3] = (b-(b>>8))>>8;
+ break;
+ }
+ default:
+ ggl_error(c, GGL_INVALID_ENUM);
+ return;
+ }
+}
+
+
+static void ggl_texParameteri(void* con,
+ GGLenum target,
+ GGLenum pname,
+ GGLint param)
+{
+ GGL_CONTEXT(c, con);
+ if (target != GGL_TEXTURE_2D) {
+ ggl_error(c, GGL_INVALID_ENUM);
+ return;
+ }
+
+ if (param == GGL_CLAMP_TO_EDGE)
+ param = GGL_CLAMP;
+
+ uint16_t* what = 0;
+ switch (pname) {
+ case GGL_TEXTURE_WRAP_S:
+ if ((param == GGL_CLAMP) ||
+ (param == GGL_REPEAT)) {
+ what = &c->activeTMU->s_wrap;
+ }
+ break;
+ case GGL_TEXTURE_WRAP_T:
+ if ((param == GGL_CLAMP) ||
+ (param == GGL_REPEAT)) {
+ what = &c->activeTMU->t_wrap;
+ }
+ break;
+ case GGL_TEXTURE_MIN_FILTER:
+ if ((param == GGL_NEAREST) ||
+ (param == GGL_NEAREST_MIPMAP_NEAREST) ||
+ (param == GGL_NEAREST_MIPMAP_LINEAR)) {
+ what = &c->activeTMU->min_filter;
+ param = GGL_NEAREST;
+ }
+ if ((param == GGL_LINEAR) ||
+ (param == GGL_LINEAR_MIPMAP_NEAREST) ||
+ (param == GGL_LINEAR_MIPMAP_LINEAR)) {
+ what = &c->activeTMU->min_filter;
+ param = GGL_LINEAR;
+ }
+ break;
+ case GGL_TEXTURE_MAG_FILTER:
+ if ((param == GGL_NEAREST) ||
+ (param == GGL_LINEAR)) {
+ what = &c->activeTMU->mag_filter;
+ }
+ break;
+ }
+
+ if (!what) {
+ ggl_error(c, GGL_INVALID_ENUM);
+ return;
+ }
+
+ if (*what != param) {
+ *what = param;
+ ggl_state_changed(c, GGL_TMU_STATE);
+ }
+}
+
+static void ggl_texCoordGradScale8xv(void* con, GGLint tmu, const int32_t* grad)
+{
+ GGL_CONTEXT(c, con);
+ texture_t& u = c->state.texture[tmu];
+ u.shade.is0 = grad[0];
+ u.shade.idsdx = grad[1];
+ u.shade.idsdy = grad[2];
+ u.shade.it0 = grad[3];
+ u.shade.idtdx = grad[4];
+ u.shade.idtdy = grad[5];
+ u.shade.sscale= grad[6];
+ u.shade.tscale= grad[7];
+}
+
+static void ggl_texCoord2x(void* con, GGLfixed s, GGLfixed t)
+{
+ GGL_CONTEXT(c, con);
+ c->activeTMU->shade.is0 = s;
+ c->activeTMU->shade.it0 = t;
+ c->activeTMU->shade.sscale= 0;
+ c->activeTMU->shade.tscale= 0;
+}
+
+static void ggl_texCoord2i(void* con, GGLint s, GGLint t)
+{
+ ggl_texCoord2x(con, s<<16, t<<16);
+}
+
+static void ggl_texGeni(void* con, GGLenum coord, GGLenum pname, GGLint param)
+{
+ GGL_CONTEXT(c, con);
+ if (pname != GGL_TEXTURE_GEN_MODE) {
+ ggl_error(c, GGL_INVALID_ENUM);
+ return;
+ }
+
+ uint32_t* coord_ptr = 0;
+ if (coord == GGL_S) coord_ptr = &(c->activeTMU->s_coord);
+ else if (coord == GGL_T) coord_ptr = &(c->activeTMU->t_coord);
+
+ if (coord_ptr) {
+ if (*coord_ptr != uint32_t(param)) {
+ *coord_ptr = uint32_t(param);
+ ggl_state_changed(c, GGL_TMU_STATE);
+ }
+ } else {
+ ggl_error(c, GGL_INVALID_ENUM);
+ }
+}
+
+static void ggl_activeTexture(void* con, GGLuint tmu)
+{
+ GGL_CONTEXT(c, con);
+ if (tmu >= GGLuint(GGL_TEXTURE_UNIT_COUNT)) {
+ ggl_error(c, GGL_INVALID_ENUM);
+ return;
+ }
+ c->activeTMUIndex = tmu;
+ c->activeTMU = &(c->state.texture[tmu]);
+}
+
+// ----------------------------------------------------------------------------
+
+static void ggl_colorMask(void* con, GGLboolean r,
+ GGLboolean g,
+ GGLboolean b,
+ GGLboolean a)
+{
+ GGL_CONTEXT(c, con);
+ int mask = 0;
+ if (a) mask |= 1 << GGLFormat::ALPHA;
+ if (r) mask |= 1 << GGLFormat::RED;
+ if (g) mask |= 1 << GGLFormat::GREEN;
+ if (b) mask |= 1 << GGLFormat::BLUE;
+ if (c->state.mask.color != mask) {
+ c->state.mask.color = mask;
+ ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE);
+ }
+}
+
+static void ggl_depthMask(void* con, GGLboolean flag)
+{
+ GGL_CONTEXT(c, con);
+ if (c->state.mask.depth != flag?1:0) {
+ c->state.mask.depth = flag?1:0;
+ ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE);
+ }
+}
+
+static void ggl_stencilMask(void* con, GGLuint mask)
+{
+ GGL_CONTEXT(c, con);
+ if (c->state.mask.stencil != mask) {
+ c->state.mask.stencil = mask;
+ ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE);
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+static void ggl_alphaFuncx(void* con, GGLenum func, GGLclampx ref)
+{
+ GGL_CONTEXT(c, con);
+ if ((func < GGL_NEVER) || (func > GGL_ALWAYS)) {
+ ggl_error(c, GGL_INVALID_ENUM);
+ return;
+ }
+ c->state.alpha_test.ref = gglFixedToIteratedColor(gglClampx(ref));
+ if (c->state.alpha_test.func != func) {
+ c->state.alpha_test.func = func;
+ ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE);
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+static void ggl_depthFunc(void* con, GGLenum func)
+{
+ GGL_CONTEXT(c, con);
+ if ((func < GGL_NEVER) || (func > GGL_ALWAYS)) {
+ ggl_error(c, GGL_INVALID_ENUM);
+ return;
+ }
+ if (c->state.depth_test.func != func) {
+ c->state.depth_test.func = func;
+ ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE);
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+static void ggl_logicOp(void* con, GGLenum opcode)
+{
+ GGL_CONTEXT(c, con);
+ if ((opcode < GGL_CLEAR) || (opcode > GGL_SET)) {
+ ggl_error(c, GGL_INVALID_ENUM);
+ return;
+ }
+ if (c->state.logic_op.opcode != opcode) {
+ c->state.logic_op.opcode = opcode;
+ ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE);
+ }
+}
+
+
+// ----------------------------------------------------------------------------
+
+void ggl_set_scissor(context_t* c)
+{
+ if (c->state.enables & GGL_ENABLE_SCISSOR_TEST) {
+ const int32_t l = c->state.scissor.user_left;
+ const int32_t t = c->state.scissor.user_top;
+ const int32_t r = c->state.scissor.user_right;
+ const int32_t b = c->state.scissor.user_bottom;
+ c->state.scissor.left = max(0, l);
+ c->state.scissor.right = min(c->state.buffers.color.width, r);
+ c->state.scissor.top = max(0, t);
+ c->state.scissor.bottom = min(c->state.buffers.color.height, b);
+ } else {
+ c->state.scissor.left = 0;
+ c->state.scissor.top = 0;
+ c->state.scissor.right = c->state.buffers.color.width;
+ c->state.scissor.bottom = c->state.buffers.color.height;
+ }
+}
+
+void ggl_enable_blending(context_t* c, int enable)
+{
+ const int e = (c->state.enables & GGL_ENABLE_BLENDING)?1:0;
+ if (e != enable) {
+ if (enable) c->state.enables |= GGL_ENABLE_BLENDING;
+ else c->state.enables &= ~GGL_ENABLE_BLENDING;
+ ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE);
+ }
+}
+
+void ggl_enable_scissor_test(context_t* c, int enable)
+{
+ const int e = (c->state.enables & GGL_ENABLE_SCISSOR_TEST)?1:0;
+ if (e != enable) {
+ if (enable) c->state.enables |= GGL_ENABLE_SCISSOR_TEST;
+ else c->state.enables &= ~GGL_ENABLE_SCISSOR_TEST;
+ ggl_set_scissor(c);
+ }
+}
+
+void ggl_enable_alpha_test(context_t* c, int enable)
+{
+ const int e = (c->state.enables & GGL_ENABLE_ALPHA_TEST)?1:0;
+ if (e != enable) {
+ if (enable) c->state.enables |= GGL_ENABLE_ALPHA_TEST;
+ else c->state.enables &= ~GGL_ENABLE_ALPHA_TEST;
+ ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE);
+ }
+}
+
+void ggl_enable_logic_op(context_t* c, int enable)
+{
+ const int e = (c->state.enables & GGL_ENABLE_LOGIC_OP)?1:0;
+ if (e != enable) {
+ if (enable) c->state.enables |= GGL_ENABLE_LOGIC_OP;
+ else c->state.enables &= ~GGL_ENABLE_LOGIC_OP;
+ ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE);
+ }
+}
+
+void ggl_enable_dither(context_t* c, int enable)
+{
+ const int e = (c->state.enables & GGL_ENABLE_DITHER)?1:0;
+ if (e != enable) {
+ if (enable) c->state.enables |= GGL_ENABLE_DITHER;
+ else c->state.enables &= ~GGL_ENABLE_DITHER;
+ ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE);
+ }
+}
+
+void ggl_enable_stencil_test(context_t* /*c*/, int /*enable*/)
+{
+}
+
+void ggl_enable_depth_test(context_t* c, int enable)
+{
+ if (c->state.buffers.depth.format == 0)
+ enable = 0;
+ const int e = (c->state.enables & GGL_ENABLE_DEPTH_TEST)?1:0;
+ if (e != enable) {
+ if (enable) c->state.enables |= GGL_ENABLE_DEPTH_TEST;
+ else c->state.enables &= ~GGL_ENABLE_DEPTH_TEST;
+ ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE);
+ }
+}
+
+void ggl_enable_aa(context_t* c, int enable)
+{
+ const int e = (c->state.enables & GGL_ENABLE_AA)?1:0;
+ if (e != enable) {
+ if (enable) c->state.enables |= GGL_ENABLE_AA;
+ else c->state.enables &= ~GGL_ENABLE_AA;
+ ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE);
+ }
+}
+
+void ggl_enable_point_aa_nice(context_t* c, int enable)
+{
+ const int e = (c->state.enables & GGL_ENABLE_POINT_AA_NICE)?1:0;
+ if (e != enable) {
+ if (enable) c->state.enables |= GGL_ENABLE_POINT_AA_NICE;
+ else c->state.enables &= ~GGL_ENABLE_POINT_AA_NICE;
+ ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE);
+ }
+}
+
+void ggl_enable_w_lerp(context_t* c, int enable)
+{
+ const int e = (c->state.enables & GGL_ENABLE_W)?1:0;
+ if (e != enable) {
+ if (enable) c->state.enables |= GGL_ENABLE_W;
+ else c->state.enables &= ~GGL_ENABLE_W;
+ ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE);
+ }
+}
+
+void ggl_enable_texture2d(context_t* c, int enable)
+{
+ if (c->activeTMU->enable != enable) {
+ const uint32_t tmu = c->activeTMUIndex;
+ c->activeTMU->enable = enable;
+ const uint32_t mask = 1UL << tmu;
+ if (enable) c->state.enabled_tmu |= mask;
+ else c->state.enabled_tmu &= ~mask;
+ if (c->state.enabled_tmu) c->state.enables |= GGL_ENABLE_TMUS;
+ else c->state.enables &= ~GGL_ENABLE_TMUS;
+ ggl_state_changed(c, GGL_TMU_STATE);
+ }
+}
+
+
+// ----------------------------------------------------------------------------
+
+int64_t ggl_system_time()
+{
+ struct timespec t;
+ t.tv_sec = t.tv_nsec = 0;
+ clock_gettime(CLOCK_THREAD_CPUTIME_ID, &t);
+ return int64_t(t.tv_sec)*1000000000LL + t.tv_nsec;
+}
+
+// ----------------------------------------------------------------------------
+
+void ggl_init_procs(context_t* c)
+{
+ GGLContext& procs = *(GGLContext*)c;
+ GGL_INIT_PROC(procs, scissor);
+ GGL_INIT_PROC(procs, activeTexture);
+ GGL_INIT_PROC(procs, bindTexture);
+ GGL_INIT_PROC(procs, bindTextureLod);
+ GGL_INIT_PROC(procs, colorBuffer);
+ GGL_INIT_PROC(procs, readBuffer);
+ GGL_INIT_PROC(procs, depthBuffer);
+ GGL_INIT_PROC(procs, enable);
+ GGL_INIT_PROC(procs, disable);
+ GGL_INIT_PROC(procs, enableDisable);
+ GGL_INIT_PROC(procs, shadeModel);
+ GGL_INIT_PROC(procs, color4xv);
+ GGL_INIT_PROC(procs, colorGrad12xv);
+ GGL_INIT_PROC(procs, zGrad3xv);
+ GGL_INIT_PROC(procs, wGrad3xv);
+ GGL_INIT_PROC(procs, fogGrad3xv);
+ GGL_INIT_PROC(procs, fogColor3xv);
+ GGL_INIT_PROC(procs, blendFunc);
+ GGL_INIT_PROC(procs, blendFuncSeparate);
+ GGL_INIT_PROC(procs, texEnvi);
+ GGL_INIT_PROC(procs, texEnvxv);
+ GGL_INIT_PROC(procs, texParameteri);
+ GGL_INIT_PROC(procs, texCoord2i);
+ GGL_INIT_PROC(procs, texCoord2x);
+ GGL_INIT_PROC(procs, texCoordGradScale8xv);
+ GGL_INIT_PROC(procs, texGeni);
+ GGL_INIT_PROC(procs, colorMask);
+ GGL_INIT_PROC(procs, depthMask);
+ GGL_INIT_PROC(procs, stencilMask);
+ GGL_INIT_PROC(procs, alphaFuncx);
+ GGL_INIT_PROC(procs, depthFunc);
+ GGL_INIT_PROC(procs, logicOp);
+ ggl_init_clear(c);
+}
+
+void ggl_init_context(context_t* c)
+{
+ memset(c, 0, sizeof(context_t));
+ ggl_init_procs(c);
+ ggl_init_trap(c);
+ ggl_init_scanline(c);
+ ggl_init_texture(c);
+ ggl_init_picker(c);
+ ggl_init_raster(c);
+ c->formats = gglGetPixelFormatTable();
+ c->state.blend.src = GGL_ONE;
+ c->state.blend.dst = GGL_ZERO;
+ c->state.blend.src_alpha = GGL_ONE;
+ c->state.blend.dst_alpha = GGL_ZERO;
+ c->state.mask.color = 0xF;
+ c->state.mask.depth = 0;
+ c->state.mask.stencil = 0xFFFFFFFF;
+ c->state.logic_op.opcode = GGL_COPY;
+ c->state.alpha_test.func = GGL_ALWAYS;
+ c->state.depth_test.func = GGL_LESS;
+ c->state.depth_test.clearValue = FIXED_ONE;
+ c->shade.w0 = FIXED_ONE;
+ memcpy(c->ditherMatrix, gDitherMatrix, sizeof(gDitherMatrix));
+}
+
+void ggl_uninit_context(context_t* c)
+{
+ ggl_uninit_scanline(c);
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+// ----------------------------------------------------------------------------
+
+
+
+using namespace android;
+
+ssize_t gglInit(GGLContext** context)
+{
+ void* const base = malloc(sizeof(context_t) + 32);
+ if (base) {
+ // always align the context on cache lines
+ context_t *c = (context_t *)((ptrdiff_t(base)+31) & ~0x1FL);
+ ggl_init_context(c);
+ c->base = base;
+ *context = (GGLContext*)c;
+ } else {
+ return -1;
+ }
+ return 0;
+}
+
+ssize_t gglUninit(GGLContext* con)
+{
+ GGL_CONTEXT(c, (void*)con);
+ ggl_uninit_context(c);
+ free(c->base);
+ return 0;
+}
+
diff --git a/libpixelflinger/raster.cpp b/libpixelflinger/raster.cpp
new file mode 100644
index 0000000..26d8e45
--- /dev/null
+++ b/libpixelflinger/raster.cpp
@@ -0,0 +1,217 @@
+/* libs/pixelflinger/raster.cpp
+**
+** Copyright 2006, 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 <string.h>
+
+#include "raster.h"
+#include "trap.h"
+
+namespace android {
+
+static void ggl_rasterPos2x(void* con, GGLfixed x, GGLfixed y);
+static void ggl_rasterPos2i(void* con, GGLint x, GGLint y);
+static void ggl_copyPixels(void* con, GGLint xs, GGLint ys,
+ GGLsizei width, GGLsizei height, GGLenum type);
+
+void ggl_init_raster(context_t* c)
+{
+ GGLContext& procs = *(GGLContext*)c;
+ GGL_INIT_PROC(procs, copyPixels);
+ GGL_INIT_PROC(procs, rasterPos2x);
+ GGL_INIT_PROC(procs, rasterPos2i);
+}
+
+void ggl_rasterPos2x(void* con, GGLfixed x, GGLfixed y)
+{
+ GGL_CONTEXT(c, con);
+ // raster pos should be processed just like glVertex
+ c->state.raster.x = x;
+ c->state.raster.y = y;
+}
+
+void ggl_rasterPos2i(void* con, GGLint x, GGLint y)
+{
+ ggl_rasterPos2x(con, gglIntToFixed(x), gglIntToFixed(y));
+}
+
+void ggl_copyPixels(void* con, GGLint xs, GGLint ys,
+ GGLsizei width, GGLsizei height, GGLenum /*type*/)
+{
+ GGL_CONTEXT(c, con);
+
+ // color-buffer
+ surface_t* cb = &(c->state.buffers.color);
+
+ // undefined behaviour if we try to copy from outside the surface
+ if (uint32_t(xs) > cb->width)
+ return;
+ if (uint32_t(ys) > cb->height)
+ return;
+ if (uint32_t(xs + width) > cb->width)
+ return;
+ if (uint32_t(ys + height) > cb->height)
+ return;
+
+ // copy to current raster position
+ GGLint xd = gglFixedToIntRound(c->state.raster.x);
+ GGLint yd = gglFixedToIntRound(c->state.raster.y);
+
+ // clip to scissor
+ if (xd < GGLint(c->state.scissor.left)) {
+ GGLint offset = GGLint(c->state.scissor.left) - xd;
+ xd = GGLint(c->state.scissor.left);
+ xs += offset;
+ width -= offset;
+ }
+ if (yd < GGLint(c->state.scissor.top)) {
+ GGLint offset = GGLint(c->state.scissor.top) - yd;
+ yd = GGLint(c->state.scissor.top);
+ ys += offset;
+ height -= offset;
+ }
+ if ((xd + width) > GGLint(c->state.scissor.right)) {
+ width = GGLint(c->state.scissor.right) - xd;
+ }
+ if ((yd + height) > GGLint(c->state.scissor.bottom)) {
+ height = GGLint(c->state.scissor.bottom) - yd;
+ }
+
+ if (width<=0 || height<=0) {
+ return; // nothing to copy
+ }
+
+ if (xs==xd && ys==yd) {
+ // nothing to do, but be careful, this might not be true when we support
+ // gglPixelTransfer, gglPixelMap and gglPixelZoom
+ return;
+ }
+
+ const GGLFormat* fp = &(c->formats[cb->format]);
+ uint8_t* src = reinterpret_cast<uint8_t*>(cb->data)
+ + (xs + (cb->stride * ys)) * fp->size;
+ uint8_t* dst = reinterpret_cast<uint8_t*>(cb->data)
+ + (xd + (cb->stride * yd)) * fp->size;
+ const size_t bpr = cb->stride * fp->size;
+ const size_t rowsize = width * fp->size;
+ size_t yc = height;
+
+ if (ys < yd) {
+ // bottom to top
+ src += height * bpr;
+ dst += height * bpr;
+ do {
+ dst -= bpr;
+ src -= bpr;
+ memcpy(dst, src, rowsize);
+ } while (--yc);
+ } else {
+ if (ys == yd) {
+ // might be right to left
+ do {
+ memmove(dst, src, rowsize);
+ dst += bpr;
+ src += bpr;
+ } while (--yc);
+ } else {
+ // top to bottom
+ do {
+ memcpy(dst, src, rowsize);
+ dst += bpr;
+ src += bpr;
+ } while (--yc);
+ }
+ }
+}
+
+}; // namespace android
+
+using namespace android;
+
+GGLint gglBitBlit(GGLContext* con, int tmu, GGLint crop[4], GGLint where[4])
+{
+ GGL_CONTEXT(c, (void*)con);
+
+ GGLint x = where[0];
+ GGLint y = where[1];
+ GGLint w = where[2];
+ GGLint h = where[3];
+
+ // exclsively enable this tmu
+ const GGLSurface& cbSurface = c->state.buffers.color.s;
+ c->procs.activeTexture(c, tmu);
+ c->procs.disable(c, GGL_W_LERP);
+
+ uint32_t tmus = 1UL<<tmu;
+ if (c->state.enabled_tmu != tmus) {
+ c->activeTMU->enable = 1;
+ c->state.enabled_tmu = tmus;
+ c->state.enables |= GGL_ENABLE_TMUS;
+ ggl_state_changed(c, GGL_TMU_STATE);
+ }
+
+ const GGLint Wcr = crop[2];
+ const GGLint Hcr = crop[3];
+ if ((w == Wcr) && (h == Hcr)) {
+ c->procs.texGeni(c, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
+ c->procs.texGeni(c, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
+ const GGLint Ucr = crop[0];
+ const GGLint Vcr = crop[1];
+ const GGLint s0 = Ucr - x;
+ const GGLint t0 = Vcr - y;
+ c->procs.texCoord2i(c, s0, t0);
+ c->procs.recti(c, x, y, x+w, y+h);
+ } else {
+ int32_t texcoords[8];
+ x = gglIntToFixed(x);
+ y = gglIntToFixed(y);
+
+ // we CLAMP here, which works with premultiplied (s,t)
+ c->procs.texParameteri(c, GGL_TEXTURE_2D, GGL_TEXTURE_WRAP_S, GGL_CLAMP);
+ c->procs.texParameteri(c, GGL_TEXTURE_2D, GGL_TEXTURE_WRAP_T, GGL_CLAMP);
+ c->procs.texGeni(c, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_AUTOMATIC);
+ c->procs.texGeni(c, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_AUTOMATIC);
+
+ const GGLint Ucr = crop[0] << 16;
+ const GGLint Vcr = crop[1] << 16;
+ const GGLint Wcr = crop[2] << 16;
+ const GGLint Hcr = crop[3] << 16;
+
+ // computes texture coordinates (pre-multiplied)
+ int32_t dsdx = Wcr / w; // dsdx = ((Wcr/w)/Wt)*Wt
+ int32_t dtdy = Hcr / h; // dtdy = ((Hcr/h)/Ht)*Ht
+ int32_t s0 = Ucr - gglMulx(dsdx, x); // s0 = Ucr - x * dsdx
+ int32_t t0 = Vcr - gglMulx(dtdy, y); // t0 = Vcr - y * dtdy
+ texcoords[0] = s0;
+ texcoords[1] = dsdx;
+ texcoords[2] = 0;
+ texcoords[3] = t0;
+ texcoords[4] = 0;
+ texcoords[5] = dtdy;
+ texcoords[6] = 0;
+ texcoords[7] = 0;
+ c->procs.texCoordGradScale8xv(c, tmu, texcoords);
+ c->procs.recti(c,
+ gglFixedToIntRound(x),
+ gglFixedToIntRound(y),
+ gglFixedToIntRound(x)+w,
+ gglFixedToIntRound(y)+h);
+ }
+ return 0;
+}
+
diff --git a/libpixelflinger/raster.h b/libpixelflinger/raster.h
new file mode 100644
index 0000000..9f0f240
--- /dev/null
+++ b/libpixelflinger/raster.h
@@ -0,0 +1,33 @@
+/* libs/pixelflinger/raster.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+
+#ifndef ANDROID_GGL_RASTER_H
+#define ANDROID_GGL_RASTER_H
+
+#include <private/pixelflinger/ggl_context.h>
+
+namespace android {
+
+void ggl_init_raster(context_t* c);
+
+void gglCopyPixels(void* c, GGLint x, GGLint y, GGLsizei width, GGLsizei height, GGLenum type);
+void gglRasterPos2d(void* c, GGLint x, GGLint y);
+
+}; // namespace android
+
+#endif // ANDROID_GGL_RASTER_H
diff --git a/libpixelflinger/rotate90CW_4x4_16v6.S b/libpixelflinger/rotate90CW_4x4_16v6.S
new file mode 100644
index 0000000..8e3e142
--- /dev/null
+++ b/libpixelflinger/rotate90CW_4x4_16v6.S
@@ -0,0 +1,62 @@
+/*
+**
+** Copyright 2006, 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.
+*/
+
+
+ .text
+ .align
+
+ .global rotate90CW_4x4_16v6
+
+// Rotates 90deg CW a 4x4 block of 16bpp pixels using ARMv6
+// src and dst must be 4 pixels-aligned (2-pixels aligned might
+// actually work)
+//
+// The code below is complicated by ARM's little endianness.
+
+rotate90CW_4x4_16v6:
+ // r0 = dst
+ // r1 = src
+ // r2 = dst stride in pixels
+ // r3 = src stride in pixels
+
+ stmfd sp!, {r4,r5, r6,r7, r8,r9, r10,r11, lr}
+ add r14, r3, r3
+ add r12, r2, r2
+
+ ldrd r2, r3, [r1], r14
+ ldrd r4, r5, [r1], r14
+ ldrd r6, r7, [r1], r14
+ ldrd r8, r9, [r1]
+
+ pkhbt r10, r8, r6, lsl #16
+ pkhbt r11, r4, r2, lsl #16
+ strd r10, r11, [r0], r12
+
+ pkhtb r10, r6, r8, asr #16
+ pkhtb r11, r2, r4, asr #16
+
+ strd r10, r11, [r0], r12
+ pkhbt r10, r9, r7, lsl #16
+ pkhbt r11, r5, r3, lsl #16
+
+ strd r10, r11, [r0], r12
+
+ pkhtb r10, r7, r9, asr #16
+ pkhtb r11, r3, r5, asr #16
+ strd r10, r11, [r0]
+
+ ldmfd sp!, {r4,r5, r6,r7, r8,r9, r10,r11, pc}
diff --git a/libpixelflinger/scanline.cpp b/libpixelflinger/scanline.cpp
new file mode 100644
index 0000000..3d14531
--- /dev/null
+++ b/libpixelflinger/scanline.cpp
@@ -0,0 +1,2367 @@
+/* libs/pixelflinger/scanline.cpp
+**
+** Copyright 2006-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.
+*/
+
+
+#define LOG_TAG "pixelflinger"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <cutils/memory.h>
+#include <cutils/log.h>
+
+#ifdef __arm__
+#include <machine/cpu-features.h>
+#endif
+
+#include "buffer.h"
+#include "scanline.h"
+
+#include "codeflinger/CodeCache.h"
+#include "codeflinger/GGLAssembler.h"
+#if defined(__arm__)
+#include "codeflinger/ARMAssembler.h"
+#elif defined(__aarch64__)
+#include "codeflinger/Arm64Assembler.h"
+#elif defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6
+#include "codeflinger/MIPSAssembler.h"
+#endif
+//#include "codeflinger/ARMAssemblerOptimizer.h"
+
+// ----------------------------------------------------------------------------
+
+#define ANDROID_CODEGEN_GENERIC 0 // force generic pixel pipeline
+#define ANDROID_CODEGEN_C 1 // hand-written C, fallback generic
+#define ANDROID_CODEGEN_ASM 2 // hand-written asm, fallback generic
+#define ANDROID_CODEGEN_GENERATED 3 // hand-written asm, fallback codegen
+
+#ifdef NDEBUG
+# define ANDROID_RELEASE
+# define ANDROID_CODEGEN ANDROID_CODEGEN_GENERATED
+#else
+# define ANDROID_DEBUG
+# define ANDROID_CODEGEN ANDROID_CODEGEN_GENERATED
+#endif
+
+#if defined(__arm__) || (defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6) || defined(__aarch64__)
+# define ANDROID_ARM_CODEGEN 1
+#else
+# define ANDROID_ARM_CODEGEN 0
+#endif
+
+#define DEBUG__CODEGEN_ONLY 0
+
+/* Set to 1 to dump to the log the states that need a new
+ * code-generated scanline callback, i.e. those that don't
+ * have a corresponding shortcut function.
+ */
+#define DEBUG_NEEDS 0
+
+#if defined( __mips__) && !defined(__LP64__) && __mips_isa_rev < 6
+#define ASSEMBLY_SCRATCH_SIZE 4096
+#elif defined(__aarch64__)
+#define ASSEMBLY_SCRATCH_SIZE 8192
+#else
+#define ASSEMBLY_SCRATCH_SIZE 2048
+#endif
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+static void init_y(context_t*, int32_t);
+static void init_y_noop(context_t*, int32_t);
+static void init_y_packed(context_t*, int32_t);
+static void init_y_error(context_t*, int32_t);
+
+static void step_y__generic(context_t* c);
+static void step_y__nop(context_t*);
+static void step_y__smooth(context_t* c);
+static void step_y__tmu(context_t* c);
+static void step_y__w(context_t* c);
+
+static void scanline(context_t* c);
+static void scanline_perspective(context_t* c);
+static void scanline_perspective_single(context_t* c);
+static void scanline_t32cb16blend(context_t* c);
+static void scanline_t32cb16blend_dither(context_t* c);
+static void scanline_t32cb16blend_srca(context_t* c);
+static void scanline_t32cb16blend_clamp(context_t* c);
+static void scanline_t32cb16blend_clamp_dither(context_t* c);
+static void scanline_t32cb16blend_clamp_mod(context_t* c);
+static void scanline_x32cb16blend_clamp_mod(context_t* c);
+static void scanline_t32cb16blend_clamp_mod_dither(context_t* c);
+static void scanline_x32cb16blend_clamp_mod_dither(context_t* c);
+static void scanline_t32cb16(context_t* c);
+static void scanline_t32cb16_dither(context_t* c);
+static void scanline_t32cb16_clamp(context_t* c);
+static void scanline_t32cb16_clamp_dither(context_t* c);
+static void scanline_col32cb16blend(context_t* c);
+static void scanline_t16cb16_clamp(context_t* c);
+static void scanline_t16cb16blend_clamp_mod(context_t* c);
+static void scanline_memcpy(context_t* c);
+static void scanline_memset8(context_t* c);
+static void scanline_memset16(context_t* c);
+static void scanline_memset32(context_t* c);
+static void scanline_noop(context_t* c);
+static void scanline_set(context_t* c);
+static void scanline_clear(context_t* c);
+
+static void rect_generic(context_t* c, size_t yc);
+static void rect_memcpy(context_t* c, size_t yc);
+
+#if defined( __arm__)
+extern "C" void scanline_t32cb16blend_arm(uint16_t*, uint32_t*, size_t);
+extern "C" void scanline_t32cb16_arm(uint16_t *dst, uint32_t *src, size_t ct);
+extern "C" void scanline_col32cb16blend_neon(uint16_t *dst, uint32_t *col, size_t ct);
+extern "C" void scanline_col32cb16blend_arm(uint16_t *dst, uint32_t col, size_t ct);
+#elif defined(__aarch64__)
+extern "C" void scanline_t32cb16blend_arm64(uint16_t*, uint32_t*, size_t);
+extern "C" void scanline_col32cb16blend_arm64(uint16_t *dst, uint32_t col, size_t ct);
+#elif defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6
+extern "C" void scanline_t32cb16blend_mips(uint16_t*, uint32_t*, size_t);
+#endif
+
+// ----------------------------------------------------------------------------
+
+static inline uint16_t convertAbgr8888ToRgb565(uint32_t pix)
+{
+ return uint16_t( ((pix << 8) & 0xf800) |
+ ((pix >> 5) & 0x07e0) |
+ ((pix >> 19) & 0x001f) );
+}
+
+struct shortcut_t {
+ needs_filter_t filter;
+ const char* desc;
+ void (*scanline)(context_t*);
+ void (*init_y)(context_t*, int32_t);
+};
+
+// Keep in sync with needs
+
+/* To understand the values here, have a look at:
+ * system/core/include/private/pixelflinger/ggl_context.h
+ *
+ * Especially the lines defining and using GGL_RESERVE_NEEDS
+ *
+ * Quick reminders:
+ * - the last nibble of the first value is the destination buffer format.
+ * - the last nibble of the third value is the source texture format
+ * - formats: 4=rgb565 1=abgr8888 2=xbgr8888
+ *
+ * In the descriptions below:
+ *
+ * SRC means we copy the source pixels to the destination
+ *
+ * SRC_OVER means we blend the source pixels to the destination
+ * with dstFactor = 1-srcA, srcFactor=1 (premultiplied source).
+ * This mode is otherwise called 'blend'.
+ *
+ * SRCA_OVER means we blend the source pixels to the destination
+ * with dstFactor=srcA*(1-srcA) srcFactor=srcA (non-premul source).
+ * This mode is otherwise called 'blend_srca'
+ *
+ * clamp means we fetch source pixels from a texture with u/v clamping
+ *
+ * mod means the source pixels are modulated (multiplied) by the
+ * a/r/g/b of the current context's color. Typically used for
+ * fade-in / fade-out.
+ *
+ * dither means we dither 32 bit values to 16 bits
+ */
+static shortcut_t shortcuts[] = {
+ { { { 0x03515104, 0x00000077, { 0x00000A01, 0x00000000 } },
+ { 0xFFFFFFFF, 0xFFFFFFFF, { 0xFFFFFFFF, 0x0000003F } } },
+ "565 fb, 8888 tx, blend SRC_OVER", scanline_t32cb16blend, init_y_noop },
+ { { { 0x03010104, 0x00000077, { 0x00000A01, 0x00000000 } },
+ { 0xFFFFFFFF, 0xFFFFFFFF, { 0xFFFFFFFF, 0x0000003F } } },
+ "565 fb, 8888 tx, SRC", scanline_t32cb16, init_y_noop },
+ /* same as first entry, but with dithering */
+ { { { 0x03515104, 0x00000177, { 0x00000A01, 0x00000000 } },
+ { 0xFFFFFFFF, 0xFFFFFFFF, { 0xFFFFFFFF, 0x0000003F } } },
+ "565 fb, 8888 tx, blend SRC_OVER dither", scanline_t32cb16blend_dither, init_y_noop },
+ /* same as second entry, but with dithering */
+ { { { 0x03010104, 0x00000177, { 0x00000A01, 0x00000000 } },
+ { 0xFFFFFFFF, 0xFFFFFFFF, { 0xFFFFFFFF, 0x0000003F } } },
+ "565 fb, 8888 tx, SRC dither", scanline_t32cb16_dither, init_y_noop },
+ /* this is used during the boot animation - CHEAT: ignore dithering */
+ { { { 0x03545404, 0x00000077, { 0x00000A01, 0x00000000 } },
+ { 0xFFFFFFFF, 0xFFFFFEFF, { 0xFFFFFFFF, 0x0000003F } } },
+ "565 fb, 8888 tx, blend dst:ONE_MINUS_SRCA src:SRCA", scanline_t32cb16blend_srca, init_y_noop },
+ /* special case for arbitrary texture coordinates (think scaling) */
+ { { { 0x03515104, 0x00000077, { 0x00000001, 0x00000000 } },
+ { 0xFFFFFFFF, 0xFFFFFFFF, { 0xFFFFFFFF, 0x0000003F } } },
+ "565 fb, 8888 tx, SRC_OVER clamp", scanline_t32cb16blend_clamp, init_y },
+ { { { 0x03515104, 0x00000177, { 0x00000001, 0x00000000 } },
+ { 0xFFFFFFFF, 0xFFFFFFFF, { 0xFFFFFFFF, 0x0000003F } } },
+ "565 fb, 8888 tx, SRC_OVER clamp dither", scanline_t32cb16blend_clamp_dither, init_y },
+ /* another case used during emulation */
+ { { { 0x03515104, 0x00000077, { 0x00001001, 0x00000000 } },
+ { 0xFFFFFFFF, 0xFFFFFFFF, { 0xFFFFFFFF, 0x0000003F } } },
+ "565 fb, 8888 tx, SRC_OVER clamp modulate", scanline_t32cb16blend_clamp_mod, init_y },
+ /* and this */
+ { { { 0x03515104, 0x00000077, { 0x00001002, 0x00000000 } },
+ { 0xFFFFFFFF, 0xFFFFFFFF, { 0xFFFFFFFF, 0x0000003F } } },
+ "565 fb, x888 tx, SRC_OVER clamp modulate", scanline_x32cb16blend_clamp_mod, init_y },
+ { { { 0x03515104, 0x00000177, { 0x00001001, 0x00000000 } },
+ { 0xFFFFFFFF, 0xFFFFFFFF, { 0xFFFFFFFF, 0x0000003F } } },
+ "565 fb, 8888 tx, SRC_OVER clamp modulate dither", scanline_t32cb16blend_clamp_mod_dither, init_y },
+ { { { 0x03515104, 0x00000177, { 0x00001002, 0x00000000 } },
+ { 0xFFFFFFFF, 0xFFFFFFFF, { 0xFFFFFFFF, 0x0000003F } } },
+ "565 fb, x888 tx, SRC_OVER clamp modulate dither", scanline_x32cb16blend_clamp_mod_dither, init_y },
+ { { { 0x03010104, 0x00000077, { 0x00000001, 0x00000000 } },
+ { 0xFFFFFFFF, 0xFFFFFFFF, { 0xFFFFFFFF, 0x0000003F } } },
+ "565 fb, 8888 tx, SRC clamp", scanline_t32cb16_clamp, init_y },
+ { { { 0x03010104, 0x00000077, { 0x00000002, 0x00000000 } },
+ { 0xFFFFFFFF, 0xFFFFFFFF, { 0xFFFFFFFF, 0x0000003F } } },
+ "565 fb, x888 tx, SRC clamp", scanline_t32cb16_clamp, init_y },
+ { { { 0x03010104, 0x00000177, { 0x00000001, 0x00000000 } },
+ { 0xFFFFFFFF, 0xFFFFFFFF, { 0xFFFFFFFF, 0x0000003F } } },
+ "565 fb, 8888 tx, SRC clamp dither", scanline_t32cb16_clamp_dither, init_y },
+ { { { 0x03010104, 0x00000177, { 0x00000002, 0x00000000 } },
+ { 0xFFFFFFFF, 0xFFFFFFFF, { 0xFFFFFFFF, 0x0000003F } } },
+ "565 fb, x888 tx, SRC clamp dither", scanline_t32cb16_clamp_dither, init_y },
+ { { { 0x03010104, 0x00000077, { 0x00000004, 0x00000000 } },
+ { 0xFFFFFFFF, 0xFFFFFFFF, { 0xFFFFFFFF, 0x0000003F } } },
+ "565 fb, 565 tx, SRC clamp", scanline_t16cb16_clamp, init_y },
+ { { { 0x03515104, 0x00000077, { 0x00001004, 0x00000000 } },
+ { 0xFFFFFFFF, 0xFFFFFFFF, { 0xFFFFFFFF, 0x0000003F } } },
+ "565 fb, 565 tx, SRC_OVER clamp", scanline_t16cb16blend_clamp_mod, init_y },
+ { { { 0x03515104, 0x00000077, { 0x00000000, 0x00000000 } },
+ { 0xFFFFFFFF, 0xFFFFFFFF, { 0xFFFFFFFF, 0xFFFFFFFF } } },
+ "565 fb, 8888 fixed color", scanline_col32cb16blend, init_y_packed },
+ { { { 0x00000000, 0x00000000, { 0x00000000, 0x00000000 } },
+ { 0x00000000, 0x00000007, { 0x00000000, 0x00000000 } } },
+ "(nop) alpha test", scanline_noop, init_y_noop },
+ { { { 0x00000000, 0x00000000, { 0x00000000, 0x00000000 } },
+ { 0x00000000, 0x00000070, { 0x00000000, 0x00000000 } } },
+ "(nop) depth test", scanline_noop, init_y_noop },
+ { { { 0x05000000, 0x00000000, { 0x00000000, 0x00000000 } },
+ { 0x0F000000, 0x00000080, { 0x00000000, 0x00000000 } } },
+ "(nop) logic_op", scanline_noop, init_y_noop },
+ { { { 0xF0000000, 0x00000000, { 0x00000000, 0x00000000 } },
+ { 0xF0000000, 0x00000080, { 0x00000000, 0x00000000 } } },
+ "(nop) color mask", scanline_noop, init_y_noop },
+ { { { 0x0F000000, 0x00000077, { 0x00000000, 0x00000000 } },
+ { 0xFF000000, 0x000000F7, { 0x00000000, 0x00000000 } } },
+ "(set) logic_op", scanline_set, init_y_noop },
+ { { { 0x00000000, 0x00000077, { 0x00000000, 0x00000000 } },
+ { 0xFF000000, 0x000000F7, { 0x00000000, 0x00000000 } } },
+ "(clear) logic_op", scanline_clear, init_y_noop },
+ { { { 0x03000000, 0x00000077, { 0x00000000, 0x00000000 } },
+ { 0xFFFFFF00, 0x000000F7, { 0x00000000, 0x00000000 } } },
+ "(clear) blending 0/0", scanline_clear, init_y_noop },
+ { { { 0x00000000, 0x00000000, { 0x00000000, 0x00000000 } },
+ { 0x0000003F, 0x00000000, { 0x00000000, 0x00000000 } } },
+ "(error) invalid color-buffer format", scanline_noop, init_y_error },
+};
+static const needs_filter_t noblend1to1 = {
+ // (disregard dithering, see below)
+ { 0x03010100, 0x00000077, { 0x00000A00, 0x00000000 } },
+ { 0xFFFFFFC0, 0xFFFFFEFF, { 0xFFFFFFC0, 0x0000003F } }
+};
+static const needs_filter_t fill16noblend = {
+ { 0x03010100, 0x00000077, { 0x00000000, 0x00000000 } },
+ { 0xFFFFFFC0, 0xFFFFFFFF, { 0x0000003F, 0x0000003F } }
+};
+
+// ----------------------------------------------------------------------------
+
+#if ANDROID_ARM_CODEGEN
+
+#if defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6
+static CodeCache gCodeCache(32 * 1024);
+#elif defined(__aarch64__)
+static CodeCache gCodeCache(48 * 1024);
+#else
+static CodeCache gCodeCache(12 * 1024);
+#endif
+
+class ScanlineAssembly : public Assembly {
+ AssemblyKey<needs_t> mKey;
+public:
+ ScanlineAssembly(needs_t needs, size_t size)
+ : Assembly(size), mKey(needs) { }
+ const AssemblyKey<needs_t>& key() const { return mKey; }
+};
+#endif
+
+// ----------------------------------------------------------------------------
+
+void ggl_init_scanline(context_t* c)
+{
+ c->init_y = init_y;
+ c->step_y = step_y__generic;
+ c->scanline = scanline;
+}
+
+void ggl_uninit_scanline(context_t* c)
+{
+ if (c->state.buffers.coverage)
+ free(c->state.buffers.coverage);
+#if ANDROID_ARM_CODEGEN
+ if (c->scanline_as)
+ c->scanline_as->decStrong(c);
+#endif
+}
+
+// ----------------------------------------------------------------------------
+
+static void pick_scanline(context_t* c)
+{
+#if (!defined(DEBUG__CODEGEN_ONLY) || (DEBUG__CODEGEN_ONLY == 0))
+
+#if ANDROID_CODEGEN == ANDROID_CODEGEN_GENERIC
+ c->init_y = init_y;
+ c->step_y = step_y__generic;
+ c->scanline = scanline;
+ return;
+#endif
+
+ //printf("*** needs [%08lx:%08lx:%08lx:%08lx]\n",
+ // c->state.needs.n, c->state.needs.p,
+ // c->state.needs.t[0], c->state.needs.t[1]);
+
+ // first handle the special case that we cannot test with a filter
+ const uint32_t cb_format = GGL_READ_NEEDS(CB_FORMAT, c->state.needs.n);
+ if (GGL_READ_NEEDS(T_FORMAT, c->state.needs.t[0]) == cb_format) {
+ if (c->state.needs.match(noblend1to1)) {
+ // this will match regardless of dithering state, since both
+ // src and dest have the same format anyway, there is no dithering
+ // to be done.
+ const GGLFormat* f =
+ &(c->formats[GGL_READ_NEEDS(T_FORMAT, c->state.needs.t[0])]);
+ if ((f->components == GGL_RGB) ||
+ (f->components == GGL_RGBA) ||
+ (f->components == GGL_LUMINANCE) ||
+ (f->components == GGL_LUMINANCE_ALPHA))
+ {
+ // format must have all of RGB components
+ // (so the current color doesn't show through)
+ c->scanline = scanline_memcpy;
+ c->init_y = init_y_noop;
+ return;
+ }
+ }
+ }
+
+ if (c->state.needs.match(fill16noblend)) {
+ c->init_y = init_y_packed;
+ switch (c->formats[cb_format].size) {
+ case 1: c->scanline = scanline_memset8; return;
+ case 2: c->scanline = scanline_memset16; return;
+ case 4: c->scanline = scanline_memset32; return;
+ }
+ }
+
+ const int numFilters = sizeof(shortcuts)/sizeof(shortcut_t);
+ for (int i=0 ; i<numFilters ; i++) {
+ if (c->state.needs.match(shortcuts[i].filter)) {
+ c->scanline = shortcuts[i].scanline;
+ c->init_y = shortcuts[i].init_y;
+ return;
+ }
+ }
+
+#if DEBUG_NEEDS
+ ALOGI("Needs: n=0x%08x p=0x%08x t0=0x%08x t1=0x%08x",
+ c->state.needs.n, c->state.needs.p,
+ c->state.needs.t[0], c->state.needs.t[1]);
+#endif
+
+#endif // DEBUG__CODEGEN_ONLY
+
+ c->init_y = init_y;
+ c->step_y = step_y__generic;
+
+#if ANDROID_ARM_CODEGEN
+ // we're going to have to generate some code...
+ // here, generate code for our pixel pipeline
+ const AssemblyKey<needs_t> key(c->state.needs);
+ sp<Assembly> assembly = gCodeCache.lookup(key);
+ if (assembly == 0) {
+ // create a new assembly region
+ sp<ScanlineAssembly> a = new ScanlineAssembly(c->state.needs,
+ ASSEMBLY_SCRATCH_SIZE);
+ // initialize our assembler
+#if defined(__arm__)
+ GGLAssembler assembler( new ARMAssembler(a) );
+ //GGLAssembler assembler(
+ // new ARMAssemblerOptimizer(new ARMAssembler(a)) );
+#endif
+#if defined(__mips__)
+ GGLAssembler assembler( new ArmToMipsAssembler(a) );
+#elif defined(__aarch64__)
+ GGLAssembler assembler( new ArmToArm64Assembler(a) );
+#endif
+ // generate the scanline code for the given needs
+ bool err = assembler.scanline(c->state.needs, c) != 0;
+ if (ggl_likely(!err)) {
+ // finally, cache this assembly
+ err = gCodeCache.cache(a->key(), a) < 0;
+ }
+ if (ggl_unlikely(err)) {
+ ALOGE("error generating or caching assembly. Reverting to NOP.");
+ c->scanline = scanline_noop;
+ c->init_y = init_y_noop;
+ c->step_y = step_y__nop;
+ return;
+ }
+ assembly = a;
+ }
+
+ // release the previous assembly
+ if (c->scanline_as) {
+ c->scanline_as->decStrong(c);
+ }
+
+ //ALOGI("using generated pixel-pipeline");
+ c->scanline_as = assembly.get();
+ c->scanline_as->incStrong(c); // hold on to assembly
+ c->scanline = (void(*)(context_t* c))assembly->base();
+#else
+// ALOGW("using generic (slow) pixel-pipeline");
+ c->scanline = scanline;
+#endif
+}
+
+void ggl_pick_scanline(context_t* c)
+{
+ pick_scanline(c);
+ if ((c->state.enables & GGL_ENABLE_W) &&
+ (c->state.enables & GGL_ENABLE_TMUS))
+ {
+ c->span = c->scanline;
+ c->scanline = scanline_perspective;
+ if (!(c->state.enabled_tmu & (c->state.enabled_tmu - 1))) {
+ // only one TMU enabled
+ c->scanline = scanline_perspective_single;
+ }
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+static void blending(context_t* c, pixel_t* fragment, pixel_t* fb);
+static void blend_factor(context_t* c, pixel_t* r, uint32_t factor,
+ const pixel_t* src, const pixel_t* dst);
+static void rescale(uint32_t& u, uint8_t& su, uint32_t& v, uint8_t& sv);
+
+#if ANDROID_ARM_CODEGEN && (ANDROID_CODEGEN == ANDROID_CODEGEN_GENERATED)
+
+// no need to compile the generic-pipeline, it can't be reached
+void scanline(context_t*)
+{
+}
+
+#else
+
+void rescale(uint32_t& u, uint8_t& su, uint32_t& v, uint8_t& sv)
+{
+ if (su && sv) {
+ if (su > sv) {
+ v = ggl_expand(v, sv, su);
+ sv = su;
+ } else if (su < sv) {
+ u = ggl_expand(u, su, sv);
+ su = sv;
+ }
+ }
+}
+
+void blending(context_t* c, pixel_t* fragment, pixel_t* fb)
+{
+ rescale(fragment->c[0], fragment->s[0], fb->c[0], fb->s[0]);
+ rescale(fragment->c[1], fragment->s[1], fb->c[1], fb->s[1]);
+ rescale(fragment->c[2], fragment->s[2], fb->c[2], fb->s[2]);
+ rescale(fragment->c[3], fragment->s[3], fb->c[3], fb->s[3]);
+
+ pixel_t sf, df;
+ blend_factor(c, &sf, c->state.blend.src, fragment, fb);
+ blend_factor(c, &df, c->state.blend.dst, fragment, fb);
+
+ fragment->c[1] =
+ gglMulAddx(fragment->c[1], sf.c[1], gglMulx(fb->c[1], df.c[1]));
+ fragment->c[2] =
+ gglMulAddx(fragment->c[2], sf.c[2], gglMulx(fb->c[2], df.c[2]));
+ fragment->c[3] =
+ gglMulAddx(fragment->c[3], sf.c[3], gglMulx(fb->c[3], df.c[3]));
+
+ if (c->state.blend.alpha_separate) {
+ blend_factor(c, &sf, c->state.blend.src_alpha, fragment, fb);
+ blend_factor(c, &df, c->state.blend.dst_alpha, fragment, fb);
+ }
+
+ fragment->c[0] =
+ gglMulAddx(fragment->c[0], sf.c[0], gglMulx(fb->c[0], df.c[0]));
+
+ // clamp to 1.0
+ if (fragment->c[0] >= (1LU<<fragment->s[0]))
+ fragment->c[0] = (1<<fragment->s[0])-1;
+ if (fragment->c[1] >= (1LU<<fragment->s[1]))
+ fragment->c[1] = (1<<fragment->s[1])-1;
+ if (fragment->c[2] >= (1LU<<fragment->s[2]))
+ fragment->c[2] = (1<<fragment->s[2])-1;
+ if (fragment->c[3] >= (1LU<<fragment->s[3]))
+ fragment->c[3] = (1<<fragment->s[3])-1;
+}
+
+static inline int blendfactor(uint32_t x, uint32_t size, uint32_t def = 0)
+{
+ if (!size)
+ return def;
+
+ // scale to 16 bits
+ if (size > 16) {
+ x >>= (size - 16);
+ } else if (size < 16) {
+ x = ggl_expand(x, size, 16);
+ }
+ x += x >> 15;
+ return x;
+}
+
+void blend_factor(context_t* /*c*/, pixel_t* r,
+ uint32_t factor, const pixel_t* src, const pixel_t* dst)
+{
+ switch (factor) {
+ case GGL_ZERO:
+ r->c[1] =
+ r->c[2] =
+ r->c[3] =
+ r->c[0] = 0;
+ break;
+ case GGL_ONE:
+ r->c[1] =
+ r->c[2] =
+ r->c[3] =
+ r->c[0] = FIXED_ONE;
+ break;
+ case GGL_DST_COLOR:
+ r->c[1] = blendfactor(dst->c[1], dst->s[1]);
+ r->c[2] = blendfactor(dst->c[2], dst->s[2]);
+ r->c[3] = blendfactor(dst->c[3], dst->s[3]);
+ r->c[0] = blendfactor(dst->c[0], dst->s[0]);
+ break;
+ case GGL_SRC_COLOR:
+ r->c[1] = blendfactor(src->c[1], src->s[1]);
+ r->c[2] = blendfactor(src->c[2], src->s[2]);
+ r->c[3] = blendfactor(src->c[3], src->s[3]);
+ r->c[0] = blendfactor(src->c[0], src->s[0]);
+ break;
+ case GGL_ONE_MINUS_DST_COLOR:
+ r->c[1] = FIXED_ONE - blendfactor(dst->c[1], dst->s[1]);
+ r->c[2] = FIXED_ONE - blendfactor(dst->c[2], dst->s[2]);
+ r->c[3] = FIXED_ONE - blendfactor(dst->c[3], dst->s[3]);
+ r->c[0] = FIXED_ONE - blendfactor(dst->c[0], dst->s[0]);
+ break;
+ case GGL_ONE_MINUS_SRC_COLOR:
+ r->c[1] = FIXED_ONE - blendfactor(src->c[1], src->s[1]);
+ r->c[2] = FIXED_ONE - blendfactor(src->c[2], src->s[2]);
+ r->c[3] = FIXED_ONE - blendfactor(src->c[3], src->s[3]);
+ r->c[0] = FIXED_ONE - blendfactor(src->c[0], src->s[0]);
+ break;
+ case GGL_SRC_ALPHA:
+ r->c[1] =
+ r->c[2] =
+ r->c[3] =
+ r->c[0] = blendfactor(src->c[0], src->s[0], FIXED_ONE);
+ break;
+ case GGL_ONE_MINUS_SRC_ALPHA:
+ r->c[1] =
+ r->c[2] =
+ r->c[3] =
+ r->c[0] = FIXED_ONE - blendfactor(src->c[0], src->s[0], FIXED_ONE);
+ break;
+ case GGL_DST_ALPHA:
+ r->c[1] =
+ r->c[2] =
+ r->c[3] =
+ r->c[0] = blendfactor(dst->c[0], dst->s[0], FIXED_ONE);
+ break;
+ case GGL_ONE_MINUS_DST_ALPHA:
+ r->c[1] =
+ r->c[2] =
+ r->c[3] =
+ r->c[0] = FIXED_ONE - blendfactor(dst->c[0], dst->s[0], FIXED_ONE);
+ break;
+ case GGL_SRC_ALPHA_SATURATE:
+ // XXX: GGL_SRC_ALPHA_SATURATE
+ break;
+ }
+}
+
+static GGLfixed wrapping(int32_t coord, uint32_t size, int tx_wrap)
+{
+ GGLfixed d;
+ if (tx_wrap == GGL_REPEAT) {
+ d = (uint32_t(coord)>>16) * size;
+ } else if (tx_wrap == GGL_CLAMP) { // CLAMP_TO_EDGE semantics
+ const GGLfixed clamp_min = FIXED_HALF;
+ const GGLfixed clamp_max = (size << 16) - FIXED_HALF;
+ if (coord < clamp_min) coord = clamp_min;
+ if (coord > clamp_max) coord = clamp_max;
+ d = coord;
+ } else { // 1:1
+ const GGLfixed clamp_min = 0;
+ const GGLfixed clamp_max = (size << 16);
+ if (coord < clamp_min) coord = clamp_min;
+ if (coord > clamp_max) coord = clamp_max;
+ d = coord;
+ }
+ return d;
+}
+
+static inline
+GGLcolor ADJUST_COLOR_ITERATOR(GGLcolor v, GGLcolor dvdx, int len)
+{
+ const int32_t end = dvdx * (len-1) + v;
+ if (end < 0)
+ v -= end;
+ v &= ~(v>>31);
+ return v;
+}
+
+void scanline(context_t* c)
+{
+ const uint32_t enables = c->state.enables;
+ const int xs = c->iterators.xl;
+ const int x1 = c->iterators.xr;
+ int xc = x1 - xs;
+ const int16_t* covPtr = c->state.buffers.coverage + xs;
+
+ // All iterated values are sampled at the pixel center
+
+ // reset iterators for that scanline...
+ GGLcolor r, g, b, a;
+ iterators_t& ci = c->iterators;
+ if (enables & GGL_ENABLE_SMOOTH) {
+ r = (xs * c->shade.drdx) + ci.ydrdy;
+ g = (xs * c->shade.dgdx) + ci.ydgdy;
+ b = (xs * c->shade.dbdx) + ci.ydbdy;
+ a = (xs * c->shade.dadx) + ci.ydady;
+ r = ADJUST_COLOR_ITERATOR(r, c->shade.drdx, xc);
+ g = ADJUST_COLOR_ITERATOR(g, c->shade.dgdx, xc);
+ b = ADJUST_COLOR_ITERATOR(b, c->shade.dbdx, xc);
+ a = ADJUST_COLOR_ITERATOR(a, c->shade.dadx, xc);
+ } else {
+ r = ci.ydrdy;
+ g = ci.ydgdy;
+ b = ci.ydbdy;
+ a = ci.ydady;
+ }
+
+ // z iterators are 1.31
+ GGLfixed z = (xs * c->shade.dzdx) + ci.ydzdy;
+ GGLfixed f = (xs * c->shade.dfdx) + ci.ydfdy;
+
+ struct {
+ GGLfixed s, t;
+ } tc[GGL_TEXTURE_UNIT_COUNT];
+ if (enables & GGL_ENABLE_TMUS) {
+ for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; ++i) {
+ if (c->state.texture[i].enable) {
+ texture_iterators_t& ti = c->state.texture[i].iterators;
+ if (enables & GGL_ENABLE_W) {
+ tc[i].s = ti.ydsdy;
+ tc[i].t = ti.ydtdy;
+ } else {
+ tc[i].s = (xs * ti.dsdx) + ti.ydsdy;
+ tc[i].t = (xs * ti.dtdx) + ti.ydtdy;
+ }
+ }
+ }
+ }
+
+ pixel_t fragment;
+ pixel_t texel;
+ pixel_t fb;
+
+ uint32_t x = xs;
+ uint32_t y = c->iterators.y;
+
+ while (xc--) {
+
+ { // just a scope
+
+ // read color (convert to 8 bits by keeping only the integer part)
+ fragment.s[1] = fragment.s[2] =
+ fragment.s[3] = fragment.s[0] = 8;
+ fragment.c[1] = r >> (GGL_COLOR_BITS-8);
+ fragment.c[2] = g >> (GGL_COLOR_BITS-8);
+ fragment.c[3] = b >> (GGL_COLOR_BITS-8);
+ fragment.c[0] = a >> (GGL_COLOR_BITS-8);
+
+ // texturing
+ if (enables & GGL_ENABLE_TMUS) {
+ for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; ++i) {
+ texture_t& tx = c->state.texture[i];
+ if (!tx.enable)
+ continue;
+ texture_iterators_t& ti = tx.iterators;
+ int32_t u, v;
+
+ // s-coordinate
+ if (tx.s_coord != GGL_ONE_TO_ONE) {
+ const int w = tx.surface.width;
+ u = wrapping(tc[i].s, w, tx.s_wrap);
+ tc[i].s += ti.dsdx;
+ } else {
+ u = (((tx.shade.is0>>16) + x)<<16) + FIXED_HALF;
+ }
+
+ // t-coordinate
+ if (tx.t_coord != GGL_ONE_TO_ONE) {
+ const int h = tx.surface.height;
+ v = wrapping(tc[i].t, h, tx.t_wrap);
+ tc[i].t += ti.dtdx;
+ } else {
+ v = (((tx.shade.it0>>16) + y)<<16) + FIXED_HALF;
+ }
+
+ // read texture
+ if (tx.mag_filter == GGL_NEAREST &&
+ tx.min_filter == GGL_NEAREST)
+ {
+ u >>= 16;
+ v >>= 16;
+ tx.surface.read(&tx.surface, c, u, v, &texel);
+ } else {
+ const int w = tx.surface.width;
+ const int h = tx.surface.height;
+ u -= FIXED_HALF;
+ v -= FIXED_HALF;
+ int u0 = u >> 16;
+ int v0 = v >> 16;
+ int u1 = u0 + 1;
+ int v1 = v0 + 1;
+ if (tx.s_wrap == GGL_REPEAT) {
+ if (u0<0) u0 += w;
+ if (u1<0) u1 += w;
+ if (u0>=w) u0 -= w;
+ if (u1>=w) u1 -= w;
+ } else {
+ if (u0<0) u0 = 0;
+ if (u1<0) u1 = 0;
+ if (u0>=w) u0 = w-1;
+ if (u1>=w) u1 = w-1;
+ }
+ if (tx.t_wrap == GGL_REPEAT) {
+ if (v0<0) v0 += h;
+ if (v1<0) v1 += h;
+ if (v0>=h) v0 -= h;
+ if (v1>=h) v1 -= h;
+ } else {
+ if (v0<0) v0 = 0;
+ if (v1<0) v1 = 0;
+ if (v0>=h) v0 = h-1;
+ if (v1>=h) v1 = h-1;
+ }
+ pixel_t texels[4];
+ uint32_t mm[4];
+ tx.surface.read(&tx.surface, c, u0, v0, &texels[0]);
+ tx.surface.read(&tx.surface, c, u0, v1, &texels[1]);
+ tx.surface.read(&tx.surface, c, u1, v0, &texels[2]);
+ tx.surface.read(&tx.surface, c, u1, v1, &texels[3]);
+ u = (u >> 12) & 0xF;
+ v = (v >> 12) & 0xF;
+ u += u>>3;
+ v += v>>3;
+ mm[0] = (0x10 - u) * (0x10 - v);
+ mm[1] = (0x10 - u) * v;
+ mm[2] = u * (0x10 - v);
+ mm[3] = 0x100 - (mm[0] + mm[1] + mm[2]);
+ for (int j=0 ; j<4 ; j++) {
+ texel.s[j] = texels[0].s[j];
+ if (!texel.s[j]) continue;
+ texel.s[j] += 8;
+ texel.c[j] = texels[0].c[j]*mm[0] +
+ texels[1].c[j]*mm[1] +
+ texels[2].c[j]*mm[2] +
+ texels[3].c[j]*mm[3] ;
+ }
+ }
+
+ // Texture environnement...
+ for (int j=0 ; j<4 ; j++) {
+ uint32_t& Cf = fragment.c[j];
+ uint32_t& Ct = texel.c[j];
+ uint8_t& sf = fragment.s[j];
+ uint8_t& st = texel.s[j];
+ uint32_t At = texel.c[0];
+ uint8_t sat = texel.s[0];
+ switch (tx.env) {
+ case GGL_REPLACE:
+ if (st) {
+ Cf = Ct;
+ sf = st;
+ }
+ break;
+ case GGL_MODULATE:
+ if (st) {
+ uint32_t factor = Ct + (Ct>>(st-1));
+ Cf = (Cf * factor) >> st;
+ }
+ break;
+ case GGL_DECAL:
+ if (sat) {
+ rescale(Cf, sf, Ct, st);
+ Cf += ((Ct - Cf) * (At + (At>>(sat-1)))) >> sat;
+ }
+ break;
+ case GGL_BLEND:
+ if (st) {
+ uint32_t Cc = tx.env_color[i];
+ if (sf>8) Cc = (Cc * ((1<<sf)-1))>>8;
+ else if (sf<8) Cc = (Cc - (Cc>>(8-sf)))>>(8-sf);
+ uint32_t factor = Ct + (Ct>>(st-1));
+ Cf = ((((1<<st) - factor) * Cf) + Ct*Cc)>>st;
+ }
+ break;
+ case GGL_ADD:
+ if (st) {
+ rescale(Cf, sf, Ct, st);
+ Cf += Ct;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ // coverage application
+ if (enables & GGL_ENABLE_AA) {
+ int16_t cf = *covPtr++;
+ fragment.c[0] = (int64_t(fragment.c[0]) * cf) >> 15;
+ }
+
+ // alpha-test
+ if (enables & GGL_ENABLE_ALPHA_TEST) {
+ GGLcolor ref = c->state.alpha_test.ref;
+ GGLcolor alpha = (uint64_t(fragment.c[0]) *
+ ((1<<GGL_COLOR_BITS)-1)) / ((1<<fragment.s[0])-1);
+ switch (c->state.alpha_test.func) {
+ case GGL_NEVER: goto discard;
+ case GGL_LESS: if (alpha<ref) break; goto discard;
+ case GGL_EQUAL: if (alpha==ref) break; goto discard;
+ case GGL_LEQUAL: if (alpha<=ref) break; goto discard;
+ case GGL_GREATER: if (alpha>ref) break; goto discard;
+ case GGL_NOTEQUAL: if (alpha!=ref) break; goto discard;
+ case GGL_GEQUAL: if (alpha>=ref) break; goto discard;
+ }
+ }
+
+ // depth test
+ if (c->state.buffers.depth.format) {
+ if (enables & GGL_ENABLE_DEPTH_TEST) {
+ surface_t* cb = &(c->state.buffers.depth);
+ uint16_t* p = (uint16_t*)(cb->data)+(x+(cb->stride*y));
+ uint16_t zz = uint32_t(z)>>(16);
+ uint16_t depth = *p;
+ switch (c->state.depth_test.func) {
+ case GGL_NEVER: goto discard;
+ case GGL_LESS: if (zz<depth) break; goto discard;
+ case GGL_EQUAL: if (zz==depth) break; goto discard;
+ case GGL_LEQUAL: if (zz<=depth) break; goto discard;
+ case GGL_GREATER: if (zz>depth) break; goto discard;
+ case GGL_NOTEQUAL: if (zz!=depth) break; goto discard;
+ case GGL_GEQUAL: if (zz>=depth) break; goto discard;
+ }
+ // depth buffer is not enabled, if depth-test is not enabled
+/*
+ fragment.s[1] = fragment.s[2] =
+ fragment.s[3] = fragment.s[0] = 8;
+ fragment.c[1] =
+ fragment.c[2] =
+ fragment.c[3] =
+ fragment.c[0] = 255 - (zz>>8);
+*/
+ if (c->state.mask.depth) {
+ *p = zz;
+ }
+ }
+ }
+
+ // fog
+ if (enables & GGL_ENABLE_FOG) {
+ for (int i=1 ; i<=3 ; i++) {
+ GGLfixed fc = (c->state.fog.color[i] * 0x10000) / 0xFF;
+ uint32_t& c = fragment.c[i];
+ uint8_t& s = fragment.s[i];
+ c = (c * 0x10000) / ((1<<s)-1);
+ c = gglMulAddx(c, f, gglMulx(fc, 0x10000 - f));
+ s = 16;
+ }
+ }
+
+ // blending
+ if (enables & GGL_ENABLE_BLENDING) {
+ fb.c[1] = fb.c[2] = fb.c[3] = fb.c[0] = 0; // placate valgrind
+ fb.s[1] = fb.s[2] = fb.s[3] = fb.s[0] = 0;
+ c->state.buffers.color.read(
+ &(c->state.buffers.color), c, x, y, &fb);
+ blending( c, &fragment, &fb );
+ }
+
+ // write
+ c->state.buffers.color.write(
+ &(c->state.buffers.color), c, x, y, &fragment);
+ }
+
+discard:
+ // iterate...
+ x += 1;
+ if (enables & GGL_ENABLE_SMOOTH) {
+ r += c->shade.drdx;
+ g += c->shade.dgdx;
+ b += c->shade.dbdx;
+ a += c->shade.dadx;
+ }
+ z += c->shade.dzdx;
+ f += c->shade.dfdx;
+ }
+}
+
+#endif // ANDROID_ARM_CODEGEN && (ANDROID_CODEGEN == ANDROID_CODEGEN_GENERATED)
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark Scanline
+#endif
+
+/* Used to parse a 32-bit source texture linearly. Usage is:
+ *
+ * horz_iterator32 hi(context);
+ * while (...) {
+ * uint32_t src_pixel = hi.get_pixel32();
+ * ...
+ * }
+ *
+ * Use only for one-to-one texture mapping.
+ */
+struct horz_iterator32 {
+ horz_iterator32(context_t* c) {
+ const int x = c->iterators.xl;
+ const int y = c->iterators.y;
+ texture_t& tx = c->state.texture[0];
+ const int32_t u = (tx.shade.is0>>16) + x;
+ const int32_t v = (tx.shade.it0>>16) + y;
+ m_src = reinterpret_cast<uint32_t*>(tx.surface.data)+(u+(tx.surface.stride*v));
+ }
+ uint32_t get_pixel32() {
+ return *m_src++;
+ }
+protected:
+ uint32_t* m_src;
+};
+
+/* A variant for 16-bit source textures. */
+struct horz_iterator16 {
+ horz_iterator16(context_t* c) {
+ const int x = c->iterators.xl;
+ const int y = c->iterators.y;
+ texture_t& tx = c->state.texture[0];
+ const int32_t u = (tx.shade.is0>>16) + x;
+ const int32_t v = (tx.shade.it0>>16) + y;
+ m_src = reinterpret_cast<uint16_t*>(tx.surface.data)+(u+(tx.surface.stride*v));
+ }
+ uint16_t get_pixel16() {
+ return *m_src++;
+ }
+protected:
+ uint16_t* m_src;
+};
+
+/* A clamp iterator is used to iterate inside a texture with GGL_CLAMP.
+ * After initialization, call get_src16() or get_src32() to get the current
+ * texture pixel value.
+ */
+struct clamp_iterator {
+ clamp_iterator(context_t* c) {
+ const int xs = c->iterators.xl;
+ texture_t& tx = c->state.texture[0];
+ texture_iterators_t& ti = tx.iterators;
+ m_s = (xs * ti.dsdx) + ti.ydsdy;
+ m_t = (xs * ti.dtdx) + ti.ydtdy;
+ m_ds = ti.dsdx;
+ m_dt = ti.dtdx;
+ m_width_m1 = tx.surface.width - 1;
+ m_height_m1 = tx.surface.height - 1;
+ m_data = tx.surface.data;
+ m_stride = tx.surface.stride;
+ }
+ uint16_t get_pixel16() {
+ int u, v;
+ get_uv(u, v);
+ uint16_t* src = reinterpret_cast<uint16_t*>(m_data) + (u + (m_stride*v));
+ return src[0];
+ }
+ uint32_t get_pixel32() {
+ int u, v;
+ get_uv(u, v);
+ uint32_t* src = reinterpret_cast<uint32_t*>(m_data) + (u + (m_stride*v));
+ return src[0];
+ }
+private:
+ void get_uv(int& u, int& v) {
+ int uu = m_s >> 16;
+ int vv = m_t >> 16;
+ if (uu < 0)
+ uu = 0;
+ if (uu > m_width_m1)
+ uu = m_width_m1;
+ if (vv < 0)
+ vv = 0;
+ if (vv > m_height_m1)
+ vv = m_height_m1;
+ u = uu;
+ v = vv;
+ m_s += m_ds;
+ m_t += m_dt;
+ }
+
+ GGLfixed m_s, m_t;
+ GGLfixed m_ds, m_dt;
+ int m_width_m1, m_height_m1;
+ uint8_t* m_data;
+ int m_stride;
+};
+
+/*
+ * The 'horizontal clamp iterator' variant corresponds to the case where
+ * the 'v' coordinate doesn't change. This is useful to avoid one mult and
+ * extra adds / checks per pixels, if the blending/processing operation after
+ * this is very fast.
+ */
+static int is_context_horizontal(const context_t* c) {
+ return (c->state.texture[0].iterators.dtdx == 0);
+}
+
+struct horz_clamp_iterator {
+ uint16_t get_pixel16() {
+ int u = m_s >> 16;
+ m_s += m_ds;
+ if (u < 0)
+ u = 0;
+ if (u > m_width_m1)
+ u = m_width_m1;
+ const uint16_t* src = reinterpret_cast<const uint16_t*>(m_data);
+ return src[u];
+ }
+ uint32_t get_pixel32() {
+ int u = m_s >> 16;
+ m_s += m_ds;
+ if (u < 0)
+ u = 0;
+ if (u > m_width_m1)
+ u = m_width_m1;
+ const uint32_t* src = reinterpret_cast<const uint32_t*>(m_data);
+ return src[u];
+ }
+protected:
+ void init(const context_t* c, int shift);
+ GGLfixed m_s;
+ GGLfixed m_ds;
+ int m_width_m1;
+ const uint8_t* m_data;
+};
+
+void horz_clamp_iterator::init(const context_t* c, int shift)
+{
+ const int xs = c->iterators.xl;
+ const texture_t& tx = c->state.texture[0];
+ const texture_iterators_t& ti = tx.iterators;
+ m_s = (xs * ti.dsdx) + ti.ydsdy;
+ m_ds = ti.dsdx;
+ m_width_m1 = tx.surface.width-1;
+ m_data = tx.surface.data;
+
+ GGLfixed t = (xs * ti.dtdx) + ti.ydtdy;
+ int v = t >> 16;
+ if (v < 0)
+ v = 0;
+ else if (v >= (int)tx.surface.height)
+ v = (int)tx.surface.height-1;
+
+ m_data += (tx.surface.stride*v) << shift;
+}
+
+struct horz_clamp_iterator16 : horz_clamp_iterator {
+ horz_clamp_iterator16(const context_t* c) {
+ init(c,1);
+ };
+};
+
+struct horz_clamp_iterator32 : horz_clamp_iterator {
+ horz_clamp_iterator32(context_t* c) {
+ init(c,2);
+ };
+};
+
+/* This is used to perform dithering operations.
+ */
+struct ditherer {
+ ditherer(const context_t* c) {
+ const int x = c->iterators.xl;
+ const int y = c->iterators.y;
+ m_line = &c->ditherMatrix[ ((y & GGL_DITHER_MASK)<<GGL_DITHER_ORDER_SHIFT) ];
+ m_index = x & GGL_DITHER_MASK;
+ }
+ void step(void) {
+ m_index++;
+ }
+ int get_value(void) {
+ int ret = m_line[m_index & GGL_DITHER_MASK];
+ m_index++;
+ return ret;
+ }
+ uint16_t abgr8888ToRgb565(uint32_t s) {
+ uint32_t r = s & 0xff;
+ uint32_t g = (s >> 8) & 0xff;
+ uint32_t b = (s >> 16) & 0xff;
+ return rgb888ToRgb565(r,g,b);
+ }
+ /* The following assumes that r/g/b are in the 0..255 range each */
+ uint16_t rgb888ToRgb565(uint32_t& r, uint32_t& g, uint32_t &b) {
+ int threshold = get_value();
+ /* dither in on GGL_DITHER_BITS, and each of r, g, b is on 8 bits */
+ r += (threshold >> (GGL_DITHER_BITS-8 +5));
+ g += (threshold >> (GGL_DITHER_BITS-8 +6));
+ b += (threshold >> (GGL_DITHER_BITS-8 +5));
+ if (r > 0xff)
+ r = 0xff;
+ if (g > 0xff)
+ g = 0xff;
+ if (b > 0xff)
+ b = 0xff;
+ return uint16_t(((r & 0xf8) << 8) | ((g & 0xfc) << 3) | (b >> 3));
+ }
+protected:
+ const uint8_t* m_line;
+ int m_index;
+};
+
+/* This structure is used to blend (SRC_OVER) 32-bit source pixels
+ * onto 16-bit destination ones. Usage is simply:
+ *
+ * blender.blend(<32-bit-src-pixel-value>,<ptr-to-16-bit-dest-pixel>)
+ */
+struct blender_32to16 {
+ blender_32to16(context_t* /*c*/) { }
+ void write(uint32_t s, uint16_t* dst) {
+ if (s == 0)
+ return;
+ s = GGL_RGBA_TO_HOST(s);
+ int sA = (s>>24);
+ if (sA == 0xff) {
+ *dst = convertAbgr8888ToRgb565(s);
+ } else {
+ int f = 0x100 - (sA + (sA>>7));
+ int sR = (s >> ( 3))&0x1F;
+ int sG = (s >> ( 8+2))&0x3F;
+ int sB = (s >> (16+3))&0x1F;
+ uint16_t d = *dst;
+ int dR = (d>>11)&0x1f;
+ int dG = (d>>5)&0x3f;
+ int dB = (d)&0x1f;
+ sR += (f*dR)>>8;
+ sG += (f*dG)>>8;
+ sB += (f*dB)>>8;
+ *dst = uint16_t((sR<<11)|(sG<<5)|sB);
+ }
+ }
+ void write(uint32_t s, uint16_t* dst, ditherer& di) {
+ if (s == 0) {
+ di.step();
+ return;
+ }
+ s = GGL_RGBA_TO_HOST(s);
+ int sA = (s>>24);
+ if (sA == 0xff) {
+ *dst = di.abgr8888ToRgb565(s);
+ } else {
+ int threshold = di.get_value() << (8 - GGL_DITHER_BITS);
+ int f = 0x100 - (sA + (sA>>7));
+ int sR = (s >> ( 3))&0x1F;
+ int sG = (s >> ( 8+2))&0x3F;
+ int sB = (s >> (16+3))&0x1F;
+ uint16_t d = *dst;
+ int dR = (d>>11)&0x1f;
+ int dG = (d>>5)&0x3f;
+ int dB = (d)&0x1f;
+ sR = ((sR << 8) + f*dR + threshold)>>8;
+ sG = ((sG << 8) + f*dG + threshold)>>8;
+ sB = ((sB << 8) + f*dB + threshold)>>8;
+ if (sR > 0x1f) sR = 0x1f;
+ if (sG > 0x3f) sG = 0x3f;
+ if (sB > 0x1f) sB = 0x1f;
+ *dst = uint16_t((sR<<11)|(sG<<5)|sB);
+ }
+ }
+};
+
+/* This blender does the same for the 'blend_srca' operation.
+ * where dstFactor=srcA*(1-srcA) srcFactor=srcA
+ */
+struct blender_32to16_srcA {
+ blender_32to16_srcA(const context_t* /*c*/) { }
+ void write(uint32_t s, uint16_t* dst) {
+ if (!s) {
+ return;
+ }
+ uint16_t d = *dst;
+ s = GGL_RGBA_TO_HOST(s);
+ int sR = (s >> ( 3))&0x1F;
+ int sG = (s >> ( 8+2))&0x3F;
+ int sB = (s >> (16+3))&0x1F;
+ int sA = (s>>24);
+ int f1 = (sA + (sA>>7));
+ int f2 = 0x100-f1;
+ int dR = (d>>11)&0x1f;
+ int dG = (d>>5)&0x3f;
+ int dB = (d)&0x1f;
+ sR = (f1*sR + f2*dR)>>8;
+ sG = (f1*sG + f2*dG)>>8;
+ sB = (f1*sB + f2*dB)>>8;
+ *dst = uint16_t((sR<<11)|(sG<<5)|sB);
+ }
+};
+
+/* Common init code the modulating blenders */
+struct blender_modulate {
+ void init(const context_t* c) {
+ const int r = c->iterators.ydrdy >> (GGL_COLOR_BITS-8);
+ const int g = c->iterators.ydgdy >> (GGL_COLOR_BITS-8);
+ const int b = c->iterators.ydbdy >> (GGL_COLOR_BITS-8);
+ const int a = c->iterators.ydady >> (GGL_COLOR_BITS-8);
+ m_r = r + (r >> 7);
+ m_g = g + (g >> 7);
+ m_b = b + (b >> 7);
+ m_a = a + (a >> 7);
+ }
+protected:
+ int m_r, m_g, m_b, m_a;
+};
+
+/* This blender does a normal blend after modulation.
+ */
+struct blender_32to16_modulate : blender_modulate {
+ blender_32to16_modulate(const context_t* c) {
+ init(c);
+ }
+ void write(uint32_t s, uint16_t* dst) {
+ // blend source and destination
+ if (!s) {
+ return;
+ }
+ s = GGL_RGBA_TO_HOST(s);
+
+ /* We need to modulate s */
+ uint32_t sA = (s >> 24);
+ uint32_t sB = (s >> 16) & 0xff;
+ uint32_t sG = (s >> 8) & 0xff;
+ uint32_t sR = s & 0xff;
+
+ sA = (sA*m_a) >> 8;
+ /* Keep R/G/B scaled to 5.8 or 6.8 fixed float format */
+ sR = (sR*m_r) >> (8 - 5);
+ sG = (sG*m_g) >> (8 - 6);
+ sB = (sB*m_b) >> (8 - 5);
+
+ /* Now do a normal blend */
+ int f = 0x100 - (sA + (sA>>7));
+ uint16_t d = *dst;
+ int dR = (d>>11)&0x1f;
+ int dG = (d>>5)&0x3f;
+ int dB = (d)&0x1f;
+ sR = (sR + f*dR)>>8;
+ sG = (sG + f*dG)>>8;
+ sB = (sB + f*dB)>>8;
+ *dst = uint16_t((sR<<11)|(sG<<5)|sB);
+ }
+ void write(uint32_t s, uint16_t* dst, ditherer& di) {
+ // blend source and destination
+ if (!s) {
+ di.step();
+ return;
+ }
+ s = GGL_RGBA_TO_HOST(s);
+
+ /* We need to modulate s */
+ uint32_t sA = (s >> 24);
+ uint32_t sB = (s >> 16) & 0xff;
+ uint32_t sG = (s >> 8) & 0xff;
+ uint32_t sR = s & 0xff;
+
+ sA = (sA*m_a) >> 8;
+ /* keep R/G/B scaled to 5.8 or 6.8 fixed float format */
+ sR = (sR*m_r) >> (8 - 5);
+ sG = (sG*m_g) >> (8 - 6);
+ sB = (sB*m_b) >> (8 - 5);
+
+ /* Scale threshold to 0.8 fixed float format */
+ int threshold = di.get_value() << (8 - GGL_DITHER_BITS);
+ int f = 0x100 - (sA + (sA>>7));
+ uint16_t d = *dst;
+ int dR = (d>>11)&0x1f;
+ int dG = (d>>5)&0x3f;
+ int dB = (d)&0x1f;
+ sR = (sR + f*dR + threshold)>>8;
+ sG = (sG + f*dG + threshold)>>8;
+ sB = (sB + f*dB + threshold)>>8;
+ if (sR > 0x1f) sR = 0x1f;
+ if (sG > 0x3f) sG = 0x3f;
+ if (sB > 0x1f) sB = 0x1f;
+ *dst = uint16_t((sR<<11)|(sG<<5)|sB);
+ }
+};
+
+/* same as 32to16_modulate, except that the input is xRGB, instead of ARGB */
+struct blender_x32to16_modulate : blender_modulate {
+ blender_x32to16_modulate(const context_t* c) {
+ init(c);
+ }
+ void write(uint32_t s, uint16_t* dst) {
+ s = GGL_RGBA_TO_HOST(s);
+
+ uint32_t sB = (s >> 16) & 0xff;
+ uint32_t sG = (s >> 8) & 0xff;
+ uint32_t sR = s & 0xff;
+
+ /* Keep R/G/B in 5.8 or 6.8 format */
+ sR = (sR*m_r) >> (8 - 5);
+ sG = (sG*m_g) >> (8 - 6);
+ sB = (sB*m_b) >> (8 - 5);
+
+ int f = 0x100 - m_a;
+ uint16_t d = *dst;
+ int dR = (d>>11)&0x1f;
+ int dG = (d>>5)&0x3f;
+ int dB = (d)&0x1f;
+ sR = (sR + f*dR)>>8;
+ sG = (sG + f*dG)>>8;
+ sB = (sB + f*dB)>>8;
+ *dst = uint16_t((sR<<11)|(sG<<5)|sB);
+ }
+ void write(uint32_t s, uint16_t* dst, ditherer& di) {
+ s = GGL_RGBA_TO_HOST(s);
+
+ uint32_t sB = (s >> 16) & 0xff;
+ uint32_t sG = (s >> 8) & 0xff;
+ uint32_t sR = s & 0xff;
+
+ sR = (sR*m_r) >> (8 - 5);
+ sG = (sG*m_g) >> (8 - 6);
+ sB = (sB*m_b) >> (8 - 5);
+
+ /* Now do a normal blend */
+ int threshold = di.get_value() << (8 - GGL_DITHER_BITS);
+ int f = 0x100 - m_a;
+ uint16_t d = *dst;
+ int dR = (d>>11)&0x1f;
+ int dG = (d>>5)&0x3f;
+ int dB = (d)&0x1f;
+ sR = (sR + f*dR + threshold)>>8;
+ sG = (sG + f*dG + threshold)>>8;
+ sB = (sB + f*dB + threshold)>>8;
+ if (sR > 0x1f) sR = 0x1f;
+ if (sG > 0x3f) sG = 0x3f;
+ if (sB > 0x1f) sB = 0x1f;
+ *dst = uint16_t((sR<<11)|(sG<<5)|sB);
+ }
+};
+
+/* Same as above, but source is 16bit rgb565 */
+struct blender_16to16_modulate : blender_modulate {
+ blender_16to16_modulate(const context_t* c) {
+ init(c);
+ }
+ void write(uint16_t s16, uint16_t* dst) {
+ uint32_t s = s16;
+
+ uint32_t sR = s >> 11;
+ uint32_t sG = (s >> 5) & 0x3f;
+ uint32_t sB = s & 0x1f;
+
+ sR = (sR*m_r);
+ sG = (sG*m_g);
+ sB = (sB*m_b);
+
+ int f = 0x100 - m_a;
+ uint16_t d = *dst;
+ int dR = (d>>11)&0x1f;
+ int dG = (d>>5)&0x3f;
+ int dB = (d)&0x1f;
+ sR = (sR + f*dR)>>8;
+ sG = (sG + f*dG)>>8;
+ sB = (sB + f*dB)>>8;
+ *dst = uint16_t((sR<<11)|(sG<<5)|sB);
+ }
+};
+
+/* This is used to iterate over a 16-bit destination color buffer.
+ * Usage is:
+ *
+ * dst_iterator16 di(context);
+ * while (di.count--) {
+ * <do stuff with dest pixel at di.dst>
+ * di.dst++;
+ * }
+ */
+struct dst_iterator16 {
+ dst_iterator16(const context_t* c) {
+ const int x = c->iterators.xl;
+ const int width = c->iterators.xr - x;
+ const int32_t y = c->iterators.y;
+ const surface_t* cb = &(c->state.buffers.color);
+ count = width;
+ dst = reinterpret_cast<uint16_t*>(cb->data) + (x+(cb->stride*y));
+ }
+ int count;
+ uint16_t* dst;
+};
+
+
+static void scanline_t32cb16_clamp(context_t* c)
+{
+ dst_iterator16 di(c);
+
+ if (is_context_horizontal(c)) {
+ /* Special case for simple horizontal scaling */
+ horz_clamp_iterator32 ci(c);
+ while (di.count--) {
+ uint32_t s = ci.get_pixel32();
+ *di.dst++ = convertAbgr8888ToRgb565(s);
+ }
+ } else {
+ /* General case */
+ clamp_iterator ci(c);
+ while (di.count--) {
+ uint32_t s = ci.get_pixel32();
+ *di.dst++ = convertAbgr8888ToRgb565(s);
+ }
+ }
+}
+
+static void scanline_t32cb16_dither(context_t* c)
+{
+ horz_iterator32 si(c);
+ dst_iterator16 di(c);
+ ditherer dither(c);
+
+ while (di.count--) {
+ uint32_t s = si.get_pixel32();
+ *di.dst++ = dither.abgr8888ToRgb565(s);
+ }
+}
+
+static void scanline_t32cb16_clamp_dither(context_t* c)
+{
+ dst_iterator16 di(c);
+ ditherer dither(c);
+
+ if (is_context_horizontal(c)) {
+ /* Special case for simple horizontal scaling */
+ horz_clamp_iterator32 ci(c);
+ while (di.count--) {
+ uint32_t s = ci.get_pixel32();
+ *di.dst++ = dither.abgr8888ToRgb565(s);
+ }
+ } else {
+ /* General case */
+ clamp_iterator ci(c);
+ while (di.count--) {
+ uint32_t s = ci.get_pixel32();
+ *di.dst++ = dither.abgr8888ToRgb565(s);
+ }
+ }
+}
+
+static void scanline_t32cb16blend_dither(context_t* c)
+{
+ dst_iterator16 di(c);
+ ditherer dither(c);
+ blender_32to16 bl(c);
+ horz_iterator32 hi(c);
+ while (di.count--) {
+ uint32_t s = hi.get_pixel32();
+ bl.write(s, di.dst, dither);
+ di.dst++;
+ }
+}
+
+static void scanline_t32cb16blend_clamp(context_t* c)
+{
+ dst_iterator16 di(c);
+ blender_32to16 bl(c);
+
+ if (is_context_horizontal(c)) {
+ horz_clamp_iterator32 ci(c);
+ while (di.count--) {
+ uint32_t s = ci.get_pixel32();
+ bl.write(s, di.dst);
+ di.dst++;
+ }
+ } else {
+ clamp_iterator ci(c);
+ while (di.count--) {
+ uint32_t s = ci.get_pixel32();
+ bl.write(s, di.dst);
+ di.dst++;
+ }
+ }
+}
+
+static void scanline_t32cb16blend_clamp_dither(context_t* c)
+{
+ dst_iterator16 di(c);
+ ditherer dither(c);
+ blender_32to16 bl(c);
+
+ clamp_iterator ci(c);
+ while (di.count--) {
+ uint32_t s = ci.get_pixel32();
+ bl.write(s, di.dst, dither);
+ di.dst++;
+ }
+}
+
+void scanline_t32cb16blend_clamp_mod(context_t* c)
+{
+ dst_iterator16 di(c);
+ blender_32to16_modulate bl(c);
+
+ clamp_iterator ci(c);
+ while (di.count--) {
+ uint32_t s = ci.get_pixel32();
+ bl.write(s, di.dst);
+ di.dst++;
+ }
+}
+
+void scanline_t32cb16blend_clamp_mod_dither(context_t* c)
+{
+ dst_iterator16 di(c);
+ blender_32to16_modulate bl(c);
+ ditherer dither(c);
+
+ clamp_iterator ci(c);
+ while (di.count--) {
+ uint32_t s = ci.get_pixel32();
+ bl.write(s, di.dst, dither);
+ di.dst++;
+ }
+}
+
+/* Variant of scanline_t32cb16blend_clamp_mod with a xRGB texture */
+void scanline_x32cb16blend_clamp_mod(context_t* c)
+{
+ dst_iterator16 di(c);
+ blender_x32to16_modulate bl(c);
+
+ clamp_iterator ci(c);
+ while (di.count--) {
+ uint32_t s = ci.get_pixel32();
+ bl.write(s, di.dst);
+ di.dst++;
+ }
+}
+
+void scanline_x32cb16blend_clamp_mod_dither(context_t* c)
+{
+ dst_iterator16 di(c);
+ blender_x32to16_modulate bl(c);
+ ditherer dither(c);
+
+ clamp_iterator ci(c);
+ while (di.count--) {
+ uint32_t s = ci.get_pixel32();
+ bl.write(s, di.dst, dither);
+ di.dst++;
+ }
+}
+
+void scanline_t16cb16_clamp(context_t* c)
+{
+ dst_iterator16 di(c);
+
+ /* Special case for simple horizontal scaling */
+ if (is_context_horizontal(c)) {
+ horz_clamp_iterator16 ci(c);
+ while (di.count--) {
+ *di.dst++ = ci.get_pixel16();
+ }
+ } else {
+ clamp_iterator ci(c);
+ while (di.count--) {
+ *di.dst++ = ci.get_pixel16();
+ }
+ }
+}
+
+
+
+template <typename T, typename U>
+static inline __attribute__((const))
+T interpolate(int y, T v0, U dvdx, U dvdy) {
+ // interpolates in pixel's centers
+ // v = v0 + (y + 0.5) * dvdy + (0.5 * dvdx)
+ return (y * dvdy) + (v0 + ((dvdy + dvdx) >> 1));
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#endif
+
+void init_y(context_t* c, int32_t ys)
+{
+ const uint32_t enables = c->state.enables;
+
+ // compute iterators...
+ iterators_t& ci = c->iterators;
+
+ // sample in the center
+ ci.y = ys;
+
+ if (enables & (GGL_ENABLE_DEPTH_TEST|GGL_ENABLE_W|GGL_ENABLE_FOG)) {
+ ci.ydzdy = interpolate(ys, c->shade.z0, c->shade.dzdx, c->shade.dzdy);
+ ci.ydwdy = interpolate(ys, c->shade.w0, c->shade.dwdx, c->shade.dwdy);
+ ci.ydfdy = interpolate(ys, c->shade.f0, c->shade.dfdx, c->shade.dfdy);
+ }
+
+ if (ggl_unlikely(enables & GGL_ENABLE_SMOOTH)) {
+ ci.ydrdy = interpolate(ys, c->shade.r0, c->shade.drdx, c->shade.drdy);
+ ci.ydgdy = interpolate(ys, c->shade.g0, c->shade.dgdx, c->shade.dgdy);
+ ci.ydbdy = interpolate(ys, c->shade.b0, c->shade.dbdx, c->shade.dbdy);
+ ci.ydady = interpolate(ys, c->shade.a0, c->shade.dadx, c->shade.dady);
+ c->step_y = step_y__smooth;
+ } else {
+ ci.ydrdy = c->shade.r0;
+ ci.ydgdy = c->shade.g0;
+ ci.ydbdy = c->shade.b0;
+ ci.ydady = c->shade.a0;
+ // XXX: do only if needed, or make sure this is fast
+ c->packed = ggl_pack_color(c, c->state.buffers.color.format,
+ ci.ydrdy, ci.ydgdy, ci.ydbdy, ci.ydady);
+ c->packed8888 = ggl_pack_color(c, GGL_PIXEL_FORMAT_RGBA_8888,
+ ci.ydrdy, ci.ydgdy, ci.ydbdy, ci.ydady);
+ }
+
+ // initialize the variables we need in the shader
+ generated_vars_t& gen = c->generated_vars;
+ gen.argb[GGLFormat::ALPHA].c = ci.ydady;
+ gen.argb[GGLFormat::ALPHA].dx = c->shade.dadx;
+ gen.argb[GGLFormat::RED ].c = ci.ydrdy;
+ gen.argb[GGLFormat::RED ].dx = c->shade.drdx;
+ gen.argb[GGLFormat::GREEN].c = ci.ydgdy;
+ gen.argb[GGLFormat::GREEN].dx = c->shade.dgdx;
+ gen.argb[GGLFormat::BLUE ].c = ci.ydbdy;
+ gen.argb[GGLFormat::BLUE ].dx = c->shade.dbdx;
+ gen.dzdx = c->shade.dzdx;
+ gen.f = ci.ydfdy;
+ gen.dfdx = c->shade.dfdx;
+
+ if (enables & GGL_ENABLE_TMUS) {
+ for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; ++i) {
+ texture_t& t = c->state.texture[i];
+ if (!t.enable) continue;
+
+ texture_iterators_t& ti = t.iterators;
+ if (t.s_coord == GGL_ONE_TO_ONE && t.t_coord == GGL_ONE_TO_ONE) {
+ // we need to set all of these to 0 because in some cases
+ // step_y__generic() or step_y__tmu() will be used and
+ // therefore will update dtdy, however, in 1:1 mode
+ // this is always done by the scanline rasterizer.
+ ti.dsdx = ti.dsdy = ti.dtdx = ti.dtdy = 0;
+ ti.ydsdy = t.shade.is0;
+ ti.ydtdy = t.shade.it0;
+ } else {
+ const int adjustSWrap = ((t.s_wrap==GGL_CLAMP)?0:16);
+ const int adjustTWrap = ((t.t_wrap==GGL_CLAMP)?0:16);
+ ti.sscale = t.shade.sscale + adjustSWrap;
+ ti.tscale = t.shade.tscale + adjustTWrap;
+ if (!(enables & GGL_ENABLE_W)) {
+ // S coordinate
+ const int32_t sscale = ti.sscale;
+ const int32_t sy = interpolate(ys,
+ t.shade.is0, t.shade.idsdx, t.shade.idsdy);
+ if (sscale>=0) {
+ ti.ydsdy= sy << sscale;
+ ti.dsdx = t.shade.idsdx << sscale;
+ ti.dsdy = t.shade.idsdy << sscale;
+ } else {
+ ti.ydsdy= sy >> -sscale;
+ ti.dsdx = t.shade.idsdx >> -sscale;
+ ti.dsdy = t.shade.idsdy >> -sscale;
+ }
+ // T coordinate
+ const int32_t tscale = ti.tscale;
+ const int32_t ty = interpolate(ys,
+ t.shade.it0, t.shade.idtdx, t.shade.idtdy);
+ if (tscale>=0) {
+ ti.ydtdy= ty << tscale;
+ ti.dtdx = t.shade.idtdx << tscale;
+ ti.dtdy = t.shade.idtdy << tscale;
+ } else {
+ ti.ydtdy= ty >> -tscale;
+ ti.dtdx = t.shade.idtdx >> -tscale;
+ ti.dtdy = t.shade.idtdy >> -tscale;
+ }
+ }
+ }
+ // mirror for generated code...
+ generated_tex_vars_t& gen = c->generated_vars.texture[i];
+ gen.width = t.surface.width;
+ gen.height = t.surface.height;
+ gen.stride = t.surface.stride;
+ gen.data = uintptr_t(t.surface.data);
+ gen.dsdx = ti.dsdx;
+ gen.dtdx = ti.dtdx;
+ }
+ }
+
+ // choose the y-stepper
+ c->step_y = step_y__nop;
+ if (enables & GGL_ENABLE_FOG) {
+ c->step_y = step_y__generic;
+ } else if (enables & GGL_ENABLE_TMUS) {
+ if (enables & GGL_ENABLE_SMOOTH) {
+ c->step_y = step_y__generic;
+ } else if (enables & GGL_ENABLE_W) {
+ c->step_y = step_y__w;
+ } else {
+ c->step_y = step_y__tmu;
+ }
+ } else {
+ if (enables & GGL_ENABLE_SMOOTH) {
+ c->step_y = step_y__smooth;
+ }
+ }
+
+ // choose the rectangle blitter
+ c->rect = rect_generic;
+ if ((c->step_y == step_y__nop) &&
+ (c->scanline == scanline_memcpy))
+ {
+ c->rect = rect_memcpy;
+ }
+}
+
+void init_y_packed(context_t* c, int32_t y0)
+{
+ uint8_t f = c->state.buffers.color.format;
+ c->packed = ggl_pack_color(c, f,
+ c->shade.r0, c->shade.g0, c->shade.b0, c->shade.a0);
+ c->packed8888 = ggl_pack_color(c, GGL_PIXEL_FORMAT_RGBA_8888,
+ c->shade.r0, c->shade.g0, c->shade.b0, c->shade.a0);
+ c->iterators.y = y0;
+ c->step_y = step_y__nop;
+ // choose the rectangle blitter
+ c->rect = rect_generic;
+ if (c->scanline == scanline_memcpy) {
+ c->rect = rect_memcpy;
+ }
+}
+
+void init_y_noop(context_t* c, int32_t y0)
+{
+ c->iterators.y = y0;
+ c->step_y = step_y__nop;
+ // choose the rectangle blitter
+ c->rect = rect_generic;
+ if (c->scanline == scanline_memcpy) {
+ c->rect = rect_memcpy;
+ }
+}
+
+void init_y_error(context_t* c, int32_t y0)
+{
+ // woooops, shoud never happen,
+ // fail gracefully (don't display anything)
+ init_y_noop(c, y0);
+ ALOGE("color-buffer has an invalid format!");
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#endif
+
+void step_y__generic(context_t* c)
+{
+ const uint32_t enables = c->state.enables;
+
+ // iterate...
+ iterators_t& ci = c->iterators;
+ ci.y += 1;
+
+ if (enables & GGL_ENABLE_SMOOTH) {
+ ci.ydrdy += c->shade.drdy;
+ ci.ydgdy += c->shade.dgdy;
+ ci.ydbdy += c->shade.dbdy;
+ ci.ydady += c->shade.dady;
+ }
+
+ const uint32_t mask =
+ GGL_ENABLE_DEPTH_TEST |
+ GGL_ENABLE_W |
+ GGL_ENABLE_FOG;
+ if (enables & mask) {
+ ci.ydzdy += c->shade.dzdy;
+ ci.ydwdy += c->shade.dwdy;
+ ci.ydfdy += c->shade.dfdy;
+ }
+
+ if ((enables & GGL_ENABLE_TMUS) && (!(enables & GGL_ENABLE_W))) {
+ for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; ++i) {
+ if (c->state.texture[i].enable) {
+ texture_iterators_t& ti = c->state.texture[i].iterators;
+ ti.ydsdy += ti.dsdy;
+ ti.ydtdy += ti.dtdy;
+ }
+ }
+ }
+}
+
+void step_y__nop(context_t* c)
+{
+ c->iterators.y += 1;
+ c->iterators.ydzdy += c->shade.dzdy;
+}
+
+void step_y__smooth(context_t* c)
+{
+ iterators_t& ci = c->iterators;
+ ci.y += 1;
+ ci.ydrdy += c->shade.drdy;
+ ci.ydgdy += c->shade.dgdy;
+ ci.ydbdy += c->shade.dbdy;
+ ci.ydady += c->shade.dady;
+ ci.ydzdy += c->shade.dzdy;
+}
+
+void step_y__w(context_t* c)
+{
+ iterators_t& ci = c->iterators;
+ ci.y += 1;
+ ci.ydzdy += c->shade.dzdy;
+ ci.ydwdy += c->shade.dwdy;
+}
+
+void step_y__tmu(context_t* c)
+{
+ iterators_t& ci = c->iterators;
+ ci.y += 1;
+ ci.ydzdy += c->shade.dzdy;
+ for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; ++i) {
+ if (c->state.texture[i].enable) {
+ texture_iterators_t& ti = c->state.texture[i].iterators;
+ ti.ydsdy += ti.dsdy;
+ ti.ydtdy += ti.dtdy;
+ }
+ }
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#endif
+
+void scanline_perspective(context_t* c)
+{
+ struct {
+ union {
+ struct {
+ int32_t s, sq;
+ int32_t t, tq;
+ } sqtq;
+ struct {
+ int32_t v, q;
+ } st[2];
+ };
+ } tc[GGL_TEXTURE_UNIT_COUNT] __attribute__((aligned(16)));
+
+ // XXX: we should have a special case when dwdx = 0
+
+ // 32 pixels spans works okay. 16 is a lot better,
+ // but hey, it's a software renderer...
+ const uint32_t SPAN_BITS = 5;
+ const uint32_t ys = c->iterators.y;
+ const uint32_t xs = c->iterators.xl;
+ const uint32_t x1 = c->iterators.xr;
+ const uint32_t xc = x1 - xs;
+ uint32_t remainder = xc & ((1<<SPAN_BITS)-1);
+ uint32_t numSpans = xc >> SPAN_BITS;
+
+ const iterators_t& ci = c->iterators;
+ int32_t w0 = (xs * c->shade.dwdx) + ci.ydwdy;
+ int32_t q0 = gglRecipQ(w0, 30);
+ const int iwscale = 32 - gglClz(q0);
+
+ const int32_t dwdx = c->shade.dwdx << SPAN_BITS;
+ int32_t xl = c->iterators.xl;
+
+ // We process s & t with a loop to reduce the code size
+ // (and i-cache pressure).
+
+ for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; ++i) {
+ const texture_t& tmu = c->state.texture[i];
+ if (!tmu.enable) continue;
+ int32_t s = tmu.shade.is0 +
+ (tmu.shade.idsdy * ys) + (tmu.shade.idsdx * xs) +
+ ((tmu.shade.idsdx + tmu.shade.idsdy)>>1);
+ int32_t t = tmu.shade.it0 +
+ (tmu.shade.idtdy * ys) + (tmu.shade.idtdx * xs) +
+ ((tmu.shade.idtdx + tmu.shade.idtdy)>>1);
+ tc[i].sqtq.s = s;
+ tc[i].sqtq.t = t;
+ tc[i].sqtq.sq = gglMulx(s, q0, iwscale);
+ tc[i].sqtq.tq = gglMulx(t, q0, iwscale);
+ }
+
+ int32_t span = 0;
+ do {
+ int32_t w1;
+ if (ggl_likely(numSpans)) {
+ w1 = w0 + dwdx;
+ } else {
+ if (remainder) {
+ // finish off the scanline...
+ span = remainder;
+ w1 = (c->shade.dwdx * span) + w0;
+ } else {
+ break;
+ }
+ }
+ int32_t q1 = gglRecipQ(w1, 30);
+ for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; ++i) {
+ texture_t& tmu = c->state.texture[i];
+ if (!tmu.enable) continue;
+ texture_iterators_t& ti = tmu.iterators;
+
+ for (int j=0 ; j<2 ; j++) {
+ int32_t v = tc[i].st[j].v;
+ if (span) v += (tmu.shade.st[j].dx)*span;
+ else v += (tmu.shade.st[j].dx)<<SPAN_BITS;
+ const int32_t v0 = tc[i].st[j].q;
+ const int32_t v1 = gglMulx(v, q1, iwscale);
+ int32_t dvdx = v1 - v0;
+ if (span) dvdx /= span;
+ else dvdx >>= SPAN_BITS;
+ tc[i].st[j].v = v;
+ tc[i].st[j].q = v1;
+
+ const int scale = ti.st[j].scale + (iwscale - 30);
+ if (scale >= 0) {
+ ti.st[j].ydvdy = v0 << scale;
+ ti.st[j].dvdx = dvdx << scale;
+ } else {
+ ti.st[j].ydvdy = v0 >> -scale;
+ ti.st[j].dvdx = dvdx >> -scale;
+ }
+ }
+ generated_tex_vars_t& gen = c->generated_vars.texture[i];
+ gen.dsdx = ti.st[0].dvdx;
+ gen.dtdx = ti.st[1].dvdx;
+ }
+ c->iterators.xl = xl;
+ c->iterators.xr = xl = xl + (span ? span : (1<<SPAN_BITS));
+ w0 = w1;
+ q0 = q1;
+ c->span(c);
+ } while(numSpans--);
+}
+
+void scanline_perspective_single(context_t* c)
+{
+ // 32 pixels spans works okay. 16 is a lot better,
+ // but hey, it's a software renderer...
+ const uint32_t SPAN_BITS = 5;
+ const uint32_t ys = c->iterators.y;
+ const uint32_t xs = c->iterators.xl;
+ const uint32_t x1 = c->iterators.xr;
+ const uint32_t xc = x1 - xs;
+
+ const iterators_t& ci = c->iterators;
+ int32_t w = (xs * c->shade.dwdx) + ci.ydwdy;
+ int32_t iw = gglRecipQ(w, 30);
+ const int iwscale = 32 - gglClz(iw);
+
+ const int i = 31 - gglClz(c->state.enabled_tmu);
+ generated_tex_vars_t& gen = c->generated_vars.texture[i];
+ texture_t& tmu = c->state.texture[i];
+ texture_iterators_t& ti = tmu.iterators;
+ const int sscale = ti.sscale + (iwscale - 30);
+ const int tscale = ti.tscale + (iwscale - 30);
+ int32_t s = tmu.shade.is0 +
+ (tmu.shade.idsdy * ys) + (tmu.shade.idsdx * xs) +
+ ((tmu.shade.idsdx + tmu.shade.idsdy)>>1);
+ int32_t t = tmu.shade.it0 +
+ (tmu.shade.idtdy * ys) + (tmu.shade.idtdx * xs) +
+ ((tmu.shade.idtdx + tmu.shade.idtdy)>>1);
+ int32_t s0 = gglMulx(s, iw, iwscale);
+ int32_t t0 = gglMulx(t, iw, iwscale);
+ int32_t xl = c->iterators.xl;
+
+ int32_t sq, tq, dsdx, dtdx;
+ int32_t premainder = xc & ((1<<SPAN_BITS)-1);
+ uint32_t numSpans = xc >> SPAN_BITS;
+ if (c->shade.dwdx == 0) {
+ // XXX: we could choose to do this if the error is small enough
+ numSpans = 0;
+ premainder = xc;
+ goto no_perspective;
+ }
+
+ if (premainder) {
+ w += c->shade.dwdx * premainder;
+ iw = gglRecipQ(w, 30);
+no_perspective:
+ s += tmu.shade.idsdx * premainder;
+ t += tmu.shade.idtdx * premainder;
+ sq = gglMulx(s, iw, iwscale);
+ tq = gglMulx(t, iw, iwscale);
+ dsdx = (sq - s0) / premainder;
+ dtdx = (tq - t0) / premainder;
+ c->iterators.xl = xl;
+ c->iterators.xr = xl = xl + premainder;
+ goto finish;
+ }
+
+ while (numSpans--) {
+ w += c->shade.dwdx << SPAN_BITS;
+ s += tmu.shade.idsdx << SPAN_BITS;
+ t += tmu.shade.idtdx << SPAN_BITS;
+ iw = gglRecipQ(w, 30);
+ sq = gglMulx(s, iw, iwscale);
+ tq = gglMulx(t, iw, iwscale);
+ dsdx = (sq - s0) >> SPAN_BITS;
+ dtdx = (tq - t0) >> SPAN_BITS;
+ c->iterators.xl = xl;
+ c->iterators.xr = xl = xl + (1<<SPAN_BITS);
+finish:
+ if (sscale >= 0) {
+ ti.ydsdy = s0 << sscale;
+ ti.dsdx = dsdx << sscale;
+ } else {
+ ti.ydsdy = s0 >>-sscale;
+ ti.dsdx = dsdx >>-sscale;
+ }
+ if (tscale >= 0) {
+ ti.ydtdy = t0 << tscale;
+ ti.dtdx = dtdx << tscale;
+ } else {
+ ti.ydtdy = t0 >>-tscale;
+ ti.dtdx = dtdx >>-tscale;
+ }
+ s0 = sq;
+ t0 = tq;
+ gen.dsdx = ti.dsdx;
+ gen.dtdx = ti.dtdx;
+ c->span(c);
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+void scanline_col32cb16blend(context_t* c)
+{
+ int32_t x = c->iterators.xl;
+ size_t ct = c->iterators.xr - x;
+ int32_t y = c->iterators.y;
+ surface_t* cb = &(c->state.buffers.color);
+ union {
+ uint16_t* dst;
+ uint32_t* dst32;
+ };
+ dst = reinterpret_cast<uint16_t*>(cb->data) + (x+(cb->stride*y));
+
+#if ((ANDROID_CODEGEN >= ANDROID_CODEGEN_ASM) && defined(__arm__))
+#if defined(__ARM_HAVE_NEON) && BYTE_ORDER == LITTLE_ENDIAN
+ scanline_col32cb16blend_neon(dst, &(c->packed8888), ct);
+#else // defined(__ARM_HAVE_NEON) && BYTE_ORDER == LITTLE_ENDIAN
+ scanline_col32cb16blend_arm(dst, GGL_RGBA_TO_HOST(c->packed8888), ct);
+#endif // defined(__ARM_HAVE_NEON) && BYTE_ORDER == LITTLE_ENDIAN
+#elif ((ANDROID_CODEGEN >= ANDROID_CODEGEN_ASM) && defined(__aarch64__))
+ scanline_col32cb16blend_arm64(dst, GGL_RGBA_TO_HOST(c->packed8888), ct);
+#else
+ uint32_t s = GGL_RGBA_TO_HOST(c->packed8888);
+ int sA = (s>>24);
+ int f = 0x100 - (sA + (sA>>7));
+ while (ct--) {
+ uint16_t d = *dst;
+ int dR = (d>>11)&0x1f;
+ int dG = (d>>5)&0x3f;
+ int dB = (d)&0x1f;
+ int sR = (s >> ( 3))&0x1F;
+ int sG = (s >> ( 8+2))&0x3F;
+ int sB = (s >> (16+3))&0x1F;
+ sR += (f*dR)>>8;
+ sG += (f*dG)>>8;
+ sB += (f*dB)>>8;
+ *dst++ = uint16_t((sR<<11)|(sG<<5)|sB);
+ }
+#endif
+
+}
+
+void scanline_t32cb16(context_t* c)
+{
+ int32_t x = c->iterators.xl;
+ size_t ct = c->iterators.xr - x;
+ int32_t y = c->iterators.y;
+ surface_t* cb = &(c->state.buffers.color);
+ union {
+ uint16_t* dst;
+ uint32_t* dst32;
+ };
+ dst = reinterpret_cast<uint16_t*>(cb->data) + (x+(cb->stride*y));
+
+ surface_t* tex = &(c->state.texture[0].surface);
+ const int32_t u = (c->state.texture[0].shade.is0>>16) + x;
+ const int32_t v = (c->state.texture[0].shade.it0>>16) + y;
+ uint32_t *src = reinterpret_cast<uint32_t*>(tex->data)+(u+(tex->stride*v));
+ int sR, sG, sB;
+ uint32_t s, d;
+
+ if (ct==1 || uintptr_t(dst)&2) {
+last_one:
+ s = GGL_RGBA_TO_HOST( *src++ );
+ *dst++ = convertAbgr8888ToRgb565(s);
+ ct--;
+ }
+
+ while (ct >= 2) {
+#if BYTE_ORDER == BIG_ENDIAN
+ s = GGL_RGBA_TO_HOST( *src++ );
+ d = convertAbgr8888ToRgb565_hi16(s);
+
+ s = GGL_RGBA_TO_HOST( *src++ );
+ d |= convertAbgr8888ToRgb565(s);
+#else
+ s = GGL_RGBA_TO_HOST( *src++ );
+ d = convertAbgr8888ToRgb565(s);
+
+ s = GGL_RGBA_TO_HOST( *src++ );
+ d |= convertAbgr8888ToRgb565(s) << 16;
+#endif
+ *dst32++ = d;
+ ct -= 2;
+ }
+
+ if (ct > 0) {
+ goto last_one;
+ }
+}
+
+void scanline_t32cb16blend(context_t* c)
+{
+#if ((ANDROID_CODEGEN >= ANDROID_CODEGEN_ASM) && (defined(__arm__) || (defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6) || defined(__aarch64__)))
+ int32_t x = c->iterators.xl;
+ size_t ct = c->iterators.xr - x;
+ int32_t y = c->iterators.y;
+ surface_t* cb = &(c->state.buffers.color);
+ uint16_t* dst = reinterpret_cast<uint16_t*>(cb->data) + (x+(cb->stride*y));
+
+ surface_t* tex = &(c->state.texture[0].surface);
+ const int32_t u = (c->state.texture[0].shade.is0>>16) + x;
+ const int32_t v = (c->state.texture[0].shade.it0>>16) + y;
+ uint32_t *src = reinterpret_cast<uint32_t*>(tex->data)+(u+(tex->stride*v));
+
+#ifdef __arm__
+ scanline_t32cb16blend_arm(dst, src, ct);
+#elif defined(__aarch64__)
+ scanline_t32cb16blend_arm64(dst, src, ct);
+#elif defined(__mips__)
+ scanline_t32cb16blend_mips(dst, src, ct);
+#endif
+#else
+ dst_iterator16 di(c);
+ horz_iterator32 hi(c);
+ blender_32to16 bl(c);
+ while (di.count--) {
+ uint32_t s = hi.get_pixel32();
+ bl.write(s, di.dst);
+ di.dst++;
+ }
+#endif
+}
+
+void scanline_t32cb16blend_srca(context_t* c)
+{
+ dst_iterator16 di(c);
+ horz_iterator32 hi(c);
+ blender_32to16_srcA blender(c);
+
+ while (di.count--) {
+ uint32_t s = hi.get_pixel32();
+ blender.write(s,di.dst);
+ di.dst++;
+ }
+}
+
+void scanline_t16cb16blend_clamp_mod(context_t* c)
+{
+ const int a = c->iterators.ydady >> (GGL_COLOR_BITS-8);
+ if (a == 0) {
+ return;
+ }
+
+ if (a == 255) {
+ scanline_t16cb16_clamp(c);
+ return;
+ }
+
+ dst_iterator16 di(c);
+ blender_16to16_modulate blender(c);
+ clamp_iterator ci(c);
+
+ while (di.count--) {
+ uint16_t s = ci.get_pixel16();
+ blender.write(s, di.dst);
+ di.dst++;
+ }
+}
+
+void scanline_memcpy(context_t* c)
+{
+ int32_t x = c->iterators.xl;
+ size_t ct = c->iterators.xr - x;
+ int32_t y = c->iterators.y;
+ surface_t* cb = &(c->state.buffers.color);
+ const GGLFormat* fp = &(c->formats[cb->format]);
+ uint8_t* dst = reinterpret_cast<uint8_t*>(cb->data) +
+ (x + (cb->stride * y)) * fp->size;
+
+ surface_t* tex = &(c->state.texture[0].surface);
+ const int32_t u = (c->state.texture[0].shade.is0>>16) + x;
+ const int32_t v = (c->state.texture[0].shade.it0>>16) + y;
+ uint8_t *src = reinterpret_cast<uint8_t*>(tex->data) +
+ (u + (tex->stride * v)) * fp->size;
+
+ const size_t size = ct * fp->size;
+ memcpy(dst, src, size);
+}
+
+void scanline_memset8(context_t* c)
+{
+ int32_t x = c->iterators.xl;
+ size_t ct = c->iterators.xr - x;
+ int32_t y = c->iterators.y;
+ surface_t* cb = &(c->state.buffers.color);
+ uint8_t* dst = reinterpret_cast<uint8_t*>(cb->data) + (x+(cb->stride*y));
+ uint32_t packed = c->packed;
+ memset(dst, packed, ct);
+}
+
+void scanline_memset16(context_t* c)
+{
+ int32_t x = c->iterators.xl;
+ size_t ct = c->iterators.xr - x;
+ int32_t y = c->iterators.y;
+ surface_t* cb = &(c->state.buffers.color);
+ uint16_t* dst = reinterpret_cast<uint16_t*>(cb->data) + (x+(cb->stride*y));
+ uint32_t packed = c->packed;
+ android_memset16(dst, packed, ct*2);
+}
+
+void scanline_memset32(context_t* c)
+{
+ int32_t x = c->iterators.xl;
+ size_t ct = c->iterators.xr - x;
+ int32_t y = c->iterators.y;
+ surface_t* cb = &(c->state.buffers.color);
+ uint32_t* dst = reinterpret_cast<uint32_t*>(cb->data) + (x+(cb->stride*y));
+ uint32_t packed = GGL_HOST_TO_RGBA(c->packed);
+ android_memset32(dst, packed, ct*4);
+}
+
+void scanline_clear(context_t* c)
+{
+ int32_t x = c->iterators.xl;
+ size_t ct = c->iterators.xr - x;
+ int32_t y = c->iterators.y;
+ surface_t* cb = &(c->state.buffers.color);
+ const GGLFormat* fp = &(c->formats[cb->format]);
+ uint8_t* dst = reinterpret_cast<uint8_t*>(cb->data) +
+ (x + (cb->stride * y)) * fp->size;
+ const size_t size = ct * fp->size;
+ memset(dst, 0, size);
+}
+
+void scanline_set(context_t* c)
+{
+ int32_t x = c->iterators.xl;
+ size_t ct = c->iterators.xr - x;
+ int32_t y = c->iterators.y;
+ surface_t* cb = &(c->state.buffers.color);
+ const GGLFormat* fp = &(c->formats[cb->format]);
+ uint8_t* dst = reinterpret_cast<uint8_t*>(cb->data) +
+ (x + (cb->stride * y)) * fp->size;
+ const size_t size = ct * fp->size;
+ memset(dst, 0xFF, size);
+}
+
+void scanline_noop(context_t* /*c*/)
+{
+}
+
+void rect_generic(context_t* c, size_t yc)
+{
+ do {
+ c->scanline(c);
+ c->step_y(c);
+ } while (--yc);
+}
+
+void rect_memcpy(context_t* c, size_t yc)
+{
+ int32_t x = c->iterators.xl;
+ size_t ct = c->iterators.xr - x;
+ int32_t y = c->iterators.y;
+ surface_t* cb = &(c->state.buffers.color);
+ const GGLFormat* fp = &(c->formats[cb->format]);
+ uint8_t* dst = reinterpret_cast<uint8_t*>(cb->data) +
+ (x + (cb->stride * y)) * fp->size;
+
+ surface_t* tex = &(c->state.texture[0].surface);
+ const int32_t u = (c->state.texture[0].shade.is0>>16) + x;
+ const int32_t v = (c->state.texture[0].shade.it0>>16) + y;
+ uint8_t *src = reinterpret_cast<uint8_t*>(tex->data) +
+ (u + (tex->stride * v)) * fp->size;
+
+ if (cb->stride == tex->stride && ct == size_t(cb->stride)) {
+ memcpy(dst, src, ct * fp->size * yc);
+ } else {
+ const size_t size = ct * fp->size;
+ const size_t dbpr = cb->stride * fp->size;
+ const size_t sbpr = tex->stride * fp->size;
+ do {
+ memcpy(dst, src, size);
+ dst += dbpr;
+ src += sbpr;
+ } while (--yc);
+ }
+}
+// ----------------------------------------------------------------------------
+}; // namespace android
+
diff --git a/libpixelflinger/scanline.h b/libpixelflinger/scanline.h
new file mode 100644
index 0000000..b6f4d37
--- /dev/null
+++ b/libpixelflinger/scanline.h
@@ -0,0 +1,32 @@
+/* libs/pixelflinger/scanline.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+
+#ifndef ANDROID_SCANLINE_H
+#define ANDROID_SCANLINE_H
+
+#include <private/pixelflinger/ggl_context.h>
+
+namespace android {
+
+void ggl_init_scanline(context_t* c);
+void ggl_uninit_scanline(context_t* c);
+void ggl_pick_scanline(context_t* c);
+
+}; // namespace android
+
+#endif
diff --git a/libpixelflinger/t32cb16blend.S b/libpixelflinger/t32cb16blend.S
new file mode 100644
index 0000000..caf9eb7
--- /dev/null
+++ b/libpixelflinger/t32cb16blend.S
@@ -0,0 +1,202 @@
+/* libs/pixelflinger/t32cb16blend.S
+**
+** Copyright 2006, 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.
+*/
+
+
+ .text
+ .align
+
+ .global scanline_t32cb16blend_arm
+
+
+/*
+ * .macro pixel
+ *
+ * \DREG is a 32-bit register containing *two* original destination RGB565
+ * pixels, with the even one in the low-16 bits, and the odd one in the
+ * high 16 bits.
+ *
+ * \SRC is a 32-bit 0xAABBGGRR pixel value, with pre-multiplied colors.
+ *
+ * \FB is a target register that will contain the blended pixel values.
+ *
+ * \ODD is either 0 or 1 and indicates if we're blending the lower or
+ * upper 16-bit pixels in DREG into FB
+ *
+ *
+ * clobbered: r6, r7, lr
+ *
+ */
+
+.macro pixel, DREG, SRC, FB, ODD
+
+ // SRC = 0xAABBGGRR
+ mov r7, \SRC, lsr #24 // sA
+ add r7, r7, r7, lsr #7 // sA + (sA >> 7)
+ rsb r7, r7, #0x100 // sA = 0x100 - (sA+(sA>>7))
+
+1:
+
+.if \ODD
+
+ // red
+ mov lr, \DREG, lsr #(16 + 11)
+ smulbb lr, r7, lr
+ mov r6, \SRC, lsr #3
+ and r6, r6, #0x1F
+ add lr, r6, lr, lsr #8
+ cmp lr, #0x1F
+ orrhs \FB, \FB, #(0x1F<<(16 + 11))
+ orrlo \FB, \FB, lr, lsl #(16 + 11)
+
+ // green
+ and r6, \DREG, #(0x3F<<(16 + 5))
+ smulbt r6, r7, r6
+ mov lr, \SRC, lsr #(8+2)
+ and lr, lr, #0x3F
+ add r6, lr, r6, lsr #(5+8)
+ cmp r6, #0x3F
+ orrhs \FB, \FB, #(0x3F<<(16 + 5))
+ orrlo \FB, \FB, r6, lsl #(16 + 5)
+
+ // blue
+ and lr, \DREG, #(0x1F << 16)
+ smulbt lr, r7, lr
+ mov r6, \SRC, lsr #(8+8+3)
+ and r6, r6, #0x1F
+ add lr, r6, lr, lsr #8
+ cmp lr, #0x1F
+ orrhs \FB, \FB, #(0x1F << 16)
+ orrlo \FB, \FB, lr, lsl #16
+
+.else
+
+ // red
+ mov lr, \DREG, lsr #11
+ and lr, lr, #0x1F
+ smulbb lr, r7, lr
+ mov r6, \SRC, lsr #3
+ and r6, r6, #0x1F
+ add lr, r6, lr, lsr #8
+ cmp lr, #0x1F
+ movhs \FB, #(0x1F<<11)
+ movlo \FB, lr, lsl #11
+
+
+ // green
+ and r6, \DREG, #(0x3F<<5)
+ smulbb r6, r7, r6
+ mov lr, \SRC, lsr #(8+2)
+ and lr, lr, #0x3F
+ add r6, lr, r6, lsr #(5+8)
+ cmp r6, #0x3F
+ orrhs \FB, \FB, #(0x3F<<5)
+ orrlo \FB, \FB, r6, lsl #5
+
+ // blue
+ and lr, \DREG, #0x1F
+ smulbb lr, r7, lr
+ mov r6, \SRC, lsr #(8+8+3)
+ and r6, r6, #0x1F
+ add lr, r6, lr, lsr #8
+ cmp lr, #0x1F
+ orrhs \FB, \FB, #0x1F
+ orrlo \FB, \FB, lr
+
+.endif
+
+ .endm
+
+
+// r0: dst ptr
+// r1: src ptr
+// r2: count
+// r3: d
+// r4: s0
+// r5: s1
+// r6: pixel
+// r7: pixel
+// r8: free
+// r9: free
+// r10: free
+// r11: free
+// r12: scratch
+// r14: pixel
+
+scanline_t32cb16blend_arm:
+ stmfd sp!, {r4-r7, lr}
+
+ pld [r0]
+ pld [r1]
+
+ // align DST to 32 bits
+ tst r0, #0x3
+ beq aligned
+ subs r2, r2, #1
+ ldmlofd sp!, {r4-r7, lr} // return
+ bxlo lr
+
+last:
+ ldr r4, [r1], #4
+ ldrh r3, [r0]
+ pixel r3, r4, r12, 0
+ strh r12, [r0], #2
+
+aligned:
+ subs r2, r2, #2
+ blo 9f
+
+ // The main loop is unrolled twice and processes 4 pixels
+8: ldmia r1!, {r4, r5}
+ // stream the source
+ pld [r1, #32]
+ add r0, r0, #4
+ // it's all zero, skip this pixel
+ orrs r3, r4, r5
+ beq 7f
+
+ // load the destination
+ ldr r3, [r0, #-4]
+ // stream the destination
+ pld [r0, #32]
+ pixel r3, r4, r12, 0
+ pixel r3, r5, r12, 1
+ // effectively, we're getting write-combining by virtue of the
+ // cpu's write-back cache.
+ str r12, [r0, #-4]
+
+ // 2nd iterration of the loop, don't stream anything
+ subs r2, r2, #2
+ movlt r4, r5
+ blt 9f
+ ldmia r1!, {r4, r5}
+ add r0, r0, #4
+ orrs r3, r4, r5
+ beq 7f
+ ldr r3, [r0, #-4]
+ pixel r3, r4, r12, 0
+ pixel r3, r5, r12, 16
+ str r12, [r0, #-4]
+
+
+7: subs r2, r2, #2
+ bhs 8b
+ mov r4, r5
+
+9: adds r2, r2, #1
+ ldmlofd sp!, {r4-r7, lr} // return
+ bxlo lr
+ b last
diff --git a/libpixelflinger/tests/Android.mk b/libpixelflinger/tests/Android.mk
new file mode 100644
index 0000000..6571161
--- /dev/null
+++ b/libpixelflinger/tests/Android.mk
@@ -0,0 +1 @@
+include $(all-subdir-makefiles)
diff --git a/libpixelflinger/tests/arch-arm64/Android.mk b/libpixelflinger/tests/arch-arm64/Android.mk
new file mode 100644
index 0000000..ca58b4b
--- /dev/null
+++ b/libpixelflinger/tests/arch-arm64/Android.mk
@@ -0,0 +1,3 @@
+ifeq ($(TARGET_ARCH),arm64)
+include $(all-subdir-makefiles)
+endif
diff --git a/libpixelflinger/tests/arch-arm64/assembler/Android.mk b/libpixelflinger/tests/arch-arm64/assembler/Android.mk
new file mode 100644
index 0000000..448d298
--- /dev/null
+++ b/libpixelflinger/tests/arch-arm64/assembler/Android.mk
@@ -0,0 +1,24 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ arm64_assembler_test.cpp\
+ asm_test_jacket.S
+
+# asm_test_jacket.S does not compile with Clang.
+LOCAL_CLANG_ASFLAGS_arm64 += -no-integrated-as
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libpixelflinger
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/../../..
+
+LOCAL_MODULE:= test-pixelflinger-arm64-assembler-test
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_MULTILIB := 64
+
+include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-arm64/assembler/arm64_assembler_test.cpp b/libpixelflinger/tests/arch-arm64/assembler/arm64_assembler_test.cpp
new file mode 100644
index 0000000..5f58797
--- /dev/null
+++ b/libpixelflinger/tests/arch-arm64/assembler/arm64_assembler_test.cpp
@@ -0,0 +1,783 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <sys/mman.h>
+#include <cutils/ashmem.h>
+#include <cutils/atomic.h>
+
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
+
+#include "codeflinger/ARMAssemblerInterface.h"
+#include "codeflinger/Arm64Assembler.h"
+using namespace android;
+
+#define TESTS_DATAOP_ENABLE 1
+#define TESTS_DATATRANSFER_ENABLE 1
+#define TESTS_LDMSTM_ENABLE 1
+#define TESTS_REG_CORRUPTION_ENABLE 0
+
+void *instrMem;
+uint32_t instrMemSize = 128 * 1024;
+char dataMem[8192];
+
+typedef void (*asm_function_t)();
+extern "C" void asm_test_jacket(asm_function_t function,
+ int64_t regs[], int32_t flags[]);
+
+#define MAX_32BIT (uint32_t)(((uint64_t)1 << 32) - 1)
+const uint32_t NA = 0;
+const uint32_t NUM_REGS = 32;
+const uint32_t NUM_FLAGS = 16;
+
+enum instr_t
+{
+ INSTR_ADD,
+ INSTR_SUB,
+ INSTR_AND,
+ INSTR_ORR,
+ INSTR_RSB,
+ INSTR_BIC,
+ INSTR_CMP,
+ INSTR_MOV,
+ INSTR_MVN,
+ INSTR_MUL,
+ INSTR_MLA,
+ INSTR_SMULBB,
+ INSTR_SMULBT,
+ INSTR_SMULTB,
+ INSTR_SMULTT,
+ INSTR_SMULWB,
+ INSTR_SMULWT,
+ INSTR_SMLABB,
+ INSTR_UXTB16,
+ INSTR_UBFX,
+ INSTR_ADDR_ADD,
+ INSTR_ADDR_SUB,
+ INSTR_LDR,
+ INSTR_LDRB,
+ INSTR_LDRH,
+ INSTR_ADDR_LDR,
+ INSTR_LDM,
+ INSTR_STR,
+ INSTR_STRB,
+ INSTR_STRH,
+ INSTR_ADDR_STR,
+ INSTR_STM
+};
+
+enum shift_t
+{
+ SHIFT_LSL,
+ SHIFT_LSR,
+ SHIFT_ASR,
+ SHIFT_ROR,
+ SHIFT_NONE
+};
+
+enum offset_t
+{
+ REG_SCALE_OFFSET,
+ REG_OFFSET,
+ IMM8_OFFSET,
+ IMM12_OFFSET,
+ NO_OFFSET
+};
+
+enum cond_t
+{
+ EQ, NE, CS, CC, MI, PL, VS, VC, HI, LS, GE, LT, GT, LE, AL, NV,
+ HS = CS,
+ LO = CC
+};
+
+const char * cc_code[] =
+{
+ "EQ", "NE", "CS", "CC", "MI", "PL", "VS", "VC",
+ "HI", "LS","GE","LT", "GT", "LE", "AL", "NV"
+};
+
+
+struct dataOpTest_t
+{
+ uint32_t id;
+ instr_t op;
+ uint32_t preFlag;
+ cond_t cond;
+ bool setFlags;
+ uint64_t RnValue;
+ uint64_t RsValue;
+ bool immediate;
+ uint32_t immValue;
+ uint64_t RmValue;
+ uint32_t shiftMode;
+ uint32_t shiftAmount;
+ uint64_t RdValue;
+ bool checkRd;
+ uint64_t postRdValue;
+ bool checkFlag;
+ uint32_t postFlag;
+};
+
+struct dataTransferTest_t
+{
+ uint32_t id;
+ instr_t op;
+ uint32_t preFlag;
+ cond_t cond;
+ bool setMem;
+ uint64_t memOffset;
+ uint64_t memValue;
+ uint64_t RnValue;
+ offset_t offsetType;
+ uint64_t RmValue;
+ uint32_t immValue;
+ bool writeBack;
+ bool preIndex;
+ bool postIndex;
+ uint64_t RdValue;
+ uint64_t postRdValue;
+ uint64_t postRnValue;
+ bool checkMem;
+ uint64_t postMemOffset;
+ uint32_t postMemLength;
+ uint64_t postMemValue;
+};
+
+
+dataOpTest_t dataOpTests [] =
+{
+ {0xA000,INSTR_ADD,AL,AL,0,1,NA,1,MAX_32BIT ,NA,NA,NA,NA,1,0,0,0},
+ {0xA001,INSTR_ADD,AL,AL,0,1,NA,1,MAX_32BIT -1,NA,NA,NA,NA,1,MAX_32BIT,0,0},
+ {0xA002,INSTR_ADD,AL,AL,0,1,NA,0,NA,MAX_32BIT ,NA,NA,NA,1,0,0,0},
+ {0xA003,INSTR_ADD,AL,AL,0,1,NA,0,NA,MAX_32BIT -1,NA,NA,NA,1,MAX_32BIT,0,0},
+ {0xA004,INSTR_ADD,AL,AL,0,1,NA,0,0,MAX_32BIT ,SHIFT_LSL,0,NA,1,0,0,0},
+ {0xA005,INSTR_ADD,AL,AL,0,1,NA,0,0,MAX_32BIT ,SHIFT_LSL,31,NA,1,0x80000001,0,0},
+ {0xA006,INSTR_ADD,AL,AL,0,1,NA,0,0,3,SHIFT_LSR,1,NA,1,2,0,0},
+ {0xA007,INSTR_ADD,AL,AL,0,1,NA,0,0,MAX_32BIT ,SHIFT_LSR,31,NA,1,2,0,0},
+ {0xA008,INSTR_ADD,AL,AL,0,0,NA,0,0,3,SHIFT_ASR,1,NA,1,1,0,0},
+ {0xA009,INSTR_ADD,AL,AL,0,1,NA,0,0,MAX_32BIT ,SHIFT_ASR,31,NA,1,0,0,0},
+ {0xA010,INSTR_AND,AL,AL,0,1,NA,1,MAX_32BIT ,0,0,0,NA,1,1,0,0},
+ {0xA011,INSTR_AND,AL,AL,0,1,NA,1,MAX_32BIT -1,0,0,0,NA,1,0,0,0},
+ {0xA012,INSTR_AND,AL,AL,0,1,NA,0,0,MAX_32BIT ,0,0,NA,1,1,0,0},
+ {0xA013,INSTR_AND,AL,AL,0,1,NA,0,0,MAX_32BIT -1,0,0,NA,1,0,0,0},
+ {0xA014,INSTR_AND,AL,AL,0,1,NA,0,0,MAX_32BIT ,SHIFT_LSL,0,NA,1,1,0,0},
+ {0xA015,INSTR_AND,AL,AL,0,1,NA,0,0,MAX_32BIT ,SHIFT_LSL,31,NA,1,0,0,0},
+ {0xA016,INSTR_AND,AL,AL,0,1,NA,0,0,3,SHIFT_LSR,1,NA,1,1,0,0},
+ {0xA017,INSTR_AND,AL,AL,0,1,NA,0,0,MAX_32BIT ,SHIFT_LSR,31,NA,1,1,0,0},
+ {0xA018,INSTR_AND,AL,AL,0,0,NA,0,0,3,SHIFT_ASR,1,NA,1,0,0,0},
+ {0xA019,INSTR_AND,AL,AL,0,1,NA,0,0,MAX_32BIT ,SHIFT_ASR,31,NA,1,1,0,0},
+ {0xA020,INSTR_ORR,AL,AL,0,3,NA,1,MAX_32BIT ,0,0,0,NA,1,MAX_32BIT,0,0},
+ {0xA021,INSTR_ORR,AL,AL,0,2,NA,1,MAX_32BIT -1,0,0,0,NA,1,MAX_32BIT-1,0,0},
+ {0xA022,INSTR_ORR,AL,AL,0,3,NA,0,0,MAX_32BIT ,0,0,NA,1,MAX_32BIT,0,0},
+ {0xA023,INSTR_ORR,AL,AL,0,2,NA,0,0,MAX_32BIT -1,0,0,NA,1,MAX_32BIT-1,0,0},
+ {0xA024,INSTR_ORR,AL,AL,0,1,NA,0,0,MAX_32BIT ,SHIFT_LSL,0,NA,1,MAX_32BIT,0,0},
+ {0xA025,INSTR_ORR,AL,AL,0,1,NA,0,0,MAX_32BIT ,SHIFT_LSL,31,NA,1,0x80000001,0,0},
+ {0xA026,INSTR_ORR,AL,AL,0,1,NA,0,0,3,SHIFT_LSR,1,NA,1,1,0,0},
+ {0xA027,INSTR_ORR,AL,AL,0,0,NA,0,0,MAX_32BIT ,SHIFT_LSR,31,NA,1,1,0,0},
+ {0xA028,INSTR_ORR,AL,AL,0,0,NA,0,0,3,SHIFT_ASR,1,NA,1,1,0,0},
+ {0xA029,INSTR_ORR,AL,AL,0,1,NA,0,0,MAX_32BIT ,SHIFT_ASR,31,NA,1,MAX_32BIT ,0,0},
+ {0xA030,INSTR_CMP,AL,AL,1,0x10000,NA,1,0x10000,0,0,0,NA,0,0,1,HS},
+ {0xA031,INSTR_CMP,AL,AL,1,0x00000,NA,1,0x10000,0,0,0,NA,0,0,1,CC},
+ {0xA032,INSTR_CMP,AL,AL,1,0x00000,NA,0,0,0x10000,0,0,NA,0,0,1,LT},
+ {0xA033,INSTR_CMP,AL,AL,1,0x10000,NA,0,0,0x10000,0,0,NA,0,0,1,EQ},
+ {0xA034,INSTR_CMP,AL,AL,1,0x00000,NA,0,0,0x10000,0,0,NA,0,0,1,LS},
+ {0xA035,INSTR_CMP,AL,AL,1,0x10000,NA,0,0,0x10000,0,0,NA,0,0,1,LS},
+ {0xA036,INSTR_CMP,AL,AL,1,0x10000,NA,0,0,0x00000,0,0,NA,0,0,1,HI},
+ {0xA037,INSTR_CMP,AL,AL,1,0x10000,NA,0,0,0x10000,0,0,NA,0,0,1,HS},
+ {0xA038,INSTR_CMP,AL,AL,1,0x10000,NA,0,0,0x00000,0,0,NA,0,0,1,HS},
+ {0xA039,INSTR_CMP,AL,AL,1,0x10000,NA,0,0,0x00000,0,0,NA,0,0,1,NE},
+ {0xA040,INSTR_CMP,AL,AL,1,0,NA,0,0,MAX_32BIT ,SHIFT_LSR,1,NA,0,0,1,LT},
+ {0xA041,INSTR_CMP,AL,AL,1,1,NA,0,0,MAX_32BIT ,SHIFT_LSR,31,NA,0,0,1,EQ},
+ {0xA042,INSTR_CMP,AL,AL,1,0,NA,0,0,0x10000,SHIFT_LSR,31,NA,0,0,1,LS},
+ {0xA043,INSTR_CMP,AL,AL,1,0x10000,NA,0,0,0x30000,SHIFT_LSR,1,NA,0,0,1,LS},
+ {0xA044,INSTR_CMP,AL,AL,1,0x10000,NA,0,0,0x00000,SHIFT_LSR,31,NA,0,0,1,HI},
+ {0xA045,INSTR_CMP,AL,AL,1,1,NA,0,0,MAX_32BIT ,SHIFT_LSR,31,NA,0,0,1,HS},
+ {0xA046,INSTR_CMP,AL,AL,1,0x10000,NA,0,0,0x2000,SHIFT_LSR,1,NA,0,0,1,HS},
+ {0xA047,INSTR_CMP,AL,AL,1,0,NA,0,0,MAX_32BIT ,SHIFT_LSR,1,NA,0,0,1,NE},
+ {0xA048,INSTR_CMP,AL,AL,1,0,NA,0,0,0x10000,SHIFT_ASR,2,NA,0,0,1,LT},
+ {0xA049,INSTR_CMP,AL,AL,1,MAX_32BIT ,NA,0,0,MAX_32BIT ,SHIFT_ASR,1,NA,0,0,1,EQ},
+ {0xA050,INSTR_CMP,AL,AL,1,MAX_32BIT ,NA,0,0,MAX_32BIT ,SHIFT_ASR,31,NA,0,0,1,LS},
+ {0xA051,INSTR_CMP,AL,AL,1,0,NA,0,0,0x10000,SHIFT_ASR,1,NA,0,0,1,LS},
+ {0xA052,INSTR_CMP,AL,AL,1,0x10000,NA,0,0,0x10000,SHIFT_ASR,1,NA,0,0,1,HI},
+ {0xA053,INSTR_CMP,AL,AL,1,1,NA,0,0,0x10000,SHIFT_ASR,31,NA,0,0,1,HS},
+ {0xA054,INSTR_CMP,AL,AL,1,1,NA,0,0,0x10000,SHIFT_ASR,16,NA,0,0,1,HS},
+ {0xA055,INSTR_CMP,AL,AL,1,1,NA,0,0,MAX_32BIT ,SHIFT_ASR,1,NA,0,0,1,NE},
+ {0xA056,INSTR_MUL,AL,AL,0,0,0x10000,0,0,0x10000,0,0,NA,1,0,0,0},
+ {0xA057,INSTR_MUL,AL,AL,0,0,0x1000,0,0,0x10000,0,0,NA,1,0x10000000,0,0},
+ {0xA058,INSTR_MUL,AL,AL,0,0,MAX_32BIT ,0,0,1,0,0,NA,1,MAX_32BIT ,0,0},
+ {0xA059,INSTR_MLA,AL,AL,0,0x10000,0x10000,0,0,0x10000,0,0,NA,1,0x10000,0,0},
+ {0xA060,INSTR_MLA,AL,AL,0,0x10000,0x1000,0,0,0x10000,0,0,NA,1,0x10010000,0,0},
+ {0xA061,INSTR_MLA,AL,AL,1,1,MAX_32BIT ,0,0,1,0,0,NA,1,0,1,PL},
+ {0xA062,INSTR_MLA,AL,AL,1,0,MAX_32BIT ,0,0,1,0,0,NA,1,MAX_32BIT ,1,MI},
+ {0xA063,INSTR_SUB,AL,AL,1,1 << 16,NA,1,1 << 16,NA,NA,NA,NA,1,0,1,PL},
+ {0xA064,INSTR_SUB,AL,AL,1,(1 << 16) + 1,NA,1,1 << 16,NA,NA,NA,NA,1,1,1,PL},
+ {0xA065,INSTR_SUB,AL,AL,1,0,NA,1,1 << 16,NA,NA,NA,NA,1,(uint32_t)(0 - (1<<16)),1,MI},
+ {0xA066,INSTR_SUB,MI,MI,0,2,NA,0,NA,1,NA,NA,2,1,1,0,NA},
+ {0xA067,INSTR_SUB,EQ,MI,0,2,NA,0,NA,1,NA,NA,2,1,2,0,NA},
+ {0xA068,INSTR_SUB,GT,GE,0,2,NA,1,1,NA,NA,NA,2,1,1,0,NA},
+ {0xA069,INSTR_SUB,LT,GE,0,2,NA,1,1,NA,NA,NA,2,1,2,0,NA},
+ {0xA070,INSTR_SUB,CS,HS,0,2,NA,1,1,NA,NA,NA,2,1,1,0,NA},
+ {0xA071,INSTR_SUB,CC,HS,0,2,NA,1,1,NA,NA,NA,2,1,2,0,NA},
+ {0xA072,INSTR_SUB,AL,AL,0,1,NA,1,1 << 16,0,0,0,NA,1,(uint32_t)(1 - (1 << 16)),0,NA},
+ {0xA073,INSTR_SUB,AL,AL,0,MAX_32BIT,NA,1,1,0,0,0,NA,1,MAX_32BIT - 1,0,NA},
+ {0xA074,INSTR_SUB,AL,AL,0,1,NA,1,1,0,0,0,NA,1,0,0,NA},
+ {0xA075,INSTR_SUB,AL,AL,0,1,NA,0,NA,1 << 16,0,0,NA,1,(uint32_t)(1 - (1 << 16)),0,NA},
+ {0xA076,INSTR_SUB,AL,AL,0,MAX_32BIT,NA,0,NA,1,0,0,NA,1,MAX_32BIT - 1,0,NA},
+ {0xA077,INSTR_SUB,AL,AL,0,1,NA,0,NA,1,0,0,NA,1,0,0,NA},
+ {0xA078,INSTR_SUB,AL,AL,0,1,NA,0,NA,1,SHIFT_LSL,16,NA,1,(uint32_t)(1 - (1 << 16)),0,NA},
+ {0xA079,INSTR_SUB,AL,AL,0,0x80000001,NA,0,NA,MAX_32BIT ,SHIFT_LSL,31,NA,1,1,0,NA},
+ {0xA080,INSTR_SUB,AL,AL,0,1,NA,0,NA,3,SHIFT_LSR,1,NA,1,0,0,NA},
+ {0xA081,INSTR_SUB,AL,AL,0,1,NA,0,NA,MAX_32BIT ,SHIFT_LSR,31,NA,1,0,0,NA},
+ {0xA082,INSTR_RSB,GT,GE,0,2,NA,1,0,NA,NA,NA,2,1,(uint32_t)-2,0,NA},
+ {0xA083,INSTR_RSB,LT,GE,0,2,NA,1,0,NA,NA,NA,2,1,2,0,NA},
+ {0xA084,INSTR_RSB,AL,AL,0,1,NA,1,1 << 16,NA,NA,NA,NA,1,(1 << 16) - 1,0,NA},
+ {0xA085,INSTR_RSB,AL,AL,0,MAX_32BIT,NA,1,1,NA,NA,NA,NA,1,(uint32_t) (1 - MAX_32BIT),0,NA},
+ {0xA086,INSTR_RSB,AL,AL,0,1,NA,1,1,NA,NA,NA,NA,1,0,0,NA},
+ {0xA087,INSTR_RSB,AL,AL,0,1,NA,0,NA,1 << 16,0,0,NA,1,(1 << 16) - 1,0,NA},
+ {0xA088,INSTR_RSB,AL,AL,0,MAX_32BIT,NA,0,NA,1,0,0,NA,1,(uint32_t) (1 - MAX_32BIT),0,NA},
+ {0xA089,INSTR_RSB,AL,AL,0,1,NA,0,NA,1,0,0,NA,1,0,0,NA},
+ {0xA090,INSTR_RSB,AL,AL,0,1,NA,0,NA,1,SHIFT_LSL,16,NA,1,(1 << 16) - 1,0,NA},
+ {0xA091,INSTR_RSB,AL,AL,0,0x80000001,NA,0,NA,MAX_32BIT ,SHIFT_LSL,31,NA,1,(uint32_t)-1,0,NA},
+ {0xA092,INSTR_RSB,AL,AL,0,1,NA,0,NA,3,SHIFT_LSR,1,NA,1,0,0,NA},
+ {0xA093,INSTR_RSB,AL,AL,0,1,NA,0,NA,MAX_32BIT ,SHIFT_LSR,31,NA,1,0,0,NA},
+ {0xA094,INSTR_MOV,AL,AL,0,NA,NA,1,0x80000001,NA,NA,NA,NA,1,0x80000001,0,0},
+ {0xA095,INSTR_MOV,AL,AL,0,NA,NA,0,0,0x80000001,0,0,NA,1,0x80000001,0,0},
+ {0xA096,INSTR_MOV,AL,AL,0,NA,NA,0,0,MAX_32BIT ,SHIFT_LSL,1,NA,1,MAX_32BIT -1,0,0},
+ {0xA097,INSTR_MOV,AL,AL,0,NA,NA,0,0,MAX_32BIT ,SHIFT_LSL,31,NA,1,0x80000000,0,0},
+ {0xA098,INSTR_MOV,AL,AL,0,NA,NA,0,0,3,SHIFT_LSR,1,NA,1,1,0,0},
+ {0xA099,INSTR_MOV,AL,AL,0,NA,NA,0,0,MAX_32BIT ,SHIFT_LSR,31,NA,1,1,0,0},
+ {0xA100,INSTR_MOV,AL,AL,0,NA,NA,0,0,3,SHIFT_ASR,1,NA,1,1,0,0},
+ {0xA101,INSTR_MOV,AL,AL,0,NA,NA,0,0,MAX_32BIT ,SHIFT_ASR,31,NA,1,MAX_32BIT ,0,0},
+ {0xA102,INSTR_MOV,AL,AL,0,NA,NA,0,0,3,SHIFT_ROR,1,NA,1,0x80000001,0,0},
+ {0xA103,INSTR_MOV,AL,AL,0,NA,NA,0,0,0x80000001,SHIFT_ROR,31,NA,1,3,0,0},
+ {0xA104,INSTR_MOV,AL,AL,1,NA,NA,0,0,MAX_32BIT -1,SHIFT_ASR,1,NA,1,MAX_32BIT,1,MI},
+ {0xA105,INSTR_MOV,AL,AL,1,NA,NA,0,0,3,SHIFT_ASR,1,NA,1,1,1,PL},
+ {0xA106,INSTR_MOV,PL,MI,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2,0,0},
+ {0xA107,INSTR_MOV,MI,MI,0,NA,NA,0,0,0x80000001,0,0,2,1,0x80000001,0,0},
+ {0xA108,INSTR_MOV,EQ,LT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2,0,0},
+ {0xA109,INSTR_MOV,LT,LT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,0x80000001,0,0},
+ {0xA110,INSTR_MOV,GT,GE,0,NA,NA,0,0,MAX_32BIT ,SHIFT_LSL,1,2,1,MAX_32BIT -1,0,0},
+ {0xA111,INSTR_MOV,EQ,GE,0,NA,NA,0,0,MAX_32BIT ,SHIFT_LSL,31,2,1,0x80000000,0,0},
+ {0xA112,INSTR_MOV,LT,GE,0,NA,NA,0,0,MAX_32BIT ,SHIFT_LSL,31,2,1,2,0,0},
+ {0xA113,INSTR_MOV,GT,LE,0,NA,NA,0,0,MAX_32BIT ,SHIFT_LSL,1,2,1,2,0,0},
+ {0xA114,INSTR_MOV,EQ,LE,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,0x80000001,0,0},
+ {0xA115,INSTR_MOV,LT,LE,0,NA,NA,0,0,MAX_32BIT ,SHIFT_LSL,31,2,1,0x80000000,0,0},
+ {0xA116,INSTR_MOV,EQ,GT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2,0,0},
+ {0xA117,INSTR_MOV,GT,GT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,0x80000001,0,0},
+ {0xA118,INSTR_MOV,LE,GT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2,0,0},
+ {0xA119,INSTR_MOV,EQ,GT,0,NA,NA,0,0,0x80000001,0,0,2,1,2,0,0},
+ {0xA120,INSTR_MOV,GT,GT,0,NA,NA,0,0,0x80000001,0,0,2,1,0x80000001,0,0},
+ {0xA121,INSTR_MOV,LE,GT,0,NA,NA,0,0,0x80000001,0,0,2,1,2,0,0},
+ {0xA122,INSTR_MOV,EQ,GT,0,NA,NA,0,0,MAX_32BIT ,SHIFT_LSL,1,2,1,2,0,0},
+ {0xA123,INSTR_MOV,GT,GT,0,NA,NA,0,0,MAX_32BIT ,SHIFT_LSL,1,2,1,MAX_32BIT -1,0,0},
+ {0xA124,INSTR_MOV,LE,GT,0,NA,NA,0,0,MAX_32BIT ,SHIFT_LSL,1,2,1,2,0,0},
+ {0xA125,INSTR_MOV,LO,HS,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2,0,0},
+ {0xA126,INSTR_MOV,HS,HS,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,0x80000001,0,0},
+ {0xA127,INSTR_MVN,LO,HS,0,NA,NA,1,MAX_32BIT -1,NA,NA,NA,2,1,2,0,0},
+ {0xA128,INSTR_MVN,HS,HS,0,NA,NA,1,MAX_32BIT -1,NA,NA,NA,2,1,1,0,0},
+ {0xA129,INSTR_MVN,AL,AL,0,NA,NA,1,0,NA,NA,NA,2,1,MAX_32BIT,0,NA},
+ {0xA130,INSTR_MVN,AL,AL,0,NA,NA,0,NA,MAX_32BIT -1,NA,0,2,1,1,0,NA},
+ {0xA131,INSTR_MVN,AL,AL,0,NA,NA,0,NA,0x80000001,NA,0,2,1,0x7FFFFFFE,0,NA},
+ {0xA132,INSTR_BIC,AL,AL,0,1,NA,1,MAX_32BIT ,NA,NA,NA,NA,1,0,0,0},
+ {0xA133,INSTR_BIC,AL,AL,0,1,NA,1,MAX_32BIT -1,NA,NA,NA,NA,1,1,0,0},
+ {0xA134,INSTR_BIC,AL,AL,0,1,NA,0,0,MAX_32BIT ,0,0,NA,1,0,0,0},
+ {0xA135,INSTR_BIC,AL,AL,0,1,NA,0,0,MAX_32BIT -1,0,0,NA,1,1,0,0},
+ {0xA136,INSTR_BIC,AL,AL,0,0xF0,NA,0,0,3,SHIFT_ASR,1,NA,1,0xF0,0,0},
+ {0xA137,INSTR_BIC,AL,AL,0,0xF0,NA,0,0,MAX_32BIT ,SHIFT_ASR,31,NA,1,0,0,0},
+ {0xA138,INSTR_SMULBB,AL,AL,0,NA,0xABCDFFFF,0,NA,0xABCD0001,NA,NA,NA,1,0xFFFFFFFF,0,0},
+ {0xA139,INSTR_SMULBB,AL,AL,0,NA,0xABCD0001,0,NA,0xABCD0FFF,NA,NA,NA,1,0x00000FFF,0,0},
+ {0xA140,INSTR_SMULBB,AL,AL,0,NA,0xABCD0001,0,NA,0xABCDFFFF,NA,NA,NA,1,0xFFFFFFFF,0,0},
+ {0xA141,INSTR_SMULBB,AL,AL,0,NA,0xABCDFFFF,0,NA,0xABCDFFFF,NA,NA,NA,1,1,0,0},
+ {0xA142,INSTR_SMULBT,AL,AL,0,NA,0xFFFFABCD,0,NA,0xABCD0001,NA,NA,NA,1,0xFFFFFFFF,0,0},
+ {0xA143,INSTR_SMULBT,AL,AL,0,NA,0x0001ABCD,0,NA,0xABCD0FFF,NA,NA,NA,1,0x00000FFF,0,0},
+ {0xA144,INSTR_SMULBT,AL,AL,0,NA,0x0001ABCD,0,NA,0xABCDFFFF,NA,NA,NA,1,0xFFFFFFFF,0,0},
+ {0xA145,INSTR_SMULBT,AL,AL,0,NA,0xFFFFABCD,0,NA,0xABCDFFFF,NA,NA,NA,1,1,0,0},
+ {0xA146,INSTR_SMULTB,AL,AL,0,NA,0xABCDFFFF,0,NA,0x0001ABCD,NA,NA,NA,1,0xFFFFFFFF,0,0},
+ {0xA147,INSTR_SMULTB,AL,AL,0,NA,0xABCD0001,0,NA,0x0FFFABCD,NA,NA,NA,1,0x00000FFF,0,0},
+ {0xA148,INSTR_SMULTB,AL,AL,0,NA,0xABCD0001,0,NA,0xFFFFABCD,NA,NA,NA,1,0xFFFFFFFF,0,0},
+ {0xA149,INSTR_SMULTB,AL,AL,0,NA,0xABCDFFFF,0,NA,0xFFFFABCD,NA,NA,NA,1,1,0,0},
+ {0xA150,INSTR_SMULTT,AL,AL,0,NA,0xFFFFABCD,0,NA,0x0001ABCD,NA,NA,NA,1,0xFFFFFFFF,0,0},
+ {0xA151,INSTR_SMULTT,AL,AL,0,NA,0x0001ABCD,0,NA,0x0FFFABCD,NA,NA,NA,1,0x00000FFF,0,0},
+ {0xA152,INSTR_SMULTT,AL,AL,0,NA,0x0001ABCD,0,NA,0xFFFFABCD,NA,NA,NA,1,0xFFFFFFFF,0,0},
+ {0xA153,INSTR_SMULTT,AL,AL,0,NA,0xFFFFABCD,0,NA,0xFFFFABCD,NA,NA,NA,1,1,0,0},
+ {0xA154,INSTR_SMULWB,AL,AL,0,NA,0xABCDFFFF,0,NA,0x0001ABCD,NA,NA,NA,1,0xFFFFFFFE,0,0},
+ {0xA155,INSTR_SMULWB,AL,AL,0,NA,0xABCD0001,0,NA,0x0FFFABCD,NA,NA,NA,1,0x00000FFF,0,0},
+ {0xA156,INSTR_SMULWB,AL,AL,0,NA,0xABCD0001,0,NA,0xFFFFABCD,NA,NA,NA,1,0xFFFFFFFF,0,0},
+ {0xA157,INSTR_SMULWB,AL,AL,0,NA,0xABCDFFFF,0,NA,0xFFFFABCD,NA,NA,NA,1,0,0,0},
+ {0xA158,INSTR_SMULWT,AL,AL,0,NA,0xFFFFABCD,0,NA,0x0001ABCD,NA,NA,NA,1,0xFFFFFFFE,0,0},
+ {0xA159,INSTR_SMULWT,AL,AL,0,NA,0x0001ABCD,0,NA,0x0FFFABCD,NA,NA,NA,1,0x00000FFF,0,0},
+ {0xA160,INSTR_SMULWT,AL,AL,0,NA,0x0001ABCD,0,NA,0xFFFFABCD,NA,NA,NA,1,0xFFFFFFFF,0,0},
+ {0xA161,INSTR_SMULWT,AL,AL,0,NA,0xFFFFABCD,0,NA,0xFFFFABCD,NA,NA,NA,1,0,0,0},
+ {0xA162,INSTR_SMLABB,AL,AL,0,1,0xABCDFFFF,0,NA,0xABCD0001,NA,NA,NA,1,0,0,0},
+ {0xA163,INSTR_SMLABB,AL,AL,0,1,0xABCD0001,0,NA,0xABCD0FFF,NA,NA,NA,1,0x00001000,0,0},
+ {0xA164,INSTR_SMLABB,AL,AL,0,0xFFFFFFFF,0xABCD0001,0,NA,0xABCDFFFF,NA,NA,NA,1,0xFFFFFFFE,0,0},
+ {0xA165,INSTR_SMLABB,AL,AL,0,0xFFFFFFFF,0xABCDFFFF,0,NA,0xABCDFFFF,NA,NA,NA,1,0,0,0},
+ {0xA166,INSTR_UXTB16,AL,AL,0,NA,NA,0,NA,0xABCDEF01,SHIFT_ROR,0,NA,1,0x00CD0001,0,0},
+ {0xA167,INSTR_UXTB16,AL,AL,0,NA,NA,0,NA,0xABCDEF01,SHIFT_ROR,1,NA,1,0x00AB00EF,0,0},
+ {0xA168,INSTR_UXTB16,AL,AL,0,NA,NA,0,NA,0xABCDEF01,SHIFT_ROR,2,NA,1,0x000100CD,0,0},
+ {0xA169,INSTR_UXTB16,AL,AL,0,NA,NA,0,NA,0xABCDEF01,SHIFT_ROR,3,NA,1,0x00EF00AB,0,0},
+ {0xA170,INSTR_UBFX,AL,AL,0,0xABCDEF01,4,0,NA,24,NA,NA,NA,1,0x00BCDEF0,0,0},
+ {0xA171,INSTR_UBFX,AL,AL,0,0xABCDEF01,1,0,NA,2,NA,NA,NA,1,0,0,0},
+ {0xA172,INSTR_UBFX,AL,AL,0,0xABCDEF01,16,0,NA,8,NA,NA,NA,1,0xCD,0,0},
+ {0xA173,INSTR_UBFX,AL,AL,0,0xABCDEF01,31,0,NA,1,NA,NA,NA,1,1,0,0},
+ {0xA174,INSTR_ADDR_ADD,AL,AL,0,0xCFFFFFFFF,NA,0,NA,0x1,SHIFT_LSL,1,NA,1,0xD00000001,0,0},
+ {0xA175,INSTR_ADDR_ADD,AL,AL,0,0x01,NA,0,NA,0x1,SHIFT_LSL,2,NA,1,0x5,0,0},
+ {0xA176,INSTR_ADDR_ADD,AL,AL,0,0xCFFFFFFFF,NA,0,NA,0x1,NA,0,NA,1,0xD00000000,0,0},
+ {0xA177,INSTR_ADDR_SUB,AL,AL,0,0xD00000001,NA,0,NA,0x010000,SHIFT_LSR,15,NA,1,0xCFFFFFFFF,0,0},
+ {0xA178,INSTR_ADDR_SUB,AL,AL,0,0xCFFFFFFFF,NA,0,NA,0x020000,SHIFT_LSR,15,NA,1,0xCFFFFFFFB,0,0},
+ {0xA179,INSTR_ADDR_SUB,AL,AL,0,3,NA,0,NA,0x010000,SHIFT_LSR,15,NA,1,1,0,0},
+};
+
+dataTransferTest_t dataTransferTests [] =
+{
+ {0xB000,INSTR_LDR,AL,AL,1,24,0xABCDEF0123456789,0,REG_SCALE_OFFSET,24,NA,NA,NA,NA,NA,0x23456789,0,0,NA,NA,NA},
+ {0xB001,INSTR_LDR,AL,AL,1,4064,0xABCDEF0123456789,0,IMM12_OFFSET,NA,4068,0,1,0,NA,0xABCDEF01,0,0,NA,NA,NA},
+ {0xB002,INSTR_LDR,AL,AL,1,0,0xABCDEF0123456789,0,IMM12_OFFSET,NA,4,1,0,1,NA,0x23456789,4,0,NA,NA,NA},
+ {0xB003,INSTR_LDR,AL,AL,1,0,0xABCDEF0123456789,0,NO_OFFSET,NA,NA,0,0,0,NA,0x23456789,0,0,NA,NA,NA},
+ {0xB004,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,0,REG_SCALE_OFFSET,4064,NA,NA,NA,NA,NA,0x89,0,0,NA,NA,NA},
+ {0xB005,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,0,IMM12_OFFSET,NA,4065,0,1,0,NA,0x67,0,0,NA,NA,NA},
+ {0xB006,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,4065,IMM12_OFFSET,NA,0,0,1,0,NA,0x67,4065,0,NA,NA,NA},
+ {0xB007,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,4065,IMM12_OFFSET,NA,1,0,1,0,NA,0x45,4065,0,NA,NA,NA},
+ {0xB008,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,4065,IMM12_OFFSET,NA,2,0,1,0,NA,0x23,4065,0,NA,NA,NA},
+ {0xB009,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,4065,IMM12_OFFSET,NA,1,1,0,1,NA,0x67,4066,0,NA,NA,NA},
+ {0xB010,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,0,NO_OFFSET,NA,NA,0,0,0,NA,0x89,0,0,NA,NA,NA},
+ {0xB011,INSTR_LDRH,AL,AL,1,0,0xABCDEF0123456789,0,IMM8_OFFSET,NA,2,1,0,1,NA,0x6789,2,0,NA,NA,NA},
+ {0xB012,INSTR_LDRH,AL,AL,1,4064,0xABCDEF0123456789,0,REG_OFFSET,4064,0,0,1,0,NA,0x6789,0,0,NA,NA,NA},
+ {0xB013,INSTR_LDRH,AL,AL,1,4064,0xABCDEF0123456789,0,REG_OFFSET,4066,0,0,1,0,NA,0x2345,0,0,NA,NA,NA},
+ {0xB014,INSTR_LDRH,AL,AL,1,0,0xABCDEF0123456789,0,NO_OFFSET,NA,0,0,0,0,NA,0x6789,0,0,NA,NA,NA},
+ {0xB015,INSTR_LDRH,AL,AL,1,0,0xABCDEF0123456789,2,NO_OFFSET,NA,0,0,0,0,NA,0x2345,2,0,NA,NA,NA},
+ {0xB016,INSTR_ADDR_LDR,AL,AL,1,4064,0xABCDEF0123456789,0,IMM12_OFFSET,NA,4064,0,1,0,NA,0xABCDEF0123456789,0,0,NA,NA,NA},
+ {0xB017,INSTR_STR,AL,AL,1,2,0xDEADBEEFDEADBEEF,4,IMM12_OFFSET,NA,4,1,0,1,0xABCDEF0123456789,0xABCDEF0123456789,8,1,2,8,0xDEAD23456789BEEF},
+ {0xB018,INSTR_STR,AL,AL,1,2,0xDEADBEEFDEADBEEF,4,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4,1,2,8,0xDEAD23456789BEEF},
+ {0xB019,INSTR_STR,AL,AL,1,4066,0xDEADBEEFDEADBEEF,4,IMM12_OFFSET,NA,4064,0,1,0,0xABCDEF0123456789,0xABCDEF0123456789,4,1,4066,8,0xDEAD23456789BEEF},
+ {0xB020,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,IMM12_OFFSET,NA,0,0,1,0,0xABCDEF0123456789,0xABCDEF0123456789,1,1,0,8,0xDEADBEEFDEAD89EF},
+ {0xB021,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,IMM12_OFFSET,NA,1,0,1,0,0xABCDEF0123456789,0xABCDEF0123456789,1,1,0,8,0xDEADBEEFDE89BEEF},
+ {0xB022,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,IMM12_OFFSET,NA,2,0,1,0,0xABCDEF0123456789,0xABCDEF0123456789,1,1,0,8,0xDEADBEEF89ADBEEF},
+ {0xB023,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,IMM12_OFFSET,NA,4,1,0,1,0xABCDEF0123456789,0xABCDEF0123456789,5,1,0,8,0xDEADBEEFDEAD89EF},
+ {0xB024,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,1,1,0,8,0xDEADBEEFDEAD89EF},
+ {0xB025,INSTR_STRH,AL,AL,1,4066,0xDEADBEEFDEADBEEF,4070,IMM12_OFFSET,NA,2,1,0,1,0xABCDEF0123456789,0xABCDEF0123456789,4072,1,4066,8,0xDEAD6789DEADBEEF},
+ {0xB026,INSTR_STRH,AL,AL,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEAD6789DEADBEEF},
+ {0xB027,INSTR_STRH,EQ,NE,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEADBEEFDEADBEEF},
+ {0xB028,INSTR_STRH,NE,NE,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEAD6789DEADBEEF},
+ {0xB029,INSTR_STRH,NE,EQ,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEADBEEFDEADBEEF},
+ {0xB030,INSTR_STRH,EQ,EQ,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEAD6789DEADBEEF},
+ {0xB031,INSTR_STRH,HI,LS,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEADBEEFDEADBEEF},
+ {0xB032,INSTR_STRH,LS,LS,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEAD6789DEADBEEF},
+ {0xB033,INSTR_STRH,LS,HI,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEADBEEFDEADBEEF},
+ {0xB034,INSTR_STRH,HI,HI,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEAD6789DEADBEEF},
+ {0xB035,INSTR_STRH,CC,HS,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEADBEEFDEADBEEF},
+ {0xB036,INSTR_STRH,CS,HS,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEAD6789DEADBEEF},
+ {0xB037,INSTR_STRH,GE,LT,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEADBEEFDEADBEEF},
+ {0xB038,INSTR_STRH,LT,LT,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEAD6789DEADBEEF},
+ {0xB039,INSTR_ADDR_STR,AL,AL,1,4064,0xDEADBEEFDEADBEEF,4,IMM12_OFFSET,NA,4060,0,1,0,0xABCDEF0123456789,0xABCDEF0123456789,4,1,4064,8,0xABCDEF0123456789},
+};
+
+
+void flushcache()
+{
+ const long base = long(instrMem);
+ const long curr = base + long(instrMemSize);
+ __builtin___clear_cache((char*)base, (char*)curr);
+}
+void dataOpTest(dataOpTest_t test, ARMAssemblerInterface *a64asm, uint32_t Rd = 0,
+ uint32_t Rn = 1, uint32_t Rm = 2, uint32_t Rs = 3)
+{
+ int64_t regs[NUM_REGS] = {0};
+ int32_t flags[NUM_FLAGS] = {0};
+ int64_t savedRegs[NUM_REGS] = {0};
+ uint32_t i;
+ uint32_t op2;
+
+ for(i = 0; i < NUM_REGS; ++i)
+ {
+ regs[i] = i;
+ }
+
+ regs[Rd] = test.RdValue;
+ regs[Rn] = test.RnValue;
+ regs[Rs] = test.RsValue;
+ flags[test.preFlag] = 1;
+ a64asm->reset();
+ a64asm->prolog();
+ if(test.immediate == true)
+ {
+ op2 = a64asm->imm(test.immValue);
+ }
+ else if(test.immediate == false && test.shiftAmount == 0)
+ {
+ op2 = Rm;
+ regs[Rm] = test.RmValue;
+ }
+ else
+ {
+ op2 = a64asm->reg_imm(Rm, test.shiftMode, test.shiftAmount);
+ regs[Rm] = test.RmValue;
+ }
+ switch(test.op)
+ {
+ case INSTR_ADD: a64asm->ADD(test.cond, test.setFlags, Rd,Rn,op2); break;
+ case INSTR_SUB: a64asm->SUB(test.cond, test.setFlags, Rd,Rn,op2); break;
+ case INSTR_RSB: a64asm->RSB(test.cond, test.setFlags, Rd,Rn,op2); break;
+ case INSTR_AND: a64asm->AND(test.cond, test.setFlags, Rd,Rn,op2); break;
+ case INSTR_ORR: a64asm->ORR(test.cond, test.setFlags, Rd,Rn,op2); break;
+ case INSTR_BIC: a64asm->BIC(test.cond, test.setFlags, Rd,Rn,op2); break;
+ case INSTR_MUL: a64asm->MUL(test.cond, test.setFlags, Rd,Rm,Rs); break;
+ case INSTR_MLA: a64asm->MLA(test.cond, test.setFlags, Rd,Rm,Rs,Rn); break;
+ case INSTR_CMP: a64asm->CMP(test.cond, Rn,op2); break;
+ case INSTR_MOV: a64asm->MOV(test.cond, test.setFlags,Rd,op2); break;
+ case INSTR_MVN: a64asm->MVN(test.cond, test.setFlags,Rd,op2); break;
+ case INSTR_SMULBB:a64asm->SMULBB(test.cond, Rd,Rm,Rs); break;
+ case INSTR_SMULBT:a64asm->SMULBT(test.cond, Rd,Rm,Rs); break;
+ case INSTR_SMULTB:a64asm->SMULTB(test.cond, Rd,Rm,Rs); break;
+ case INSTR_SMULTT:a64asm->SMULTT(test.cond, Rd,Rm,Rs); break;
+ case INSTR_SMULWB:a64asm->SMULWB(test.cond, Rd,Rm,Rs); break;
+ case INSTR_SMULWT:a64asm->SMULWT(test.cond, Rd,Rm,Rs); break;
+ case INSTR_SMLABB:a64asm->SMLABB(test.cond, Rd,Rm,Rs,Rn); break;
+ case INSTR_UXTB16:a64asm->UXTB16(test.cond, Rd,Rm,test.shiftAmount); break;
+ case INSTR_UBFX:
+ {
+ int32_t lsb = test.RsValue;
+ int32_t width = test.RmValue;
+ a64asm->UBFX(test.cond, Rd,Rn,lsb, width);
+ break;
+ }
+ case INSTR_ADDR_ADD: a64asm->ADDR_ADD(test.cond, test.setFlags, Rd,Rn,op2); break;
+ case INSTR_ADDR_SUB: a64asm->ADDR_SUB(test.cond, test.setFlags, Rd,Rn,op2); break;
+ default: printf("Error"); return;
+ }
+ a64asm->epilog(0);
+ flushcache();
+
+ asm_function_t asm_function = (asm_function_t)(instrMem);
+
+ for(i = 0; i < NUM_REGS; ++i)
+ savedRegs[i] = regs[i];
+
+ asm_test_jacket(asm_function, regs, flags);
+
+ /* Check if all regs except Rd is same */
+ for(i = 0; i < NUM_REGS; ++i)
+ {
+ if(i == Rd) continue;
+ if(regs[i] != savedRegs[i])
+ {
+ printf("Test %x failed Reg(%d) tampered Expected(0x%" PRIx64 "),"
+ "Actual(0x%" PRIx64 ") t\n", test.id, i, savedRegs[i],
+ regs[i]);
+ return;
+ }
+ }
+
+ if(test.checkRd == 1 && (uint64_t)regs[Rd] != test.postRdValue)
+ {
+ printf("Test %x failed, Expected(%" PRIx64 "), Actual(%" PRIx64 ")\n",
+ test.id, test.postRdValue, regs[Rd]);
+ }
+ else if(test.checkFlag == 1 && flags[test.postFlag] == 0)
+ {
+ printf("Test %x failed Flag(%s) NOT set\n",
+ test.id,cc_code[test.postFlag]);
+ }
+ else
+ {
+ printf("Test %x passed\n", test.id);
+ }
+}
+
+
+void dataTransferTest(dataTransferTest_t test, ARMAssemblerInterface *a64asm,
+ uint32_t Rd = 0, uint32_t Rn = 1,uint32_t Rm = 2)
+{
+ int64_t regs[NUM_REGS] = {0};
+ int64_t savedRegs[NUM_REGS] = {0};
+ int32_t flags[NUM_FLAGS] = {0};
+ uint32_t i;
+ for(i = 0; i < NUM_REGS; ++i)
+ {
+ regs[i] = i;
+ }
+
+ uint32_t op2;
+
+ regs[Rd] = test.RdValue;
+ regs[Rn] = (uint64_t)(&dataMem[test.RnValue]);
+ regs[Rm] = test.RmValue;
+ flags[test.preFlag] = 1;
+
+ if(test.setMem == true)
+ {
+ unsigned char *mem = (unsigned char *)&dataMem[test.memOffset];
+ uint64_t value = test.memValue;
+ for(int j = 0; j < 8; ++j)
+ {
+ mem[j] = value & 0x00FF;
+ value >>= 8;
+ }
+ }
+ a64asm->reset();
+ a64asm->prolog();
+ if(test.offsetType == REG_SCALE_OFFSET)
+ {
+ op2 = a64asm->reg_scale_pre(Rm);
+ }
+ else if(test.offsetType == REG_OFFSET)
+ {
+ op2 = a64asm->reg_pre(Rm);
+ }
+ else if(test.offsetType == IMM12_OFFSET && test.preIndex == true)
+ {
+ op2 = a64asm->immed12_pre(test.immValue, test.writeBack);
+ }
+ else if(test.offsetType == IMM12_OFFSET && test.postIndex == true)
+ {
+ op2 = a64asm->immed12_post(test.immValue);
+ }
+ else if(test.offsetType == IMM8_OFFSET && test.preIndex == true)
+ {
+ op2 = a64asm->immed8_pre(test.immValue, test.writeBack);
+ }
+ else if(test.offsetType == IMM8_OFFSET && test.postIndex == true)
+ {
+ op2 = a64asm->immed8_post(test.immValue);
+ }
+ else if(test.offsetType == NO_OFFSET)
+ {
+ op2 = a64asm->__immed12_pre(0);
+ }
+ else
+ {
+ printf("Error - Unknown offset\n"); return;
+ }
+
+ switch(test.op)
+ {
+ case INSTR_LDR: a64asm->LDR(test.cond, Rd,Rn,op2); break;
+ case INSTR_LDRB: a64asm->LDRB(test.cond, Rd,Rn,op2); break;
+ case INSTR_LDRH: a64asm->LDRH(test.cond, Rd,Rn,op2); break;
+ case INSTR_ADDR_LDR: a64asm->ADDR_LDR(test.cond, Rd,Rn,op2); break;
+ case INSTR_STR: a64asm->STR(test.cond, Rd,Rn,op2); break;
+ case INSTR_STRB: a64asm->STRB(test.cond, Rd,Rn,op2); break;
+ case INSTR_STRH: a64asm->STRH(test.cond, Rd,Rn,op2); break;
+ case INSTR_ADDR_STR: a64asm->ADDR_STR(test.cond, Rd,Rn,op2); break;
+ default: printf("Error"); return;
+ }
+ a64asm->epilog(0);
+ flushcache();
+
+ asm_function_t asm_function = (asm_function_t)(instrMem);
+
+ for(i = 0; i < NUM_REGS; ++i)
+ savedRegs[i] = regs[i];
+
+
+ asm_test_jacket(asm_function, regs, flags);
+
+ /* Check if all regs except Rd/Rn are same */
+ for(i = 0; i < NUM_REGS; ++i)
+ {
+ if(i == Rd || i == Rn) continue;
+ if(regs[i] != savedRegs[i])
+ {
+ printf("Test %x failed Reg(%d) tampered"
+ " Expected(0x%" PRIx64 "), Actual(0x%" PRIx64 ") t\n",
+ test.id, i, savedRegs[i], regs[i]);
+ return;
+ }
+ }
+
+ if((uint64_t)regs[Rd] != test.postRdValue)
+ {
+ printf("Test %x failed, "
+ "Expected in Rd(0x%" PRIx64 "), Actual(0x%" PRIx64 ")\n",
+ test.id, test.postRdValue, regs[Rd]);
+ }
+ else if((uint64_t)regs[Rn] != (uint64_t)(&dataMem[test.postRnValue]))
+ {
+ printf("Test %x failed, "
+ "Expected in Rn(0x%" PRIx64 "), Actual(0x%" PRIx64 ")\n",
+ test.id, test.postRnValue, regs[Rn] - (uint64_t)dataMem);
+ }
+ else if(test.checkMem == true)
+ {
+ unsigned char *addr = (unsigned char *)&dataMem[test.postMemOffset];
+ uint64_t value;
+ value = 0;
+ for(uint32_t j = 0; j < test.postMemLength; ++j)
+ value = (value << 8) | addr[test.postMemLength-j-1];
+ if(value != test.postMemValue)
+ {
+ printf("Test %x failed, "
+ "Expected in Mem(0x%" PRIx64 "), Actual(0x%" PRIx64 ")\n",
+ test.id, test.postMemValue, value);
+ }
+ else
+ {
+ printf("Test %x passed\n", test.id);
+ }
+ }
+ else
+ {
+ printf("Test %x passed\n", test.id);
+ }
+}
+
+void dataTransferLDMSTM(ARMAssemblerInterface *a64asm)
+{
+ int64_t regs[NUM_REGS] = {0};
+ int32_t flags[NUM_FLAGS] = {0};
+ const uint32_t numArmv7Regs = 16;
+
+ uint32_t Rn = ARMAssemblerInterface::SP;
+
+ uint32_t patterns[] =
+ {
+ 0x5A03,
+ 0x4CF0,
+ 0x1EA6,
+ 0x0DBF,
+ };
+
+ uint32_t i, j;
+ for(i = 0; i < sizeof(patterns)/sizeof(uint32_t); ++i)
+ {
+ for(j = 0; j < NUM_REGS; ++j)
+ {
+ regs[j] = j;
+ }
+ a64asm->reset();
+ a64asm->prolog();
+ a64asm->STM(AL,ARMAssemblerInterface::DB,Rn,1,patterns[i]);
+ for(j = 0; j < numArmv7Regs; ++j)
+ {
+ uint32_t op2 = a64asm->imm(0x31);
+ a64asm->MOV(AL, 0,j,op2);
+ }
+ a64asm->LDM(AL,ARMAssemblerInterface::IA,Rn,1,patterns[i]);
+ a64asm->epilog(0);
+ flushcache();
+
+ asm_function_t asm_function = (asm_function_t)(instrMem);
+ asm_test_jacket(asm_function, regs, flags);
+
+ for(j = 0; j < numArmv7Regs; ++j)
+ {
+ if((1 << j) & patterns[i])
+ {
+ if(regs[j] != j)
+ {
+ printf("LDM/STM Test %x failed "
+ "Reg%d expected(0x%x) Actual(0x%" PRIx64 ") \n",
+ patterns[i], j, j, regs[j]);
+ break;
+ }
+ }
+ }
+ if(j == numArmv7Regs)
+ printf("LDM/STM Test %x passed\n", patterns[i]);
+ }
+}
+
+int main(void)
+{
+ uint32_t i;
+
+ /* Allocate memory to store instructions generated by ArmToArm64Assembler */
+ {
+ int fd = ashmem_create_region("code cache", instrMemSize);
+ if(fd < 0)
+ printf("Creating code cache, ashmem_create_region "
+ "failed with error '%s'", strerror(errno));
+ instrMem = mmap(NULL, instrMemSize,
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_PRIVATE, fd, 0);
+ }
+
+ ArmToArm64Assembler a64asm(instrMem);
+
+ if(TESTS_DATAOP_ENABLE)
+ {
+ printf("Running data processing tests\n");
+ for(i = 0; i < sizeof(dataOpTests)/sizeof(dataOpTest_t); ++i)
+ dataOpTest(dataOpTests[i], &a64asm);
+ }
+
+ if(TESTS_DATATRANSFER_ENABLE)
+ {
+ printf("Running data transfer tests\n");
+ for(i = 0; i < sizeof(dataTransferTests)/sizeof(dataTransferTest_t); ++i)
+ dataTransferTest(dataTransferTests[i], &a64asm);
+ }
+
+ if(TESTS_LDMSTM_ENABLE)
+ {
+ printf("Running LDM/STM tests\n");
+ dataTransferLDMSTM(&a64asm);
+ }
+
+
+ if(TESTS_REG_CORRUPTION_ENABLE)
+ {
+ uint32_t reg_list[] = {0,1,12,14};
+ uint32_t Rd, Rm, Rs, Rn;
+ uint32_t i;
+ uint32_t numRegs = sizeof(reg_list)/sizeof(uint32_t);
+
+ printf("Running Register corruption tests\n");
+ for(i = 0; i < sizeof(dataOpTests)/sizeof(dataOpTest_t); ++i)
+ {
+ for(Rd = 0; Rd < numRegs; ++Rd)
+ {
+ for(Rn = 0; Rn < numRegs; ++Rn)
+ {
+ for(Rm = 0; Rm < numRegs; ++Rm)
+ {
+ for(Rs = 0; Rs < numRegs;++Rs)
+ {
+ if(Rd == Rn || Rd == Rm || Rd == Rs) continue;
+ if(Rn == Rm || Rn == Rs) continue;
+ if(Rm == Rs) continue;
+ printf("Testing combination Rd(%d), Rn(%d),"
+ " Rm(%d), Rs(%d): ",
+ reg_list[Rd], reg_list[Rn], reg_list[Rm], reg_list[Rs]);
+ dataOpTest(dataOpTests[i], &a64asm, reg_list[Rd],
+ reg_list[Rn], reg_list[Rm], reg_list[Rs]);
+ }
+ }
+ }
+ }
+ }
+ }
+ return 0;
+}
diff --git a/libpixelflinger/tests/arch-arm64/assembler/asm_test_jacket.S b/libpixelflinger/tests/arch-arm64/assembler/asm_test_jacket.S
new file mode 100644
index 0000000..a1392c2
--- /dev/null
+++ b/libpixelflinger/tests/arch-arm64/assembler/asm_test_jacket.S
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+ .text
+ .align
+
+ .global asm_test_jacket
+
+ // Set the register and flag values
+ // Calls the asm function
+ // Reads the register/flag values to output register
+
+ // Parameters
+ // X0 - Function to jump
+ // X1 - register values array
+ // X2 - flag values array
+asm_test_jacket:
+ // Save registers to stack
+ stp x29, x30, [sp,#-16]!
+ stp x27, x28, [sp,#-16]!
+
+ mov x30, x0
+ mov x28, x1
+ mov x27, x2
+
+ //Set the flags based on flag array
+ //EQ
+ ldr w0, [x27,#0]
+ cmp w0, #1
+ b.ne bt_aeq
+ cmp w0,#1
+ b bt_end
+bt_aeq:
+
+ //NE
+ ldr w0, [x27,#4]
+ cmp w0, #1
+ b.ne bt_ane
+ cmp w0,#2
+ b bt_end
+bt_ane:
+
+ //CS
+ ldr w0, [x27,#8]
+ cmp w0, #1
+ b.ne bt_acs
+ cmp w0,#0
+ b bt_end
+bt_acs:
+
+ //CC
+ ldr w0, [x27,#12]
+ cmp w0, #1
+ b.ne bt_acc
+ cmp w0,#2
+ b bt_end
+bt_acc:
+
+ //MI
+ ldr w0, [x27,#16]
+ cmp w0, #1
+ b.ne bt_ami
+ subs w0,w0,#2
+ b bt_end
+bt_ami:
+
+ //PL
+ ldr w0, [x27,#20]
+ cmp w0, #1
+ b.ne bt_apl
+ subs w0,w0,#0
+ b bt_end
+bt_apl:
+ //HI - (C==1) && (Z==0)
+ ldr w0, [x27,#32]
+ cmp w0, #1
+ b.ne bt_ahi
+ cmp w0,#0
+ b bt_end
+bt_ahi:
+
+ //LS - (C==0) || (Z==1)
+ ldr w0, [x27,#36]
+ cmp w0, #1
+ b.ne bt_als
+ cmp w0,#1
+ b bt_end
+bt_als:
+
+ //GE
+ ldr w0, [x27,#40]
+ cmp w0, #1
+ b.ne bt_age
+ cmp w0,#0
+ b bt_end
+bt_age:
+
+ //LT
+ ldr w0, [x27,#44]
+ cmp w0, #1
+ b.ne bt_alt
+ cmp w0,#2
+ b bt_end
+bt_alt:
+
+ //GT
+ ldr w0, [x27,#48]
+ cmp w0, #1
+ b.ne bt_agt
+ cmp w0,#0
+ b bt_end
+bt_agt:
+
+ //LE
+ ldr w0, [x27,#52]
+ cmp w0, #1
+ b.ne bt_ale
+ cmp w0,#2
+ b bt_end
+bt_ale:
+
+
+bt_end:
+
+ // Load the registers from reg array
+ ldr x0, [x28,#0]
+ ldr x1, [x28,#8]
+ ldr x2, [x28,#16]
+ ldr x3, [x28,#24]
+ ldr x4, [x28,#32]
+ ldr x5, [x28,#40]
+ ldr x6, [x28,#48]
+ ldr x7, [x28,#56]
+ ldr x8, [x28,#64]
+ ldr x9, [x28,#72]
+ ldr x10, [x28,#80]
+ ldr x11, [x28,#88]
+ ldr x12, [x28,#96]
+ ldr x14, [x28,#112]
+
+ // Call the function
+ blr X30
+
+ // Save the registers to reg array
+ str x0, [x28,#0]
+ str x1, [x28,#8]
+ str x2, [x28,#16]
+ str x3, [x28,#24]
+ str x4, [x28,#32]
+ str x5, [x28,#40]
+ str x6, [x28,#48]
+ str x7, [x28,#56]
+ str x8, [x28,#64]
+ str x9, [x28,#72]
+ str x10, [x28,#80]
+ str x11, [x28,#88]
+ str x12, [x28,#96]
+ str x14, [x28,#112]
+
+ //Set the flags array based on result flags
+ movz w0, #0
+ movz w1, #1
+ csel w2, w1, w0, EQ
+ str w2, [x27,#0]
+ csel w2, w1, w0, NE
+ str w2, [x27,#4]
+ csel w2, w1, w0, CS
+ str w2, [x27,#8]
+ csel w2, w1, w0, CC
+ str w2, [x27,#12]
+ csel w2, w1, w0, MI
+ str w2, [x27,#16]
+ csel w2, w1, w0, PL
+ str w2, [x27,#20]
+ csel w2, w1, w0, VS
+ str w2, [x27,#24]
+ csel w2, w1, w0, VC
+ str w2, [x27,#28]
+ csel w2, w1, w0, HI
+ str w2, [x27,#32]
+ csel w2, w1, w0, LS
+ str w2, [x27,#36]
+ csel w2, w1, w0, GE
+ str w2, [x27,#40]
+ csel w2, w1, w0, LT
+ str w2, [x27,#44]
+ csel w2, w1, w0, GT
+ str w2, [x27,#48]
+ csel w2, w1, w0, LE
+ str w2, [x27,#52]
+
+ // Restore registers from stack
+ ldp x27, x28, [sp],#16
+ ldp x29, x30, [sp],#16
+ ret
+
diff --git a/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.mk b/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.mk
new file mode 100644
index 0000000..5d69203
--- /dev/null
+++ b/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.mk
@@ -0,0 +1,20 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ col32cb16blend_test.c \
+ ../../../arch-arm64/col32cb16blend.S
+
+LOCAL_CLANG_ASFLAGS_arm64 += -no-integrated-as
+
+LOCAL_SHARED_LIBRARIES :=
+
+LOCAL_C_INCLUDES :=
+
+LOCAL_MODULE:= test-pixelflinger-arm64-col32cb16blend
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_MULTILIB := 64
+
+include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-arm64/col32cb16blend/col32cb16blend_test.c b/libpixelflinger/tests/arch-arm64/col32cb16blend/col32cb16blend_test.c
new file mode 100644
index 0000000..c6a3017
--- /dev/null
+++ b/libpixelflinger/tests/arch-arm64/col32cb16blend/col32cb16blend_test.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+
+#define ARGB_8888_MAX 0xFFFFFFFF
+#define ARGB_8888_MIN 0x00000000
+#define RGB_565_MAX 0xFFFF
+#define RGB_565_MIN 0x0000
+
+struct test_t
+{
+ char name[256];
+ uint32_t dst_color;
+ uint32_t src_color;
+ size_t count;
+};
+
+struct test_t tests[] =
+{
+ {"Count 1, Src=Max, Dst=Min", ARGB_8888_MAX, RGB_565_MIN, 1},
+ {"Count 2, Src=Min, Dst=Max", ARGB_8888_MIN, RGB_565_MAX, 2},
+ {"Count 3, Src=Max, Dst=Max", ARGB_8888_MAX, RGB_565_MAX, 3},
+ {"Count 4, Src=Min, Dst=Min", ARGB_8888_MAX, RGB_565_MAX, 4},
+ {"Count 1, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 1},
+ {"Count 2, Src=Rand, Dst=Rand", 0xABCDEF12, 0x2345, 2},
+ {"Count 3, Src=Rand, Dst=Rand", 0x11111111, 0xEDFE, 3},
+ {"Count 4, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 4},
+ {"Count 5, Src=Rand, Dst=Rand", 0xEFEFFEFE, 0xFACC, 5},
+ {"Count 10, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 10}
+};
+
+void scanline_col32cb16blend_arm64(uint16_t *dst, int32_t src, size_t count);
+void scanline_col32cb16blend_c(uint16_t * dst, int32_t src, size_t count)
+{
+ int srcAlpha = (src>>24);
+ int f = 0x100 - (srcAlpha + (srcAlpha>>7));
+ while (count--)
+ {
+ uint16_t d = *dst;
+ int dstR = (d>>11)&0x1f;
+ int dstG = (d>>5)&0x3f;
+ int dstB = (d)&0x1f;
+ int srcR = (src >> ( 3))&0x1F;
+ int srcG = (src >> ( 8+2))&0x3F;
+ int srcB = (src >> (16+3))&0x1F;
+ srcR += (f*dstR)>>8;
+ srcG += (f*dstG)>>8;
+ srcB += (f*dstB)>>8;
+ *dst++ = (uint16_t)((srcR<<11)|(srcG<<5)|srcB);
+ }
+}
+
+void scanline_col32cb16blend_test()
+{
+ uint16_t dst_c[16], dst_asm[16];
+ uint32_t i, j;
+
+ for(i = 0; i < sizeof(tests)/sizeof(struct test_t); ++i)
+ {
+ struct test_t test = tests[i];
+
+ printf("Testing - %s:",test.name);
+
+ memset(dst_c, 0, sizeof(dst_c));
+ memset(dst_asm, 0, sizeof(dst_asm));
+
+ for(j = 0; j < test.count; ++j)
+ {
+ dst_c[j] = test.dst_color;
+ dst_asm[j] = test.dst_color;
+ }
+
+
+ scanline_col32cb16blend_c(dst_c, test.src_color, test.count);
+ scanline_col32cb16blend_arm64(dst_asm, test.src_color, test.count);
+
+
+ if(memcmp(dst_c, dst_asm, sizeof(dst_c)) == 0)
+ printf("Passed\n");
+ else
+ printf("Failed\n");
+
+ for(j = 0; j < test.count; ++j)
+ {
+ printf("dst_c[%d] = %x, dst_asm[%d] = %x \n", j, dst_c[j], j, dst_asm[j]);
+ }
+ }
+}
+
+int main()
+{
+ scanline_col32cb16blend_test();
+ return 0;
+}
diff --git a/libpixelflinger/tests/arch-arm64/disassembler/Android.mk b/libpixelflinger/tests/arch-arm64/disassembler/Android.mk
new file mode 100644
index 0000000..d8f7e69
--- /dev/null
+++ b/libpixelflinger/tests/arch-arm64/disassembler/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ arm64_diassembler_test.cpp \
+ ../../../codeflinger/Arm64Disassembler.cpp
+
+LOCAL_SHARED_LIBRARIES :=
+
+LOCAL_MODULE:= test-pixelflinger-arm64-disassembler-test
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_MULTILIB := 64
+
+include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-arm64/disassembler/arm64_diassembler_test.cpp b/libpixelflinger/tests/arch-arm64/disassembler/arm64_diassembler_test.cpp
new file mode 100644
index 0000000..af3183b
--- /dev/null
+++ b/libpixelflinger/tests/arch-arm64/disassembler/arm64_diassembler_test.cpp
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <stdio.h>
+#include <inttypes.h>
+#include <string.h>
+
+int arm64_disassemble(uint32_t code, char* instr);
+
+struct test_table_entry_t
+{
+ uint32_t code;
+ const char *instr;
+};
+static test_table_entry_t test_table [] =
+{
+ { 0x91000240, "add x0, x18, #0x0, lsl #0" },
+ { 0x9140041f, "add sp, x0, #0x1, lsl #12" },
+ { 0x917ffff2, "add x18, sp, #0xfff, lsl #12" },
+
+ { 0xd13ffe40, "sub x0, x18, #0xfff, lsl #0" },
+ { 0xd140001f, "sub sp, x0, #0x0, lsl #12" },
+ { 0xd14007f2, "sub x18, sp, #0x1, lsl #12" },
+
+ { 0x8b1e0200, "add x0, x16, x30, lsl #0" },
+ { 0x8b507fdf, "add xzr, x30, x16, lsr #31" },
+ { 0x8b8043f0, "add x16, xzr, x0, asr #16" },
+ { 0x8b5f401e, "add x30, x0, xzr, lsr #16" },
+
+
+ { 0x4b1e0200, "sub w0, w16, w30, lsl #0" },
+ { 0x4b507fdf, "sub wzr, w30, w16, lsr #31" },
+ { 0x4b8043f0, "sub w16, wzr, w0, asr #16" },
+ { 0x4b5f401e, "sub w30, w0, wzr, lsr #16" },
+
+ { 0x6b1e0200, "subs w0, w16, w30, lsl #0" },
+ { 0x6b507fdf, "subs wzr, w30, w16, lsr #31" },
+ { 0x6b8043f0, "subs w16, wzr, w0, asr #16" },
+ { 0x6b5f401e, "subs w30, w0, wzr, lsr #16" },
+
+ { 0x0a1e0200, "and w0, w16, w30, lsl #0" },
+ { 0x0a507fdf, "and wzr, w30, w16, lsr #31" },
+ { 0x0a8043f0, "and w16, wzr, w0, asr #16" },
+ { 0x0adf401e, "and w30, w0, wzr, ror #16" },
+
+ { 0x2a1e0200, "orr w0, w16, w30, lsl #0" },
+ { 0x2a507fdf, "orr wzr, w30, w16, lsr #31" },
+ { 0x2a8043f0, "orr w16, wzr, w0, asr #16" },
+ { 0x2adf401e, "orr w30, w0, wzr, ror #16" },
+
+ { 0x2a3e0200, "orn w0, w16, w30, lsl #0" },
+ { 0x2a707fdf, "orn wzr, w30, w16, lsr #31" },
+ { 0x2aa043f0, "orn w16, wzr, w0, asr #16" },
+ { 0x2aff401e, "orn w30, w0, wzr, ror #16" },
+
+ { 0x729fffe0, "movk w0, #0xffff, lsl #0" },
+ { 0x72a0000f, "movk w15, #0x0, lsl #16" },
+ { 0x7281fffe, "movk w30, #0xfff, lsl #0" },
+ { 0x72a0003f, "movk wzr, #0x1, lsl #16" },
+
+ { 0x529fffe0, "movz w0, #0xffff, lsl #0" },
+ { 0x52a0000f, "movz w15, #0x0, lsl #16" },
+ { 0x5281fffe, "movz w30, #0xfff, lsl #0" },
+ { 0x52a0003f, "movz wzr, #0x1, lsl #16" },
+
+ { 0xd29fffe0, "movz x0, #0xffff, lsl #0" },
+ { 0xd2a0000f, "movz x15, #0x0, lsl #16" },
+ { 0xd2c1fffe, "movz x30, #0xfff, lsl #32" },
+ { 0xd2e0003f, "movz xzr, #0x1, lsl #48" },
+
+ { 0x1a8003e0, "csel w0, wzr, w0, eq" },
+ { 0x1a831001, "csel w1, w0, w3, ne" },
+ { 0x1a9e2022, "csel w2, w1, w30, cs" },
+ { 0x1a8a3083, "csel w3, w4, w10, cc" },
+ { 0x1a8b40e4, "csel w4, w7, w11, mi" },
+ { 0x1a9b5105, "csel w5, w8, w27, pl" },
+ { 0x1a846167, "csel w7, w11, w4, vs" },
+ { 0x1a8671c8, "csel w8, w14, w6, vc" },
+ { 0x1a878289, "csel w9, w20, w7, hi" },
+ { 0x1a8c92aa, "csel w10, w21, w12, ls" },
+ { 0x1a8ea2ce, "csel w14, w22, w14, ge" },
+ { 0x1a9fb3b2, "csel w18, w29, wzr, lt" },
+ { 0x1a9fc3d8, "csel w24, w30, wzr, gt" },
+ { 0x1a82d17e, "csel w30, w11, w2, le" },
+ { 0x1a81e19f, "csel wzr, w12, w1, al" },
+
+ { 0x9a8003e0, "csel x0, xzr, x0, eq" },
+ { 0x9a831001, "csel x1, x0, x3, ne" },
+ { 0x9a9e2022, "csel x2, x1, x30, cs" },
+ { 0x9a8a3083, "csel x3, x4, x10, cc" },
+ { 0x9a8b40e4, "csel x4, x7, x11, mi" },
+ { 0x9a9b5105, "csel x5, x8, x27, pl" },
+ { 0x9a846167, "csel x7, x11, x4, vs" },
+ { 0x9a8671c8, "csel x8, x14, x6, vc" },
+ { 0x9a878289, "csel x9, x20, x7, hi" },
+ { 0x9a8c92aa, "csel x10, x21, x12, ls" },
+ { 0x9a8ea2ce, "csel x14, x22, x14, ge" },
+ { 0x9a9fb3b2, "csel x18, x29, xzr, lt" },
+ { 0x9a9fc3d8, "csel x24, x30, xzr, gt" },
+ { 0x9a82d17e, "csel x30, x11, x2, le" },
+ { 0x9a81e19f, "csel xzr, x12, x1, al" },
+
+ { 0x5a8003e0, "csinv w0, wzr, w0, eq" },
+ { 0x5a831001, "csinv w1, w0, w3, ne" },
+ { 0x5a9e2022, "csinv w2, w1, w30, cs" },
+ { 0x5a8a3083, "csinv w3, w4, w10, cc" },
+ { 0x5a8b40e4, "csinv w4, w7, w11, mi" },
+ { 0x5a9b5105, "csinv w5, w8, w27, pl" },
+ { 0x5a846167, "csinv w7, w11, w4, vs" },
+ { 0x5a8671c8, "csinv w8, w14, w6, vc" },
+ { 0x5a878289, "csinv w9, w20, w7, hi" },
+ { 0x5a8c92aa, "csinv w10, w21, w12, ls" },
+ { 0x5a8ea2ce, "csinv w14, w22, w14, ge" },
+ { 0x5a9fb3b2, "csinv w18, w29, wzr, lt" },
+ { 0x5a9fc3d8, "csinv w24, w30, wzr, gt" },
+ { 0x5a82d17e, "csinv w30, w11, w2, le" },
+ { 0x5a81e19f, "csinv wzr, w12, w1, al" },
+
+ { 0x1b1f3fc0, "madd w0, w30, wzr, w15" },
+ { 0x1b0079ef, "madd w15, w15, w0, w30" },
+ { 0x1b0f7ffe, "madd w30, wzr, w15, wzr" },
+ { 0x1b1e001f, "madd wzr, w0, w30, w0" },
+
+ { 0x9b3f3fc0, "smaddl x0, w30, wzr, x15" },
+ { 0x9b2079ef, "smaddl x15, w15, w0, x30" },
+ { 0x9b2f7ffe, "smaddl x30, wzr, w15, xzr" },
+ { 0x9b3e001f, "smaddl xzr, w0, w30, x0" },
+
+ { 0xd65f0000, "ret x0" },
+ { 0xd65f01e0, "ret x15" },
+ { 0xd65f03c0, "ret x30" },
+ { 0xd65f03e0, "ret xzr" },
+
+ { 0xb87f4be0, "ldr w0, [sp, wzr, uxtw #0]" },
+ { 0xb87ed80f, "ldr w15, [x0, w30, sxtw #2]" },
+ { 0xb86fc9fe, "ldr w30, [x15, w15, sxtw #0]" },
+ { 0xb8605bdf, "ldr wzr, [x30, w0, uxtw #2]" },
+ { 0xb87febe0, "ldr w0, [sp, xzr, sxtx #0]" },
+ { 0xb87e780f, "ldr w15, [x0, x30, lsl #2]" },
+ { 0xb86f69fe, "ldr w30, [x15, x15, lsl #0]" },
+ { 0xb860fbdf, "ldr wzr, [x30, x0, sxtx #2]" },
+
+ { 0xb83f4be0, "str w0, [sp, wzr, uxtw #0]" },
+ { 0xb83ed80f, "str w15, [x0, w30, sxtw #2]" },
+ { 0xb82fc9fe, "str w30, [x15, w15, sxtw #0]" },
+ { 0xb8205bdf, "str wzr, [x30, w0, uxtw #2]" },
+ { 0xb83febe0, "str w0, [sp, xzr, sxtx #0]" },
+ { 0xb83e780f, "str w15, [x0, x30, lsl #2]" },
+ { 0xb82f69fe, "str w30, [x15, x15, lsl #0]" },
+ { 0xb820fbdf, "str wzr, [x30, x0, sxtx #2]" },
+
+ { 0x787f4be0, "ldrh w0, [sp, wzr, uxtw #0]" },
+ { 0x787ed80f, "ldrh w15, [x0, w30, sxtw #1]" },
+ { 0x786fc9fe, "ldrh w30, [x15, w15, sxtw #0]" },
+ { 0x78605bdf, "ldrh wzr, [x30, w0, uxtw #1]" },
+ { 0x787febe0, "ldrh w0, [sp, xzr, sxtx #0]" },
+ { 0x787e780f, "ldrh w15, [x0, x30, lsl #1]" },
+ { 0x786f69fe, "ldrh w30, [x15, x15, lsl #0]" },
+ { 0x7860fbdf, "ldrh wzr, [x30, x0, sxtx #1]" },
+
+ { 0x783f4be0, "strh w0, [sp, wzr, uxtw #0]" },
+ { 0x783ed80f, "strh w15, [x0, w30, sxtw #1]" },
+ { 0x782fc9fe, "strh w30, [x15, w15, sxtw #0]" },
+ { 0x78205bdf, "strh wzr, [x30, w0, uxtw #1]" },
+ { 0x783febe0, "strh w0, [sp, xzr, sxtx #0]" },
+ { 0x783e780f, "strh w15, [x0, x30, lsl #1]" },
+ { 0x782f69fe, "strh w30, [x15, x15, lsl #0]" },
+ { 0x7820fbdf, "strh wzr, [x30, x0, sxtx #1]" },
+
+ { 0x387f5be0, "ldrb w0, [sp, wzr, uxtw #0]" },
+ { 0x387ec80f, "ldrb w15, [x0, w30, sxtw ]" },
+ { 0x386fd9fe, "ldrb w30, [x15, w15, sxtw #0]" },
+ { 0x38604bdf, "ldrb wzr, [x30, w0, uxtw ]" },
+ { 0x387ffbe0, "ldrb w0, [sp, xzr, sxtx #0]" },
+ { 0x387e780f, "ldrb w15, [x0, x30, lsl #0]" },
+ { 0x386f79fe, "ldrb w30, [x15, x15, lsl #0]" },
+ { 0x3860ebdf, "ldrb wzr, [x30, x0, sxtx ]" },
+
+ { 0x383f5be0, "strb w0, [sp, wzr, uxtw #0]" },
+ { 0x383ec80f, "strb w15, [x0, w30, sxtw ]" },
+ { 0x382fd9fe, "strb w30, [x15, w15, sxtw #0]" },
+ { 0x38204bdf, "strb wzr, [x30, w0, uxtw ]" },
+ { 0x383ffbe0, "strb w0, [sp, xzr, sxtx #0]" },
+ { 0x383e780f, "strb w15, [x0, x30, lsl #0]" },
+ { 0x382f79fe, "strb w30, [x15, x15, lsl #0]" },
+ { 0x3820ebdf, "strb wzr, [x30, x0, sxtx ]" },
+
+ { 0xf87f4be0, "ldr x0, [sp, wzr, uxtw #0]" },
+ { 0xf87ed80f, "ldr x15, [x0, w30, sxtw #3]" },
+ { 0xf86fc9fe, "ldr x30, [x15, w15, sxtw #0]" },
+ { 0xf8605bdf, "ldr xzr, [x30, w0, uxtw #3]" },
+ { 0xf87febe0, "ldr x0, [sp, xzr, sxtx #0]" },
+ { 0xf87e780f, "ldr x15, [x0, x30, lsl #3]" },
+ { 0xf86f69fe, "ldr x30, [x15, x15, lsl #0]" },
+ { 0xf860fbdf, "ldr xzr, [x30, x0, sxtx #3]" },
+
+ { 0xf83f4be0, "str x0, [sp, wzr, uxtw #0]" },
+ { 0xf83ed80f, "str x15, [x0, w30, sxtw #3]" },
+ { 0xf82fc9fe, "str x30, [x15, w15, sxtw #0]" },
+ { 0xf8205bdf, "str xzr, [x30, w0, uxtw #3]" },
+ { 0xf83febe0, "str x0, [sp, xzr, sxtx #0]" },
+ { 0xf83e780f, "str x15, [x0, x30, lsl #3]" },
+ { 0xf82f69fe, "str x30, [x15, x15, lsl #0]" },
+ { 0xf820fbdf, "str xzr, [x30, x0, sxtx #3]" },
+
+ { 0xb85007e0, "ldr w0, [sp], #-256" },
+ { 0xb840040f, "ldr w15, [x0], #0" },
+ { 0xb84015fe, "ldr w30, [x15], #1" },
+ { 0xb84ff7df, "ldr wzr, [x30], #255" },
+ { 0xb8100fe0, "str w0, [sp, #-256]!" },
+ { 0xb8000c0f, "str w15, [x0, #0]!" },
+ { 0xb8001dfe, "str w30, [x15, #1]!" },
+ { 0xb80fffdf, "str wzr, [x30, #255]!" },
+
+ { 0x13017be0, "sbfm w0, wzr, #1, #30" },
+ { 0x131e7fcf, "sbfm w15, w30, #30, #31" },
+ { 0x131f01fe, "sbfm w30, w15, #31, #0" },
+ { 0x1300041f, "sbfm wzr, w0, #0, #1" },
+
+ { 0x53017be0, "ubfm w0, wzr, #1, #30" },
+ { 0x531e7fcf, "ubfm w15, w30, #30, #31" },
+ { 0x531f01fe, "ubfm w30, w15, #31, #0" },
+ { 0x5300041f, "ubfm wzr, w0, #0, #1" },
+ { 0xd3417fe0, "ubfm x0, xzr, #1, #31" },
+ { 0xd35fffcf, "ubfm x15, x30, #31, #63" },
+ { 0xd35f01fe, "ubfm x30, x15, #31, #0" },
+ { 0xd340041f, "ubfm xzr, x0, #0, #1" },
+
+ { 0x139e7be0, "extr w0, wzr, w30, #30" },
+ { 0x138f7fcf, "extr w15, w30, w15, #31" },
+ { 0x138001fe, "extr w30, w15, w0, #0" },
+ { 0x139f041f, "extr wzr, w0, wzr, #1" },
+
+ { 0x54000020, "b.eq #.+4" },
+ { 0x54000201, "b.ne #.+64" },
+ { 0x54000802, "b.cs #.+256" },
+ { 0x54002003, "b.cc #.+1024" },
+ { 0x54008004, "b.mi #.+4096" },
+ { 0x54ffffe5, "b.pl #.-4" },
+ { 0x54ffff06, "b.vs #.-32" },
+ { 0x54fffc07, "b.vc #.-128" },
+ { 0x54fff008, "b.hi #.-512" },
+ { 0x54000049, "b.ls #.+8" },
+ { 0x5400006a, "b.ge #.+12" },
+ { 0x5400008b, "b.lt #.+16" },
+ { 0x54ffffcc, "b.gt #.-8" },
+ { 0x54ffffad, "b.le #.-12" },
+ { 0x54ffff8e, "b.al #.-16" },
+
+ { 0x8b2001e0, "add x0, x15, w0, uxtb #0" },
+ { 0x8b2f27cf, "add x15, x30, w15, uxth #1" },
+ { 0x8b3e4bfe, "add x30, sp, w30, uxtw #2" },
+ { 0x8b3f6c1f, "add sp, x0, xzr, uxtx #3" },
+ { 0x8b2091e0, "add x0, x15, w0, sxtb #4" },
+ { 0x8b2fa3cf, "add x15, x30, w15, sxth #0" },
+ { 0x8b3ec7fe, "add x30, sp, w30, sxtw #1" },
+ { 0x8b3fe81f, "add sp, x0, xzr, sxtx #2" },
+
+ { 0xcb2001e0, "sub x0, x15, w0, uxtb #0" },
+ { 0xcb2f27cf, "sub x15, x30, w15, uxth #1" },
+ { 0xcb3e4bfe, "sub x30, sp, w30, uxtw #2" },
+ { 0xcb3f6c1f, "sub sp, x0, xzr, uxtx #3" },
+ { 0xcb2091e0, "sub x0, x15, w0, sxtb #4" },
+ { 0xcb2fa3cf, "sub x15, x30, w15, sxth #0" },
+ { 0xcb3ec7fe, "sub x30, sp, w30, sxtw #1" },
+ { 0xcb3fe81f, "sub sp, x0, xzr, sxtx #2" }
+};
+
+int main()
+{
+ char instr[256];
+ uint32_t failed = 0;
+ for(uint32_t i = 0; i < sizeof(test_table)/sizeof(test_table_entry_t); ++i)
+ {
+ test_table_entry_t *test;
+ test = &test_table[i];
+ arm64_disassemble(test->code, instr);
+ if(strcmp(instr, test->instr) != 0)
+ {
+ printf("Test Failed \n"
+ "Code : 0x%0x\n"
+ "Expected : %s\n"
+ "Actual : %s\n", test->code, test->instr, instr);
+ failed++;
+ }
+ }
+ if(failed == 0)
+ {
+ printf("All tests PASSED\n");
+ return 0;
+ }
+ else
+ {
+ printf("%d tests FAILED\n", failed);
+ return -1;
+ }
+}
diff --git a/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.mk b/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.mk
new file mode 100644
index 0000000..2c1379b
--- /dev/null
+++ b/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.mk
@@ -0,0 +1,20 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ t32cb16blend_test.c \
+ ../../../arch-arm64/t32cb16blend.S
+
+LOCAL_CLANG_ASFLAGS_arm64 += -no-integrated-as
+
+LOCAL_SHARED_LIBRARIES :=
+
+LOCAL_C_INCLUDES :=
+
+LOCAL_MODULE:= test-pixelflinger-arm64-t32cb16blend
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_MULTILIB := 64
+
+include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-arm64/t32cb16blend/t32cb16blend_test.c b/libpixelflinger/tests/arch-arm64/t32cb16blend/t32cb16blend_test.c
new file mode 100644
index 0000000..afb36fb
--- /dev/null
+++ b/libpixelflinger/tests/arch-arm64/t32cb16blend/t32cb16blend_test.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#define ARGB_8888_MAX 0xFFFFFFFF
+#define ARGB_8888_MIN 0x00000000
+#define RGB_565_MAX 0xFFFF
+#define RGB_565_MIN 0x0000
+
+struct test_t
+{
+ char name[256];
+ uint32_t src_color;
+ uint16_t dst_color;
+ size_t count;
+};
+
+struct test_t tests[] =
+{
+ {"Count 0", 0, 0, 0},
+ {"Count 1, Src=Max, Dst=Min", ARGB_8888_MAX, RGB_565_MIN, 1},
+ {"Count 2, Src=Min, Dst=Max", ARGB_8888_MIN, RGB_565_MAX, 2},
+ {"Count 3, Src=Max, Dst=Max", ARGB_8888_MAX, RGB_565_MAX, 3},
+ {"Count 4, Src=Min, Dst=Min", ARGB_8888_MAX, RGB_565_MAX, 4},
+ {"Count 1, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 1},
+ {"Count 2, Src=Rand, Dst=Rand", 0xABCDEF12, 0x2345, 2},
+ {"Count 3, Src=Rand, Dst=Rand", 0x11111111, 0xEDFE, 3},
+ {"Count 4, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 4},
+ {"Count 5, Src=Rand, Dst=Rand", 0xEFEFFEFE, 0xFACC, 5},
+ {"Count 10, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 10}
+
+};
+
+void scanline_t32cb16blend_arm64(uint16_t*, uint32_t*, size_t);
+void scanline_t32cb16blend_c(uint16_t * dst, uint32_t* src, size_t count)
+{
+ while (count--)
+ {
+ uint16_t d = *dst;
+ uint32_t s = *src++;
+ int dstR = (d>>11)&0x1f;
+ int dstG = (d>>5)&0x3f;
+ int dstB = (d)&0x1f;
+ int srcR = (s >> ( 3))&0x1F;
+ int srcG = (s >> ( 8+2))&0x3F;
+ int srcB = (s >> (16+3))&0x1F;
+ int srcAlpha = (s>>24) & 0xFF;
+
+
+ int f = 0x100 - (srcAlpha + ((srcAlpha>>7) & 0x1));
+ srcR += (f*dstR)>>8;
+ srcG += (f*dstG)>>8;
+ srcB += (f*dstB)>>8;
+ srcR = srcR > 0x1F? 0x1F: srcR;
+ srcG = srcG > 0x3F? 0x3F: srcG;
+ srcB = srcB > 0x1F? 0x1F: srcB;
+ *dst++ = (uint16_t)((srcR<<11)|(srcG<<5)|srcB);
+ }
+}
+
+void scanline_t32cb16blend_test()
+{
+ uint16_t dst_c[16], dst_asm[16];
+ uint32_t src[16];
+ uint32_t i;
+ uint32_t j;
+
+ for(i = 0; i < sizeof(tests)/sizeof(struct test_t); ++i)
+ {
+ struct test_t test = tests[i];
+
+ printf("Testing - %s:",test.name);
+
+ memset(dst_c, 0, sizeof(dst_c));
+ memset(dst_asm, 0, sizeof(dst_asm));
+
+ for(j = 0; j < test.count; ++j)
+ {
+ dst_c[j] = test.dst_color;
+ dst_asm[j] = test.dst_color;
+ src[j] = test.src_color;
+ }
+
+ scanline_t32cb16blend_c(dst_c,src,test.count);
+ scanline_t32cb16blend_arm64(dst_asm,src,test.count);
+
+
+ if(memcmp(dst_c, dst_asm, sizeof(dst_c)) == 0)
+ printf("Passed\n");
+ else
+ printf("Failed\n");
+
+ for(j = 0; j < test.count; ++j)
+ {
+ printf("dst_c[%d] = %x, dst_asm[%d] = %x \n", j, dst_c[j], j, dst_asm[j]);
+ }
+ }
+}
+
+int main()
+{
+ scanline_t32cb16blend_test();
+ return 0;
+}
diff --git a/libpixelflinger/tests/codegen/Android.mk b/libpixelflinger/tests/codegen/Android.mk
new file mode 100644
index 0000000..2f9ca2f
--- /dev/null
+++ b/libpixelflinger/tests/codegen/Android.mk
@@ -0,0 +1,18 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ codegen.cpp.arm
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libpixelflinger
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/../..
+
+LOCAL_MODULE:= test-opengl-codegen
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/codegen/codegen.cpp b/libpixelflinger/tests/codegen/codegen.cpp
new file mode 100644
index 0000000..148b6f4
--- /dev/null
+++ b/libpixelflinger/tests/codegen/codegen.cpp
@@ -0,0 +1,88 @@
+#include <stdio.h>
+#include <stdint.h>
+
+#include "private/pixelflinger/ggl_context.h"
+
+#include "buffer.h"
+#include "scanline.h"
+
+#include "codeflinger/CodeCache.h"
+#include "codeflinger/GGLAssembler.h"
+#include "codeflinger/ARMAssembler.h"
+#if defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6
+#include "codeflinger/MIPSAssembler.h"
+#endif
+#include "codeflinger/Arm64Assembler.h"
+
+#if defined(__arm__) || (defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6) || defined(__aarch64__)
+# define ANDROID_ARM_CODEGEN 1
+#else
+# define ANDROID_ARM_CODEGEN 0
+#endif
+
+#if defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6
+#define ASSEMBLY_SCRATCH_SIZE 4096
+#elif defined(__aarch64__)
+#define ASSEMBLY_SCRATCH_SIZE 8192
+#else
+#define ASSEMBLY_SCRATCH_SIZE 2048
+#endif
+
+using namespace android;
+
+class ScanlineAssembly : public Assembly {
+ AssemblyKey<needs_t> mKey;
+public:
+ ScanlineAssembly(needs_t needs, size_t size)
+ : Assembly(size), mKey(needs) { }
+ const AssemblyKey<needs_t>& key() const { return mKey; }
+};
+
+static void ggl_test_codegen(uint32_t n, uint32_t p, uint32_t t0, uint32_t t1)
+{
+#if ANDROID_ARM_CODEGEN
+ GGLContext* c;
+ gglInit(&c);
+ needs_t needs;
+ needs.n = n;
+ needs.p = p;
+ needs.t[0] = t0;
+ needs.t[1] = t1;
+ sp<ScanlineAssembly> a(new ScanlineAssembly(needs, ASSEMBLY_SCRATCH_SIZE));
+
+#if defined(__arm__)
+ GGLAssembler assembler( new ARMAssembler(a) );
+#endif
+
+#if defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6
+ GGLAssembler assembler( new ArmToMipsAssembler(a) );
+#endif
+
+#if defined(__aarch64__)
+ GGLAssembler assembler( new ArmToArm64Assembler(a) );
+#endif
+
+ int err = assembler.scanline(needs, (context_t*)c);
+ if (err != 0) {
+ printf("error %08x (%s)\n", err, strerror(-err));
+ }
+ gglUninit(c);
+#else
+ printf("This test runs only on ARM, Arm64 or MIPS\n");
+#endif
+}
+
+int main(int argc, char** argv)
+{
+ if (argc != 2) {
+ printf("usage: %s 00000117:03454504_00001501_00000000\n", argv[0]);
+ return 0;
+ }
+ uint32_t n;
+ uint32_t p;
+ uint32_t t0;
+ uint32_t t1;
+ sscanf(argv[1], "%08x:%08x_%08x_%08x", &p, &n, &t0, &t1);
+ ggl_test_codegen(n, p, t0, t1);
+ return 0;
+}
diff --git a/libpixelflinger/tests/gglmul/Android.mk b/libpixelflinger/tests/gglmul/Android.mk
new file mode 100644
index 0000000..75bd39e
--- /dev/null
+++ b/libpixelflinger/tests/gglmul/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ gglmul_test.cpp
+
+LOCAL_SHARED_LIBRARIES :=
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/../../include
+
+LOCAL_MODULE:= test-pixelflinger-gglmul
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/gglmul/gglmul_test.cpp b/libpixelflinger/tests/gglmul/gglmul_test.cpp
new file mode 100644
index 0000000..5d460d6
--- /dev/null
+++ b/libpixelflinger/tests/gglmul/gglmul_test.cpp
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <inttypes.h>
+
+#include "private/pixelflinger/ggl_fixed.h"
+
+// gglClampx() tests
+struct gglClampx_test_t
+{
+ GGLfixed input;
+ GGLfixed output;
+};
+
+gglClampx_test_t gglClampx_tests[] =
+{
+ {FIXED_ONE + 1, FIXED_ONE},
+ {FIXED_ONE, FIXED_ONE},
+ {FIXED_ONE - 1, FIXED_ONE - 1},
+ {1, 1},
+ {0, 0},
+ {FIXED_MIN,0}
+};
+
+void gglClampx_test()
+{
+ uint32_t i;
+
+ printf("Testing gglClampx\n");
+ for(i = 0; i < sizeof(gglClampx_tests)/sizeof(gglClampx_test_t); ++i)
+ {
+ gglClampx_test_t *test = &gglClampx_tests[i];
+ printf("Test input=0x%08x output=0x%08x :",
+ test->input, test->output);
+ if(gglClampx(test->input) == test->output)
+ printf("Passed\n");
+ else
+ printf("Failed\n");
+ }
+}
+
+// gglClz() tests
+struct gglClz_test_t
+{
+ GGLfixed input;
+ GGLfixed output;
+};
+
+gglClz_test_t gglClz_tests[] =
+{
+ {0, 32},
+ {1, 31},
+ {-1,0}
+};
+
+void gglClz_test()
+{
+ uint32_t i;
+
+ printf("Testing gglClz\n");
+ for(i = 0; i < sizeof(gglClz_tests)/sizeof(gglClz_test_t); ++i)
+ {
+ gglClz_test_t *test = &gglClz_tests[i];
+ printf("Test input=0x%08x output=%2d :", test->input, test->output);
+ if(gglClz(test->input) == test->output)
+ printf("Passed\n");
+ else
+ printf("Failed\n");
+ }
+}
+
+// gglMulx() tests
+struct gglMulx_test_t
+{
+ GGLfixed x;
+ GGLfixed y;
+ int shift;
+};
+
+gglMulx_test_t gglMulx_tests[] =
+{
+ {1,1,1},
+ {0,1,1},
+ {FIXED_ONE,FIXED_ONE,16},
+ {FIXED_MIN,FIXED_MAX,16},
+ {FIXED_MAX,FIXED_MAX,16},
+ {FIXED_MIN,FIXED_MIN,16},
+ {FIXED_HALF,FIXED_ONE,16},
+ {FIXED_MAX,FIXED_MAX,31},
+ {FIXED_ONE,FIXED_MAX,31}
+};
+
+void gglMulx_test()
+{
+ uint32_t i;
+ GGLfixed actual, expected;
+
+ printf("Testing gglMulx\n");
+ for(i = 0; i < sizeof(gglMulx_tests)/sizeof(gglMulx_test_t); ++i)
+ {
+ gglMulx_test_t *test = &gglMulx_tests[i];
+ printf("Test x=0x%08x y=0x%08x shift=%2d :",
+ test->x, test->y, test->shift);
+ actual = gglMulx(test->x, test->y, test->shift);
+ expected =
+ ((int64_t)test->x * test->y + (1 << (test->shift-1))) >> test->shift;
+ if(actual == expected)
+ printf(" Passed\n");
+ else
+ printf(" Failed Actual(0x%08x) Expected(0x%08x)\n",
+ actual, expected);
+ }
+}
+// gglMulAddx() tests
+struct gglMulAddx_test_t
+{
+ GGLfixed x;
+ GGLfixed y;
+ int shift;
+ GGLfixed a;
+};
+
+gglMulAddx_test_t gglMulAddx_tests[] =
+{
+ {1,2,1,1},
+ {0,1,1,1},
+ {FIXED_ONE,FIXED_ONE,16, 0},
+ {FIXED_MIN,FIXED_MAX,16, FIXED_HALF},
+ {FIXED_MAX,FIXED_MAX,16, FIXED_MIN},
+ {FIXED_MIN,FIXED_MIN,16, FIXED_MAX},
+ {FIXED_HALF,FIXED_ONE,16,FIXED_ONE},
+ {FIXED_MAX,FIXED_MAX,31, FIXED_HALF},
+ {FIXED_ONE,FIXED_MAX,31, FIXED_HALF}
+};
+
+void gglMulAddx_test()
+{
+ uint32_t i;
+ GGLfixed actual, expected;
+
+ printf("Testing gglMulAddx\n");
+ for(i = 0; i < sizeof(gglMulAddx_tests)/sizeof(gglMulAddx_test_t); ++i)
+ {
+ gglMulAddx_test_t *test = &gglMulAddx_tests[i];
+ printf("Test x=0x%08x y=0x%08x shift=%2d a=0x%08x :",
+ test->x, test->y, test->shift, test->a);
+ actual = gglMulAddx(test->x, test->y,test->a, test->shift);
+ expected = (((int64_t)test->x * test->y) >> test->shift) + test->a;
+
+ if(actual == expected)
+ printf(" Passed\n");
+ else
+ printf(" Failed Actual(0x%08x) Expected(0x%08x)\n",
+ actual, expected);
+ }
+}
+// gglMulSubx() tests
+struct gglMulSubx_test_t
+{
+ GGLfixed x;
+ GGLfixed y;
+ int shift;
+ GGLfixed a;
+};
+
+gglMulSubx_test_t gglMulSubx_tests[] =
+{
+ {1,2,1,1},
+ {0,1,1,1},
+ {FIXED_ONE,FIXED_ONE,16, 0},
+ {FIXED_MIN,FIXED_MAX,16, FIXED_HALF},
+ {FIXED_MAX,FIXED_MAX,16, FIXED_MIN},
+ {FIXED_MIN,FIXED_MIN,16, FIXED_MAX},
+ {FIXED_HALF,FIXED_ONE,16,FIXED_ONE},
+ {FIXED_MAX,FIXED_MAX,31, FIXED_HALF},
+ {FIXED_ONE,FIXED_MAX,31, FIXED_HALF}
+};
+
+void gglMulSubx_test()
+{
+ uint32_t i;
+ GGLfixed actual, expected;
+
+ printf("Testing gglMulSubx\n");
+ for(i = 0; i < sizeof(gglMulSubx_tests)/sizeof(gglMulSubx_test_t); ++i)
+ {
+ gglMulSubx_test_t *test = &gglMulSubx_tests[i];
+ printf("Test x=0x%08x y=0x%08x shift=%2d a=0x%08x :",
+ test->x, test->y, test->shift, test->a);
+ actual = gglMulSubx(test->x, test->y, test->a, test->shift);
+ expected = (((int64_t)test->x * test->y) >> test->shift) - test->a;
+
+ if(actual == expected)
+ printf(" Passed\n");
+ else
+ printf(" Failed Actual(0x%08x) Expected(0x%08x)\n",
+ actual, expected);
+ }
+}
+
+// gglMulii() tests
+
+struct gglMulii_test_t
+{
+ int32_t x;
+ int32_t y;
+};
+
+gglMulii_test_t gglMulii_tests[] =
+{
+ {1,INT32_MIN},
+ {1,INT32_MAX},
+ {0,INT32_MIN},
+ {0,INT32_MAX},
+ {INT32_MIN, INT32_MAX},
+ {INT32_MAX, INT32_MIN},
+ {INT32_MIN, INT32_MIN},
+ {INT32_MAX, INT32_MAX}
+};
+
+void gglMulii_test()
+{
+ uint32_t i;
+ int64_t actual, expected;
+
+ printf("Testing gglMulii\n");
+ for(i = 0; i < sizeof(gglMulii_tests)/sizeof(gglMulii_test_t); ++i)
+ {
+ gglMulii_test_t *test = &gglMulii_tests[i];
+ printf("Test x=0x%08x y=0x%08x :", test->x, test->y);
+ actual = gglMulii(test->x, test->y);
+ expected = ((int64_t)test->x * test->y);
+
+ if(actual == expected)
+ printf(" Passed\n");
+ else
+ printf(" Failed Actual(%" PRId64 ") Expected(%" PRId64 ")\n",
+ actual, expected);
+ }
+}
+
+int main(int /*argc*/, char** /*argv*/)
+{
+ gglClampx_test();
+ gglClz_test();
+ gglMulx_test();
+ gglMulAddx_test();
+ gglMulSubx_test();
+ gglMulii_test();
+ return 0;
+}
diff --git a/libpixelflinger/trap.cpp b/libpixelflinger/trap.cpp
new file mode 100644
index 0000000..80efeff
--- /dev/null
+++ b/libpixelflinger/trap.cpp
@@ -0,0 +1,1173 @@
+/* libs/pixelflinger/trap.cpp
+**
+** Copyright 2006, 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 <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "trap.h"
+#include "picker.h"
+
+#include <cutils/log.h>
+#include <cutils/memory.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+// enable to see triangles edges
+#define DEBUG_TRANGLES 0
+
+// ----------------------------------------------------------------------------
+
+static void pointx_validate(void *con, const GGLcoord* c, GGLcoord r);
+static void pointx(void *con, const GGLcoord* c, GGLcoord r);
+static void aa_pointx(void *con, const GGLcoord* c, GGLcoord r);
+static void aa_nice_pointx(void *con, const GGLcoord* c, GGLcoord r);
+
+static void linex_validate(void *con, const GGLcoord* v0, const GGLcoord* v1, GGLcoord w);
+static void linex(void *con, const GGLcoord* v0, const GGLcoord* v1, GGLcoord w);
+static void aa_linex(void *con, const GGLcoord* v0, const GGLcoord* v1, GGLcoord w);
+
+static void recti_validate(void* c, GGLint l, GGLint t, GGLint r, GGLint b);
+static void recti(void* c, GGLint l, GGLint t, GGLint r, GGLint b);
+
+static void trianglex_validate(void*,
+ const GGLcoord*, const GGLcoord*, const GGLcoord*);
+static void trianglex_small(void*,
+ const GGLcoord*, const GGLcoord*, const GGLcoord*);
+static void trianglex_big(void*,
+ const GGLcoord*, const GGLcoord*, const GGLcoord*);
+static void aa_trianglex(void*,
+ const GGLcoord*, const GGLcoord*, const GGLcoord*);
+static void trianglex_debug(void* con,
+ const GGLcoord*, const GGLcoord*, const GGLcoord*);
+
+static void aapolyx(void* con,
+ const GGLcoord* pts, int count);
+
+static inline int min(int a, int b) CONST;
+static inline int max(int a, int b) CONST;
+static inline int min(int a, int b, int c) CONST;
+static inline int max(int a, int b, int c) CONST;
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark Tools
+#endif
+
+inline int min(int a, int b) {
+ return a<b ? a : b;
+}
+inline int max(int a, int b) {
+ return a<b ? b : a;
+}
+inline int min(int a, int b, int c) {
+ return min(a,min(b,c));
+}
+inline int max(int a, int b, int c) {
+ return max(a,max(b,c));
+}
+
+template <typename T>
+static inline void swap(T& a, T& b) {
+ T t(a);
+ a = b;
+ b = t;
+}
+
+static void
+triangle_dump_points( const GGLcoord* v0,
+ const GGLcoord* v1,
+ const GGLcoord* v2 )
+{
+ float tri = 1.0f / TRI_ONE;
+ ALOGD(" P0=(%.3f, %.3f) [%08x, %08x]\n"
+ " P1=(%.3f, %.3f) [%08x, %08x]\n"
+ " P2=(%.3f, %.3f) [%08x, %08x]\n",
+ v0[0]*tri, v0[1]*tri, v0[0], v0[1],
+ v1[0]*tri, v1[1]*tri, v1[0], v1[1],
+ v2[0]*tri, v2[1]*tri, v2[0], v2[1] );
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark Misc
+#endif
+
+void ggl_init_trap(context_t* c)
+{
+ ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE|GGL_TMU_STATE|GGL_CB_STATE);
+}
+
+void ggl_state_changed(context_t* c, int flags)
+{
+ if (ggl_likely(!c->dirty)) {
+ c->procs.pointx = pointx_validate;
+ c->procs.linex = linex_validate;
+ c->procs.recti = recti_validate;
+ c->procs.trianglex = trianglex_validate;
+ }
+ c->dirty |= uint32_t(flags);
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark Point
+#endif
+
+void pointx_validate(void *con, const GGLcoord* v, GGLcoord rad)
+{
+ GGL_CONTEXT(c, con);
+ ggl_pick(c);
+ if (c->state.needs.p & GGL_NEED_MASK(P_AA)) {
+ if (c->state.enables & GGL_ENABLE_POINT_AA_NICE) {
+ c->procs.pointx = aa_nice_pointx;
+ } else {
+ c->procs.pointx = aa_pointx;
+ }
+ } else {
+ c->procs.pointx = pointx;
+ }
+ c->procs.pointx(con, v, rad);
+}
+
+void pointx(void *con, const GGLcoord* v, GGLcoord rad)
+{
+ GGL_CONTEXT(c, con);
+ GGLcoord halfSize = TRI_ROUND(rad) >> 1;
+ if (halfSize == 0)
+ halfSize = TRI_HALF;
+ GGLcoord xc = v[0];
+ GGLcoord yc = v[1];
+ if (halfSize & TRI_HALF) { // size odd
+ xc = TRI_FLOOR(xc) + TRI_HALF;
+ yc = TRI_FLOOR(yc) + TRI_HALF;
+ } else { // size even
+ xc = TRI_ROUND(xc);
+ yc = TRI_ROUND(yc);
+ }
+ GGLint l = (xc - halfSize) >> TRI_FRACTION_BITS;
+ GGLint t = (yc - halfSize) >> TRI_FRACTION_BITS;
+ GGLint r = (xc + halfSize) >> TRI_FRACTION_BITS;
+ GGLint b = (yc + halfSize) >> TRI_FRACTION_BITS;
+ recti(c, l, t, r, b);
+}
+
+// This way of computing the coverage factor, is more accurate and gives
+// better results for small circles, but it is also a lot slower.
+// Here we use super-sampling.
+static int32_t coverageNice(GGLcoord x, GGLcoord y,
+ GGLcoord rmin, GGLcoord rmax, GGLcoord rr)
+{
+ const GGLcoord d2 = x*x + y*y;
+ if (d2 >= rmax) return 0;
+ if (d2 < rmin) return 0x7FFF;
+
+ const int kSamples = 4;
+ const int kInc = 4; // 1/4 = 0.25
+ const int kCoverageUnit = 1; // 1/(4^2) = 0.0625
+ const GGLcoord kCoordOffset = -6; // -0.375
+
+ int hits = 0;
+ int x_sample = x + kCoordOffset;
+ for (int i=0 ; i<kSamples ; i++, x_sample += kInc) {
+ const int xval = rr - (x_sample * x_sample);
+ int y_sample = y + kCoordOffset;
+ for (int j=0 ; j<kSamples ; j++, y_sample += kInc) {
+ if (xval - (y_sample * y_sample) > 0)
+ hits += kCoverageUnit;
+ }
+ }
+ return min(0x7FFF, hits << (15 - kSamples));
+}
+
+
+void aa_nice_pointx(void *con, const GGLcoord* v, GGLcoord size)
+{
+ GGL_CONTEXT(c, con);
+
+ GGLcoord rad = ((size + 1)>>1);
+ GGLint l = (v[0] - rad) >> TRI_FRACTION_BITS;
+ GGLint t = (v[1] - rad) >> TRI_FRACTION_BITS;
+ GGLint r = (v[0] + rad + (TRI_ONE-1)) >> TRI_FRACTION_BITS;
+ GGLint b = (v[1] + rad + (TRI_ONE-1)) >> TRI_FRACTION_BITS;
+ GGLcoord xstart = TRI_FROM_INT(l) - v[0] + TRI_HALF;
+ GGLcoord ystart = TRI_FROM_INT(t) - v[1] + TRI_HALF;
+
+ // scissor...
+ if (l < GGLint(c->state.scissor.left)) {
+ xstart += TRI_FROM_INT(c->state.scissor.left-l);
+ l = GGLint(c->state.scissor.left);
+ }
+ if (t < GGLint(c->state.scissor.top)) {
+ ystart += TRI_FROM_INT(c->state.scissor.top-t);
+ t = GGLint(c->state.scissor.top);
+ }
+ if (r > GGLint(c->state.scissor.right)) {
+ r = GGLint(c->state.scissor.right);
+ }
+ if (b > GGLint(c->state.scissor.bottom)) {
+ b = GGLint(c->state.scissor.bottom);
+ }
+
+ int xc = r - l;
+ int yc = b - t;
+ if (xc>0 && yc>0) {
+ int16_t* covPtr = c->state.buffers.coverage;
+ const int32_t sqr2Over2 = 0xC; // rounded up
+ GGLcoord rr = rad*rad;
+ GGLcoord rmin = (rad - sqr2Over2)*(rad - sqr2Over2);
+ GGLcoord rmax = (rad + sqr2Over2)*(rad + sqr2Over2);
+ GGLcoord y = ystart;
+ c->iterators.xl = l;
+ c->iterators.xr = r;
+ c->init_y(c, t);
+ do {
+ // compute coverage factors for each pixel
+ GGLcoord x = xstart;
+ for (int i=l ; i<r ; i++) {
+ covPtr[i] = coverageNice(x, y, rmin, rmax, rr);
+ x += TRI_ONE;
+ }
+ y += TRI_ONE;
+ c->scanline(c);
+ c->step_y(c);
+ } while (--yc);
+ }
+}
+
+// This is a cheap way of computing the coverage factor for a circle.
+// We just lerp between the circles of radii r-sqrt(2)/2 and r+sqrt(2)/2
+static inline int32_t coverageFast(GGLcoord x, GGLcoord y,
+ GGLcoord rmin, GGLcoord rmax, GGLcoord scale)
+{
+ const GGLcoord d2 = x*x + y*y;
+ if (d2 >= rmax) return 0;
+ if (d2 < rmin) return 0x7FFF;
+ return 0x7FFF - (d2-rmin)*scale;
+}
+
+void aa_pointx(void *con, const GGLcoord* v, GGLcoord size)
+{
+ GGL_CONTEXT(c, con);
+
+ GGLcoord rad = ((size + 1)>>1);
+ GGLint l = (v[0] - rad) >> TRI_FRACTION_BITS;
+ GGLint t = (v[1] - rad) >> TRI_FRACTION_BITS;
+ GGLint r = (v[0] + rad + (TRI_ONE-1)) >> TRI_FRACTION_BITS;
+ GGLint b = (v[1] + rad + (TRI_ONE-1)) >> TRI_FRACTION_BITS;
+ GGLcoord xstart = TRI_FROM_INT(l) - v[0] + TRI_HALF;
+ GGLcoord ystart = TRI_FROM_INT(t) - v[1] + TRI_HALF;
+
+ // scissor...
+ if (l < GGLint(c->state.scissor.left)) {
+ xstart += TRI_FROM_INT(c->state.scissor.left-l);
+ l = GGLint(c->state.scissor.left);
+ }
+ if (t < GGLint(c->state.scissor.top)) {
+ ystart += TRI_FROM_INT(c->state.scissor.top-t);
+ t = GGLint(c->state.scissor.top);
+ }
+ if (r > GGLint(c->state.scissor.right)) {
+ r = GGLint(c->state.scissor.right);
+ }
+ if (b > GGLint(c->state.scissor.bottom)) {
+ b = GGLint(c->state.scissor.bottom);
+ }
+
+ int xc = r - l;
+ int yc = b - t;
+ if (xc>0 && yc>0) {
+ int16_t* covPtr = c->state.buffers.coverage;
+ rad <<= 4;
+ const int32_t sqr2Over2 = 0xB5; // fixed-point 24.8
+ GGLcoord rmin = rad - sqr2Over2;
+ GGLcoord rmax = rad + sqr2Over2;
+ GGLcoord scale;
+ rmin *= rmin;
+ rmax *= rmax;
+ scale = 0x800000 / (rmax - rmin);
+ rmin >>= 8;
+ rmax >>= 8;
+
+ GGLcoord y = ystart;
+ c->iterators.xl = l;
+ c->iterators.xr = r;
+ c->init_y(c, t);
+
+ do {
+ // compute coverage factors for each pixel
+ GGLcoord x = xstart;
+ for (int i=l ; i<r ; i++) {
+ covPtr[i] = coverageFast(x, y, rmin, rmax, scale);
+ x += TRI_ONE;
+ }
+ y += TRI_ONE;
+ c->scanline(c);
+ c->step_y(c);
+ } while (--yc);
+ }
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark Line
+#endif
+
+void linex_validate(void *con, const GGLcoord* v0, const GGLcoord* v1, GGLcoord w)
+{
+ GGL_CONTEXT(c, con);
+ ggl_pick(c);
+ if (c->state.needs.p & GGL_NEED_MASK(P_AA)) {
+ c->procs.linex = aa_linex;
+ } else {
+ c->procs.linex = linex;
+ }
+ c->procs.linex(con, v0, v1, w);
+}
+
+static void linex(void *con, const GGLcoord* v0, const GGLcoord* v1, GGLcoord width)
+{
+ GGL_CONTEXT(c, con);
+ GGLcoord v[4][2];
+ v[0][0] = v0[0]; v[0][1] = v0[1];
+ v[1][0] = v1[0]; v[1][1] = v1[1];
+ v0 = v[0];
+ v1 = v[1];
+ const GGLcoord dx = abs(v0[0] - v1[0]);
+ const GGLcoord dy = abs(v0[1] - v1[1]);
+ GGLcoord nx, ny;
+ nx = ny = 0;
+
+ GGLcoord halfWidth = TRI_ROUND(width) >> 1;
+ if (halfWidth == 0)
+ halfWidth = TRI_HALF;
+
+ ((dx > dy) ? ny : nx) = halfWidth;
+ v[2][0] = v1[0]; v[2][1] = v1[1];
+ v[3][0] = v0[0]; v[3][1] = v0[1];
+ v[0][0] += nx; v[0][1] += ny;
+ v[1][0] += nx; v[1][1] += ny;
+ v[2][0] -= nx; v[2][1] -= ny;
+ v[3][0] -= nx; v[3][1] -= ny;
+ trianglex_big(con, v[0], v[1], v[2]);
+ trianglex_big(con, v[0], v[2], v[3]);
+}
+
+static void aa_linex(void *con, const GGLcoord* v0, const GGLcoord* v1, GGLcoord width)
+{
+ GGL_CONTEXT(c, con);
+ GGLcoord v[4][2];
+ v[0][0] = v0[0]; v[0][1] = v0[1];
+ v[1][0] = v1[0]; v[1][1] = v1[1];
+ v0 = v[0];
+ v1 = v[1];
+
+ const GGLcoord dx = v0[0] - v1[0];
+ const GGLcoord dy = v0[1] - v1[1];
+ GGLcoord nx = -dy;
+ GGLcoord ny = dx;
+
+ // generally, this will be well below 1.0
+ const GGLfixed norm = gglMulx(width, gglSqrtRecipx(nx*nx+ny*ny), 4);
+ nx = gglMulx(nx, norm, 21);
+ ny = gglMulx(ny, norm, 21);
+
+ v[2][0] = v1[0]; v[2][1] = v1[1];
+ v[3][0] = v0[0]; v[3][1] = v0[1];
+ v[0][0] += nx; v[0][1] += ny;
+ v[1][0] += nx; v[1][1] += ny;
+ v[2][0] -= nx; v[2][1] -= ny;
+ v[3][0] -= nx; v[3][1] -= ny;
+ aapolyx(con, v[0], 4);
+}
+
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark Rect
+#endif
+
+void recti_validate(void *con, GGLint l, GGLint t, GGLint r, GGLint b)
+{
+ GGL_CONTEXT(c, con);
+ ggl_pick(c);
+ c->procs.recti = recti;
+ c->procs.recti(con, l, t, r, b);
+}
+
+void recti(void* con, GGLint l, GGLint t, GGLint r, GGLint b)
+{
+ GGL_CONTEXT(c, con);
+
+ // scissor...
+ if (l < GGLint(c->state.scissor.left))
+ l = GGLint(c->state.scissor.left);
+ if (t < GGLint(c->state.scissor.top))
+ t = GGLint(c->state.scissor.top);
+ if (r > GGLint(c->state.scissor.right))
+ r = GGLint(c->state.scissor.right);
+ if (b > GGLint(c->state.scissor.bottom))
+ b = GGLint(c->state.scissor.bottom);
+
+ int xc = r - l;
+ int yc = b - t;
+ if (xc>0 && yc>0) {
+ c->iterators.xl = l;
+ c->iterators.xr = r;
+ c->init_y(c, t);
+ c->rect(c, yc);
+ }
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark Triangle / Debugging
+#endif
+
+static void scanline_set(context_t* c)
+{
+ int32_t x = c->iterators.xl;
+ size_t ct = c->iterators.xr - x;
+ int32_t y = c->iterators.y;
+ surface_t* cb = &(c->state.buffers.color);
+ const GGLFormat* fp = &(c->formats[cb->format]);
+ uint8_t* dst = reinterpret_cast<uint8_t*>(cb->data) +
+ (x + (cb->stride * y)) * fp->size;
+ const size_t size = ct * fp->size;
+ memset(dst, 0xFF, size);
+}
+
+static void trianglex_debug(void* con,
+ const GGLcoord* v0, const GGLcoord* v1, const GGLcoord* v2)
+{
+ GGL_CONTEXT(c, con);
+ if (c->state.needs.p & GGL_NEED_MASK(P_AA)) {
+ aa_trianglex(con,v0,v1,v2);
+ } else {
+ trianglex_big(con,v0,v1,v2);
+ }
+ void (*save_scanline)(context_t*) = c->scanline;
+ c->scanline = scanline_set;
+ linex(con, v0, v1, TRI_ONE);
+ linex(con, v1, v2, TRI_ONE);
+ linex(con, v2, v0, TRI_ONE);
+ c->scanline = save_scanline;
+}
+
+static void trianglex_xor(void* con,
+ const GGLcoord* v0, const GGLcoord* v1, const GGLcoord* v2)
+{
+ trianglex_big(con,v0,v1,v2);
+ trianglex_small(con,v0,v1,v2);
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark Triangle
+#endif
+
+void trianglex_validate(void *con,
+ const GGLcoord* v0, const GGLcoord* v1, const GGLcoord* v2)
+{
+ GGL_CONTEXT(c, con);
+ ggl_pick(c);
+ if (c->state.needs.p & GGL_NEED_MASK(P_AA)) {
+ c->procs.trianglex = DEBUG_TRANGLES ? trianglex_debug : aa_trianglex;
+ } else {
+ c->procs.trianglex = DEBUG_TRANGLES ? trianglex_debug : trianglex_big;
+ }
+ c->procs.trianglex(con, v0, v1, v2);
+}
+
+// ----------------------------------------------------------------------------
+
+void trianglex_small(void* con,
+ const GGLcoord* v0, const GGLcoord* v1, const GGLcoord* v2)
+{
+ GGL_CONTEXT(c, con);
+
+ // vertices are in 28.4 fixed point, which allows
+ // us to use 32 bits multiplies below.
+ int32_t x0 = v0[0];
+ int32_t y0 = v0[1];
+ int32_t x1 = v1[0];
+ int32_t y1 = v1[1];
+ int32_t x2 = v2[0];
+ int32_t y2 = v2[1];
+
+ int32_t dx01 = x0 - x1;
+ int32_t dy20 = y2 - y0;
+ int32_t dy01 = y0 - y1;
+ int32_t dx20 = x2 - x0;
+
+ // The code below works only with CCW triangles
+ // so if we get a CW triangle, we need to swap two of its vertices
+ if (dx01*dy20 < dy01*dx20) {
+ swap(x0, x1);
+ swap(y0, y1);
+ dx01 = x0 - x1;
+ dy01 = y0 - y1;
+ dx20 = x2 - x0;
+ dy20 = y2 - y0;
+ }
+ int32_t dx12 = x1 - x2;
+ int32_t dy12 = y1 - y2;
+
+ // bounding box & scissor
+ const int32_t bminx = TRI_FLOOR(min(x0, x1, x2)) >> TRI_FRACTION_BITS;
+ const int32_t bminy = TRI_FLOOR(min(y0, y1, y2)) >> TRI_FRACTION_BITS;
+ const int32_t bmaxx = TRI_CEIL( max(x0, x1, x2)) >> TRI_FRACTION_BITS;
+ const int32_t bmaxy = TRI_CEIL( max(y0, y1, y2)) >> TRI_FRACTION_BITS;
+ const int32_t minx = max(bminx, c->state.scissor.left);
+ const int32_t miny = max(bminy, c->state.scissor.top);
+ const int32_t maxx = min(bmaxx, c->state.scissor.right);
+ const int32_t maxy = min(bmaxy, c->state.scissor.bottom);
+ if ((minx >= maxx) || (miny >= maxy))
+ return; // too small or clipped out...
+
+ // step equations to the bounding box and snap to pixel center
+ const int32_t my = (miny << TRI_FRACTION_BITS) + TRI_HALF;
+ const int32_t mx = (minx << TRI_FRACTION_BITS) + TRI_HALF;
+ int32_t ey0 = dy01 * (x0 - mx) - dx01 * (y0 - my);
+ int32_t ey1 = dy12 * (x1 - mx) - dx12 * (y1 - my);
+ int32_t ey2 = dy20 * (x2 - mx) - dx20 * (y2 - my);
+
+ // right-exclusive fill rule, to avoid rare cases
+ // of over drawing
+ if (dy01<0 || (dy01 == 0 && dx01>0)) ey0++;
+ if (dy12<0 || (dy12 == 0 && dx12>0)) ey1++;
+ if (dy20<0 || (dy20 == 0 && dx20>0)) ey2++;
+
+ c->init_y(c, miny);
+ for (int32_t y = miny; y < maxy; y++) {
+ register int32_t ex0 = ey0;
+ register int32_t ex1 = ey1;
+ register int32_t ex2 = ey2;
+ register int32_t xl, xr;
+ for (xl=minx ; xl<maxx ; xl++) {
+ if (ex0>0 && ex1>0 && ex2>0)
+ break; // all strictly positive
+ ex0 -= dy01 << TRI_FRACTION_BITS;
+ ex1 -= dy12 << TRI_FRACTION_BITS;
+ ex2 -= dy20 << TRI_FRACTION_BITS;
+ }
+ xr = xl;
+ for ( ; xr<maxx ; xr++) {
+ if (!(ex0>0 && ex1>0 && ex2>0))
+ break; // not all strictly positive
+ ex0 -= dy01 << TRI_FRACTION_BITS;
+ ex1 -= dy12 << TRI_FRACTION_BITS;
+ ex2 -= dy20 << TRI_FRACTION_BITS;
+ }
+
+ if (xl < xr) {
+ c->iterators.xl = xl;
+ c->iterators.xr = xr;
+ c->scanline(c);
+ }
+ c->step_y(c);
+
+ ey0 += dx01 << TRI_FRACTION_BITS;
+ ey1 += dx12 << TRI_FRACTION_BITS;
+ ey2 += dx20 << TRI_FRACTION_BITS;
+ }
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#endif
+
+// the following routine fills a triangle via edge stepping, which
+// unfortunately requires divisions in the setup phase to get right,
+// it should probably only be used for relatively large trianges
+
+
+// x = y*DX/DY (ou DX and DY are constants, DY > 0, et y >= 0)
+//
+// for an equation of the type:
+// x' = y*K/2^p (with K and p constants "carefully chosen")
+//
+// We can now do a DDA without precision loss. We define 'e' by:
+// x' - x = y*(DX/DY - K/2^p) = y*e
+//
+// If we choose K = round(DX*2^p/DY) then,
+// abs(e) <= 1/2^(p+1) by construction
+//
+// therefore abs(x'-x) = y*abs(e) <= y/2^(p+1) <= DY/2^(p+1) <= DMAX/2^(p+1)
+//
+// which means that if DMAX <= 2^p, therefore abs(x-x') <= 1/2, including
+// at the last line. In fact, it's even a strict inequality except in one
+// extrem case (DY == DMAX et e = +/- 1/2)
+//
+// Applying that to our coordinates, we need 2^p >= 4096*16 = 65536
+// so p = 16 is enough, we're so lucky!
+
+const int TRI_ITERATORS_BITS = 16;
+
+struct Edge
+{
+ int32_t x; // edge position in 16.16 coordinates
+ int32_t x_incr; // on each step, increment x by that amount
+ int32_t y_top; // starting scanline, 16.4 format
+ int32_t y_bot;
+};
+
+static void
+edge_dump( Edge* edge )
+{
+ ALOGI( " top=%d (%.3f) bot=%d (%.3f) x=%d (%.3f) ix=%d (%.3f)",
+ edge->y_top, edge->y_top/float(TRI_ONE),
+ edge->y_bot, edge->y_bot/float(TRI_ONE),
+ edge->x, edge->x/float(FIXED_ONE),
+ edge->x_incr, edge->x_incr/float(FIXED_ONE) );
+}
+
+static void
+triangle_dump_edges( Edge* edges,
+ int count )
+{
+ ALOGI( "%d edge%s:\n", count, count == 1 ? "" : "s" );
+ for ( ; count > 0; count--, edges++ )
+ edge_dump( edges );
+}
+
+// the following function sets up an edge, it assumes
+// that ymin and ymax are in already in the 'reduced'
+// format
+static __attribute__((noinline))
+void edge_setup(
+ Edge* edges,
+ int* pcount,
+ const GGLcoord* p1,
+ const GGLcoord* p2,
+ int32_t ymin,
+ int32_t ymax )
+{
+ const GGLfixed* top = p1;
+ const GGLfixed* bot = p2;
+ Edge* edge = edges + *pcount;
+
+ if (top[1] > bot[1]) {
+ swap(top, bot);
+ }
+
+ int y1 = top[1] | 1;
+ int y2 = bot[1] | 1;
+ int dy = y2 - y1;
+
+ if ( dy == 0 || y1 > ymax || y2 < ymin )
+ return;
+
+ if ( y1 > ymin )
+ ymin = TRI_SNAP_NEXT_HALF(y1);
+
+ if ( y2 < ymax )
+ ymax = TRI_SNAP_PREV_HALF(y2);
+
+ if ( ymin > ymax ) // when the edge doesn't cross any scanline
+ return;
+
+ const int x1 = top[0];
+ const int dx = bot[0] - x1;
+ const int shift = TRI_ITERATORS_BITS - TRI_FRACTION_BITS;
+
+ // setup edge fields
+ // We add 0.5 to edge->x here because it simplifies the rounding
+ // in triangle_sweep_edges() -- this doesn't change the ordering of 'x'
+ edge->x = (x1 << shift) + (1LU << (TRI_ITERATORS_BITS-1));
+ edge->x_incr = 0;
+ edge->y_top = ymin;
+ edge->y_bot = ymax;
+
+ if (ggl_likely(ymin <= ymax && dx)) {
+ edge->x_incr = gglDivQ16(dx, dy);
+ }
+ if (ggl_likely(y1 < ymin)) {
+ int32_t xadjust = (edge->x_incr * (ymin-y1)) >> TRI_FRACTION_BITS;
+ edge->x += xadjust;
+ }
+
+ ++*pcount;
+}
+
+
+static void
+triangle_sweep_edges( Edge* left,
+ Edge* right,
+ int ytop,
+ int ybot,
+ context_t* c )
+{
+ int count = ((ybot - ytop)>>TRI_FRACTION_BITS) + 1;
+ if (count<=0) return;
+
+ // sort the edges horizontally
+ if ((left->x > right->x) ||
+ ((left->x == right->x) && (left->x_incr > right->x_incr))) {
+ swap(left, right);
+ }
+
+ int left_x = left->x;
+ int right_x = right->x;
+ const int left_xi = left->x_incr;
+ const int right_xi = right->x_incr;
+ left->x += left_xi * count;
+ right->x += right_xi * count;
+
+ const int xmin = c->state.scissor.left;
+ const int xmax = c->state.scissor.right;
+ do {
+ // horizontal scissoring
+ const int32_t xl = max(left_x >> TRI_ITERATORS_BITS, xmin);
+ const int32_t xr = min(right_x >> TRI_ITERATORS_BITS, xmax);
+ left_x += left_xi;
+ right_x += right_xi;
+ // invoke the scanline rasterizer
+ if (ggl_likely(xl < xr)) {
+ c->iterators.xl = xl;
+ c->iterators.xr = xr;
+ c->scanline(c);
+ }
+ c->step_y(c);
+ } while (--count);
+}
+
+
+void trianglex_big(void* con,
+ const GGLcoord* v0, const GGLcoord* v1, const GGLcoord* v2)
+{
+ GGL_CONTEXT(c, con);
+
+ Edge edges[3];
+ int num_edges = 0;
+ int32_t ymin = TRI_FROM_INT(c->state.scissor.top) + TRI_HALF;
+ int32_t ymax = TRI_FROM_INT(c->state.scissor.bottom) - TRI_HALF;
+
+ edge_setup( edges, &num_edges, v0, v1, ymin, ymax );
+ edge_setup( edges, &num_edges, v0, v2, ymin, ymax );
+ edge_setup( edges, &num_edges, v1, v2, ymin, ymax );
+
+ if (ggl_unlikely(num_edges<2)) // for really tiny triangles that don't
+ return; // cross any scanline centers
+
+ Edge* left = &edges[0];
+ Edge* right = &edges[1];
+ Edge* other = &edges[2];
+ int32_t y_top = min(left->y_top, right->y_top);
+ int32_t y_bot = max(left->y_bot, right->y_bot);
+
+ if (ggl_likely(num_edges==3)) {
+ y_top = min(y_top, edges[2].y_top);
+ y_bot = max(y_bot, edges[2].y_bot);
+ if (edges[0].y_top > y_top) {
+ other = &edges[0];
+ left = &edges[2];
+ } else if (edges[1].y_top > y_top) {
+ other = &edges[1];
+ right = &edges[2];
+ }
+ }
+
+ c->init_y(c, y_top >> TRI_FRACTION_BITS);
+
+ int32_t y_mid = min(left->y_bot, right->y_bot);
+ triangle_sweep_edges( left, right, y_top, y_mid, c );
+
+ // second scanline sweep loop, if necessary
+ y_mid += TRI_ONE;
+ if (y_mid <= y_bot) {
+ ((left->y_bot == y_bot) ? right : left) = other;
+ if (other->y_top < y_mid) {
+ other->x += other->x_incr;
+ }
+ triangle_sweep_edges( left, right, y_mid, y_bot, c );
+ }
+}
+
+void aa_trianglex(void* con,
+ const GGLcoord* a, const GGLcoord* b, const GGLcoord* c)
+{
+ GGLcoord pts[6] = { a[0], a[1], b[0], b[1], c[0], c[1] };
+ aapolyx(con, pts, 3);
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#endif
+
+struct AAEdge
+{
+ GGLfixed x; // edge position in 12.16 coordinates
+ GGLfixed x_incr; // on each y step, increment x by that amount
+ GGLfixed y_incr; // on each x step, increment y by that amount
+ int16_t y_top; // starting scanline, 12.4 format
+ int16_t y_bot; // starting scanline, 12.4 format
+ void dump();
+};
+
+void AAEdge::dump()
+{
+ float tri = 1.0f / TRI_ONE;
+ float iter = 1.0f / (1<<TRI_ITERATORS_BITS);
+ float fix = 1.0f / FIXED_ONE;
+ ALOGD( "x=%08x (%.3f), "
+ "x_incr=%08x (%.3f), y_incr=%08x (%.3f), "
+ "y_top=%08x (%.3f), y_bot=%08x (%.3f) ",
+ x, x*fix,
+ x_incr, x_incr*iter,
+ y_incr, y_incr*iter,
+ y_top, y_top*tri,
+ y_bot, y_bot*tri );
+}
+
+// the following function sets up an edge, it assumes
+// that ymin and ymax are in already in the 'reduced'
+// format
+static __attribute__((noinline))
+void aa_edge_setup(
+ AAEdge* edges,
+ int* pcount,
+ const GGLcoord* p1,
+ const GGLcoord* p2,
+ int32_t ymin,
+ int32_t ymax )
+{
+ const GGLfixed* top = p1;
+ const GGLfixed* bot = p2;
+ AAEdge* edge = edges + *pcount;
+
+ if (top[1] > bot[1])
+ swap(top, bot);
+
+ int y1 = top[1];
+ int y2 = bot[1];
+ int dy = y2 - y1;
+
+ if (dy==0 || y1>ymax || y2<ymin)
+ return;
+
+ if (y1 > ymin)
+ ymin = y1;
+
+ if (y2 < ymax)
+ ymax = y2;
+
+ const int x1 = top[0];
+ const int dx = bot[0] - x1;
+ const int shift = FIXED_BITS - TRI_FRACTION_BITS;
+
+ // setup edge fields
+ edge->x = x1 << shift;
+ edge->x_incr = 0;
+ edge->y_top = ymin;
+ edge->y_bot = ymax;
+ edge->y_incr = 0x7FFFFFFF;
+
+ if (ggl_likely(ymin <= ymax && dx)) {
+ edge->x_incr = gglDivQ16(dx, dy);
+ if (dx != 0) {
+ edge->y_incr = abs(gglDivQ16(dy, dx));
+ }
+ }
+ if (ggl_likely(y1 < ymin)) {
+ int32_t xadjust = (edge->x_incr * (ymin-y1))
+ >> (TRI_FRACTION_BITS + TRI_ITERATORS_BITS - FIXED_BITS);
+ edge->x += xadjust;
+ }
+
+ ++*pcount;
+}
+
+
+typedef int (*compar_t)(const void*, const void*);
+static int compare_edges(const AAEdge *e0, const AAEdge *e1) {
+ if (e0->y_top > e1->y_top) return 1;
+ if (e0->y_top < e1->y_top) return -1;
+ if (e0->x > e1->x) return 1;
+ if (e0->x < e1->x) return -1;
+ if (e0->x_incr > e1->x_incr) return 1;
+ if (e0->x_incr < e1->x_incr) return -1;
+ return 0; // same edges, should never happen
+}
+
+static inline
+void SET_COVERAGE(int16_t*& p, int32_t value, ssize_t n)
+{
+ android_memset16((uint16_t*)p, value, n*2);
+ p += n;
+}
+
+static inline
+void ADD_COVERAGE(int16_t*& p, int32_t value)
+{
+ value = *p + value;
+ if (value >= 0x8000)
+ value = 0x7FFF;
+ *p++ = value;
+}
+
+static inline
+void SUB_COVERAGE(int16_t*& p, int32_t value)
+{
+ value = *p - value;
+ value &= ~(value>>31);
+ *p++ = value;
+}
+
+void aapolyx(void* con,
+ const GGLcoord* pts, int count)
+{
+ /*
+ * NOTE: This routine assumes that the polygon has been clipped to the
+ * viewport already, that is, no vertex lies outside of the framebuffer.
+ * If this happens, the code below won't corrupt memory but the
+ * coverage values may not be correct.
+ */
+
+ GGL_CONTEXT(c, con);
+
+ // we do only quads for now (it's used for thick lines)
+ if ((count>4) || (count<2)) return;
+
+ // take scissor into account
+ const int xmin = c->state.scissor.left;
+ const int xmax = c->state.scissor.right;
+ if (xmin >= xmax) return;
+
+ // generate edges from the vertices
+ int32_t ymin = TRI_FROM_INT(c->state.scissor.top);
+ int32_t ymax = TRI_FROM_INT(c->state.scissor.bottom);
+ if (ymin >= ymax) return;
+
+ AAEdge edges[4];
+ int num_edges = 0;
+ GGLcoord const * p = pts;
+ for (int i=0 ; i<count-1 ; i++, p+=2) {
+ aa_edge_setup(edges, &num_edges, p, p+2, ymin, ymax);
+ }
+ aa_edge_setup(edges, &num_edges, p, pts, ymin, ymax );
+ if (ggl_unlikely(num_edges<2))
+ return;
+
+ // sort the edge list top to bottom, left to right.
+ qsort(edges, num_edges, sizeof(AAEdge), (compar_t)compare_edges);
+
+ int16_t* const covPtr = c->state.buffers.coverage;
+ memset(covPtr+xmin, 0, (xmax-xmin)*sizeof(*covPtr));
+
+ // now, sweep all edges in order
+ // start with the 2 first edges. We know that they share their top
+ // vertex, by construction.
+ int i = 2;
+ AAEdge* left = &edges[0];
+ AAEdge* right = &edges[1];
+ int32_t yt = left->y_top;
+ GGLfixed l = left->x;
+ GGLfixed r = right->x;
+ int retire = 0;
+ int16_t* coverage;
+
+ // at this point we can initialize the rasterizer
+ c->init_y(c, yt>>TRI_FRACTION_BITS);
+ c->iterators.xl = xmax;
+ c->iterators.xr = xmin;
+
+ do {
+ int32_t y = min(min(left->y_bot, right->y_bot), TRI_FLOOR(yt + TRI_ONE));
+ const int32_t shift = TRI_FRACTION_BITS + TRI_ITERATORS_BITS - FIXED_BITS;
+ const int cf_shift = (1 + TRI_FRACTION_BITS*2 + TRI_ITERATORS_BITS - 15);
+
+ // compute xmin and xmax for the left edge
+ GGLfixed l_min = gglMulAddx(left->x_incr, y - left->y_top, left->x, shift);
+ GGLfixed l_max = l;
+ l = l_min;
+ if (l_min > l_max)
+ swap(l_min, l_max);
+
+ // compute xmin and xmax for the right edge
+ GGLfixed r_min = gglMulAddx(right->x_incr, y - right->y_top, right->x, shift);
+ GGLfixed r_max = r;
+ r = r_min;
+ if (r_min > r_max)
+ swap(r_min, r_max);
+
+ // make sure we're not touching coverage values outside of the
+ // framebuffer
+ l_min &= ~(l_min>>31);
+ r_min &= ~(r_min>>31);
+ l_max &= ~(l_max>>31);
+ r_max &= ~(r_max>>31);
+ if (gglFixedToIntFloor(l_min) >= xmax) l_min = gglIntToFixed(xmax)-1;
+ if (gglFixedToIntFloor(r_min) >= xmax) r_min = gglIntToFixed(xmax)-1;
+ if (gglFixedToIntCeil(l_max) >= xmax) l_max = gglIntToFixed(xmax)-1;
+ if (gglFixedToIntCeil(r_max) >= xmax) r_max = gglIntToFixed(xmax)-1;
+
+ // compute the integer versions of the above
+ const GGLfixed l_min_i = gglFloorx(l_min);
+ const GGLfixed l_max_i = gglCeilx (l_max);
+ const GGLfixed r_min_i = gglFloorx(r_min);
+ const GGLfixed r_max_i = gglCeilx (r_max);
+
+ // clip horizontally using the scissor
+ const int xml = max(xmin, gglFixedToIntFloor(l_min_i));
+ const int xmr = min(xmax, gglFixedToIntFloor(r_max_i));
+
+ // if we just stepped to a new scanline, render the previous one.
+ // and clear the coverage buffer
+ if (retire) {
+ if (c->iterators.xl < c->iterators.xr)
+ c->scanline(c);
+ c->step_y(c);
+ memset(covPtr+xmin, 0, (xmax-xmin)*sizeof(*covPtr));
+ c->iterators.xl = xml;
+ c->iterators.xr = xmr;
+ } else {
+ // update the horizontal range of this scanline
+ c->iterators.xl = min(c->iterators.xl, xml);
+ c->iterators.xr = max(c->iterators.xr, xmr);
+ }
+
+ coverage = covPtr + gglFixedToIntFloor(l_min_i);
+ if (l_min_i == gglFloorx(l_max)) {
+
+ /*
+ * fully traverse this pixel vertically
+ * l_max
+ * +-----/--+ yt
+ * | / |
+ * | / |
+ * | / |
+ * +-/------+ y
+ * l_min (l_min_i + TRI_ONE)
+ */
+
+ GGLfixed dx = l_max - l_min;
+ int32_t dy = y - yt;
+ int cf = gglMulx((dx >> 1) + (l_min_i + FIXED_ONE - l_max), dy,
+ FIXED_BITS + TRI_FRACTION_BITS - 15);
+ ADD_COVERAGE(coverage, cf);
+ // all pixels on the right have cf = 1.0
+ } else {
+ /*
+ * spans several pixels in one scanline
+ * l_max
+ * +--------+--/-----+ yt
+ * | |/ |
+ * | /| |
+ * | / | |
+ * +---/----+--------+ y
+ * l_min (l_min_i + TRI_ONE)
+ */
+
+ // handle the first pixel separately...
+ const int32_t y_incr = left->y_incr;
+ int32_t dx = TRI_FROM_FIXED(l_min_i - l_min) + TRI_ONE;
+ int32_t cf = (dx * dx * y_incr) >> cf_shift;
+ ADD_COVERAGE(coverage, cf);
+
+ // following pixels get covered by y_incr, but we need
+ // to fix-up the cf to account for previous partial pixel
+ dx = TRI_FROM_FIXED(l_min - l_min_i);
+ cf -= (dx * dx * y_incr) >> cf_shift;
+ for (int x = l_min_i+FIXED_ONE ; x < l_max_i-FIXED_ONE ; x += FIXED_ONE) {
+ cf += y_incr >> (TRI_ITERATORS_BITS-15);
+ ADD_COVERAGE(coverage, cf);
+ }
+
+ // and the last pixel
+ dx = TRI_FROM_FIXED(l_max - l_max_i) - TRI_ONE;
+ cf += (dx * dx * y_incr) >> cf_shift;
+ ADD_COVERAGE(coverage, cf);
+ }
+
+ // now, fill up all fully covered pixels
+ coverage = covPtr + gglFixedToIntFloor(l_max_i);
+ int cf = ((y - yt) << (15 - TRI_FRACTION_BITS));
+ if (ggl_likely(cf >= 0x8000)) {
+ SET_COVERAGE(coverage, 0x7FFF, ((r_max - l_max_i)>>FIXED_BITS)+1);
+ } else {
+ for (int x=l_max_i ; x<r_max ; x+=FIXED_ONE) {
+ ADD_COVERAGE(coverage, cf);
+ }
+ }
+
+ // subtract the coverage of the right edge
+ coverage = covPtr + gglFixedToIntFloor(r_min_i);
+ if (r_min_i == gglFloorx(r_max)) {
+ GGLfixed dx = r_max - r_min;
+ int32_t dy = y - yt;
+ int cf = gglMulx((dx >> 1) + (r_min_i + FIXED_ONE - r_max), dy,
+ FIXED_BITS + TRI_FRACTION_BITS - 15);
+ SUB_COVERAGE(coverage, cf);
+ // all pixels on the right have cf = 1.0
+ } else {
+ // handle the first pixel separately...
+ const int32_t y_incr = right->y_incr;
+ int32_t dx = TRI_FROM_FIXED(r_min_i - r_min) + TRI_ONE;
+ int32_t cf = (dx * dx * y_incr) >> cf_shift;
+ SUB_COVERAGE(coverage, cf);
+
+ // following pixels get covered by y_incr, but we need
+ // to fix-up the cf to account for previous partial pixel
+ dx = TRI_FROM_FIXED(r_min - r_min_i);
+ cf -= (dx * dx * y_incr) >> cf_shift;
+ for (int x = r_min_i+FIXED_ONE ; x < r_max_i-FIXED_ONE ; x += FIXED_ONE) {
+ cf += y_incr >> (TRI_ITERATORS_BITS-15);
+ SUB_COVERAGE(coverage, cf);
+ }
+
+ // and the last pixel
+ dx = TRI_FROM_FIXED(r_max - r_max_i) - TRI_ONE;
+ cf += (dx * dx * y_incr) >> cf_shift;
+ SUB_COVERAGE(coverage, cf);
+ }
+
+ // did we reach the end of an edge? if so, get a new one.
+ if (y == left->y_bot || y == right->y_bot) {
+ // bail out if we're done
+ if (i>=num_edges)
+ break;
+ if (y == left->y_bot)
+ left = &edges[i++];
+ if (y == right->y_bot)
+ right = &edges[i++];
+ }
+
+ // next scanline
+ yt = y;
+
+ // did we just finish a scanline?
+ retire = (y << (32-TRI_FRACTION_BITS)) == 0;
+ } while (true);
+
+ // render the last scanline
+ if (c->iterators.xl < c->iterators.xr)
+ c->scanline(c);
+}
+
+}; // namespace android
diff --git a/libpixelflinger/trap.h b/libpixelflinger/trap.h
new file mode 100644
index 0000000..7cce7b3
--- /dev/null
+++ b/libpixelflinger/trap.h
@@ -0,0 +1,31 @@
+/* libs/pixelflinger/trap.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+
+#ifndef ANDROID_TRAP_H
+#define ANDROID_TRAP_H
+
+#include <private/pixelflinger/ggl_context.h>
+
+namespace android {
+
+void ggl_init_trap(context_t* c);
+void ggl_state_changed(context_t* c, int flags);
+
+}; // namespace android
+
+#endif
diff --git a/libprocessgroup/Android.mk b/libprocessgroup/Android.mk
new file mode 100644
index 0000000..ee6ba58
--- /dev/null
+++ b/libprocessgroup/Android.mk
@@ -0,0 +1,20 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := processgroup.cpp
+LOCAL_MODULE := libprocessgroup
+LOCAL_SHARED_LIBRARIES := liblog libutils
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_CFLAGS := -Wall -Werror
+LOCAL_REQUIRED_MODULE := processgroup_cleanup
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := cleanup.cpp
+LOCAL_MODULE := processgroup_cleanup
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_CFLAGS := -Wall -Werror
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_STATIC_LIBRARIES := libc libcutils
+include $(BUILD_EXECUTABLE)
diff --git a/libprocessgroup/cleanup.cpp b/libprocessgroup/cleanup.cpp
new file mode 100644
index 0000000..cca8dc4
--- /dev/null
+++ b/libprocessgroup/cleanup.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2014 Google, Inc
+ *
+ * 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 <string.h>
+#include <unistd.h>
+#include <sys/syslimits.h>
+
+#include "processgroup_priv.h"
+
+int main(int argc, char **argv)
+{
+ char buf[PATH_MAX];
+ if (argc != 2)
+ return -1;
+
+ memcpy(buf, PROCESSGROUP_CGROUP_PATH, sizeof(PROCESSGROUP_CGROUP_PATH));
+ strlcat(buf, argv[1], sizeof(buf));
+ return rmdir(buf);
+}
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
new file mode 100644
index 0000000..11bd8cc
--- /dev/null
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2014 Google, Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _PROCESSGROUP_H_
+#define _PROCESSGROUP_H_
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+__BEGIN_DECLS
+
+int killProcessGroup(uid_t uid, int initialPid, int signal);
+
+int createProcessGroup(uid_t uid, int initialPid);
+
+void removeAllProcessGroups(void);
+
+__END_DECLS
+
+#endif
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
new file mode 100644
index 0000000..a80965f
--- /dev/null
+++ b/libprocessgroup/processgroup.cpp
@@ -0,0 +1,343 @@
+/*
+ * Copyright 2014 Google, Inc
+ *
+ * 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_NDEBUG 0
+#define LOG_TAG "libprocessgroup"
+
+#include <assert.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <log/log.h>
+#include <private/android_filesystem_config.h>
+
+#include <utils/SystemClock.h>
+
+#include <processgroup/processgroup.h>
+#include "processgroup_priv.h"
+
+struct ctx {
+ bool initialized;
+ int fd;
+ char buf[128];
+ char *buf_ptr;
+ size_t buf_len;
+};
+
+static int convertUidToPath(char *path, size_t size, uid_t uid)
+{
+ return snprintf(path, size, "%s/%s%d",
+ PROCESSGROUP_CGROUP_PATH,
+ PROCESSGROUP_UID_PREFIX,
+ uid);
+}
+
+static int convertUidPidToPath(char *path, size_t size, uid_t uid, int pid)
+{
+ return snprintf(path, size, "%s/%s%d/%s%d",
+ PROCESSGROUP_CGROUP_PATH,
+ PROCESSGROUP_UID_PREFIX,
+ uid,
+ PROCESSGROUP_PID_PREFIX,
+ pid);
+}
+
+static int initCtx(uid_t uid, int pid, struct ctx *ctx)
+{
+ int ret;
+ char path[PROCESSGROUP_MAX_PATH_LEN] = {0};
+ convertUidPidToPath(path, sizeof(path), uid, pid);
+ strlcat(path, PROCESSGROUP_CGROUP_PROCS_FILE, sizeof(path));
+
+ int fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ ret = -errno;
+ SLOGW("failed to open %s: %s", path, strerror(errno));
+ return ret;
+ }
+
+ ctx->fd = fd;
+ ctx->buf_ptr = ctx->buf;
+ ctx->buf_len = 0;
+ ctx->initialized = true;
+
+ SLOGV("Initialized context for %s", path);
+
+ return 0;
+}
+
+static int refillBuffer(struct ctx *ctx)
+{
+ memmove(ctx->buf, ctx->buf_ptr, ctx->buf_len);
+ ctx->buf_ptr = ctx->buf;
+
+ ssize_t ret = read(ctx->fd, ctx->buf_ptr + ctx->buf_len,
+ sizeof(ctx->buf) - ctx->buf_len - 1);
+ if (ret < 0) {
+ return -errno;
+ } else if (ret == 0) {
+ return 0;
+ }
+
+ ctx->buf_len += ret;
+ ctx->buf[ctx->buf_len] = 0;
+ SLOGV("Read %zd to buffer: %s", ret, ctx->buf);
+
+ assert(ctx->buf_len <= sizeof(ctx->buf));
+
+ return ret;
+}
+
+static pid_t getOneAppProcess(uid_t uid, int appProcessPid, struct ctx *ctx)
+{
+ if (!ctx->initialized) {
+ int ret = initCtx(uid, appProcessPid, ctx);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ char *eptr;
+ while ((eptr = (char *)memchr(ctx->buf_ptr, '\n', ctx->buf_len)) == NULL) {
+ int ret = refillBuffer(ctx);
+ if (ret == 0) {
+ return -ERANGE;
+ }
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ *eptr = '\0';
+ char *pid_eptr = NULL;
+ errno = 0;
+ long pid = strtol(ctx->buf_ptr, &pid_eptr, 10);
+ if (errno != 0) {
+ return -errno;
+ }
+ if (pid_eptr != eptr) {
+ return -EINVAL;
+ }
+
+ ctx->buf_len -= (eptr - ctx->buf_ptr) + 1;
+ ctx->buf_ptr = eptr + 1;
+
+ return (pid_t)pid;
+}
+
+static int removeProcessGroup(uid_t uid, int pid)
+{
+ int ret;
+ char path[PROCESSGROUP_MAX_PATH_LEN] = {0};
+
+ convertUidPidToPath(path, sizeof(path), uid, pid);
+ ret = rmdir(path);
+
+ convertUidToPath(path, sizeof(path), uid);
+ rmdir(path);
+
+ return ret;
+}
+
+static void removeUidProcessGroups(const char *uid_path)
+{
+ DIR *uid = opendir(uid_path);
+ if (uid != NULL) {
+ struct dirent cur;
+ struct dirent *dir;
+ while ((readdir_r(uid, &cur, &dir) == 0) && dir) {
+ char path[PROCESSGROUP_MAX_PATH_LEN];
+
+ if (dir->d_type != DT_DIR) {
+ continue;
+ }
+
+ if (strncmp(dir->d_name, PROCESSGROUP_PID_PREFIX, strlen(PROCESSGROUP_PID_PREFIX))) {
+ continue;
+ }
+
+ snprintf(path, sizeof(path), "%s/%s", uid_path, dir->d_name);
+ SLOGV("removing %s\n", path);
+ rmdir(path);
+ }
+ closedir(uid);
+ }
+}
+
+void removeAllProcessGroups()
+{
+ SLOGV("removeAllProcessGroups()");
+ DIR *root = opendir(PROCESSGROUP_CGROUP_PATH);
+ if (root == NULL) {
+ SLOGE("failed to open %s: %s", PROCESSGROUP_CGROUP_PATH, strerror(errno));
+ } else {
+ struct dirent cur;
+ struct dirent *dir;
+ while ((readdir_r(root, &cur, &dir) == 0) && dir) {
+ char path[PROCESSGROUP_MAX_PATH_LEN];
+
+ if (dir->d_type != DT_DIR) {
+ continue;
+ }
+ if (strncmp(dir->d_name, PROCESSGROUP_UID_PREFIX, strlen(PROCESSGROUP_UID_PREFIX))) {
+ continue;
+ }
+
+ snprintf(path, sizeof(path), "%s/%s", PROCESSGROUP_CGROUP_PATH, dir->d_name);
+ removeUidProcessGroups(path);
+ SLOGV("removing %s\n", path);
+ rmdir(path);
+ }
+ closedir(root);
+ }
+}
+
+static int killProcessGroupOnce(uid_t uid, int initialPid, int signal)
+{
+ int processes = 0;
+ struct ctx ctx;
+ pid_t pid;
+
+ ctx.initialized = false;
+
+ while ((pid = getOneAppProcess(uid, initialPid, &ctx)) >= 0) {
+ processes++;
+ if (pid == 0) {
+ // Should never happen... but if it does, trying to kill this
+ // will boomerang right back and kill us! Let's not let that happen.
+ SLOGW("Yikes, we've been told to kill pid 0! How about we don't do that.");
+ continue;
+ }
+ if (pid != initialPid) {
+ // We want to be noisy about killing processes so we can understand
+ // what is going on in the log; however, don't be noisy about the base
+ // process, since that it something we always kill, and we have already
+ // logged elsewhere about killing it.
+ SLOGI("Killing pid %d in uid %d as part of process group %d", pid, uid, initialPid);
+ }
+ int ret = kill(pid, signal);
+ if (ret == -1) {
+ SLOGW("failed to kill pid %d: %s", pid, strerror(errno));
+ }
+ }
+
+ if (ctx.initialized) {
+ close(ctx.fd);
+ }
+
+ return processes;
+}
+
+int killProcessGroup(uid_t uid, int initialPid, int signal)
+{
+ int processes;
+ int sleep_us = 100;
+ int64_t startTime = android::uptimeMillis();
+
+ while ((processes = killProcessGroupOnce(uid, initialPid, signal)) > 0) {
+ SLOGV("killed %d processes for processgroup %d\n", processes, initialPid);
+ if (sleep_us < 128000) {
+ usleep(sleep_us);
+ sleep_us *= 2;
+ } else {
+ SLOGE("failed to kill %d processes for processgroup %d\n",
+ processes, initialPid);
+ break;
+ }
+ }
+
+ SLOGV("Killed process group uid %d pid %d in %" PRId64 "ms, %d procs remain", uid, initialPid,
+ android::uptimeMillis()-startTime, processes);
+
+ if (processes == 0) {
+ return removeProcessGroup(uid, initialPid);
+ } else {
+ return -1;
+ }
+}
+
+static int mkdirAndChown(const char *path, mode_t mode, uid_t uid, gid_t gid)
+{
+ int ret;
+
+ ret = mkdir(path, mode);
+ if (ret < 0 && errno != EEXIST) {
+ return -errno;
+ }
+
+ ret = chown(path, uid, gid);
+ if (ret < 0) {
+ ret = -errno;
+ rmdir(path);
+ return ret;
+ }
+
+ return 0;
+}
+
+int createProcessGroup(uid_t uid, int initialPid)
+{
+ char path[PROCESSGROUP_MAX_PATH_LEN] = {0};
+ int ret;
+
+ convertUidToPath(path, sizeof(path), uid);
+
+ ret = mkdirAndChown(path, 0750, AID_SYSTEM, AID_SYSTEM);
+ if (ret < 0) {
+ SLOGE("failed to make and chown %s: %s", path, strerror(-ret));
+ return ret;
+ }
+
+ convertUidPidToPath(path, sizeof(path), uid, initialPid);
+
+ ret = mkdirAndChown(path, 0750, AID_SYSTEM, AID_SYSTEM);
+ if (ret < 0) {
+ SLOGE("failed to make and chown %s: %s", path, strerror(-ret));
+ return ret;
+ }
+
+ strlcat(path, PROCESSGROUP_CGROUP_PROCS_FILE, sizeof(path));
+
+ int fd = open(path, O_WRONLY);
+ if (fd < 0) {
+ ret = -errno;
+ SLOGE("failed to open %s: %s", path, strerror(errno));
+ return ret;
+ }
+
+ char pid[PROCESSGROUP_MAX_PID_LEN + 1] = {0};
+ int len = snprintf(pid, sizeof(pid), "%d", initialPid);
+
+ ret = write(fd, pid, len);
+ if (ret < 0) {
+ ret = -errno;
+ SLOGE("failed to write '%s' to %s: %s", pid, path, strerror(errno));
+ } else {
+ ret = 0;
+ }
+
+ close(fd);
+ return ret;
+}
+
diff --git a/libprocessgroup/processgroup_priv.h b/libprocessgroup/processgroup_priv.h
new file mode 100644
index 0000000..1895bf9
--- /dev/null
+++ b/libprocessgroup/processgroup_priv.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2014 Google, Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _PROCESSGROUP_PRIV_H_
+#define _PROCESSGROUP_PRIV_H_
+
+#define PROCESSGROUP_CGROUP_PATH "/acct"
+#define PROCESSGROUP_UID_PREFIX "uid_"
+#define PROCESSGROUP_PID_PREFIX "pid_"
+#define PROCESSGROUP_CGROUP_PROCS_FILE "/cgroup.procs"
+#define PROCESSGROUP_MAX_UID_LEN 11
+#define PROCESSGROUP_MAX_PID_LEN 11
+#define PROCESSGROUP_MAX_PATH_LEN \
+ (sizeof(PROCESSGROUP_CGROUP_PATH) + \
+ sizeof(PROCESSGROUP_UID_PREFIX) + 1 + \
+ PROCESSGROUP_MAX_UID_LEN + \
+ sizeof(PROCESSGROUP_PID_PREFIX) + 1 + \
+ PROCESSGROUP_MAX_PID_LEN + \
+ sizeof(PROCESSGROUP_CGROUP_PROCS_FILE) + \
+ 1)
+
+#endif
diff --git a/libsparse/Android.mk b/libsparse/Android.mk
new file mode 100644
index 0000000..925b98b
--- /dev/null
+++ b/libsparse/Android.mk
@@ -0,0 +1,110 @@
+# Copyright 2010 The Android Open Source Project
+
+LOCAL_PATH:= $(call my-dir)
+
+libsparse_src_files := \
+ backed_block.c \
+ output_file.c \
+ sparse.c \
+ sparse_crc32.c \
+ sparse_err.c \
+ sparse_read.c
+
+
+include $(CLEAR_VARS)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_SRC_FILES := $(libsparse_src_files)
+LOCAL_MODULE := libsparse_host
+LOCAL_STATIC_LIBRARIES := libz
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_CFLAGS := -Werror
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+
+include $(CLEAR_VARS)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_SRC_FILES := $(libsparse_src_files)
+LOCAL_MODULE := libsparse
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
+LOCAL_SHARED_LIBRARIES := \
+ libz
+LOCAL_CFLAGS := -Werror
+include $(BUILD_SHARED_LIBRARY)
+
+
+include $(CLEAR_VARS)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_SRC_FILES := $(libsparse_src_files)
+LOCAL_MODULE := libsparse_static
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
+LOCAL_STATIC_LIBRARIES := libz
+LOCAL_CFLAGS := -Werror
+include $(BUILD_STATIC_LIBRARY)
+
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := simg2img.c \
+ sparse_crc32.c
+LOCAL_MODULE := simg2img_host
+# Need a unique module name, but exe should still be called simg2img
+LOCAL_MODULE_STEM := simg2img
+LOCAL_STATIC_LIBRARIES := \
+ libsparse_host \
+ libz
+LOCAL_CFLAGS := -Werror
+include $(BUILD_HOST_EXECUTABLE)
+
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := simg2img.c \
+ sparse_crc32.c
+LOCAL_MODULE := simg2img
+LOCAL_STATIC_LIBRARIES := \
+ libsparse_static \
+ libz
+LOCAL_CFLAGS := -Werror
+include $(BUILD_EXECUTABLE)
+
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := img2simg.c
+LOCAL_MODULE := img2simg_host
+# Need a unique module name, but exe should still be called simg2img
+LOCAL_MODULE_STEM := img2simg
+LOCAL_STATIC_LIBRARIES := \
+ libsparse_host \
+ libz
+LOCAL_CFLAGS := -Werror
+include $(BUILD_HOST_EXECUTABLE)
+
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := img2simg.c
+LOCAL_MODULE := img2simg
+LOCAL_STATIC_LIBRARIES := \
+ libsparse_static \
+ libz
+LOCAL_CFLAGS := -Werror
+include $(BUILD_EXECUTABLE)
+
+
+ifneq ($(HOST_OS),windows)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := append2simg.c
+LOCAL_MODULE := append2simg
+LOCAL_STATIC_LIBRARIES := \
+ libsparse_host \
+ libz
+LOCAL_CFLAGS := -Werror
+include $(BUILD_HOST_EXECUTABLE)
+
+endif
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := simg_dump.py
+LOCAL_SRC_FILES := simg_dump.py
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_IS_HOST_MODULE := true
+LOCAL_CFLAGS := -Werror
+include $(BUILD_PREBUILT)
diff --git a/libsparse/append2simg.c b/libsparse/append2simg.c
new file mode 100644
index 0000000..1cf827c
--- /dev/null
+++ b/libsparse/append2simg.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _FILE_OFFSET_BITS 64
+#define _LARGEFILE64_SOURCE 1
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sparse/sparse.h>
+#include "sparse_file.h"
+#include "backed_block.h"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#if defined(__APPLE__) && defined(__MACH__)
+#define lseek64 lseek
+#endif
+#if defined(__APPLE__) && defined(__MACH__)
+#define lseek64 lseek
+#define off64_t off_t
+#endif
+
+void usage()
+{
+ fprintf(stderr, "Usage: append2simg <output> <input>\n");
+}
+
+int main(int argc, char *argv[])
+{
+ int output;
+ int output_block;
+ char *output_path;
+ struct sparse_file *sparse_output;
+
+ int input;
+ char *input_path;
+ off64_t input_len;
+
+ int tmp_fd;
+ char *tmp_path;
+
+ int ret;
+
+ if (argc == 3) {
+ output_path = argv[1];
+ input_path = argv[2];
+ } else {
+ usage();
+ exit(-1);
+ }
+
+ ret = asprintf(&tmp_path, "%s.append2simg", output_path);
+ if (ret < 0) {
+ fprintf(stderr, "Couldn't allocate filename\n");
+ exit(-1);
+ }
+
+ output = open(output_path, O_RDWR | O_BINARY);
+ if (output < 0) {
+ fprintf(stderr, "Couldn't open output file (%s)\n", strerror(errno));
+ exit(-1);
+ }
+
+ sparse_output = sparse_file_import_auto(output, true, true);
+ if (!sparse_output) {
+ fprintf(stderr, "Couldn't import output file\n");
+ exit(-1);
+ }
+
+ input = open(input_path, O_RDONLY | O_BINARY);
+ if (input < 0) {
+ fprintf(stderr, "Couldn't open input file (%s)\n", strerror(errno));
+ exit(-1);
+ }
+
+ input_len = lseek64(input, 0, SEEK_END);
+ if (input_len < 0) {
+ fprintf(stderr, "Couldn't get input file length (%s)\n", strerror(errno));
+ exit(-1);
+ } else if (input_len % sparse_output->block_size) {
+ fprintf(stderr, "Input file is not a multiple of the output file's block size");
+ exit(-1);
+ }
+ lseek64(input, 0, SEEK_SET);
+
+ output_block = sparse_output->len / sparse_output->block_size;
+ if (sparse_file_add_fd(sparse_output, input, 0, input_len, output_block) < 0) {
+ fprintf(stderr, "Couldn't add input file\n");
+ exit(-1);
+ }
+ sparse_output->len += input_len;
+
+ tmp_fd = open(tmp_path, O_WRONLY | O_CREAT | O_BINARY, 0664);
+ if (tmp_fd < 0) {
+ fprintf(stderr, "Couldn't open temporary file (%s)\n", strerror(errno));
+ exit(-1);
+ }
+
+ lseek64(output, 0, SEEK_SET);
+ if (sparse_file_write(sparse_output, tmp_fd, false, true, false) < 0) {
+ fprintf(stderr, "Failed to write sparse file\n");
+ exit(-1);
+ }
+
+ sparse_file_destroy(sparse_output);
+ close(tmp_fd);
+ close(output);
+ close(input);
+
+ ret = rename(tmp_path, output_path);
+ if (ret < 0) {
+ fprintf(stderr, "Failed to rename temporary file (%s)\n", strerror(errno));
+ exit(-1);
+ }
+
+ free(tmp_path);
+
+ exit(0);
+}
diff --git a/libsparse/backed_block.c b/libsparse/backed_block.c
new file mode 100644
index 0000000..3e72b57
--- /dev/null
+++ b/libsparse/backed_block.c
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "backed_block.h"
+#include "sparse_defs.h"
+
+struct backed_block {
+ unsigned int block;
+ unsigned int len;
+ enum backed_block_type type;
+ union {
+ struct {
+ void *data;
+ } data;
+ struct {
+ char *filename;
+ int64_t offset;
+ } file;
+ struct {
+ int fd;
+ int64_t offset;
+ } fd;
+ struct {
+ uint32_t val;
+ } fill;
+ };
+ struct backed_block *next;
+};
+
+struct backed_block_list {
+ struct backed_block *data_blocks;
+ struct backed_block *last_used;
+ unsigned int block_size;
+};
+
+struct backed_block *backed_block_iter_new(struct backed_block_list *bbl)
+{
+ return bbl->data_blocks;
+}
+
+struct backed_block *backed_block_iter_next(struct backed_block *bb)
+{
+ return bb->next;
+}
+
+unsigned int backed_block_len(struct backed_block *bb)
+{
+ return bb->len;
+}
+
+unsigned int backed_block_block(struct backed_block *bb)
+{
+ return bb->block;
+}
+
+void *backed_block_data(struct backed_block *bb)
+{
+ assert(bb->type == BACKED_BLOCK_DATA);
+ return bb->data.data;
+}
+
+const char *backed_block_filename(struct backed_block *bb)
+{
+ assert(bb->type == BACKED_BLOCK_FILE);
+ return bb->file.filename;
+}
+
+int backed_block_fd(struct backed_block *bb)
+{
+ assert(bb->type == BACKED_BLOCK_FD);
+ return bb->fd.fd;
+}
+
+int64_t backed_block_file_offset(struct backed_block *bb)
+{
+ assert(bb->type == BACKED_BLOCK_FILE || bb->type == BACKED_BLOCK_FD);
+ if (bb->type == BACKED_BLOCK_FILE) {
+ return bb->file.offset;
+ } else { /* bb->type == BACKED_BLOCK_FD */
+ return bb->fd.offset;
+ }
+}
+
+uint32_t backed_block_fill_val(struct backed_block *bb)
+{
+ assert(bb->type == BACKED_BLOCK_FILL);
+ return bb->fill.val;
+}
+
+enum backed_block_type backed_block_type(struct backed_block *bb)
+{
+ return bb->type;
+}
+
+void backed_block_destroy(struct backed_block *bb)
+{
+ if (bb->type == BACKED_BLOCK_FILE) {
+ free(bb->file.filename);
+ }
+
+ free(bb);
+}
+
+struct backed_block_list *backed_block_list_new(unsigned int block_size)
+{
+ struct backed_block_list *b = calloc(sizeof(struct backed_block_list), 1);
+ b->block_size = block_size;
+ return b;
+}
+
+void backed_block_list_destroy(struct backed_block_list *bbl)
+{
+ if (bbl->data_blocks) {
+ struct backed_block *bb = bbl->data_blocks;
+ while (bb) {
+ struct backed_block *next = bb->next;
+ backed_block_destroy(bb);
+ bb = next;
+ }
+ }
+
+ free(bbl);
+}
+
+void backed_block_list_move(struct backed_block_list *from,
+ struct backed_block_list *to, struct backed_block *start,
+ struct backed_block *end)
+{
+ struct backed_block *bb;
+
+ if (start == NULL) {
+ start = from->data_blocks;
+ }
+
+ if (!end) {
+ for (end = start; end && end->next; end = end->next)
+ ;
+ }
+
+ if (start == NULL || end == NULL) {
+ return;
+ }
+
+ from->last_used = NULL;
+ to->last_used = NULL;
+ if (from->data_blocks == start) {
+ from->data_blocks = end->next;
+ } else {
+ for (bb = from->data_blocks; bb; bb = bb->next) {
+ if (bb->next == start) {
+ bb->next = end->next;
+ break;
+ }
+ }
+ }
+
+ if (!to->data_blocks) {
+ to->data_blocks = start;
+ end->next = NULL;
+ } else {
+ for (bb = to->data_blocks; bb; bb = bb->next) {
+ if (!bb->next || bb->next->block > start->block) {
+ end->next = bb->next;
+ bb->next = start;
+ break;
+ }
+ }
+ }
+}
+
+/* may free b */
+static int merge_bb(struct backed_block_list *bbl,
+ struct backed_block *a, struct backed_block *b)
+{
+ unsigned int block_len;
+
+ /* Block doesn't exist (possible if one block is the last block) */
+ if (!a || !b) {
+ return -EINVAL;
+ }
+
+ assert(a->block < b->block);
+
+ /* Blocks are of different types */
+ if (a->type != b->type) {
+ return -EINVAL;
+ }
+
+ /* Blocks are not adjacent */
+ block_len = a->len / bbl->block_size; /* rounds down */
+ if (a->block + block_len != b->block) {
+ return -EINVAL;
+ }
+
+ switch (a->type) {
+ case BACKED_BLOCK_DATA:
+ /* Don't support merging data for now */
+ return -EINVAL;
+ case BACKED_BLOCK_FILL:
+ if (a->fill.val != b->fill.val) {
+ return -EINVAL;
+ }
+ break;
+ case BACKED_BLOCK_FILE:
+ if (a->file.filename != b->file.filename ||
+ a->file.offset + a->len != b->file.offset) {
+ return -EINVAL;
+ }
+ break;
+ case BACKED_BLOCK_FD:
+ if (a->fd.fd != b->fd.fd ||
+ a->fd.offset + a->len != b->fd.offset) {
+ return -EINVAL;
+ }
+ break;
+ }
+
+ /* Blocks are compatible and adjacent, with a before b. Merge b into a,
+ * and free b */
+ a->len += b->len;
+ a->next = b->next;
+
+ backed_block_destroy(b);
+
+ return 0;
+}
+
+static int queue_bb(struct backed_block_list *bbl, struct backed_block *new_bb)
+{
+ struct backed_block *bb;
+
+ if (bbl->data_blocks == NULL) {
+ bbl->data_blocks = new_bb;
+ return 0;
+ }
+
+ if (bbl->data_blocks->block > new_bb->block) {
+ new_bb->next = bbl->data_blocks;
+ bbl->data_blocks = new_bb;
+ return 0;
+ }
+
+ /* Optimization: blocks are mostly queued in sequence, so save the
+ pointer to the last bb that was added, and start searching from
+ there if the next block number is higher */
+ if (bbl->last_used && new_bb->block > bbl->last_used->block)
+ bb = bbl->last_used;
+ else
+ bb = bbl->data_blocks;
+ bbl->last_used = new_bb;
+
+ for (; bb->next && bb->next->block < new_bb->block; bb = bb->next)
+ ;
+
+ if (bb->next == NULL) {
+ bb->next = new_bb;
+ } else {
+ new_bb->next = bb->next;
+ bb->next = new_bb;
+ }
+
+ merge_bb(bbl, new_bb, new_bb->next);
+ merge_bb(bbl, bb, new_bb);
+
+ return 0;
+}
+
+/* Queues a fill block of memory to be written to the specified data blocks */
+int backed_block_add_fill(struct backed_block_list *bbl, unsigned int fill_val,
+ unsigned int len, unsigned int block)
+{
+ struct backed_block *bb = calloc(1, sizeof(struct backed_block));
+ if (bb == NULL) {
+ return -ENOMEM;
+ }
+
+ bb->block = block;
+ bb->len = len;
+ bb->type = BACKED_BLOCK_FILL;
+ bb->fill.val = fill_val;
+ bb->next = NULL;
+
+ return queue_bb(bbl, bb);
+}
+
+/* Queues a block of memory to be written to the specified data blocks */
+int backed_block_add_data(struct backed_block_list *bbl, void *data,
+ unsigned int len, unsigned int block)
+{
+ struct backed_block *bb = calloc(1, sizeof(struct backed_block));
+ if (bb == NULL) {
+ return -ENOMEM;
+ }
+
+ bb->block = block;
+ bb->len = len;
+ bb->type = BACKED_BLOCK_DATA;
+ bb->data.data = data;
+ bb->next = NULL;
+
+ return queue_bb(bbl, bb);
+}
+
+/* Queues a chunk of a file on disk to be written to the specified data blocks */
+int backed_block_add_file(struct backed_block_list *bbl, const char *filename,
+ int64_t offset, unsigned int len, unsigned int block)
+{
+ struct backed_block *bb = calloc(1, sizeof(struct backed_block));
+ if (bb == NULL) {
+ return -ENOMEM;
+ }
+
+ bb->block = block;
+ bb->len = len;
+ bb->type = BACKED_BLOCK_FILE;
+ bb->file.filename = strdup(filename);
+ bb->file.offset = offset;
+ bb->next = NULL;
+
+ return queue_bb(bbl, bb);
+}
+
+/* Queues a chunk of a fd to be written to the specified data blocks */
+int backed_block_add_fd(struct backed_block_list *bbl, int fd, int64_t offset,
+ unsigned int len, unsigned int block)
+{
+ struct backed_block *bb = calloc(1, sizeof(struct backed_block));
+ if (bb == NULL) {
+ return -ENOMEM;
+ }
+
+ bb->block = block;
+ bb->len = len;
+ bb->type = BACKED_BLOCK_FD;
+ bb->fd.fd = fd;
+ bb->fd.offset = offset;
+ bb->next = NULL;
+
+ return queue_bb(bbl, bb);
+}
+
+int backed_block_split(struct backed_block_list *bbl, struct backed_block *bb,
+ unsigned int max_len)
+{
+ struct backed_block *new_bb;
+
+ max_len = ALIGN_DOWN(max_len, bbl->block_size);
+
+ if (bb->len <= max_len) {
+ return 0;
+ }
+
+ new_bb = malloc(sizeof(struct backed_block));
+ if (new_bb == NULL) {
+ return -ENOMEM;
+ }
+
+ *new_bb = *bb;
+
+ new_bb->len = bb->len - max_len;
+ new_bb->block = bb->block + max_len / bbl->block_size;
+ new_bb->next = bb->next;
+ bb->next = new_bb;
+ bb->len = max_len;
+
+ switch (bb->type) {
+ case BACKED_BLOCK_DATA:
+ new_bb->data.data = (char *)bb->data.data + max_len;
+ break;
+ case BACKED_BLOCK_FILE:
+ new_bb->file.offset += max_len;
+ break;
+ case BACKED_BLOCK_FD:
+ new_bb->fd.offset += max_len;
+ break;
+ case BACKED_BLOCK_FILL:
+ break;
+ }
+
+ return 0;
+}
diff --git a/libsparse/backed_block.h b/libsparse/backed_block.h
new file mode 100644
index 0000000..1a159be
--- /dev/null
+++ b/libsparse/backed_block.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _BACKED_BLOCK_H_
+#define _BACKED_BLOCK_H_
+
+#include <stdint.h>
+
+struct backed_block_list;
+struct backed_block;
+
+enum backed_block_type {
+ BACKED_BLOCK_DATA,
+ BACKED_BLOCK_FILE,
+ BACKED_BLOCK_FD,
+ BACKED_BLOCK_FILL,
+};
+
+int backed_block_add_data(struct backed_block_list *bbl, void *data,
+ unsigned int len, unsigned int block);
+int backed_block_add_fill(struct backed_block_list *bbl, unsigned int fill_val,
+ unsigned int len, unsigned int block);
+int backed_block_add_file(struct backed_block_list *bbl, const char *filename,
+ int64_t offset, unsigned int len, unsigned int block);
+int backed_block_add_fd(struct backed_block_list *bbl, int fd,
+ int64_t offset, unsigned int len, unsigned int block);
+
+struct backed_block *backed_block_iter_new(struct backed_block_list *bbl);
+struct backed_block *backed_block_iter_next(struct backed_block *bb);
+unsigned int backed_block_len(struct backed_block *bb);
+unsigned int backed_block_block(struct backed_block *bb);
+void *backed_block_data(struct backed_block *bb);
+const char *backed_block_filename(struct backed_block *bb);
+int backed_block_fd(struct backed_block *bb);
+int64_t backed_block_file_offset(struct backed_block *bb);
+uint32_t backed_block_fill_val(struct backed_block *bb);
+enum backed_block_type backed_block_type(struct backed_block *bb);
+int backed_block_split(struct backed_block_list *bbl, struct backed_block *bb,
+ unsigned int max_len);
+
+struct backed_block *backed_block_iter_new(struct backed_block_list *bbl);
+struct backed_block *backed_block_iter_next(struct backed_block *bb);
+
+struct backed_block_list *backed_block_list_new(unsigned int block_size);
+void backed_block_list_destroy(struct backed_block_list *bbl);
+
+void backed_block_list_move(struct backed_block_list *from,
+ struct backed_block_list *to, struct backed_block *start,
+ struct backed_block *end);
+
+#endif
diff --git a/libsparse/defs.h b/libsparse/defs.h
new file mode 100644
index 0000000..34e63c5
--- /dev/null
+++ b/libsparse/defs.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBSPARSE_DEFS_H_
+
+#ifndef __unused
+#define __unused __attribute__((__unused__))
+#endif
+
+#endif /* _LIBSPARSE_DEFS_H_ */
diff --git a/libsparse/img2simg.c b/libsparse/img2simg.c
new file mode 100644
index 0000000..a0db36f
--- /dev/null
+++ b/libsparse/img2simg.c
@@ -0,0 +1,115 @@
+/*
+ * 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 _FILE_OFFSET_BITS 64
+#define _LARGEFILE64_SOURCE 1
+
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <sparse/sparse.h>
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#if defined(__APPLE__) && defined(__MACH__)
+#define lseek64 lseek
+#define off64_t off_t
+#endif
+
+void usage()
+{
+ fprintf(stderr, "Usage: img2simg <raw_image_file> <sparse_image_file> [<block_size>]\n");
+}
+
+int main(int argc, char *argv[])
+{
+ int in;
+ int out;
+ int ret;
+ struct sparse_file *s;
+ unsigned int block_size = 4096;
+ off64_t len;
+
+ if (argc < 3 || argc > 4) {
+ usage();
+ exit(-1);
+ }
+
+ if (argc == 4) {
+ block_size = atoi(argv[3]);
+ }
+
+ if (block_size < 1024 || block_size % 4 != 0) {
+ usage();
+ exit(-1);
+ }
+
+ if (strcmp(argv[1], "-") == 0) {
+ in = STDIN_FILENO;
+ } else {
+ in = open(argv[1], O_RDONLY | O_BINARY);
+ if (in < 0) {
+ fprintf(stderr, "Cannot open input file %s\n", argv[1]);
+ exit(-1);
+ }
+ }
+
+ if (strcmp(argv[2], "-") == 0) {
+ out = STDOUT_FILENO;
+ } else {
+ out = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
+ if (out < 0) {
+ fprintf(stderr, "Cannot open output file %s\n", argv[2]);
+ exit(-1);
+ }
+ }
+
+ len = lseek64(in, 0, SEEK_END);
+ lseek64(in, 0, SEEK_SET);
+
+ s = sparse_file_new(block_size, len);
+ if (!s) {
+ fprintf(stderr, "Failed to create sparse file\n");
+ exit(-1);
+ }
+
+ sparse_file_verbose(s);
+ ret = sparse_file_read(s, in, false, false);
+ if (ret) {
+ fprintf(stderr, "Failed to read file\n");
+ exit(-1);
+ }
+
+ ret = sparse_file_write(s, out, false, true, false);
+ if (ret) {
+ fprintf(stderr, "Failed to write sparse file\n");
+ exit(-1);
+ }
+
+ close(in);
+ close(out);
+
+ exit(0);
+}
diff --git a/libsparse/include/sparse/sparse.h b/libsparse/include/sparse/sparse.h
new file mode 100644
index 0000000..42d4adb
--- /dev/null
+++ b/libsparse/include/sparse/sparse.h
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBSPARSE_SPARSE_H_
+#define _LIBSPARSE_SPARSE_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct sparse_file;
+
+/**
+ * sparse_file_new - create a new sparse file cookie
+ *
+ * @block_size - minimum size of a chunk
+ * @len - size of the expanded sparse file.
+ *
+ * Creates a new sparse_file cookie that can be used to associate data
+ * blocks. Can later be written to a file with a variety of options.
+ * block_size specifies the minimum size of a chunk in the file. The maximum
+ * size of the file is 2**32 * block_size (16TB for 4k block size).
+ *
+ * Returns the sparse file cookie, or NULL on error.
+ */
+struct sparse_file *sparse_file_new(unsigned int block_size, int64_t len);
+
+/**
+ * sparse_file_destroy - destroy a sparse file cookie
+ *
+ * @s - sparse file cookie
+ *
+ * Destroys a sparse file cookie. After destroy, all memory passed in to
+ * sparse_file_add_data can be freed by the caller
+ */
+void sparse_file_destroy(struct sparse_file *s);
+
+/**
+ * sparse_file_add_data - associate a data chunk with a sparse file
+ *
+ * @s - sparse file cookie
+ * @data - pointer to data block
+ * @len - length of the data block
+ * @block - offset in blocks into the sparse file to place the data chunk
+ *
+ * Associates a data chunk with a sparse file cookie. The region
+ * [block * block_size : block * block_size + len) must not already be used in
+ * the sparse file. If len is not a multiple of the block size the data
+ * will be padded with zeros.
+ *
+ * The data pointer must remain valid until the sparse file is closed or the
+ * data block is removed from the sparse file.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+int sparse_file_add_data(struct sparse_file *s,
+ void *data, unsigned int len, unsigned int block);
+
+/**
+ * sparse_file_add_fill - associate a fill chunk with a sparse file
+ *
+ * @s - sparse file cookie
+ * @fill_val - 32 bit fill data
+ * @len - length of the fill block
+ * @block - offset in blocks into the sparse file to place the fill chunk
+ *
+ * Associates a chunk filled with fill_val with a sparse file cookie.
+ * The region [block * block_size : block * block_size + len) must not already
+ * be used in the sparse file. If len is not a multiple of the block size the
+ * data will be padded with zeros.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+int sparse_file_add_fill(struct sparse_file *s,
+ uint32_t fill_val, unsigned int len, unsigned int block);
+
+/**
+ * sparse_file_add_file - associate a chunk of a file with a sparse file
+ *
+ * @s - sparse file cookie
+ * @filename - filename of the file to be copied
+ * @file_offset - offset into the copied file
+ * @len - length of the copied block
+ * @block - offset in blocks into the sparse file to place the file chunk
+ *
+ * Associates a chunk of an existing file with a sparse file cookie.
+ * The region [block * block_size : block * block_size + len) must not already
+ * be used in the sparse file. If len is not a multiple of the block size the
+ * data will be padded with zeros.
+ *
+ * Allows adding large amounts of data to a sparse file without needing to keep
+ * it all mapped. File size is limited by available virtual address space,
+ * exceptionally large files may need to be added in multiple chunks.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+int sparse_file_add_file(struct sparse_file *s,
+ const char *filename, int64_t file_offset, unsigned int len,
+ unsigned int block);
+
+/**
+ * sparse_file_add_file - associate a chunk of a file with a sparse file
+ *
+ * @s - sparse file cookie
+ * @filename - filename of the file to be copied
+ * @file_offset - offset into the copied file
+ * @len - length of the copied block
+ * @block - offset in blocks into the sparse file to place the file chunk
+ *
+ * Associates a chunk of an existing fd with a sparse file cookie.
+ * The region [block * block_size : block * block_size + len) must not already
+ * be used in the sparse file. If len is not a multiple of the block size the
+ * data will be padded with zeros.
+ *
+ * Allows adding large amounts of data to a sparse file without needing to keep
+ * it all mapped. File size is limited by available virtual address space,
+ * exceptionally large files may need to be added in multiple chunks.
+ *
+ * The fd must remain open until the sparse file is closed or the fd block is
+ * removed from the sparse file.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+int sparse_file_add_fd(struct sparse_file *s,
+ int fd, int64_t file_offset, unsigned int len, unsigned int block);
+
+/**
+ * sparse_file_write - write a sparse file to a file
+ *
+ * @s - sparse file cookie
+ * @fd - file descriptor to write to
+ * @gz - write a gzipped file
+ * @sparse - write in the Android sparse file format
+ * @crc - append a crc chunk
+ *
+ * Writes a sparse file to a file. If gz is true, the data will be passed
+ * through zlib. If sparse is true, the file will be written in the Android
+ * sparse file format. If sparse is false, the file will be written by seeking
+ * over unused chunks, producing a smaller file if the filesystem supports
+ * sparse files. If crc is true, the crc of the expanded data will be
+ * calculated and appended in a crc chunk.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+int sparse_file_write(struct sparse_file *s, int fd, bool gz, bool sparse,
+ bool crc);
+
+/**
+ * sparse_file_len - return the length of a sparse file if written to disk
+ *
+ * @s - sparse file cookie
+ * @sparse - write in the Android sparse file format
+ * @crc - append a crc chunk
+ *
+ * Returns the size a sparse file would be on disk if it were written in the
+ * specified format. If sparse is true, this is the size of the data in the
+ * sparse format. If sparse is false, this is the size of the normal
+ * non-sparse file.
+ */
+int64_t sparse_file_len(struct sparse_file *s, bool sparse, bool crc);
+
+/**
+ * sparse_file_callback - call a callback for blocks in sparse file
+ *
+ * @s - sparse file cookie
+ * @sparse - write in the Android sparse file format
+ * @crc - append a crc chunk
+ * @write - function to call for each block
+ * @priv - value that will be passed as the first argument to write
+ *
+ * Writes a sparse file by calling a callback function. If sparse is true, the
+ * file will be written in the Android sparse file format. If crc is true, the
+ * crc of the expanded data will be calculated and appended in a crc chunk.
+ * The callback 'write' will be called with data and length for each data,
+ * and with data==NULL to skip over a region (only used for non-sparse format).
+ * The callback should return negative on error, 0 on success.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+int sparse_file_callback(struct sparse_file *s, bool sparse, bool crc,
+ int (*write)(void *priv, const void *data, int len), void *priv);
+
+/**
+ * sparse_file_read - read a file into a sparse file cookie
+ *
+ * @s - sparse file cookie
+ * @fd - file descriptor to read from
+ * @sparse - read a file in the Android sparse file format
+ * @crc - verify the crc of a file in the Android sparse file format
+ *
+ * Reads a file into a sparse file cookie. If sparse is true, the file is
+ * assumed to be in the Android sparse file format. If sparse is false, the
+ * file will be sparsed by looking for block aligned chunks of all zeros or
+ * another 32 bit value. If crc is true, the crc of the sparse file will be
+ * verified.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+int sparse_file_read(struct sparse_file *s, int fd, bool sparse, bool crc);
+
+/**
+ * sparse_file_import - import an existing sparse file
+ *
+ * @s - sparse file cookie
+ * @verbose - print verbose errors while reading the sparse file
+ * @crc - verify the crc of a file in the Android sparse file format
+ *
+ * Reads an existing sparse file into a sparse file cookie, recreating the same
+ * sparse cookie that was used to write it. If verbose is true, prints verbose
+ * errors when the sparse file is formatted incorrectly.
+ *
+ * Returns a new sparse file cookie on success, NULL on error.
+ */
+struct sparse_file *sparse_file_import(int fd, bool verbose, bool crc);
+
+/**
+ * sparse_file_import_auto - import an existing sparse or normal file
+ *
+ * @fd - file descriptor to read from
+ * @crc - verify the crc of a file in the Android sparse file format
+ * @verbose - whether to use verbose logging
+ *
+ * Reads an existing sparse or normal file into a sparse file cookie.
+ * Attempts to determine if the file is sparse or not by looking for the sparse
+ * file magic number in the first 4 bytes. If the file is not sparse, the file
+ * will be sparsed by looking for block aligned chunks of all zeros or another
+ * 32 bit value. If crc is true, the crc of the sparse file will be verified.
+ *
+ * Returns a new sparse file cookie on success, NULL on error.
+ */
+struct sparse_file *sparse_file_import_auto(int fd, bool crc, bool verbose);
+
+/** sparse_file_resparse - rechunk an existing sparse file into smaller files
+ *
+ * @in_s - sparse file cookie of the existing sparse file
+ * @max_len - maximum file size
+ * @out_s - array of sparse file cookies
+ * @out_s_count - size of out_s array
+ *
+ * Splits chunks of an existing sparse file into smaller sparse files such that
+ * each sparse file is less than max_len. Returns the number of sparse_files
+ * that would have been written to out_s if out_s were big enough.
+ */
+int sparse_file_resparse(struct sparse_file *in_s, unsigned int max_len,
+ struct sparse_file **out_s, int out_s_count);
+
+/**
+ * sparse_file_verbose - set a sparse file cookie to print verbose errors
+ *
+ * @s - sparse file cookie
+ *
+ * Print verbose sparse file errors whenever using the sparse file cookie.
+ */
+void sparse_file_verbose(struct sparse_file *s);
+
+/**
+ * sparse_print_verbose - function called to print verbose errors
+ *
+ * By default, verbose errors will print to standard error.
+ * sparse_print_verbose may be overridden to log verbose errors somewhere else.
+ *
+ */
+extern void (*sparse_print_verbose)(const char *fmt, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libsparse/output_file.c b/libsparse/output_file.c
new file mode 100644
index 0000000..cd30800
--- /dev/null
+++ b/libsparse/output_file.c
@@ -0,0 +1,766 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _FILE_OFFSET_BITS 64
+#define _LARGEFILE64_SOURCE 1
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <zlib.h>
+
+#include "defs.h"
+#include "output_file.h"
+#include "sparse_crc32.h"
+#include "sparse_format.h"
+
+#ifndef USE_MINGW
+#include <sys/mman.h>
+#define O_BINARY 0
+#else
+#define ftruncate64 ftruncate
+#endif
+
+#if defined(__APPLE__) && defined(__MACH__)
+#define lseek64 lseek
+#define ftruncate64 ftruncate
+#define mmap64 mmap
+#define off64_t off_t
+#endif
+
+#define min(a, b) \
+ ({ typeof(a) _a = (a); typeof(b) _b = (b); (_a < _b) ? _a : _b; })
+
+#define SPARSE_HEADER_MAJOR_VER 1
+#define SPARSE_HEADER_MINOR_VER 0
+#define SPARSE_HEADER_LEN (sizeof(sparse_header_t))
+#define CHUNK_HEADER_LEN (sizeof(chunk_header_t))
+
+#define container_of(inner, outer_t, elem) \
+ ((outer_t *)((char *)inner - offsetof(outer_t, elem)))
+
+struct output_file_ops {
+ int (*open)(struct output_file *, int fd);
+ int (*skip)(struct output_file *, int64_t);
+ int (*pad)(struct output_file *, int64_t);
+ int (*write)(struct output_file *, void *, int);
+ void (*close)(struct output_file *);
+};
+
+struct sparse_file_ops {
+ int (*write_data_chunk)(struct output_file *out, unsigned int len,
+ void *data);
+ int (*write_fill_chunk)(struct output_file *out, unsigned int len,
+ uint32_t fill_val);
+ int (*write_skip_chunk)(struct output_file *out, int64_t len);
+ int (*write_end_chunk)(struct output_file *out);
+};
+
+struct output_file {
+ int64_t cur_out_ptr;
+ unsigned int chunk_cnt;
+ uint32_t crc32;
+ struct output_file_ops *ops;
+ struct sparse_file_ops *sparse_ops;
+ int use_crc;
+ unsigned int block_size;
+ int64_t len;
+ char *zero_buf;
+ uint32_t *fill_buf;
+ char *buf;
+};
+
+struct output_file_gz {
+ struct output_file out;
+ gzFile gz_fd;
+};
+
+#define to_output_file_gz(_o) \
+ container_of((_o), struct output_file_gz, out)
+
+struct output_file_normal {
+ struct output_file out;
+ int fd;
+};
+
+#define to_output_file_normal(_o) \
+ container_of((_o), struct output_file_normal, out)
+
+struct output_file_callback {
+ struct output_file out;
+ void *priv;
+ int (*write)(void *priv, const void *buf, int len);
+};
+
+#define to_output_file_callback(_o) \
+ container_of((_o), struct output_file_callback, out)
+
+static int file_open(struct output_file *out, int fd)
+{
+ struct output_file_normal *outn = to_output_file_normal(out);
+
+ outn->fd = fd;
+ return 0;
+}
+
+static int file_skip(struct output_file *out, int64_t cnt)
+{
+ off64_t ret;
+ struct output_file_normal *outn = to_output_file_normal(out);
+
+ ret = lseek64(outn->fd, cnt, SEEK_CUR);
+ if (ret < 0) {
+ error_errno("lseek64");
+ return -1;
+ }
+ return 0;
+}
+
+static int file_pad(struct output_file *out, int64_t len)
+{
+ int ret;
+ struct output_file_normal *outn = to_output_file_normal(out);
+
+ ret = ftruncate64(outn->fd, len);
+ if (ret < 0) {
+ return -errno;
+ }
+
+ return 0;
+}
+
+static int file_write(struct output_file *out, void *data, int len)
+{
+ int ret;
+ struct output_file_normal *outn = to_output_file_normal(out);
+
+ ret = write(outn->fd, data, len);
+ if (ret < 0) {
+ error_errno("write");
+ return -1;
+ } else if (ret < len) {
+ error("incomplete write");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void file_close(struct output_file *out)
+{
+ struct output_file_normal *outn = to_output_file_normal(out);
+
+ free(outn);
+}
+
+static struct output_file_ops file_ops = {
+ .open = file_open,
+ .skip = file_skip,
+ .pad = file_pad,
+ .write = file_write,
+ .close = file_close,
+};
+
+static int gz_file_open(struct output_file *out, int fd)
+{
+ struct output_file_gz *outgz = to_output_file_gz(out);
+
+ outgz->gz_fd = gzdopen(fd, "wb9");
+ if (!outgz->gz_fd) {
+ error_errno("gzopen");
+ return -errno;
+ }
+
+ return 0;
+}
+
+
+static int gz_file_skip(struct output_file *out, int64_t cnt)
+{
+ off64_t ret;
+ struct output_file_gz *outgz = to_output_file_gz(out);
+
+ ret = gzseek(outgz->gz_fd, cnt, SEEK_CUR);
+ if (ret < 0) {
+ error_errno("gzseek");
+ return -1;
+ }
+ return 0;
+}
+
+static int gz_file_pad(struct output_file *out, int64_t len)
+{
+ off64_t ret;
+ struct output_file_gz *outgz = to_output_file_gz(out);
+
+ ret = gztell(outgz->gz_fd);
+ if (ret < 0) {
+ return -1;
+ }
+
+ if (ret >= len) {
+ return 0;
+ }
+
+ ret = gzseek(outgz->gz_fd, len - 1, SEEK_SET);
+ if (ret < 0) {
+ return -1;
+ }
+
+ gzwrite(outgz->gz_fd, "", 1);
+
+ return 0;
+}
+
+static int gz_file_write(struct output_file *out, void *data, int len)
+{
+ int ret;
+ struct output_file_gz *outgz = to_output_file_gz(out);
+
+ ret = gzwrite(outgz->gz_fd, data, len);
+ if (ret < 0) {
+ error_errno("gzwrite");
+ return -1;
+ } else if (ret < len) {
+ error("incomplete gzwrite");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void gz_file_close(struct output_file *out)
+{
+ struct output_file_gz *outgz = to_output_file_gz(out);
+
+ gzclose(outgz->gz_fd);
+ free(outgz);
+}
+
+static struct output_file_ops gz_file_ops = {
+ .open = gz_file_open,
+ .skip = gz_file_skip,
+ .pad = gz_file_pad,
+ .write = gz_file_write,
+ .close = gz_file_close,
+};
+
+static int callback_file_open(struct output_file *out __unused, int fd __unused)
+{
+ return 0;
+}
+
+static int callback_file_skip(struct output_file *out, int64_t off)
+{
+ struct output_file_callback *outc = to_output_file_callback(out);
+ int to_write;
+ int ret;
+
+ while (off > 0) {
+ to_write = min(off, (int64_t)INT_MAX);
+ ret = outc->write(outc->priv, NULL, to_write);
+ if (ret < 0) {
+ return ret;
+ }
+ off -= to_write;
+ }
+
+ return 0;
+}
+
+static int callback_file_pad(struct output_file *out __unused, int64_t len __unused)
+{
+ return -1;
+}
+
+static int callback_file_write(struct output_file *out, void *data, int len)
+{
+ struct output_file_callback *outc = to_output_file_callback(out);
+
+ return outc->write(outc->priv, data, len);
+}
+
+static void callback_file_close(struct output_file *out)
+{
+ struct output_file_callback *outc = to_output_file_callback(out);
+
+ free(outc);
+}
+
+static struct output_file_ops callback_file_ops = {
+ .open = callback_file_open,
+ .skip = callback_file_skip,
+ .pad = callback_file_pad,
+ .write = callback_file_write,
+ .close = callback_file_close,
+};
+
+int read_all(int fd, void *buf, size_t len)
+{
+ size_t total = 0;
+ int ret;
+ char *ptr = buf;
+
+ while (total < len) {
+ ret = read(fd, ptr, len - total);
+
+ if (ret < 0)
+ return -errno;
+
+ if (ret == 0)
+ return -EINVAL;
+
+ ptr += ret;
+ total += ret;
+ }
+
+ return 0;
+}
+
+static int write_sparse_skip_chunk(struct output_file *out, int64_t skip_len)
+{
+ chunk_header_t chunk_header;
+ int ret;
+
+ if (skip_len % out->block_size) {
+ error("don't care size %"PRIi64" is not a multiple of the block size %u",
+ skip_len, out->block_size);
+ return -1;
+ }
+
+ /* We are skipping data, so emit a don't care chunk. */
+ chunk_header.chunk_type = CHUNK_TYPE_DONT_CARE;
+ chunk_header.reserved1 = 0;
+ chunk_header.chunk_sz = skip_len / out->block_size;
+ chunk_header.total_sz = CHUNK_HEADER_LEN;
+ ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
+ if (ret < 0)
+ return -1;
+
+ out->cur_out_ptr += skip_len;
+ out->chunk_cnt++;
+
+ return 0;
+}
+
+static int write_sparse_fill_chunk(struct output_file *out, unsigned int len,
+ uint32_t fill_val)
+{
+ chunk_header_t chunk_header;
+ int rnd_up_len, count;
+ int ret;
+
+ /* Round up the fill length to a multiple of the block size */
+ rnd_up_len = ALIGN(len, out->block_size);
+
+ /* Finally we can safely emit a chunk of data */
+ chunk_header.chunk_type = CHUNK_TYPE_FILL;
+ chunk_header.reserved1 = 0;
+ chunk_header.chunk_sz = rnd_up_len / out->block_size;
+ chunk_header.total_sz = CHUNK_HEADER_LEN + sizeof(fill_val);
+ ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
+
+ if (ret < 0)
+ return -1;
+ ret = out->ops->write(out, &fill_val, sizeof(fill_val));
+ if (ret < 0)
+ return -1;
+
+ if (out->use_crc) {
+ count = out->block_size / sizeof(uint32_t);
+ while (count--)
+ out->crc32 = sparse_crc32(out->crc32, &fill_val, sizeof(uint32_t));
+ }
+
+ out->cur_out_ptr += rnd_up_len;
+ out->chunk_cnt++;
+
+ return 0;
+}
+
+static int write_sparse_data_chunk(struct output_file *out, unsigned int len,
+ void *data)
+{
+ chunk_header_t chunk_header;
+ int rnd_up_len, zero_len;
+ int ret;
+
+ /* Round up the data length to a multiple of the block size */
+ rnd_up_len = ALIGN(len, out->block_size);
+ zero_len = rnd_up_len - len;
+
+ /* Finally we can safely emit a chunk of data */
+ chunk_header.chunk_type = CHUNK_TYPE_RAW;
+ chunk_header.reserved1 = 0;
+ chunk_header.chunk_sz = rnd_up_len / out->block_size;
+ chunk_header.total_sz = CHUNK_HEADER_LEN + rnd_up_len;
+ ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
+
+ if (ret < 0)
+ return -1;
+ ret = out->ops->write(out, data, len);
+ if (ret < 0)
+ return -1;
+ if (zero_len) {
+ ret = out->ops->write(out, out->zero_buf, zero_len);
+ if (ret < 0)
+ return -1;
+ }
+
+ if (out->use_crc) {
+ out->crc32 = sparse_crc32(out->crc32, data, len);
+ if (zero_len)
+ out->crc32 = sparse_crc32(out->crc32, out->zero_buf, zero_len);
+ }
+
+ out->cur_out_ptr += rnd_up_len;
+ out->chunk_cnt++;
+
+ return 0;
+}
+
+int write_sparse_end_chunk(struct output_file *out)
+{
+ chunk_header_t chunk_header;
+ int ret;
+
+ if (out->use_crc) {
+ chunk_header.chunk_type = CHUNK_TYPE_CRC32;
+ chunk_header.reserved1 = 0;
+ chunk_header.chunk_sz = 0;
+ chunk_header.total_sz = CHUNK_HEADER_LEN + 4;
+
+ ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
+ if (ret < 0) {
+ return ret;
+ }
+ out->ops->write(out, &out->crc32, 4);
+ if (ret < 0) {
+ return ret;
+ }
+
+ out->chunk_cnt++;
+ }
+
+ return 0;
+}
+
+static struct sparse_file_ops sparse_file_ops = {
+ .write_data_chunk = write_sparse_data_chunk,
+ .write_fill_chunk = write_sparse_fill_chunk,
+ .write_skip_chunk = write_sparse_skip_chunk,
+ .write_end_chunk = write_sparse_end_chunk,
+};
+
+static int write_normal_data_chunk(struct output_file *out, unsigned int len,
+ void *data)
+{
+ int ret;
+ unsigned int rnd_up_len = ALIGN(len, out->block_size);
+
+ ret = out->ops->write(out, data, len);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (rnd_up_len > len) {
+ ret = out->ops->skip(out, rnd_up_len - len);
+ }
+
+ return ret;
+}
+
+static int write_normal_fill_chunk(struct output_file *out, unsigned int len,
+ uint32_t fill_val)
+{
+ int ret;
+ unsigned int i;
+ unsigned int write_len;
+
+ /* Initialize fill_buf with the fill_val */
+ for (i = 0; i < out->block_size / sizeof(uint32_t); i++) {
+ out->fill_buf[i] = fill_val;
+ }
+
+ while (len) {
+ write_len = min(len, out->block_size);
+ ret = out->ops->write(out, out->fill_buf, write_len);
+ if (ret < 0) {
+ return ret;
+ }
+
+ len -= write_len;
+ }
+
+ return 0;
+}
+
+static int write_normal_skip_chunk(struct output_file *out, int64_t len)
+{
+ return out->ops->skip(out, len);
+}
+
+int write_normal_end_chunk(struct output_file *out)
+{
+ return out->ops->pad(out, out->len);
+}
+
+static struct sparse_file_ops normal_file_ops = {
+ .write_data_chunk = write_normal_data_chunk,
+ .write_fill_chunk = write_normal_fill_chunk,
+ .write_skip_chunk = write_normal_skip_chunk,
+ .write_end_chunk = write_normal_end_chunk,
+};
+
+void output_file_close(struct output_file *out)
+{
+ out->sparse_ops->write_end_chunk(out);
+ out->ops->close(out);
+}
+
+static int output_file_init(struct output_file *out, int block_size,
+ int64_t len, bool sparse, int chunks, bool crc)
+{
+ int ret;
+
+ out->len = len;
+ out->block_size = block_size;
+ out->cur_out_ptr = 0ll;
+ out->chunk_cnt = 0;
+ out->crc32 = 0;
+ out->use_crc = crc;
+
+ out->zero_buf = calloc(block_size, 1);
+ if (!out->zero_buf) {
+ error_errno("malloc zero_buf");
+ return -ENOMEM;
+ }
+
+ out->fill_buf = calloc(block_size, 1);
+ if (!out->fill_buf) {
+ error_errno("malloc fill_buf");
+ ret = -ENOMEM;
+ goto err_fill_buf;
+ }
+
+ if (sparse) {
+ out->sparse_ops = &sparse_file_ops;
+ } else {
+ out->sparse_ops = &normal_file_ops;
+ }
+
+ if (sparse) {
+ sparse_header_t sparse_header = {
+ .magic = SPARSE_HEADER_MAGIC,
+ .major_version = SPARSE_HEADER_MAJOR_VER,
+ .minor_version = SPARSE_HEADER_MINOR_VER,
+ .file_hdr_sz = SPARSE_HEADER_LEN,
+ .chunk_hdr_sz = CHUNK_HEADER_LEN,
+ .blk_sz = out->block_size,
+ .total_blks = out->len / out->block_size,
+ .total_chunks = chunks,
+ .image_checksum = 0
+ };
+
+ if (out->use_crc) {
+ sparse_header.total_chunks++;
+ }
+
+ ret = out->ops->write(out, &sparse_header, sizeof(sparse_header));
+ if (ret < 0) {
+ goto err_write;
+ }
+ }
+
+ return 0;
+
+err_write:
+ free(out->fill_buf);
+err_fill_buf:
+ free(out->zero_buf);
+ return ret;
+}
+
+static struct output_file *output_file_new_gz(void)
+{
+ struct output_file_gz *outgz = calloc(1, sizeof(struct output_file_gz));
+ if (!outgz) {
+ error_errno("malloc struct outgz");
+ return NULL;
+ }
+
+ outgz->out.ops = &gz_file_ops;
+
+ return &outgz->out;
+}
+
+static struct output_file *output_file_new_normal(void)
+{
+ struct output_file_normal *outn = calloc(1, sizeof(struct output_file_normal));
+ if (!outn) {
+ error_errno("malloc struct outn");
+ return NULL;
+ }
+
+ outn->out.ops = &file_ops;
+
+ return &outn->out;
+}
+
+struct output_file *output_file_open_callback(int (*write)(void *, const void *, int),
+ void *priv, unsigned int block_size, int64_t len,
+ int gz __unused, int sparse, int chunks, int crc)
+{
+ int ret;
+ struct output_file_callback *outc;
+
+ outc = calloc(1, sizeof(struct output_file_callback));
+ if (!outc) {
+ error_errno("malloc struct outc");
+ return NULL;
+ }
+
+ outc->out.ops = &callback_file_ops;
+ outc->priv = priv;
+ outc->write = write;
+
+ ret = output_file_init(&outc->out, block_size, len, sparse, chunks, crc);
+ if (ret < 0) {
+ free(outc);
+ return NULL;
+ }
+
+ return &outc->out;
+}
+
+struct output_file *output_file_open_fd(int fd, unsigned int block_size, int64_t len,
+ int gz, int sparse, int chunks, int crc)
+{
+ int ret;
+ struct output_file *out;
+
+ if (gz) {
+ out = output_file_new_gz();
+ } else {
+ out = output_file_new_normal();
+ }
+ if (!out) {
+ return NULL;
+ }
+
+ out->ops->open(out, fd);
+
+ ret = output_file_init(out, block_size, len, sparse, chunks, crc);
+ if (ret < 0) {
+ free(out);
+ return NULL;
+ }
+
+ return out;
+}
+
+/* Write a contiguous region of data blocks from a memory buffer */
+int write_data_chunk(struct output_file *out, unsigned int len, void *data)
+{
+ return out->sparse_ops->write_data_chunk(out, len, data);
+}
+
+/* Write a contiguous region of data blocks with a fill value */
+int write_fill_chunk(struct output_file *out, unsigned int len,
+ uint32_t fill_val)
+{
+ return out->sparse_ops->write_fill_chunk(out, len, fill_val);
+}
+
+int write_fd_chunk(struct output_file *out, unsigned int len,
+ int fd, int64_t offset)
+{
+ int ret;
+ int64_t aligned_offset;
+ int aligned_diff;
+ int buffer_size;
+ char *ptr;
+
+ aligned_offset = offset & ~(4096 - 1);
+ aligned_diff = offset - aligned_offset;
+ buffer_size = len + aligned_diff;
+
+#ifndef USE_MINGW
+ char *data = mmap64(NULL, buffer_size, PROT_READ, MAP_SHARED, fd,
+ aligned_offset);
+ if (data == MAP_FAILED) {
+ return -errno;
+ }
+ ptr = data + aligned_diff;
+#else
+ off64_t pos;
+ char *data = malloc(len);
+ if (!data) {
+ return -errno;
+ }
+ pos = lseek64(fd, offset, SEEK_SET);
+ if (pos < 0) {
+ free(data);
+ return -errno;
+ }
+ ret = read_all(fd, data, len);
+ if (ret < 0) {
+ free(data);
+ return ret;
+ }
+ ptr = data;
+#endif
+
+ ret = out->sparse_ops->write_data_chunk(out, len, ptr);
+
+#ifndef USE_MINGW
+ munmap(data, buffer_size);
+#else
+ free(data);
+#endif
+
+ return ret;
+}
+
+/* Write a contiguous region of data blocks from a file */
+int write_file_chunk(struct output_file *out, unsigned int len,
+ const char *file, int64_t offset)
+{
+ int ret;
+
+ int file_fd = open(file, O_RDONLY | O_BINARY);
+ if (file_fd < 0) {
+ return -errno;
+ }
+
+ ret = write_fd_chunk(out, len, file_fd, offset);
+
+ close(file_fd);
+
+ return ret;
+}
+
+int write_skip_chunk(struct output_file *out, int64_t len)
+{
+ return out->sparse_ops->write_skip_chunk(out, len);
+}
diff --git a/libsparse/output_file.h b/libsparse/output_file.h
new file mode 100644
index 0000000..474c1fc
--- /dev/null
+++ b/libsparse/output_file.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _OUTPUT_FILE_H_
+#define _OUTPUT_FILE_H_
+
+#include <sparse/sparse.h>
+
+struct output_file;
+
+struct output_file *output_file_open_fd(int fd, unsigned int block_size, int64_t len,
+ int gz, int sparse, int chunks, int crc);
+struct output_file *output_file_open_callback(int (*write)(void *, const void *, int),
+ void *priv, unsigned int block_size, int64_t len, int gz, int sparse,
+ int chunks, int crc);
+int write_data_chunk(struct output_file *out, unsigned int len, void *data);
+int write_fill_chunk(struct output_file *out, unsigned int len,
+ uint32_t fill_val);
+int write_file_chunk(struct output_file *out, unsigned int len,
+ const char *file, int64_t offset);
+int write_fd_chunk(struct output_file *out, unsigned int len,
+ int fd, int64_t offset);
+int write_skip_chunk(struct output_file *out, int64_t len);
+void output_file_close(struct output_file *out);
+
+int read_all(int fd, void *buf, size_t len);
+
+#endif
diff --git a/libsparse/simg2img.c b/libsparse/simg2img.c
new file mode 100644
index 0000000..95e9b5b
--- /dev/null
+++ b/libsparse/simg2img.c
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sparse/sparse.h>
+
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+void usage()
+{
+ fprintf(stderr, "Usage: simg2img <sparse_image_files> <raw_image_file>\n");
+}
+
+int main(int argc, char *argv[])
+{
+ int in;
+ int out;
+ int i;
+ int ret;
+ struct sparse_file *s;
+
+ if (argc < 3) {
+ usage();
+ exit(-1);
+ }
+
+ out = open(argv[argc - 1], O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
+ if (out < 0) {
+ fprintf(stderr, "Cannot open output file %s\n", argv[argc - 1]);
+ exit(-1);
+ }
+
+ for (i = 1; i < argc - 1; i++) {
+ if (strcmp(argv[i], "-") == 0) {
+ in = STDIN_FILENO;
+ } else {
+ in = open(argv[i], O_RDONLY | O_BINARY);
+ if (in < 0) {
+ fprintf(stderr, "Cannot open input file %s\n", argv[i]);
+ exit(-1);
+ }
+ }
+
+ s = sparse_file_import(in, true, false);
+ if (!s) {
+ fprintf(stderr, "Failed to read sparse file\n");
+ exit(-1);
+ }
+
+ lseek(out, SEEK_SET, 0);
+
+ ret = sparse_file_write(s, out, false, false, false);
+ if (ret < 0) {
+ fprintf(stderr, "Cannot write output file\n");
+ exit(-1);
+ }
+ sparse_file_destroy(s);
+ close(in);
+ }
+
+ close(out);
+
+ exit(0);
+}
+
diff --git a/libsparse/simg2simg.c b/libsparse/simg2simg.c
new file mode 100644
index 0000000..5f9ccf6
--- /dev/null
+++ b/libsparse/simg2simg.c
@@ -0,0 +1,115 @@
+/*
+ * 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 _FILE_OFFSET_BITS 64
+#define _LARGEFILE64_SOURCE 1
+#define _GNU_SOURCE
+
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <sparse/sparse.h>
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+void usage()
+{
+ fprintf(stderr, "Usage: simg2simg <sparse image file> <sparse_image_file> <max_size>\n");
+}
+
+int main(int argc, char *argv[])
+{
+ int in;
+ int out;
+ int i;
+ int ret;
+ struct sparse_file *s;
+ int64_t max_size;
+ struct sparse_file **out_s;
+ int files;
+ char filename[4096];
+
+ if (argc != 4) {
+ usage();
+ exit(-1);
+ }
+
+ max_size = atoll(argv[3]);
+
+ in = open(argv[1], O_RDONLY | O_BINARY);
+ if (in < 0) {
+ fprintf(stderr, "Cannot open input file %s\n", argv[1]);
+ exit(-1);
+ }
+
+ s = sparse_file_import(in, true, false);
+ if (!s) {
+ fprintf(stderr, "Failed to import sparse file\n");
+ exit(-1);
+ }
+
+ files = sparse_file_resparse(s, max_size, NULL, 0);
+ if (files < 0) {
+ fprintf(stderr, "Failed to resparse\n");
+ exit(-1);
+ }
+
+ out_s = calloc(sizeof(struct sparse_file *), files);
+ if (!out_s) {
+ fprintf(stderr, "Failed to allocate sparse file array\n");
+ exit(-1);
+ }
+
+ files = sparse_file_resparse(s, max_size, out_s, files);
+ if (files < 0) {
+ fprintf(stderr, "Failed to resparse\n");
+ exit(-1);
+ }
+
+ for (i = 0; i < files; i++) {
+ ret = snprintf(filename, sizeof(filename), "%s.%d", argv[2], i);
+ if (ret >= (int)sizeof(filename)) {
+ fprintf(stderr, "Filename too long\n");
+ exit(-1);
+ }
+
+ out = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
+ if (out < 0) {
+ fprintf(stderr, "Cannot open output file %s\n", argv[2]);
+ exit(-1);
+ }
+
+ ret = sparse_file_write(out_s[i], out, false, true, false);
+ if (ret) {
+ fprintf(stderr, "Failed to write sparse file\n");
+ exit(-1);
+ }
+ close(out);
+ }
+
+ close(in);
+
+ exit(0);
+}
diff --git a/libsparse/simg_dump.py b/libsparse/simg_dump.py
new file mode 100755
index 0000000..c70d45f
--- /dev/null
+++ b/libsparse/simg_dump.py
@@ -0,0 +1,169 @@
+#! /usr/bin/env python
+
+# 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.
+
+from __future__ import print_function
+import getopt, posixpath, signal, struct, sys
+
+def usage(argv0):
+ print("""
+Usage: %s [-v] sparse_image_file ...
+ -v verbose output
+""" % ( argv0 ))
+ sys.exit(2)
+
+def main():
+
+ signal.signal(signal.SIGPIPE, signal.SIG_DFL)
+
+ me = posixpath.basename(sys.argv[0])
+
+ # Parse the command line
+ verbose = 0 # -v
+ try:
+ opts, args = getopt.getopt(sys.argv[1:],
+ "v",
+ ["verbose"])
+ except getopt.GetoptError, e:
+ print(e)
+ usage(me)
+ for o, a in opts:
+ if o in ("-v", "--verbose"):
+ verbose += 1
+ else:
+ print("Unrecognized option \"%s\"" % (o))
+ usage(me)
+
+ if len(args) == 0:
+ print("No sparse_image_file specified")
+ usage(me)
+
+ for path in args:
+ FH = open(path, 'rb')
+ header_bin = FH.read(28)
+ header = struct.unpack("<I4H4I", header_bin)
+
+ magic = header[0]
+ major_version = header[1]
+ minor_version = header[2]
+ file_hdr_sz = header[3]
+ chunk_hdr_sz = header[4]
+ blk_sz = header[5]
+ total_blks = header[6]
+ total_chunks = header[7]
+ image_checksum = header[8]
+
+ if magic != 0xED26FF3A:
+ print("%s: %s: Magic should be 0xED26FF3A but is 0x%08X"
+ % (me, path, magic))
+ continue
+ if major_version != 1 or minor_version != 0:
+ print("%s: %s: I only know about version 1.0, but this is version %u.%u"
+ % (me, path, major_version, minor_version))
+ continue
+ if file_hdr_sz != 28:
+ print("%s: %s: The file header size was expected to be 28, but is %u."
+ % (me, path, file_hdr_sz))
+ continue
+ if chunk_hdr_sz != 12:
+ print("%s: %s: The chunk header size was expected to be 12, but is %u."
+ % (me, path, chunk_hdr_sz))
+ continue
+
+ print("%s: Total of %u %u-byte output blocks in %u input chunks."
+ % (path, total_blks, blk_sz, total_chunks))
+
+ if image_checksum != 0:
+ print("checksum=0x%08X" % (image_checksum))
+
+ if not verbose:
+ continue
+ print(" input_bytes output_blocks")
+ print("chunk offset number offset number")
+ offset = 0
+ for i in xrange(1,total_chunks+1):
+ header_bin = FH.read(12)
+ header = struct.unpack("<2H2I", header_bin)
+ chunk_type = header[0]
+ reserved1 = header[1]
+ chunk_sz = header[2]
+ total_sz = header[3]
+ data_sz = total_sz - 12
+
+ print("%4u %10u %10u %7u %7u" % (i, FH.tell(), data_sz, offset, chunk_sz),
+ end=" ")
+
+ if chunk_type == 0xCAC1:
+ if data_sz != (chunk_sz * blk_sz):
+ print("Raw chunk input size (%u) does not match output size (%u)"
+ % (data_sz, chunk_sz * blk_sz))
+ break;
+ else:
+ print("Raw data", end="")
+ FH.read(data_sz)
+ elif chunk_type == 0xCAC2:
+ if data_sz != 4:
+ print("Fill chunk should have 4 bytes of fill, but this has %u"
+ % (data_sz), end="")
+ break;
+ else:
+ fill_bin = FH.read(4)
+ fill = struct.unpack("<I", fill_bin)
+ print("Fill with 0x%08X" % (fill))
+ elif chunk_type == 0xCAC3:
+ if data_sz != 0:
+ print("Don't care chunk input size is non-zero (%u)" % (data_sz))
+ break;
+ else:
+ print("Don't care", end="")
+ elif chunk_type == 0xCAC4:
+ if data_sz != 4:
+ print("CRC32 chunk should have 4 bytes of CRC, but this has %u"
+ % (data_sz), end="")
+ break;
+ else:
+ crc_bin = FH.read(4)
+ crc = struct.unpack("<I", crc_bin)
+ print("Unverified CRC32 0x%08X" % (crc))
+ else:
+ print("Unknown chunk type 0x%04X" % (chunk_type), end="")
+ break;
+
+ if verbose > 1:
+ header = struct.unpack("<12B", header_bin)
+ print(" (%02X%02X %02X%02X %02X%02X%02X%02X %02X%02X%02X%02X)"
+ % (header[0], header[1], header[2], header[3],
+ header[4], header[5], header[6], header[7],
+ header[8], header[9], header[10], header[11]))
+ else:
+ print()
+
+ offset += chunk_sz
+
+ print(" %10u %7u End" % (FH.tell(), offset))
+
+ if total_blks != offset:
+ print("The header said we should have %u output blocks, but we saw %u"
+ % (total_blks, offset))
+
+ junk_len = len(FH.read())
+ if junk_len:
+ print("There were %u bytes of extra data at the end of the file."
+ % (junk_len))
+
+ sys.exit(0)
+
+if __name__ == "__main__":
+ main()
diff --git a/libsparse/sparse.c b/libsparse/sparse.c
new file mode 100644
index 0000000..311678a
--- /dev/null
+++ b/libsparse/sparse.c
@@ -0,0 +1,339 @@
+/*
+ * 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 <assert.h>
+#include <stdlib.h>
+
+#include <sparse/sparse.h>
+
+#include "defs.h"
+#include "sparse_file.h"
+
+#include "output_file.h"
+#include "backed_block.h"
+#include "sparse_defs.h"
+#include "sparse_format.h"
+
+struct sparse_file *sparse_file_new(unsigned int block_size, int64_t len)
+{
+ struct sparse_file *s = calloc(sizeof(struct sparse_file), 1);
+ if (!s) {
+ return NULL;
+ }
+
+ s->backed_block_list = backed_block_list_new(block_size);
+ if (!s->backed_block_list) {
+ free(s);
+ return NULL;
+ }
+
+ s->block_size = block_size;
+ s->len = len;
+
+ return s;
+}
+
+void sparse_file_destroy(struct sparse_file *s)
+{
+ backed_block_list_destroy(s->backed_block_list);
+ free(s);
+}
+
+int sparse_file_add_data(struct sparse_file *s,
+ void *data, unsigned int len, unsigned int block)
+{
+ return backed_block_add_data(s->backed_block_list, data, len, block);
+}
+
+int sparse_file_add_fill(struct sparse_file *s,
+ uint32_t fill_val, unsigned int len, unsigned int block)
+{
+ return backed_block_add_fill(s->backed_block_list, fill_val, len, block);
+}
+
+int sparse_file_add_file(struct sparse_file *s,
+ const char *filename, int64_t file_offset, unsigned int len,
+ unsigned int block)
+{
+ return backed_block_add_file(s->backed_block_list, filename, file_offset,
+ len, block);
+}
+
+int sparse_file_add_fd(struct sparse_file *s,
+ int fd, int64_t file_offset, unsigned int len, unsigned int block)
+{
+ return backed_block_add_fd(s->backed_block_list, fd, file_offset,
+ len, block);
+}
+unsigned int sparse_count_chunks(struct sparse_file *s)
+{
+ struct backed_block *bb;
+ unsigned int last_block = 0;
+ unsigned int chunks = 0;
+
+ for (bb = backed_block_iter_new(s->backed_block_list); bb;
+ bb = backed_block_iter_next(bb)) {
+ if (backed_block_block(bb) > last_block) {
+ /* If there is a gap between chunks, add a skip chunk */
+ chunks++;
+ }
+ chunks++;
+ last_block = backed_block_block(bb) +
+ DIV_ROUND_UP(backed_block_len(bb), s->block_size);
+ }
+ if (last_block < DIV_ROUND_UP(s->len, s->block_size)) {
+ chunks++;
+ }
+
+ return chunks;
+}
+
+static int sparse_file_write_block(struct output_file *out,
+ struct backed_block *bb)
+{
+ int ret = -EINVAL;
+
+ switch (backed_block_type(bb)) {
+ case BACKED_BLOCK_DATA:
+ ret = write_data_chunk(out, backed_block_len(bb), backed_block_data(bb));
+ break;
+ case BACKED_BLOCK_FILE:
+ ret = write_file_chunk(out, backed_block_len(bb),
+ backed_block_filename(bb),
+ backed_block_file_offset(bb));
+ break;
+ case BACKED_BLOCK_FD:
+ ret = write_fd_chunk(out, backed_block_len(bb),
+ backed_block_fd(bb),
+ backed_block_file_offset(bb));
+ break;
+ case BACKED_BLOCK_FILL:
+ ret = write_fill_chunk(out, backed_block_len(bb),
+ backed_block_fill_val(bb));
+ break;
+ }
+
+ return ret;
+}
+
+static int write_all_blocks(struct sparse_file *s, struct output_file *out)
+{
+ struct backed_block *bb;
+ unsigned int last_block = 0;
+ int64_t pad;
+ int ret = 0;
+
+ for (bb = backed_block_iter_new(s->backed_block_list); bb;
+ bb = backed_block_iter_next(bb)) {
+ if (backed_block_block(bb) > last_block) {
+ unsigned int blocks = backed_block_block(bb) - last_block;
+ write_skip_chunk(out, (int64_t)blocks * s->block_size);
+ }
+ ret = sparse_file_write_block(out, bb);
+ if (ret)
+ return ret;
+ last_block = backed_block_block(bb) +
+ DIV_ROUND_UP(backed_block_len(bb), s->block_size);
+ }
+
+ pad = s->len - (int64_t)last_block * s->block_size;
+ assert(pad >= 0);
+ if (pad > 0) {
+ write_skip_chunk(out, pad);
+ }
+
+ return 0;
+}
+
+int sparse_file_write(struct sparse_file *s, int fd, bool gz, bool sparse,
+ bool crc)
+{
+ int ret;
+ int chunks;
+ struct output_file *out;
+
+ chunks = sparse_count_chunks(s);
+ out = output_file_open_fd(fd, s->block_size, s->len, gz, sparse, chunks, crc);
+
+ if (!out)
+ return -ENOMEM;
+
+ ret = write_all_blocks(s, out);
+
+ output_file_close(out);
+
+ return ret;
+}
+
+int sparse_file_callback(struct sparse_file *s, bool sparse, bool crc,
+ int (*write)(void *priv, const void *data, int len), void *priv)
+{
+ int ret;
+ int chunks;
+ struct output_file *out;
+
+ chunks = sparse_count_chunks(s);
+ out = output_file_open_callback(write, priv, s->block_size, s->len, false,
+ sparse, chunks, crc);
+
+ if (!out)
+ return -ENOMEM;
+
+ ret = write_all_blocks(s, out);
+
+ output_file_close(out);
+
+ return ret;
+}
+
+static int out_counter_write(void *priv, const void *data __unused, int len)
+{
+ int64_t *count = priv;
+ *count += len;
+ return 0;
+}
+
+int64_t sparse_file_len(struct sparse_file *s, bool sparse, bool crc)
+{
+ int ret;
+ int chunks = sparse_count_chunks(s);
+ int64_t count = 0;
+ struct output_file *out;
+
+ out = output_file_open_callback(out_counter_write, &count,
+ s->block_size, s->len, false, sparse, chunks, crc);
+ if (!out) {
+ return -1;
+ }
+
+ ret = write_all_blocks(s, out);
+
+ output_file_close(out);
+
+ if (ret < 0) {
+ return -1;
+ }
+
+ return count;
+}
+
+static struct backed_block *move_chunks_up_to_len(struct sparse_file *from,
+ struct sparse_file *to, unsigned int len)
+{
+ int64_t count = 0;
+ struct output_file *out_counter;
+ struct backed_block *last_bb = NULL;
+ struct backed_block *bb;
+ struct backed_block *start;
+ unsigned int last_block = 0;
+ int64_t file_len = 0;
+ int ret;
+
+ /*
+ * overhead is sparse file header, the potential end skip
+ * chunk and crc chunk.
+ */
+ int overhead = sizeof(sparse_header_t) + 2 * sizeof(chunk_header_t) +
+ sizeof(uint32_t);
+ len -= overhead;
+
+ start = backed_block_iter_new(from->backed_block_list);
+ out_counter = output_file_open_callback(out_counter_write, &count,
+ to->block_size, to->len, false, true, 0, false);
+ if (!out_counter) {
+ return NULL;
+ }
+
+ for (bb = start; bb; bb = backed_block_iter_next(bb)) {
+ count = 0;
+ if (backed_block_block(bb) > last_block)
+ count += sizeof(chunk_header_t);
+ last_block = backed_block_block(bb) +
+ DIV_ROUND_UP(backed_block_len(bb), to->block_size);
+
+ /* will call out_counter_write to update count */
+ ret = sparse_file_write_block(out_counter, bb);
+ if (ret) {
+ bb = NULL;
+ goto out;
+ }
+ if (file_len + count > len) {
+ /*
+ * If the remaining available size is more than 1/8th of the
+ * requested size, split the chunk. Results in sparse files that
+ * are at least 7/8ths of the requested size
+ */
+ file_len += sizeof(chunk_header_t);
+ if (!last_bb || (len - file_len > (len / 8))) {
+ backed_block_split(from->backed_block_list, bb, len - file_len);
+ last_bb = bb;
+ }
+ goto move;
+ }
+ file_len += count;
+ last_bb = bb;
+ }
+
+move:
+ backed_block_list_move(from->backed_block_list,
+ to->backed_block_list, start, last_bb);
+
+out:
+ output_file_close(out_counter);
+
+ return bb;
+}
+
+int sparse_file_resparse(struct sparse_file *in_s, unsigned int max_len,
+ struct sparse_file **out_s, int out_s_count)
+{
+ struct backed_block *bb;
+ struct sparse_file *s;
+ struct sparse_file *tmp;
+ int c = 0;
+
+ tmp = sparse_file_new(in_s->block_size, in_s->len);
+ if (!tmp) {
+ return -ENOMEM;
+ }
+
+ do {
+ s = sparse_file_new(in_s->block_size, in_s->len);
+
+ bb = move_chunks_up_to_len(in_s, s, max_len);
+
+ if (c < out_s_count) {
+ out_s[c] = s;
+ } else {
+ backed_block_list_move(s->backed_block_list, tmp->backed_block_list,
+ NULL, NULL);
+ sparse_file_destroy(s);
+ }
+ c++;
+ } while (bb);
+
+ backed_block_list_move(tmp->backed_block_list, in_s->backed_block_list,
+ NULL, NULL);
+
+ sparse_file_destroy(tmp);
+
+ return c;
+}
+
+void sparse_file_verbose(struct sparse_file *s)
+{
+ s->verbose = true;
+}
diff --git a/libsparse/sparse_crc32.c b/libsparse/sparse_crc32.c
new file mode 100644
index 0000000..38bfe4a
--- /dev/null
+++ b/libsparse/sparse_crc32.c
@@ -0,0 +1,111 @@
+/*-
+ * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or
+ * code or tables extracted from it, as desired without restriction.
+ */
+
+/*
+ * First, the polynomial itself and its table of feedback terms. The
+ * polynomial is
+ * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
+ *
+ * Note that we take it "backwards" and put the highest-order term in
+ * the lowest-order bit. The X^32 term is "implied"; the LSB is the
+ * X^31 term, etc. The X^0 term (usually shown as "+1") results in
+ * the MSB being 1
+ *
+ * Note that the usual hardware shift register implementation, which
+ * is what we're using (we're merely optimizing it by doing eight-bit
+ * chunks at a time) shifts bits into the lowest-order term. In our
+ * implementation, that means shifting towards the right. Why do we
+ * do it this way? Because the calculated CRC must be transmitted in
+ * order from highest-order term to lowest-order term. UARTs transmit
+ * characters in order from LSB to MSB. By storing the CRC this way
+ * we hand it to the UART in the order low-byte to high-byte; the UART
+ * sends each low-bit to hight-bit; and the result is transmission bit
+ * by bit from highest- to lowest-order term without requiring any bit
+ * shuffling on our part. Reception works similarly
+ *
+ * The feedback terms table consists of 256, 32-bit entries. Notes
+ *
+ * The table can be generated at runtime if desired; code to do so
+ * is shown later. It might not be obvious, but the feedback
+ * terms simply represent the results of eight shift/xor opera
+ * tions for all combinations of data and CRC register values
+ *
+ * The values must be right-shifted by eight bits by the "updcrc
+ * logic; the shift must be unsigned (bring in zeroes). On some
+ * hardware you could probably optimize the shift in assembler by
+ * using byte-swap instructions
+ * polynomial $edb88320
+ *
+ *
+ * CRC32 code derived from work by Gary S. Brown.
+ */
+
+/* Code taken from FreeBSD 8 */
+#include <stdint.h>
+
+static uint32_t crc32_tab[] = {
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
+ 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+ 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
+ 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+ 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+ 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
+ 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
+ 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
+ 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
+ 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+ 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+ 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
+ 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+ 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
+ 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
+ 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+ 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
+ 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+ 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+ 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
+ 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
+ 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
+ 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
+ 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+ 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+ 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
+ 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+ 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
+ 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
+ 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+ 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+};
+
+/*
+ * A function that calculates the CRC-32 based on the table above is
+ * given below for documentation purposes. An equivalent implementation
+ * of this function that's actually used in the kernel can be found
+ * in sys/libkern.h, where it can be inlined.
+ */
+
+uint32_t sparse_crc32(uint32_t crc_in, const void *buf, int size)
+{
+ const uint8_t *p = buf;
+ uint32_t crc;
+
+ crc = crc_in ^ ~0U;
+ while (size--)
+ crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
+ return crc ^ ~0U;
+}
+
diff --git a/libsparse/sparse_crc32.h b/libsparse/sparse_crc32.h
new file mode 100644
index 0000000..50cd9e9
--- /dev/null
+++ b/libsparse/sparse_crc32.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBSPARSE_SPARSE_CRC32_H_
+#define _LIBSPARSE_SPARSE_CRC32_H_
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+uint32_t sparse_crc32(uint32_t crc, const void *buf, size_t size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libsparse/sparse_defs.h b/libsparse/sparse_defs.h
new file mode 100644
index 0000000..b99cfd5
--- /dev/null
+++ b/libsparse/sparse_defs.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBSPARSE_SPARSE_DEFS_
+#define _LIBSPARSE_SPARSE_DEFS_
+
+#include <errno.h>
+#include <stdio.h>
+
+#define __le64 u64
+#define __le32 u32
+#define __le16 u16
+
+#define __be64 u64
+#define __be32 u32
+#define __be16 u16
+
+#define __u64 u64
+#define __u32 u32
+#define __u16 u16
+#define __u8 u8
+
+typedef unsigned long long u64;
+typedef signed long long s64;
+typedef unsigned int u32;
+typedef unsigned short int u16;
+typedef unsigned char u8;
+
+#define DIV_ROUND_UP(x, y) (((x) + (y) - 1)/(y))
+#define ALIGN(x, y) ((y) * DIV_ROUND_UP((x), (y)))
+#define ALIGN_DOWN(x, y) ((y) * ((x) / (y)))
+
+#define error(fmt, args...) do { fprintf(stderr, "error: %s: " fmt "\n", __func__, ## args); } while (0)
+#define error_errno(s, args...) error(s ": %s", ##args, strerror(errno))
+
+#endif
diff --git a/libsparse/sparse_err.c b/libsparse/sparse_err.c
new file mode 100644
index 0000000..0f392ad
--- /dev/null
+++ b/libsparse/sparse_err.c
@@ -0,0 +1,33 @@
+/*
+ * 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 <sparse/sparse.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <unistd.h>
+
+void sparse_default_print(const char *fmt, ...)
+{
+ va_list argp;
+
+ va_start(argp, fmt);
+ vfprintf(stderr, fmt, argp);
+ va_end(argp);
+}
+
+void (*sparse_print_error)(const char *fmt, ...) = sparse_default_print;
+void (*sparse_print_verbose)(const char *fmt, ...) = sparse_default_print;
diff --git a/libsparse/sparse_file.h b/libsparse/sparse_file.h
new file mode 100644
index 0000000..91a12e6
--- /dev/null
+++ b/libsparse/sparse_file.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBSPARSE_SPARSE_FILE_H_
+#define _LIBSPARSE_SPARSE_FILE_H_
+
+#include <sparse/sparse.h>
+
+struct sparse_file {
+ unsigned int block_size;
+ int64_t len;
+ bool verbose;
+
+ struct backed_block_list *backed_block_list;
+ struct output_file *out;
+};
+
+
+#endif /* _LIBSPARSE_SPARSE_FILE_H_ */
diff --git a/libsparse/sparse_format.h b/libsparse/sparse_format.h
new file mode 100644
index 0000000..c41f12a
--- /dev/null
+++ b/libsparse/sparse_format.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBSPARSE_SPARSE_FORMAT_H_
+#define _LIBSPARSE_SPARSE_FORMAT_H_
+#include "sparse_defs.h"
+
+typedef struct sparse_header {
+ __le32 magic; /* 0xed26ff3a */
+ __le16 major_version; /* (0x1) - reject images with higher major versions */
+ __le16 minor_version; /* (0x0) - allow images with higer minor versions */
+ __le16 file_hdr_sz; /* 28 bytes for first revision of the file format */
+ __le16 chunk_hdr_sz; /* 12 bytes for first revision of the file format */
+ __le32 blk_sz; /* block size in bytes, must be a multiple of 4 (4096) */
+ __le32 total_blks; /* total blocks in the non-sparse output image */
+ __le32 total_chunks; /* total chunks in the sparse input image */
+ __le32 image_checksum; /* CRC32 checksum of the original data, counting "don't care" */
+ /* as 0. Standard 802.3 polynomial, use a Public Domain */
+ /* table implementation */
+} sparse_header_t;
+
+#define SPARSE_HEADER_MAGIC 0xed26ff3a
+
+#define CHUNK_TYPE_RAW 0xCAC1
+#define CHUNK_TYPE_FILL 0xCAC2
+#define CHUNK_TYPE_DONT_CARE 0xCAC3
+#define CHUNK_TYPE_CRC32 0xCAC4
+
+typedef struct chunk_header {
+ __le16 chunk_type; /* 0xCAC1 -> raw; 0xCAC2 -> fill; 0xCAC3 -> don't care */
+ __le16 reserved1;
+ __le32 chunk_sz; /* in blocks in output image */
+ __le32 total_sz; /* in bytes of chunk input file including chunk header and data */
+} chunk_header_t;
+
+/* Following a Raw or Fill or CRC32 chunk is data.
+ * For a Raw chunk, it's the data in chunk_sz * blk_sz.
+ * For a Fill chunk, it's 4 bytes of the fill data.
+ * For a CRC32 chunk, it's 4 bytes of CRC32
+ */
+
+#endif
diff --git a/libsparse/sparse_read.c b/libsparse/sparse_read.c
new file mode 100644
index 0000000..9b10293
--- /dev/null
+++ b/libsparse/sparse_read.c
@@ -0,0 +1,505 @@
+/*
+ * 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 _GNU_SOURCE
+#define _FILE_OFFSET_BITS 64
+#define _LARGEFILE64_SOURCE 1
+
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sparse/sparse.h>
+
+#include "defs.h"
+#include "output_file.h"
+#include "sparse_crc32.h"
+#include "sparse_file.h"
+#include "sparse_format.h"
+
+#if defined(__APPLE__) && defined(__MACH__)
+#define lseek64 lseek
+#define off64_t off_t
+#endif
+
+#define SPARSE_HEADER_MAJOR_VER 1
+#define SPARSE_HEADER_LEN (sizeof(sparse_header_t))
+#define CHUNK_HEADER_LEN (sizeof(chunk_header_t))
+
+#define COPY_BUF_SIZE (1024U*1024U)
+static char *copybuf;
+
+#define min(a, b) \
+ ({ typeof(a) _a = (a); typeof(b) _b = (b); (_a < _b) ? _a : _b; })
+
+static void verbose_error(bool verbose, int err, const char *fmt, ...)
+{
+ char *s = "";
+ char *at = "";
+ if (fmt) {
+ va_list argp;
+ int size;
+
+ va_start(argp, fmt);
+ size = vsnprintf(NULL, 0, fmt, argp);
+ va_end(argp);
+
+ if (size < 0) {
+ return;
+ }
+
+ at = malloc(size + 1);
+ if (at == NULL) {
+ return;
+ }
+
+ va_start(argp, fmt);
+ vsnprintf(at, size, fmt, argp);
+ va_end(argp);
+ at[size] = 0;
+ s = " at ";
+ }
+ if (verbose) {
+#ifndef USE_MINGW
+ if (err == -EOVERFLOW) {
+ sparse_print_verbose("EOF while reading file%s%s\n", s, at);
+ } else
+#endif
+ if (err == -EINVAL) {
+ sparse_print_verbose("Invalid sparse file format%s%s\n", s, at);
+ } else if (err == -ENOMEM) {
+ sparse_print_verbose("Failed allocation while reading file%s%s\n",
+ s, at);
+ } else {
+ sparse_print_verbose("Unknown error %d%s%s\n", err, s, at);
+ }
+ }
+ if (fmt) {
+ free(at);
+ }
+}
+
+static int process_raw_chunk(struct sparse_file *s, unsigned int chunk_size,
+ int fd, int64_t offset, unsigned int blocks, unsigned int block,
+ uint32_t *crc32)
+{
+ int ret;
+ int chunk;
+ unsigned int len = blocks * s->block_size;
+
+ if (chunk_size % s->block_size != 0) {
+ return -EINVAL;
+ }
+
+ if (chunk_size / s->block_size != blocks) {
+ return -EINVAL;
+ }
+
+ ret = sparse_file_add_fd(s, fd, offset, len, block);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (crc32) {
+ while (len) {
+ chunk = min(len, COPY_BUF_SIZE);
+ ret = read_all(fd, copybuf, chunk);
+ if (ret < 0) {
+ return ret;
+ }
+ *crc32 = sparse_crc32(*crc32, copybuf, chunk);
+ len -= chunk;
+ }
+ } else {
+ lseek64(fd, len, SEEK_CUR);
+ }
+
+ return 0;
+}
+
+static int process_fill_chunk(struct sparse_file *s, unsigned int chunk_size,
+ int fd, unsigned int blocks, unsigned int block, uint32_t *crc32)
+{
+ int ret;
+ int chunk;
+ int64_t len = (int64_t)blocks * s->block_size;
+ uint32_t fill_val;
+ uint32_t *fillbuf;
+ unsigned int i;
+
+ if (chunk_size != sizeof(fill_val)) {
+ return -EINVAL;
+ }
+
+ ret = read_all(fd, &fill_val, sizeof(fill_val));
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = sparse_file_add_fill(s, fill_val, len, block);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (crc32) {
+ /* Fill copy_buf with the fill value */
+ fillbuf = (uint32_t *)copybuf;
+ for (i = 0; i < (COPY_BUF_SIZE / sizeof(fill_val)); i++) {
+ fillbuf[i] = fill_val;
+ }
+
+ while (len) {
+ chunk = min(len, COPY_BUF_SIZE);
+ *crc32 = sparse_crc32(*crc32, copybuf, chunk);
+ len -= chunk;
+ }
+ }
+
+ return 0;
+}
+
+static int process_skip_chunk(struct sparse_file *s, unsigned int chunk_size,
+ int fd __unused, unsigned int blocks,
+ unsigned int block __unused, uint32_t *crc32)
+{
+ if (chunk_size != 0) {
+ return -EINVAL;
+ }
+
+ if (crc32) {
+ int64_t len = (int64_t)blocks * s->block_size;
+ memset(copybuf, 0, COPY_BUF_SIZE);
+
+ while (len) {
+ int chunk = min(len, COPY_BUF_SIZE);
+ *crc32 = sparse_crc32(*crc32, copybuf, chunk);
+ len -= chunk;
+ }
+ }
+
+ return 0;
+}
+
+static int process_crc32_chunk(int fd, unsigned int chunk_size, uint32_t crc32)
+{
+ uint32_t file_crc32;
+ int ret;
+
+ if (chunk_size != sizeof(file_crc32)) {
+ return -EINVAL;
+ }
+
+ ret = read_all(fd, &file_crc32, sizeof(file_crc32));
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (file_crc32 != crc32) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int process_chunk(struct sparse_file *s, int fd, off64_t offset,
+ unsigned int chunk_hdr_sz, chunk_header_t *chunk_header,
+ unsigned int cur_block, uint32_t *crc_ptr)
+{
+ int ret;
+ unsigned int chunk_data_size;
+
+ chunk_data_size = chunk_header->total_sz - chunk_hdr_sz;
+
+ switch (chunk_header->chunk_type) {
+ case CHUNK_TYPE_RAW:
+ ret = process_raw_chunk(s, chunk_data_size, fd, offset,
+ chunk_header->chunk_sz, cur_block, crc_ptr);
+ if (ret < 0) {
+ verbose_error(s->verbose, ret, "data block at %lld", offset);
+ return ret;
+ }
+ return chunk_header->chunk_sz;
+ case CHUNK_TYPE_FILL:
+ ret = process_fill_chunk(s, chunk_data_size, fd,
+ chunk_header->chunk_sz, cur_block, crc_ptr);
+ if (ret < 0) {
+ verbose_error(s->verbose, ret, "fill block at %lld", offset);
+ return ret;
+ }
+ return chunk_header->chunk_sz;
+ case CHUNK_TYPE_DONT_CARE:
+ ret = process_skip_chunk(s, chunk_data_size, fd,
+ chunk_header->chunk_sz, cur_block, crc_ptr);
+ if (chunk_data_size != 0) {
+ if (ret < 0) {
+ verbose_error(s->verbose, ret, "skip block at %lld", offset);
+ return ret;
+ }
+ }
+ return chunk_header->chunk_sz;
+ case CHUNK_TYPE_CRC32:
+ ret = process_crc32_chunk(fd, chunk_data_size, *crc_ptr);
+ if (ret < 0) {
+ verbose_error(s->verbose, -EINVAL, "crc block at %lld",
+ offset);
+ return ret;
+ }
+ return 0;
+ default:
+ verbose_error(s->verbose, -EINVAL, "unknown block %04X at %lld",
+ chunk_header->chunk_type, offset);
+ }
+
+ return 0;
+}
+
+static int sparse_file_read_sparse(struct sparse_file *s, int fd, bool crc)
+{
+ int ret;
+ unsigned int i;
+ sparse_header_t sparse_header;
+ chunk_header_t chunk_header;
+ uint32_t crc32 = 0;
+ uint32_t *crc_ptr = 0;
+ unsigned int cur_block = 0;
+ off64_t offset;
+
+ if (!copybuf) {
+ copybuf = malloc(COPY_BUF_SIZE);
+ }
+
+ if (!copybuf) {
+ return -ENOMEM;
+ }
+
+ if (crc) {
+ crc_ptr = &crc32;
+ }
+
+ ret = read_all(fd, &sparse_header, sizeof(sparse_header));
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
+ return -EINVAL;
+ }
+
+ if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {
+ return -EINVAL;
+ }
+
+ if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) {
+ return -EINVAL;
+ }
+
+ if (sparse_header.chunk_hdr_sz < sizeof(chunk_header)) {
+ return -EINVAL;
+ }
+
+ if (sparse_header.file_hdr_sz > SPARSE_HEADER_LEN) {
+ /* Skip the remaining bytes in a header that is longer than
+ * we expected.
+ */
+ lseek64(fd, sparse_header.file_hdr_sz - SPARSE_HEADER_LEN, SEEK_CUR);
+ }
+
+ for (i = 0; i < sparse_header.total_chunks; i++) {
+ ret = read_all(fd, &chunk_header, sizeof(chunk_header));
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (sparse_header.chunk_hdr_sz > CHUNK_HEADER_LEN) {
+ /* Skip the remaining bytes in a header that is longer than
+ * we expected.
+ */
+ lseek64(fd, sparse_header.chunk_hdr_sz - CHUNK_HEADER_LEN, SEEK_CUR);
+ }
+
+ offset = lseek64(fd, 0, SEEK_CUR);
+
+ ret = process_chunk(s, fd, offset, sparse_header.chunk_hdr_sz, &chunk_header,
+ cur_block, crc_ptr);
+ if (ret < 0) {
+ return ret;
+ }
+
+ cur_block += ret;
+ }
+
+ if (sparse_header.total_blks != cur_block) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sparse_file_read_normal(struct sparse_file *s, int fd)
+{
+ int ret;
+ uint32_t *buf = malloc(s->block_size);
+ unsigned int block = 0;
+ int64_t remain = s->len;
+ int64_t offset = 0;
+ unsigned int to_read;
+ unsigned int i;
+ bool sparse_block;
+
+ if (!buf) {
+ return -ENOMEM;
+ }
+
+ while (remain > 0) {
+ to_read = min(remain, s->block_size);
+ ret = read_all(fd, buf, to_read);
+ if (ret < 0) {
+ error("failed to read sparse file");
+ return ret;
+ }
+
+ if (to_read == s->block_size) {
+ sparse_block = true;
+ for (i = 1; i < s->block_size / sizeof(uint32_t); i++) {
+ if (buf[0] != buf[i]) {
+ sparse_block = false;
+ break;
+ }
+ }
+ } else {
+ sparse_block = false;
+ }
+
+ if (sparse_block) {
+ /* TODO: add flag to use skip instead of fill for buf[0] == 0 */
+ sparse_file_add_fill(s, buf[0], to_read, block);
+ } else {
+ sparse_file_add_fd(s, fd, offset, to_read, block);
+ }
+
+ remain -= to_read;
+ offset += to_read;
+ block++;
+ }
+
+ return 0;
+}
+
+int sparse_file_read(struct sparse_file *s, int fd, bool sparse, bool crc)
+{
+ if (crc && !sparse) {
+ return -EINVAL;
+ }
+
+ if (sparse) {
+ return sparse_file_read_sparse(s, fd, crc);
+ } else {
+ return sparse_file_read_normal(s, fd);
+ }
+}
+
+struct sparse_file *sparse_file_import(int fd, bool verbose, bool crc)
+{
+ int ret;
+ sparse_header_t sparse_header;
+ int64_t len;
+ struct sparse_file *s;
+
+ ret = read_all(fd, &sparse_header, sizeof(sparse_header));
+ if (ret < 0) {
+ verbose_error(verbose, ret, "header");
+ return NULL;
+ }
+
+ if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
+ verbose_error(verbose, -EINVAL, "header magic");
+ return NULL;
+ }
+
+ if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {
+ verbose_error(verbose, -EINVAL, "header major version");
+ return NULL;
+ }
+
+ if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) {
+ return NULL;
+ }
+
+ if (sparse_header.chunk_hdr_sz < sizeof(chunk_header_t)) {
+ return NULL;
+ }
+
+ len = (int64_t)sparse_header.total_blks * sparse_header.blk_sz;
+ s = sparse_file_new(sparse_header.blk_sz, len);
+ if (!s) {
+ verbose_error(verbose, -EINVAL, NULL);
+ return NULL;
+ }
+
+ ret = lseek64(fd, 0, SEEK_SET);
+ if (ret < 0) {
+ verbose_error(verbose, ret, "seeking");
+ sparse_file_destroy(s);
+ return NULL;
+ }
+
+ s->verbose = verbose;
+
+ ret = sparse_file_read(s, fd, true, crc);
+ if (ret < 0) {
+ sparse_file_destroy(s);
+ return NULL;
+ }
+
+ return s;
+}
+
+struct sparse_file *sparse_file_import_auto(int fd, bool crc, bool verbose)
+{
+ struct sparse_file *s;
+ int64_t len;
+ int ret;
+
+ s = sparse_file_import(fd, verbose, crc);
+ if (s) {
+ return s;
+ }
+
+ len = lseek64(fd, 0, SEEK_END);
+ if (len < 0) {
+ return NULL;
+ }
+
+ lseek64(fd, 0, SEEK_SET);
+
+ s = sparse_file_new(4096, len);
+ if (!s) {
+ return NULL;
+ }
+
+ ret = sparse_file_read_normal(s, fd);
+ if (ret < 0) {
+ sparse_file_destroy(s);
+ return NULL;
+ }
+
+ return s;
+}
diff --git a/libsuspend/Android.mk b/libsuspend/Android.mk
new file mode 100644
index 0000000..1ba2f59
--- /dev/null
+++ b/libsuspend/Android.mk
@@ -0,0 +1,33 @@
+# Copyright 2012 The Android Open Source Project
+
+LOCAL_PATH:= $(call my-dir)
+
+libsuspend_src_files := \
+ autosuspend.c \
+ autosuspend_autosleep.c \
+ autosuspend_earlysuspend.c \
+ autosuspend_wakeup_count.c \
+
+libsuspend_libraries := \
+ liblog libcutils
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(libsuspend_src_files)
+LOCAL_MODULE := libsuspend
+LOCAL_MODULE_TAGS := optional
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
+LOCAL_SHARED_LIBRARIES := $(libsuspend_libraries)
+LOCAL_CFLAGS := -Werror
+#LOCAL_CFLAGS += -DLOG_NDEBUG=0
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(libsuspend_src_files)
+LOCAL_MODULE := libsuspend
+LOCAL_MODULE_TAGS := optional
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
+LOCAL_CFLAGS := -Werror
+#LOCAL_CFLAGS += -DLOG_NDEBUG=0
+include $(BUILD_STATIC_LIBRARY)
diff --git a/libsuspend/autosuspend.c b/libsuspend/autosuspend.c
new file mode 100644
index 0000000..edd1007
--- /dev/null
+++ b/libsuspend/autosuspend.c
@@ -0,0 +1,112 @@
+/*
+ * 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 <stdbool.h>
+
+#define LOG_TAG "libsuspend"
+#include <cutils/log.h>
+
+#include <suspend/autosuspend.h>
+
+#include "autosuspend_ops.h"
+
+static struct autosuspend_ops *autosuspend_ops;
+static bool autosuspend_enabled;
+static bool autosuspend_inited;
+
+static int autosuspend_init(void)
+{
+ if (autosuspend_inited) {
+ return 0;
+ }
+
+ autosuspend_ops = autosuspend_earlysuspend_init();
+ if (autosuspend_ops) {
+ goto out;
+ }
+
+/* Remove autosleep so userspace can manager suspend/resume and keep stats */
+#if 0
+ autosuspend_ops = autosuspend_autosleep_init();
+ if (autosuspend_ops) {
+ goto out;
+ }
+#endif
+
+ autosuspend_ops = autosuspend_wakeup_count_init();
+ if (autosuspend_ops) {
+ goto out;
+ }
+
+ if (!autosuspend_ops) {
+ ALOGE("failed to initialize autosuspend\n");
+ return -1;
+ }
+
+out:
+ autosuspend_inited = true;
+
+ ALOGV("autosuspend initialized\n");
+ return 0;
+}
+
+int autosuspend_enable(void)
+{
+ int ret;
+
+ ret = autosuspend_init();
+ if (ret) {
+ return ret;
+ }
+
+ ALOGV("autosuspend_enable\n");
+
+ if (autosuspend_enabled) {
+ return 0;
+ }
+
+ ret = autosuspend_ops->enable();
+ if (ret) {
+ return ret;
+ }
+
+ autosuspend_enabled = true;
+ return 0;
+}
+
+int autosuspend_disable(void)
+{
+ int ret;
+
+ ret = autosuspend_init();
+ if (ret) {
+ return ret;
+ }
+
+ ALOGV("autosuspend_disable\n");
+
+ if (!autosuspend_enabled) {
+ return 0;
+ }
+
+ ret = autosuspend_ops->disable();
+ if (ret) {
+ return ret;
+ }
+
+ autosuspend_enabled = false;
+ return 0;
+}
diff --git a/libsuspend/autosuspend_autosleep.c b/libsuspend/autosuspend_autosleep.c
new file mode 100644
index 0000000..0d31e74
--- /dev/null
+++ b/libsuspend/autosuspend_autosleep.c
@@ -0,0 +1,101 @@
+/*
+ * 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 <stddef.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#define LOG_TAG "libsuspend"
+#include <cutils/log.h>
+
+#include "autosuspend_ops.h"
+
+#define SYS_POWER_AUTOSLEEP "/sys/power/autosleep"
+
+static int autosleep_fd;
+static const char *sleep_state = "mem";
+static const char *on_state = "off";
+
+static int autosuspend_autosleep_enable(void)
+{
+ char buf[80];
+ int ret;
+
+ ALOGV("autosuspend_autosleep_enable\n");
+
+ ret = write(autosleep_fd, sleep_state, strlen(sleep_state));
+ if (ret < 0) {
+ strerror_r(errno, buf, sizeof(buf));
+ ALOGE("Error writing to %s: %s\n", SYS_POWER_AUTOSLEEP, buf);
+ goto err;
+ }
+
+ ALOGV("autosuspend_autosleep_enable done\n");
+
+ return 0;
+
+err:
+ return ret;
+}
+
+static int autosuspend_autosleep_disable(void)
+{
+ char buf[80];
+ int ret;
+
+ ALOGV("autosuspend_autosleep_disable\n");
+
+ ret = write(autosleep_fd, on_state, strlen(on_state));
+ if (ret < 0) {
+ strerror_r(errno, buf, sizeof(buf));
+ ALOGE("Error writing to %s: %s\n", SYS_POWER_AUTOSLEEP, buf);
+ goto err;
+ }
+
+ ALOGV("autosuspend_autosleep_disable done\n");
+
+ return 0;
+
+err:
+ return ret;
+}
+
+struct autosuspend_ops autosuspend_autosleep_ops = {
+ .enable = autosuspend_autosleep_enable,
+ .disable = autosuspend_autosleep_disable,
+};
+
+struct autosuspend_ops *autosuspend_autosleep_init(void)
+{
+ char buf[80];
+
+ autosleep_fd = open(SYS_POWER_AUTOSLEEP, O_WRONLY);
+ if (autosleep_fd < 0) {
+ strerror_r(errno, buf, sizeof(buf));
+ ALOGE("Error opening %s: %s\n", SYS_POWER_AUTOSLEEP, buf);
+ return NULL;
+ }
+
+ ALOGI("Selected autosleep\n");
+
+ autosuspend_autosleep_disable();
+
+ return &autosuspend_autosleep_ops;
+}
diff --git a/libsuspend/autosuspend_earlysuspend.c b/libsuspend/autosuspend_earlysuspend.c
new file mode 100644
index 0000000..2bece4c
--- /dev/null
+++ b/libsuspend/autosuspend_earlysuspend.c
@@ -0,0 +1,222 @@
+/*
+ * 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 <pthread.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#define LOG_TAG "libsuspend"
+#include <cutils/log.h>
+
+#include "autosuspend_ops.h"
+
+#define EARLYSUSPEND_SYS_POWER_STATE "/sys/power/state"
+#define EARLYSUSPEND_WAIT_FOR_FB_SLEEP "/sys/power/wait_for_fb_sleep"
+#define EARLYSUSPEND_WAIT_FOR_FB_WAKE "/sys/power/wait_for_fb_wake"
+
+static int sPowerStatefd;
+static const char *pwr_state_mem = "mem";
+static const char *pwr_state_on = "on";
+static pthread_t earlysuspend_thread;
+static pthread_mutex_t earlysuspend_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t earlysuspend_cond = PTHREAD_COND_INITIALIZER;
+static bool wait_for_earlysuspend;
+static enum {
+ EARLYSUSPEND_ON,
+ EARLYSUSPEND_MEM,
+} earlysuspend_state = EARLYSUSPEND_ON;
+
+int wait_for_fb_wake(void)
+{
+ int err = 0;
+ char buf;
+ int fd = open(EARLYSUSPEND_WAIT_FOR_FB_WAKE, O_RDONLY, 0);
+ // if the file doesn't exist, the error will be caught in read() below
+ do {
+ err = read(fd, &buf, 1);
+ } while (err < 0 && errno == EINTR);
+ ALOGE_IF(err < 0,
+ "*** ANDROID_WAIT_FOR_FB_WAKE failed (%s)", strerror(errno));
+ close(fd);
+ return err < 0 ? err : 0;
+}
+
+static int wait_for_fb_sleep(void)
+{
+ int err = 0;
+ char buf;
+ int fd = open(EARLYSUSPEND_WAIT_FOR_FB_SLEEP, O_RDONLY, 0);
+ // if the file doesn't exist, the error will be caught in read() below
+ do {
+ err = read(fd, &buf, 1);
+ } while (err < 0 && errno == EINTR);
+ ALOGE_IF(err < 0,
+ "*** ANDROID_WAIT_FOR_FB_SLEEP failed (%s)", strerror(errno));
+ close(fd);
+ return err < 0 ? err : 0;
+}
+
+static void *earlysuspend_thread_func(void __unused *arg)
+{
+ while (1) {
+ if (wait_for_fb_sleep()) {
+ ALOGE("Failed reading wait_for_fb_sleep, exiting earlysuspend thread\n");
+ return NULL;
+ }
+ pthread_mutex_lock(&earlysuspend_mutex);
+ earlysuspend_state = EARLYSUSPEND_MEM;
+ pthread_cond_signal(&earlysuspend_cond);
+ pthread_mutex_unlock(&earlysuspend_mutex);
+
+ if (wait_for_fb_wake()) {
+ ALOGE("Failed reading wait_for_fb_wake, exiting earlysuspend thread\n");
+ return NULL;
+ }
+ pthread_mutex_lock(&earlysuspend_mutex);
+ earlysuspend_state = EARLYSUSPEND_ON;
+ pthread_cond_signal(&earlysuspend_cond);
+ pthread_mutex_unlock(&earlysuspend_mutex);
+ }
+}
+static int autosuspend_earlysuspend_enable(void)
+{
+ char buf[80];
+ int ret;
+
+ ALOGV("autosuspend_earlysuspend_enable\n");
+
+ ret = write(sPowerStatefd, pwr_state_mem, strlen(pwr_state_mem));
+ if (ret < 0) {
+ strerror_r(errno, buf, sizeof(buf));
+ ALOGE("Error writing to %s: %s\n", EARLYSUSPEND_SYS_POWER_STATE, buf);
+ goto err;
+ }
+
+ if (wait_for_earlysuspend) {
+ pthread_mutex_lock(&earlysuspend_mutex);
+ while (earlysuspend_state != EARLYSUSPEND_MEM) {
+ pthread_cond_wait(&earlysuspend_cond, &earlysuspend_mutex);
+ }
+ pthread_mutex_unlock(&earlysuspend_mutex);
+ }
+
+ ALOGV("autosuspend_earlysuspend_enable done\n");
+
+ return 0;
+
+err:
+ return ret;
+}
+
+static int autosuspend_earlysuspend_disable(void)
+{
+ char buf[80];
+ int ret;
+
+ ALOGV("autosuspend_earlysuspend_disable\n");
+
+ ret = write(sPowerStatefd, pwr_state_on, strlen(pwr_state_on));
+ if (ret < 0) {
+ strerror_r(errno, buf, sizeof(buf));
+ ALOGE("Error writing to %s: %s\n", EARLYSUSPEND_SYS_POWER_STATE, buf);
+ goto err;
+ }
+
+ if (wait_for_earlysuspend) {
+ pthread_mutex_lock(&earlysuspend_mutex);
+ while (earlysuspend_state != EARLYSUSPEND_ON) {
+ pthread_cond_wait(&earlysuspend_cond, &earlysuspend_mutex);
+ }
+ pthread_mutex_unlock(&earlysuspend_mutex);
+ }
+
+ ALOGV("autosuspend_earlysuspend_disable done\n");
+
+ return 0;
+
+err:
+ return ret;
+}
+
+struct autosuspend_ops autosuspend_earlysuspend_ops = {
+ .enable = autosuspend_earlysuspend_enable,
+ .disable = autosuspend_earlysuspend_disable,
+};
+
+void start_earlysuspend_thread(void)
+{
+ char buf[80];
+ int ret;
+
+ ret = access(EARLYSUSPEND_WAIT_FOR_FB_SLEEP, F_OK);
+ if (ret < 0) {
+ return;
+ }
+
+ ret = access(EARLYSUSPEND_WAIT_FOR_FB_WAKE, F_OK);
+ if (ret < 0) {
+ return;
+ }
+
+ wait_for_fb_wake();
+
+ ALOGI("Starting early suspend unblocker thread\n");
+ ret = pthread_create(&earlysuspend_thread, NULL, earlysuspend_thread_func, NULL);
+ if (ret) {
+ strerror_r(errno, buf, sizeof(buf));
+ ALOGE("Error creating thread: %s\n", buf);
+ return;
+ }
+
+ wait_for_earlysuspend = true;
+}
+
+struct autosuspend_ops *autosuspend_earlysuspend_init(void)
+{
+ char buf[80];
+ int ret;
+
+ sPowerStatefd = open(EARLYSUSPEND_SYS_POWER_STATE, O_RDWR);
+
+ if (sPowerStatefd < 0) {
+ strerror_r(errno, buf, sizeof(buf));
+ ALOGW("Error opening %s: %s\n", EARLYSUSPEND_SYS_POWER_STATE, buf);
+ return NULL;
+ }
+
+ ret = write(sPowerStatefd, "on", 2);
+ if (ret < 0) {
+ strerror_r(errno, buf, sizeof(buf));
+ ALOGW("Error writing 'on' to %s: %s\n", EARLYSUSPEND_SYS_POWER_STATE, buf);
+ goto err_write;
+ }
+
+ ALOGI("Selected early suspend\n");
+
+ start_earlysuspend_thread();
+
+ return &autosuspend_earlysuspend_ops;
+
+err_write:
+ close(sPowerStatefd);
+ return NULL;
+}
diff --git a/libsuspend/autosuspend_ops.h b/libsuspend/autosuspend_ops.h
new file mode 100644
index 0000000..698e25b
--- /dev/null
+++ b/libsuspend/autosuspend_ops.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBSUSPEND_AUTOSUSPEND_OPS_H_
+#define _LIBSUSPEND_AUTOSUSPEND_OPS_H_
+
+struct autosuspend_ops {
+ int (*enable)(void);
+ int (*disable)(void);
+};
+
+struct autosuspend_ops *autosuspend_autosleep_init(void);
+struct autosuspend_ops *autosuspend_earlysuspend_init(void);
+struct autosuspend_ops *autosuspend_wakeup_count_init(void);
+
+#endif
diff --git a/libsuspend/autosuspend_wakeup_count.c b/libsuspend/autosuspend_wakeup_count.c
new file mode 100644
index 0000000..7483a8f
--- /dev/null
+++ b/libsuspend/autosuspend_wakeup_count.c
@@ -0,0 +1,198 @@
+/*
+ * 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 <pthread.h>
+#include <semaphore.h>
+#include <stddef.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#define LOG_TAG "libsuspend"
+//#define LOG_NDEBUG 0
+#include <cutils/log.h>
+
+#include "autosuspend_ops.h"
+
+#define SYS_POWER_STATE "/sys/power/state"
+#define SYS_POWER_WAKEUP_COUNT "/sys/power/wakeup_count"
+
+static int state_fd;
+static int wakeup_count_fd;
+static pthread_t suspend_thread;
+static sem_t suspend_lockout;
+static const char *sleep_state = "mem";
+static void (*wakeup_func)(void) = NULL;
+
+static void *suspend_thread_func(void *arg __attribute__((unused)))
+{
+ char buf[80];
+ char wakeup_count[20];
+ int wakeup_count_len;
+ int ret;
+
+ while (1) {
+ usleep(100000);
+ ALOGV("%s: read wakeup_count\n", __func__);
+ lseek(wakeup_count_fd, 0, SEEK_SET);
+ wakeup_count_len = read(wakeup_count_fd, wakeup_count, sizeof(wakeup_count));
+ if (wakeup_count_len < 0) {
+ strerror_r(errno, buf, sizeof(buf));
+ ALOGE("Error reading from %s: %s\n", SYS_POWER_WAKEUP_COUNT, buf);
+ wakeup_count_len = 0;
+ continue;
+ }
+ if (!wakeup_count_len) {
+ ALOGE("Empty wakeup count\n");
+ continue;
+ }
+
+ ALOGV("%s: wait\n", __func__);
+ ret = sem_wait(&suspend_lockout);
+ if (ret < 0) {
+ strerror_r(errno, buf, sizeof(buf));
+ ALOGE("Error waiting on semaphore: %s\n", buf);
+ continue;
+ }
+
+ ALOGV("%s: write %*s to wakeup_count\n", __func__, wakeup_count_len, wakeup_count);
+ ret = write(wakeup_count_fd, wakeup_count, wakeup_count_len);
+ if (ret < 0) {
+ strerror_r(errno, buf, sizeof(buf));
+ ALOGE("Error writing to %s: %s\n", SYS_POWER_WAKEUP_COUNT, buf);
+ } else {
+ ALOGV("%s: write %s to %s\n", __func__, sleep_state, SYS_POWER_STATE);
+ ret = write(state_fd, sleep_state, strlen(sleep_state));
+ if (ret < 0) {
+ strerror_r(errno, buf, sizeof(buf));
+ ALOGE("Error writing to %s: %s\n", SYS_POWER_STATE, buf);
+ } else {
+ void (*func)(void) = wakeup_func;
+ if (func != NULL) {
+ (*func)();
+ }
+ }
+ }
+
+ ALOGV("%s: release sem\n", __func__);
+ ret = sem_post(&suspend_lockout);
+ if (ret < 0) {
+ strerror_r(errno, buf, sizeof(buf));
+ ALOGE("Error releasing semaphore: %s\n", buf);
+ }
+ }
+ return NULL;
+}
+
+static int autosuspend_wakeup_count_enable(void)
+{
+ char buf[80];
+ int ret;
+
+ ALOGV("autosuspend_wakeup_count_enable\n");
+
+ ret = sem_post(&suspend_lockout);
+
+ if (ret < 0) {
+ strerror_r(errno, buf, sizeof(buf));
+ ALOGE("Error changing semaphore: %s\n", buf);
+ }
+
+ ALOGV("autosuspend_wakeup_count_enable done\n");
+
+ return ret;
+}
+
+static int autosuspend_wakeup_count_disable(void)
+{
+ char buf[80];
+ int ret;
+
+ ALOGV("autosuspend_wakeup_count_disable\n");
+
+ ret = sem_wait(&suspend_lockout);
+
+ if (ret < 0) {
+ strerror_r(errno, buf, sizeof(buf));
+ ALOGE("Error changing semaphore: %s\n", buf);
+ }
+
+ ALOGV("autosuspend_wakeup_count_disable done\n");
+
+ return ret;
+}
+
+void set_wakeup_callback(void (*func)(void))
+{
+ if (wakeup_func != NULL) {
+ ALOGE("Duplicate wakeup callback applied, keeping original");
+ return;
+ }
+ wakeup_func = func;
+}
+
+struct autosuspend_ops autosuspend_wakeup_count_ops = {
+ .enable = autosuspend_wakeup_count_enable,
+ .disable = autosuspend_wakeup_count_disable,
+};
+
+struct autosuspend_ops *autosuspend_wakeup_count_init(void)
+{
+ int ret;
+ char buf[80];
+
+ state_fd = open(SYS_POWER_STATE, O_RDWR);
+ if (state_fd < 0) {
+ strerror_r(errno, buf, sizeof(buf));
+ ALOGE("Error opening %s: %s\n", SYS_POWER_STATE, buf);
+ goto err_open_state;
+ }
+
+ wakeup_count_fd = open(SYS_POWER_WAKEUP_COUNT, O_RDWR);
+ if (wakeup_count_fd < 0) {
+ strerror_r(errno, buf, sizeof(buf));
+ ALOGE("Error opening %s: %s\n", SYS_POWER_WAKEUP_COUNT, buf);
+ goto err_open_wakeup_count;
+ }
+
+ ret = sem_init(&suspend_lockout, 0, 0);
+ if (ret < 0) {
+ strerror_r(errno, buf, sizeof(buf));
+ ALOGE("Error creating semaphore: %s\n", buf);
+ goto err_sem_init;
+ }
+ ret = pthread_create(&suspend_thread, NULL, suspend_thread_func, NULL);
+ if (ret) {
+ strerror_r(ret, buf, sizeof(buf));
+ ALOGE("Error creating thread: %s\n", buf);
+ goto err_pthread_create;
+ }
+
+ ALOGI("Selected wakeup count\n");
+ return &autosuspend_wakeup_count_ops;
+
+err_pthread_create:
+ sem_destroy(&suspend_lockout);
+err_sem_init:
+ close(wakeup_count_fd);
+err_open_wakeup_count:
+ close(state_fd);
+err_open_state:
+ return NULL;
+}
diff --git a/libsuspend/include/suspend/autosuspend.h b/libsuspend/include/suspend/autosuspend.h
new file mode 100644
index 0000000..10e3d27
--- /dev/null
+++ b/libsuspend/include/suspend/autosuspend.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBSUSPEND_AUTOSUSPEND_H_
+#define _LIBSUSPEND_AUTOSUSPEND_H_
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/*
+ * autosuspend_enable
+ *
+ * Turn on autosuspend in the kernel, allowing it to enter suspend if no
+ * wakelocks/wakeup_sources are held.
+ *
+ *
+ *
+ * Returns 0 on success, -1 if autosuspend was not enabled.
+ */
+int autosuspend_enable(void);
+
+/*
+ * autosuspend_disable
+ *
+ * Turn off autosuspend in the kernel, preventing suspend and synchronizing
+ * with any in-progress resume.
+ *
+ * Returns 0 on success, -1 if autosuspend was not disabled.
+ */
+int autosuspend_disable(void);
+
+/*
+ * set_wakeup_callback
+ *
+ * Set a function to be called each time the device wakes up from suspend.
+ */
+void set_wakeup_callback(void (*func)(void));
+
+__END_DECLS
+
+#endif
diff --git a/libsync/Android.mk b/libsync/Android.mk
new file mode 100644
index 0000000..fd1c88c
--- /dev/null
+++ b/libsync/Android.mk
@@ -0,0 +1,20 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := sync.c
+LOCAL_MODULE := libsync
+LOCAL_MODULE_TAGS := optional
+LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_CFLAGS := -Werror
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := sync.c sync_test.c
+LOCAL_MODULE := sync_test
+LOCAL_MODULE_TAGS := optional tests
+LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_CFLAGS := -Werror
+include $(BUILD_EXECUTABLE)
diff --git a/libsync/include/sync/sync.h b/libsync/include/sync/sync.h
new file mode 100644
index 0000000..2e5d82f
--- /dev/null
+++ b/libsync/include/sync/sync.h
@@ -0,0 +1,54 @@
+/*
+ * sync.h
+ *
+ * Copyright 2012 Google, Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __SYS_CORE_SYNC_H
+#define __SYS_CORE_SYNC_H
+
+#include <sys/cdefs.h>
+#include <stdint.h>
+
+__BEGIN_DECLS
+
+// XXX: These structs are copied from the header "linux/sync.h".
+struct sync_fence_info_data {
+ uint32_t len;
+ char name[32];
+ int32_t status;
+ uint8_t pt_info[0];
+};
+
+struct sync_pt_info {
+ uint32_t len;
+ char obj_name[32];
+ char driver_name[32];
+ int32_t status;
+ uint64_t timestamp_ns;
+ uint8_t driver_data[0];
+};
+
+/* timeout in msecs */
+int sync_wait(int fd, int timeout);
+int sync_merge(const char *name, int fd1, int fd2);
+struct sync_fence_info_data *sync_fence_info(int fd);
+struct sync_pt_info *sync_pt_info(struct sync_fence_info_data *info,
+ struct sync_pt_info *itr);
+void sync_fence_info_free(struct sync_fence_info_data *info);
+
+__END_DECLS
+
+#endif /* __SYS_CORE_SYNC_H */
diff --git a/libsync/sw_sync.h b/libsync/sw_sync.h
new file mode 100644
index 0000000..fda1c4c
--- /dev/null
+++ b/libsync/sw_sync.h
@@ -0,0 +1,35 @@
+/*
+ * sw_sync.h
+ *
+ * Copyright 2013 Google, Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __SYS_CORE_SW_SYNC_H
+#define __SYS_CORE_SW_SYNC_H
+
+__BEGIN_DECLS
+
+/*
+ * sw_sync is mainly intended for testing and should not be compiled into
+ * production kernels
+ */
+
+int sw_sync_timeline_create(void);
+int sw_sync_timeline_inc(int fd, unsigned count);
+int sw_sync_fence_create(int fd, const char *name, unsigned value);
+
+__END_DECLS
+
+#endif /* __SYS_CORE_SW_SYNC_H */
diff --git a/libsync/sync.c b/libsync/sync.c
new file mode 100644
index 0000000..d73bb11
--- /dev/null
+++ b/libsync/sync.c
@@ -0,0 +1,117 @@
+/*
+ * sync.c
+ *
+ * Copyright 2012 Google, Inc
+ *
+ * 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 <fcntl.h>
+#include <malloc.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <linux/sync.h>
+#include <linux/sw_sync.h>
+
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+int sync_wait(int fd, int timeout)
+{
+ __s32 to = timeout;
+
+ return ioctl(fd, SYNC_IOC_WAIT, &to);
+}
+
+int sync_merge(const char *name, int fd1, int fd2)
+{
+ struct sync_merge_data data;
+ int err;
+
+ data.fd2 = fd2;
+ strlcpy(data.name, name, sizeof(data.name));
+
+ err = ioctl(fd1, SYNC_IOC_MERGE, &data);
+ if (err < 0)
+ return err;
+
+ return data.fence;
+}
+
+struct sync_fence_info_data *sync_fence_info(int fd)
+{
+ struct sync_fence_info_data *info;
+ int err;
+
+ info = malloc(4096);
+ if (info == NULL)
+ return NULL;
+
+ info->len = 4096;
+ err = ioctl(fd, SYNC_IOC_FENCE_INFO, info);
+ if (err < 0) {
+ free(info);
+ return NULL;
+ }
+
+ return info;
+}
+
+struct sync_pt_info *sync_pt_info(struct sync_fence_info_data *info,
+ struct sync_pt_info *itr)
+{
+ if (itr == NULL)
+ itr = (struct sync_pt_info *) info->pt_info;
+ else
+ itr = (struct sync_pt_info *) ((__u8 *)itr + itr->len);
+
+ if ((__u8 *)itr - (__u8 *)info >= (int)info->len)
+ return NULL;
+
+ return itr;
+}
+
+void sync_fence_info_free(struct sync_fence_info_data *info)
+{
+ free(info);
+}
+
+
+int sw_sync_timeline_create(void)
+{
+ return open("/dev/sw_sync", O_RDWR);
+}
+
+int sw_sync_timeline_inc(int fd, unsigned count)
+{
+ __u32 arg = count;
+
+ return ioctl(fd, SW_SYNC_IOC_INC, &arg);
+}
+
+int sw_sync_fence_create(int fd, const char *name, unsigned value)
+{
+ struct sw_sync_create_fence_data data;
+ int err;
+
+ data.value = value;
+ strlcpy(data.name, name, sizeof(data.name));
+
+ err = ioctl(fd, SW_SYNC_IOC_CREATE_FENCE, &data);
+ if (err < 0)
+ return err;
+
+ return data.fence;
+}
diff --git a/libsync/sync_test.c b/libsync/sync_test.c
new file mode 100644
index 0000000..ee9ea3c
--- /dev/null
+++ b/libsync/sync_test.c
@@ -0,0 +1,147 @@
+/*
+ * sync_test.c
+ *
+ * Copyright 2012 Google, Inc
+ *
+ * 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 <pthread.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sync/sync.h>
+#include "sw_sync.h"
+
+pthread_mutex_t printf_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+struct sync_thread_data {
+ int thread_no;
+ int fd[2];
+};
+
+void *sync_thread(void *data)
+{
+ struct sync_thread_data *sync_data = data;
+ struct sync_fence_info_data *info;
+ int err;
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ err = sync_wait(sync_data->fd[i], 10000);
+
+ pthread_mutex_lock(&printf_mutex);
+ if (err < 0) {
+ printf("thread %d wait %d failed: %s\n", sync_data->thread_no,
+ i, strerror(errno));
+ } else {
+ printf("thread %d wait %d done\n", sync_data->thread_no, i);
+ }
+ info = sync_fence_info(sync_data->fd[i]);
+ if (info) {
+ struct sync_pt_info *pt_info = NULL;
+ printf(" fence %s %d\n", info->name, info->status);
+
+ while ((pt_info = sync_pt_info(info, pt_info))) {
+ int ts_sec = pt_info->timestamp_ns / 1000000000LL;
+ int ts_usec = (pt_info->timestamp_ns % 1000000000LL) / 1000LL;
+ printf(" pt %s %s %d %d.%06d", pt_info->obj_name,
+ pt_info->driver_name, pt_info->status,
+ ts_sec, ts_usec);
+ if (!strcmp(pt_info->driver_name, "sw_sync"))
+ printf(" val=%d\n", *(uint32_t *)pt_info->driver_data);
+ else
+ printf("\n");
+ }
+ sync_fence_info_free(info);
+ }
+ pthread_mutex_unlock(&printf_mutex);
+ }
+
+ return NULL;
+}
+
+int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
+{
+ struct sync_thread_data sync_data[4];
+ pthread_t threads[4];
+ int sync_timeline_fd;
+ int i, j;
+ char str[256];
+
+ sync_timeline_fd = sw_sync_timeline_create();
+ if (sync_timeline_fd < 0) {
+ perror("can't create sw_sync_timeline:");
+ return 1;
+ }
+
+ for (i = 0; i < 3; i++) {
+ sync_data[i].thread_no = i;
+
+ for (j = 0; j < 2; j++) {
+ unsigned val = i + j * 3 + 1;
+ sprintf(str, "test_fence%d-%d", i, j);
+ int fd = sw_sync_fence_create(sync_timeline_fd, str, val);
+ if (fd < 0) {
+ printf("can't create sync pt %d: %s", val, strerror(errno));
+ return 1;
+ }
+ sync_data[i].fd[j] = fd;
+ printf("sync_data[%d].fd[%d] = %d;\n", i, j, fd);
+
+ }
+ }
+
+ sync_data[3].thread_no = 3;
+ for (j = 0; j < 2; j++) {
+ sprintf(str, "merged_fence%d", j);
+ sync_data[3].fd[j] = sync_merge(str, sync_data[0].fd[j], sync_data[1].fd[j]);
+ if (sync_data[3].fd[j] < 0) {
+ printf("can't merge sync pts %d and %d: %s\n",
+ sync_data[0].fd[j], sync_data[1].fd[j], strerror(errno));
+ return 1;
+ }
+ }
+
+ for (i = 0; i < 4; i++)
+ pthread_create(&threads[i], NULL, sync_thread, &sync_data[i]);
+
+
+ for (i = 0; i < 3; i++) {
+ int err;
+ printf("press enter to inc to %d\n", i+1);
+ fgets(str, sizeof(str), stdin);
+ err = sw_sync_timeline_inc(sync_timeline_fd, 1);
+ if (err < 0) {
+ perror("can't increment sync obj:");
+ return 1;
+ }
+ }
+
+ printf("press enter to close sync_timeline\n");
+ fgets(str, sizeof(str), stdin);
+
+ close(sync_timeline_fd);
+
+ printf("press enter to end test\n");
+ fgets(str, sizeof(str), stdin);
+
+ for (i = 0; i < 3; i++) {
+ void *val;
+ pthread_join(threads[i], &val);
+ }
+
+ return 0;
+}
diff --git a/libsync/tests/Android.mk b/libsync/tests/Android.mk
new file mode 100644
index 0000000..8137c7a
--- /dev/null
+++ b/libsync/tests/Android.mk
@@ -0,0 +1,29 @@
+#
+# Copyright 2014 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_CLANG := true
+LOCAL_MODULE := sync-unit-tests
+LOCAL_CFLAGS += -g -Wall -Werror -std=gnu++11 -Wno-missing-field-initializers -Wno-sign-compare
+LOCAL_SHARED_LIBRARIES += libsync
+LOCAL_STATIC_LIBRARIES += libgtest_main
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/../include
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/..
+LOCAL_SRC_FILES := \
+ sync_test.cpp
+include $(BUILD_NATIVE_TEST)
diff --git a/libsync/tests/sync_test.cpp b/libsync/tests/sync_test.cpp
new file mode 100644
index 0000000..55cd687
--- /dev/null
+++ b/libsync/tests/sync_test.cpp
@@ -0,0 +1,615 @@
+#include <gtest/gtest.h>
+#include <sync/sync.h>
+#include <sw_sync.h>
+#include <fcntl.h>
+#include <vector>
+#include <string>
+#include <cassert>
+#include <iostream>
+#include <unistd.h>
+#include <thread>
+#include <poll.h>
+#include <mutex>
+#include <algorithm>
+#include <tuple>
+#include <random>
+#include <unordered_map>
+
+// TODO: better stress tests?
+// Handle more than 64 fd's simultaneously, i.e. fix sync_fence_info's 4k limit.
+// Handle wraparound in timelines like nvidia.
+
+using namespace std;
+
+namespace {
+
+// C++ wrapper class for sync timeline.
+class SyncTimeline {
+ int m_fd = -1;
+ bool m_fdInitialized = false;
+public:
+ SyncTimeline(const SyncTimeline &) = delete;
+ SyncTimeline& operator=(SyncTimeline&) = delete;
+ SyncTimeline() noexcept {
+ int fd = sw_sync_timeline_create();
+ if (fd == -1)
+ return;
+ m_fdInitialized = true;
+ m_fd = fd;
+ }
+ void destroy() {
+ if (m_fdInitialized) {
+ close(m_fd);
+ m_fd = -1;
+ m_fdInitialized = false;
+ }
+ }
+ ~SyncTimeline() {
+ destroy();
+ }
+ bool isValid() const {
+ if (m_fdInitialized) {
+ int status = fcntl(m_fd, F_GETFD, 0);
+ if (status == 0)
+ return true;
+ else
+ return false;
+ }
+ else {
+ return false;
+ }
+ }
+ int getFd() const {
+ return m_fd;
+ }
+ int inc(int val = 1) {
+ return sw_sync_timeline_inc(m_fd, val);
+ }
+};
+
+struct SyncPointInfo {
+ std::string driverName;
+ std::string objectName;
+ uint64_t timeStampNs;
+ int status; // 1 sig, 0 active, neg is err
+};
+
+// Wrapper class for sync fence.
+class SyncFence {
+ int m_fd = -1;
+ bool m_fdInitialized = false;
+ static int s_fenceCount;
+
+ void setFd(int fd) {
+ m_fd = fd;
+ m_fdInitialized = true;
+ }
+ void clearFd() {
+ m_fd = -1;
+ m_fdInitialized = false;
+ }
+public:
+ bool isValid() const {
+ if (m_fdInitialized) {
+ int status = fcntl(m_fd, F_GETFD, 0);
+ if (status == 0)
+ return true;
+ else
+ return false;
+ }
+ else {
+ return false;
+ }
+ }
+ SyncFence& operator=(SyncFence &&rhs) noexcept {
+ destroy();
+ if (rhs.isValid()) {
+ setFd(rhs.getFd());
+ rhs.clearFd();
+ }
+ return *this;
+ }
+ SyncFence(SyncFence &&fence) noexcept {
+ if (fence.isValid()) {
+ setFd(fence.getFd());
+ fence.clearFd();
+ }
+ }
+ SyncFence(const SyncFence &fence) noexcept {
+ // This is ok, as sync fences are immutable after construction, so a dup
+ // is basically the same thing as a copy.
+ if (fence.isValid()) {
+ int fd = dup(fence.getFd());
+ if (fd == -1)
+ return;
+ setFd(fd);
+ }
+ }
+ SyncFence(const SyncTimeline &timeline,
+ int value,
+ const char *name = nullptr) noexcept {
+ std::string autoName = "allocFence";
+ autoName += s_fenceCount;
+ s_fenceCount++;
+ int fd = sw_sync_fence_create(timeline.getFd(), name ? name : autoName.c_str(), value);
+ if (fd == -1)
+ return;
+ setFd(fd);
+ }
+ SyncFence(const SyncFence &a, const SyncFence &b, const char *name = nullptr) noexcept {
+ std::string autoName = "mergeFence";
+ autoName += s_fenceCount;
+ s_fenceCount++;
+ int fd = sync_merge(name ? name : autoName.c_str(), a.getFd(), b.getFd());
+ if (fd == -1)
+ return;
+ setFd(fd);
+ }
+ SyncFence(const vector<SyncFence> &sources) noexcept {
+ assert(sources.size());
+ SyncFence temp(*begin(sources));
+ for (auto itr = ++begin(sources); itr != end(sources); ++itr) {
+ temp = SyncFence(*itr, temp);
+ }
+ if (temp.isValid()) {
+ setFd(temp.getFd());
+ temp.clearFd();
+ }
+ }
+ void destroy() {
+ if (isValid()) {
+ close(m_fd);
+ clearFd();
+ }
+ }
+ ~SyncFence() {
+ destroy();
+ }
+ int getFd() const {
+ return m_fd;
+ }
+ int wait(int timeout = -1) {
+ return sync_wait(m_fd, timeout);
+ }
+ vector<SyncPointInfo> getInfo() const {
+ struct sync_pt_info *pointInfo = nullptr;
+ vector<SyncPointInfo> fenceInfo;
+ sync_fence_info_data *info = sync_fence_info(getFd());
+ if (!info) {
+ return fenceInfo;
+ }
+ while ((pointInfo = sync_pt_info(info, pointInfo))) {
+ fenceInfo.push_back(SyncPointInfo{
+ pointInfo->driver_name,
+ pointInfo->obj_name,
+ pointInfo->timestamp_ns,
+ pointInfo->status});
+ }
+ sync_fence_info_free(info);
+ return fenceInfo;
+ }
+ int getSize() const {
+ return getInfo().size();
+ }
+ int getSignaledCount() const {
+ return countWithStatus(1);
+ }
+ int getActiveCount() const {
+ return countWithStatus(0);
+ }
+ int getErrorCount() const {
+ return countWithStatus(-1);
+ }
+private:
+ int countWithStatus(int status) const {
+ int count = 0;
+ for (auto &info : getInfo()) {
+ if (info.status == status) {
+ count++;
+ }
+ }
+ return count;
+ }
+};
+
+int SyncFence::s_fenceCount = 0;
+
+TEST(AllocTest, Timeline) {
+ SyncTimeline timeline;
+ ASSERT_TRUE(timeline.isValid());
+}
+
+TEST(AllocTest, Fence) {
+ SyncTimeline timeline;
+ ASSERT_TRUE(timeline.isValid());
+
+ SyncFence fence(timeline, 1);
+ ASSERT_TRUE(fence.isValid());
+}
+
+TEST(AllocTest, FenceNegative) {
+ int timeline = sw_sync_timeline_create();
+ ASSERT_GT(timeline, 0);
+
+ // bad fd.
+ ASSERT_LT(sw_sync_fence_create(-1, "fence", 1), 0);
+
+ // No name - segfaults in user space.
+ // Maybe we should be friendlier here?
+ /*
+ ASSERT_LT(sw_sync_fence_create(timeline, nullptr, 1), 0);
+ */
+ close(timeline);
+}
+
+TEST(FenceTest, OneTimelineWait) {
+ SyncTimeline timeline;
+ ASSERT_TRUE(timeline.isValid());
+
+ SyncFence fence(timeline, 5);
+ ASSERT_TRUE(fence.isValid());
+
+ // Wait on fence until timeout.
+ ASSERT_EQ(fence.wait(0), -1);
+ ASSERT_EQ(errno, ETIME);
+
+ // Advance timeline from 0 -> 1
+ ASSERT_EQ(timeline.inc(1), 0);
+
+ // Wait on fence until timeout.
+ ASSERT_EQ(fence.wait(0), -1);
+ ASSERT_EQ(errno, ETIME);
+
+ // Signal the fence.
+ ASSERT_EQ(timeline.inc(4), 0);
+
+ // Wait successfully.
+ ASSERT_EQ(fence.wait(0), 0);
+
+ // Go even futher, and confirm wait still succeeds.
+ ASSERT_EQ(timeline.inc(10), 0);
+ ASSERT_EQ(fence.wait(0), 0);
+}
+
+TEST(FenceTest, OneTimelinePoll) {
+ SyncTimeline timeline;
+ ASSERT_TRUE(timeline.isValid());
+
+ SyncFence fence(timeline, 100);
+ ASSERT_TRUE(fence.isValid());
+
+ fd_set set;
+ FD_ZERO(&set);
+ FD_SET(fence.getFd(), &set);
+
+ // Poll the fence, and wait till timeout.
+ timeval time = {0};
+ ASSERT_EQ(select(fence.getFd() + 1, &set, nullptr, nullptr, &time), 0);
+
+ // Advance the timeline.
+ timeline.inc(100);
+ timeline.inc(100);
+
+ // Select should return that the fd is read for reading.
+ FD_ZERO(&set);
+ FD_SET(fence.getFd(), &set);
+
+ ASSERT_EQ(select(fence.getFd() + 1, &set, nullptr, nullptr, &time), 1);
+ ASSERT_TRUE(FD_ISSET(fence.getFd(), &set));
+}
+
+TEST(FenceTest, OneTimelineMerge) {
+ SyncTimeline timeline;
+ ASSERT_TRUE(timeline.isValid());
+
+ // create fence a,b,c and then merge them all into fence d.
+ SyncFence a(timeline, 1), b(timeline, 2), c(timeline, 3);
+ ASSERT_TRUE(a.isValid());
+ ASSERT_TRUE(b.isValid());
+ ASSERT_TRUE(c.isValid());
+
+ SyncFence d({a,b,c});
+ ASSERT_TRUE(d.isValid());
+
+ // confirm all fences have one active point (even d).
+ ASSERT_EQ(a.getActiveCount(), 1);
+ ASSERT_EQ(b.getActiveCount(), 1);
+ ASSERT_EQ(c.getActiveCount(), 1);
+ ASSERT_EQ(d.getActiveCount(), 1);
+
+ // confirm that d is not signaled until the max of a,b,c
+ timeline.inc(1);
+ ASSERT_EQ(a.getSignaledCount(), 1);
+ ASSERT_EQ(d.getActiveCount(), 1);
+
+ timeline.inc(1);
+ ASSERT_EQ(b.getSignaledCount(), 1);
+ ASSERT_EQ(d.getActiveCount(), 1);
+
+ timeline.inc(1);
+ ASSERT_EQ(c.getSignaledCount(), 1);
+ ASSERT_EQ(d.getActiveCount(), 0);
+ ASSERT_EQ(d.getSignaledCount(), 1);
+}
+
+TEST(FenceTest, MergeSameFence) {
+ SyncTimeline timeline;
+ ASSERT_TRUE(timeline.isValid());
+
+ SyncFence fence(timeline, 5);
+ ASSERT_TRUE(fence.isValid());
+
+ SyncFence selfMergeFence(fence, fence);
+ ASSERT_TRUE(selfMergeFence.isValid());
+
+ ASSERT_EQ(selfMergeFence.getSignaledCount(), 0);
+
+ timeline.inc(5);
+ ASSERT_EQ(selfMergeFence.getSignaledCount(), 1);
+}
+
+TEST(FenceTest, WaitOnDestroyedTimeline) {
+ SyncTimeline timeline;
+ ASSERT_TRUE(timeline.isValid());
+
+ SyncFence fenceSig(timeline, 100);
+ SyncFence fenceKill(timeline, 200);
+
+ // Spawn a thread to wait on a fence when the timeline is killed.
+ thread waitThread{
+ [&]() {
+ ASSERT_EQ(timeline.inc(100), 0);
+
+ ASSERT_EQ(fenceKill.wait(-1), -1);
+ ASSERT_EQ(errno, ENOENT);
+ }
+ };
+
+ // Wait for the thread to spool up.
+ fenceSig.wait();
+
+ // Kill the timeline.
+ timeline.destroy();
+
+ // wait for the thread to clean up.
+ waitThread.join();
+}
+
+TEST(FenceTest, PollOnDestroyedTimeline) {
+ SyncTimeline timeline;
+ ASSERT_TRUE(timeline.isValid());
+
+ SyncFence fenceSig(timeline, 100);
+ SyncFence fenceKill(timeline, 200);
+
+ // Spawn a thread to wait on a fence when the timeline is killed.
+ thread waitThread{
+ [&]() {
+ ASSERT_EQ(timeline.inc(100), 0);
+
+ // Wait on the fd.
+ struct pollfd fds;
+ fds.fd = fenceKill.getFd();
+ fds.events = POLLIN | POLLERR;
+ ASSERT_EQ(poll(&fds, 1, -1), 1);
+ ASSERT_TRUE(fds.revents & POLLERR);
+ }
+ };
+
+ // Wait for the thread to spool up.
+ fenceSig.wait();
+
+ // Kill the timeline.
+ timeline.destroy();
+
+ // wait for the thread to clean up.
+ waitThread.join();
+}
+
+TEST(FenceTest, MultiTimelineWait) {
+ SyncTimeline timelineA, timelineB, timelineC;
+
+ SyncFence fenceA(timelineA, 5);
+ SyncFence fenceB(timelineB, 5);
+ SyncFence fenceC(timelineC, 5);
+
+ // Make a larger fence using 3 other fences from different timelines.
+ SyncFence mergedFence({fenceA, fenceB, fenceC});
+ ASSERT_TRUE(mergedFence.isValid());
+
+ // Confirm fence isn't signaled
+ ASSERT_EQ(mergedFence.getActiveCount(), 3);
+ ASSERT_EQ(mergedFence.wait(0), -1);
+ ASSERT_EQ(errno, ETIME);
+
+ timelineA.inc(5);
+ ASSERT_EQ(mergedFence.getActiveCount(), 2);
+ ASSERT_EQ(mergedFence.getSignaledCount(), 1);
+
+ timelineB.inc(5);
+ ASSERT_EQ(mergedFence.getActiveCount(), 1);
+ ASSERT_EQ(mergedFence.getSignaledCount(), 2);
+
+ timelineC.inc(5);
+ ASSERT_EQ(mergedFence.getActiveCount(), 0);
+ ASSERT_EQ(mergedFence.getSignaledCount(), 3);
+
+ // confirm you can successfully wait.
+ ASSERT_EQ(mergedFence.wait(100), 0);
+}
+
+TEST(StressTest, TwoThreadsSharedTimeline) {
+ const int iterations = 1 << 16;
+ int counter = 0;
+ SyncTimeline timeline;
+ ASSERT_TRUE(timeline.isValid());
+
+ // Use a single timeline to synchronize two threads
+ // hammmering on the same counter.
+ auto threadMain = [&](int threadId) {
+ for (int i = 0; i < iterations; i++) {
+ SyncFence fence(timeline, i * 2 + threadId);
+ ASSERT_TRUE(fence.isValid());
+
+ // Wait on the prior thread to complete.
+ ASSERT_EQ(fence.wait(), 0);
+
+ // Confirm the previous thread's writes are visible and then inc.
+ ASSERT_EQ(counter, i * 2 + threadId);
+ counter++;
+
+ // Kick off the other thread.
+ ASSERT_EQ(timeline.inc(), 0);
+ }
+ };
+
+ thread a{threadMain, 0};
+ thread b{threadMain, 1};
+ a.join();
+ b.join();
+
+ // make sure the threads did not trample on one another.
+ ASSERT_EQ(counter, iterations * 2);
+}
+
+class ConsumerStressTest : public ::testing::TestWithParam<int> {};
+
+TEST_P(ConsumerStressTest, MultiProducerSingleConsumer) {
+ mutex lock;
+ int counter = 0;
+ int iterations = 1 << 12;
+
+ vector<SyncTimeline> producerTimelines(GetParam());
+ vector<thread> threads;
+ SyncTimeline consumerTimeline;
+
+ // Producer threads run this lambda.
+ auto threadMain = [&](int threadId) {
+ for (int i = 0; i < iterations; i++) {
+ SyncFence fence(consumerTimeline, i);
+ ASSERT_TRUE(fence.isValid());
+
+ // Wait for the consumer to finish. Use alternate
+ // means of waiting on the fence.
+ if ((iterations + threadId) % 8 != 0) {
+ ASSERT_EQ(fence.wait(), 0);
+ }
+ else {
+ while (fence.getSignaledCount() != 1) {
+ ASSERT_EQ(fence.getErrorCount(), 0);
+ }
+ }
+
+ // Every producer increments the counter, the consumer checks + erases it.
+ lock.lock();
+ counter++;
+ lock.unlock();
+
+ ASSERT_EQ(producerTimelines[threadId].inc(), 0);
+ }
+ };
+
+ for (int i = 0; i < GetParam(); i++) {
+ threads.push_back(thread{threadMain, i});
+ }
+
+ // Consumer thread runs this loop.
+ for (int i = 1; i <= iterations; i++) {
+ // Create a fence representing all producers final timelines.
+ vector<SyncFence> fences;
+ for (auto& timeline : producerTimelines) {
+ fences.push_back(SyncFence(timeline, i));
+ }
+ SyncFence mergeFence(fences);
+ ASSERT_TRUE(mergeFence.isValid());
+
+ // Make sure we see an increment from every producer thread. Vary
+ // the means by which we wait.
+ if (iterations % 8 != 0) {
+ ASSERT_EQ(mergeFence.wait(), 0);
+ }
+ else {
+ while (mergeFence.getSignaledCount() != mergeFence.getSize()) {
+ ASSERT_EQ(mergeFence.getErrorCount(), 0);
+ }
+ }
+ ASSERT_EQ(counter, GetParam()*i);
+
+ // Release the producer threads.
+ ASSERT_EQ(consumerTimeline.inc(), 0);
+ }
+
+ for_each(begin(threads), end(threads), [](thread& thread) { thread.join(); });
+}
+INSTANTIATE_TEST_CASE_P(
+ ParameterizedStressTest,
+ ConsumerStressTest,
+ ::testing::Values(2,4,16));
+
+class MergeStressTest : public ::testing::TestWithParam<tuple<int, int>> {};
+
+template <typename K, typename V> using dict = unordered_map<K,V>;
+
+TEST_P(MergeStressTest, RandomMerge) {
+ int timelineCount = get<0>(GetParam());
+ int mergeCount = get<1>(GetParam());
+
+ vector<SyncTimeline> timelines(timelineCount);
+
+ default_random_engine generator;
+ uniform_int_distribution<int> timelineDist(0, timelines.size()-1);
+ uniform_int_distribution<int> syncPointDist(0, numeric_limits<int>::max());
+
+ SyncFence fence(timelines[0], 0);
+ ASSERT_TRUE(fence.isValid());
+
+ unordered_map<int, int> fenceMap;
+ fenceMap.insert(make_tuple(0, 0));
+
+ // Randomly create syncpoints out of a fixed set of timelines, and merge them together.
+ for (int i = 0; i < mergeCount; i++) {
+
+ // Generate syncpoint.
+ int timelineOffset = timelineDist(generator);
+ const SyncTimeline& timeline = timelines[timelineOffset];
+ int syncPoint = syncPointDist(generator);
+
+ // Keep track of the latest syncpoint in each timeline.
+ auto itr = fenceMap.find(timelineOffset);
+ if (itr == end(fenceMap)) {
+ fenceMap.insert(tie(timelineOffset, syncPoint));
+ }
+ else {
+ int oldSyncPoint = itr->second;
+ fenceMap.erase(itr);
+ fenceMap.insert(tie(timelineOffset, max(syncPoint, oldSyncPoint)));
+ }
+
+ // Merge.
+ fence = SyncFence(fence, SyncFence(timeline, syncPoint));
+ ASSERT_TRUE(fence.isValid());
+ }
+
+ // Confirm our map matches the fence.
+ ASSERT_EQ(fence.getSize(), fenceMap.size());
+
+ // Trigger the merged fence.
+ for (auto& item: fenceMap) {
+ ASSERT_EQ(fence.wait(0), -1);
+ ASSERT_EQ(errno, ETIME);
+
+ // Increment the timeline to the last syncpoint.
+ timelines[item.first].inc(item.second);
+ }
+
+ // Check that the fence is triggered.
+ ASSERT_EQ(fence.wait(0), 0);
+}
+
+INSTANTIATE_TEST_CASE_P(
+ ParameterizedMergeStressTest,
+ MergeStressTest,
+ ::testing::Combine(::testing::Values(16,32), ::testing::Values(32, 1024, 1024*32)));
+
+}
+
diff --git a/libsysutils/Android.mk b/libsysutils/Android.mk
new file mode 100644
index 0000000..7bf53e3
--- /dev/null
+++ b/libsysutils/Android.mk
@@ -0,0 +1,25 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ src/SocketListener.cpp \
+ src/FrameworkListener.cpp \
+ src/NetlinkListener.cpp \
+ src/NetlinkEvent.cpp \
+ src/FrameworkCommand.cpp \
+ src/SocketClient.cpp \
+ src/ServiceManager.cpp \
+ EventLogTags.logtags
+
+LOCAL_MODULE:= libsysutils
+
+LOCAL_CFLAGS := -Werror
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ liblog \
+ libnl
+
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/libsysutils/EventLogTags.logtags b/libsysutils/EventLogTags.logtags
new file mode 100644
index 0000000..713f8cd
--- /dev/null
+++ b/libsysutils/EventLogTags.logtags
@@ -0,0 +1,5 @@
+# See system/core/logcat/event.logtags for a description of the format of this file.
+
+# FrameworkListener dispatchCommand overflow
+78001 exp_det_dispatchCommand_overflow
+65537 exp_det_netlink_failure (uid|1)
diff --git a/libsysutils/src/FrameworkClient.cpp b/libsysutils/src/FrameworkClient.cpp
new file mode 100644
index 0000000..2f37055
--- /dev/null
+++ b/libsysutils/src/FrameworkClient.cpp
@@ -0,0 +1,42 @@
+#include <alloca.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <pthread.h>
+
+#define LOG_TAG "FrameworkClient"
+#include <cutils/log.h>
+
+#include <sysutils/FrameworkClient.h>
+
+FrameworkClient::FrameworkClient(int socket) {
+ mSocket = socket;
+ pthread_mutex_init(&mWriteMutex, NULL);
+}
+
+int FrameworkClient::sendMsg(const char *msg) {
+ int ret;
+ if (mSocket < 0) {
+ errno = EHOSTUNREACH;
+ return -1;
+ }
+
+ pthread_mutex_lock(&mWriteMutex);
+ ret = TEMP_FAILURE_RETRY(write(mSocket, msg, strlen(msg) +1));
+ if (ret < 0) {
+ SLOGW("Unable to send msg '%s' (%s)", msg, strerror(errno));
+ }
+ pthread_mutex_unlock(&mWriteMutex);
+ return 0;
+}
+
+int FrameworkClient::sendMsg(const char *msg, const char *data) {
+ size_t bufflen = strlen(msg) + strlen(data) + 1;
+ char *buffer = (char *) alloca(bufflen);
+ if (!buffer) {
+ errno = -ENOMEM;
+ return -1;
+ }
+ snprintf(buffer, bufflen, "%s%s", msg, data);
+ return sendMsg(buffer);
+}
+
diff --git a/libsysutils/src/FrameworkCommand.cpp b/libsysutils/src/FrameworkCommand.cpp
new file mode 100644
index 0000000..0b95a81
--- /dev/null
+++ b/libsysutils/src/FrameworkCommand.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <errno.h>
+
+#define LOG_TAG "FrameworkCommand"
+
+#include <cutils/log.h>
+
+#include <sysutils/FrameworkCommand.h>
+
+#define UNUSED __attribute__((unused))
+
+FrameworkCommand::FrameworkCommand(const char *cmd) {
+ mCommand = cmd;
+}
+
+int FrameworkCommand::runCommand(SocketClient *c UNUSED, int argc UNUSED,
+ char **argv UNUSED) {
+ SLOGW("Command %s has no run handler!", getCommand());
+ errno = ENOSYS;
+ return -1;
+}
diff --git a/libsysutils/src/FrameworkListener.cpp b/libsysutils/src/FrameworkListener.cpp
new file mode 100644
index 0000000..e7b3dd6
--- /dev/null
+++ b/libsysutils/src/FrameworkListener.cpp
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define LOG_TAG "FrameworkListener"
+
+#include <cutils/log.h>
+
+#include <sysutils/FrameworkListener.h>
+#include <sysutils/FrameworkCommand.h>
+#include <sysutils/SocketClient.h>
+
+static const int CMD_BUF_SIZE = 1024;
+
+#define UNUSED __attribute__((unused))
+
+FrameworkListener::FrameworkListener(const char *socketName, bool withSeq) :
+ SocketListener(socketName, true, withSeq) {
+ init(socketName, withSeq);
+}
+
+FrameworkListener::FrameworkListener(const char *socketName) :
+ SocketListener(socketName, true, false) {
+ init(socketName, false);
+}
+
+FrameworkListener::FrameworkListener(int sock) :
+ SocketListener(sock, true) {
+ init(NULL, false);
+}
+
+void FrameworkListener::init(const char *socketName UNUSED, bool withSeq) {
+ mCommands = new FrameworkCommandCollection();
+ errorRate = 0;
+ mCommandCount = 0;
+ mWithSeq = withSeq;
+}
+
+bool FrameworkListener::onDataAvailable(SocketClient *c) {
+ char buffer[CMD_BUF_SIZE];
+ int len;
+
+ len = TEMP_FAILURE_RETRY(read(c->getSocket(), buffer, sizeof(buffer)));
+ if (len < 0) {
+ SLOGE("read() failed (%s)", strerror(errno));
+ return false;
+ } else if (!len)
+ return false;
+ if(buffer[len-1] != '\0')
+ SLOGW("String is not zero-terminated");
+
+ int offset = 0;
+ int i;
+
+ for (i = 0; i < len; i++) {
+ if (buffer[i] == '\0') {
+ /* IMPORTANT: dispatchCommand() expects a zero-terminated string */
+ dispatchCommand(c, buffer + offset);
+ offset = i + 1;
+ }
+ }
+
+ return true;
+}
+
+void FrameworkListener::registerCmd(FrameworkCommand *cmd) {
+ mCommands->push_back(cmd);
+}
+
+void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) {
+ FrameworkCommandCollection::iterator i;
+ int argc = 0;
+ char *argv[FrameworkListener::CMD_ARGS_MAX];
+ char tmp[CMD_BUF_SIZE];
+ char *p = data;
+ char *q = tmp;
+ char *qlimit = tmp + sizeof(tmp) - 1;
+ bool esc = false;
+ bool quote = false;
+ bool haveCmdNum = !mWithSeq;
+
+ memset(argv, 0, sizeof(argv));
+ memset(tmp, 0, sizeof(tmp));
+ while(*p) {
+ if (*p == '\\') {
+ if (esc) {
+ if (q >= qlimit)
+ goto overflow;
+ *q++ = '\\';
+ esc = false;
+ } else
+ esc = true;
+ p++;
+ continue;
+ } else if (esc) {
+ if (*p == '"') {
+ if (q >= qlimit)
+ goto overflow;
+ *q++ = '"';
+ } else if (*p == '\\') {
+ if (q >= qlimit)
+ goto overflow;
+ *q++ = '\\';
+ } else {
+ cli->sendMsg(500, "Unsupported escape sequence", false);
+ goto out;
+ }
+ p++;
+ esc = false;
+ continue;
+ }
+
+ if (*p == '"') {
+ if (quote)
+ quote = false;
+ else
+ quote = true;
+ p++;
+ continue;
+ }
+
+ if (q >= qlimit)
+ goto overflow;
+ *q = *p++;
+ if (!quote && *q == ' ') {
+ *q = '\0';
+ if (!haveCmdNum) {
+ char *endptr;
+ int cmdNum = (int)strtol(tmp, &endptr, 0);
+ if (endptr == NULL || *endptr != '\0') {
+ cli->sendMsg(500, "Invalid sequence number", false);
+ goto out;
+ }
+ cli->setCmdNum(cmdNum);
+ haveCmdNum = true;
+ } else {
+ if (argc >= CMD_ARGS_MAX)
+ goto overflow;
+ argv[argc++] = strdup(tmp);
+ }
+ memset(tmp, 0, sizeof(tmp));
+ q = tmp;
+ continue;
+ }
+ q++;
+ }
+
+ *q = '\0';
+ if (argc >= CMD_ARGS_MAX)
+ goto overflow;
+ argv[argc++] = strdup(tmp);
+#if 0
+ for (int k = 0; k < argc; k++) {
+ SLOGD("arg[%d] = '%s'", k, argv[k]);
+ }
+#endif
+
+ if (quote) {
+ cli->sendMsg(500, "Unclosed quotes error", false);
+ goto out;
+ }
+
+ if (errorRate && (++mCommandCount % errorRate == 0)) {
+ /* ignore this command - let the timeout handler handle it */
+ SLOGE("Faking a timeout");
+ goto out;
+ }
+
+ for (i = mCommands->begin(); i != mCommands->end(); ++i) {
+ FrameworkCommand *c = *i;
+
+ if (!strcmp(argv[0], c->getCommand())) {
+ if (c->runCommand(cli, argc, argv)) {
+ SLOGW("Handler '%s' error (%s)", c->getCommand(), strerror(errno));
+ }
+ goto out;
+ }
+ }
+ cli->sendMsg(500, "Command not recognized", false);
+out:
+ int j;
+ for (j = 0; j < argc; j++)
+ free(argv[j]);
+ return;
+
+overflow:
+ LOG_EVENT_INT(78001, cli->getUid());
+ cli->sendMsg(500, "Command too long", false);
+ goto out;
+}
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
new file mode 100644
index 0000000..909df86
--- /dev/null
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -0,0 +1,657 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdlib.h>
+#include <string.h>
+
+#define LOG_TAG "NetlinkEvent"
+#include <cutils/log.h>
+
+#include <sysutils/NetlinkEvent.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/icmp6.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+
+#include <linux/if.h>
+#include <linux/if_addr.h>
+#include <linux/if_link.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nfnetlink_log.h>
+#include <linux/netfilter_ipv4/ipt_ULOG.h>
+
+/* From kernel's net/netfilter/xt_quota2.c */
+const int LOCAL_QLOG_NL_EVENT = 112;
+const int LOCAL_NFLOG_PACKET = NFNL_SUBSYS_ULOG << 8 | NFULNL_MSG_PACKET;
+
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+#include <netlink/attr.h>
+#include <netlink/genl/genl.h>
+#include <netlink/handlers.h>
+#include <netlink/msg.h>
+
+const int NetlinkEvent::NlActionUnknown = 0;
+const int NetlinkEvent::NlActionAdd = 1;
+const int NetlinkEvent::NlActionRemove = 2;
+const int NetlinkEvent::NlActionChange = 3;
+const int NetlinkEvent::NlActionLinkUp = 4;
+const int NetlinkEvent::NlActionLinkDown = 5;
+const int NetlinkEvent::NlActionAddressUpdated = 6;
+const int NetlinkEvent::NlActionAddressRemoved = 7;
+const int NetlinkEvent::NlActionRdnss = 8;
+const int NetlinkEvent::NlActionRouteUpdated = 9;
+const int NetlinkEvent::NlActionRouteRemoved = 10;
+
+NetlinkEvent::NetlinkEvent() {
+ mAction = NlActionUnknown;
+ memset(mParams, 0, sizeof(mParams));
+ mPath = NULL;
+ mSubsystem = NULL;
+}
+
+NetlinkEvent::~NetlinkEvent() {
+ int i;
+ if (mPath)
+ free(mPath);
+ if (mSubsystem)
+ free(mSubsystem);
+ for (i = 0; i < NL_PARAMS_MAX; i++) {
+ if (!mParams[i])
+ break;
+ free(mParams[i]);
+ }
+}
+
+void NetlinkEvent::dump() {
+ int i;
+
+ for (i = 0; i < NL_PARAMS_MAX; i++) {
+ if (!mParams[i])
+ break;
+ SLOGD("NL param '%s'\n", mParams[i]);
+ }
+}
+
+/*
+ * Returns the message name for a message in the NETLINK_ROUTE family, or NULL
+ * if parsing that message is not supported.
+ */
+static const char *rtMessageName(int type) {
+#define NL_EVENT_RTM_NAME(rtm) case rtm: return #rtm;
+ switch (type) {
+ NL_EVENT_RTM_NAME(RTM_NEWLINK);
+ NL_EVENT_RTM_NAME(RTM_DELLINK);
+ NL_EVENT_RTM_NAME(RTM_NEWADDR);
+ NL_EVENT_RTM_NAME(RTM_DELADDR);
+ NL_EVENT_RTM_NAME(RTM_NEWROUTE);
+ NL_EVENT_RTM_NAME(RTM_DELROUTE);
+ NL_EVENT_RTM_NAME(RTM_NEWNDUSEROPT);
+ NL_EVENT_RTM_NAME(LOCAL_QLOG_NL_EVENT);
+ NL_EVENT_RTM_NAME(LOCAL_NFLOG_PACKET);
+ default:
+ return NULL;
+ }
+#undef NL_EVENT_RTM_NAME
+}
+
+/*
+ * Checks that a binary NETLINK_ROUTE message is long enough for a payload of
+ * size bytes.
+ */
+static bool checkRtNetlinkLength(const struct nlmsghdr *nh, size_t size) {
+ if (nh->nlmsg_len < NLMSG_LENGTH(size)) {
+ SLOGE("Got a short %s message\n", rtMessageName(nh->nlmsg_type));
+ return false;
+ }
+ return true;
+}
+
+/*
+ * Utility function to log errors.
+ */
+static bool maybeLogDuplicateAttribute(bool isDup,
+ const char *attributeName,
+ const char *messageName) {
+ if (isDup) {
+ SLOGE("Multiple %s attributes in %s, ignoring\n", attributeName, messageName);
+ return true;
+ }
+ return false;
+}
+
+/*
+ * Parse a RTM_NEWLINK message.
+ */
+bool NetlinkEvent::parseIfInfoMessage(const struct nlmsghdr *nh) {
+ struct ifinfomsg *ifi = (struct ifinfomsg *) NLMSG_DATA(nh);
+ if (!checkRtNetlinkLength(nh, sizeof(*ifi)))
+ return false;
+
+ if ((ifi->ifi_flags & IFF_LOOPBACK) != 0) {
+ return false;
+ }
+
+ int len = IFLA_PAYLOAD(nh);
+ struct rtattr *rta;
+ for (rta = IFLA_RTA(ifi); RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
+ switch(rta->rta_type) {
+ case IFLA_IFNAME:
+ asprintf(&mParams[0], "INTERFACE=%s", (char *) RTA_DATA(rta));
+ mAction = (ifi->ifi_flags & IFF_LOWER_UP) ? NlActionLinkUp :
+ NlActionLinkDown;
+ mSubsystem = strdup("net");
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/*
+ * Parse a RTM_NEWADDR or RTM_DELADDR message.
+ */
+bool NetlinkEvent::parseIfAddrMessage(const struct nlmsghdr *nh) {
+ struct ifaddrmsg *ifaddr = (struct ifaddrmsg *) NLMSG_DATA(nh);
+ struct ifa_cacheinfo *cacheinfo = NULL;
+ char addrstr[INET6_ADDRSTRLEN] = "";
+ char ifname[IFNAMSIZ];
+
+ if (!checkRtNetlinkLength(nh, sizeof(*ifaddr)))
+ return false;
+
+ // Sanity check.
+ int type = nh->nlmsg_type;
+ if (type != RTM_NEWADDR && type != RTM_DELADDR) {
+ SLOGE("parseIfAddrMessage on incorrect message type 0x%x\n", type);
+ return false;
+ }
+
+ // For log messages.
+ const char *msgtype = rtMessageName(type);
+
+ struct rtattr *rta;
+ int len = IFA_PAYLOAD(nh);
+ for (rta = IFA_RTA(ifaddr); RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
+ if (rta->rta_type == IFA_ADDRESS) {
+ // Only look at the first address, because we only support notifying
+ // one change at a time.
+ if (maybeLogDuplicateAttribute(*addrstr != '\0', "IFA_ADDRESS", msgtype))
+ continue;
+
+ // Convert the IP address to a string.
+ if (ifaddr->ifa_family == AF_INET) {
+ struct in_addr *addr4 = (struct in_addr *) RTA_DATA(rta);
+ if (RTA_PAYLOAD(rta) < sizeof(*addr4)) {
+ SLOGE("Short IPv4 address (%zu bytes) in %s",
+ RTA_PAYLOAD(rta), msgtype);
+ continue;
+ }
+ inet_ntop(AF_INET, addr4, addrstr, sizeof(addrstr));
+ } else if (ifaddr->ifa_family == AF_INET6) {
+ struct in6_addr *addr6 = (struct in6_addr *) RTA_DATA(rta);
+ if (RTA_PAYLOAD(rta) < sizeof(*addr6)) {
+ SLOGE("Short IPv6 address (%zu bytes) in %s",
+ RTA_PAYLOAD(rta), msgtype);
+ continue;
+ }
+ inet_ntop(AF_INET6, addr6, addrstr, sizeof(addrstr));
+ } else {
+ SLOGE("Unknown address family %d\n", ifaddr->ifa_family);
+ continue;
+ }
+
+ // Find the interface name.
+ if (!if_indextoname(ifaddr->ifa_index, ifname)) {
+ SLOGE("Unknown ifindex %d in %s", ifaddr->ifa_index, msgtype);
+ return false;
+ }
+
+ } else if (rta->rta_type == IFA_CACHEINFO) {
+ // Address lifetime information.
+ if (maybeLogDuplicateAttribute(cacheinfo, "IFA_CACHEINFO", msgtype))
+ continue;
+
+ if (RTA_PAYLOAD(rta) < sizeof(*cacheinfo)) {
+ SLOGE("Short IFA_CACHEINFO (%zu vs. %zu bytes) in %s",
+ RTA_PAYLOAD(rta), sizeof(cacheinfo), msgtype);
+ continue;
+ }
+
+ cacheinfo = (struct ifa_cacheinfo *) RTA_DATA(rta);
+ }
+ }
+
+ if (addrstr[0] == '\0') {
+ SLOGE("No IFA_ADDRESS in %s\n", msgtype);
+ return false;
+ }
+
+ // Fill in netlink event information.
+ mAction = (type == RTM_NEWADDR) ? NlActionAddressUpdated :
+ NlActionAddressRemoved;
+ mSubsystem = strdup("net");
+ asprintf(&mParams[0], "ADDRESS=%s/%d", addrstr,
+ ifaddr->ifa_prefixlen);
+ asprintf(&mParams[1], "INTERFACE=%s", ifname);
+ asprintf(&mParams[2], "FLAGS=%u", ifaddr->ifa_flags);
+ asprintf(&mParams[3], "SCOPE=%u", ifaddr->ifa_scope);
+
+ if (cacheinfo) {
+ asprintf(&mParams[4], "PREFERRED=%u", cacheinfo->ifa_prefered);
+ asprintf(&mParams[5], "VALID=%u", cacheinfo->ifa_valid);
+ asprintf(&mParams[6], "CSTAMP=%u", cacheinfo->cstamp);
+ asprintf(&mParams[7], "TSTAMP=%u", cacheinfo->tstamp);
+ }
+
+ return true;
+}
+
+/*
+ * Parse a QLOG_NL_EVENT message.
+ */
+bool NetlinkEvent::parseUlogPacketMessage(const struct nlmsghdr *nh) {
+ const char *devname;
+ ulog_packet_msg_t *pm = (ulog_packet_msg_t *) NLMSG_DATA(nh);
+ if (!checkRtNetlinkLength(nh, sizeof(*pm)))
+ return false;
+
+ devname = pm->indev_name[0] ? pm->indev_name : pm->outdev_name;
+ asprintf(&mParams[0], "ALERT_NAME=%s", pm->prefix);
+ asprintf(&mParams[1], "INTERFACE=%s", devname);
+ mSubsystem = strdup("qlog");
+ mAction = NlActionChange;
+ return true;
+}
+
+/*
+ * Parse a LOCAL_NFLOG_PACKET message.
+ */
+bool NetlinkEvent::parseNfPacketMessage(struct nlmsghdr *nh) {
+ int uid = -1;
+ int len = 0;
+ char* raw = NULL;
+
+ struct nlattr *uid_attr = nlmsg_find_attr(nh, sizeof(struct genlmsghdr), NFULA_UID);
+ if (uid_attr) {
+ uid = ntohl(nla_get_u32(uid_attr));
+ }
+
+ struct nlattr *payload = nlmsg_find_attr(nh, sizeof(struct genlmsghdr), NFULA_PAYLOAD);
+ if (payload) {
+ /* First 256 bytes is plenty */
+ len = nla_len(payload);
+ if (len > 256) len = 256;
+ raw = (char*) nla_data(payload);
+ }
+
+ char* hex = (char*) calloc(1, 5 + (len * 2));
+ strcpy(hex, "HEX=");
+ for (int i = 0; i < len; i++) {
+ hex[4 + (i * 2)] = "0123456789abcdef"[(raw[i] >> 4) & 0xf];
+ hex[5 + (i * 2)] = "0123456789abcdef"[raw[i] & 0xf];
+ }
+
+ asprintf(&mParams[0], "UID=%d", uid);
+ mParams[1] = hex;
+ mSubsystem = strdup("strict");
+ mAction = NlActionChange;
+ return true;
+}
+
+/*
+ * Parse a RTM_NEWROUTE or RTM_DELROUTE message.
+ */
+bool NetlinkEvent::parseRtMessage(const struct nlmsghdr *nh) {
+ uint8_t type = nh->nlmsg_type;
+ const char *msgname = rtMessageName(type);
+
+ // Sanity check.
+ if (type != RTM_NEWROUTE && type != RTM_DELROUTE) {
+ SLOGE("%s: incorrect message type %d (%s)\n", __func__, type, msgname);
+ return false;
+ }
+
+ struct rtmsg *rtm = (struct rtmsg *) NLMSG_DATA(nh);
+ if (!checkRtNetlinkLength(nh, sizeof(*rtm)))
+ return false;
+
+ if (// Ignore static routes we've set up ourselves.
+ (rtm->rtm_protocol != RTPROT_KERNEL &&
+ rtm->rtm_protocol != RTPROT_RA) ||
+ // We're only interested in global unicast routes.
+ (rtm->rtm_scope != RT_SCOPE_UNIVERSE) ||
+ (rtm->rtm_type != RTN_UNICAST) ||
+ // We don't support source routing.
+ (rtm->rtm_src_len != 0) ||
+ // Cloned routes aren't real routes.
+ (rtm->rtm_flags & RTM_F_CLONED)) {
+ return false;
+ }
+
+ int family = rtm->rtm_family;
+ int prefixLength = rtm->rtm_dst_len;
+
+ // Currently we only support: destination, (one) next hop, ifindex.
+ char dst[INET6_ADDRSTRLEN] = "";
+ char gw[INET6_ADDRSTRLEN] = "";
+ char dev[IFNAMSIZ] = "";
+
+ size_t len = RTM_PAYLOAD(nh);
+ struct rtattr *rta;
+ for (rta = RTM_RTA(rtm); RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
+ switch (rta->rta_type) {
+ case RTA_DST:
+ if (maybeLogDuplicateAttribute(*dst, "RTA_DST", msgname))
+ continue;
+ if (!inet_ntop(family, RTA_DATA(rta), dst, sizeof(dst)))
+ return false;
+ continue;
+ case RTA_GATEWAY:
+ if (maybeLogDuplicateAttribute(*gw, "RTA_GATEWAY", msgname))
+ continue;
+ if (!inet_ntop(family, RTA_DATA(rta), gw, sizeof(gw)))
+ return false;
+ continue;
+ case RTA_OIF:
+ if (maybeLogDuplicateAttribute(*dev, "RTA_OIF", msgname))
+ continue;
+ if (!if_indextoname(* (int *) RTA_DATA(rta), dev))
+ return false;
+ default:
+ continue;
+ }
+ }
+
+ // If there's no RTA_DST attribute, then:
+ // - If the prefix length is zero, it's the default route.
+ // - If the prefix length is nonzero, there's something we don't understand.
+ // Ignore the event.
+ if (!*dst && !prefixLength) {
+ if (family == AF_INET) {
+ strncpy(dst, "0.0.0.0", sizeof(dst));
+ } else if (family == AF_INET6) {
+ strncpy(dst, "::", sizeof(dst));
+ }
+ }
+
+ // A useful route must have a destination and at least either a gateway or
+ // an interface.
+ if (!*dst || (!*gw && !*dev))
+ return false;
+
+ // Fill in netlink event information.
+ mAction = (type == RTM_NEWROUTE) ? NlActionRouteUpdated :
+ NlActionRouteRemoved;
+ mSubsystem = strdup("net");
+ asprintf(&mParams[0], "ROUTE=%s/%d", dst, prefixLength);
+ asprintf(&mParams[1], "GATEWAY=%s", (*gw) ? gw : "");
+ asprintf(&mParams[2], "INTERFACE=%s", (*dev) ? dev : "");
+
+ return true;
+}
+
+/*
+ * Parse a RTM_NEWNDUSEROPT message.
+ */
+bool NetlinkEvent::parseNdUserOptMessage(const struct nlmsghdr *nh) {
+ struct nduseroptmsg *msg = (struct nduseroptmsg *) NLMSG_DATA(nh);
+ if (!checkRtNetlinkLength(nh, sizeof(*msg)))
+ return false;
+
+ // Check the length is valid.
+ int len = NLMSG_PAYLOAD(nh, sizeof(*msg));
+ if (msg->nduseropt_opts_len > len) {
+ SLOGE("RTM_NEWNDUSEROPT invalid length %d > %d\n",
+ msg->nduseropt_opts_len, len);
+ return false;
+ }
+ len = msg->nduseropt_opts_len;
+
+ // Check address family and packet type.
+ if (msg->nduseropt_family != AF_INET6) {
+ SLOGE("RTM_NEWNDUSEROPT message for unknown family %d\n",
+ msg->nduseropt_family);
+ return false;
+ }
+
+ if (msg->nduseropt_icmp_type != ND_ROUTER_ADVERT ||
+ msg->nduseropt_icmp_code != 0) {
+ SLOGE("RTM_NEWNDUSEROPT message for unknown ICMPv6 type/code %d/%d\n",
+ msg->nduseropt_icmp_type, msg->nduseropt_icmp_code);
+ return false;
+ }
+
+ // Find the interface name.
+ char ifname[IFNAMSIZ];
+ if (!if_indextoname(msg->nduseropt_ifindex, ifname)) {
+ SLOGE("RTM_NEWNDUSEROPT on unknown ifindex %d\n",
+ msg->nduseropt_ifindex);
+ return false;
+ }
+
+ // The kernel sends a separate netlink message for each ND option in the RA.
+ // So only parse the first ND option in the message.
+ struct nd_opt_hdr *opthdr = (struct nd_opt_hdr *) (msg + 1);
+
+ // The length is in multiples of 8 octets.
+ uint16_t optlen = opthdr->nd_opt_len;
+ if (optlen * 8 > len) {
+ SLOGE("Invalid option length %d > %d for ND option %d\n",
+ optlen * 8, len, opthdr->nd_opt_type);
+ return false;
+ }
+
+ if (opthdr->nd_opt_type == ND_OPT_RDNSS) {
+ // DNS Servers (RFC 6106).
+ // Each address takes up 2*8 octets, and the header takes up 8 octets.
+ // So for a valid option with one or more addresses, optlen must be
+ // odd and greater than 1.
+ if ((optlen < 3) || !(optlen & 0x1)) {
+ SLOGE("Invalid optlen %d for RDNSS option\n", optlen);
+ return false;
+ }
+ int numaddrs = (optlen - 1) / 2;
+
+ // Find the lifetime.
+ struct nd_opt_rdnss *rndss_opt = (struct nd_opt_rdnss *) opthdr;
+ uint32_t lifetime = ntohl(rndss_opt->nd_opt_rdnss_lifetime);
+
+ // Construct "SERVERS=<comma-separated string of DNS addresses>".
+ // Reserve (INET6_ADDRSTRLEN + 1) chars for each address: all but the
+ // the last address are followed by ','; the last is followed by '\0'.
+ static const char kServerTag[] = "SERVERS=";
+ static const int kTagLength = sizeof(kServerTag) - 1;
+ int bufsize = kTagLength + numaddrs * (INET6_ADDRSTRLEN + 1);
+ char *buf = (char *) malloc(bufsize);
+ if (!buf) {
+ SLOGE("RDNSS option: out of memory\n");
+ return false;
+ }
+ strcpy(buf, kServerTag);
+ int pos = kTagLength;
+
+ struct in6_addr *addrs = (struct in6_addr *) (rndss_opt + 1);
+ for (int i = 0; i < numaddrs; i++) {
+ if (i > 0) {
+ buf[pos++] = ',';
+ }
+ inet_ntop(AF_INET6, addrs + i, buf + pos, bufsize - pos);
+ pos += strlen(buf + pos);
+ }
+ buf[pos] = '\0';
+
+ mAction = NlActionRdnss;
+ mSubsystem = strdup("net");
+ asprintf(&mParams[0], "INTERFACE=%s", ifname);
+ asprintf(&mParams[1], "LIFETIME=%u", lifetime);
+ mParams[2] = buf;
+ } else {
+ SLOGD("Unknown ND option type %d\n", opthdr->nd_opt_type);
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Parse a binary message from a NETLINK_ROUTE netlink socket.
+ *
+ * Note that this function can only parse one message, because the message's
+ * content has to be stored in the class's member variables (mAction,
+ * mSubsystem, etc.). Invalid or unrecognized messages are skipped, but if
+ * there are multiple valid messages in the buffer, only the first one will be
+ * returned.
+ *
+ * TODO: consider only ever looking at the first message.
+ */
+bool NetlinkEvent::parseBinaryNetlinkMessage(char *buffer, int size) {
+ struct nlmsghdr *nh;
+
+ for (nh = (struct nlmsghdr *) buffer;
+ NLMSG_OK(nh, (unsigned) size) && (nh->nlmsg_type != NLMSG_DONE);
+ nh = NLMSG_NEXT(nh, size)) {
+
+ if (!rtMessageName(nh->nlmsg_type)) {
+ SLOGD("Unexpected netlink message type %d\n", nh->nlmsg_type);
+ continue;
+ }
+
+ if (nh->nlmsg_type == RTM_NEWLINK) {
+ if (parseIfInfoMessage(nh))
+ return true;
+
+ } else if (nh->nlmsg_type == LOCAL_QLOG_NL_EVENT) {
+ if (parseUlogPacketMessage(nh))
+ return true;
+
+ } else if (nh->nlmsg_type == RTM_NEWADDR ||
+ nh->nlmsg_type == RTM_DELADDR) {
+ if (parseIfAddrMessage(nh))
+ return true;
+
+ } else if (nh->nlmsg_type == RTM_NEWROUTE ||
+ nh->nlmsg_type == RTM_DELROUTE) {
+ if (parseRtMessage(nh))
+ return true;
+
+ } else if (nh->nlmsg_type == RTM_NEWNDUSEROPT) {
+ if (parseNdUserOptMessage(nh))
+ return true;
+
+ } else if (nh->nlmsg_type == LOCAL_NFLOG_PACKET) {
+ if (parseNfPacketMessage(nh))
+ return true;
+
+ }
+ }
+
+ return false;
+}
+
+/* If the string between 'str' and 'end' begins with 'prefixlen' characters
+ * from the 'prefix' array, then return 'str + prefixlen', otherwise return
+ * NULL.
+ */
+static const char*
+has_prefix(const char* str, const char* end, const char* prefix, size_t prefixlen)
+{
+ if ((end-str) >= (ptrdiff_t)prefixlen && !memcmp(str, prefix, prefixlen))
+ return str + prefixlen;
+ else
+ return NULL;
+}
+
+/* Same as strlen(x) for constant string literals ONLY */
+#define CONST_STRLEN(x) (sizeof(x)-1)
+
+/* Convenience macro to call has_prefix with a constant string literal */
+#define HAS_CONST_PREFIX(str,end,prefix) has_prefix((str),(end),prefix,CONST_STRLEN(prefix))
+
+
+/*
+ * Parse an ASCII-formatted message from a NETLINK_KOBJECT_UEVENT
+ * netlink socket.
+ */
+bool NetlinkEvent::parseAsciiNetlinkMessage(char *buffer, int size) {
+ const char *s = buffer;
+ const char *end;
+ int param_idx = 0;
+ int first = 1;
+
+ if (size == 0)
+ return false;
+
+ /* Ensure the buffer is zero-terminated, the code below depends on this */
+ buffer[size-1] = '\0';
+
+ end = s + size;
+ while (s < end) {
+ if (first) {
+ const char *p;
+ /* buffer is 0-terminated, no need to check p < end */
+ for (p = s; *p != '@'; p++) {
+ if (!*p) { /* no '@', should not happen */
+ return false;
+ }
+ }
+ mPath = strdup(p+1);
+ first = 0;
+ } else {
+ const char* a;
+ if ((a = HAS_CONST_PREFIX(s, end, "ACTION=")) != NULL) {
+ if (!strcmp(a, "add"))
+ mAction = NlActionAdd;
+ else if (!strcmp(a, "remove"))
+ mAction = NlActionRemove;
+ else if (!strcmp(a, "change"))
+ mAction = NlActionChange;
+ } else if ((a = HAS_CONST_PREFIX(s, end, "SEQNUM=")) != NULL) {
+ mSeq = atoi(a);
+ } else if ((a = HAS_CONST_PREFIX(s, end, "SUBSYSTEM=")) != NULL) {
+ mSubsystem = strdup(a);
+ } else if (param_idx < NL_PARAMS_MAX) {
+ mParams[param_idx++] = strdup(s);
+ }
+ }
+ s += strlen(s) + 1;
+ }
+ return true;
+}
+
+bool NetlinkEvent::decode(char *buffer, int size, int format) {
+ if (format == NetlinkListener::NETLINK_FORMAT_BINARY
+ || format == NetlinkListener::NETLINK_FORMAT_BINARY_UNICAST) {
+ return parseBinaryNetlinkMessage(buffer, size);
+ } else {
+ return parseAsciiNetlinkMessage(buffer, size);
+ }
+}
+
+const char *NetlinkEvent::findParam(const char *paramName) {
+ size_t len = strlen(paramName);
+ for (int i = 0; i < NL_PARAMS_MAX && mParams[i] != NULL; ++i) {
+ const char *ptr = mParams[i] + len;
+ if (!strncmp(mParams[i], paramName, len) && *ptr == '=')
+ return ++ptr;
+ }
+
+ SLOGE("NetlinkEvent::FindParam(): Parameter '%s' not found", paramName);
+ return NULL;
+}
diff --git a/libsysutils/src/NetlinkListener.cpp b/libsysutils/src/NetlinkListener.cpp
new file mode 100644
index 0000000..637aa1e
--- /dev/null
+++ b/libsysutils/src/NetlinkListener.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <linux/netlink.h>
+#include <string.h>
+
+#define LOG_TAG "NetlinkListener"
+#include <cutils/log.h>
+#include <cutils/uevent.h>
+
+#include <sysutils/NetlinkEvent.h>
+
+#if 1
+/* temporary version until we can get Motorola to update their
+ * ril.so. Their prebuilt ril.so is using this private class
+ * so changing the NetlinkListener() constructor breaks their ril.
+ */
+NetlinkListener::NetlinkListener(int socket) :
+ SocketListener(socket, false) {
+ mFormat = NETLINK_FORMAT_ASCII;
+}
+#endif
+
+NetlinkListener::NetlinkListener(int socket, int format) :
+ SocketListener(socket, false), mFormat(format) {
+}
+
+bool NetlinkListener::onDataAvailable(SocketClient *cli)
+{
+ int socket = cli->getSocket();
+ ssize_t count;
+ uid_t uid = -1;
+
+ bool require_group = true;
+ if (mFormat == NETLINK_FORMAT_BINARY_UNICAST) {
+ require_group = false;
+ }
+
+ count = TEMP_FAILURE_RETRY(uevent_kernel_recv(socket,
+ mBuffer, sizeof(mBuffer), require_group, &uid));
+ if (count < 0) {
+ if (uid > 0)
+ LOG_EVENT_INT(65537, uid);
+ SLOGE("recvmsg failed (%s)", strerror(errno));
+ return false;
+ }
+
+ NetlinkEvent *evt = new NetlinkEvent();
+ if (evt->decode(mBuffer, count, mFormat)) {
+ onEvent(evt);
+ } else if (mFormat != NETLINK_FORMAT_BINARY) {
+ // Don't complain if parseBinaryNetlinkMessage returns false. That can
+ // just mean that the buffer contained no messages we're interested in.
+ SLOGE("Error decoding NetlinkEvent");
+ }
+
+ delete evt;
+ return true;
+}
diff --git a/libsysutils/src/ServiceManager.cpp b/libsysutils/src/ServiceManager.cpp
new file mode 100644
index 0000000..41ac1dd
--- /dev/null
+++ b/libsysutils/src/ServiceManager.cpp
@@ -0,0 +1,116 @@
+#include <errno.h>
+#include <string.h>
+
+#include <sysutils/ServiceManager.h>
+
+#define LOG_TAG "Service"
+#include <cutils/log.h>
+#include <cutils/properties.h>
+
+ServiceManager::ServiceManager() {
+}
+
+/* The service name should not exceed SERVICE_NAME_MAX to avoid
+ * some weird things. This is due to the fact that:
+ *
+ * - Starting a service is done by writing its name to the "ctl.start"
+ * system property. This triggers the init daemon to actually start
+ * the service for us.
+ *
+ * - Stopping the service is done by writing its name to "ctl.stop"
+ * in a similar way.
+ *
+ * - Reading the status of a service is done by reading the property
+ * named "init.svc.<name>"
+ *
+ * If strlen(<name>) > (PROPERTY_KEY_MAX-1)-9, then you can start/stop
+ * the service by writing to ctl.start/stop, but you won't be able to
+ * read its state due to the truncation of "init.svc.<name>" into a
+ * zero-terminated buffer of PROPERTY_KEY_MAX characters.
+ */
+#define SERVICE_NAME_MAX (PROPERTY_KEY_MAX-10)
+
+/* The maximum amount of time to wait for a service to start or stop,
+ * in micro-seconds (really an approximation) */
+#define SLEEP_MAX_USEC 2000000 /* 2 seconds */
+
+/* The minimal sleeping interval between checking for the service's state
+ * when looping for SLEEP_MAX_USEC */
+#define SLEEP_MIN_USEC 200000 /* 200 msec */
+
+int ServiceManager::start(const char *name) {
+ if (strlen(name) > SERVICE_NAME_MAX) {
+ SLOGE("Service name '%s' is too long", name);
+ return 0;
+ }
+ if (isRunning(name)) {
+ SLOGW("Service '%s' is already running", name);
+ return 0;
+ }
+
+ SLOGD("Starting service '%s'", name);
+ property_set("ctl.start", name);
+
+ int count = SLEEP_MAX_USEC;
+ while(count > 0) {
+ usleep(SLEEP_MIN_USEC);
+ count -= SLEEP_MIN_USEC;
+ if (isRunning(name))
+ break;
+ }
+ if (count <= 0) {
+ SLOGW("Timed out waiting for service '%s' to start", name);
+ errno = ETIMEDOUT;
+ return -1;
+ }
+ SLOGD("Sucessfully started '%s'", name);
+ return 0;
+}
+
+int ServiceManager::stop(const char *name) {
+ if (strlen(name) > SERVICE_NAME_MAX) {
+ SLOGE("Service name '%s' is too long", name);
+ return 0;
+ }
+ if (!isRunning(name)) {
+ SLOGW("Service '%s' is already stopped", name);
+ return 0;
+ }
+
+ SLOGD("Stopping service '%s'", name);
+ property_set("ctl.stop", name);
+
+ int count = SLEEP_MAX_USEC;
+ while(count > 0) {
+ usleep(SLEEP_MIN_USEC);
+ count -= SLEEP_MIN_USEC;
+ if (!isRunning(name))
+ break;
+ }
+
+ if (count <= 0) {
+ SLOGW("Timed out waiting for service '%s' to stop", name);
+ errno = ETIMEDOUT;
+ return -1;
+ }
+ SLOGD("Successfully stopped '%s'", name);
+ return 0;
+}
+
+bool ServiceManager::isRunning(const char *name) {
+ char propVal[PROPERTY_VALUE_MAX];
+ char propName[PROPERTY_KEY_MAX];
+ int ret;
+
+ ret = snprintf(propName, sizeof(propName), "init.svc.%s", name);
+ if (ret > (int)sizeof(propName)-1) {
+ SLOGD("Service name '%s' is too long", name);
+ return false;
+ }
+
+ if (property_get(propName, propVal, NULL)) {
+ if (!strcmp(propVal, "running"))
+ return true;
+ }
+ return false;
+}
diff --git a/libsysutils/src/SocketClient.cpp b/libsysutils/src/SocketClient.cpp
new file mode 100644
index 0000000..bb9b6a1
--- /dev/null
+++ b/libsysutils/src/SocketClient.cpp
@@ -0,0 +1,250 @@
+#include <alloca.h>
+#include <errno.h>
+#include <malloc.h>
+#include <pthread.h>
+#include <signal.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#define LOG_TAG "SocketClient"
+#include <cutils/log.h>
+
+#include <sysutils/SocketClient.h>
+
+SocketClient::SocketClient(int socket, bool owned) {
+ init(socket, owned, false);
+}
+
+SocketClient::SocketClient(int socket, bool owned, bool useCmdNum) {
+ init(socket, owned, useCmdNum);
+}
+
+void SocketClient::init(int socket, bool owned, bool useCmdNum) {
+ mSocket = socket;
+ mSocketOwned = owned;
+ mUseCmdNum = useCmdNum;
+ pthread_mutex_init(&mWriteMutex, NULL);
+ pthread_mutex_init(&mRefCountMutex, NULL);
+ mPid = -1;
+ mUid = -1;
+ mGid = -1;
+ mRefCount = 1;
+ mCmdNum = 0;
+
+ struct ucred creds;
+ socklen_t szCreds = sizeof(creds);
+ memset(&creds, 0, szCreds);
+
+ int err = getsockopt(socket, SOL_SOCKET, SO_PEERCRED, &creds, &szCreds);
+ if (err == 0) {
+ mPid = creds.pid;
+ mUid = creds.uid;
+ mGid = creds.gid;
+ }
+}
+
+SocketClient::~SocketClient() {
+ if (mSocketOwned) {
+ close(mSocket);
+ }
+}
+
+int SocketClient::sendMsg(int code, const char *msg, bool addErrno) {
+ return sendMsg(code, msg, addErrno, mUseCmdNum);
+}
+
+int SocketClient::sendMsg(int code, const char *msg, bool addErrno, bool useCmdNum) {
+ char *buf;
+ int ret = 0;
+
+ if (addErrno) {
+ if (useCmdNum) {
+ ret = asprintf(&buf, "%d %d %s (%s)", code, getCmdNum(), msg, strerror(errno));
+ } else {
+ ret = asprintf(&buf, "%d %s (%s)", code, msg, strerror(errno));
+ }
+ } else {
+ if (useCmdNum) {
+ ret = asprintf(&buf, "%d %d %s", code, getCmdNum(), msg);
+ } else {
+ ret = asprintf(&buf, "%d %s", code, msg);
+ }
+ }
+ // Send the zero-terminated message
+ if (ret != -1) {
+ ret = sendMsg(buf);
+ free(buf);
+ }
+ return ret;
+}
+
+// send 3-digit code, null, binary-length, binary data
+int SocketClient::sendBinaryMsg(int code, const void *data, int len) {
+
+ // 4 bytes for the code & null + 4 bytes for the len
+ char buf[8];
+ // Write the code
+ snprintf(buf, 4, "%.3d", code);
+ // Write the len
+ uint32_t tmp = htonl(len);
+ memcpy(buf + 4, &tmp, sizeof(uint32_t));
+
+ struct iovec vec[2];
+ vec[0].iov_base = (void *) buf;
+ vec[0].iov_len = sizeof(buf);
+ vec[1].iov_base = (void *) data;
+ vec[1].iov_len = len;
+
+ pthread_mutex_lock(&mWriteMutex);
+ int result = sendDataLockedv(vec, (len > 0) ? 2 : 1);
+ pthread_mutex_unlock(&mWriteMutex);
+
+ return result;
+}
+
+// Sends the code (c-string null-terminated).
+int SocketClient::sendCode(int code) {
+ char buf[4];
+ snprintf(buf, sizeof(buf), "%.3d", code);
+ return sendData(buf, sizeof(buf));
+}
+
+char *SocketClient::quoteArg(const char *arg) {
+ int len = strlen(arg);
+ char *result = (char *)malloc(len * 2 + 3);
+ char *current = result;
+ const char *end = arg + len;
+ char *oldresult;
+
+ if(result == NULL) {
+ SLOGW("malloc error (%s)", strerror(errno));
+ return NULL;
+ }
+
+ *(current++) = '"';
+ while (arg < end) {
+ switch (*arg) {
+ case '\\':
+ case '"':
+ *(current++) = '\\'; // fallthrough
+ default:
+ *(current++) = *(arg++);
+ }
+ }
+ *(current++) = '"';
+ *(current++) = '\0';
+ oldresult = result; // save pointer in case realloc fails
+ result = (char *)realloc(result, current-result);
+ return result ? result : oldresult;
+}
+
+
+int SocketClient::sendMsg(const char *msg) {
+ // Send the message including null character
+ if (sendData(msg, strlen(msg) + 1) != 0) {
+ SLOGW("Unable to send msg '%s'", msg);
+ return -1;
+ }
+ return 0;
+}
+
+int SocketClient::sendData(const void *data, int len) {
+ struct iovec vec[1];
+ vec[0].iov_base = (void *) data;
+ vec[0].iov_len = len;
+
+ pthread_mutex_lock(&mWriteMutex);
+ int rc = sendDataLockedv(vec, 1);
+ pthread_mutex_unlock(&mWriteMutex);
+
+ return rc;
+}
+
+int SocketClient::sendDatav(struct iovec *iov, int iovcnt) {
+ pthread_mutex_lock(&mWriteMutex);
+ int rc = sendDataLockedv(iov, iovcnt);
+ pthread_mutex_unlock(&mWriteMutex);
+
+ return rc;
+}
+
+int SocketClient::sendDataLockedv(struct iovec *iov, int iovcnt) {
+
+ if (mSocket < 0) {
+ errno = EHOSTUNREACH;
+ return -1;
+ }
+
+ if (iovcnt <= 0) {
+ return 0;
+ }
+
+ int ret = 0;
+ int e = 0; // SLOGW and sigaction are not inert regarding errno
+ int current = 0;
+
+ struct sigaction new_action, old_action;
+ memset(&new_action, 0, sizeof(new_action));
+ new_action.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &new_action, &old_action);
+
+ for (;;) {
+ ssize_t rc = TEMP_FAILURE_RETRY(
+ writev(mSocket, iov + current, iovcnt - current));
+
+ if (rc > 0) {
+ size_t written = rc;
+ while ((current < iovcnt) && (written >= iov[current].iov_len)) {
+ written -= iov[current].iov_len;
+ current++;
+ }
+ if (current == iovcnt) {
+ break;
+ }
+ iov[current].iov_base = (char *)iov[current].iov_base + written;
+ iov[current].iov_len -= written;
+ continue;
+ }
+
+ if (rc == 0) {
+ e = EIO;
+ SLOGW("0 length write :(");
+ } else {
+ e = errno;
+ SLOGW("write error (%s)", strerror(e));
+ }
+ ret = -1;
+ break;
+ }
+
+ sigaction(SIGPIPE, &old_action, &new_action);
+
+ if (e != 0) {
+ errno = e;
+ }
+ return ret;
+}
+
+void SocketClient::incRef() {
+ pthread_mutex_lock(&mRefCountMutex);
+ mRefCount++;
+ pthread_mutex_unlock(&mRefCountMutex);
+}
+
+bool SocketClient::decRef() {
+ bool deleteSelf = false;
+ pthread_mutex_lock(&mRefCountMutex);
+ mRefCount--;
+ if (mRefCount == 0) {
+ deleteSelf = true;
+ } else if (mRefCount < 0) {
+ SLOGE("SocketClient refcount went negative!");
+ }
+ pthread_mutex_unlock(&mRefCountMutex);
+ if (deleteSelf) {
+ delete this;
+ }
+ return deleteSelf;
+}
diff --git a/libsysutils/src/SocketListener.cpp b/libsysutils/src/SocketListener.cpp
new file mode 100644
index 0000000..527a6a0
--- /dev/null
+++ b/libsysutils/src/SocketListener.cpp
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2008-2014 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 <errno.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
+#define LOG_TAG "SocketListener"
+#include <cutils/log.h>
+#include <cutils/sockets.h>
+
+#include <sysutils/SocketListener.h>
+#include <sysutils/SocketClient.h>
+
+#define CtrlPipe_Shutdown 0
+#define CtrlPipe_Wakeup 1
+
+SocketListener::SocketListener(const char *socketName, bool listen) {
+ init(socketName, -1, listen, false);
+}
+
+SocketListener::SocketListener(int socketFd, bool listen) {
+ init(NULL, socketFd, listen, false);
+}
+
+SocketListener::SocketListener(const char *socketName, bool listen, bool useCmdNum) {
+ init(socketName, -1, listen, useCmdNum);
+}
+
+void SocketListener::init(const char *socketName, int socketFd, bool listen, bool useCmdNum) {
+ mListen = listen;
+ mSocketName = socketName;
+ mSock = socketFd;
+ mUseCmdNum = useCmdNum;
+ pthread_mutex_init(&mClientsLock, NULL);
+ mClients = new SocketClientCollection();
+}
+
+SocketListener::~SocketListener() {
+ if (mSocketName && mSock > -1)
+ close(mSock);
+
+ if (mCtrlPipe[0] != -1) {
+ close(mCtrlPipe[0]);
+ close(mCtrlPipe[1]);
+ }
+ SocketClientCollection::iterator it;
+ for (it = mClients->begin(); it != mClients->end();) {
+ (*it)->decRef();
+ it = mClients->erase(it);
+ }
+ delete mClients;
+}
+
+int SocketListener::startListener() {
+ return startListener(4);
+}
+
+int SocketListener::startListener(int backlog) {
+
+ if (!mSocketName && mSock == -1) {
+ SLOGE("Failed to start unbound listener");
+ errno = EINVAL;
+ return -1;
+ } else if (mSocketName) {
+ if ((mSock = android_get_control_socket(mSocketName)) < 0) {
+ SLOGE("Obtaining file descriptor socket '%s' failed: %s",
+ mSocketName, strerror(errno));
+ return -1;
+ }
+ SLOGV("got mSock = %d for %s", mSock, mSocketName);
+ }
+
+ if (mListen && listen(mSock, backlog) < 0) {
+ SLOGE("Unable to listen on socket (%s)", strerror(errno));
+ return -1;
+ } else if (!mListen)
+ mClients->push_back(new SocketClient(mSock, false, mUseCmdNum));
+
+ if (pipe(mCtrlPipe)) {
+ SLOGE("pipe failed (%s)", strerror(errno));
+ return -1;
+ }
+
+ if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {
+ SLOGE("pthread_create (%s)", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+int SocketListener::stopListener() {
+ char c = CtrlPipe_Shutdown;
+ int rc;
+
+ rc = TEMP_FAILURE_RETRY(write(mCtrlPipe[1], &c, 1));
+ if (rc != 1) {
+ SLOGE("Error writing to control pipe (%s)", strerror(errno));
+ return -1;
+ }
+
+ void *ret;
+ if (pthread_join(mThread, &ret)) {
+ SLOGE("Error joining to listener thread (%s)", strerror(errno));
+ return -1;
+ }
+ close(mCtrlPipe[0]);
+ close(mCtrlPipe[1]);
+ mCtrlPipe[0] = -1;
+ mCtrlPipe[1] = -1;
+
+ if (mSocketName && mSock > -1) {
+ close(mSock);
+ mSock = -1;
+ }
+
+ SocketClientCollection::iterator it;
+ for (it = mClients->begin(); it != mClients->end();) {
+ delete (*it);
+ it = mClients->erase(it);
+ }
+ return 0;
+}
+
+void *SocketListener::threadStart(void *obj) {
+ SocketListener *me = reinterpret_cast<SocketListener *>(obj);
+
+ me->runListener();
+ pthread_exit(NULL);
+ return NULL;
+}
+
+void SocketListener::runListener() {
+
+ SocketClientCollection pendingList;
+
+ while(1) {
+ SocketClientCollection::iterator it;
+ fd_set read_fds;
+ int rc = 0;
+ int max = -1;
+
+ FD_ZERO(&read_fds);
+
+ if (mListen) {
+ max = mSock;
+ FD_SET(mSock, &read_fds);
+ }
+
+ FD_SET(mCtrlPipe[0], &read_fds);
+ if (mCtrlPipe[0] > max)
+ max = mCtrlPipe[0];
+
+ pthread_mutex_lock(&mClientsLock);
+ for (it = mClients->begin(); it != mClients->end(); ++it) {
+ // NB: calling out to an other object with mClientsLock held (safe)
+ int fd = (*it)->getSocket();
+ FD_SET(fd, &read_fds);
+ if (fd > max) {
+ max = fd;
+ }
+ }
+ pthread_mutex_unlock(&mClientsLock);
+ SLOGV("mListen=%d, max=%d, mSocketName=%s", mListen, max, mSocketName);
+ if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) {
+ if (errno == EINTR)
+ continue;
+ SLOGE("select failed (%s) mListen=%d, max=%d", strerror(errno), mListen, max);
+ sleep(1);
+ continue;
+ } else if (!rc)
+ continue;
+
+ if (FD_ISSET(mCtrlPipe[0], &read_fds)) {
+ char c = CtrlPipe_Shutdown;
+ TEMP_FAILURE_RETRY(read(mCtrlPipe[0], &c, 1));
+ if (c == CtrlPipe_Shutdown) {
+ break;
+ }
+ continue;
+ }
+ if (mListen && FD_ISSET(mSock, &read_fds)) {
+ struct sockaddr addr;
+ socklen_t alen;
+ int c;
+
+ do {
+ alen = sizeof(addr);
+ c = accept(mSock, &addr, &alen);
+ SLOGV("%s got %d from accept", mSocketName, c);
+ } while (c < 0 && errno == EINTR);
+ if (c < 0) {
+ SLOGE("accept failed (%s)", strerror(errno));
+ sleep(1);
+ continue;
+ }
+ pthread_mutex_lock(&mClientsLock);
+ mClients->push_back(new SocketClient(c, true, mUseCmdNum));
+ pthread_mutex_unlock(&mClientsLock);
+ }
+
+ /* Add all active clients to the pending list first */
+ pendingList.clear();
+ pthread_mutex_lock(&mClientsLock);
+ for (it = mClients->begin(); it != mClients->end(); ++it) {
+ SocketClient* c = *it;
+ // NB: calling out to an other object with mClientsLock held (safe)
+ int fd = c->getSocket();
+ if (FD_ISSET(fd, &read_fds)) {
+ pendingList.push_back(c);
+ c->incRef();
+ }
+ }
+ pthread_mutex_unlock(&mClientsLock);
+
+ /* Process the pending list, since it is owned by the thread,
+ * there is no need to lock it */
+ while (!pendingList.empty()) {
+ /* Pop the first item from the list */
+ it = pendingList.begin();
+ SocketClient* c = *it;
+ pendingList.erase(it);
+ /* Process it, if false is returned, remove from list */
+ if (!onDataAvailable(c)) {
+ release(c, false);
+ }
+ c->decRef();
+ }
+ }
+}
+
+bool SocketListener::release(SocketClient* c, bool wakeup) {
+ bool ret = false;
+ /* if our sockets are connection-based, remove and destroy it */
+ if (mListen && c) {
+ /* Remove the client from our array */
+ SLOGV("going to zap %d for %s", c->getSocket(), mSocketName);
+ pthread_mutex_lock(&mClientsLock);
+ SocketClientCollection::iterator it;
+ for (it = mClients->begin(); it != mClients->end(); ++it) {
+ if (*it == c) {
+ mClients->erase(it);
+ ret = true;
+ break;
+ }
+ }
+ pthread_mutex_unlock(&mClientsLock);
+ if (ret) {
+ ret = c->decRef();
+ if (wakeup) {
+ char b = CtrlPipe_Wakeup;
+ TEMP_FAILURE_RETRY(write(mCtrlPipe[1], &b, 1));
+ }
+ }
+ }
+ return ret;
+}
+
+void SocketListener::sendBroadcast(int code, const char *msg, bool addErrno) {
+ SocketClientCollection safeList;
+
+ /* Add all active clients to the safe list first */
+ safeList.clear();
+ pthread_mutex_lock(&mClientsLock);
+ SocketClientCollection::iterator i;
+
+ for (i = mClients->begin(); i != mClients->end(); ++i) {
+ SocketClient* c = *i;
+ c->incRef();
+ safeList.push_back(c);
+ }
+ pthread_mutex_unlock(&mClientsLock);
+
+ while (!safeList.empty()) {
+ /* Pop the first item from the list */
+ i = safeList.begin();
+ SocketClient* c = *i;
+ safeList.erase(i);
+ // broadcasts are unsolicited and should not include a cmd number
+ if (c->sendMsg(code, msg, addErrno, false)) {
+ SLOGW("Error sending broadcast (%s)", strerror(errno));
+ }
+ c->decRef();
+ }
+}
+
+void SocketListener::runOnEachSocket(SocketClientCommand *command) {
+ SocketClientCollection safeList;
+
+ /* Add all active clients to the safe list first */
+ safeList.clear();
+ pthread_mutex_lock(&mClientsLock);
+ SocketClientCollection::iterator i;
+
+ for (i = mClients->begin(); i != mClients->end(); ++i) {
+ SocketClient* c = *i;
+ c->incRef();
+ safeList.push_back(c);
+ }
+ pthread_mutex_unlock(&mClientsLock);
+
+ while (!safeList.empty()) {
+ /* Pop the first item from the list */
+ i = safeList.begin();
+ SocketClient* c = *i;
+ safeList.erase(i);
+ command->runSocketCommand(c);
+ c->decRef();
+ }
+}
diff --git a/libusbhost/Android.mk b/libusbhost/Android.mk
new file mode 100644
index 0000000..5c12f2c
--- /dev/null
+++ b/libusbhost/Android.mk
@@ -0,0 +1,58 @@
+#
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+# Static library for Linux host
+# ========================================================
+
+ifeq ($(HOST_OS),linux)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libusbhost
+LOCAL_SRC_FILES := usbhost.c
+LOCAL_CFLAGS := -Werror
+
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+endif
+
+# Shared library for target
+# ========================================================
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libusbhost
+LOCAL_SRC_FILES := usbhost.c
+
+LOCAL_CFLAGS := -g -DUSE_LIBLOG -Werror
+
+# needed for logcat
+LOCAL_SHARED_LIBRARIES := libcutils
+
+include $(BUILD_SHARED_LIBRARY)
+
+# Static library for target
+# ========================================================
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libusbhost
+LOCAL_SRC_FILES := usbhost.c
+LOCAL_CFLAGS := -Werror
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/libusbhost/usbhost.c b/libusbhost/usbhost.c
new file mode 100644
index 0000000..684f401
--- /dev/null
+++ b/libusbhost/usbhost.c
@@ -0,0 +1,700 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// #define DEBUG 1
+#if DEBUG
+
+#ifdef USE_LIBLOG
+#define LOG_TAG "usbhost"
+#include "utils/Log.h"
+#define D ALOGD
+#else
+#define D printf
+#endif
+
+#else
+#define D(...)
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stddef.h>
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/inotify.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+#include <pthread.h>
+
+#include <linux/usbdevice_fs.h>
+#include <asm/byteorder.h>
+
+#include "usbhost/usbhost.h"
+
+#define DEV_DIR "/dev"
+#define DEV_BUS_DIR DEV_DIR "/bus"
+#define USB_FS_DIR DEV_BUS_DIR "/usb"
+#define USB_FS_ID_SCANNER USB_FS_DIR "/%d/%d"
+#define USB_FS_ID_FORMAT USB_FS_DIR "/%03d/%03d"
+
+// From drivers/usb/core/devio.c
+// I don't know why this isn't in a kernel header
+#define MAX_USBFS_BUFFER_SIZE 16384
+
+#define MAX_USBFS_WD_COUNT 10
+
+struct usb_host_context {
+ int fd;
+ usb_device_added_cb cb_added;
+ usb_device_removed_cb cb_removed;
+ void *data;
+ int wds[MAX_USBFS_WD_COUNT];
+ int wdd;
+ int wddbus;
+};
+
+struct usb_device {
+ char dev_name[64];
+ unsigned char desc[4096];
+ int desc_length;
+ int fd;
+ int writeable;
+};
+
+static inline int badname(const char *name)
+{
+ while(*name) {
+ if(!isdigit(*name++)) return 1;
+ }
+ return 0;
+}
+
+static int find_existing_devices_bus(char *busname,
+ usb_device_added_cb added_cb,
+ void *client_data)
+{
+ char devname[32];
+ DIR *devdir;
+ struct dirent *de;
+ int done = 0;
+
+ devdir = opendir(busname);
+ if(devdir == 0) return 0;
+
+ while ((de = readdir(devdir)) && !done) {
+ if(badname(de->d_name)) continue;
+
+ snprintf(devname, sizeof(devname), "%s/%s", busname, de->d_name);
+ done = added_cb(devname, client_data);
+ } // end of devdir while
+ closedir(devdir);
+
+ return done;
+}
+
+/* returns true if one of the callbacks indicates we are done */
+static int find_existing_devices(usb_device_added_cb added_cb,
+ void *client_data)
+{
+ char busname[32];
+ DIR *busdir;
+ struct dirent *de;
+ int done = 0;
+
+ busdir = opendir(USB_FS_DIR);
+ if(busdir == 0) return 0;
+
+ while ((de = readdir(busdir)) != 0 && !done) {
+ if(badname(de->d_name)) continue;
+
+ snprintf(busname, sizeof(busname), USB_FS_DIR "/%s", de->d_name);
+ done = find_existing_devices_bus(busname, added_cb,
+ client_data);
+ } //end of busdir while
+ closedir(busdir);
+
+ return done;
+}
+
+static void watch_existing_subdirs(struct usb_host_context *context,
+ int *wds, int wd_count)
+{
+ char path[100];
+ int i, ret;
+
+ wds[0] = inotify_add_watch(context->fd, USB_FS_DIR, IN_CREATE | IN_DELETE);
+ if (wds[0] < 0)
+ return;
+
+ /* watch existing subdirectories of USB_FS_DIR */
+ for (i = 1; i < wd_count; i++) {
+ snprintf(path, sizeof(path), USB_FS_DIR "/%03d", i);
+ ret = inotify_add_watch(context->fd, path, IN_CREATE | IN_DELETE);
+ if (ret >= 0)
+ wds[i] = ret;
+ }
+}
+
+struct usb_host_context *usb_host_init()
+{
+ struct usb_host_context *context = calloc(1, sizeof(struct usb_host_context));
+ if (!context) {
+ fprintf(stderr, "out of memory in usb_host_context\n");
+ return NULL;
+ }
+ context->fd = inotify_init();
+ if (context->fd < 0) {
+ fprintf(stderr, "inotify_init failed\n");
+ free(context);
+ return NULL;
+ }
+ return context;
+}
+
+void usb_host_cleanup(struct usb_host_context *context)
+{
+ close(context->fd);
+ free(context);
+}
+
+int usb_host_get_fd(struct usb_host_context *context)
+{
+ return context->fd;
+} /* usb_host_get_fd() */
+
+int usb_host_load(struct usb_host_context *context,
+ usb_device_added_cb added_cb,
+ usb_device_removed_cb removed_cb,
+ usb_discovery_done_cb discovery_done_cb,
+ void *client_data)
+{
+ int done = 0;
+ int i;
+
+ context->cb_added = added_cb;
+ context->cb_removed = removed_cb;
+ context->data = client_data;
+
+ D("Created device discovery thread\n");
+
+ /* watch for files added and deleted within USB_FS_DIR */
+ context->wddbus = -1;
+ for (i = 0; i < MAX_USBFS_WD_COUNT; i++)
+ context->wds[i] = -1;
+
+ /* watch the root for new subdirectories */
+ context->wdd = inotify_add_watch(context->fd, DEV_DIR, IN_CREATE | IN_DELETE);
+ if (context->wdd < 0) {
+ fprintf(stderr, "inotify_add_watch failed\n");
+ if (discovery_done_cb)
+ discovery_done_cb(client_data);
+ return done;
+ }
+
+ watch_existing_subdirs(context, context->wds, MAX_USBFS_WD_COUNT);
+
+ /* check for existing devices first, after we have inotify set up */
+ done = find_existing_devices(added_cb, client_data);
+ if (discovery_done_cb)
+ done |= discovery_done_cb(client_data);
+
+ return done;
+} /* usb_host_load() */
+
+int usb_host_read_event(struct usb_host_context *context)
+{
+ struct inotify_event* event;
+ char event_buf[512];
+ char path[100];
+ int i, ret, done = 0;
+ int offset = 0;
+ int wd;
+
+ ret = read(context->fd, event_buf, sizeof(event_buf));
+ if (ret >= (int)sizeof(struct inotify_event)) {
+ while (offset < ret && !done) {
+ event = (struct inotify_event*)&event_buf[offset];
+ done = 0;
+ wd = event->wd;
+ if (wd == context->wdd) {
+ if ((event->mask & IN_CREATE) && !strcmp(event->name, "bus")) {
+ context->wddbus = inotify_add_watch(context->fd, DEV_BUS_DIR, IN_CREATE | IN_DELETE);
+ if (context->wddbus < 0) {
+ done = 1;
+ } else {
+ watch_existing_subdirs(context, context->wds, MAX_USBFS_WD_COUNT);
+ done = find_existing_devices(context->cb_added, context->data);
+ }
+ }
+ } else if (wd == context->wddbus) {
+ if ((event->mask & IN_CREATE) && !strcmp(event->name, "usb")) {
+ watch_existing_subdirs(context, context->wds, MAX_USBFS_WD_COUNT);
+ done = find_existing_devices(context->cb_added, context->data);
+ } else if ((event->mask & IN_DELETE) && !strcmp(event->name, "usb")) {
+ for (i = 0; i < MAX_USBFS_WD_COUNT; i++) {
+ if (context->wds[i] >= 0) {
+ inotify_rm_watch(context->fd, context->wds[i]);
+ context->wds[i] = -1;
+ }
+ }
+ }
+ } else if (wd == context->wds[0]) {
+ i = atoi(event->name);
+ snprintf(path, sizeof(path), USB_FS_DIR "/%s", event->name);
+ D("%s subdirectory %s: index: %d\n", (event->mask & IN_CREATE) ?
+ "new" : "gone", path, i);
+ if (i > 0 && i < MAX_USBFS_WD_COUNT) {
+ int local_ret = 0;
+ if (event->mask & IN_CREATE) {
+ local_ret = inotify_add_watch(context->fd, path,
+ IN_CREATE | IN_DELETE);
+ if (local_ret >= 0)
+ context->wds[i] = local_ret;
+ done = find_existing_devices_bus(path, context->cb_added,
+ context->data);
+ } else if (event->mask & IN_DELETE) {
+ inotify_rm_watch(context->fd, context->wds[i]);
+ context->wds[i] = -1;
+ }
+ }
+ } else {
+ for (i = 1; (i < MAX_USBFS_WD_COUNT) && !done; i++) {
+ if (wd == context->wds[i]) {
+ snprintf(path, sizeof(path), USB_FS_DIR "/%03d/%s", i, event->name);
+ if (event->mask == IN_CREATE) {
+ D("new device %s\n", path);
+ done = context->cb_added(path, context->data);
+ } else if (event->mask == IN_DELETE) {
+ D("gone device %s\n", path);
+ done = context->cb_removed(path, context->data);
+ }
+ }
+ }
+ }
+
+ offset += sizeof(struct inotify_event) + event->len;
+ }
+ }
+
+ return done;
+} /* usb_host_read_event() */
+
+void usb_host_run(struct usb_host_context *context,
+ usb_device_added_cb added_cb,
+ usb_device_removed_cb removed_cb,
+ usb_discovery_done_cb discovery_done_cb,
+ void *client_data)
+{
+ int done;
+
+ done = usb_host_load(context, added_cb, removed_cb, discovery_done_cb, client_data);
+
+ while (!done) {
+
+ done = usb_host_read_event(context);
+ }
+} /* usb_host_run() */
+
+struct usb_device *usb_device_open(const char *dev_name)
+{
+ int fd, did_retry = 0, writeable = 1;
+
+ D("usb_device_open %s\n", dev_name);
+
+retry:
+ fd = open(dev_name, O_RDWR);
+ if (fd < 0) {
+ /* if we fail, see if have read-only access */
+ fd = open(dev_name, O_RDONLY);
+ D("usb_device_open open returned %d errno %d\n", fd, errno);
+ if (fd < 0 && (errno == EACCES || errno == ENOENT) && !did_retry) {
+ /* work around race condition between inotify and permissions management */
+ sleep(1);
+ did_retry = 1;
+ goto retry;
+ }
+
+ if (fd < 0)
+ return NULL;
+ writeable = 0;
+ D("[ usb open read-only %s fd = %d]\n", dev_name, fd);
+ }
+
+ struct usb_device* result = usb_device_new(dev_name, fd);
+ if (result)
+ result->writeable = writeable;
+ return result;
+}
+
+void usb_device_close(struct usb_device *device)
+{
+ close(device->fd);
+ free(device);
+}
+
+struct usb_device *usb_device_new(const char *dev_name, int fd)
+{
+ struct usb_device *device = calloc(1, sizeof(struct usb_device));
+ int length;
+
+ D("usb_device_new %s fd: %d\n", dev_name, fd);
+
+ if (lseek(fd, 0, SEEK_SET) != 0)
+ goto failed;
+ length = read(fd, device->desc, sizeof(device->desc));
+ D("usb_device_new read returned %d errno %d\n", length, errno);
+ if (length < 0)
+ goto failed;
+
+ strncpy(device->dev_name, dev_name, sizeof(device->dev_name) - 1);
+ device->fd = fd;
+ device->desc_length = length;
+ // assume we are writeable, since usb_device_get_fd will only return writeable fds
+ device->writeable = 1;
+ return device;
+
+failed:
+ close(fd);
+ free(device);
+ return NULL;
+}
+
+static int usb_device_reopen_writeable(struct usb_device *device)
+{
+ if (device->writeable)
+ return 1;
+
+ int fd = open(device->dev_name, O_RDWR);
+ if (fd >= 0) {
+ close(device->fd);
+ device->fd = fd;
+ device->writeable = 1;
+ return 1;
+ }
+ D("usb_device_reopen_writeable failed errno %d\n", errno);
+ return 0;
+}
+
+int usb_device_get_fd(struct usb_device *device)
+{
+ if (!usb_device_reopen_writeable(device))
+ return -1;
+ return device->fd;
+}
+
+const char* usb_device_get_name(struct usb_device *device)
+{
+ return device->dev_name;
+}
+
+int usb_device_get_unique_id(struct usb_device *device)
+{
+ int bus = 0, dev = 0;
+ sscanf(device->dev_name, USB_FS_ID_SCANNER, &bus, &dev);
+ return bus * 1000 + dev;
+}
+
+int usb_device_get_unique_id_from_name(const char* name)
+{
+ int bus = 0, dev = 0;
+ sscanf(name, USB_FS_ID_SCANNER, &bus, &dev);
+ return bus * 1000 + dev;
+}
+
+char* usb_device_get_name_from_unique_id(int id)
+{
+ int bus = id / 1000;
+ int dev = id % 1000;
+ char* result = (char *)calloc(1, strlen(USB_FS_ID_FORMAT));
+ snprintf(result, strlen(USB_FS_ID_FORMAT) - 1, USB_FS_ID_FORMAT, bus, dev);
+ return result;
+}
+
+uint16_t usb_device_get_vendor_id(struct usb_device *device)
+{
+ struct usb_device_descriptor* desc = (struct usb_device_descriptor*)device->desc;
+ return __le16_to_cpu(desc->idVendor);
+}
+
+uint16_t usb_device_get_product_id(struct usb_device *device)
+{
+ struct usb_device_descriptor* desc = (struct usb_device_descriptor*)device->desc;
+ return __le16_to_cpu(desc->idProduct);
+}
+
+const struct usb_device_descriptor* usb_device_get_device_descriptor(struct usb_device *device)
+{
+ return (struct usb_device_descriptor*)device->desc;
+}
+
+char* usb_device_get_string(struct usb_device *device, int id)
+{
+ char string[256];
+ __u16 buffer[128];
+ __u16 languages[128];
+ int i, result;
+ int languageCount = 0;
+
+ if (id == 0) return NULL;
+
+ string[0] = 0;
+ memset(languages, 0, sizeof(languages));
+
+ // read list of supported languages
+ result = usb_device_control_transfer(device,
+ USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE, USB_REQ_GET_DESCRIPTOR,
+ (USB_DT_STRING << 8) | 0, 0, languages, sizeof(languages), 0);
+ if (result > 0)
+ languageCount = (result - 2) / 2;
+
+ for (i = 1; i <= languageCount; i++) {
+ memset(buffer, 0, sizeof(buffer));
+
+ result = usb_device_control_transfer(device,
+ USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE, USB_REQ_GET_DESCRIPTOR,
+ (USB_DT_STRING << 8) | id, languages[i], buffer, sizeof(buffer), 0);
+ if (result > 0) {
+ int i;
+ // skip first word, and copy the rest to the string, changing shorts to bytes.
+ result /= 2;
+ for (i = 1; i < result; i++)
+ string[i - 1] = buffer[i];
+ string[i - 1] = 0;
+ return strdup(string);
+ }
+ }
+
+ return NULL;
+}
+
+char* usb_device_get_manufacturer_name(struct usb_device *device)
+{
+ struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc;
+ return usb_device_get_string(device, desc->iManufacturer);
+}
+
+char* usb_device_get_product_name(struct usb_device *device)
+{
+ struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc;
+ return usb_device_get_string(device, desc->iProduct);
+}
+
+char* usb_device_get_serial(struct usb_device *device)
+{
+ struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc;
+ return usb_device_get_string(device, desc->iSerialNumber);
+}
+
+int usb_device_is_writeable(struct usb_device *device)
+{
+ return device->writeable;
+}
+
+void usb_descriptor_iter_init(struct usb_device *device, struct usb_descriptor_iter *iter)
+{
+ iter->config = device->desc;
+ iter->config_end = device->desc + device->desc_length;
+ iter->curr_desc = device->desc;
+}
+
+struct usb_descriptor_header *usb_descriptor_iter_next(struct usb_descriptor_iter *iter)
+{
+ struct usb_descriptor_header* next;
+ if (iter->curr_desc >= iter->config_end)
+ return NULL;
+ next = (struct usb_descriptor_header*)iter->curr_desc;
+ iter->curr_desc += next->bLength;
+ return next;
+}
+
+int usb_device_claim_interface(struct usb_device *device, unsigned int interface)
+{
+ return ioctl(device->fd, USBDEVFS_CLAIMINTERFACE, &interface);
+}
+
+int usb_device_release_interface(struct usb_device *device, unsigned int interface)
+{
+ return ioctl(device->fd, USBDEVFS_RELEASEINTERFACE, &interface);
+}
+
+int usb_device_connect_kernel_driver(struct usb_device *device,
+ unsigned int interface, int connect)
+{
+ struct usbdevfs_ioctl ctl;
+
+ ctl.ifno = interface;
+ ctl.ioctl_code = (connect ? USBDEVFS_CONNECT : USBDEVFS_DISCONNECT);
+ ctl.data = NULL;
+ return ioctl(device->fd, USBDEVFS_IOCTL, &ctl);
+}
+
+int usb_device_set_configuration(struct usb_device *device, int configuration)
+{
+ return ioctl(device->fd, USBDEVFS_SETCONFIGURATION, &configuration);
+}
+
+int usb_device_set_interface(struct usb_device *device, unsigned int interface,
+ unsigned int alt_setting)
+{
+ struct usbdevfs_setinterface ctl;
+
+ ctl.interface = interface;
+ ctl.altsetting = alt_setting;
+ return ioctl(device->fd, USBDEVFS_SETINTERFACE, &ctl);
+}
+
+int usb_device_control_transfer(struct usb_device *device,
+ int requestType,
+ int request,
+ int value,
+ int index,
+ void* buffer,
+ int length,
+ unsigned int timeout)
+{
+ struct usbdevfs_ctrltransfer ctrl;
+
+ // this usually requires read/write permission
+ if (!usb_device_reopen_writeable(device))
+ return -1;
+
+ memset(&ctrl, 0, sizeof(ctrl));
+ ctrl.bRequestType = requestType;
+ ctrl.bRequest = request;
+ ctrl.wValue = value;
+ ctrl.wIndex = index;
+ ctrl.wLength = length;
+ ctrl.data = buffer;
+ ctrl.timeout = timeout;
+ return ioctl(device->fd, USBDEVFS_CONTROL, &ctrl);
+}
+
+int usb_device_bulk_transfer(struct usb_device *device,
+ int endpoint,
+ void* buffer,
+ int length,
+ unsigned int timeout)
+{
+ struct usbdevfs_bulktransfer ctrl;
+
+ // need to limit request size to avoid EINVAL
+ if (length > MAX_USBFS_BUFFER_SIZE)
+ length = MAX_USBFS_BUFFER_SIZE;
+
+ memset(&ctrl, 0, sizeof(ctrl));
+ ctrl.ep = endpoint;
+ ctrl.len = length;
+ ctrl.data = buffer;
+ ctrl.timeout = timeout;
+ return ioctl(device->fd, USBDEVFS_BULK, &ctrl);
+}
+
+struct usb_request *usb_request_new(struct usb_device *dev,
+ const struct usb_endpoint_descriptor *ep_desc)
+{
+ struct usbdevfs_urb *urb = calloc(1, sizeof(struct usbdevfs_urb));
+ if (!urb)
+ return NULL;
+
+ if ((ep_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK)
+ urb->type = USBDEVFS_URB_TYPE_BULK;
+ else if ((ep_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)
+ urb->type = USBDEVFS_URB_TYPE_INTERRUPT;
+ else {
+ D("Unsupported endpoint type %d", ep_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK);
+ free(urb);
+ return NULL;
+ }
+ urb->endpoint = ep_desc->bEndpointAddress;
+
+ struct usb_request *req = calloc(1, sizeof(struct usb_request));
+ if (!req) {
+ free(urb);
+ return NULL;
+ }
+
+ req->dev = dev;
+ req->max_packet_size = __le16_to_cpu(ep_desc->wMaxPacketSize);
+ req->private_data = urb;
+ req->endpoint = urb->endpoint;
+ urb->usercontext = req;
+
+ return req;
+}
+
+void usb_request_free(struct usb_request *req)
+{
+ free(req->private_data);
+ free(req);
+}
+
+int usb_request_queue(struct usb_request *req)
+{
+ struct usbdevfs_urb *urb = (struct usbdevfs_urb*)req->private_data;
+ int res;
+
+ urb->status = -1;
+ urb->buffer = req->buffer;
+ // need to limit request size to avoid EINVAL
+ if (req->buffer_length > MAX_USBFS_BUFFER_SIZE)
+ urb->buffer_length = MAX_USBFS_BUFFER_SIZE;
+ else
+ urb->buffer_length = req->buffer_length;
+
+ do {
+ res = ioctl(req->dev->fd, USBDEVFS_SUBMITURB, urb);
+ } while((res < 0) && (errno == EINTR));
+
+ return res;
+}
+
+struct usb_request *usb_request_wait(struct usb_device *dev)
+{
+ struct usbdevfs_urb *urb = NULL;
+ struct usb_request *req = NULL;
+
+ while (1) {
+ int res = ioctl(dev->fd, USBDEVFS_REAPURB, &urb);
+ D("USBDEVFS_REAPURB returned %d\n", res);
+ if (res < 0) {
+ if(errno == EINTR) {
+ continue;
+ }
+ D("[ reap urb - error ]\n");
+ return NULL;
+ } else {
+ D("[ urb @%p status = %d, actual = %d ]\n",
+ urb, urb->status, urb->actual_length);
+ req = (struct usb_request*)urb->usercontext;
+ req->actual_length = urb->actual_length;
+ }
+ break;
+ }
+ return req;
+}
+
+int usb_request_cancel(struct usb_request *req)
+{
+ struct usbdevfs_urb *urb = ((struct usbdevfs_urb*)req->private_data);
+ return ioctl(req->dev->fd, USBDEVFS_DISCARDURB, urb);
+}
+
diff --git a/libutils/Android.mk b/libutils/Android.mk
new file mode 100644
index 0000000..8f829f3
--- /dev/null
+++ b/libutils/Android.mk
@@ -0,0 +1,113 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+commonSources:= \
+ BasicHashtable.cpp \
+ BlobCache.cpp \
+ CallStack.cpp \
+ FileMap.cpp \
+ JenkinsHash.cpp \
+ LinearAllocator.cpp \
+ LinearTransform.cpp \
+ Log.cpp \
+ NativeHandle.cpp \
+ Printer.cpp \
+ ProcessCallStack.cpp \
+ PropertyMap.cpp \
+ RefBase.cpp \
+ SharedBuffer.cpp \
+ Static.cpp \
+ StopWatch.cpp \
+ String8.cpp \
+ String16.cpp \
+ SystemClock.cpp \
+ Threads.cpp \
+ Timers.cpp \
+ Tokenizer.cpp \
+ Unicode.cpp \
+ VectorImpl.cpp \
+ misc.cpp \
+
+host_commonCflags := -DLIBUTILS_NATIVE=1 $(TOOL_CFLAGS) -Werror
+
+ifeq ($(HOST_OS),windows)
+ifeq ($(strip $(USE_CYGWIN),),)
+# Under MinGW, ctype.h doesn't need multi-byte support
+host_commonCflags += -DMB_CUR_MAX=1
+endif
+endif
+
+# For the host
+# =====================================================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES:= $(commonSources)
+ifeq ($(HOST_OS), linux)
+LOCAL_SRC_FILES += Looper.cpp
+endif
+ifeq ($(HOST_OS),darwin)
+LOCAL_CFLAGS += -Wno-unused-parameter
+endif
+LOCAL_MODULE:= libutils
+LOCAL_STATIC_LIBRARIES := liblog
+LOCAL_CFLAGS += $(host_commonCflags)
+LOCAL_MULTILIB := both
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+
+# For the device, static
+# =====================================================
+include $(CLEAR_VARS)
+
+
+# we have the common sources, plus some device-specific stuff
+LOCAL_SRC_FILES:= \
+ $(commonSources) \
+ Looper.cpp \
+ Trace.cpp
+
+ifeq ($(TARGET_ARCH),mips)
+LOCAL_CFLAGS += -DALIGN_DOUBLE
+endif
+LOCAL_CFLAGS += -Werror
+
+LOCAL_STATIC_LIBRARIES := \
+ libcutils
+
+LOCAL_SHARED_LIBRARIES := \
+ libbacktrace \
+ liblog \
+ libdl
+
+LOCAL_MODULE := libutils
+include $(BUILD_STATIC_LIBRARY)
+
+# For the device, shared
+# =====================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE:= libutils
+LOCAL_WHOLE_STATIC_LIBRARIES := libutils
+LOCAL_SHARED_LIBRARIES := \
+ libbacktrace \
+ libcutils \
+ libdl \
+ liblog
+LOCAL_CFLAGS := -Werror
+
+include $(BUILD_SHARED_LIBRARY)
+
+
+# Build the tests in the tests/ subdirectory.
+include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/libutils/BasicHashtable.cpp b/libutils/BasicHashtable.cpp
new file mode 100644
index 0000000..491d9e9
--- /dev/null
+++ b/libutils/BasicHashtable.cpp
@@ -0,0 +1,342 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "BasicHashtable"
+
+#include <math.h>
+
+#include <utils/Log.h>
+#include <utils/BasicHashtable.h>
+#include <utils/misc.h>
+
+namespace android {
+
+BasicHashtableImpl::BasicHashtableImpl(size_t entrySize, bool hasTrivialDestructor,
+ size_t minimumInitialCapacity, float loadFactor) :
+ mBucketSize(entrySize + sizeof(Bucket)), mHasTrivialDestructor(hasTrivialDestructor),
+ mLoadFactor(loadFactor), mSize(0),
+ mFilledBuckets(0), mBuckets(NULL) {
+ determineCapacity(minimumInitialCapacity, mLoadFactor, &mBucketCount, &mCapacity);
+}
+
+BasicHashtableImpl::BasicHashtableImpl(const BasicHashtableImpl& other) :
+ mBucketSize(other.mBucketSize), mHasTrivialDestructor(other.mHasTrivialDestructor),
+ mCapacity(other.mCapacity), mLoadFactor(other.mLoadFactor),
+ mSize(other.mSize), mFilledBuckets(other.mFilledBuckets),
+ mBucketCount(other.mBucketCount), mBuckets(other.mBuckets) {
+ if (mBuckets) {
+ SharedBuffer::bufferFromData(mBuckets)->acquire();
+ }
+}
+
+BasicHashtableImpl::~BasicHashtableImpl()
+{
+}
+
+void BasicHashtableImpl::dispose() {
+ if (mBuckets) {
+ releaseBuckets(mBuckets, mBucketCount);
+ }
+}
+
+void BasicHashtableImpl::clone() {
+ if (mBuckets) {
+ void* newBuckets = allocateBuckets(mBucketCount);
+ copyBuckets(mBuckets, newBuckets, mBucketCount);
+ releaseBuckets(mBuckets, mBucketCount);
+ mBuckets = newBuckets;
+ }
+}
+
+void BasicHashtableImpl::setTo(const BasicHashtableImpl& other) {
+ if (mBuckets) {
+ releaseBuckets(mBuckets, mBucketCount);
+ }
+
+ mCapacity = other.mCapacity;
+ mLoadFactor = other.mLoadFactor;
+ mSize = other.mSize;
+ mFilledBuckets = other.mFilledBuckets;
+ mBucketCount = other.mBucketCount;
+ mBuckets = other.mBuckets;
+
+ if (mBuckets) {
+ SharedBuffer::bufferFromData(mBuckets)->acquire();
+ }
+}
+
+void BasicHashtableImpl::clear() {
+ if (mBuckets) {
+ if (mFilledBuckets) {
+ SharedBuffer* sb = SharedBuffer::bufferFromData(mBuckets);
+ if (sb->onlyOwner()) {
+ destroyBuckets(mBuckets, mBucketCount);
+ for (size_t i = 0; i < mBucketCount; i++) {
+ Bucket& bucket = bucketAt(mBuckets, i);
+ bucket.cookie = 0;
+ }
+ } else {
+ releaseBuckets(mBuckets, mBucketCount);
+ mBuckets = NULL;
+ }
+ mFilledBuckets = 0;
+ }
+ mSize = 0;
+ }
+}
+
+ssize_t BasicHashtableImpl::next(ssize_t index) const {
+ if (mSize) {
+ while (size_t(++index) < mBucketCount) {
+ const Bucket& bucket = bucketAt(mBuckets, index);
+ if (bucket.cookie & Bucket::PRESENT) {
+ return index;
+ }
+ }
+ }
+ return -1;
+}
+
+ssize_t BasicHashtableImpl::find(ssize_t index, hash_t hash,
+ const void* __restrict__ key) const {
+ if (!mSize) {
+ return -1;
+ }
+
+ hash = trimHash(hash);
+ if (index < 0) {
+ index = chainStart(hash, mBucketCount);
+
+ const Bucket& bucket = bucketAt(mBuckets, size_t(index));
+ if (bucket.cookie & Bucket::PRESENT) {
+ if (compareBucketKey(bucket, key)) {
+ return index;
+ }
+ } else {
+ if (!(bucket.cookie & Bucket::COLLISION)) {
+ return -1;
+ }
+ }
+ }
+
+ size_t inc = chainIncrement(hash, mBucketCount);
+ for (;;) {
+ index = chainSeek(index, inc, mBucketCount);
+
+ const Bucket& bucket = bucketAt(mBuckets, size_t(index));
+ if (bucket.cookie & Bucket::PRESENT) {
+ if ((bucket.cookie & Bucket::HASH_MASK) == hash
+ && compareBucketKey(bucket, key)) {
+ return index;
+ }
+ }
+ if (!(bucket.cookie & Bucket::COLLISION)) {
+ return -1;
+ }
+ }
+}
+
+size_t BasicHashtableImpl::add(hash_t hash, const void* entry) {
+ if (!mBuckets) {
+ mBuckets = allocateBuckets(mBucketCount);
+ } else {
+ edit();
+ }
+
+ hash = trimHash(hash);
+ for (;;) {
+ size_t index = chainStart(hash, mBucketCount);
+ Bucket* bucket = &bucketAt(mBuckets, size_t(index));
+ if (bucket->cookie & Bucket::PRESENT) {
+ size_t inc = chainIncrement(hash, mBucketCount);
+ do {
+ bucket->cookie |= Bucket::COLLISION;
+ index = chainSeek(index, inc, mBucketCount);
+ bucket = &bucketAt(mBuckets, size_t(index));
+ } while (bucket->cookie & Bucket::PRESENT);
+ }
+
+ uint32_t collision = bucket->cookie & Bucket::COLLISION;
+ if (!collision) {
+ if (mFilledBuckets >= mCapacity) {
+ rehash(mCapacity * 2, mLoadFactor);
+ continue;
+ }
+ mFilledBuckets += 1;
+ }
+
+ bucket->cookie = collision | Bucket::PRESENT | hash;
+ mSize += 1;
+ initializeBucketEntry(*bucket, entry);
+ return index;
+ }
+}
+
+void BasicHashtableImpl::removeAt(size_t index) {
+ edit();
+
+ Bucket& bucket = bucketAt(mBuckets, index);
+ bucket.cookie &= ~Bucket::PRESENT;
+ if (!(bucket.cookie & Bucket::COLLISION)) {
+ mFilledBuckets -= 1;
+ }
+ mSize -= 1;
+ if (!mHasTrivialDestructor) {
+ destroyBucketEntry(bucket);
+ }
+}
+
+void BasicHashtableImpl::rehash(size_t minimumCapacity, float loadFactor) {
+ if (minimumCapacity < mSize) {
+ minimumCapacity = mSize;
+ }
+ size_t newBucketCount, newCapacity;
+ determineCapacity(minimumCapacity, loadFactor, &newBucketCount, &newCapacity);
+
+ if (newBucketCount != mBucketCount || newCapacity != mCapacity) {
+ if (mBuckets) {
+ void* newBuckets;
+ if (mSize) {
+ newBuckets = allocateBuckets(newBucketCount);
+ for (size_t i = 0; i < mBucketCount; i++) {
+ const Bucket& fromBucket = bucketAt(mBuckets, i);
+ if (fromBucket.cookie & Bucket::PRESENT) {
+ hash_t hash = fromBucket.cookie & Bucket::HASH_MASK;
+ size_t index = chainStart(hash, newBucketCount);
+ Bucket* toBucket = &bucketAt(newBuckets, size_t(index));
+ if (toBucket->cookie & Bucket::PRESENT) {
+ size_t inc = chainIncrement(hash, newBucketCount);
+ do {
+ toBucket->cookie |= Bucket::COLLISION;
+ index = chainSeek(index, inc, newBucketCount);
+ toBucket = &bucketAt(newBuckets, size_t(index));
+ } while (toBucket->cookie & Bucket::PRESENT);
+ }
+ toBucket->cookie = Bucket::PRESENT | hash;
+ initializeBucketEntry(*toBucket, fromBucket.entry);
+ }
+ }
+ } else {
+ newBuckets = NULL;
+ }
+ releaseBuckets(mBuckets, mBucketCount);
+ mBuckets = newBuckets;
+ mFilledBuckets = mSize;
+ }
+ mBucketCount = newBucketCount;
+ mCapacity = newCapacity;
+ }
+ mLoadFactor = loadFactor;
+}
+
+void* BasicHashtableImpl::allocateBuckets(size_t count) const {
+ size_t bytes = count * mBucketSize;
+ SharedBuffer* sb = SharedBuffer::alloc(bytes);
+ LOG_ALWAYS_FATAL_IF(!sb, "Could not allocate %u bytes for hashtable with %u buckets.",
+ uint32_t(bytes), uint32_t(count));
+ void* buckets = sb->data();
+ for (size_t i = 0; i < count; i++) {
+ Bucket& bucket = bucketAt(buckets, i);
+ bucket.cookie = 0;
+ }
+ return buckets;
+}
+
+void BasicHashtableImpl::releaseBuckets(void* __restrict__ buckets, size_t count) const {
+ SharedBuffer* sb = SharedBuffer::bufferFromData(buckets);
+ if (sb->release(SharedBuffer::eKeepStorage) == 1) {
+ destroyBuckets(buckets, count);
+ SharedBuffer::dealloc(sb);
+ }
+}
+
+void BasicHashtableImpl::destroyBuckets(void* __restrict__ buckets, size_t count) const {
+ if (!mHasTrivialDestructor) {
+ for (size_t i = 0; i < count; i++) {
+ Bucket& bucket = bucketAt(buckets, i);
+ if (bucket.cookie & Bucket::PRESENT) {
+ destroyBucketEntry(bucket);
+ }
+ }
+ }
+}
+
+void BasicHashtableImpl::copyBuckets(const void* __restrict__ fromBuckets,
+ void* __restrict__ toBuckets, size_t count) const {
+ for (size_t i = 0; i < count; i++) {
+ const Bucket& fromBucket = bucketAt(fromBuckets, i);
+ Bucket& toBucket = bucketAt(toBuckets, i);
+ toBucket.cookie = fromBucket.cookie;
+ if (fromBucket.cookie & Bucket::PRESENT) {
+ initializeBucketEntry(toBucket, fromBucket.entry);
+ }
+ }
+}
+
+// Table of 31-bit primes where each prime is no less than twice as large
+// as the previous one. Generated by "primes.py".
+static size_t PRIMES[] = {
+ 5,
+ 11,
+ 23,
+ 47,
+ 97,
+ 197,
+ 397,
+ 797,
+ 1597,
+ 3203,
+ 6421,
+ 12853,
+ 25717,
+ 51437,
+ 102877,
+ 205759,
+ 411527,
+ 823117,
+ 1646237,
+ 3292489,
+ 6584983,
+ 13169977,
+ 26339969,
+ 52679969,
+ 105359939,
+ 210719881,
+ 421439783,
+ 842879579,
+ 1685759167,
+ 0,
+};
+
+void BasicHashtableImpl::determineCapacity(size_t minimumCapacity, float loadFactor,
+ size_t* __restrict__ outBucketCount, size_t* __restrict__ outCapacity) {
+ LOG_ALWAYS_FATAL_IF(loadFactor <= 0.0f || loadFactor > 1.0f,
+ "Invalid load factor %0.3f. Must be in the range (0, 1].", loadFactor);
+
+ size_t count = ceilf(minimumCapacity / loadFactor) + 1;
+ size_t i = 0;
+ while (count > PRIMES[i] && i < NELEM(PRIMES)) {
+ i++;
+ }
+ count = PRIMES[i];
+ LOG_ALWAYS_FATAL_IF(!count, "Could not determine required number of buckets for "
+ "hashtable with minimum capacity %u and load factor %0.3f.",
+ uint32_t(minimumCapacity), loadFactor);
+ *outBucketCount = count;
+ *outCapacity = ceilf((count - 1) * loadFactor);
+}
+
+}; // namespace android
diff --git a/libutils/BlobCache.cpp b/libutils/BlobCache.cpp
new file mode 100644
index 0000000..0ea09cf
--- /dev/null
+++ b/libutils/BlobCache.cpp
@@ -0,0 +1,370 @@
+/*
+ ** Copyright 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.
+ */
+
+#define LOG_TAG "BlobCache"
+//#define LOG_NDEBUG 0
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <utils/BlobCache.h>
+#include <utils/Errors.h>
+#include <utils/Log.h>
+
+namespace android {
+
+// BlobCache::Header::mMagicNumber value
+static const uint32_t blobCacheMagic = ('_' << 24) + ('B' << 16) + ('b' << 8) + '$';
+
+// BlobCache::Header::mBlobCacheVersion value
+static const uint32_t blobCacheVersion = 2;
+
+// BlobCache::Header::mDeviceVersion value
+static const uint32_t blobCacheDeviceVersion = 1;
+
+BlobCache::BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize):
+ mMaxKeySize(maxKeySize),
+ mMaxValueSize(maxValueSize),
+ mMaxTotalSize(maxTotalSize),
+ mTotalSize(0) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+#ifdef _WIN32
+ srand(now);
+#else
+ mRandState[0] = (now >> 0) & 0xFFFF;
+ mRandState[1] = (now >> 16) & 0xFFFF;
+ mRandState[2] = (now >> 32) & 0xFFFF;
+#endif
+ ALOGV("initializing random seed using %lld", (unsigned long long)now);
+}
+
+void BlobCache::set(const void* key, size_t keySize, const void* value,
+ size_t valueSize) {
+ if (mMaxKeySize < keySize) {
+ ALOGV("set: not caching because the key is too large: %zu (limit: %zu)",
+ keySize, mMaxKeySize);
+ return;
+ }
+ if (mMaxValueSize < valueSize) {
+ ALOGV("set: not caching because the value is too large: %zu (limit: %zu)",
+ valueSize, mMaxValueSize);
+ return;
+ }
+ if (mMaxTotalSize < keySize + valueSize) {
+ ALOGV("set: not caching because the combined key/value size is too "
+ "large: %zu (limit: %zu)", keySize + valueSize, mMaxTotalSize);
+ return;
+ }
+ if (keySize == 0) {
+ ALOGW("set: not caching because keySize is 0");
+ return;
+ }
+ if (valueSize <= 0) {
+ ALOGW("set: not caching because valueSize is 0");
+ return;
+ }
+
+ sp<Blob> dummyKey(new Blob(key, keySize, false));
+ CacheEntry dummyEntry(dummyKey, NULL);
+
+ while (true) {
+ ssize_t index = mCacheEntries.indexOf(dummyEntry);
+ if (index < 0) {
+ // Create a new cache entry.
+ sp<Blob> keyBlob(new Blob(key, keySize, true));
+ sp<Blob> valueBlob(new Blob(value, valueSize, true));
+ size_t newTotalSize = mTotalSize + keySize + valueSize;
+ if (mMaxTotalSize < newTotalSize) {
+ if (isCleanable()) {
+ // Clean the cache and try again.
+ clean();
+ continue;
+ } else {
+ ALOGV("set: not caching new key/value pair because the "
+ "total cache size limit would be exceeded: %zu "
+ "(limit: %zu)",
+ keySize + valueSize, mMaxTotalSize);
+ break;
+ }
+ }
+ mCacheEntries.add(CacheEntry(keyBlob, valueBlob));
+ mTotalSize = newTotalSize;
+ ALOGV("set: created new cache entry with %zu byte key and %zu byte value",
+ keySize, valueSize);
+ } else {
+ // Update the existing cache entry.
+ sp<Blob> valueBlob(new Blob(value, valueSize, true));
+ sp<Blob> oldValueBlob(mCacheEntries[index].getValue());
+ size_t newTotalSize = mTotalSize + valueSize - oldValueBlob->getSize();
+ if (mMaxTotalSize < newTotalSize) {
+ if (isCleanable()) {
+ // Clean the cache and try again.
+ clean();
+ continue;
+ } else {
+ ALOGV("set: not caching new value because the total cache "
+ "size limit would be exceeded: %zu (limit: %zu)",
+ keySize + valueSize, mMaxTotalSize);
+ break;
+ }
+ }
+ mCacheEntries.editItemAt(index).setValue(valueBlob);
+ mTotalSize = newTotalSize;
+ ALOGV("set: updated existing cache entry with %zu byte key and %zu byte "
+ "value", keySize, valueSize);
+ }
+ break;
+ }
+}
+
+size_t BlobCache::get(const void* key, size_t keySize, void* value,
+ size_t valueSize) {
+ if (mMaxKeySize < keySize) {
+ ALOGV("get: not searching because the key is too large: %zu (limit %zu)",
+ keySize, mMaxKeySize);
+ return 0;
+ }
+ sp<Blob> dummyKey(new Blob(key, keySize, false));
+ CacheEntry dummyEntry(dummyKey, NULL);
+ ssize_t index = mCacheEntries.indexOf(dummyEntry);
+ if (index < 0) {
+ ALOGV("get: no cache entry found for key of size %zu", keySize);
+ return 0;
+ }
+
+ // The key was found. Return the value if the caller's buffer is large
+ // enough.
+ sp<Blob> valueBlob(mCacheEntries[index].getValue());
+ size_t valueBlobSize = valueBlob->getSize();
+ if (valueBlobSize <= valueSize) {
+ ALOGV("get: copying %zu bytes to caller's buffer", valueBlobSize);
+ memcpy(value, valueBlob->getData(), valueBlobSize);
+ } else {
+ ALOGV("get: caller's buffer is too small for value: %zu (needs %zu)",
+ valueSize, valueBlobSize);
+ }
+ return valueBlobSize;
+}
+
+static inline size_t align4(size_t size) {
+ return (size + 3) & ~3;
+}
+
+size_t BlobCache::getFlattenedSize() const {
+ size_t size = align4(sizeof(Header));
+ for (size_t i = 0; i < mCacheEntries.size(); i++) {
+ const CacheEntry& e(mCacheEntries[i]);
+ sp<Blob> keyBlob = e.getKey();
+ sp<Blob> valueBlob = e.getValue();
+ size += align4(sizeof(EntryHeader) + keyBlob->getSize() +
+ valueBlob->getSize());
+ }
+ return size;
+}
+
+status_t BlobCache::flatten(void* buffer, size_t size) const {
+ // Write the cache header
+ if (size < sizeof(Header)) {
+ ALOGE("flatten: not enough room for cache header");
+ return BAD_VALUE;
+ }
+ Header* header = reinterpret_cast<Header*>(buffer);
+ header->mMagicNumber = blobCacheMagic;
+ header->mBlobCacheVersion = blobCacheVersion;
+ header->mDeviceVersion = blobCacheDeviceVersion;
+ header->mNumEntries = mCacheEntries.size();
+
+ // Write cache entries
+ uint8_t* byteBuffer = reinterpret_cast<uint8_t*>(buffer);
+ off_t byteOffset = align4(sizeof(Header));
+ for (size_t i = 0; i < mCacheEntries.size(); i++) {
+ const CacheEntry& e(mCacheEntries[i]);
+ sp<Blob> keyBlob = e.getKey();
+ sp<Blob> valueBlob = e.getValue();
+ size_t keySize = keyBlob->getSize();
+ size_t valueSize = valueBlob->getSize();
+
+ size_t entrySize = sizeof(EntryHeader) + keySize + valueSize;
+ size_t totalSize = align4(entrySize);
+ if (byteOffset + totalSize > size) {
+ ALOGE("flatten: not enough room for cache entries");
+ return BAD_VALUE;
+ }
+
+ EntryHeader* eheader = reinterpret_cast<EntryHeader*>(
+ &byteBuffer[byteOffset]);
+ eheader->mKeySize = keySize;
+ eheader->mValueSize = valueSize;
+
+ memcpy(eheader->mData, keyBlob->getData(), keySize);
+ memcpy(eheader->mData + keySize, valueBlob->getData(), valueSize);
+
+ if (totalSize > entrySize) {
+ // We have padding bytes. Those will get written to storage, and contribute to the CRC,
+ // so make sure we zero-them to have reproducible results.
+ memset(eheader->mData + keySize + valueSize, 0, totalSize - entrySize);
+ }
+
+ byteOffset += totalSize;
+ }
+
+ return OK;
+}
+
+status_t BlobCache::unflatten(void const* buffer, size_t size) {
+ // All errors should result in the BlobCache being in an empty state.
+ mCacheEntries.clear();
+
+ // Read the cache header
+ if (size < sizeof(Header)) {
+ ALOGE("unflatten: not enough room for cache header");
+ return BAD_VALUE;
+ }
+ const Header* header = reinterpret_cast<const Header*>(buffer);
+ if (header->mMagicNumber != blobCacheMagic) {
+ ALOGE("unflatten: bad magic number: %" PRIu32, header->mMagicNumber);
+ return BAD_VALUE;
+ }
+ if (header->mBlobCacheVersion != blobCacheVersion ||
+ header->mDeviceVersion != blobCacheDeviceVersion) {
+ // We treat version mismatches as an empty cache.
+ return OK;
+ }
+
+ // Read cache entries
+ const uint8_t* byteBuffer = reinterpret_cast<const uint8_t*>(buffer);
+ off_t byteOffset = align4(sizeof(Header));
+ size_t numEntries = header->mNumEntries;
+ for (size_t i = 0; i < numEntries; i++) {
+ if (byteOffset + sizeof(EntryHeader) > size) {
+ mCacheEntries.clear();
+ ALOGE("unflatten: not enough room for cache entry headers");
+ return BAD_VALUE;
+ }
+
+ const EntryHeader* eheader = reinterpret_cast<const EntryHeader*>(
+ &byteBuffer[byteOffset]);
+ size_t keySize = eheader->mKeySize;
+ size_t valueSize = eheader->mValueSize;
+ size_t entrySize = sizeof(EntryHeader) + keySize + valueSize;
+
+ size_t totalSize = align4(entrySize);
+ if (byteOffset + totalSize > size) {
+ mCacheEntries.clear();
+ ALOGE("unflatten: not enough room for cache entry headers");
+ return BAD_VALUE;
+ }
+
+ const uint8_t* data = eheader->mData;
+ set(data, keySize, data + keySize, valueSize);
+
+ byteOffset += totalSize;
+ }
+
+ return OK;
+}
+
+long int BlobCache::blob_random() {
+#ifdef _WIN32
+ return rand();
+#else
+ return nrand48(mRandState);
+#endif
+}
+
+void BlobCache::clean() {
+ // Remove a random cache entry until the total cache size gets below half
+ // the maximum total cache size.
+ while (mTotalSize > mMaxTotalSize / 2) {
+ size_t i = size_t(blob_random() % (mCacheEntries.size()));
+ const CacheEntry& entry(mCacheEntries[i]);
+ mTotalSize -= entry.getKey()->getSize() + entry.getValue()->getSize();
+ mCacheEntries.removeAt(i);
+ }
+}
+
+bool BlobCache::isCleanable() const {
+ return mTotalSize > mMaxTotalSize / 2;
+}
+
+BlobCache::Blob::Blob(const void* data, size_t size, bool copyData):
+ mData(copyData ? malloc(size) : data),
+ mSize(size),
+ mOwnsData(copyData) {
+ if (data != NULL && copyData) {
+ memcpy(const_cast<void*>(mData), data, size);
+ }
+}
+
+BlobCache::Blob::~Blob() {
+ if (mOwnsData) {
+ free(const_cast<void*>(mData));
+ }
+}
+
+bool BlobCache::Blob::operator<(const Blob& rhs) const {
+ if (mSize == rhs.mSize) {
+ return memcmp(mData, rhs.mData, mSize) < 0;
+ } else {
+ return mSize < rhs.mSize;
+ }
+}
+
+const void* BlobCache::Blob::getData() const {
+ return mData;
+}
+
+size_t BlobCache::Blob::getSize() const {
+ return mSize;
+}
+
+BlobCache::CacheEntry::CacheEntry() {
+}
+
+BlobCache::CacheEntry::CacheEntry(const sp<Blob>& key, const sp<Blob>& value):
+ mKey(key),
+ mValue(value) {
+}
+
+BlobCache::CacheEntry::CacheEntry(const CacheEntry& ce):
+ mKey(ce.mKey),
+ mValue(ce.mValue) {
+}
+
+bool BlobCache::CacheEntry::operator<(const CacheEntry& rhs) const {
+ return *mKey < *rhs.mKey;
+}
+
+const BlobCache::CacheEntry& BlobCache::CacheEntry::operator=(const CacheEntry& rhs) {
+ mKey = rhs.mKey;
+ mValue = rhs.mValue;
+ return *this;
+}
+
+sp<BlobCache::Blob> BlobCache::CacheEntry::getKey() const {
+ return mKey;
+}
+
+sp<BlobCache::Blob> BlobCache::CacheEntry::getValue() const {
+ return mValue;
+}
+
+void BlobCache::CacheEntry::setValue(const sp<Blob>& value) {
+ mValue = value;
+}
+
+} // namespace android
diff --git a/libutils/CallStack.cpp b/libutils/CallStack.cpp
new file mode 100644
index 0000000..0bfb520
--- /dev/null
+++ b/libutils/CallStack.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2007 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 "CallStack"
+
+#include <utils/CallStack.h>
+#include <utils/Printer.h>
+#include <utils/Errors.h>
+#include <utils/Log.h>
+#include <UniquePtr.h>
+
+#include <backtrace/Backtrace.h>
+
+namespace android {
+
+CallStack::CallStack() {
+}
+
+CallStack::CallStack(const char* logtag, int32_t ignoreDepth) {
+ this->update(ignoreDepth+1);
+ this->log(logtag);
+}
+
+CallStack::~CallStack() {
+}
+
+void CallStack::update(int32_t ignoreDepth, pid_t tid) {
+ mFrameLines.clear();
+
+ UniquePtr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid));
+ if (!backtrace->Unwind(ignoreDepth)) {
+ ALOGW("%s: Failed to unwind callstack.", __FUNCTION__);
+ }
+ for (size_t i = 0; i < backtrace->NumFrames(); i++) {
+ mFrameLines.push_back(String8(backtrace->FormatFrameData(i).c_str()));
+ }
+}
+
+void CallStack::log(const char* logtag, android_LogPriority priority, const char* prefix) const {
+ LogPrinter printer(logtag, priority, prefix, /*ignoreBlankLines*/false);
+ print(printer);
+}
+
+void CallStack::dump(int fd, int indent, const char* prefix) const {
+ FdPrinter printer(fd, indent, prefix);
+ print(printer);
+}
+
+String8 CallStack::toString(const char* prefix) const {
+ String8 str;
+
+ String8Printer printer(&str, prefix);
+ print(printer);
+
+ return str;
+}
+
+void CallStack::print(Printer& printer) const {
+ for (size_t i = 0; i < mFrameLines.size(); i++) {
+ printer.printLine(mFrameLines[i]);
+ }
+}
+
+}; // namespace android
diff --git a/libutils/CleanSpec.mk b/libutils/CleanSpec.mk
new file mode 100644
index 0000000..c3c5651
--- /dev/null
+++ b/libutils/CleanSpec.mk
@@ -0,0 +1,51 @@
+# 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.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list. These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+# $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT_DIR) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list. E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+
+# For example:
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+$(call add-clean-step, rm -rf $(HOST_OUT)/obj/STATIC_LIBRARIES/libutils_intermediates/import_includes)
+$(call add-clean-step, rm -rf $(HOST_OUT)/obj/STATIC_LIBRARIES/lib64utils_intermediates/import_includes)
diff --git a/libutils/FileMap.cpp b/libutils/FileMap.cpp
new file mode 100644
index 0000000..91e45d8
--- /dev/null
+++ b/libutils/FileMap.cpp
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2006 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.
+ */
+
+//
+// Shared file mapping class.
+//
+
+#define LOG_TAG "filemap"
+
+#include <utils/FileMap.h>
+#include <utils/Log.h>
+
+#if defined(__MINGW32__) && !defined(__USE_MINGW_ANSI_STDIO)
+# define PRId32 "I32d"
+# define PRIx32 "I32x"
+# define PRId64 "I64d"
+#else
+#include <inttypes.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+
+#if !defined(__MINGW32__)
+#include <sys/mman.h>
+#endif
+
+#include <string.h>
+#include <memory.h>
+#include <errno.h>
+#include <assert.h>
+
+using namespace android;
+
+/*static*/ long FileMap::mPageSize = -1;
+
+// Constructor. Create an empty object.
+FileMap::FileMap(void)
+ : mFileName(NULL), mBasePtr(NULL), mBaseLength(0),
+ mDataPtr(NULL), mDataLength(0)
+{
+}
+
+// Destructor.
+FileMap::~FileMap(void)
+{
+ if (mFileName != NULL) {
+ free(mFileName);
+ }
+#if defined(__MINGW32__)
+ if (mBasePtr && UnmapViewOfFile(mBasePtr) == 0) {
+ ALOGD("UnmapViewOfFile(%p) failed, error = %" PRId32 "\n", mBasePtr,
+ GetLastError() );
+ }
+ if (mFileMapping != INVALID_HANDLE_VALUE) {
+ CloseHandle(mFileMapping);
+ }
+#else
+ if (mBasePtr && munmap(mBasePtr, mBaseLength) != 0) {
+ ALOGD("munmap(%p, %zu) failed\n", mBasePtr, mBaseLength);
+ }
+#endif
+}
+
+
+// Create a new mapping on an open file.
+//
+// Closing the file descriptor does not unmap the pages, so we don't
+// claim ownership of the fd.
+//
+// Returns "false" on failure.
+bool FileMap::create(const char* origFileName, int fd, off64_t offset, size_t length,
+ bool readOnly)
+{
+#if defined(__MINGW32__)
+ int adjust;
+ off64_t adjOffset;
+ size_t adjLength;
+
+ if (mPageSize == -1) {
+ SYSTEM_INFO si;
+
+ GetSystemInfo( &si );
+ mPageSize = si.dwAllocationGranularity;
+ }
+
+ DWORD protect = readOnly ? PAGE_READONLY : PAGE_READWRITE;
+
+ mFileHandle = (HANDLE) _get_osfhandle(fd);
+ mFileMapping = CreateFileMapping( mFileHandle, NULL, protect, 0, 0, NULL);
+ if (mFileMapping == NULL) {
+ ALOGE("CreateFileMapping(%p, %" PRIx32 ") failed with error %" PRId32 "\n",
+ mFileHandle, protect, GetLastError() );
+ return false;
+ }
+
+ adjust = offset % mPageSize;
+ adjOffset = offset - adjust;
+ adjLength = length + adjust;
+
+ mBasePtr = MapViewOfFile( mFileMapping,
+ readOnly ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS,
+ 0,
+ (DWORD)(adjOffset),
+ adjLength );
+ if (mBasePtr == NULL) {
+ ALOGE("MapViewOfFile(%" PRId64 ", %zu) failed with error %" PRId32 "\n",
+ adjOffset, adjLength, GetLastError() );
+ CloseHandle(mFileMapping);
+ mFileMapping = INVALID_HANDLE_VALUE;
+ return false;
+ }
+#else // !defined(__MINGW32__)
+ int prot, flags, adjust;
+ off64_t adjOffset;
+ size_t adjLength;
+
+ void* ptr;
+
+ assert(fd >= 0);
+ assert(offset >= 0);
+ assert(length > 0);
+
+ // init on first use
+ if (mPageSize == -1) {
+ mPageSize = sysconf(_SC_PAGESIZE);
+ if (mPageSize == -1) {
+ ALOGE("could not get _SC_PAGESIZE\n");
+ return false;
+ }
+ }
+
+ adjust = offset % mPageSize;
+ adjOffset = offset - adjust;
+ adjLength = length + adjust;
+
+ flags = MAP_SHARED;
+ prot = PROT_READ;
+ if (!readOnly)
+ prot |= PROT_WRITE;
+
+ ptr = mmap(NULL, adjLength, prot, flags, fd, adjOffset);
+ if (ptr == MAP_FAILED) {
+ ALOGE("mmap(%lld,%zu) failed: %s\n",
+ (long long)adjOffset, adjLength, strerror(errno));
+ return false;
+ }
+ mBasePtr = ptr;
+#endif // !defined(__MINGW32__)
+
+ mFileName = origFileName != NULL ? strdup(origFileName) : NULL;
+ mBaseLength = adjLength;
+ mDataOffset = offset;
+ mDataPtr = (char*) mBasePtr + adjust;
+ mDataLength = length;
+
+ assert(mBasePtr != NULL);
+
+ ALOGV("MAP: base %p/%zu data %p/%zu\n",
+ mBasePtr, mBaseLength, mDataPtr, mDataLength);
+
+ return true;
+}
+
+// Provide guidance to the system.
+#if !defined(_WIN32)
+int FileMap::advise(MapAdvice advice)
+{
+ int cc, sysAdvice;
+
+ switch (advice) {
+ case NORMAL: sysAdvice = MADV_NORMAL; break;
+ case RANDOM: sysAdvice = MADV_RANDOM; break;
+ case SEQUENTIAL: sysAdvice = MADV_SEQUENTIAL; break;
+ case WILLNEED: sysAdvice = MADV_WILLNEED; break;
+ case DONTNEED: sysAdvice = MADV_DONTNEED; break;
+ default:
+ assert(false);
+ return -1;
+ }
+
+ cc = madvise(mBasePtr, mBaseLength, sysAdvice);
+ if (cc != 0)
+ ALOGW("madvise(%d) failed: %s\n", sysAdvice, strerror(errno));
+ return cc;
+}
+
+#else
+int FileMap::advise(MapAdvice /* advice */)
+{
+ return -1;
+}
+#endif
diff --git a/libutils/JenkinsHash.cpp b/libutils/JenkinsHash.cpp
new file mode 100644
index 0000000..52c9bb7
--- /dev/null
+++ b/libutils/JenkinsHash.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Implementation of Jenkins one-at-a-time hash function. These choices are
+ * optimized for code size and portability, rather than raw speed. But speed
+ * should still be quite good.
+ **/
+
+#include <utils/JenkinsHash.h>
+
+namespace android {
+
+hash_t JenkinsHashWhiten(uint32_t hash) {
+ hash += (hash << 3);
+ hash ^= (hash >> 11);
+ hash += (hash << 15);
+ return hash;
+}
+
+uint32_t JenkinsHashMixBytes(uint32_t hash, const uint8_t* bytes, size_t size) {
+ hash = JenkinsHashMix(hash, (uint32_t)size);
+ size_t i;
+ for (i = 0; i < (size & -4); i += 4) {
+ uint32_t data = bytes[i] | (bytes[i+1] << 8) | (bytes[i+2] << 16) | (bytes[i+3] << 24);
+ hash = JenkinsHashMix(hash, data);
+ }
+ if (size & 3) {
+ uint32_t data = bytes[i];
+ data |= ((size & 3) > 1) ? (bytes[i+1] << 8) : 0;
+ data |= ((size & 3) > 2) ? (bytes[i+2] << 16) : 0;
+ hash = JenkinsHashMix(hash, data);
+ }
+ return hash;
+}
+
+uint32_t JenkinsHashMixShorts(uint32_t hash, const uint16_t* shorts, size_t size) {
+ hash = JenkinsHashMix(hash, (uint32_t)size);
+ size_t i;
+ for (i = 0; i < (size & -2); i += 2) {
+ uint32_t data = shorts[i] | (shorts[i+1] << 16);
+ hash = JenkinsHashMix(hash, data);
+ }
+ if (size & 1) {
+ uint32_t data = shorts[i];
+ hash = JenkinsHashMix(hash, data);
+ }
+ return hash;
+}
+
+}
+
diff --git a/libutils/LinearAllocator.cpp b/libutils/LinearAllocator.cpp
new file mode 100644
index 0000000..8b90696
--- /dev/null
+++ b/libutils/LinearAllocator.cpp
@@ -0,0 +1,227 @@
+/*
+ * Copyright 2012, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define LOG_TAG "LinearAllocator"
+#define LOG_NDEBUG 1
+
+#include <stdlib.h>
+#include <utils/LinearAllocator.h>
+#include <utils/Log.h>
+
+
+// The ideal size of a page allocation (these need to be multiples of 8)
+#define INITIAL_PAGE_SIZE ((size_t)4096) // 4kb
+#define MAX_PAGE_SIZE ((size_t)131072) // 128kb
+
+// The maximum amount of wasted space we can have per page
+// Allocations exceeding this will have their own dedicated page
+// If this is too low, we will malloc too much
+// Too high, and we may waste too much space
+// Must be smaller than INITIAL_PAGE_SIZE
+#define MAX_WASTE_SIZE ((size_t)1024)
+
+#if ALIGN_DOUBLE
+#define ALIGN_SZ (sizeof(double))
+#else
+#define ALIGN_SZ (sizeof(int))
+#endif
+
+#define ALIGN(x) ((x + ALIGN_SZ - 1 ) & ~(ALIGN_SZ - 1))
+#define ALIGN_PTR(p) ((void*)(ALIGN((size_t)p)))
+
+#if LOG_NDEBUG
+#define ADD_ALLOCATION(size)
+#define RM_ALLOCATION(size)
+#else
+#include <utils/Thread.h>
+#include <utils/Timers.h>
+static size_t s_totalAllocations = 0;
+static nsecs_t s_nextLog = 0;
+static android::Mutex s_mutex;
+
+static void _logUsageLocked() {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ if (now > s_nextLog) {
+ s_nextLog = now + milliseconds_to_nanoseconds(10);
+ ALOGV("Total memory usage: %zu kb", s_totalAllocations / 1024);
+ }
+}
+
+static void _addAllocation(size_t size) {
+ android::AutoMutex lock(s_mutex);
+ s_totalAllocations += size;
+ _logUsageLocked();
+}
+
+#define ADD_ALLOCATION(size) _addAllocation(size);
+#define RM_ALLOCATION(size) _addAllocation(-size);
+#endif
+
+#define min(x,y) (((x) < (y)) ? (x) : (y))
+
+namespace android {
+
+class LinearAllocator::Page {
+public:
+ Page* next() { return mNextPage; }
+ void setNext(Page* next) { mNextPage = next; }
+
+ Page()
+ : mNextPage(0)
+ {}
+
+ void* operator new(size_t /*size*/, void* buf) { return buf; }
+
+ void* start() {
+ return (void*) (((size_t)this) + sizeof(Page));
+ }
+
+ void* end(int pageSize) {
+ return (void*) (((size_t)start()) + pageSize);
+ }
+
+private:
+ Page(const Page& /*other*/) {}
+ Page* mNextPage;
+};
+
+LinearAllocator::LinearAllocator()
+ : mPageSize(INITIAL_PAGE_SIZE)
+ , mMaxAllocSize(MAX_WASTE_SIZE)
+ , mNext(0)
+ , mCurrentPage(0)
+ , mPages(0)
+ , mTotalAllocated(0)
+ , mWastedSpace(0)
+ , mPageCount(0)
+ , mDedicatedPageCount(0) {}
+
+LinearAllocator::~LinearAllocator(void) {
+ Page* p = mPages;
+ while (p) {
+ Page* next = p->next();
+ p->~Page();
+ free(p);
+ RM_ALLOCATION(mPageSize);
+ p = next;
+ }
+}
+
+void* LinearAllocator::start(Page* p) {
+ return ALIGN_PTR(((size_t*)p) + sizeof(Page));
+}
+
+void* LinearAllocator::end(Page* p) {
+ return ((char*)p) + mPageSize;
+}
+
+bool LinearAllocator::fitsInCurrentPage(size_t size) {
+ return mNext && ((char*)mNext + size) <= end(mCurrentPage);
+}
+
+void LinearAllocator::ensureNext(size_t size) {
+ if (fitsInCurrentPage(size)) return;
+
+ if (mCurrentPage && mPageSize < MAX_PAGE_SIZE) {
+ mPageSize = min(MAX_PAGE_SIZE, mPageSize * 2);
+ mPageSize = ALIGN(mPageSize);
+ }
+ mWastedSpace += mPageSize;
+ Page* p = newPage(mPageSize);
+ if (mCurrentPage) {
+ mCurrentPage->setNext(p);
+ }
+ mCurrentPage = p;
+ if (!mPages) {
+ mPages = mCurrentPage;
+ }
+ mNext = start(mCurrentPage);
+}
+
+void* LinearAllocator::alloc(size_t size) {
+ size = ALIGN(size);
+ if (size > mMaxAllocSize && !fitsInCurrentPage(size)) {
+ ALOGV("Exceeded max size %zu > %zu", size, mMaxAllocSize);
+ // Allocation is too large, create a dedicated page for the allocation
+ Page* page = newPage(size);
+ mDedicatedPageCount++;
+ page->setNext(mPages);
+ mPages = page;
+ if (!mCurrentPage)
+ mCurrentPage = mPages;
+ return start(page);
+ }
+ ensureNext(size);
+ void* ptr = mNext;
+ mNext = ((char*)mNext) + size;
+ mWastedSpace -= size;
+ return ptr;
+}
+
+void LinearAllocator::rewindIfLastAlloc(void* ptr, size_t allocSize) {
+ // Don't bother rewinding across pages
+ allocSize = ALIGN(allocSize);
+ if (ptr >= start(mCurrentPage) && ptr < end(mCurrentPage)
+ && ptr == ((char*)mNext - allocSize)) {
+ mTotalAllocated -= allocSize;
+ mWastedSpace += allocSize;
+ mNext = ptr;
+ }
+}
+
+LinearAllocator::Page* LinearAllocator::newPage(size_t pageSize) {
+ pageSize = ALIGN(pageSize + sizeof(LinearAllocator::Page));
+ ADD_ALLOCATION(pageSize);
+ mTotalAllocated += pageSize;
+ mPageCount++;
+ void* buf = malloc(pageSize);
+ return new (buf) Page();
+}
+
+static const char* toSize(size_t value, float& result) {
+ if (value < 2000) {
+ result = value;
+ return "B";
+ }
+ if (value < 2000000) {
+ result = value / 1024.0f;
+ return "KB";
+ }
+ result = value / 1048576.0f;
+ return "MB";
+}
+
+void LinearAllocator::dumpMemoryStats(const char* prefix) {
+ float prettySize;
+ const char* prettySuffix;
+ prettySuffix = toSize(mTotalAllocated, prettySize);
+ ALOGD("%sTotal allocated: %.2f%s", prefix, prettySize, prettySuffix);
+ prettySuffix = toSize(mWastedSpace, prettySize);
+ ALOGD("%sWasted space: %.2f%s (%.1f%%)", prefix, prettySize, prettySuffix,
+ (float) mWastedSpace / (float) mTotalAllocated * 100.0f);
+ ALOGD("%sPages %zu (dedicated %zu)", prefix, mPageCount, mDedicatedPageCount);
+}
+
+}; // namespace android
diff --git a/libutils/LinearTransform.cpp b/libutils/LinearTransform.cpp
new file mode 100644
index 0000000..b7d28d4
--- /dev/null
+++ b/libutils/LinearTransform.cpp
@@ -0,0 +1,265 @@
+/*
+ * 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.
+ */
+
+#define __STDC_LIMIT_MACROS
+
+#include <assert.h>
+#include <stdint.h>
+
+#include <utils/LinearTransform.h>
+
+namespace android {
+
+template<class T> static inline T ABS(T x) { return (x < 0) ? -x : x; }
+
+// Static math methods involving linear transformations
+static bool scale_u64_to_u64(
+ uint64_t val,
+ uint32_t N,
+ uint32_t D,
+ uint64_t* res,
+ bool round_up_not_down) {
+ uint64_t tmp1, tmp2;
+ uint32_t r;
+
+ assert(res);
+ assert(D);
+
+ // Let U32(X) denote a uint32_t containing the upper 32 bits of a 64 bit
+ // integer X.
+ // Let L32(X) denote a uint32_t containing the lower 32 bits of a 64 bit
+ // integer X.
+ // Let X[A, B] with A <= B denote bits A through B of the integer X.
+ // Let (A | B) denote the concatination of two 32 bit ints, A and B.
+ // IOW X = (A | B) => U32(X) == A && L32(X) == B
+ //
+ // compute M = val * N (a 96 bit int)
+ // ---------------------------------
+ // tmp2 = U32(val) * N (a 64 bit int)
+ // tmp1 = L32(val) * N (a 64 bit int)
+ // which means
+ // M = val * N = (tmp2 << 32) + tmp1
+ tmp2 = (val >> 32) * N;
+ tmp1 = (val & UINT32_MAX) * N;
+
+ // compute M[32, 95]
+ // tmp2 = tmp2 + U32(tmp1)
+ // = (U32(val) * N) + U32(L32(val) * N)
+ // = M[32, 95]
+ tmp2 += tmp1 >> 32;
+
+ // if M[64, 95] >= D, then M/D has bits > 63 set and we have
+ // an overflow.
+ if ((tmp2 >> 32) >= D) {
+ *res = UINT64_MAX;
+ return false;
+ }
+
+ // Divide. Going in we know
+ // tmp2 = M[32, 95]
+ // U32(tmp2) < D
+ r = tmp2 % D;
+ tmp2 /= D;
+
+ // At this point
+ // tmp1 = L32(val) * N
+ // tmp2 = M[32, 95] / D
+ // = (M / D)[32, 95]
+ // r = M[32, 95] % D
+ // U32(tmp2) = 0
+ //
+ // compute tmp1 = (r | M[0, 31])
+ tmp1 = (tmp1 & UINT32_MAX) | ((uint64_t)r << 32);
+
+ // Divide again. Keep the remainder around in order to round properly.
+ r = tmp1 % D;
+ tmp1 /= D;
+
+ // At this point
+ // tmp2 = (M / D)[32, 95]
+ // tmp1 = (M / D)[ 0, 31]
+ // r = M % D
+ // U32(tmp1) = 0
+ // U32(tmp2) = 0
+
+ // Pack the result and deal with the round-up case (As well as the
+ // remote possiblility over overflow in such a case).
+ *res = (tmp2 << 32) | tmp1;
+ if (r && round_up_not_down) {
+ ++(*res);
+ if (!(*res)) {
+ *res = UINT64_MAX;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool linear_transform_s64_to_s64(
+ int64_t val,
+ int64_t basis1,
+ int32_t N,
+ uint32_t D,
+ bool invert_frac,
+ int64_t basis2,
+ int64_t* out) {
+ uint64_t scaled, res;
+ uint64_t abs_val;
+ bool is_neg;
+
+ if (!out)
+ return false;
+
+ // Compute abs(val - basis_64). Keep track of whether or not this delta
+ // will be negative after the scale opertaion.
+ if (val < basis1) {
+ is_neg = true;
+ abs_val = basis1 - val;
+ } else {
+ is_neg = false;
+ abs_val = val - basis1;
+ }
+
+ if (N < 0)
+ is_neg = !is_neg;
+
+ if (!scale_u64_to_u64(abs_val,
+ invert_frac ? D : ABS(N),
+ invert_frac ? ABS(N) : D,
+ &scaled,
+ is_neg))
+ return false; // overflow/undeflow
+
+ // if scaled is >= 0x8000<etc>, then we are going to overflow or
+ // underflow unless ABS(basis2) is large enough to pull us back into the
+ // non-overflow/underflow region.
+ if (scaled & INT64_MIN) {
+ if (is_neg && (basis2 < 0))
+ return false; // certain underflow
+
+ if (!is_neg && (basis2 >= 0))
+ return false; // certain overflow
+
+ if (ABS(basis2) <= static_cast<int64_t>(scaled & INT64_MAX))
+ return false; // not enough
+
+ // Looks like we are OK
+ *out = (is_neg ? (-scaled) : scaled) + basis2;
+ } else {
+ // Scaled fits within signed bounds, so we just need to check for
+ // over/underflow for two signed integers. Basically, if both scaled
+ // and basis2 have the same sign bit, and the result has a different
+ // sign bit, then we have under/overflow. An easy way to compute this
+ // is
+ // (scaled_signbit XNOR basis_signbit) &&
+ // (scaled_signbit XOR res_signbit)
+ // ==
+ // (scaled_signbit XOR basis_signbit XOR 1) &&
+ // (scaled_signbit XOR res_signbit)
+
+ if (is_neg)
+ scaled = -scaled;
+ res = scaled + basis2;
+
+ if ((scaled ^ basis2 ^ INT64_MIN) & (scaled ^ res) & INT64_MIN)
+ return false;
+
+ *out = res;
+ }
+
+ return true;
+}
+
+bool LinearTransform::doForwardTransform(int64_t a_in, int64_t* b_out) const {
+ if (0 == a_to_b_denom)
+ return false;
+
+ return linear_transform_s64_to_s64(a_in,
+ a_zero,
+ a_to_b_numer,
+ a_to_b_denom,
+ false,
+ b_zero,
+ b_out);
+}
+
+bool LinearTransform::doReverseTransform(int64_t b_in, int64_t* a_out) const {
+ if (0 == a_to_b_numer)
+ return false;
+
+ return linear_transform_s64_to_s64(b_in,
+ b_zero,
+ a_to_b_numer,
+ a_to_b_denom,
+ true,
+ a_zero,
+ a_out);
+}
+
+template <class T> void LinearTransform::reduce(T* N, T* D) {
+ T a, b;
+ if (!N || !D || !(*D)) {
+ assert(false);
+ return;
+ }
+
+ a = *N;
+ b = *D;
+
+ if (a == 0) {
+ *D = 1;
+ return;
+ }
+
+ // This implements Euclid's method to find GCD.
+ if (a < b) {
+ T tmp = a;
+ a = b;
+ b = tmp;
+ }
+
+ while (1) {
+ // a is now the greater of the two.
+ const T remainder = a % b;
+ if (remainder == 0) {
+ *N /= b;
+ *D /= b;
+ return;
+ }
+ // by swapping remainder and b, we are guaranteeing that a is
+ // still the greater of the two upon entrance to the loop.
+ a = b;
+ b = remainder;
+ }
+};
+
+template void LinearTransform::reduce<uint64_t>(uint64_t* N, uint64_t* D);
+template void LinearTransform::reduce<uint32_t>(uint32_t* N, uint32_t* D);
+
+void LinearTransform::reduce(int32_t* N, uint32_t* D) {
+ if (N && D && *D) {
+ if (*N < 0) {
+ *N = -(*N);
+ reduce(reinterpret_cast<uint32_t*>(N), D);
+ *N = -(*N);
+ } else {
+ reduce(reinterpret_cast<uint32_t*>(N), D);
+ }
+ }
+}
+
+} // namespace android
diff --git a/libutils/Log.cpp b/libutils/Log.cpp
new file mode 100644
index 0000000..bffb56e
--- /dev/null
+++ b/libutils/Log.cpp
@@ -0,0 +1,37 @@
+/*
+ * 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 LOG_TAG "Log"
+
+#include <utils/Log.h>
+#include <utils/Timers.h>
+
+namespace android {
+
+LogIfSlow::LogIfSlow(const char* tag, android_LogPriority priority,
+ int timeoutMillis, const char* message) :
+ mTag(tag), mPriority(priority), mTimeoutMillis(timeoutMillis), mMessage(message),
+ mStart(systemTime(SYSTEM_TIME_BOOTTIME)) {
+}
+
+LogIfSlow::~LogIfSlow() {
+ int durationMillis = nanoseconds_to_milliseconds(systemTime(SYSTEM_TIME_BOOTTIME) - mStart);
+ if (durationMillis > mTimeoutMillis) {
+ LOG_PRI(mPriority, mTag, "%s: %dms", mMessage, durationMillis);
+ }
+}
+
+} // namespace android
diff --git a/libutils/Looper.cpp b/libutils/Looper.cpp
new file mode 100644
index 0000000..20c1e92
--- /dev/null
+++ b/libutils/Looper.cpp
@@ -0,0 +1,575 @@
+//
+// Copyright 2010 The Android Open Source Project
+//
+// A looper implementation based on epoll().
+//
+#define LOG_TAG "Looper"
+
+//#define LOG_NDEBUG 0
+
+// Debugs poll and wake interactions.
+#define DEBUG_POLL_AND_WAKE 0
+
+// Debugs callback registration and invocation.
+#define DEBUG_CALLBACKS 0
+
+#include <cutils/log.h>
+#include <utils/Looper.h>
+#include <utils/Timers.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <string.h>
+#include <unistd.h>
+
+
+namespace android {
+
+// --- WeakMessageHandler ---
+
+WeakMessageHandler::WeakMessageHandler(const wp<MessageHandler>& handler) :
+ mHandler(handler) {
+}
+
+WeakMessageHandler::~WeakMessageHandler() {
+}
+
+void WeakMessageHandler::handleMessage(const Message& message) {
+ sp<MessageHandler> handler = mHandler.promote();
+ if (handler != NULL) {
+ handler->handleMessage(message);
+ }
+}
+
+
+// --- SimpleLooperCallback ---
+
+SimpleLooperCallback::SimpleLooperCallback(Looper_callbackFunc callback) :
+ mCallback(callback) {
+}
+
+SimpleLooperCallback::~SimpleLooperCallback() {
+}
+
+int SimpleLooperCallback::handleEvent(int fd, int events, void* data) {
+ return mCallback(fd, events, data);
+}
+
+
+// --- Looper ---
+
+// Hint for number of file descriptors to be associated with the epoll instance.
+static const int EPOLL_SIZE_HINT = 8;
+
+// Maximum number of file descriptors for which to retrieve poll events each iteration.
+static const int EPOLL_MAX_EVENTS = 16;
+
+static pthread_once_t gTLSOnce = PTHREAD_ONCE_INIT;
+static pthread_key_t gTLSKey = 0;
+
+Looper::Looper(bool allowNonCallbacks) :
+ mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
+ mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
+ int wakeFds[2];
+ int result = pipe(wakeFds);
+ LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe: %s", strerror(errno));
+
+ mWakeReadPipeFd = wakeFds[0];
+ mWakeWritePipeFd = wakeFds[1];
+
+ result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
+ LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking: %s",
+ strerror(errno));
+
+ result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
+ LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking: %s",
+ strerror(errno));
+
+ mIdling = false;
+
+ // Allocate the epoll instance and register the wake pipe.
+ mEpollFd = epoll_create(EPOLL_SIZE_HINT);
+ LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
+
+ struct epoll_event eventItem;
+ memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
+ eventItem.events = EPOLLIN;
+ eventItem.data.fd = mWakeReadPipeFd;
+ result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem);
+ LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance: %s",
+ strerror(errno));
+}
+
+Looper::~Looper() {
+ close(mWakeReadPipeFd);
+ close(mWakeWritePipeFd);
+ close(mEpollFd);
+}
+
+void Looper::initTLSKey() {
+ int result = pthread_key_create(& gTLSKey, threadDestructor);
+ LOG_ALWAYS_FATAL_IF(result != 0, "Could not allocate TLS key.");
+}
+
+void Looper::threadDestructor(void *st) {
+ Looper* const self = static_cast<Looper*>(st);
+ if (self != NULL) {
+ self->decStrong((void*)threadDestructor);
+ }
+}
+
+void Looper::setForThread(const sp<Looper>& looper) {
+ sp<Looper> old = getForThread(); // also has side-effect of initializing TLS
+
+ if (looper != NULL) {
+ looper->incStrong((void*)threadDestructor);
+ }
+
+ pthread_setspecific(gTLSKey, looper.get());
+
+ if (old != NULL) {
+ old->decStrong((void*)threadDestructor);
+ }
+}
+
+sp<Looper> Looper::getForThread() {
+ int result = pthread_once(& gTLSOnce, initTLSKey);
+ LOG_ALWAYS_FATAL_IF(result != 0, "pthread_once failed");
+
+ return (Looper*)pthread_getspecific(gTLSKey);
+}
+
+sp<Looper> Looper::prepare(int opts) {
+ bool allowNonCallbacks = opts & PREPARE_ALLOW_NON_CALLBACKS;
+ sp<Looper> looper = Looper::getForThread();
+ if (looper == NULL) {
+ looper = new Looper(allowNonCallbacks);
+ Looper::setForThread(looper);
+ }
+ if (looper->getAllowNonCallbacks() != allowNonCallbacks) {
+ ALOGW("Looper already prepared for this thread with a different value for the "
+ "LOOPER_PREPARE_ALLOW_NON_CALLBACKS option.");
+ }
+ return looper;
+}
+
+bool Looper::getAllowNonCallbacks() const {
+ return mAllowNonCallbacks;
+}
+
+int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
+ int result = 0;
+ for (;;) {
+ while (mResponseIndex < mResponses.size()) {
+ const Response& response = mResponses.itemAt(mResponseIndex++);
+ int ident = response.request.ident;
+ if (ident >= 0) {
+ int fd = response.request.fd;
+ int events = response.events;
+ void* data = response.request.data;
+#if DEBUG_POLL_AND_WAKE
+ ALOGD("%p ~ pollOnce - returning signalled identifier %d: "
+ "fd=%d, events=0x%x, data=%p",
+ this, ident, fd, events, data);
+#endif
+ if (outFd != NULL) *outFd = fd;
+ if (outEvents != NULL) *outEvents = events;
+ if (outData != NULL) *outData = data;
+ return ident;
+ }
+ }
+
+ if (result != 0) {
+#if DEBUG_POLL_AND_WAKE
+ ALOGD("%p ~ pollOnce - returning result %d", this, result);
+#endif
+ if (outFd != NULL) *outFd = 0;
+ if (outEvents != NULL) *outEvents = 0;
+ if (outData != NULL) *outData = NULL;
+ return result;
+ }
+
+ result = pollInner(timeoutMillis);
+ }
+}
+
+int Looper::pollInner(int timeoutMillis) {
+#if DEBUG_POLL_AND_WAKE
+ ALOGD("%p ~ pollOnce - waiting: timeoutMillis=%d", this, timeoutMillis);
+#endif
+
+ // Adjust the timeout based on when the next message is due.
+ if (timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ int messageTimeoutMillis = toMillisecondTimeoutDelay(now, mNextMessageUptime);
+ if (messageTimeoutMillis >= 0
+ && (timeoutMillis < 0 || messageTimeoutMillis < timeoutMillis)) {
+ timeoutMillis = messageTimeoutMillis;
+ }
+#if DEBUG_POLL_AND_WAKE
+ ALOGD("%p ~ pollOnce - next message in %lldns, adjusted timeout: timeoutMillis=%d",
+ this, mNextMessageUptime - now, timeoutMillis);
+#endif
+ }
+
+ // Poll.
+ int result = POLL_WAKE;
+ mResponses.clear();
+ mResponseIndex = 0;
+
+ // We are about to idle.
+ mIdling = true;
+
+ struct epoll_event eventItems[EPOLL_MAX_EVENTS];
+ int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
+
+ // No longer idling.
+ mIdling = false;
+
+ // Acquire lock.
+ mLock.lock();
+
+ // Check for poll error.
+ if (eventCount < 0) {
+ if (errno == EINTR) {
+ goto Done;
+ }
+ ALOGW("Poll failed with an unexpected error: %s", strerror(errno));
+ result = POLL_ERROR;
+ goto Done;
+ }
+
+ // Check for poll timeout.
+ if (eventCount == 0) {
+#if DEBUG_POLL_AND_WAKE
+ ALOGD("%p ~ pollOnce - timeout", this);
+#endif
+ result = POLL_TIMEOUT;
+ goto Done;
+ }
+
+ // Handle all events.
+#if DEBUG_POLL_AND_WAKE
+ ALOGD("%p ~ pollOnce - handling events from %d fds", this, eventCount);
+#endif
+
+ for (int i = 0; i < eventCount; i++) {
+ int fd = eventItems[i].data.fd;
+ uint32_t epollEvents = eventItems[i].events;
+ if (fd == mWakeReadPipeFd) {
+ if (epollEvents & EPOLLIN) {
+ awoken();
+ } else {
+ ALOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents);
+ }
+ } else {
+ ssize_t requestIndex = mRequests.indexOfKey(fd);
+ if (requestIndex >= 0) {
+ int events = 0;
+ if (epollEvents & EPOLLIN) events |= EVENT_INPUT;
+ if (epollEvents & EPOLLOUT) events |= EVENT_OUTPUT;
+ if (epollEvents & EPOLLERR) events |= EVENT_ERROR;
+ if (epollEvents & EPOLLHUP) events |= EVENT_HANGUP;
+ pushResponse(events, mRequests.valueAt(requestIndex));
+ } else {
+ ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is "
+ "no longer registered.", epollEvents, fd);
+ }
+ }
+ }
+Done: ;
+
+ // Invoke pending message callbacks.
+ mNextMessageUptime = LLONG_MAX;
+ while (mMessageEnvelopes.size() != 0) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(0);
+ if (messageEnvelope.uptime <= now) {
+ // Remove the envelope from the list.
+ // We keep a strong reference to the handler until the call to handleMessage
+ // finishes. Then we drop it so that the handler can be deleted *before*
+ // we reacquire our lock.
+ { // obtain handler
+ sp<MessageHandler> handler = messageEnvelope.handler;
+ Message message = messageEnvelope.message;
+ mMessageEnvelopes.removeAt(0);
+ mSendingMessage = true;
+ mLock.unlock();
+
+#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS
+ ALOGD("%p ~ pollOnce - sending message: handler=%p, what=%d",
+ this, handler.get(), message.what);
+#endif
+ handler->handleMessage(message);
+ } // release handler
+
+ mLock.lock();
+ mSendingMessage = false;
+ result = POLL_CALLBACK;
+ } else {
+ // The last message left at the head of the queue determines the next wakeup time.
+ mNextMessageUptime = messageEnvelope.uptime;
+ break;
+ }
+ }
+
+ // Release lock.
+ mLock.unlock();
+
+ // Invoke all response callbacks.
+ for (size_t i = 0; i < mResponses.size(); i++) {
+ Response& response = mResponses.editItemAt(i);
+ if (response.request.ident == POLL_CALLBACK) {
+ int fd = response.request.fd;
+ int events = response.events;
+ void* data = response.request.data;
+#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS
+ ALOGD("%p ~ pollOnce - invoking fd event callback %p: fd=%d, events=0x%x, data=%p",
+ this, response.request.callback.get(), fd, events, data);
+#endif
+ int callbackResult = response.request.callback->handleEvent(fd, events, data);
+ if (callbackResult == 0) {
+ removeFd(fd);
+ }
+ // Clear the callback reference in the response structure promptly because we
+ // will not clear the response vector itself until the next poll.
+ response.request.callback.clear();
+ result = POLL_CALLBACK;
+ }
+ }
+ return result;
+}
+
+int Looper::pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
+ if (timeoutMillis <= 0) {
+ int result;
+ do {
+ result = pollOnce(timeoutMillis, outFd, outEvents, outData);
+ } while (result == POLL_CALLBACK);
+ return result;
+ } else {
+ nsecs_t endTime = systemTime(SYSTEM_TIME_MONOTONIC)
+ + milliseconds_to_nanoseconds(timeoutMillis);
+
+ for (;;) {
+ int result = pollOnce(timeoutMillis, outFd, outEvents, outData);
+ if (result != POLL_CALLBACK) {
+ return result;
+ }
+
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ timeoutMillis = toMillisecondTimeoutDelay(now, endTime);
+ if (timeoutMillis == 0) {
+ return POLL_TIMEOUT;
+ }
+ }
+ }
+}
+
+void Looper::wake() {
+#if DEBUG_POLL_AND_WAKE
+ ALOGD("%p ~ wake", this);
+#endif
+
+ ssize_t nWrite;
+ do {
+ nWrite = write(mWakeWritePipeFd, "W", 1);
+ } while (nWrite == -1 && errno == EINTR);
+
+ if (nWrite != 1) {
+ if (errno != EAGAIN) {
+ ALOGW("Could not write wake signal: %s", strerror(errno));
+ }
+ }
+}
+
+void Looper::awoken() {
+#if DEBUG_POLL_AND_WAKE
+ ALOGD("%p ~ awoken", this);
+#endif
+
+ char buffer[16];
+ ssize_t nRead;
+ do {
+ nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
+ } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
+}
+
+void Looper::pushResponse(int events, const Request& request) {
+ Response response;
+ response.events = events;
+ response.request = request;
+ mResponses.push(response);
+}
+
+int Looper::addFd(int fd, int ident, int events, Looper_callbackFunc callback, void* data) {
+ return addFd(fd, ident, events, callback ? new SimpleLooperCallback(callback) : NULL, data);
+}
+
+int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data) {
+#if DEBUG_CALLBACKS
+ ALOGD("%p ~ addFd - fd=%d, ident=%d, events=0x%x, callback=%p, data=%p", this, fd, ident,
+ events, callback.get(), data);
+#endif
+
+ if (!callback.get()) {
+ if (! mAllowNonCallbacks) {
+ ALOGE("Invalid attempt to set NULL callback but not allowed for this looper.");
+ return -1;
+ }
+
+ if (ident < 0) {
+ ALOGE("Invalid attempt to set NULL callback with ident < 0.");
+ return -1;
+ }
+ } else {
+ ident = POLL_CALLBACK;
+ }
+
+ int epollEvents = 0;
+ if (events & EVENT_INPUT) epollEvents |= EPOLLIN;
+ if (events & EVENT_OUTPUT) epollEvents |= EPOLLOUT;
+
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ Request request;
+ request.fd = fd;
+ request.ident = ident;
+ request.callback = callback;
+ request.data = data;
+
+ struct epoll_event eventItem;
+ memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
+ eventItem.events = epollEvents;
+ eventItem.data.fd = fd;
+
+ ssize_t requestIndex = mRequests.indexOfKey(fd);
+ if (requestIndex < 0) {
+ int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
+ if (epollResult < 0) {
+ ALOGE("Error adding epoll events for fd %d: %s", fd, strerror(errno));
+ return -1;
+ }
+ mRequests.add(fd, request);
+ } else {
+ int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem);
+ if (epollResult < 0) {
+ ALOGE("Error modifying epoll events for fd %d: %s", fd, strerror(errno));
+ return -1;
+ }
+ mRequests.replaceValueAt(requestIndex, request);
+ }
+ } // release lock
+ return 1;
+}
+
+int Looper::removeFd(int fd) {
+#if DEBUG_CALLBACKS
+ ALOGD("%p ~ removeFd - fd=%d", this, fd);
+#endif
+
+ { // acquire lock
+ AutoMutex _l(mLock);
+ ssize_t requestIndex = mRequests.indexOfKey(fd);
+ if (requestIndex < 0) {
+ return 0;
+ }
+
+ int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_DEL, fd, NULL);
+ if (epollResult < 0) {
+ ALOGE("Error removing epoll events for fd %d: %s", fd, strerror(errno));
+ return -1;
+ }
+
+ mRequests.removeItemsAt(requestIndex);
+ } // release lock
+ return 1;
+}
+
+void Looper::sendMessage(const sp<MessageHandler>& handler, const Message& message) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ sendMessageAtTime(now, handler, message);
+}
+
+void Looper::sendMessageDelayed(nsecs_t uptimeDelay, const sp<MessageHandler>& handler,
+ const Message& message) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ sendMessageAtTime(now + uptimeDelay, handler, message);
+}
+
+void Looper::sendMessageAtTime(nsecs_t uptime, const sp<MessageHandler>& handler,
+ const Message& message) {
+#if DEBUG_CALLBACKS
+ ALOGD("%p ~ sendMessageAtTime - uptime=%lld, handler=%p, what=%d",
+ this, uptime, handler.get(), message.what);
+#endif
+
+ size_t i = 0;
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ size_t messageCount = mMessageEnvelopes.size();
+ while (i < messageCount && uptime >= mMessageEnvelopes.itemAt(i).uptime) {
+ i += 1;
+ }
+
+ MessageEnvelope messageEnvelope(uptime, handler, message);
+ mMessageEnvelopes.insertAt(messageEnvelope, i, 1);
+
+ // Optimization: If the Looper is currently sending a message, then we can skip
+ // the call to wake() because the next thing the Looper will do after processing
+ // messages is to decide when the next wakeup time should be. In fact, it does
+ // not even matter whether this code is running on the Looper thread.
+ if (mSendingMessage) {
+ return;
+ }
+ } // release lock
+
+ // Wake the poll loop only when we enqueue a new message at the head.
+ if (i == 0) {
+ wake();
+ }
+}
+
+void Looper::removeMessages(const sp<MessageHandler>& handler) {
+#if DEBUG_CALLBACKS
+ ALOGD("%p ~ removeMessages - handler=%p", this, handler.get());
+#endif
+
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ for (size_t i = mMessageEnvelopes.size(); i != 0; ) {
+ const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(--i);
+ if (messageEnvelope.handler == handler) {
+ mMessageEnvelopes.removeAt(i);
+ }
+ }
+ } // release lock
+}
+
+void Looper::removeMessages(const sp<MessageHandler>& handler, int what) {
+#if DEBUG_CALLBACKS
+ ALOGD("%p ~ removeMessages - handler=%p, what=%d", this, handler.get(), what);
+#endif
+
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ for (size_t i = mMessageEnvelopes.size(); i != 0; ) {
+ const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(--i);
+ if (messageEnvelope.handler == handler
+ && messageEnvelope.message.what == what) {
+ mMessageEnvelopes.removeAt(i);
+ }
+ }
+ } // release lock
+}
+
+bool Looper::isIdling() const {
+ return mIdling;
+}
+
+} // namespace android
diff --git a/libutils/MODULE_LICENSE_APACHE2 b/libutils/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libutils/MODULE_LICENSE_APACHE2
diff --git a/libutils/NOTICE b/libutils/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/libutils/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/libutils/NativeHandle.cpp b/libutils/NativeHandle.cpp
new file mode 100644
index 0000000..e4daca7
--- /dev/null
+++ b/libutils/NativeHandle.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2014 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 <utils/NativeHandle.h>
+#include <cutils/native_handle.h>
+
+namespace android {
+
+sp<NativeHandle> NativeHandle::create(
+ native_handle_t* handle, bool ownsHandle) {
+ return handle ? new NativeHandle(handle, ownsHandle) : NULL;
+}
+
+NativeHandle::NativeHandle(native_handle_t* handle, bool ownsHandle)
+: mHandle(handle), mOwnsHandle(ownsHandle)
+{}
+
+NativeHandle::~NativeHandle() {
+ if (mOwnsHandle) {
+ native_handle_close(mHandle);
+ native_handle_delete(mHandle);
+ }
+}
+
+} // namespace android
diff --git a/libutils/Printer.cpp b/libutils/Printer.cpp
new file mode 100644
index 0000000..1dc8632
--- /dev/null
+++ b/libutils/Printer.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Printer"
+// #define LOG_NDEBUG 0
+
+#include <utils/Printer.h>
+#include <utils/String8.h>
+#include <utils/Log.h>
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+namespace android {
+
+/*
+ * Implementation of Printer
+ */
+Printer::Printer() {
+ // Intentionally left empty
+}
+
+Printer::~Printer() {
+ // Intentionally left empty
+}
+
+void Printer::printFormatLine(const char* format, ...) {
+ va_list arglist;
+ va_start(arglist, format);
+
+ char* formattedString;
+
+#ifndef USE_MINGW
+ if (vasprintf(&formattedString, format, arglist) < 0) { // returns -1 on error
+ ALOGE("%s: Failed to format string", __FUNCTION__);
+ return;
+ }
+#else
+ return;
+#endif
+
+ va_end(arglist);
+
+ printLine(formattedString);
+ free(formattedString);
+}
+
+/*
+ * Implementation of LogPrinter
+ */
+LogPrinter::LogPrinter(const char* logtag,
+ android_LogPriority priority,
+ const char* prefix,
+ bool ignoreBlankLines) :
+ mLogTag(logtag),
+ mPriority(priority),
+ mPrefix(prefix ?: ""),
+ mIgnoreBlankLines(ignoreBlankLines) {
+}
+
+void LogPrinter::printLine(const char* string) {
+ if (string == NULL) {
+ ALOGW("%s: NULL string passed in", __FUNCTION__);
+ return;
+ }
+
+ if (mIgnoreBlankLines || (*string)) {
+ // Simple case: Line is not blank, or we don't care about printing blank lines
+ printRaw(string);
+ } else {
+ // Force logcat to print empty lines by adding prefixing with a space
+ printRaw(" ");
+ }
+}
+
+void LogPrinter::printRaw(const char* string) {
+ __android_log_print(mPriority, mLogTag, "%s%s", mPrefix, string);
+}
+
+
+/*
+ * Implementation of FdPrinter
+ */
+FdPrinter::FdPrinter(int fd, unsigned int indent, const char* prefix) :
+ mFd(fd), mIndent(indent), mPrefix(prefix ?: "") {
+
+ if (fd < 0) {
+ ALOGW("%s: File descriptor out of range (%d)", __FUNCTION__, fd);
+ }
+
+ // <indent><prefix><line> -- e.g. '%-4s%s\n' for indent=4
+ snprintf(mFormatString, sizeof(mFormatString), "%%-%us%%s\n", mIndent);
+}
+
+void FdPrinter::printLine(const char* string) {
+ if (string == NULL) {
+ ALOGW("%s: NULL string passed in", __FUNCTION__);
+ return;
+ } else if (mFd < 0) {
+ ALOGW("%s: File descriptor out of range (%d)", __FUNCTION__, mFd);
+ return;
+ }
+
+#ifndef USE_MINGW
+ dprintf(mFd, mFormatString, mPrefix, string);
+#endif
+}
+
+/*
+ * Implementation of String8Printer
+ */
+String8Printer::String8Printer(String8* target, const char* prefix) :
+ mTarget(target),
+ mPrefix(prefix ?: "") {
+
+ if (target == NULL) {
+ ALOGW("%s: Target string was NULL", __FUNCTION__);
+ }
+}
+
+void String8Printer::printLine(const char* string) {
+ if (string == NULL) {
+ ALOGW("%s: NULL string passed in", __FUNCTION__);
+ return;
+ } else if (mTarget == NULL) {
+ ALOGW("%s: Target string was NULL", __FUNCTION__);
+ return;
+ }
+
+ mTarget->append(mPrefix);
+ mTarget->append(string);
+ mTarget->append("\n");
+}
+
+/*
+ * Implementation of PrefixPrinter
+ */
+PrefixPrinter::PrefixPrinter(Printer& printer, const char* prefix) :
+ mPrinter(printer), mPrefix(prefix ?: "") {
+}
+
+void PrefixPrinter::printLine(const char* string) {
+ mPrinter.printFormatLine("%s%s", mPrefix, string);
+}
+
+}; //namespace android
diff --git a/libutils/ProcessCallStack.cpp b/libutils/ProcessCallStack.cpp
new file mode 100644
index 0000000..011c302
--- /dev/null
+++ b/libutils/ProcessCallStack.cpp
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "ProcessCallStack"
+// #define LOG_NDEBUG 0
+
+#include <dirent.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <utils/Log.h>
+#include <utils/Errors.h>
+#include <utils/ProcessCallStack.h>
+#include <utils/Printer.h>
+
+#include <limits.h>
+
+namespace android {
+
+enum {
+ // Max sizes for various dynamically generated strings
+ MAX_TIME_STRING = 64,
+ MAX_PROC_PATH = 1024,
+
+ // Dump related prettiness constants
+ IGNORE_DEPTH_CURRENT_THREAD = 2,
+};
+
+static const char* CALL_STACK_PREFIX = " ";
+static const char* PATH_THREAD_NAME = "/proc/self/task/%d/comm";
+static const char* PATH_SELF_TASK = "/proc/self/task";
+
+static void dumpProcessHeader(Printer& printer, pid_t pid, const char* timeStr) {
+ if (timeStr == NULL) {
+ ALOGW("%s: timeStr was NULL", __FUNCTION__);
+ return;
+ }
+
+ char path[PATH_MAX];
+ char procNameBuf[MAX_PROC_PATH];
+ char* procName = NULL;
+ FILE* fp;
+
+ snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
+ if ((fp = fopen(path, "r"))) {
+ procName = fgets(procNameBuf, sizeof(procNameBuf), fp);
+ fclose(fp);
+ }
+
+ if (!procName) {
+ procName = const_cast<char*>("<unknown>");
+ }
+
+ printer.printLine();
+ printer.printLine();
+ printer.printFormatLine("----- pid %d at %s -----", pid, timeStr);
+ printer.printFormatLine("Cmd line: %s", procName);
+}
+
+static void dumpProcessFooter(Printer& printer, pid_t pid) {
+ printer.printLine();
+ printer.printFormatLine("----- end %d -----", pid);
+ printer.printLine();
+}
+
+static String8 getThreadName(pid_t tid) {
+ char path[PATH_MAX];
+ char* procName = NULL;
+ char procNameBuf[MAX_PROC_PATH];
+ FILE* fp;
+
+ snprintf(path, sizeof(path), PATH_THREAD_NAME, tid);
+ if ((fp = fopen(path, "r"))) {
+ procName = fgets(procNameBuf, sizeof(procNameBuf), fp);
+ fclose(fp);
+ } else {
+ ALOGE("%s: Failed to open %s", __FUNCTION__, path);
+ }
+
+ if (procName == NULL) {
+ // Reading /proc/self/task/%d/comm failed due to a race
+ return String8::format("[err-unknown-tid-%d]", tid);
+ }
+
+ // Strip ending newline
+ strtok(procName, "\n");
+
+ return String8(procName);
+}
+
+static String8 getTimeString(struct tm tm) {
+ char timestr[MAX_TIME_STRING];
+ // i.e. '2013-10-22 14:42:05'
+ strftime(timestr, sizeof(timestr), "%F %T", &tm);
+
+ return String8(timestr);
+}
+
+/*
+ * Implementation of ProcessCallStack
+ */
+ProcessCallStack::ProcessCallStack() {
+}
+
+ProcessCallStack::ProcessCallStack(const ProcessCallStack& rhs) :
+ mThreadMap(rhs.mThreadMap),
+ mTimeUpdated(rhs.mTimeUpdated) {
+}
+
+ProcessCallStack::~ProcessCallStack() {
+}
+
+void ProcessCallStack::clear() {
+ mThreadMap.clear();
+ mTimeUpdated = tm();
+}
+
+void ProcessCallStack::update() {
+ DIR *dp;
+ struct dirent *ep;
+ struct dirent entry;
+
+ dp = opendir(PATH_SELF_TASK);
+ if (dp == NULL) {
+ ALOGE("%s: Failed to update the process's call stacks: %s",
+ __FUNCTION__, strerror(errno));
+ return;
+ }
+
+ pid_t selfPid = getpid();
+
+ clear();
+
+ // Get current time.
+#ifndef USE_MINGW
+ {
+ time_t t = time(NULL);
+ struct tm tm;
+ localtime_r(&t, &tm);
+
+ mTimeUpdated = tm;
+ }
+
+ /*
+ * Each tid is a directory inside of /proc/self/task
+ * - Read every file in directory => get every tid
+ */
+ int code;
+ while ((code = readdir_r(dp, &entry, &ep)) == 0 && ep != NULL) {
+ pid_t tid = -1;
+ sscanf(ep->d_name, "%d", &tid);
+
+ if (tid < 0) {
+ // Ignore '.' and '..'
+ ALOGV("%s: Failed to read tid from %s/%s",
+ __FUNCTION__, PATH_SELF_TASK, ep->d_name);
+ continue;
+ }
+
+ ssize_t idx = mThreadMap.add(tid, ThreadInfo());
+ if (idx < 0) { // returns negative error value on error
+ ALOGE("%s: Failed to add new ThreadInfo: %s",
+ __FUNCTION__, strerror(-idx));
+ continue;
+ }
+
+ ThreadInfo& threadInfo = mThreadMap.editValueAt(static_cast<size_t>(idx));
+
+ /*
+ * Ignore CallStack::update and ProcessCallStack::update for current thread
+ * - Every other thread doesn't need this since we call update off-thread
+ */
+ int ignoreDepth = (selfPid == tid) ? IGNORE_DEPTH_CURRENT_THREAD : 0;
+
+ // Update thread's call stacks
+ threadInfo.callStack.update(ignoreDepth, tid);
+
+ // Read/save thread name
+ threadInfo.threadName = getThreadName(tid);
+
+ ALOGV("%s: Got call stack for tid %d (size %zu)",
+ __FUNCTION__, tid, threadInfo.callStack.size());
+ }
+ if (code != 0) { // returns positive error value on error
+ ALOGE("%s: Failed to readdir from %s: %s",
+ __FUNCTION__, PATH_SELF_TASK, strerror(code));
+ }
+#endif
+
+ closedir(dp);
+}
+
+void ProcessCallStack::log(const char* logtag, android_LogPriority priority,
+ const char* prefix) const {
+ LogPrinter printer(logtag, priority, prefix, /*ignoreBlankLines*/false);
+ print(printer);
+}
+
+void ProcessCallStack::print(Printer& printer) const {
+ /*
+ * Print the header/footer with the regular printer.
+ * Print the callstack with an additional two spaces as the prefix for legibility.
+ */
+ PrefixPrinter csPrinter(printer, CALL_STACK_PREFIX);
+ printInternal(printer, csPrinter);
+}
+
+void ProcessCallStack::printInternal(Printer& printer, Printer& csPrinter) const {
+ dumpProcessHeader(printer, getpid(),
+ getTimeString(mTimeUpdated).string());
+
+ for (size_t i = 0; i < mThreadMap.size(); ++i) {
+ pid_t tid = mThreadMap.keyAt(i);
+ const ThreadInfo& threadInfo = mThreadMap.valueAt(i);
+ const String8& threadName = threadInfo.threadName;
+
+ printer.printLine("");
+ printer.printFormatLine("\"%s\" sysTid=%d", threadName.string(), tid);
+
+ threadInfo.callStack.print(csPrinter);
+ }
+
+ dumpProcessFooter(printer, getpid());
+}
+
+void ProcessCallStack::dump(int fd, int indent, const char* prefix) const {
+
+ if (indent < 0) {
+ ALOGW("%s: Bad indent (%d)", __FUNCTION__, indent);
+ return;
+ }
+
+ FdPrinter printer(fd, static_cast<unsigned int>(indent), prefix);
+ print(printer);
+}
+
+String8 ProcessCallStack::toString(const char* prefix) const {
+
+ String8 dest;
+ String8Printer printer(&dest, prefix);
+ print(printer);
+
+ return dest;
+}
+
+size_t ProcessCallStack::size() const {
+ return mThreadMap.size();
+}
+
+}; //namespace android
diff --git a/libutils/PropertyMap.cpp b/libutils/PropertyMap.cpp
new file mode 100644
index 0000000..5520702
--- /dev/null
+++ b/libutils/PropertyMap.cpp
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "PropertyMap"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <utils/PropertyMap.h>
+#include <utils/Log.h>
+
+// Enables debug output for the parser.
+#define DEBUG_PARSER 0
+
+// Enables debug output for parser performance.
+#define DEBUG_PARSER_PERFORMANCE 0
+
+
+namespace android {
+
+static const char* WHITESPACE = " \t\r";
+static const char* WHITESPACE_OR_PROPERTY_DELIMITER = " \t\r=";
+
+
+// --- PropertyMap ---
+
+PropertyMap::PropertyMap() {
+}
+
+PropertyMap::~PropertyMap() {
+}
+
+void PropertyMap::clear() {
+ mProperties.clear();
+}
+
+void PropertyMap::addProperty(const String8& key, const String8& value) {
+ mProperties.add(key, value);
+}
+
+bool PropertyMap::hasProperty(const String8& key) const {
+ return mProperties.indexOfKey(key) >= 0;
+}
+
+bool PropertyMap::tryGetProperty(const String8& key, String8& outValue) const {
+ ssize_t index = mProperties.indexOfKey(key);
+ if (index < 0) {
+ return false;
+ }
+
+ outValue = mProperties.valueAt(index);
+ return true;
+}
+
+bool PropertyMap::tryGetProperty(const String8& key, bool& outValue) const {
+ int32_t intValue;
+ if (!tryGetProperty(key, intValue)) {
+ return false;
+ }
+
+ outValue = intValue;
+ return true;
+}
+
+bool PropertyMap::tryGetProperty(const String8& key, int32_t& outValue) const {
+ String8 stringValue;
+ if (! tryGetProperty(key, stringValue) || stringValue.length() == 0) {
+ return false;
+ }
+
+ char* end;
+ int value = strtol(stringValue.string(), & end, 10);
+ if (*end != '\0') {
+ ALOGW("Property key '%s' has invalid value '%s'. Expected an integer.",
+ key.string(), stringValue.string());
+ return false;
+ }
+ outValue = value;
+ return true;
+}
+
+bool PropertyMap::tryGetProperty(const String8& key, float& outValue) const {
+ String8 stringValue;
+ if (! tryGetProperty(key, stringValue) || stringValue.length() == 0) {
+ return false;
+ }
+
+ char* end;
+ float value = strtof(stringValue.string(), & end);
+ if (*end != '\0') {
+ ALOGW("Property key '%s' has invalid value '%s'. Expected a float.",
+ key.string(), stringValue.string());
+ return false;
+ }
+ outValue = value;
+ return true;
+}
+
+void PropertyMap::addAll(const PropertyMap* map) {
+ for (size_t i = 0; i < map->mProperties.size(); i++) {
+ mProperties.add(map->mProperties.keyAt(i), map->mProperties.valueAt(i));
+ }
+}
+
+status_t PropertyMap::load(const String8& filename, PropertyMap** outMap) {
+ *outMap = NULL;
+
+ Tokenizer* tokenizer;
+ status_t status = Tokenizer::open(filename, &tokenizer);
+ if (status) {
+ ALOGE("Error %d opening property file %s.", status, filename.string());
+ } else {
+ PropertyMap* map = new PropertyMap();
+ if (!map) {
+ ALOGE("Error allocating property map.");
+ status = NO_MEMORY;
+ } else {
+#if DEBUG_PARSER_PERFORMANCE
+ nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
+#endif
+ Parser parser(map, tokenizer);
+ status = parser.parse();
+#if DEBUG_PARSER_PERFORMANCE
+ nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
+ ALOGD("Parsed property file '%s' %d lines in %0.3fms.",
+ tokenizer->getFilename().string(), tokenizer->getLineNumber(),
+ elapsedTime / 1000000.0);
+#endif
+ if (status) {
+ delete map;
+ } else {
+ *outMap = map;
+ }
+ }
+ delete tokenizer;
+ }
+ return status;
+}
+
+
+// --- PropertyMap::Parser ---
+
+PropertyMap::Parser::Parser(PropertyMap* map, Tokenizer* tokenizer) :
+ mMap(map), mTokenizer(tokenizer) {
+}
+
+PropertyMap::Parser::~Parser() {
+}
+
+status_t PropertyMap::Parser::parse() {
+ while (!mTokenizer->isEof()) {
+#if DEBUG_PARSER
+ ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(),
+ mTokenizer->peekRemainderOfLine().string());
+#endif
+
+ mTokenizer->skipDelimiters(WHITESPACE);
+
+ if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
+ String8 keyToken = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER);
+ if (keyToken.isEmpty()) {
+ ALOGE("%s: Expected non-empty property key.", mTokenizer->getLocation().string());
+ return BAD_VALUE;
+ }
+
+ mTokenizer->skipDelimiters(WHITESPACE);
+
+ if (mTokenizer->nextChar() != '=') {
+ ALOGE("%s: Expected '=' between property key and value.",
+ mTokenizer->getLocation().string());
+ return BAD_VALUE;
+ }
+
+ mTokenizer->skipDelimiters(WHITESPACE);
+
+ String8 valueToken = mTokenizer->nextToken(WHITESPACE);
+ if (valueToken.find("\\", 0) >= 0 || valueToken.find("\"", 0) >= 0) {
+ ALOGE("%s: Found reserved character '\\' or '\"' in property value.",
+ mTokenizer->getLocation().string());
+ return BAD_VALUE;
+ }
+
+ mTokenizer->skipDelimiters(WHITESPACE);
+ if (!mTokenizer->isEol()) {
+ ALOGE("%s: Expected end of line, got '%s'.",
+ mTokenizer->getLocation().string(),
+ mTokenizer->peekRemainderOfLine().string());
+ return BAD_VALUE;
+ }
+
+ if (mMap->hasProperty(keyToken)) {
+ ALOGE("%s: Duplicate property value for key '%s'.",
+ mTokenizer->getLocation().string(), keyToken.string());
+ return BAD_VALUE;
+ }
+
+ mMap->addProperty(keyToken, valueToken);
+ }
+
+ mTokenizer->nextLine();
+ }
+ return NO_ERROR;
+}
+
+} // namespace android
diff --git a/libutils/README b/libutils/README
new file mode 100644
index 0000000..01741e0
--- /dev/null
+++ b/libutils/README
@@ -0,0 +1,289 @@
+Android Utility Function Library
+================================
+
+
+If you need a feature that is native to Linux but not present on other
+platforms, construct a platform-dependent implementation that shares
+the Linux interface. That way the actual device runs as "light" as
+possible.
+
+If that isn't feasible, create a system-independent interface and hide
+the details.
+
+The ultimate goal is *not* to create a super-duper platform abstraction
+layer. The goal is to provide an optimized solution for Linux with
+reasonable implementations for other platforms.
+
+
+
+Resource overlay
+================
+
+
+Introduction
+------------
+
+Overlay packages are special .apk files which provide no code but
+additional resource values (and possibly new configurations) for
+resources in other packages. When an application requests resources,
+the system will return values from either the application's original
+package or any associated overlay package. Any redirection is completely
+transparent to the calling application.
+
+Resource values have the following precedence table, listed in
+descending precedence.
+
+ * overlay package, matching config (eg res/values-en-land)
+
+ * original package, matching config
+
+ * overlay package, no config (eg res/values)
+
+ * original package, no config
+
+During compilation, overlay packages are differentiated from regular
+packages by passing the -o flag to aapt.
+
+
+Background
+----------
+
+This section provides generic background material on resources in
+Android.
+
+
+How resources are bundled in .apk files
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Android .apk files are .zip files, usually housing .dex code,
+certificates and resources, though packages containing resources but
+no code are possible. Resources can be divided into the following
+categories; a `configuration' indicates a set of phone language, display
+density, network operator, etc.
+
+ * assets: uncompressed, raw files packaged as part of an .apk and
+ explicitly referenced by filename. These files are
+ independent of configuration.
+
+ * res/drawable: bitmap or xml graphics. Each file may have different
+ values depending on configuration.
+
+ * res/values: integers, strings, etc. Each resource may have different
+ values depending on configuration.
+
+Resource meta information and information proper is stored in a binary
+format in a named file resources.arsc, bundled as part of the .apk.
+
+Resource IDs and lookup
+~~~~~~~~~~~~~~~~~~~~~~~
+During compilation, the aapt tool gathers application resources and
+generates a resources.arsc file. Each resource name is assigned an
+integer ID 0xppttiii (translated to a symbolic name via R.java), where
+
+ * pp: corresponds to the package namespace (details below).
+
+ * tt: corresponds to the resource type (string, int, etc). Every
+ resource of the same type within the same package has the same
+ tt value, but depending on available types, the actual numerical
+ value may be different between packages.
+
+ * iiii: sequential number, assigned in the order resources are found.
+
+Resource values are specified paired with a set of configuration
+constraints (the default being the empty set), eg res/values-sv-port
+which imposes restrictions on language (Swedish) and display orientation
+(portrait). During lookup, every constraint set is matched against the
+current configuration, and the value corresponding to the best matching
+constraint set is returned (ResourceTypes.{h,cpp}).
+
+Parsing of resources.arsc is handled by ResourceTypes.cpp; this utility
+is governed by AssetManager.cpp, which tracks loaded resources per
+process.
+
+Assets are looked up by path and filename in AssetManager.cpp. The path
+to resources in res/drawable are located by ResourceTypes.cpp and then
+handled like assets by AssetManager.cpp. Other resources are handled
+solely by ResourceTypes.cpp.
+
+Package ID as namespace
+~~~~~~~~~~~~~~~~~~~~~~~
+The pp part of a resource ID defines a namespace. Android currently
+defines two namespaces:
+
+ * 0x01: system resources (pre-installed in framework-res.apk)
+
+ * 0x7f: application resources (bundled in the application .apk)
+
+ResourceTypes.cpp supports package IDs between 0x01 and 0x7f
+(inclusive); values outside this range are invalid.
+
+Each running (Dalvik) process is assigned a unique instance of
+AssetManager, which in turn keeps a forest structure of loaded
+resource.arsc files. Normally, this forest is structured as follows,
+where mPackageMap is the internal vector employed in ResourceTypes.cpp.
+
+mPackageMap[0x00] -> system package
+mPackageMap[0x01] -> NULL
+mPackageMap[0x02] -> NULL
+...
+mPackageMap[0x7f - 2] -> NULL
+mPackageMap[0x7f - 1] -> application package
+
+
+
+The resource overlay extension
+------------------------------
+
+The resource overlay mechanism aims to (partly) shadow and extend
+existing resources with new values for defined and new configurations.
+Technically, this is achieved by adding resource-only packages (called
+overlay packages) to existing resource namespaces, like so:
+
+mPackageMap[0x00] -> system package -> system overlay package
+mPackageMap[0x01] -> NULL
+mPackageMap[0x02] -> NULL
+...
+mPackageMap[0x7f - 2] -> NULL
+mPackageMap[0x7f - 1] -> application package -> overlay 1 -> overlay 2
+
+The use of overlay resources is completely transparent to
+applications; no additional resource identifiers are introduced, only
+configuration/value pairs. Any number of overlay packages may be loaded
+at a time; overlay packages are agnostic to what they target -- both
+system and application resources are fair game.
+
+The package targeted by an overlay package is called the target or
+original package.
+
+Resource overlay operates on symbolic resources names. Hence, to
+override the string/str1 resources in a package, the overlay package
+would include a resource also named string/str1. The end user does not
+have to worry about the numeric resources IDs assigned by aapt, as this
+is resolved automatically by the system.
+
+As of this writing, the use of resource overlay has not been fully
+explored. Until it has, only OEMs are trusted to use resource overlay.
+For this reason, overlay packages must reside in /system/overlay.
+
+
+Resource ID mapping
+~~~~~~~~~~~~~~~~~~~
+Resource identifiers must be coherent within the same namespace (ie
+PackageGroup in ResourceTypes.cpp). Calling applications will refer to
+resources using the IDs defined in the original package, but there is no
+guarantee aapt has assigned the same ID to the corresponding resource in
+an overlay package. To translate between the two, a resource ID mapping
+{original ID -> overlay ID} is created during package installation
+(PackageManagerService.java) and used during resource lookup. The
+mapping is stored in /data/resource-cache, with a @idmap file name
+suffix.
+
+The idmap file format is documented in a separate section, below.
+
+
+Package management
+~~~~~~~~~~~~~~~~~~
+Packages are managed by the PackageManagerService. Addition and removal
+of packages are monitored via the inotify framework, exposed via
+android.os.FileObserver.
+
+During initialization of a Dalvik process, ActivityThread.java requests
+the process' AssetManager (by proxy, via AssetManager.java and JNI)
+to load a list of packages. This list includes overlay packages, if
+present.
+
+When a target package or a corresponding overlay package is installed,
+the target package's process is stopped and a new idmap is generated.
+This is similar to how applications are stopped when their packages are
+upgraded.
+
+
+Creating overlay packages
+-------------------------
+
+Overlay packages should contain no code, define (some) resources with
+the same type and name as in the original package, and be compiled with
+the -o flag passed to aapt.
+
+The aapt -o flag instructs aapt to create an overlay package.
+Technically, this means the package will be assigned package id 0x00.
+
+There are no restrictions on overlay packages names, though the naming
+convention <original.package.name>.overlay.<name> is recommended.
+
+
+Example overlay package
+~~~~~~~~~~~~~~~~~~~~~~~
+
+To overlay the resource bool/b in package com.foo.bar, to be applied
+when the display is in landscape mode, create a new package with
+no source code and a single .xml file under res/values-land, with
+an entry for bool/b. Compile with aapt -o and place the results in
+/system/overlay by adding the following to Android.mk:
+
+LOCAL_AAPT_FLAGS := -o com.foo.bar
+LOCAL_MODULE_PATH := $(TARGET_OUT)/overlay
+
+
+The ID map (idmap) file format
+------------------------------
+
+The idmap format is designed for lookup performance. However, leading
+and trailing undefined overlay values are discarded to reduce the memory
+footprint.
+
+
+idmap grammar
+~~~~~~~~~~~~~
+All atoms (names in square brackets) are uint32_t integers. The
+idmap-magic constant spells "idmp" in ASCII. Offsets are given relative
+to the data_header, not to the beginning of the file.
+
+map := header data
+header := idmap-magic <crc32-original-pkg> <crc32-overlay-pkg>
+idmap-magic := <0x706d6469>
+data := data_header type_block+
+data_header := <m> header_block{m}
+header_block := <0> | <type_block_offset>
+type_block := <n> <id_offset> entry{n}
+entry := <resource_id_in_target_package>
+
+
+idmap example
+~~~~~~~~~~~~~
+Given a pair of target and overlay packages with CRC sums 0x216a8fe2
+and 0x6b9beaec, each defining the following resources
+
+Name Target package Overlay package
+string/str0 0x7f010000 -
+string/str1 0x7f010001 0x7f010000
+string/str2 0x7f010002 -
+string/str3 0x7f010003 0x7f010001
+string/str4 0x7f010004 -
+bool/bool0 0x7f020000 -
+integer/int0 0x7f030000 0x7f020000
+integer/int1 0x7f030001 -
+
+the corresponding resource map is
+
+0x706d6469 0x216a8fe2 0x6b9beaec 0x00000003 \
+0x00000004 0x00000000 0x00000009 0x00000003 \
+0x00000001 0x7f010000 0x00000000 0x7f010001 \
+0x00000001 0x00000000 0x7f020000
+
+or, formatted differently
+
+0x706d6469 # magic: all idmap files begin with this constant
+0x216a8fe2 # CRC32 of the resources.arsc file in the original package
+0x6b9beaec # CRC32 of the resources.arsc file in the overlay package
+0x00000003 # header; three types (string, bool, integer) in the target package
+0x00000004 # header_block for type 0 (string) is located at offset 4
+0x00000000 # no bool type exists in overlay package -> no header_block
+0x00000009 # header_block for type 2 (integer) is located at offset 9
+0x00000003 # header_block for string; overlay IDs span 3 elements
+0x00000001 # the first string in target package is entry 1 == offset
+0x7f010000 # target 0x7f01001 -> overlay 0x7f010000
+0x00000000 # str2 not defined in overlay package
+0x7f010001 # target 0x7f010003 -> overlay 0x7f010001
+0x00000001 # header_block for integer; overlay IDs span 1 element
+0x00000000 # offset == 0
+0x7f020000 # target 0x7f030000 -> overlay 0x7f020000
diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp
new file mode 100644
index 0000000..02907ad
--- /dev/null
+++ b/libutils/RefBase.cpp
@@ -0,0 +1,656 @@
+/*
+ * Copyright (C) 2005 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 "RefBase"
+// #define LOG_NDEBUG 0
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <typeinfo>
+#include <unistd.h>
+
+#include <utils/RefBase.h>
+
+#include <utils/Atomic.h>
+#include <utils/CallStack.h>
+#include <utils/Log.h>
+#include <utils/threads.h>
+
+#ifndef __unused
+#define __unused __attribute__((__unused__))
+#endif
+
+// compile with refcounting debugging enabled
+#define DEBUG_REFS 0
+
+// whether ref-tracking is enabled by default, if not, trackMe(true, false)
+// needs to be called explicitly
+#define DEBUG_REFS_ENABLED_BY_DEFAULT 0
+
+// whether callstack are collected (significantly slows things down)
+#define DEBUG_REFS_CALLSTACK_ENABLED 1
+
+// folder where stack traces are saved when DEBUG_REFS is enabled
+// this folder needs to exist and be writable
+#define DEBUG_REFS_CALLSTACK_PATH "/data/debug"
+
+// log all reference counting operations
+#define PRINT_REFS 0
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+#define INITIAL_STRONG_VALUE (1<<28)
+
+// ---------------------------------------------------------------------------
+
+class RefBase::weakref_impl : public RefBase::weakref_type
+{
+public:
+ volatile int32_t mStrong;
+ volatile int32_t mWeak;
+ RefBase* const mBase;
+ volatile int32_t mFlags;
+
+#if !DEBUG_REFS
+
+ weakref_impl(RefBase* base)
+ : mStrong(INITIAL_STRONG_VALUE)
+ , mWeak(0)
+ , mBase(base)
+ , mFlags(0)
+ {
+ }
+
+ void addStrongRef(const void* /*id*/) { }
+ void removeStrongRef(const void* /*id*/) { }
+ void renameStrongRefId(const void* /*old_id*/, const void* /*new_id*/) { }
+ void addWeakRef(const void* /*id*/) { }
+ void removeWeakRef(const void* /*id*/) { }
+ void renameWeakRefId(const void* /*old_id*/, const void* /*new_id*/) { }
+ void printRefs() const { }
+ void trackMe(bool, bool) { }
+
+#else
+
+ weakref_impl(RefBase* base)
+ : mStrong(INITIAL_STRONG_VALUE)
+ , mWeak(0)
+ , mBase(base)
+ , mFlags(0)
+ , mStrongRefs(NULL)
+ , mWeakRefs(NULL)
+ , mTrackEnabled(!!DEBUG_REFS_ENABLED_BY_DEFAULT)
+ , mRetain(false)
+ {
+ }
+
+ ~weakref_impl()
+ {
+ bool dumpStack = false;
+ if (!mRetain && mStrongRefs != NULL) {
+ dumpStack = true;
+ ALOGE("Strong references remain:");
+ ref_entry* refs = mStrongRefs;
+ while (refs) {
+ char inc = refs->ref >= 0 ? '+' : '-';
+ ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref);
+#if DEBUG_REFS_CALLSTACK_ENABLED
+ refs->stack.log(LOG_TAG);
+#endif
+ refs = refs->next;
+ }
+ }
+
+ if (!mRetain && mWeakRefs != NULL) {
+ dumpStack = true;
+ ALOGE("Weak references remain!");
+ ref_entry* refs = mWeakRefs;
+ while (refs) {
+ char inc = refs->ref >= 0 ? '+' : '-';
+ ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref);
+#if DEBUG_REFS_CALLSTACK_ENABLED
+ refs->stack.log(LOG_TAG);
+#endif
+ refs = refs->next;
+ }
+ }
+ if (dumpStack) {
+ ALOGE("above errors at:");
+ CallStack stack(LOG_TAG);
+ }
+ }
+
+ void addStrongRef(const void* id) {
+ //ALOGD_IF(mTrackEnabled,
+ // "addStrongRef: RefBase=%p, id=%p", mBase, id);
+ addRef(&mStrongRefs, id, mStrong);
+ }
+
+ void removeStrongRef(const void* id) {
+ //ALOGD_IF(mTrackEnabled,
+ // "removeStrongRef: RefBase=%p, id=%p", mBase, id);
+ if (!mRetain) {
+ removeRef(&mStrongRefs, id);
+ } else {
+ addRef(&mStrongRefs, id, -mStrong);
+ }
+ }
+
+ void renameStrongRefId(const void* old_id, const void* new_id) {
+ //ALOGD_IF(mTrackEnabled,
+ // "renameStrongRefId: RefBase=%p, oid=%p, nid=%p",
+ // mBase, old_id, new_id);
+ renameRefsId(mStrongRefs, old_id, new_id);
+ }
+
+ void addWeakRef(const void* id) {
+ addRef(&mWeakRefs, id, mWeak);
+ }
+
+ void removeWeakRef(const void* id) {
+ if (!mRetain) {
+ removeRef(&mWeakRefs, id);
+ } else {
+ addRef(&mWeakRefs, id, -mWeak);
+ }
+ }
+
+ void renameWeakRefId(const void* old_id, const void* new_id) {
+ renameRefsId(mWeakRefs, old_id, new_id);
+ }
+
+ void trackMe(bool track, bool retain)
+ {
+ mTrackEnabled = track;
+ mRetain = retain;
+ }
+
+ void printRefs() const
+ {
+ String8 text;
+
+ {
+ Mutex::Autolock _l(mMutex);
+ char buf[128];
+ sprintf(buf, "Strong references on RefBase %p (weakref_type %p):\n", mBase, this);
+ text.append(buf);
+ printRefsLocked(&text, mStrongRefs);
+ sprintf(buf, "Weak references on RefBase %p (weakref_type %p):\n", mBase, this);
+ text.append(buf);
+ printRefsLocked(&text, mWeakRefs);
+ }
+
+ {
+ char name[100];
+ snprintf(name, 100, DEBUG_REFS_CALLSTACK_PATH "/%p.stack", this);
+ int rc = open(name, O_RDWR | O_CREAT | O_APPEND, 644);
+ if (rc >= 0) {
+ write(rc, text.string(), text.length());
+ close(rc);
+ ALOGD("STACK TRACE for %p saved in %s", this, name);
+ }
+ else ALOGE("FAILED TO PRINT STACK TRACE for %p in %s: %s", this,
+ name, strerror(errno));
+ }
+ }
+
+private:
+ struct ref_entry
+ {
+ ref_entry* next;
+ const void* id;
+#if DEBUG_REFS_CALLSTACK_ENABLED
+ CallStack stack;
+#endif
+ int32_t ref;
+ };
+
+ void addRef(ref_entry** refs, const void* id, int32_t mRef)
+ {
+ if (mTrackEnabled) {
+ AutoMutex _l(mMutex);
+
+ ref_entry* ref = new ref_entry;
+ // Reference count at the time of the snapshot, but before the
+ // update. Positive value means we increment, negative--we
+ // decrement the reference count.
+ ref->ref = mRef;
+ ref->id = id;
+#if DEBUG_REFS_CALLSTACK_ENABLED
+ ref->stack.update(2);
+#endif
+ ref->next = *refs;
+ *refs = ref;
+ }
+ }
+
+ void removeRef(ref_entry** refs, const void* id)
+ {
+ if (mTrackEnabled) {
+ AutoMutex _l(mMutex);
+
+ ref_entry* const head = *refs;
+ ref_entry* ref = head;
+ while (ref != NULL) {
+ if (ref->id == id) {
+ *refs = ref->next;
+ delete ref;
+ return;
+ }
+ refs = &ref->next;
+ ref = *refs;
+ }
+
+ ALOGE("RefBase: removing id %p on RefBase %p"
+ "(weakref_type %p) that doesn't exist!",
+ id, mBase, this);
+
+ ref = head;
+ while (ref) {
+ char inc = ref->ref >= 0 ? '+' : '-';
+ ALOGD("\t%c ID %p (ref %d):", inc, ref->id, ref->ref);
+ ref = ref->next;
+ }
+
+ CallStack stack(LOG_TAG);
+ }
+ }
+
+ void renameRefsId(ref_entry* r, const void* old_id, const void* new_id)
+ {
+ if (mTrackEnabled) {
+ AutoMutex _l(mMutex);
+ ref_entry* ref = r;
+ while (ref != NULL) {
+ if (ref->id == old_id) {
+ ref->id = new_id;
+ }
+ ref = ref->next;
+ }
+ }
+ }
+
+ void printRefsLocked(String8* out, const ref_entry* refs) const
+ {
+ char buf[128];
+ while (refs) {
+ char inc = refs->ref >= 0 ? '+' : '-';
+ sprintf(buf, "\t%c ID %p (ref %d):\n",
+ inc, refs->id, refs->ref);
+ out->append(buf);
+#if DEBUG_REFS_CALLSTACK_ENABLED
+ out->append(refs->stack.toString("\t\t"));
+#else
+ out->append("\t\t(call stacks disabled)");
+#endif
+ refs = refs->next;
+ }
+ }
+
+ mutable Mutex mMutex;
+ ref_entry* mStrongRefs;
+ ref_entry* mWeakRefs;
+
+ bool mTrackEnabled;
+ // Collect stack traces on addref and removeref, instead of deleting the stack references
+ // on removeref that match the address ones.
+ bool mRetain;
+
+#endif
+};
+
+// ---------------------------------------------------------------------------
+
+void RefBase::incStrong(const void* id) const
+{
+ weakref_impl* const refs = mRefs;
+ refs->incWeak(id);
+
+ refs->addStrongRef(id);
+ const int32_t c = android_atomic_inc(&refs->mStrong);
+ ALOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs);
+#if PRINT_REFS
+ ALOGD("incStrong of %p from %p: cnt=%d\n", this, id, c);
+#endif
+ if (c != INITIAL_STRONG_VALUE) {
+ return;
+ }
+
+ android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);
+ refs->mBase->onFirstRef();
+}
+
+void RefBase::decStrong(const void* id) const
+{
+ weakref_impl* const refs = mRefs;
+ refs->removeStrongRef(id);
+ const int32_t c = android_atomic_dec(&refs->mStrong);
+#if PRINT_REFS
+ ALOGD("decStrong of %p from %p: cnt=%d\n", this, id, c);
+#endif
+ ALOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs);
+ if (c == 1) {
+ refs->mBase->onLastStrongRef(id);
+ if ((refs->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {
+ delete this;
+ }
+ }
+ refs->decWeak(id);
+}
+
+void RefBase::forceIncStrong(const void* id) const
+{
+ weakref_impl* const refs = mRefs;
+ refs->incWeak(id);
+
+ refs->addStrongRef(id);
+ const int32_t c = android_atomic_inc(&refs->mStrong);
+ ALOG_ASSERT(c >= 0, "forceIncStrong called on %p after ref count underflow",
+ refs);
+#if PRINT_REFS
+ ALOGD("forceIncStrong of %p from %p: cnt=%d\n", this, id, c);
+#endif
+
+ switch (c) {
+ case INITIAL_STRONG_VALUE:
+ android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);
+ // fall through...
+ case 0:
+ refs->mBase->onFirstRef();
+ }
+}
+
+int32_t RefBase::getStrongCount() const
+{
+ return mRefs->mStrong;
+}
+
+RefBase* RefBase::weakref_type::refBase() const
+{
+ return static_cast<const weakref_impl*>(this)->mBase;
+}
+
+void RefBase::weakref_type::incWeak(const void* id)
+{
+ weakref_impl* const impl = static_cast<weakref_impl*>(this);
+ impl->addWeakRef(id);
+ const int32_t c __unused = android_atomic_inc(&impl->mWeak);
+ ALOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);
+}
+
+
+void RefBase::weakref_type::decWeak(const void* id)
+{
+ weakref_impl* const impl = static_cast<weakref_impl*>(this);
+ impl->removeWeakRef(id);
+ const int32_t c = android_atomic_dec(&impl->mWeak);
+ ALOG_ASSERT(c >= 1, "decWeak called on %p too many times", this);
+ if (c != 1) return;
+
+ if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) {
+ // This is the regular lifetime case. The object is destroyed
+ // when the last strong reference goes away. Since weakref_impl
+ // outlive the object, it is not destroyed in the dtor, and
+ // we'll have to do it here.
+ if (impl->mStrong == INITIAL_STRONG_VALUE) {
+ // Special case: we never had a strong reference, so we need to
+ // destroy the object now.
+ delete impl->mBase;
+ } else {
+ // ALOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase);
+ delete impl;
+ }
+ } else {
+ // less common case: lifetime is OBJECT_LIFETIME_{WEAK|FOREVER}
+ impl->mBase->onLastWeakRef(id);
+ if ((impl->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK) {
+ // this is the OBJECT_LIFETIME_WEAK case. The last weak-reference
+ // is gone, we can destroy the object.
+ delete impl->mBase;
+ }
+ }
+}
+
+bool RefBase::weakref_type::attemptIncStrong(const void* id)
+{
+ incWeak(id);
+
+ weakref_impl* const impl = static_cast<weakref_impl*>(this);
+ int32_t curCount = impl->mStrong;
+
+ ALOG_ASSERT(curCount >= 0,
+ "attemptIncStrong called on %p after underflow", this);
+
+ while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) {
+ // we're in the easy/common case of promoting a weak-reference
+ // from an existing strong reference.
+ if (android_atomic_cmpxchg(curCount, curCount+1, &impl->mStrong) == 0) {
+ break;
+ }
+ // the strong count has changed on us, we need to re-assert our
+ // situation.
+ curCount = impl->mStrong;
+ }
+
+ if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) {
+ // we're now in the harder case of either:
+ // - there never was a strong reference on us
+ // - or, all strong references have been released
+ if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) {
+ // this object has a "normal" life-time, i.e.: it gets destroyed
+ // when the last strong reference goes away
+ if (curCount <= 0) {
+ // the last strong-reference got released, the object cannot
+ // be revived.
+ decWeak(id);
+ return false;
+ }
+
+ // here, curCount == INITIAL_STRONG_VALUE, which means
+ // there never was a strong-reference, so we can try to
+ // promote this object; we need to do that atomically.
+ while (curCount > 0) {
+ if (android_atomic_cmpxchg(curCount, curCount + 1,
+ &impl->mStrong) == 0) {
+ break;
+ }
+ // the strong count has changed on us, we need to re-assert our
+ // situation (e.g.: another thread has inc/decStrong'ed us)
+ curCount = impl->mStrong;
+ }
+
+ if (curCount <= 0) {
+ // promote() failed, some other thread destroyed us in the
+ // meantime (i.e.: strong count reached zero).
+ decWeak(id);
+ return false;
+ }
+ } else {
+ // this object has an "extended" life-time, i.e.: it can be
+ // revived from a weak-reference only.
+ // Ask the object's implementation if it agrees to be revived
+ if (!impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id)) {
+ // it didn't so give-up.
+ decWeak(id);
+ return false;
+ }
+ // grab a strong-reference, which is always safe due to the
+ // extended life-time.
+ curCount = android_atomic_inc(&impl->mStrong);
+ }
+
+ // If the strong reference count has already been incremented by
+ // someone else, the implementor of onIncStrongAttempted() is holding
+ // an unneeded reference. So call onLastStrongRef() here to remove it.
+ // (No, this is not pretty.) Note that we MUST NOT do this if we
+ // are in fact acquiring the first reference.
+ if (curCount > 0 && curCount < INITIAL_STRONG_VALUE) {
+ impl->mBase->onLastStrongRef(id);
+ }
+ }
+
+ impl->addStrongRef(id);
+
+#if PRINT_REFS
+ ALOGD("attemptIncStrong of %p from %p: cnt=%d\n", this, id, curCount);
+#endif
+
+ // now we need to fix-up the count if it was INITIAL_STRONG_VALUE
+ // this must be done safely, i.e.: handle the case where several threads
+ // were here in attemptIncStrong().
+ curCount = impl->mStrong;
+ while (curCount >= INITIAL_STRONG_VALUE) {
+ ALOG_ASSERT(curCount > INITIAL_STRONG_VALUE,
+ "attemptIncStrong in %p underflowed to INITIAL_STRONG_VALUE",
+ this);
+ if (android_atomic_cmpxchg(curCount, curCount-INITIAL_STRONG_VALUE,
+ &impl->mStrong) == 0) {
+ break;
+ }
+ // the strong-count changed on us, we need to re-assert the situation,
+ // for e.g.: it's possible the fix-up happened in another thread.
+ curCount = impl->mStrong;
+ }
+
+ return true;
+}
+
+bool RefBase::weakref_type::attemptIncWeak(const void* id)
+{
+ weakref_impl* const impl = static_cast<weakref_impl*>(this);
+
+ int32_t curCount = impl->mWeak;
+ ALOG_ASSERT(curCount >= 0, "attemptIncWeak called on %p after underflow",
+ this);
+ while (curCount > 0) {
+ if (android_atomic_cmpxchg(curCount, curCount+1, &impl->mWeak) == 0) {
+ break;
+ }
+ curCount = impl->mWeak;
+ }
+
+ if (curCount > 0) {
+ impl->addWeakRef(id);
+ }
+
+ return curCount > 0;
+}
+
+int32_t RefBase::weakref_type::getWeakCount() const
+{
+ return static_cast<const weakref_impl*>(this)->mWeak;
+}
+
+void RefBase::weakref_type::printRefs() const
+{
+ static_cast<const weakref_impl*>(this)->printRefs();
+}
+
+void RefBase::weakref_type::trackMe(bool enable, bool retain)
+{
+ static_cast<weakref_impl*>(this)->trackMe(enable, retain);
+}
+
+RefBase::weakref_type* RefBase::createWeak(const void* id) const
+{
+ mRefs->incWeak(id);
+ return mRefs;
+}
+
+RefBase::weakref_type* RefBase::getWeakRefs() const
+{
+ return mRefs;
+}
+
+RefBase::RefBase()
+ : mRefs(new weakref_impl(this))
+{
+}
+
+RefBase::~RefBase()
+{
+ if (mRefs->mStrong == INITIAL_STRONG_VALUE) {
+ // we never acquired a strong (and/or weak) reference on this object.
+ delete mRefs;
+ } else {
+ // life-time of this object is extended to WEAK or FOREVER, in
+ // which case weakref_impl doesn't out-live the object and we
+ // can free it now.
+ if ((mRefs->mFlags & OBJECT_LIFETIME_MASK) != OBJECT_LIFETIME_STRONG) {
+ // It's possible that the weak count is not 0 if the object
+ // re-acquired a weak reference in its destructor
+ if (mRefs->mWeak == 0) {
+ delete mRefs;
+ }
+ }
+ }
+ // for debugging purposes, clear this.
+ const_cast<weakref_impl*&>(mRefs) = NULL;
+}
+
+void RefBase::extendObjectLifetime(int32_t mode)
+{
+ android_atomic_or(mode, &mRefs->mFlags);
+}
+
+void RefBase::onFirstRef()
+{
+}
+
+void RefBase::onLastStrongRef(const void* /*id*/)
+{
+}
+
+bool RefBase::onIncStrongAttempted(uint32_t flags, const void* /*id*/)
+{
+ return (flags&FIRST_INC_STRONG) ? true : false;
+}
+
+void RefBase::onLastWeakRef(const void* /*id*/)
+{
+}
+
+// ---------------------------------------------------------------------------
+
+#if DEBUG_REFS
+void RefBase::renameRefs(size_t n, const ReferenceRenamer& renamer) {
+ for (size_t i=0 ; i<n ; i++) {
+ renamer(i);
+ }
+}
+#else
+void RefBase::renameRefs(size_t /*n*/, const ReferenceRenamer& /*renamer*/) { }
+#endif
+
+void RefBase::renameRefId(weakref_type* ref,
+ const void* old_id, const void* new_id) {
+ weakref_impl* const impl = static_cast<weakref_impl*>(ref);
+ impl->renameStrongRefId(old_id, new_id);
+ impl->renameWeakRefId(old_id, new_id);
+}
+
+void RefBase::renameRefId(RefBase* ref,
+ const void* old_id, const void* new_id) {
+ ref->mRefs->renameStrongRefId(old_id, new_id);
+ ref->mRefs->renameWeakRefId(old_id, new_id);
+}
+
+}; // namespace android
diff --git a/libutils/SharedBuffer.cpp b/libutils/SharedBuffer.cpp
new file mode 100644
index 0000000..3555fb7
--- /dev/null
+++ b/libutils/SharedBuffer.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2005 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>
+
+#include <utils/SharedBuffer.h>
+#include <utils/Atomic.h>
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+SharedBuffer* SharedBuffer::alloc(size_t size)
+{
+ SharedBuffer* sb = static_cast<SharedBuffer *>(malloc(sizeof(SharedBuffer) + size));
+ if (sb) {
+ sb->mRefs = 1;
+ sb->mSize = size;
+ }
+ return sb;
+}
+
+
+ssize_t SharedBuffer::dealloc(const SharedBuffer* released)
+{
+ if (released->mRefs != 0) return -1; // XXX: invalid operation
+ free(const_cast<SharedBuffer*>(released));
+ return 0;
+}
+
+SharedBuffer* SharedBuffer::edit() const
+{
+ if (onlyOwner()) {
+ return const_cast<SharedBuffer*>(this);
+ }
+ SharedBuffer* sb = alloc(mSize);
+ if (sb) {
+ memcpy(sb->data(), data(), size());
+ release();
+ }
+ return sb;
+}
+
+SharedBuffer* SharedBuffer::editResize(size_t newSize) const
+{
+ if (onlyOwner()) {
+ SharedBuffer* buf = const_cast<SharedBuffer*>(this);
+ if (buf->mSize == newSize) return buf;
+ buf = (SharedBuffer*)realloc(buf, sizeof(SharedBuffer) + newSize);
+ if (buf != NULL) {
+ buf->mSize = newSize;
+ return buf;
+ }
+ }
+ SharedBuffer* sb = alloc(newSize);
+ if (sb) {
+ const size_t mySize = mSize;
+ memcpy(sb->data(), data(), newSize < mySize ? newSize : mySize);
+ release();
+ }
+ return sb;
+}
+
+SharedBuffer* SharedBuffer::attemptEdit() const
+{
+ if (onlyOwner()) {
+ return const_cast<SharedBuffer*>(this);
+ }
+ return 0;
+}
+
+SharedBuffer* SharedBuffer::reset(size_t new_size) const
+{
+ // cheap-o-reset.
+ SharedBuffer* sb = alloc(new_size);
+ if (sb) {
+ release();
+ }
+ return sb;
+}
+
+void SharedBuffer::acquire() const {
+ android_atomic_inc(&mRefs);
+}
+
+int32_t SharedBuffer::release(uint32_t flags) const
+{
+ int32_t prev = 1;
+ if (onlyOwner() || ((prev = android_atomic_dec(&mRefs)) == 1)) {
+ mRefs = 0;
+ if ((flags & eKeepStorage) == 0) {
+ free(const_cast<SharedBuffer*>(this));
+ }
+ }
+ return prev;
+}
+
+
+}; // namespace android
diff --git a/libutils/Static.cpp b/libutils/Static.cpp
new file mode 100644
index 0000000..3ed07a1
--- /dev/null
+++ b/libutils/Static.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// All static variables go here, to control initialization and
+// destruction order in the library.
+
+namespace android {
+
+// For String8.cpp
+extern void initialize_string8();
+extern void terminate_string8();
+
+// For String16.cpp
+extern void initialize_string16();
+extern void terminate_string16();
+
+class LibUtilsFirstStatics
+{
+public:
+ LibUtilsFirstStatics()
+ {
+ initialize_string8();
+ initialize_string16();
+ }
+
+ ~LibUtilsFirstStatics()
+ {
+ terminate_string16();
+ terminate_string8();
+ }
+};
+
+static LibUtilsFirstStatics gFirstStatics;
+int gDarwinCantLoadAllObjects = 1;
+
+} // namespace android
diff --git a/libutils/StopWatch.cpp b/libutils/StopWatch.cpp
new file mode 100644
index 0000000..8c7b596
--- /dev/null
+++ b/libutils/StopWatch.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2005 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 "StopWatch"
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+/* for PRId64 */
+#ifndef __STDC_FORMAT_MACROS
+#define __STDC_FORMAT_MACROS 1
+#endif
+#include <inttypes.h>
+
+#include <utils/Log.h>
+#include <utils/Errors.h>
+#include <utils/StopWatch.h>
+
+/*****************************************************************************/
+
+namespace android {
+
+
+StopWatch::StopWatch(const char *name, int clock, uint32_t flags)
+ : mName(name), mClock(clock), mFlags(flags)
+{
+ reset();
+}
+
+StopWatch::~StopWatch()
+{
+ nsecs_t elapsed = elapsedTime();
+ const int n = mNumLaps;
+ ALOGD("StopWatch %s (us): %" PRId64 " ", mName, ns2us(elapsed));
+ for (int i=0 ; i<n ; i++) {
+ const nsecs_t soFar = mLaps[i].soFar;
+ const nsecs_t thisLap = mLaps[i].thisLap;
+ ALOGD(" [%d: %" PRId64 ", %" PRId64, i, ns2us(soFar), ns2us(thisLap));
+ }
+}
+
+const char* StopWatch::name() const
+{
+ return mName;
+}
+
+nsecs_t StopWatch::lap()
+{
+ nsecs_t elapsed = elapsedTime();
+ if (mNumLaps >= 8) {
+ elapsed = 0;
+ } else {
+ const int n = mNumLaps;
+ mLaps[n].soFar = elapsed;
+ mLaps[n].thisLap = n ? (elapsed - mLaps[n-1].soFar) : elapsed;
+ mNumLaps = n+1;
+ }
+ return elapsed;
+}
+
+nsecs_t StopWatch::elapsedTime() const
+{
+ return systemTime(mClock) - mStartTime;
+}
+
+void StopWatch::reset()
+{
+ mNumLaps = 0;
+ mStartTime = systemTime(mClock);
+}
+
+
+/*****************************************************************************/
+
+}; // namespace android
+
diff --git a/libutils/String16.cpp b/libutils/String16.cpp
new file mode 100644
index 0000000..91efdaa
--- /dev/null
+++ b/libutils/String16.cpp
@@ -0,0 +1,419 @@
+/*
+ * Copyright (C) 2005 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 <utils/String16.h>
+
+#include <utils/Log.h>
+#include <utils/Unicode.h>
+#include <utils/String8.h>
+#include <utils/threads.h>
+
+#include <memory.h>
+#include <stdio.h>
+#include <ctype.h>
+
+
+namespace android {
+
+static SharedBuffer* gEmptyStringBuf = NULL;
+static char16_t* gEmptyString = NULL;
+
+static inline char16_t* getEmptyString()
+{
+ gEmptyStringBuf->acquire();
+ return gEmptyString;
+}
+
+void initialize_string16()
+{
+ SharedBuffer* buf = SharedBuffer::alloc(sizeof(char16_t));
+ char16_t* str = (char16_t*)buf->data();
+ *str = 0;
+ gEmptyStringBuf = buf;
+ gEmptyString = str;
+}
+
+void terminate_string16()
+{
+ SharedBuffer::bufferFromData(gEmptyString)->release();
+ gEmptyStringBuf = NULL;
+ gEmptyString = NULL;
+}
+
+// ---------------------------------------------------------------------------
+
+static char16_t* allocFromUTF8(const char* u8str, size_t u8len)
+{
+ if (u8len == 0) return getEmptyString();
+
+ const uint8_t* u8cur = (const uint8_t*) u8str;
+
+ const ssize_t u16len = utf8_to_utf16_length(u8cur, u8len);
+ if (u16len < 0) {
+ return getEmptyString();
+ }
+
+ SharedBuffer* buf = SharedBuffer::alloc(sizeof(char16_t)*(u16len+1));
+ if (buf) {
+ u8cur = (const uint8_t*) u8str;
+ char16_t* u16str = (char16_t*)buf->data();
+
+ utf8_to_utf16(u8cur, u8len, u16str);
+
+ //printf("Created UTF-16 string from UTF-8 \"%s\":", in);
+ //printHexData(1, str, buf->size(), 16, 1);
+ //printf("\n");
+
+ return u16str;
+ }
+
+ return getEmptyString();
+}
+
+// ---------------------------------------------------------------------------
+
+String16::String16()
+ : mString(getEmptyString())
+{
+}
+
+String16::String16(StaticLinkage)
+ : mString(0)
+{
+ // this constructor is used when we can't rely on the static-initializers
+ // having run. In this case we always allocate an empty string. It's less
+ // efficient than using getEmptyString(), but we assume it's uncommon.
+
+ char16_t* data = static_cast<char16_t*>(
+ SharedBuffer::alloc(sizeof(char16_t))->data());
+ data[0] = 0;
+ mString = data;
+}
+
+String16::String16(const String16& o)
+ : mString(o.mString)
+{
+ SharedBuffer::bufferFromData(mString)->acquire();
+}
+
+String16::String16(const String16& o, size_t len, size_t begin)
+ : mString(getEmptyString())
+{
+ setTo(o, len, begin);
+}
+
+String16::String16(const char16_t* o)
+{
+ size_t len = strlen16(o);
+ SharedBuffer* buf = SharedBuffer::alloc((len+1)*sizeof(char16_t));
+ ALOG_ASSERT(buf, "Unable to allocate shared buffer");
+ if (buf) {
+ char16_t* str = (char16_t*)buf->data();
+ strcpy16(str, o);
+ mString = str;
+ return;
+ }
+
+ mString = getEmptyString();
+}
+
+String16::String16(const char16_t* o, size_t len)
+{
+ SharedBuffer* buf = SharedBuffer::alloc((len+1)*sizeof(char16_t));
+ ALOG_ASSERT(buf, "Unable to allocate shared buffer");
+ if (buf) {
+ char16_t* str = (char16_t*)buf->data();
+ memcpy(str, o, len*sizeof(char16_t));
+ str[len] = 0;
+ mString = str;
+ return;
+ }
+
+ mString = getEmptyString();
+}
+
+String16::String16(const String8& o)
+ : mString(allocFromUTF8(o.string(), o.size()))
+{
+}
+
+String16::String16(const char* o)
+ : mString(allocFromUTF8(o, strlen(o)))
+{
+}
+
+String16::String16(const char* o, size_t len)
+ : mString(allocFromUTF8(o, len))
+{
+}
+
+String16::~String16()
+{
+ SharedBuffer::bufferFromData(mString)->release();
+}
+
+void String16::setTo(const String16& other)
+{
+ SharedBuffer::bufferFromData(other.mString)->acquire();
+ SharedBuffer::bufferFromData(mString)->release();
+ mString = other.mString;
+}
+
+status_t String16::setTo(const String16& other, size_t len, size_t begin)
+{
+ const size_t N = other.size();
+ if (begin >= N) {
+ SharedBuffer::bufferFromData(mString)->release();
+ mString = getEmptyString();
+ return NO_ERROR;
+ }
+ if ((begin+len) > N) len = N-begin;
+ if (begin == 0 && len == N) {
+ setTo(other);
+ return NO_ERROR;
+ }
+
+ if (&other == this) {
+ LOG_ALWAYS_FATAL("Not implemented");
+ }
+
+ return setTo(other.string()+begin, len);
+}
+
+status_t String16::setTo(const char16_t* other)
+{
+ return setTo(other, strlen16(other));
+}
+
+status_t String16::setTo(const char16_t* other, size_t len)
+{
+ SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
+ ->editResize((len+1)*sizeof(char16_t));
+ if (buf) {
+ char16_t* str = (char16_t*)buf->data();
+ memmove(str, other, len*sizeof(char16_t));
+ str[len] = 0;
+ mString = str;
+ return NO_ERROR;
+ }
+ return NO_MEMORY;
+}
+
+status_t String16::append(const String16& other)
+{
+ const size_t myLen = size();
+ const size_t otherLen = other.size();
+ if (myLen == 0) {
+ setTo(other);
+ return NO_ERROR;
+ } else if (otherLen == 0) {
+ return NO_ERROR;
+ }
+
+ SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
+ ->editResize((myLen+otherLen+1)*sizeof(char16_t));
+ if (buf) {
+ char16_t* str = (char16_t*)buf->data();
+ memcpy(str+myLen, other, (otherLen+1)*sizeof(char16_t));
+ mString = str;
+ return NO_ERROR;
+ }
+ return NO_MEMORY;
+}
+
+status_t String16::append(const char16_t* chrs, size_t otherLen)
+{
+ const size_t myLen = size();
+ if (myLen == 0) {
+ setTo(chrs, otherLen);
+ return NO_ERROR;
+ } else if (otherLen == 0) {
+ return NO_ERROR;
+ }
+
+ SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
+ ->editResize((myLen+otherLen+1)*sizeof(char16_t));
+ if (buf) {
+ char16_t* str = (char16_t*)buf->data();
+ memcpy(str+myLen, chrs, otherLen*sizeof(char16_t));
+ str[myLen+otherLen] = 0;
+ mString = str;
+ return NO_ERROR;
+ }
+ return NO_MEMORY;
+}
+
+status_t String16::insert(size_t pos, const char16_t* chrs)
+{
+ return insert(pos, chrs, strlen16(chrs));
+}
+
+status_t String16::insert(size_t pos, const char16_t* chrs, size_t len)
+{
+ const size_t myLen = size();
+ if (myLen == 0) {
+ return setTo(chrs, len);
+ return NO_ERROR;
+ } else if (len == 0) {
+ return NO_ERROR;
+ }
+
+ if (pos > myLen) pos = myLen;
+
+ #if 0
+ printf("Insert in to %s: pos=%d, len=%d, myLen=%d, chrs=%s\n",
+ String8(*this).string(), pos,
+ len, myLen, String8(chrs, len).string());
+ #endif
+
+ SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
+ ->editResize((myLen+len+1)*sizeof(char16_t));
+ if (buf) {
+ char16_t* str = (char16_t*)buf->data();
+ if (pos < myLen) {
+ memmove(str+pos+len, str+pos, (myLen-pos)*sizeof(char16_t));
+ }
+ memcpy(str+pos, chrs, len*sizeof(char16_t));
+ str[myLen+len] = 0;
+ mString = str;
+ #if 0
+ printf("Result (%d chrs): %s\n", size(), String8(*this).string());
+ #endif
+ return NO_ERROR;
+ }
+ return NO_MEMORY;
+}
+
+ssize_t String16::findFirst(char16_t c) const
+{
+ const char16_t* str = string();
+ const char16_t* p = str;
+ const char16_t* e = p + size();
+ while (p < e) {
+ if (*p == c) {
+ return p-str;
+ }
+ p++;
+ }
+ return -1;
+}
+
+ssize_t String16::findLast(char16_t c) const
+{
+ const char16_t* str = string();
+ const char16_t* p = str;
+ const char16_t* e = p + size();
+ while (p < e) {
+ e--;
+ if (*e == c) {
+ return e-str;
+ }
+ }
+ return -1;
+}
+
+bool String16::startsWith(const String16& prefix) const
+{
+ const size_t ps = prefix.size();
+ if (ps > size()) return false;
+ return strzcmp16(mString, ps, prefix.string(), ps) == 0;
+}
+
+bool String16::startsWith(const char16_t* prefix) const
+{
+ const size_t ps = strlen16(prefix);
+ if (ps > size()) return false;
+ return strncmp16(mString, prefix, ps) == 0;
+}
+
+status_t String16::makeLower()
+{
+ const size_t N = size();
+ const char16_t* str = string();
+ char16_t* edit = NULL;
+ for (size_t i=0; i<N; i++) {
+ const char16_t v = str[i];
+ if (v >= 'A' && v <= 'Z') {
+ if (!edit) {
+ SharedBuffer* buf = SharedBuffer::bufferFromData(mString)->edit();
+ if (!buf) {
+ return NO_MEMORY;
+ }
+ edit = (char16_t*)buf->data();
+ mString = str = edit;
+ }
+ edit[i] = tolower((char)v);
+ }
+ }
+ return NO_ERROR;
+}
+
+status_t String16::replaceAll(char16_t replaceThis, char16_t withThis)
+{
+ const size_t N = size();
+ const char16_t* str = string();
+ char16_t* edit = NULL;
+ for (size_t i=0; i<N; i++) {
+ if (str[i] == replaceThis) {
+ if (!edit) {
+ SharedBuffer* buf = SharedBuffer::bufferFromData(mString)->edit();
+ if (!buf) {
+ return NO_MEMORY;
+ }
+ edit = (char16_t*)buf->data();
+ mString = str = edit;
+ }
+ edit[i] = withThis;
+ }
+ }
+ return NO_ERROR;
+}
+
+status_t String16::remove(size_t len, size_t begin)
+{
+ const size_t N = size();
+ if (begin >= N) {
+ SharedBuffer::bufferFromData(mString)->release();
+ mString = getEmptyString();
+ return NO_ERROR;
+ }
+ if ((begin+len) > N) len = N-begin;
+ if (begin == 0 && len == N) {
+ return NO_ERROR;
+ }
+
+ if (begin > 0) {
+ SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
+ ->editResize((N+1)*sizeof(char16_t));
+ if (!buf) {
+ return NO_MEMORY;
+ }
+ char16_t* str = (char16_t*)buf->data();
+ memmove(str, str+begin, (N-begin+1)*sizeof(char16_t));
+ mString = str;
+ }
+ SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
+ ->editResize((len+1)*sizeof(char16_t));
+ if (buf) {
+ char16_t* str = (char16_t*)buf->data();
+ str[len] = 0;
+ mString = str;
+ return NO_ERROR;
+ }
+ return NO_MEMORY;
+}
+
+}; // namespace android
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
new file mode 100644
index 0000000..3323b82
--- /dev/null
+++ b/libutils/String8.cpp
@@ -0,0 +1,672 @@
+/*
+ * Copyright (C) 2005 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 <utils/String8.h>
+
+#include <utils/Log.h>
+#include <utils/Unicode.h>
+#include <utils/SharedBuffer.h>
+#include <utils/String16.h>
+#include <utils/threads.h>
+
+#include <ctype.h>
+
+/*
+ * Functions outside android is below the namespace android, since they use
+ * functions and constants in android namespace.
+ */
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+// Separator used by resource paths. This is not platform dependent contrary
+// to OS_PATH_SEPARATOR.
+#define RES_PATH_SEPARATOR '/'
+
+static SharedBuffer* gEmptyStringBuf = NULL;
+static char* gEmptyString = NULL;
+
+extern int gDarwinCantLoadAllObjects;
+int gDarwinIsReallyAnnoying;
+
+void initialize_string8();
+
+static inline char* getEmptyString()
+{
+ gEmptyStringBuf->acquire();
+ return gEmptyString;
+}
+
+void initialize_string8()
+{
+ // HACK: This dummy dependency forces linking libutils Static.cpp,
+ // which is needed to initialize String8/String16 classes.
+ // These variables are named for Darwin, but are needed elsewhere too,
+ // including static linking on any platform.
+ gDarwinIsReallyAnnoying = gDarwinCantLoadAllObjects;
+
+ SharedBuffer* buf = SharedBuffer::alloc(1);
+ char* str = (char*)buf->data();
+ *str = 0;
+ gEmptyStringBuf = buf;
+ gEmptyString = str;
+}
+
+void terminate_string8()
+{
+ SharedBuffer::bufferFromData(gEmptyString)->release();
+ gEmptyStringBuf = NULL;
+ gEmptyString = NULL;
+}
+
+// ---------------------------------------------------------------------------
+
+static char* allocFromUTF8(const char* in, size_t len)
+{
+ if (len > 0) {
+ SharedBuffer* buf = SharedBuffer::alloc(len+1);
+ ALOG_ASSERT(buf, "Unable to allocate shared buffer");
+ if (buf) {
+ char* str = (char*)buf->data();
+ memcpy(str, in, len);
+ str[len] = 0;
+ return str;
+ }
+ return NULL;
+ }
+
+ return getEmptyString();
+}
+
+static char* allocFromUTF16(const char16_t* in, size_t len)
+{
+ if (len == 0) return getEmptyString();
+
+ const ssize_t bytes = utf16_to_utf8_length(in, len);
+ if (bytes < 0) {
+ return getEmptyString();
+ }
+
+ SharedBuffer* buf = SharedBuffer::alloc(bytes+1);
+ ALOG_ASSERT(buf, "Unable to allocate shared buffer");
+ if (!buf) {
+ return getEmptyString();
+ }
+
+ char* str = (char*)buf->data();
+ utf16_to_utf8(in, len, str);
+ return str;
+}
+
+static char* allocFromUTF32(const char32_t* in, size_t len)
+{
+ if (len == 0) {
+ return getEmptyString();
+ }
+
+ const ssize_t bytes = utf32_to_utf8_length(in, len);
+ if (bytes < 0) {
+ return getEmptyString();
+ }
+
+ SharedBuffer* buf = SharedBuffer::alloc(bytes+1);
+ ALOG_ASSERT(buf, "Unable to allocate shared buffer");
+ if (!buf) {
+ return getEmptyString();
+ }
+
+ char* str = (char*) buf->data();
+ utf32_to_utf8(in, len, str);
+
+ return str;
+}
+
+// ---------------------------------------------------------------------------
+
+String8::String8()
+ : mString(getEmptyString())
+{
+}
+
+String8::String8(StaticLinkage)
+ : mString(0)
+{
+ // this constructor is used when we can't rely on the static-initializers
+ // having run. In this case we always allocate an empty string. It's less
+ // efficient than using getEmptyString(), but we assume it's uncommon.
+
+ char* data = static_cast<char*>(
+ SharedBuffer::alloc(sizeof(char))->data());
+ data[0] = 0;
+ mString = data;
+}
+
+String8::String8(const String8& o)
+ : mString(o.mString)
+{
+ SharedBuffer::bufferFromData(mString)->acquire();
+}
+
+String8::String8(const char* o)
+ : mString(allocFromUTF8(o, strlen(o)))
+{
+ if (mString == NULL) {
+ mString = getEmptyString();
+ }
+}
+
+String8::String8(const char* o, size_t len)
+ : mString(allocFromUTF8(o, len))
+{
+ if (mString == NULL) {
+ mString = getEmptyString();
+ }
+}
+
+String8::String8(const String16& o)
+ : mString(allocFromUTF16(o.string(), o.size()))
+{
+}
+
+String8::String8(const char16_t* o)
+ : mString(allocFromUTF16(o, strlen16(o)))
+{
+}
+
+String8::String8(const char16_t* o, size_t len)
+ : mString(allocFromUTF16(o, len))
+{
+}
+
+String8::String8(const char32_t* o)
+ : mString(allocFromUTF32(o, strlen32(o)))
+{
+}
+
+String8::String8(const char32_t* o, size_t len)
+ : mString(allocFromUTF32(o, len))
+{
+}
+
+String8::~String8()
+{
+ SharedBuffer::bufferFromData(mString)->release();
+}
+
+String8 String8::format(const char* fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+
+ String8 result(formatV(fmt, args));
+
+ va_end(args);
+ return result;
+}
+
+String8 String8::formatV(const char* fmt, va_list args)
+{
+ String8 result;
+ result.appendFormatV(fmt, args);
+ return result;
+}
+
+void String8::clear() {
+ SharedBuffer::bufferFromData(mString)->release();
+ mString = getEmptyString();
+}
+
+void String8::setTo(const String8& other)
+{
+ SharedBuffer::bufferFromData(other.mString)->acquire();
+ SharedBuffer::bufferFromData(mString)->release();
+ mString = other.mString;
+}
+
+status_t String8::setTo(const char* other)
+{
+ const char *newString = allocFromUTF8(other, strlen(other));
+ SharedBuffer::bufferFromData(mString)->release();
+ mString = newString;
+ if (mString) return NO_ERROR;
+
+ mString = getEmptyString();
+ return NO_MEMORY;
+}
+
+status_t String8::setTo(const char* other, size_t len)
+{
+ const char *newString = allocFromUTF8(other, len);
+ SharedBuffer::bufferFromData(mString)->release();
+ mString = newString;
+ if (mString) return NO_ERROR;
+
+ mString = getEmptyString();
+ return NO_MEMORY;
+}
+
+status_t String8::setTo(const char16_t* other, size_t len)
+{
+ const char *newString = allocFromUTF16(other, len);
+ SharedBuffer::bufferFromData(mString)->release();
+ mString = newString;
+ if (mString) return NO_ERROR;
+
+ mString = getEmptyString();
+ return NO_MEMORY;
+}
+
+status_t String8::setTo(const char32_t* other, size_t len)
+{
+ const char *newString = allocFromUTF32(other, len);
+ SharedBuffer::bufferFromData(mString)->release();
+ mString = newString;
+ if (mString) return NO_ERROR;
+
+ mString = getEmptyString();
+ return NO_MEMORY;
+}
+
+status_t String8::append(const String8& other)
+{
+ const size_t otherLen = other.bytes();
+ if (bytes() == 0) {
+ setTo(other);
+ return NO_ERROR;
+ } else if (otherLen == 0) {
+ return NO_ERROR;
+ }
+
+ return real_append(other.string(), otherLen);
+}
+
+status_t String8::append(const char* other)
+{
+ return append(other, strlen(other));
+}
+
+status_t String8::append(const char* other, size_t otherLen)
+{
+ if (bytes() == 0) {
+ return setTo(other, otherLen);
+ } else if (otherLen == 0) {
+ return NO_ERROR;
+ }
+
+ return real_append(other, otherLen);
+}
+
+status_t String8::appendFormat(const char* fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+
+ status_t result = appendFormatV(fmt, args);
+
+ va_end(args);
+ return result;
+}
+
+status_t String8::appendFormatV(const char* fmt, va_list args)
+{
+ int n, result = NO_ERROR;
+ va_list tmp_args;
+
+ /* args is undefined after vsnprintf.
+ * So we need a copy here to avoid the
+ * second vsnprintf access undefined args.
+ */
+ va_copy(tmp_args, args);
+ n = vsnprintf(NULL, 0, fmt, tmp_args);
+ va_end(tmp_args);
+
+ if (n != 0) {
+ size_t oldLength = length();
+ char* buf = lockBuffer(oldLength + n);
+ if (buf) {
+ vsnprintf(buf + oldLength, n + 1, fmt, args);
+ } else {
+ result = NO_MEMORY;
+ }
+ }
+ return result;
+}
+
+status_t String8::real_append(const char* other, size_t otherLen)
+{
+ const size_t myLen = bytes();
+
+ SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
+ ->editResize(myLen+otherLen+1);
+ if (buf) {
+ char* str = (char*)buf->data();
+ mString = str;
+ str += myLen;
+ memcpy(str, other, otherLen);
+ str[otherLen] = '\0';
+ return NO_ERROR;
+ }
+ return NO_MEMORY;
+}
+
+char* String8::lockBuffer(size_t size)
+{
+ SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
+ ->editResize(size+1);
+ if (buf) {
+ char* str = (char*)buf->data();
+ mString = str;
+ return str;
+ }
+ return NULL;
+}
+
+void String8::unlockBuffer()
+{
+ unlockBuffer(strlen(mString));
+}
+
+status_t String8::unlockBuffer(size_t size)
+{
+ if (size != this->size()) {
+ SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
+ ->editResize(size+1);
+ if (! buf) {
+ return NO_MEMORY;
+ }
+
+ char* str = (char*)buf->data();
+ str[size] = 0;
+ mString = str;
+ }
+
+ return NO_ERROR;
+}
+
+ssize_t String8::find(const char* other, size_t start) const
+{
+ size_t len = size();
+ if (start >= len) {
+ return -1;
+ }
+ const char* s = mString+start;
+ const char* p = strstr(s, other);
+ return p ? p-mString : -1;
+}
+
+bool String8::removeAll(const char* other) {
+ ssize_t index = find(other);
+ if (index < 0) return false;
+
+ char* buf = lockBuffer(size());
+ if (!buf) return false; // out of memory
+
+ size_t skip = strlen(other);
+ size_t len = size();
+ size_t tail = index;
+ while (size_t(index) < len) {
+ ssize_t next = find(other, index + skip);
+ if (next < 0) {
+ next = len;
+ }
+
+ memmove(buf + tail, buf + index + skip, next - index - skip);
+ tail += next - index - skip;
+ index = next;
+ }
+ unlockBuffer(tail);
+ return true;
+}
+
+void String8::toLower()
+{
+ toLower(0, size());
+}
+
+void String8::toLower(size_t start, size_t length)
+{
+ const size_t len = size();
+ if (start >= len) {
+ return;
+ }
+ if (start+length > len) {
+ length = len-start;
+ }
+ char* buf = lockBuffer(len);
+ buf += start;
+ while (length > 0) {
+ *buf = tolower(*buf);
+ buf++;
+ length--;
+ }
+ unlockBuffer(len);
+}
+
+void String8::toUpper()
+{
+ toUpper(0, size());
+}
+
+void String8::toUpper(size_t start, size_t length)
+{
+ const size_t len = size();
+ if (start >= len) {
+ return;
+ }
+ if (start+length > len) {
+ length = len-start;
+ }
+ char* buf = lockBuffer(len);
+ buf += start;
+ while (length > 0) {
+ *buf = toupper(*buf);
+ buf++;
+ length--;
+ }
+ unlockBuffer(len);
+}
+
+size_t String8::getUtf32Length() const
+{
+ return utf8_to_utf32_length(mString, length());
+}
+
+int32_t String8::getUtf32At(size_t index, size_t *next_index) const
+{
+ return utf32_from_utf8_at(mString, length(), index, next_index);
+}
+
+void String8::getUtf32(char32_t* dst) const
+{
+ utf8_to_utf32(mString, length(), dst);
+}
+
+// ---------------------------------------------------------------------------
+// Path functions
+
+void String8::setPathName(const char* name)
+{
+ setPathName(name, strlen(name));
+}
+
+void String8::setPathName(const char* name, size_t len)
+{
+ char* buf = lockBuffer(len);
+
+ memcpy(buf, name, len);
+
+ // remove trailing path separator, if present
+ if (len > 0 && buf[len-1] == OS_PATH_SEPARATOR)
+ len--;
+
+ buf[len] = '\0';
+
+ unlockBuffer(len);
+}
+
+String8 String8::getPathLeaf(void) const
+{
+ const char* cp;
+ const char*const buf = mString;
+
+ cp = strrchr(buf, OS_PATH_SEPARATOR);
+ if (cp == NULL)
+ return String8(*this);
+ else
+ return String8(cp+1);
+}
+
+String8 String8::getPathDir(void) const
+{
+ const char* cp;
+ const char*const str = mString;
+
+ cp = strrchr(str, OS_PATH_SEPARATOR);
+ if (cp == NULL)
+ return String8("");
+ else
+ return String8(str, cp - str);
+}
+
+String8 String8::walkPath(String8* outRemains) const
+{
+ const char* cp;
+ const char*const str = mString;
+ const char* buf = str;
+
+ cp = strchr(buf, OS_PATH_SEPARATOR);
+ if (cp == buf) {
+ // don't include a leading '/'.
+ buf = buf+1;
+ cp = strchr(buf, OS_PATH_SEPARATOR);
+ }
+
+ if (cp == NULL) {
+ String8 res = buf != str ? String8(buf) : *this;
+ if (outRemains) *outRemains = String8("");
+ return res;
+ }
+
+ String8 res(buf, cp-buf);
+ if (outRemains) *outRemains = String8(cp+1);
+ return res;
+}
+
+/*
+ * Helper function for finding the start of an extension in a pathname.
+ *
+ * Returns a pointer inside mString, or NULL if no extension was found.
+ */
+char* String8::find_extension(void) const
+{
+ const char* lastSlash;
+ const char* lastDot;
+ const char* const str = mString;
+
+ // only look at the filename
+ lastSlash = strrchr(str, OS_PATH_SEPARATOR);
+ if (lastSlash == NULL)
+ lastSlash = str;
+ else
+ lastSlash++;
+
+ // find the last dot
+ lastDot = strrchr(lastSlash, '.');
+ if (lastDot == NULL)
+ return NULL;
+
+ // looks good, ship it
+ return const_cast<char*>(lastDot);
+}
+
+String8 String8::getPathExtension(void) const
+{
+ char* ext;
+
+ ext = find_extension();
+ if (ext != NULL)
+ return String8(ext);
+ else
+ return String8("");
+}
+
+String8 String8::getBasePath(void) const
+{
+ char* ext;
+ const char* const str = mString;
+
+ ext = find_extension();
+ if (ext == NULL)
+ return String8(*this);
+ else
+ return String8(str, ext - str);
+}
+
+String8& String8::appendPath(const char* name)
+{
+ // TODO: The test below will fail for Win32 paths. Fix later or ignore.
+ if (name[0] != OS_PATH_SEPARATOR) {
+ if (*name == '\0') {
+ // nothing to do
+ return *this;
+ }
+
+ size_t len = length();
+ if (len == 0) {
+ // no existing filename, just use the new one
+ setPathName(name);
+ return *this;
+ }
+
+ // make room for oldPath + '/' + newPath
+ int newlen = strlen(name);
+
+ char* buf = lockBuffer(len+1+newlen);
+
+ // insert a '/' if needed
+ if (buf[len-1] != OS_PATH_SEPARATOR)
+ buf[len++] = OS_PATH_SEPARATOR;
+
+ memcpy(buf+len, name, newlen+1);
+ len += newlen;
+
+ unlockBuffer(len);
+
+ return *this;
+ } else {
+ setPathName(name);
+ return *this;
+ }
+}
+
+String8& String8::convertToResPath()
+{
+#if OS_PATH_SEPARATOR != RES_PATH_SEPARATOR
+ size_t len = length();
+ if (len > 0) {
+ char * buf = lockBuffer(len);
+ for (char * end = buf + len; buf < end; ++buf) {
+ if (*buf == OS_PATH_SEPARATOR)
+ *buf = RES_PATH_SEPARATOR;
+ }
+ unlockBuffer(len);
+ }
+#endif
+ return *this;
+}
+
+}; // namespace android
diff --git a/libutils/SystemClock.cpp b/libutils/SystemClock.cpp
new file mode 100644
index 0000000..64204a8
--- /dev/null
+++ b/libutils/SystemClock.cpp
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/*
+ * System clock functions.
+ */
+
+#ifdef HAVE_ANDROID_OS
+#include <linux/ioctl.h>
+#include <linux/rtc.h>
+#include <utils/Atomic.h>
+#include <linux/android_alarm.h>
+#endif
+
+#include <sys/time.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include <utils/SystemClock.h>
+#include <utils/Timers.h>
+
+#define LOG_TAG "SystemClock"
+#include <utils/Log.h>
+
+namespace android {
+
+/*
+ * native public static long uptimeMillis();
+ */
+int64_t uptimeMillis()
+{
+ int64_t when = systemTime(SYSTEM_TIME_MONOTONIC);
+ return (int64_t) nanoseconds_to_milliseconds(when);
+}
+
+/*
+ * native public static long elapsedRealtime();
+ */
+int64_t elapsedRealtime()
+{
+ return nanoseconds_to_milliseconds(elapsedRealtimeNano());
+}
+
+#define METHOD_CLOCK_GETTIME 0
+#define METHOD_IOCTL 1
+#define METHOD_SYSTEMTIME 2
+
+/*
+ * To debug/verify the timestamps returned by the kernel, change
+ * DEBUG_TIMESTAMP to 1 and call the timestamp routine from a single thread
+ * in the test program. b/10899829
+ */
+#define DEBUG_TIMESTAMP 0
+
+#if DEBUG_TIMESTAMP && defined(__arm__)
+static inline void checkTimeStamps(int64_t timestamp,
+ int64_t volatile *prevTimestampPtr,
+ int volatile *prevMethodPtr,
+ int curMethod)
+{
+ /*
+ * Disable the check for SDK since the prebuilt toolchain doesn't contain
+ * gettid, and int64_t is different on the ARM platform
+ * (ie long vs long long).
+ */
+ int64_t prevTimestamp = *prevTimestampPtr;
+ int prevMethod = *prevMethodPtr;
+
+ if (timestamp < prevTimestamp) {
+ static const char *gettime_method_names[] = {
+ "clock_gettime",
+ "ioctl",
+ "systemTime",
+ };
+
+ ALOGW("time going backwards: prev %lld(%s) vs now %lld(%s), tid=%d",
+ prevTimestamp, gettime_method_names[prevMethod],
+ timestamp, gettime_method_names[curMethod],
+ gettid());
+ }
+ // NOTE - not atomic and may generate spurious warnings if the 64-bit
+ // write is interrupted or not observed as a whole.
+ *prevTimestampPtr = timestamp;
+ *prevMethodPtr = curMethod;
+}
+#else
+#define checkTimeStamps(timestamp, prevTimestampPtr, prevMethodPtr, curMethod)
+#endif
+
+/*
+ * native public static long elapsedRealtimeNano();
+ */
+int64_t elapsedRealtimeNano()
+{
+#ifdef HAVE_ANDROID_OS
+ struct timespec ts;
+ int result;
+ int64_t timestamp;
+#if DEBUG_TIMESTAMP
+ static volatile int64_t prevTimestamp;
+ static volatile int prevMethod;
+#endif
+
+ static int s_fd = -1;
+
+ if (s_fd == -1) {
+ int fd = open("/dev/alarm", O_RDONLY);
+ if (android_atomic_cmpxchg(-1, fd, &s_fd)) {
+ close(fd);
+ }
+ }
+
+ result = ioctl(s_fd,
+ ANDROID_ALARM_GET_TIME(ANDROID_ALARM_ELAPSED_REALTIME), &ts);
+
+ if (result == 0) {
+ timestamp = seconds_to_nanoseconds(ts.tv_sec) + ts.tv_nsec;
+ checkTimeStamps(timestamp, &prevTimestamp, &prevMethod, METHOD_IOCTL);
+ return timestamp;
+ }
+
+ // /dev/alarm doesn't exist, fallback to CLOCK_BOOTTIME
+ result = clock_gettime(CLOCK_BOOTTIME, &ts);
+ if (result == 0) {
+ timestamp = seconds_to_nanoseconds(ts.tv_sec) + ts.tv_nsec;
+ checkTimeStamps(timestamp, &prevTimestamp, &prevMethod,
+ METHOD_CLOCK_GETTIME);
+ return timestamp;
+ }
+
+ // XXX: there was an error, probably because the driver didn't
+ // exist ... this should return
+ // a real error, like an exception!
+ timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
+ checkTimeStamps(timestamp, &prevTimestamp, &prevMethod,
+ METHOD_SYSTEMTIME);
+ return timestamp;
+#else
+ return systemTime(SYSTEM_TIME_MONOTONIC);
+#endif
+}
+
+}; // namespace android
diff --git a/libutils/Threads.cpp b/libutils/Threads.cpp
new file mode 100644
index 0000000..c3666e4
--- /dev/null
+++ b/libutils/Threads.cpp
@@ -0,0 +1,867 @@
+/*
+ * Copyright (C) 2007 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_NDEBUG 0
+#define LOG_TAG "libutils.threads"
+
+#include <assert.h>
+#include <errno.h>
+#include <memory.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#if !defined(_WIN32)
+# include <pthread.h>
+# include <sched.h>
+# include <sys/resource.h>
+#else
+# include <windows.h>
+# include <stdint.h>
+# include <process.h>
+# define HAVE_CREATETHREAD // Cygwin, vs. HAVE__BEGINTHREADEX for MinGW
+#endif
+
+#if defined(__linux__)
+#include <sys/prctl.h>
+#endif
+
+#include <utils/threads.h>
+#include <utils/Log.h>
+
+#include <cutils/sched_policy.h>
+
+#ifdef HAVE_ANDROID_OS
+# define __android_unused
+#else
+# define __android_unused __attribute__((__unused__))
+#endif
+
+/*
+ * ===========================================================================
+ * Thread wrappers
+ * ===========================================================================
+ */
+
+using namespace android;
+
+// ----------------------------------------------------------------------------
+#if !defined(_WIN32)
+// ----------------------------------------------------------------------------
+
+/*
+ * Create and run a new thread.
+ *
+ * We create it "detached", so it cleans up after itself.
+ */
+
+typedef void* (*android_pthread_entry)(void*);
+
+struct thread_data_t {
+ thread_func_t entryFunction;
+ void* userData;
+ int priority;
+ char * threadName;
+
+ // we use this trampoline when we need to set the priority with
+ // nice/setpriority, and name with prctl.
+ static int trampoline(const thread_data_t* t) {
+ thread_func_t f = t->entryFunction;
+ void* u = t->userData;
+ int prio = t->priority;
+ char * name = t->threadName;
+ delete t;
+ setpriority(PRIO_PROCESS, 0, prio);
+ if (prio >= ANDROID_PRIORITY_BACKGROUND) {
+ set_sched_policy(0, SP_BACKGROUND);
+ } else {
+ set_sched_policy(0, SP_FOREGROUND);
+ }
+
+ if (name) {
+ androidSetThreadName(name);
+ free(name);
+ }
+ return f(u);
+ }
+};
+
+void androidSetThreadName(const char* name) {
+#if defined(__linux__)
+ // Mac OS doesn't have this, and we build libutil for the host too
+ int hasAt = 0;
+ int hasDot = 0;
+ const char *s = name;
+ while (*s) {
+ if (*s == '.') hasDot = 1;
+ else if (*s == '@') hasAt = 1;
+ s++;
+ }
+ int len = s - name;
+ if (len < 15 || hasAt || !hasDot) {
+ s = name;
+ } else {
+ s = name + len - 15;
+ }
+ prctl(PR_SET_NAME, (unsigned long) s, 0, 0, 0);
+#endif
+}
+
+int androidCreateRawThreadEtc(android_thread_func_t entryFunction,
+ void *userData,
+ const char* threadName __android_unused,
+ int32_t threadPriority,
+ size_t threadStackSize,
+ android_thread_id_t *threadId)
+{
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+#ifdef HAVE_ANDROID_OS /* valgrind is rejecting RT-priority create reqs */
+ if (threadPriority != PRIORITY_DEFAULT || threadName != NULL) {
+ // Now that the pthread_t has a method to find the associated
+ // android_thread_id_t (pid) from pthread_t, it would be possible to avoid
+ // this trampoline in some cases as the parent could set the properties
+ // for the child. However, there would be a race condition because the
+ // child becomes ready immediately, and it doesn't work for the name.
+ // prctl(PR_SET_NAME) only works for self; prctl(PR_SET_THREAD_NAME) was
+ // proposed but not yet accepted.
+ thread_data_t* t = new thread_data_t;
+ t->priority = threadPriority;
+ t->threadName = threadName ? strdup(threadName) : NULL;
+ t->entryFunction = entryFunction;
+ t->userData = userData;
+ entryFunction = (android_thread_func_t)&thread_data_t::trampoline;
+ userData = t;
+ }
+#endif
+
+ if (threadStackSize) {
+ pthread_attr_setstacksize(&attr, threadStackSize);
+ }
+
+ errno = 0;
+ pthread_t thread;
+ int result = pthread_create(&thread, &attr,
+ (android_pthread_entry)entryFunction, userData);
+ pthread_attr_destroy(&attr);
+ if (result != 0) {
+ ALOGE("androidCreateRawThreadEtc failed (entry=%p, res=%d, %s)\n"
+ "(android threadPriority=%d)",
+ entryFunction, result, strerror(errno), threadPriority);
+ return 0;
+ }
+
+ // Note that *threadID is directly available to the parent only, as it is
+ // assigned after the child starts. Use memory barrier / lock if the child
+ // or other threads also need access.
+ if (threadId != NULL) {
+ *threadId = (android_thread_id_t)thread; // XXX: this is not portable
+ }
+ return 1;
+}
+
+#ifdef HAVE_ANDROID_OS
+static pthread_t android_thread_id_t_to_pthread(android_thread_id_t thread)
+{
+ return (pthread_t) thread;
+}
+#endif
+
+android_thread_id_t androidGetThreadId()
+{
+ return (android_thread_id_t)pthread_self();
+}
+
+// ----------------------------------------------------------------------------
+#else // !defined(_WIN32)
+// ----------------------------------------------------------------------------
+
+/*
+ * Trampoline to make us __stdcall-compliant.
+ *
+ * We're expected to delete "vDetails" when we're done.
+ */
+struct threadDetails {
+ int (*func)(void*);
+ void* arg;
+};
+static __stdcall unsigned int threadIntermediary(void* vDetails)
+{
+ struct threadDetails* pDetails = (struct threadDetails*) vDetails;
+ int result;
+
+ result = (*(pDetails->func))(pDetails->arg);
+
+ delete pDetails;
+
+ ALOG(LOG_VERBOSE, "thread", "thread exiting\n");
+ return (unsigned int) result;
+}
+
+/*
+ * Create and run a new thread.
+ */
+static bool doCreateThread(android_thread_func_t fn, void* arg, android_thread_id_t *id)
+{
+ HANDLE hThread;
+ struct threadDetails* pDetails = new threadDetails; // must be on heap
+ unsigned int thrdaddr;
+
+ pDetails->func = fn;
+ pDetails->arg = arg;
+
+#if defined(HAVE__BEGINTHREADEX)
+ hThread = (HANDLE) _beginthreadex(NULL, 0, threadIntermediary, pDetails, 0,
+ &thrdaddr);
+ if (hThread == 0)
+#elif defined(HAVE_CREATETHREAD)
+ hThread = CreateThread(NULL, 0,
+ (LPTHREAD_START_ROUTINE) threadIntermediary,
+ (void*) pDetails, 0, (DWORD*) &thrdaddr);
+ if (hThread == NULL)
+#endif
+ {
+ ALOG(LOG_WARN, "thread", "WARNING: thread create failed\n");
+ return false;
+ }
+
+#if defined(HAVE_CREATETHREAD)
+ /* close the management handle */
+ CloseHandle(hThread);
+#endif
+
+ if (id != NULL) {
+ *id = (android_thread_id_t)thrdaddr;
+ }
+
+ return true;
+}
+
+int androidCreateRawThreadEtc(android_thread_func_t fn,
+ void *userData,
+ const char* /*threadName*/,
+ int32_t /*threadPriority*/,
+ size_t /*threadStackSize*/,
+ android_thread_id_t *threadId)
+{
+ return doCreateThread( fn, userData, threadId);
+}
+
+android_thread_id_t androidGetThreadId()
+{
+ return (android_thread_id_t)GetCurrentThreadId();
+}
+
+// ----------------------------------------------------------------------------
+#endif // !defined(_WIN32)
+
+// ----------------------------------------------------------------------------
+
+int androidCreateThread(android_thread_func_t fn, void* arg)
+{
+ return createThreadEtc(fn, arg);
+}
+
+int androidCreateThreadGetID(android_thread_func_t fn, void *arg, android_thread_id_t *id)
+{
+ return createThreadEtc(fn, arg, "android:unnamed_thread",
+ PRIORITY_DEFAULT, 0, id);
+}
+
+static android_create_thread_fn gCreateThreadFn = androidCreateRawThreadEtc;
+
+int androidCreateThreadEtc(android_thread_func_t entryFunction,
+ void *userData,
+ const char* threadName,
+ int32_t threadPriority,
+ size_t threadStackSize,
+ android_thread_id_t *threadId)
+{
+ return gCreateThreadFn(entryFunction, userData, threadName,
+ threadPriority, threadStackSize, threadId);
+}
+
+void androidSetCreateThreadFunc(android_create_thread_fn func)
+{
+ gCreateThreadFn = func;
+}
+
+#ifdef HAVE_ANDROID_OS
+int androidSetThreadPriority(pid_t tid, int pri)
+{
+ int rc = 0;
+
+#if !defined(_WIN32)
+ int lasterr = 0;
+
+ if (pri >= ANDROID_PRIORITY_BACKGROUND) {
+ rc = set_sched_policy(tid, SP_BACKGROUND);
+ } else if (getpriority(PRIO_PROCESS, tid) >= ANDROID_PRIORITY_BACKGROUND) {
+ rc = set_sched_policy(tid, SP_FOREGROUND);
+ }
+
+ if (rc) {
+ lasterr = errno;
+ }
+
+ if (setpriority(PRIO_PROCESS, tid, pri) < 0) {
+ rc = INVALID_OPERATION;
+ } else {
+ errno = lasterr;
+ }
+#endif
+
+ return rc;
+}
+
+int androidGetThreadPriority(pid_t tid) {
+#if !defined(_WIN32)
+ return getpriority(PRIO_PROCESS, tid);
+#else
+ return ANDROID_PRIORITY_NORMAL;
+#endif
+}
+
+#endif
+
+namespace android {
+
+/*
+ * ===========================================================================
+ * Mutex class
+ * ===========================================================================
+ */
+
+#if !defined(_WIN32)
+// implemented as inlines in threads.h
+#else
+
+Mutex::Mutex()
+{
+ HANDLE hMutex;
+
+ assert(sizeof(hMutex) == sizeof(mState));
+
+ hMutex = CreateMutex(NULL, FALSE, NULL);
+ mState = (void*) hMutex;
+}
+
+Mutex::Mutex(const char* name)
+{
+ // XXX: name not used for now
+ HANDLE hMutex;
+
+ assert(sizeof(hMutex) == sizeof(mState));
+
+ hMutex = CreateMutex(NULL, FALSE, NULL);
+ mState = (void*) hMutex;
+}
+
+Mutex::Mutex(int type, const char* name)
+{
+ // XXX: type and name not used for now
+ HANDLE hMutex;
+
+ assert(sizeof(hMutex) == sizeof(mState));
+
+ hMutex = CreateMutex(NULL, FALSE, NULL);
+ mState = (void*) hMutex;
+}
+
+Mutex::~Mutex()
+{
+ CloseHandle((HANDLE) mState);
+}
+
+status_t Mutex::lock()
+{
+ DWORD dwWaitResult;
+ dwWaitResult = WaitForSingleObject((HANDLE) mState, INFINITE);
+ return dwWaitResult != WAIT_OBJECT_0 ? -1 : NO_ERROR;
+}
+
+void Mutex::unlock()
+{
+ if (!ReleaseMutex((HANDLE) mState))
+ ALOG(LOG_WARN, "thread", "WARNING: bad result from unlocking mutex\n");
+}
+
+status_t Mutex::tryLock()
+{
+ DWORD dwWaitResult;
+
+ dwWaitResult = WaitForSingleObject((HANDLE) mState, 0);
+ if (dwWaitResult != WAIT_OBJECT_0 && dwWaitResult != WAIT_TIMEOUT)
+ ALOG(LOG_WARN, "thread", "WARNING: bad result from try-locking mutex\n");
+ return (dwWaitResult == WAIT_OBJECT_0) ? 0 : -1;
+}
+
+#endif // !defined(_WIN32)
+
+
+/*
+ * ===========================================================================
+ * Condition class
+ * ===========================================================================
+ */
+
+#if !defined(_WIN32)
+// implemented as inlines in threads.h
+#else
+
+/*
+ * Windows doesn't have a condition variable solution. It's possible
+ * to create one, but it's easy to get it wrong. For a discussion, and
+ * the origin of this implementation, see:
+ *
+ * http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
+ *
+ * The implementation shown on the page does NOT follow POSIX semantics.
+ * As an optimization they require acquiring the external mutex before
+ * calling signal() and broadcast(), whereas POSIX only requires grabbing
+ * it before calling wait(). The implementation here has been un-optimized
+ * to have the correct behavior.
+ */
+typedef struct WinCondition {
+ // Number of waiting threads.
+ int waitersCount;
+
+ // Serialize access to waitersCount.
+ CRITICAL_SECTION waitersCountLock;
+
+ // Semaphore used to queue up threads waiting for the condition to
+ // become signaled.
+ HANDLE sema;
+
+ // An auto-reset event used by the broadcast/signal thread to wait
+ // for all the waiting thread(s) to wake up and be released from
+ // the semaphore.
+ HANDLE waitersDone;
+
+ // This mutex wouldn't be necessary if we required that the caller
+ // lock the external mutex before calling signal() and broadcast().
+ // I'm trying to mimic pthread semantics though.
+ HANDLE internalMutex;
+
+ // Keeps track of whether we were broadcasting or signaling. This
+ // allows us to optimize the code if we're just signaling.
+ bool wasBroadcast;
+
+ status_t wait(WinCondition* condState, HANDLE hMutex, nsecs_t* abstime)
+ {
+ // Increment the wait count, avoiding race conditions.
+ EnterCriticalSection(&condState->waitersCountLock);
+ condState->waitersCount++;
+ //printf("+++ wait: incr waitersCount to %d (tid=%ld)\n",
+ // condState->waitersCount, getThreadId());
+ LeaveCriticalSection(&condState->waitersCountLock);
+
+ DWORD timeout = INFINITE;
+ if (abstime) {
+ nsecs_t reltime = *abstime - systemTime();
+ if (reltime < 0)
+ reltime = 0;
+ timeout = reltime/1000000;
+ }
+
+ // Atomically release the external mutex and wait on the semaphore.
+ DWORD res =
+ SignalObjectAndWait(hMutex, condState->sema, timeout, FALSE);
+
+ //printf("+++ wait: awake (tid=%ld)\n", getThreadId());
+
+ // Reacquire lock to avoid race conditions.
+ EnterCriticalSection(&condState->waitersCountLock);
+
+ // No longer waiting.
+ condState->waitersCount--;
+
+ // Check to see if we're the last waiter after a broadcast.
+ bool lastWaiter = (condState->wasBroadcast && condState->waitersCount == 0);
+
+ //printf("+++ wait: lastWaiter=%d (wasBc=%d wc=%d)\n",
+ // lastWaiter, condState->wasBroadcast, condState->waitersCount);
+
+ LeaveCriticalSection(&condState->waitersCountLock);
+
+ // If we're the last waiter thread during this particular broadcast
+ // then signal broadcast() that we're all awake. It'll drop the
+ // internal mutex.
+ if (lastWaiter) {
+ // Atomically signal the "waitersDone" event and wait until we
+ // can acquire the internal mutex. We want to do this in one step
+ // because it ensures that everybody is in the mutex FIFO before
+ // any thread has a chance to run. Without it, another thread
+ // could wake up, do work, and hop back in ahead of us.
+ SignalObjectAndWait(condState->waitersDone, condState->internalMutex,
+ INFINITE, FALSE);
+ } else {
+ // Grab the internal mutex.
+ WaitForSingleObject(condState->internalMutex, INFINITE);
+ }
+
+ // Release the internal and grab the external.
+ ReleaseMutex(condState->internalMutex);
+ WaitForSingleObject(hMutex, INFINITE);
+
+ return res == WAIT_OBJECT_0 ? NO_ERROR : -1;
+ }
+} WinCondition;
+
+/*
+ * Constructor. Set up the WinCondition stuff.
+ */
+Condition::Condition()
+{
+ WinCondition* condState = new WinCondition;
+
+ condState->waitersCount = 0;
+ condState->wasBroadcast = false;
+ // semaphore: no security, initial value of 0
+ condState->sema = CreateSemaphore(NULL, 0, 0x7fffffff, NULL);
+ InitializeCriticalSection(&condState->waitersCountLock);
+ // auto-reset event, not signaled initially
+ condState->waitersDone = CreateEvent(NULL, FALSE, FALSE, NULL);
+ // used so we don't have to lock external mutex on signal/broadcast
+ condState->internalMutex = CreateMutex(NULL, FALSE, NULL);
+
+ mState = condState;
+}
+
+/*
+ * Destructor. Free Windows resources as well as our allocated storage.
+ */
+Condition::~Condition()
+{
+ WinCondition* condState = (WinCondition*) mState;
+ if (condState != NULL) {
+ CloseHandle(condState->sema);
+ CloseHandle(condState->waitersDone);
+ delete condState;
+ }
+}
+
+
+status_t Condition::wait(Mutex& mutex)
+{
+ WinCondition* condState = (WinCondition*) mState;
+ HANDLE hMutex = (HANDLE) mutex.mState;
+
+ return ((WinCondition*)mState)->wait(condState, hMutex, NULL);
+}
+
+status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime)
+{
+ WinCondition* condState = (WinCondition*) mState;
+ HANDLE hMutex = (HANDLE) mutex.mState;
+ nsecs_t absTime = systemTime()+reltime;
+
+ return ((WinCondition*)mState)->wait(condState, hMutex, &absTime);
+}
+
+/*
+ * Signal the condition variable, allowing one thread to continue.
+ */
+void Condition::signal()
+{
+ WinCondition* condState = (WinCondition*) mState;
+
+ // Lock the internal mutex. This ensures that we don't clash with
+ // broadcast().
+ WaitForSingleObject(condState->internalMutex, INFINITE);
+
+ EnterCriticalSection(&condState->waitersCountLock);
+ bool haveWaiters = (condState->waitersCount > 0);
+ LeaveCriticalSection(&condState->waitersCountLock);
+
+ // If no waiters, then this is a no-op. Otherwise, knock the semaphore
+ // down a notch.
+ if (haveWaiters)
+ ReleaseSemaphore(condState->sema, 1, 0);
+
+ // Release internal mutex.
+ ReleaseMutex(condState->internalMutex);
+}
+
+/*
+ * Signal the condition variable, allowing all threads to continue.
+ *
+ * First we have to wake up all threads waiting on the semaphore, then
+ * we wait until all of the threads have actually been woken before
+ * releasing the internal mutex. This ensures that all threads are woken.
+ */
+void Condition::broadcast()
+{
+ WinCondition* condState = (WinCondition*) mState;
+
+ // Lock the internal mutex. This keeps the guys we're waking up
+ // from getting too far.
+ WaitForSingleObject(condState->internalMutex, INFINITE);
+
+ EnterCriticalSection(&condState->waitersCountLock);
+ bool haveWaiters = false;
+
+ if (condState->waitersCount > 0) {
+ haveWaiters = true;
+ condState->wasBroadcast = true;
+ }
+
+ if (haveWaiters) {
+ // Wake up all the waiters.
+ ReleaseSemaphore(condState->sema, condState->waitersCount, 0);
+
+ LeaveCriticalSection(&condState->waitersCountLock);
+
+ // Wait for all awakened threads to acquire the counting semaphore.
+ // The last guy who was waiting sets this.
+ WaitForSingleObject(condState->waitersDone, INFINITE);
+
+ // Reset wasBroadcast. (No crit section needed because nobody
+ // else can wake up to poke at it.)
+ condState->wasBroadcast = 0;
+ } else {
+ // nothing to do
+ LeaveCriticalSection(&condState->waitersCountLock);
+ }
+
+ // Release internal mutex.
+ ReleaseMutex(condState->internalMutex);
+}
+
+#endif // !defined(_WIN32)
+
+// ----------------------------------------------------------------------------
+
+/*
+ * This is our thread object!
+ */
+
+Thread::Thread(bool canCallJava)
+ : mCanCallJava(canCallJava),
+ mThread(thread_id_t(-1)),
+ mLock("Thread::mLock"),
+ mStatus(NO_ERROR),
+ mExitPending(false), mRunning(false)
+#ifdef HAVE_ANDROID_OS
+ , mTid(-1)
+#endif
+{
+}
+
+Thread::~Thread()
+{
+}
+
+status_t Thread::readyToRun()
+{
+ return NO_ERROR;
+}
+
+status_t Thread::run(const char* name, int32_t priority, size_t stack)
+{
+ Mutex::Autolock _l(mLock);
+
+ if (mRunning) {
+ // thread already started
+ return INVALID_OPERATION;
+ }
+
+ // reset status and exitPending to their default value, so we can
+ // try again after an error happened (either below, or in readyToRun())
+ mStatus = NO_ERROR;
+ mExitPending = false;
+ mThread = thread_id_t(-1);
+
+ // hold a strong reference on ourself
+ mHoldSelf = this;
+
+ mRunning = true;
+
+ bool res;
+ if (mCanCallJava) {
+ res = createThreadEtc(_threadLoop,
+ this, name, priority, stack, &mThread);
+ } else {
+ res = androidCreateRawThreadEtc(_threadLoop,
+ this, name, priority, stack, &mThread);
+ }
+
+ if (res == false) {
+ mStatus = UNKNOWN_ERROR; // something happened!
+ mRunning = false;
+ mThread = thread_id_t(-1);
+ mHoldSelf.clear(); // "this" may have gone away after this.
+
+ return UNKNOWN_ERROR;
+ }
+
+ // Do not refer to mStatus here: The thread is already running (may, in fact
+ // already have exited with a valid mStatus result). The NO_ERROR indication
+ // here merely indicates successfully starting the thread and does not
+ // imply successful termination/execution.
+ return NO_ERROR;
+
+ // Exiting scope of mLock is a memory barrier and allows new thread to run
+}
+
+int Thread::_threadLoop(void* user)
+{
+ Thread* const self = static_cast<Thread*>(user);
+
+ sp<Thread> strong(self->mHoldSelf);
+ wp<Thread> weak(strong);
+ self->mHoldSelf.clear();
+
+#ifdef HAVE_ANDROID_OS
+ // this is very useful for debugging with gdb
+ self->mTid = gettid();
+#endif
+
+ bool first = true;
+
+ do {
+ bool result;
+ if (first) {
+ first = false;
+ self->mStatus = self->readyToRun();
+ result = (self->mStatus == NO_ERROR);
+
+ if (result && !self->exitPending()) {
+ // Binder threads (and maybe others) rely on threadLoop
+ // running at least once after a successful ::readyToRun()
+ // (unless, of course, the thread has already been asked to exit
+ // at that point).
+ // This is because threads are essentially used like this:
+ // (new ThreadSubclass())->run();
+ // The caller therefore does not retain a strong reference to
+ // the thread and the thread would simply disappear after the
+ // successful ::readyToRun() call instead of entering the
+ // threadLoop at least once.
+ result = self->threadLoop();
+ }
+ } else {
+ result = self->threadLoop();
+ }
+
+ // establish a scope for mLock
+ {
+ Mutex::Autolock _l(self->mLock);
+ if (result == false || self->mExitPending) {
+ self->mExitPending = true;
+ self->mRunning = false;
+ // clear thread ID so that requestExitAndWait() does not exit if
+ // called by a new thread using the same thread ID as this one.
+ self->mThread = thread_id_t(-1);
+ // note that interested observers blocked in requestExitAndWait are
+ // awoken by broadcast, but blocked on mLock until break exits scope
+ self->mThreadExitedCondition.broadcast();
+ break;
+ }
+ }
+
+ // Release our strong reference, to let a chance to the thread
+ // to die a peaceful death.
+ strong.clear();
+ // And immediately, re-acquire a strong reference for the next loop
+ strong = weak.promote();
+ } while(strong != 0);
+
+ return 0;
+}
+
+void Thread::requestExit()
+{
+ Mutex::Autolock _l(mLock);
+ mExitPending = true;
+}
+
+status_t Thread::requestExitAndWait()
+{
+ Mutex::Autolock _l(mLock);
+ if (mThread == getThreadId()) {
+ ALOGW(
+ "Thread (this=%p): don't call waitForExit() from this "
+ "Thread object's thread. It's a guaranteed deadlock!",
+ this);
+
+ return WOULD_BLOCK;
+ }
+
+ mExitPending = true;
+
+ while (mRunning == true) {
+ mThreadExitedCondition.wait(mLock);
+ }
+ // This next line is probably not needed any more, but is being left for
+ // historical reference. Note that each interested party will clear flag.
+ mExitPending = false;
+
+ return mStatus;
+}
+
+status_t Thread::join()
+{
+ Mutex::Autolock _l(mLock);
+ if (mThread == getThreadId()) {
+ ALOGW(
+ "Thread (this=%p): don't call join() from this "
+ "Thread object's thread. It's a guaranteed deadlock!",
+ this);
+
+ return WOULD_BLOCK;
+ }
+
+ while (mRunning == true) {
+ mThreadExitedCondition.wait(mLock);
+ }
+
+ return mStatus;
+}
+
+bool Thread::isRunning() const {
+ Mutex::Autolock _l(mLock);
+ return mRunning;
+}
+
+#ifdef HAVE_ANDROID_OS
+pid_t Thread::getTid() const
+{
+ // mTid is not defined until the child initializes it, and the caller may need it earlier
+ Mutex::Autolock _l(mLock);
+ pid_t tid;
+ if (mRunning) {
+ pthread_t pthread = android_thread_id_t_to_pthread(mThread);
+ tid = pthread_gettid_np(pthread);
+ } else {
+ ALOGW("Thread (this=%p): getTid() is undefined before run()", this);
+ tid = -1;
+ }
+ return tid;
+}
+#endif
+
+bool Thread::exitPending() const
+{
+ Mutex::Autolock _l(mLock);
+ return mExitPending;
+}
+
+
+
+}; // namespace android
diff --git a/libutils/Timers.cpp b/libutils/Timers.cpp
new file mode 100644
index 0000000..fb70e15
--- /dev/null
+++ b/libutils/Timers.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2005 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.
+ */
+
+//
+// Timer functions.
+//
+#include <utils/Timers.h>
+
+#include <limits.h>
+#include <sys/time.h>
+#include <time.h>
+
+#if defined(HAVE_ANDROID_OS)
+nsecs_t systemTime(int clock)
+{
+ static const clockid_t clocks[] = {
+ CLOCK_REALTIME,
+ CLOCK_MONOTONIC,
+ CLOCK_PROCESS_CPUTIME_ID,
+ CLOCK_THREAD_CPUTIME_ID,
+ CLOCK_BOOTTIME
+ };
+ struct timespec t;
+ t.tv_sec = t.tv_nsec = 0;
+ clock_gettime(clocks[clock], &t);
+ return nsecs_t(t.tv_sec)*1000000000LL + t.tv_nsec;
+}
+#else
+nsecs_t systemTime(int /*clock*/)
+{
+ // Clock support varies widely across hosts. Mac OS doesn't support
+ // posix clocks, older glibcs don't support CLOCK_BOOTTIME and Windows
+ // is windows.
+ struct timeval t;
+ t.tv_sec = t.tv_usec = 0;
+ gettimeofday(&t, NULL);
+ return nsecs_t(t.tv_sec)*1000000000LL + nsecs_t(t.tv_usec)*1000LL;
+}
+#endif
+
+int toMillisecondTimeoutDelay(nsecs_t referenceTime, nsecs_t timeoutTime)
+{
+ int timeoutDelayMillis;
+ if (timeoutTime > referenceTime) {
+ uint64_t timeoutDelay = uint64_t(timeoutTime - referenceTime);
+ if (timeoutDelay > uint64_t((INT_MAX - 1) * 1000000LL)) {
+ timeoutDelayMillis = -1;
+ } else {
+ timeoutDelayMillis = (timeoutDelay + 999999LL) / 1000000LL;
+ }
+ } else {
+ timeoutDelayMillis = 0;
+ }
+ return timeoutDelayMillis;
+}
diff --git a/libutils/Tokenizer.cpp b/libutils/Tokenizer.cpp
new file mode 100644
index 0000000..2d0e83d
--- /dev/null
+++ b/libutils/Tokenizer.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Tokenizer"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <utils/Log.h>
+#include <utils/Tokenizer.h>
+
+// Enables debug output for the tokenizer.
+#define DEBUG_TOKENIZER 0
+
+
+namespace android {
+
+static inline bool isDelimiter(char ch, const char* delimiters) {
+ return strchr(delimiters, ch) != NULL;
+}
+
+Tokenizer::Tokenizer(const String8& filename, FileMap* fileMap, char* buffer,
+ bool ownBuffer, size_t length) :
+ mFilename(filename), mFileMap(fileMap),
+ mBuffer(buffer), mOwnBuffer(ownBuffer), mLength(length),
+ mCurrent(buffer), mLineNumber(1) {
+}
+
+Tokenizer::~Tokenizer() {
+ delete mFileMap;
+ if (mOwnBuffer) {
+ delete[] mBuffer;
+ }
+}
+
+status_t Tokenizer::open(const String8& filename, Tokenizer** outTokenizer) {
+ *outTokenizer = NULL;
+
+ int result = NO_ERROR;
+ int fd = ::open(filename.string(), O_RDONLY);
+ if (fd < 0) {
+ result = -errno;
+ ALOGE("Error opening file '%s': %s", filename.string(), strerror(errno));
+ } else {
+ struct stat stat;
+ if (fstat(fd, &stat)) {
+ result = -errno;
+ ALOGE("Error getting size of file '%s': %s", filename.string(), strerror(errno));
+ } else {
+ size_t length = size_t(stat.st_size);
+
+ FileMap* fileMap = new FileMap();
+ bool ownBuffer = false;
+ char* buffer;
+ if (fileMap->create(NULL, fd, 0, length, true)) {
+ fileMap->advise(FileMap::SEQUENTIAL);
+ buffer = static_cast<char*>(fileMap->getDataPtr());
+ } else {
+ delete fileMap;
+ fileMap = NULL;
+
+ // Fall back to reading into a buffer since we can't mmap files in sysfs.
+ // The length we obtained from stat is wrong too (it will always be 4096)
+ // so we must trust that read will read the entire file.
+ buffer = new char[length];
+ ownBuffer = true;
+ ssize_t nrd = read(fd, buffer, length);
+ if (nrd < 0) {
+ result = -errno;
+ ALOGE("Error reading file '%s': %s", filename.string(), strerror(errno));
+ delete[] buffer;
+ buffer = NULL;
+ } else {
+ length = size_t(nrd);
+ }
+ }
+
+ if (!result) {
+ *outTokenizer = new Tokenizer(filename, fileMap, buffer, ownBuffer, length);
+ }
+ }
+ close(fd);
+ }
+ return result;
+}
+
+status_t Tokenizer::fromContents(const String8& filename,
+ const char* contents, Tokenizer** outTokenizer) {
+ *outTokenizer = new Tokenizer(filename, NULL,
+ const_cast<char*>(contents), false, strlen(contents));
+ return OK;
+}
+
+String8 Tokenizer::getLocation() const {
+ String8 result;
+ result.appendFormat("%s:%d", mFilename.string(), mLineNumber);
+ return result;
+}
+
+String8 Tokenizer::peekRemainderOfLine() const {
+ const char* end = getEnd();
+ const char* eol = mCurrent;
+ while (eol != end) {
+ char ch = *eol;
+ if (ch == '\n') {
+ break;
+ }
+ eol += 1;
+ }
+ return String8(mCurrent, eol - mCurrent);
+}
+
+String8 Tokenizer::nextToken(const char* delimiters) {
+#if DEBUG_TOKENIZER
+ ALOGD("nextToken");
+#endif
+ const char* end = getEnd();
+ const char* tokenStart = mCurrent;
+ while (mCurrent != end) {
+ char ch = *mCurrent;
+ if (ch == '\n' || isDelimiter(ch, delimiters)) {
+ break;
+ }
+ mCurrent += 1;
+ }
+ return String8(tokenStart, mCurrent - tokenStart);
+}
+
+void Tokenizer::nextLine() {
+#if DEBUG_TOKENIZER
+ ALOGD("nextLine");
+#endif
+ const char* end = getEnd();
+ while (mCurrent != end) {
+ char ch = *(mCurrent++);
+ if (ch == '\n') {
+ mLineNumber += 1;
+ break;
+ }
+ }
+}
+
+void Tokenizer::skipDelimiters(const char* delimiters) {
+#if DEBUG_TOKENIZER
+ ALOGD("skipDelimiters");
+#endif
+ const char* end = getEnd();
+ while (mCurrent != end) {
+ char ch = *mCurrent;
+ if (ch == '\n' || !isDelimiter(ch, delimiters)) {
+ break;
+ }
+ mCurrent += 1;
+ }
+}
+
+} // namespace android
diff --git a/libutils/Trace.cpp b/libutils/Trace.cpp
new file mode 100644
index 0000000..36fd802
--- /dev/null
+++ b/libutils/Trace.cpp
@@ -0,0 +1,25 @@
+/*
+ * 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 <utils/misc.h>
+#include <utils/Trace.h>
+
+static void traceInit() __attribute__((constructor));
+
+static void traceInit()
+{
+ ::android::add_sysprop_change_callback(atrace_update_tags, 0);
+}
diff --git a/libutils/Unicode.cpp b/libutils/Unicode.cpp
new file mode 100644
index 0000000..fb876c9
--- /dev/null
+++ b/libutils/Unicode.cpp
@@ -0,0 +1,601 @@
+/*
+ * Copyright (C) 2005 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 <utils/Unicode.h>
+
+#include <stddef.h>
+
+#ifdef HAVE_WINSOCK
+# undef nhtol
+# undef htonl
+# undef nhtos
+# undef htons
+
+# define ntohl(x) ( ((x) << 24) | (((x) >> 24) & 255) | (((x) << 8) & 0xff0000) | (((x) >> 8) & 0xff00) )
+# define htonl(x) ntohl(x)
+# define ntohs(x) ( (((x) << 8) & 0xff00) | (((x) >> 8) & 255) )
+# define htons(x) ntohs(x)
+#else
+# include <netinet/in.h>
+#endif
+
+extern "C" {
+
+static const char32_t kByteMask = 0x000000BF;
+static const char32_t kByteMark = 0x00000080;
+
+// Surrogates aren't valid for UTF-32 characters, so define some
+// constants that will let us screen them out.
+static const char32_t kUnicodeSurrogateHighStart = 0x0000D800;
+// Unused, here for completeness:
+// static const char32_t kUnicodeSurrogateHighEnd = 0x0000DBFF;
+// static const char32_t kUnicodeSurrogateLowStart = 0x0000DC00;
+static const char32_t kUnicodeSurrogateLowEnd = 0x0000DFFF;
+static const char32_t kUnicodeSurrogateStart = kUnicodeSurrogateHighStart;
+static const char32_t kUnicodeSurrogateEnd = kUnicodeSurrogateLowEnd;
+static const char32_t kUnicodeMaxCodepoint = 0x0010FFFF;
+
+// Mask used to set appropriate bits in first byte of UTF-8 sequence,
+// indexed by number of bytes in the sequence.
+// 0xxxxxxx
+// -> (00-7f) 7bit. Bit mask for the first byte is 0x00000000
+// 110yyyyx 10xxxxxx
+// -> (c0-df)(80-bf) 11bit. Bit mask is 0x000000C0
+// 1110yyyy 10yxxxxx 10xxxxxx
+// -> (e0-ef)(80-bf)(80-bf) 16bit. Bit mask is 0x000000E0
+// 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx
+// -> (f0-f7)(80-bf)(80-bf)(80-bf) 21bit. Bit mask is 0x000000F0
+static const char32_t kFirstByteMark[] = {
+ 0x00000000, 0x00000000, 0x000000C0, 0x000000E0, 0x000000F0
+};
+
+// --------------------------------------------------------------------------
+// UTF-32
+// --------------------------------------------------------------------------
+
+/**
+ * Return number of UTF-8 bytes required for the character. If the character
+ * is invalid, return size of 0.
+ */
+static inline size_t utf32_codepoint_utf8_length(char32_t srcChar)
+{
+ // Figure out how many bytes the result will require.
+ if (srcChar < 0x00000080) {
+ return 1;
+ } else if (srcChar < 0x00000800) {
+ return 2;
+ } else if (srcChar < 0x00010000) {
+ if ((srcChar < kUnicodeSurrogateStart) || (srcChar > kUnicodeSurrogateEnd)) {
+ return 3;
+ } else {
+ // Surrogates are invalid UTF-32 characters.
+ return 0;
+ }
+ }
+ // Max code point for Unicode is 0x0010FFFF.
+ else if (srcChar <= kUnicodeMaxCodepoint) {
+ return 4;
+ } else {
+ // Invalid UTF-32 character.
+ return 0;
+ }
+}
+
+// Write out the source character to <dstP>.
+
+static inline void utf32_codepoint_to_utf8(uint8_t* dstP, char32_t srcChar, size_t bytes)
+{
+ dstP += bytes;
+ switch (bytes)
+ { /* note: everything falls through. */
+ case 4: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6;
+ case 3: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6;
+ case 2: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6;
+ case 1: *--dstP = (uint8_t)(srcChar | kFirstByteMark[bytes]);
+ }
+}
+
+size_t strlen32(const char32_t *s)
+{
+ const char32_t *ss = s;
+ while ( *ss )
+ ss++;
+ return ss-s;
+}
+
+size_t strnlen32(const char32_t *s, size_t maxlen)
+{
+ const char32_t *ss = s;
+ while ((maxlen > 0) && *ss) {
+ ss++;
+ maxlen--;
+ }
+ return ss-s;
+}
+
+static inline int32_t utf32_at_internal(const char* cur, size_t *num_read)
+{
+ const char first_char = *cur;
+ if ((first_char & 0x80) == 0) { // ASCII
+ *num_read = 1;
+ return *cur;
+ }
+ cur++;
+ char32_t mask, to_ignore_mask;
+ size_t num_to_read = 0;
+ char32_t utf32 = first_char;
+ for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0xFFFFFF80;
+ (first_char & mask);
+ num_to_read++, to_ignore_mask |= mask, mask >>= 1) {
+ // 0x3F == 00111111
+ utf32 = (utf32 << 6) + (*cur++ & 0x3F);
+ }
+ to_ignore_mask |= mask;
+ utf32 &= ~(to_ignore_mask << (6 * (num_to_read - 1)));
+
+ *num_read = num_to_read;
+ return static_cast<int32_t>(utf32);
+}
+
+int32_t utf32_from_utf8_at(const char *src, size_t src_len, size_t index, size_t *next_index)
+{
+ if (index >= src_len) {
+ return -1;
+ }
+ size_t dummy_index;
+ if (next_index == NULL) {
+ next_index = &dummy_index;
+ }
+ size_t num_read;
+ int32_t ret = utf32_at_internal(src + index, &num_read);
+ if (ret >= 0) {
+ *next_index = index + num_read;
+ }
+
+ return ret;
+}
+
+ssize_t utf32_to_utf8_length(const char32_t *src, size_t src_len)
+{
+ if (src == NULL || src_len == 0) {
+ return -1;
+ }
+
+ size_t ret = 0;
+ const char32_t *end = src + src_len;
+ while (src < end) {
+ ret += utf32_codepoint_utf8_length(*src++);
+ }
+ return ret;
+}
+
+void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst)
+{
+ if (src == NULL || src_len == 0 || dst == NULL) {
+ return;
+ }
+
+ const char32_t *cur_utf32 = src;
+ const char32_t *end_utf32 = src + src_len;
+ char *cur = dst;
+ while (cur_utf32 < end_utf32) {
+ size_t len = utf32_codepoint_utf8_length(*cur_utf32);
+ utf32_codepoint_to_utf8((uint8_t *)cur, *cur_utf32++, len);
+ cur += len;
+ }
+ *cur = '\0';
+}
+
+// --------------------------------------------------------------------------
+// UTF-16
+// --------------------------------------------------------------------------
+
+int strcmp16(const char16_t *s1, const char16_t *s2)
+{
+ char16_t ch;
+ int d = 0;
+
+ while ( 1 ) {
+ d = (int)(ch = *s1++) - (int)*s2++;
+ if ( d || !ch )
+ break;
+ }
+
+ return d;
+}
+
+int strncmp16(const char16_t *s1, const char16_t *s2, size_t n)
+{
+ char16_t ch;
+ int d = 0;
+
+ while ( n-- ) {
+ d = (int)(ch = *s1++) - (int)*s2++;
+ if ( d || !ch )
+ break;
+ }
+
+ return d;
+}
+
+char16_t *strcpy16(char16_t *dst, const char16_t *src)
+{
+ char16_t *q = dst;
+ const char16_t *p = src;
+ char16_t ch;
+
+ do {
+ *q++ = ch = *p++;
+ } while ( ch );
+
+ return dst;
+}
+
+size_t strlen16(const char16_t *s)
+{
+ const char16_t *ss = s;
+ while ( *ss )
+ ss++;
+ return ss-s;
+}
+
+
+char16_t *strncpy16(char16_t *dst, const char16_t *src, size_t n)
+{
+ char16_t *q = dst;
+ const char16_t *p = src;
+ char ch;
+
+ while (n) {
+ n--;
+ *q++ = ch = *p++;
+ if ( !ch )
+ break;
+ }
+
+ *q = 0;
+
+ return dst;
+}
+
+size_t strnlen16(const char16_t *s, size_t maxlen)
+{
+ const char16_t *ss = s;
+
+ /* Important: the maxlen test must precede the reference through ss;
+ since the byte beyond the maximum may segfault */
+ while ((maxlen > 0) && *ss) {
+ ss++;
+ maxlen--;
+ }
+ return ss-s;
+}
+
+int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2)
+{
+ const char16_t* e1 = s1+n1;
+ const char16_t* e2 = s2+n2;
+
+ while (s1 < e1 && s2 < e2) {
+ const int d = (int)*s1++ - (int)*s2++;
+ if (d) {
+ return d;
+ }
+ }
+
+ return n1 < n2
+ ? (0 - (int)*s2)
+ : (n1 > n2
+ ? ((int)*s1 - 0)
+ : 0);
+}
+
+int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2)
+{
+ const char16_t* e1 = s1H+n1;
+ const char16_t* e2 = s2N+n2;
+
+ while (s1H < e1 && s2N < e2) {
+ const char16_t c2 = ntohs(*s2N);
+ const int d = (int)*s1H++ - (int)c2;
+ s2N++;
+ if (d) {
+ return d;
+ }
+ }
+
+ return n1 < n2
+ ? (0 - (int)ntohs(*s2N))
+ : (n1 > n2
+ ? ((int)*s1H - 0)
+ : 0);
+}
+
+void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst)
+{
+ if (src == NULL || src_len == 0 || dst == NULL) {
+ return;
+ }
+
+ const char16_t* cur_utf16 = src;
+ const char16_t* const end_utf16 = src + src_len;
+ char *cur = dst;
+ while (cur_utf16 < end_utf16) {
+ char32_t utf32;
+ // surrogate pairs
+ if((*cur_utf16 & 0xFC00) == 0xD800 && (cur_utf16 + 1) < end_utf16
+ && (*(cur_utf16 + 1) & 0xFC00) == 0xDC00) {
+ utf32 = (*cur_utf16++ - 0xD800) << 10;
+ utf32 |= *cur_utf16++ - 0xDC00;
+ utf32 += 0x10000;
+ } else {
+ utf32 = (char32_t) *cur_utf16++;
+ }
+ const size_t len = utf32_codepoint_utf8_length(utf32);
+ utf32_codepoint_to_utf8((uint8_t*)cur, utf32, len);
+ cur += len;
+ }
+ *cur = '\0';
+}
+
+// --------------------------------------------------------------------------
+// UTF-8
+// --------------------------------------------------------------------------
+
+ssize_t utf8_length(const char *src)
+{
+ const char *cur = src;
+ size_t ret = 0;
+ while (*cur != '\0') {
+ const char first_char = *cur++;
+ if ((first_char & 0x80) == 0) { // ASCII
+ ret += 1;
+ continue;
+ }
+ // (UTF-8's character must not be like 10xxxxxx,
+ // but 110xxxxx, 1110xxxx, ... or 1111110x)
+ if ((first_char & 0x40) == 0) {
+ return -1;
+ }
+
+ int32_t mask, to_ignore_mask;
+ size_t num_to_read = 0;
+ char32_t utf32 = 0;
+ for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0x80;
+ num_to_read < 5 && (first_char & mask);
+ num_to_read++, to_ignore_mask |= mask, mask >>= 1) {
+ if ((*cur & 0xC0) != 0x80) { // must be 10xxxxxx
+ return -1;
+ }
+ // 0x3F == 00111111
+ utf32 = (utf32 << 6) + (*cur++ & 0x3F);
+ }
+ // "first_char" must be (110xxxxx - 11110xxx)
+ if (num_to_read == 5) {
+ return -1;
+ }
+ to_ignore_mask |= mask;
+ utf32 |= ((~to_ignore_mask) & first_char) << (6 * (num_to_read - 1));
+ if (utf32 > kUnicodeMaxCodepoint) {
+ return -1;
+ }
+
+ ret += num_to_read;
+ }
+ return ret;
+}
+
+ssize_t utf16_to_utf8_length(const char16_t *src, size_t src_len)
+{
+ if (src == NULL || src_len == 0) {
+ return -1;
+ }
+
+ size_t ret = 0;
+ const char16_t* const end = src + src_len;
+ while (src < end) {
+ if ((*src & 0xFC00) == 0xD800 && (src + 1) < end
+ && (*++src & 0xFC00) == 0xDC00) {
+ // surrogate pairs are always 4 bytes.
+ ret += 4;
+ src++;
+ } else {
+ ret += utf32_codepoint_utf8_length((char32_t) *src++);
+ }
+ }
+ return ret;
+}
+
+/**
+ * Returns 1-4 based on the number of leading bits.
+ *
+ * 1111 -> 4
+ * 1110 -> 3
+ * 110x -> 2
+ * 10xx -> 1
+ * 0xxx -> 1
+ */
+static inline size_t utf8_codepoint_len(uint8_t ch)
+{
+ return ((0xe5000000 >> ((ch >> 3) & 0x1e)) & 3) + 1;
+}
+
+static inline void utf8_shift_and_mask(uint32_t* codePoint, const uint8_t byte)
+{
+ *codePoint <<= 6;
+ *codePoint |= 0x3F & byte;
+}
+
+size_t utf8_to_utf32_length(const char *src, size_t src_len)
+{
+ if (src == NULL || src_len == 0) {
+ return 0;
+ }
+ size_t ret = 0;
+ const char* cur;
+ const char* end;
+ size_t num_to_skip;
+ for (cur = src, end = src + src_len, num_to_skip = 1;
+ cur < end;
+ cur += num_to_skip, ret++) {
+ const char first_char = *cur;
+ num_to_skip = 1;
+ if ((first_char & 0x80) == 0) { // ASCII
+ continue;
+ }
+ int32_t mask;
+
+ for (mask = 0x40; (first_char & mask); num_to_skip++, mask >>= 1) {
+ }
+ }
+ return ret;
+}
+
+void utf8_to_utf32(const char* src, size_t src_len, char32_t* dst)
+{
+ if (src == NULL || src_len == 0 || dst == NULL) {
+ return;
+ }
+
+ const char* cur = src;
+ const char* const end = src + src_len;
+ char32_t* cur_utf32 = dst;
+ while (cur < end) {
+ size_t num_read;
+ *cur_utf32++ = static_cast<char32_t>(utf32_at_internal(cur, &num_read));
+ cur += num_read;
+ }
+ *cur_utf32 = 0;
+}
+
+static inline uint32_t utf8_to_utf32_codepoint(const uint8_t *src, size_t length)
+{
+ uint32_t unicode;
+
+ switch (length)
+ {
+ case 1:
+ return src[0];
+ case 2:
+ unicode = src[0] & 0x1f;
+ utf8_shift_and_mask(&unicode, src[1]);
+ return unicode;
+ case 3:
+ unicode = src[0] & 0x0f;
+ utf8_shift_and_mask(&unicode, src[1]);
+ utf8_shift_and_mask(&unicode, src[2]);
+ return unicode;
+ case 4:
+ unicode = src[0] & 0x07;
+ utf8_shift_and_mask(&unicode, src[1]);
+ utf8_shift_and_mask(&unicode, src[2]);
+ utf8_shift_and_mask(&unicode, src[3]);
+ return unicode;
+ default:
+ return 0xffff;
+ }
+
+ //printf("Char at %p: len=%d, utf-16=%p\n", src, length, (void*)result);
+}
+
+ssize_t utf8_to_utf16_length(const uint8_t* u8str, size_t u8len)
+{
+ const uint8_t* const u8end = u8str + u8len;
+ const uint8_t* u8cur = u8str;
+
+ /* Validate that the UTF-8 is the correct len */
+ size_t u16measuredLen = 0;
+ while (u8cur < u8end) {
+ u16measuredLen++;
+ int u8charLen = utf8_codepoint_len(*u8cur);
+ uint32_t codepoint = utf8_to_utf32_codepoint(u8cur, u8charLen);
+ if (codepoint > 0xFFFF) u16measuredLen++; // this will be a surrogate pair in utf16
+ u8cur += u8charLen;
+ }
+
+ /**
+ * Make sure that we ended where we thought we would and the output UTF-16
+ * will be exactly how long we were told it would be.
+ */
+ if (u8cur != u8end) {
+ return -1;
+ }
+
+ return u16measuredLen;
+}
+
+char16_t* utf8_to_utf16_no_null_terminator(const uint8_t* u8str, size_t u8len, char16_t* u16str)
+{
+ const uint8_t* const u8end = u8str + u8len;
+ const uint8_t* u8cur = u8str;
+ char16_t* u16cur = u16str;
+
+ while (u8cur < u8end) {
+ size_t u8len = utf8_codepoint_len(*u8cur);
+ uint32_t codepoint = utf8_to_utf32_codepoint(u8cur, u8len);
+
+ // Convert the UTF32 codepoint to one or more UTF16 codepoints
+ if (codepoint <= 0xFFFF) {
+ // Single UTF16 character
+ *u16cur++ = (char16_t) codepoint;
+ } else {
+ // Multiple UTF16 characters with surrogates
+ codepoint = codepoint - 0x10000;
+ *u16cur++ = (char16_t) ((codepoint >> 10) + 0xD800);
+ *u16cur++ = (char16_t) ((codepoint & 0x3FF) + 0xDC00);
+ }
+
+ u8cur += u8len;
+ }
+ return u16cur;
+}
+
+void utf8_to_utf16(const uint8_t* u8str, size_t u8len, char16_t* u16str) {
+ char16_t* end = utf8_to_utf16_no_null_terminator(u8str, u8len, u16str);
+ *end = 0;
+}
+
+char16_t* utf8_to_utf16_n(const uint8_t* src, size_t srcLen, char16_t* dst, size_t dstLen) {
+ const uint8_t* const u8end = src + srcLen;
+ const uint8_t* u8cur = src;
+ const char16_t* const u16end = dst + dstLen;
+ char16_t* u16cur = dst;
+
+ while (u8cur < u8end && u16cur < u16end) {
+ size_t u8len = utf8_codepoint_len(*u8cur);
+ uint32_t codepoint = utf8_to_utf32_codepoint(u8cur, u8len);
+
+ // Convert the UTF32 codepoint to one or more UTF16 codepoints
+ if (codepoint <= 0xFFFF) {
+ // Single UTF16 character
+ *u16cur++ = (char16_t) codepoint;
+ } else {
+ // Multiple UTF16 characters with surrogates
+ codepoint = codepoint - 0x10000;
+ *u16cur++ = (char16_t) ((codepoint >> 10) + 0xD800);
+ if (u16cur >= u16end) {
+ // Ooops... not enough room for this surrogate pair.
+ return u16cur-1;
+ }
+ *u16cur++ = (char16_t) ((codepoint & 0x3FF) + 0xDC00);
+ }
+
+ u8cur += u8len;
+ }
+ return u16cur;
+}
+
+}
diff --git a/libutils/VectorImpl.cpp b/libutils/VectorImpl.cpp
new file mode 100644
index 0000000..30ca663
--- /dev/null
+++ b/libutils/VectorImpl.cpp
@@ -0,0 +1,637 @@
+/*
+ * Copyright (C) 2005 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 "Vector"
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <cutils/log.h>
+
+#include <utils/Errors.h>
+#include <utils/SharedBuffer.h>
+#include <utils/VectorImpl.h>
+
+/*****************************************************************************/
+
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+const size_t kMinVectorCapacity = 4;
+
+static inline size_t max(size_t a, size_t b) {
+ return a>b ? a : b;
+}
+
+// ----------------------------------------------------------------------------
+
+VectorImpl::VectorImpl(size_t itemSize, uint32_t flags)
+ : mStorage(0), mCount(0), mFlags(flags), mItemSize(itemSize)
+{
+}
+
+VectorImpl::VectorImpl(const VectorImpl& rhs)
+ : mStorage(rhs.mStorage), mCount(rhs.mCount),
+ mFlags(rhs.mFlags), mItemSize(rhs.mItemSize)
+{
+ if (mStorage) {
+ SharedBuffer::bufferFromData(mStorage)->acquire();
+ }
+}
+
+VectorImpl::~VectorImpl()
+{
+ ALOGW_IF(mCount,
+ "[%p] subclasses of VectorImpl must call finish_vector()"
+ " in their destructor. Leaking %d bytes.",
+ this, (int)(mCount*mItemSize));
+ // We can't call _do_destroy() here because the vtable is already gone.
+}
+
+VectorImpl& VectorImpl::operator = (const VectorImpl& rhs)
+{
+ LOG_ALWAYS_FATAL_IF(mItemSize != rhs.mItemSize,
+ "Vector<> have different types (this=%p, rhs=%p)", this, &rhs);
+ if (this != &rhs) {
+ release_storage();
+ if (rhs.mCount) {
+ mStorage = rhs.mStorage;
+ mCount = rhs.mCount;
+ SharedBuffer::bufferFromData(mStorage)->acquire();
+ } else {
+ mStorage = 0;
+ mCount = 0;
+ }
+ }
+ return *this;
+}
+
+void* VectorImpl::editArrayImpl()
+{
+ if (mStorage) {
+ SharedBuffer* sb = SharedBuffer::bufferFromData(mStorage)->attemptEdit();
+ if (sb == 0) {
+ sb = SharedBuffer::alloc(capacity() * mItemSize);
+ if (sb) {
+ _do_copy(sb->data(), mStorage, mCount);
+ release_storage();
+ mStorage = sb->data();
+ }
+ }
+ }
+ return mStorage;
+}
+
+size_t VectorImpl::capacity() const
+{
+ if (mStorage) {
+ return SharedBuffer::bufferFromData(mStorage)->size() / mItemSize;
+ }
+ return 0;
+}
+
+ssize_t VectorImpl::insertVectorAt(const VectorImpl& vector, size_t index)
+{
+ return insertArrayAt(vector.arrayImpl(), index, vector.size());
+}
+
+ssize_t VectorImpl::appendVector(const VectorImpl& vector)
+{
+ return insertVectorAt(vector, size());
+}
+
+ssize_t VectorImpl::insertArrayAt(const void* array, size_t index, size_t length)
+{
+ if (index > size())
+ return BAD_INDEX;
+ void* where = _grow(index, length);
+ if (where) {
+ _do_copy(where, array, length);
+ }
+ return where ? index : (ssize_t)NO_MEMORY;
+}
+
+ssize_t VectorImpl::appendArray(const void* array, size_t length)
+{
+ return insertArrayAt(array, size(), length);
+}
+
+ssize_t VectorImpl::insertAt(size_t index, size_t numItems)
+{
+ return insertAt(0, index, numItems);
+}
+
+ssize_t VectorImpl::insertAt(const void* item, size_t index, size_t numItems)
+{
+ if (index > size())
+ return BAD_INDEX;
+ void* where = _grow(index, numItems);
+ if (where) {
+ if (item) {
+ _do_splat(where, item, numItems);
+ } else {
+ _do_construct(where, numItems);
+ }
+ }
+ return where ? index : (ssize_t)NO_MEMORY;
+}
+
+static int sortProxy(const void* lhs, const void* rhs, void* func)
+{
+ return (*(VectorImpl::compar_t)func)(lhs, rhs);
+}
+
+status_t VectorImpl::sort(VectorImpl::compar_t cmp)
+{
+ return sort(sortProxy, (void*)cmp);
+}
+
+status_t VectorImpl::sort(VectorImpl::compar_r_t cmp, void* state)
+{
+ // the sort must be stable. we're using insertion sort which
+ // is well suited for small and already sorted arrays
+ // for big arrays, it could be better to use mergesort
+ const ssize_t count = size();
+ if (count > 1) {
+ void* array = const_cast<void*>(arrayImpl());
+ void* temp = 0;
+ ssize_t i = 1;
+ while (i < count) {
+ void* item = reinterpret_cast<char*>(array) + mItemSize*(i);
+ void* curr = reinterpret_cast<char*>(array) + mItemSize*(i-1);
+ if (cmp(curr, item, state) > 0) {
+
+ if (!temp) {
+ // we're going to have to modify the array...
+ array = editArrayImpl();
+ if (!array) return NO_MEMORY;
+ temp = malloc(mItemSize);
+ if (!temp) return NO_MEMORY;
+ item = reinterpret_cast<char*>(array) + mItemSize*(i);
+ curr = reinterpret_cast<char*>(array) + mItemSize*(i-1);
+ } else {
+ _do_destroy(temp, 1);
+ }
+
+ _do_copy(temp, item, 1);
+
+ ssize_t j = i-1;
+ void* next = reinterpret_cast<char*>(array) + mItemSize*(i);
+ do {
+ _do_destroy(next, 1);
+ _do_copy(next, curr, 1);
+ next = curr;
+ --j;
+ curr = reinterpret_cast<char*>(array) + mItemSize*(j);
+ } while (j>=0 && (cmp(curr, temp, state) > 0));
+
+ _do_destroy(next, 1);
+ _do_copy(next, temp, 1);
+ }
+ i++;
+ }
+
+ if (temp) {
+ _do_destroy(temp, 1);
+ free(temp);
+ }
+ }
+ return NO_ERROR;
+}
+
+void VectorImpl::pop()
+{
+ if (size())
+ removeItemsAt(size()-1, 1);
+}
+
+void VectorImpl::push()
+{
+ push(0);
+}
+
+void VectorImpl::push(const void* item)
+{
+ insertAt(item, size());
+}
+
+ssize_t VectorImpl::add()
+{
+ return add(0);
+}
+
+ssize_t VectorImpl::add(const void* item)
+{
+ return insertAt(item, size());
+}
+
+ssize_t VectorImpl::replaceAt(size_t index)
+{
+ return replaceAt(0, index);
+}
+
+ssize_t VectorImpl::replaceAt(const void* prototype, size_t index)
+{
+ ALOG_ASSERT(index<size(),
+ "[%p] replace: index=%d, size=%d", this, (int)index, (int)size());
+
+ if (index >= size()) {
+ return BAD_INDEX;
+ }
+
+ void* item = editItemLocation(index);
+ if (item != prototype) {
+ if (item == 0)
+ return NO_MEMORY;
+ _do_destroy(item, 1);
+ if (prototype == 0) {
+ _do_construct(item, 1);
+ } else {
+ _do_copy(item, prototype, 1);
+ }
+ }
+ return ssize_t(index);
+}
+
+ssize_t VectorImpl::removeItemsAt(size_t index, size_t count)
+{
+ ALOG_ASSERT((index+count)<=size(),
+ "[%p] remove: index=%d, count=%d, size=%d",
+ this, (int)index, (int)count, (int)size());
+
+ if ((index+count) > size())
+ return BAD_VALUE;
+ _shrink(index, count);
+ return index;
+}
+
+void VectorImpl::finish_vector()
+{
+ release_storage();
+ mStorage = 0;
+ mCount = 0;
+}
+
+void VectorImpl::clear()
+{
+ _shrink(0, mCount);
+}
+
+void* VectorImpl::editItemLocation(size_t index)
+{
+ ALOG_ASSERT(index<capacity(),
+ "[%p] editItemLocation: index=%d, capacity=%d, count=%d",
+ this, (int)index, (int)capacity(), (int)mCount);
+
+ if (index < capacity()) {
+ void* buffer = editArrayImpl();
+ if (buffer) {
+ return reinterpret_cast<char*>(buffer) + index*mItemSize;
+ }
+ }
+ return 0;
+}
+
+const void* VectorImpl::itemLocation(size_t index) const
+{
+ ALOG_ASSERT(index<capacity(),
+ "[%p] itemLocation: index=%d, capacity=%d, count=%d",
+ this, (int)index, (int)capacity(), (int)mCount);
+
+ if (index < capacity()) {
+ const void* buffer = arrayImpl();
+ if (buffer) {
+ return reinterpret_cast<const char*>(buffer) + index*mItemSize;
+ }
+ }
+ return 0;
+}
+
+ssize_t VectorImpl::setCapacity(size_t new_capacity)
+{
+ size_t current_capacity = capacity();
+ ssize_t amount = new_capacity - size();
+ if (amount <= 0) {
+ // we can't reduce the capacity
+ return current_capacity;
+ }
+ SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize);
+ if (sb) {
+ void* array = sb->data();
+ _do_copy(array, mStorage, size());
+ release_storage();
+ mStorage = const_cast<void*>(array);
+ } else {
+ return NO_MEMORY;
+ }
+ return new_capacity;
+}
+
+ssize_t VectorImpl::resize(size_t size) {
+ ssize_t result = NO_ERROR;
+ if (size > mCount) {
+ result = insertAt(mCount, size - mCount);
+ } else if (size < mCount) {
+ result = removeItemsAt(size, mCount - size);
+ }
+ return result < 0 ? result : size;
+}
+
+void VectorImpl::release_storage()
+{
+ if (mStorage) {
+ const SharedBuffer* sb = SharedBuffer::bufferFromData(mStorage);
+ if (sb->release(SharedBuffer::eKeepStorage) == 1) {
+ _do_destroy(mStorage, mCount);
+ SharedBuffer::dealloc(sb);
+ }
+ }
+}
+
+void* VectorImpl::_grow(size_t where, size_t amount)
+{
+// ALOGV("_grow(this=%p, where=%d, amount=%d) count=%d, capacity=%d",
+// this, (int)where, (int)amount, (int)mCount, (int)capacity());
+
+ ALOG_ASSERT(where <= mCount,
+ "[%p] _grow: where=%d, amount=%d, count=%d",
+ this, (int)where, (int)amount, (int)mCount); // caller already checked
+
+ const size_t new_size = mCount + amount;
+ if (capacity() < new_size) {
+ const size_t new_capacity = max(kMinVectorCapacity, ((new_size*3)+1)/2);
+// ALOGV("grow vector %p, new_capacity=%d", this, (int)new_capacity);
+ if ((mStorage) &&
+ (mCount==where) &&
+ (mFlags & HAS_TRIVIAL_COPY) &&
+ (mFlags & HAS_TRIVIAL_DTOR))
+ {
+ const SharedBuffer* cur_sb = SharedBuffer::bufferFromData(mStorage);
+ SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize);
+ if (sb) {
+ mStorage = sb->data();
+ } else {
+ return NULL;
+ }
+ } else {
+ SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize);
+ if (sb) {
+ void* array = sb->data();
+ if (where != 0) {
+ _do_copy(array, mStorage, where);
+ }
+ if (where != mCount) {
+ const void* from = reinterpret_cast<const uint8_t *>(mStorage) + where*mItemSize;
+ void* dest = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize;
+ _do_copy(dest, from, mCount-where);
+ }
+ release_storage();
+ mStorage = const_cast<void*>(array);
+ } else {
+ return NULL;
+ }
+ }
+ } else {
+ void* array = editArrayImpl();
+ if (where != mCount) {
+ const void* from = reinterpret_cast<const uint8_t *>(array) + where*mItemSize;
+ void* to = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize;
+ _do_move_forward(to, from, mCount - where);
+ }
+ }
+ mCount = new_size;
+ void* free_space = const_cast<void*>(itemLocation(where));
+ return free_space;
+}
+
+void VectorImpl::_shrink(size_t where, size_t amount)
+{
+ if (!mStorage)
+ return;
+
+// ALOGV("_shrink(this=%p, where=%d, amount=%d) count=%d, capacity=%d",
+// this, (int)where, (int)amount, (int)mCount, (int)capacity());
+
+ ALOG_ASSERT(where + amount <= mCount,
+ "[%p] _shrink: where=%d, amount=%d, count=%d",
+ this, (int)where, (int)amount, (int)mCount); // caller already checked
+
+ const size_t new_size = mCount - amount;
+ if (new_size*3 < capacity()) {
+ const size_t new_capacity = max(kMinVectorCapacity, new_size*2);
+// ALOGV("shrink vector %p, new_capacity=%d", this, (int)new_capacity);
+ if ((where == new_size) &&
+ (mFlags & HAS_TRIVIAL_COPY) &&
+ (mFlags & HAS_TRIVIAL_DTOR))
+ {
+ const SharedBuffer* cur_sb = SharedBuffer::bufferFromData(mStorage);
+ SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize);
+ if (sb) {
+ mStorage = sb->data();
+ } else {
+ return;
+ }
+ } else {
+ SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize);
+ if (sb) {
+ void* array = sb->data();
+ if (where != 0) {
+ _do_copy(array, mStorage, where);
+ }
+ if (where != new_size) {
+ const void* from = reinterpret_cast<const uint8_t *>(mStorage) + (where+amount)*mItemSize;
+ void* dest = reinterpret_cast<uint8_t *>(array) + where*mItemSize;
+ _do_copy(dest, from, new_size - where);
+ }
+ release_storage();
+ mStorage = const_cast<void*>(array);
+ } else{
+ return;
+ }
+ }
+ } else {
+ void* array = editArrayImpl();
+ void* to = reinterpret_cast<uint8_t *>(array) + where*mItemSize;
+ _do_destroy(to, amount);
+ if (where != new_size) {
+ const void* from = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize;
+ _do_move_backward(to, from, new_size - where);
+ }
+ }
+ mCount = new_size;
+}
+
+size_t VectorImpl::itemSize() const {
+ return mItemSize;
+}
+
+void VectorImpl::_do_construct(void* storage, size_t num) const
+{
+ if (!(mFlags & HAS_TRIVIAL_CTOR)) {
+ do_construct(storage, num);
+ }
+}
+
+void VectorImpl::_do_destroy(void* storage, size_t num) const
+{
+ if (!(mFlags & HAS_TRIVIAL_DTOR)) {
+ do_destroy(storage, num);
+ }
+}
+
+void VectorImpl::_do_copy(void* dest, const void* from, size_t num) const
+{
+ if (!(mFlags & HAS_TRIVIAL_COPY)) {
+ do_copy(dest, from, num);
+ } else {
+ memcpy(dest, from, num*itemSize());
+ }
+}
+
+void VectorImpl::_do_splat(void* dest, const void* item, size_t num) const {
+ do_splat(dest, item, num);
+}
+
+void VectorImpl::_do_move_forward(void* dest, const void* from, size_t num) const {
+ do_move_forward(dest, from, num);
+}
+
+void VectorImpl::_do_move_backward(void* dest, const void* from, size_t num) const {
+ do_move_backward(dest, from, num);
+}
+
+/*****************************************************************************/
+
+SortedVectorImpl::SortedVectorImpl(size_t itemSize, uint32_t flags)
+ : VectorImpl(itemSize, flags)
+{
+}
+
+SortedVectorImpl::SortedVectorImpl(const VectorImpl& rhs)
+: VectorImpl(rhs)
+{
+}
+
+SortedVectorImpl::~SortedVectorImpl()
+{
+}
+
+SortedVectorImpl& SortedVectorImpl::operator = (const SortedVectorImpl& rhs)
+{
+ return static_cast<SortedVectorImpl&>( VectorImpl::operator = (static_cast<const VectorImpl&>(rhs)) );
+}
+
+ssize_t SortedVectorImpl::indexOf(const void* item) const
+{
+ return _indexOrderOf(item);
+}
+
+size_t SortedVectorImpl::orderOf(const void* item) const
+{
+ size_t o;
+ _indexOrderOf(item, &o);
+ return o;
+}
+
+ssize_t SortedVectorImpl::_indexOrderOf(const void* item, size_t* order) const
+{
+ // binary search
+ ssize_t err = NAME_NOT_FOUND;
+ ssize_t l = 0;
+ ssize_t h = size()-1;
+ ssize_t mid;
+ const void* a = arrayImpl();
+ const size_t s = itemSize();
+ while (l <= h) {
+ mid = l + (h - l)/2;
+ const void* const curr = reinterpret_cast<const char *>(a) + (mid*s);
+ const int c = do_compare(curr, item);
+ if (c == 0) {
+ err = l = mid;
+ break;
+ } else if (c < 0) {
+ l = mid + 1;
+ } else {
+ h = mid - 1;
+ }
+ }
+ if (order) *order = l;
+ return err;
+}
+
+ssize_t SortedVectorImpl::add(const void* item)
+{
+ size_t order;
+ ssize_t index = _indexOrderOf(item, &order);
+ if (index < 0) {
+ index = VectorImpl::insertAt(item, order, 1);
+ } else {
+ index = VectorImpl::replaceAt(item, index);
+ }
+ return index;
+}
+
+ssize_t SortedVectorImpl::merge(const VectorImpl& vector)
+{
+ // naive merge...
+ if (!vector.isEmpty()) {
+ const void* buffer = vector.arrayImpl();
+ const size_t is = itemSize();
+ size_t s = vector.size();
+ for (size_t i=0 ; i<s ; i++) {
+ ssize_t err = add( reinterpret_cast<const char*>(buffer) + i*is );
+ if (err<0) {
+ return err;
+ }
+ }
+ }
+ return NO_ERROR;
+}
+
+ssize_t SortedVectorImpl::merge(const SortedVectorImpl& vector)
+{
+ // we've merging a sorted vector... nice!
+ ssize_t err = NO_ERROR;
+ if (!vector.isEmpty()) {
+ // first take care of the case where the vectors are sorted together
+ if (do_compare(vector.itemLocation(vector.size()-1), arrayImpl()) <= 0) {
+ err = VectorImpl::insertVectorAt(static_cast<const VectorImpl&>(vector), 0);
+ } else if (do_compare(vector.arrayImpl(), itemLocation(size()-1)) >= 0) {
+ err = VectorImpl::appendVector(static_cast<const VectorImpl&>(vector));
+ } else {
+ // this could be made a little better
+ err = merge(static_cast<const VectorImpl&>(vector));
+ }
+ }
+ return err;
+}
+
+ssize_t SortedVectorImpl::remove(const void* item)
+{
+ ssize_t i = indexOf(item);
+ if (i>=0) {
+ VectorImpl::removeItemsAt(i, 1);
+ }
+ return i;
+}
+
+/*****************************************************************************/
+
+}; // namespace android
+
diff --git a/libutils/misc.cpp b/libutils/misc.cpp
new file mode 100644
index 0000000..216dc14
--- /dev/null
+++ b/libutils/misc.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2005 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 "misc"
+
+//
+// Miscellaneous utility functions.
+//
+#include <utils/misc.h>
+#include <utils/Log.h>
+
+#include <sys/stat.h>
+#include <string.h>
+#include <stdio.h>
+
+#if !defined(_WIN32)
+# include <pthread.h>
+#endif
+
+#include <utils/Vector.h>
+
+using namespace android;
+
+namespace android {
+
+struct sysprop_change_callback_info {
+ sysprop_change_callback callback;
+ int priority;
+};
+
+#if !defined(_WIN32)
+static pthread_mutex_t gSyspropMutex = PTHREAD_MUTEX_INITIALIZER;
+static Vector<sysprop_change_callback_info>* gSyspropList = NULL;
+#endif
+
+void add_sysprop_change_callback(sysprop_change_callback cb, int priority) {
+#if !defined(_WIN32)
+ pthread_mutex_lock(&gSyspropMutex);
+ if (gSyspropList == NULL) {
+ gSyspropList = new Vector<sysprop_change_callback_info>();
+ }
+ sysprop_change_callback_info info;
+ info.callback = cb;
+ info.priority = priority;
+ bool added = false;
+ for (size_t i=0; i<gSyspropList->size(); i++) {
+ if (priority >= gSyspropList->itemAt(i).priority) {
+ gSyspropList->insertAt(info, i);
+ added = true;
+ break;
+ }
+ }
+ if (!added) {
+ gSyspropList->add(info);
+ }
+ pthread_mutex_unlock(&gSyspropMutex);
+#endif
+}
+
+void report_sysprop_change() {
+#if !defined(_WIN32)
+ pthread_mutex_lock(&gSyspropMutex);
+ Vector<sysprop_change_callback_info> listeners;
+ if (gSyspropList != NULL) {
+ listeners = *gSyspropList;
+ }
+ pthread_mutex_unlock(&gSyspropMutex);
+
+ //ALOGI("Reporting sysprop change to %d listeners", listeners.size());
+ for (size_t i=0; i<listeners.size(); i++) {
+ listeners[i].callback();
+ }
+#endif
+}
+
+}; // namespace android
diff --git a/libutils/primes.py b/libutils/primes.py
new file mode 100755
index 0000000..e161dd8
--- /dev/null
+++ b/libutils/primes.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python2.6
+#
+# 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.
+#
+
+#
+# Generates a table of prime numbers for use in BasicHashtable.cpp.
+#
+# Each prime is chosen such that it is a little more than twice as large as
+# the previous prime in the table. This makes it easier to choose a new
+# hashtable size when the underlying array is grown by as nominal factor
+# of two each time.
+#
+
+def is_odd_prime(n):
+ limit = (n - 1) / 2
+ d = 3
+ while d <= limit:
+ if n % d == 0:
+ return False
+ d += 2
+ return True
+
+print "static size_t PRIMES[] = {"
+
+n = 5
+max = 2**31 - 1
+while n < max:
+ print " %d," % (n)
+ n = n * 2 + 1
+ while not is_odd_prime(n):
+ n += 2
+
+print " 0,"
+print "};"
diff --git a/libutils/tests/Android.mk b/libutils/tests/Android.mk
new file mode 100644
index 0000000..7cfad89
--- /dev/null
+++ b/libutils/tests/Android.mk
@@ -0,0 +1,40 @@
+#
+# Copyright (C) 2014 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.
+#
+
+# Build the unit tests.
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libutils_tests
+
+LOCAL_SRC_FILES := \
+ BasicHashtable_test.cpp \
+ BlobCache_test.cpp \
+ BitSet_test.cpp \
+ Looper_test.cpp \
+ LruCache_test.cpp \
+ String8_test.cpp \
+ Unicode_test.cpp \
+ Vector_test.cpp \
+
+LOCAL_SHARED_LIBRARIES := \
+ libz \
+ liblog \
+ libcutils \
+ libutils \
+
+include $(BUILD_NATIVE_TEST)
diff --git a/libutils/tests/BasicHashtable_test.cpp b/libutils/tests/BasicHashtable_test.cpp
new file mode 100644
index 0000000..4b3a717
--- /dev/null
+++ b/libutils/tests/BasicHashtable_test.cpp
@@ -0,0 +1,582 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "BasicHashtable_test"
+
+#include <utils/BasicHashtable.h>
+#include <cutils/log.h>
+#include <gtest/gtest.h>
+#include <unistd.h>
+
+namespace {
+
+typedef int SimpleKey;
+typedef int SimpleValue;
+typedef android::key_value_pair_t<SimpleKey, SimpleValue> SimpleEntry;
+typedef android::BasicHashtable<SimpleKey, SimpleEntry> SimpleHashtable;
+
+struct ComplexKey {
+ int k;
+
+ explicit ComplexKey(int k) : k(k) {
+ instanceCount += 1;
+ }
+
+ ComplexKey(const ComplexKey& other) : k(other.k) {
+ instanceCount += 1;
+ }
+
+ ~ComplexKey() {
+ instanceCount -= 1;
+ }
+
+ bool operator ==(const ComplexKey& other) const {
+ return k == other.k;
+ }
+
+ bool operator !=(const ComplexKey& other) const {
+ return k != other.k;
+ }
+
+ static ssize_t instanceCount;
+};
+
+ssize_t ComplexKey::instanceCount = 0;
+
+struct ComplexValue {
+ int v;
+
+ explicit ComplexValue(int v) : v(v) {
+ instanceCount += 1;
+ }
+
+ ComplexValue(const ComplexValue& other) : v(other.v) {
+ instanceCount += 1;
+ }
+
+ ~ComplexValue() {
+ instanceCount -= 1;
+ }
+
+ static ssize_t instanceCount;
+};
+
+ssize_t ComplexValue::instanceCount = 0;
+
+} // namespace
+
+
+namespace android {
+
+typedef key_value_pair_t<ComplexKey, ComplexValue> ComplexEntry;
+typedef BasicHashtable<ComplexKey, ComplexEntry> ComplexHashtable;
+
+template<> inline hash_t hash_type(const ComplexKey& value) {
+ return hash_type(value.k);
+}
+
+class BasicHashtableTest : public testing::Test {
+protected:
+ virtual void SetUp() {
+ ComplexKey::instanceCount = 0;
+ ComplexValue::instanceCount = 0;
+ }
+
+ virtual void TearDown() {
+ ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0));
+ }
+
+ void assertInstanceCount(ssize_t keys, ssize_t values) {
+ if (keys != ComplexKey::instanceCount || values != ComplexValue::instanceCount) {
+ FAIL() << "Expected " << keys << " keys and " << values << " values "
+ "but there were actually " << ComplexKey::instanceCount << " keys and "
+ << ComplexValue::instanceCount << " values";
+ }
+ }
+
+public:
+ template <typename TKey, typename TEntry>
+ static void cookieAt(const BasicHashtable<TKey, TEntry>& h, size_t index,
+ bool* collision, bool* present, hash_t* hash) {
+ uint32_t cookie = h.cookieAt(index);
+ *collision = cookie & BasicHashtable<TKey, TEntry>::Bucket::COLLISION;
+ *present = cookie & BasicHashtable<TKey, TEntry>::Bucket::PRESENT;
+ *hash = cookie & BasicHashtable<TKey, TEntry>::Bucket::HASH_MASK;
+ }
+
+ template <typename TKey, typename TEntry>
+ static const void* getBuckets(const BasicHashtable<TKey, TEntry>& h) {
+ return h.mBuckets;
+ }
+};
+
+template <typename TKey, typename TValue>
+static size_t add(BasicHashtable<TKey, key_value_pair_t<TKey, TValue> >& h,
+ const TKey& key, const TValue& value) {
+ return h.add(hash_type(key), key_value_pair_t<TKey, TValue>(key, value));
+}
+
+template <typename TKey, typename TValue>
+static ssize_t find(BasicHashtable<TKey, key_value_pair_t<TKey, TValue> >& h,
+ ssize_t index, const TKey& key) {
+ return h.find(index, hash_type(key), key);
+}
+
+template <typename TKey, typename TValue>
+static bool remove(BasicHashtable<TKey, key_value_pair_t<TKey, TValue> >& h,
+ const TKey& key) {
+ ssize_t index = find(h, -1, key);
+ if (index >= 0) {
+ h.removeAt(index);
+ return true;
+ }
+ return false;
+}
+
+template <typename TEntry>
+static void getKeyValue(const TEntry& entry, int* key, int* value);
+
+template <> void getKeyValue(const SimpleEntry& entry, int* key, int* value) {
+ *key = entry.key;
+ *value = entry.value;
+}
+
+template <> void getKeyValue(const ComplexEntry& entry, int* key, int* value) {
+ *key = entry.key.k;
+ *value = entry.value.v;
+}
+
+template <typename TKey, typename TValue>
+static void dump(BasicHashtable<TKey, key_value_pair_t<TKey, TValue> >& h) {
+ ALOGD("hashtable %p, size=%u, capacity=%u, bucketCount=%u",
+ &h, h.size(), h.capacity(), h.bucketCount());
+ for (size_t i = 0; i < h.bucketCount(); i++) {
+ bool collision, present;
+ hash_t hash;
+ BasicHashtableTest::cookieAt(h, i, &collision, &present, &hash);
+ if (present) {
+ int key, value;
+ getKeyValue(h.entryAt(i), &key, &value);
+ ALOGD(" [%3u] = collision=%d, present=%d, hash=0x%08x, key=%3d, value=%3d, "
+ "hash_type(key)=0x%08x",
+ i, collision, present, hash, key, value, hash_type(key));
+ } else {
+ ALOGD(" [%3u] = collision=%d, present=%d",
+ i, collision, present);
+ }
+ }
+}
+
+TEST_F(BasicHashtableTest, DefaultConstructor_WithDefaultProperties) {
+ SimpleHashtable h;
+
+ EXPECT_EQ(0U, h.size());
+ EXPECT_EQ(3U, h.capacity());
+ EXPECT_EQ(5U, h.bucketCount());
+ EXPECT_EQ(0.75f, h.loadFactor());
+}
+
+TEST_F(BasicHashtableTest, Constructor_WithNonUnityLoadFactor) {
+ SimpleHashtable h(52, 0.8f);
+
+ EXPECT_EQ(0U, h.size());
+ EXPECT_EQ(77U, h.capacity());
+ EXPECT_EQ(97U, h.bucketCount());
+ EXPECT_EQ(0.8f, h.loadFactor());
+}
+
+TEST_F(BasicHashtableTest, Constructor_WithUnityLoadFactorAndExactCapacity) {
+ SimpleHashtable h(46, 1.0f);
+
+ EXPECT_EQ(0U, h.size());
+ EXPECT_EQ(46U, h.capacity()); // must be one less than bucketCount because loadFactor == 1.0f
+ EXPECT_EQ(47U, h.bucketCount());
+ EXPECT_EQ(1.0f, h.loadFactor());
+}
+
+TEST_F(BasicHashtableTest, Constructor_WithUnityLoadFactorAndInexactCapacity) {
+ SimpleHashtable h(42, 1.0f);
+
+ EXPECT_EQ(0U, h.size());
+ EXPECT_EQ(46U, h.capacity()); // must be one less than bucketCount because loadFactor == 1.0f
+ EXPECT_EQ(47U, h.bucketCount());
+ EXPECT_EQ(1.0f, h.loadFactor());
+}
+
+TEST_F(BasicHashtableTest, FindAddFindRemoveFind_OneEntry) {
+ SimpleHashtable h;
+ ssize_t index = find(h, -1, 8);
+ ASSERT_EQ(-1, index);
+
+ index = add(h, 8, 1);
+ ASSERT_EQ(1U, h.size());
+
+ ASSERT_EQ(index, find(h, -1, 8));
+ ASSERT_EQ(8, h.entryAt(index).key);
+ ASSERT_EQ(1, h.entryAt(index).value);
+
+ index = find(h, index, 8);
+ ASSERT_EQ(-1, index);
+
+ ASSERT_TRUE(remove(h, 8));
+ ASSERT_EQ(0U, h.size());
+
+ index = find(h, -1, 8);
+ ASSERT_EQ(-1, index);
+}
+
+TEST_F(BasicHashtableTest, FindAddFindRemoveFind_MultipleEntryWithUniqueKey) {
+ const size_t N = 11;
+
+ SimpleHashtable h;
+ for (size_t i = 0; i < N; i++) {
+ ssize_t index = find(h, -1, int(i));
+ ASSERT_EQ(-1, index);
+
+ index = add(h, int(i), int(i * 10));
+ ASSERT_EQ(i + 1, h.size());
+
+ ASSERT_EQ(index, find(h, -1, int(i)));
+ ASSERT_EQ(int(i), h.entryAt(index).key);
+ ASSERT_EQ(int(i * 10), h.entryAt(index).value);
+
+ index = find(h, index, int(i));
+ ASSERT_EQ(-1, index);
+ }
+
+ for (size_t i = N; --i > 0; ) {
+ ASSERT_TRUE(remove(h, int(i))) << "i = " << i;
+ ASSERT_EQ(i, h.size());
+
+ ssize_t index = find(h, -1, int(i));
+ ASSERT_EQ(-1, index);
+ }
+}
+
+TEST_F(BasicHashtableTest, FindAddFindRemoveFind_MultipleEntryWithDuplicateKey) {
+ const size_t N = 11;
+ const int K = 1;
+
+ SimpleHashtable h;
+ for (size_t i = 0; i < N; i++) {
+ ssize_t index = find(h, -1, K);
+ if (i == 0) {
+ ASSERT_EQ(-1, index);
+ } else {
+ ASSERT_NE(-1, index);
+ }
+
+ add(h, K, int(i));
+ ASSERT_EQ(i + 1, h.size());
+
+ index = -1;
+ int values = 0;
+ for (size_t j = 0; j <= i; j++) {
+ index = find(h, index, K);
+ ASSERT_GE(index, 0);
+ ASSERT_EQ(K, h.entryAt(index).key);
+ values |= 1 << h.entryAt(index).value;
+ }
+ ASSERT_EQ(values, (1 << (i + 1)) - 1);
+
+ index = find(h, index, K);
+ ASSERT_EQ(-1, index);
+ }
+
+ for (size_t i = N; --i > 0; ) {
+ ASSERT_TRUE(remove(h, K)) << "i = " << i;
+ ASSERT_EQ(i, h.size());
+
+ ssize_t index = -1;
+ for (size_t j = 0; j < i; j++) {
+ index = find(h, index, K);
+ ASSERT_GE(index, 0);
+ ASSERT_EQ(K, h.entryAt(index).key);
+ }
+
+ index = find(h, index, K);
+ ASSERT_EQ(-1, index);
+ }
+}
+
+TEST_F(BasicHashtableTest, Clear_WhenAlreadyEmpty_DoesNothing) {
+ SimpleHashtable h;
+ h.clear();
+
+ EXPECT_EQ(0U, h.size());
+ EXPECT_EQ(3U, h.capacity());
+ EXPECT_EQ(5U, h.bucketCount());
+ EXPECT_EQ(0.75f, h.loadFactor());
+}
+
+TEST_F(BasicHashtableTest, Clear_AfterElementsAdded_RemovesThem) {
+ SimpleHashtable h;
+ add(h, 0, 0);
+ add(h, 1, 0);
+ h.clear();
+
+ EXPECT_EQ(0U, h.size());
+ EXPECT_EQ(3U, h.capacity());
+ EXPECT_EQ(5U, h.bucketCount());
+ EXPECT_EQ(0.75f, h.loadFactor());
+}
+
+TEST_F(BasicHashtableTest, Clear_AfterElementsAdded_DestroysThem) {
+ ComplexHashtable h;
+ add(h, ComplexKey(0), ComplexValue(0));
+ add(h, ComplexKey(1), ComplexValue(0));
+ ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
+
+ h.clear();
+ ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0));
+
+ EXPECT_EQ(0U, h.size());
+ EXPECT_EQ(3U, h.capacity());
+ EXPECT_EQ(5U, h.bucketCount());
+ EXPECT_EQ(0.75f, h.loadFactor());
+}
+
+TEST_F(BasicHashtableTest, Remove_AfterElementsAdded_DestroysThem) {
+ ComplexHashtable h;
+ add(h, ComplexKey(0), ComplexValue(0));
+ add(h, ComplexKey(1), ComplexValue(0));
+ ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
+
+ ASSERT_TRUE(remove(h, ComplexKey(0)));
+ ASSERT_NO_FATAL_FAILURE(assertInstanceCount(1, 1));
+
+ ASSERT_TRUE(remove(h, ComplexKey(1)));
+ ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0));
+
+ EXPECT_EQ(0U, h.size());
+ EXPECT_EQ(3U, h.capacity());
+ EXPECT_EQ(5U, h.bucketCount());
+ EXPECT_EQ(0.75f, h.loadFactor());
+}
+
+TEST_F(BasicHashtableTest, Destructor_AfterElementsAdded_DestroysThem) {
+ {
+ ComplexHashtable h;
+ add(h, ComplexKey(0), ComplexValue(0));
+ add(h, ComplexKey(1), ComplexValue(0));
+ ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
+ } // h is destroyed here
+
+ ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0));
+}
+
+TEST_F(BasicHashtableTest, Next_WhenEmpty_ReturnsMinusOne) {
+ SimpleHashtable h;
+
+ ASSERT_EQ(-1, h.next(-1));
+}
+
+TEST_F(BasicHashtableTest, Next_WhenNonEmpty_IteratesOverAllEntries) {
+ const int N = 88;
+
+ SimpleHashtable h;
+ for (int i = 0; i < N; i++) {
+ add(h, i, i * 10);
+ }
+
+ bool set[N];
+ memset(set, 0, sizeof(bool) * N);
+ int count = 0;
+ for (ssize_t index = -1; (index = h.next(index)) != -1; ) {
+ ASSERT_GE(index, 0);
+ ASSERT_LT(size_t(index), h.bucketCount());
+
+ const SimpleEntry& entry = h.entryAt(index);
+ ASSERT_GE(entry.key, 0);
+ ASSERT_LT(entry.key, N);
+ ASSERT_FALSE(set[entry.key]);
+ ASSERT_EQ(entry.key * 10, entry.value);
+
+ set[entry.key] = true;
+ count += 1;
+ }
+ ASSERT_EQ(N, count);
+}
+
+TEST_F(BasicHashtableTest, Add_RehashesOnDemand) {
+ SimpleHashtable h;
+ size_t initialCapacity = h.capacity();
+ size_t initialBucketCount = h.bucketCount();
+
+ for (size_t i = 0; i < initialCapacity; i++) {
+ add(h, int(i), 0);
+ }
+
+ EXPECT_EQ(initialCapacity, h.size());
+ EXPECT_EQ(initialCapacity, h.capacity());
+ EXPECT_EQ(initialBucketCount, h.bucketCount());
+
+ add(h, -1, -1);
+
+ EXPECT_EQ(initialCapacity + 1, h.size());
+ EXPECT_GT(h.capacity(), initialCapacity);
+ EXPECT_GT(h.bucketCount(), initialBucketCount);
+ EXPECT_GT(h.bucketCount(), h.capacity());
+}
+
+TEST_F(BasicHashtableTest, Rehash_WhenCapacityAndBucketCountUnchanged_DoesNothing) {
+ ComplexHashtable h;
+ add(h, ComplexKey(0), ComplexValue(0));
+ const void* oldBuckets = getBuckets(h);
+ ASSERT_NE((void*)NULL, oldBuckets);
+ ASSERT_NO_FATAL_FAILURE(assertInstanceCount(1, 1));
+
+ h.rehash(h.capacity(), h.loadFactor());
+
+ ASSERT_EQ(oldBuckets, getBuckets(h));
+ ASSERT_NO_FATAL_FAILURE(assertInstanceCount(1, 1));
+}
+
+TEST_F(BasicHashtableTest, Rehash_WhenEmptyAndHasNoBuckets_ButDoesNotAllocateBuckets) {
+ ComplexHashtable h;
+ ASSERT_EQ((void*)NULL, getBuckets(h));
+ ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0));
+
+ h.rehash(9, 1.0f);
+
+ EXPECT_EQ(0U, h.size());
+ EXPECT_EQ(10U, h.capacity());
+ EXPECT_EQ(11U, h.bucketCount());
+ EXPECT_EQ(1.0f, h.loadFactor());
+ EXPECT_EQ((void*)NULL, getBuckets(h));
+ ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0));
+}
+
+TEST_F(BasicHashtableTest, Rehash_WhenEmptyAndHasBuckets_ReleasesBucketsAndSetsCapacity) {
+ ComplexHashtable h(10);
+ add(h, ComplexKey(0), ComplexValue(0));
+ ASSERT_TRUE(remove(h, ComplexKey(0)));
+ ASSERT_NE((void*)NULL, getBuckets(h));
+ ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0));
+
+ h.rehash(0, 0.75f);
+
+ EXPECT_EQ(0U, h.size());
+ EXPECT_EQ(3U, h.capacity());
+ EXPECT_EQ(5U, h.bucketCount());
+ EXPECT_EQ(0.75f, h.loadFactor());
+ EXPECT_EQ((void*)NULL, getBuckets(h));
+ ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0));
+}
+
+TEST_F(BasicHashtableTest, Rehash_WhenLessThanCurrentCapacity_ShrinksBuckets) {
+ ComplexHashtable h(10);
+ add(h, ComplexKey(0), ComplexValue(0));
+ add(h, ComplexKey(1), ComplexValue(1));
+ const void* oldBuckets = getBuckets(h);
+ ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
+
+ h.rehash(0, 0.75f);
+
+ EXPECT_EQ(2U, h.size());
+ EXPECT_EQ(3U, h.capacity());
+ EXPECT_EQ(5U, h.bucketCount());
+ EXPECT_EQ(0.75f, h.loadFactor());
+ EXPECT_NE(oldBuckets, getBuckets(h));
+ ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
+}
+
+TEST_F(BasicHashtableTest, CopyOnWrite) {
+ ComplexHashtable h1;
+ add(h1, ComplexKey(0), ComplexValue(0));
+ add(h1, ComplexKey(1), ComplexValue(1));
+ const void* originalBuckets = getBuckets(h1);
+ ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
+ ssize_t index0 = find(h1, -1, ComplexKey(0));
+ EXPECT_GE(index0, 0);
+
+ // copy constructor acquires shared reference
+ ComplexHashtable h2(h1);
+ ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
+ ASSERT_EQ(originalBuckets, getBuckets(h2));
+ EXPECT_EQ(h1.size(), h2.size());
+ EXPECT_EQ(h1.capacity(), h2.capacity());
+ EXPECT_EQ(h1.bucketCount(), h2.bucketCount());
+ EXPECT_EQ(h1.loadFactor(), h2.loadFactor());
+ EXPECT_EQ(index0, find(h2, -1, ComplexKey(0)));
+
+ // operator= acquires shared reference
+ ComplexHashtable h3;
+ h3 = h2;
+ ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
+ ASSERT_EQ(originalBuckets, getBuckets(h3));
+ EXPECT_EQ(h1.size(), h3.size());
+ EXPECT_EQ(h1.capacity(), h3.capacity());
+ EXPECT_EQ(h1.bucketCount(), h3.bucketCount());
+ EXPECT_EQ(h1.loadFactor(), h3.loadFactor());
+ EXPECT_EQ(index0, find(h3, -1, ComplexKey(0)));
+
+ // editEntryAt copies shared contents
+ h1.editEntryAt(index0).value.v = 42;
+ ASSERT_NO_FATAL_FAILURE(assertInstanceCount(4, 4));
+ ASSERT_NE(originalBuckets, getBuckets(h1));
+ EXPECT_EQ(42, h1.entryAt(index0).value.v);
+ EXPECT_EQ(0, h2.entryAt(index0).value.v);
+ EXPECT_EQ(0, h3.entryAt(index0).value.v);
+
+ // clear releases reference to shared contents
+ h2.clear();
+ ASSERT_NO_FATAL_FAILURE(assertInstanceCount(4, 4));
+ EXPECT_EQ(0U, h2.size());
+ ASSERT_NE(originalBuckets, getBuckets(h2));
+
+ // operator= acquires shared reference, destroys unshared contents
+ h1 = h3;
+ ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
+ ASSERT_EQ(originalBuckets, getBuckets(h1));
+ EXPECT_EQ(h3.size(), h1.size());
+ EXPECT_EQ(h3.capacity(), h1.capacity());
+ EXPECT_EQ(h3.bucketCount(), h1.bucketCount());
+ EXPECT_EQ(h3.loadFactor(), h1.loadFactor());
+ EXPECT_EQ(index0, find(h1, -1, ComplexKey(0)));
+
+ // add copies shared contents
+ add(h1, ComplexKey(2), ComplexValue(2));
+ ASSERT_NO_FATAL_FAILURE(assertInstanceCount(5, 5));
+ ASSERT_NE(originalBuckets, getBuckets(h1));
+ EXPECT_EQ(3U, h1.size());
+ EXPECT_EQ(0U, h2.size());
+ EXPECT_EQ(2U, h3.size());
+
+ // remove copies shared contents
+ h1 = h3;
+ ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
+ ASSERT_EQ(originalBuckets, getBuckets(h1));
+ h1.removeAt(index0);
+ ASSERT_NO_FATAL_FAILURE(assertInstanceCount(3, 3));
+ ASSERT_NE(originalBuckets, getBuckets(h1));
+ EXPECT_EQ(1U, h1.size());
+ EXPECT_EQ(0U, h2.size());
+ EXPECT_EQ(2U, h3.size());
+
+ // rehash copies shared contents
+ h1 = h3;
+ ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
+ ASSERT_EQ(originalBuckets, getBuckets(h1));
+ h1.rehash(10, 1.0f);
+ ASSERT_NO_FATAL_FAILURE(assertInstanceCount(4, 4));
+ ASSERT_NE(originalBuckets, getBuckets(h1));
+ EXPECT_EQ(2U, h1.size());
+ EXPECT_EQ(0U, h2.size());
+ EXPECT_EQ(2U, h3.size());
+}
+
+} // namespace android
diff --git a/libutils/tests/BitSet_test.cpp b/libutils/tests/BitSet_test.cpp
new file mode 100644
index 0000000..38b668a
--- /dev/null
+++ b/libutils/tests/BitSet_test.cpp
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BitSet_test"
+
+#include <utils/BitSet.h>
+#include <cutils/log.h>
+#include <gtest/gtest.h>
+#include <unistd.h>
+
+namespace android {
+
+class BitSet32Test : public testing::Test {
+protected:
+ BitSet32 b1;
+ BitSet32 b2;
+ virtual void TearDown() {
+ b1.clear();
+ b2.clear();
+ }
+};
+
+
+TEST_F(BitSet32Test, BitWiseOr) {
+ b1.markBit(2);
+ b2.markBit(4);
+
+ BitSet32 tmp = b1 | b2;
+ EXPECT_EQ(tmp.count(), 2u);
+ EXPECT_TRUE(tmp.hasBit(2) && tmp.hasBit(4));
+ // Check that the operator is symmetric
+ EXPECT_TRUE((b2 | b1) == (b1 | b2));
+
+ b1 |= b2;
+ EXPECT_EQ(b1.count(), 2u);
+ EXPECT_TRUE(b1.hasBit(2) && b1.hasBit(4));
+ EXPECT_TRUE(b2.hasBit(4) && b2.count() == 1u);
+}
+TEST_F(BitSet32Test, BitWiseAnd_Disjoint) {
+ b1.markBit(2);
+ b1.markBit(4);
+ b1.markBit(6);
+
+ BitSet32 tmp = b1 & b2;
+ EXPECT_TRUE(tmp.isEmpty());
+ // Check that the operator is symmetric
+ EXPECT_TRUE((b2 & b1) == (b1 & b2));
+
+ b2 &= b1;
+ EXPECT_TRUE(b2.isEmpty());
+ EXPECT_EQ(b1.count(), 3u);
+ EXPECT_TRUE(b1.hasBit(2) && b1.hasBit(4) && b1.hasBit(6));
+}
+
+TEST_F(BitSet32Test, BitWiseAnd_NonDisjoint) {
+ b1.markBit(2);
+ b1.markBit(4);
+ b1.markBit(6);
+ b2.markBit(3);
+ b2.markBit(6);
+ b2.markBit(9);
+
+ BitSet32 tmp = b1 & b2;
+ EXPECT_EQ(tmp.count(), 1u);
+ EXPECT_TRUE(tmp.hasBit(6));
+ // Check that the operator is symmetric
+ EXPECT_TRUE((b2 & b1) == (b1 & b2));
+
+ b1 &= b2;
+ EXPECT_EQ(b1.count(), 1u);
+ EXPECT_EQ(b2.count(), 3u);
+ EXPECT_TRUE(b2.hasBit(3) && b2.hasBit(6) && b2.hasBit(9));
+}
+
+TEST_F(BitSet32Test, MarkFirstUnmarkedBit) {
+ b1.markBit(1);
+
+ b1.markFirstUnmarkedBit();
+ EXPECT_EQ(b1.count(), 2u);
+ EXPECT_TRUE(b1.hasBit(0) && b1.hasBit(1));
+
+ b1.markFirstUnmarkedBit();
+ EXPECT_EQ(b1.count(), 3u);
+ EXPECT_TRUE(b1.hasBit(0) && b1.hasBit(1) && b1.hasBit(2));
+}
+
+TEST_F(BitSet32Test, ClearFirstMarkedBit) {
+ b1.markBit(0);
+ b1.markBit(10);
+
+ b1.clearFirstMarkedBit();
+ EXPECT_EQ(b1.count(), 1u);
+ EXPECT_TRUE(b1.hasBit(10));
+
+ b1.markBit(30);
+ b1.clearFirstMarkedBit();
+ EXPECT_EQ(b1.count(), 1u);
+ EXPECT_TRUE(b1.hasBit(30));
+}
+
+TEST_F(BitSet32Test, ClearLastMarkedBit) {
+ b1.markBit(10);
+ b1.markBit(31);
+
+ b1.clearLastMarkedBit();
+ EXPECT_EQ(b1.count(), 1u);
+ EXPECT_TRUE(b1.hasBit(10));
+
+ b1.markBit(5);
+ b1.clearLastMarkedBit();
+ EXPECT_EQ(b1.count(), 1u);
+ EXPECT_TRUE(b1.hasBit(5));
+}
+
+TEST_F(BitSet32Test, FillAndClear) {
+ EXPECT_TRUE(b1.isEmpty());
+ for (size_t i = 0; i < 32; i++) {
+ b1.markFirstUnmarkedBit();
+ }
+ EXPECT_TRUE(b1.isFull());
+ b1.clear();
+ EXPECT_TRUE(b1.isEmpty());
+}
+
+TEST_F(BitSet32Test, GetIndexOfBit) {
+ b1.markBit(1);
+ b1.markBit(4);
+ EXPECT_EQ(b1.getIndexOfBit(1), 0);
+ EXPECT_EQ(b1.getIndexOfBit(4), 1);
+ b1.markFirstUnmarkedBit();
+ EXPECT_EQ(b1.getIndexOfBit(1), 1);
+ EXPECT_EQ(b1.getIndexOfBit(4), 2);
+}
+
+class BitSet64Test : public testing::Test {
+protected:
+ BitSet64 b1;
+ BitSet64 b2;
+ virtual void TearDown() {
+ b1.clear();
+ b2.clear();
+ }
+};
+
+
+TEST_F(BitSet64Test, BitWiseOr) {
+ b1.markBit(20);
+ b2.markBit(40);
+
+ BitSet64 tmp = b1 | b2;
+ EXPECT_EQ(tmp.count(), 2u);
+ EXPECT_TRUE(tmp.hasBit(20) && tmp.hasBit(40));
+ // Check that the operator is symmetric
+ EXPECT_TRUE((b2 | b1) == (b1 | b2));
+
+ b1 |= b2;
+ EXPECT_EQ(b1.count(), 2u);
+ EXPECT_TRUE(b1.hasBit(20) && b1.hasBit(40));
+ EXPECT_TRUE(b2.hasBit(40) && b2.count() == 1u);
+}
+TEST_F(BitSet64Test, BitWiseAnd_Disjoint) {
+ b1.markBit(20);
+ b1.markBit(40);
+ b1.markBit(60);
+
+ BitSet64 tmp = b1 & b2;
+ EXPECT_TRUE(tmp.isEmpty());
+ // Check that the operator is symmetric
+ EXPECT_TRUE((b2 & b1) == (b1 & b2));
+
+ b2 &= b1;
+ EXPECT_TRUE(b2.isEmpty());
+ EXPECT_EQ(b1.count(), 3u);
+ EXPECT_TRUE(b1.hasBit(20) && b1.hasBit(40) && b1.hasBit(60));
+}
+
+TEST_F(BitSet64Test, BitWiseAnd_NonDisjoint) {
+ b1.markBit(20);
+ b1.markBit(40);
+ b1.markBit(60);
+ b2.markBit(30);
+ b2.markBit(60);
+ b2.markBit(63);
+
+ BitSet64 tmp = b1 & b2;
+ EXPECT_EQ(tmp.count(), 1u);
+ EXPECT_TRUE(tmp.hasBit(60));
+ // Check that the operator is symmetric
+ EXPECT_TRUE((b2 & b1) == (b1 & b2));
+
+ b1 &= b2;
+ EXPECT_EQ(b1.count(), 1u);
+ EXPECT_EQ(b2.count(), 3u);
+ EXPECT_TRUE(b2.hasBit(30) && b2.hasBit(60) && b2.hasBit(63));
+}
+
+TEST_F(BitSet64Test, MarkFirstUnmarkedBit) {
+ b1.markBit(1);
+
+ b1.markFirstUnmarkedBit();
+ EXPECT_EQ(b1.count(), 2u);
+ EXPECT_TRUE(b1.hasBit(0) && b1.hasBit(1));
+
+ b1.markFirstUnmarkedBit();
+ EXPECT_EQ(b1.count(), 3u);
+ EXPECT_TRUE(b1.hasBit(0) && b1.hasBit(1) && b1.hasBit(2));
+}
+
+TEST_F(BitSet64Test, ClearFirstMarkedBit) {
+ b1.markBit(0);
+ b1.markBit(10);
+
+ b1.clearFirstMarkedBit();
+ EXPECT_EQ(b1.count(), 1u);
+ EXPECT_TRUE(b1.hasBit(10));
+
+ b1.markBit(50);
+ b1.clearFirstMarkedBit();
+ EXPECT_EQ(b1.count(), 1u);
+ EXPECT_TRUE(b1.hasBit(50));
+}
+
+TEST_F(BitSet64Test, ClearLastMarkedBit) {
+ b1.markBit(10);
+ b1.markBit(63);
+
+ b1.clearLastMarkedBit();
+ EXPECT_EQ(b1.count(), 1u);
+ EXPECT_TRUE(b1.hasBit(10));
+
+ b1.markBit(5);
+ b1.clearLastMarkedBit();
+ EXPECT_EQ(b1.count(), 1u);
+ EXPECT_TRUE(b1.hasBit(5));
+}
+
+TEST_F(BitSet64Test, FillAndClear) {
+ EXPECT_TRUE(b1.isEmpty());
+ for (size_t i = 0; i < 64; i++) {
+ b1.markFirstUnmarkedBit();
+ }
+ EXPECT_TRUE(b1.isFull());
+ b1.clear();
+ EXPECT_TRUE(b1.isEmpty());
+}
+
+TEST_F(BitSet64Test, GetIndexOfBit) {
+ b1.markBit(10);
+ b1.markBit(40);
+ EXPECT_EQ(b1.getIndexOfBit(10), 0);
+ EXPECT_EQ(b1.getIndexOfBit(40), 1);
+ b1.markFirstUnmarkedBit();
+ EXPECT_EQ(b1.getIndexOfBit(10), 1);
+ EXPECT_EQ(b1.getIndexOfBit(40), 2);
+}
+
+} // namespace android
diff --git a/libutils/tests/BlobCache_test.cpp b/libutils/tests/BlobCache_test.cpp
new file mode 100644
index 0000000..dac4e2c
--- /dev/null
+++ b/libutils/tests/BlobCache_test.cpp
@@ -0,0 +1,421 @@
+/*
+ ** Copyright 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 <fcntl.h>
+#include <stdio.h>
+
+#include <gtest/gtest.h>
+
+#include <utils/BlobCache.h>
+#include <utils/Errors.h>
+
+namespace android {
+
+class BlobCacheTest : public ::testing::Test {
+protected:
+ enum {
+ MAX_KEY_SIZE = 6,
+ MAX_VALUE_SIZE = 8,
+ MAX_TOTAL_SIZE = 13,
+ };
+
+ virtual void SetUp() {
+ mBC = new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE);
+ }
+
+ virtual void TearDown() {
+ mBC.clear();
+ }
+
+ sp<BlobCache> mBC;
+};
+
+TEST_F(BlobCacheTest, CacheSingleValueSucceeds) {
+ unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ mBC->set("abcd", 4, "efgh", 4);
+ ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
+ ASSERT_EQ('e', buf[0]);
+ ASSERT_EQ('f', buf[1]);
+ ASSERT_EQ('g', buf[2]);
+ ASSERT_EQ('h', buf[3]);
+}
+
+TEST_F(BlobCacheTest, CacheTwoValuesSucceeds) {
+ unsigned char buf[2] = { 0xee, 0xee };
+ mBC->set("ab", 2, "cd", 2);
+ mBC->set("ef", 2, "gh", 2);
+ ASSERT_EQ(size_t(2), mBC->get("ab", 2, buf, 2));
+ ASSERT_EQ('c', buf[0]);
+ ASSERT_EQ('d', buf[1]);
+ ASSERT_EQ(size_t(2), mBC->get("ef", 2, buf, 2));
+ ASSERT_EQ('g', buf[0]);
+ ASSERT_EQ('h', buf[1]);
+}
+
+TEST_F(BlobCacheTest, GetOnlyWritesInsideBounds) {
+ unsigned char buf[6] = { 0xee, 0xee, 0xee, 0xee, 0xee, 0xee };
+ mBC->set("abcd", 4, "efgh", 4);
+ ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf+1, 4));
+ ASSERT_EQ(0xee, buf[0]);
+ ASSERT_EQ('e', buf[1]);
+ ASSERT_EQ('f', buf[2]);
+ ASSERT_EQ('g', buf[3]);
+ ASSERT_EQ('h', buf[4]);
+ ASSERT_EQ(0xee, buf[5]);
+}
+
+TEST_F(BlobCacheTest, GetOnlyWritesIfBufferIsLargeEnough) {
+ unsigned char buf[3] = { 0xee, 0xee, 0xee };
+ mBC->set("abcd", 4, "efgh", 4);
+ ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 3));
+ ASSERT_EQ(0xee, buf[0]);
+ ASSERT_EQ(0xee, buf[1]);
+ ASSERT_EQ(0xee, buf[2]);
+}
+
+TEST_F(BlobCacheTest, GetDoesntAccessNullBuffer) {
+ mBC->set("abcd", 4, "efgh", 4);
+ ASSERT_EQ(size_t(4), mBC->get("abcd", 4, NULL, 0));
+}
+
+TEST_F(BlobCacheTest, MultipleSetsCacheLatestValue) {
+ unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ mBC->set("abcd", 4, "efgh", 4);
+ mBC->set("abcd", 4, "ijkl", 4);
+ ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
+ ASSERT_EQ('i', buf[0]);
+ ASSERT_EQ('j', buf[1]);
+ ASSERT_EQ('k', buf[2]);
+ ASSERT_EQ('l', buf[3]);
+}
+
+TEST_F(BlobCacheTest, SecondSetKeepsFirstValueIfTooLarge) {
+ unsigned char buf[MAX_VALUE_SIZE+1] = { 0xee, 0xee, 0xee, 0xee };
+ mBC->set("abcd", 4, "efgh", 4);
+ mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1);
+ ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
+ ASSERT_EQ('e', buf[0]);
+ ASSERT_EQ('f', buf[1]);
+ ASSERT_EQ('g', buf[2]);
+ ASSERT_EQ('h', buf[3]);
+}
+
+TEST_F(BlobCacheTest, DoesntCacheIfKeyIsTooBig) {
+ char key[MAX_KEY_SIZE+1];
+ unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ for (int i = 0; i < MAX_KEY_SIZE+1; i++) {
+ key[i] = 'a';
+ }
+ mBC->set(key, MAX_KEY_SIZE+1, "bbbb", 4);
+ ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE+1, buf, 4));
+ ASSERT_EQ(0xee, buf[0]);
+ ASSERT_EQ(0xee, buf[1]);
+ ASSERT_EQ(0xee, buf[2]);
+ ASSERT_EQ(0xee, buf[3]);
+}
+
+TEST_F(BlobCacheTest, DoesntCacheIfValueIsTooBig) {
+ char buf[MAX_VALUE_SIZE+1];
+ for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
+ buf[i] = 'b';
+ }
+ mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1);
+ for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
+ buf[i] = 0xee;
+ }
+ ASSERT_EQ(size_t(0), mBC->get("abcd", 4, buf, MAX_VALUE_SIZE+1));
+ for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
+ SCOPED_TRACE(i);
+ ASSERT_EQ(0xee, buf[i]);
+ }
+}
+
+TEST_F(BlobCacheTest, DoesntCacheIfKeyValuePairIsTooBig) {
+ // Check a testing assumptions
+ ASSERT_TRUE(MAX_TOTAL_SIZE < MAX_KEY_SIZE + MAX_VALUE_SIZE);
+ ASSERT_TRUE(MAX_KEY_SIZE < MAX_TOTAL_SIZE);
+
+ enum { bufSize = MAX_TOTAL_SIZE - MAX_KEY_SIZE + 1 };
+
+ char key[MAX_KEY_SIZE];
+ char buf[bufSize];
+ for (int i = 0; i < MAX_KEY_SIZE; i++) {
+ key[i] = 'a';
+ }
+ for (int i = 0; i < bufSize; i++) {
+ buf[i] = 'b';
+ }
+
+ mBC->set(key, MAX_KEY_SIZE, buf, MAX_VALUE_SIZE);
+ ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE, NULL, 0));
+}
+
+TEST_F(BlobCacheTest, CacheMaxKeySizeSucceeds) {
+ char key[MAX_KEY_SIZE];
+ unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ for (int i = 0; i < MAX_KEY_SIZE; i++) {
+ key[i] = 'a';
+ }
+ mBC->set(key, MAX_KEY_SIZE, "wxyz", 4);
+ ASSERT_EQ(size_t(4), mBC->get(key, MAX_KEY_SIZE, buf, 4));
+ ASSERT_EQ('w', buf[0]);
+ ASSERT_EQ('x', buf[1]);
+ ASSERT_EQ('y', buf[2]);
+ ASSERT_EQ('z', buf[3]);
+}
+
+TEST_F(BlobCacheTest, CacheMaxValueSizeSucceeds) {
+ char buf[MAX_VALUE_SIZE];
+ for (int i = 0; i < MAX_VALUE_SIZE; i++) {
+ buf[i] = 'b';
+ }
+ mBC->set("abcd", 4, buf, MAX_VALUE_SIZE);
+ for (int i = 0; i < MAX_VALUE_SIZE; i++) {
+ buf[i] = 0xee;
+ }
+ ASSERT_EQ(size_t(MAX_VALUE_SIZE), mBC->get("abcd", 4, buf,
+ MAX_VALUE_SIZE));
+ for (int i = 0; i < MAX_VALUE_SIZE; i++) {
+ SCOPED_TRACE(i);
+ ASSERT_EQ('b', buf[i]);
+ }
+}
+
+TEST_F(BlobCacheTest, CacheMaxKeyValuePairSizeSucceeds) {
+ // Check a testing assumption
+ ASSERT_TRUE(MAX_KEY_SIZE < MAX_TOTAL_SIZE);
+
+ enum { bufSize = MAX_TOTAL_SIZE - MAX_KEY_SIZE };
+
+ char key[MAX_KEY_SIZE];
+ char buf[bufSize];
+ for (int i = 0; i < MAX_KEY_SIZE; i++) {
+ key[i] = 'a';
+ }
+ for (int i = 0; i < bufSize; i++) {
+ buf[i] = 'b';
+ }
+
+ mBC->set(key, MAX_KEY_SIZE, buf, bufSize);
+ ASSERT_EQ(size_t(bufSize), mBC->get(key, MAX_KEY_SIZE, NULL, 0));
+}
+
+TEST_F(BlobCacheTest, CacheMinKeyAndValueSizeSucceeds) {
+ unsigned char buf[1] = { 0xee };
+ mBC->set("x", 1, "y", 1);
+ ASSERT_EQ(size_t(1), mBC->get("x", 1, buf, 1));
+ ASSERT_EQ('y', buf[0]);
+}
+
+TEST_F(BlobCacheTest, CacheSizeDoesntExceedTotalLimit) {
+ for (int i = 0; i < 256; i++) {
+ uint8_t k = i;
+ mBC->set(&k, 1, "x", 1);
+ }
+ int numCached = 0;
+ for (int i = 0; i < 256; i++) {
+ uint8_t k = i;
+ if (mBC->get(&k, 1, NULL, 0) == 1) {
+ numCached++;
+ }
+ }
+ ASSERT_GE(MAX_TOTAL_SIZE / 2, numCached);
+}
+
+TEST_F(BlobCacheTest, ExceedingTotalLimitHalvesCacheSize) {
+ // Fill up the entire cache with 1 char key/value pairs.
+ const int maxEntries = MAX_TOTAL_SIZE / 2;
+ for (int i = 0; i < maxEntries; i++) {
+ uint8_t k = i;
+ mBC->set(&k, 1, "x", 1);
+ }
+ // Insert one more entry, causing a cache overflow.
+ {
+ uint8_t k = maxEntries;
+ mBC->set(&k, 1, "x", 1);
+ }
+ // Count the number of entries in the cache.
+ int numCached = 0;
+ for (int i = 0; i < maxEntries+1; i++) {
+ uint8_t k = i;
+ if (mBC->get(&k, 1, NULL, 0) == 1) {
+ numCached++;
+ }
+ }
+ ASSERT_EQ(maxEntries/2 + 1, numCached);
+}
+
+class BlobCacheFlattenTest : public BlobCacheTest {
+protected:
+ virtual void SetUp() {
+ BlobCacheTest::SetUp();
+ mBC2 = new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE);
+ }
+
+ virtual void TearDown() {
+ mBC2.clear();
+ BlobCacheTest::TearDown();
+ }
+
+ void roundTrip() {
+ size_t size = mBC->getFlattenedSize();
+ uint8_t* flat = new uint8_t[size];
+ ASSERT_EQ(OK, mBC->flatten(flat, size));
+ ASSERT_EQ(OK, mBC2->unflatten(flat, size));
+ delete[] flat;
+ }
+
+ sp<BlobCache> mBC2;
+};
+
+TEST_F(BlobCacheFlattenTest, FlattenOneValue) {
+ unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ mBC->set("abcd", 4, "efgh", 4);
+ roundTrip();
+ ASSERT_EQ(size_t(4), mBC2->get("abcd", 4, buf, 4));
+ ASSERT_EQ('e', buf[0]);
+ ASSERT_EQ('f', buf[1]);
+ ASSERT_EQ('g', buf[2]);
+ ASSERT_EQ('h', buf[3]);
+}
+
+TEST_F(BlobCacheFlattenTest, FlattenFullCache) {
+ // Fill up the entire cache with 1 char key/value pairs.
+ const int maxEntries = MAX_TOTAL_SIZE / 2;
+ for (int i = 0; i < maxEntries; i++) {
+ uint8_t k = i;
+ mBC->set(&k, 1, &k, 1);
+ }
+
+ roundTrip();
+
+ // Verify the deserialized cache
+ for (int i = 0; i < maxEntries; i++) {
+ uint8_t k = i;
+ uint8_t v = 0xee;
+ ASSERT_EQ(size_t(1), mBC2->get(&k, 1, &v, 1));
+ ASSERT_EQ(k, v);
+ }
+}
+
+TEST_F(BlobCacheFlattenTest, FlattenDoesntChangeCache) {
+ // Fill up the entire cache with 1 char key/value pairs.
+ const int maxEntries = MAX_TOTAL_SIZE / 2;
+ for (int i = 0; i < maxEntries; i++) {
+ uint8_t k = i;
+ mBC->set(&k, 1, &k, 1);
+ }
+
+ size_t size = mBC->getFlattenedSize();
+ uint8_t* flat = new uint8_t[size];
+ ASSERT_EQ(OK, mBC->flatten(flat, size));
+ delete[] flat;
+
+ // Verify the cache that we just serialized
+ for (int i = 0; i < maxEntries; i++) {
+ uint8_t k = i;
+ uint8_t v = 0xee;
+ ASSERT_EQ(size_t(1), mBC->get(&k, 1, &v, 1));
+ ASSERT_EQ(k, v);
+ }
+}
+
+TEST_F(BlobCacheFlattenTest, FlattenCatchesBufferTooSmall) {
+ // Fill up the entire cache with 1 char key/value pairs.
+ const int maxEntries = MAX_TOTAL_SIZE / 2;
+ for (int i = 0; i < maxEntries; i++) {
+ uint8_t k = i;
+ mBC->set(&k, 1, &k, 1);
+ }
+
+ size_t size = mBC->getFlattenedSize() - 1;
+ uint8_t* flat = new uint8_t[size];
+ ASSERT_EQ(BAD_VALUE, mBC->flatten(flat, size));
+ delete[] flat;
+}
+
+TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadMagic) {
+ unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ mBC->set("abcd", 4, "efgh", 4);
+
+ size_t size = mBC->getFlattenedSize();
+ uint8_t* flat = new uint8_t[size];
+ ASSERT_EQ(OK, mBC->flatten(flat, size));
+ flat[1] = ~flat[1];
+
+ // Bad magic should cause an error.
+ ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size));
+ delete[] flat;
+
+ // The error should cause the unflatten to result in an empty cache
+ ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
+}
+
+TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheVersion) {
+ unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ mBC->set("abcd", 4, "efgh", 4);
+
+ size_t size = mBC->getFlattenedSize();
+ uint8_t* flat = new uint8_t[size];
+ ASSERT_EQ(OK, mBC->flatten(flat, size));
+ flat[5] = ~flat[5];
+
+ // Version mismatches shouldn't cause errors, but should not use the
+ // serialized entries
+ ASSERT_EQ(OK, mBC2->unflatten(flat, size));
+ delete[] flat;
+
+ // The version mismatch should cause the unflatten to result in an empty
+ // cache
+ ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
+}
+
+TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheDeviceVersion) {
+ unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ mBC->set("abcd", 4, "efgh", 4);
+
+ size_t size = mBC->getFlattenedSize();
+ uint8_t* flat = new uint8_t[size];
+ ASSERT_EQ(OK, mBC->flatten(flat, size));
+ flat[10] = ~flat[10];
+
+ // Version mismatches shouldn't cause errors, but should not use the
+ // serialized entries
+ ASSERT_EQ(OK, mBC2->unflatten(flat, size));
+ delete[] flat;
+
+ // The version mismatch should cause the unflatten to result in an empty
+ // cache
+ ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
+}
+
+TEST_F(BlobCacheFlattenTest, UnflattenCatchesBufferTooSmall) {
+ unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ mBC->set("abcd", 4, "efgh", 4);
+
+ size_t size = mBC->getFlattenedSize();
+ uint8_t* flat = new uint8_t[size];
+ ASSERT_EQ(OK, mBC->flatten(flat, size));
+
+ // A buffer truncation shouldt cause an error
+ ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size-1));
+ delete[] flat;
+
+ // The error should cause the unflatten to result in an empty cache
+ ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
+}
+
+} // namespace android
diff --git a/libutils/tests/Looper_test.cpp b/libutils/tests/Looper_test.cpp
new file mode 100644
index 0000000..00077e6
--- /dev/null
+++ b/libutils/tests/Looper_test.cpp
@@ -0,0 +1,693 @@
+//
+// Copyright 2010 The Android Open Source Project
+//
+
+#include <utils/Looper.h>
+#include <utils/Timers.h>
+#include <utils/StopWatch.h>
+#include <gtest/gtest.h>
+#include <unistd.h>
+#include <time.h>
+
+#include "TestHelpers.h"
+
+// # of milliseconds to fudge stopwatch measurements
+#define TIMING_TOLERANCE_MS 25
+
+namespace android {
+
+enum {
+ MSG_TEST1 = 1,
+ MSG_TEST2 = 2,
+ MSG_TEST3 = 3,
+ MSG_TEST4 = 4,
+};
+
+class DelayedWake : public DelayedTask {
+ sp<Looper> mLooper;
+
+public:
+ DelayedWake(int delayMillis, const sp<Looper> looper) :
+ DelayedTask(delayMillis), mLooper(looper) {
+ }
+
+protected:
+ virtual void doTask() {
+ mLooper->wake();
+ }
+};
+
+class DelayedWriteSignal : public DelayedTask {
+ Pipe* mPipe;
+
+public:
+ DelayedWriteSignal(int delayMillis, Pipe* pipe) :
+ DelayedTask(delayMillis), mPipe(pipe) {
+ }
+
+protected:
+ virtual void doTask() {
+ mPipe->writeSignal();
+ }
+};
+
+class CallbackHandler {
+public:
+ void setCallback(const sp<Looper>& looper, int fd, int events) {
+ looper->addFd(fd, 0, events, staticHandler, this);
+ }
+
+protected:
+ virtual ~CallbackHandler() { }
+
+ virtual int handler(int fd, int events) = 0;
+
+private:
+ static int staticHandler(int fd, int events, void* data) {
+ return static_cast<CallbackHandler*>(data)->handler(fd, events);
+ }
+};
+
+class StubCallbackHandler : public CallbackHandler {
+public:
+ int nextResult;
+ int callbackCount;
+
+ int fd;
+ int events;
+
+ StubCallbackHandler(int nextResult) : nextResult(nextResult),
+ callbackCount(0), fd(-1), events(-1) {
+ }
+
+protected:
+ virtual int handler(int fd, int events) {
+ callbackCount += 1;
+ this->fd = fd;
+ this->events = events;
+ return nextResult;
+ }
+};
+
+class StubMessageHandler : public MessageHandler {
+public:
+ Vector<Message> messages;
+
+ virtual void handleMessage(const Message& message) {
+ messages.push(message);
+ }
+};
+
+class LooperTest : public testing::Test {
+protected:
+ sp<Looper> mLooper;
+
+ virtual void SetUp() {
+ mLooper = new Looper(true);
+ }
+
+ virtual void TearDown() {
+ mLooper.clear();
+ }
+};
+
+
+TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndNotAwoken_WaitsForTimeout) {
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(100);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. equal timeout";
+ EXPECT_EQ(Looper::POLL_TIMEOUT, result)
+ << "pollOnce result should be LOOPER_POLL_TIMEOUT";
+}
+
+TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndAwokenBeforeWaiting_ImmediatelyReturns) {
+ mLooper->wake();
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(1000);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. zero because wake() was called before waiting";
+ EXPECT_EQ(Looper::POLL_WAKE, result)
+ << "pollOnce result should be Looper::POLL_CALLBACK because loop was awoken";
+}
+
+TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndAwokenWhileWaiting_PromptlyReturns) {
+ sp<DelayedWake> delayedWake = new DelayedWake(100, mLooper);
+ delayedWake->run();
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(1000);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. equal wake delay";
+ EXPECT_EQ(Looper::POLL_WAKE, result)
+ << "pollOnce result should be Looper::POLL_CALLBACK because loop was awoken";
+}
+
+TEST_F(LooperTest, PollOnce_WhenZeroTimeoutAndNoRegisteredFDs_ImmediatelyReturns) {
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(0);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should be approx. zero";
+ EXPECT_EQ(Looper::POLL_TIMEOUT, result)
+ << "pollOnce result should be Looper::POLL_TIMEOUT";
+}
+
+TEST_F(LooperTest, PollOnce_WhenZeroTimeoutAndNoSignalledFDs_ImmediatelyReturns) {
+ Pipe pipe;
+ StubCallbackHandler handler(true);
+
+ handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(0);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should be approx. zero";
+ EXPECT_EQ(Looper::POLL_TIMEOUT, result)
+ << "pollOnce result should be Looper::POLL_TIMEOUT";
+ EXPECT_EQ(0, handler.callbackCount)
+ << "callback should not have been invoked because FD was not signalled";
+}
+
+TEST_F(LooperTest, PollOnce_WhenZeroTimeoutAndSignalledFD_ImmediatelyInvokesCallbackAndReturns) {
+ Pipe pipe;
+ StubCallbackHandler handler(true);
+
+ ASSERT_EQ(OK, pipe.writeSignal());
+ handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(0);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should be approx. zero";
+ EXPECT_EQ(Looper::POLL_CALLBACK, result)
+ << "pollOnce result should be Looper::POLL_CALLBACK because FD was signalled";
+ EXPECT_EQ(1, handler.callbackCount)
+ << "callback should be invoked exactly once";
+ EXPECT_EQ(pipe.receiveFd, handler.fd)
+ << "callback should have received pipe fd as parameter";
+ EXPECT_EQ(Looper::EVENT_INPUT, handler.events)
+ << "callback should have received Looper::EVENT_INPUT as events";
+}
+
+TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndNoSignalledFDs_WaitsForTimeoutAndReturns) {
+ Pipe pipe;
+ StubCallbackHandler handler(true);
+
+ handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(100);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. equal timeout";
+ EXPECT_EQ(Looper::POLL_TIMEOUT, result)
+ << "pollOnce result should be Looper::POLL_TIMEOUT";
+ EXPECT_EQ(0, handler.callbackCount)
+ << "callback should not have been invoked because FD was not signalled";
+}
+
+TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDBeforeWaiting_ImmediatelyInvokesCallbackAndReturns) {
+ Pipe pipe;
+ StubCallbackHandler handler(true);
+
+ pipe.writeSignal();
+ handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(100);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ ASSERT_EQ(OK, pipe.readSignal())
+ << "signal should actually have been written";
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should be approx. zero";
+ EXPECT_EQ(Looper::POLL_CALLBACK, result)
+ << "pollOnce result should be Looper::POLL_CALLBACK because FD was signalled";
+ EXPECT_EQ(1, handler.callbackCount)
+ << "callback should be invoked exactly once";
+ EXPECT_EQ(pipe.receiveFd, handler.fd)
+ << "callback should have received pipe fd as parameter";
+ EXPECT_EQ(Looper::EVENT_INPUT, handler.events)
+ << "callback should have received Looper::EVENT_INPUT as events";
+}
+
+TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDWhileWaiting_PromptlyInvokesCallbackAndReturns) {
+ Pipe pipe;
+ StubCallbackHandler handler(true);
+ sp<DelayedWriteSignal> delayedWriteSignal = new DelayedWriteSignal(100, & pipe);
+
+ handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);
+ delayedWriteSignal->run();
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(1000);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ ASSERT_EQ(OK, pipe.readSignal())
+ << "signal should actually have been written";
+ EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. equal signal delay";
+ EXPECT_EQ(Looper::POLL_CALLBACK, result)
+ << "pollOnce result should be Looper::POLL_CALLBACK because FD was signalled";
+ EXPECT_EQ(1, handler.callbackCount)
+ << "callback should be invoked exactly once";
+ EXPECT_EQ(pipe.receiveFd, handler.fd)
+ << "callback should have received pipe fd as parameter";
+ EXPECT_EQ(Looper::EVENT_INPUT, handler.events)
+ << "callback should have received Looper::EVENT_INPUT as events";
+}
+
+TEST_F(LooperTest, PollOnce_WhenCallbackAddedThenRemoved_CallbackShouldNotBeInvoked) {
+ Pipe pipe;
+ StubCallbackHandler handler(true);
+
+ handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);
+ pipe.writeSignal(); // would cause FD to be considered signalled
+ mLooper->removeFd(pipe.receiveFd);
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(100);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ ASSERT_EQ(OK, pipe.readSignal())
+ << "signal should actually have been written";
+ EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. equal timeout because FD was no longer registered";
+ EXPECT_EQ(Looper::POLL_TIMEOUT, result)
+ << "pollOnce result should be Looper::POLL_TIMEOUT";
+ EXPECT_EQ(0, handler.callbackCount)
+ << "callback should not be invoked";
+}
+
+TEST_F(LooperTest, PollOnce_WhenCallbackReturnsFalse_CallbackShouldNotBeInvokedAgainLater) {
+ Pipe pipe;
+ StubCallbackHandler handler(false);
+
+ handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);
+
+ // First loop: Callback is registered and FD is signalled.
+ pipe.writeSignal();
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(0);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ ASSERT_EQ(OK, pipe.readSignal())
+ << "signal should actually have been written";
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. equal zero because FD was already signalled";
+ EXPECT_EQ(Looper::POLL_CALLBACK, result)
+ << "pollOnce result should be Looper::POLL_CALLBACK because FD was signalled";
+ EXPECT_EQ(1, handler.callbackCount)
+ << "callback should be invoked";
+
+ // Second loop: Callback is no longer registered and FD is signalled.
+ pipe.writeSignal();
+
+ stopWatch.reset();
+ result = mLooper->pollOnce(0);
+ elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ ASSERT_EQ(OK, pipe.readSignal())
+ << "signal should actually have been written";
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. equal zero because timeout was zero";
+ EXPECT_EQ(Looper::POLL_TIMEOUT, result)
+ << "pollOnce result should be Looper::POLL_TIMEOUT";
+ EXPECT_EQ(1, handler.callbackCount)
+ << "callback should not be invoked this time";
+}
+
+TEST_F(LooperTest, PollOnce_WhenNonCallbackFdIsSignalled_ReturnsIdent) {
+ const int expectedIdent = 5;
+ void* expectedData = this;
+
+ Pipe pipe;
+
+ pipe.writeSignal();
+ mLooper->addFd(pipe.receiveFd, expectedIdent, Looper::EVENT_INPUT, NULL, expectedData);
+
+ StopWatch stopWatch("pollOnce");
+ int fd;
+ int events;
+ void* data;
+ int result = mLooper->pollOnce(100, &fd, &events, &data);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ ASSERT_EQ(OK, pipe.readSignal())
+ << "signal should actually have been written";
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should be approx. zero";
+ EXPECT_EQ(expectedIdent, result)
+ << "pollOnce result should be the ident of the FD that was signalled";
+ EXPECT_EQ(pipe.receiveFd, fd)
+ << "pollOnce should have returned the received pipe fd";
+ EXPECT_EQ(Looper::EVENT_INPUT, events)
+ << "pollOnce should have returned Looper::EVENT_INPUT as events";
+ EXPECT_EQ(expectedData, data)
+ << "pollOnce should have returned the data";
+}
+
+TEST_F(LooperTest, AddFd_WhenCallbackAdded_ReturnsOne) {
+ Pipe pipe;
+ int result = mLooper->addFd(pipe.receiveFd, 0, Looper::EVENT_INPUT, NULL, NULL);
+
+ EXPECT_EQ(1, result)
+ << "addFd should return 1 because FD was added";
+}
+
+TEST_F(LooperTest, AddFd_WhenIdentIsNegativeAndCallbackIsNull_ReturnsError) {
+ Pipe pipe;
+ int result = mLooper->addFd(pipe.receiveFd, -1, Looper::EVENT_INPUT, NULL, NULL);
+
+ EXPECT_EQ(-1, result)
+ << "addFd should return -1 because arguments were invalid";
+}
+
+TEST_F(LooperTest, AddFd_WhenNoCallbackAndAllowNonCallbacksIsFalse_ReturnsError) {
+ Pipe pipe;
+ sp<Looper> looper = new Looper(false /*allowNonCallbacks*/);
+ int result = looper->addFd(pipe.receiveFd, 0, 0, NULL, NULL);
+
+ EXPECT_EQ(-1, result)
+ << "addFd should return -1 because arguments were invalid";
+}
+
+TEST_F(LooperTest, RemoveFd_WhenCallbackNotAdded_ReturnsZero) {
+ int result = mLooper->removeFd(1);
+
+ EXPECT_EQ(0, result)
+ << "removeFd should return 0 because FD not registered";
+}
+
+TEST_F(LooperTest, RemoveFd_WhenCallbackAddedThenRemovedTwice_ReturnsOnceFirstTimeAndReturnsZeroSecondTime) {
+ Pipe pipe;
+ StubCallbackHandler handler(false);
+ handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);
+
+ // First time.
+ int result = mLooper->removeFd(pipe.receiveFd);
+
+ EXPECT_EQ(1, result)
+ << "removeFd should return 1 first time because FD was registered";
+
+ // Second time.
+ result = mLooper->removeFd(pipe.receiveFd);
+
+ EXPECT_EQ(0, result)
+ << "removeFd should return 0 second time because FD was no longer registered";
+}
+
+TEST_F(LooperTest, PollOnce_WhenCallbackAddedTwice_OnlySecondCallbackShouldBeInvoked) {
+ Pipe pipe;
+ StubCallbackHandler handler1(true);
+ StubCallbackHandler handler2(true);
+
+ handler1.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);
+ handler2.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT); // replace it
+ pipe.writeSignal(); // would cause FD to be considered signalled
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(100);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ ASSERT_EQ(OK, pipe.readSignal())
+ << "signal should actually have been written";
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. zero because FD was already signalled";
+ EXPECT_EQ(Looper::POLL_CALLBACK, result)
+ << "pollOnce result should be Looper::POLL_CALLBACK because FD was signalled";
+ EXPECT_EQ(0, handler1.callbackCount)
+ << "original handler callback should not be invoked because it was replaced";
+ EXPECT_EQ(1, handler2.callbackCount)
+ << "replacement handler callback should be invoked";
+}
+
+TEST_F(LooperTest, SendMessage_WhenOneMessageIsEnqueue_ShouldInvokeHandlerDuringNextPoll) {
+ sp<StubMessageHandler> handler = new StubMessageHandler();
+ mLooper->sendMessage(handler, Message(MSG_TEST1));
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(100);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. zero because message was already sent";
+ EXPECT_EQ(Looper::POLL_CALLBACK, result)
+ << "pollOnce result should be Looper::POLL_CALLBACK because message was sent";
+ EXPECT_EQ(size_t(1), handler->messages.size())
+ << "handled message";
+ EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
+ << "handled message";
+}
+
+TEST_F(LooperTest, SendMessage_WhenMultipleMessagesAreEnqueued_ShouldInvokeHandlersInOrderDuringNextPoll) {
+ sp<StubMessageHandler> handler1 = new StubMessageHandler();
+ sp<StubMessageHandler> handler2 = new StubMessageHandler();
+ mLooper->sendMessage(handler1, Message(MSG_TEST1));
+ mLooper->sendMessage(handler2, Message(MSG_TEST2));
+ mLooper->sendMessage(handler1, Message(MSG_TEST3));
+ mLooper->sendMessage(handler1, Message(MSG_TEST4));
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(1000);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. zero because message was already sent";
+ EXPECT_EQ(Looper::POLL_CALLBACK, result)
+ << "pollOnce result should be Looper::POLL_CALLBACK because message was sent";
+ EXPECT_EQ(size_t(3), handler1->messages.size())
+ << "handled message";
+ EXPECT_EQ(MSG_TEST1, handler1->messages[0].what)
+ << "handled message";
+ EXPECT_EQ(MSG_TEST3, handler1->messages[1].what)
+ << "handled message";
+ EXPECT_EQ(MSG_TEST4, handler1->messages[2].what)
+ << "handled message";
+ EXPECT_EQ(size_t(1), handler2->messages.size())
+ << "handled message";
+ EXPECT_EQ(MSG_TEST2, handler2->messages[0].what)
+ << "handled message";
+}
+
+TEST_F(LooperTest, SendMessageDelayed_WhenSentToTheFuture_ShouldInvokeHandlerAfterDelayTime) {
+ sp<StubMessageHandler> handler = new StubMessageHandler();
+ mLooper->sendMessageDelayed(ms2ns(100), handler, Message(MSG_TEST1));
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(1000);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "first poll should end quickly because next message timeout was computed";
+ EXPECT_EQ(Looper::POLL_WAKE, result)
+ << "pollOnce result should be Looper::POLL_WAKE due to wakeup";
+ EXPECT_EQ(size_t(0), handler->messages.size())
+ << "no message handled yet";
+
+ result = mLooper->pollOnce(1000);
+ elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_EQ(size_t(1), handler->messages.size())
+ << "handled message";
+ EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
+ << "handled message";
+ EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "second poll should end around the time of the delayed message dispatch";
+ EXPECT_EQ(Looper::POLL_CALLBACK, result)
+ << "pollOnce result should be Looper::POLL_CALLBACK because message was sent";
+
+ result = mLooper->pollOnce(100);
+ elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(100 + 100, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "third poll should timeout";
+ EXPECT_EQ(Looper::POLL_TIMEOUT, result)
+ << "pollOnce result should be Looper::POLL_TIMEOUT because there were no messages left";
+}
+
+TEST_F(LooperTest, SendMessageDelayed_WhenSentToThePast_ShouldInvokeHandlerDuringNextPoll) {
+ sp<StubMessageHandler> handler = new StubMessageHandler();
+ mLooper->sendMessageDelayed(ms2ns(-1000), handler, Message(MSG_TEST1));
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(100);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. zero because message was already sent";
+ EXPECT_EQ(Looper::POLL_CALLBACK, result)
+ << "pollOnce result should be Looper::POLL_CALLBACK because message was sent";
+ EXPECT_EQ(size_t(1), handler->messages.size())
+ << "handled message";
+ EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
+ << "handled message";
+}
+
+TEST_F(LooperTest, SendMessageDelayed_WhenSentToThePresent_ShouldInvokeHandlerDuringNextPoll) {
+ sp<StubMessageHandler> handler = new StubMessageHandler();
+ mLooper->sendMessageDelayed(0, handler, Message(MSG_TEST1));
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(100);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. zero because message was already sent";
+ EXPECT_EQ(Looper::POLL_CALLBACK, result)
+ << "pollOnce result should be Looper::POLL_CALLBACK because message was sent";
+ EXPECT_EQ(size_t(1), handler->messages.size())
+ << "handled message";
+ EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
+ << "handled message";
+}
+
+TEST_F(LooperTest, SendMessageAtTime_WhenSentToTheFuture_ShouldInvokeHandlerAfterDelayTime) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ sp<StubMessageHandler> handler = new StubMessageHandler();
+ mLooper->sendMessageAtTime(now + ms2ns(100), handler, Message(MSG_TEST1));
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(1000);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "first poll should end quickly because next message timeout was computed";
+ EXPECT_EQ(Looper::POLL_WAKE, result)
+ << "pollOnce result should be Looper::POLL_WAKE due to wakeup";
+ EXPECT_EQ(size_t(0), handler->messages.size())
+ << "no message handled yet";
+
+ result = mLooper->pollOnce(1000);
+ elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_EQ(size_t(1), handler->messages.size())
+ << "handled message";
+ EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
+ << "handled message";
+ EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "second poll should end around the time of the delayed message dispatch";
+ EXPECT_EQ(Looper::POLL_CALLBACK, result)
+ << "pollOnce result should be Looper::POLL_CALLBACK because message was sent";
+
+ result = mLooper->pollOnce(100);
+ elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(100 + 100, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "third poll should timeout";
+ EXPECT_EQ(Looper::POLL_TIMEOUT, result)
+ << "pollOnce result should be Looper::POLL_TIMEOUT because there were no messages left";
+}
+
+TEST_F(LooperTest, SendMessageAtTime_WhenSentToThePast_ShouldInvokeHandlerDuringNextPoll) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ sp<StubMessageHandler> handler = new StubMessageHandler();
+ mLooper->sendMessageAtTime(now - ms2ns(1000), handler, Message(MSG_TEST1));
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(100);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. zero because message was already sent";
+ EXPECT_EQ(Looper::POLL_CALLBACK, result)
+ << "pollOnce result should be Looper::POLL_CALLBACK because message was sent";
+ EXPECT_EQ(size_t(1), handler->messages.size())
+ << "handled message";
+ EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
+ << "handled message";
+}
+
+TEST_F(LooperTest, SendMessageAtTime_WhenSentToThePresent_ShouldInvokeHandlerDuringNextPoll) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ sp<StubMessageHandler> handler = new StubMessageHandler();
+ mLooper->sendMessageAtTime(now, handler, Message(MSG_TEST1));
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(100);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. zero because message was already sent";
+ EXPECT_EQ(Looper::POLL_CALLBACK, result)
+ << "pollOnce result should be Looper::POLL_CALLBACK because message was sent";
+ EXPECT_EQ(size_t(1), handler->messages.size())
+ << "handled message";
+ EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
+ << "handled message";
+}
+
+TEST_F(LooperTest, RemoveMessage_WhenRemovingAllMessagesForHandler_ShouldRemoveThoseMessage) {
+ sp<StubMessageHandler> handler = new StubMessageHandler();
+ mLooper->sendMessage(handler, Message(MSG_TEST1));
+ mLooper->sendMessage(handler, Message(MSG_TEST2));
+ mLooper->sendMessage(handler, Message(MSG_TEST3));
+ mLooper->removeMessages(handler);
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(0);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. zero because message was sent so looper was awoken";
+ EXPECT_EQ(Looper::POLL_WAKE, result)
+ << "pollOnce result should be Looper::POLL_WAKE because looper was awoken";
+ EXPECT_EQ(size_t(0), handler->messages.size())
+ << "no messages to handle";
+
+ result = mLooper->pollOnce(0);
+
+ EXPECT_EQ(Looper::POLL_TIMEOUT, result)
+ << "pollOnce result should be Looper::POLL_TIMEOUT because there was nothing to do";
+ EXPECT_EQ(size_t(0), handler->messages.size())
+ << "no messages to handle";
+}
+
+TEST_F(LooperTest, RemoveMessage_WhenRemovingSomeMessagesForHandler_ShouldRemoveThoseMessage) {
+ sp<StubMessageHandler> handler = new StubMessageHandler();
+ mLooper->sendMessage(handler, Message(MSG_TEST1));
+ mLooper->sendMessage(handler, Message(MSG_TEST2));
+ mLooper->sendMessage(handler, Message(MSG_TEST3));
+ mLooper->sendMessage(handler, Message(MSG_TEST4));
+ mLooper->removeMessages(handler, MSG_TEST3);
+ mLooper->removeMessages(handler, MSG_TEST1);
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(0);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. zero because message was sent so looper was awoken";
+ EXPECT_EQ(Looper::POLL_CALLBACK, result)
+ << "pollOnce result should be Looper::POLL_CALLBACK because two messages were sent";
+ EXPECT_EQ(size_t(2), handler->messages.size())
+ << "no messages to handle";
+ EXPECT_EQ(MSG_TEST2, handler->messages[0].what)
+ << "handled message";
+ EXPECT_EQ(MSG_TEST4, handler->messages[1].what)
+ << "handled message";
+
+ result = mLooper->pollOnce(0);
+
+ EXPECT_EQ(Looper::POLL_TIMEOUT, result)
+ << "pollOnce result should be Looper::POLL_TIMEOUT because there was nothing to do";
+ EXPECT_EQ(size_t(2), handler->messages.size())
+ << "no more messages to handle";
+}
+
+} // namespace android
diff --git a/libutils/tests/LruCache_test.cpp b/libutils/tests/LruCache_test.cpp
new file mode 100644
index 0000000..6534211
--- /dev/null
+++ b/libutils/tests/LruCache_test.cpp
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <utils/JenkinsHash.h>
+#include <utils/LruCache.h>
+#include <cutils/log.h>
+#include <gtest/gtest.h>
+
+namespace {
+
+typedef int SimpleKey;
+typedef const char* StringValue;
+
+struct ComplexKey {
+ int k;
+
+ explicit ComplexKey(int k) : k(k) {
+ instanceCount += 1;
+ }
+
+ ComplexKey(const ComplexKey& other) : k(other.k) {
+ instanceCount += 1;
+ }
+
+ ~ComplexKey() {
+ instanceCount -= 1;
+ }
+
+ bool operator ==(const ComplexKey& other) const {
+ return k == other.k;
+ }
+
+ bool operator !=(const ComplexKey& other) const {
+ return k != other.k;
+ }
+
+ static ssize_t instanceCount;
+};
+
+ssize_t ComplexKey::instanceCount = 0;
+
+struct ComplexValue {
+ int v;
+
+ explicit ComplexValue(int v) : v(v) {
+ instanceCount += 1;
+ }
+
+ ComplexValue(const ComplexValue& other) : v(other.v) {
+ instanceCount += 1;
+ }
+
+ ~ComplexValue() {
+ instanceCount -= 1;
+ }
+
+ static ssize_t instanceCount;
+};
+
+ssize_t ComplexValue::instanceCount = 0;
+
+} // namespace
+
+
+namespace android {
+
+typedef LruCache<ComplexKey, ComplexValue> ComplexCache;
+
+template<> inline android::hash_t hash_type(const ComplexKey& value) {
+ return hash_type(value.k);
+}
+
+class EntryRemovedCallback : public OnEntryRemoved<SimpleKey, StringValue> {
+public:
+ EntryRemovedCallback() : callbackCount(0), lastKey(-1), lastValue(NULL) { }
+ ~EntryRemovedCallback() {}
+ void operator()(SimpleKey& k, StringValue& v) {
+ callbackCount += 1;
+ lastKey = k;
+ lastValue = v;
+ }
+ ssize_t callbackCount;
+ SimpleKey lastKey;
+ StringValue lastValue;
+};
+
+class LruCacheTest : public testing::Test {
+protected:
+ virtual void SetUp() {
+ ComplexKey::instanceCount = 0;
+ ComplexValue::instanceCount = 0;
+ }
+
+ virtual void TearDown() {
+ ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0));
+ }
+
+ void assertInstanceCount(ssize_t keys, ssize_t values) {
+ if (keys != ComplexKey::instanceCount || values != ComplexValue::instanceCount) {
+ FAIL() << "Expected " << keys << " keys and " << values << " values "
+ "but there were actually " << ComplexKey::instanceCount << " keys and "
+ << ComplexValue::instanceCount << " values";
+ }
+ }
+};
+
+TEST_F(LruCacheTest, Empty) {
+ LruCache<SimpleKey, StringValue> cache(100);
+
+ EXPECT_EQ(NULL, cache.get(0));
+ EXPECT_EQ(0u, cache.size());
+}
+
+TEST_F(LruCacheTest, Simple) {
+ LruCache<SimpleKey, StringValue> cache(100);
+
+ cache.put(1, "one");
+ cache.put(2, "two");
+ cache.put(3, "three");
+ EXPECT_STREQ("one", cache.get(1));
+ EXPECT_STREQ("two", cache.get(2));
+ EXPECT_STREQ("three", cache.get(3));
+ EXPECT_EQ(3u, cache.size());
+}
+
+TEST_F(LruCacheTest, MaxCapacity) {
+ LruCache<SimpleKey, StringValue> cache(2);
+
+ cache.put(1, "one");
+ cache.put(2, "two");
+ cache.put(3, "three");
+ EXPECT_EQ(NULL, cache.get(1));
+ EXPECT_STREQ("two", cache.get(2));
+ EXPECT_STREQ("three", cache.get(3));
+ EXPECT_EQ(2u, cache.size());
+}
+
+TEST_F(LruCacheTest, RemoveLru) {
+ LruCache<SimpleKey, StringValue> cache(100);
+
+ cache.put(1, "one");
+ cache.put(2, "two");
+ cache.put(3, "three");
+ cache.removeOldest();
+ EXPECT_EQ(NULL, cache.get(1));
+ EXPECT_STREQ("two", cache.get(2));
+ EXPECT_STREQ("three", cache.get(3));
+ EXPECT_EQ(2u, cache.size());
+}
+
+TEST_F(LruCacheTest, GetUpdatesLru) {
+ LruCache<SimpleKey, StringValue> cache(100);
+
+ cache.put(1, "one");
+ cache.put(2, "two");
+ cache.put(3, "three");
+ EXPECT_STREQ("one", cache.get(1));
+ cache.removeOldest();
+ EXPECT_STREQ("one", cache.get(1));
+ EXPECT_EQ(NULL, cache.get(2));
+ EXPECT_STREQ("three", cache.get(3));
+ EXPECT_EQ(2u, cache.size());
+}
+
+uint32_t hash_int(int x) {
+ return JenkinsHashWhiten(JenkinsHashMix(0, x));
+}
+
+TEST_F(LruCacheTest, StressTest) {
+ const size_t kCacheSize = 512;
+ LruCache<SimpleKey, StringValue> cache(512);
+ const size_t kNumKeys = 16 * 1024;
+ const size_t kNumIters = 100000;
+ char* strings[kNumKeys];
+
+ for (size_t i = 0; i < kNumKeys; i++) {
+ strings[i] = (char *)malloc(16);
+ sprintf(strings[i], "%zu", i);
+ }
+
+ srandom(12345);
+ int hitCount = 0;
+ for (size_t i = 0; i < kNumIters; i++) {
+ int index = random() % kNumKeys;
+ uint32_t key = hash_int(index);
+ const char *val = cache.get(key);
+ if (val != NULL) {
+ EXPECT_EQ(strings[index], val);
+ hitCount++;
+ } else {
+ cache.put(key, strings[index]);
+ }
+ }
+ size_t expectedHitCount = kNumIters * kCacheSize / kNumKeys;
+ EXPECT_LT(int(expectedHitCount * 0.9), hitCount);
+ EXPECT_GT(int(expectedHitCount * 1.1), hitCount);
+ EXPECT_EQ(kCacheSize, cache.size());
+
+ for (size_t i = 0; i < kNumKeys; i++) {
+ free((void *)strings[i]);
+ }
+}
+
+TEST_F(LruCacheTest, NoLeak) {
+ ComplexCache cache(100);
+
+ cache.put(ComplexKey(0), ComplexValue(0));
+ cache.put(ComplexKey(1), ComplexValue(1));
+ EXPECT_EQ(2, cache.size());
+ assertInstanceCount(2, 3); // the null value counts as an instance
+}
+
+TEST_F(LruCacheTest, Clear) {
+ ComplexCache cache(100);
+
+ cache.put(ComplexKey(0), ComplexValue(0));
+ cache.put(ComplexKey(1), ComplexValue(1));
+ EXPECT_EQ(2, cache.size());
+ assertInstanceCount(2, 3);
+ cache.clear();
+ assertInstanceCount(0, 1);
+}
+
+TEST_F(LruCacheTest, ClearNoDoubleFree) {
+ {
+ ComplexCache cache(100);
+
+ cache.put(ComplexKey(0), ComplexValue(0));
+ cache.put(ComplexKey(1), ComplexValue(1));
+ EXPECT_EQ(2, cache.size());
+ assertInstanceCount(2, 3);
+ cache.removeOldest();
+ cache.clear();
+ assertInstanceCount(0, 1);
+ }
+ assertInstanceCount(0, 0);
+}
+
+TEST_F(LruCacheTest, ClearReuseOk) {
+ ComplexCache cache(100);
+
+ cache.put(ComplexKey(0), ComplexValue(0));
+ cache.put(ComplexKey(1), ComplexValue(1));
+ EXPECT_EQ(2, cache.size());
+ assertInstanceCount(2, 3);
+ cache.clear();
+ assertInstanceCount(0, 1);
+ cache.put(ComplexKey(0), ComplexValue(0));
+ cache.put(ComplexKey(1), ComplexValue(1));
+ EXPECT_EQ(2, cache.size());
+ assertInstanceCount(2, 3);
+}
+
+TEST_F(LruCacheTest, Callback) {
+ LruCache<SimpleKey, StringValue> cache(100);
+ EntryRemovedCallback callback;
+ cache.setOnEntryRemovedListener(&callback);
+
+ cache.put(1, "one");
+ cache.put(2, "two");
+ cache.put(3, "three");
+ EXPECT_EQ(3, cache.size());
+ cache.removeOldest();
+ EXPECT_EQ(1, callback.callbackCount);
+ EXPECT_EQ(1, callback.lastKey);
+ EXPECT_STREQ("one", callback.lastValue);
+}
+
+TEST_F(LruCacheTest, CallbackOnClear) {
+ LruCache<SimpleKey, StringValue> cache(100);
+ EntryRemovedCallback callback;
+ cache.setOnEntryRemovedListener(&callback);
+
+ cache.put(1, "one");
+ cache.put(2, "two");
+ cache.put(3, "three");
+ EXPECT_EQ(3, cache.size());
+ cache.clear();
+ EXPECT_EQ(3, callback.callbackCount);
+}
+
+}
diff --git a/libutils/tests/String8_test.cpp b/libutils/tests/String8_test.cpp
new file mode 100644
index 0000000..c42c68d
--- /dev/null
+++ b/libutils/tests/String8_test.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "String8_test"
+#include <utils/Log.h>
+#include <utils/String8.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+class String8Test : public testing::Test {
+protected:
+ virtual void SetUp() {
+ }
+
+ virtual void TearDown() {
+ }
+};
+
+TEST_F(String8Test, Cstr) {
+ String8 tmp("Hello, world!");
+
+ EXPECT_STREQ(tmp.string(), "Hello, world!");
+}
+
+TEST_F(String8Test, OperatorPlus) {
+ String8 src1("Hello, ");
+
+ // Test adding String8 + const char*
+ const char* ccsrc2 = "world!";
+ String8 dst1 = src1 + ccsrc2;
+ EXPECT_STREQ(dst1.string(), "Hello, world!");
+ EXPECT_STREQ(src1.string(), "Hello, ");
+ EXPECT_STREQ(ccsrc2, "world!");
+
+ // Test adding String8 + String8
+ String8 ssrc2("world!");
+ String8 dst2 = src1 + ssrc2;
+ EXPECT_STREQ(dst2.string(), "Hello, world!");
+ EXPECT_STREQ(src1.string(), "Hello, ");
+ EXPECT_STREQ(ssrc2.string(), "world!");
+}
+
+TEST_F(String8Test, OperatorPlusEquals) {
+ String8 src1("My voice");
+
+ // Testing String8 += String8
+ String8 src2(" is my passport.");
+ src1 += src2;
+ EXPECT_STREQ(src1.string(), "My voice is my passport.");
+ EXPECT_STREQ(src2.string(), " is my passport.");
+
+ // Adding const char* to the previous string.
+ const char* src3 = " Verify me.";
+ src1 += src3;
+ EXPECT_STREQ(src1.string(), "My voice is my passport. Verify me.");
+ EXPECT_STREQ(src2.string(), " is my passport.");
+ EXPECT_STREQ(src3, " Verify me.");
+}
+
+}
diff --git a/libutils/tests/TestHelpers.h b/libutils/tests/TestHelpers.h
new file mode 100644
index 0000000..d8e985e
--- /dev/null
+++ b/libutils/tests/TestHelpers.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TESTHELPERS_H
+#define TESTHELPERS_H
+
+#include <utils/threads.h>
+
+namespace android {
+
+class Pipe {
+public:
+ int sendFd;
+ int receiveFd;
+
+ Pipe() {
+ int fds[2];
+ ::pipe(fds);
+
+ receiveFd = fds[0];
+ sendFd = fds[1];
+ }
+
+ ~Pipe() {
+ if (sendFd != -1) {
+ ::close(sendFd);
+ }
+
+ if (receiveFd != -1) {
+ ::close(receiveFd);
+ }
+ }
+
+ status_t writeSignal() {
+ ssize_t nWritten = ::write(sendFd, "*", 1);
+ return nWritten == 1 ? 0 : -errno;
+ }
+
+ status_t readSignal() {
+ char buf[1];
+ ssize_t nRead = ::read(receiveFd, buf, 1);
+ return nRead == 1 ? 0 : nRead == 0 ? -EPIPE : -errno;
+ }
+};
+
+class DelayedTask : public Thread {
+ int mDelayMillis;
+
+public:
+ DelayedTask(int delayMillis) : mDelayMillis(delayMillis) { }
+
+protected:
+ virtual ~DelayedTask() { }
+
+ virtual void doTask() = 0;
+
+ virtual bool threadLoop() {
+ usleep(mDelayMillis * 1000);
+ doTask();
+ return false;
+ }
+};
+
+} // namespace android
+
+#endif // TESTHELPERS_H
diff --git a/libutils/tests/Unicode_test.cpp b/libutils/tests/Unicode_test.cpp
new file mode 100644
index 0000000..18c130c
--- /dev/null
+++ b/libutils/tests/Unicode_test.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Unicode_test"
+#include <utils/Log.h>
+#include <utils/Unicode.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+class UnicodeTest : public testing::Test {
+protected:
+ virtual void SetUp() {
+ }
+
+ virtual void TearDown() {
+ }
+};
+
+TEST_F(UnicodeTest, UTF8toUTF16ZeroLength) {
+ ssize_t measured;
+
+ const uint8_t str[] = { };
+
+ measured = utf8_to_utf16_length(str, 0);
+ EXPECT_EQ(0, measured)
+ << "Zero length input should return zero length output.";
+}
+
+TEST_F(UnicodeTest, UTF8toUTF16ASCIILength) {
+ ssize_t measured;
+
+ // U+0030 or ASCII '0'
+ const uint8_t str[] = { 0x30 };
+
+ measured = utf8_to_utf16_length(str, sizeof(str));
+ EXPECT_EQ(1, measured)
+ << "ASCII glyphs should have a length of 1 char16_t";
+}
+
+TEST_F(UnicodeTest, UTF8toUTF16Plane1Length) {
+ ssize_t measured;
+
+ // U+2323 SMILE
+ const uint8_t str[] = { 0xE2, 0x8C, 0xA3 };
+
+ measured = utf8_to_utf16_length(str, sizeof(str));
+ EXPECT_EQ(1, measured)
+ << "Plane 1 glyphs should have a length of 1 char16_t";
+}
+
+TEST_F(UnicodeTest, UTF8toUTF16SurrogateLength) {
+ ssize_t measured;
+
+ // U+10000
+ const uint8_t str[] = { 0xF0, 0x90, 0x80, 0x80 };
+
+ measured = utf8_to_utf16_length(str, sizeof(str));
+ EXPECT_EQ(2, measured)
+ << "Surrogate pairs should have a length of 2 char16_t";
+}
+
+TEST_F(UnicodeTest, UTF8toUTF16TruncatedUTF8) {
+ ssize_t measured;
+
+ // Truncated U+2323 SMILE
+ // U+2323 SMILE
+ const uint8_t str[] = { 0xE2, 0x8C };
+
+ measured = utf8_to_utf16_length(str, sizeof(str));
+ EXPECT_EQ(-1, measured)
+ << "Truncated UTF-8 should return -1 to indicate invalid";
+}
+
+TEST_F(UnicodeTest, UTF8toUTF16Normal) {
+ const uint8_t str[] = {
+ 0x30, // U+0030, 1 UTF-16 character
+ 0xC4, 0x80, // U+0100, 1 UTF-16 character
+ 0xE2, 0x8C, 0xA3, // U+2323, 1 UTF-16 character
+ 0xF0, 0x90, 0x80, 0x80, // U+10000, 2 UTF-16 character
+ };
+
+ char16_t output[1 + 1 + 1 + 2 + 1]; // Room for NULL
+
+ utf8_to_utf16(str, sizeof(str), output);
+
+ EXPECT_EQ(0x0030, output[0])
+ << "should be U+0030";
+ EXPECT_EQ(0x0100, output[1])
+ << "should be U+0100";
+ EXPECT_EQ(0x2323, output[2])
+ << "should be U+2323";
+ EXPECT_EQ(0xD800, output[3])
+ << "should be first half of surrogate U+10000";
+ EXPECT_EQ(0xDC00, output[4])
+ << "should be second half of surrogate U+10000";
+ EXPECT_EQ(NULL, output[5])
+ << "should be NULL terminated";
+}
+
+}
diff --git a/libutils/tests/Vector_test.cpp b/libutils/tests/Vector_test.cpp
new file mode 100644
index 0000000..d29c054
--- /dev/null
+++ b/libutils/tests/Vector_test.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Vector_test"
+
+#include <utils/Vector.h>
+#include <cutils/log.h>
+#include <gtest/gtest.h>
+#include <unistd.h>
+
+namespace android {
+
+class VectorTest : public testing::Test {
+protected:
+ virtual void SetUp() {
+ }
+
+ virtual void TearDown() {
+ }
+
+public:
+};
+
+
+TEST_F(VectorTest, CopyOnWrite_CopyAndAddElements) {
+
+ Vector<int> vector;
+ Vector<int> other;
+ vector.setCapacity(8);
+
+ vector.add(1);
+ vector.add(2);
+ vector.add(3);
+
+ EXPECT_EQ(vector.size(), 3);
+
+ // copy the vector
+ other = vector;
+
+ EXPECT_EQ(other.size(), 3);
+
+ // add an element to the first vector
+ vector.add(4);
+
+ // make sure the sizes are correct
+ EXPECT_EQ(vector.size(), 4);
+ EXPECT_EQ(other.size(), 3);
+
+ // add an element to the copy
+ other.add(5);
+
+ // make sure the sizes are correct
+ EXPECT_EQ(vector.size(), 4);
+ EXPECT_EQ(other.size(), 4);
+
+ // make sure the content of both vectors are correct
+ EXPECT_EQ(vector[3], 4);
+ EXPECT_EQ(other[3], 5);
+}
+
+
+} // namespace android
diff --git a/libziparchive/Android.mk b/libziparchive/Android.mk
new file mode 100644
index 0000000..559c48b
--- /dev/null
+++ b/libziparchive/Android.mk
@@ -0,0 +1,73 @@
+#
+# Copyright (C) 2013 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+source_files := zip_archive.cc
+
+include $(CLEAR_VARS)
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_SRC_FILES := ${source_files}
+LOCAL_STATIC_LIBRARIES := libz
+LOCAL_SHARED_LIBRARIES := libutils libbase
+LOCAL_MODULE:= libziparchive
+LOCAL_CFLAGS := -Werror -Wall
+LOCAL_CPPFLAGS := -Wold-style-cast
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_SRC_FILES := ${source_files}
+LOCAL_STATIC_LIBRARIES := libz libutils libbase
+LOCAL_MODULE:= libziparchive-host
+LOCAL_CFLAGS := -Werror
+ifneq ($(strip $(USE_MINGW)),)
+ LOCAL_CFLAGS += -mno-ms-bitfields
+endif
+LOCAL_MULTILIB := both
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_SRC_FILES := ${source_files}
+LOCAL_STATIC_LIBRARIES := libutils
+LOCAL_SHARED_LIBRARIES := libz-host liblog libbase
+LOCAL_MODULE:= libziparchive-host
+LOCAL_CFLAGS := -Werror
+LOCAL_MULTILIB := both
+include $(BUILD_HOST_SHARED_LIBRARY)
+
+# Tests.
+include $(CLEAR_VARS)
+LOCAL_MODULE := ziparchive-tests
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_CFLAGS := -Werror
+LOCAL_SRC_FILES := zip_archive_test.cc entry_name_utils_test.cc
+LOCAL_SHARED_LIBRARIES := liblog libbase
+LOCAL_STATIC_LIBRARIES := libziparchive libz libutils
+include $(BUILD_NATIVE_TEST)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := ziparchive-tests-host
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_CFLAGS += \
+ -Werror \
+ -Wno-unnamed-type-template-args
+LOCAL_SRC_FILES := zip_archive_test.cc entry_name_utils_test.cc
+LOCAL_SHARED_LIBRARIES := libziparchive-host liblog libbase
+LOCAL_STATIC_LIBRARIES := \
+ libz \
+ libutils
+include $(BUILD_HOST_NATIVE_TEST)
diff --git a/libziparchive/entry_name_utils-inl.h b/libziparchive/entry_name_utils-inl.h
new file mode 100644
index 0000000..ddbc286
--- /dev/null
+++ b/libziparchive/entry_name_utils-inl.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBZIPARCHIVE_ENTRY_NAME_UTILS_INL_H_
+#define LIBZIPARCHIVE_ENTRY_NAME_UTILS_INL_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+// Check if |length| bytes at |entry_name| constitute a valid entry name.
+// Entry names must be valid UTF-8 and must not contain '0'.
+inline bool IsValidEntryName(const uint8_t* entry_name, const size_t length) {
+ for (size_t i = 0; i < length; ++i) {
+ const uint8_t byte = entry_name[i];
+ if (byte == 0) {
+ return false;
+ } else if ((byte & 0x80) == 0) {
+ // Single byte sequence.
+ continue;
+ } else if ((byte & 0xc0) == 0x80 || (byte & 0xfe) == 0xfe) {
+ // Invalid sequence.
+ return false;
+ } else {
+ // 2-5 byte sequences.
+ for (uint8_t first = byte << 1; first & 0x80; first <<= 1) {
+ ++i;
+
+ // Missing continuation byte..
+ if (i == length) {
+ return false;
+ }
+
+ // Invalid continuation byte.
+ const uint8_t continuation_byte = entry_name[i];
+ if ((continuation_byte & 0xc0) != 0x80) {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+
+#endif // LIBZIPARCHIVE_ENTRY_NAME_UTILS_INL_H_
diff --git a/libziparchive/entry_name_utils_test.cc b/libziparchive/entry_name_utils_test.cc
new file mode 100644
index 0000000..20715bb
--- /dev/null
+++ b/libziparchive/entry_name_utils_test.cc
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2014 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 "entry_name_utils-inl.h"
+
+#include <gtest/gtest.h>
+
+TEST(entry_name_utils, NullChars) {
+ // 'A', 'R', '\0', 'S', 'E'
+ const uint8_t zeroes[] = { 0x41, 0x52, 0x00, 0x53, 0x45 };
+ ASSERT_FALSE(IsValidEntryName(zeroes, sizeof(zeroes)));
+
+ const uint8_t zeroes_continuation_chars[] = { 0xc2, 0xa1, 0xc2, 0x00 };
+ ASSERT_FALSE(IsValidEntryName(zeroes_continuation_chars,
+ sizeof(zeroes_continuation_chars)));
+}
+
+TEST(entry_name_utils, InvalidSequence) {
+ // 0xfe is an invalid start byte
+ const uint8_t invalid[] = { 0x41, 0xfe };
+ ASSERT_FALSE(IsValidEntryName(invalid, sizeof(invalid)));
+
+ // 0x91 is an invalid start byte (it's a valid continuation byte).
+ const uint8_t invalid2[] = { 0x41, 0x91 };
+ ASSERT_FALSE(IsValidEntryName(invalid2, sizeof(invalid2)));
+}
+
+TEST(entry_name_utils, TruncatedContinuation) {
+ // Malayalam script with truncated bytes. There should be 2 bytes
+ // after 0xe0
+ const uint8_t truncated[] = { 0xe0, 0xb4, 0x85, 0xe0, 0xb4 };
+ ASSERT_FALSE(IsValidEntryName(truncated, sizeof(truncated)));
+
+ // 0xc2 is the start of a 2 byte sequence that we've subsequently
+ // dropped.
+ const uint8_t truncated2[] = { 0xc2, 0xc2, 0xa1 };
+ ASSERT_FALSE(IsValidEntryName(truncated2, sizeof(truncated2)));
+}
+
+TEST(entry_name_utils, BadContinuation) {
+ // 0x41 is an invalid continuation char, since it's MSBs
+ // aren't "10..." (are 01).
+ const uint8_t bad[] = { 0xc2, 0xa1, 0xc2, 0x41 };
+ ASSERT_FALSE(IsValidEntryName(bad, sizeof(bad)));
+
+ // 0x41 is an invalid continuation char, since it's MSBs
+ // aren't "10..." (are 11).
+ const uint8_t bad2[] = { 0xc2, 0xa1, 0xc2, 0xfe };
+ ASSERT_FALSE(IsValidEntryName(bad2, sizeof(bad2)));
+}
diff --git a/libziparchive/testdata/declaredlength.zip b/libziparchive/testdata/declaredlength.zip
new file mode 100644
index 0000000..773380c
--- /dev/null
+++ b/libziparchive/testdata/declaredlength.zip
Binary files differ
diff --git a/libziparchive/testdata/valid.zip b/libziparchive/testdata/valid.zip
new file mode 100644
index 0000000..9e7cb78
--- /dev/null
+++ b/libziparchive/testdata/valid.zip
Binary files differ
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
new file mode 100644
index 0000000..3716343
--- /dev/null
+++ b/libziparchive/zip_archive.cc
@@ -0,0 +1,1281 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Read-only access to Zip archives, with minimal heap allocation.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <memory>
+#include <vector>
+
+#include "base/file.h"
+#include "base/macros.h" // TEMP_FAILURE_RETRY may or may not be in unistd
+#include "base/memory.h"
+#include "log/log.h"
+#include "utils/Compat.h"
+#include "utils/FileMap.h"
+#include "zlib.h"
+
+#include "entry_name_utils-inl.h"
+#include "ziparchive/zip_archive.h"
+
+using android::base::get_unaligned;
+
+// This is for windows. If we don't open a file in binary mode, weird
+// things will happen.
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+// The "end of central directory" (EOCD) record. Each archive
+// contains exactly once such record which appears at the end of
+// the archive. It contains archive wide information like the
+// number of entries in the archive and the offset to the central
+// directory of the offset.
+struct EocdRecord {
+ static const uint32_t kSignature = 0x06054b50;
+
+ // End of central directory signature, should always be
+ // |kSignature|.
+ uint32_t eocd_signature;
+ // The number of the current "disk", i.e, the "disk" that this
+ // central directory is on.
+ //
+ // This implementation assumes that each archive spans a single
+ // disk only. i.e, that disk_num == 1.
+ uint16_t disk_num;
+ // The disk where the central directory starts.
+ //
+ // This implementation assumes that each archive spans a single
+ // disk only. i.e, that cd_start_disk == 1.
+ uint16_t cd_start_disk;
+ // The number of central directory records on this disk.
+ //
+ // This implementation assumes that each archive spans a single
+ // disk only. i.e, that num_records_on_disk == num_records.
+ uint16_t num_records_on_disk;
+ // The total number of central directory records.
+ uint16_t num_records;
+ // The size of the central directory (in bytes).
+ uint32_t cd_size;
+ // The offset of the start of the central directory, relative
+ // to the start of the file.
+ uint32_t cd_start_offset;
+ // Length of the central directory comment.
+ uint16_t comment_length;
+ private:
+ EocdRecord() = default;
+ DISALLOW_COPY_AND_ASSIGN(EocdRecord);
+} __attribute__((packed));
+
+// A structure representing the fixed length fields for a single
+// record in the central directory of the archive. In addition to
+// the fixed length fields listed here, each central directory
+// record contains a variable length "file_name" and "extra_field"
+// whose lengths are given by |file_name_length| and |extra_field_length|
+// respectively.
+struct CentralDirectoryRecord {
+ static const uint32_t kSignature = 0x02014b50;
+
+ // The start of record signature. Must be |kSignature|.
+ uint32_t record_signature;
+ // Tool version. Ignored by this implementation.
+ uint16_t version_made_by;
+ // Tool version. Ignored by this implementation.
+ uint16_t version_needed;
+ // The "general purpose bit flags" for this entry. The only
+ // flag value that we currently check for is the "data descriptor"
+ // flag.
+ uint16_t gpb_flags;
+ // The compression method for this entry, one of |kCompressStored|
+ // and |kCompressDeflated|.
+ uint16_t compression_method;
+ // The file modification time and date for this entry.
+ uint16_t last_mod_time;
+ uint16_t last_mod_date;
+ // The CRC-32 checksum for this entry.
+ uint32_t crc32;
+ // The compressed size (in bytes) of this entry.
+ uint32_t compressed_size;
+ // The uncompressed size (in bytes) of this entry.
+ uint32_t uncompressed_size;
+ // The length of the entry file name in bytes. The file name
+ // will appear immediately after this record.
+ uint16_t file_name_length;
+ // The length of the extra field info (in bytes). This data
+ // will appear immediately after the entry file name.
+ uint16_t extra_field_length;
+ // The length of the entry comment (in bytes). This data will
+ // appear immediately after the extra field.
+ uint16_t comment_length;
+ // The start disk for this entry. Ignored by this implementation).
+ uint16_t file_start_disk;
+ // File attributes. Ignored by this implementation.
+ uint16_t internal_file_attributes;
+ // File attributes. Ignored by this implementation.
+ uint32_t external_file_attributes;
+ // The offset to the local file header for this entry, from the
+ // beginning of this archive.
+ uint32_t local_file_header_offset;
+ private:
+ CentralDirectoryRecord() = default;
+ DISALLOW_COPY_AND_ASSIGN(CentralDirectoryRecord);
+} __attribute__((packed));
+
+// The local file header for a given entry. This duplicates information
+// present in the central directory of the archive. It is an error for
+// the information here to be different from the central directory
+// information for a given entry.
+struct LocalFileHeader {
+ static const uint32_t kSignature = 0x04034b50;
+
+ // The local file header signature, must be |kSignature|.
+ uint32_t lfh_signature;
+ // Tool version. Ignored by this implementation.
+ uint16_t version_needed;
+ // The "general purpose bit flags" for this entry. The only
+ // flag value that we currently check for is the "data descriptor"
+ // flag.
+ uint16_t gpb_flags;
+ // The compression method for this entry, one of |kCompressStored|
+ // and |kCompressDeflated|.
+ uint16_t compression_method;
+ // The file modification time and date for this entry.
+ uint16_t last_mod_time;
+ uint16_t last_mod_date;
+ // The CRC-32 checksum for this entry.
+ uint32_t crc32;
+ // The compressed size (in bytes) of this entry.
+ uint32_t compressed_size;
+ // The uncompressed size (in bytes) of this entry.
+ uint32_t uncompressed_size;
+ // The length of the entry file name in bytes. The file name
+ // will appear immediately after this record.
+ uint16_t file_name_length;
+ // The length of the extra field info (in bytes). This data
+ // will appear immediately after the entry file name.
+ uint16_t extra_field_length;
+ private:
+ LocalFileHeader() = default;
+ DISALLOW_COPY_AND_ASSIGN(LocalFileHeader);
+} __attribute__((packed));
+
+struct DataDescriptor {
+ // The *optional* data descriptor start signature.
+ static const uint32_t kOptSignature = 0x08074b50;
+
+ // CRC-32 checksum of the entry.
+ uint32_t crc32;
+ // Compressed size of the entry.
+ uint32_t compressed_size;
+ // Uncompressed size of the entry.
+ uint32_t uncompressed_size;
+ private:
+ DataDescriptor() = default;
+ DISALLOW_COPY_AND_ASSIGN(DataDescriptor);
+} __attribute__((packed));
+
+
+static const uint32_t kGPBDDFlagMask = 0x0008; // mask value that signifies that the entry has a DD
+
+// The maximum size of a central directory or a file
+// comment in bytes.
+static const uint32_t kMaxCommentLen = 65535;
+
+// The maximum number of bytes to scan backwards for the EOCD start.
+static const uint32_t kMaxEOCDSearch = kMaxCommentLen + sizeof(EocdRecord);
+
+static const char* kErrorMessages[] = {
+ "Unknown return code.",
+ "Iteration ended",
+ "Zlib error",
+ "Invalid file",
+ "Invalid handle",
+ "Duplicate entries in archive",
+ "Empty archive",
+ "Entry not found",
+ "Invalid offset",
+ "Inconsistent information",
+ "Invalid entry name",
+ "I/O Error",
+ "File mapping failed"
+};
+
+static const int32_t kErrorMessageUpperBound = 0;
+
+static const int32_t kIterationEnd = -1;
+
+// We encountered a Zlib error when inflating a stream from this file.
+// Usually indicates file corruption.
+static const int32_t kZlibError = -2;
+
+// The input file cannot be processed as a zip archive. Usually because
+// it's too small, too large or does not have a valid signature.
+static const int32_t kInvalidFile = -3;
+
+// An invalid iteration / ziparchive handle was passed in as an input
+// argument.
+static const int32_t kInvalidHandle = -4;
+
+// The zip archive contained two (or possibly more) entries with the same
+// name.
+static const int32_t kDuplicateEntry = -5;
+
+// The zip archive contains no entries.
+static const int32_t kEmptyArchive = -6;
+
+// The specified entry was not found in the archive.
+static const int32_t kEntryNotFound = -7;
+
+// The zip archive contained an invalid local file header pointer.
+static const int32_t kInvalidOffset = -8;
+
+// The zip archive contained inconsistent entry information. This could
+// be because the central directory & local file header did not agree, or
+// if the actual uncompressed length or crc32 do not match their declared
+// values.
+static const int32_t kInconsistentInformation = -9;
+
+// An invalid entry name was encountered.
+static const int32_t kInvalidEntryName = -10;
+
+// An I/O related system call (read, lseek, ftruncate, map) failed.
+static const int32_t kIoError = -11;
+
+// We were not able to mmap the central directory or entry contents.
+static const int32_t kMmapFailed = -12;
+
+static const int32_t kErrorMessageLowerBound = -13;
+
+/*
+ * A Read-only Zip archive.
+ *
+ * We want "open" and "find entry by name" to be fast operations, and
+ * we want to use as little memory as possible. We memory-map the zip
+ * central directory, and load a hash table with pointers to the filenames
+ * (which aren't null-terminated). The other fields are at a fixed offset
+ * from the filename, so we don't need to extract those (but we do need
+ * to byte-read and endian-swap them every time we want them).
+ *
+ * It's possible that somebody has handed us a massive (~1GB) zip archive,
+ * so we can't expect to mmap the entire file.
+ *
+ * To speed comparisons when doing a lookup by name, we could make the mapping
+ * "private" (copy-on-write) and null-terminate the filenames after verifying
+ * the record structure. However, this requires a private mapping of
+ * every page that the Central Directory touches. Easier to tuck a copy
+ * of the string length into the hash table entry.
+ */
+struct ZipArchive {
+ /* open Zip archive */
+ const int fd;
+ const bool close_file;
+
+ /* mapped central directory area */
+ off64_t directory_offset;
+ android::FileMap directory_map;
+
+ /* number of entries in the Zip archive */
+ uint16_t num_entries;
+
+ /*
+ * We know how many entries are in the Zip archive, so we can have a
+ * fixed-size hash table. We define a load factor of 0.75 and overallocat
+ * so the maximum number entries can never be higher than
+ * ((4 * UINT16_MAX) / 3 + 1) which can safely fit into a uint32_t.
+ */
+ uint32_t hash_table_size;
+ ZipString* hash_table;
+
+ ZipArchive(const int fd, bool assume_ownership) :
+ fd(fd),
+ close_file(assume_ownership),
+ directory_offset(0),
+ num_entries(0),
+ hash_table_size(0),
+ hash_table(NULL) {}
+
+ ~ZipArchive() {
+ if (close_file && fd >= 0) {
+ close(fd);
+ }
+
+ free(hash_table);
+ }
+};
+
+/*
+ * Round up to the next highest power of 2.
+ *
+ * Found on http://graphics.stanford.edu/~seander/bithacks.html.
+ */
+static uint32_t RoundUpPower2(uint32_t val) {
+ val--;
+ val |= val >> 1;
+ val |= val >> 2;
+ val |= val >> 4;
+ val |= val >> 8;
+ val |= val >> 16;
+ val++;
+
+ return val;
+}
+
+static uint32_t ComputeHash(const ZipString& name) {
+ uint32_t hash = 0;
+ uint16_t len = name.name_length;
+ const uint8_t* str = name.name;
+
+ while (len--) {
+ hash = hash * 31 + *str++;
+ }
+
+ return hash;
+}
+
+/*
+ * Convert a ZipEntry to a hash table index, verifying that it's in a
+ * valid range.
+ */
+static int64_t EntryToIndex(const ZipString* hash_table,
+ const uint32_t hash_table_size,
+ const ZipString& name) {
+ const uint32_t hash = ComputeHash(name);
+
+ // NOTE: (hash_table_size - 1) is guaranteed to be non-negative.
+ uint32_t ent = hash & (hash_table_size - 1);
+ while (hash_table[ent].name != NULL) {
+ if (hash_table[ent] == name) {
+ return ent;
+ }
+
+ ent = (ent + 1) & (hash_table_size - 1);
+ }
+
+ ALOGV("Zip: Unable to find entry %.*s", name.name_length, name.name);
+ return kEntryNotFound;
+}
+
+/*
+ * Add a new entry to the hash table.
+ */
+static int32_t AddToHash(ZipString *hash_table, const uint64_t hash_table_size,
+ const ZipString& name) {
+ const uint64_t hash = ComputeHash(name);
+ uint32_t ent = hash & (hash_table_size - 1);
+
+ /*
+ * We over-allocated the table, so we're guaranteed to find an empty slot.
+ * Further, we guarantee that the hashtable size is not 0.
+ */
+ while (hash_table[ent].name != NULL) {
+ if (hash_table[ent] == name) {
+ // We've found a duplicate entry. We don't accept it
+ ALOGW("Zip: Found duplicate entry %.*s", name.name_length, name.name);
+ return kDuplicateEntry;
+ }
+ ent = (ent + 1) & (hash_table_size - 1);
+ }
+
+ hash_table[ent].name = name.name;
+ hash_table[ent].name_length = name.name_length;
+ return 0;
+}
+
+static int32_t MapCentralDirectory0(int fd, const char* debug_file_name,
+ ZipArchive* archive, off64_t file_length,
+ off64_t read_amount, uint8_t* scan_buffer) {
+ const off64_t search_start = file_length - read_amount;
+
+ if (lseek64(fd, search_start, SEEK_SET) != search_start) {
+ ALOGW("Zip: seek %" PRId64 " failed: %s", static_cast<int64_t>(search_start),
+ strerror(errno));
+ return kIoError;
+ }
+ ssize_t actual = TEMP_FAILURE_RETRY(
+ read(fd, scan_buffer, static_cast<size_t>(read_amount)));
+ if (actual != static_cast<ssize_t>(read_amount)) {
+ ALOGW("Zip: read %" PRId64 " failed: %s", static_cast<int64_t>(read_amount),
+ strerror(errno));
+ return kIoError;
+ }
+
+ /*
+ * Scan backward for the EOCD magic. In an archive without a trailing
+ * comment, we'll find it on the first try. (We may want to consider
+ * doing an initial minimal read; if we don't find it, retry with a
+ * second read as above.)
+ */
+ int i = read_amount - sizeof(EocdRecord);
+ for (; i >= 0; i--) {
+ if (scan_buffer[i] == 0x50) {
+ uint32_t* sig_addr = reinterpret_cast<uint32_t*>(&scan_buffer[i]);
+ if (get_unaligned<uint32_t>(sig_addr) == EocdRecord::kSignature) {
+ ALOGV("+++ Found EOCD at buf+%d", i);
+ break;
+ }
+ }
+ }
+ if (i < 0) {
+ ALOGD("Zip: EOCD not found, %s is not zip", debug_file_name);
+ return kInvalidFile;
+ }
+
+ const off64_t eocd_offset = search_start + i;
+ const EocdRecord* eocd = reinterpret_cast<const EocdRecord*>(scan_buffer + i);
+ /*
+ * Verify that there's no trailing space at the end of the central directory
+ * and its comment.
+ */
+ const off64_t calculated_length = eocd_offset + sizeof(EocdRecord)
+ + eocd->comment_length;
+ if (calculated_length != file_length) {
+ ALOGW("Zip: %" PRId64 " extraneous bytes at the end of the central directory",
+ static_cast<int64_t>(file_length - calculated_length));
+ return kInvalidFile;
+ }
+
+ /*
+ * Grab the CD offset and size, and the number of entries in the
+ * archive and verify that they look reasonable.
+ */
+ if (eocd->cd_start_offset + eocd->cd_size > eocd_offset) {
+ ALOGW("Zip: bad offsets (dir %" PRIu32 ", size %" PRIu32 ", eocd %" PRId64 ")",
+ eocd->cd_start_offset, eocd->cd_size, static_cast<int64_t>(eocd_offset));
+ return kInvalidOffset;
+ }
+ if (eocd->num_records == 0) {
+ ALOGW("Zip: empty archive?");
+ return kEmptyArchive;
+ }
+
+ ALOGV("+++ num_entries=%" PRIu32 " dir_size=%" PRIu32 " dir_offset=%" PRIu32,
+ eocd->num_records, eocd->cd_size, eocd->cd_start_offset);
+
+ /*
+ * It all looks good. Create a mapping for the CD, and set the fields
+ * in archive.
+ */
+ if (!archive->directory_map.create(debug_file_name, fd,
+ static_cast<off64_t>(eocd->cd_start_offset),
+ static_cast<size_t>(eocd->cd_size), true /* read only */) ) {
+ return kMmapFailed;
+ }
+
+ archive->num_entries = eocd->num_records;
+ archive->directory_offset = eocd->cd_start_offset;
+
+ return 0;
+}
+
+/*
+ * Find the zip Central Directory and memory-map it.
+ *
+ * On success, returns 0 after populating fields from the EOCD area:
+ * directory_offset
+ * directory_map
+ * num_entries
+ */
+static int32_t MapCentralDirectory(int fd, const char* debug_file_name,
+ ZipArchive* archive) {
+
+ // Test file length. We use lseek64 to make sure the file
+ // is small enough to be a zip file (Its size must be less than
+ // 0xffffffff bytes).
+ off64_t file_length = lseek64(fd, 0, SEEK_END);
+ if (file_length == -1) {
+ ALOGV("Zip: lseek on fd %d failed", fd);
+ return kInvalidFile;
+ }
+
+ if (file_length > static_cast<off64_t>(0xffffffff)) {
+ ALOGV("Zip: zip file too long %" PRId64, static_cast<int64_t>(file_length));
+ return kInvalidFile;
+ }
+
+ if (file_length < static_cast<off64_t>(sizeof(EocdRecord))) {
+ ALOGV("Zip: length %" PRId64 " is too small to be zip", static_cast<int64_t>(file_length));
+ return kInvalidFile;
+ }
+
+ /*
+ * Perform the traditional EOCD snipe hunt.
+ *
+ * We're searching for the End of Central Directory magic number,
+ * which appears at the start of the EOCD block. It's followed by
+ * 18 bytes of EOCD stuff and up to 64KB of archive comment. We
+ * need to read the last part of the file into a buffer, dig through
+ * it to find the magic number, parse some values out, and use those
+ * to determine the extent of the CD.
+ *
+ * We start by pulling in the last part of the file.
+ */
+ off64_t read_amount = kMaxEOCDSearch;
+ if (file_length < read_amount) {
+ read_amount = file_length;
+ }
+
+ uint8_t* scan_buffer = reinterpret_cast<uint8_t*>(malloc(read_amount));
+ int32_t result = MapCentralDirectory0(fd, debug_file_name, archive,
+ file_length, read_amount, scan_buffer);
+
+ free(scan_buffer);
+ return result;
+}
+
+/*
+ * Parses the Zip archive's Central Directory. Allocates and populates the
+ * hash table.
+ *
+ * Returns 0 on success.
+ */
+static int32_t ParseZipArchive(ZipArchive* archive) {
+ const uint8_t* const cd_ptr =
+ reinterpret_cast<const uint8_t*>(archive->directory_map.getDataPtr());
+ const size_t cd_length = archive->directory_map.getDataLength();
+ const uint16_t num_entries = archive->num_entries;
+
+ /*
+ * Create hash table. We have a minimum 75% load factor, possibly as
+ * low as 50% after we round off to a power of 2. There must be at
+ * least one unused entry to avoid an infinite loop during creation.
+ */
+ archive->hash_table_size = RoundUpPower2(1 + (num_entries * 4) / 3);
+ archive->hash_table = reinterpret_cast<ZipString*>(calloc(archive->hash_table_size,
+ sizeof(ZipString)));
+
+ /*
+ * Walk through the central directory, adding entries to the hash
+ * table and verifying values.
+ */
+ const uint8_t* const cd_end = cd_ptr + cd_length;
+ const uint8_t* ptr = cd_ptr;
+ for (uint16_t i = 0; i < num_entries; i++) {
+ const CentralDirectoryRecord* cdr =
+ reinterpret_cast<const CentralDirectoryRecord*>(ptr);
+ if (cdr->record_signature != CentralDirectoryRecord::kSignature) {
+ ALOGW("Zip: missed a central dir sig (at %" PRIu16 ")", i);
+ return -1;
+ }
+
+ if (ptr + sizeof(CentralDirectoryRecord) > cd_end) {
+ ALOGW("Zip: ran off the end (at %" PRIu16 ")", i);
+ return -1;
+ }
+
+ const off64_t local_header_offset = cdr->local_file_header_offset;
+ if (local_header_offset >= archive->directory_offset) {
+ ALOGW("Zip: bad LFH offset %" PRId64 " at entry %" PRIu16,
+ static_cast<int64_t>(local_header_offset), i);
+ return -1;
+ }
+
+ const uint16_t file_name_length = cdr->file_name_length;
+ const uint16_t extra_length = cdr->extra_field_length;
+ const uint16_t comment_length = cdr->comment_length;
+ const uint8_t* file_name = ptr + sizeof(CentralDirectoryRecord);
+
+ /* check that file name is valid UTF-8 and doesn't contain NUL (U+0000) characters */
+ if (!IsValidEntryName(file_name, file_name_length)) {
+ return -1;
+ }
+
+ /* add the CDE filename to the hash table */
+ ZipString entry_name;
+ entry_name.name = file_name;
+ entry_name.name_length = file_name_length;
+ const int add_result = AddToHash(archive->hash_table,
+ archive->hash_table_size, entry_name);
+ if (add_result != 0) {
+ ALOGW("Zip: Error adding entry to hash table %d", add_result);
+ return add_result;
+ }
+
+ ptr += sizeof(CentralDirectoryRecord) + file_name_length + extra_length + comment_length;
+ if ((ptr - cd_ptr) > static_cast<int64_t>(cd_length)) {
+ ALOGW("Zip: bad CD advance (%tu vs %zu) at entry %" PRIu16,
+ ptr - cd_ptr, cd_length, i);
+ return -1;
+ }
+ }
+ ALOGV("+++ zip good scan %" PRIu16 " entries", num_entries);
+
+ return 0;
+}
+
+static int32_t OpenArchiveInternal(ZipArchive* archive,
+ const char* debug_file_name) {
+ int32_t result = -1;
+ if ((result = MapCentralDirectory(archive->fd, debug_file_name, archive))) {
+ return result;
+ }
+
+ if ((result = ParseZipArchive(archive))) {
+ return result;
+ }
+
+ return 0;
+}
+
+int32_t OpenArchiveFd(int fd, const char* debug_file_name,
+ ZipArchiveHandle* handle, bool assume_ownership) {
+ ZipArchive* archive = new ZipArchive(fd, assume_ownership);
+ *handle = archive;
+ return OpenArchiveInternal(archive, debug_file_name);
+}
+
+int32_t OpenArchive(const char* fileName, ZipArchiveHandle* handle) {
+ const int fd = open(fileName, O_RDONLY | O_BINARY, 0);
+ ZipArchive* archive = new ZipArchive(fd, true);
+ *handle = archive;
+
+ if (fd < 0) {
+ ALOGW("Unable to open '%s': %s", fileName, strerror(errno));
+ return kIoError;
+ }
+
+ return OpenArchiveInternal(archive, fileName);
+}
+
+/*
+ * Close a ZipArchive, closing the file and freeing the contents.
+ */
+void CloseArchive(ZipArchiveHandle handle) {
+ ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
+ ALOGV("Closing archive %p", archive);
+ delete archive;
+}
+
+static int32_t UpdateEntryFromDataDescriptor(int fd,
+ ZipEntry *entry) {
+ uint8_t ddBuf[sizeof(DataDescriptor) + sizeof(DataDescriptor::kOptSignature)];
+ ssize_t actual = TEMP_FAILURE_RETRY(read(fd, ddBuf, sizeof(ddBuf)));
+ if (actual != sizeof(ddBuf)) {
+ return kIoError;
+ }
+
+ const uint32_t ddSignature = *(reinterpret_cast<const uint32_t*>(ddBuf));
+ const uint16_t offset = (ddSignature == DataDescriptor::kOptSignature) ? 4 : 0;
+ const DataDescriptor* descriptor = reinterpret_cast<const DataDescriptor*>(ddBuf + offset);
+
+ entry->crc32 = descriptor->crc32;
+ entry->compressed_length = descriptor->compressed_size;
+ entry->uncompressed_length = descriptor->uncompressed_size;
+
+ return 0;
+}
+
+// Attempts to read |len| bytes into |buf| at offset |off|.
+//
+// This method uses pread64 on platforms that support it and
+// lseek64 + read on platforms that don't. This implies that
+// callers should not rely on the |fd| offset being incremented
+// as a side effect of this call.
+static inline ssize_t ReadAtOffset(int fd, uint8_t* buf, size_t len,
+ off64_t off) {
+#if !defined(_WIN32)
+ return TEMP_FAILURE_RETRY(pread64(fd, buf, len, off));
+#else
+ // The only supported platform that doesn't support pread at the moment
+ // is Windows. Only recent versions of windows support unix like forks,
+ // and even there the semantics are quite different.
+ if (lseek64(fd, off, SEEK_SET) != off) {
+ ALOGW("Zip: failed seek to offset %" PRId64, off);
+ return kIoError;
+ }
+
+ return TEMP_FAILURE_RETRY(read(fd, buf, len));
+#endif
+}
+
+static int32_t FindEntry(const ZipArchive* archive, const int ent,
+ ZipEntry* data) {
+ const uint16_t nameLen = archive->hash_table[ent].name_length;
+
+ // Recover the start of the central directory entry from the filename
+ // pointer. The filename is the first entry past the fixed-size data,
+ // so we can just subtract back from that.
+ const uint8_t* ptr = archive->hash_table[ent].name;
+ ptr -= sizeof(CentralDirectoryRecord);
+
+ // This is the base of our mmapped region, we have to sanity check that
+ // the name that's in the hash table is a pointer to a location within
+ // this mapped region.
+ const uint8_t* base_ptr = reinterpret_cast<const uint8_t*>(
+ archive->directory_map.getDataPtr());
+ if (ptr < base_ptr || ptr > base_ptr + archive->directory_map.getDataLength()) {
+ ALOGW("Zip: Invalid entry pointer");
+ return kInvalidOffset;
+ }
+
+ const CentralDirectoryRecord *cdr =
+ reinterpret_cast<const CentralDirectoryRecord*>(ptr);
+
+ // The offset of the start of the central directory in the zipfile.
+ // We keep this lying around so that we can sanity check all our lengths
+ // and our per-file structures.
+ const off64_t cd_offset = archive->directory_offset;
+
+ // Fill out the compression method, modification time, crc32
+ // and other interesting attributes from the central directory. These
+ // will later be compared against values from the local file header.
+ data->method = cdr->compression_method;
+ data->mod_time = cdr->last_mod_time;
+ data->crc32 = cdr->crc32;
+ data->compressed_length = cdr->compressed_size;
+ data->uncompressed_length = cdr->uncompressed_size;
+
+ // Figure out the local header offset from the central directory. The
+ // actual file data will begin after the local header and the name /
+ // extra comments.
+ const off64_t local_header_offset = cdr->local_file_header_offset;
+ if (local_header_offset + static_cast<off64_t>(sizeof(LocalFileHeader)) >= cd_offset) {
+ ALOGW("Zip: bad local hdr offset in zip");
+ return kInvalidOffset;
+ }
+
+ uint8_t lfh_buf[sizeof(LocalFileHeader)];
+ ssize_t actual = ReadAtOffset(archive->fd, lfh_buf, sizeof(lfh_buf),
+ local_header_offset);
+ if (actual != sizeof(lfh_buf)) {
+ ALOGW("Zip: failed reading lfh name from offset %" PRId64,
+ static_cast<int64_t>(local_header_offset));
+ return kIoError;
+ }
+
+ const LocalFileHeader *lfh = reinterpret_cast<const LocalFileHeader*>(lfh_buf);
+
+ if (lfh->lfh_signature != LocalFileHeader::kSignature) {
+ ALOGW("Zip: didn't find signature at start of lfh, offset=%" PRId64,
+ static_cast<int64_t>(local_header_offset));
+ return kInvalidOffset;
+ }
+
+ // Paranoia: Match the values specified in the local file header
+ // to those specified in the central directory.
+ if ((lfh->gpb_flags & kGPBDDFlagMask) == 0) {
+ data->has_data_descriptor = 0;
+ if (data->compressed_length != lfh->compressed_size
+ || data->uncompressed_length != lfh->uncompressed_size
+ || data->crc32 != lfh->crc32) {
+ ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu32 ", %" PRIu32
+ ", %" PRIx32 "}, was {%" PRIu32 ", %" PRIu32 ", %" PRIx32 "}",
+ data->compressed_length, data->uncompressed_length, data->crc32,
+ lfh->compressed_size, lfh->uncompressed_size, lfh->crc32);
+ return kInconsistentInformation;
+ }
+ } else {
+ data->has_data_descriptor = 1;
+ }
+
+ // Check that the local file header name matches the declared
+ // name in the central directory.
+ if (lfh->file_name_length == nameLen) {
+ const off64_t name_offset = local_header_offset + sizeof(LocalFileHeader);
+ if (name_offset + lfh->file_name_length > cd_offset) {
+ ALOGW("Zip: Invalid declared length");
+ return kInvalidOffset;
+ }
+
+ uint8_t* name_buf = reinterpret_cast<uint8_t*>(malloc(nameLen));
+ ssize_t actual = ReadAtOffset(archive->fd, name_buf, nameLen,
+ name_offset);
+
+ if (actual != nameLen) {
+ ALOGW("Zip: failed reading lfh name from offset %" PRId64, static_cast<int64_t>(name_offset));
+ free(name_buf);
+ return kIoError;
+ }
+
+ if (memcmp(archive->hash_table[ent].name, name_buf, nameLen)) {
+ free(name_buf);
+ return kInconsistentInformation;
+ }
+
+ free(name_buf);
+ } else {
+ ALOGW("Zip: lfh name did not match central directory.");
+ return kInconsistentInformation;
+ }
+
+ const off64_t data_offset = local_header_offset + sizeof(LocalFileHeader)
+ + lfh->file_name_length + lfh->extra_field_length;
+ if (data_offset > cd_offset) {
+ ALOGW("Zip: bad data offset %" PRId64 " in zip", static_cast<int64_t>(data_offset));
+ return kInvalidOffset;
+ }
+
+ if (static_cast<off64_t>(data_offset + data->compressed_length) > cd_offset) {
+ ALOGW("Zip: bad compressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")",
+ static_cast<int64_t>(data_offset), data->compressed_length, static_cast<int64_t>(cd_offset));
+ return kInvalidOffset;
+ }
+
+ if (data->method == kCompressStored &&
+ static_cast<off64_t>(data_offset + data->uncompressed_length) > cd_offset) {
+ ALOGW("Zip: bad uncompressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")",
+ static_cast<int64_t>(data_offset), data->uncompressed_length,
+ static_cast<int64_t>(cd_offset));
+ return kInvalidOffset;
+ }
+
+ data->offset = data_offset;
+ return 0;
+}
+
+struct IterationHandle {
+ uint32_t position;
+ // We're not using vector here because this code is used in the Windows SDK
+ // where the STL is not available.
+ ZipString prefix;
+ ZipString suffix;
+ ZipArchive* archive;
+
+ IterationHandle(const ZipString* in_prefix,
+ const ZipString* in_suffix) {
+ if (in_prefix) {
+ uint8_t* name_copy = new uint8_t[in_prefix->name_length];
+ memcpy(name_copy, in_prefix->name, in_prefix->name_length);
+ prefix.name = name_copy;
+ prefix.name_length = in_prefix->name_length;
+ } else {
+ prefix.name = NULL;
+ prefix.name_length = 0;
+ }
+ if (in_suffix) {
+ uint8_t* name_copy = new uint8_t[in_suffix->name_length];
+ memcpy(name_copy, in_suffix->name, in_suffix->name_length);
+ suffix.name = name_copy;
+ suffix.name_length = in_suffix->name_length;
+ } else {
+ suffix.name = NULL;
+ suffix.name_length = 0;
+ }
+ }
+
+ ~IterationHandle() {
+ delete[] prefix.name;
+ delete[] suffix.name;
+ }
+};
+
+int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr,
+ const ZipString* optional_prefix,
+ const ZipString* optional_suffix) {
+ ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
+
+ if (archive == NULL || archive->hash_table == NULL) {
+ ALOGW("Zip: Invalid ZipArchiveHandle");
+ return kInvalidHandle;
+ }
+
+ IterationHandle* cookie = new IterationHandle(optional_prefix, optional_suffix);
+ cookie->position = 0;
+ cookie->archive = archive;
+
+ *cookie_ptr = cookie ;
+ return 0;
+}
+
+void EndIteration(void* cookie) {
+ delete reinterpret_cast<IterationHandle*>(cookie);
+}
+
+int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName,
+ ZipEntry* data) {
+ const ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
+ if (entryName.name_length == 0) {
+ ALOGW("Zip: Invalid filename %.*s", entryName.name_length, entryName.name);
+ return kInvalidEntryName;
+ }
+
+ const int64_t ent = EntryToIndex(archive->hash_table,
+ archive->hash_table_size, entryName);
+
+ if (ent < 0) {
+ ALOGV("Zip: Could not find entry %.*s", entryName.name_length, entryName.name);
+ return ent;
+ }
+
+ return FindEntry(archive, ent, data);
+}
+
+int32_t Next(void* cookie, ZipEntry* data, ZipString* name) {
+ IterationHandle* handle = reinterpret_cast<IterationHandle*>(cookie);
+ if (handle == NULL) {
+ return kInvalidHandle;
+ }
+
+ ZipArchive* archive = handle->archive;
+ if (archive == NULL || archive->hash_table == NULL) {
+ ALOGW("Zip: Invalid ZipArchiveHandle");
+ return kInvalidHandle;
+ }
+
+ const uint32_t currentOffset = handle->position;
+ const uint32_t hash_table_length = archive->hash_table_size;
+ const ZipString* hash_table = archive->hash_table;
+
+ for (uint32_t i = currentOffset; i < hash_table_length; ++i) {
+ if (hash_table[i].name != NULL &&
+ (handle->prefix.name_length == 0 ||
+ hash_table[i].StartsWith(handle->prefix)) &&
+ (handle->suffix.name_length == 0 ||
+ hash_table[i].EndsWith(handle->suffix))) {
+ handle->position = (i + 1);
+ const int error = FindEntry(archive, i, data);
+ if (!error) {
+ name->name = hash_table[i].name;
+ name->name_length = hash_table[i].name_length;
+ }
+
+ return error;
+ }
+ }
+
+ handle->position = 0;
+ return kIterationEnd;
+}
+
+class Writer {
+ public:
+ virtual bool Append(uint8_t* buf, size_t buf_size) = 0;
+ virtual ~Writer() {}
+ protected:
+ Writer() = default;
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Writer);
+};
+
+// A Writer that writes data to a fixed size memory region.
+// The size of the memory region must be equal to the total size of
+// the data appended to it.
+class MemoryWriter : public Writer {
+ public:
+ MemoryWriter(uint8_t* buf, size_t size) : Writer(),
+ buf_(buf), size_(size), bytes_written_(0) {
+ }
+
+ virtual bool Append(uint8_t* buf, size_t buf_size) override {
+ if (bytes_written_ + buf_size > size_) {
+ ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)",
+ size_, bytes_written_ + buf_size);
+ return false;
+ }
+
+ memcpy(buf_ + bytes_written_, buf, buf_size);
+ bytes_written_ += buf_size;
+ return true;
+ }
+
+ private:
+ uint8_t* const buf_;
+ const size_t size_;
+ size_t bytes_written_;
+};
+
+// A Writer that appends data to a file |fd| at its current position.
+// The file will be truncated to the end of the written data.
+class FileWriter : public Writer {
+ public:
+
+ // Creates a FileWriter for |fd| and prepare to write |entry| to it,
+ // guaranteeing that the file descriptor is valid and that there's enough
+ // space on the volume to write out the entry completely and that the file
+ // is truncated to the correct length.
+ //
+ // Returns a valid FileWriter on success, |nullptr| if an error occurred.
+ static std::unique_ptr<FileWriter> Create(int fd, const ZipEntry* entry) {
+ const uint32_t declared_length = entry->uncompressed_length;
+ const off64_t current_offset = lseek64(fd, 0, SEEK_CUR);
+ if (current_offset == -1) {
+ ALOGW("Zip: unable to seek to current location on fd %d: %s", fd, strerror(errno));
+ return nullptr;
+ }
+
+ int result = 0;
+#if defined(__linux__)
+ if (declared_length > 0) {
+ // Make sure we have enough space on the volume to extract the compressed
+ // entry. Note that the call to ftruncate below will change the file size but
+ // will not allocate space on disk and this call to fallocate will not
+ // change the file size.
+ // Note: fallocate is only supported by the following filesystems -
+ // btrfs, ext4, ocfs2, and xfs. Therefore fallocate might fail with
+ // EOPNOTSUPP error when issued in other filesystems.
+ // Hence, check for the return error code before concluding that the
+ // disk does not have enough space.
+ result = TEMP_FAILURE_RETRY(fallocate(fd, 0, current_offset, declared_length));
+ if (result == -1 && errno == ENOSPC) {
+ ALOGW("Zip: unable to allocate space for file to %" PRId64 ": %s",
+ static_cast<int64_t>(declared_length + current_offset), strerror(errno));
+ return std::unique_ptr<FileWriter>(nullptr);
+ }
+ }
+#endif // __linux__
+
+ result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset));
+ if (result == -1) {
+ ALOGW("Zip: unable to truncate file to %" PRId64 ": %s",
+ static_cast<int64_t>(declared_length + current_offset), strerror(errno));
+ return std::unique_ptr<FileWriter>(nullptr);
+ }
+
+ return std::unique_ptr<FileWriter>(new FileWriter(fd, declared_length));
+ }
+
+ virtual bool Append(uint8_t* buf, size_t buf_size) override {
+ if (total_bytes_written_ + buf_size > declared_length_) {
+ ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)",
+ declared_length_, total_bytes_written_ + buf_size);
+ return false;
+ }
+
+ const bool result = android::base::WriteFully(fd_, buf, buf_size);
+ if (result) {
+ total_bytes_written_ += buf_size;
+ } else {
+ ALOGW("Zip: unable to write " ZD " bytes to file; %s", buf_size, strerror(errno));
+ }
+
+ return result;
+ }
+ private:
+ FileWriter(const int fd, const size_t declared_length) :
+ Writer(),
+ fd_(fd),
+ declared_length_(declared_length),
+ total_bytes_written_(0) {
+ }
+
+ const int fd_;
+ const size_t declared_length_;
+ size_t total_bytes_written_;
+};
+
+// This method is using libz macros with old-style-casts
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wold-style-cast"
+static inline int zlib_inflateInit2(z_stream* stream, int window_bits) {
+ return inflateInit2(stream, window_bits);
+}
+#pragma GCC diagnostic pop
+
+static int32_t InflateEntryToWriter(int fd, const ZipEntry* entry,
+ Writer* writer, uint64_t* crc_out) {
+ const size_t kBufSize = 32768;
+ std::vector<uint8_t> read_buf(kBufSize);
+ std::vector<uint8_t> write_buf(kBufSize);
+ z_stream zstream;
+ int zerr;
+
+ /*
+ * Initialize the zlib stream struct.
+ */
+ memset(&zstream, 0, sizeof(zstream));
+ zstream.zalloc = Z_NULL;
+ zstream.zfree = Z_NULL;
+ zstream.opaque = Z_NULL;
+ zstream.next_in = NULL;
+ zstream.avail_in = 0;
+ zstream.next_out = &write_buf[0];
+ zstream.avail_out = kBufSize;
+ zstream.data_type = Z_UNKNOWN;
+
+ /*
+ * Use the undocumented "negative window bits" feature to tell zlib
+ * that there's no zlib header waiting for it.
+ */
+ zerr = zlib_inflateInit2(&zstream, -MAX_WBITS);
+ if (zerr != Z_OK) {
+ if (zerr == Z_VERSION_ERROR) {
+ ALOGE("Installed zlib is not compatible with linked version (%s)",
+ ZLIB_VERSION);
+ } else {
+ ALOGW("Call to inflateInit2 failed (zerr=%d)", zerr);
+ }
+
+ return kZlibError;
+ }
+
+ auto zstream_deleter = [](z_stream* stream) {
+ inflateEnd(stream); /* free up any allocated structures */
+ };
+
+ std::unique_ptr<z_stream, decltype(zstream_deleter)> zstream_guard(&zstream, zstream_deleter);
+
+ const uint32_t uncompressed_length = entry->uncompressed_length;
+
+ uint32_t compressed_length = entry->compressed_length;
+ do {
+ /* read as much as we can */
+ if (zstream.avail_in == 0) {
+ const ZD_TYPE getSize = (compressed_length > kBufSize) ? kBufSize : compressed_length;
+ const ZD_TYPE actual = TEMP_FAILURE_RETRY(read(fd, &read_buf[0], getSize));
+ if (actual != getSize) {
+ ALOGW("Zip: inflate read failed (" ZD " vs " ZD ")", actual, getSize);
+ return kIoError;
+ }
+
+ compressed_length -= getSize;
+
+ zstream.next_in = &read_buf[0];
+ zstream.avail_in = getSize;
+ }
+
+ /* uncompress the data */
+ zerr = inflate(&zstream, Z_NO_FLUSH);
+ if (zerr != Z_OK && zerr != Z_STREAM_END) {
+ ALOGW("Zip: inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)",
+ zerr, zstream.next_in, zstream.avail_in,
+ zstream.next_out, zstream.avail_out);
+ return kZlibError;
+ }
+
+ /* write when we're full or when we're done */
+ if (zstream.avail_out == 0 ||
+ (zerr == Z_STREAM_END && zstream.avail_out != kBufSize)) {
+ const size_t write_size = zstream.next_out - &write_buf[0];
+ if (!writer->Append(&write_buf[0], write_size)) {
+ // The file might have declared a bogus length.
+ return kInconsistentInformation;
+ }
+
+ zstream.next_out = &write_buf[0];
+ zstream.avail_out = kBufSize;
+ }
+ } while (zerr == Z_OK);
+
+ assert(zerr == Z_STREAM_END); /* other errors should've been caught */
+
+ // stream.adler holds the crc32 value for such streams.
+ *crc_out = zstream.adler;
+
+ if (zstream.total_out != uncompressed_length || compressed_length != 0) {
+ ALOGW("Zip: size mismatch on inflated file (%lu vs %" PRIu32 ")",
+ zstream.total_out, uncompressed_length);
+ return kInconsistentInformation;
+ }
+
+ return 0;
+}
+
+static int32_t CopyEntryToWriter(int fd, const ZipEntry* entry, Writer* writer,
+ uint64_t *crc_out) {
+ static const uint32_t kBufSize = 32768;
+ std::vector<uint8_t> buf(kBufSize);
+
+ const uint32_t length = entry->uncompressed_length;
+ uint32_t count = 0;
+ uint64_t crc = 0;
+ while (count < length) {
+ uint32_t remaining = length - count;
+
+ // Safe conversion because kBufSize is narrow enough for a 32 bit signed
+ // value.
+ const ssize_t block_size = (remaining > kBufSize) ? kBufSize : remaining;
+ const ssize_t actual = TEMP_FAILURE_RETRY(read(fd, &buf[0], block_size));
+
+ if (actual != block_size) {
+ ALOGW("CopyFileToFile: copy read failed (" ZD " vs " ZD ")", actual, block_size);
+ return kIoError;
+ }
+
+ if (!writer->Append(&buf[0], block_size)) {
+ return kIoError;
+ }
+ crc = crc32(crc, &buf[0], block_size);
+ count += block_size;
+ }
+
+ *crc_out = crc;
+
+ return 0;
+}
+
+int32_t ExtractToWriter(ZipArchiveHandle handle,
+ ZipEntry* entry, Writer* writer) {
+ ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
+ const uint16_t method = entry->method;
+ off64_t data_offset = entry->offset;
+
+ if (lseek64(archive->fd, data_offset, SEEK_SET) != data_offset) {
+ ALOGW("Zip: lseek to data at %" PRId64 " failed", static_cast<int64_t>(data_offset));
+ return kIoError;
+ }
+
+ // this should default to kUnknownCompressionMethod.
+ int32_t return_value = -1;
+ uint64_t crc = 0;
+ if (method == kCompressStored) {
+ return_value = CopyEntryToWriter(archive->fd, entry, writer, &crc);
+ } else if (method == kCompressDeflated) {
+ return_value = InflateEntryToWriter(archive->fd, entry, writer, &crc);
+ }
+
+ if (!return_value && entry->has_data_descriptor) {
+ return_value = UpdateEntryFromDataDescriptor(archive->fd, entry);
+ if (return_value) {
+ return return_value;
+ }
+ }
+
+ // TODO: Fix this check by passing the right flags to inflate2 so that
+ // it calculates the CRC for us.
+ if (entry->crc32 != crc && false) {
+ ALOGW("Zip: crc mismatch: expected %" PRIu32 ", was %" PRIu64, entry->crc32, crc);
+ return kInconsistentInformation;
+ }
+
+ return return_value;
+}
+
+int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry,
+ uint8_t* begin, uint32_t size) {
+ std::unique_ptr<Writer> writer(new MemoryWriter(begin, size));
+ return ExtractToWriter(handle, entry, writer.get());
+}
+
+int32_t ExtractEntryToFile(ZipArchiveHandle handle,
+ ZipEntry* entry, int fd) {
+ std::unique_ptr<Writer> writer(FileWriter::Create(fd, entry));
+ if (writer.get() == nullptr) {
+ return kIoError;
+ }
+
+ return ExtractToWriter(handle, entry, writer.get());
+}
+
+const char* ErrorCodeString(int32_t error_code) {
+ if (error_code > kErrorMessageLowerBound && error_code < kErrorMessageUpperBound) {
+ return kErrorMessages[error_code * -1];
+ }
+
+ return kErrorMessages[0];
+}
+
+int GetFileDescriptor(const ZipArchiveHandle handle) {
+ return reinterpret_cast<ZipArchive*>(handle)->fd;
+}
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
new file mode 100644
index 0000000..9a3cdb4
--- /dev/null
+++ b/libziparchive/zip_archive_test.cc
@@ -0,0 +1,565 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ziparchive/zip_archive.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <vector>
+
+#include <base/file.h>
+#include <gtest/gtest.h>
+
+static std::string test_data_dir;
+
+static const std::string kMissingZip = "missing.zip";
+static const std::string kValidZip = "valid.zip";
+
+static const uint8_t kATxtContents[] = {
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
+ '\n'
+};
+
+static const uint8_t kBTxtContents[] = {
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
+ '\n'
+};
+
+static const uint16_t kATxtNameLength = 5;
+static const uint16_t kBTxtNameLength = 5;
+static const uint16_t kNonexistentTxtNameLength = 15;
+static const uint16_t kEmptyTxtNameLength = 9;
+
+static const uint8_t kATxtName[kATxtNameLength] = {
+ 'a', '.', 't', 'x', 't'
+};
+
+static const uint8_t kBTxtName[kBTxtNameLength] = {
+ 'b', '.', 't', 'x', 't'
+};
+
+static const uint8_t kNonexistentTxtName[kNonexistentTxtNameLength] = {
+ 'n', 'o', 'n', 'e', 'x', 'i', 's', 't', 'e', 'n', 't', '.', 't', 'x' ,'t'
+};
+
+static const uint8_t kEmptyTxtName[kEmptyTxtNameLength] = {
+ 'e', 'm', 'p', 't', 'y', '.', 't', 'x', 't'
+};
+
+static int32_t OpenArchiveWrapper(const std::string& name,
+ ZipArchiveHandle* handle) {
+ const std::string abs_path = test_data_dir + "/" + name;
+ return OpenArchive(abs_path.c_str(), handle);
+}
+
+static void AssertNameEquals(const std::string& name_str,
+ const ZipString& name) {
+ ASSERT_EQ(name_str.size(), name.name_length);
+ ASSERT_EQ(0, memcmp(name_str.c_str(), name.name, name.name_length));
+}
+
+TEST(ziparchive, Open) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+ CloseArchive(handle);
+}
+
+TEST(ziparchive, OpenMissing) {
+ ZipArchiveHandle handle;
+ ASSERT_NE(0, OpenArchiveWrapper(kMissingZip, &handle));
+
+ // Confirm the file descriptor is not going to be mistaken for a valid one.
+ ASSERT_EQ(-1, GetFileDescriptor(handle));
+}
+
+TEST(ziparchive, OpenAssumeFdOwnership) {
+ int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY);
+ ASSERT_NE(-1, fd);
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveFd(fd, "OpenWithAssumeFdOwnership", &handle));
+ CloseArchive(handle);
+ ASSERT_EQ(-1, lseek(fd, 0, SEEK_SET));
+ ASSERT_EQ(EBADF, errno);
+}
+
+TEST(ziparchive, OpenDoNotAssumeFdOwnership) {
+ int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY);
+ ASSERT_NE(-1, fd);
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveFd(fd, "OpenWithAssumeFdOwnership", &handle, false));
+ CloseArchive(handle);
+ ASSERT_EQ(0, lseek(fd, 0, SEEK_SET));
+ close(fd);
+}
+
+TEST(ziparchive, Iteration) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+ void* iteration_cookie;
+ ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, NULL, NULL));
+
+ ZipEntry data;
+ ZipString name;
+
+ // b/c.txt
+ ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
+ AssertNameEquals("b/c.txt", name);
+
+ // b/d.txt
+ ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
+ AssertNameEquals("b/d.txt", name);
+
+ // a.txt
+ ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
+ AssertNameEquals("a.txt", name);
+
+ // b.txt
+ ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
+ AssertNameEquals("b.txt", name);
+
+ // b/
+ ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
+ AssertNameEquals("b/", name);
+
+ // End of iteration.
+ ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
+
+ CloseArchive(handle);
+}
+
+TEST(ziparchive, IterationWithPrefix) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+ void* iteration_cookie;
+ ZipString prefix("b/");
+ ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, &prefix, NULL));
+
+ ZipEntry data;
+ ZipString name;
+
+ // b/c.txt
+ ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
+ AssertNameEquals("b/c.txt", name);
+
+ // b/d.txt
+ ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
+ AssertNameEquals("b/d.txt", name);
+
+ // b/
+ ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
+ AssertNameEquals("b/", name);
+
+ // End of iteration.
+ ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
+
+ CloseArchive(handle);
+}
+
+TEST(ziparchive, IterationWithSuffix) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+ void* iteration_cookie;
+ ZipString suffix(".txt");
+ ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, NULL, &suffix));
+
+ ZipEntry data;
+ ZipString name;
+
+ // b/c.txt
+ ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
+ AssertNameEquals("b/c.txt", name);
+
+ // b/d.txt
+ ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
+ AssertNameEquals("b/d.txt", name);
+
+ // a.txt
+ ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
+ AssertNameEquals("a.txt", name);
+
+ // b.txt
+ ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
+ AssertNameEquals("b.txt", name);
+
+ // End of iteration.
+ ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
+
+ CloseArchive(handle);
+}
+
+TEST(ziparchive, IterationWithPrefixAndSuffix) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+ void* iteration_cookie;
+ ZipString prefix("b");
+ ZipString suffix(".txt");
+ ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, &prefix, &suffix));
+
+ ZipEntry data;
+ ZipString name;
+
+ // b/c.txt
+ ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
+ AssertNameEquals("b/c.txt", name);
+
+ // b/d.txt
+ ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
+ AssertNameEquals("b/d.txt", name);
+
+ // b.txt
+ ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
+ AssertNameEquals("b.txt", name);
+
+ // End of iteration.
+ ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
+
+ CloseArchive(handle);
+}
+
+TEST(ziparchive, IterationWithBadPrefixAndSuffix) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+ void* iteration_cookie;
+ ZipString prefix("x");
+ ZipString suffix("y");
+ ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, &prefix, &suffix));
+
+ ZipEntry data;
+ ZipString name;
+
+ // End of iteration.
+ ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
+
+ CloseArchive(handle);
+}
+
+TEST(ziparchive, FindEntry) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+ ZipEntry data;
+ ZipString name;
+ name.name = kATxtName;
+ name.name_length = kATxtNameLength;
+ ASSERT_EQ(0, FindEntry(handle, name, &data));
+
+ // Known facts about a.txt, from zipinfo -v.
+ ASSERT_EQ(63, data.offset);
+ ASSERT_EQ(kCompressDeflated, data.method);
+ ASSERT_EQ(static_cast<uint32_t>(17), data.uncompressed_length);
+ ASSERT_EQ(static_cast<uint32_t>(13), data.compressed_length);
+ ASSERT_EQ(0x950821c5, data.crc32);
+
+ // An entry that doesn't exist. Should be a negative return code.
+ ZipString absent_name;
+ absent_name.name = kNonexistentTxtName;
+ absent_name.name_length = kNonexistentTxtNameLength;
+ ASSERT_LT(FindEntry(handle, absent_name, &data), 0);
+
+ CloseArchive(handle);
+}
+
+TEST(ziparchive, TestInvalidDeclaredLength) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveWrapper("declaredlength.zip", &handle));
+
+ void* iteration_cookie;
+ ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, NULL, NULL));
+
+ ZipString name;
+ ZipEntry data;
+
+ ASSERT_EQ(Next(iteration_cookie, &data, &name), 0);
+ ASSERT_EQ(Next(iteration_cookie, &data, &name), 0);
+
+ CloseArchive(handle);
+}
+
+TEST(ziparchive, ExtractToMemory) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+ // An entry that's deflated.
+ ZipEntry data;
+ ZipString a_name;
+ a_name.name = kATxtName;
+ a_name.name_length = kATxtNameLength;
+ ASSERT_EQ(0, FindEntry(handle, a_name, &data));
+ const uint32_t a_size = data.uncompressed_length;
+ ASSERT_EQ(a_size, sizeof(kATxtContents));
+ uint8_t* buffer = new uint8_t[a_size];
+ ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, a_size));
+ ASSERT_EQ(0, memcmp(buffer, kATxtContents, a_size));
+ delete[] buffer;
+
+ // An entry that's stored.
+ ZipString b_name;
+ b_name.name = kBTxtName;
+ b_name.name_length = kBTxtNameLength;
+ ASSERT_EQ(0, FindEntry(handle, b_name, &data));
+ const uint32_t b_size = data.uncompressed_length;
+ ASSERT_EQ(b_size, sizeof(kBTxtContents));
+ buffer = new uint8_t[b_size];
+ ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, b_size));
+ ASSERT_EQ(0, memcmp(buffer, kBTxtContents, b_size));
+ delete[] buffer;
+
+ CloseArchive(handle);
+}
+
+static const uint32_t kEmptyEntriesZip[] = {
+ 0x04034b50, 0x0000000a, 0x63600000, 0x00004438, 0x00000000, 0x00000000,
+ 0x00090000, 0x6d65001c, 0x2e797470, 0x55747874, 0x03000954, 0x52e25c13,
+ 0x52e25c24, 0x000b7875, 0x42890401, 0x88040000, 0x50000013, 0x1e02014b,
+ 0x00000a03, 0x60000000, 0x00443863, 0x00000000, 0x00000000, 0x09000000,
+ 0x00001800, 0x00000000, 0xa0000000, 0x00000081, 0x706d6500, 0x742e7974,
+ 0x54557478, 0x13030005, 0x7552e25c, 0x01000b78, 0x00428904, 0x13880400,
+ 0x4b500000, 0x00000605, 0x00010000, 0x004f0001, 0x00430000, 0x00000000 };
+
+// This is a zip file containing a single entry (ab.txt) that contains
+// 90072 repetitions of the string "ab\n" and has an uncompressed length
+// of 270216 bytes.
+static const uint16_t kAbZip[] = {
+ 0x4b50, 0x0403, 0x0014, 0x0000, 0x0008, 0x51d2, 0x4698, 0xc4b0,
+ 0x2cda, 0x011b, 0x0000, 0x1f88, 0x0004, 0x0006, 0x001c, 0x6261,
+ 0x742e, 0x7478, 0x5455, 0x0009, 0x7c03, 0x3a09, 0x7c55, 0x3a09,
+ 0x7555, 0x0b78, 0x0100, 0x8904, 0x0042, 0x0400, 0x1388, 0x0000,
+ 0xc2ed, 0x0d31, 0x0000, 0x030c, 0x7fa0, 0x3b2e, 0x22ff, 0xa2aa,
+ 0x841f, 0x45fc, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0xdd55, 0x502c, 0x014b, 0x1e02,
+ 0x1403, 0x0000, 0x0800, 0xd200, 0x9851, 0xb046, 0xdac4, 0x1b2c,
+ 0x0001, 0x8800, 0x041f, 0x0600, 0x1800, 0x0000, 0x0000, 0x0100,
+ 0x0000, 0xa000, 0x0081, 0x0000, 0x6100, 0x2e62, 0x7874, 0x5574,
+ 0x0554, 0x0300, 0x097c, 0x553a, 0x7875, 0x000b, 0x0401, 0x4289,
+ 0x0000, 0x8804, 0x0013, 0x5000, 0x054b, 0x0006, 0x0000, 0x0100,
+ 0x0100, 0x4c00, 0x0000, 0x5b00, 0x0001, 0x0000, 0x0000
+};
+
+static const uint8_t kAbTxtName[] = { 'a', 'b', '.', 't', 'x', 't' };
+static const uint16_t kAbTxtNameLength = sizeof(kAbTxtName);
+static const size_t kAbUncompressedSize = 270216;
+
+static int make_temporary_file(const char* file_name_pattern) {
+ char full_path[1024];
+ // Account for differences between the host and the target.
+ //
+ // TODO: Maybe reuse bionic/tests/TemporaryFile.h.
+ snprintf(full_path, sizeof(full_path), "/data/local/tmp/%s", file_name_pattern);
+ int fd = mkstemp(full_path);
+ if (fd == -1) {
+ snprintf(full_path, sizeof(full_path), "/tmp/%s", file_name_pattern);
+ fd = mkstemp(full_path);
+ }
+
+ return fd;
+}
+
+TEST(ziparchive, EmptyEntries) {
+ char temp_file_pattern[] = "empty_entries_test_XXXXXX";
+ int fd = make_temporary_file(temp_file_pattern);
+ ASSERT_NE(-1, fd);
+ const ssize_t file_size = sizeof(kEmptyEntriesZip);
+ ASSERT_EQ(file_size, TEMP_FAILURE_RETRY(write(fd, kEmptyEntriesZip, file_size)));
+
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveFd(fd, "EmptyEntriesTest", &handle));
+
+ ZipEntry entry;
+ ZipString empty_name;
+ empty_name.name = kEmptyTxtName;
+ empty_name.name_length = kEmptyTxtNameLength;
+ ASSERT_EQ(0, FindEntry(handle, empty_name, &entry));
+ ASSERT_EQ(static_cast<uint32_t>(0), entry.uncompressed_length);
+ uint8_t buffer[1];
+ ASSERT_EQ(0, ExtractToMemory(handle, &entry, buffer, 1));
+
+ char output_file_pattern[] = "empty_entries_output_XXXXXX";
+ int output_fd = make_temporary_file(output_file_pattern);
+ ASSERT_NE(-1, output_fd);
+ ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, output_fd));
+
+ struct stat stat_buf;
+ ASSERT_EQ(0, fstat(output_fd, &stat_buf));
+ ASSERT_EQ(0, stat_buf.st_size);
+
+ close(fd);
+ close(output_fd);
+}
+
+TEST(ziparchive, EntryLargerThan32K) {
+ char temp_file_pattern[] = "entry_larger_than_32k_test_XXXXXX";
+ int fd = make_temporary_file(temp_file_pattern);
+ ASSERT_NE(-1, fd);
+ ASSERT_TRUE(android::base::WriteFully(fd, reinterpret_cast<const uint8_t*>(kAbZip),
+ sizeof(kAbZip) - 1));
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveFd(fd, "EntryLargerThan32KTest", &handle));
+
+ ZipEntry entry;
+ ZipString ab_name;
+ ab_name.name = kAbTxtName;
+ ab_name.name_length = kAbTxtNameLength;
+ ASSERT_EQ(0, FindEntry(handle, ab_name, &entry));
+ ASSERT_EQ(kAbUncompressedSize, entry.uncompressed_length);
+
+ // Extract the entry to memory.
+ std::vector<uint8_t> buffer(kAbUncompressedSize);
+ ASSERT_EQ(0, ExtractToMemory(handle, &entry, &buffer[0], buffer.size()));
+
+ // Extract the entry to a file.
+ char output_file_pattern[] = "entry_larger_than_32k_test_output_XXXXXX";
+ int output_fd = make_temporary_file(output_file_pattern);
+ ASSERT_NE(-1, output_fd);
+ ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, output_fd));
+
+ // Make sure the extracted file size is as expected.
+ struct stat stat_buf;
+ ASSERT_EQ(0, fstat(output_fd, &stat_buf));
+ ASSERT_EQ(kAbUncompressedSize, static_cast<size_t>(stat_buf.st_size));
+
+ // Read the file back to a buffer and make sure the contents are
+ // the same as the memory buffer we extracted directly to.
+ std::vector<uint8_t> file_contents(kAbUncompressedSize);
+ ASSERT_EQ(0, lseek64(output_fd, 0, SEEK_SET));
+ ASSERT_TRUE(android::base::ReadFully(output_fd, &file_contents[0], file_contents.size()));
+ ASSERT_EQ(file_contents, buffer);
+
+ for (int i = 0; i < 90072; ++i) {
+ const uint8_t* line = &file_contents[0] + (3 * i);
+ ASSERT_EQ('a', line[0]);
+ ASSERT_EQ('b', line[1]);
+ ASSERT_EQ('\n', line[2]);
+ }
+
+ close(fd);
+ close(output_fd);
+}
+
+TEST(ziparchive, TrailerAfterEOCD) {
+ char temp_file_pattern[] = "trailer_after_eocd_test_XXXXXX";
+ int fd = make_temporary_file(temp_file_pattern);
+ ASSERT_NE(-1, fd);
+
+ // Create a file with 8 bytes of random garbage.
+ static const uint8_t trailer[] = { 'A' ,'n', 'd', 'r', 'o', 'i', 'd', 'z' };
+ const ssize_t file_size = sizeof(kEmptyEntriesZip);
+ const ssize_t trailer_size = sizeof(trailer);
+ ASSERT_EQ(file_size, TEMP_FAILURE_RETRY(write(fd, kEmptyEntriesZip, file_size)));
+ ASSERT_EQ(trailer_size, TEMP_FAILURE_RETRY(write(fd, trailer, trailer_size)));
+
+ ZipArchiveHandle handle;
+ ASSERT_GT(0, OpenArchiveFd(fd, "EmptyEntriesTest", &handle));
+}
+
+TEST(ziparchive, ExtractToFile) {
+ char kTempFilePattern[] = "zip_archive_input_XXXXXX";
+ int fd = make_temporary_file(kTempFilePattern);
+ ASSERT_NE(-1, fd);
+ const uint8_t data[8] = { '1', '2', '3', '4', '5', '6', '7', '8' };
+ const ssize_t data_size = sizeof(data);
+
+ ASSERT_EQ(data_size, TEMP_FAILURE_RETRY(write(fd, data, data_size)));
+
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+ ZipEntry entry;
+ ZipString name;
+ name.name = kATxtName;
+ name.name_length = kATxtNameLength;
+ ASSERT_EQ(0, FindEntry(handle, name, &entry));
+ ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, fd));
+
+
+ // Assert that the first 8 bytes of the file haven't been clobbered.
+ uint8_t read_buffer[data_size];
+ ASSERT_EQ(0, lseek64(fd, 0, SEEK_SET));
+ ASSERT_EQ(data_size, TEMP_FAILURE_RETRY(read(fd, read_buffer, data_size)));
+ ASSERT_EQ(0, memcmp(read_buffer, data, data_size));
+
+ // Assert that the remainder of the file contains the incompressed data.
+ std::vector<uint8_t> uncompressed_data(entry.uncompressed_length);
+ ASSERT_EQ(static_cast<ssize_t>(entry.uncompressed_length),
+ TEMP_FAILURE_RETRY(
+ read(fd, &uncompressed_data[0], entry.uncompressed_length)));
+ ASSERT_EQ(0, memcmp(&uncompressed_data[0], kATxtContents,
+ sizeof(kATxtContents)));
+
+ // Assert that the total length of the file is sane
+ ASSERT_EQ(data_size + static_cast<ssize_t>(sizeof(kATxtContents)),
+ lseek64(fd, 0, SEEK_END));
+
+ close(fd);
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+
+ static struct option options[] = {
+ { "test_data_dir", required_argument, NULL, 't' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ while (true) {
+ int option_index;
+ const int c = getopt_long_only(argc, argv, "", options, &option_index);
+ if (c == -1) {
+ break;
+ }
+
+ if (c == 't') {
+ test_data_dir = optarg;
+ }
+ }
+
+ if (test_data_dir.size() == 0) {
+ printf("Test data flag (--test_data_dir) required\n\n");
+ return -1;
+ }
+
+ if (test_data_dir[0] != '/') {
+ printf("Test data must be an absolute path, was %s\n\n",
+ test_data_dir.c_str());
+ return -2;
+ }
+
+ return RUN_ALL_TESTS();
+}
diff --git a/lmkd/Android.mk b/lmkd/Android.mk
new file mode 100644
index 0000000..39081d6
--- /dev/null
+++ b/lmkd/Android.mk
@@ -0,0 +1,10 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := lmkd.c
+LOCAL_SHARED_LIBRARIES := liblog libm libc libprocessgroup
+LOCAL_CFLAGS := -Werror
+
+LOCAL_MODULE := lmkd
+
+include $(BUILD_EXECUTABLE)
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
new file mode 100644
index 0000000..7bbc811
--- /dev/null
+++ b/lmkd/lmkd.c
@@ -0,0 +1,831 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "lowmemorykiller"
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/cdefs.h>
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <cutils/sockets.h>
+#include <log/log.h>
+#include <processgroup/processgroup.h>
+
+#ifndef __unused
+#define __unused __attribute__((__unused__))
+#endif
+
+#define MEMCG_SYSFS_PATH "/dev/memcg/"
+#define MEMPRESSURE_WATCH_LEVEL "medium"
+#define ZONEINFO_PATH "/proc/zoneinfo"
+#define LINE_MAX 128
+
+#define INKERNEL_MINFREE_PATH "/sys/module/lowmemorykiller/parameters/minfree"
+#define INKERNEL_ADJ_PATH "/sys/module/lowmemorykiller/parameters/adj"
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
+
+enum lmk_cmd {
+ LMK_TARGET,
+ LMK_PROCPRIO,
+ LMK_PROCREMOVE,
+};
+
+#define MAX_TARGETS 6
+/*
+ * longest is LMK_TARGET followed by MAX_TARGETS each minfree and minkillprio
+ * values
+ */
+#define CTRL_PACKET_MAX (sizeof(int) * (MAX_TARGETS * 2 + 1))
+
+/* default to old in-kernel interface if no memory pressure events */
+static int use_inkernel_interface = 1;
+
+/* memory pressure level medium event */
+static int mpevfd;
+
+/* control socket listen and data */
+static int ctrl_lfd;
+static int ctrl_dfd = -1;
+static int ctrl_dfd_reopened; /* did we reopen ctrl conn on this loop? */
+
+/* 1 memory pressure level, 1 ctrl listen socket, 1 ctrl data socket */
+#define MAX_EPOLL_EVENTS 3
+static int epollfd;
+static int maxevents;
+
+#define OOM_DISABLE (-17)
+/* inclusive */
+#define OOM_ADJUST_MIN (-16)
+#define OOM_ADJUST_MAX 15
+
+/* kernel OOM score values */
+#define OOM_SCORE_ADJ_MIN (-1000)
+#define OOM_SCORE_ADJ_MAX 1000
+
+static int lowmem_adj[MAX_TARGETS];
+static int lowmem_minfree[MAX_TARGETS];
+static int lowmem_targets_size;
+
+struct sysmeminfo {
+ int nr_free_pages;
+ int nr_file_pages;
+ int nr_shmem;
+ int totalreserve_pages;
+};
+
+struct adjslot_list {
+ struct adjslot_list *next;
+ struct adjslot_list *prev;
+};
+
+struct proc {
+ struct adjslot_list asl;
+ int pid;
+ uid_t uid;
+ int oomadj;
+ struct proc *pidhash_next;
+};
+
+#define PIDHASH_SZ 1024
+static struct proc *pidhash[PIDHASH_SZ];
+#define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1))
+
+#define ADJTOSLOT(adj) (adj + -OOM_ADJUST_MIN)
+static struct adjslot_list procadjslot_list[ADJTOSLOT(OOM_ADJUST_MAX) + 1];
+
+/*
+ * Wait 1-2 seconds for the death report of a killed process prior to
+ * considering killing more processes.
+ */
+#define KILL_TIMEOUT 2
+/* Time of last process kill we initiated, stop me before I kill again */
+static time_t kill_lasttime;
+
+/* PAGE_SIZE / 1024 */
+static long page_k;
+
+static ssize_t read_all(int fd, char *buf, size_t max_len)
+{
+ ssize_t ret = 0;
+
+ while (max_len > 0) {
+ ssize_t r = read(fd, buf, max_len);
+ if (r == 0) {
+ break;
+ }
+ if (r == -1) {
+ return -1;
+ }
+ ret += r;
+ buf += r;
+ max_len -= r;
+ }
+
+ return ret;
+}
+
+static int lowmem_oom_adj_to_oom_score_adj(int oom_adj)
+{
+ if (oom_adj == OOM_ADJUST_MAX)
+ return OOM_SCORE_ADJ_MAX;
+ else
+ return (oom_adj * OOM_SCORE_ADJ_MAX) / -OOM_DISABLE;
+}
+
+static struct proc *pid_lookup(int pid) {
+ struct proc *procp;
+
+ for (procp = pidhash[pid_hashfn(pid)]; procp && procp->pid != pid;
+ procp = procp->pidhash_next)
+ ;
+
+ return procp;
+}
+
+static void adjslot_insert(struct adjslot_list *head, struct adjslot_list *new)
+{
+ struct adjslot_list *next = head->next;
+ new->prev = head;
+ new->next = next;
+ next->prev = new;
+ head->next = new;
+}
+
+static void adjslot_remove(struct adjslot_list *old)
+{
+ struct adjslot_list *prev = old->prev;
+ struct adjslot_list *next = old->next;
+ next->prev = prev;
+ prev->next = next;
+}
+
+static struct adjslot_list *adjslot_tail(struct adjslot_list *head) {
+ struct adjslot_list *asl = head->prev;
+
+ return asl == head ? NULL : asl;
+}
+
+static void proc_slot(struct proc *procp) {
+ int adjslot = ADJTOSLOT(procp->oomadj);
+
+ adjslot_insert(&procadjslot_list[adjslot], &procp->asl);
+}
+
+static void proc_unslot(struct proc *procp) {
+ adjslot_remove(&procp->asl);
+}
+
+static void proc_insert(struct proc *procp) {
+ int hval = pid_hashfn(procp->pid);
+
+ procp->pidhash_next = pidhash[hval];
+ pidhash[hval] = procp;
+ proc_slot(procp);
+}
+
+static int pid_remove(int pid) {
+ int hval = pid_hashfn(pid);
+ struct proc *procp;
+ struct proc *prevp;
+
+ for (procp = pidhash[hval], prevp = NULL; procp && procp->pid != pid;
+ procp = procp->pidhash_next)
+ prevp = procp;
+
+ if (!procp)
+ return -1;
+
+ if (!prevp)
+ pidhash[hval] = procp->pidhash_next;
+ else
+ prevp->pidhash_next = procp->pidhash_next;
+
+ proc_unslot(procp);
+ free(procp);
+ return 0;
+}
+
+static void writefilestring(char *path, char *s) {
+ int fd = open(path, O_WRONLY);
+ int len = strlen(s);
+ int ret;
+
+ if (fd < 0) {
+ ALOGE("Error opening %s; errno=%d", path, errno);
+ return;
+ }
+
+ ret = write(fd, s, len);
+ if (ret < 0) {
+ ALOGE("Error writing %s; errno=%d", path, errno);
+ } else if (ret < len) {
+ ALOGE("Short write on %s; length=%d", path, ret);
+ }
+
+ close(fd);
+}
+
+static void cmd_procprio(int pid, int uid, int oomadj) {
+ struct proc *procp;
+ char path[80];
+ char val[20];
+
+ if (oomadj < OOM_DISABLE || oomadj > OOM_ADJUST_MAX) {
+ ALOGE("Invalid PROCPRIO oomadj argument %d", oomadj);
+ return;
+ }
+
+ snprintf(path, sizeof(path), "/proc/%d/oom_score_adj", pid);
+ snprintf(val, sizeof(val), "%d", lowmem_oom_adj_to_oom_score_adj(oomadj));
+ writefilestring(path, val);
+
+ if (use_inkernel_interface)
+ return;
+
+ procp = pid_lookup(pid);
+ if (!procp) {
+ procp = malloc(sizeof(struct proc));
+ if (!procp) {
+ // Oh, the irony. May need to rebuild our state.
+ return;
+ }
+
+ procp->pid = pid;
+ procp->uid = uid;
+ procp->oomadj = oomadj;
+ proc_insert(procp);
+ } else {
+ proc_unslot(procp);
+ procp->oomadj = oomadj;
+ proc_slot(procp);
+ }
+}
+
+static void cmd_procremove(int pid) {
+ if (use_inkernel_interface)
+ return;
+
+ pid_remove(pid);
+ kill_lasttime = 0;
+}
+
+static void cmd_target(int ntargets, int *params) {
+ int i;
+
+ if (ntargets > (int)ARRAY_SIZE(lowmem_adj))
+ return;
+
+ for (i = 0; i < ntargets; i++) {
+ lowmem_minfree[i] = ntohl(*params++);
+ lowmem_adj[i] = ntohl(*params++);
+ }
+
+ lowmem_targets_size = ntargets;
+
+ if (use_inkernel_interface) {
+ char minfreestr[128];
+ char killpriostr[128];
+
+ minfreestr[0] = '\0';
+ killpriostr[0] = '\0';
+
+ for (i = 0; i < lowmem_targets_size; i++) {
+ char val[40];
+
+ if (i) {
+ strlcat(minfreestr, ",", sizeof(minfreestr));
+ strlcat(killpriostr, ",", sizeof(killpriostr));
+ }
+
+ snprintf(val, sizeof(val), "%d", lowmem_minfree[i]);
+ strlcat(minfreestr, val, sizeof(minfreestr));
+ snprintf(val, sizeof(val), "%d", lowmem_adj[i]);
+ strlcat(killpriostr, val, sizeof(killpriostr));
+ }
+
+ writefilestring(INKERNEL_MINFREE_PATH, minfreestr);
+ writefilestring(INKERNEL_ADJ_PATH, killpriostr);
+ }
+}
+
+static void ctrl_data_close(void) {
+ ALOGI("Closing Activity Manager data connection");
+ close(ctrl_dfd);
+ ctrl_dfd = -1;
+ maxevents--;
+}
+
+static int ctrl_data_read(char *buf, size_t bufsz) {
+ int ret = 0;
+
+ ret = read(ctrl_dfd, buf, bufsz);
+
+ if (ret == -1) {
+ ALOGE("control data socket read failed; errno=%d", errno);
+ } else if (ret == 0) {
+ ALOGE("Got EOF on control data socket");
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static void ctrl_command_handler(void) {
+ int ibuf[CTRL_PACKET_MAX / sizeof(int)];
+ int len;
+ int cmd = -1;
+ int nargs;
+ int targets;
+
+ len = ctrl_data_read((char *)ibuf, CTRL_PACKET_MAX);
+ if (len <= 0)
+ return;
+
+ nargs = len / sizeof(int) - 1;
+ if (nargs < 0)
+ goto wronglen;
+
+ cmd = ntohl(ibuf[0]);
+
+ switch(cmd) {
+ case LMK_TARGET:
+ targets = nargs / 2;
+ if (nargs & 0x1 || targets > (int)ARRAY_SIZE(lowmem_adj))
+ goto wronglen;
+ cmd_target(targets, &ibuf[1]);
+ break;
+ case LMK_PROCPRIO:
+ if (nargs != 3)
+ goto wronglen;
+ cmd_procprio(ntohl(ibuf[1]), ntohl(ibuf[2]), ntohl(ibuf[3]));
+ break;
+ case LMK_PROCREMOVE:
+ if (nargs != 1)
+ goto wronglen;
+ cmd_procremove(ntohl(ibuf[1]));
+ break;
+ default:
+ ALOGE("Received unknown command code %d", cmd);
+ return;
+ }
+
+ return;
+
+wronglen:
+ ALOGE("Wrong control socket read length cmd=%d len=%d", cmd, len);
+}
+
+static void ctrl_data_handler(uint32_t events) {
+ if (events & EPOLLHUP) {
+ ALOGI("ActivityManager disconnected");
+ if (!ctrl_dfd_reopened)
+ ctrl_data_close();
+ } else if (events & EPOLLIN) {
+ ctrl_command_handler();
+ }
+}
+
+static void ctrl_connect_handler(uint32_t events __unused) {
+ struct sockaddr addr;
+ socklen_t alen;
+ struct epoll_event epev;
+
+ if (ctrl_dfd >= 0) {
+ ctrl_data_close();
+ ctrl_dfd_reopened = 1;
+ }
+
+ alen = sizeof(addr);
+ ctrl_dfd = accept(ctrl_lfd, &addr, &alen);
+
+ if (ctrl_dfd < 0) {
+ ALOGE("lmkd control socket accept failed; errno=%d", errno);
+ return;
+ }
+
+ ALOGI("ActivityManager connected");
+ maxevents++;
+ epev.events = EPOLLIN;
+ epev.data.ptr = (void *)ctrl_data_handler;
+ if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ctrl_dfd, &epev) == -1) {
+ ALOGE("epoll_ctl for data connection socket failed; errno=%d", errno);
+ ctrl_data_close();
+ return;
+ }
+}
+
+static int zoneinfo_parse_protection(char *cp) {
+ int max = 0;
+ int zoneval;
+ char *save_ptr;
+
+ for (cp = strtok_r(cp, "(), ", &save_ptr); cp; cp = strtok_r(NULL, "), ", &save_ptr)) {
+ zoneval = strtol(cp, &cp, 0);
+ if (zoneval > max)
+ max = zoneval;
+ }
+
+ return max;
+}
+
+static void zoneinfo_parse_line(char *line, struct sysmeminfo *mip) {
+ char *cp = line;
+ char *ap;
+ char *save_ptr;
+
+ cp = strtok_r(line, " ", &save_ptr);
+ if (!cp)
+ return;
+
+ ap = strtok_r(NULL, " ", &save_ptr);
+ if (!ap)
+ return;
+
+ if (!strcmp(cp, "nr_free_pages"))
+ mip->nr_free_pages += strtol(ap, NULL, 0);
+ else if (!strcmp(cp, "nr_file_pages"))
+ mip->nr_file_pages += strtol(ap, NULL, 0);
+ else if (!strcmp(cp, "nr_shmem"))
+ mip->nr_shmem += strtol(ap, NULL, 0);
+ else if (!strcmp(cp, "high"))
+ mip->totalreserve_pages += strtol(ap, NULL, 0);
+ else if (!strcmp(cp, "protection:"))
+ mip->totalreserve_pages += zoneinfo_parse_protection(ap);
+}
+
+static int zoneinfo_parse(struct sysmeminfo *mip) {
+ int fd;
+ ssize_t size;
+ char buf[PAGE_SIZE];
+ char *save_ptr;
+ char *line;
+
+ memset(mip, 0, sizeof(struct sysmeminfo));
+
+ fd = open(ZONEINFO_PATH, O_RDONLY);
+ if (fd == -1) {
+ ALOGE("%s open: errno=%d", ZONEINFO_PATH, errno);
+ return -1;
+ }
+
+ size = read_all(fd, buf, sizeof(buf) - 1);
+ if (size < 0) {
+ ALOGE("%s read: errno=%d", ZONEINFO_PATH, errno);
+ close(fd);
+ return -1;
+ }
+ ALOG_ASSERT((size_t)size < sizeof(buf) - 1, "/proc/zoneinfo too large");
+ buf[size] = 0;
+
+ for (line = strtok_r(buf, "\n", &save_ptr); line; line = strtok_r(NULL, "\n", &save_ptr))
+ zoneinfo_parse_line(line, mip);
+
+ close(fd);
+ return 0;
+}
+
+static int proc_get_size(int pid) {
+ char path[PATH_MAX];
+ char line[LINE_MAX];
+ int fd;
+ int rss = 0;
+ int total;
+ ssize_t ret;
+
+ snprintf(path, PATH_MAX, "/proc/%d/statm", pid);
+ fd = open(path, O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ ret = read_all(fd, line, sizeof(line) - 1);
+ if (ret < 0) {
+ close(fd);
+ return -1;
+ }
+
+ sscanf(line, "%d %d ", &total, &rss);
+ close(fd);
+ return rss;
+}
+
+static char *proc_get_name(int pid) {
+ char path[PATH_MAX];
+ static char line[LINE_MAX];
+ int fd;
+ char *cp;
+ ssize_t ret;
+
+ snprintf(path, PATH_MAX, "/proc/%d/cmdline", pid);
+ fd = open(path, O_RDONLY);
+ if (fd == -1)
+ return NULL;
+ ret = read_all(fd, line, sizeof(line) - 1);
+ close(fd);
+ if (ret < 0) {
+ return NULL;
+ }
+
+ cp = strchr(line, ' ');
+ if (cp)
+ *cp = '\0';
+
+ return line;
+}
+
+static struct proc *proc_adj_lru(int oomadj) {
+ return (struct proc *)adjslot_tail(&procadjslot_list[ADJTOSLOT(oomadj)]);
+}
+
+/* Kill one process specified by procp. Returns the size of the process killed */
+static int kill_one_process(struct proc *procp, int other_free, int other_file,
+ int minfree, int min_score_adj, bool first)
+{
+ int pid = procp->pid;
+ uid_t uid = procp->uid;
+ char *taskname;
+ int tasksize;
+ int r;
+
+ taskname = proc_get_name(pid);
+ if (!taskname) {
+ pid_remove(pid);
+ return -1;
+ }
+
+ tasksize = proc_get_size(pid);
+ if (tasksize <= 0) {
+ pid_remove(pid);
+ return -1;
+ }
+
+ ALOGI("Killing '%s' (%d), uid %d, adj %d\n"
+ " to free %ldkB because cache %s%ldkB is below limit %ldkB for oom_adj %d\n"
+ " Free memory is %s%ldkB %s reserved",
+ taskname, pid, uid, procp->oomadj, tasksize * page_k,
+ first ? "" : "~", other_file * page_k, minfree * page_k, min_score_adj,
+ first ? "" : "~", other_free * page_k, other_free >= 0 ? "above" : "below");
+ r = kill(pid, SIGKILL);
+ killProcessGroup(uid, pid, SIGKILL);
+ pid_remove(pid);
+
+ if (r) {
+ ALOGE("kill(%d): errno=%d", procp->pid, errno);
+ return -1;
+ } else {
+ return tasksize;
+ }
+}
+
+/*
+ * Find a process to kill based on the current (possibly estimated) free memory
+ * and cached memory sizes. Returns the size of the killed processes.
+ */
+static int find_and_kill_process(int other_free, int other_file, bool first)
+{
+ int i;
+ int min_score_adj = OOM_ADJUST_MAX + 1;
+ int minfree = 0;
+ int killed_size = 0;
+
+ for (i = 0; i < lowmem_targets_size; i++) {
+ minfree = lowmem_minfree[i];
+ if (other_free < minfree && other_file < minfree) {
+ min_score_adj = lowmem_adj[i];
+ break;
+ }
+ }
+
+ if (min_score_adj == OOM_ADJUST_MAX + 1)
+ return 0;
+
+ for (i = OOM_ADJUST_MAX; i >= min_score_adj; i--) {
+ struct proc *procp;
+
+retry:
+ procp = proc_adj_lru(i);
+
+ if (procp) {
+ killed_size = kill_one_process(procp, other_free, other_file, minfree, min_score_adj, first);
+ if (killed_size < 0) {
+ goto retry;
+ } else {
+ return killed_size;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void mp_event(uint32_t events __unused) {
+ int ret;
+ unsigned long long evcount;
+ struct sysmeminfo mi;
+ int other_free;
+ int other_file;
+ int killed_size;
+ bool first = true;
+
+ ret = read(mpevfd, &evcount, sizeof(evcount));
+ if (ret < 0)
+ ALOGE("Error reading memory pressure event fd; errno=%d",
+ errno);
+
+ if (time(NULL) - kill_lasttime < KILL_TIMEOUT)
+ return;
+
+ while (zoneinfo_parse(&mi) < 0) {
+ // Failed to read /proc/zoneinfo, assume ENOMEM and kill something
+ find_and_kill_process(0, 0, true);
+ }
+
+ other_free = mi.nr_free_pages - mi.totalreserve_pages;
+ other_file = mi.nr_file_pages - mi.nr_shmem;
+
+ do {
+ killed_size = find_and_kill_process(other_free, other_file, first);
+ if (killed_size > 0) {
+ first = false;
+ other_free += killed_size;
+ other_file += killed_size;
+ }
+ } while (killed_size > 0);
+}
+
+static int init_mp(char *levelstr, void *event_handler)
+{
+ int mpfd;
+ int evfd;
+ int evctlfd;
+ char buf[256];
+ struct epoll_event epev;
+ int ret;
+
+ mpfd = open(MEMCG_SYSFS_PATH "memory.pressure_level", O_RDONLY);
+ if (mpfd < 0) {
+ ALOGI("No kernel memory.pressure_level support (errno=%d)", errno);
+ goto err_open_mpfd;
+ }
+
+ evctlfd = open(MEMCG_SYSFS_PATH "cgroup.event_control", O_WRONLY);
+ if (evctlfd < 0) {
+ ALOGI("No kernel memory cgroup event control (errno=%d)", errno);
+ goto err_open_evctlfd;
+ }
+
+ evfd = eventfd(0, EFD_NONBLOCK);
+ if (evfd < 0) {
+ ALOGE("eventfd failed for level %s; errno=%d", levelstr, errno);
+ goto err_eventfd;
+ }
+
+ ret = snprintf(buf, sizeof(buf), "%d %d %s", evfd, mpfd, levelstr);
+ if (ret >= (ssize_t)sizeof(buf)) {
+ ALOGE("cgroup.event_control line overflow for level %s", levelstr);
+ goto err;
+ }
+
+ ret = write(evctlfd, buf, strlen(buf) + 1);
+ if (ret == -1) {
+ ALOGE("cgroup.event_control write failed for level %s; errno=%d",
+ levelstr, errno);
+ goto err;
+ }
+
+ epev.events = EPOLLIN;
+ epev.data.ptr = event_handler;
+ ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, evfd, &epev);
+ if (ret == -1) {
+ ALOGE("epoll_ctl for level %s failed; errno=%d", levelstr, errno);
+ goto err;
+ }
+ maxevents++;
+ mpevfd = evfd;
+ return 0;
+
+err:
+ close(evfd);
+err_eventfd:
+ close(evctlfd);
+err_open_evctlfd:
+ close(mpfd);
+err_open_mpfd:
+ return -1;
+}
+
+static int init(void) {
+ struct epoll_event epev;
+ int i;
+ int ret;
+
+ page_k = sysconf(_SC_PAGESIZE);
+ if (page_k == -1)
+ page_k = PAGE_SIZE;
+ page_k /= 1024;
+
+ epollfd = epoll_create(MAX_EPOLL_EVENTS);
+ if (epollfd == -1) {
+ ALOGE("epoll_create failed (errno=%d)", errno);
+ return -1;
+ }
+
+ ctrl_lfd = android_get_control_socket("lmkd");
+ if (ctrl_lfd < 0) {
+ ALOGE("get lmkd control socket failed");
+ return -1;
+ }
+
+ ret = listen(ctrl_lfd, 1);
+ if (ret < 0) {
+ ALOGE("lmkd control socket listen failed (errno=%d)", errno);
+ return -1;
+ }
+
+ epev.events = EPOLLIN;
+ epev.data.ptr = (void *)ctrl_connect_handler;
+ if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ctrl_lfd, &epev) == -1) {
+ ALOGE("epoll_ctl for lmkd control socket failed (errno=%d)", errno);
+ return -1;
+ }
+ maxevents++;
+
+ use_inkernel_interface = !access(INKERNEL_MINFREE_PATH, W_OK);
+
+ if (use_inkernel_interface) {
+ ALOGI("Using in-kernel low memory killer interface");
+ } else {
+ ret = init_mp(MEMPRESSURE_WATCH_LEVEL, (void *)&mp_event);
+ if (ret)
+ ALOGE("Kernel does not support memory pressure events or in-kernel low memory killer");
+ }
+
+ for (i = 0; i <= ADJTOSLOT(OOM_ADJUST_MAX); i++) {
+ procadjslot_list[i].next = &procadjslot_list[i];
+ procadjslot_list[i].prev = &procadjslot_list[i];
+ }
+
+ return 0;
+}
+
+static void mainloop(void) {
+ while (1) {
+ struct epoll_event events[maxevents];
+ int nevents;
+ int i;
+
+ ctrl_dfd_reopened = 0;
+ nevents = epoll_wait(epollfd, events, maxevents, -1);
+
+ if (nevents == -1) {
+ if (errno == EINTR)
+ continue;
+ ALOGE("epoll_wait failed (errno=%d)", errno);
+ continue;
+ }
+
+ for (i = 0; i < nevents; ++i) {
+ if (events[i].events & EPOLLERR)
+ ALOGD("EPOLLERR on event #%d", i);
+ if (events[i].data.ptr)
+ (*(void (*)(uint32_t))events[i].data.ptr)(events[i].events);
+ }
+ }
+}
+
+int main(int argc __unused, char **argv __unused) {
+ struct sched_param param = {
+ .sched_priority = 1,
+ };
+
+ mlockall(MCL_FUTURE);
+ sched_setscheduler(0, SCHED_FIFO, ¶m);
+ if (!init())
+ mainloop();
+
+ ALOGI("exiting");
+ return 0;
+}
diff --git a/logcat/Android.mk b/logcat/Android.mk
new file mode 100644
index 0000000..7115f9b
--- /dev/null
+++ b/logcat/Android.mk
@@ -0,0 +1,16 @@
+# Copyright 2006-2014 The Android Open Source Project
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= logcat.cpp event.logtags
+
+LOCAL_SHARED_LIBRARIES := liblog libbase libcutils
+
+LOCAL_MODULE := logcat
+
+LOCAL_CFLAGS := -Werror
+
+include $(BUILD_EXECUTABLE)
+
+include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/logcat/MODULE_LICENSE_APACHE2 b/logcat/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/logcat/MODULE_LICENSE_APACHE2
diff --git a/logcat/NOTICE b/logcat/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/logcat/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/logcat/event.logtags b/logcat/event.logtags
new file mode 100644
index 0000000..909f8e2
--- /dev/null
+++ b/logcat/event.logtags
@@ -0,0 +1,141 @@
+# The entries in this file map a sparse set of log tag numbers to tag names.
+# This is installed on the device, in /system/etc, and parsed by logcat.
+#
+# Tag numbers are decimal integers, from 0 to 2^31. (Let's leave the
+# negative values alone for now.)
+#
+# Tag names are one or more ASCII letters and numbers or underscores, i.e.
+# "[A-Z][a-z][0-9]_". Do not include spaces or punctuation (the former
+# impacts log readability, the latter makes regex searches more annoying).
+#
+# Tag numbers and names are separated by whitespace. Blank lines and lines
+# starting with '#' are ignored.
+#
+# Optionally, after the tag names can be put a description for the value(s)
+# of the tag. Description are in the format
+# (<name>|data type[|data unit])
+# Multiple values are separated by commas.
+#
+# The data type is a number from the following values:
+# 1: int
+# 2: long
+# 3: string
+# 4: list
+# 5: float
+#
+# The data unit is a number taken from the following list:
+# 1: Number of objects
+# 2: Number of bytes
+# 3: Number of milliseconds
+# 4: Number of allocations
+# 5: Id
+# 6: Percent
+# Default value for data of type int/long is 2 (bytes).
+#
+# TODO: generate ".java" and ".h" files with integer constants from this file.
+
+# These are used for testing, do not modify without updating
+# tests/framework-tests/src/android/util/EventLogFunctionalTest.java.
+42 answer (to life the universe etc|3)
+314 pi
+2718 e
+
+# "account" is the java hash of the account name
+2720 sync (id|3),(event|1|5),(source|1|5),(account|1|5)
+
+# This event is logged when the location service uploads location data.
+2740 location_controller
+# This event is logged when someone is deciding to force a garbage collection
+2741 force_gc (reason|3)
+# This event is logged on each tickle
+2742 tickle (authority|3)
+
+# contacts aggregation: time and number of contacts.
+# count is negative for query phase, positive for merge phase
+2747 contacts_aggregation (aggregation time|2|3), (count|1|1)
+
+# Device boot timings. We include monotonic clock values because the
+# intrinsic event log times are wall-clock.
+#
+# Runtime starts:
+3000 boot_progress_start (time|2|3)
+# ZygoteInit class preloading starts:
+3020 boot_progress_preload_start (time|2|3)
+# ZygoteInit class preloading ends:
+3030 boot_progress_preload_end (time|2|3)
+
+# Dalvik VM
+20003 dvm_lock_sample (process|3),(main|1|5),(thread|3),(time|1|3),(file|3),(line|1|5),(ownerfile|3),(ownerline|1|5),(sample_percent|1|6)
+
+75000 sqlite_mem_alarm_current (current|1|2)
+75001 sqlite_mem_alarm_max (max|1|2)
+75002 sqlite_mem_alarm_alloc_attempt (attempts|1|4)
+75003 sqlite_mem_released (Memory released|1|2)
+75004 sqlite_db_corrupt (Database file corrupt|3)
+
+50000 menu_item_selected (Menu type where 0 is options and 1 is context|1|5),(Menu item title|3)
+50001 menu_opened (Menu type where 0 is options and 1 is context|1|5)
+
+# HSM wifi state change
+# Hierarchical state class name (as defined in WifiStateTracker.java)
+# Logged on every state change in the hierarchical state machine
+50021 wifi_state_changed (wifi_state|3)
+# HSM wifi event
+# [31-16] Reserved for future use
+# [15 - 0] HSM event (as defined in WifiStateTracker.java)
+# Logged when an event is handled in a hierarchical state
+50022 wifi_event_handled (wifi_event|1|5)
+# Supplicant state change
+# [31-13] Reserved for future use
+# [8 - 0] Supplicant state (as defined in SupplicantState.java)
+# Logged when the supplicant switches to a new state
+50023 wifi_supplicant_state_changed (supplicant_state|1|5)
+
+# Database operation samples.
+# db: the filename of the database
+# sql: the executed query (without query args)
+# time: cpu time millis (not wall time), including lock acquisition
+# blocking_package: if this is on a main thread, the package name, otherwise ""
+# sample_percent: the percent likelihood this query was logged
+52000 db_sample (db|3),(sql|3),(time|1|3),(blocking_package|3),(sample_percent|1|6)
+
+# http request/response stats
+52001 http_stats (useragent|3),(response|2|3),(processing|2|3),(tx|1|2),(rx|1|2)
+60000 viewroot_draw (Draw time|1|3)
+60001 viewroot_layout (Layout time|1|3)
+60002 view_build_drawing_cache (View created drawing cache|1|5)
+60003 view_use_drawing_cache (View drawn using bitmap cache|1|5)
+
+# graphics timestamp
+# 60100 - 60199 reserved for surfaceflinger
+
+# 0 for screen off, 1 for screen on, 2 for key-guard done
+70000 screen_toggled (screen_state|1|5)
+
+# aggregation service
+70200 aggregation (aggregation time|2|3)
+70201 aggregation_test (field1|1|2),(field2|1|2),(field3|1|2),(field4|1|2),(field5|1|2)
+
+# libc failure logging
+80100 bionic_event_memcpy_buffer_overflow (uid|1)
+80105 bionic_event_strcat_buffer_overflow (uid|1)
+80110 bionic_event_memmov_buffer_overflow (uid|1)
+80115 bionic_event_strncat_buffer_overflow (uid|1)
+80120 bionic_event_strncpy_buffer_overflow (uid|1)
+80125 bionic_event_memset_buffer_overflow (uid|1)
+80130 bionic_event_strcpy_buffer_overflow (uid|1)
+
+80200 bionic_event_strcat_integer_overflow (uid|1)
+80205 bionic_event_strncat_integer_overflow (uid|1)
+
+80300 bionic_event_resolver_old_response (uid|1)
+80305 bionic_event_resolver_wrong_server (uid|1)
+80310 bionic_event_resolver_wrong_query (uid|1)
+
+# libcore failure logging
+90100 exp_det_cert_pin_failure (certs|4)
+
+1397638484 snet_event_log (subtag|3) (uid|1) (message|3)
+
+# NOTE - the range 1000000-2000000 is reserved for partners and others who
+# want to define their own log tags without conflicting with the core platform.
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
new file mode 100644
index 0000000..e598bb8
--- /dev/null
+++ b/logcat/logcat.cpp
@@ -0,0 +1,1007 @@
+// Copyright 2006-2015 The Android Open Source Project
+
+#include <arpa/inet.h>
+#include <assert.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <math.h>
+#include <sched.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/cdefs.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+
+#include <base/file.h>
+#include <base/strings.h>
+#include <cutils/sched_policy.h>
+#include <cutils/sockets.h>
+#include <log/event_tag_map.h>
+#include <log/log.h>
+#include <log/log_read.h>
+#include <log/logd.h>
+#include <log/logger.h>
+#include <log/logprint.h>
+#include <utils/threads.h>
+
+#define DEFAULT_MAX_ROTATED_LOGS 4
+
+static AndroidLogFormat * g_logformat;
+
+/* logd prefixes records with a length field */
+#define RECORD_LENGTH_FIELD_SIZE_BYTES sizeof(uint32_t)
+
+struct log_device_t {
+ const char* device;
+ bool binary;
+ struct logger *logger;
+ struct logger_list *logger_list;
+ bool printed;
+
+ log_device_t* next;
+
+ log_device_t(const char* d, bool b) {
+ device = d;
+ binary = b;
+ next = NULL;
+ printed = false;
+ logger = NULL;
+ logger_list = NULL;
+ }
+};
+
+namespace android {
+
+/* Global Variables */
+
+static const char * g_outputFileName = NULL;
+// 0 means "no log rotation"
+static size_t g_logRotateSizeKBytes = 0;
+// 0 means "unbounded"
+static size_t g_maxRotatedLogs = DEFAULT_MAX_ROTATED_LOGS;
+static int g_outFD = -1;
+static size_t g_outByteCount = 0;
+static int g_printBinary = 0;
+static int g_devCount = 0; // >1 means multiple
+
+__noreturn static void logcat_panic(bool showHelp, const char *fmt, ...) __printflike(2,3);
+
+static int openLogFile (const char *pathname)
+{
+ return open(pathname, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
+}
+
+static void rotateLogs()
+{
+ int err;
+
+ // Can't rotate logs if we're not outputting to a file
+ if (g_outputFileName == NULL) {
+ return;
+ }
+
+ close(g_outFD);
+
+ // Compute the maximum number of digits needed to count up to g_maxRotatedLogs in decimal.
+ // eg: g_maxRotatedLogs == 30 -> log10(30) == 1.477 -> maxRotationCountDigits == 2
+ int maxRotationCountDigits =
+ (g_maxRotatedLogs > 0) ? (int) (floor(log10(g_maxRotatedLogs) + 1)) : 0;
+
+ for (int i = g_maxRotatedLogs ; i > 0 ; i--) {
+ char *file0, *file1;
+
+ asprintf(&file1, "%s.%.*d", g_outputFileName, maxRotationCountDigits, i);
+
+ if (i - 1 == 0) {
+ asprintf(&file0, "%s", g_outputFileName);
+ } else {
+ asprintf(&file0, "%s.%.*d", g_outputFileName, maxRotationCountDigits, i - 1);
+ }
+
+ if (!file0 || !file1) {
+ perror("while rotating log files");
+ break;
+ }
+
+ err = rename(file0, file1);
+
+ if (err < 0 && errno != ENOENT) {
+ perror("while rotating log files");
+ }
+
+ free(file1);
+ free(file0);
+ }
+
+ g_outFD = openLogFile(g_outputFileName);
+
+ if (g_outFD < 0) {
+ logcat_panic(false, "couldn't open output file");
+ }
+
+ g_outByteCount = 0;
+
+}
+
+void printBinary(struct log_msg *buf)
+{
+ size_t size = buf->len();
+
+ TEMP_FAILURE_RETRY(write(g_outFD, buf, size));
+}
+
+static void processBuffer(log_device_t* dev, struct log_msg *buf)
+{
+ int bytesWritten = 0;
+ int err;
+ AndroidLogEntry entry;
+ char binaryMsgBuf[1024];
+
+ if (dev->binary) {
+ static bool hasOpenedEventTagMap = false;
+ static EventTagMap *eventTagMap = NULL;
+
+ if (!eventTagMap && !hasOpenedEventTagMap) {
+ eventTagMap = android_openEventTagMap(EVENT_TAG_MAP_FILE);
+ hasOpenedEventTagMap = true;
+ }
+ err = android_log_processBinaryLogBuffer(&buf->entry_v1, &entry,
+ eventTagMap,
+ binaryMsgBuf,
+ sizeof(binaryMsgBuf));
+ //printf(">>> pri=%d len=%d msg='%s'\n",
+ // entry.priority, entry.messageLen, entry.message);
+ } else {
+ err = android_log_processLogBuffer(&buf->entry_v1, &entry);
+ }
+ if (err < 0) {
+ goto error;
+ }
+
+ if (android_log_shouldPrintLine(g_logformat, entry.tag, entry.priority)) {
+ bytesWritten = android_log_printLogLine(g_logformat, g_outFD, &entry);
+
+ if (bytesWritten < 0) {
+ logcat_panic(false, "output error");
+ }
+ }
+
+ g_outByteCount += bytesWritten;
+
+ if (g_logRotateSizeKBytes > 0
+ && (g_outByteCount / 1024) >= g_logRotateSizeKBytes
+ ) {
+ rotateLogs();
+ }
+
+error:
+ //fprintf (stderr, "Error processing record\n");
+ return;
+}
+
+static void maybePrintStart(log_device_t* dev, bool printDividers) {
+ if (!dev->printed || printDividers) {
+ if (g_devCount > 1 && !g_printBinary) {
+ char buf[1024];
+ snprintf(buf, sizeof(buf), "--------- %s %s\n",
+ dev->printed ? "switch to" : "beginning of",
+ dev->device);
+ if (write(g_outFD, buf, strlen(buf)) < 0) {
+ logcat_panic(false, "output error");
+ }
+ }
+ dev->printed = true;
+ }
+}
+
+static void setupOutput()
+{
+
+ if (g_outputFileName == NULL) {
+ g_outFD = STDOUT_FILENO;
+
+ } else {
+ if (set_sched_policy(0, SP_BACKGROUND) < 0) {
+ fprintf(stderr, "failed to set background scheduling policy\n");
+ }
+
+ struct sched_param param;
+ memset(¶m, 0, sizeof(param));
+ if (sched_setscheduler((pid_t) 0, SCHED_BATCH, ¶m) < 0) {
+ fprintf(stderr, "failed to set to batch scheduler\n");
+ }
+
+ if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) {
+ fprintf(stderr, "failed set to priority\n");
+ }
+
+ g_outFD = openLogFile (g_outputFileName);
+
+ if (g_outFD < 0) {
+ logcat_panic(false, "couldn't open output file");
+ }
+
+ struct stat statbuf;
+ if (fstat(g_outFD, &statbuf) == -1) {
+ close(g_outFD);
+ logcat_panic(false, "couldn't get output file stat\n");
+ }
+
+ if ((size_t) statbuf.st_size > SIZE_MAX || statbuf.st_size < 0) {
+ close(g_outFD);
+ logcat_panic(false, "invalid output file stat\n");
+ }
+
+ g_outByteCount = statbuf.st_size;
+ }
+}
+
+static void show_help(const char *cmd)
+{
+ fprintf(stderr,"Usage: %s [options] [filterspecs]\n", cmd);
+
+ fprintf(stderr, "options include:\n"
+ " -s Set default filter to silent.\n"
+ " Like specifying filterspec '*:S'\n"
+ " -f <filename> Log to file. Default is stdout\n"
+ " -r <kbytes> Rotate log every kbytes. Requires -f\n"
+ " -n <count> Sets max number of rotated logs to <count>, default 4\n"
+ " -v <format> Sets the log print format, where <format> is:\n\n"
+ " brief color long printable process raw tag thread\n"
+ " threadtime time usec\n\n"
+ " -D print dividers between each log buffer\n"
+ " -c clear (flush) the entire log and exit\n"
+ " -d dump the log and then exit (don't block)\n"
+ " -t <count> print only the most recent <count> lines (implies -d)\n"
+ " -t '<time>' print most recent lines since specified time (implies -d)\n"
+ " -T <count> print only the most recent <count> lines (does not imply -d)\n"
+ " -T '<time>' print most recent lines since specified time (not imply -d)\n"
+ " count is pure numerical, time is 'MM-DD hh:mm:ss.mmm'\n"
+ " -g get the size of the log's ring buffer and exit\n"
+ " -L dump logs from prior to last reboot\n"
+ " -b <buffer> Request alternate ring buffer, 'main', 'system', 'radio',\n"
+ " 'events', 'crash' or 'all'. Multiple -b parameters are\n"
+ " allowed and results are interleaved. The default is\n"
+ " -b main -b system -b crash.\n"
+ " -B output the log in binary.\n"
+ " -S output statistics.\n"
+ " -G <size> set size of log ring buffer, may suffix with K or M.\n"
+ " -p print prune white and ~black list. Service is specified as\n"
+ " UID, UID/PID or /PID. Weighed for quicker pruning if prefix\n"
+ " with ~, otherwise weighed for longevity if unadorned. All\n"
+ " other pruning activity is oldest first. Special case ~!\n"
+ " represents an automatic quicker pruning for the noisiest\n"
+ " UID as determined by the current statistics.\n"
+ " -P '<list> ...' set prune white and ~black list, using same format as\n"
+ " printed above. Must be quoted.\n");
+
+ fprintf(stderr,"\nfilterspecs are a series of \n"
+ " <tag>[:priority]\n\n"
+ "where <tag> is a log component tag (or * for all) and priority is:\n"
+ " V Verbose (default for <tag>)\n"
+ " D Debug (default for '*')\n"
+ " I Info\n"
+ " W Warn\n"
+ " E Error\n"
+ " F Fatal\n"
+ " S Silent (suppress all output)\n"
+ "\n'*' by itself means '*:D' and <tag> by itself means <tag>:V.\n"
+ "If no '*' filterspec or -s on command line, all filter defaults to '*:V'.\n"
+ "eg: '*:S <tag>' prints only <tag>, '<tag>:S' suppresses all <tag> log messages.\n"
+ "\nIf not specified on the command line, filterspec is set from ANDROID_LOG_TAGS.\n"
+ "\nIf not specified with -v on command line, format is set from ANDROID_PRINTF_LOG\n"
+ "or defaults to \"threadtime\"\n\n");
+}
+
+static int setLogFormat(const char * formatString)
+{
+ static AndroidLogPrintFormat format;
+
+ format = android_log_formatFromString(formatString);
+
+ if (format == FORMAT_OFF) {
+ // FORMAT_OFF means invalid string
+ return -1;
+ }
+
+ return android_log_setPrintFormat(g_logformat, format);
+}
+
+static const char multipliers[][2] = {
+ { "" },
+ { "K" },
+ { "M" },
+ { "G" }
+};
+
+static unsigned long value_of_size(unsigned long value)
+{
+ for (unsigned i = 0;
+ (i < sizeof(multipliers)/sizeof(multipliers[0])) && (value >= 1024);
+ value /= 1024, ++i) ;
+ return value;
+}
+
+static const char *multiplier_of_size(unsigned long value)
+{
+ unsigned i;
+ for (i = 0;
+ (i < sizeof(multipliers)/sizeof(multipliers[0])) && (value >= 1024);
+ value /= 1024, ++i) ;
+ return multipliers[i];
+}
+
+/*String to unsigned int, returns -1 if it fails*/
+static bool getSizeTArg(char *ptr, size_t *val, size_t min = 0,
+ size_t max = SIZE_MAX)
+{
+ char *endp;
+ errno = 0;
+ size_t ret = (size_t) strtoll(ptr, &endp, 0);
+
+ if (endp[0] != '\0' || errno != 0 ) {
+ return false;
+ }
+
+ if (ret > max || ret < min) {
+ return false;
+ }
+
+ *val = ret;
+ return true;
+}
+
+static void logcat_panic(bool showHelp, const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+
+ if (showHelp) {
+ show_help(getprogname());
+ }
+
+ exit(EXIT_FAILURE);
+}
+
+static const char g_defaultTimeFormat[] = "%m-%d %H:%M:%S.%q";
+
+// Find last logged line in gestalt of all matching existing output files
+static log_time lastLogTime(char *outputFileName) {
+ log_time retval(log_time::EPOCH);
+ if (!outputFileName) {
+ return retval;
+ }
+
+ log_time now(CLOCK_REALTIME);
+
+ std::string directory;
+ char *file = strrchr(outputFileName, '/');
+ if (!file) {
+ directory = ".";
+ file = outputFileName;
+ } else {
+ *file = '\0';
+ directory = outputFileName;
+ *file = '/';
+ ++file;
+ }
+ size_t len = strlen(file);
+ log_time modulo(0, NS_PER_SEC);
+ std::unique_ptr<DIR, int(*)(DIR*)>dir(opendir(directory.c_str()), closedir);
+ struct dirent *dp;
+ while ((dp = readdir(dir.get())) != NULL) {
+ if ((dp->d_type != DT_REG)
+ || strncmp(dp->d_name, file, len)
+ || (dp->d_name[len]
+ && ((dp->d_name[len] != '.')
+ || !isdigit(dp->d_name[len+1])))) {
+ continue;
+ }
+
+ std::string file_name = directory;
+ file_name += "/";
+ file_name += dp->d_name;
+ std::string file;
+ if (!android::base::ReadFileToString(file_name, &file)) {
+ continue;
+ }
+
+ bool found = false;
+ for (const auto& line : android::base::Split(file, "\n")) {
+ log_time t(log_time::EPOCH);
+ char *ep = t.strptime(line.c_str(), g_defaultTimeFormat);
+ if (!ep || (*ep != ' ')) {
+ continue;
+ }
+ // determine the time precision of the logs (eg: msec or usec)
+ for (unsigned long mod = 1UL; mod < modulo.tv_nsec; mod *= 10) {
+ if (t.tv_nsec % (mod * 10)) {
+ modulo.tv_nsec = mod;
+ break;
+ }
+ }
+ // We filter any times later than current as we may not have the
+ // year stored with each log entry. Also, since it is possible for
+ // entries to be recorded out of order (very rare) we select the
+ // maximum we find just in case.
+ if ((t < now) && (t > retval)) {
+ retval = t;
+ found = true;
+ }
+ }
+ // We count on the basename file to be the definitive end, so stop here.
+ if (!dp->d_name[len] && found) {
+ break;
+ }
+ }
+ if (retval == log_time::EPOCH) {
+ return retval;
+ }
+ // tail_time prints matching or higher, round up by the modulo to prevent
+ // a replay of the last entry we have just checked.
+ retval += modulo;
+ return retval;
+}
+
+} /* namespace android */
+
+
+int main(int argc, char **argv)
+{
+ using namespace android;
+ int err;
+ int hasSetLogFormat = 0;
+ int clearLog = 0;
+ int getLogSize = 0;
+ unsigned long setLogSize = 0;
+ int getPruneList = 0;
+ char *setPruneList = NULL;
+ int printStatistics = 0;
+ int mode = ANDROID_LOG_RDONLY;
+ const char *forceFilters = NULL;
+ log_device_t* devices = NULL;
+ log_device_t* dev;
+ bool printDividers = false;
+ struct logger_list *logger_list;
+ size_t tail_lines = 0;
+ log_time tail_time(log_time::EPOCH);
+
+ signal(SIGPIPE, exit);
+
+ g_logformat = android_log_format_new();
+
+ if (argc == 2 && 0 == strcmp(argv[1], "--help")) {
+ show_help(argv[0]);
+ return EXIT_SUCCESS;
+ }
+
+ for (;;) {
+ int ret;
+
+ ret = getopt(argc, argv, ":cdDLt:T:gG:sQf:r:n:v:b:BSpP:");
+
+ if (ret < 0) {
+ break;
+ }
+
+ switch(ret) {
+ case 's':
+ // default to all silent
+ android_log_addFilterRule(g_logformat, "*:s");
+ break;
+
+ case 'c':
+ clearLog = 1;
+ mode |= ANDROID_LOG_WRONLY;
+ break;
+
+ case 'L':
+ mode |= ANDROID_LOG_PSTORE;
+ break;
+
+ case 'd':
+ mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
+ break;
+
+ case 't':
+ mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
+ /* FALLTHRU */
+ case 'T':
+ if (strspn(optarg, "0123456789") != strlen(optarg)) {
+ char *cp = tail_time.strptime(optarg, g_defaultTimeFormat);
+ if (!cp) {
+ logcat_panic(false,
+ "-%c \"%s\" not in \"%s\" time format\n",
+ ret, optarg, g_defaultTimeFormat);
+ }
+ if (*cp) {
+ char c = *cp;
+ *cp = '\0';
+ fprintf(stderr,
+ "WARNING: -%c \"%s\"\"%c%s\" time truncated\n",
+ ret, optarg, c, cp + 1);
+ *cp = c;
+ }
+ } else {
+ if (!getSizeTArg(optarg, &tail_lines, 1)) {
+ fprintf(stderr,
+ "WARNING: -%c %s invalid, setting to 1\n",
+ ret, optarg);
+ tail_lines = 1;
+ }
+ }
+ break;
+
+ case 'D':
+ printDividers = true;
+ break;
+
+ case 'g':
+ getLogSize = 1;
+ break;
+
+ case 'G': {
+ char *cp;
+ if (strtoll(optarg, &cp, 0) > 0) {
+ setLogSize = strtoll(optarg, &cp, 0);
+ } else {
+ setLogSize = 0;
+ }
+
+ switch(*cp) {
+ case 'g':
+ case 'G':
+ setLogSize *= 1024;
+ /* FALLTHRU */
+ case 'm':
+ case 'M':
+ setLogSize *= 1024;
+ /* FALLTHRU */
+ case 'k':
+ case 'K':
+ setLogSize *= 1024;
+ /* FALLTHRU */
+ case '\0':
+ break;
+
+ default:
+ setLogSize = 0;
+ }
+
+ if (!setLogSize) {
+ fprintf(stderr, "ERROR: -G <num><multiplier>\n");
+ return EXIT_FAILURE;
+ }
+ }
+ break;
+
+ case 'p':
+ getPruneList = 1;
+ break;
+
+ case 'P':
+ setPruneList = optarg;
+ break;
+
+ case 'b': {
+ if (strcmp(optarg, "all") == 0) {
+ while (devices) {
+ dev = devices;
+ devices = dev->next;
+ delete dev;
+ }
+
+ devices = dev = NULL;
+ g_devCount = 0;
+ for(int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
+ const char *name = android_log_id_to_name((log_id_t)i);
+ log_id_t log_id = android_name_to_log_id(name);
+
+ if (log_id != (log_id_t)i) {
+ continue;
+ }
+
+ bool binary = strcmp(name, "events") == 0;
+ log_device_t* d = new log_device_t(name, binary);
+
+ if (dev) {
+ dev->next = d;
+ dev = d;
+ } else {
+ devices = dev = d;
+ }
+ g_devCount++;
+ }
+ break;
+ }
+
+ bool binary = strcmp(optarg, "events") == 0;
+
+ if (devices) {
+ dev = devices;
+ while (dev->next) {
+ dev = dev->next;
+ }
+ dev->next = new log_device_t(optarg, binary);
+ } else {
+ devices = new log_device_t(optarg, binary);
+ }
+ g_devCount++;
+ }
+ break;
+
+ case 'B':
+ g_printBinary = 1;
+ break;
+
+ case 'f':
+ if ((tail_time == log_time::EPOCH) && (tail_lines != 0)) {
+ tail_time = lastLogTime(optarg);
+ }
+ // redirect output to a file
+ g_outputFileName = optarg;
+ break;
+
+ case 'r':
+ if (!getSizeTArg(optarg, &g_logRotateSizeKBytes, 1)) {
+ logcat_panic(true, "Invalid parameter %s to -r\n", optarg);
+ }
+ break;
+
+ case 'n':
+ if (!getSizeTArg(optarg, &g_maxRotatedLogs, 1)) {
+ logcat_panic(true, "Invalid parameter %s to -n\n", optarg);
+ }
+ break;
+
+ case 'v':
+ err = setLogFormat (optarg);
+ if (err < 0) {
+ logcat_panic(true, "Invalid parameter %s to -v\n", optarg);
+ }
+ hasSetLogFormat |= err;
+ break;
+
+ case 'Q':
+ /* this is a *hidden* option used to start a version of logcat */
+ /* in an emulated device only. it basically looks for androidboot.logcat= */
+ /* on the kernel command line. If something is found, it extracts a log filter */
+ /* and uses it to run the program. If nothing is found, the program should */
+ /* quit immediately */
+#define KERNEL_OPTION "androidboot.logcat="
+#define CONSOLE_OPTION "androidboot.console="
+ {
+ int fd;
+ char* logcat;
+ char* console;
+ int force_exit = 1;
+ static char cmdline[1024];
+
+ fd = open("/proc/cmdline", O_RDONLY);
+ if (fd >= 0) {
+ int n = read(fd, cmdline, sizeof(cmdline)-1 );
+ if (n < 0) n = 0;
+ cmdline[n] = 0;
+ close(fd);
+ } else {
+ cmdline[0] = 0;
+ }
+
+ logcat = strstr( cmdline, KERNEL_OPTION );
+ console = strstr( cmdline, CONSOLE_OPTION );
+ if (logcat != NULL) {
+ char* p = logcat + sizeof(KERNEL_OPTION)-1;;
+ char* q = strpbrk( p, " \t\n\r" );;
+
+ if (q != NULL)
+ *q = 0;
+
+ forceFilters = p;
+ force_exit = 0;
+ }
+ /* if nothing found or invalid filters, exit quietly */
+ if (force_exit) {
+ return EXIT_SUCCESS;
+ }
+
+ /* redirect our output to the emulator console */
+ if (console) {
+ char* p = console + sizeof(CONSOLE_OPTION)-1;
+ char* q = strpbrk( p, " \t\n\r" );
+ char devname[64];
+ int len;
+
+ if (q != NULL) {
+ len = q - p;
+ } else
+ len = strlen(p);
+
+ len = snprintf( devname, sizeof(devname), "/dev/%.*s", len, p );
+ fprintf(stderr, "logcat using %s (%d)\n", devname, len);
+ if (len < (int)sizeof(devname)) {
+ fd = open( devname, O_WRONLY );
+ if (fd >= 0) {
+ dup2(fd, 1);
+ dup2(fd, 2);
+ close(fd);
+ }
+ }
+ }
+ }
+ break;
+
+ case 'S':
+ printStatistics = 1;
+ break;
+
+ case ':':
+ logcat_panic(true, "Option -%c needs an argument\n", optopt);
+ break;
+
+ default:
+ logcat_panic(true, "Unrecognized Option %c\n", optopt);
+ break;
+ }
+ }
+
+ if (!devices) {
+ dev = devices = new log_device_t("main", false);
+ g_devCount = 1;
+ if (android_name_to_log_id("system") == LOG_ID_SYSTEM) {
+ dev = dev->next = new log_device_t("system", false);
+ g_devCount++;
+ }
+ if (android_name_to_log_id("crash") == LOG_ID_CRASH) {
+ dev = dev->next = new log_device_t("crash", false);
+ g_devCount++;
+ }
+ }
+
+ if (g_logRotateSizeKBytes != 0 && g_outputFileName == NULL) {
+ logcat_panic(true, "-r requires -f as well\n");
+ }
+
+ setupOutput();
+
+ if (hasSetLogFormat == 0) {
+ const char* logFormat = getenv("ANDROID_PRINTF_LOG");
+
+ if (logFormat != NULL) {
+ err = setLogFormat(logFormat);
+ if (err < 0) {
+ fprintf(stderr, "invalid format in ANDROID_PRINTF_LOG '%s'\n",
+ logFormat);
+ }
+ } else {
+ setLogFormat("threadtime");
+ }
+ }
+
+ if (forceFilters) {
+ err = android_log_addFilterString(g_logformat, forceFilters);
+ if (err < 0) {
+ logcat_panic(false, "Invalid filter expression in logcat args\n");
+ }
+ } else if (argc == optind) {
+ // Add from environment variable
+ char *env_tags_orig = getenv("ANDROID_LOG_TAGS");
+
+ if (env_tags_orig != NULL) {
+ err = android_log_addFilterString(g_logformat, env_tags_orig);
+
+ if (err < 0) {
+ logcat_panic(true,
+ "Invalid filter expression in ANDROID_LOG_TAGS\n");
+ }
+ }
+ } else {
+ // Add from commandline
+ for (int i = optind ; i < argc ; i++) {
+ err = android_log_addFilterString(g_logformat, argv[i]);
+
+ if (err < 0) {
+ logcat_panic(true, "Invalid filter expression '%s'\n", argv[i]);
+ }
+ }
+ }
+
+ dev = devices;
+ if (tail_time != log_time::EPOCH) {
+ logger_list = android_logger_list_alloc_time(mode, tail_time, 0);
+ } else {
+ logger_list = android_logger_list_alloc(mode, tail_lines, 0);
+ }
+ while (dev) {
+ dev->logger_list = logger_list;
+ dev->logger = android_logger_open(logger_list,
+ android_name_to_log_id(dev->device));
+ if (!dev->logger) {
+ logcat_panic(false, "Unable to open log device '%s'\n",
+ dev->device);
+ }
+
+ if (clearLog) {
+ int ret;
+ ret = android_logger_clear(dev->logger);
+ if (ret) {
+ logcat_panic(false, "failed to clear the log");
+ }
+ }
+
+ if (setLogSize && android_logger_set_log_size(dev->logger, setLogSize)) {
+ logcat_panic(false, "failed to set the log size");
+ }
+
+ if (getLogSize) {
+ long size, readable;
+
+ size = android_logger_get_log_size(dev->logger);
+ if (size < 0) {
+ logcat_panic(false, "failed to get the log size");
+ }
+
+ readable = android_logger_get_log_readable_size(dev->logger);
+ if (readable < 0) {
+ logcat_panic(false, "failed to get the readable log size");
+ }
+
+ printf("%s: ring buffer is %ld%sb (%ld%sb consumed), "
+ "max entry is %db, max payload is %db\n", dev->device,
+ value_of_size(size), multiplier_of_size(size),
+ value_of_size(readable), multiplier_of_size(readable),
+ (int) LOGGER_ENTRY_MAX_LEN, (int) LOGGER_ENTRY_MAX_PAYLOAD);
+ }
+
+ dev = dev->next;
+ }
+
+ if (setPruneList) {
+ size_t len = strlen(setPruneList);
+ /*extra 32 bytes are needed by android_logger_set_prune_list */
+ size_t bLen = len + 32;
+ char *buf = NULL;
+ if (asprintf(&buf, "%-*s", (int)(bLen - 1), setPruneList) > 0) {
+ buf[len] = '\0';
+ if (android_logger_set_prune_list(logger_list, buf, bLen)) {
+ logcat_panic(false, "failed to set the prune list");
+ }
+ free(buf);
+ } else {
+ logcat_panic(false, "failed to set the prune list (alloc)");
+ }
+ }
+
+ if (printStatistics || getPruneList) {
+ size_t len = 8192;
+ char *buf;
+
+ for(int retry = 32;
+ (retry >= 0) && ((buf = new char [len]));
+ delete [] buf, buf = NULL, --retry) {
+ if (getPruneList) {
+ android_logger_get_prune_list(logger_list, buf, len);
+ } else {
+ android_logger_get_statistics(logger_list, buf, len);
+ }
+ buf[len-1] = '\0';
+ if (atol(buf) < 3) {
+ delete [] buf;
+ buf = NULL;
+ break;
+ }
+ size_t ret = atol(buf) + 1;
+ if (ret <= len) {
+ len = ret;
+ break;
+ }
+ len = ret;
+ }
+
+ if (!buf) {
+ logcat_panic(false, "failed to read data");
+ }
+
+ // remove trailing FF
+ char *cp = buf + len - 1;
+ *cp = '\0';
+ bool truncated = *--cp != '\f';
+ if (!truncated) {
+ *cp = '\0';
+ }
+
+ // squash out the byte count
+ cp = buf;
+ if (!truncated) {
+ while (isdigit(*cp)) {
+ ++cp;
+ }
+ if (*cp == '\n') {
+ ++cp;
+ }
+ }
+
+ printf("%s", cp);
+ delete [] buf;
+ return EXIT_SUCCESS;
+ }
+
+
+ if (getLogSize) {
+ return EXIT_SUCCESS;
+ }
+ if (setLogSize || setPruneList) {
+ return EXIT_SUCCESS;
+ }
+ if (clearLog) {
+ return EXIT_SUCCESS;
+ }
+
+ //LOG_EVENT_INT(10, 12345);
+ //LOG_EVENT_LONG(11, 0x1122334455667788LL);
+ //LOG_EVENT_STRING(0, "whassup, doc?");
+
+ dev = NULL;
+ log_device_t unexpected("unexpected", false);
+ while (1) {
+ struct log_msg log_msg;
+ log_device_t* d;
+ int ret = android_logger_list_read(logger_list, &log_msg);
+
+ if (ret == 0) {
+ logcat_panic(false, "read: unexpected EOF!\n");
+ }
+
+ if (ret < 0) {
+ if (ret == -EAGAIN) {
+ break;
+ }
+
+ if (ret == -EIO) {
+ logcat_panic(false, "read: unexpected EOF!\n");
+ }
+ if (ret == -EINVAL) {
+ logcat_panic(false, "read: unexpected length.\n");
+ }
+ logcat_panic(false, "logcat read failure");
+ }
+
+ for(d = devices; d; d = d->next) {
+ if (android_name_to_log_id(d->device) == log_msg.id()) {
+ break;
+ }
+ }
+ if (!d) {
+ g_devCount = 2; // set to Multiple
+ d = &unexpected;
+ d->binary = log_msg.id() == LOG_ID_EVENTS;
+ }
+
+ if (dev != d) {
+ dev = d;
+ maybePrintStart(dev, printDividers);
+ }
+ if (g_printBinary) {
+ printBinary(&log_msg);
+ } else {
+ processBuffer(dev, &log_msg);
+ }
+ }
+
+ android_logger_list_free(logger_list);
+
+ return EXIT_SUCCESS;
+}
diff --git a/logcat/tests/Android.mk b/logcat/tests/Android.mk
new file mode 100644
index 0000000..a28664e
--- /dev/null
+++ b/logcat/tests/Android.mk
@@ -0,0 +1,61 @@
+#
+# Copyright (C) 2013-2014 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)
+
+test_module_prefix := logcat-
+test_tags := tests
+
+test_c_flags := \
+ -fstack-protector-all \
+ -g \
+ -Wall -Wextra \
+ -Werror \
+ -fno-builtin \
+ -std=gnu++11
+
+# -----------------------------------------------------------------------------
+# Benchmarks (actually a gTest where the result code does not matter)
+# ----------------------------------------------------------------------------
+
+benchmark_src_files := \
+ logcat_benchmark.cpp
+
+# Build benchmarks for the device. Run with:
+# adb shell /data/nativetest/logcat-benchmarks/logcat-benchmarks
+include $(CLEAR_VARS)
+LOCAL_MODULE := $(test_module_prefix)benchmarks
+LOCAL_MODULE_TAGS := $(test_tags)
+LOCAL_CFLAGS += $(test_c_flags)
+LOCAL_SRC_FILES := $(benchmark_src_files)
+include $(BUILD_NATIVE_TEST)
+
+# -----------------------------------------------------------------------------
+# Unit tests.
+# -----------------------------------------------------------------------------
+
+test_src_files := \
+ logcat_test.cpp \
+
+# Build tests for the device (with .so). Run with:
+# adb shell /data/nativetest/logcat-unit-tests/logcat-unit-tests
+include $(CLEAR_VARS)
+LOCAL_MODULE := $(test_module_prefix)unit-tests
+LOCAL_MODULE_TAGS := $(test_tags)
+LOCAL_CFLAGS += $(test_c_flags)
+LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_SRC_FILES := $(test_src_files)
+include $(BUILD_NATIVE_TEST)
diff --git a/logcat/tests/logcat_benchmark.cpp b/logcat/tests/logcat_benchmark.cpp
new file mode 100644
index 0000000..be815be
--- /dev/null
+++ b/logcat/tests/logcat_benchmark.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2013-2014 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 <gtest/gtest.h>
+
+static const char begin[] = "--------- beginning of ";
+
+TEST(logcat, sorted_order) {
+ FILE *fp;
+
+ ASSERT_TRUE(NULL != (fp = popen(
+ "logcat -v time -b radio -b events -b system -b main -d 2>/dev/null",
+ "r")));
+
+ class timestamp {
+ private:
+ int month;
+ int day;
+ int hour;
+ int minute;
+ int second;
+ int millisecond;
+ bool ok;
+
+ public:
+ void init(const char *buffer)
+ {
+ ok = false;
+ if (buffer != NULL) {
+ ok = sscanf(buffer, "%d-%d %d:%d:%d.%d ",
+ &month, &day, &hour, &minute, &second, &millisecond) == 6;
+ }
+ }
+
+ timestamp(const char *buffer)
+ {
+ init(buffer);
+ }
+
+ bool operator< (timestamp &T)
+ {
+ return !ok || !T.ok
+ || (month < T.month)
+ || ((month == T.month)
+ && ((day < T.day)
+ || ((day == T.day)
+ && ((hour < T.hour)
+ || ((hour == T.hour)
+ && ((minute < T.minute)
+ || ((minute == T.minute)
+ && ((second < T.second)
+ || ((second == T.second)
+ && (millisecond < T.millisecond))))))))));
+ }
+
+ bool valid(void)
+ {
+ return ok;
+ }
+ } last(NULL);
+
+ char *last_buffer = NULL;
+ char buffer[5120];
+
+ int count = 0;
+ int next_lt_last = 0;
+
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ if (!strncmp(begin, buffer, sizeof(begin) - 1)) {
+ continue;
+ }
+ if (!last.valid()) {
+ free(last_buffer);
+ last_buffer = strdup(buffer);
+ last.init(buffer);
+ }
+ timestamp next(buffer);
+ if (next < last) {
+ if (last_buffer) {
+ fprintf(stderr, "<%s", last_buffer);
+ }
+ fprintf(stderr, ">%s", buffer);
+ ++next_lt_last;
+ }
+ if (next.valid()) {
+ free(last_buffer);
+ last_buffer = strdup(buffer);
+ last.init(buffer);
+ }
+ ++count;
+ }
+ free(last_buffer);
+
+ pclose(fp);
+
+ static const int max_ok = 2;
+
+ // Allow few fails, happens with readers active
+ fprintf(stderr, "%s: %d/%d out of order entries\n",
+ (next_lt_last)
+ ? ((next_lt_last <= max_ok)
+ ? "WARNING"
+ : "ERROR")
+ : "INFO",
+ next_lt_last, count);
+
+ EXPECT_GE(max_ok, next_lt_last);
+
+ // sample statistically too small
+ EXPECT_LT(100, count);
+}
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
new file mode 100644
index 0000000..de2db67
--- /dev/null
+++ b/logcat/tests/logcat_test.cpp
@@ -0,0 +1,753 @@
+/*
+ * Copyright (C) 2013-2014 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 <ctype.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <gtest/gtest.h>
+#include <log/log.h>
+#include <log/logger.h>
+#include <log/log_read.h>
+
+// enhanced version of LOG_FAILURE_RETRY to add support for EAGAIN and
+// non-syscall libs. Since we are only using this in the emergency of
+// a signal to stuff a terminating code into the logs, we will spin rather
+// than try a usleep.
+#define LOG_FAILURE_RETRY(exp) ({ \
+ typeof (exp) _rc; \
+ do { \
+ _rc = (exp); \
+ } while (((_rc == -1) \
+ && ((errno == EINTR) \
+ || (errno == EAGAIN))) \
+ || (_rc == -EINTR) \
+ || (_rc == -EAGAIN)); \
+ _rc; })
+
+static const char begin[] = "--------- beginning of ";
+
+TEST(logcat, buckets) {
+ FILE *fp;
+
+ ASSERT_TRUE(NULL != (fp = popen(
+ "logcat -b radio -b events -b system -b main -d 2>/dev/null",
+ "r")));
+
+ char buffer[5120];
+
+ int ids = 0;
+ int count = 0;
+
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ if (!strncmp(begin, buffer, sizeof(begin) - 1)) {
+ while (char *cp = strrchr(buffer, '\n')) {
+ *cp = '\0';
+ }
+ log_id_t id = android_name_to_log_id(buffer + sizeof(begin) - 1);
+ ids |= 1 << id;
+ ++count;
+ }
+ }
+
+ pclose(fp);
+
+ EXPECT_EQ(15, ids);
+
+ EXPECT_EQ(4, count);
+}
+
+TEST(logcat, tail_3) {
+ FILE *fp;
+
+ ASSERT_TRUE(NULL != (fp = popen(
+ "logcat -v long -b radio -b events -b system -b main -t 3 2>/dev/null",
+ "r")));
+
+ char buffer[5120];
+
+ int count = 0;
+
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ if ((buffer[0] == '[') && (buffer[1] == ' ')
+ && isdigit(buffer[2]) && isdigit(buffer[3])
+ && (buffer[4] == '-')) {
+ ++count;
+ }
+ }
+
+ pclose(fp);
+
+ ASSERT_EQ(3, count);
+}
+
+TEST(logcat, tail_10) {
+ FILE *fp;
+
+ ASSERT_TRUE(NULL != (fp = popen(
+ "logcat -v long -b radio -b events -b system -b main -t 10 2>/dev/null",
+ "r")));
+
+ char buffer[5120];
+
+ int count = 0;
+
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ if ((buffer[0] == '[') && (buffer[1] == ' ')
+ && isdigit(buffer[2]) && isdigit(buffer[3])
+ && (buffer[4] == '-')) {
+ ++count;
+ }
+ }
+
+ pclose(fp);
+
+ ASSERT_EQ(10, count);
+}
+
+TEST(logcat, tail_100) {
+ FILE *fp;
+
+ ASSERT_TRUE(NULL != (fp = popen(
+ "logcat -v long -b radio -b events -b system -b main -t 100 2>/dev/null",
+ "r")));
+
+ char buffer[5120];
+
+ int count = 0;
+
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ if ((buffer[0] == '[') && (buffer[1] == ' ')
+ && isdigit(buffer[2]) && isdigit(buffer[3])
+ && (buffer[4] == '-')) {
+ ++count;
+ }
+ }
+
+ pclose(fp);
+
+ ASSERT_EQ(100, count);
+}
+
+TEST(logcat, tail_1000) {
+ FILE *fp;
+
+ ASSERT_TRUE(NULL != (fp = popen(
+ "logcat -v long -b radio -b events -b system -b main -t 1000 2>/dev/null",
+ "r")));
+
+ char buffer[5120];
+
+ int count = 0;
+
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ if ((buffer[0] == '[') && (buffer[1] == ' ')
+ && isdigit(buffer[2]) && isdigit(buffer[3])
+ && (buffer[4] == '-')) {
+ ++count;
+ }
+ }
+
+ pclose(fp);
+
+ ASSERT_EQ(1000, count);
+}
+
+TEST(logcat, tail_time) {
+ FILE *fp;
+
+ ASSERT_TRUE(NULL != (fp = popen("logcat -v long -b all -t 10 2>&1", "r")));
+
+ char buffer[5120];
+ char *last_timestamp = NULL;
+ char *first_timestamp = NULL;
+ int count = 0;
+ const unsigned int time_length = 18;
+ const unsigned int time_offset = 2;
+
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ if ((buffer[0] == '[') && (buffer[1] == ' ')
+ && isdigit(buffer[time_offset]) && isdigit(buffer[time_offset + 1])
+ && (buffer[time_offset + 2] == '-')) {
+ ++count;
+ buffer[time_length + time_offset] = '\0';
+ if (!first_timestamp) {
+ first_timestamp = strdup(buffer + time_offset);
+ }
+ free(last_timestamp);
+ last_timestamp = strdup(buffer + time_offset);
+ }
+ }
+ pclose(fp);
+
+ EXPECT_EQ(10, count);
+ EXPECT_TRUE(last_timestamp != NULL);
+ EXPECT_TRUE(first_timestamp != NULL);
+
+ snprintf(buffer, sizeof(buffer), "logcat -v long -b all -t '%s' 2>&1",
+ first_timestamp);
+ ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
+
+ int second_count = 0;
+ int last_timestamp_count = -1;
+
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ if ((buffer[0] == '[') && (buffer[1] == ' ')
+ && isdigit(buffer[time_offset]) && isdigit(buffer[time_offset + 1])
+ && (buffer[time_offset + 2] == '-')) {
+ ++second_count;
+ buffer[time_length + time_offset] = '\0';
+ if (first_timestamp) {
+ // we can get a transitory *extremely* rare failure if hidden
+ // underneath the time is *exactly* XX-XX XX:XX:XX.XXX000000
+ EXPECT_STREQ(buffer + time_offset, first_timestamp);
+ free(first_timestamp);
+ first_timestamp = NULL;
+ }
+ if (!strcmp(buffer + time_offset, last_timestamp)) {
+ last_timestamp_count = second_count;
+ }
+ }
+ }
+ pclose(fp);
+
+ free(last_timestamp);
+ last_timestamp = NULL;
+
+ EXPECT_TRUE(first_timestamp == NULL);
+ EXPECT_LE(count, second_count);
+ EXPECT_LE(count, last_timestamp_count);
+}
+
+TEST(logcat, End_to_End) {
+ pid_t pid = getpid();
+
+ log_time ts(CLOCK_MONOTONIC);
+
+ ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
+
+ FILE *fp;
+ ASSERT_TRUE(NULL != (fp = popen(
+ "logcat -v brief -b events -t 100 2>/dev/null",
+ "r")));
+
+ char buffer[5120];
+
+ int count = 0;
+
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ int p;
+ unsigned long long t;
+
+ if ((2 != sscanf(buffer, "I/[0] ( %d): %llu", &p, &t))
+ || (p != pid)) {
+ continue;
+ }
+
+ log_time tx((const char *) &t);
+ if (ts == tx) {
+ ++count;
+ }
+ }
+
+ pclose(fp);
+
+ ASSERT_EQ(1, count);
+}
+
+TEST(logcat, get_size) {
+ FILE *fp;
+
+ // NB: crash log only available in user space
+ ASSERT_TRUE(NULL != (fp = popen(
+ "logcat -v brief -b radio -b events -b system -b main -g 2>/dev/null",
+ "r")));
+
+ char buffer[5120];
+
+ int count = 0;
+
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ int size, consumed, max, payload;
+ char size_mult[2], consumed_mult[2];
+ long full_size, full_consumed;
+
+ size = consumed = max = payload = 0;
+ // NB: crash log can be very small, not hit a Kb of consumed space
+ // doubly lucky we are not including it.
+ if (6 != sscanf(buffer, "%*s ring buffer is %d%2s (%d%2s consumed),"
+ " max entry is %db, max payload is %db",
+ &size, size_mult, &consumed, consumed_mult,
+ &max, &payload)) {
+ fprintf(stderr, "WARNING: Parse error: %s", buffer);
+ continue;
+ }
+ full_size = size;
+ switch(size_mult[0]) {
+ case 'G':
+ full_size *= 1024;
+ /* FALLTHRU */
+ case 'M':
+ full_size *= 1024;
+ /* FALLTHRU */
+ case 'K':
+ full_size *= 1024;
+ /* FALLTHRU */
+ case 'b':
+ break;
+ }
+ full_consumed = consumed;
+ switch(consumed_mult[0]) {
+ case 'G':
+ full_consumed *= 1024;
+ /* FALLTHRU */
+ case 'M':
+ full_consumed *= 1024;
+ /* FALLTHRU */
+ case 'K':
+ full_consumed *= 1024;
+ /* FALLTHRU */
+ case 'b':
+ break;
+ }
+ EXPECT_GT((full_size * 9) / 4, full_consumed);
+ EXPECT_GT(full_size, max);
+ EXPECT_GT(max, payload);
+
+ if ((((full_size * 9) / 4) >= full_consumed)
+ && (full_size > max)
+ && (max > payload)) {
+ ++count;
+ }
+ }
+
+ pclose(fp);
+
+ ASSERT_EQ(4, count);
+}
+
+static void caught_blocking(int /*signum*/)
+{
+ unsigned long long v = 0xDEADBEEFA55A0000ULL;
+
+ v += getpid() & 0xFFFF;
+
+ LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v)));
+}
+
+TEST(logcat, blocking) {
+ FILE *fp;
+ unsigned long long v = 0xDEADBEEFA55F0000ULL;
+
+ pid_t pid = getpid();
+
+ v += pid & 0xFFFF;
+
+ LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v)));
+
+ v &= 0xFFFFFFFFFFFAFFFFULL;
+
+ ASSERT_TRUE(NULL != (fp = popen(
+ "( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&"
+ " logcat -v brief -b events 2>&1",
+ "r")));
+
+ char buffer[5120];
+
+ int count = 0;
+
+ int signals = 0;
+
+ signal(SIGALRM, caught_blocking);
+ alarm(2);
+ while (fgets(buffer, sizeof(buffer), fp)) {
+
+ if (!strncmp(buffer, "DONE", 4)) {
+ break;
+ }
+
+ ++count;
+
+ int p;
+ unsigned long long l;
+
+ if ((2 != sscanf(buffer, "I/[0] ( %u): %lld", &p, &l))
+ || (p != pid)) {
+ continue;
+ }
+
+ if (l == v) {
+ ++signals;
+ break;
+ }
+ }
+ alarm(0);
+ signal(SIGALRM, SIG_DFL);
+
+ // Generate SIGPIPE
+ fclose(fp);
+ caught_blocking(0);
+
+ pclose(fp);
+
+ EXPECT_LE(2, count);
+
+ EXPECT_EQ(1, signals);
+}
+
+static void caught_blocking_tail(int /*signum*/)
+{
+ unsigned long long v = 0xA55ADEADBEEF0000ULL;
+
+ v += getpid() & 0xFFFF;
+
+ LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v)));
+}
+
+TEST(logcat, blocking_tail) {
+ FILE *fp;
+ unsigned long long v = 0xA55FDEADBEEF0000ULL;
+
+ pid_t pid = getpid();
+
+ v += pid & 0xFFFF;
+
+ LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v)));
+
+ v &= 0xFFFAFFFFFFFFFFFFULL;
+
+ ASSERT_TRUE(NULL != (fp = popen(
+ "( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&"
+ " logcat -v brief -b events -T 5 2>&1",
+ "r")));
+
+ char buffer[5120];
+
+ int count = 0;
+
+ int signals = 0;
+
+ signal(SIGALRM, caught_blocking_tail);
+ alarm(2);
+ while (fgets(buffer, sizeof(buffer), fp)) {
+
+ if (!strncmp(buffer, "DONE", 4)) {
+ break;
+ }
+
+ ++count;
+
+ int p;
+ unsigned long long l;
+
+ if ((2 != sscanf(buffer, "I/[0] ( %u): %lld", &p, &l))
+ || (p != pid)) {
+ continue;
+ }
+
+ if (l == v) {
+ if (count >= 5) {
+ ++signals;
+ }
+ break;
+ }
+ }
+ alarm(0);
+ signal(SIGALRM, SIG_DFL);
+
+ // Generate SIGPIPE
+ fclose(fp);
+ caught_blocking_tail(0);
+
+ pclose(fp);
+
+ EXPECT_LE(2, count);
+
+ EXPECT_EQ(1, signals);
+}
+
+TEST(logcat, logrotate) {
+ static const char form[] = "/data/local/tmp/logcat.logrotate.XXXXXX";
+ char buf[sizeof(form)];
+ ASSERT_TRUE(NULL != mkdtemp(strcpy(buf, form)));
+
+ static const char comm[] = "logcat -b radio -b events -b system -b main"
+ " -d -f %s/log.txt -n 7 -r 1";
+ char command[sizeof(buf) + sizeof(comm)];
+ sprintf(command, comm, buf);
+
+ int ret;
+ EXPECT_FALSE((ret = system(command)));
+ if (!ret) {
+ sprintf(command, "ls -s %s 2>/dev/null", buf);
+
+ FILE *fp;
+ EXPECT_TRUE(NULL != (fp = popen(command, "r")));
+ if (fp) {
+ char buffer[5120];
+ int count = 0;
+
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ static const char match_1[] = "4 log.txt";
+ static const char match_2[] = "8 log.txt";
+ static const char match_3[] = "12 log.txt";
+ static const char match_4[] = "16 log.txt";
+ static const char total[] = "total ";
+
+ if (!strncmp(buffer, match_1, sizeof(match_1) - 1)
+ || !strncmp(buffer, match_2, sizeof(match_2) - 1)
+ || !strncmp(buffer, match_3, sizeof(match_3) - 1)
+ || !strncmp(buffer, match_4, sizeof(match_4) - 1)) {
+ ++count;
+ } else if (strncmp(buffer, total, sizeof(total) - 1)) {
+ fprintf(stderr, "WARNING: Parse error: %s", buffer);
+ }
+ }
+ pclose(fp);
+ EXPECT_TRUE(count == 7 || count == 8);
+ }
+ }
+ sprintf(command, "rm -rf %s", buf);
+ EXPECT_FALSE(system(command));
+}
+
+TEST(logcat, logrotate_suffix) {
+ static const char tmp_out_dir_form[] = "/data/local/tmp/logcat.logrotate.XXXXXX";
+ char tmp_out_dir[sizeof(tmp_out_dir_form)];
+ ASSERT_TRUE(NULL != mkdtemp(strcpy(tmp_out_dir, tmp_out_dir_form)));
+
+ static const char logcat_cmd[] = "logcat -b radio -b events -b system -b main"
+ " -d -f %s/log.txt -n 10 -r 1";
+ char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd)];
+ sprintf(command, logcat_cmd, tmp_out_dir);
+
+ int ret;
+ EXPECT_FALSE((ret = system(command)));
+ if (!ret) {
+ sprintf(command, "ls %s 2>/dev/null", tmp_out_dir);
+
+ FILE *fp;
+ EXPECT_TRUE(NULL != (fp = popen(command, "r")));
+ char buffer[5120];
+ int log_file_count = 0;
+
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ static const char rotated_log_filename_prefix[] = "log.txt.";
+ static const size_t rotated_log_filename_prefix_len =
+ strlen(rotated_log_filename_prefix);
+ static const char log_filename[] = "log.txt";
+
+ if (!strncmp(buffer, rotated_log_filename_prefix, rotated_log_filename_prefix_len)) {
+ // Rotated file should have form log.txt.##
+ char* rotated_log_filename_suffix = buffer + rotated_log_filename_prefix_len;
+ char* endptr;
+ const long int suffix_value = strtol(rotated_log_filename_suffix, &endptr, 10);
+ EXPECT_EQ(rotated_log_filename_suffix + 2, endptr);
+ EXPECT_LE(suffix_value, 10);
+ EXPECT_GT(suffix_value, 0);
+ ++log_file_count;
+ continue;
+ }
+
+ if (!strncmp(buffer, log_filename, strlen(log_filename))) {
+ ++log_file_count;
+ continue;
+ }
+
+ fprintf(stderr, "ERROR: Unexpected file: %s", buffer);
+ ADD_FAILURE();
+ }
+ pclose(fp);
+ EXPECT_EQ(11, log_file_count);
+ }
+ sprintf(command, "rm -rf %s", tmp_out_dir);
+ EXPECT_FALSE(system(command));
+}
+
+static void caught_blocking_clear(int /*signum*/)
+{
+ unsigned long long v = 0xDEADBEEFA55C0000ULL;
+
+ v += getpid() & 0xFFFF;
+
+ LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v)));
+}
+
+TEST(logcat, blocking_clear) {
+ FILE *fp;
+ unsigned long long v = 0xDEADBEEFA55C0000ULL;
+
+ pid_t pid = getpid();
+
+ v += pid & 0xFFFF;
+
+ // This test is racey; an event occurs between clear and dump.
+ // We accept that we will get a false positive, but never a false negative.
+ ASSERT_TRUE(NULL != (fp = popen(
+ "( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&"
+ " logcat -b events -c 2>&1 ;"
+ " logcat -v brief -b events 2>&1",
+ "r")));
+
+ char buffer[5120];
+
+ int count = 0;
+
+ int signals = 0;
+
+ signal(SIGALRM, caught_blocking_clear);
+ alarm(2);
+ while (fgets(buffer, sizeof(buffer), fp)) {
+
+ if (!strncmp(buffer, "clearLog: ", 10)) {
+ fprintf(stderr, "WARNING: Test lacks permission to run :-(\n");
+ count = signals = 1;
+ break;
+ }
+
+ if (!strncmp(buffer, "DONE", 4)) {
+ break;
+ }
+
+ ++count;
+
+ int p;
+ unsigned long long l;
+
+ if ((2 != sscanf(buffer, "I/[0] ( %u): %lld", &p, &l))
+ || (p != pid)) {
+ continue;
+ }
+
+ if (l == v) {
+ if (count > 1) {
+ fprintf(stderr, "WARNING: Possible false positive\n");
+ }
+ ++signals;
+ break;
+ }
+ }
+ alarm(0);
+ signal(SIGALRM, SIG_DFL);
+
+ // Generate SIGPIPE
+ fclose(fp);
+ caught_blocking_clear(0);
+
+ pclose(fp);
+
+ EXPECT_LE(1, count);
+
+ EXPECT_EQ(1, signals);
+}
+
+static bool get_white_black(char **list) {
+ FILE *fp;
+
+ fp = popen("logcat -p 2>/dev/null", "r");
+ if (fp == NULL) {
+ fprintf(stderr, "ERROR: logcat -p 2>/dev/null\n");
+ return false;
+ }
+
+ char buffer[5120];
+
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ char *hold = *list;
+ char *buf = buffer;
+ while (isspace(*buf)) {
+ ++buf;
+ }
+ char *end = buf + strlen(buf);
+ while (isspace(*--end) && (end >= buf)) {
+ *end = '\0';
+ }
+ if (end < buf) {
+ continue;
+ }
+ if (hold) {
+ asprintf(list, "%s %s", hold, buf);
+ free(hold);
+ } else {
+ asprintf(list, "%s", buf);
+ }
+ }
+ pclose(fp);
+ return *list != NULL;
+}
+
+static bool set_white_black(const char *list) {
+ FILE *fp;
+
+ char buffer[5120];
+
+ snprintf(buffer, sizeof(buffer), "logcat -P '%s' 2>&1", list ? list : "");
+ fp = popen(buffer, "r");
+ if (fp == NULL) {
+ fprintf(stderr, "ERROR: %s\n", buffer);
+ return false;
+ }
+
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ char *buf = buffer;
+ while (isspace(*buf)) {
+ ++buf;
+ }
+ char *end = buf + strlen(buf);
+ while ((end > buf) && isspace(*--end)) {
+ *end = '\0';
+ }
+ if (end <= buf) {
+ continue;
+ }
+ fprintf(stderr, "%s\n", buf);
+ pclose(fp);
+ return false;
+ }
+ return pclose(fp) == 0;
+}
+
+TEST(logcat, white_black_adjust) {
+ char *list = NULL;
+ char *adjust = NULL;
+
+ get_white_black(&list);
+
+ static const char adjustment[] = "~! 300/20 300/25 2000 ~1000/5 ~1000/30";
+ ASSERT_EQ(true, set_white_black(adjustment));
+ ASSERT_EQ(true, get_white_black(&adjust));
+ EXPECT_STREQ(adjustment, adjust);
+ free(adjust);
+ adjust = NULL;
+
+ static const char adjustment2[] = "300/20 300/21 2000 ~1000";
+ ASSERT_EQ(true, set_white_black(adjustment2));
+ ASSERT_EQ(true, get_white_black(&adjust));
+ EXPECT_STREQ(adjustment2, adjust);
+ free(adjust);
+ adjust = NULL;
+
+ ASSERT_EQ(true, set_white_black(list));
+ get_white_black(&adjust);
+ EXPECT_STREQ(list ? list : "", adjust ? adjust : "");
+ free(adjust);
+ adjust = NULL;
+
+ free(list);
+ list = NULL;
+}
diff --git a/logd/Android.mk b/logd/Android.mk
new file mode 100644
index 0000000..615d030
--- /dev/null
+++ b/logd/Android.mk
@@ -0,0 +1,55 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE:= logd
+
+LOCAL_SRC_FILES := \
+ main.cpp \
+ LogCommand.cpp \
+ CommandListener.cpp \
+ LogListener.cpp \
+ LogReader.cpp \
+ FlushCommand.cpp \
+ LogBuffer.cpp \
+ LogBufferElement.cpp \
+ LogTimes.cpp \
+ LogStatistics.cpp \
+ LogWhiteBlackList.cpp \
+ libaudit.c \
+ LogAudit.cpp \
+ LogKlog.cpp \
+ event.logtags
+
+LOCAL_SHARED_LIBRARIES := \
+ libsysutils \
+ liblog \
+ libcutils \
+ libutils
+
+# This is what we want to do:
+# event_logtags = $(shell \
+# sed -n \
+# "s/^\([0-9]*\)[ \t]*$1[ \t].*/-D`echo $1 | tr a-z A-Z`_LOG_TAG=\1/p" \
+# $(LOCAL_PATH)/$2/event.logtags)
+# event_flag := $(call event_logtags,auditd)
+# event_flag += $(call event_logtags,logd)
+# so make sure we do not regret hard-coding it as follows:
+event_flag := -DAUDITD_LOG_TAG=1003 -DLOGD_LOG_TAG=1004
+
+LOCAL_CFLAGS := -Werror $(event_flag)
+
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := logpersist.start
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE_PATH := $(bin_dir)
+LOCAL_SRC_FILES := logpersist
+ALL_TOOLS := logpersist.start logpersist.stop logpersist.cat
+LOCAL_POST_INSTALL_CMD := $(hide) $(foreach t,$(filter-out $(LOCAL_MODULE),$(ALL_TOOLS)),ln -sf $(LOCAL_MODULE) $(TARGET_OUT)/bin/$(t);)
+include $(BUILD_PREBUILT)
+
+include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
new file mode 100644
index 0000000..489bea6
--- /dev/null
+++ b/logd/CommandListener.cpp
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2012-2014 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 <arpa/inet.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/prctl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <cutils/sockets.h>
+#include <private/android_filesystem_config.h>
+#include <sysutils/SocketClient.h>
+
+#include "CommandListener.h"
+#include "LogCommand.h"
+
+CommandListener::CommandListener(LogBuffer *buf, LogReader * /*reader*/,
+ LogListener * /*swl*/) :
+ FrameworkListener(getLogSocket()) {
+ // registerCmd(new ShutdownCmd(buf, writer, swl));
+ registerCmd(new ClearCmd(buf));
+ registerCmd(new GetBufSizeCmd(buf));
+ registerCmd(new SetBufSizeCmd(buf));
+ registerCmd(new GetBufSizeUsedCmd(buf));
+ registerCmd(new GetStatisticsCmd(buf));
+ registerCmd(new SetPruneListCmd(buf));
+ registerCmd(new GetPruneListCmd(buf));
+ registerCmd(new ReinitCmd());
+}
+
+CommandListener::ShutdownCmd::ShutdownCmd(LogReader *reader,
+ LogListener *swl) :
+ LogCommand("shutdown"),
+ mReader(*reader),
+ mSwl(*swl) {
+}
+
+int CommandListener::ShutdownCmd::runCommand(SocketClient * /*cli*/,
+ int /*argc*/,
+ char ** /*argv*/) {
+ mSwl.stopListener();
+ mReader.stopListener();
+ exit(0);
+}
+
+CommandListener::ClearCmd::ClearCmd(LogBuffer *buf) :
+ LogCommand("clear"),
+ mBuf(*buf) {
+}
+
+static void setname() {
+ static bool name_set;
+ if (!name_set) {
+ prctl(PR_SET_NAME, "logd.control");
+ name_set = true;
+ }
+}
+
+int CommandListener::ClearCmd::runCommand(SocketClient *cli,
+ int argc, char **argv) {
+ setname();
+ uid_t uid = cli->getUid();
+ if (clientHasLogCredentials(cli)) {
+ uid = AID_ROOT;
+ }
+
+ if (argc < 2) {
+ cli->sendMsg("Missing Argument");
+ return 0;
+ }
+
+ int id = atoi(argv[1]);
+ if ((id < LOG_ID_MIN) || (LOG_ID_MAX <= id)) {
+ cli->sendMsg("Range Error");
+ return 0;
+ }
+
+ mBuf.clear((log_id_t) id, uid);
+ cli->sendMsg("success");
+ return 0;
+}
+
+CommandListener::GetBufSizeCmd::GetBufSizeCmd(LogBuffer *buf) :
+ LogCommand("getLogSize"),
+ mBuf(*buf) {
+}
+
+int CommandListener::GetBufSizeCmd::runCommand(SocketClient *cli,
+ int argc, char **argv) {
+ setname();
+ if (argc < 2) {
+ cli->sendMsg("Missing Argument");
+ return 0;
+ }
+
+ int id = atoi(argv[1]);
+ if ((id < LOG_ID_MIN) || (LOG_ID_MAX <= id)) {
+ cli->sendMsg("Range Error");
+ return 0;
+ }
+
+ unsigned long size = mBuf.getSize((log_id_t) id);
+ char buf[512];
+ snprintf(buf, sizeof(buf), "%lu", size);
+ cli->sendMsg(buf);
+ return 0;
+}
+
+CommandListener::SetBufSizeCmd::SetBufSizeCmd(LogBuffer *buf) :
+ LogCommand("setLogSize"),
+ mBuf(*buf) {
+}
+
+int CommandListener::SetBufSizeCmd::runCommand(SocketClient *cli,
+ int argc, char **argv) {
+ setname();
+ if (!clientHasLogCredentials(cli)) {
+ cli->sendMsg("Permission Denied");
+ return 0;
+ }
+
+ if (argc < 3) {
+ cli->sendMsg("Missing Argument");
+ return 0;
+ }
+
+ int id = atoi(argv[1]);
+ if ((id < LOG_ID_MIN) || (LOG_ID_MAX <= id)) {
+ cli->sendMsg("Range Error");
+ return 0;
+ }
+
+ unsigned long size = atol(argv[2]);
+ if (mBuf.setSize((log_id_t) id, size)) {
+ cli->sendMsg("Range Error");
+ return 0;
+ }
+
+ cli->sendMsg("success");
+ return 0;
+}
+
+CommandListener::GetBufSizeUsedCmd::GetBufSizeUsedCmd(LogBuffer *buf) :
+ LogCommand("getLogSizeUsed"),
+ mBuf(*buf) {
+}
+
+int CommandListener::GetBufSizeUsedCmd::runCommand(SocketClient *cli,
+ int argc, char **argv) {
+ setname();
+ if (argc < 2) {
+ cli->sendMsg("Missing Argument");
+ return 0;
+ }
+
+ int id = atoi(argv[1]);
+ if ((id < LOG_ID_MIN) || (LOG_ID_MAX <= id)) {
+ cli->sendMsg("Range Error");
+ return 0;
+ }
+
+ unsigned long size = mBuf.getSizeUsed((log_id_t) id);
+ char buf[512];
+ snprintf(buf, sizeof(buf), "%lu", size);
+ cli->sendMsg(buf);
+ return 0;
+}
+
+CommandListener::GetStatisticsCmd::GetStatisticsCmd(LogBuffer *buf) :
+ LogCommand("getStatistics"),
+ mBuf(*buf) {
+}
+
+static void package_string(char **strp) {
+ const char *a = *strp;
+ if (!a) {
+ a = "";
+ }
+
+ // Calculate total buffer size prefix, count is the string length w/o nul
+ char fmt[32];
+ for(size_t l = strlen(a), y = 0, x = 6; y != x; y = x, x = strlen(fmt) - 2) {
+ snprintf(fmt, sizeof(fmt), "%zu\n%%s\n\f", l + x);
+ }
+
+ char *b = *strp;
+ *strp = NULL;
+ asprintf(strp, fmt, a);
+ free(b);
+}
+
+int CommandListener::GetStatisticsCmd::runCommand(SocketClient *cli,
+ int argc, char **argv) {
+ setname();
+ uid_t uid = cli->getUid();
+ if (clientHasLogCredentials(cli)) {
+ uid = AID_ROOT;
+ }
+
+ unsigned int logMask = -1;
+ if (argc > 1) {
+ logMask = 0;
+ for (int i = 1; i < argc; ++i) {
+ int id = atoi(argv[i]);
+ if ((id < LOG_ID_MIN) || (LOG_ID_MAX <= id)) {
+ cli->sendMsg("Range Error");
+ return 0;
+ }
+ logMask |= 1 << id;
+ }
+ }
+
+ char *buf = NULL;
+
+ mBuf.formatStatistics(&buf, uid, logMask);
+ if (!buf) {
+ cli->sendMsg("Failed");
+ } else {
+ package_string(&buf);
+ cli->sendMsg(buf);
+ free(buf);
+ }
+ return 0;
+}
+
+CommandListener::GetPruneListCmd::GetPruneListCmd(LogBuffer *buf) :
+ LogCommand("getPruneList"),
+ mBuf(*buf) {
+}
+
+int CommandListener::GetPruneListCmd::runCommand(SocketClient *cli,
+ int /*argc*/, char ** /*argv*/) {
+ setname();
+ char *buf = NULL;
+ mBuf.formatPrune(&buf);
+ if (!buf) {
+ cli->sendMsg("Failed");
+ } else {
+ package_string(&buf);
+ cli->sendMsg(buf);
+ free(buf);
+ }
+ return 0;
+}
+
+CommandListener::SetPruneListCmd::SetPruneListCmd(LogBuffer *buf) :
+ LogCommand("setPruneList"),
+ mBuf(*buf) {
+}
+
+int CommandListener::SetPruneListCmd::runCommand(SocketClient *cli,
+ int argc, char **argv) {
+ setname();
+ if (!clientHasLogCredentials(cli)) {
+ cli->sendMsg("Permission Denied");
+ return 0;
+ }
+
+ char *cp = NULL;
+ for (int i = 1; i < argc; ++i) {
+ char *p = cp;
+ if (p) {
+ cp = NULL;
+ asprintf(&cp, "%s %s", p, argv[i]);
+ free(p);
+ } else {
+ asprintf(&cp, "%s", argv[i]);
+ }
+ }
+
+ int ret = mBuf.initPrune(cp);
+ free(cp);
+
+ if (ret) {
+ cli->sendMsg("Invalid");
+ return 0;
+ }
+
+ cli->sendMsg("success");
+
+ return 0;
+}
+
+CommandListener::ReinitCmd::ReinitCmd() : LogCommand("reinit") {
+}
+
+int CommandListener::ReinitCmd::runCommand(SocketClient *cli,
+ int /*argc*/, char ** /*argv*/) {
+ setname();
+
+ reinit_signal_handler(SIGHUP);
+
+ cli->sendMsg("success");
+
+ return 0;
+}
+
+int CommandListener::getLogSocket() {
+ static const char socketName[] = "logd";
+ int sock = android_get_control_socket(socketName);
+
+ if (sock < 0) {
+ sock = socket_local_server(socketName,
+ ANDROID_SOCKET_NAMESPACE_RESERVED,
+ SOCK_STREAM);
+ }
+
+ return sock;
+}
diff --git a/logd/CommandListener.h b/logd/CommandListener.h
new file mode 100644
index 0000000..3877675
--- /dev/null
+++ b/logd/CommandListener.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2012-2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _COMMANDLISTENER_H__
+#define _COMMANDLISTENER_H__
+
+#include <sysutils/FrameworkListener.h>
+#include "LogCommand.h"
+#include "LogBuffer.h"
+#include "LogReader.h"
+#include "LogListener.h"
+
+// See main.cpp for implementation
+void reinit_signal_handler(int /*signal*/);
+
+class CommandListener : public FrameworkListener {
+
+public:
+ CommandListener(LogBuffer *buf, LogReader *reader, LogListener *swl);
+ virtual ~CommandListener() {}
+
+private:
+ static int getLogSocket();
+
+ class ShutdownCmd : public LogCommand {
+ LogReader &mReader;
+ LogListener &mSwl;
+
+ public:
+ ShutdownCmd(LogReader *reader, LogListener *swl);
+ virtual ~ShutdownCmd() {}
+ int runCommand(SocketClient *c, int argc, char ** argv);
+ };
+
+#define LogBufferCmd(name) \
+ class name##Cmd : public LogCommand { \
+ LogBuffer &mBuf; \
+ public: \
+ name##Cmd(LogBuffer *buf); \
+ virtual ~name##Cmd() {} \
+ int runCommand(SocketClient *c, int argc, char ** argv); \
+ };
+
+ LogBufferCmd(Clear)
+ LogBufferCmd(GetBufSize)
+ LogBufferCmd(SetBufSize)
+ LogBufferCmd(GetBufSizeUsed)
+ LogBufferCmd(GetStatistics)
+ LogBufferCmd(GetPruneList)
+ LogBufferCmd(SetPruneList)
+
+ class ReinitCmd : public LogCommand {
+ public:
+ ReinitCmd();
+ virtual ~ReinitCmd() {}
+ int runCommand(SocketClient *c, int argc, char ** argv);
+ };
+
+};
+
+#endif
diff --git a/logd/FlushCommand.cpp b/logd/FlushCommand.cpp
new file mode 100644
index 0000000..d584925
--- /dev/null
+++ b/logd/FlushCommand.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2012-2014 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 "FlushCommand.h"
+#include "LogBufferElement.h"
+#include "LogCommand.h"
+#include "LogReader.h"
+#include "LogTimes.h"
+
+FlushCommand::FlushCommand(LogReader &reader,
+ bool nonBlock,
+ unsigned long tail,
+ unsigned int logMask,
+ pid_t pid,
+ uint64_t start) :
+ mReader(reader),
+ mNonBlock(nonBlock),
+ mTail(tail),
+ mLogMask(logMask),
+ mPid(pid),
+ mStart(start) {
+}
+
+// runSocketCommand is called once for every open client on the
+// log reader socket. Here we manage and associated the reader
+// client tracking and log region locks LastLogTimes list of
+// LogTimeEntrys, and spawn a transitory per-client thread to
+// work at filing data to the socket.
+//
+// global LogTimeEntry::lock() is used to protect access,
+// reference counts are used to ensure that individual
+// LogTimeEntry lifetime is managed when not protected.
+void FlushCommand::runSocketCommand(SocketClient *client) {
+ LogTimeEntry *entry = NULL;
+ LastLogTimes × = mReader.logbuf().mTimes;
+
+ LogTimeEntry::lock();
+ LastLogTimes::iterator it = times.begin();
+ while(it != times.end()) {
+ entry = (*it);
+ if (entry->mClient == client) {
+ entry->triggerReader_Locked();
+ if (entry->runningReader_Locked()) {
+ LogTimeEntry::unlock();
+ return;
+ }
+ entry->incRef_Locked();
+ break;
+ }
+ it++;
+ }
+
+ if (it == times.end()) {
+ // Create LogTimeEntry in notifyNewLog() ?
+ if (mTail == (unsigned long) -1) {
+ LogTimeEntry::unlock();
+ return;
+ }
+ entry = new LogTimeEntry(mReader, client, mNonBlock, mTail, mLogMask, mPid, mStart);
+ times.push_back(entry);
+ }
+
+ client->incRef();
+
+ // release client and entry reference counts once done
+ entry->startReader_Locked();
+ LogTimeEntry::unlock();
+}
+
+bool FlushCommand::hasReadLogs(SocketClient *client) {
+ return clientHasLogCredentials(client);
+}
diff --git a/logd/FlushCommand.h b/logd/FlushCommand.h
new file mode 100644
index 0000000..61c6858
--- /dev/null
+++ b/logd/FlushCommand.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2012-2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _FLUSH_COMMAND_H
+#define _FLUSH_COMMAND_H
+
+#include <log/log_read.h>
+#include <sysutils/SocketClientCommand.h>
+
+class LogBufferElement;
+
+#include "LogTimes.h"
+
+class LogReader;
+
+class FlushCommand : public SocketClientCommand {
+ LogReader &mReader;
+ bool mNonBlock;
+ unsigned long mTail;
+ unsigned int mLogMask;
+ pid_t mPid;
+ uint64_t mStart;
+
+public:
+ FlushCommand(LogReader &mReader,
+ bool nonBlock = false,
+ unsigned long tail = -1,
+ unsigned int logMask = -1,
+ pid_t pid = 0,
+ uint64_t start = 1);
+ virtual void runSocketCommand(SocketClient *client);
+
+ static bool hasReadLogs(SocketClient *client);
+};
+
+#endif
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
new file mode 100644
index 0000000..4b3547c
--- /dev/null
+++ b/logd/LogAudit.cpp
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2014 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 <ctype.h>
+#include <endian.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <sys/prctl.h>
+#include <sys/uio.h>
+#include <syslog.h>
+
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+
+#include "libaudit.h"
+#include "LogAudit.h"
+#include "LogKlog.h"
+
+#define KMSG_PRIORITY(PRI) \
+ '<', \
+ '0' + LOG_MAKEPRI(LOG_AUTH, LOG_PRI(PRI)) / 10, \
+ '0' + LOG_MAKEPRI(LOG_AUTH, LOG_PRI(PRI)) % 10, \
+ '>'
+
+LogAudit::LogAudit(LogBuffer *buf, LogReader *reader, int fdDmesg) :
+ SocketListener(getLogSocket(), false),
+ logbuf(buf),
+ reader(reader),
+ fdDmesg(fdDmesg),
+ initialized(false) {
+ static const char auditd_message[] = { KMSG_PRIORITY(LOG_INFO),
+ 'l', 'o', 'g', 'd', '.', 'a', 'u', 'd', 'i', 't', 'd', ':',
+ ' ', 's', 't', 'a', 'r', 't', '\n' };
+ write(fdDmesg, auditd_message, sizeof(auditd_message));
+}
+
+bool LogAudit::onDataAvailable(SocketClient *cli) {
+ if (!initialized) {
+ prctl(PR_SET_NAME, "logd.auditd");
+ initialized = true;
+ }
+
+ struct audit_message rep;
+
+ rep.nlh.nlmsg_type = 0;
+ rep.nlh.nlmsg_len = 0;
+ rep.data[0] = '\0';
+
+ if (audit_get_reply(cli->getSocket(), &rep, GET_REPLY_BLOCKING, 0) < 0) {
+ SLOGE("Failed on audit_get_reply with error: %s", strerror(errno));
+ return false;
+ }
+
+ logPrint("type=%d %.*s",
+ rep.nlh.nlmsg_type, rep.nlh.nlmsg_len, rep.data);
+
+ return true;
+}
+
+int LogAudit::logPrint(const char *fmt, ...) {
+ if (fmt == NULL) {
+ return -EINVAL;
+ }
+
+ va_list args;
+
+ char *str = NULL;
+ va_start(args, fmt);
+ int rc = vasprintf(&str, fmt, args);
+ va_end(args);
+
+ if (rc < 0) {
+ return rc;
+ }
+
+ char *cp;
+ while ((cp = strstr(str, " "))) {
+ memmove(cp, cp + 1, strlen(cp + 1) + 1);
+ }
+
+ bool info = strstr(str, " permissive=1") || strstr(str, " policy loaded ");
+ if ((fdDmesg >= 0) && initialized) {
+ struct iovec iov[3];
+ static const char log_info[] = { KMSG_PRIORITY(LOG_INFO) };
+ static const char log_warning[] = { KMSG_PRIORITY(LOG_WARNING) };
+
+ iov[0].iov_base = info ? const_cast<char *>(log_info)
+ : const_cast<char *>(log_warning);
+ iov[0].iov_len = info ? sizeof(log_info) : sizeof(log_warning);
+ iov[1].iov_base = str;
+ iov[1].iov_len = strlen(str);
+ iov[2].iov_base = const_cast<char *>("\n");
+ iov[2].iov_len = 1;
+
+ writev(fdDmesg, iov, sizeof(iov) / sizeof(iov[0]));
+ }
+
+ pid_t pid = getpid();
+ pid_t tid = gettid();
+ uid_t uid = AID_LOGD;
+ log_time now;
+
+ static const char audit_str[] = " audit(";
+ char *timeptr = strstr(str, audit_str);
+ if (timeptr
+ && ((cp = now.strptime(timeptr + sizeof(audit_str) - 1, "%s.%q")))
+ && (*cp == ':')) {
+ memcpy(timeptr + sizeof(audit_str) - 1, "0.0", 3);
+ memmove(timeptr + sizeof(audit_str) - 1 + 3, cp, strlen(cp) + 1);
+ //
+ // We are either in 1970ish (MONOTONIC) or 2015+ish (REALTIME) so to
+ // differentiate without prejudice, we use 1980 to delineate, earlier
+ // is monotonic, later is real.
+ //
+# define EPOCH_PLUS_10_YEARS (10 * 1461 / 4 * 24 * 60 * 60)
+ if (now.tv_sec < EPOCH_PLUS_10_YEARS) {
+ LogKlog::convertMonotonicToReal(now);
+ }
+ } else {
+ now.strptime("", ""); // side effect of setting CLOCK_REALTIME
+ }
+
+ static const char pid_str[] = " pid=";
+ char *pidptr = strstr(str, pid_str);
+ if (pidptr && isdigit(pidptr[sizeof(pid_str) - 1])) {
+ cp = pidptr + sizeof(pid_str) - 1;
+ pid = 0;
+ while (isdigit(*cp)) {
+ pid = (pid * 10) + (*cp - '0');
+ ++cp;
+ }
+ tid = pid;
+ logbuf->lock();
+ uid = logbuf->pidToUid(pid);
+ logbuf->unlock();
+ memmove(pidptr, cp, strlen(cp) + 1);
+ }
+
+ // log to events
+
+ size_t l = strlen(str);
+ size_t n = l + sizeof(android_log_event_string_t);
+
+ bool notify = false;
+
+ android_log_event_string_t *event = static_cast<android_log_event_string_t *>(malloc(n));
+ if (!event) {
+ rc = -ENOMEM;
+ } else {
+ event->header.tag = htole32(AUDITD_LOG_TAG);
+ event->type = EVENT_TYPE_STRING;
+ event->length = htole32(l);
+ memcpy(event->data, str, l);
+
+ rc = logbuf->log(LOG_ID_EVENTS, now, uid, pid, tid,
+ reinterpret_cast<char *>(event),
+ (n <= USHRT_MAX) ? (unsigned short) n : USHRT_MAX);
+ free(event);
+
+ if (rc >= 0) {
+ notify = true;
+ }
+ }
+
+ // log to main
+
+ static const char comm_str[] = " comm=\"";
+ const char *comm = strstr(str, comm_str);
+ const char *estr = str + strlen(str);
+ char *commfree = NULL;
+ if (comm) {
+ estr = comm;
+ comm += sizeof(comm_str) - 1;
+ } else if (pid == getpid()) {
+ pid = tid;
+ comm = "auditd";
+ } else {
+ logbuf->lock();
+ comm = commfree = logbuf->pidToName(pid);
+ logbuf->unlock();
+ if (!comm) {
+ comm = "unknown";
+ }
+ }
+
+ const char *ecomm = strchr(comm, '"');
+ if (ecomm) {
+ ++ecomm;
+ l = ecomm - comm;
+ } else {
+ l = strlen(comm) + 1;
+ ecomm = "";
+ }
+ n = (estr - str) + strlen(ecomm) + l + 2;
+
+ char *newstr = static_cast<char *>(malloc(n));
+ if (!newstr) {
+ rc = -ENOMEM;
+ } else {
+ *newstr = info ? ANDROID_LOG_INFO : ANDROID_LOG_WARN;
+ strlcpy(newstr + 1, comm, l);
+ strncpy(newstr + 1 + l, str, estr - str);
+ strcpy(newstr + 1 + l + (estr - str), ecomm);
+
+ rc = logbuf->log(LOG_ID_MAIN, now, uid, pid, tid, newstr,
+ (n <= USHRT_MAX) ? (unsigned short) n : USHRT_MAX);
+ free(newstr);
+
+ if (rc >= 0) {
+ notify = true;
+ }
+ }
+
+ free(commfree);
+ free(str);
+
+ if (notify) {
+ reader->notifyNewLog();
+ if (rc < 0) {
+ rc = n;
+ }
+ }
+
+ return rc;
+}
+
+int LogAudit::log(char *buf) {
+ char *audit = strstr(buf, " audit(");
+ if (!audit) {
+ return 0;
+ }
+
+ *audit = '\0';
+
+ int rc;
+ char *type = strstr(buf, "type=");
+ if (type) {
+ rc = logPrint("%s %s", type, audit + 1);
+ } else {
+ rc = logPrint("%s", audit + 1);
+ }
+ *audit = ' ';
+ return rc;
+}
+
+int LogAudit::getLogSocket() {
+ int fd = audit_open();
+ if (fd < 0) {
+ return fd;
+ }
+ if (audit_setup(fd, getpid()) < 0) {
+ audit_close(fd);
+ fd = -1;
+ }
+ return fd;
+}
diff --git a/logd/LogAudit.h b/logd/LogAudit.h
new file mode 100644
index 0000000..f977be9
--- /dev/null
+++ b/logd/LogAudit.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LOGD_LOG_AUDIT_H__
+#define _LOGD_LOG_AUDIT_H__
+
+#include <sysutils/SocketListener.h>
+#include "LogReader.h"
+
+class LogAudit : public SocketListener {
+ LogBuffer *logbuf;
+ LogReader *reader;
+ int fdDmesg;
+ bool initialized;
+
+public:
+ LogAudit(LogBuffer *buf, LogReader *reader, int fdDmesg);
+ int log(char *buf);
+
+protected:
+ virtual bool onDataAvailable(SocketClient *cli);
+
+private:
+ static int getLogSocket();
+ int logPrint(const char *fmt, ...)
+ __attribute__ ((__format__ (__printf__, 2, 3)));
+};
+
+#endif
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
new file mode 100644
index 0000000..0f5071b
--- /dev/null
+++ b/logd/LogBuffer.cpp
@@ -0,0 +1,679 @@
+/*
+ * Copyright (C) 2012-2014 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 <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/user.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <unordered_map>
+
+#include <cutils/properties.h>
+#include <log/logger.h>
+
+#include "LogBuffer.h"
+#include "LogReader.h"
+
+// Default
+#define LOG_BUFFER_SIZE (256 * 1024) // Tuned on a per-platform basis here?
+#define log_buffer_size(id) mMaxSize[id]
+#define LOG_BUFFER_MIN_SIZE (64 * 1024UL)
+#define LOG_BUFFER_MAX_SIZE (256 * 1024 * 1024UL)
+
+static bool valid_size(unsigned long value) {
+ if ((value < LOG_BUFFER_MIN_SIZE) || (LOG_BUFFER_MAX_SIZE < value)) {
+ return false;
+ }
+
+ long pages = sysconf(_SC_PHYS_PAGES);
+ if (pages < 1) {
+ return true;
+ }
+
+ long pagesize = sysconf(_SC_PAGESIZE);
+ if (pagesize <= 1) {
+ pagesize = PAGE_SIZE;
+ }
+
+ // maximum memory impact a somewhat arbitrary ~3%
+ pages = (pages + 31) / 32;
+ unsigned long maximum = pages * pagesize;
+
+ if ((maximum < LOG_BUFFER_MIN_SIZE) || (LOG_BUFFER_MAX_SIZE < maximum)) {
+ return true;
+ }
+
+ return value <= maximum;
+}
+
+static unsigned long property_get_size(const char *key) {
+ char property[PROPERTY_VALUE_MAX];
+ property_get(key, property, "");
+
+ char *cp;
+ unsigned long value = strtoul(property, &cp, 10);
+
+ switch(*cp) {
+ case 'm':
+ case 'M':
+ value *= 1024;
+ /* FALLTHRU */
+ case 'k':
+ case 'K':
+ value *= 1024;
+ /* FALLTHRU */
+ case '\0':
+ break;
+
+ default:
+ value = 0;
+ }
+
+ if (!valid_size(value)) {
+ value = 0;
+ }
+
+ return value;
+}
+
+void LogBuffer::init() {
+ static const char global_tuneable[] = "persist.logd.size"; // Settings App
+ static const char global_default[] = "ro.logd.size"; // BoardConfig.mk
+
+ unsigned long default_size = property_get_size(global_tuneable);
+ if (!default_size) {
+ default_size = property_get_size(global_default);
+ }
+
+ log_id_for_each(i) {
+ char key[PROP_NAME_MAX];
+
+ snprintf(key, sizeof(key), "%s.%s",
+ global_tuneable, android_log_id_to_name(i));
+ unsigned long property_size = property_get_size(key);
+
+ if (!property_size) {
+ snprintf(key, sizeof(key), "%s.%s",
+ global_default, android_log_id_to_name(i));
+ property_size = property_get_size(key);
+ }
+
+ if (!property_size) {
+ property_size = default_size;
+ }
+
+ if (!property_size) {
+ property_size = LOG_BUFFER_SIZE;
+ }
+
+ if (setSize(i, property_size)) {
+ setSize(i, LOG_BUFFER_MIN_SIZE);
+ }
+ }
+}
+
+LogBuffer::LogBuffer(LastLogTimes *times) : mTimes(*times) {
+ pthread_mutex_init(&mLogElementsLock, NULL);
+
+ init();
+}
+
+int LogBuffer::log(log_id_t log_id, log_time realtime,
+ uid_t uid, pid_t pid, pid_t tid,
+ const char *msg, unsigned short len) {
+ if ((log_id >= LOG_ID_MAX) || (log_id < 0)) {
+ return -EINVAL;
+ }
+
+ LogBufferElement *elem = new LogBufferElement(log_id, realtime,
+ uid, pid, tid, msg, len);
+ int prio = ANDROID_LOG_INFO;
+ const char *tag = NULL;
+ if (log_id == LOG_ID_EVENTS) {
+ tag = android::tagToName(elem->getTag());
+ } else {
+ prio = *msg;
+ tag = msg + 1;
+ }
+ if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
+ // Log traffic received to total
+ pthread_mutex_lock(&mLogElementsLock);
+ stats.add(elem);
+ stats.subtract(elem);
+ pthread_mutex_unlock(&mLogElementsLock);
+ delete elem;
+ return -EACCES;
+ }
+
+ pthread_mutex_lock(&mLogElementsLock);
+
+ // Insert elements in time sorted order if possible
+ // NB: if end is region locked, place element at end of list
+ LogBufferElementCollection::iterator it = mLogElements.end();
+ LogBufferElementCollection::iterator last = it;
+ while (last != mLogElements.begin()) {
+ --it;
+ if ((*it)->getRealTime() <= realtime) {
+ break;
+ }
+ last = it;
+ }
+
+ if (last == mLogElements.end()) {
+ mLogElements.push_back(elem);
+ } else {
+ uint64_t end = 1;
+ bool end_set = false;
+ bool end_always = false;
+
+ LogTimeEntry::lock();
+
+ LastLogTimes::iterator t = mTimes.begin();
+ while(t != mTimes.end()) {
+ LogTimeEntry *entry = (*t);
+ if (entry->owned_Locked()) {
+ if (!entry->mNonBlock) {
+ end_always = true;
+ break;
+ }
+ if (!end_set || (end <= entry->mEnd)) {
+ end = entry->mEnd;
+ end_set = true;
+ }
+ }
+ t++;
+ }
+
+ if (end_always
+ || (end_set && (end >= (*last)->getSequence()))) {
+ mLogElements.push_back(elem);
+ } else {
+ mLogElements.insert(last,elem);
+ }
+
+ LogTimeEntry::unlock();
+ }
+
+ stats.add(elem);
+ maybePrune(log_id);
+ pthread_mutex_unlock(&mLogElementsLock);
+
+ return len;
+}
+
+// If we're using more than 256K of memory for log entries, prune
+// at least 10% of the log entries.
+//
+// mLogElementsLock must be held when this function is called.
+void LogBuffer::maybePrune(log_id_t id) {
+ size_t sizes = stats.sizes(id);
+ if (sizes > log_buffer_size(id)) {
+ size_t sizeOver90Percent = sizes - ((log_buffer_size(id) * 9) / 10);
+ size_t elements = stats.elements(id);
+ unsigned long pruneRows = elements * sizeOver90Percent / sizes;
+ elements /= 10;
+ if (pruneRows <= elements) {
+ pruneRows = elements;
+ }
+ prune(id, pruneRows);
+ }
+}
+
+LogBufferElementCollection::iterator LogBuffer::erase(LogBufferElementCollection::iterator it) {
+ LogBufferElement *e = *it;
+
+ it = mLogElements.erase(it);
+ stats.subtract(e);
+ delete e;
+
+ return it;
+}
+
+// Define a temporary mechanism to report the last LogBufferElement pointer
+// for the specified uid, pid and tid. Used below to help merge-sort when
+// pruning for worst UID.
+class LogBufferElementKey {
+ const union {
+ struct {
+ uint16_t uid;
+ uint16_t pid;
+ uint16_t tid;
+ uint16_t padding;
+ } __packed;
+ uint64_t value;
+ } __packed;
+
+public:
+ LogBufferElementKey(uid_t u, pid_t p, pid_t t):uid(u),pid(p),tid(t),padding(0) { }
+ LogBufferElementKey(uint64_t k):value(k) { }
+
+ uint64_t getKey() { return value; }
+};
+
+class LogBufferElementLast {
+
+ typedef std::unordered_map<uint64_t, LogBufferElement *> LogBufferElementMap;
+ LogBufferElementMap map;
+
+public:
+
+ bool merge(LogBufferElement *e, unsigned short dropped) {
+ LogBufferElementKey key(e->getUid(), e->getPid(), e->getTid());
+ LogBufferElementMap::iterator it = map.find(key.getKey());
+ if (it != map.end()) {
+ LogBufferElement *l = it->second;
+ unsigned short d = l->getDropped();
+ if ((dropped + d) > USHRT_MAX) {
+ map.erase(it);
+ } else {
+ l->setDropped(dropped + d);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void add(LogBufferElement *e) {
+ LogBufferElementKey key(e->getUid(), e->getPid(), e->getTid());
+ map[key.getKey()] = e;
+ }
+
+ inline void clear() {
+ map.clear();
+ }
+
+ void clear(LogBufferElement *e) {
+ uint64_t current = e->getRealTime().nsec()
+ - (EXPIRE_RATELIMIT * NS_PER_SEC);
+ for(LogBufferElementMap::iterator it = map.begin(); it != map.end();) {
+ LogBufferElement *l = it->second;
+ if ((l->getDropped() >= EXPIRE_THRESHOLD)
+ && (current > l->getRealTime().nsec())) {
+ it = map.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+
+};
+
+// prune "pruneRows" of type "id" from the buffer.
+//
+// mLogElementsLock must be held when this function is called.
+void LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
+ LogTimeEntry *oldest = NULL;
+
+ LogTimeEntry::lock();
+
+ // Region locked?
+ LastLogTimes::iterator t = mTimes.begin();
+ while(t != mTimes.end()) {
+ LogTimeEntry *entry = (*t);
+ if (entry->owned_Locked() && entry->isWatching(id)
+ && (!oldest || (oldest->mStart > entry->mStart))) {
+ oldest = entry;
+ }
+ t++;
+ }
+
+ LogBufferElementCollection::iterator it;
+
+ if (caller_uid != AID_ROOT) {
+ for(it = mLogElements.begin(); it != mLogElements.end();) {
+ LogBufferElement *e = *it;
+
+ if (oldest && (oldest->mStart <= e->getSequence())) {
+ break;
+ }
+
+ if (e->getLogId() != id) {
+ ++it;
+ continue;
+ }
+
+ if (e->getUid() == caller_uid) {
+ it = erase(it);
+ pruneRows--;
+ if (pruneRows == 0) {
+ break;
+ }
+ } else {
+ ++it;
+ }
+ }
+ LogTimeEntry::unlock();
+ return;
+ }
+
+ // prune by worst offender by uid
+ bool hasBlacklist = mPrune.naughty();
+ while (pruneRows > 0) {
+ // recalculate the worst offender on every batched pass
+ uid_t worst = (uid_t) -1;
+ size_t worst_sizes = 0;
+ size_t second_worst_sizes = 0;
+
+ if (worstUidEnabledForLogid(id) && mPrune.worstUidEnabled()) {
+ std::unique_ptr<const UidEntry *[]> sorted = stats.sort(2, id);
+
+ if (sorted.get()) {
+ if (sorted[0] && sorted[1]) {
+ worst_sizes = sorted[0]->getSizes();
+ // Calculate threshold as 12.5% of available storage
+ size_t threshold = log_buffer_size(id) / 8;
+ if (worst_sizes > threshold) {
+ worst = sorted[0]->getKey();
+ second_worst_sizes = sorted[1]->getSizes();
+ if (second_worst_sizes < threshold) {
+ second_worst_sizes = threshold;
+ }
+ }
+ }
+ }
+ }
+
+ // skip if we have neither worst nor naughty filters
+ if ((worst == (uid_t) -1) && !hasBlacklist) {
+ break;
+ }
+
+ bool kick = false;
+ bool leading = true;
+ LogBufferElementLast last;
+ for(it = mLogElements.begin(); it != mLogElements.end();) {
+ LogBufferElement *e = *it;
+
+ if (oldest && (oldest->mStart <= e->getSequence())) {
+ break;
+ }
+
+ if (e->getLogId() != id) {
+ ++it;
+ continue;
+ }
+
+ unsigned short dropped = e->getDropped();
+
+ // remove any leading drops
+ if (leading && dropped) {
+ it = erase(it);
+ continue;
+ }
+
+ // merge any drops
+ if (dropped && last.merge(e, dropped)) {
+ it = mLogElements.erase(it);
+ stats.erase(e);
+ delete e;
+ continue;
+ }
+
+ if (hasBlacklist && mPrune.naughty(e)) {
+ last.clear(e);
+ it = erase(it);
+ if (dropped) {
+ continue;
+ }
+
+ pruneRows--;
+ if (pruneRows == 0) {
+ break;
+ }
+
+ if (e->getUid() == worst) {
+ kick = true;
+ if (worst_sizes < second_worst_sizes) {
+ break;
+ }
+ worst_sizes -= e->getMsgLen();
+ }
+ continue;
+ }
+
+ if (dropped) {
+ last.add(e);
+ ++it;
+ continue;
+ }
+
+ if (e->getUid() != worst) {
+ if (leading) {
+ static const timespec too_old = {
+ EXPIRE_HOUR_THRESHOLD * 60 * 60, 0
+ };
+ LogBufferElementCollection::iterator last;
+ last = mLogElements.end();
+ --last;
+ if ((e->getRealTime() < ((*last)->getRealTime() - too_old))
+ || (e->getRealTime() > (*last)->getRealTime())) {
+ break;
+ }
+ }
+ leading = false;
+ last.clear(e);
+ ++it;
+ continue;
+ }
+
+ pruneRows--;
+ if (pruneRows == 0) {
+ break;
+ }
+
+ kick = true;
+
+ unsigned short len = e->getMsgLen();
+
+ // do not create any leading drops
+ if (leading) {
+ it = erase(it);
+ } else {
+ stats.drop(e);
+ e->setDropped(1);
+ if (last.merge(e, 1)) {
+ it = mLogElements.erase(it);
+ stats.erase(e);
+ delete e;
+ } else {
+ last.add(e);
+ ++it;
+ }
+ }
+ if (worst_sizes < second_worst_sizes) {
+ break;
+ }
+ worst_sizes -= len;
+ }
+ last.clear();
+
+ if (!kick || !mPrune.worstUidEnabled()) {
+ break; // the following loop will ask bad clients to skip/drop
+ }
+ }
+
+ bool whitelist = false;
+ bool hasWhitelist = mPrune.nice();
+ it = mLogElements.begin();
+ while((pruneRows > 0) && (it != mLogElements.end())) {
+ LogBufferElement *e = *it;
+
+ if (e->getLogId() != id) {
+ it++;
+ continue;
+ }
+
+ if (oldest && (oldest->mStart <= e->getSequence())) {
+ if (whitelist) {
+ break;
+ }
+
+ if (stats.sizes(id) > (2 * log_buffer_size(id))) {
+ // kick a misbehaving log reader client off the island
+ oldest->release_Locked();
+ } else {
+ oldest->triggerSkip_Locked(id, pruneRows);
+ }
+ break;
+ }
+
+ if (hasWhitelist && !e->getDropped() && mPrune.nice(e)) { // WhiteListed
+ whitelist = true;
+ it++;
+ continue;
+ }
+
+ it = erase(it);
+ pruneRows--;
+ }
+
+ // Do not save the whitelist if we are reader range limited
+ if (whitelist && (pruneRows > 0)) {
+ it = mLogElements.begin();
+ while((it != mLogElements.end()) && (pruneRows > 0)) {
+ LogBufferElement *e = *it;
+
+ if (e->getLogId() != id) {
+ ++it;
+ continue;
+ }
+
+ if (oldest && (oldest->mStart <= e->getSequence())) {
+ if (stats.sizes(id) > (2 * log_buffer_size(id))) {
+ // kick a misbehaving log reader client off the island
+ oldest->release_Locked();
+ } else {
+ oldest->triggerSkip_Locked(id, pruneRows);
+ }
+ break;
+ }
+
+ it = erase(it);
+ pruneRows--;
+ }
+ }
+
+ LogTimeEntry::unlock();
+}
+
+// clear all rows of type "id" from the buffer.
+void LogBuffer::clear(log_id_t id, uid_t uid) {
+ pthread_mutex_lock(&mLogElementsLock);
+ prune(id, ULONG_MAX, uid);
+ pthread_mutex_unlock(&mLogElementsLock);
+}
+
+// get the used space associated with "id".
+unsigned long LogBuffer::getSizeUsed(log_id_t id) {
+ pthread_mutex_lock(&mLogElementsLock);
+ size_t retval = stats.sizes(id);
+ pthread_mutex_unlock(&mLogElementsLock);
+ return retval;
+}
+
+// set the total space allocated to "id"
+int LogBuffer::setSize(log_id_t id, unsigned long size) {
+ // Reasonable limits ...
+ if (!valid_size(size)) {
+ return -1;
+ }
+ pthread_mutex_lock(&mLogElementsLock);
+ log_buffer_size(id) = size;
+ pthread_mutex_unlock(&mLogElementsLock);
+ return 0;
+}
+
+// get the total space allocated to "id"
+unsigned long LogBuffer::getSize(log_id_t id) {
+ pthread_mutex_lock(&mLogElementsLock);
+ size_t retval = log_buffer_size(id);
+ pthread_mutex_unlock(&mLogElementsLock);
+ return retval;
+}
+
+uint64_t LogBuffer::flushTo(
+ SocketClient *reader, const uint64_t start, bool privileged,
+ int (*filter)(const LogBufferElement *element, void *arg), void *arg) {
+ LogBufferElementCollection::iterator it;
+ uint64_t max = start;
+ uid_t uid = reader->getUid();
+
+ pthread_mutex_lock(&mLogElementsLock);
+
+ if (start <= 1) {
+ // client wants to start from the beginning
+ it = mLogElements.begin();
+ } else {
+ // Client wants to start from some specified time. Chances are
+ // we are better off starting from the end of the time sorted list.
+ for (it = mLogElements.end(); it != mLogElements.begin(); /* do nothing */) {
+ --it;
+ LogBufferElement *element = *it;
+ if (element->getSequence() <= start) {
+ it++;
+ break;
+ }
+ }
+ }
+
+ for (; it != mLogElements.end(); ++it) {
+ LogBufferElement *element = *it;
+
+ if (!privileged && (element->getUid() != uid)) {
+ continue;
+ }
+
+ if (element->getSequence() <= start) {
+ continue;
+ }
+
+ // NB: calling out to another object with mLogElementsLock held (safe)
+ if (filter) {
+ int ret = (*filter)(element, arg);
+ if (ret == false) {
+ continue;
+ }
+ if (ret != true) {
+ break;
+ }
+ }
+
+ pthread_mutex_unlock(&mLogElementsLock);
+
+ // range locking in LastLogTimes looks after us
+ max = element->flushTo(reader, this);
+
+ if (max == element->FLUSH_ERROR) {
+ return max;
+ }
+
+ pthread_mutex_lock(&mLogElementsLock);
+ }
+ pthread_mutex_unlock(&mLogElementsLock);
+
+ return max;
+}
+
+void LogBuffer::formatStatistics(char **strp, uid_t uid, unsigned int logMask) {
+ pthread_mutex_lock(&mLogElementsLock);
+
+ stats.format(strp, uid, logMask);
+
+ pthread_mutex_unlock(&mLogElementsLock);
+}
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
new file mode 100644
index 0000000..a13fded
--- /dev/null
+++ b/logd/LogBuffer.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2012-2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LOGD_LOG_BUFFER_H__
+#define _LOGD_LOG_BUFFER_H__
+
+#include <sys/types.h>
+
+#include <log/log.h>
+#include <sysutils/SocketClient.h>
+#include <utils/List.h>
+
+#include <private/android_filesystem_config.h>
+
+#include "LogBufferElement.h"
+#include "LogTimes.h"
+#include "LogStatistics.h"
+#include "LogWhiteBlackList.h"
+
+typedef android::List<LogBufferElement *> LogBufferElementCollection;
+
+class LogBuffer {
+ LogBufferElementCollection mLogElements;
+ pthread_mutex_t mLogElementsLock;
+
+ LogStatistics stats;
+
+ PruneList mPrune;
+
+ unsigned long mMaxSize[LOG_ID_MAX];
+
+public:
+ LastLogTimes &mTimes;
+
+ LogBuffer(LastLogTimes *times);
+ void init();
+
+ int log(log_id_t log_id, log_time realtime,
+ uid_t uid, pid_t pid, pid_t tid,
+ const char *msg, unsigned short len);
+ uint64_t flushTo(SocketClient *writer, const uint64_t start,
+ bool privileged,
+ int (*filter)(const LogBufferElement *element, void *arg) = NULL,
+ void *arg = NULL);
+
+ void clear(log_id_t id, uid_t uid = AID_ROOT);
+ unsigned long getSize(log_id_t id);
+ int setSize(log_id_t id, unsigned long size);
+ unsigned long getSizeUsed(log_id_t id);
+ // *strp uses malloc, use free to release.
+ void formatStatistics(char **strp, uid_t uid, unsigned int logMask);
+
+ void enableStatistics() {
+ stats.enableStatistics();
+ }
+
+ int initPrune(char *cp) { return mPrune.init(cp); }
+ // *strp uses malloc, use free to release.
+ void formatPrune(char **strp) { mPrune.format(strp); }
+
+ // helper must be protected directly or implicitly by lock()/unlock()
+ char *pidToName(pid_t pid) { return stats.pidToName(pid); }
+ uid_t pidToUid(pid_t pid) { return stats.pidToUid(pid); }
+ char *uidToName(uid_t uid) { return stats.uidToName(uid); }
+ void lock() { pthread_mutex_lock(&mLogElementsLock); }
+ void unlock() { pthread_mutex_unlock(&mLogElementsLock); }
+
+private:
+ void maybePrune(log_id_t id);
+ void prune(log_id_t id, unsigned long pruneRows, uid_t uid = AID_ROOT);
+ LogBufferElementCollection::iterator erase(LogBufferElementCollection::iterator it);
+};
+
+#endif // _LOGD_LOG_BUFFER_H__
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
new file mode 100644
index 0000000..9fb1439
--- /dev/null
+++ b/logd/LogBufferElement.cpp
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2012-2014 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 <ctype.h>
+#include <endian.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <log/logger.h>
+#include <private/android_logger.h>
+
+#include "LogBufferElement.h"
+#include "LogCommand.h"
+#include "LogReader.h"
+
+const uint64_t LogBufferElement::FLUSH_ERROR(0);
+atomic_int_fast64_t LogBufferElement::sequence(1);
+
+LogBufferElement::LogBufferElement(log_id_t log_id, log_time realtime,
+ uid_t uid, pid_t pid, pid_t tid,
+ const char *msg, unsigned short len) :
+ mLogId(log_id),
+ mUid(uid),
+ mPid(pid),
+ mTid(tid),
+ mMsgLen(len),
+ mSequence(sequence.fetch_add(1, memory_order_relaxed)),
+ mRealTime(realtime) {
+ mMsg = new char[len];
+ memcpy(mMsg, msg, len);
+}
+
+LogBufferElement::~LogBufferElement() {
+ delete [] mMsg;
+}
+
+uint32_t LogBufferElement::getTag() const {
+ if ((mLogId != LOG_ID_EVENTS) || !mMsg || (mMsgLen < sizeof(uint32_t))) {
+ return 0;
+ }
+ return le32toh(reinterpret_cast<android_event_header_t *>(mMsg)->tag);
+}
+
+// caller must own and free character string
+char *android::tidToName(pid_t tid) {
+ char *retval = NULL;
+ char buffer[256];
+ snprintf(buffer, sizeof(buffer), "/proc/%u/comm", tid);
+ int fd = open(buffer, O_RDONLY);
+ if (fd >= 0) {
+ ssize_t ret = read(fd, buffer, sizeof(buffer));
+ if (ret >= (ssize_t)sizeof(buffer)) {
+ ret = sizeof(buffer) - 1;
+ }
+ while ((ret > 0) && isspace(buffer[ret - 1])) {
+ --ret;
+ }
+ if (ret > 0) {
+ buffer[ret] = '\0';
+ retval = strdup(buffer);
+ }
+ close(fd);
+ }
+
+ // if nothing for comm, check out cmdline
+ char *name = android::pidToName(tid);
+ if (!retval) {
+ retval = name;
+ name = NULL;
+ }
+
+ // check if comm is truncated, see if cmdline has full representation
+ if (name) {
+ // impossible for retval to be NULL if name not NULL
+ size_t retval_len = strlen(retval);
+ size_t name_len = strlen(name);
+ // KISS: ToDo: Only checks prefix truncated, not suffix, or both
+ if ((retval_len < name_len) && !strcmp(retval, name + name_len - retval_len)) {
+ free(retval);
+ retval = name;
+ } else {
+ free(name);
+ }
+ }
+ return retval;
+}
+
+// assumption: mMsg == NULL
+size_t LogBufferElement::populateDroppedMessage(char *&buffer,
+ LogBuffer *parent) {
+ static const char tag[] = "chatty";
+
+ if (!__android_log_is_loggable(ANDROID_LOG_INFO, tag, ANDROID_LOG_VERBOSE)) {
+ return 0;
+ }
+
+ static const char format_uid[] = "uid=%u%s%s expire %u line%s";
+ parent->lock();
+ char *name = parent->uidToName(mUid);
+ parent->unlock();
+ char *commName = android::tidToName(mTid);
+ if (!commName && (mTid != mPid)) {
+ commName = android::tidToName(mPid);
+ }
+ if (!commName) {
+ parent->lock();
+ commName = parent->pidToName(mPid);
+ parent->unlock();
+ }
+ size_t len = name ? strlen(name) : 0;
+ if (len && commName && !strncmp(name, commName, len)) {
+ if (commName[len] == '\0') {
+ free(commName);
+ commName = NULL;
+ } else {
+ free(name);
+ name = NULL;
+ }
+ }
+ if (name) {
+ char *p = NULL;
+ asprintf(&p, "(%s)", name);
+ if (p) {
+ free(name);
+ name = p;
+ }
+ }
+ if (commName) {
+ char *p = NULL;
+ asprintf(&p, " %s", commName);
+ if (p) {
+ free(commName);
+ commName = p;
+ }
+ }
+ // identical to below to calculate the buffer size required
+ len = snprintf(NULL, 0, format_uid, mUid, name ? name : "",
+ commName ? commName : "",
+ mDropped, (mDropped > 1) ? "s" : "");
+
+ size_t hdrLen;
+ if (mLogId == LOG_ID_EVENTS) {
+ hdrLen = sizeof(android_log_event_string_t);
+ } else {
+ hdrLen = 1 + sizeof(tag);
+ }
+
+ buffer = static_cast<char *>(calloc(1, hdrLen + len + 1));
+ if (!buffer) {
+ free(name);
+ free(commName);
+ return 0;
+ }
+
+ size_t retval = hdrLen + len;
+ if (mLogId == LOG_ID_EVENTS) {
+ android_log_event_string_t *e = reinterpret_cast<android_log_event_string_t *>(buffer);
+
+ e->header.tag = htole32(LOGD_LOG_TAG);
+ e->type = EVENT_TYPE_STRING;
+ e->length = htole32(len);
+ } else {
+ ++retval;
+ buffer[0] = ANDROID_LOG_INFO;
+ strcpy(buffer + 1, tag);
+ }
+
+ snprintf(buffer + hdrLen, len + 1, format_uid, mUid, name ? name : "",
+ commName ? commName : "",
+ mDropped, (mDropped > 1) ? "s" : "");
+ free(name);
+ free(commName);
+
+ return retval;
+}
+
+uint64_t LogBufferElement::flushTo(SocketClient *reader, LogBuffer *parent) {
+ struct logger_entry_v3 entry;
+
+ memset(&entry, 0, sizeof(struct logger_entry_v3));
+
+ entry.hdr_size = sizeof(struct logger_entry_v3);
+ entry.lid = mLogId;
+ entry.pid = mPid;
+ entry.tid = mTid;
+ entry.sec = mRealTime.tv_sec;
+ entry.nsec = mRealTime.tv_nsec;
+
+ struct iovec iovec[2];
+ iovec[0].iov_base = &entry;
+ iovec[0].iov_len = sizeof(struct logger_entry_v3);
+
+ char *buffer = NULL;
+
+ if (!mMsg) {
+ entry.len = populateDroppedMessage(buffer, parent);
+ if (!entry.len) {
+ return mSequence;
+ }
+ iovec[1].iov_base = buffer;
+ } else {
+ entry.len = mMsgLen;
+ iovec[1].iov_base = mMsg;
+ }
+ iovec[1].iov_len = entry.len;
+
+ uint64_t retval = reader->sendDatav(iovec, 2) ? FLUSH_ERROR : mSequence;
+
+ if (buffer) {
+ free(buffer);
+ }
+
+ return retval;
+}
diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h
new file mode 100644
index 0000000..ca2c3a6
--- /dev/null
+++ b/logd/LogBufferElement.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2012-2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LOGD_LOG_BUFFER_ELEMENT_H__
+#define _LOGD_LOG_BUFFER_ELEMENT_H__
+
+#include <stdatomic.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#include <sysutils/SocketClient.h>
+#include <log/log.h>
+#include <log/log_read.h>
+
+// Hijack this header as a common include file used by most all sources
+// to report some utilities defined here and there.
+
+namespace android {
+
+// Furnished in main.cpp. Caller must own and free returned value
+char *uidToName(uid_t uid);
+
+// Furnished in LogStatistics.cpp. Caller must own and free returned value
+char *pidToName(pid_t pid);
+char *tidToName(pid_t tid);
+
+// Furnished in main.cpp. Thread safe.
+const char *tagToName(uint32_t tag);
+
+}
+
+static inline bool worstUidEnabledForLogid(log_id_t id) {
+ return (id != LOG_ID_CRASH) && (id != LOG_ID_KERNEL) && (id != LOG_ID_EVENTS);
+}
+
+class LogBuffer;
+
+#define EXPIRE_HOUR_THRESHOLD 24 // Only expire chatty UID logs to preserve
+ // non-chatty UIDs less than this age in hours
+#define EXPIRE_THRESHOLD 10 // A smaller expire count is considered too
+ // chatty for the temporal expire messages
+#define EXPIRE_RATELIMIT 10 // maximum rate in seconds to report expiration
+
+class LogBufferElement {
+ const log_id_t mLogId;
+ const uid_t mUid;
+ const pid_t mPid;
+ const pid_t mTid;
+ char *mMsg;
+ union {
+ const unsigned short mMsgLen; // mMSg != NULL
+ unsigned short mDropped; // mMsg == NULL
+ };
+ const uint64_t mSequence;
+ const log_time mRealTime;
+ static atomic_int_fast64_t sequence;
+
+ // assumption: mMsg == NULL
+ size_t populateDroppedMessage(char *&buffer,
+ LogBuffer *parent);
+
+public:
+ LogBufferElement(log_id_t log_id, log_time realtime,
+ uid_t uid, pid_t pid, pid_t tid,
+ const char *msg, unsigned short len);
+ virtual ~LogBufferElement();
+
+ log_id_t getLogId() const { return mLogId; }
+ uid_t getUid(void) const { return mUid; }
+ pid_t getPid(void) const { return mPid; }
+ pid_t getTid(void) const { return mTid; }
+ unsigned short getDropped(void) const { return mMsg ? 0 : mDropped; }
+ unsigned short setDropped(unsigned short value) {
+ if (mMsg) {
+ free(mMsg);
+ mMsg = NULL;
+ }
+ return mDropped = value;
+ }
+ unsigned short getMsgLen() const { return mMsg ? mMsgLen : 0; }
+ uint64_t getSequence(void) const { return mSequence; }
+ static uint64_t getCurrentSequence(void) { return sequence.load(memory_order_relaxed); }
+ log_time getRealTime(void) const { return mRealTime; }
+
+ uint32_t getTag(void) const;
+
+ static const uint64_t FLUSH_ERROR;
+ uint64_t flushTo(SocketClient *writer, LogBuffer *parent);
+};
+
+#endif
diff --git a/logd/LogCommand.cpp b/logd/LogCommand.cpp
new file mode 100644
index 0000000..06d865c
--- /dev/null
+++ b/logd/LogCommand.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2012-2014 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <private/android_filesystem_config.h>
+
+#include "LogCommand.h"
+
+LogCommand::LogCommand(const char *cmd) : FrameworkCommand(cmd) {
+}
+
+// gets a list of supplementary group IDs associated with
+// the socket peer. This is implemented by opening
+// /proc/PID/status and look for the "Group:" line.
+//
+// This function introduces races especially since status
+// can change 'shape' while reading, the net result is err
+// on lack of permission.
+//
+// Race-free alternative is to introduce pairs of sockets
+// and threads for each command and reading, one each that
+// has open permissions, and one that has restricted
+// permissions.
+
+static bool groupIsLog(char *buf) {
+ char *ptr;
+ static const char ws[] = " \n";
+ bool ret = false;
+
+ for (buf = strtok_r(buf, ws, &ptr); buf; buf = strtok_r(NULL, ws, &ptr)) {
+ errno = 0;
+ gid_t Gid = strtol(buf, NULL, 10);
+ if (errno != 0) {
+ return false;
+ }
+ if (Gid == AID_LOG) {
+ ret = true;
+ }
+ }
+ return ret;
+}
+
+bool clientHasLogCredentials(SocketClient * cli) {
+ uid_t uid = cli->getUid();
+ if (uid == AID_ROOT) {
+ return true;
+ }
+
+ gid_t gid = cli->getGid();
+ if ((gid == AID_ROOT) || (gid == AID_SYSTEM) || (gid == AID_LOG)) {
+ return true;
+ }
+
+ // FYI We will typically be here for 'adb logcat'
+ bool ret = false;
+
+ char filename[1024];
+ snprintf(filename, sizeof(filename), "/proc/%d/status", cli->getPid());
+
+ FILE *file = fopen(filename, "r");
+ if (!file) {
+ return ret;
+ }
+
+ bool foundGid = false;
+ bool foundUid = false;
+
+ char line[1024];
+ while (fgets(line, sizeof(line), file)) {
+ static const char groups_string[] = "Groups:\t";
+ static const char uid_string[] = "Uid:\t";
+ static const char gid_string[] = "Gid:\t";
+
+ if (strncmp(groups_string, line, strlen(groups_string)) == 0) {
+ ret = groupIsLog(line + strlen(groups_string));
+ if (!ret) {
+ break;
+ }
+ } else if (strncmp(uid_string, line, strlen(uid_string)) == 0) {
+ uid_t u[4] = { (uid_t) -1, (uid_t) -1, (uid_t) -1, (uid_t) -1};
+
+ sscanf(line + strlen(uid_string), "%u\t%u\t%u\t%u",
+ &u[0], &u[1], &u[2], &u[3]);
+
+ // Protect against PID reuse by checking that the UID is the same
+ if ((uid != u[0]) || (uid != u[1]) || (uid != u[2]) || (uid != u[3])) {
+ ret = false;
+ break;
+ }
+ foundUid = true;
+ } else if (strncmp(gid_string, line, strlen(gid_string)) == 0) {
+ gid_t g[4] = { (gid_t) -1, (gid_t) -1, (gid_t) -1, (gid_t) -1};
+
+ sscanf(line + strlen(gid_string), "%u\t%u\t%u\t%u",
+ &g[0], &g[1], &g[2], &g[3]);
+
+ // Protect against PID reuse by checking that the GID is the same
+ if ((gid != g[0]) || (gid != g[1]) || (gid != g[2]) || (gid != g[3])) {
+ ret = false;
+ break;
+ }
+ foundGid = true;
+ }
+ }
+
+ fclose(file);
+
+ if (!foundGid || !foundUid) {
+ ret = false;
+ }
+
+ return ret;
+}
diff --git a/logd/LogCommand.h b/logd/LogCommand.h
new file mode 100644
index 0000000..e3b96a2
--- /dev/null
+++ b/logd/LogCommand.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2012-2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LOGD_COMMAND_H
+#define _LOGD_COMMAND_H
+
+#include <sysutils/SocketClient.h>
+#include <sysutils/FrameworkCommand.h>
+
+class LogCommand : public FrameworkCommand {
+public:
+ LogCommand(const char *cmd);
+ virtual ~LogCommand() {}
+};
+
+bool clientHasLogCredentials(SocketClient * cli);
+
+#endif
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
new file mode 100644
index 0000000..eff26f5
--- /dev/null
+++ b/logd/LogKlog.cpp
@@ -0,0 +1,601 @@
+/*
+ * Copyright (C) 2014 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 <ctype.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <sys/prctl.h>
+#include <sys/uio.h>
+#include <syslog.h>
+
+#include <log/logger.h>
+
+#include "LogKlog.h"
+
+#define KMSG_PRIORITY(PRI) \
+ '<', \
+ '0' + (LOG_SYSLOG | (PRI)) / 10, \
+ '0' + (LOG_SYSLOG | (PRI)) % 10, \
+ '>'
+
+static const char priority_message[] = { KMSG_PRIORITY(LOG_INFO), '\0' };
+
+// Parsing is hard
+
+// called if we see a '<', s is the next character, returns pointer after '>'
+static char *is_prio(char *s) {
+ if (!isdigit(*s++)) {
+ return NULL;
+ }
+ static const size_t max_prio_len = 4;
+ size_t len = 0;
+ char c;
+ while (((c = *s++)) && (++len <= max_prio_len)) {
+ if (!isdigit(c)) {
+ return (c == '>') ? s : NULL;
+ }
+ }
+ return NULL;
+}
+
+// called if we see a '[', s is the next character, returns pointer after ']'
+static char *is_timestamp(char *s) {
+ while (*s == ' ') {
+ ++s;
+ }
+ if (!isdigit(*s++)) {
+ return NULL;
+ }
+ bool first_period = true;
+ char c;
+ while ((c = *s++)) {
+ if ((c == '.') && first_period) {
+ first_period = false;
+ } else if (!isdigit(c)) {
+ return ((c == ']') && !first_period && (*s == ' ')) ? s : NULL;
+ }
+ }
+ return NULL;
+}
+
+// Like strtok_r with "\r\n" except that we look for log signatures (regex)
+// \(\(<[0-9]\{1,4\}>\)\([[] *[0-9]+[.][0-9]+[]] \)\{0,1\}\|[[] *[0-9]+[.][0-9]+[]] \)
+// and split if we see a second one without a newline.
+
+#define SIGNATURE_MASK 0xF0
+// <digit> following ('0' to '9' masked with ~SIGNATURE_MASK) added to signature
+#define LESS_THAN_SIG SIGNATURE_MASK
+#define OPEN_BRACKET_SIG ((SIGNATURE_MASK << 1) & SIGNATURE_MASK)
+// space is one more than <digit> of 9
+#define OPEN_BRACKET_SPACE ((char)(OPEN_BRACKET_SIG | 10))
+
+char *log_strtok_r(char *s, char **last) {
+ if (!s) {
+ if (!(s = *last)) {
+ return NULL;
+ }
+ // fixup for log signature split <,
+ // LESS_THAN_SIG + <digit>
+ if ((*s & SIGNATURE_MASK) == LESS_THAN_SIG) {
+ *s = (*s & ~SIGNATURE_MASK) + '0';
+ *--s = '<';
+ }
+ // fixup for log signature split [,
+ // OPEN_BRACKET_SPACE is space, OPEN_BRACKET_SIG + <digit>
+ if ((*s & SIGNATURE_MASK) == OPEN_BRACKET_SIG) {
+ if (*s == OPEN_BRACKET_SPACE) {
+ *s = ' ';
+ } else {
+ *s = (*s & ~SIGNATURE_MASK) + '0';
+ }
+ *--s = '[';
+ }
+ }
+
+ s += strspn(s, "\r\n");
+
+ if (!*s) { // no non-delimiter characters
+ *last = NULL;
+ return NULL;
+ }
+ char *peek, *tok = s;
+
+ for (;;) {
+ char c = *s++;
+ switch (c) {
+ case '\0':
+ *last = NULL;
+ return tok;
+
+ case '\r':
+ case '\n':
+ s[-1] = '\0';
+ *last = s;
+ return tok;
+
+ case '<':
+ peek = is_prio(s);
+ if (!peek) {
+ break;
+ }
+ if (s != (tok + 1)) { // not first?
+ s[-1] = '\0';
+ *s &= ~SIGNATURE_MASK;
+ *s |= LESS_THAN_SIG; // signature for '<'
+ *last = s;
+ return tok;
+ }
+ s = peek;
+ if ((*s == '[') && ((peek = is_timestamp(s + 1)))) {
+ s = peek;
+ }
+ break;
+
+ case '[':
+ peek = is_timestamp(s);
+ if (!peek) {
+ break;
+ }
+ if (s != (tok + 1)) { // not first?
+ s[-1] = '\0';
+ if (*s == ' ') {
+ *s = OPEN_BRACKET_SPACE;
+ } else {
+ *s &= ~SIGNATURE_MASK;
+ *s |= OPEN_BRACKET_SIG; // signature for '['
+ }
+ *last = s;
+ return tok;
+ }
+ s = peek;
+ break;
+ }
+ }
+ // NOTREACHED
+}
+
+log_time LogKlog::correction = log_time(CLOCK_REALTIME) - log_time(CLOCK_MONOTONIC);
+
+LogKlog::LogKlog(LogBuffer *buf, LogReader *reader, int fdWrite, int fdRead, bool auditd) :
+ SocketListener(fdRead, false),
+ logbuf(buf),
+ reader(reader),
+ signature(CLOCK_MONOTONIC),
+ initialized(false),
+ enableLogging(true),
+ auditd(auditd) {
+ static const char klogd_message[] = "%slogd.klogd: %" PRIu64 "\n";
+ char buffer[sizeof(priority_message) + sizeof(klogd_message) + 20 - 4];
+ snprintf(buffer, sizeof(buffer), klogd_message, priority_message,
+ signature.nsec());
+ write(fdWrite, buffer, strlen(buffer));
+}
+
+bool LogKlog::onDataAvailable(SocketClient *cli) {
+ if (!initialized) {
+ prctl(PR_SET_NAME, "logd.klogd");
+ initialized = true;
+ enableLogging = false;
+ }
+
+ char buffer[LOGGER_ENTRY_MAX_PAYLOAD];
+ size_t len = 0;
+
+ for(;;) {
+ ssize_t retval = 0;
+ if ((sizeof(buffer) - 1 - len) > 0) {
+ retval = read(cli->getSocket(), buffer + len, sizeof(buffer) - 1 - len);
+ }
+ if ((retval == 0) && (len == 0)) {
+ break;
+ }
+ if (retval < 0) {
+ return false;
+ }
+ len += retval;
+ bool full = len == (sizeof(buffer) - 1);
+ char *ep = buffer + len;
+ *ep = '\0';
+ len = 0;
+ for(char *ptr = NULL, *tok = buffer;
+ ((tok = log_strtok_r(tok, &ptr)));
+ tok = NULL) {
+ if (((tok + strlen(tok)) == ep) && (retval != 0) && full) {
+ len = strlen(tok);
+ memmove(buffer, tok, len);
+ break;
+ }
+ if (*tok) {
+ log(tok);
+ }
+ }
+ }
+
+ return true;
+}
+
+
+void LogKlog::calculateCorrection(const log_time &monotonic,
+ const char *real_string) {
+ log_time real;
+ if (!real.strptime(real_string, "%Y-%m-%d %H:%M:%S.%09q UTC")) {
+ return;
+ }
+ // kernel report UTC, log_time::strptime is localtime from calendar.
+ // Bionic and liblog strptime does not support %z or %Z to pick up
+ // timezone so we are calculating our own correction.
+ time_t now = real.tv_sec;
+ struct tm tm;
+ memset(&tm, 0, sizeof(tm));
+ tm.tm_isdst = -1;
+ localtime_r(&now, &tm);
+ real.tv_sec += tm.tm_gmtoff;
+ correction = real - monotonic;
+}
+
+void LogKlog::sniffTime(log_time &now, const char **buf, bool reverse) {
+ const char *cp;
+ if ((cp = now.strptime(*buf, "[ %s.%q]"))) {
+ static const char suspend[] = "PM: suspend entry ";
+ static const char resume[] = "PM: suspend exit ";
+ static const char suspended[] = "Suspended for ";
+
+ if (isspace(*cp)) {
+ ++cp;
+ }
+ if (!strncmp(cp, suspend, sizeof(suspend) - 1)) {
+ calculateCorrection(now, cp + sizeof(suspend) - 1);
+ } else if (!strncmp(cp, resume, sizeof(resume) - 1)) {
+ calculateCorrection(now, cp + sizeof(resume) - 1);
+ } else if (!strncmp(cp, suspended, sizeof(suspended) - 1)) {
+ log_time real;
+ char *endp;
+ real.tv_sec = strtol(cp + sizeof(suspended) - 1, &endp, 10);
+ if (*endp == '.') {
+ real.tv_nsec = strtol(endp + 1, &endp, 10) * 1000000L;
+ if (reverse) {
+ correction -= real;
+ } else {
+ correction += real;
+ }
+ }
+ }
+
+ convertMonotonicToReal(now);
+ *buf = cp;
+ } else {
+ now = log_time(CLOCK_REALTIME);
+ }
+}
+
+// Passed the entire SYSLOG_ACTION_READ_ALL buffer and interpret a
+// compensated start time.
+void LogKlog::synchronize(const char *buf) {
+ const char *cp = strstr(buf, "] PM: suspend e");
+ if (!cp) {
+ return;
+ }
+
+ do {
+ --cp;
+ } while ((cp > buf) && (isdigit(*cp) || isspace(*cp) || (*cp == '.')));
+
+ log_time now;
+ sniffTime(now, &cp, true);
+
+ char *suspended = strstr(buf, "] Suspended for ");
+ if (!suspended || (suspended > cp)) {
+ return;
+ }
+ cp = suspended;
+
+ do {
+ --cp;
+ } while ((cp > buf) && (isdigit(*cp) || isspace(*cp) || (*cp == '.')));
+
+ sniffTime(now, &cp, true);
+}
+
+// kernel log prefix, convert to a kernel log priority number
+static int parseKernelPrio(const char **buf) {
+ int pri = LOG_USER | LOG_INFO;
+ const char *cp = *buf;
+ if (*cp == '<') {
+ pri = 0;
+ while(isdigit(*++cp)) {
+ pri = (pri * 10) + *cp - '0';
+ }
+ if (*cp == '>') {
+ ++cp;
+ } else {
+ cp = *buf;
+ pri = LOG_USER | LOG_INFO;
+ }
+ *buf = cp;
+ }
+ return pri;
+}
+
+// Convert kernel log priority number into an Android Logger priority number
+static int convertKernelPrioToAndroidPrio(int pri) {
+ switch(pri & LOG_PRIMASK) {
+ case LOG_EMERG:
+ // FALLTHRU
+ case LOG_ALERT:
+ // FALLTHRU
+ case LOG_CRIT:
+ return ANDROID_LOG_FATAL;
+
+ case LOG_ERR:
+ return ANDROID_LOG_ERROR;
+
+ case LOG_WARNING:
+ return ANDROID_LOG_WARN;
+
+ default:
+ // FALLTHRU
+ case LOG_NOTICE:
+ // FALLTHRU
+ case LOG_INFO:
+ break;
+
+ case LOG_DEBUG:
+ return ANDROID_LOG_DEBUG;
+ }
+
+ return ANDROID_LOG_INFO;
+}
+
+//
+// log a message into the kernel log buffer
+//
+// Filter rules to parse <PRI> <TIME> <tag> and <message> in order for
+// them to appear correct in the logcat output:
+//
+// LOG_KERN (0):
+// <PRI>[<TIME>] <tag> ":" <message>
+// <PRI>[<TIME>] <tag> <tag> ":" <message>
+// <PRI>[<TIME>] <tag> <tag>_work ":" <message>
+// <PRI>[<TIME>] <tag> '<tag>.<num>' ":" <message>
+// <PRI>[<TIME>] <tag> '<tag><num>' ":" <message>
+// <PRI>[<TIME>] <tag>_host '<tag>.<num>' ":" <message>
+// (unimplemented) <PRI>[<TIME>] <tag> '<num>.<tag>' ":" <message>
+// <PRI>[<TIME>] "[INFO]"<tag> : <message>
+// <PRI>[<TIME>] "------------[ cut here ]------------" (?)
+// <PRI>[<TIME>] "---[ end trace 3225a3070ca3e4ac ]---" (?)
+// LOG_USER, LOG_MAIL, LOG_DAEMON, LOG_AUTH, LOG_SYSLOG, LOG_LPR, LOG_NEWS
+// LOG_UUCP, LOG_CRON, LOG_AUTHPRIV, LOG_FTP:
+// <PRI+TAG>[<TIME>] (see sys/syslog.h)
+// Observe:
+// Minimum tag length = 3 NB: drops things like r5:c00bbadf, but allow PM:
+// Maximum tag words = 2
+// Maximum tag length = 16 NB: we are thinking of how ugly logcat can get.
+// Not a Tag if there is no message content.
+// leading additional spaces means no tag, inherit last tag.
+// Not a Tag if <tag>: is "ERROR:", "WARNING:", "INFO:" or "CPU:"
+// Drop:
+// empty messages
+// messages with ' audit(' in them if auditd is running
+// logd.klogd:
+// return -1 if message logd.klogd: <signature>
+//
+int LogKlog::log(const char *buf) {
+ if (auditd && strstr(buf, " audit(")) {
+ return 0;
+ }
+
+ int pri = parseKernelPrio(&buf);
+
+ log_time now;
+ sniffTime(now, &buf, false);
+
+ // sniff for start marker
+ const char klogd_message[] = "logd.klogd: ";
+ if (!strncmp(buf, klogd_message, sizeof(klogd_message) - 1)) {
+ char *endp;
+ uint64_t sig = strtoll(buf + sizeof(klogd_message) - 1, &endp, 10);
+ if (sig == signature.nsec()) {
+ if (initialized) {
+ enableLogging = true;
+ } else {
+ enableLogging = false;
+ }
+ return -1;
+ }
+ return 0;
+ }
+
+ if (!enableLogging) {
+ return 0;
+ }
+
+ // Parse pid, tid and uid (not possible)
+ const pid_t pid = 0;
+ const pid_t tid = 0;
+ const uid_t uid = 0;
+
+ // Parse (rules at top) to pull out a tag from the incoming kernel message.
+ // Some may view the following as an ugly heuristic, the desire is to
+ // beautify the kernel logs into an Android Logging format; the goal is
+ // admirable but costly.
+ while (isspace(*buf)) {
+ ++buf;
+ }
+ if (!*buf) {
+ return 0;
+ }
+ const char *start = buf;
+ const char *tag = "";
+ const char *etag = tag;
+ if (!isspace(*buf)) {
+ const char *bt, *et, *cp;
+
+ bt = buf;
+ if (!strncmp(buf, "[INFO]", 6)) {
+ // <PRI>[<TIME>] "[INFO]"<tag> ":" message
+ bt = buf + 6;
+ }
+ for(et = bt; *et && (*et != ':') && !isspace(*et); ++et);
+ for(cp = et; isspace(*cp); ++cp);
+ size_t size;
+
+ if (*cp == ':') {
+ // One Word
+ tag = bt;
+ etag = et;
+ buf = cp + 1;
+ } else {
+ size = et - bt;
+ if (strncmp(bt, cp, size)) {
+ // <PRI>[<TIME>] <tag>_host '<tag>.<num>' : message
+ if (!strncmp(bt + size - 5, "_host", 5)
+ && !strncmp(bt, cp, size - 5)) {
+ const char *b = cp;
+ cp += size - 5;
+ if (*cp == '.') {
+ while (!isspace(*++cp) && (*cp != ':'));
+ const char *e;
+ for(e = cp; isspace(*cp); ++cp);
+ if (*cp == ':') {
+ tag = b;
+ etag = e;
+ buf = cp + 1;
+ }
+ }
+ } else {
+ while (!isspace(*++cp) && (*cp != ':'));
+ const char *e;
+ for(e = cp; isspace(*cp); ++cp);
+ // Two words
+ if (*cp == ':') {
+ tag = bt;
+ etag = e;
+ buf = cp + 1;
+ }
+ }
+ } else if (isspace(cp[size])) {
+ cp += size;
+ while (isspace(*++cp));
+ // <PRI>[<TIME>] <tag> <tag> : message
+ if (*cp == ':') {
+ tag = bt;
+ etag = et;
+ buf = cp + 1;
+ }
+ } else if (cp[size] == ':') {
+ // <PRI>[<TIME>] <tag> <tag> : message
+ tag = bt;
+ etag = et;
+ buf = cp + size + 1;
+ } else if ((cp[size] == '.') || isdigit(cp[size])) {
+ // <PRI>[<TIME>] <tag> '<tag>.<num>' : message
+ // <PRI>[<TIME>] <tag> '<tag><num>' : message
+ const char *b = cp;
+ cp += size;
+ while (!isspace(*++cp) && (*cp != ':'));
+ const char *e = cp;
+ while (isspace(*cp)) {
+ ++cp;
+ }
+ if (*cp == ':') {
+ tag = b;
+ etag = e;
+ buf = cp + 1;
+ }
+ } else {
+ while (!isspace(*++cp) && (*cp != ':'));
+ const char *e = cp;
+ while (isspace(*cp)) {
+ ++cp;
+ }
+ // Two words
+ if (*cp == ':') {
+ tag = bt;
+ etag = e;
+ buf = cp + 1;
+ }
+ }
+ }
+ size = etag - tag;
+ if ((size <= 1)
+ // register names like x9
+ || ((size == 2) && (isdigit(tag[0]) || isdigit(tag[1])))
+ // register names like x18 but not driver names like en0
+ || ((size == 3) && (isdigit(tag[1]) && isdigit(tag[2])))
+ // blacklist
+ || ((size == 3) && !strncmp(tag, "CPU", 3))
+ || ((size == 7) && !strncmp(tag, "WARNING", 7))
+ || ((size == 5) && !strncmp(tag, "ERROR", 5))
+ || ((size == 4) && !strncmp(tag, "INFO", 4))) {
+ buf = start;
+ etag = tag = "";
+ }
+ }
+ size_t l = etag - tag;
+ // skip leading space
+ while (isspace(*buf)) {
+ ++buf;
+ }
+ // truncate trailing space
+ size_t b = strlen(buf);
+ while (b && isspace(buf[b-1])) {
+ --b;
+ }
+ // trick ... allow tag with empty content to be logged. log() drops empty
+ if (!b && l) {
+ buf = " ";
+ b = 1;
+ }
+ size_t n = 1 + l + 1 + b + 1;
+
+ // Allocate a buffer to hold the interpreted log message
+ int rc = n;
+ char *newstr = reinterpret_cast<char *>(malloc(n));
+ if (!newstr) {
+ rc = -ENOMEM;
+ return rc;
+ }
+ char *np = newstr;
+
+ // Convert priority into single-byte Android logger priority
+ *np = convertKernelPrioToAndroidPrio(pri);
+ ++np;
+
+ // Copy parsed tag following priority
+ strncpy(np, tag, l);
+ np += l;
+ *np = '\0';
+ ++np;
+
+ // Copy main message to the remainder
+ strncpy(np, buf, b);
+ np[b] = '\0';
+
+ // Log message
+ rc = logbuf->log(LOG_ID_KERNEL, now, uid, pid, tid, newstr,
+ (n <= USHRT_MAX) ? (unsigned short) n : USHRT_MAX);
+ free(newstr);
+
+ // notify readers
+ if (!rc) {
+ reader->notifyNewLog();
+ }
+
+ return rc;
+}
diff --git a/logd/LogKlog.h b/logd/LogKlog.h
new file mode 100644
index 0000000..24b2685
--- /dev/null
+++ b/logd/LogKlog.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LOGD_LOG_KLOG_H__
+#define _LOGD_LOG_KLOG_H__
+
+#include <sysutils/SocketListener.h>
+#include <log/log_read.h>
+#include "LogReader.h"
+
+char *log_strtok_r(char *str, char **saveptr);
+
+class LogKlog : public SocketListener {
+ LogBuffer *logbuf;
+ LogReader *reader;
+ const log_time signature;
+ // Set once thread is started, separates KLOG_ACTION_READ_ALL
+ // and KLOG_ACTION_READ phases.
+ bool initialized;
+ // Used during each of the above phases to control logging.
+ bool enableLogging;
+ // set if we are also running auditd, to filter out audit reports from
+ // our copy of the kernel log
+ bool auditd;
+
+ static log_time correction;
+
+public:
+ LogKlog(LogBuffer *buf, LogReader *reader, int fdWrite, int fdRead, bool auditd);
+ int log(const char *buf);
+ void synchronize(const char *buf);
+
+ static void convertMonotonicToReal(log_time &real) { real += correction; }
+
+protected:
+ void sniffTime(log_time &now, const char **buf, bool reverse);
+ void calculateCorrection(const log_time &monotonic, const char *real_string);
+ virtual bool onDataAvailable(SocketClient *cli);
+
+};
+
+#endif
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp
new file mode 100644
index 0000000..b29f5ab
--- /dev/null
+++ b/logd/LogListener.cpp
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2012-2014 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 <limits.h>
+#include <sys/prctl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <cutils/sockets.h>
+#include <log/logger.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+
+#include "LogListener.h"
+
+LogListener::LogListener(LogBuffer *buf, LogReader *reader) :
+ SocketListener(getLogSocket(), false),
+ logbuf(buf),
+ reader(reader) {
+}
+
+bool LogListener::onDataAvailable(SocketClient *cli) {
+ static bool name_set;
+ if (!name_set) {
+ prctl(PR_SET_NAME, "logd.writer");
+ name_set = true;
+ }
+
+ char buffer[sizeof_log_id_t + sizeof(uint16_t) + sizeof(log_time)
+ + LOGGER_ENTRY_MAX_PAYLOAD];
+ struct iovec iov = { buffer, sizeof(buffer) };
+ memset(buffer, 0, sizeof(buffer));
+
+ char control[CMSG_SPACE(sizeof(struct ucred))];
+ struct msghdr hdr = {
+ NULL,
+ 0,
+ &iov,
+ 1,
+ control,
+ sizeof(control),
+ 0,
+ };
+
+ int socket = cli->getSocket();
+
+ ssize_t n = recvmsg(socket, &hdr, 0);
+ if (n <= (ssize_t)(sizeof(android_log_header_t))) {
+ return false;
+ }
+
+ struct ucred *cred = NULL;
+
+ struct cmsghdr *cmsg = CMSG_FIRSTHDR(&hdr);
+ while (cmsg != NULL) {
+ if (cmsg->cmsg_level == SOL_SOCKET
+ && cmsg->cmsg_type == SCM_CREDENTIALS) {
+ cred = (struct ucred *)CMSG_DATA(cmsg);
+ break;
+ }
+ cmsg = CMSG_NXTHDR(&hdr, cmsg);
+ }
+
+ if (cred == NULL) {
+ return false;
+ }
+
+ if (cred->uid == AID_LOGD) {
+ // ignore log messages we send to ourself.
+ // Such log messages are often generated by libraries we depend on
+ // which use standard Android logging.
+ return false;
+ }
+
+ android_log_header_t *header = reinterpret_cast<android_log_header_t *>(buffer);
+ if (/* header->id < LOG_ID_MIN || */ header->id >= LOG_ID_MAX || header->id == LOG_ID_KERNEL) {
+ return false;
+ }
+
+ char *msg = ((char *)buffer) + sizeof(android_log_header_t);
+ n -= sizeof(android_log_header_t);
+
+ // NB: hdr.msg_flags & MSG_TRUNC is not tested, silently passing a
+ // truncated message to the logs.
+
+ if (logbuf->log((log_id_t)header->id, header->realtime,
+ cred->uid, cred->pid, header->tid, msg,
+ ((size_t) n <= USHRT_MAX) ? (unsigned short) n : USHRT_MAX) >= 0) {
+ reader->notifyNewLog();
+ }
+
+ return true;
+}
+
+int LogListener::getLogSocket() {
+ static const char socketName[] = "logdw";
+ int sock = android_get_control_socket(socketName);
+
+ if (sock < 0) {
+ sock = socket_local_server(socketName,
+ ANDROID_SOCKET_NAMESPACE_RESERVED,
+ SOCK_DGRAM);
+ }
+
+ int on = 1;
+ if (setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
+ return -1;
+ }
+ return sock;
+}
diff --git a/logd/LogListener.h b/logd/LogListener.h
new file mode 100644
index 0000000..7099e13
--- /dev/null
+++ b/logd/LogListener.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2012-2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LOGD_LOG_LISTENER_H__
+#define _LOGD_LOG_LISTENER_H__
+
+#include <sysutils/SocketListener.h>
+#include "LogReader.h"
+
+class LogListener : public SocketListener {
+ LogBuffer *logbuf;
+ LogReader *reader;
+
+public:
+ LogListener(LogBuffer *buf, LogReader *reader);
+
+protected:
+ virtual bool onDataAvailable(SocketClient *cli);
+
+private:
+ static int getLogSocket();
+};
+
+#endif
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
new file mode 100644
index 0000000..c7deec0
--- /dev/null
+++ b/logd/LogReader.cpp
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2012-2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <poll.h>
+#include <sys/prctl.h>
+#include <sys/socket.h>
+
+#include <cutils/sockets.h>
+
+#include "LogReader.h"
+#include "FlushCommand.h"
+
+LogReader::LogReader(LogBuffer *logbuf) :
+ SocketListener(getLogSocket(), true),
+ mLogbuf(*logbuf) {
+}
+
+// When we are notified a new log entry is available, inform
+// all of our listening sockets.
+void LogReader::notifyNewLog() {
+ FlushCommand command(*this);
+ runOnEachSocket(&command);
+}
+
+bool LogReader::onDataAvailable(SocketClient *cli) {
+ static bool name_set;
+ if (!name_set) {
+ prctl(PR_SET_NAME, "logd.reader");
+ name_set = true;
+ }
+
+ char buffer[255];
+
+ int len = read(cli->getSocket(), buffer, sizeof(buffer) - 1);
+ if (len <= 0) {
+ doSocketDelete(cli);
+ return false;
+ }
+ buffer[len] = '\0';
+
+ unsigned long tail = 0;
+ static const char _tail[] = " tail=";
+ char *cp = strstr(buffer, _tail);
+ if (cp) {
+ tail = atol(cp + sizeof(_tail) - 1);
+ }
+
+ log_time start(log_time::EPOCH);
+ static const char _start[] = " start=";
+ cp = strstr(buffer, _start);
+ if (cp) {
+ // Parse errors will result in current time
+ start.strptime(cp + sizeof(_start) - 1, "%s.%q");
+ }
+
+ unsigned int logMask = -1;
+ static const char _logIds[] = " lids=";
+ cp = strstr(buffer, _logIds);
+ if (cp) {
+ logMask = 0;
+ cp += sizeof(_logIds) - 1;
+ while (*cp && *cp != '\0') {
+ int val = 0;
+ while (isdigit(*cp)) {
+ val = val * 10 + *cp - '0';
+ ++cp;
+ }
+ logMask |= 1 << val;
+ if (*cp != ',') {
+ break;
+ }
+ ++cp;
+ }
+ }
+
+ pid_t pid = 0;
+ static const char _pid[] = " pid=";
+ cp = strstr(buffer, _pid);
+ if (cp) {
+ pid = atol(cp + sizeof(_pid) - 1);
+ }
+
+ bool nonBlock = false;
+ if (strncmp(buffer, "dumpAndClose", 12) == 0) {
+ // Allow writer to get some cycles, and wait for pending notifications
+ sched_yield();
+ LogTimeEntry::lock();
+ LogTimeEntry::unlock();
+ sched_yield();
+ nonBlock = true;
+ }
+
+ uint64_t sequence = 1;
+ // Convert realtime to sequence number
+ if (start != log_time::EPOCH) {
+ class LogFindStart {
+ const pid_t mPid;
+ const unsigned mLogMask;
+ bool startTimeSet;
+ log_time &start;
+ uint64_t &sequence;
+ uint64_t last;
+
+ public:
+ LogFindStart(unsigned logMask, pid_t pid, log_time &start, uint64_t &sequence) :
+ mPid(pid),
+ mLogMask(logMask),
+ startTimeSet(false),
+ start(start),
+ sequence(sequence),
+ last(sequence) {
+ }
+
+ static int callback(const LogBufferElement *element, void *obj) {
+ LogFindStart *me = reinterpret_cast<LogFindStart *>(obj);
+ if ((!me->mPid || (me->mPid == element->getPid()))
+ && (me->mLogMask & (1 << element->getLogId()))) {
+ if (me->start == element->getRealTime()) {
+ me->sequence = element->getSequence();
+ me->startTimeSet = true;
+ return -1;
+ } else {
+ if (me->start < element->getRealTime()) {
+ me->sequence = me->last;
+ me->startTimeSet = true;
+ return -1;
+ }
+ me->last = element->getSequence();
+ }
+ }
+ return false;
+ }
+
+ bool found() { return startTimeSet; }
+ } logFindStart(logMask, pid, start, sequence);
+
+ logbuf().flushTo(cli, sequence, FlushCommand::hasReadLogs(cli),
+ logFindStart.callback, &logFindStart);
+
+ if (!logFindStart.found()) {
+ if (nonBlock) {
+ doSocketDelete(cli);
+ return false;
+ }
+ sequence = LogBufferElement::getCurrentSequence();
+ }
+ }
+
+ FlushCommand command(*this, nonBlock, tail, logMask, pid, sequence);
+ command.runSocketCommand(cli);
+ return true;
+}
+
+void LogReader::doSocketDelete(SocketClient *cli) {
+ LastLogTimes × = mLogbuf.mTimes;
+ LogTimeEntry::lock();
+ LastLogTimes::iterator it = times.begin();
+ while(it != times.end()) {
+ LogTimeEntry *entry = (*it);
+ if (entry->mClient == cli) {
+ times.erase(it);
+ entry->release_Locked();
+ break;
+ }
+ it++;
+ }
+ LogTimeEntry::unlock();
+}
+
+int LogReader::getLogSocket() {
+ static const char socketName[] = "logdr";
+ int sock = android_get_control_socket(socketName);
+
+ if (sock < 0) {
+ sock = socket_local_server(socketName,
+ ANDROID_SOCKET_NAMESPACE_RESERVED,
+ SOCK_SEQPACKET);
+ }
+
+ return sock;
+}
diff --git a/logd/LogReader.h b/logd/LogReader.h
new file mode 100644
index 0000000..91559a3
--- /dev/null
+++ b/logd/LogReader.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2012-2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LOGD_LOG_WRITER_H__
+#define _LOGD_LOG_WRITER_H__
+
+#include <sysutils/SocketListener.h>
+#include "LogBuffer.h"
+#include "LogTimes.h"
+
+class LogReader : public SocketListener {
+ LogBuffer &mLogbuf;
+
+public:
+ LogReader(LogBuffer *logbuf);
+ void notifyNewLog();
+
+ LogBuffer &logbuf(void) const { return mLogbuf; }
+
+protected:
+ virtual bool onDataAvailable(SocketClient *cli);
+
+private:
+ static int getLogSocket();
+
+ void doSocketDelete(SocketClient *cli);
+
+};
+
+#endif
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
new file mode 100644
index 0000000..48c2fe6
--- /dev/null
+++ b/logd/LogStatistics.cpp
@@ -0,0 +1,532 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <algorithm> // std::max
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <log/logger.h>
+#include <private/android_filesystem_config.h>
+#include <utils/String8.h>
+
+#include "LogStatistics.h"
+
+LogStatistics::LogStatistics() : enable(false) {
+ log_id_for_each(id) {
+ mSizes[id] = 0;
+ mElements[id] = 0;
+ mSizesTotal[id] = 0;
+ mElementsTotal[id] = 0;
+ }
+}
+
+namespace android {
+
+// caller must own and free character string
+char *pidToName(pid_t pid) {
+ char *retval = NULL;
+ if (pid == 0) { // special case from auditd/klogd for kernel
+ retval = strdup("logd");
+ } else {
+ char buffer[512];
+ snprintf(buffer, sizeof(buffer), "/proc/%u/cmdline", pid);
+ int fd = open(buffer, O_RDONLY);
+ if (fd >= 0) {
+ ssize_t ret = read(fd, buffer, sizeof(buffer));
+ if (ret > 0) {
+ buffer[sizeof(buffer)-1] = '\0';
+ // frameworks intermediate state
+ if (strcmp(buffer, "<pre-initialized>")) {
+ retval = strdup(buffer);
+ }
+ }
+ close(fd);
+ }
+ }
+ return retval;
+}
+
+}
+
+void LogStatistics::add(LogBufferElement *e) {
+ log_id_t log_id = e->getLogId();
+ unsigned short size = e->getMsgLen();
+ mSizes[log_id] += size;
+ ++mElements[log_id];
+
+ mSizesTotal[log_id] += size;
+ ++mElementsTotal[log_id];
+
+ if (log_id == LOG_ID_KERNEL) {
+ return;
+ }
+
+ uidTable[log_id].add(e->getUid(), e);
+
+ if (!enable) {
+ return;
+ }
+
+ pidTable.add(e->getPid(), e);
+ tidTable.add(e->getTid(), e);
+
+ uint32_t tag = e->getTag();
+ if (tag) {
+ tagTable.add(tag, e);
+ }
+}
+
+void LogStatistics::subtract(LogBufferElement *e) {
+ log_id_t log_id = e->getLogId();
+ unsigned short size = e->getMsgLen();
+ mSizes[log_id] -= size;
+ --mElements[log_id];
+
+ if (log_id == LOG_ID_KERNEL) {
+ return;
+ }
+
+ uidTable[log_id].subtract(e->getUid(), e);
+
+ if (!enable) {
+ return;
+ }
+
+ pidTable.subtract(e->getPid(), e);
+ tidTable.subtract(e->getTid(), e);
+
+ uint32_t tag = e->getTag();
+ if (tag) {
+ tagTable.subtract(tag, e);
+ }
+}
+
+// Atomically set an entry to drop
+// entry->setDropped(1) must follow this call, caller should do this explicitly.
+void LogStatistics::drop(LogBufferElement *e) {
+ log_id_t log_id = e->getLogId();
+ unsigned short size = e->getMsgLen();
+ mSizes[log_id] -= size;
+
+ uidTable[log_id].drop(e->getUid(), e);
+
+ if (!enable) {
+ return;
+ }
+
+ pidTable.drop(e->getPid(), e);
+ tidTable.drop(e->getTid(), e);
+}
+
+// caller must own and free character string
+char *LogStatistics::uidToName(uid_t uid) {
+ // Local hard coded favourites
+ if (uid == AID_LOGD) {
+ return strdup("auditd");
+ }
+
+ // Android hard coded
+ const struct android_id_info *info = android_ids;
+
+ for (size_t i = 0; i < android_id_count; ++i) {
+ if (info->aid == uid) {
+ return strdup(info->name);
+ }
+ ++info;
+ }
+
+ // Parse /data/system/packages.list
+ uid_t userId = uid % AID_USER;
+ char *name = android::uidToName(userId);
+ if (!name && (userId > (AID_SHARED_GID_START - AID_APP))) {
+ name = android::uidToName(userId - (AID_SHARED_GID_START - AID_APP));
+ }
+ if (name) {
+ return name;
+ }
+
+ // report uid -> pid(s) -> pidToName if unique
+ for(pidTable_t::iterator it = pidTable.begin(); it != pidTable.end(); ++it) {
+ const PidEntry &entry = it->second;
+
+ if (entry.getUid() == uid) {
+ const char *n = entry.getName();
+
+ if (n) {
+ if (!name) {
+ name = strdup(n);
+ } else if (strcmp(name, n)) {
+ free(name);
+ name = NULL;
+ break;
+ }
+ }
+ }
+ }
+
+ // No one
+ return name;
+}
+
+static void format_line(android::String8 &output,
+ android::String8 &name, android::String8 &size, android::String8 &pruned) {
+ static const size_t pruned_len = 6;
+ static const size_t total_len = 70 + pruned_len;
+
+ ssize_t drop_len = std::max(pruned.length() + 1, pruned_len);
+ ssize_t size_len = std::max(size.length() + 1,
+ total_len - name.length() - drop_len - 1);
+
+ if (pruned.length()) {
+ output.appendFormat("%s%*s%*s\n", name.string(),
+ (int)size_len, size.string(),
+ (int)drop_len, pruned.string());
+ } else {
+ output.appendFormat("%s%*s\n", name.string(),
+ (int)size_len, size.string());
+ }
+}
+
+void LogStatistics::format(char **buf, uid_t uid, unsigned int logMask) {
+ static const unsigned short spaces_total = 19;
+
+ if (*buf) {
+ free(*buf);
+ *buf = NULL;
+ }
+
+ // Report on total logging, current and for all time
+
+ android::String8 output("size/num");
+ size_t oldLength;
+ short spaces = 1;
+
+ log_id_for_each(id) {
+ if (!(logMask & (1 << id))) {
+ continue;
+ }
+ oldLength = output.length();
+ if (spaces < 0) {
+ spaces = 0;
+ }
+ output.appendFormat("%*s%s", spaces, "", android_log_id_to_name(id));
+ spaces += spaces_total + oldLength - output.length();
+ }
+
+ spaces = 4;
+ output.appendFormat("\nTotal");
+
+ log_id_for_each(id) {
+ if (!(logMask & (1 << id))) {
+ continue;
+ }
+ oldLength = output.length();
+ if (spaces < 0) {
+ spaces = 0;
+ }
+ output.appendFormat("%*s%zu/%zu", spaces, "",
+ sizesTotal(id), elementsTotal(id));
+ spaces += spaces_total + oldLength - output.length();
+ }
+
+ spaces = 6;
+ output.appendFormat("\nNow");
+
+ log_id_for_each(id) {
+ if (!(logMask & (1 << id))) {
+ continue;
+ }
+
+ size_t els = elements(id);
+ if (els) {
+ oldLength = output.length();
+ if (spaces < 0) {
+ spaces = 0;
+ }
+ output.appendFormat("%*s%zu/%zu", spaces, "", sizes(id), els);
+ spaces -= output.length() - oldLength;
+ }
+ spaces += spaces_total;
+ }
+
+ // Report on Chattiest
+
+ // Chattiest by application (UID)
+ static const size_t maximum_sorted_entries = 32;
+ log_id_for_each(id) {
+ if (!(logMask & (1 << id))) {
+ continue;
+ }
+
+ bool headerPrinted = false;
+ std::unique_ptr<const UidEntry *[]> sorted = sort(maximum_sorted_entries, id);
+ ssize_t index = -1;
+ while ((index = uidTable_t::next(index, sorted, maximum_sorted_entries)) >= 0) {
+ const UidEntry *entry = sorted[index];
+ uid_t u = entry->getKey();
+ if ((uid != AID_ROOT) && (u != uid)) {
+ continue;
+ }
+
+ if (!headerPrinted) {
+ output.appendFormat("\n\n");
+ android::String8 name("");
+ if (uid == AID_ROOT) {
+ name.appendFormat(
+ "Chattiest UIDs in %s log buffer:",
+ android_log_id_to_name(id));
+ } else {
+ name.appendFormat(
+ "Logging for your UID in %s log buffer:",
+ android_log_id_to_name(id));
+ }
+ android::String8 size("Size");
+ android::String8 pruned("Pruned");
+ if (!worstUidEnabledForLogid(id)) {
+ pruned.setTo("");
+ }
+ format_line(output, name, size, pruned);
+
+ name.setTo("UID PACKAGE");
+ size.setTo("BYTES");
+ pruned.setTo("LINES");
+ if (!worstUidEnabledForLogid(id)) {
+ pruned.setTo("");
+ }
+ format_line(output, name, size, pruned);
+
+ headerPrinted = true;
+ }
+
+ android::String8 name("");
+ name.appendFormat("%u", u);
+ char *n = uidToName(u);
+ if (n) {
+ name.appendFormat("%*s%s", (int)std::max(6 - name.length(), (size_t)1), "", n);
+ free(n);
+ }
+
+ android::String8 size("");
+ size.appendFormat("%zu", entry->getSizes());
+
+ android::String8 pruned("");
+ size_t dropped = entry->getDropped();
+ if (dropped) {
+ pruned.appendFormat("%zu", dropped);
+ }
+
+ format_line(output, name, size, pruned);
+ }
+ }
+
+ if (enable) {
+ // Pid table
+ bool headerPrinted = false;
+ std::unique_ptr<const PidEntry *[]> sorted = pidTable.sort(maximum_sorted_entries);
+ ssize_t index = -1;
+ while ((index = pidTable.next(index, sorted, maximum_sorted_entries)) >= 0) {
+ const PidEntry *entry = sorted[index];
+ uid_t u = entry->getUid();
+ if ((uid != AID_ROOT) && (u != uid)) {
+ continue;
+ }
+
+ if (!headerPrinted) {
+ output.appendFormat("\n\n");
+ android::String8 name("");
+ if (uid == AID_ROOT) {
+ name.appendFormat("Chattiest PIDs:");
+ } else {
+ name.appendFormat("Logging for this PID:");
+ }
+ android::String8 size("Size");
+ android::String8 pruned("Pruned");
+ format_line(output, name, size, pruned);
+
+ name.setTo(" PID/UID COMMAND LINE");
+ size.setTo("BYTES");
+ pruned.setTo("LINES");
+ format_line(output, name, size, pruned);
+
+ headerPrinted = true;
+ }
+
+ android::String8 name("");
+ name.appendFormat("%5u/%u", entry->getKey(), u);
+ const char *n = entry->getName();
+ if (n) {
+ name.appendFormat("%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", n);
+ } else {
+ char *un = uidToName(u);
+ if (un) {
+ name.appendFormat("%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", un);
+ free(un);
+ }
+ }
+
+ android::String8 size("");
+ size.appendFormat("%zu", entry->getSizes());
+
+ android::String8 pruned("");
+ size_t dropped = entry->getDropped();
+ if (dropped) {
+ pruned.appendFormat("%zu", dropped);
+ }
+
+ format_line(output, name, size, pruned);
+ }
+ }
+
+ if (enable) {
+ // Tid table
+ bool headerPrinted = false;
+ // sort() returns list of references, unique_ptr makes sure self-delete
+ std::unique_ptr<const TidEntry *[]> sorted = tidTable.sort(maximum_sorted_entries);
+ ssize_t index = -1;
+ while ((index = tidTable.next(index, sorted, maximum_sorted_entries)) >= 0) {
+ const TidEntry *entry = sorted[index];
+ uid_t u = entry->getUid();
+ if ((uid != AID_ROOT) && (u != uid)) {
+ continue;
+ }
+
+ if (!headerPrinted) { // Only print header if we have table to print
+ output.appendFormat("\n\n");
+ android::String8 name("Chattiest TIDs:");
+ android::String8 size("Size");
+ android::String8 pruned("Pruned");
+ format_line(output, name, size, pruned);
+
+ name.setTo(" TID/UID COMM");
+ size.setTo("BYTES");
+ pruned.setTo("LINES");
+ format_line(output, name, size, pruned);
+
+ headerPrinted = true;
+ }
+
+ android::String8 name("");
+ name.appendFormat("%5u/%u", entry->getKey(), u);
+ const char *n = entry->getName();
+ if (n) {
+ name.appendFormat("%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", n);
+ } else {
+ // if we do not have a PID name, lets punt to try UID name?
+ char *un = uidToName(u);
+ if (un) {
+ name.appendFormat("%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", un);
+ free(un);
+ }
+ // We tried, better to not have a name at all, we still
+ // have TID/UID by number to report in any case.
+ }
+
+ android::String8 size("");
+ size.appendFormat("%zu", entry->getSizes());
+
+ android::String8 pruned("");
+ size_t dropped = entry->getDropped();
+ if (dropped) {
+ pruned.appendFormat("%zu", dropped);
+ }
+
+ format_line(output, name, size, pruned);
+ }
+ }
+
+ if (enable && (logMask & (1 << LOG_ID_EVENTS))) {
+ // Tag table
+ bool headerPrinted = false;
+ std::unique_ptr<const TagEntry *[]> sorted = tagTable.sort(maximum_sorted_entries);
+ ssize_t index = -1;
+ while ((index = tagTable.next(index, sorted, maximum_sorted_entries)) >= 0) {
+ const TagEntry *entry = sorted[index];
+ uid_t u = entry->getUid();
+ if ((uid != AID_ROOT) && (u != uid)) {
+ continue;
+ }
+
+ android::String8 pruned("");
+
+ if (!headerPrinted) {
+ output.appendFormat("\n\n");
+ android::String8 name("Chattiest events log buffer TAGs:");
+ android::String8 size("Size");
+ format_line(output, name, size, pruned);
+
+ name.setTo(" TAG/UID TAGNAME");
+ size.setTo("BYTES");
+ format_line(output, name, size, pruned);
+
+ headerPrinted = true;
+ }
+
+ android::String8 name("");
+ if (u == (uid_t)-1) {
+ name.appendFormat("%7u", entry->getKey());
+ } else {
+ name.appendFormat("%7u/%u", entry->getKey(), u);
+ }
+ const char *n = entry->getName();
+ if (n) {
+ name.appendFormat("%*s%s", (int)std::max(14 - name.length(), (size_t)1), "", n);
+ }
+
+ android::String8 size("");
+ size.appendFormat("%zu", entry->getSizes());
+
+ format_line(output, name, size, pruned);
+ }
+ }
+
+ *buf = strdup(output.string());
+}
+
+namespace android {
+
+uid_t pidToUid(pid_t pid) {
+ char buffer[512];
+ snprintf(buffer, sizeof(buffer), "/proc/%u/status", pid);
+ FILE *fp = fopen(buffer, "r");
+ if (fp) {
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ int uid;
+ if (sscanf(buffer, "Uid: %d", &uid) == 1) {
+ fclose(fp);
+ return uid;
+ }
+ }
+ fclose(fp);
+ }
+ return AID_LOGD; // associate this with the logger
+}
+
+}
+
+uid_t LogStatistics::pidToUid(pid_t pid) {
+ return pidTable.add(pid)->second.getUid();
+}
+
+// caller must free character string
+char *LogStatistics::pidToName(pid_t pid) {
+ const char *name = pidTable.add(pid)->second.getName();
+ if (!name) {
+ return NULL;
+ }
+ return strdup(name);
+}
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
new file mode 100644
index 0000000..760d6b2
--- /dev/null
+++ b/logd/LogStatistics.h
@@ -0,0 +1,343 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LOGD_LOG_STATISTICS_H__
+#define _LOGD_LOG_STATISTICS_H__
+
+#include <memory>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#include <unordered_map>
+
+#include <log/log.h>
+
+#include "LogBufferElement.h"
+
+#define log_id_for_each(i) \
+ for (log_id_t i = LOG_ID_MIN; i < LOG_ID_MAX; i = (log_id_t) (i + 1))
+
+template <typename TKey, typename TEntry>
+class LogHashtable {
+
+ std::unordered_map<TKey, TEntry> map;
+
+public:
+
+ typedef typename std::unordered_map<TKey, TEntry>::iterator iterator;
+
+ std::unique_ptr<const TEntry *[]> sort(size_t n) {
+ if (!n) {
+ std::unique_ptr<const TEntry *[]> sorted(NULL);
+ return sorted;
+ }
+
+ const TEntry **retval = new const TEntry* [n];
+ memset(retval, 0, sizeof(*retval) * n);
+
+ for(iterator it = map.begin(); it != map.end(); ++it) {
+ const TEntry &entry = it->second;
+ size_t s = entry.getSizes();
+ ssize_t i = n - 1;
+ while ((!retval[i] || (s > retval[i]->getSizes())) && (--i >= 0))
+ ;
+ if (++i < (ssize_t)n) {
+ size_t b = n - i - 1;
+ if (b) {
+ memmove(&retval[i+1], &retval[i], b * sizeof(retval[0]));
+ }
+ retval[i] = &entry;
+ }
+ }
+ std::unique_ptr<const TEntry *[]> sorted(retval);
+ return sorted;
+ }
+
+ // Iteration handler for the sort method output
+ static ssize_t next(ssize_t index, std::unique_ptr<const TEntry *[]> &sorted, size_t n) {
+ ++index;
+ if (!sorted.get() || (index < 0) || (n <= (size_t)index) || !sorted[index]
+ || (sorted[index]->getSizes() <= (sorted[0]->getSizes() / 100))) {
+ return -1;
+ }
+ return index;
+ }
+
+ inline iterator add(TKey key, LogBufferElement *e) {
+ iterator it = map.find(key);
+ if (it == map.end()) {
+ it = map.insert(std::make_pair(key, TEntry(e))).first;
+ } else {
+ it->second.add(e);
+ }
+ return it;
+ }
+
+ inline iterator add(TKey key) {
+ iterator it = map.find(key);
+ if (it == map.end()) {
+ it = map.insert(std::make_pair(key, TEntry(key))).first;
+ } else {
+ it->second.add(key);
+ }
+ return it;
+ }
+
+ void subtract(TKey key, LogBufferElement *e) {
+ iterator it = map.find(key);
+ if ((it != map.end()) && it->second.subtract(e)) {
+ map.erase(it);
+ }
+ }
+
+ inline void drop(TKey key, LogBufferElement *e) {
+ iterator it = map.find(key);
+ if (it != map.end()) {
+ it->second.drop(e);
+ }
+ }
+
+ inline iterator begin() { return map.begin(); }
+ inline iterator end() { return map.end(); }
+
+};
+
+struct EntryBase {
+ size_t size;
+
+ EntryBase():size(0) { }
+ EntryBase(LogBufferElement *e):size(e->getMsgLen()) { }
+
+ size_t getSizes() const { return size; }
+
+ inline void add(LogBufferElement *e) { size += e->getMsgLen(); }
+ inline bool subtract(LogBufferElement *e) { size -= e->getMsgLen(); return !size; }
+};
+
+struct EntryBaseDropped : public EntryBase {
+ size_t dropped;
+
+ EntryBaseDropped():dropped(0) { }
+ EntryBaseDropped(LogBufferElement *e):EntryBase(e),dropped(e->getDropped()){ }
+
+ size_t getDropped() const { return dropped; }
+
+ inline void add(LogBufferElement *e) {
+ dropped += e->getDropped();
+ EntryBase::add(e);
+ }
+ inline bool subtract(LogBufferElement *e) {
+ dropped -= e->getDropped();
+ return EntryBase::subtract(e) && !dropped;
+ }
+ inline void drop(LogBufferElement *e) {
+ dropped += 1;
+ EntryBase::subtract(e);
+ }
+};
+
+struct UidEntry : public EntryBaseDropped {
+ const uid_t uid;
+
+ UidEntry(LogBufferElement *e):EntryBaseDropped(e),uid(e->getUid()) { }
+
+ inline const uid_t&getKey() const { return uid; }
+};
+
+namespace android {
+uid_t pidToUid(pid_t pid);
+}
+
+struct PidEntry : public EntryBaseDropped {
+ const pid_t pid;
+ uid_t uid;
+ char *name;
+
+ PidEntry(pid_t p):
+ EntryBaseDropped(),
+ pid(p),
+ uid(android::pidToUid(p)),
+ name(android::pidToName(pid)) { }
+ PidEntry(LogBufferElement *e):
+ EntryBaseDropped(e),
+ pid(e->getPid()),
+ uid(e->getUid()),
+ name(android::pidToName(e->getPid())) { }
+ PidEntry(const PidEntry &c):
+ EntryBaseDropped(c),
+ pid(c.pid),
+ uid(c.uid),
+ name(c.name ? strdup(c.name) : NULL) { }
+ ~PidEntry() { free(name); }
+
+ const pid_t&getKey() const { return pid; }
+ const uid_t&getUid() const { return uid; }
+ const char*getName() const { return name; }
+
+ inline void add(pid_t p) {
+ if (name && !strncmp(name, "zygote", 6)) {
+ free(name);
+ name = NULL;
+ }
+ if (!name) {
+ char *n = android::pidToName(p);
+ if (n) {
+ name = n;
+ }
+ }
+ }
+
+ inline void add(LogBufferElement *e) {
+ uid_t u = e->getUid();
+ if (getUid() != u) {
+ uid = u;
+ free(name);
+ name = android::pidToName(e->getPid());
+ } else {
+ add(e->getPid());
+ }
+ EntryBaseDropped::add(e);
+ }
+};
+
+struct TidEntry : public EntryBaseDropped {
+ const pid_t tid;
+ uid_t uid;
+ char *name;
+
+ TidEntry(pid_t t):
+ EntryBaseDropped(),
+ tid(t),
+ uid(android::pidToUid(t)),
+ name(android::tidToName(tid)) { }
+ TidEntry(LogBufferElement *e):
+ EntryBaseDropped(e),
+ tid(e->getTid()),
+ uid(e->getUid()),
+ name(android::tidToName(e->getTid())) { }
+ TidEntry(const TidEntry &c):
+ EntryBaseDropped(c),
+ tid(c.tid),
+ uid(c.uid),
+ name(c.name ? strdup(c.name) : NULL) { }
+ ~TidEntry() { free(name); }
+
+ const pid_t&getKey() const { return tid; }
+ const uid_t&getUid() const { return uid; }
+ const char*getName() const { return name; }
+
+ inline void add(pid_t t) {
+ if (name && !strncmp(name, "zygote", 6)) {
+ free(name);
+ name = NULL;
+ }
+ if (!name) {
+ char *n = android::tidToName(t);
+ if (n) {
+ name = n;
+ }
+ }
+ }
+
+ inline void add(LogBufferElement *e) {
+ uid_t u = e->getUid();
+ if (getUid() != u) {
+ uid = u;
+ free(name);
+ name = android::tidToName(e->getTid());
+ } else {
+ add(e->getTid());
+ }
+ EntryBaseDropped::add(e);
+ }
+};
+
+struct TagEntry : public EntryBase {
+ const uint32_t tag;
+ uid_t uid;
+
+ TagEntry(LogBufferElement *e):
+ EntryBase(e),
+ tag(e->getTag()),
+ uid(e->getUid()) { }
+
+ const uint32_t&getKey() const { return tag; }
+ const uid_t&getUid() const { return uid; }
+ const char*getName() const { return android::tagToName(tag); }
+
+ inline void add(LogBufferElement *e) {
+ uid_t u = e->getUid();
+ if (uid != u) {
+ uid = -1;
+ }
+ EntryBase::add(e);
+ }
+};
+
+// Log Statistics
+class LogStatistics {
+ size_t mSizes[LOG_ID_MAX];
+ size_t mElements[LOG_ID_MAX];
+ size_t mSizesTotal[LOG_ID_MAX];
+ size_t mElementsTotal[LOG_ID_MAX];
+ bool enable;
+
+ // uid to size list
+ typedef LogHashtable<uid_t, UidEntry> uidTable_t;
+ uidTable_t uidTable[LOG_ID_MAX];
+
+ // pid to uid list
+ typedef LogHashtable<pid_t, PidEntry> pidTable_t;
+ pidTable_t pidTable;
+
+ // tid to uid list
+ typedef LogHashtable<pid_t, TidEntry> tidTable_t;
+ tidTable_t tidTable;
+
+ // tag list
+ typedef LogHashtable<uint32_t, TagEntry> tagTable_t;
+ tagTable_t tagTable;
+
+public:
+ LogStatistics();
+
+ void enableStatistics() { enable = true; }
+
+ void add(LogBufferElement *entry);
+ void subtract(LogBufferElement *entry);
+ // entry->setDropped(1) must follow this call
+ void drop(LogBufferElement *entry);
+ // Correct for merging two entries referencing dropped content
+ void erase(LogBufferElement *e) { --mElements[e->getLogId()]; }
+
+ std::unique_ptr<const UidEntry *[]> sort(size_t n, log_id i) { return uidTable[i].sort(n); }
+
+ // fast track current value by id only
+ size_t sizes(log_id_t id) const { return mSizes[id]; }
+ size_t elements(log_id_t id) const { return mElements[id]; }
+ size_t sizesTotal(log_id_t id) const { return mSizesTotal[id]; }
+ size_t elementsTotal(log_id_t id) const { return mElementsTotal[id]; }
+
+ // *strp = malloc, balance with free
+ void format(char **strp, uid_t uid, unsigned int logMask);
+
+ // helper (must be locked directly or implicitly by mLogElementsLock)
+ char *pidToName(pid_t pid);
+ uid_t pidToUid(pid_t pid);
+ char *uidToName(uid_t uid);
+};
+
+#endif // _LOGD_LOG_STATISTICS_H__
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp
new file mode 100644
index 0000000..68a0680
--- /dev/null
+++ b/logd/LogTimes.cpp
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2014 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/prctl.h>
+
+#include "FlushCommand.h"
+#include "LogBuffer.h"
+#include "LogTimes.h"
+#include "LogReader.h"
+
+pthread_mutex_t LogTimeEntry::timesLock = PTHREAD_MUTEX_INITIALIZER;
+
+LogTimeEntry::LogTimeEntry(LogReader &reader, SocketClient *client,
+ bool nonBlock, unsigned long tail,
+ unsigned int logMask, pid_t pid,
+ uint64_t start) :
+ mRefCount(1),
+ mRelease(false),
+ mError(false),
+ threadRunning(false),
+ leadingDropped(false),
+ mReader(reader),
+ mLogMask(logMask),
+ mPid(pid),
+ mCount(0),
+ mTail(tail),
+ mIndex(0),
+ mClient(client),
+ mStart(start),
+ mNonBlock(nonBlock),
+ mEnd(LogBufferElement::getCurrentSequence()) {
+ pthread_cond_init(&threadTriggeredCondition, NULL);
+ cleanSkip_Locked();
+}
+
+void LogTimeEntry::startReader_Locked(void) {
+ pthread_attr_t attr;
+
+ threadRunning = true;
+
+ if (!pthread_attr_init(&attr)) {
+ if (!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
+ if (!pthread_create(&mThread, &attr,
+ LogTimeEntry::threadStart, this)) {
+ pthread_attr_destroy(&attr);
+ return;
+ }
+ }
+ pthread_attr_destroy(&attr);
+ }
+ threadRunning = false;
+ if (mClient) {
+ mClient->decRef();
+ }
+ decRef_Locked();
+}
+
+void LogTimeEntry::threadStop(void *obj) {
+ LogTimeEntry *me = reinterpret_cast<LogTimeEntry *>(obj);
+
+ lock();
+
+ if (me->mNonBlock) {
+ me->error_Locked();
+ }
+
+ SocketClient *client = me->mClient;
+
+ if (me->isError_Locked()) {
+ LogReader &reader = me->mReader;
+ LastLogTimes × = reader.logbuf().mTimes;
+
+ LastLogTimes::iterator it = times.begin();
+ while(it != times.end()) {
+ if (*it == me) {
+ times.erase(it);
+ me->release_Locked();
+ break;
+ }
+ it++;
+ }
+
+ me->mClient = NULL;
+ reader.release(client);
+ }
+
+ if (client) {
+ client->decRef();
+ }
+
+ me->threadRunning = false;
+ me->decRef_Locked();
+
+ unlock();
+}
+
+void *LogTimeEntry::threadStart(void *obj) {
+ prctl(PR_SET_NAME, "logd.reader.per");
+
+ LogTimeEntry *me = reinterpret_cast<LogTimeEntry *>(obj);
+
+ pthread_cleanup_push(threadStop, obj);
+
+ SocketClient *client = me->mClient;
+ if (!client) {
+ me->error();
+ return NULL;
+ }
+
+ LogBuffer &logbuf = me->mReader.logbuf();
+
+ bool privileged = FlushCommand::hasReadLogs(client);
+
+ me->leadingDropped = true;
+
+ lock();
+
+ while (me->threadRunning && !me->isError_Locked()) {
+ uint64_t start = me->mStart;
+
+ unlock();
+
+ if (me->mTail) {
+ logbuf.flushTo(client, start, privileged, FilterFirstPass, me);
+ me->leadingDropped = true;
+ }
+ start = logbuf.flushTo(client, start, privileged, FilterSecondPass, me);
+
+ lock();
+
+ if (start == LogBufferElement::FLUSH_ERROR) {
+ me->error_Locked();
+ }
+
+ if (me->mNonBlock || !me->threadRunning || me->isError_Locked()) {
+ break;
+ }
+
+ me->cleanSkip_Locked();
+
+ pthread_cond_wait(&me->threadTriggeredCondition, ×Lock);
+ }
+
+ unlock();
+
+ pthread_cleanup_pop(true);
+
+ return NULL;
+}
+
+// A first pass to count the number of elements
+int LogTimeEntry::FilterFirstPass(const LogBufferElement *element, void *obj) {
+ LogTimeEntry *me = reinterpret_cast<LogTimeEntry *>(obj);
+
+ LogTimeEntry::lock();
+
+ if (me->leadingDropped) {
+ if (element->getDropped()) {
+ LogTimeEntry::unlock();
+ return false;
+ }
+ me->leadingDropped = false;
+ }
+
+ if (me->mCount == 0) {
+ me->mStart = element->getSequence();
+ }
+
+ if ((!me->mPid || (me->mPid == element->getPid()))
+ && (me->isWatching(element->getLogId()))) {
+ ++me->mCount;
+ }
+
+ LogTimeEntry::unlock();
+
+ return false;
+}
+
+// A second pass to send the selected elements
+int LogTimeEntry::FilterSecondPass(const LogBufferElement *element, void *obj) {
+ LogTimeEntry *me = reinterpret_cast<LogTimeEntry *>(obj);
+
+ LogTimeEntry::lock();
+
+ me->mStart = element->getSequence();
+
+ if (me->skipAhead[element->getLogId()]) {
+ me->skipAhead[element->getLogId()]--;
+ goto skip;
+ }
+
+ if (me->leadingDropped) {
+ if (element->getDropped()) {
+ goto skip;
+ }
+ me->leadingDropped = false;
+ }
+
+ // Truncate to close race between first and second pass
+ if (me->mNonBlock && me->mTail && (me->mIndex >= me->mCount)) {
+ goto stop;
+ }
+
+ if (!me->isWatching(element->getLogId())) {
+ goto skip;
+ }
+
+ if (me->mPid && (me->mPid != element->getPid())) {
+ goto skip;
+ }
+
+ if (me->isError_Locked()) {
+ goto stop;
+ }
+
+ if (!me->mTail) {
+ goto ok;
+ }
+
+ ++me->mIndex;
+
+ if ((me->mCount > me->mTail) && (me->mIndex <= (me->mCount - me->mTail))) {
+ goto skip;
+ }
+
+ if (!me->mNonBlock) {
+ me->mTail = 0;
+ }
+
+ok:
+ if (!me->skipAhead[element->getLogId()]) {
+ LogTimeEntry::unlock();
+ return true;
+ }
+ // FALLTHRU
+
+skip:
+ LogTimeEntry::unlock();
+ return false;
+
+stop:
+ LogTimeEntry::unlock();
+ return -1;
+}
+
+void LogTimeEntry::cleanSkip_Locked(void) {
+ for (log_id_t i = LOG_ID_MIN; i < LOG_ID_MAX; i = (log_id_t) (i + 1)) {
+ skipAhead[i] = 0;
+ }
+}
diff --git a/logd/LogTimes.h b/logd/LogTimes.h
new file mode 100644
index 0000000..783bce6
--- /dev/null
+++ b/logd/LogTimes.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2012-2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LOGD_LOG_TIMES_H__
+#define _LOGD_LOG_TIMES_H__
+
+#include <pthread.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sysutils/SocketClient.h>
+#include <utils/List.h>
+#include <log/log.h>
+
+class LogReader;
+
+class LogTimeEntry {
+ static pthread_mutex_t timesLock;
+ unsigned int mRefCount;
+ bool mRelease;
+ bool mError;
+ bool threadRunning;
+ bool leadingDropped;
+ pthread_cond_t threadTriggeredCondition;
+ pthread_t mThread;
+ LogReader &mReader;
+ static void *threadStart(void *me);
+ static void threadStop(void *me);
+ const unsigned int mLogMask;
+ const pid_t mPid;
+ unsigned int skipAhead[LOG_ID_MAX];
+ unsigned long mCount;
+ unsigned long mTail;
+ unsigned long mIndex;
+
+public:
+ LogTimeEntry(LogReader &reader, SocketClient *client, bool nonBlock,
+ unsigned long tail, unsigned int logMask, pid_t pid,
+ uint64_t start);
+
+ SocketClient *mClient;
+ uint64_t mStart;
+ const bool mNonBlock;
+ const uint64_t mEnd; // only relevant if mNonBlock
+
+ // Protect List manipulations
+ static void lock(void) { pthread_mutex_lock(×Lock); }
+ static void unlock(void) { pthread_mutex_unlock(×Lock); }
+
+ void startReader_Locked(void);
+
+ bool runningReader_Locked(void) const {
+ return threadRunning || mRelease || mError || mNonBlock;
+ }
+ void triggerReader_Locked(void) {
+ pthread_cond_signal(&threadTriggeredCondition);
+ }
+
+ void triggerSkip_Locked(log_id_t id, unsigned int skip) { skipAhead[id] = skip; }
+ void cleanSkip_Locked(void);
+
+ // Called after LogTimeEntry removed from list, lock implicitly held
+ void release_Locked(void) {
+ mRelease = true;
+ pthread_cond_signal(&threadTriggeredCondition);
+ if (mRefCount || threadRunning) {
+ return;
+ }
+ // No one else is holding a reference to this
+ delete this;
+ }
+
+ // Called to mark socket in jeopardy
+ void error_Locked(void) { mError = true; }
+ void error(void) { lock(); error_Locked(); unlock(); }
+
+ bool isError_Locked(void) const { return mRelease || mError; }
+
+ // Mark Used
+ // Locking implied, grabbed when protection around loop iteration
+ void incRef_Locked(void) { ++mRefCount; }
+
+ bool owned_Locked(void) const { return mRefCount != 0; }
+
+ void decRef_Locked(void) {
+ if ((mRefCount && --mRefCount) || !mRelease || threadRunning) {
+ return;
+ }
+ // No one else is holding a reference to this
+ delete this;
+ }
+ bool isWatching(log_id_t id) { return (mLogMask & (1<<id)) != 0; }
+ // flushTo filter callbacks
+ static int FilterFirstPass(const LogBufferElement *element, void *me);
+ static int FilterSecondPass(const LogBufferElement *element, void *me);
+};
+
+typedef android::List<LogTimeEntry *> LastLogTimes;
+
+#endif
diff --git a/logd/LogWhiteBlackList.cpp b/logd/LogWhiteBlackList.cpp
new file mode 100644
index 0000000..277b3ca
--- /dev/null
+++ b/logd/LogWhiteBlackList.cpp
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2014 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 <ctype.h>
+
+#include <utils/String8.h>
+
+#include "LogWhiteBlackList.h"
+
+// White and Black list
+
+Prune::Prune(uid_t uid, pid_t pid) : mUid(uid), mPid(pid) {
+}
+
+int Prune::cmp(uid_t uid, pid_t pid) const {
+ if ((mUid == uid_all) || (mUid == uid)) {
+ if (mPid == pid_all) {
+ return 0;
+ }
+ return pid - mPid;
+ }
+ return uid - mUid;
+}
+
+void Prune::format(char **strp) {
+ if (mUid != uid_all) {
+ if (mPid != pid_all) {
+ asprintf(strp, "%u/%u", mUid, mPid);
+ } else {
+ asprintf(strp, "%u", mUid);
+ }
+ } else if (mPid != pid_all) {
+ asprintf(strp, "/%u", mPid);
+ } else { // NB: mPid == pid_all can not happen if mUid == uid_all
+ asprintf(strp, "/");
+ }
+}
+
+PruneList::PruneList() : mWorstUidEnabled(true) {
+ mNaughty.clear();
+ mNice.clear();
+}
+
+PruneList::~PruneList() {
+ PruneCollection::iterator it;
+ for (it = mNice.begin(); it != mNice.end();) {
+ delete (*it);
+ it = mNice.erase(it);
+ }
+ for (it = mNaughty.begin(); it != mNaughty.end();) {
+ delete (*it);
+ it = mNaughty.erase(it);
+ }
+}
+
+int PruneList::init(char *str) {
+ mWorstUidEnabled = true;
+ PruneCollection::iterator it;
+ for (it = mNice.begin(); it != mNice.end();) {
+ delete (*it);
+ it = mNice.erase(it);
+ }
+ for (it = mNaughty.begin(); it != mNaughty.end();) {
+ delete (*it);
+ it = mNaughty.erase(it);
+ }
+
+ if (!str) {
+ return 0;
+ }
+
+ mWorstUidEnabled = false;
+
+ for(; *str; ++str) {
+ if (isspace(*str)) {
+ continue;
+ }
+
+ PruneCollection *list;
+ if ((*str == '~') || (*str == '!')) { // ~ supported, ! undocumented
+ ++str;
+ // special case, translates to worst UID at priority in blacklist
+ if (*str == '!') {
+ mWorstUidEnabled = true;
+ ++str;
+ if (!*str) {
+ break;
+ }
+ if (!isspace(*str)) {
+ return 1;
+ }
+ continue;
+ }
+ if (!*str) {
+ return 1;
+ }
+ list = &mNaughty;
+ } else {
+ list = &mNice;
+ }
+
+ uid_t uid = Prune::uid_all;
+ if (isdigit(*str)) {
+ uid = 0;
+ do {
+ uid = uid * 10 + *str++ - '0';
+ } while (isdigit(*str));
+ }
+
+ pid_t pid = Prune::pid_all;
+ if (*str == '/') {
+ ++str;
+ if (isdigit(*str)) {
+ pid = 0;
+ do {
+ pid = pid * 10 + *str++ - '0';
+ } while (isdigit(*str));
+ }
+ }
+
+ if ((uid == Prune::uid_all) && (pid == Prune::pid_all)) {
+ return 1;
+ }
+
+ if (*str && !isspace(*str)) {
+ return 1;
+ }
+
+ // insert sequentially into list
+ PruneCollection::iterator it = list->begin();
+ while (it != list->end()) {
+ Prune *p = *it;
+ int m = uid - p->mUid;
+ if (m == 0) {
+ if (p->mPid == p->pid_all) {
+ break;
+ }
+ if ((pid == p->pid_all) && (p->mPid != p->pid_all)) {
+ it = list->erase(it);
+ continue;
+ }
+ m = pid - p->mPid;
+ }
+ if (m <= 0) {
+ if (m < 0) {
+ list->insert(it, new Prune(uid,pid));
+ }
+ break;
+ }
+ ++it;
+ }
+ if (it == list->end()) {
+ list->push_back(new Prune(uid,pid));
+ }
+ if (!*str) {
+ break;
+ }
+ }
+
+ return 0;
+}
+
+void PruneList::format(char **strp) {
+ if (*strp) {
+ free(*strp);
+ *strp = NULL;
+ }
+
+ static const char nice_format[] = " %s";
+ const char *fmt = nice_format + 1;
+
+ android::String8 string;
+
+ if (mWorstUidEnabled) {
+ string.setTo("~!");
+ fmt = nice_format;
+ }
+
+ PruneCollection::iterator it;
+
+ for (it = mNice.begin(); it != mNice.end(); ++it) {
+ char *a = NULL;
+ (*it)->format(&a);
+
+ string.appendFormat(fmt, a);
+ fmt = nice_format;
+
+ free(a);
+ }
+
+ static const char naughty_format[] = " ~%s";
+ fmt = naughty_format + (*fmt != ' ');
+ for (it = mNaughty.begin(); it != mNaughty.end(); ++it) {
+ char *a = NULL;
+ (*it)->format(&a);
+
+ string.appendFormat(fmt, a);
+ fmt = naughty_format;
+
+ free(a);
+ }
+
+ *strp = strdup(string.string());
+}
+
+// ToDo: Lists are in sorted order, Prune->cmp() returns + or -
+// If there is scaling issues, resort to a better algorithm than linear
+// based on these assumptions.
+
+bool PruneList::naughty(LogBufferElement *element) {
+ PruneCollection::iterator it;
+ for (it = mNaughty.begin(); it != mNaughty.end(); ++it) {
+ if (!(*it)->cmp(element)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool PruneList::nice(LogBufferElement *element) {
+ PruneCollection::iterator it;
+ for (it = mNice.begin(); it != mNice.end(); ++it) {
+ if (!(*it)->cmp(element)) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/logd/LogWhiteBlackList.h b/logd/LogWhiteBlackList.h
new file mode 100644
index 0000000..5f60801
--- /dev/null
+++ b/logd/LogWhiteBlackList.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LOGD_LOG_WHITE_BLACK_LIST_H__
+#define _LOGD_LOG_WHITE_BLACK_LIST_H__
+
+#include <sys/types.h>
+
+#include <utils/List.h>
+
+#include <LogBufferElement.h>
+
+// White and Blacklist
+
+class Prune {
+ friend class PruneList;
+
+ const uid_t mUid;
+ const pid_t mPid;
+ int cmp(uid_t uid, pid_t pid) const;
+
+public:
+ static const uid_t uid_all = (uid_t) -1;
+ static const pid_t pid_all = (pid_t) -1;
+
+ Prune(uid_t uid, pid_t pid);
+
+ uid_t getUid() const { return mUid; }
+ pid_t getPid() const { return mPid; }
+
+ int cmp(LogBufferElement *e) const { return cmp(e->getUid(), e->getPid()); }
+
+ // *strp is malloc'd, use free to release
+ void format(char **strp);
+};
+
+typedef android::List<Prune *> PruneCollection;
+
+class PruneList {
+ PruneCollection mNaughty;
+ PruneCollection mNice;
+ bool mWorstUidEnabled;
+
+public:
+ PruneList();
+ ~PruneList();
+
+ int init(char *str);
+
+ bool naughty(LogBufferElement *element);
+ bool naughty(void) { return !mNaughty.empty(); }
+ bool nice(LogBufferElement *element);
+ bool nice(void) { return !mNice.empty(); }
+ bool worstUidEnabled() const { return mWorstUidEnabled; }
+
+ // *strp is malloc'd, use free to release
+ void format(char **strp);
+};
+
+#endif // _LOGD_LOG_WHITE_BLACK_LIST_H__
diff --git a/logd/README.auditd b/logd/README.auditd
new file mode 100644
index 0000000..3f614a3
--- /dev/null
+++ b/logd/README.auditd
@@ -0,0 +1,17 @@
+Auditd Daemon
+
+The audit daemon is a simplified version of its desktop
+counterpart designed to gather the audit logs from the
+audit kernel subsystem. The audit subsystem of the kernel
+includes Linux Security Modules (LSM) messages as well.
+
+To enable the audit subsystem, you must add this to your
+kernel config:
+CONFIG_AUDIT=y
+
+To enable a LSM, you must consult that LSM's documentation, the
+example below is for SELinux:
+CONFIG_SECURITY_SELINUX=y
+
+This does not include possible dependencies that may need to be
+satisfied for that particular LSM.
diff --git a/logd/README.property b/logd/README.property
new file mode 100644
index 0000000..a472efd
--- /dev/null
+++ b/logd/README.property
@@ -0,0 +1,27 @@
+The properties that logd responds to are:
+
+name type default description
+logd.auditd bool true Enable selinux audit daemon
+logd.auditd.dmesg bool true selinux audit messages duplicated and
+ sent on to dmesg log
+logd.klogd bool depends Enable klogd daemon
+logd.statistics bool depends Enable logcat -S statistics.
+ro.config.low_ram bool false if true, logd.statistics & logd.klogd
+ default false
+ro.build.type string if user, logd.statistics & logd.klogd
+ default false
+persist.logd.logpersistd string Enable logpersist daemon, "logcatd"
+ turns on logcat -f in logd context
+persist.logd.size number 256K default size of the buffer for all
+ log ids at initial startup, at runtime
+ use: logcat -b all -G <value>
+persist.logd.size.main number 256K Size of the buffer for the main log
+persist.logd.size.system number 256K Size of the buffer for the system log
+persist.logd.size.radio number 256K Size of the buffer for the radio log
+persist.logd.size.event number 256K Size of the buffer for the event log
+persist.logd.size.crash number 256K Size of the buffer for the crash log
+
+NB:
+- number support multipliers (K or M) for convenience. Range is limited
+ to between 64K and 256M for log buffer sizes. Individual logs override the
+ global default.
diff --git a/logd/event.logtags b/logd/event.logtags
new file mode 100644
index 0000000..db8c19b
--- /dev/null
+++ b/logd/event.logtags
@@ -0,0 +1,37 @@
+# The entries in this file map a sparse set of log tag numbers to tag names.
+# This is installed on the device, in /system/etc, and parsed by logcat.
+#
+# Tag numbers are decimal integers, from 0 to 2^31. (Let's leave the
+# negative values alone for now.)
+#
+# Tag names are one or more ASCII letters and numbers or underscores, i.e.
+# "[A-Z][a-z][0-9]_". Do not include spaces or punctuation (the former
+# impacts log readability, the latter makes regex searches more annoying).
+#
+# Tag numbers and names are separated by whitespace. Blank lines and lines
+# starting with '#' are ignored.
+#
+# Optionally, after the tag names can be put a description for the value(s)
+# of the tag. Description are in the format
+# (<name>|data type[|data unit])
+# Multiple values are separated by commas.
+#
+# The data type is a number from the following values:
+# 1: int
+# 2: long
+# 3: string
+# 4: list
+#
+# The data unit is a number taken from the following list:
+# 1: Number of objects
+# 2: Number of bytes
+# 3: Number of milliseconds
+# 4: Number of allocations
+# 5: Id
+# 6: Percent
+# Default value for data of type int/long is 2 (bytes).
+#
+# TODO: generate ".java" and ".h" files with integer constants from this file.
+
+1003 auditd (avc|3)
+1004 logd (dropped|3)
diff --git a/logd/libaudit.c b/logd/libaudit.c
new file mode 100644
index 0000000..d00d579
--- /dev/null
+++ b/logd/libaudit.c
@@ -0,0 +1,274 @@
+/*
+ * Copyright 2012, Samsung Telecommunications of America
+ * Copyright (C) 2014 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.
+ *
+ * Written by William Roberts <w.roberts@sta.samsung.com>
+ *
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#define LOG_TAG "libaudit"
+#include <log/log.h>
+
+#include "libaudit.h"
+
+/**
+ * Waits for an ack from the kernel
+ * @param fd
+ * The netlink socket fd
+ * @param seq
+ * The current sequence number were acking on
+ * @return
+ * This function returns 0 on success, else -errno.
+ */
+static int get_ack(int fd, int16_t seq)
+{
+ int rc;
+ struct audit_message rep;
+
+ /* Sanity check, this is an internal interface this shouldn't happen */
+ if (fd < 0) {
+ return -EINVAL;
+ }
+
+ rc = audit_get_reply(fd, &rep, GET_REPLY_BLOCKING, MSG_PEEK);
+ if (rc < 0) {
+ return rc;
+ }
+
+ if (rep.nlh.nlmsg_type == NLMSG_ERROR) {
+ audit_get_reply(fd, &rep, GET_REPLY_BLOCKING, 0);
+ rc = ((struct nlmsgerr *)rep.data)->error;
+ if (rc) {
+ return -rc;
+ }
+ }
+
+ if ((int16_t)rep.nlh.nlmsg_seq != seq) {
+ SLOGW("Expected sequence number between user space and kernel space is out of skew, "
+ "expected %u got %u", seq, rep.nlh.nlmsg_seq);
+ }
+
+ return 0;
+}
+
+/**
+ *
+ * @param fd
+ * The netlink socket fd
+ * @param type
+ * The type of netlink message
+ * @param data
+ * The data to send
+ * @param size
+ * The length of the data in bytes
+ * @return
+ * This function returns a positive sequence number on success, else -errno.
+ */
+static int audit_send(int fd, int type, const void *data, size_t size)
+{
+ int rc;
+ static int16_t sequence = 0;
+ struct audit_message req;
+ struct sockaddr_nl addr;
+
+ memset(&req, 0, sizeof(req));
+ memset(&addr, 0, sizeof(addr));
+
+ /* We always send netlink messaged */
+ addr.nl_family = AF_NETLINK;
+
+ /* Set up the netlink headers */
+ req.nlh.nlmsg_type = type;
+ req.nlh.nlmsg_len = NLMSG_SPACE(size);
+ req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+
+ /*
+ * Check for a valid fd, even though sendto would catch this, its easier
+ * to always blindly increment the sequence number
+ */
+ if (fd < 0) {
+ return -EBADF;
+ }
+
+ /* Ensure the message is not too big */
+ if (NLMSG_SPACE(size) > MAX_AUDIT_MESSAGE_LENGTH) {
+ SLOGE("netlink message is too large");
+ return -EINVAL;
+ }
+
+ /* Only memcpy in the data if it was specified */
+ if (size && data) {
+ memcpy(NLMSG_DATA(&req.nlh), data, size);
+ }
+
+ /*
+ * Only increment the sequence number on a guarantee
+ * you will send it to the kernel.
+ *
+ * Also, the sequence is defined as a u32 in the kernel
+ * struct. Using an int here might not work on 32/64 bit splits. A
+ * signed 64 bit value can overflow a u32..but a u32
+ * might not fit in the response, so we need to use s32.
+ * Which is still kind of hackish since int could be 16 bits
+ * in size. The only safe type to use here is a signed 16
+ * bit value.
+ */
+ req.nlh.nlmsg_seq = ++sequence;
+
+ /* While failing and its due to interrupts */
+
+ rc = TEMP_FAILURE_RETRY(sendto(fd, &req, req.nlh.nlmsg_len, 0,
+ (struct sockaddr*) &addr, sizeof(addr)));
+
+ /* Not all the bytes were sent */
+ if (rc < 0) {
+ rc = -errno;
+ SLOGE("Error sending data over the netlink socket: %s", strerror(-errno));
+ goto out;
+ } else if ((uint32_t) rc != req.nlh.nlmsg_len) {
+ rc = -EPROTO;
+ goto out;
+ }
+
+ /* We sent all the bytes, get the ack */
+ rc = get_ack(fd, sequence);
+
+ /* If the ack failed, return the error, else return the sequence number */
+ rc = (rc == 0) ? (int) sequence : rc;
+
+out:
+ /* Don't let sequence roll to negative */
+ if (sequence < 0) {
+ SLOGW("Auditd to Kernel sequence number has rolled over");
+ sequence = 0;
+ }
+
+ return rc;
+}
+
+int audit_setup(int fd, uint32_t pid)
+{
+ int rc;
+ struct audit_message rep;
+ struct audit_status status;
+
+ memset(&status, 0, sizeof(status));
+
+ /*
+ * In order to set the auditd PID we send an audit message over the netlink
+ * socket with the pid field of the status struct set to our current pid,
+ * and the the mask set to AUDIT_STATUS_PID
+ */
+ status.pid = pid;
+ status.mask = AUDIT_STATUS_PID | AUDIT_STATUS_RATE_LIMIT;
+ status.rate_limit = 20; // audit entries per second
+
+ /* Let the kernel know this pid will be registering for audit events */
+ rc = audit_send(fd, AUDIT_SET, &status, sizeof(status));
+ if (rc < 0) {
+ SLOGE("Could net set pid for audit events, error: %s", strerror(-rc));
+ return rc;
+ }
+
+ /*
+ * In a request where we need to wait for a response, wait for the message
+ * and discard it. This message confirms and sync's us with the kernel.
+ * This daemon is now registered as the audit logger.
+ *
+ * TODO
+ * If the daemon dies and restarts the message didn't come back,
+ * so I went to non-blocking and it seemed to fix the bug.
+ * Need to investigate further.
+ */
+ audit_get_reply(fd, &rep, GET_REPLY_NONBLOCKING, 0);
+
+ return 0;
+}
+
+int audit_open()
+{
+ return socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_AUDIT);
+}
+
+int audit_get_reply(int fd, struct audit_message *rep, reply_t block, int peek)
+{
+ ssize_t len;
+ int flags;
+ int rc = 0;
+
+ struct sockaddr_nl nladdr;
+ socklen_t nladdrlen = sizeof(nladdr);
+
+ if (fd < 0) {
+ return -EBADF;
+ }
+
+ /* Set up the flags for recv from */
+ flags = (block == GET_REPLY_NONBLOCKING) ? MSG_DONTWAIT : 0;
+ flags |= peek;
+
+ /*
+ * Get the data from the netlink socket but on error we need to be carefull,
+ * the interface shows that EINTR can never be returned, other errors,
+ * however, can be returned.
+ */
+ len = TEMP_FAILURE_RETRY(recvfrom(fd, rep, sizeof(*rep), flags,
+ (struct sockaddr*) &nladdr, &nladdrlen));
+
+ /*
+ * EAGAIN should be re-tried until success or another error manifests.
+ */
+ if (len < 0) {
+ rc = -errno;
+ if (block == GET_REPLY_NONBLOCKING && rc == -EAGAIN) {
+ /* If request is non blocking and errno is EAGAIN, just return 0 */
+ return 0;
+ }
+ SLOGE("Error receiving from netlink socket, error: %s", strerror(-rc));
+ return rc;
+ }
+
+ if (nladdrlen != sizeof(nladdr)) {
+ SLOGE("Protocol fault, error: %s", strerror(EPROTO));
+ return -EPROTO;
+ }
+
+ /* Make sure the netlink message was not spoof'd */
+ if (nladdr.nl_pid) {
+ SLOGE("Invalid netlink pid received, expected 0 got: %d", nladdr.nl_pid);
+ return -EINVAL;
+ }
+
+ /* Check if the reply from the kernel was ok */
+ if (!NLMSG_OK(&rep->nlh, (size_t)len)) {
+ rc = (len == sizeof(*rep)) ? -EFBIG : -EBADE;
+ SLOGE("Bad kernel response %s", strerror(-rc));
+ }
+
+ return rc;
+}
+
+void audit_close(int fd)
+{
+ int rc = close(fd);
+ if (rc < 0) {
+ SLOGE("Attempting to close invalid fd %d, error: %s", fd, strerror(errno));
+ }
+ return;
+}
diff --git a/logd/libaudit.h b/logd/libaudit.h
new file mode 100644
index 0000000..b9e330d
--- /dev/null
+++ b/logd/libaudit.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2012, Samsung Telecommunications of America
+ * Copyright (C) 2014 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.
+ *
+ * Written by William Roberts <w.roberts@sta.samsung.com>
+ */
+
+#ifndef _LIBAUDIT_H_
+#define _LIBAUDIT_H_
+
+#include <stdint.h>
+#include <sys/cdefs.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <linux/netlink.h>
+#include <linux/audit.h>
+
+__BEGIN_DECLS
+
+#define MAX_AUDIT_MESSAGE_LENGTH 8970
+
+typedef enum {
+ GET_REPLY_BLOCKING=0,
+ GET_REPLY_NONBLOCKING
+} reply_t;
+
+/* type == AUDIT_SIGNAL_INFO */
+struct audit_sig_info {
+ uid_t uid;
+ pid_t pid;
+ char ctx[0];
+};
+
+struct audit_message {
+ struct nlmsghdr nlh;
+ char data[MAX_AUDIT_MESSAGE_LENGTH];
+};
+
+/**
+ * Opens a connection to the Audit netlink socket
+ * @return
+ * A valid fd on success or < 0 on error with errno set.
+ * Returns the same errors as man 2 socket.
+ */
+extern int audit_open(void);
+
+/**
+ * Closes the fd returned from audit_open()
+ * @param fd
+ * The fd to close
+ */
+extern void audit_close(int fd);
+
+/**
+ *
+ * @param fd
+ * The fd returned by a call to audit_open()
+ * @param rep
+ * The response struct to store the response in.
+ * @param block
+ * Whether or not to block on IO
+ * @param peek
+ * Whether or not we are to remove the message from
+ * the queue when we do a read on the netlink socket.
+ * @return
+ * This function returns 0 on success, else -errno.
+ */
+extern int audit_get_reply(int fd, struct audit_message *rep, reply_t block,
+ int peek);
+
+/**
+ * Sets a pid to recieve audit netlink events from the kernel
+ * @param fd
+ * The fd returned by a call to audit_open()
+ * @param pid
+ * The pid whom to set as the reciever of audit messages
+ * @return
+ * This function returns 0 on success, -errno on error.
+ */
+extern int audit_setup(int fd, uint32_t pid);
+
+__END_DECLS
+
+#endif
diff --git a/logd/logpersist b/logd/logpersist
new file mode 100755
index 0000000..215e1e2
--- /dev/null
+++ b/logd/logpersist
@@ -0,0 +1,36 @@
+#! /system/bin/sh
+# logpersist cat start and stop handlers
+data=/data/misc/logd
+property=persist.logd.logpersistd
+service=logcatd
+progname="${0##*/}"
+if [ X"${1}" = "-h" -o X"${1}" = X"--help" ]; then
+ echo "${progname%.*}.cat - dump current ${service%d} logs"
+ echo "${progname%.*}.start - start ${service} service"
+ echo "${progname%.*}.stop [--clear] - stop ${service} service"
+ exit 0
+fi
+case ${progname} in
+*.cat)
+ su 1036 ls "${data}" |
+ tr -d '\r' |
+ sort -ru |
+ sed "s#^#${data}/#" |
+ su 1036 xargs cat
+ ;;
+*.start)
+ su 0 setprop ${property} ${service}
+ getprop ${property}
+ sleep 1
+ ps -t | grep "${data##*/}.*${service%d}"
+ ;;
+*.stop)
+ su 0 stop ${service}
+ su 0 setprop ${property} ""
+ [ X"${1}" != X"-c" -a X"${1}" != X"--clear" ] ||
+ ( sleep 1 ; su 1036,9998 rm -rf "${data}" )
+ ;;
+*)
+ echo "Unexpected command ${0##*/} ${@}" >&2
+ exit 1
+esac
diff --git a/logd/main.cpp b/logd/main.cpp
new file mode 100644
index 0000000..9b88983
--- /dev/null
+++ b/logd/main.cpp
@@ -0,0 +1,469 @@
+/*
+ * Copyright (C) 2012-2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <sched.h>
+#include <semaphore.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/capability.h>
+#include <sys/klog.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include <memory>
+
+#include <cutils/properties.h>
+#include <cutils/sched_policy.h>
+#include <cutils/sockets.h>
+#include <log/event_tag_map.h>
+#include <private/android_filesystem_config.h>
+#include <utils/threads.h>
+
+#include "CommandListener.h"
+#include "LogBuffer.h"
+#include "LogListener.h"
+#include "LogAudit.h"
+#include "LogKlog.h"
+
+#define KMSG_PRIORITY(PRI) \
+ '<', \
+ '0' + LOG_MAKEPRI(LOG_DAEMON, LOG_PRI(PRI)) / 10, \
+ '0' + LOG_MAKEPRI(LOG_DAEMON, LOG_PRI(PRI)) % 10, \
+ '>'
+
+//
+// The service is designed to be run by init, it does not respond well
+// to starting up manually. When starting up manually the sockets will
+// fail to open typically for one of the following reasons:
+// EADDRINUSE if logger is running.
+// EACCESS if started without precautions (below)
+//
+// Here is a cookbook procedure for starting up logd manually assuming
+// init is out of the way, pedantically all permissions and selinux
+// security is put back in place:
+//
+// setenforce 0
+// rm /dev/socket/logd*
+// chmod 777 /dev/socket
+// # here is where you would attach the debugger or valgrind for example
+// runcon u:r:logd:s0 /system/bin/logd </dev/null >/dev/null 2>&1 &
+// sleep 1
+// chmod 755 /dev/socket
+// chown logd.logd /dev/socket/logd*
+// restorecon /dev/socket/logd*
+// setenforce 1
+//
+// If minimalism prevails, typical for debugging and security is not a concern:
+//
+// setenforce 0
+// chmod 777 /dev/socket
+// logd
+//
+
+static int drop_privs() {
+ struct sched_param param;
+ memset(¶m, 0, sizeof(param));
+
+ if (set_sched_policy(0, SP_BACKGROUND) < 0) {
+ return -1;
+ }
+
+ if (sched_setscheduler((pid_t) 0, SCHED_BATCH, ¶m) < 0) {
+ return -1;
+ }
+
+ if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) {
+ return -1;
+ }
+
+ if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
+ return -1;
+ }
+
+ if (setgid(AID_LOGD) != 0) {
+ return -1;
+ }
+
+ if (setuid(AID_LOGD) != 0) {
+ return -1;
+ }
+
+ struct __user_cap_header_struct capheader;
+ struct __user_cap_data_struct capdata[2];
+ memset(&capheader, 0, sizeof(capheader));
+ memset(&capdata, 0, sizeof(capdata));
+ capheader.version = _LINUX_CAPABILITY_VERSION_3;
+ capheader.pid = 0;
+
+ capdata[CAP_TO_INDEX(CAP_SYSLOG)].permitted = CAP_TO_MASK(CAP_SYSLOG);
+ capdata[CAP_TO_INDEX(CAP_AUDIT_CONTROL)].permitted |= CAP_TO_MASK(CAP_AUDIT_CONTROL);
+
+ 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) {
+ return -1;
+ }
+
+ return 0;
+}
+
+// Property helper
+static bool property_get_bool(const char *key, bool def) {
+ char property[PROPERTY_VALUE_MAX];
+ property_get(key, property, "");
+
+ if (!strcasecmp(property, "true")) {
+ return true;
+ }
+ if (!strcasecmp(property, "false")) {
+ return false;
+ }
+
+ return def;
+}
+
+// Remove the static, and use this variable
+// globally for debugging if necessary. eg:
+// write(fdDmesg, "I am here\n", 10);
+static int fdDmesg = -1;
+
+static sem_t uidName;
+static uid_t uid;
+static char *name;
+
+static sem_t reinit;
+static bool reinit_running = false;
+static LogBuffer *logBuf = NULL;
+
+static void *reinit_thread_start(void * /*obj*/) {
+ prctl(PR_SET_NAME, "logd.daemon");
+ set_sched_policy(0, SP_BACKGROUND);
+ setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND);
+
+ setgid(AID_SYSTEM);
+ setuid(AID_SYSTEM);
+
+ while (reinit_running && !sem_wait(&reinit) && reinit_running) {
+
+ // uidToName Privileged Worker
+ if (uid) {
+ name = NULL;
+
+ FILE *fp = fopen("/data/system/packages.list", "r");
+ if (fp) {
+ // This simple parser is sensitive to format changes in
+ // frameworks/base/services/core/java/com/android/server/pm/Settings.java
+ // A dependency note has been added to that file to correct
+ // this parser.
+
+ char *buffer = NULL;
+ size_t len;
+ while (getline(&buffer, &len, fp) > 0) {
+ char *userId = strchr(buffer, ' ');
+ if (!userId) {
+ continue;
+ }
+ *userId = '\0';
+ unsigned long value = strtoul(userId + 1, NULL, 10);
+ if (value != uid) {
+ continue;
+ }
+ name = strdup(buffer);
+ break;
+ }
+ free(buffer);
+ fclose(fp);
+ }
+ uid = 0;
+ sem_post(&uidName);
+ continue;
+ }
+
+ if (fdDmesg >= 0) {
+ static const char reinit_message[] = { KMSG_PRIORITY(LOG_INFO),
+ 'l', 'o', 'g', 'd', '.', 'd', 'a', 'e', 'm', 'o', 'n', ':',
+ ' ', 'r', 'e', 'i', 'n', 'i', 't', '\n' };
+ write(fdDmesg, reinit_message, sizeof(reinit_message));
+ }
+
+ // Anything that reads persist.<property>
+ if (logBuf) {
+ logBuf->init();
+ }
+ }
+
+ return NULL;
+}
+
+static sem_t sem_name;
+
+char *android::uidToName(uid_t u) {
+ if (!u || !reinit_running) {
+ return NULL;
+ }
+
+ sem_wait(&sem_name);
+
+ // Not multi-thread safe, we use sem_name to protect
+ uid = u;
+
+ name = NULL;
+ sem_post(&reinit);
+ sem_wait(&uidName);
+ char *ret = name;
+
+ sem_post(&sem_name);
+
+ return ret;
+}
+
+// Serves as a global method to trigger reinitialization
+// and as a function that can be provided to signal().
+void reinit_signal_handler(int /*signal*/) {
+ sem_post(&reinit);
+}
+
+// tagToName converts an events tag into a name
+const char *android::tagToName(uint32_t tag) {
+ static const EventTagMap *map;
+
+ if (!map) {
+ sem_wait(&sem_name);
+ if (!map) {
+ map = android_openEventTagMap(EVENT_TAG_MAP_FILE);
+ }
+ sem_post(&sem_name);
+ if (!map) {
+ return NULL;
+ }
+ }
+ return android_lookupEventTag(map, tag);
+}
+
+static bool property_get_bool_svelte(const char *key) {
+ bool not_user;
+ {
+ char property[PROPERTY_VALUE_MAX];
+ property_get("ro.build.type", property, "");
+ not_user = !!strcmp(property, "user");
+ }
+ return property_get_bool(key, not_user
+ && !property_get_bool("ro.config.low_ram", false));
+}
+
+static void readDmesg(LogAudit *al, LogKlog *kl) {
+ if (!al && !kl) {
+ return;
+ }
+
+ int len = klogctl(KLOG_SIZE_BUFFER, NULL, 0);
+ if (len <= 0) {
+ return;
+ }
+
+ len += 1024; // Margin for additional input race or trailing nul
+ std::unique_ptr<char []> buf(new char[len]);
+
+ int rc = klogctl(KLOG_READ_ALL, buf.get(), len);
+ if (rc <= 0) {
+ return;
+ }
+
+ if (rc < len) {
+ len = rc + 1;
+ }
+ buf[len - 1] = '\0';
+
+ if (kl) {
+ kl->synchronize(buf.get());
+ }
+
+ for (char *ptr = NULL, *tok = buf.get();
+ (rc >= 0) && ((tok = log_strtok_r(tok, &ptr)));
+ tok = NULL) {
+ if (al) {
+ rc = al->log(tok);
+ }
+ if (kl) {
+ rc = kl->log(tok);
+ }
+ }
+}
+
+// Foreground waits for exit of the main persistent threads
+// that are started here. The threads are created to manage
+// UNIX domain client sockets for writing, reading and
+// controlling the user space logger, and for any additional
+// logging plugins like auditd and restart control. Additional
+// transitory per-client threads are created for each reader.
+int main(int argc, char *argv[]) {
+ int fdPmesg = -1;
+ bool klogd = property_get_bool_svelte("logd.klogd");
+ if (klogd) {
+ fdPmesg = open("/proc/kmsg", O_RDONLY | O_NDELAY);
+ }
+ fdDmesg = open("/dev/kmsg", O_WRONLY);
+
+ // issue reinit command. KISS argument parsing.
+ if ((argc > 1) && argv[1] && !strcmp(argv[1], "--reinit")) {
+ int sock = TEMP_FAILURE_RETRY(
+ socket_local_client("logd",
+ ANDROID_SOCKET_NAMESPACE_RESERVED,
+ SOCK_STREAM));
+ if (sock < 0) {
+ return -errno;
+ }
+ static const char reinit[] = "reinit";
+ ssize_t ret = TEMP_FAILURE_RETRY(write(sock, reinit, sizeof(reinit)));
+ if (ret < 0) {
+ return -errno;
+ }
+ struct pollfd p;
+ memset(&p, 0, sizeof(p));
+ p.fd = sock;
+ p.events = POLLIN;
+ ret = TEMP_FAILURE_RETRY(poll(&p, 1, 100));
+ if (ret < 0) {
+ return -errno;
+ }
+ if ((ret == 0) || !(p.revents & POLLIN)) {
+ return -ETIME;
+ }
+ static const char success[] = "success";
+ char buffer[sizeof(success) - 1];
+ memset(buffer, 0, sizeof(buffer));
+ ret = TEMP_FAILURE_RETRY(read(sock, buffer, sizeof(buffer)));
+ if (ret < 0) {
+ return -errno;
+ }
+ return strncmp(buffer, success, sizeof(success) - 1) != 0;
+ }
+
+ // Reinit Thread
+ sem_init(&reinit, 0, 0);
+ sem_init(&uidName, 0, 0);
+ sem_init(&sem_name, 0, 1);
+ pthread_attr_t attr;
+ if (!pthread_attr_init(&attr)) {
+ struct sched_param param;
+
+ memset(¶m, 0, sizeof(param));
+ pthread_attr_setschedparam(&attr, ¶m);
+ pthread_attr_setschedpolicy(&attr, SCHED_BATCH);
+ if (!pthread_attr_setdetachstate(&attr,
+ PTHREAD_CREATE_DETACHED)) {
+ pthread_t thread;
+ reinit_running = true;
+ if (pthread_create(&thread, &attr, reinit_thread_start, NULL)) {
+ reinit_running = false;
+ }
+ }
+ pthread_attr_destroy(&attr);
+ }
+
+ if (drop_privs() != 0) {
+ return -1;
+ }
+
+ // Serves the purpose of managing the last logs times read on a
+ // socket connection, and as a reader lock on a range of log
+ // entries.
+
+ LastLogTimes *times = new LastLogTimes();
+
+ // LogBuffer is the object which is responsible for holding all
+ // log entries.
+
+ logBuf = new LogBuffer(times);
+
+ signal(SIGHUP, reinit_signal_handler);
+
+ if (property_get_bool_svelte("logd.statistics")) {
+ logBuf->enableStatistics();
+ }
+
+ // LogReader listens on /dev/socket/logdr. When a client
+ // connects, log entries in the LogBuffer are written to the client.
+
+ LogReader *reader = new LogReader(logBuf);
+ if (reader->startListener()) {
+ exit(1);
+ }
+
+ // LogListener listens on /dev/socket/logdw for client
+ // initiated log messages. New log entries are added to LogBuffer
+ // and LogReader is notified to send updates to connected clients.
+
+ LogListener *swl = new LogListener(logBuf, reader);
+ // Backlog and /proc/sys/net/unix/max_dgram_qlen set to large value
+ if (swl->startListener(300)) {
+ exit(1);
+ }
+
+ // Command listener listens on /dev/socket/logd for incoming logd
+ // administrative commands.
+
+ CommandListener *cl = new CommandListener(logBuf, reader, swl);
+ if (cl->startListener()) {
+ exit(1);
+ }
+
+ // LogAudit listens on NETLINK_AUDIT socket for selinux
+ // initiated log messages. New log entries are added to LogBuffer
+ // and LogReader is notified to send updates to connected clients.
+
+ bool auditd = property_get_bool("logd.auditd", true);
+
+ LogAudit *al = NULL;
+ if (auditd) {
+ bool dmesg = property_get_bool("logd.auditd.dmesg", true);
+ al = new LogAudit(logBuf, reader, dmesg ? fdDmesg : -1);
+ }
+
+ LogKlog *kl = NULL;
+ if (klogd) {
+ kl = new LogKlog(logBuf, reader, fdDmesg, fdPmesg, al != NULL);
+ }
+
+ readDmesg(al, kl);
+
+ // failure is an option ... messages are in dmesg (required by standard)
+
+ if (kl && kl->startListener()) {
+ delete kl;
+ }
+
+ if (al && al->startListener()) {
+ delete al;
+ }
+
+ TEMP_FAILURE_RETRY(pause());
+
+ exit(0);
+}
diff --git a/logd/tests/Android.mk b/logd/tests/Android.mk
new file mode 100644
index 0000000..85ca4ac
--- /dev/null
+++ b/logd/tests/Android.mk
@@ -0,0 +1,52 @@
+#
+# Copyright (C) 2014 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)
+
+# -----------------------------------------------------------------------------
+# Benchmarks. (see ../../liblog/tests)
+# -----------------------------------------------------------------------------
+
+test_module_prefix := logd-
+test_tags := tests
+
+# -----------------------------------------------------------------------------
+# Unit tests.
+# -----------------------------------------------------------------------------
+
+test_c_flags := \
+ -fstack-protector-all \
+ -g \
+ -Wall -Wextra \
+ -Werror \
+ -fno-builtin \
+
+ifneq ($(TARGET_USES_LOGD),false)
+test_c_flags += -DTARGET_USES_LOGD=1
+endif
+
+test_src_files := \
+ logd_test.cpp
+
+# Build tests for the logger. Run with:
+# adb shell /data/nativetest/logd-unit-tests/logd-unit-tests
+include $(CLEAR_VARS)
+LOCAL_MODULE := $(test_module_prefix)unit-tests
+LOCAL_MODULE_TAGS := $(test_tags)
+LOCAL_CFLAGS += $(test_c_flags)
+LOCAL_SHARED_LIBRARIES := libcutils
+LOCAL_SRC_FILES := $(test_src_files)
+include $(BUILD_NATIVE_TEST)
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
new file mode 100644
index 0000000..3266360
--- /dev/null
+++ b/logd/tests/logd_test.cpp
@@ -0,0 +1,535 @@
+/*
+ * Copyright (C) 2014 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 <fcntl.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <gtest/gtest.h>
+
+#include "cutils/sockets.h"
+#include "log/log.h"
+#include "log/logger.h"
+
+#define __unused __attribute__((__unused__))
+
+/*
+ * returns statistics
+ */
+static void my_android_logger_get_statistics(char *buf, size_t len)
+{
+ snprintf(buf, len, "getStatistics 0 1 2 3 4");
+ int sock = socket_local_client("logd",
+ ANDROID_SOCKET_NAMESPACE_RESERVED,
+ SOCK_STREAM);
+ if (sock >= 0) {
+ if (write(sock, buf, strlen(buf) + 1) > 0) {
+ ssize_t ret;
+ while ((ret = read(sock, buf, len)) > 0) {
+ if ((size_t)ret == len) {
+ break;
+ }
+ len -= ret;
+ buf += ret;
+
+ struct pollfd p = {
+ .fd = sock,
+ .events = POLLIN,
+ .revents = 0
+ };
+
+ ret = poll(&p, 1, 20);
+ if ((ret <= 0) || !(p.revents & POLLIN)) {
+ break;
+ }
+ }
+ }
+ close(sock);
+ }
+}
+
+static void alloc_statistics(char **buffer, size_t *length)
+{
+ size_t len = 8192;
+ char *buf;
+
+ for(int retry = 32; (retry >= 0); delete [] buf, --retry) {
+ buf = new char [len];
+ my_android_logger_get_statistics(buf, len);
+
+ buf[len-1] = '\0';
+ size_t ret = atol(buf) + 1;
+ if (ret < 4) {
+ delete [] buf;
+ buf = NULL;
+ break;
+ }
+ bool check = ret <= len;
+ len = ret;
+ if (check) {
+ break;
+ }
+ len += len / 8; // allow for some slop
+ }
+ *buffer = buf;
+ *length = len;
+}
+
+static char *find_benchmark_spam(char *cp)
+{
+ // liblog_benchmarks has been run designed to SPAM. The signature of
+ // a noisiest UID statistics is:
+ //
+ // Chattiest UIDs in main log buffer: Size Pruned
+ // UID PACKAGE BYTES LINES
+ // 0 root 54164 147569
+ //
+ char *benchmark = NULL;
+ do {
+ static const char signature[] = "\n0 root ";
+
+ benchmark = strstr(cp, signature);
+ if (!benchmark) {
+ break;
+ }
+ cp = benchmark + sizeof(signature);
+ while (isspace(*cp)) {
+ ++cp;
+ }
+ benchmark = cp;
+ while (isdigit(*cp)) {
+ ++cp;
+ }
+ while (isspace(*cp)) {
+ ++cp;
+ }
+ unsigned long value = 0;
+ while (isdigit(*cp)) {
+ value = value * 10ULL + *cp - '0';
+ ++cp;
+ }
+ if (value > 100000UL) {
+ break;
+ }
+ benchmark = NULL;
+ } while (*cp);
+ return benchmark;
+}
+
+TEST(logd, statistics) {
+ size_t len;
+ char *buf;
+
+ alloc_statistics(&buf, &len);
+
+#ifdef TARGET_USES_LOGD
+ ASSERT_TRUE(NULL != buf);
+#else
+ if (!buf) {
+ return;
+ }
+#endif
+
+ // remove trailing FF
+ char *cp = buf + len - 1;
+ *cp = '\0';
+ bool truncated = *--cp != '\f';
+ if (!truncated) {
+ *cp = '\0';
+ }
+
+ // squash out the byte count
+ cp = buf;
+ if (!truncated) {
+ while (isdigit(*cp) || (*cp == '\n')) {
+ ++cp;
+ }
+ }
+
+ fprintf(stderr, "%s", cp);
+
+ EXPECT_LT((size_t)64, strlen(cp));
+
+ EXPECT_EQ(0, truncated);
+
+#ifdef TARGET_USES_LOGD
+ char *main_logs = strstr(cp, "\nChattiest UIDs in main ");
+ EXPECT_TRUE(NULL != main_logs);
+
+ char *radio_logs = strstr(cp, "\nChattiest UIDs in radio ");
+ EXPECT_TRUE(NULL != radio_logs);
+
+ char *system_logs = strstr(cp, "\nChattiest UIDs in system ");
+ EXPECT_TRUE(NULL != system_logs);
+
+ char *events_logs = strstr(cp, "\nChattiest UIDs in events ");
+ EXPECT_TRUE(NULL != events_logs);
+#endif
+
+ delete [] buf;
+}
+
+static void caught_signal(int signum __unused) { }
+
+static void dump_log_msg(const char *prefix,
+ log_msg *msg, unsigned int version, int lid) {
+ switch(msg->entry.hdr_size) {
+ case 0:
+ version = 1;
+ break;
+
+ case sizeof(msg->entry_v2):
+ if (version == 0) {
+ version = 2;
+ }
+ break;
+ }
+
+ fprintf(stderr, "%s: v%u[%u] ", prefix, version, msg->len());
+ if (version != 1) {
+ fprintf(stderr, "hdr_size=%u ", msg->entry.hdr_size);
+ }
+ fprintf(stderr, "pid=%u tid=%u %u.%09u ",
+ msg->entry.pid, msg->entry.tid, msg->entry.sec, msg->entry.nsec);
+ switch(version) {
+ case 1:
+ break;
+ case 2:
+ fprintf(stderr, "euid=%u ", msg->entry_v2.euid);
+ break;
+ case 3:
+ default:
+ lid = msg->entry.lid;
+ break;
+ }
+
+ switch(lid) {
+ case 0:
+ fprintf(stderr, "lid=main ");
+ break;
+ case 1:
+ fprintf(stderr, "lid=radio ");
+ break;
+ case 2:
+ fprintf(stderr, "lid=events ");
+ break;
+ case 3:
+ fprintf(stderr, "lid=system ");
+ break;
+ default:
+ if (lid >= 0) {
+ fprintf(stderr, "lid=%d ", lid);
+ }
+ }
+
+ unsigned int len = msg->entry.len;
+ fprintf(stderr, "msg[%u]={", len);
+ unsigned char *cp = reinterpret_cast<unsigned char *>(msg->msg());
+ while(len) {
+ unsigned char *p = cp;
+ while (*p && (((' ' <= *p) && (*p < 0x7F)) || (*p == '\n'))) {
+ ++p;
+ }
+ if (((p - cp) > 3) && !*p && ((unsigned int)(p - cp) < len)) {
+ fprintf(stderr, "\"");
+ while (*cp) {
+ if (*cp != '\n') {
+ fprintf(stderr, "%c", *cp);
+ } else {
+ fprintf(stderr, "\\n");
+ }
+ ++cp;
+ --len;
+ }
+ fprintf(stderr, "\"");
+ } else {
+ fprintf(stderr, "%02x", *cp);
+ }
+ ++cp;
+ if (--len) {
+ fprintf(stderr, ", ");
+ }
+ }
+ fprintf(stderr, "}\n");
+}
+
+TEST(logd, both) {
+ log_msg msg;
+
+ // check if we can read any logs from logd
+ bool user_logger_available = false;
+ bool user_logger_content = false;
+
+ int fd = socket_local_client("logdr",
+ ANDROID_SOCKET_NAMESPACE_RESERVED,
+ SOCK_SEQPACKET);
+ if (fd >= 0) {
+ struct sigaction ignore, old_sigaction;
+ memset(&ignore, 0, sizeof(ignore));
+ ignore.sa_handler = caught_signal;
+ sigemptyset(&ignore.sa_mask);
+ sigaction(SIGALRM, &ignore, &old_sigaction);
+ unsigned int old_alarm = alarm(10);
+
+ static const char ask[] = "dumpAndClose lids=0,1,2,3";
+ user_logger_available = write(fd, ask, sizeof(ask)) == sizeof(ask);
+
+ user_logger_content = recv(fd, msg.buf, sizeof(msg), 0) > 0;
+
+ if (user_logger_content) {
+ dump_log_msg("user", &msg, 3, -1);
+ }
+
+ alarm(old_alarm);
+ sigaction(SIGALRM, &old_sigaction, NULL);
+
+ close(fd);
+ }
+
+ // check if we can read any logs from kernel logger
+ bool kernel_logger_available = false;
+ bool kernel_logger_content = false;
+
+ static const char *loggers[] = {
+ "/dev/log/main", "/dev/log_main",
+ "/dev/log/radio", "/dev/log_radio",
+ "/dev/log/events", "/dev/log_events",
+ "/dev/log/system", "/dev/log_system",
+ };
+
+ for (unsigned int i = 0; i < (sizeof(loggers) / sizeof(loggers[0])); ++i) {
+ fd = open(loggers[i], O_RDONLY);
+ if (fd < 0) {
+ continue;
+ }
+ kernel_logger_available = true;
+ fcntl(fd, F_SETFL, O_RDONLY | O_NONBLOCK);
+ int result = TEMP_FAILURE_RETRY(read(fd, msg.buf, sizeof(msg)));
+ if (result > 0) {
+ kernel_logger_content = true;
+ dump_log_msg("kernel", &msg, 0, i / 2);
+ }
+ close(fd);
+ }
+
+ static const char yes[] = "\xE2\x9C\x93";
+ static const char no[] = "\xE2\x9c\x98";
+ fprintf(stderr,
+ "LOGGER Available Content\n"
+ "user %-13s%s\n"
+ "kernel %-13s%s\n"
+ " status %-11s%s\n",
+ (user_logger_available) ? yes : no,
+ (user_logger_content) ? yes : no,
+ (kernel_logger_available) ? yes : no,
+ (kernel_logger_content) ? yes : no,
+ (user_logger_available && kernel_logger_available) ? "ERROR" : "ok",
+ (user_logger_content && kernel_logger_content) ? "ERROR" : "ok");
+
+ EXPECT_EQ(0, user_logger_available && kernel_logger_available);
+ EXPECT_EQ(0, !user_logger_available && !kernel_logger_available);
+ EXPECT_EQ(0, user_logger_content && kernel_logger_content);
+ EXPECT_EQ(0, !user_logger_content && !kernel_logger_content);
+}
+
+// BAD ROBOT
+// Benchmark threshold are generally considered bad form unless there is
+// is some human love applied to the continued maintenance and whether the
+// thresholds are tuned on a per-target basis. Here we check if the values
+// are more than double what is expected. Doubling will not prevent failure
+// on busy or low-end systems that could have a tendency to stretch values.
+//
+// The primary goal of this test is to simulate a spammy app (benchmark
+// being the worst) and check to make sure the logger can deal with it
+// appropriately by checking all the statistics are in an expected range.
+//
+TEST(logd, benchmark) {
+ size_t len;
+ char *buf;
+
+ alloc_statistics(&buf, &len);
+ bool benchmark_already_run = buf && find_benchmark_spam(buf);
+ delete [] buf;
+
+ if (benchmark_already_run) {
+ fprintf(stderr, "WARNING: spam already present and too much history\n"
+ " false OK for prune by worst UID check\n");
+ }
+
+ FILE *fp;
+
+ // Introduce some extreme spam for the worst UID filter
+ ASSERT_TRUE(NULL != (fp = popen(
+ "/data/nativetest/liblog-benchmarks/liblog-benchmarks",
+ "r")));
+
+ char buffer[5120];
+
+ static const char *benchmarks[] = {
+ "BM_log_maximum_retry ",
+ "BM_log_maximum ",
+ "BM_clock_overhead ",
+ "BM_log_overhead ",
+ "BM_log_latency ",
+ "BM_log_delay "
+ };
+ static const unsigned int log_maximum_retry = 0;
+ static const unsigned int log_maximum = 1;
+ static const unsigned int clock_overhead = 2;
+ static const unsigned int log_overhead = 3;
+ static const unsigned int log_latency = 4;
+ static const unsigned int log_delay = 5;
+
+ unsigned long ns[sizeof(benchmarks) / sizeof(benchmarks[0])];
+
+ memset(ns, 0, sizeof(ns));
+
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ for (unsigned i = 0; i < sizeof(ns) / sizeof(ns[0]); ++i) {
+ char *cp = strstr(buffer, benchmarks[i]);
+ if (!cp) {
+ continue;
+ }
+ sscanf(cp, "%*s %lu %lu", &ns[i], &ns[i]);
+ fprintf(stderr, "%-22s%8lu\n", benchmarks[i], ns[i]);
+ }
+ }
+ int ret = pclose(fp);
+
+ if (!WIFEXITED(ret) || (WEXITSTATUS(ret) == 127)) {
+ fprintf(stderr,
+ "WARNING: "
+ "/data/nativetest/liblog-benchmarks/liblog-benchmarks missing\n"
+ " can not perform test\n");
+ return;
+ }
+
+#ifdef TARGET_USES_LOGD
+ EXPECT_GE(200000UL, ns[log_maximum_retry]); // 104734 user
+#else
+ EXPECT_GE(10000UL, ns[log_maximum_retry]); // 5636 kernel
+#endif
+
+#ifdef TARGET_USES_LOGD
+ EXPECT_GE(90000UL, ns[log_maximum]); // 46913 user
+#else
+ EXPECT_GE(10000UL, ns[log_maximum]); // 5637 kernel
+#endif
+
+ EXPECT_GE(4096UL, ns[clock_overhead]); // 4095
+
+#ifdef TARGET_USES_LOGD
+ EXPECT_GE(250000UL, ns[log_overhead]); // 126886 user
+#else
+ EXPECT_GE(100000UL, ns[log_overhead]); // 50945 kernel
+#endif
+
+#ifdef TARGET_USES_LOGD
+ EXPECT_GE(10000UL, ns[log_latency]); // 5669 user space
+#else
+ EXPECT_GE(500000UL, ns[log_latency]); // 254200 kernel
+#endif
+
+#ifdef TARGET_USES_LOGD
+ EXPECT_GE(20000000UL, ns[log_delay]); // 10500289 user
+#else
+ EXPECT_GE(55000UL, ns[log_delay]); // 27341 kernel
+#endif
+
+ for (unsigned i = 0; i < sizeof(ns) / sizeof(ns[0]); ++i) {
+ EXPECT_NE(0UL, ns[i]);
+ }
+
+ alloc_statistics(&buf, &len);
+
+#ifdef TARGET_USES_LOGD
+ bool collected_statistics = !!buf;
+ EXPECT_EQ(true, collected_statistics);
+#else
+ if (!buf) {
+ return;
+ }
+#endif
+
+ ASSERT_TRUE(NULL != buf);
+
+ char *benchmark_statistics_found = find_benchmark_spam(buf);
+ ASSERT_TRUE(benchmark_statistics_found != NULL);
+
+ // Check how effective the SPAM filter is, parse out Now size.
+ // 0 root 54164 147569
+ // ^-- benchmark_statistics_found
+
+ unsigned long nowSpamSize = atol(benchmark_statistics_found);
+
+ delete [] buf;
+
+ ASSERT_NE(0UL, nowSpamSize);
+
+ // Determine if we have the spam filter enabled
+ int sock = socket_local_client("logd",
+ ANDROID_SOCKET_NAMESPACE_RESERVED,
+ SOCK_STREAM);
+
+ ASSERT_TRUE(sock >= 0);
+
+ static const char getPruneList[] = "getPruneList";
+ if (write(sock, getPruneList, sizeof(getPruneList)) > 0) {
+ char buffer[80];
+ memset(buffer, 0, sizeof(buffer));
+ read(sock, buffer, sizeof(buffer));
+ char *cp = strchr(buffer, '\n');
+ if (!cp || (cp[1] != '~') || (cp[2] != '!')) {
+ close(sock);
+ fprintf(stderr,
+ "WARNING: "
+ "Logger has SPAM filtration turned off \"%s\"\n", buffer);
+ return;
+ }
+ } else {
+ int save_errno = errno;
+ close(sock);
+ FAIL() << "Can not send " << getPruneList << " to logger -- " << strerror(save_errno);
+ }
+
+ static const unsigned long expected_absolute_minimum_log_size = 65536UL;
+ unsigned long totalSize = expected_absolute_minimum_log_size;
+ static const char getSize[] = {
+ 'g', 'e', 't', 'L', 'o', 'g', 'S', 'i', 'z', 'e', ' ',
+ LOG_ID_MAIN + '0', '\0'
+ };
+ if (write(sock, getSize, sizeof(getSize)) > 0) {
+ char buffer[80];
+ memset(buffer, 0, sizeof(buffer));
+ read(sock, buffer, sizeof(buffer));
+ totalSize = atol(buffer);
+ if (totalSize < expected_absolute_minimum_log_size) {
+ fprintf(stderr,
+ "WARNING: "
+ "Logger had unexpected referenced size \"%s\"\n", buffer);
+ totalSize = expected_absolute_minimum_log_size;
+ }
+ }
+ close(sock);
+
+ // logd allows excursions to 110% of total size
+ totalSize = (totalSize * 11 ) / 10;
+
+ // 50% threshold for SPAM filter (<20% typical, lots of engineering margin)
+ ASSERT_GT(totalSize, nowSpamSize * 2);
+}
diff --git a/logwrapper/Android.mk b/logwrapper/Android.mk
new file mode 100644
index 0000000..61b4659
--- /dev/null
+++ b/logwrapper/Android.mk
@@ -0,0 +1,37 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# ========================================================
+# Static library
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := liblogwrap
+LOCAL_SRC_FILES := logwrap.c
+LOCAL_SHARED_LIBRARIES := libcutils liblog
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_CFLAGS := -Werror
+include $(BUILD_STATIC_LIBRARY)
+
+# ========================================================
+# Shared library
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := liblogwrap
+LOCAL_SHARED_LIBRARIES := libcutils liblog
+LOCAL_WHOLE_STATIC_LIBRARIES := liblogwrap
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_CFLAGS := -Werror
+include $(BUILD_SHARED_LIBRARY)
+
+# ========================================================
+# Executable
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES:= logwrapper.c
+LOCAL_MODULE := logwrapper
+LOCAL_STATIC_LIBRARIES := liblog liblogwrap libcutils
+LOCAL_CFLAGS := -Werror
+include $(BUILD_EXECUTABLE)
diff --git a/logwrapper/NOTICE b/logwrapper/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/logwrapper/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/logwrapper/include/logwrap/logwrap.h b/logwrapper/include/logwrap/logwrap.h
new file mode 100644
index 0000000..4307a30
--- /dev/null
+++ b/logwrapper/include/logwrap/logwrap.h
@@ -0,0 +1,87 @@
+/* system/core/include/logwrap/logwrap.h
+ *
+ * Copyright 2013, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LIBS_LOGWRAP_H
+#define __LIBS_LOGWRAP_H
+
+#include <stdbool.h>
+
+__BEGIN_DECLS
+
+/*
+ * Run a command while logging its stdout and stderr
+ *
+ * WARNING: while this function is running it will clear all SIGCHLD handlers
+ * if you rely on SIGCHLD in the caller there is a chance zombies will be
+ * created if you're not calling waitpid after calling this. This function will
+ * log a warning when it clears SIGCHLD for processes other than the child it
+ * created.
+ *
+ * Arguments:
+ * argc: the number of elements in argv
+ * argv: an array of strings containing the command to be executed and its
+ * arguments as separate strings. argv does not need to be
+ * NULL-terminated
+ * status: the equivalent child status as populated by wait(status). This
+ * value is only valid when logwrap successfully completes. If NULL
+ * the return value of the child will be the function's return value.
+ * ignore_int_quit: set to true if you want to completely ignore SIGINT and
+ * SIGQUIT while logwrap is running. This may force the end-user to
+ * send a signal twice to signal the caller (once for the child, and
+ * once for the caller)
+ * log_target: Specify where to log the output of the child, either LOG_NONE,
+ * LOG_ALOG (for the Android system log), LOG_KLOG (for the kernel
+ * log), or LOG_FILE (and you need to specify a pathname in the
+ * file_path argument, otherwise pass NULL). These are bit fields,
+ * and can be OR'ed together to log to multiple places.
+ * abbreviated: If true, capture up to the first 100 lines and last 4K of
+ * output from the child. The abbreviated output is not dumped to
+ * the specified log until the child has exited.
+ * file_path: if log_target has the LOG_FILE bit set, then this parameter
+ * must be set to the pathname of the file to log to.
+ *
+ * Return value:
+ * 0 when logwrap successfully run the child process and captured its status
+ * -1 when an internal error occurred
+ * -ECHILD if status is NULL and the child didn't exit properly
+ * the return value of the child if it exited properly and status is NULL
+ *
+ */
+
+/* Values for the log_target parameter android_fork_execvp_ext() */
+#define LOG_NONE 0
+#define LOG_ALOG 1
+#define LOG_KLOG 2
+#define LOG_FILE 4
+
+int android_fork_execvp_ext(int argc, char* argv[], int *status, bool ignore_int_quit,
+ int log_target, bool abbreviated, char *file_path);
+
+/* Similar to above, except abbreviated logging is not available, and if logwrap
+ * is true, logging is to the Android system log, and if false, there is no
+ * logging.
+ */
+static inline int android_fork_execvp(int argc, char* argv[], int *status,
+ bool ignore_int_quit, bool logwrap)
+{
+ return android_fork_execvp_ext(argc, argv, status, ignore_int_quit,
+ (logwrap ? LOG_ALOG : LOG_NONE), false, NULL);
+}
+
+__END_DECLS
+
+#endif /* __LIBS_LOGWRAP_H */
diff --git a/logwrapper/logwrap.c b/logwrapper/logwrap.c
new file mode 100644
index 0000000..777dafe
--- /dev/null
+++ b/logwrapper/logwrap.c
@@ -0,0 +1,566 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <poll.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <stdbool.h>
+#include <pthread.h>
+
+#include <logwrap/logwrap.h>
+#include "private/android_filesystem_config.h"
+#include "cutils/log.h"
+#include <cutils/klog.h>
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
+#define MIN(a,b) (((a)<(b))?(a):(b))
+
+static pthread_mutex_t fd_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+#define ERROR(fmt, args...) \
+do { \
+ fprintf(stderr, fmt, ## args); \
+ ALOG(LOG_ERROR, "logwrapper", fmt, ## args); \
+} while(0)
+
+#define FATAL_CHILD(fmt, args...) \
+do { \
+ ERROR(fmt, ## args); \
+ _exit(-1); \
+} while(0)
+
+#define MAX_KLOG_TAG 16
+
+/* This is a simple buffer that holds up to the first beginning_buf->buf_size
+ * bytes of output from a command.
+ */
+#define BEGINNING_BUF_SIZE 0x1000
+struct beginning_buf {
+ char *buf;
+ size_t alloc_len;
+ /* buf_size is the usable space, which is one less than the allocated size */
+ size_t buf_size;
+ size_t used_len;
+};
+
+/* This is a circular buf that holds up to the last ending_buf->buf_size bytes
+ * of output from a command after the first beginning_buf->buf_size bytes
+ * (which are held in beginning_buf above).
+ */
+#define ENDING_BUF_SIZE 0x1000
+struct ending_buf {
+ char *buf;
+ ssize_t alloc_len;
+ /* buf_size is the usable space, which is one less than the allocated size */
+ ssize_t buf_size;
+ ssize_t used_len;
+ /* read and write offsets into the circular buffer */
+ int read;
+ int write;
+};
+
+ /* A structure to hold all the abbreviated buf data */
+struct abbr_buf {
+ struct beginning_buf b_buf;
+ struct ending_buf e_buf;
+ int beginning_buf_full;
+};
+
+/* Collect all the various bits of info needed for logging in one place. */
+struct log_info {
+ int log_target;
+ char klog_fmt[MAX_KLOG_TAG * 2];
+ char *btag;
+ bool abbreviated;
+ FILE *fp;
+ struct abbr_buf a_buf;
+};
+
+/* Forware declaration */
+static void add_line_to_abbr_buf(struct abbr_buf *a_buf, char *linebuf, int linelen);
+
+/* Return 0 on success, and 1 when full */
+static int add_line_to_linear_buf(struct beginning_buf *b_buf,
+ char *line, ssize_t line_len)
+{
+ int full = 0;
+
+ if ((line_len + b_buf->used_len) > b_buf->buf_size) {
+ full = 1;
+ } else {
+ /* Add to the end of the buf */
+ memcpy(b_buf->buf + b_buf->used_len, line, line_len);
+ b_buf->used_len += line_len;
+ }
+
+ return full;
+}
+
+static void add_line_to_circular_buf(struct ending_buf *e_buf,
+ char *line, ssize_t line_len)
+{
+ ssize_t free_len;
+ ssize_t needed_space;
+ int cnt;
+
+ if (e_buf->buf == NULL) {
+ return;
+ }
+
+ if (line_len > e_buf->buf_size) {
+ return;
+ }
+
+ free_len = e_buf->buf_size - e_buf->used_len;
+
+ if (line_len > free_len) {
+ /* remove oldest entries at read, and move read to make
+ * room for the new string */
+ needed_space = line_len - free_len;
+ e_buf->read = (e_buf->read + needed_space) % e_buf->buf_size;
+ e_buf->used_len -= needed_space;
+ }
+
+ /* Copy the line into the circular buffer, dealing with possible
+ * wraparound.
+ */
+ cnt = MIN(line_len, e_buf->buf_size - e_buf->write);
+ memcpy(e_buf->buf + e_buf->write, line, cnt);
+ if (cnt < line_len) {
+ memcpy(e_buf->buf, line + cnt, line_len - cnt);
+ }
+ e_buf->used_len += line_len;
+ e_buf->write = (e_buf->write + line_len) % e_buf->buf_size;
+}
+
+/* Log directly to the specified log */
+static void do_log_line(struct log_info *log_info, char *line) {
+ if (log_info->log_target & LOG_KLOG) {
+ klog_write(6, log_info->klog_fmt, line);
+ }
+ if (log_info->log_target & LOG_ALOG) {
+ ALOG(LOG_INFO, log_info->btag, "%s", line);
+ }
+ if (log_info->log_target & LOG_FILE) {
+ fprintf(log_info->fp, "%s\n", line);
+ }
+}
+
+/* Log to either the abbreviated buf, or directly to the specified log
+ * via do_log_line() above.
+ */
+static void log_line(struct log_info *log_info, char *line, int len) {
+ if (log_info->abbreviated) {
+ add_line_to_abbr_buf(&log_info->a_buf, line, len);
+ } else {
+ do_log_line(log_info, line);
+ }
+}
+
+/*
+ * The kernel will take a maximum of 1024 bytes in any single write to
+ * the kernel logging device file, so find and print each line one at
+ * a time. The allocated size for buf should be at least 1 byte larger
+ * than buf_size (the usable size of the buffer) to make sure there is
+ * room to temporarily stuff a null byte to terminate a line for logging.
+ */
+static void print_buf_lines(struct log_info *log_info, char *buf, int buf_size)
+{
+ char *line_start;
+ char c;
+ int i;
+
+ line_start = buf;
+ for (i = 0; i < buf_size; i++) {
+ if (*(buf + i) == '\n') {
+ /* Found a line ending, print the line and compute new line_start */
+ /* Save the next char and replace with \0 */
+ c = *(buf + i + 1);
+ *(buf + i + 1) = '\0';
+ do_log_line(log_info, line_start);
+ /* Restore the saved char */
+ *(buf + i + 1) = c;
+ line_start = buf + i + 1;
+ } else if (*(buf + i) == '\0') {
+ /* The end of the buffer, print the last bit */
+ do_log_line(log_info, line_start);
+ break;
+ }
+ }
+ /* If the buffer was completely full, and didn't end with a newline, just
+ * ignore the partial last line.
+ */
+}
+
+static void init_abbr_buf(struct abbr_buf *a_buf) {
+ char *new_buf;
+
+ memset(a_buf, 0, sizeof(struct abbr_buf));
+ new_buf = malloc(BEGINNING_BUF_SIZE);
+ if (new_buf) {
+ a_buf->b_buf.buf = new_buf;
+ a_buf->b_buf.alloc_len = BEGINNING_BUF_SIZE;
+ a_buf->b_buf.buf_size = BEGINNING_BUF_SIZE - 1;
+ }
+ new_buf = malloc(ENDING_BUF_SIZE);
+ if (new_buf) {
+ a_buf->e_buf.buf = new_buf;
+ a_buf->e_buf.alloc_len = ENDING_BUF_SIZE;
+ a_buf->e_buf.buf_size = ENDING_BUF_SIZE - 1;
+ }
+}
+
+static void free_abbr_buf(struct abbr_buf *a_buf) {
+ free(a_buf->b_buf.buf);
+ free(a_buf->e_buf.buf);
+}
+
+static void add_line_to_abbr_buf(struct abbr_buf *a_buf, char *linebuf, int linelen) {
+ if (!a_buf->beginning_buf_full) {
+ a_buf->beginning_buf_full =
+ add_line_to_linear_buf(&a_buf->b_buf, linebuf, linelen);
+ }
+ if (a_buf->beginning_buf_full) {
+ add_line_to_circular_buf(&a_buf->e_buf, linebuf, linelen);
+ }
+}
+
+static void print_abbr_buf(struct log_info *log_info) {
+ struct abbr_buf *a_buf = &log_info->a_buf;
+
+ /* Add the abbreviated output to the kernel log */
+ if (a_buf->b_buf.alloc_len) {
+ print_buf_lines(log_info, a_buf->b_buf.buf, a_buf->b_buf.used_len);
+ }
+
+ /* Print an ellipsis to indicate that the buffer has wrapped or
+ * is full, and some data was not logged.
+ */
+ if (a_buf->e_buf.used_len == a_buf->e_buf.buf_size) {
+ do_log_line(log_info, "...\n");
+ }
+
+ if (a_buf->e_buf.used_len == 0) {
+ return;
+ }
+
+ /* Simplest way to print the circular buffer is allocate a second buf
+ * of the same size, and memcpy it so it's a simple linear buffer,
+ * and then cal print_buf_lines on it */
+ if (a_buf->e_buf.read < a_buf->e_buf.write) {
+ /* no wrap around, just print it */
+ print_buf_lines(log_info, a_buf->e_buf.buf + a_buf->e_buf.read,
+ a_buf->e_buf.used_len);
+ } else {
+ /* The circular buffer will always have at least 1 byte unused,
+ * so by allocating alloc_len here we will have at least
+ * 1 byte of space available as required by print_buf_lines().
+ */
+ char * nbuf = malloc(a_buf->e_buf.alloc_len);
+ if (!nbuf) {
+ return;
+ }
+ int first_chunk_len = a_buf->e_buf.buf_size - a_buf->e_buf.read;
+ memcpy(nbuf, a_buf->e_buf.buf + a_buf->e_buf.read, first_chunk_len);
+ /* copy second chunk */
+ memcpy(nbuf + first_chunk_len, a_buf->e_buf.buf, a_buf->e_buf.write);
+ print_buf_lines(log_info, nbuf, first_chunk_len + a_buf->e_buf.write);
+ free(nbuf);
+ }
+}
+
+static int parent(const char *tag, int parent_read, pid_t pid,
+ int *chld_sts, int log_target, bool abbreviated, char *file_path) {
+ int status = 0;
+ char buffer[4096];
+ struct pollfd poll_fds[] = {
+ [0] = {
+ .fd = parent_read,
+ .events = POLLIN,
+ },
+ };
+ int rc = 0;
+ int fd;
+
+ struct log_info log_info;
+
+ int a = 0; // start index of unprocessed data
+ int b = 0; // end index of unprocessed data
+ int sz;
+ bool found_child = false;
+ char tmpbuf[256];
+
+ log_info.btag = basename(tag);
+ if (!log_info.btag) {
+ log_info.btag = (char*) tag;
+ }
+
+ if (abbreviated && (log_target == LOG_NONE)) {
+ abbreviated = 0;
+ }
+ if (abbreviated) {
+ init_abbr_buf(&log_info.a_buf);
+ }
+
+ if (log_target & LOG_KLOG) {
+ snprintf(log_info.klog_fmt, sizeof(log_info.klog_fmt),
+ "<6>%.*s: %%s", MAX_KLOG_TAG, log_info.btag);
+ }
+
+ if ((log_target & LOG_FILE) && !file_path) {
+ /* No file_path specified, clear the LOG_FILE bit */
+ log_target &= ~LOG_FILE;
+ }
+
+ if (log_target & LOG_FILE) {
+ fd = open(file_path, O_WRONLY | O_CREAT, 0664);
+ if (fd < 0) {
+ ERROR("Cannot log to file %s\n", file_path);
+ log_target &= ~LOG_FILE;
+ } else {
+ lseek(fd, 0, SEEK_END);
+ log_info.fp = fdopen(fd, "a");
+ }
+ }
+
+ log_info.log_target = log_target;
+ log_info.abbreviated = abbreviated;
+
+ while (!found_child) {
+ if (TEMP_FAILURE_RETRY(poll(poll_fds, ARRAY_SIZE(poll_fds), -1)) < 0) {
+ ERROR("poll failed\n");
+ rc = -1;
+ goto err_poll;
+ }
+
+ if (poll_fds[0].revents & POLLIN) {
+ sz = TEMP_FAILURE_RETRY(
+ read(parent_read, &buffer[b], sizeof(buffer) - 1 - b));
+
+ sz += b;
+ // Log one line at a time
+ for (b = 0; b < sz; b++) {
+ if (buffer[b] == '\r') {
+ if (abbreviated) {
+ /* The abbreviated logging code uses newline as
+ * the line separator. Lucikly, the pty layer
+ * helpfully cooks the output of the command
+ * being run and inserts a CR before NL. So
+ * I just change it to NL here when doing
+ * abbreviated logging.
+ */
+ buffer[b] = '\n';
+ } else {
+ buffer[b] = '\0';
+ }
+ } else if (buffer[b] == '\n') {
+ buffer[b] = '\0';
+ log_line(&log_info, &buffer[a], b - a);
+ a = b + 1;
+ }
+ }
+
+ if (a == 0 && b == sizeof(buffer) - 1) {
+ // buffer is full, flush
+ buffer[b] = '\0';
+ log_line(&log_info, &buffer[a], b - a);
+ b = 0;
+ } else if (a != b) {
+ // Keep left-overs
+ b -= a;
+ memmove(buffer, &buffer[a], b);
+ a = 0;
+ } else {
+ a = 0;
+ b = 0;
+ }
+ }
+
+ if (poll_fds[0].revents & POLLHUP) {
+ int ret;
+
+ ret = waitpid(pid, &status, WNOHANG);
+ if (ret < 0) {
+ rc = errno;
+ ALOG(LOG_ERROR, "logwrap", "waitpid failed with %s\n", strerror(errno));
+ goto err_waitpid;
+ }
+ if (ret > 0) {
+ found_child = true;
+ }
+ }
+ }
+
+ if (chld_sts != NULL) {
+ *chld_sts = status;
+ } else {
+ if (WIFEXITED(status))
+ rc = WEXITSTATUS(status);
+ else
+ rc = -ECHILD;
+ }
+
+ // Flush remaining data
+ if (a != b) {
+ buffer[b] = '\0';
+ log_line(&log_info, &buffer[a], b - a);
+ }
+
+ /* All the output has been processed, time to dump the abbreviated output */
+ if (abbreviated) {
+ print_abbr_buf(&log_info);
+ }
+
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status)) {
+ snprintf(tmpbuf, sizeof(tmpbuf),
+ "%s terminated by exit(%d)\n", log_info.btag, WEXITSTATUS(status));
+ do_log_line(&log_info, tmpbuf);
+ }
+ } else {
+ if (WIFSIGNALED(status)) {
+ snprintf(tmpbuf, sizeof(tmpbuf),
+ "%s terminated by signal %d\n", log_info.btag, WTERMSIG(status));
+ do_log_line(&log_info, tmpbuf);
+ } else if (WIFSTOPPED(status)) {
+ snprintf(tmpbuf, sizeof(tmpbuf),
+ "%s stopped by signal %d\n", log_info.btag, WSTOPSIG(status));
+ do_log_line(&log_info, tmpbuf);
+ }
+ }
+
+err_waitpid:
+err_poll:
+ if (log_target & LOG_FILE) {
+ fclose(log_info.fp); /* Also closes underlying fd */
+ }
+ if (abbreviated) {
+ free_abbr_buf(&log_info.a_buf);
+ }
+ return rc;
+}
+
+static void child(int argc, char* argv[]) {
+ // create null terminated argv_child array
+ char* argv_child[argc + 1];
+ memcpy(argv_child, argv, argc * sizeof(char *));
+ argv_child[argc] = NULL;
+
+ if (execvp(argv_child[0], argv_child)) {
+ FATAL_CHILD("executing %s failed: %s\n", argv_child[0],
+ strerror(errno));
+ }
+}
+
+int android_fork_execvp_ext(int argc, char* argv[], int *status, bool ignore_int_quit,
+ int log_target, bool abbreviated, char *file_path) {
+ pid_t pid;
+ int parent_ptty;
+ int child_ptty;
+ struct sigaction intact;
+ struct sigaction quitact;
+ sigset_t blockset;
+ sigset_t oldset;
+ int rc = 0;
+
+ rc = pthread_mutex_lock(&fd_mutex);
+ if (rc) {
+ ERROR("failed to lock signal_fd mutex\n");
+ goto err_lock;
+ }
+
+ /* Use ptty instead of socketpair so that STDOUT is not buffered */
+ parent_ptty = TEMP_FAILURE_RETRY(open("/dev/ptmx", O_RDWR));
+ if (parent_ptty < 0) {
+ ERROR("Cannot create parent ptty\n");
+ rc = -1;
+ goto err_open;
+ }
+
+ char child_devname[64];
+ if (grantpt(parent_ptty) || unlockpt(parent_ptty) ||
+ ptsname_r(parent_ptty, child_devname, sizeof(child_devname)) != 0) {
+ ERROR("Problem with /dev/ptmx\n");
+ rc = -1;
+ goto err_ptty;
+ }
+
+ child_ptty = TEMP_FAILURE_RETRY(open(child_devname, O_RDWR));
+ if (child_ptty < 0) {
+ ERROR("Cannot open child_ptty\n");
+ rc = -1;
+ goto err_child_ptty;
+ }
+
+ sigemptyset(&blockset);
+ sigaddset(&blockset, SIGINT);
+ sigaddset(&blockset, SIGQUIT);
+ pthread_sigmask(SIG_BLOCK, &blockset, &oldset);
+
+ pid = fork();
+ if (pid < 0) {
+ close(child_ptty);
+ ERROR("Failed to fork\n");
+ rc = -1;
+ goto err_fork;
+ } else if (pid == 0) {
+ pthread_mutex_unlock(&fd_mutex);
+ pthread_sigmask(SIG_SETMASK, &oldset, NULL);
+ close(parent_ptty);
+
+ // redirect stdout and stderr
+ dup2(child_ptty, 1);
+ dup2(child_ptty, 2);
+ close(child_ptty);
+
+ child(argc, argv);
+ } else {
+ close(child_ptty);
+ if (ignore_int_quit) {
+ struct sigaction ignact;
+
+ memset(&ignact, 0, sizeof(ignact));
+ ignact.sa_handler = SIG_IGN;
+ sigaction(SIGINT, &ignact, &intact);
+ sigaction(SIGQUIT, &ignact, &quitact);
+ }
+
+ rc = parent(argv[0], parent_ptty, pid, status, log_target,
+ abbreviated, file_path);
+ }
+
+ if (ignore_int_quit) {
+ sigaction(SIGINT, &intact, NULL);
+ sigaction(SIGQUIT, &quitact, NULL);
+ }
+err_fork:
+ pthread_sigmask(SIG_SETMASK, &oldset, NULL);
+err_child_ptty:
+err_ptty:
+ close(parent_ptty);
+err_open:
+ pthread_mutex_unlock(&fd_mutex);
+err_lock:
+ return rc;
+}
diff --git a/logwrapper/logwrapper.c b/logwrapper/logwrapper.c
new file mode 100644
index 0000000..9e0385d
--- /dev/null
+++ b/logwrapper/logwrapper.c
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <logwrap/logwrap.h>
+#include <cutils/klog.h>
+
+#include "cutils/log.h"
+
+void fatal(const char *msg) {
+ fprintf(stderr, "%s", msg);
+ ALOG(LOG_ERROR, "logwrapper", "%s", msg);
+ exit(-1);
+}
+
+void usage() {
+ fatal(
+ "Usage: logwrapper [-a] [-d] [-k] BINARY [ARGS ...]\n"
+ "\n"
+ "Forks and executes BINARY ARGS, redirecting stdout and stderr to\n"
+ "the Android logging system. Tag is set to BINARY, priority is\n"
+ "always LOG_INFO.\n"
+ "\n"
+ "-a: Causes logwrapper to do abbreviated logging.\n"
+ " This logs up to the first 4K and last 4K of the command\n"
+ " being run, and logs the output when the command exits\n"
+ "-d: Causes logwrapper to SIGSEGV when BINARY terminates\n"
+ " fault address is set to the status of wait()\n"
+ "-k: Causes logwrapper to log to the kernel log instead of\n"
+ " the Android system log\n");
+}
+
+int main(int argc, char* argv[]) {
+ int seg_fault_on_exit = 0;
+ int log_target = LOG_ALOG;
+ bool abbreviated = false;
+ int ch;
+ int status = 0xAAAA;
+ int rc;
+
+ while ((ch = getopt(argc, argv, "adk")) != -1) {
+ switch (ch) {
+ case 'a':
+ abbreviated = true;
+ break;
+ case 'd':
+ seg_fault_on_exit = 1;
+ break;
+ case 'k':
+ log_target = LOG_KLOG;
+ klog_set_level(6);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ usage();
+ }
+
+ rc = android_fork_execvp_ext(argc, &argv[0], &status, true,
+ log_target, abbreviated, NULL);
+ if (!rc) {
+ if (WIFEXITED(status))
+ rc = WEXITSTATUS(status);
+ else
+ rc = -ECHILD;
+ }
+
+ if (seg_fault_on_exit) {
+ uintptr_t fault_address = (uintptr_t) status;
+ *(int *) fault_address = 0; // causes SIGSEGV with fault_address = status
+ }
+
+ return rc;
+}
diff --git a/mkbootimg/Android.mk b/mkbootimg/Android.mk
new file mode 100644
index 0000000..8661d7d
--- /dev/null
+++ b/mkbootimg/Android.mk
@@ -0,0 +1,11 @@
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := mkbootimg
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_IS_HOST_MODULE := true
+
+LOCAL_MODULE := mkbootimg
+
+include $(BUILD_PREBUILT)
diff --git a/mkbootimg/bootimg.h b/mkbootimg/bootimg.h
new file mode 100644
index 0000000..5ab6195
--- /dev/null
+++ b/mkbootimg/bootimg.h
@@ -0,0 +1,104 @@
+/* tools/mkbootimg/bootimg.h
+**
+** Copyright 2007, 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>
+
+#ifndef _BOOT_IMAGE_H_
+#define _BOOT_IMAGE_H_
+
+typedef struct boot_img_hdr boot_img_hdr;
+
+#define BOOT_MAGIC "ANDROID!"
+#define BOOT_MAGIC_SIZE 8
+#define BOOT_NAME_SIZE 16
+#define BOOT_ARGS_SIZE 512
+#define BOOT_EXTRA_ARGS_SIZE 1024
+
+struct boot_img_hdr
+{
+ uint8_t magic[BOOT_MAGIC_SIZE];
+
+ uint32_t kernel_size; /* size in bytes */
+ uint32_t kernel_addr; /* physical load addr */
+
+ uint32_t ramdisk_size; /* size in bytes */
+ uint32_t ramdisk_addr; /* physical load addr */
+
+ uint32_t second_size; /* size in bytes */
+ uint32_t second_addr; /* physical load addr */
+
+ uint32_t tags_addr; /* physical addr for kernel tags */
+ uint32_t page_size; /* flash page size we assume */
+ uint32_t unused[2]; /* future expansion: should be 0 */
+
+ uint8_t name[BOOT_NAME_SIZE]; /* asciiz product name */
+
+ uint8_t cmdline[BOOT_ARGS_SIZE];
+
+ uint32_t id[8]; /* timestamp / checksum / sha1 / etc */
+
+ /* Supplemental command line data; kept here to maintain
+ * binary compatibility with older versions of mkbootimg */
+ uint8_t extra_cmdline[BOOT_EXTRA_ARGS_SIZE];
+} __attribute__((packed));
+
+/*
+** +-----------------+
+** | boot header | 1 page
+** +-----------------+
+** | kernel | n pages
+** +-----------------+
+** | ramdisk | m pages
+** +-----------------+
+** | second stage | o pages
+** +-----------------+
+**
+** n = (kernel_size + page_size - 1) / page_size
+** m = (ramdisk_size + page_size - 1) / page_size
+** o = (second_size + page_size - 1) / page_size
+**
+** 0. all entities are page_size aligned in flash
+** 1. kernel and ramdisk are required (size != 0)
+** 2. second is optional (second_size == 0 -> no second)
+** 3. load each element (kernel, ramdisk, second) at
+** the specified physical address (kernel_addr, etc)
+** 4. prepare tags at tag_addr. kernel_args[] is
+** appended to the kernel commandline in the tags.
+** 5. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
+** 6. if second_size != 0: jump to second_addr
+** else: jump to kernel_addr
+*/
+
+#if 0
+typedef struct ptentry ptentry;
+
+struct ptentry {
+ char name[16]; /* asciiz partition name */
+ unsigned start; /* starting block number */
+ unsigned length; /* length in blocks */
+ unsigned flags; /* set to zero */
+};
+
+/* MSM Partition Table ATAG
+**
+** length: 2 + 7 * n
+** atag: 0x4d534d70
+** <ptentry> x n
+*/
+#endif
+
+#endif
diff --git a/mkbootimg/mkbootimg b/mkbootimg/mkbootimg
new file mode 100755
index 0000000..aea2585
--- /dev/null
+++ b/mkbootimg/mkbootimg
@@ -0,0 +1,140 @@
+#!/usr/bin/env python
+# Copyright 2015, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from __future__ import print_function
+from sys import argv, exit, stderr
+from argparse import ArgumentParser, FileType, Action
+from os import fstat
+from struct import pack
+from hashlib import sha1
+
+def filesize(f):
+ if f is None:
+ return 0
+ try:
+ return fstat(f.fileno()).st_size
+ except OSError:
+ return 0
+
+
+def update_sha(sha, f):
+ if f:
+ sha.update(f.read())
+ f.seek(0)
+ sha.update(pack('I', filesize(f)))
+ else:
+ sha.update(pack('I', 0))
+
+
+def pad_file(f, padding):
+ pad = (padding - (f.tell() & (padding - 1))) & (padding - 1)
+ f.write(pack(str(pad) + 'x'))
+
+
+def write_header(args):
+ BOOT_MAGIC = 'ANDROID!'.encode()
+ args.output.write(pack('8s', BOOT_MAGIC))
+ args.output.write(pack('8I',
+ filesize(args.kernel), # size in bytes
+ args.base + args.kernel_offset, # physical load addr
+ filesize(args.ramdisk), # size in bytes
+ args.base + args.ramdisk_offset, # physical load addr
+ filesize(args.second), # size in bytes
+ args.base + args.second_offset, # physical load addr
+ args.base + args.tags_offset, # physical addr for kernel tags
+ args.pagesize)) # flash page size we assume
+ args.output.write(pack('8x')) # future expansion: should be 0
+ args.output.write(pack('16s', args.board.encode())) # asciiz product name
+ args.output.write(pack('512s', args.cmdline[:512].encode()))
+
+ sha = sha1()
+ update_sha(sha, args.kernel)
+ update_sha(sha, args.ramdisk)
+ update_sha(sha, args.second)
+ img_id = pack('32s', sha.digest())
+
+ args.output.write(img_id)
+ args.output.write(pack('1024s', args.cmdline[512:].encode()))
+ pad_file(args.output, args.pagesize)
+ return img_id
+
+
+class ValidateStrLenAction(Action):
+ def __init__(self, option_strings, dest, nargs=None, **kwargs):
+ if 'maxlen' not in kwargs:
+ raise ValueError('maxlen must be set')
+ self.maxlen = int(kwargs['maxlen'])
+ del kwargs['maxlen']
+ super(ValidateStrLenAction, self).__init__(option_strings, dest, **kwargs)
+
+ def __call__(self, parser, namespace, values, option_string=None):
+ if len(values) > self.maxlen:
+ raise ValueError('String argument too long: max {0:d}, got {1:d}'.
+ format(self.maxlen, len(values)))
+ setattr(namespace, self.dest, values)
+
+
+def write_padded_file(f_out, f_in, padding):
+ if f_in is None:
+ return
+ f_out.write(f_in.read())
+ pad_file(f_out, padding)
+
+
+def parse_int(x):
+ return int(x, 0)
+
+
+def parse_cmdline():
+ parser = ArgumentParser()
+ parser.add_argument('--kernel', help='path to the kernel', type=FileType('rb'),
+ required=True)
+ parser.add_argument('--ramdisk', help='path to the ramdisk', type=FileType('rb'))
+ parser.add_argument('--second', help='path to the 2nd bootloader', type=FileType('rb'))
+ parser.add_argument('--cmdline', help='extra arguments to be passed on the '
+ 'kernel command line', default='', action=ValidateStrLenAction, maxlen=1536)
+ parser.add_argument('--base', help='base address', type=parse_int, default=0x10000000)
+ parser.add_argument('--kernel_offset', help='kernel offset', type=parse_int, default=0x00008000)
+ parser.add_argument('--ramdisk_offset', help='ramdisk offset', type=parse_int, default=0x01000000)
+ parser.add_argument('--second_offset', help='2nd bootloader offset', type=parse_int,
+ default=0x00f00000)
+ parser.add_argument('--tags_offset', help='tags offset', type=parse_int, default=0x00000100)
+ parser.add_argument('--board', help='board name', default='', action=ValidateStrLenAction,
+ maxlen=16)
+ parser.add_argument('--pagesize', help='page size', type=parse_int,
+ choices=[2**i for i in range(11,15)], default=2048)
+ parser.add_argument('--id', help='print the image ID on standard output',
+ action='store_true')
+ parser.add_argument('-o', '--output', help='output file name', type=FileType('wb'),
+ required=True)
+ return parser.parse_args()
+
+
+def write_data(args):
+ write_padded_file(args.output, args.kernel, args.pagesize)
+ write_padded_file(args.output, args.ramdisk, args.pagesize)
+ write_padded_file(args.output, args.second, args.pagesize)
+
+
+def main():
+ args = parse_cmdline()
+ img_id = write_header(args)
+ write_data(args)
+ if args.id:
+ print('0x' + ''.join('{:02x}'.format(ord(c)) for c in img_id))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/reboot/Android.mk b/reboot/Android.mk
new file mode 100644
index 0000000..7a24f99
--- /dev/null
+++ b/reboot/Android.mk
@@ -0,0 +1,14 @@
+# Copyright 2013 The Android Open Source Project
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := reboot.c
+
+LOCAL_SHARED_LIBRARIES := libcutils
+
+LOCAL_MODULE := reboot
+
+LOCAL_CFLAGS := -Werror
+
+include $(BUILD_EXECUTABLE)
diff --git a/reboot/reboot.c b/reboot/reboot.c
new file mode 100644
index 0000000..007dfba
--- /dev/null
+++ b/reboot/reboot.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <cutils/properties.h>
+#include <cutils/android_reboot.h>
+#include <unistd.h>
+
+int main(int argc, char *argv[])
+{
+ int ret;
+ size_t prop_len;
+ char property_val[PROPERTY_VALUE_MAX];
+ const char *cmd = "reboot";
+ char *optarg = "";
+
+ opterr = 0;
+ do {
+ int c;
+
+ c = getopt(argc, argv, "p");
+
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case 'p':
+ cmd = "shutdown";
+ break;
+ case '?':
+ fprintf(stderr, "usage: %s [-p] [rebootcommand]\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ } while (1);
+
+ if(argc > optind + 1) {
+ fprintf(stderr, "%s: too many arguments\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ if (argc > optind)
+ optarg = argv[optind];
+
+ prop_len = snprintf(property_val, sizeof(property_val), "%s,%s", cmd, optarg);
+ if (prop_len >= sizeof(property_val)) {
+ fprintf(stderr, "reboot command too long: %s\n", optarg);
+ exit(EXIT_FAILURE);
+ }
+
+ ret = property_set(ANDROID_RB_PROPERTY, property_val);
+ if(ret < 0) {
+ perror("reboot");
+ exit(EXIT_FAILURE);
+ }
+
+ // Don't return early. Give the reboot command time to take effect
+ // to avoid messing up scripts which do "adb shell reboot && adb wait-for-device"
+ while(1) { pause(); }
+
+ fprintf(stderr, "Done\n");
+ return 0;
+}
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
new file mode 100644
index 0000000..d6dad2d
--- /dev/null
+++ b/rootdir/Android.mk
@@ -0,0 +1,53 @@
+LOCAL_PATH:= $(call my-dir)
+
+#######################################
+# init.rc
+# Only copy init.rc if the target doesn't have its own.
+ifneq ($(TARGET_PROVIDES_INIT_RC),true)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := init.rc
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
+
+include $(BUILD_PREBUILT)
+endif
+#######################################
+# init.environ.rc
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE := init.environ.rc
+LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
+
+# Put it here instead of in init.rc module definition,
+# because init.rc is conditionally included.
+#
+# create some directories (some are mount points)
+LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \
+ sbin dev proc sys system data oem acct cache config storage mnt root)
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+EXPORT_GLOBAL_ASAN_OPTIONS :=
+ifeq (address,$(strip $(SANITIZE_TARGET)))
+ EXPORT_GLOBAL_ASAN_OPTIONS := export ASAN_OPTIONS allow_user_segv_handler=1:detect_odr_violation=0:alloc_dealloc_mismatch=0
+endif
+
+# Regenerate init.environ.rc if PRODUCT_BOOTCLASSPATH has changed.
+bcp_md5 := $(word 1, $(shell echo $(PRODUCT_BOOTCLASSPATH) $(PRODUCT_SYSTEM_SERVER_CLASSPATH) | $(MD5SUM)))
+bcp_dep := $(intermediates)/$(bcp_md5).bcp.dep
+$(bcp_dep) :
+ $(hide) mkdir -p $(dir $@) && rm -rf $(dir $@)*.bcp.dep && touch $@
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/init.environ.rc.in $(bcp_dep)
+ @echo "Generate: $< -> $@"
+ @mkdir -p $(dir $@)
+ $(hide) sed -e 's?%BOOTCLASSPATH%?$(PRODUCT_BOOTCLASSPATH)?g' $< >$@
+ $(hide) sed -i -e 's?%SYSTEMSERVERCLASSPATH%?$(PRODUCT_SYSTEM_SERVER_CLASSPATH)?g' $@
+ $(hide) sed -i -e 's?%EXPORT_GLOBAL_ASAN_OPTIONS%?$(EXPORT_GLOBAL_ASAN_OPTIONS)?g' $@
+
+bcp_md5 :=
+bcp_dep :=
+#######################################
diff --git a/rootdir/etc/hosts b/rootdir/etc/hosts
new file mode 100644
index 0000000..649151c
--- /dev/null
+++ b/rootdir/etc/hosts
@@ -0,0 +1,2 @@
+127.0.0.1 localhost
+::1 ip6-localhost
diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in
new file mode 100644
index 0000000..e8b46eb
--- /dev/null
+++ b/rootdir/init.environ.rc.in
@@ -0,0 +1,12 @@
+# set up the global environment
+on init
+ export ANDROID_BOOTLOGO 1
+ export ANDROID_ROOT /system
+ export ANDROID_ASSETS /system/app
+ export ANDROID_DATA /data
+ export ANDROID_STORAGE /storage
+ export ASEC_MOUNTPOINT /mnt/asec
+ export LOOP_MOUNTPOINT /mnt/obb
+ export BOOTCLASSPATH %BOOTCLASSPATH%
+ export SYSTEMSERVERCLASSPATH %SYSTEMSERVERCLASSPATH%
+ %EXPORT_GLOBAL_ASAN_OPTIONS%
diff --git a/rootdir/init.rc b/rootdir/init.rc
new file mode 100644
index 0000000..f5febde
--- /dev/null
+++ b/rootdir/init.rc
@@ -0,0 +1,665 @@
+# Copyright (C) 2012 The Android Open Source Project
+#
+# IMPORTANT: Do not create world writable files or directories.
+# This is a common source of Android security bugs.
+#
+
+import /init.environ.rc
+import /init.usb.rc
+import /init.${ro.hardware}.rc
+import /init.${ro.zygote}.rc
+import /init.trace.rc
+
+on early-init
+ # Set init and its forked children's oom_adj.
+ write /proc/1/oom_score_adj -1000
+
+ # Set the security context of /adb_keys if present.
+ restorecon /adb_keys
+
+ # Shouldn't be necessary, but sdcard won't start without it. http://b/22568628.
+ mkdir /mnt 0775 root system
+
+ start ueventd
+
+on init
+ sysclktz 0
+
+ # Backward compatibility.
+ symlink /system/etc /etc
+ symlink /sys/kernel/debug /d
+
+ # Link /vendor to /system/vendor for devices without a vendor partition.
+ symlink /system/vendor /vendor
+
+ # Mount cgroup mount point for cpu accounting
+ mount cgroup none /acct cpuacct
+ mkdir /acct/uid
+
+ # Create cgroup mount point for memory
+ mount tmpfs none /sys/fs/cgroup mode=0750,uid=0,gid=1000
+ mkdir /sys/fs/cgroup/memory 0750 root system
+ mount cgroup none /sys/fs/cgroup/memory memory
+ write /sys/fs/cgroup/memory/memory.move_charge_at_immigrate 1
+ chown root system /sys/fs/cgroup/memory/tasks
+ chmod 0660 /sys/fs/cgroup/memory/tasks
+ mkdir /sys/fs/cgroup/memory/sw 0750 root system
+ write /sys/fs/cgroup/memory/sw/memory.swappiness 100
+ write /sys/fs/cgroup/memory/sw/memory.move_charge_at_immigrate 1
+ chown root system /sys/fs/cgroup/memory/sw/tasks
+ chmod 0660 /sys/fs/cgroup/memory/sw/tasks
+
+ # See storage config details at http://source.android.com/tech/storage/
+ mkdir /mnt/shell 0700 shell shell
+ mkdir /mnt/media_rw 0700 media_rw media_rw
+ mkdir /storage 0751 root sdcard_r
+
+ # Directory for putting things only root should see.
+ mkdir /mnt/secure 0700 root root
+
+ # Directory for staging bindmounts
+ mkdir /mnt/secure/staging 0700 root root
+
+ # Directory-target for where the secure container
+ # imagefile directory will be bind-mounted
+ mkdir /mnt/secure/asec 0700 root root
+
+ # Secure container public mount points.
+ mkdir /mnt/asec 0700 root system
+ mount tmpfs tmpfs /mnt/asec mode=0755,gid=1000
+
+ # Filesystem image public mount points.
+ mkdir /mnt/obb 0700 root system
+ mount tmpfs tmpfs /mnt/obb mode=0755,gid=1000
+
+ # memory control cgroup
+ mkdir /dev/memcg 0700 root system
+ mount cgroup none /dev/memcg memory
+
+ write /proc/sys/kernel/panic_on_oops 1
+ write /proc/sys/kernel/hung_task_timeout_secs 0
+ write /proc/cpu/alignment 4
+ write /proc/sys/kernel/sched_latency_ns 10000000
+ write /proc/sys/kernel/sched_wakeup_granularity_ns 2000000
+ write /proc/sys/kernel/sched_compat_yield 1
+ write /proc/sys/kernel/sched_child_runs_first 0
+ write /proc/sys/kernel/randomize_va_space 2
+ write /proc/sys/kernel/kptr_restrict 2
+ write /proc/sys/vm/mmap_min_addr 32768
+ write /proc/sys/net/ipv4/ping_group_range "0 2147483647"
+ write /proc/sys/net/unix/max_dgram_qlen 300
+ write /proc/sys/kernel/sched_rt_runtime_us 950000
+ write /proc/sys/kernel/sched_rt_period_us 1000000
+
+ # reflect fwmark from incoming packets onto generated replies
+ write /proc/sys/net/ipv4/fwmark_reflect 1
+ write /proc/sys/net/ipv6/fwmark_reflect 1
+
+ # set fwmark on accepted sockets
+ write /proc/sys/net/ipv4/tcp_fwmark_accept 1
+
+ # Create cgroup mount points for process groups
+ mkdir /dev/cpuctl
+ mount cgroup none /dev/cpuctl cpu
+ chown system system /dev/cpuctl
+ chown system system /dev/cpuctl/tasks
+ chmod 0666 /dev/cpuctl/tasks
+ write /dev/cpuctl/cpu.shares 1024
+ write /dev/cpuctl/cpu.rt_runtime_us 800000
+ write /dev/cpuctl/cpu.rt_period_us 1000000
+
+ mkdir /dev/cpuctl/bg_non_interactive
+ chown system system /dev/cpuctl/bg_non_interactive/tasks
+ chmod 0666 /dev/cpuctl/bg_non_interactive/tasks
+ # 5.0 %
+ write /dev/cpuctl/bg_non_interactive/cpu.shares 52
+ write /dev/cpuctl/bg_non_interactive/cpu.rt_runtime_us 700000
+ write /dev/cpuctl/bg_non_interactive/cpu.rt_period_us 1000000
+
+ # qtaguid will limit access to specific data based on group memberships.
+ # net_bw_acct grants impersonation of socket owners.
+ # net_bw_stats grants access to other apps' detailed tagged-socket stats.
+ chown root net_bw_acct /proc/net/xt_qtaguid/ctrl
+ chown root net_bw_stats /proc/net/xt_qtaguid/stats
+
+ # Allow everybody to read the xt_qtaguid resource tracking misc dev.
+ # This is needed by any process that uses socket tagging.
+ chmod 0644 /dev/xt_qtaguid
+
+ # Create location for fs_mgr to store abbreviated output from filesystem
+ # checker programs.
+ mkdir /dev/fscklogs 0770 root system
+
+ # pstore/ramoops previous console log
+ mount pstore pstore /sys/fs/pstore
+ chown system log /sys/fs/pstore/console-ramoops
+ chmod 0440 /sys/fs/pstore/console-ramoops
+ chown system log /sys/fs/pstore/pmsg-ramoops-0
+ chmod 0440 /sys/fs/pstore/pmsg-ramoops-0
+
+ # enable armv8_deprecated instruction hooks
+ write /proc/sys/abi/swp 1
+
+# Healthd can trigger a full boot from charger mode by signaling this
+# property when the power button is held.
+on property:sys.boot_from_charger_mode=1
+ class_stop charger
+ trigger late-init
+
+# Load properties from /system/ + /factory after fs mount.
+on load_all_props_action
+ load_all_props
+ start logd
+ start logd-reinit
+
+# Indicate to fw loaders that the relevant mounts are up.
+on firmware_mounts_complete
+ rm /dev/.booting
+
+# Mount filesystems and start core system services.
+on late-init
+ trigger early-fs
+ trigger fs
+ trigger post-fs
+ trigger post-fs-data
+
+ # Load properties from /system/ + /factory after fs mount. Place
+ # this in another action so that the load will be scheduled after the prior
+ # issued fs triggers have completed.
+ trigger load_all_props_action
+
+ # Remove a file to wake up anything waiting for firmware.
+ trigger firmware_mounts_complete
+
+ trigger early-boot
+ trigger boot
+
+
+on post-fs
+ start logd
+ # once everything is setup, no need to modify /
+ mount rootfs rootfs / ro remount
+ # mount shared so changes propagate into child namespaces
+ mount rootfs rootfs / shared rec
+
+ # We chown/chmod /cache again so because mount is run as root + defaults
+ chown system cache /cache
+ chmod 0770 /cache
+ # We restorecon /cache in case the cache partition has been reset.
+ restorecon_recursive /cache
+
+ # Create /cache/recovery in case it's not there. It'll also fix the odd
+ # permissions if created by the recovery system.
+ mkdir /cache/recovery 0770 system cache
+
+ #change permissions on vmallocinfo so we can grab it from bugreports
+ chown root log /proc/vmallocinfo
+ chmod 0440 /proc/vmallocinfo
+
+ chown root log /proc/slabinfo
+ chmod 0440 /proc/slabinfo
+
+ #change permissions on kmsg & sysrq-trigger so bugreports can grab kthread stacks
+ chown root system /proc/kmsg
+ chmod 0440 /proc/kmsg
+ chown root system /proc/sysrq-trigger
+ chmod 0220 /proc/sysrq-trigger
+ chown system log /proc/last_kmsg
+ chmod 0440 /proc/last_kmsg
+
+ # make the selinux kernel policy world-readable
+ chmod 0444 /sys/fs/selinux/policy
+
+ # create the lost+found directories, so as to enforce our permissions
+ mkdir /cache/lost+found 0770 root root
+
+on post-fs-data
+ # We chown/chmod /data again so because mount is run as root + defaults
+ chown system system /data
+ chmod 0771 /data
+ # We restorecon /data in case the userdata partition has been reset.
+ restorecon /data
+
+ # Make sure we have the device encryption key
+ start logd
+ start vold
+ installkey /data
+
+ # Start bootcharting as soon as possible after the data partition is
+ # mounted to collect more data.
+ mkdir /data/bootchart 0755 shell shell
+ bootchart_init
+
+ # Avoid predictable entropy pool. Carry over entropy from previous boot.
+ copy /data/system/entropy.dat /dev/urandom
+
+ # create basic filesystem structure
+ mkdir /data/misc 01771 system misc
+ mkdir /data/misc/adb 02750 system shell
+ mkdir /data/misc/bluedroid 0770 bluetooth net_bt_stack
+ mkdir /data/misc/bluetooth 0770 system system
+ mkdir /data/misc/keystore 0700 keystore keystore
+ mkdir /data/misc/gatekeeper 0700 system system
+ mkdir /data/misc/keychain 0771 system system
+ mkdir /data/misc/net 0750 root shell
+ mkdir /data/misc/radio 0770 system radio
+ mkdir /data/misc/sms 0770 system radio
+ mkdir /data/misc/zoneinfo 0775 system system
+ mkdir /data/misc/vpn 0770 system vpn
+ mkdir /data/misc/shared_relro 0771 shared_relro shared_relro
+ mkdir /data/misc/systemkeys 0700 system system
+ mkdir /data/misc/wifi 0770 wifi wifi
+ mkdir /data/misc/wifi/sockets 0770 wifi wifi
+ mkdir /data/misc/wifi/wpa_supplicant 0770 wifi wifi
+ mkdir /data/misc/ethernet 0770 system system
+ mkdir /data/misc/dhcp 0770 dhcp dhcp
+ mkdir /data/misc/user 0771 root root
+ mkdir /data/misc/perfprofd 0775 root root
+ # give system access to wpa_supplicant.conf for backup and restore
+ chmod 0660 /data/misc/wifi/wpa_supplicant.conf
+ mkdir /data/local 0751 root root
+ mkdir /data/misc/media 0700 media media
+
+ # For security reasons, /data/local/tmp should always be empty.
+ # Do not place files or directories in /data/local/tmp
+ mkdir /data/local/tmp 0771 shell shell
+ mkdir /data/data 0771 system system
+ mkdir /data/app-private 0771 system system
+ mkdir /data/app-asec 0700 root root
+ mkdir /data/app-lib 0771 system system
+ mkdir /data/app 0771 system system
+ mkdir /data/property 0700 root root
+ mkdir /data/tombstones 0771 system system
+
+ # create dalvik-cache, so as to enforce our permissions
+ mkdir /data/dalvik-cache 0771 root root
+ mkdir /data/dalvik-cache/profiles 0711 system system
+
+ # create resource-cache and double-check the perms
+ mkdir /data/resource-cache 0771 system system
+ chown system system /data/resource-cache
+ chmod 0771 /data/resource-cache
+
+ # create the lost+found directories, so as to enforce our permissions
+ mkdir /data/lost+found 0770 root root
+
+ # create directory for DRM plug-ins - give drm the read/write access to
+ # the following directory.
+ mkdir /data/drm 0770 drm drm
+
+ # create directory for MediaDrm plug-ins - give drm the read/write access to
+ # the following directory.
+ mkdir /data/mediadrm 0770 mediadrm mediadrm
+
+ mkdir /data/adb 0700 root root
+ mkdir /data/anr 0775 system system
+
+ # symlink to bugreport storage location
+ symlink /data/data/com.android.shell/files/bugreports /data/bugreports
+
+ # Separate location for storing security policy files on data
+ mkdir /data/security 0711 system system
+
+ # Create all remaining /data root dirs so that they are made through init
+ # and get proper encryption policy installed
+ mkdir /data/backup 0700 system system
+ mkdir /data/media 0770 media_rw media_rw
+ mkdir /data/ss 0700 system system
+ mkdir /data/system 0775 system system
+ mkdir /data/system/heapdump 0700 system system
+ mkdir /data/user 0711 system system
+
+ # Reload policy from /data/security if present.
+ setprop selinux.reload_policy 1
+
+ # Set SELinux security contexts on upgrade or policy update.
+ restorecon_recursive /data
+
+ # Check any timezone data in /data is newer than the copy in /system, delete if not.
+ exec - system system -- /system/bin/tzdatacheck /system/usr/share/zoneinfo /data/misc/zoneinfo
+
+ # If there is no fs-post-data action in the init.<device>.rc file, you
+ # must uncomment this line, otherwise encrypted filesystems
+ # won't work.
+ # Set indication (checked by vold) that we have finished this action
+ #setprop vold.post_fs_data_done 1
+
+on boot
+ # basic network init
+ ifup lo
+ hostname localhost
+ domainname localdomain
+
+ # set RLIMIT_NICE to allow priorities from 19 to -20
+ setrlimit 13 40 40
+
+ # Memory management. Basic kernel parameters, and allow the high
+ # level system server to be able to adjust the kernel OOM driver
+ # parameters to match how it is managing things.
+ write /proc/sys/vm/overcommit_memory 1
+ write /proc/sys/vm/min_free_order_shift 4
+ chown root system /sys/module/lowmemorykiller/parameters/adj
+ chmod 0220 /sys/module/lowmemorykiller/parameters/adj
+ chown root system /sys/module/lowmemorykiller/parameters/minfree
+ chmod 0220 /sys/module/lowmemorykiller/parameters/minfree
+
+ # Tweak background writeout
+ write /proc/sys/vm/dirty_expire_centisecs 200
+ write /proc/sys/vm/dirty_background_ratio 5
+
+ # Permissions for System Server and daemons.
+ chown radio system /sys/android_power/state
+ chown radio system /sys/android_power/request_state
+ chown radio system /sys/android_power/acquire_full_wake_lock
+ chown radio system /sys/android_power/acquire_partial_wake_lock
+ chown radio system /sys/android_power/release_wake_lock
+ chown system system /sys/power/autosleep
+ chown system system /sys/power/state
+ chown system system /sys/power/wakeup_count
+ chown radio system /sys/power/wake_lock
+ chown radio system /sys/power/wake_unlock
+ chmod 0660 /sys/power/state
+ chmod 0660 /sys/power/wake_lock
+ chmod 0660 /sys/power/wake_unlock
+
+ chown system system /sys/devices/system/cpu/cpufreq/interactive/timer_rate
+ chmod 0660 /sys/devices/system/cpu/cpufreq/interactive/timer_rate
+ chown system system /sys/devices/system/cpu/cpufreq/interactive/timer_slack
+ chmod 0660 /sys/devices/system/cpu/cpufreq/interactive/timer_slack
+ chown system system /sys/devices/system/cpu/cpufreq/interactive/min_sample_time
+ chmod 0660 /sys/devices/system/cpu/cpufreq/interactive/min_sample_time
+ chown system system /sys/devices/system/cpu/cpufreq/interactive/hispeed_freq
+ chmod 0660 /sys/devices/system/cpu/cpufreq/interactive/hispeed_freq
+ chown system system /sys/devices/system/cpu/cpufreq/interactive/target_loads
+ chmod 0660 /sys/devices/system/cpu/cpufreq/interactive/target_loads
+ chown system system /sys/devices/system/cpu/cpufreq/interactive/go_hispeed_load
+ chmod 0660 /sys/devices/system/cpu/cpufreq/interactive/go_hispeed_load
+ chown system system /sys/devices/system/cpu/cpufreq/interactive/above_hispeed_delay
+ chmod 0660 /sys/devices/system/cpu/cpufreq/interactive/above_hispeed_delay
+ chown system system /sys/devices/system/cpu/cpufreq/interactive/boost
+ chmod 0660 /sys/devices/system/cpu/cpufreq/interactive/boost
+ chown system system /sys/devices/system/cpu/cpufreq/interactive/boostpulse
+ chown system system /sys/devices/system/cpu/cpufreq/interactive/input_boost
+ chmod 0660 /sys/devices/system/cpu/cpufreq/interactive/input_boost
+ chown system system /sys/devices/system/cpu/cpufreq/interactive/boostpulse_duration
+ chmod 0660 /sys/devices/system/cpu/cpufreq/interactive/boostpulse_duration
+ chown system system /sys/devices/system/cpu/cpufreq/interactive/io_is_busy
+ chmod 0660 /sys/devices/system/cpu/cpufreq/interactive/io_is_busy
+
+ # Assume SMP uses shared cpufreq policy for all CPUs
+ chown system system /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq
+ chmod 0660 /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq
+
+ chown system system /sys/class/timed_output/vibrator/enable
+ chown system system /sys/class/leds/keyboard-backlight/brightness
+ chown system system /sys/class/leds/lcd-backlight/brightness
+ chown system system /sys/class/leds/button-backlight/brightness
+ chown system system /sys/class/leds/jogball-backlight/brightness
+ chown system system /sys/class/leds/red/brightness
+ chown system system /sys/class/leds/green/brightness
+ chown system system /sys/class/leds/blue/brightness
+ chown system system /sys/class/leds/red/device/grpfreq
+ chown system system /sys/class/leds/red/device/grppwm
+ chown system system /sys/class/leds/red/device/blink
+ chown system system /sys/class/timed_output/vibrator/enable
+ chown system system /sys/module/sco/parameters/disable_esco
+ chown system system /sys/kernel/ipv4/tcp_wmem_min
+ chown system system /sys/kernel/ipv4/tcp_wmem_def
+ chown system system /sys/kernel/ipv4/tcp_wmem_max
+ chown system system /sys/kernel/ipv4/tcp_rmem_min
+ chown system system /sys/kernel/ipv4/tcp_rmem_def
+ chown system system /sys/kernel/ipv4/tcp_rmem_max
+ chown root radio /proc/cmdline
+
+ # Define default initial receive window size in segments.
+ setprop net.tcp.default_init_rwnd 60
+
+ class_start core
+
+on nonencrypted
+ class_start main
+ class_start late_start
+
+on property:vold.decrypt=trigger_default_encryption
+ start defaultcrypto
+
+on property:vold.decrypt=trigger_encryption
+ start surfaceflinger
+ start encrypt
+
+on property:sys.init_log_level=*
+ loglevel ${sys.init_log_level}
+
+on charger
+ class_start charger
+
+on property:vold.decrypt=trigger_reset_main
+ class_reset main
+
+on property:vold.decrypt=trigger_load_persist_props
+ load_persist_props
+ start logd
+ start logd-reinit
+
+on property:vold.decrypt=trigger_post_fs_data
+ trigger post-fs-data
+
+on property:vold.decrypt=trigger_restart_min_framework
+ class_start main
+
+on property:vold.decrypt=trigger_restart_framework
+ class_start main
+ class_start late_start
+
+on property:vold.decrypt=trigger_shutdown_framework
+ class_reset late_start
+ class_reset main
+
+on property:sys.powerctl=*
+ powerctl ${sys.powerctl}
+
+# system server cannot write to /proc/sys files,
+# and chown/chmod does not work for /proc/sys/ entries.
+# So proxy writes through init.
+on property:sys.sysctl.extra_free_kbytes=*
+ write /proc/sys/vm/extra_free_kbytes ${sys.sysctl.extra_free_kbytes}
+
+# "tcp_default_init_rwnd" Is too long!
+on property:sys.sysctl.tcp_def_init_rwnd=*
+ write /proc/sys/net/ipv4/tcp_default_init_rwnd ${sys.sysctl.tcp_def_init_rwnd}
+
+
+## Daemon processes to be run by init.
+##
+service ueventd /sbin/ueventd
+ class core
+ critical
+ seclabel u:r:ueventd:s0
+
+service logd /system/bin/logd
+ class core
+ socket logd stream 0666 logd logd
+ socket logdr seqpacket 0666 logd logd
+ socket logdw dgram 0222 logd logd
+
+service logd-reinit /system/bin/logd --reinit
+ oneshot
+ disabled
+
+service healthd /sbin/healthd
+ class core
+ critical
+ seclabel u:r:healthd:s0
+
+service console /system/bin/sh
+ class core
+ console
+ disabled
+ user shell
+ group shell log
+ seclabel u:r:shell:s0
+
+on property:ro.debuggable=1
+ start console
+
+# adbd is controlled via property triggers in init.<platform>.usb.rc
+service adbd /sbin/adbd --root_seclabel=u:r:su:s0
+ class core
+ socket adbd stream 660 system system
+ disabled
+ seclabel u:r:adbd:s0
+
+# adbd on at boot in emulator
+on property:ro.kernel.qemu=1
+ start adbd
+
+service lmkd /system/bin/lmkd
+ class core
+ critical
+ socket lmkd seqpacket 0660 system system
+
+service servicemanager /system/bin/servicemanager
+ class core
+ user system
+ group system
+ critical
+ onrestart restart healthd
+ onrestart restart zygote
+ onrestart restart media
+ onrestart restart surfaceflinger
+ onrestart restart drm
+
+service vold /system/bin/vold
+ class core
+ socket vold stream 0660 root mount
+ ioprio be 2
+
+service netd /system/bin/netd
+ class main
+ socket netd stream 0660 root system
+ socket dnsproxyd stream 0660 root inet
+ socket mdns stream 0660 root system
+ socket fwmarkd stream 0660 root inet
+
+service debuggerd /system/bin/debuggerd
+ class main
+
+service debuggerd64 /system/bin/debuggerd64
+ class main
+
+service ril-daemon /system/bin/rild
+ class main
+ socket rild stream 660 root radio
+ socket rild-debug stream 660 radio system
+ user root
+ group radio cache inet misc audio log
+
+service surfaceflinger /system/bin/surfaceflinger
+ class core
+ user system
+ group graphics drmrpc
+ onrestart restart zygote
+
+service drm /system/bin/drmserver
+ class main
+ user drm
+ group drm system inet drmrpc
+
+service media /system/bin/mediaserver
+ class main
+ user media
+ group audio camera inet net_bt net_bt_admin net_bw_acct drmrpc mediadrm
+ ioprio rt 4
+
+# One shot invocation to deal with encrypted volume.
+service defaultcrypto /system/bin/vdc --wait cryptfs mountdefaultencrypted
+ disabled
+ oneshot
+ # vold will set vold.decrypt to trigger_restart_framework (default
+ # encryption) or trigger_restart_min_framework (other encryption)
+
+# One shot invocation to encrypt unencrypted volumes
+service encrypt /system/bin/vdc --wait cryptfs enablecrypto inplace default
+ disabled
+ oneshot
+ # vold will set vold.decrypt to trigger_restart_framework (default
+ # encryption)
+
+service bootanim /system/bin/bootanimation
+ class core
+ user graphics
+ group graphics audio
+ disabled
+ oneshot
+
+service installd /system/bin/installd
+ class main
+ socket installd stream 600 system system
+
+service flash_recovery /system/bin/install-recovery.sh
+ class main
+ oneshot
+
+service racoon /system/bin/racoon
+ class main
+ socket racoon stream 600 system system
+ # IKE uses UDP port 500. Racoon will setuid to vpn after binding the port.
+ group vpn net_admin inet
+ disabled
+ oneshot
+
+service mtpd /system/bin/mtpd
+ class main
+ socket mtpd stream 600 system system
+ user vpn
+ group vpn net_admin inet net_raw
+ disabled
+ oneshot
+
+service keystore /system/bin/keystore /data/misc/keystore
+ class main
+ user keystore
+ group keystore drmrpc
+
+service dumpstate /system/bin/dumpstate -s
+ class main
+ socket dumpstate stream 0660 shell log
+ disabled
+ oneshot
+
+service mdnsd /system/bin/mdnsd
+ class main
+ user mdnsr
+ group inet net_raw
+ socket mdnsd stream 0660 mdnsr inet
+ disabled
+ oneshot
+
+service uncrypt /system/bin/uncrypt
+ class main
+ disabled
+ oneshot
+
+service pre-recovery /system/bin/uncrypt --reboot
+ class main
+ disabled
+ oneshot
+
+service perfprofd /system/xbin/perfprofd
+ class late_start
+ user root
+ oneshot
+
+on property:persist.logd.logpersistd=logcatd
+ # all exec/services are called with umask(077), so no gain beyond 0700
+ mkdir /data/misc/logd 0700 logd log
+ # logd for write to /data/misc/logd, log group for read from pstore (-L)
+ exec - logd log -- /system/bin/logcat -L -b all -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 64 -n 256
+ start logcatd
+
+service logcatd /system/bin/logcat -b all -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 64 -n 256
+ class late_start
+ disabled
+ # logd for write to /data/misc/logd, log group for read from log daemon
+ user logd
+ group log
diff --git a/rootdir/init.trace.rc b/rootdir/init.trace.rc
new file mode 100644
index 0000000..50944e6
--- /dev/null
+++ b/rootdir/init.trace.rc
@@ -0,0 +1,35 @@
+## Permissions to allow system-wide tracing to the kernel trace buffer.
+##
+on boot
+
+# Allow writing to the kernel trace log.
+ chmod 0222 /sys/kernel/debug/tracing/trace_marker
+
+# Allow the shell group to enable (some) kernel tracing.
+ chown root shell /sys/kernel/debug/tracing/trace_clock
+ chown root shell /sys/kernel/debug/tracing/buffer_size_kb
+ chown root shell /sys/kernel/debug/tracing/options/overwrite
+ chown root shell /sys/kernel/debug/tracing/options/print-tgid
+ chown root shell /sys/kernel/debug/tracing/events/sched/sched_switch/enable
+ chown root shell /sys/kernel/debug/tracing/events/sched/sched_wakeup/enable
+ chown root shell /sys/kernel/debug/tracing/events/power/cpu_frequency/enable
+ chown root shell /sys/kernel/debug/tracing/events/power/cpu_idle/enable
+ chown root shell /sys/kernel/debug/tracing/events/power/clock_set_rate/enable
+ chown root shell /sys/kernel/debug/tracing/events/cpufreq_interactive/enable
+ chown root shell /sys/kernel/debug/tracing/tracing_on
+
+ chmod 0664 /sys/kernel/debug/tracing/trace_clock
+ chmod 0664 /sys/kernel/debug/tracing/buffer_size_kb
+ chmod 0664 /sys/kernel/debug/tracing/options/overwrite
+ chmod 0664 /sys/kernel/debug/tracing/options/print-tgid
+ chmod 0664 /sys/kernel/debug/tracing/events/sched/sched_switch/enable
+ chmod 0664 /sys/kernel/debug/tracing/events/sched/sched_wakeup/enable
+ chmod 0664 /sys/kernel/debug/tracing/events/power/cpu_frequency/enable
+ chmod 0664 /sys/kernel/debug/tracing/events/power/cpu_idle/enable
+ chmod 0664 /sys/kernel/debug/tracing/events/power/clock_set_rate/enable
+ chmod 0664 /sys/kernel/debug/tracing/events/cpufreq_interactive/enable
+ chmod 0664 /sys/kernel/debug/tracing/tracing_on
+
+# Allow only the shell group to read and truncate the kernel trace.
+ chown root shell /sys/kernel/debug/tracing/trace
+ chmod 0660 /sys/kernel/debug/tracing/trace
diff --git a/rootdir/init.usb.rc b/rootdir/init.usb.rc
new file mode 100644
index 0000000..e290ca4
--- /dev/null
+++ b/rootdir/init.usb.rc
@@ -0,0 +1,91 @@
+# Copyright (C) 2012 The Android Open Source Project
+#
+# USB configuration common for all android devices
+#
+
+on post-fs-data
+ chown system system /sys/class/android_usb/android0/f_mass_storage/lun/file
+ chmod 0660 /sys/class/android_usb/android0/f_mass_storage/lun/file
+ chown system system /sys/class/android_usb/android0/f_rndis/ethaddr
+ chmod 0660 /sys/class/android_usb/android0/f_rndis/ethaddr
+
+# Used to disable USB when switching states
+on property:sys.usb.config=none
+ stop adbd
+ write /sys/class/android_usb/android0/enable 0
+ write /sys/class/android_usb/android0/bDeviceClass 0
+ setprop sys.usb.state ${sys.usb.config}
+
+# adb only USB configuration
+# This is the fallback configuration if the
+# USB manager fails to set a standard configuration
+on property:sys.usb.config=adb
+ write /sys/class/android_usb/android0/enable 0
+ write /sys/class/android_usb/android0/idVendor 18d1
+ write /sys/class/android_usb/android0/idProduct 4EE7
+ write /sys/class/android_usb/android0/functions ${sys.usb.config}
+ write /sys/class/android_usb/android0/enable 1
+ start adbd
+ setprop sys.usb.state ${sys.usb.config}
+
+# USB accessory configuration
+on property:sys.usb.config=accessory
+ write /sys/class/android_usb/android0/enable 0
+ write /sys/class/android_usb/android0/idVendor 18d1
+ write /sys/class/android_usb/android0/idProduct 2d00
+ write /sys/class/android_usb/android0/functions ${sys.usb.config}
+ write /sys/class/android_usb/android0/enable 1
+ setprop sys.usb.state ${sys.usb.config}
+
+# USB accessory configuration, with adb
+on property:sys.usb.config=accessory,adb
+ write /sys/class/android_usb/android0/enable 0
+ write /sys/class/android_usb/android0/idVendor 18d1
+ write /sys/class/android_usb/android0/idProduct 2d01
+ write /sys/class/android_usb/android0/functions ${sys.usb.config}
+ write /sys/class/android_usb/android0/enable 1
+ start adbd
+ setprop sys.usb.state ${sys.usb.config}
+
+# audio accessory configuration
+on property:sys.usb.config=audio_source
+ write /sys/class/android_usb/android0/enable 0
+ write /sys/class/android_usb/android0/idVendor 18d1
+ write /sys/class/android_usb/android0/idProduct 2d02
+ write /sys/class/android_usb/android0/functions ${sys.usb.config}
+ write /sys/class/android_usb/android0/enable 1
+ setprop sys.usb.state ${sys.usb.config}
+
+# audio accessory configuration, with adb
+on property:sys.usb.config=audio_source,adb
+ write /sys/class/android_usb/android0/enable 0
+ write /sys/class/android_usb/android0/idVendor 18d1
+ write /sys/class/android_usb/android0/idProduct 2d03
+ write /sys/class/android_usb/android0/functions ${sys.usb.config}
+ write /sys/class/android_usb/android0/enable 1
+ start adbd
+ setprop sys.usb.state ${sys.usb.config}
+
+# USB and audio accessory configuration
+on property:sys.usb.config=accessory,audio_source
+ write /sys/class/android_usb/android0/enable 0
+ write /sys/class/android_usb/android0/idVendor 18d1
+ write /sys/class/android_usb/android0/idProduct 2d04
+ write /sys/class/android_usb/android0/functions ${sys.usb.config}
+ write /sys/class/android_usb/android0/enable 1
+ setprop sys.usb.state ${sys.usb.config}
+
+# USB and audio accessory configuration, with adb
+on property:sys.usb.config=accessory,audio_source,adb
+ write /sys/class/android_usb/android0/enable 0
+ write /sys/class/android_usb/android0/idVendor 18d1
+ write /sys/class/android_usb/android0/idProduct 2d05
+ write /sys/class/android_usb/android0/functions ${sys.usb.config}
+ write /sys/class/android_usb/android0/enable 1
+ start adbd
+ setprop sys.usb.state ${sys.usb.config}
+
+# Used to set USB configuration at boot and to switch the configuration
+# when changing the default configuration
+on property:persist.sys.usb.config=*
+ setprop sys.usb.config ${persist.sys.usb.config}
diff --git a/rootdir/init.zygote32.rc b/rootdir/init.zygote32.rc
new file mode 100644
index 0000000..75961e6
--- /dev/null
+++ b/rootdir/init.zygote32.rc
@@ -0,0 +1,8 @@
+service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
+ class main
+ socket zygote stream 660 root system
+ onrestart write /sys/android_power/request_state wake
+ onrestart write /sys/power/state on
+ onrestart restart media
+ onrestart restart netd
+
diff --git a/rootdir/init.zygote32_64.rc b/rootdir/init.zygote32_64.rc
new file mode 100644
index 0000000..68c0668
--- /dev/null
+++ b/rootdir/init.zygote32_64.rc
@@ -0,0 +1,12 @@
+service zygote /system/bin/app_process32 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
+ class main
+ socket zygote stream 660 root system
+ onrestart write /sys/android_power/request_state wake
+ onrestart write /sys/power/state on
+ onrestart restart media
+ onrestart restart netd
+
+service zygote_secondary /system/bin/app_process64 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
+ class main
+ socket zygote_secondary stream 660 root system
+ onrestart restart zygote
diff --git a/rootdir/init.zygote64.rc b/rootdir/init.zygote64.rc
new file mode 100644
index 0000000..afb6d63
--- /dev/null
+++ b/rootdir/init.zygote64.rc
@@ -0,0 +1,8 @@
+service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
+ class main
+ socket zygote stream 660 root system
+ onrestart write /sys/android_power/request_state wake
+ onrestart write /sys/power/state on
+ onrestart restart media
+ onrestart restart netd
+
diff --git a/rootdir/init.zygote64_32.rc b/rootdir/init.zygote64_32.rc
new file mode 100644
index 0000000..979ab3b
--- /dev/null
+++ b/rootdir/init.zygote64_32.rc
@@ -0,0 +1,12 @@
+service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
+ class main
+ socket zygote stream 660 root system
+ onrestart write /sys/android_power/request_state wake
+ onrestart write /sys/power/state on
+ onrestart restart media
+ onrestart restart netd
+
+service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
+ class main
+ socket zygote_secondary stream 660 root system
+ onrestart restart zygote
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
new file mode 100644
index 0000000..9cf9ed9
--- /dev/null
+++ b/rootdir/ueventd.rc
@@ -0,0 +1,97 @@
+subsystem adf
+ devname uevent_devname
+
+/dev/null 0666 root root
+/dev/zero 0666 root root
+/dev/full 0666 root root
+/dev/ptmx 0666 root root
+/dev/tty 0666 root root
+/dev/random 0666 root root
+/dev/urandom 0666 root root
+# Make HW RNG readable by group system to let EntropyMixer read it.
+/dev/hw_random 0440 root system
+/dev/ashmem 0666 root root
+/dev/binder 0666 root root
+
+# Anyone can read the logs, but if they're not in the "logs"
+# group, then they'll only see log entries for their UID.
+/dev/log/* 0666 root log
+/dev/pmsg0 0222 root log
+
+# the msm hw3d client device node is world writable/readable.
+/dev/msm_hw3dc 0666 root root
+
+# gpu driver for adreno200 is globally accessible
+/dev/kgsl 0666 root root
+
+# kms driver for drm based gpu
+/dev/dri/* 0666 root graphics
+
+# these should not be world writable
+/dev/diag 0660 radio radio
+/dev/diag_arm9 0660 radio radio
+/dev/android_adb 0660 adb adb
+/dev/android_adb_enable 0660 adb adb
+/dev/ttyMSM0 0600 bluetooth bluetooth
+/dev/uhid 0660 system net_bt_stack
+/dev/uinput 0660 system net_bt_stack
+/dev/alarm 0664 system radio
+/dev/rtc0 0640 system system
+/dev/tty0 0660 root system
+/dev/graphics/* 0660 root graphics
+/dev/msm_hw3dm 0660 system graphics
+/dev/input/* 0660 root input
+/dev/eac 0660 root audio
+/dev/cam 0660 root camera
+/dev/pmem 0660 system graphics
+/dev/pmem_adsp* 0660 system audio
+/dev/pmem_camera* 0660 system camera
+/dev/oncrpc/* 0660 root system
+/dev/adsp/* 0660 system audio
+/dev/snd/* 0660 system audio
+/dev/mt9t013 0660 system system
+/dev/msm_camera/* 0660 system system
+/dev/akm8976_daemon 0640 compass system
+/dev/akm8976_aot 0640 compass system
+/dev/akm8973_daemon 0640 compass system
+/dev/akm8973_aot 0640 compass system
+/dev/bma150 0640 compass system
+/dev/cm3602 0640 compass system
+/dev/akm8976_pffd 0640 compass system
+/dev/lightsensor 0640 system system
+/dev/msm_pcm_out* 0660 system audio
+/dev/msm_pcm_in* 0660 system audio
+/dev/msm_pcm_ctl* 0660 system audio
+/dev/msm_snd* 0660 system audio
+/dev/msm_mp3* 0660 system audio
+/dev/audience_a1026* 0660 system audio
+/dev/tpa2018d1* 0660 system audio
+/dev/msm_audpre 0660 system audio
+/dev/msm_audio_ctl 0660 system audio
+/dev/htc-acoustic 0660 system audio
+/dev/vdec 0660 system audio
+/dev/q6venc 0660 system audio
+/dev/snd/dsp 0660 system audio
+/dev/snd/dsp1 0660 system audio
+/dev/snd/mixer 0660 system audio
+/dev/smd0 0640 radio radio
+/dev/qmi 0640 radio radio
+/dev/qmi0 0640 radio radio
+/dev/qmi1 0640 radio radio
+/dev/qmi2 0640 radio radio
+/dev/bus/usb/* 0660 root usb
+/dev/mtp_usb 0660 root mtp
+/dev/usb_accessory 0660 root usb
+/dev/tun 0660 system vpn
+
+# CDMA radio interface MUX
+/dev/ts0710mux* 0640 radio radio
+/dev/ppp 0660 radio vpn
+
+# sysfs properties
+/sys/devices/platform/trusty.* trusty_version 0440 root log
+/sys/devices/virtual/input/input* enable 0660 root input
+/sys/devices/virtual/input/input* poll_delay 0660 root input
+/sys/devices/virtual/usb_composite/* enable 0664 root system
+/sys/devices/system/cpu/cpu* cpufreq/scaling_max_freq 0664 system system
+/sys/devices/system/cpu/cpu* cpufreq/scaling_min_freq 0664 system system
diff --git a/run-as/Android.mk b/run-as/Android.mk
new file mode 100644
index 0000000..3774acc
--- /dev/null
+++ b/run-as/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := run-as.c package.c
+
+LOCAL_SHARED_LIBRARIES := libselinux
+
+LOCAL_MODULE := run-as
+
+LOCAL_CFLAGS := -Werror
+
+include $(BUILD_EXECUTABLE)
diff --git a/run-as/NOTICE b/run-as/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/run-as/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/run-as/package.c b/run-as/package.c
new file mode 100644
index 0000000..9e1f5bb
--- /dev/null
+++ b/run-as/package.c
@@ -0,0 +1,542 @@
+/*
+**
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <private/android_filesystem_config.h>
+#include "package.h"
+
+/*
+ * WARNING WARNING WARNING WARNING
+ *
+ * The following code runs as root on production devices, before
+ * the run-as command has dropped the uid/gid. Hence be very
+ * conservative and keep in mind the following:
+ *
+ * - Performance does not matter here, clarity and safety of the code
+ * does however. Documentation is a must.
+ *
+ * - Avoid calling C library functions with complex implementations
+ * like malloc() and printf(). You want to depend on simple system
+ * calls instead, which behaviour is not going to be altered in
+ * unpredictible ways by environment variables or system properties.
+ *
+ * - Do not trust user input and/or the filesystem whenever possible.
+ *
+ */
+
+/* The file containing the list of installed packages on the system */
+#define PACKAGES_LIST_FILE "/data/system/packages.list"
+
+/* Copy 'srclen' string bytes from 'src' into buffer 'dst' of size 'dstlen'
+ * This function always zero-terminate the destination buffer unless
+ * 'dstlen' is 0, even in case of overflow.
+ * Returns a pointer into the src string, leaving off where the copy
+ * has stopped. The copy will stop when dstlen, srclen or a null
+ * character on src has been reached.
+ */
+static const char*
+string_copy(char* dst, size_t dstlen, const char* src, size_t srclen)
+{
+ const char* srcend = src + srclen;
+ const char* dstend = dst + dstlen;
+
+ if (dstlen == 0)
+ return src;
+
+ dstend--; /* make room for terminating zero */
+
+ while (dst < dstend && src < srcend && *src != '\0')
+ *dst++ = *src++;
+
+ *dst = '\0'; /* zero-terminate result */
+ return src;
+}
+
+/* Open 'filename' and map it into our address-space.
+ * Returns buffer address, or NULL on error
+ * On exit, *filesize will be set to the file's size, or 0 on error
+ */
+static void*
+map_file(const char* filename, size_t* filesize)
+{
+ int fd, ret, old_errno;
+ struct stat st;
+ size_t length = 0;
+ void* address = NULL;
+ gid_t oldegid;
+
+ *filesize = 0;
+
+ /*
+ * Temporarily switch effective GID to allow us to read
+ * the packages file
+ */
+
+ oldegid = getegid();
+ if (setegid(AID_PACKAGE_INFO) < 0) {
+ return NULL;
+ }
+
+ /* open the file for reading */
+ fd = TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
+ if (fd < 0) {
+ return NULL;
+ }
+
+ /* restore back to our old egid */
+ if (setegid(oldegid) < 0) {
+ goto EXIT;
+ }
+
+ /* get its size */
+ ret = TEMP_FAILURE_RETRY(fstat(fd, &st));
+ if (ret < 0)
+ goto EXIT;
+
+ /* Ensure that the file is owned by the system user */
+ if ((st.st_uid != AID_SYSTEM) || (st.st_gid != AID_PACKAGE_INFO)) {
+ goto EXIT;
+ }
+
+ /* Ensure that the file has sane permissions */
+ if ((st.st_mode & S_IWOTH) != 0) {
+ goto EXIT;
+ }
+
+ /* Ensure that the size is not ridiculously large */
+ length = (size_t)st.st_size;
+ if ((off_t)length != st.st_size) {
+ errno = ENOMEM;
+ goto EXIT;
+ }
+
+ /* Memory-map the file now */
+ do {
+ address = mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0);
+ } while (address == MAP_FAILED && errno == EINTR);
+ if (address == MAP_FAILED) {
+ address = NULL;
+ goto EXIT;
+ }
+
+ /* We're good, return size */
+ *filesize = length;
+
+EXIT:
+ /* close the file, preserve old errno for better diagnostics */
+ old_errno = errno;
+ close(fd);
+ errno = old_errno;
+
+ return address;
+}
+
+/* unmap the file, but preserve errno */
+static void
+unmap_file(void* address, size_t size)
+{
+ int old_errno = errno;
+ TEMP_FAILURE_RETRY(munmap(address, size));
+ errno = old_errno;
+}
+
+/* Check that a given directory:
+ * - exists
+ * - is owned by a given uid/gid
+ * - is a real directory, not a symlink
+ * - isn't readable or writable by others
+ *
+ * Return 0 on success, or -1 on error.
+ * errno is set to EINVAL in case of failed check.
+ */
+static int
+check_directory_ownership(const char* path, uid_t uid)
+{
+ int ret;
+ struct stat st;
+
+ do {
+ ret = lstat(path, &st);
+ } while (ret < 0 && errno == EINTR);
+
+ if (ret < 0)
+ return -1;
+
+ /* must be a real directory, not a symlink */
+ if (!S_ISDIR(st.st_mode))
+ goto BAD;
+
+ /* must be owned by specific uid/gid */
+ if (st.st_uid != uid || st.st_gid != uid)
+ goto BAD;
+
+ /* must not be readable or writable by others */
+ if ((st.st_mode & (S_IROTH|S_IWOTH)) != 0)
+ goto BAD;
+
+ /* everything ok */
+ return 0;
+
+BAD:
+ errno = EINVAL;
+ return -1;
+}
+
+/* This function is used to check the data directory path for safety.
+ * We check that every sub-directory is owned by the 'system' user
+ * and exists and is not a symlink. We also check that the full directory
+ * path is properly owned by the user ID.
+ *
+ * Return 0 on success, -1 on error.
+ */
+int
+check_data_path(const char* dataPath, uid_t uid)
+{
+ int nn;
+
+ /* the path should be absolute */
+ if (dataPath[0] != '/') {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* look for all sub-paths, we do that by finding
+ * directory separators in the input path and
+ * checking each sub-path independently
+ */
+ for (nn = 1; dataPath[nn] != '\0'; nn++)
+ {
+ char subpath[PATH_MAX];
+
+ /* skip non-separator characters */
+ if (dataPath[nn] != '/')
+ continue;
+
+ /* handle trailing separator case */
+ if (dataPath[nn+1] == '\0') {
+ break;
+ }
+
+ /* found a separator, check that dataPath is not too long. */
+ if (nn >= (int)(sizeof subpath)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* reject any '..' subpath */
+ if (nn >= 3 &&
+ dataPath[nn-3] == '/' &&
+ dataPath[nn-2] == '.' &&
+ dataPath[nn-1] == '.') {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* copy to 'subpath', then check ownership */
+ memcpy(subpath, dataPath, nn);
+ subpath[nn] = '\0';
+
+ if (check_directory_ownership(subpath, AID_SYSTEM) < 0)
+ return -1;
+ }
+
+ /* All sub-paths were checked, now verify that the full data
+ * directory is owned by the application uid
+ */
+ if (check_directory_ownership(dataPath, uid) < 0)
+ return -1;
+
+ /* all clear */
+ return 0;
+}
+
+/* Return TRUE iff a character is a space or tab */
+static inline int
+is_space(char c)
+{
+ return (c == ' ' || c == '\t');
+}
+
+/* Skip any space or tab character from 'p' until 'end' is reached.
+ * Return new position.
+ */
+static const char*
+skip_spaces(const char* p, const char* end)
+{
+ while (p < end && is_space(*p))
+ p++;
+
+ return p;
+}
+
+/* Skip any non-space and non-tab character from 'p' until 'end'.
+ * Return new position.
+ */
+static const char*
+skip_non_spaces(const char* p, const char* end)
+{
+ while (p < end && !is_space(*p))
+ p++;
+
+ return p;
+}
+
+/* Find the first occurence of 'ch' between 'p' and 'end'
+ * Return its position, or 'end' if none is found.
+ */
+static const char*
+find_first(const char* p, const char* end, char ch)
+{
+ while (p < end && *p != ch)
+ p++;
+
+ return p;
+}
+
+/* Check that the non-space string starting at 'p' and eventually
+ * ending at 'end' equals 'name'. Return new position (after name)
+ * on success, or NULL on failure.
+ *
+ * This function fails is 'name' is NULL, empty or contains any space.
+ */
+static const char*
+compare_name(const char* p, const char* end, const char* name)
+{
+ /* 'name' must not be NULL or empty */
+ if (name == NULL || name[0] == '\0' || p == end)
+ return NULL;
+
+ /* compare characters to those in 'name', excluding spaces */
+ while (*name) {
+ /* note, we don't check for *p == '\0' since
+ * it will be caught in the next conditional.
+ */
+ if (p >= end || is_space(*p))
+ goto BAD;
+
+ if (*p != *name)
+ goto BAD;
+
+ p++;
+ name++;
+ }
+
+ /* must be followed by end of line or space */
+ if (p < end && !is_space(*p))
+ goto BAD;
+
+ return p;
+
+BAD:
+ return NULL;
+}
+
+/* Parse one or more whitespace characters starting from '*pp'
+ * until 'end' is reached. Updates '*pp' on exit.
+ *
+ * Return 0 on success, -1 on failure.
+ */
+static int
+parse_spaces(const char** pp, const char* end)
+{
+ const char* p = *pp;
+
+ if (p >= end || !is_space(*p)) {
+ errno = EINVAL;
+ return -1;
+ }
+ p = skip_spaces(p, end);
+ *pp = p;
+ return 0;
+}
+
+/* Parse a positive decimal number starting from '*pp' until 'end'
+ * is reached. Adjust '*pp' on exit. Return decimal value or -1
+ * in case of error.
+ *
+ * If the value is larger than INT_MAX, -1 will be returned,
+ * and errno set to EOVERFLOW.
+ *
+ * If '*pp' does not start with a decimal digit, -1 is returned
+ * and errno set to EINVAL.
+ */
+static int
+parse_positive_decimal(const char** pp, const char* end)
+{
+ const char* p = *pp;
+ int value = 0;
+ int overflow = 0;
+
+ if (p >= end || *p < '0' || *p > '9') {
+ errno = EINVAL;
+ return -1;
+ }
+
+ while (p < end) {
+ int ch = *p;
+ unsigned d = (unsigned)(ch - '0');
+ int val2;
+
+ if (d >= 10U) /* d is unsigned, no lower bound check */
+ break;
+
+ val2 = value*10 + (int)d;
+ if (val2 < value)
+ overflow = 1;
+ value = val2;
+ p++;
+ }
+ *pp = p;
+
+ if (overflow) {
+ errno = EOVERFLOW;
+ value = -1;
+ }
+ return value;
+}
+
+/* Read the system's package database and extract information about
+ * 'pkgname'. Return 0 in case of success, or -1 in case of error.
+ *
+ * If the package is unknown, return -1 and set errno to ENOENT
+ * If the package database is corrupted, return -1 and set errno to EINVAL
+ */
+int
+get_package_info(const char* pkgName, PackageInfo *info)
+{
+ char* buffer;
+ size_t buffer_len;
+ const char* p;
+ const char* buffer_end;
+ int result = -1;
+
+ info->uid = 0;
+ info->isDebuggable = 0;
+ info->dataDir[0] = '\0';
+ info->seinfo[0] = '\0';
+
+ buffer = map_file(PACKAGES_LIST_FILE, &buffer_len);
+ if (buffer == NULL)
+ return -1;
+
+ p = buffer;
+ buffer_end = buffer + buffer_len;
+
+ /* expect the following format on each line of the control file:
+ *
+ * <pkgName> <uid> <debugFlag> <dataDir> <seinfo>
+ *
+ * where:
+ * <pkgName> is the package's name
+ * <uid> is the application-specific user Id (decimal)
+ * <debugFlag> is 1 if the package is debuggable, or 0 otherwise
+ * <dataDir> is the path to the package's data directory (e.g. /data/data/com.example.foo)
+ * <seinfo> is the seinfo label associated with the package
+ *
+ * The file is generated in com.android.server.PackageManagerService.Settings.writeLP()
+ */
+
+ while (p < buffer_end) {
+ /* find end of current line and start of next one */
+ const char* end = find_first(p, buffer_end, '\n');
+ const char* next = (end < buffer_end) ? end + 1 : buffer_end;
+ const char* q;
+ int uid, debugFlag;
+
+ /* first field is the package name */
+ p = compare_name(p, end, pkgName);
+ if (p == NULL)
+ goto NEXT_LINE;
+
+ /* skip spaces */
+ if (parse_spaces(&p, end) < 0)
+ goto BAD_FORMAT;
+
+ /* second field is the pid */
+ uid = parse_positive_decimal(&p, end);
+ if (uid < 0)
+ return -1;
+
+ info->uid = (uid_t) uid;
+
+ /* skip spaces */
+ if (parse_spaces(&p, end) < 0)
+ goto BAD_FORMAT;
+
+ /* third field is debug flag (0 or 1) */
+ debugFlag = parse_positive_decimal(&p, end);
+ switch (debugFlag) {
+ case 0:
+ info->isDebuggable = 0;
+ break;
+ case 1:
+ info->isDebuggable = 1;
+ break;
+ default:
+ goto BAD_FORMAT;
+ }
+
+ /* skip spaces */
+ if (parse_spaces(&p, end) < 0)
+ goto BAD_FORMAT;
+
+ /* fourth field is data directory path and must not contain
+ * spaces.
+ */
+ q = skip_non_spaces(p, end);
+ if (q == p)
+ goto BAD_FORMAT;
+
+ p = string_copy(info->dataDir, sizeof info->dataDir, p, q - p);
+
+ /* skip spaces */
+ if (parse_spaces(&p, end) < 0)
+ goto BAD_FORMAT;
+
+ /* fifth field is the seinfo string */
+ q = skip_non_spaces(p, end);
+ if (q == p)
+ goto BAD_FORMAT;
+
+ string_copy(info->seinfo, sizeof info->seinfo, p, q - p);
+
+ /* Ignore the rest */
+ result = 0;
+ goto EXIT;
+
+ NEXT_LINE:
+ p = next;
+ }
+
+ /* the package is unknown */
+ errno = ENOENT;
+ result = -1;
+ goto EXIT;
+
+BAD_FORMAT:
+ errno = EINVAL;
+ result = -1;
+
+EXIT:
+ unmap_file(buffer, buffer_len);
+ return result;
+}
diff --git a/run-as/package.h b/run-as/package.h
new file mode 100644
index 0000000..34603c0
--- /dev/null
+++ b/run-as/package.h
@@ -0,0 +1,42 @@
+/*
+**
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+#ifndef RUN_AS_PACKAGE_H
+#define RUN_AS_PACKAGE_H
+
+#include <limits.h>
+#include <sys/types.h>
+
+typedef enum {
+ PACKAGE_IS_DEBUGGABLE = 0,
+ PACKAGE_IS_NOT_DEBUGGABLE,
+ PACKAGE_IS_UNKNOWN,
+} PackageStatus;
+
+typedef struct {
+ uid_t uid;
+ char isDebuggable;
+ char dataDir[PATH_MAX];
+ char seinfo[PATH_MAX];
+} PackageInfo;
+
+/* see documentation in package.c for these functiosn */
+
+extern int get_package_info(const char* packageName, PackageInfo* info);
+
+extern int check_data_path(const char* dataDir, uid_t uid);
+
+#endif /* RUN_AS_PACKAGE_H */
diff --git a/run-as/run-as.c b/run-as/run-as.c
new file mode 100644
index 0000000..368b8f1
--- /dev/null
+++ b/run-as/run-as.c
@@ -0,0 +1,192 @@
+/*
+**
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#define PROGNAME "run-as"
+#define LOG_TAG PROGNAME
+
+#include <dirent.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/capability.h>
+#include <sys/cdefs.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <private/android_filesystem_config.h>
+#include <selinux/android.h>
+
+#include "package.h"
+
+/*
+ * WARNING WARNING WARNING WARNING
+ *
+ * This program runs with CAP_SETUID and CAP_SETGID capabilities on Android
+ * production devices. Be very conservative when modifying it to avoid any
+ * serious security issue. Keep in mind the following:
+ *
+ * - This program should only run for the 'root' or 'shell' users
+ *
+ * - Avoid anything that is more complex than simple system calls
+ * until the uid/gid has been dropped to that of a normal user
+ * or you are sure to exit.
+ *
+ * This avoids depending on environment variables, system properties
+ * and other external factors that may affect the C library in
+ * unpredictable ways.
+ *
+ * - Do not trust user input and/or the filesystem whenever possible.
+ *
+ * Read README.TXT for more details.
+ *
+ *
+ *
+ * The purpose of this program is to run a command as a specific
+ * application user-id. Typical usage is:
+ *
+ * run-as <package-name> <command> <args>
+ *
+ * The 'run-as' binary is installed with CAP_SETUID and CAP_SETGID file
+ * capabilities, but will check the following:
+ *
+ * - that it is invoked from the 'shell' or 'root' user (abort otherwise)
+ * - that '<package-name>' is the name of an installed and debuggable package
+ * - that the package's data directory is well-formed (see package.c)
+ *
+ * If so, it will drop to the application's user id / group id, cd to the
+ * package's data directory, then run the command there.
+ *
+ * NOTE: In the future it might not be possible to cd to the package's data
+ * directory under that package's user id / group id, in which case this
+ * utility will need to be changed accordingly.
+ *
+ * This can be useful for a number of different things on production devices:
+ *
+ * - Allow application developers to look at their own applicative data
+ * during development.
+ *
+ * - Run the 'gdbserver' binary executable to allow native debugging
+ */
+
+__noreturn static void
+panic(const char* format, ...)
+{
+ va_list args;
+ int e = errno;
+
+ fprintf(stderr, "%s: ", PROGNAME);
+ va_start(args, format);
+ vfprintf(stderr, format, args);
+ va_end(args);
+ exit(e ? -e : 1);
+}
+
+static void
+usage(void)
+{
+ panic("Usage:\n " PROGNAME " <package-name> <command> [<args>]\n");
+}
+
+int main(int argc, char **argv)
+{
+ const char* pkgname;
+ int myuid, uid, gid;
+ PackageInfo info;
+ struct __user_cap_header_struct capheader;
+ struct __user_cap_data_struct capdata[2];
+
+ /* check arguments */
+ if (argc < 2) {
+ usage();
+ }
+
+ /* check userid of caller - must be 'shell' or 'root' */
+ myuid = getuid();
+ if (myuid != AID_SHELL && myuid != AID_ROOT) {
+ panic("only 'shell' or 'root' users can run this program\n");
+ }
+
+ memset(&capheader, 0, sizeof(capheader));
+ memset(&capdata, 0, sizeof(capdata));
+ capheader.version = _LINUX_CAPABILITY_VERSION_3;
+ capdata[CAP_TO_INDEX(CAP_SETUID)].effective |= CAP_TO_MASK(CAP_SETUID);
+ capdata[CAP_TO_INDEX(CAP_SETGID)].effective |= CAP_TO_MASK(CAP_SETGID);
+ capdata[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID);
+ capdata[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);
+
+ if (capset(&capheader, &capdata[0]) < 0) {
+ panic("Could not set capabilities: %s\n", strerror(errno));
+ }
+
+ /* retrieve package information from system (does setegid) */
+ pkgname = argv[1];
+ if (get_package_info(pkgname, &info) < 0) {
+ panic("Package '%s' is unknown\n", pkgname);
+ }
+
+ /* reject system packages */
+ if (info.uid < AID_APP) {
+ panic("Package '%s' is not an application\n", pkgname);
+ }
+
+ /* reject any non-debuggable package */
+ if (!info.isDebuggable) {
+ panic("Package '%s' is not debuggable\n", pkgname);
+ }
+
+ /* check that the data directory path is valid */
+ if (check_data_path(info.dataDir, info.uid) < 0) {
+ panic("Package '%s' has corrupt installation\n", pkgname);
+ }
+
+ /* Ensure that we change all real/effective/saved IDs at the
+ * same time to avoid nasty surprises.
+ */
+ uid = gid = info.uid;
+ if(setresgid(gid,gid,gid) || setresuid(uid,uid,uid)) {
+ panic("Permission denied\n");
+ }
+
+ /* Required if caller has uid and gid all non-zero */
+ memset(&capdata, 0, sizeof(capdata));
+ if (capset(&capheader, &capdata[0]) < 0) {
+ panic("Could not clear all capabilities: %s\n", strerror(errno));
+ }
+
+ if (selinux_android_setcontext(uid, 0, info.seinfo, pkgname) < 0) {
+ panic("Could not set SELinux security context: %s\n", strerror(errno));
+ }
+
+ /* cd into the data directory */
+ if (TEMP_FAILURE_RETRY(chdir(info.dataDir)) < 0) {
+ panic("Could not cd to package's data directory: %s\n", strerror(errno));
+ }
+
+ /* User specified command for exec. */
+ if ((argc >= 3) && (execvp(argv[2], argv+2) < 0)) {
+ panic("exec failed for %s: %s\n", argv[2], strerror(errno));
+ }
+
+ /* Default exec shell. */
+ execlp("/system/bin/sh", "sh", NULL);
+
+ panic("exec failed: %s\n", strerror(errno));
+}
diff --git a/sdcard/Android.mk b/sdcard/Android.mk
new file mode 100644
index 0000000..cb3a8fb
--- /dev/null
+++ b/sdcard/Android.mk
@@ -0,0 +1,11 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := sdcard.c
+LOCAL_MODULE := sdcard
+LOCAL_CFLAGS := -Wall -Wno-unused-parameter -Werror
+
+LOCAL_SHARED_LIBRARIES := libcutils
+
+include $(BUILD_EXECUTABLE)
diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c
new file mode 100644
index 0000000..893c0dc
--- /dev/null
+++ b/sdcard/sdcard.c
@@ -0,0 +1,2023 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "sdcard"
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <linux/fuse.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/inotify.h>
+#include <sys/mount.h>
+#include <sys/param.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/statfs.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include <cutils/fs.h>
+#include <cutils/hashmap.h>
+#include <cutils/log.h>
+#include <cutils/multiuser.h>
+
+#include <private/android_filesystem_config.h>
+
+/* README
+ *
+ * What is this?
+ *
+ * sdcard is a program that uses FUSE to emulate FAT-on-sdcard style
+ * directory permissions (all files are given fixed owner, group, and
+ * permissions at creation, owner, group, and permissions are not
+ * changeable, symlinks and hardlinks are not createable, etc.
+ *
+ * See usage() for command line options.
+ *
+ * It must be run as root, but will drop to requested UID/GID as soon as it
+ * mounts a filesystem. It will refuse to run if requested UID/GID are zero.
+ *
+ * Things I believe to be true:
+ *
+ * - ops that return a fuse_entry (LOOKUP, MKNOD, MKDIR, LINK, SYMLINK,
+ * CREAT) must bump that node's refcount
+ * - don't forget that FORGET can forget multiple references (req->nlookup)
+ * - if an op that returns a fuse_entry fails writing the reply to the
+ * kernel, you must rollback the refcount to reflect the reference the
+ * kernel did not actually acquire
+ *
+ * This daemon can also derive custom filesystem permissions based on directory
+ * structure when requested. These custom permissions support several features:
+ *
+ * - Apps can access their own files in /Android/data/com.example/ without
+ * requiring any additional GIDs.
+ * - Separate permissions for protecting directories like Pictures and Music.
+ * - Multi-user separation on the same physical device.
+ *
+ * The derived permissions look like this:
+ *
+ * rwxrwx--x root:sdcard_rw /
+ * rwxrwx--- root:sdcard_pics /Pictures
+ * rwxrwx--- root:sdcard_av /Music
+ *
+ * rwxrwx--x root:sdcard_rw /Android
+ * rwxrwx--x root:sdcard_rw /Android/data
+ * rwxrwx--- u0_a12:sdcard_rw /Android/data/com.example
+ * rwxrwx--x root:sdcard_rw /Android/obb/
+ * rwxrwx--- u0_a12:sdcard_rw /Android/obb/com.example
+ *
+ * rwxrwx--- root:sdcard_all /Android/user
+ * rwxrwx--x root:sdcard_rw /Android/user/10
+ * rwxrwx--- u10_a12:sdcard_rw /Android/user/10/Android/data/com.example
+ */
+
+#define FUSE_TRACE 0
+
+#if FUSE_TRACE
+#define TRACE(x...) ALOGD(x)
+#else
+#define TRACE(x...) do {} while (0)
+#endif
+
+#define ERROR(x...) ALOGE(x)
+
+#define FUSE_UNKNOWN_INO 0xffffffff
+
+/* Maximum number of bytes to write in one request. */
+#define MAX_WRITE (256 * 1024)
+
+/* Maximum number of bytes to read in one request. */
+#define MAX_READ (128 * 1024)
+
+/* Largest possible request.
+ * The request size is bounded by the maximum size of a FUSE_WRITE request because it has
+ * the largest possible data payload. */
+#define MAX_REQUEST_SIZE (sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in) + MAX_WRITE)
+
+/* Default number of threads. */
+#define DEFAULT_NUM_THREADS 2
+
+/* Pseudo-error constant used to indicate that no fuse status is needed
+ * or that a reply has already been written. */
+#define NO_STATUS 1
+
+/* Path to system-provided mapping of package name to appIds */
+static const char* const kPackagesListFile = "/data/system/packages.list";
+
+/* Supplementary groups to execute with */
+static const gid_t kGroups[1] = { AID_PACKAGE_INFO };
+
+/* Permission mode for a specific node. Controls how file permissions
+ * are derived for children nodes. */
+typedef enum {
+ /* Nothing special; this node should just inherit from its parent. */
+ PERM_INHERIT,
+ /* This node is one level above a normal root; used for legacy layouts
+ * which use the first level to represent user_id. */
+ PERM_LEGACY_PRE_ROOT,
+ /* This node is "/" */
+ PERM_ROOT,
+ /* This node is "/Android" */
+ PERM_ANDROID,
+ /* This node is "/Android/data" */
+ PERM_ANDROID_DATA,
+ /* This node is "/Android/obb" */
+ PERM_ANDROID_OBB,
+ /* This node is "/Android/media" */
+ PERM_ANDROID_MEDIA,
+ /* This node is "/Android/user" */
+ PERM_ANDROID_USER,
+} perm_t;
+
+/* Permissions structure to derive */
+typedef enum {
+ DERIVE_NONE,
+ DERIVE_LEGACY,
+ DERIVE_UNIFIED,
+} derive_t;
+
+struct handle {
+ int fd;
+};
+
+struct dirhandle {
+ DIR *d;
+};
+
+struct node {
+ __u32 refcount;
+ __u64 nid;
+ __u64 gen;
+ /*
+ * The inode number for this FUSE node. Note that this isn't stable across
+ * multiple invocations of the FUSE daemon.
+ */
+ __u32 ino;
+
+ /* State derived based on current position in hierarchy. */
+ perm_t perm;
+ userid_t userid;
+ uid_t uid;
+ gid_t gid;
+ mode_t mode;
+
+ struct node *next; /* per-dir sibling list */
+ struct node *child; /* first contained file by this dir */
+ struct node *parent; /* containing directory */
+
+ size_t namelen;
+ char *name;
+ /* If non-null, this is the real name of the file in the underlying storage.
+ * This may differ from the field "name" only by case.
+ * strlen(actual_name) will always equal strlen(name), so it is safe to use
+ * namelen for both fields.
+ */
+ char *actual_name;
+
+ /* If non-null, an exact underlying path that should be grafted into this
+ * position. Used to support things like OBB. */
+ char* graft_path;
+ size_t graft_pathlen;
+
+ bool deleted;
+};
+
+static int str_hash(void *key) {
+ return hashmapHash(key, strlen(key));
+}
+
+/** Test if two string keys are equal ignoring case */
+static bool str_icase_equals(void *keyA, void *keyB) {
+ return strcasecmp(keyA, keyB) == 0;
+}
+
+static int int_hash(void *key) {
+ return (int) (uintptr_t) key;
+}
+
+static bool int_equals(void *keyA, void *keyB) {
+ return keyA == keyB;
+}
+
+/* Global data structure shared by all fuse handlers. */
+struct fuse {
+ pthread_mutex_t lock;
+
+ __u64 next_generation;
+ int fd;
+ derive_t derive;
+ bool split_perms;
+ gid_t write_gid;
+ struct node root;
+ char obbpath[PATH_MAX];
+
+ /* Used to allocate unique inode numbers for fuse nodes. We use
+ * a simple counter based scheme where inode numbers from deleted
+ * nodes aren't reused. Note that inode allocations are not stable
+ * across multiple invocation of the sdcard daemon, but that shouldn't
+ * be a huge problem in practice.
+ *
+ * Note that we restrict inodes to 32 bit unsigned integers to prevent
+ * truncation on 32 bit processes when unsigned long long stat.st_ino is
+ * assigned to an unsigned long ino_t type in an LP32 process.
+ *
+ * Also note that fuse_attr and fuse_dirent inode values are 64 bits wide
+ * on both LP32 and LP64, but the fuse kernel code doesn't squash 64 bit
+ * inode numbers into 32 bit values on 64 bit kernels (see fuse_squash_ino
+ * in fs/fuse/inode.c).
+ *
+ * Accesses must be guarded by |lock|.
+ */
+ __u32 inode_ctr;
+
+ Hashmap* package_to_appid;
+ Hashmap* appid_with_rw;
+};
+
+/* Private data used by a single fuse handler. */
+struct fuse_handler {
+ struct fuse* fuse;
+ int token;
+
+ /* To save memory, we never use the contents of the request buffer and the read
+ * buffer at the same time. This allows us to share the underlying storage. */
+ union {
+ __u8 request_buffer[MAX_REQUEST_SIZE];
+ __u8 read_buffer[MAX_READ + PAGESIZE];
+ };
+};
+
+static inline void *id_to_ptr(__u64 nid)
+{
+ return (void *) (uintptr_t) nid;
+}
+
+static inline __u64 ptr_to_id(void *ptr)
+{
+ return (__u64) (uintptr_t) ptr;
+}
+
+static void acquire_node_locked(struct node* node)
+{
+ node->refcount++;
+ TRACE("ACQUIRE %p (%s) rc=%d\n", node, node->name, node->refcount);
+}
+
+static void remove_node_from_parent_locked(struct node* node);
+
+static void release_node_locked(struct node* node)
+{
+ TRACE("RELEASE %p (%s) rc=%d\n", node, node->name, node->refcount);
+ if (node->refcount > 0) {
+ node->refcount--;
+ if (!node->refcount) {
+ TRACE("DESTROY %p (%s)\n", node, node->name);
+ remove_node_from_parent_locked(node);
+
+ /* TODO: remove debugging - poison memory */
+ memset(node->name, 0xef, node->namelen);
+ free(node->name);
+ free(node->actual_name);
+ memset(node, 0xfc, sizeof(*node));
+ free(node);
+ }
+ } else {
+ ERROR("Zero refcnt %p\n", node);
+ }
+}
+
+static void add_node_to_parent_locked(struct node *node, struct node *parent) {
+ node->parent = parent;
+ node->next = parent->child;
+ parent->child = node;
+ acquire_node_locked(parent);
+}
+
+static void remove_node_from_parent_locked(struct node* node)
+{
+ if (node->parent) {
+ if (node->parent->child == node) {
+ node->parent->child = node->parent->child->next;
+ } else {
+ struct node *node2;
+ node2 = node->parent->child;
+ while (node2->next != node)
+ node2 = node2->next;
+ node2->next = node->next;
+ }
+ release_node_locked(node->parent);
+ node->parent = NULL;
+ node->next = NULL;
+ }
+}
+
+/* Gets the absolute path to a node into the provided buffer.
+ *
+ * Populates 'buf' with the path and returns the length of the path on success,
+ * or returns -1 if the path is too long for the provided buffer.
+ */
+static ssize_t get_node_path_locked(struct node* node, char* buf, size_t bufsize) {
+ const char* name;
+ size_t namelen;
+ if (node->graft_path) {
+ name = node->graft_path;
+ namelen = node->graft_pathlen;
+ } else if (node->actual_name) {
+ name = node->actual_name;
+ namelen = node->namelen;
+ } else {
+ name = node->name;
+ namelen = node->namelen;
+ }
+
+ if (bufsize < namelen + 1) {
+ return -1;
+ }
+
+ ssize_t pathlen = 0;
+ if (node->parent && node->graft_path == NULL) {
+ pathlen = get_node_path_locked(node->parent, buf, bufsize - namelen - 2);
+ if (pathlen < 0) {
+ return -1;
+ }
+ buf[pathlen++] = '/';
+ }
+
+ memcpy(buf + pathlen, name, namelen + 1); /* include trailing \0 */
+ return pathlen + namelen;
+}
+
+/* Finds the absolute path of a file within a given directory.
+ * Performs a case-insensitive search for the file and sets the buffer to the path
+ * of the first matching file. If 'search' is zero or if no match is found, sets
+ * the buffer to the path that the file would have, assuming the name were case-sensitive.
+ *
+ * Populates 'buf' with the path and returns the actual name (within 'buf') on success,
+ * or returns NULL if the path is too long for the provided buffer.
+ */
+static char* find_file_within(const char* path, const char* name,
+ char* buf, size_t bufsize, int search)
+{
+ size_t pathlen = strlen(path);
+ size_t namelen = strlen(name);
+ size_t childlen = pathlen + namelen + 1;
+ char* actual;
+
+ if (bufsize <= childlen) {
+ return NULL;
+ }
+
+ memcpy(buf, path, pathlen);
+ buf[pathlen] = '/';
+ actual = buf + pathlen + 1;
+ memcpy(actual, name, namelen + 1);
+
+ if (search && access(buf, F_OK)) {
+ struct dirent* entry;
+ DIR* dir = opendir(path);
+ if (!dir) {
+ ERROR("opendir %s failed: %s\n", path, strerror(errno));
+ return actual;
+ }
+ while ((entry = readdir(dir))) {
+ if (!strcasecmp(entry->d_name, name)) {
+ /* we have a match - replace the name, don't need to copy the null again */
+ memcpy(actual, entry->d_name, namelen);
+ break;
+ }
+ }
+ closedir(dir);
+ }
+ return actual;
+}
+
+static void attr_from_stat(struct fuse_attr *attr, const struct stat *s, const struct node* node)
+{
+ attr->ino = node->ino;
+ attr->size = s->st_size;
+ attr->blocks = s->st_blocks;
+ attr->atime = s->st_atim.tv_sec;
+ attr->mtime = s->st_mtim.tv_sec;
+ attr->ctime = s->st_ctim.tv_sec;
+ attr->atimensec = s->st_atim.tv_nsec;
+ attr->mtimensec = s->st_mtim.tv_nsec;
+ attr->ctimensec = s->st_ctim.tv_nsec;
+ attr->mode = s->st_mode;
+ attr->nlink = s->st_nlink;
+
+ attr->uid = node->uid;
+ attr->gid = node->gid;
+
+ /* Filter requested mode based on underlying file, and
+ * pass through file type. */
+ int owner_mode = s->st_mode & 0700;
+ int filtered_mode = node->mode & (owner_mode | (owner_mode >> 3) | (owner_mode >> 6));
+ attr->mode = (attr->mode & S_IFMT) | filtered_mode;
+}
+
+static int touch(char* path, mode_t mode) {
+ int fd = open(path, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, mode);
+ if (fd == -1) {
+ if (errno == EEXIST) {
+ return 0;
+ } else {
+ ERROR("Failed to open(%s): %s\n", path, strerror(errno));
+ return -1;
+ }
+ }
+ close(fd);
+ return 0;
+}
+
+static void derive_permissions_locked(struct fuse* fuse, struct node *parent,
+ struct node *node) {
+ appid_t appid;
+
+ /* By default, each node inherits from its parent */
+ node->perm = PERM_INHERIT;
+ node->userid = parent->userid;
+ node->uid = parent->uid;
+ node->gid = parent->gid;
+ node->mode = parent->mode;
+
+ if (fuse->derive == DERIVE_NONE) {
+ return;
+ }
+
+ /* Derive custom permissions based on parent and current node */
+ switch (parent->perm) {
+ case PERM_INHERIT:
+ /* Already inherited above */
+ break;
+ case PERM_LEGACY_PRE_ROOT:
+ /* Legacy internal layout places users at top level */
+ node->perm = PERM_ROOT;
+ node->userid = strtoul(node->name, NULL, 10);
+ break;
+ case PERM_ROOT:
+ /* Assume masked off by default. */
+ node->mode = 0770;
+ if (!strcasecmp(node->name, "Android")) {
+ /* App-specific directories inside; let anyone traverse */
+ node->perm = PERM_ANDROID;
+ node->mode = 0771;
+ } else if (fuse->split_perms) {
+ if (!strcasecmp(node->name, "DCIM")
+ || !strcasecmp(node->name, "Pictures")) {
+ node->gid = AID_SDCARD_PICS;
+ } else if (!strcasecmp(node->name, "Alarms")
+ || !strcasecmp(node->name, "Movies")
+ || !strcasecmp(node->name, "Music")
+ || !strcasecmp(node->name, "Notifications")
+ || !strcasecmp(node->name, "Podcasts")
+ || !strcasecmp(node->name, "Ringtones")) {
+ node->gid = AID_SDCARD_AV;
+ }
+ }
+ break;
+ case PERM_ANDROID:
+ if (!strcasecmp(node->name, "data")) {
+ /* App-specific directories inside; let anyone traverse */
+ node->perm = PERM_ANDROID_DATA;
+ node->mode = 0771;
+ } else if (!strcasecmp(node->name, "obb")) {
+ /* App-specific directories inside; let anyone traverse */
+ node->perm = PERM_ANDROID_OBB;
+ node->mode = 0771;
+ /* Single OBB directory is always shared */
+ node->graft_path = fuse->obbpath;
+ node->graft_pathlen = strlen(fuse->obbpath);
+ } else if (!strcasecmp(node->name, "media")) {
+ /* App-specific directories inside; let anyone traverse */
+ node->perm = PERM_ANDROID_MEDIA;
+ node->mode = 0771;
+ } else if (!strcasecmp(node->name, "user")) {
+ /* User directories must only be accessible to system, protected
+ * by sdcard_all. Zygote will bind mount the appropriate user-
+ * specific path. */
+ node->perm = PERM_ANDROID_USER;
+ node->gid = AID_SDCARD_ALL;
+ node->mode = 0770;
+ }
+ break;
+ case PERM_ANDROID_DATA:
+ case PERM_ANDROID_OBB:
+ case PERM_ANDROID_MEDIA:
+ appid = (appid_t) (uintptr_t) hashmapGet(fuse->package_to_appid, node->name);
+ if (appid != 0) {
+ node->uid = multiuser_get_uid(parent->userid, appid);
+ }
+ node->mode = 0770;
+ break;
+ case PERM_ANDROID_USER:
+ /* Root of a secondary user */
+ node->perm = PERM_ROOT;
+ node->userid = strtoul(node->name, NULL, 10);
+ node->gid = AID_SDCARD_R;
+ node->mode = 0771;
+ break;
+ }
+}
+
+/* Return if the calling UID holds sdcard_rw. */
+static bool get_caller_has_rw_locked(struct fuse* fuse, const struct fuse_in_header *hdr) {
+ /* No additional permissions enforcement */
+ if (fuse->derive == DERIVE_NONE) {
+ return true;
+ }
+
+ appid_t appid = multiuser_get_app_id(hdr->uid);
+ return hashmapContainsKey(fuse->appid_with_rw, (void*) (uintptr_t) appid);
+}
+
+/* Kernel has already enforced everything we returned through
+ * derive_permissions_locked(), so this is used to lock down access
+ * even further, such as enforcing that apps hold sdcard_rw. */
+static bool check_caller_access_to_name(struct fuse* fuse,
+ const struct fuse_in_header *hdr, const struct node* parent_node,
+ const char* name, int mode, bool has_rw) {
+ /* Always block security-sensitive files at root */
+ if (parent_node && parent_node->perm == PERM_ROOT) {
+ if (!strcasecmp(name, "autorun.inf")
+ || !strcasecmp(name, ".android_secure")
+ || !strcasecmp(name, "android_secure")) {
+ return false;
+ }
+ }
+
+ /* No additional permissions enforcement */
+ if (fuse->derive == DERIVE_NONE) {
+ return true;
+ }
+
+ /* Root always has access; access for any other UIDs should always
+ * be controlled through packages.list. */
+ if (hdr->uid == 0) {
+ return true;
+ }
+
+ /* If asking to write, verify that caller either owns the
+ * parent or holds sdcard_rw. */
+ if (mode & W_OK) {
+ if (parent_node && hdr->uid == parent_node->uid) {
+ return true;
+ }
+
+ return has_rw;
+ }
+
+ /* No extra permissions to enforce */
+ return true;
+}
+
+static bool check_caller_access_to_node(struct fuse* fuse,
+ const struct fuse_in_header *hdr, const struct node* node, int mode, bool has_rw) {
+ return check_caller_access_to_name(fuse, hdr, node->parent, node->name, mode, has_rw);
+}
+
+struct node *create_node_locked(struct fuse* fuse,
+ struct node *parent, const char *name, const char* actual_name)
+{
+ struct node *node;
+ size_t namelen = strlen(name);
+
+ // Detect overflows in the inode counter. "4 billion nodes should be enough
+ // for everybody".
+ if (fuse->inode_ctr == 0) {
+ ERROR("No more inode numbers available");
+ return NULL;
+ }
+
+ node = calloc(1, sizeof(struct node));
+ if (!node) {
+ return NULL;
+ }
+ node->name = malloc(namelen + 1);
+ if (!node->name) {
+ free(node);
+ return NULL;
+ }
+ memcpy(node->name, name, namelen + 1);
+ if (strcmp(name, actual_name)) {
+ node->actual_name = malloc(namelen + 1);
+ if (!node->actual_name) {
+ free(node->name);
+ free(node);
+ return NULL;
+ }
+ memcpy(node->actual_name, actual_name, namelen + 1);
+ }
+ node->namelen = namelen;
+ node->nid = ptr_to_id(node);
+ node->ino = fuse->inode_ctr++;
+ node->gen = fuse->next_generation++;
+
+ node->deleted = false;
+
+ derive_permissions_locked(fuse, parent, node);
+ acquire_node_locked(node);
+ add_node_to_parent_locked(node, parent);
+ return node;
+}
+
+static int rename_node_locked(struct node *node, const char *name,
+ const char* actual_name)
+{
+ size_t namelen = strlen(name);
+ int need_actual_name = strcmp(name, actual_name);
+
+ /* make the storage bigger without actually changing the name
+ * in case an error occurs part way */
+ if (namelen > node->namelen) {
+ char* new_name = realloc(node->name, namelen + 1);
+ if (!new_name) {
+ return -ENOMEM;
+ }
+ node->name = new_name;
+ if (need_actual_name && node->actual_name) {
+ char* new_actual_name = realloc(node->actual_name, namelen + 1);
+ if (!new_actual_name) {
+ return -ENOMEM;
+ }
+ node->actual_name = new_actual_name;
+ }
+ }
+
+ /* update the name, taking care to allocate storage before overwriting the old name */
+ if (need_actual_name) {
+ if (!node->actual_name) {
+ node->actual_name = malloc(namelen + 1);
+ if (!node->actual_name) {
+ return -ENOMEM;
+ }
+ }
+ memcpy(node->actual_name, actual_name, namelen + 1);
+ } else {
+ free(node->actual_name);
+ node->actual_name = NULL;
+ }
+ memcpy(node->name, name, namelen + 1);
+ node->namelen = namelen;
+ return 0;
+}
+
+static struct node *lookup_node_by_id_locked(struct fuse *fuse, __u64 nid)
+{
+ if (nid == FUSE_ROOT_ID) {
+ return &fuse->root;
+ } else {
+ return id_to_ptr(nid);
+ }
+}
+
+static struct node* lookup_node_and_path_by_id_locked(struct fuse* fuse, __u64 nid,
+ char* buf, size_t bufsize)
+{
+ struct node* node = lookup_node_by_id_locked(fuse, nid);
+ if (node && get_node_path_locked(node, buf, bufsize) < 0) {
+ node = NULL;
+ }
+ return node;
+}
+
+static struct node *lookup_child_by_name_locked(struct node *node, const char *name)
+{
+ for (node = node->child; node; node = node->next) {
+ /* use exact string comparison, nodes that differ by case
+ * must be considered distinct even if they refer to the same
+ * underlying file as otherwise operations such as "mv x x"
+ * will not work because the source and target nodes are the same. */
+ if (!strcmp(name, node->name) && !node->deleted) {
+ return node;
+ }
+ }
+ return 0;
+}
+
+static struct node* acquire_or_create_child_locked(
+ struct fuse* fuse, struct node* parent,
+ const char* name, const char* actual_name)
+{
+ struct node* child = lookup_child_by_name_locked(parent, name);
+ if (child) {
+ acquire_node_locked(child);
+ } else {
+ child = create_node_locked(fuse, parent, name, actual_name);
+ }
+ return child;
+}
+
+static void fuse_init(struct fuse *fuse, int fd, const char *source_path,
+ gid_t write_gid, derive_t derive, bool split_perms) {
+ pthread_mutex_init(&fuse->lock, NULL);
+
+ fuse->fd = fd;
+ fuse->next_generation = 0;
+ fuse->derive = derive;
+ fuse->split_perms = split_perms;
+ fuse->write_gid = write_gid;
+ fuse->inode_ctr = 1;
+
+ memset(&fuse->root, 0, sizeof(fuse->root));
+ fuse->root.nid = FUSE_ROOT_ID; /* 1 */
+ fuse->root.refcount = 2;
+ fuse->root.namelen = strlen(source_path);
+ fuse->root.name = strdup(source_path);
+ fuse->root.userid = 0;
+ fuse->root.uid = AID_ROOT;
+
+ /* Set up root node for various modes of operation */
+ switch (derive) {
+ case DERIVE_NONE:
+ /* Traditional behavior that treats entire device as being accessible
+ * to sdcard_rw, and no permissions are derived. */
+ fuse->root.perm = PERM_ROOT;
+ fuse->root.mode = 0775;
+ fuse->root.gid = AID_SDCARD_RW;
+ break;
+ case DERIVE_LEGACY:
+ /* Legacy behavior used to support internal multiuser layout which
+ * places user_id at the top directory level, with the actual roots
+ * just below that. Shared OBB path is also at top level. */
+ fuse->root.perm = PERM_LEGACY_PRE_ROOT;
+ fuse->root.mode = 0771;
+ fuse->root.gid = AID_SDCARD_R;
+ fuse->package_to_appid = hashmapCreate(256, str_hash, str_icase_equals);
+ fuse->appid_with_rw = hashmapCreate(128, int_hash, int_equals);
+ snprintf(fuse->obbpath, sizeof(fuse->obbpath), "%s/obb", source_path);
+ fs_prepare_dir(fuse->obbpath, 0775, getuid(), getgid());
+ break;
+ case DERIVE_UNIFIED:
+ /* Unified multiuser layout which places secondary user_id under
+ * /Android/user and shared OBB path under /Android/obb. */
+ fuse->root.perm = PERM_ROOT;
+ fuse->root.mode = 0771;
+ fuse->root.gid = AID_SDCARD_R;
+ fuse->package_to_appid = hashmapCreate(256, str_hash, str_icase_equals);
+ fuse->appid_with_rw = hashmapCreate(128, int_hash, int_equals);
+ snprintf(fuse->obbpath, sizeof(fuse->obbpath), "%s/Android/obb", source_path);
+ break;
+ }
+}
+
+static void fuse_status(struct fuse *fuse, __u64 unique, int err)
+{
+ struct fuse_out_header hdr;
+ hdr.len = sizeof(hdr);
+ hdr.error = err;
+ hdr.unique = unique;
+ write(fuse->fd, &hdr, sizeof(hdr));
+}
+
+static void fuse_reply(struct fuse *fuse, __u64 unique, void *data, int len)
+{
+ struct fuse_out_header hdr;
+ struct iovec vec[2];
+ int res;
+
+ hdr.len = len + sizeof(hdr);
+ hdr.error = 0;
+ hdr.unique = unique;
+
+ vec[0].iov_base = &hdr;
+ vec[0].iov_len = sizeof(hdr);
+ vec[1].iov_base = data;
+ vec[1].iov_len = len;
+
+ res = writev(fuse->fd, vec, 2);
+ if (res < 0) {
+ ERROR("*** REPLY FAILED *** %d\n", errno);
+ }
+}
+
+static int fuse_reply_entry(struct fuse* fuse, __u64 unique,
+ struct node* parent, const char* name, const char* actual_name,
+ const char* path)
+{
+ struct node* node;
+ struct fuse_entry_out out;
+ struct stat s;
+
+ if (lstat(path, &s) < 0) {
+ return -errno;
+ }
+
+ pthread_mutex_lock(&fuse->lock);
+ node = acquire_or_create_child_locked(fuse, parent, name, actual_name);
+ if (!node) {
+ pthread_mutex_unlock(&fuse->lock);
+ return -ENOMEM;
+ }
+ memset(&out, 0, sizeof(out));
+ attr_from_stat(&out.attr, &s, node);
+ out.attr_valid = 10;
+ out.entry_valid = 10;
+ out.nodeid = node->nid;
+ out.generation = node->gen;
+ pthread_mutex_unlock(&fuse->lock);
+ fuse_reply(fuse, unique, &out, sizeof(out));
+ return NO_STATUS;
+}
+
+static int fuse_reply_attr(struct fuse* fuse, __u64 unique, const struct node* node,
+ const char* path)
+{
+ struct fuse_attr_out out;
+ struct stat s;
+
+ if (lstat(path, &s) < 0) {
+ return -errno;
+ }
+ memset(&out, 0, sizeof(out));
+ attr_from_stat(&out.attr, &s, node);
+ out.attr_valid = 10;
+ fuse_reply(fuse, unique, &out, sizeof(out));
+ return NO_STATUS;
+}
+
+static int handle_lookup(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header *hdr, const char* name)
+{
+ struct node* parent_node;
+ char parent_path[PATH_MAX];
+ char child_path[PATH_MAX];
+ const char* actual_name;
+
+ pthread_mutex_lock(&fuse->lock);
+ parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
+ parent_path, sizeof(parent_path));
+ TRACE("[%d] LOOKUP %s @ %"PRIx64" (%s)\n", handler->token, name, hdr->nodeid,
+ parent_node ? parent_node->name : "?");
+ pthread_mutex_unlock(&fuse->lock);
+
+ if (!parent_node || !(actual_name = find_file_within(parent_path, name,
+ child_path, sizeof(child_path), 1))) {
+ return -ENOENT;
+ }
+ if (!check_caller_access_to_name(fuse, hdr, parent_node, name, R_OK, false)) {
+ return -EACCES;
+ }
+
+ return fuse_reply_entry(fuse, hdr->unique, parent_node, name, actual_name, child_path);
+}
+
+static int handle_forget(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header *hdr, const struct fuse_forget_in *req)
+{
+ struct node* node;
+
+ pthread_mutex_lock(&fuse->lock);
+ node = lookup_node_by_id_locked(fuse, hdr->nodeid);
+ TRACE("[%d] FORGET #%"PRIu64" @ %"PRIx64" (%s)\n", handler->token, req->nlookup,
+ hdr->nodeid, node ? node->name : "?");
+ if (node) {
+ __u64 n = req->nlookup;
+ while (n--) {
+ release_node_locked(node);
+ }
+ }
+ pthread_mutex_unlock(&fuse->lock);
+ return NO_STATUS; /* no reply */
+}
+
+static int handle_getattr(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header *hdr, const struct fuse_getattr_in *req)
+{
+ struct node* node;
+ char path[PATH_MAX];
+
+ pthread_mutex_lock(&fuse->lock);
+ node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path));
+ TRACE("[%d] GETATTR flags=%x fh=%"PRIx64" @ %"PRIx64" (%s)\n", handler->token,
+ req->getattr_flags, req->fh, hdr->nodeid, node ? node->name : "?");
+ pthread_mutex_unlock(&fuse->lock);
+
+ if (!node) {
+ return -ENOENT;
+ }
+ if (!check_caller_access_to_node(fuse, hdr, node, R_OK, false)) {
+ return -EACCES;
+ }
+
+ return fuse_reply_attr(fuse, hdr->unique, node, path);
+}
+
+static int handle_setattr(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header *hdr, const struct fuse_setattr_in *req)
+{
+ bool has_rw;
+ struct node* node;
+ char path[PATH_MAX];
+ struct timespec times[2];
+
+ pthread_mutex_lock(&fuse->lock);
+ has_rw = get_caller_has_rw_locked(fuse, hdr);
+ node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path));
+ TRACE("[%d] SETATTR fh=%"PRIx64" valid=%x @ %"PRIx64" (%s)\n", handler->token,
+ req->fh, req->valid, hdr->nodeid, node ? node->name : "?");
+ pthread_mutex_unlock(&fuse->lock);
+
+ if (!node) {
+ return -ENOENT;
+ }
+
+ if (!(req->valid & FATTR_FH) &&
+ !check_caller_access_to_node(fuse, hdr, node, W_OK, has_rw)) {
+ return -EACCES;
+ }
+
+ /* XXX: incomplete implementation on purpose.
+ * chmod/chown should NEVER be implemented.*/
+
+ if ((req->valid & FATTR_SIZE) && truncate64(path, req->size) < 0) {
+ return -errno;
+ }
+
+ /* Handle changing atime and mtime. If FATTR_ATIME_and FATTR_ATIME_NOW
+ * are both set, then set it to the current time. Else, set it to the
+ * time specified in the request. Same goes for mtime. Use utimensat(2)
+ * as it allows ATIME and MTIME to be changed independently, and has
+ * nanosecond resolution which fuse also has.
+ */
+ if (req->valid & (FATTR_ATIME | FATTR_MTIME)) {
+ times[0].tv_nsec = UTIME_OMIT;
+ times[1].tv_nsec = UTIME_OMIT;
+ if (req->valid & FATTR_ATIME) {
+ if (req->valid & FATTR_ATIME_NOW) {
+ times[0].tv_nsec = UTIME_NOW;
+ } else {
+ times[0].tv_sec = req->atime;
+ times[0].tv_nsec = req->atimensec;
+ }
+ }
+ if (req->valid & FATTR_MTIME) {
+ if (req->valid & FATTR_MTIME_NOW) {
+ times[1].tv_nsec = UTIME_NOW;
+ } else {
+ times[1].tv_sec = req->mtime;
+ times[1].tv_nsec = req->mtimensec;
+ }
+ }
+ TRACE("[%d] Calling utimensat on %s with atime %ld, mtime=%ld\n",
+ handler->token, path, times[0].tv_sec, times[1].tv_sec);
+ if (utimensat(-1, path, times, 0) < 0) {
+ return -errno;
+ }
+ }
+ return fuse_reply_attr(fuse, hdr->unique, node, path);
+}
+
+static int handle_mknod(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header* hdr, const struct fuse_mknod_in* req, const char* name)
+{
+ bool has_rw;
+ struct node* parent_node;
+ char parent_path[PATH_MAX];
+ char child_path[PATH_MAX];
+ const char* actual_name;
+
+ pthread_mutex_lock(&fuse->lock);
+ has_rw = get_caller_has_rw_locked(fuse, hdr);
+ parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
+ parent_path, sizeof(parent_path));
+ TRACE("[%d] MKNOD %s 0%o @ %"PRIx64" (%s)\n", handler->token,
+ name, req->mode, hdr->nodeid, parent_node ? parent_node->name : "?");
+ pthread_mutex_unlock(&fuse->lock);
+
+ if (!parent_node || !(actual_name = find_file_within(parent_path, name,
+ child_path, sizeof(child_path), 1))) {
+ return -ENOENT;
+ }
+ if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK, has_rw)) {
+ return -EACCES;
+ }
+ __u32 mode = (req->mode & (~0777)) | 0664;
+ if (mknod(child_path, mode, req->rdev) < 0) {
+ return -errno;
+ }
+ return fuse_reply_entry(fuse, hdr->unique, parent_node, name, actual_name, child_path);
+}
+
+static int handle_mkdir(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header* hdr, const struct fuse_mkdir_in* req, const char* name)
+{
+ bool has_rw;
+ struct node* parent_node;
+ char parent_path[PATH_MAX];
+ char child_path[PATH_MAX];
+ const char* actual_name;
+
+ pthread_mutex_lock(&fuse->lock);
+ has_rw = get_caller_has_rw_locked(fuse, hdr);
+ parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
+ parent_path, sizeof(parent_path));
+ TRACE("[%d] MKDIR %s 0%o @ %"PRIx64" (%s)\n", handler->token,
+ name, req->mode, hdr->nodeid, parent_node ? parent_node->name : "?");
+ pthread_mutex_unlock(&fuse->lock);
+
+ if (!parent_node || !(actual_name = find_file_within(parent_path, name,
+ child_path, sizeof(child_path), 1))) {
+ return -ENOENT;
+ }
+ if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK, has_rw)) {
+ return -EACCES;
+ }
+ __u32 mode = (req->mode & (~0777)) | 0775;
+ if (mkdir(child_path, mode) < 0) {
+ return -errno;
+ }
+
+ /* When creating /Android/data and /Android/obb, mark them as .nomedia */
+ if (parent_node->perm == PERM_ANDROID && !strcasecmp(name, "data")) {
+ char nomedia[PATH_MAX];
+ snprintf(nomedia, PATH_MAX, "%s/.nomedia", child_path);
+ if (touch(nomedia, 0664) != 0) {
+ ERROR("Failed to touch(%s): %s\n", nomedia, strerror(errno));
+ return -ENOENT;
+ }
+ }
+ if (parent_node->perm == PERM_ANDROID && !strcasecmp(name, "obb")) {
+ char nomedia[PATH_MAX];
+ snprintf(nomedia, PATH_MAX, "%s/.nomedia", fuse->obbpath);
+ if (touch(nomedia, 0664) != 0) {
+ ERROR("Failed to touch(%s): %s\n", nomedia, strerror(errno));
+ return -ENOENT;
+ }
+ }
+
+ return fuse_reply_entry(fuse, hdr->unique, parent_node, name, actual_name, child_path);
+}
+
+static int handle_unlink(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header* hdr, const char* name)
+{
+ bool has_rw;
+ struct node* parent_node;
+ struct node* child_node;
+ char parent_path[PATH_MAX];
+ char child_path[PATH_MAX];
+
+ pthread_mutex_lock(&fuse->lock);
+ has_rw = get_caller_has_rw_locked(fuse, hdr);
+ parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
+ parent_path, sizeof(parent_path));
+ TRACE("[%d] UNLINK %s @ %"PRIx64" (%s)\n", handler->token,
+ name, hdr->nodeid, parent_node ? parent_node->name : "?");
+ pthread_mutex_unlock(&fuse->lock);
+
+ if (!parent_node || !find_file_within(parent_path, name,
+ child_path, sizeof(child_path), 1)) {
+ return -ENOENT;
+ }
+ if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK, has_rw)) {
+ return -EACCES;
+ }
+ if (unlink(child_path) < 0) {
+ return -errno;
+ }
+ pthread_mutex_lock(&fuse->lock);
+ child_node = lookup_child_by_name_locked(parent_node, name);
+ if (child_node) {
+ child_node->deleted = true;
+ }
+ pthread_mutex_unlock(&fuse->lock);
+ return 0;
+}
+
+static int handle_rmdir(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header* hdr, const char* name)
+{
+ bool has_rw;
+ struct node* child_node;
+ struct node* parent_node;
+ char parent_path[PATH_MAX];
+ char child_path[PATH_MAX];
+
+ pthread_mutex_lock(&fuse->lock);
+ has_rw = get_caller_has_rw_locked(fuse, hdr);
+ parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
+ parent_path, sizeof(parent_path));
+ TRACE("[%d] RMDIR %s @ %"PRIx64" (%s)\n", handler->token,
+ name, hdr->nodeid, parent_node ? parent_node->name : "?");
+ pthread_mutex_unlock(&fuse->lock);
+
+ if (!parent_node || !find_file_within(parent_path, name,
+ child_path, sizeof(child_path), 1)) {
+ return -ENOENT;
+ }
+ if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK, has_rw)) {
+ return -EACCES;
+ }
+ if (rmdir(child_path) < 0) {
+ return -errno;
+ }
+ pthread_mutex_lock(&fuse->lock);
+ child_node = lookup_child_by_name_locked(parent_node, name);
+ if (child_node) {
+ child_node->deleted = true;
+ }
+ pthread_mutex_unlock(&fuse->lock);
+ return 0;
+}
+
+static int handle_rename(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header* hdr, const struct fuse_rename_in* req,
+ const char* old_name, const char* new_name)
+{
+ bool has_rw;
+ struct node* old_parent_node;
+ struct node* new_parent_node;
+ struct node* child_node;
+ char old_parent_path[PATH_MAX];
+ char new_parent_path[PATH_MAX];
+ char old_child_path[PATH_MAX];
+ char new_child_path[PATH_MAX];
+ const char* new_actual_name;
+ int res;
+
+ pthread_mutex_lock(&fuse->lock);
+ has_rw = get_caller_has_rw_locked(fuse, hdr);
+ old_parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
+ old_parent_path, sizeof(old_parent_path));
+ new_parent_node = lookup_node_and_path_by_id_locked(fuse, req->newdir,
+ new_parent_path, sizeof(new_parent_path));
+ TRACE("[%d] RENAME %s->%s @ %"PRIx64" (%s) -> %"PRIx64" (%s)\n", handler->token,
+ old_name, new_name,
+ hdr->nodeid, old_parent_node ? old_parent_node->name : "?",
+ req->newdir, new_parent_node ? new_parent_node->name : "?");
+ if (!old_parent_node || !new_parent_node) {
+ res = -ENOENT;
+ goto lookup_error;
+ }
+ if (!check_caller_access_to_name(fuse, hdr, old_parent_node, old_name, W_OK, has_rw)) {
+ res = -EACCES;
+ goto lookup_error;
+ }
+ if (!check_caller_access_to_name(fuse, hdr, new_parent_node, new_name, W_OK, has_rw)) {
+ res = -EACCES;
+ goto lookup_error;
+ }
+ child_node = lookup_child_by_name_locked(old_parent_node, old_name);
+ if (!child_node || get_node_path_locked(child_node,
+ old_child_path, sizeof(old_child_path)) < 0) {
+ res = -ENOENT;
+ goto lookup_error;
+ }
+ acquire_node_locked(child_node);
+ pthread_mutex_unlock(&fuse->lock);
+
+ /* Special case for renaming a file where destination is same path
+ * differing only by case. In this case we don't want to look for a case
+ * insensitive match. This allows commands like "mv foo FOO" to work as expected.
+ */
+ int search = old_parent_node != new_parent_node
+ || strcasecmp(old_name, new_name);
+ if (!(new_actual_name = find_file_within(new_parent_path, new_name,
+ new_child_path, sizeof(new_child_path), search))) {
+ res = -ENOENT;
+ goto io_error;
+ }
+
+ TRACE("[%d] RENAME %s->%s\n", handler->token, old_child_path, new_child_path);
+ res = rename(old_child_path, new_child_path);
+ if (res < 0) {
+ res = -errno;
+ goto io_error;
+ }
+
+ pthread_mutex_lock(&fuse->lock);
+ res = rename_node_locked(child_node, new_name, new_actual_name);
+ if (!res) {
+ remove_node_from_parent_locked(child_node);
+ add_node_to_parent_locked(child_node, new_parent_node);
+ }
+ goto done;
+
+io_error:
+ pthread_mutex_lock(&fuse->lock);
+done:
+ release_node_locked(child_node);
+lookup_error:
+ pthread_mutex_unlock(&fuse->lock);
+ return res;
+}
+
+static int open_flags_to_access_mode(int open_flags) {
+ if ((open_flags & O_ACCMODE) == O_RDONLY) {
+ return R_OK;
+ } else if ((open_flags & O_ACCMODE) == O_WRONLY) {
+ return W_OK;
+ } else {
+ /* Probably O_RDRW, but treat as default to be safe */
+ return R_OK | W_OK;
+ }
+}
+
+static int handle_open(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header* hdr, const struct fuse_open_in* req)
+{
+ bool has_rw;
+ struct node* node;
+ char path[PATH_MAX];
+ struct fuse_open_out out;
+ struct handle *h;
+
+ pthread_mutex_lock(&fuse->lock);
+ has_rw = get_caller_has_rw_locked(fuse, hdr);
+ node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path));
+ TRACE("[%d] OPEN 0%o @ %"PRIx64" (%s)\n", handler->token,
+ req->flags, hdr->nodeid, node ? node->name : "?");
+ pthread_mutex_unlock(&fuse->lock);
+
+ if (!node) {
+ return -ENOENT;
+ }
+ if (!check_caller_access_to_node(fuse, hdr, node,
+ open_flags_to_access_mode(req->flags), has_rw)) {
+ return -EACCES;
+ }
+ h = malloc(sizeof(*h));
+ if (!h) {
+ return -ENOMEM;
+ }
+ TRACE("[%d] OPEN %s\n", handler->token, path);
+ h->fd = open(path, req->flags);
+ if (h->fd < 0) {
+ free(h);
+ return -errno;
+ }
+ out.fh = ptr_to_id(h);
+ out.open_flags = 0;
+ out.padding = 0;
+ fuse_reply(fuse, hdr->unique, &out, sizeof(out));
+ return NO_STATUS;
+}
+
+static int handle_read(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header* hdr, const struct fuse_read_in* req)
+{
+ struct handle *h = id_to_ptr(req->fh);
+ __u64 unique = hdr->unique;
+ __u32 size = req->size;
+ __u64 offset = req->offset;
+ int res;
+ __u8 *read_buffer = (__u8 *) ((uintptr_t)(handler->read_buffer + PAGESIZE) & ~((uintptr_t)PAGESIZE-1));
+
+ /* Don't access any other fields of hdr or req beyond this point, the read buffer
+ * overlaps the request buffer and will clobber data in the request. This
+ * saves us 128KB per request handler thread at the cost of this scary comment. */
+
+ TRACE("[%d] READ %p(%d) %u@%"PRIu64"\n", handler->token,
+ h, h->fd, size, (uint64_t) offset);
+ if (size > MAX_READ) {
+ return -EINVAL;
+ }
+ res = pread64(h->fd, read_buffer, size, offset);
+ if (res < 0) {
+ return -errno;
+ }
+ fuse_reply(fuse, unique, read_buffer, res);
+ return NO_STATUS;
+}
+
+static int handle_write(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header* hdr, const struct fuse_write_in* req,
+ const void* buffer)
+{
+ struct fuse_write_out out;
+ struct handle *h = id_to_ptr(req->fh);
+ int res;
+ __u8 aligned_buffer[req->size] __attribute__((__aligned__(PAGESIZE)));
+
+ if (req->flags & O_DIRECT) {
+ memcpy(aligned_buffer, buffer, req->size);
+ buffer = (const __u8*) aligned_buffer;
+ }
+
+ TRACE("[%d] WRITE %p(%d) %u@%"PRIu64"\n", handler->token,
+ h, h->fd, req->size, req->offset);
+ res = pwrite64(h->fd, buffer, req->size, req->offset);
+ if (res < 0) {
+ return -errno;
+ }
+ out.size = res;
+ out.padding = 0;
+ fuse_reply(fuse, hdr->unique, &out, sizeof(out));
+ return NO_STATUS;
+}
+
+static int handle_statfs(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header* hdr)
+{
+ char path[PATH_MAX];
+ struct statfs stat;
+ struct fuse_statfs_out out;
+ int res;
+
+ pthread_mutex_lock(&fuse->lock);
+ TRACE("[%d] STATFS\n", handler->token);
+ res = get_node_path_locked(&fuse->root, path, sizeof(path));
+ pthread_mutex_unlock(&fuse->lock);
+ if (res < 0) {
+ return -ENOENT;
+ }
+ if (statfs(fuse->root.name, &stat) < 0) {
+ return -errno;
+ }
+ memset(&out, 0, sizeof(out));
+ out.st.blocks = stat.f_blocks;
+ out.st.bfree = stat.f_bfree;
+ out.st.bavail = stat.f_bavail;
+ out.st.files = stat.f_files;
+ out.st.ffree = stat.f_ffree;
+ out.st.bsize = stat.f_bsize;
+ out.st.namelen = stat.f_namelen;
+ out.st.frsize = stat.f_frsize;
+ fuse_reply(fuse, hdr->unique, &out, sizeof(out));
+ return NO_STATUS;
+}
+
+static int handle_release(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header* hdr, const struct fuse_release_in* req)
+{
+ struct handle *h = id_to_ptr(req->fh);
+
+ TRACE("[%d] RELEASE %p(%d)\n", handler->token, h, h->fd);
+ close(h->fd);
+ free(h);
+ return 0;
+}
+
+static int handle_fsync(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header* hdr, const struct fuse_fsync_in* req)
+{
+ bool is_dir = (hdr->opcode == FUSE_FSYNCDIR);
+ bool is_data_sync = req->fsync_flags & 1;
+
+ int fd = -1;
+ if (is_dir) {
+ struct dirhandle *dh = id_to_ptr(req->fh);
+ fd = dirfd(dh->d);
+ } else {
+ struct handle *h = id_to_ptr(req->fh);
+ fd = h->fd;
+ }
+
+ TRACE("[%d] %s %p(%d) is_data_sync=%d\n", handler->token,
+ is_dir ? "FSYNCDIR" : "FSYNC",
+ id_to_ptr(req->fh), fd, is_data_sync);
+ int res = is_data_sync ? fdatasync(fd) : fsync(fd);
+ if (res == -1) {
+ return -errno;
+ }
+ return 0;
+}
+
+static int handle_flush(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header* hdr)
+{
+ TRACE("[%d] FLUSH\n", handler->token);
+ return 0;
+}
+
+static int handle_opendir(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header* hdr, const struct fuse_open_in* req)
+{
+ struct node* node;
+ char path[PATH_MAX];
+ struct fuse_open_out out;
+ struct dirhandle *h;
+
+ pthread_mutex_lock(&fuse->lock);
+ node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path));
+ TRACE("[%d] OPENDIR @ %"PRIx64" (%s)\n", handler->token,
+ hdr->nodeid, node ? node->name : "?");
+ pthread_mutex_unlock(&fuse->lock);
+
+ if (!node) {
+ return -ENOENT;
+ }
+ if (!check_caller_access_to_node(fuse, hdr, node, R_OK, false)) {
+ return -EACCES;
+ }
+ h = malloc(sizeof(*h));
+ if (!h) {
+ return -ENOMEM;
+ }
+ TRACE("[%d] OPENDIR %s\n", handler->token, path);
+ h->d = opendir(path);
+ if (!h->d) {
+ free(h);
+ return -errno;
+ }
+ out.fh = ptr_to_id(h);
+ out.open_flags = 0;
+ out.padding = 0;
+ fuse_reply(fuse, hdr->unique, &out, sizeof(out));
+ return NO_STATUS;
+}
+
+static int handle_readdir(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header* hdr, const struct fuse_read_in* req)
+{
+ char buffer[8192];
+ struct fuse_dirent *fde = (struct fuse_dirent*) buffer;
+ struct dirent *de;
+ struct dirhandle *h = id_to_ptr(req->fh);
+
+ TRACE("[%d] READDIR %p\n", handler->token, h);
+ if (req->offset == 0) {
+ /* rewinddir() might have been called above us, so rewind here too */
+ TRACE("[%d] calling rewinddir()\n", handler->token);
+ rewinddir(h->d);
+ }
+ de = readdir(h->d);
+ if (!de) {
+ return 0;
+ }
+ fde->ino = FUSE_UNKNOWN_INO;
+ /* increment the offset so we can detect when rewinddir() seeks back to the beginning */
+ fde->off = req->offset + 1;
+ fde->type = de->d_type;
+ fde->namelen = strlen(de->d_name);
+ memcpy(fde->name, de->d_name, fde->namelen + 1);
+ fuse_reply(fuse, hdr->unique, fde,
+ FUSE_DIRENT_ALIGN(sizeof(struct fuse_dirent) + fde->namelen));
+ return NO_STATUS;
+}
+
+static int handle_releasedir(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header* hdr, const struct fuse_release_in* req)
+{
+ struct dirhandle *h = id_to_ptr(req->fh);
+
+ TRACE("[%d] RELEASEDIR %p\n", handler->token, h);
+ closedir(h->d);
+ free(h);
+ return 0;
+}
+
+static int handle_init(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header* hdr, const struct fuse_init_in* req)
+{
+ struct fuse_init_out out;
+ size_t fuse_struct_size;
+
+ TRACE("[%d] INIT ver=%d.%d maxread=%d flags=%x\n",
+ handler->token, req->major, req->minor, req->max_readahead, req->flags);
+
+ /* Kernel 2.6.16 is the first stable kernel with struct fuse_init_out
+ * defined (fuse version 7.6). The structure is the same from 7.6 through
+ * 7.22. Beginning with 7.23, the structure increased in size and added
+ * new parameters.
+ */
+ if (req->major != FUSE_KERNEL_VERSION || req->minor < 6) {
+ ERROR("Fuse kernel version mismatch: Kernel version %d.%d, Expected at least %d.6",
+ req->major, req->minor, FUSE_KERNEL_VERSION);
+ return -1;
+ }
+
+ out.minor = MIN(req->minor, FUSE_KERNEL_MINOR_VERSION);
+ fuse_struct_size = sizeof(out);
+#if defined(FUSE_COMPAT_22_INIT_OUT_SIZE)
+ /* FUSE_KERNEL_VERSION >= 23. */
+
+ /* If the kernel only works on minor revs older than or equal to 22,
+ * then use the older structure size since this code only uses the 7.22
+ * version of the structure. */
+ if (req->minor <= 22) {
+ fuse_struct_size = FUSE_COMPAT_22_INIT_OUT_SIZE;
+ }
+#endif
+
+ out.major = FUSE_KERNEL_VERSION;
+ out.max_readahead = req->max_readahead;
+ out.flags = FUSE_ATOMIC_O_TRUNC | FUSE_BIG_WRITES;
+ out.max_background = 32;
+ out.congestion_threshold = 32;
+ out.max_write = MAX_WRITE;
+ fuse_reply(fuse, hdr->unique, &out, fuse_struct_size);
+ return NO_STATUS;
+}
+
+static int handle_fuse_request(struct fuse *fuse, struct fuse_handler* handler,
+ const struct fuse_in_header *hdr, const void *data, size_t data_len)
+{
+ switch (hdr->opcode) {
+ case FUSE_LOOKUP: { /* bytez[] -> entry_out */
+ const char* name = data;
+ return handle_lookup(fuse, handler, hdr, name);
+ }
+
+ case FUSE_FORGET: {
+ const struct fuse_forget_in *req = data;
+ return handle_forget(fuse, handler, hdr, req);
+ }
+
+ case FUSE_GETATTR: { /* getattr_in -> attr_out */
+ const struct fuse_getattr_in *req = data;
+ return handle_getattr(fuse, handler, hdr, req);
+ }
+
+ case FUSE_SETATTR: { /* setattr_in -> attr_out */
+ const struct fuse_setattr_in *req = data;
+ return handle_setattr(fuse, handler, hdr, req);
+ }
+
+// case FUSE_READLINK:
+// case FUSE_SYMLINK:
+ case FUSE_MKNOD: { /* mknod_in, bytez[] -> entry_out */
+ const struct fuse_mknod_in *req = data;
+ const char *name = ((const char*) data) + sizeof(*req);
+ return handle_mknod(fuse, handler, hdr, req, name);
+ }
+
+ case FUSE_MKDIR: { /* mkdir_in, bytez[] -> entry_out */
+ const struct fuse_mkdir_in *req = data;
+ const char *name = ((const char*) data) + sizeof(*req);
+ return handle_mkdir(fuse, handler, hdr, req, name);
+ }
+
+ case FUSE_UNLINK: { /* bytez[] -> */
+ const char* name = data;
+ return handle_unlink(fuse, handler, hdr, name);
+ }
+
+ case FUSE_RMDIR: { /* bytez[] -> */
+ const char* name = data;
+ return handle_rmdir(fuse, handler, hdr, name);
+ }
+
+ case FUSE_RENAME: { /* rename_in, oldname, newname -> */
+ const struct fuse_rename_in *req = data;
+ const char *old_name = ((const char*) data) + sizeof(*req);
+ const char *new_name = old_name + strlen(old_name) + 1;
+ return handle_rename(fuse, handler, hdr, req, old_name, new_name);
+ }
+
+// case FUSE_LINK:
+ case FUSE_OPEN: { /* open_in -> open_out */
+ const struct fuse_open_in *req = data;
+ return handle_open(fuse, handler, hdr, req);
+ }
+
+ case FUSE_READ: { /* read_in -> byte[] */
+ const struct fuse_read_in *req = data;
+ return handle_read(fuse, handler, hdr, req);
+ }
+
+ case FUSE_WRITE: { /* write_in, byte[write_in.size] -> write_out */
+ const struct fuse_write_in *req = data;
+ const void* buffer = (const __u8*)data + sizeof(*req);
+ return handle_write(fuse, handler, hdr, req, buffer);
+ }
+
+ case FUSE_STATFS: { /* getattr_in -> attr_out */
+ return handle_statfs(fuse, handler, hdr);
+ }
+
+ case FUSE_RELEASE: { /* release_in -> */
+ const struct fuse_release_in *req = data;
+ return handle_release(fuse, handler, hdr, req);
+ }
+
+ case FUSE_FSYNC:
+ case FUSE_FSYNCDIR: {
+ const struct fuse_fsync_in *req = data;
+ return handle_fsync(fuse, handler, hdr, req);
+ }
+
+// case FUSE_SETXATTR:
+// case FUSE_GETXATTR:
+// case FUSE_LISTXATTR:
+// case FUSE_REMOVEXATTR:
+ case FUSE_FLUSH: {
+ return handle_flush(fuse, handler, hdr);
+ }
+
+ case FUSE_OPENDIR: { /* open_in -> open_out */
+ const struct fuse_open_in *req = data;
+ return handle_opendir(fuse, handler, hdr, req);
+ }
+
+ case FUSE_READDIR: {
+ const struct fuse_read_in *req = data;
+ return handle_readdir(fuse, handler, hdr, req);
+ }
+
+ case FUSE_RELEASEDIR: { /* release_in -> */
+ const struct fuse_release_in *req = data;
+ return handle_releasedir(fuse, handler, hdr, req);
+ }
+
+ case FUSE_INIT: { /* init_in -> init_out */
+ const struct fuse_init_in *req = data;
+ return handle_init(fuse, handler, hdr, req);
+ }
+
+ default: {
+ TRACE("[%d] NOTIMPL op=%d uniq=%"PRIx64" nid=%"PRIx64"\n",
+ handler->token, hdr->opcode, hdr->unique, hdr->nodeid);
+ return -ENOSYS;
+ }
+ }
+}
+
+static void handle_fuse_requests(struct fuse_handler* handler)
+{
+ struct fuse* fuse = handler->fuse;
+ for (;;) {
+ ssize_t len = read(fuse->fd,
+ handler->request_buffer, sizeof(handler->request_buffer));
+ if (len < 0) {
+ if (errno != EINTR) {
+ ERROR("[%d] handle_fuse_requests: errno=%d\n", handler->token, errno);
+ }
+ continue;
+ }
+
+ if ((size_t)len < sizeof(struct fuse_in_header)) {
+ ERROR("[%d] request too short: len=%zu\n", handler->token, (size_t)len);
+ continue;
+ }
+
+ const struct fuse_in_header *hdr = (void*)handler->request_buffer;
+ if (hdr->len != (size_t)len) {
+ ERROR("[%d] malformed header: len=%zu, hdr->len=%u\n",
+ handler->token, (size_t)len, hdr->len);
+ continue;
+ }
+
+ const void *data = handler->request_buffer + sizeof(struct fuse_in_header);
+ size_t data_len = len - sizeof(struct fuse_in_header);
+ __u64 unique = hdr->unique;
+ int res = handle_fuse_request(fuse, handler, hdr, data, data_len);
+
+ /* We do not access the request again after this point because the underlying
+ * buffer storage may have been reused while processing the request. */
+
+ if (res != NO_STATUS) {
+ if (res) {
+ TRACE("[%d] ERROR %d\n", handler->token, res);
+ }
+ fuse_status(fuse, unique, res);
+ }
+ }
+}
+
+static void* start_handler(void* data)
+{
+ struct fuse_handler* handler = data;
+ handle_fuse_requests(handler);
+ return NULL;
+}
+
+static bool remove_str_to_int(void *key, void *value, void *context) {
+ Hashmap* map = context;
+ hashmapRemove(map, key);
+ free(key);
+ return true;
+}
+
+static bool remove_int_to_null(void *key, void *value, void *context) {
+ Hashmap* map = context;
+ hashmapRemove(map, key);
+ return true;
+}
+
+static int read_package_list(struct fuse *fuse) {
+ pthread_mutex_lock(&fuse->lock);
+
+ hashmapForEach(fuse->package_to_appid, remove_str_to_int, fuse->package_to_appid);
+ hashmapForEach(fuse->appid_with_rw, remove_int_to_null, fuse->appid_with_rw);
+
+ FILE* file = fopen(kPackagesListFile, "r");
+ if (!file) {
+ ERROR("failed to open package list: %s\n", strerror(errno));
+ pthread_mutex_unlock(&fuse->lock);
+ return -1;
+ }
+
+ char buf[512];
+ while (fgets(buf, sizeof(buf), file) != NULL) {
+ char package_name[512];
+ int appid;
+ char gids[512];
+
+ if (sscanf(buf, "%s %d %*d %*s %*s %s", package_name, &appid, gids) == 3) {
+ char* package_name_dup = strdup(package_name);
+ hashmapPut(fuse->package_to_appid, package_name_dup, (void*) (uintptr_t) appid);
+
+ char* token = strtok(gids, ",");
+ while (token != NULL) {
+ if (strtoul(token, NULL, 10) == fuse->write_gid) {
+ hashmapPut(fuse->appid_with_rw, (void*) (uintptr_t) appid, (void*) (uintptr_t) 1);
+ break;
+ }
+ token = strtok(NULL, ",");
+ }
+ }
+ }
+
+ TRACE("read_package_list: found %zu packages, %zu with write_gid\n",
+ hashmapSize(fuse->package_to_appid),
+ hashmapSize(fuse->appid_with_rw));
+ fclose(file);
+ pthread_mutex_unlock(&fuse->lock);
+ return 0;
+}
+
+static void watch_package_list(struct fuse* fuse) {
+ struct inotify_event *event;
+ char event_buf[512];
+
+ int nfd = inotify_init();
+ if (nfd < 0) {
+ ERROR("inotify_init failed: %s\n", strerror(errno));
+ return;
+ }
+
+ bool active = false;
+ while (1) {
+ if (!active) {
+ int res = inotify_add_watch(nfd, kPackagesListFile, IN_DELETE_SELF);
+ if (res == -1) {
+ if (errno == ENOENT || errno == EACCES) {
+ /* Framework may not have created yet, sleep and retry */
+ ERROR("missing packages.list; retrying\n");
+ sleep(3);
+ continue;
+ } else {
+ ERROR("inotify_add_watch failed: %s\n", strerror(errno));
+ return;
+ }
+ }
+
+ /* Watch above will tell us about any future changes, so
+ * read the current state. */
+ if (read_package_list(fuse) == -1) {
+ ERROR("read_package_list failed: %s\n", strerror(errno));
+ return;
+ }
+ active = true;
+ }
+
+ int event_pos = 0;
+ int res = read(nfd, event_buf, sizeof(event_buf));
+ if (res < (int) sizeof(*event)) {
+ if (errno == EINTR)
+ continue;
+ ERROR("failed to read inotify event: %s\n", strerror(errno));
+ return;
+ }
+
+ while (res >= (int) sizeof(*event)) {
+ int event_size;
+ event = (struct inotify_event *) (event_buf + event_pos);
+
+ TRACE("inotify event: %08x\n", event->mask);
+ if ((event->mask & IN_IGNORED) == IN_IGNORED) {
+ /* Previously watched file was deleted, probably due to move
+ * that swapped in new data; re-arm the watch and read. */
+ active = false;
+ }
+
+ event_size = sizeof(*event) + event->len;
+ res -= event_size;
+ event_pos += event_size;
+ }
+ }
+}
+
+static int ignite_fuse(struct fuse* fuse, int num_threads)
+{
+ struct fuse_handler* handlers;
+ int i;
+
+ handlers = malloc(num_threads * sizeof(struct fuse_handler));
+ if (!handlers) {
+ ERROR("cannot allocate storage for threads\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < num_threads; i++) {
+ handlers[i].fuse = fuse;
+ handlers[i].token = i;
+ }
+
+ /* When deriving permissions, this thread is used to process inotify events,
+ * otherwise it becomes one of the FUSE handlers. */
+ i = (fuse->derive == DERIVE_NONE) ? 1 : 0;
+ for (; i < num_threads; i++) {
+ pthread_t thread;
+ int res = pthread_create(&thread, NULL, start_handler, &handlers[i]);
+ if (res) {
+ ERROR("failed to start thread #%d, error=%d\n", i, res);
+ goto quit;
+ }
+ }
+
+ if (fuse->derive == DERIVE_NONE) {
+ handle_fuse_requests(&handlers[0]);
+ } else {
+ watch_package_list(fuse);
+ }
+
+ ERROR("terminated prematurely\n");
+
+ /* don't bother killing all of the other threads or freeing anything,
+ * should never get here anyhow */
+quit:
+ exit(1);
+}
+
+static int usage()
+{
+ ERROR("usage: sdcard [OPTIONS] <source_path> <dest_path>\n"
+ " -u: specify UID to run as\n"
+ " -g: specify GID to run as\n"
+ " -w: specify GID required to write (default sdcard_rw, requires -d or -l)\n"
+ " -t: specify number of threads to use (default %d)\n"
+ " -d: derive file permissions based on path\n"
+ " -l: derive file permissions based on legacy internal layout\n"
+ " -s: split derived permissions for pics, av\n"
+ "\n", DEFAULT_NUM_THREADS);
+ return 1;
+}
+
+static int run(const char* source_path, const char* dest_path, uid_t uid,
+ gid_t gid, gid_t write_gid, int num_threads, derive_t derive,
+ bool split_perms) {
+ int fd;
+ char opts[256];
+ int res;
+ struct fuse fuse;
+
+ /* cleanup from previous instance, if necessary */
+ umount2(dest_path, MNT_DETACH);
+
+ fd = open("/dev/fuse", O_RDWR);
+ if (fd < 0){
+ ERROR("cannot open fuse device: %s\n", strerror(errno));
+ return -1;
+ }
+
+ snprintf(opts, sizeof(opts),
+ "fd=%i,rootmode=40000,default_permissions,allow_other,user_id=%d,group_id=%d",
+ fd, uid, gid);
+
+ res = mount("/dev/fuse", dest_path, "fuse", MS_NOSUID | MS_NODEV | MS_NOEXEC |
+ MS_NOATIME, opts);
+ if (res < 0) {
+ ERROR("cannot mount fuse filesystem: %s\n", strerror(errno));
+ goto error;
+ }
+
+ res = setgroups(sizeof(kGroups) / sizeof(kGroups[0]), kGroups);
+ if (res < 0) {
+ ERROR("cannot setgroups: %s\n", strerror(errno));
+ goto error;
+ }
+
+ res = setgid(gid);
+ if (res < 0) {
+ ERROR("cannot setgid: %s\n", strerror(errno));
+ goto error;
+ }
+
+ res = setuid(uid);
+ if (res < 0) {
+ ERROR("cannot setuid: %s\n", strerror(errno));
+ goto error;
+ }
+
+ fuse_init(&fuse, fd, source_path, write_gid, derive, split_perms);
+
+ umask(0);
+ res = ignite_fuse(&fuse, num_threads);
+
+ /* we do not attempt to umount the file system here because we are no longer
+ * running as the root user */
+
+error:
+ close(fd);
+ return res;
+}
+
+int main(int argc, char **argv)
+{
+ int res;
+ const char *source_path = NULL;
+ const char *dest_path = NULL;
+ uid_t uid = 0;
+ gid_t gid = 0;
+ gid_t write_gid = AID_SDCARD_RW;
+ int num_threads = DEFAULT_NUM_THREADS;
+ derive_t derive = DERIVE_NONE;
+ bool split_perms = false;
+ int i;
+ struct rlimit rlim;
+ int fs_version;
+
+ int opt;
+ while ((opt = getopt(argc, argv, "u:g:w:t:dls")) != -1) {
+ switch (opt) {
+ case 'u':
+ uid = strtoul(optarg, NULL, 10);
+ break;
+ case 'g':
+ gid = strtoul(optarg, NULL, 10);
+ break;
+ case 'w':
+ write_gid = strtoul(optarg, NULL, 10);
+ break;
+ case 't':
+ num_threads = strtoul(optarg, NULL, 10);
+ break;
+ case 'd':
+ derive = DERIVE_UNIFIED;
+ break;
+ case 'l':
+ derive = DERIVE_LEGACY;
+ break;
+ case 's':
+ split_perms = true;
+ break;
+ case '?':
+ default:
+ return usage();
+ }
+ }
+
+ for (i = optind; i < argc; i++) {
+ char* arg = argv[i];
+ if (!source_path) {
+ source_path = arg;
+ } else if (!dest_path) {
+ dest_path = arg;
+ } else if (!uid) {
+ uid = strtoul(arg, NULL, 10);
+ } else if (!gid) {
+ gid = strtoul(arg, NULL, 10);
+ } else {
+ ERROR("too many arguments\n");
+ return usage();
+ }
+ }
+
+ if (!source_path) {
+ ERROR("no source path specified\n");
+ return usage();
+ }
+ if (!dest_path) {
+ ERROR("no dest path specified\n");
+ return usage();
+ }
+ if (!uid || !gid) {
+ ERROR("uid and gid must be nonzero\n");
+ return usage();
+ }
+ if (num_threads < 1) {
+ ERROR("number of threads must be at least 1\n");
+ return usage();
+ }
+ if (split_perms && derive == DERIVE_NONE) {
+ ERROR("cannot split permissions without deriving\n");
+ return usage();
+ }
+
+ rlim.rlim_cur = 8192;
+ rlim.rlim_max = 8192;
+ if (setrlimit(RLIMIT_NOFILE, &rlim)) {
+ ERROR("Error setting RLIMIT_NOFILE, errno = %d\n", errno);
+ }
+
+ while ((fs_read_atomic_int("/data/.layout_version", &fs_version) == -1) || (fs_version < 3)) {
+ ERROR("installd fs upgrade not yet complete. Waiting...\n");
+ sleep(1);
+ }
+
+ res = run(source_path, dest_path, uid, gid, write_gid, num_threads, derive, split_perms);
+ return res < 0 ? 1 : 0;
+}
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
new file mode 100644
index 0000000..273b263
--- /dev/null
+++ b/toolbox/Android.mk
@@ -0,0 +1,124 @@
+LOCAL_PATH:= $(call my-dir)
+
+
+common_cflags := \
+ -Werror -Wno-unused-parameter \
+ -I$(LOCAL_PATH)/upstream-netbsd/include/ \
+ -include bsd-compatibility.h \
+
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+ upstream-netbsd/bin/dd/args.c \
+ upstream-netbsd/bin/dd/conv.c \
+ upstream-netbsd/bin/dd/dd.c \
+ upstream-netbsd/bin/dd/dd_hostops.c \
+ upstream-netbsd/bin/dd/misc.c \
+ upstream-netbsd/bin/dd/position.c \
+ upstream-netbsd/lib/libc/gen/getbsize.c \
+ upstream-netbsd/lib/libc/gen/humanize_number.c \
+ upstream-netbsd/lib/libc/stdlib/strsuftoll.c \
+ upstream-netbsd/lib/libc/string/swab.c \
+ upstream-netbsd/lib/libutil/raise_default_signal.c
+LOCAL_CFLAGS += $(common_cflags) -Dmain=dd_main -DNO_CONV
+LOCAL_MODULE := libtoolbox_dd
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := upstream-netbsd/usr.bin/du/du.c
+LOCAL_CFLAGS += $(common_cflags) -Dmain=du_main
+LOCAL_MODULE := libtoolbox_du
+include $(BUILD_STATIC_LIBRARY)
+
+
+include $(CLEAR_VARS)
+
+BSD_TOOLS := \
+ dd \
+ du \
+
+OUR_TOOLS := \
+ df \
+ getevent \
+ iftop \
+ ioctl \
+ ionice \
+ log \
+ ls \
+ lsof \
+ nandread \
+ newfs_msdos \
+ ps \
+ prlimit \
+ renice \
+ sendevent \
+ start \
+ stop \
+ top \
+ uptime \
+ watchprops \
+
+ALL_TOOLS = $(BSD_TOOLS) $(OUR_TOOLS)
+
+LOCAL_SRC_FILES := \
+ start_stop.cpp \
+ toolbox.c \
+ $(patsubst %,%.c,$(OUR_TOOLS)) \
+
+LOCAL_CFLAGS += $(common_cflags)
+LOCAL_CONLYFLAGS += -std=gnu99
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libselinux \
+
+LOCAL_WHOLE_STATIC_LIBRARIES := $(patsubst %,libtoolbox_%,$(BSD_TOOLS))
+
+LOCAL_MODULE := toolbox
+
+# Install the symlinks.
+LOCAL_POST_INSTALL_CMD := $(hide) $(foreach t,$(ALL_TOOLS),ln -sf toolbox $(TARGET_OUT)/bin/$(t);)
+
+# Including this will define $(intermediates).
+#
+include $(BUILD_EXECUTABLE)
+
+$(LOCAL_PATH)/toolbox.c: $(intermediates)/tools.h
+
+TOOLS_H := $(intermediates)/tools.h
+$(TOOLS_H): PRIVATE_TOOLS := $(ALL_TOOLS)
+$(TOOLS_H): PRIVATE_CUSTOM_TOOL = echo "/* file generated automatically */" > $@ ; for t in $(PRIVATE_TOOLS) ; do echo "TOOL($$t)" >> $@ ; done
+$(TOOLS_H): $(LOCAL_PATH)/Android.mk
+$(TOOLS_H):
+ $(transform-generated-source)
+
+$(LOCAL_PATH)/getevent.c: $(intermediates)/input.h-labels.h
+
+INPUT_H_LABELS_H := $(intermediates)/input.h-labels.h
+$(INPUT_H_LABELS_H): PRIVATE_LOCAL_PATH := $(LOCAL_PATH)
+$(INPUT_H_LABELS_H): PRIVATE_CUSTOM_TOOL = $(PRIVATE_LOCAL_PATH)/generate-input.h-labels.py > $@
+$(INPUT_H_LABELS_H): $(LOCAL_PATH)/Android.mk $(LOCAL_PATH)/generate-input.h-labels.py
+$(INPUT_H_LABELS_H):
+ $(transform-generated-source)
+
+# We only want 'r' on userdebug and eng builds.
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := r.c
+LOCAL_CFLAGS += $(common_cflags)
+LOCAL_MODULE := r
+LOCAL_MODULE_TAGS := debug
+include $(BUILD_EXECUTABLE)
+
+
+# We build BSD grep separately, so it can provide egrep and fgrep too.
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+ upstream-netbsd/usr.bin/grep/fastgrep.c \
+ upstream-netbsd/usr.bin/grep/file.c \
+ upstream-netbsd/usr.bin/grep/grep.c \
+ upstream-netbsd/usr.bin/grep/queue.c \
+ upstream-netbsd/usr.bin/grep/util.c
+LOCAL_CFLAGS += $(common_cflags)
+LOCAL_MODULE := grep
+LOCAL_POST_INSTALL_CMD := $(hide) $(foreach t,egrep fgrep,ln -sf grep $(TARGET_OUT)/bin/$(t);)
+include $(BUILD_EXECUTABLE)
diff --git a/toolbox/MODULE_LICENSE_BSD b/toolbox/MODULE_LICENSE_BSD
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/toolbox/MODULE_LICENSE_BSD
diff --git a/toolbox/NOTICE b/toolbox/NOTICE
new file mode 100644
index 0000000..e9ab58d
--- /dev/null
+++ b/toolbox/NOTICE
@@ -0,0 +1,987 @@
+Copyright (C) 2010 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+-------------------------------------------------------------------
+
+Copyright (C) 2014, The Android Open Source Project
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
+Copyright (c) 1987, 1993
+ The Regents of the University of California. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. Neither the name of the University nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
+Copyright (c) 1987, 1993, 1994
+ The Regents of the University of California. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. Neither the name of the University nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
+Copyright (c) 1988, 1993
+ The Regents of the University of California. All rights reserved.
+
+This code is derived from software contributed to Berkeley by
+Jeffrey Mogul.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. Neither the name of the University nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
+Copyright (c) 1988, 1993, 1994
+ The Regents of the University of California. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. Neither the name of the University nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
+Copyright (c) 1988, 1993, 1994
+ The Regents of the University of California. All rights reserved.
+
+This code is derived from software contributed to Berkeley by
+David Hitz of Auspex Systems Inc.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. Neither the name of the University nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
+Copyright (c) 1988, 1993, 1994, 2003
+ The Regents of the University of California. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. Neither the name of the University nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
+Copyright (c) 1989, 1993
+ The Regents of the University of California. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. Neither the name of the University nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
+Copyright (c) 1989, 1993
+ The Regents of the University of California. All rights reserved.
+
+This code is derived from software contributed to Berkeley by
+Kevin Fall.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. Neither the name of the University nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
+Copyright (c) 1989, 1993, 1994
+ The Regents of the University of California. All rights reserved.
+
+This code is derived from software contributed to Berkeley by
+Chris Newcomb.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. Neither the name of the University nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
+Copyright (c) 1989, 1993, 1994
+ The Regents of the University of California. All rights reserved.
+
+This code is derived from software contributed to Berkeley by
+Ken Smith of The State University of New York at Buffalo.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. Neither the name of the University nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
+Copyright (c) 1990, 1993, 1994, 2003
+ The Regents of the University of California. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. Neither the name of the University nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
+Copyright (c) 1991, 1993
+ The Regents of the University of California. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. Neither the name of the University nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
+Copyright (c) 1991, 1993, 1994
+ The Regents of the University of California. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. Neither the name of the University nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
+Copyright (c) 1991, 1993, 1994
+ The Regents of the University of California. All rights reserved.
+
+This code is derived from software contributed to Berkeley by
+Keith Muller of the University of California, San Diego and Lance
+Visser of Convex Computer Corporation.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. Neither the name of the University nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
+Copyright (c) 1992, 1993, 1994
+ The Regents of the University of California. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. Neither the name of the University nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
+Copyright (c) 1997, 1998, 1999, 2002 The NetBSD Foundation, Inc.
+All rights reserved.
+
+This code is derived from software contributed to The NetBSD Foundation
+by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
+NASA Ames Research Center, by Luke Mewburn and by Tomas Svensson.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
+Copyright (c) 1998 Robert Nordier
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
+OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
+Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
+Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
+Copyright (C) 2008 Gabor Kovesdan <gabor@FreeBSD.org>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
+Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
+Copyright (C) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
+Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
+Copyright (C) 2008-2010 Gabor Kovesdan <gabor@FreeBSD.org>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
+Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
+Copyright (C) 2008-2010 Gabor Kovesdan <gabor@FreeBSD.org>
+Copyright (C) 2010 Dimitry Andric <dimitry@andric.com>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
+Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
+Copyright (c) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
+Copyright (c) 2001-2002,2004 The NetBSD Foundation, Inc.
+All rights reserved.
+
+This code is derived from software contributed to The NetBSD Foundation
+by Luke Mewburn.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
+Copyright (c) 2007 The NetBSD Foundation, Inc.
+All rights reserved.
+
+This code is derived from software contributed to The NetBSD Foundation
+by Luke Mewburn.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
+Copyright (c) 2008, The Android Open Source Project
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name of Google, Inc. nor the names of its contributors
+ may be used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
+Copyright (c) 2009, The Android Open Source Project
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name of Google, Inc. nor the names of its contributors
+ may be used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
+Copyright (c) 2010 The NetBSD Foundation, Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
+Copyright (c) 2010, The Android Open Source Project
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name of Google, Inc. nor the names of its contributors
+ may be used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
+Copyright (c) 2012, The Android Open Source Project
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name of Google, Inc. nor the names of its contributors
+ may be used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
+Copyright (c) 2013, The Android Open Source Project
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name of Google, Inc. nor the names of its contributors
+ may be used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
+Copyright (c) 2014, The Android Open Source Project
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name of Google, Inc. nor the names of its contributors
+ may be used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
diff --git a/toolbox/bsd-compatibility.h b/toolbox/bsd-compatibility.h
new file mode 100644
index 0000000..434d370
--- /dev/null
+++ b/toolbox/bsd-compatibility.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2014, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdbool.h>
+#include <sys/types.h>
+
+/* We want chown to support user.group as well as user:group. */
+#define SUPPORT_DOT
+
+/* We don't localize /system/bin! */
+#define WITHOUT_NLS
+
+// NetBSD uses _DIAGASSERT to null-check arguments and the like.
+#include <assert.h>
+#define _DIAGASSERT(e) ((e) ? (void) 0 : __assert2(__FILE__, __LINE__, __func__, #e))
+
+// TODO: update our <sys/cdefs.h> to support this properly.
+#define __type_fit(t, a) (0 == 0)
+
+// TODO: should this be in our <sys/cdefs.h>?
+#define __arraycount(a) (sizeof(a) / sizeof(a[0]))
+
+// This at least matches GNU dd(1) behavior.
+#define SIGINFO SIGUSR1
+
+#define S_ISWHT(x) false
+
+__BEGIN_DECLS
+
+/* From NetBSD <stdlib.h>. */
+#define HN_DECIMAL 0x01
+#define HN_NOSPACE 0x02
+#define HN_B 0x04
+#define HN_DIVISOR_1000 0x08
+#define HN_GETSCALE 0x10
+#define HN_AUTOSCALE 0x20
+int humanize_number(char *, size_t, int64_t, const char *, int, int);
+int dehumanize_number(const char *, int64_t *);
+char *getbsize(int *, long *);
+long long strsuftoll(const char *, const char *, long long, long long);
+long long strsuftollx(const char *, const char *, long long, long long,
+ char *, size_t);
+
+/* From NetBSD <string.h>. */
+void strmode(mode_t, char*);
+
+/* From NetBSD <sys/param.h>. */
+#define MAXBSIZE 65536
+
+/* From NetBSD <sys/stat.h>. */
+#define DEFFILEMODE (S_IRUSR | S_IWUSR)
+
+/* From NetBSD <unistd.h>. */
+void swab(const void * __restrict, void * __restrict, ssize_t);
+
+/* From NetBSD <util.h>. */
+int raise_default_signal(int);
+
+__END_DECLS
diff --git a/toolbox/df.c b/toolbox/df.c
new file mode 100644
index 0000000..9cd0743
--- /dev/null
+++ b/toolbox/df.c
@@ -0,0 +1,85 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/statfs.h>
+
+static int ok = EXIT_SUCCESS;
+
+static void printsize(long long n)
+{
+ char unit = 'K';
+ long long t;
+
+ n *= 10;
+
+ if (n > 1024*1024*10) {
+ n /= 1024;
+ unit = 'M';
+ }
+
+ if (n > 1024*1024*10) {
+ n /= 1024;
+ unit = 'G';
+ }
+
+ t = (n + 512) / 1024;
+ printf("%4lld.%1lld%c", t/10, t%10, unit);
+}
+
+static void df(char *s, int always) {
+ struct statfs st;
+
+ if (statfs(s, &st) < 0) {
+ fprintf(stderr, "%s: %s\n", s, strerror(errno));
+ ok = EXIT_FAILURE;
+ } else {
+ if (st.f_blocks == 0 && !always)
+ return;
+ printf("%-20s ", s);
+ printsize((long long)st.f_blocks * (long long)st.f_bsize);
+ printf(" ");
+ printsize((long long)(st.f_blocks - (long long)st.f_bfree) * st.f_bsize);
+ printf(" ");
+ printsize((long long)st.f_bfree * (long long)st.f_bsize);
+ printf(" %d\n", (int) st.f_bsize);
+ }
+}
+
+int df_main(int argc, char *argv[]) {
+ printf("Filesystem Size Used Free Blksize\n");
+ if (argc == 1) {
+ char s[2000];
+ FILE *f = fopen("/proc/mounts", "r");
+
+ while (fgets(s, 2000, f)) {
+ char *c, *e = s;
+
+ for (c = s; *c; c++) {
+ if (*c == ' ') {
+ e = c + 1;
+ break;
+ }
+ }
+
+ for (c = e; *c; c++) {
+ if (*c == ' ') {
+ *c = '\0';
+ break;
+ }
+ }
+
+ df(e, 0);
+ }
+
+ fclose(f);
+ } else {
+ int i;
+
+ for (i = 1; i < argc; i++) {
+ df(argv[i], 1);
+ }
+ }
+
+ exit(ok);
+}
diff --git a/toolbox/generate-input.h-labels.py b/toolbox/generate-input.h-labels.py
new file mode 100755
index 0000000..ebb9588
--- /dev/null
+++ b/toolbox/generate-input.h-labels.py
@@ -0,0 +1,93 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# pylint: disable=bad-indentation,bad-continuation
+
+import os
+import re
+
+input_prop_list = []
+ev_list = []
+syn_list = []
+key_list = []
+rel_list = []
+abs_list = []
+sw_list = []
+msc_list = []
+led_list = []
+rep_list = []
+snd_list = []
+mt_tool_list = []
+ff_status_list = []
+ff_list = []
+
+r = re.compile(r'#define\s+(\S+)\s+((?:0x)?\d+)')
+
+with open('bionic/libc/kernel/uapi/linux/input.h', 'r') as f:
+ for line in f:
+ m = r.match(line)
+ if m:
+ name = m.group(1)
+ if name.startswith("INPUT_PROP_"):
+ input_prop_list.append(name)
+ elif name.startswith("EV_"):
+ ev_list.append(name)
+ elif name.startswith("SYN_"):
+ syn_list.append(name)
+ elif name.startswith("KEY_") or name.startswith("BTN_"):
+ key_list.append(name)
+ elif name.startswith("REL_"):
+ rel_list.append(name)
+ elif name.startswith("ABS_"):
+ abs_list.append(name)
+ elif name.startswith("SW_"):
+ sw_list.append(name)
+ elif name.startswith("MSC_"):
+ msc_list.append(name)
+ elif name.startswith("LED_"):
+ led_list.append(name)
+ elif name.startswith("REP_"):
+ rep_list.append(name)
+ elif name.startswith("SND_"):
+ snd_list.append(name)
+ elif name.startswith("MT_TOOL_"):
+ mt_tool_list.append(name)
+ elif name.startswith("FF_STATUS_"):
+ ff_status_list.append(name)
+ elif name.startswith("FF_"):
+ ff_list.append(name)
+
+def Dump(struct_name, values):
+ print 'static struct label %s[] = {' % (struct_name)
+ for value in values:
+ print ' LABEL(%s),' % (value)
+ print ' LABEL_END,'
+ print '};'
+
+Dump("input_prop_labels", input_prop_list)
+Dump("ev_labels", ev_list)
+Dump("syn_labels", syn_list)
+Dump("key_labels", key_list)
+Dump("rel_labels", rel_list)
+Dump("abs_labels", abs_list)
+Dump("sw_labels", sw_list)
+Dump("msc_labels", msc_list)
+Dump("led_labels", led_list)
+Dump("rep_labels", rep_list)
+Dump("snd_labels", snd_list)
+Dump("mt_tool_labels", mt_tool_list)
+Dump("ff_status_labels", ff_status_list)
+Dump("ff_labels", ff_list)
diff --git a/toolbox/getevent.c b/toolbox/getevent.c
new file mode 100644
index 0000000..30053af
--- /dev/null
+++ b/toolbox/getevent.c
@@ -0,0 +1,690 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/inotify.h>
+#include <sys/limits.h>
+#include <sys/poll.h>
+#include <linux/input.h>
+#include <errno.h>
+#include <unistd.h>
+
+struct label {
+ const char *name;
+ int value;
+};
+
+#define LABEL(constant) { #constant, constant }
+#define LABEL_END { NULL, -1 }
+
+static struct label key_value_labels[] = {
+ { "UP", 0 },
+ { "DOWN", 1 },
+ { "REPEAT", 2 },
+ LABEL_END,
+};
+
+#include "input.h-labels.h"
+
+#undef LABEL
+#undef LABEL_END
+
+static struct pollfd *ufds;
+static char **device_names;
+static int nfds;
+
+enum {
+ PRINT_DEVICE_ERRORS = 1U << 0,
+ PRINT_DEVICE = 1U << 1,
+ PRINT_DEVICE_NAME = 1U << 2,
+ PRINT_DEVICE_INFO = 1U << 3,
+ PRINT_VERSION = 1U << 4,
+ PRINT_POSSIBLE_EVENTS = 1U << 5,
+ PRINT_INPUT_PROPS = 1U << 6,
+ PRINT_HID_DESCRIPTOR = 1U << 7,
+
+ PRINT_ALL_INFO = (1U << 8) - 1,
+
+ PRINT_LABELS = 1U << 16,
+};
+
+static const char *get_label(const struct label *labels, int value)
+{
+ while(labels->name && value != labels->value) {
+ labels++;
+ }
+ return labels->name;
+}
+
+static int print_input_props(int fd)
+{
+ uint8_t bits[INPUT_PROP_CNT / 8];
+ int i, j;
+ int res;
+ int count;
+ const char *bit_label;
+
+ printf(" input props:\n");
+ res = ioctl(fd, EVIOCGPROP(sizeof(bits)), bits);
+ if(res < 0) {
+ printf(" <not available\n");
+ return 1;
+ }
+ count = 0;
+ for(i = 0; i < res; i++) {
+ for(j = 0; j < 8; j++) {
+ if (bits[i] & 1 << j) {
+ bit_label = get_label(input_prop_labels, i * 8 + j);
+ if(bit_label)
+ printf(" %s\n", bit_label);
+ else
+ printf(" %04x\n", i * 8 + j);
+ count++;
+ }
+ }
+ }
+ if (!count)
+ printf(" <none>\n");
+ return 0;
+}
+
+static int print_possible_events(int fd, int print_flags)
+{
+ uint8_t *bits = NULL;
+ ssize_t bits_size = 0;
+ const char* label;
+ int i, j, k;
+ int res, res2;
+ struct label* bit_labels;
+ const char *bit_label;
+
+ printf(" events:\n");
+ for(i = EV_KEY; i <= EV_MAX; i++) { // skip EV_SYN since we cannot query its available codes
+ int count = 0;
+ while(1) {
+ res = ioctl(fd, EVIOCGBIT(i, bits_size), bits);
+ if(res < bits_size)
+ break;
+ bits_size = res + 16;
+ bits = realloc(bits, bits_size * 2);
+ if(bits == NULL) {
+ fprintf(stderr, "failed to allocate buffer of size %d\n", (int)bits_size);
+ return 1;
+ }
+ }
+ res2 = 0;
+ switch(i) {
+ case EV_KEY:
+ res2 = ioctl(fd, EVIOCGKEY(res), bits + bits_size);
+ label = "KEY";
+ bit_labels = key_labels;
+ break;
+ case EV_REL:
+ label = "REL";
+ bit_labels = rel_labels;
+ break;
+ case EV_ABS:
+ label = "ABS";
+ bit_labels = abs_labels;
+ break;
+ case EV_MSC:
+ label = "MSC";
+ bit_labels = msc_labels;
+ break;
+ case EV_LED:
+ res2 = ioctl(fd, EVIOCGLED(res), bits + bits_size);
+ label = "LED";
+ bit_labels = led_labels;
+ break;
+ case EV_SND:
+ res2 = ioctl(fd, EVIOCGSND(res), bits + bits_size);
+ label = "SND";
+ bit_labels = snd_labels;
+ break;
+ case EV_SW:
+ res2 = ioctl(fd, EVIOCGSW(bits_size), bits + bits_size);
+ label = "SW ";
+ bit_labels = sw_labels;
+ break;
+ case EV_REP:
+ label = "REP";
+ bit_labels = rep_labels;
+ break;
+ case EV_FF:
+ label = "FF ";
+ bit_labels = ff_labels;
+ break;
+ case EV_PWR:
+ label = "PWR";
+ bit_labels = NULL;
+ break;
+ case EV_FF_STATUS:
+ label = "FFS";
+ bit_labels = ff_status_labels;
+ break;
+ default:
+ res2 = 0;
+ label = "???";
+ bit_labels = NULL;
+ }
+ for(j = 0; j < res; j++) {
+ for(k = 0; k < 8; k++)
+ if(bits[j] & 1 << k) {
+ char down;
+ if(j < res2 && (bits[j + bits_size] & 1 << k))
+ down = '*';
+ else
+ down = ' ';
+ if(count == 0)
+ printf(" %s (%04x):", label, i);
+ else if((count & (print_flags & PRINT_LABELS ? 0x3 : 0x7)) == 0 || i == EV_ABS)
+ printf("\n ");
+ if(bit_labels && (print_flags & PRINT_LABELS)) {
+ bit_label = get_label(bit_labels, j * 8 + k);
+ if(bit_label)
+ printf(" %.20s%c%*s", bit_label, down, (int) (20 - strlen(bit_label)), "");
+ else
+ printf(" %04x%c ", j * 8 + k, down);
+ } else {
+ printf(" %04x%c", j * 8 + k, down);
+ }
+ if(i == EV_ABS) {
+ struct input_absinfo abs;
+ if(ioctl(fd, EVIOCGABS(j * 8 + k), &abs) == 0) {
+ printf(" : value %d, min %d, max %d, fuzz %d, flat %d, resolution %d",
+ abs.value, abs.minimum, abs.maximum, abs.fuzz, abs.flat,
+ abs.resolution);
+ }
+ }
+ count++;
+ }
+ }
+ if(count)
+ printf("\n");
+ }
+ free(bits);
+ return 0;
+}
+
+static void print_event(int type, int code, int value, int print_flags)
+{
+ const char *type_label, *code_label, *value_label;
+
+ if (print_flags & PRINT_LABELS) {
+ type_label = get_label(ev_labels, type);
+ code_label = NULL;
+ value_label = NULL;
+
+ switch(type) {
+ case EV_SYN:
+ code_label = get_label(syn_labels, code);
+ break;
+ case EV_KEY:
+ code_label = get_label(key_labels, code);
+ value_label = get_label(key_value_labels, value);
+ break;
+ case EV_REL:
+ code_label = get_label(rel_labels, code);
+ break;
+ case EV_ABS:
+ code_label = get_label(abs_labels, code);
+ switch(code) {
+ case ABS_MT_TOOL_TYPE:
+ value_label = get_label(mt_tool_labels, value);
+ }
+ break;
+ case EV_MSC:
+ code_label = get_label(msc_labels, code);
+ break;
+ case EV_LED:
+ code_label = get_label(led_labels, code);
+ break;
+ case EV_SND:
+ code_label = get_label(snd_labels, code);
+ break;
+ case EV_SW:
+ code_label = get_label(sw_labels, code);
+ break;
+ case EV_REP:
+ code_label = get_label(rep_labels, code);
+ break;
+ case EV_FF:
+ code_label = get_label(ff_labels, code);
+ break;
+ case EV_FF_STATUS:
+ code_label = get_label(ff_status_labels, code);
+ break;
+ }
+
+ if (type_label)
+ printf("%-12.12s", type_label);
+ else
+ printf("%04x ", type);
+ if (code_label)
+ printf(" %-20.20s", code_label);
+ else
+ printf(" %04x ", code);
+ if (value_label)
+ printf(" %-20.20s", value_label);
+ else
+ printf(" %08x ", value);
+ } else {
+ printf("%04x %04x %08x", type, code, value);
+ }
+}
+
+static void print_hid_descriptor(int bus, int vendor, int product)
+{
+ const char *dirname = "/sys/kernel/debug/hid";
+ char prefix[16];
+ DIR *dir;
+ struct dirent *de;
+ char filename[PATH_MAX];
+ FILE *file;
+ char line[2048];
+
+ snprintf(prefix, sizeof(prefix), "%04X:%04X:%04X.", bus, vendor, product);
+
+ dir = opendir(dirname);
+ if(dir == NULL)
+ return;
+ while((de = readdir(dir))) {
+ if (strstr(de->d_name, prefix) == de->d_name) {
+ snprintf(filename, sizeof(filename), "%s/%s/rdesc", dirname, de->d_name);
+
+ file = fopen(filename, "r");
+ if (file) {
+ printf(" HID descriptor: %s\n\n", de->d_name);
+ while (fgets(line, sizeof(line), file)) {
+ fputs(" ", stdout);
+ fputs(line, stdout);
+ }
+ fclose(file);
+ puts("");
+ }
+ }
+ }
+ closedir(dir);
+}
+
+static int open_device(const char *device, int print_flags)
+{
+ int version;
+ int fd;
+ int clkid = CLOCK_MONOTONIC;
+ struct pollfd *new_ufds;
+ char **new_device_names;
+ char name[80];
+ char location[80];
+ char idstr[80];
+ struct input_id id;
+
+ fd = open(device, O_RDWR);
+ if(fd < 0) {
+ if(print_flags & PRINT_DEVICE_ERRORS)
+ fprintf(stderr, "could not open %s, %s\n", device, strerror(errno));
+ return -1;
+ }
+
+ if(ioctl(fd, EVIOCGVERSION, &version)) {
+ if(print_flags & PRINT_DEVICE_ERRORS)
+ fprintf(stderr, "could not get driver version for %s, %s\n", device, strerror(errno));
+ return -1;
+ }
+ if(ioctl(fd, EVIOCGID, &id)) {
+ if(print_flags & PRINT_DEVICE_ERRORS)
+ fprintf(stderr, "could not get driver id for %s, %s\n", device, strerror(errno));
+ return -1;
+ }
+ name[sizeof(name) - 1] = '\0';
+ location[sizeof(location) - 1] = '\0';
+ idstr[sizeof(idstr) - 1] = '\0';
+ if(ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) {
+ //fprintf(stderr, "could not get device name for %s, %s\n", device, strerror(errno));
+ name[0] = '\0';
+ }
+ if(ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) {
+ //fprintf(stderr, "could not get location for %s, %s\n", device, strerror(errno));
+ location[0] = '\0';
+ }
+ if(ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1) {
+ //fprintf(stderr, "could not get idstring for %s, %s\n", device, strerror(errno));
+ idstr[0] = '\0';
+ }
+
+ if (ioctl(fd, EVIOCSCLOCKID, &clkid) != 0) {
+ fprintf(stderr, "Can't enable monotonic clock reporting: %s\n", strerror(errno));
+ // a non-fatal error
+ }
+
+ new_ufds = realloc(ufds, sizeof(ufds[0]) * (nfds + 1));
+ if(new_ufds == NULL) {
+ fprintf(stderr, "out of memory\n");
+ return -1;
+ }
+ ufds = new_ufds;
+ new_device_names = realloc(device_names, sizeof(device_names[0]) * (nfds + 1));
+ if(new_device_names == NULL) {
+ fprintf(stderr, "out of memory\n");
+ return -1;
+ }
+ device_names = new_device_names;
+
+ if(print_flags & PRINT_DEVICE)
+ printf("add device %d: %s\n", nfds, device);
+ if(print_flags & PRINT_DEVICE_INFO)
+ printf(" bus: %04x\n"
+ " vendor %04x\n"
+ " product %04x\n"
+ " version %04x\n",
+ id.bustype, id.vendor, id.product, id.version);
+ if(print_flags & PRINT_DEVICE_NAME)
+ printf(" name: \"%s\"\n", name);
+ if(print_flags & PRINT_DEVICE_INFO)
+ printf(" location: \"%s\"\n"
+ " id: \"%s\"\n", location, idstr);
+ if(print_flags & PRINT_VERSION)
+ printf(" version: %d.%d.%d\n",
+ version >> 16, (version >> 8) & 0xff, version & 0xff);
+
+ if(print_flags & PRINT_POSSIBLE_EVENTS) {
+ print_possible_events(fd, print_flags);
+ }
+
+ if(print_flags & PRINT_INPUT_PROPS) {
+ print_input_props(fd);
+ }
+ if(print_flags & PRINT_HID_DESCRIPTOR) {
+ print_hid_descriptor(id.bustype, id.vendor, id.product);
+ }
+
+ ufds[nfds].fd = fd;
+ ufds[nfds].events = POLLIN;
+ device_names[nfds] = strdup(device);
+ nfds++;
+
+ return 0;
+}
+
+int close_device(const char *device, int print_flags)
+{
+ int i;
+ for(i = 1; i < nfds; i++) {
+ if(strcmp(device_names[i], device) == 0) {
+ int count = nfds - i - 1;
+ if(print_flags & PRINT_DEVICE)
+ printf("remove device %d: %s\n", i, device);
+ free(device_names[i]);
+ memmove(device_names + i, device_names + i + 1, sizeof(device_names[0]) * count);
+ memmove(ufds + i, ufds + i + 1, sizeof(ufds[0]) * count);
+ nfds--;
+ return 0;
+ }
+ }
+ if(print_flags & PRINT_DEVICE_ERRORS)
+ fprintf(stderr, "remote device: %s not found\n", device);
+ return -1;
+}
+
+static int read_notify(const char *dirname, int nfd, int print_flags)
+{
+ int res;
+ char devname[PATH_MAX];
+ char *filename;
+ char event_buf[512];
+ int event_size;
+ int event_pos = 0;
+ struct inotify_event *event;
+
+ res = read(nfd, event_buf, sizeof(event_buf));
+ if(res < (int)sizeof(*event)) {
+ if(errno == EINTR)
+ return 0;
+ fprintf(stderr, "could not get event, %s\n", strerror(errno));
+ return 1;
+ }
+ //printf("got %d bytes of event information\n", res);
+
+ strcpy(devname, dirname);
+ filename = devname + strlen(devname);
+ *filename++ = '/';
+
+ while(res >= (int)sizeof(*event)) {
+ event = (struct inotify_event *)(event_buf + event_pos);
+ //printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : "");
+ if(event->len) {
+ strcpy(filename, event->name);
+ if(event->mask & IN_CREATE) {
+ open_device(devname, print_flags);
+ }
+ else {
+ close_device(devname, print_flags);
+ }
+ }
+ event_size = sizeof(*event) + event->len;
+ res -= event_size;
+ event_pos += event_size;
+ }
+ return 0;
+}
+
+static int scan_dir(const char *dirname, int print_flags)
+{
+ char devname[PATH_MAX];
+ char *filename;
+ DIR *dir;
+ struct dirent *de;
+ dir = opendir(dirname);
+ if(dir == NULL)
+ return -1;
+ strcpy(devname, dirname);
+ filename = devname + strlen(devname);
+ *filename++ = '/';
+ while((de = readdir(dir))) {
+ if(de->d_name[0] == '.' &&
+ (de->d_name[1] == '\0' ||
+ (de->d_name[1] == '.' && de->d_name[2] == '\0')))
+ continue;
+ strcpy(filename, de->d_name);
+ open_device(devname, print_flags);
+ }
+ closedir(dir);
+ return 0;
+}
+
+static void usage(char *name)
+{
+ fprintf(stderr, "Usage: %s [-t] [-n] [-s switchmask] [-S] [-v [mask]] [-d] [-p] [-i] [-l] [-q] [-c count] [-r] [device]\n", name);
+ fprintf(stderr, " -t: show time stamps\n");
+ fprintf(stderr, " -n: don't print newlines\n");
+ fprintf(stderr, " -s: print switch states for given bits\n");
+ fprintf(stderr, " -S: print all switch states\n");
+ fprintf(stderr, " -v: verbosity mask (errs=1, dev=2, name=4, info=8, vers=16, pos. events=32, props=64)\n");
+ fprintf(stderr, " -d: show HID descriptor, if available\n");
+ fprintf(stderr, " -p: show possible events (errs, dev, name, pos. events)\n");
+ fprintf(stderr, " -i: show all device info and possible events\n");
+ fprintf(stderr, " -l: label event types and names in plain text\n");
+ fprintf(stderr, " -q: quiet (clear verbosity mask)\n");
+ fprintf(stderr, " -c: print given number of events then exit\n");
+ fprintf(stderr, " -r: print rate events are received\n");
+}
+
+int getevent_main(int argc, char *argv[])
+{
+ int c;
+ int i;
+ int res;
+ int get_time = 0;
+ int print_device = 0;
+ char *newline = "\n";
+ uint16_t get_switch = 0;
+ struct input_event event;
+ int print_flags = 0;
+ int print_flags_set = 0;
+ int dont_block = -1;
+ int event_count = 0;
+ int sync_rate = 0;
+ int64_t last_sync_time = 0;
+ const char *device = NULL;
+ const char *device_path = "/dev/input";
+
+ opterr = 0;
+ do {
+ c = getopt(argc, argv, "tns:Sv::dpilqc:rh");
+ if (c == EOF)
+ break;
+ switch (c) {
+ case 't':
+ get_time = 1;
+ break;
+ case 'n':
+ newline = "";
+ break;
+ case 's':
+ get_switch = strtoul(optarg, NULL, 0);
+ if(dont_block == -1)
+ dont_block = 1;
+ break;
+ case 'S':
+ get_switch = ~0;
+ if(dont_block == -1)
+ dont_block = 1;
+ break;
+ case 'v':
+ if(optarg)
+ print_flags |= strtoul(optarg, NULL, 0);
+ else
+ print_flags |= PRINT_DEVICE | PRINT_DEVICE_NAME | PRINT_DEVICE_INFO | PRINT_VERSION;
+ print_flags_set = 1;
+ break;
+ case 'd':
+ print_flags |= PRINT_HID_DESCRIPTOR;
+ break;
+ case 'p':
+ print_flags |= PRINT_DEVICE_ERRORS | PRINT_DEVICE
+ | PRINT_DEVICE_NAME | PRINT_POSSIBLE_EVENTS | PRINT_INPUT_PROPS;
+ print_flags_set = 1;
+ if(dont_block == -1)
+ dont_block = 1;
+ break;
+ case 'i':
+ print_flags |= PRINT_ALL_INFO;
+ print_flags_set = 1;
+ if(dont_block == -1)
+ dont_block = 1;
+ break;
+ case 'l':
+ print_flags |= PRINT_LABELS;
+ break;
+ case 'q':
+ print_flags_set = 1;
+ break;
+ case 'c':
+ event_count = atoi(optarg);
+ dont_block = 0;
+ break;
+ case 'r':
+ sync_rate = 1;
+ break;
+ case '?':
+ fprintf(stderr, "%s: invalid option -%c\n",
+ argv[0], optopt);
+ case 'h':
+ usage(argv[0]);
+ exit(1);
+ }
+ } while (1);
+ if(dont_block == -1)
+ dont_block = 0;
+
+ if (optind + 1 == argc) {
+ device = argv[optind];
+ optind++;
+ }
+ if (optind != argc) {
+ usage(argv[0]);
+ exit(1);
+ }
+ nfds = 1;
+ ufds = calloc(1, sizeof(ufds[0]));
+ ufds[0].fd = inotify_init();
+ ufds[0].events = POLLIN;
+ if(device) {
+ if(!print_flags_set)
+ print_flags |= PRINT_DEVICE_ERRORS;
+ res = open_device(device, print_flags);
+ if(res < 0) {
+ return 1;
+ }
+ } else {
+ if(!print_flags_set)
+ print_flags |= PRINT_DEVICE_ERRORS | PRINT_DEVICE | PRINT_DEVICE_NAME;
+ print_device = 1;
+ res = inotify_add_watch(ufds[0].fd, device_path, IN_DELETE | IN_CREATE);
+ if(res < 0) {
+ fprintf(stderr, "could not add watch for %s, %s\n", device_path, strerror(errno));
+ return 1;
+ }
+ res = scan_dir(device_path, print_flags);
+ if(res < 0) {
+ fprintf(stderr, "scan dir failed for %s\n", device_path);
+ return 1;
+ }
+ }
+
+ if(get_switch) {
+ for(i = 1; i < nfds; i++) {
+ uint16_t sw;
+ res = ioctl(ufds[i].fd, EVIOCGSW(1), &sw);
+ if(res < 0) {
+ fprintf(stderr, "could not get switch state, %s\n", strerror(errno));
+ return 1;
+ }
+ sw &= get_switch;
+ printf("%04x%s", sw, newline);
+ }
+ }
+
+ if(dont_block)
+ return 0;
+
+ while(1) {
+ //int pollres =
+ poll(ufds, nfds, -1);
+ //printf("poll %d, returned %d\n", nfds, pollres);
+ if(ufds[0].revents & POLLIN) {
+ read_notify(device_path, ufds[0].fd, print_flags);
+ }
+ for(i = 1; i < nfds; i++) {
+ if(ufds[i].revents) {
+ if(ufds[i].revents & POLLIN) {
+ res = read(ufds[i].fd, &event, sizeof(event));
+ if(res < (int)sizeof(event)) {
+ fprintf(stderr, "could not get event\n");
+ return 1;
+ }
+ if(get_time) {
+ printf("[%8ld.%06ld] ", event.time.tv_sec, event.time.tv_usec);
+ }
+ if(print_device)
+ printf("%s: ", device_names[i]);
+ print_event(event.type, event.code, event.value, print_flags);
+ if(sync_rate && event.type == 0 && event.code == 0) {
+ int64_t now = event.time.tv_sec * 1000000LL + event.time.tv_usec;
+ if(last_sync_time)
+ printf(" rate %lld", 1000000LL / (now - last_sync_time));
+ last_sync_time = now;
+ }
+ printf("%s", newline);
+ if(event_count && --event_count == 0)
+ return 0;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/toolbox/iftop.c b/toolbox/iftop.c
new file mode 100644
index 0000000..800c0f0
--- /dev/null
+++ b/toolbox/iftop.c
@@ -0,0 +1,278 @@
+/*
+ * Copyright (c) 2008, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+
+#define PROC_NET_DEV "/proc/net/dev"
+
+#define MAX_IF 8 /* max interfaces we can handle */
+
+#ifndef PAGE_SIZE
+# define PAGE_SIZE 4096
+#endif
+
+#define _STR(s) #s
+#define STR(s) _STR(s)
+
+struct if_stats {
+ char name[IFNAMSIZ];
+
+ unsigned int mtu;
+
+ unsigned int rx_bytes;
+ unsigned int rx_packets;
+ unsigned int rx_errors;
+ unsigned int rx_dropped;
+
+ unsigned int tx_bytes;
+ unsigned int tx_packets;
+ unsigned int tx_errors;
+ unsigned int tx_dropped;
+};
+
+static int get_mtu(const char *if_name)
+{
+ struct ifreq ifr;
+ int s, ret;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ perror("socket");
+ exit(EXIT_FAILURE);
+ }
+
+ memset(&ifr, 0, sizeof(struct ifreq));
+ ifr.ifr_addr.sa_family = AF_INET;
+ strcpy(ifr.ifr_name, if_name);
+
+ ret = ioctl(s, SIOCGIFMTU, &ifr);
+ if (ret < 0) {
+ perror("ioctl");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = close(s);
+ if (ret < 0) {
+ perror("close");
+ exit(EXIT_FAILURE);
+ }
+
+ return ifr.ifr_mtu;
+}
+
+static int get_interfaces(struct if_stats *ifs)
+{
+ char buf[PAGE_SIZE];
+ char *p;
+ int ret, nr, fd;
+
+ fd = open(PROC_NET_DEV, O_RDONLY);
+ if (fd < 0) {
+ perror("open");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = read(fd, buf, sizeof(buf) - 1);
+ if (ret < 0) {
+ perror("read");
+ exit(EXIT_FAILURE);
+ } else if (!ret) {
+ fprintf(stderr, "reading " PROC_NET_DEV " returned premature EOF\n");
+ exit(EXIT_FAILURE);
+ }
+ buf[ret] = '\0';
+
+ /* skip down to the third line */
+ p = strchr(buf, '\n');
+ if (!p) {
+ fprintf(stderr, "parsing " PROC_NET_DEV " failed unexpectedly\n");
+ exit(EXIT_FAILURE);
+ }
+ p = strchr(p + 1, '\n');
+ if (!p) {
+ fprintf(stderr, "parsing " PROC_NET_DEV " failed unexpectedly\n");
+ exit(EXIT_FAILURE);
+ }
+ p += 1;
+
+ /*
+ * Key:
+ * if: (Rx) bytes packets errs drop fifo frame compressed multicast \
+ * (Tx) bytes packets errs drop fifo colls carrier compressed
+ */
+ for (nr = 0; nr < MAX_IF; nr++) {
+ char *c;
+
+ ret = sscanf(p, "%" STR(IFNAMSIZ) "s", ifs->name);
+ if (ret != 1) {
+ fprintf(stderr, "parsing " PROC_NET_DEV " failed unexpectedly\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ * This works around a bug in the proc file where large interface names
+ * or Rx byte counts eat the delimiter, breaking sscanf.
+ */
+ c = strchr(ifs->name, ':');
+ if (c)
+ *c = '\0';
+
+ p = strchr(p, ':') + 1;
+
+ ret = sscanf(p, "%u %u %u %u %*u %*u %*u %*u %u %u %u %u %*u %*u "
+ "%*u %*u\n", &ifs->rx_bytes, &ifs->rx_packets,
+ &ifs->rx_errors, &ifs->rx_dropped, &ifs->tx_bytes,
+ &ifs->tx_packets, &ifs->tx_errors, &ifs->tx_dropped);
+ if (ret != 8) {
+ fprintf(stderr, "parsing " PROC_NET_DEV " failed unexpectedly\n");
+ exit(EXIT_FAILURE);
+ }
+
+ ifs->mtu = get_mtu(ifs->name);
+
+ p = strchr(p, '\n') + 1;
+ if (*p == '\0') {
+ nr++;
+ break;
+ }
+
+ ifs++;
+ }
+
+ ret = close(fd);
+ if (ret) {
+ perror("close");
+ exit(EXIT_FAILURE);
+ }
+
+ return nr;
+}
+
+static void print_header(void)
+{
+ printf(" Rx Tx\n");
+ printf("%-8s %-5s %-10s %-8s %-5s %-5s %-10s %-8s %-5s %-5s\n",
+ "name", "MTU", "bytes", "packets", "errs", "drpd", "bytes",
+ "packets", "errs", "drpd");
+}
+
+static int print_interfaces(struct if_stats *old, struct if_stats *new, int nr)
+{
+ int i = 0;
+
+ while (nr--) {
+ if (old->rx_packets || old->tx_packets) {
+ printf("%-8s %-5u %-10u %-8u %-5u %-5u %-10u %-8u %-5u %-5u\n",
+ new->name, new->mtu,
+ new->rx_bytes - old->rx_bytes,
+ new->rx_packets - old->rx_packets,
+ new->rx_errors - old->rx_errors,
+ new->rx_dropped - old->rx_dropped,
+ new->tx_bytes - old->tx_bytes,
+ new->tx_packets - old->tx_packets,
+ new->tx_errors - old->tx_errors,
+ new->tx_dropped - old->tx_dropped);
+ i++;
+ }
+ old++;
+ new++;
+ }
+
+ return i;
+}
+
+static void usage(const char *cmd)
+{
+ fprintf(stderr, "usage: %s [ -r repeats] [ -d delay ]\n", cmd);
+}
+
+int iftop_main(int argc, char *argv[])
+{
+ struct if_stats ifs[2][MAX_IF];
+ int count = 0, header_interval = 22, delay = 1, i;
+ unsigned int toggle = 0;
+
+ for (i = 1; i < argc; i++) {
+ if (!strcmp(argv[i], "-d")) {
+ if (i >= argc - 1) {
+ fprintf(stderr, "Option -d requires an argument.\n");
+ exit(EXIT_FAILURE);
+ }
+ delay = atoi(argv[i++]);
+ if (!delay)
+ delay = 1;
+ continue;
+ }
+ if (!strcmp(argv[i], "-r")) {
+ if (i >= argc - 1) {
+ fprintf(stderr, "Option -r requires an argument.\n");
+ exit(EXIT_FAILURE);
+ }
+ header_interval = atoi(argv[i++]);
+ if (header_interval < MAX_IF)
+ header_interval = MAX_IF;
+ continue;
+ }
+ if (!strcmp(argv[i], "-h")) {
+ usage(argv[0]);
+ exit(EXIT_SUCCESS);
+ }
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ get_interfaces(ifs[!toggle]);
+ if (header_interval)
+ print_header();
+ while (1) {
+ int nr;
+
+ sleep(delay);
+ nr = get_interfaces(ifs[toggle]);
+ if (header_interval && count + nr > header_interval) {
+ print_header();
+ count = 0;
+ }
+ count += print_interfaces(ifs[!toggle], ifs[toggle], nr);
+ toggle = !toggle;
+ }
+
+ return 0;
+}
diff --git a/toolbox/ioctl.c b/toolbox/ioctl.c
new file mode 100644
index 0000000..093e467
--- /dev/null
+++ b/toolbox/ioctl.c
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2008, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <error.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+static void usage() {
+ fprintf(stderr, "%s [-l <length>] [-a <argsize>] [-rdh] <device> <ioctlnr>\n"
+ " -l <length> Length of io buffer\n"
+ " -a <argsize> Size of each argument (1-8)\n"
+ " -r Open device in read only mode\n"
+ " -d Direct argument (no iobuffer)\n"
+ " -h Print help\n", getprogname());
+ exit(1);
+}
+
+static int xstrtoi(const char* s, const char* what) {
+ char* endp;
+ errno = 0;
+ long result = strtol(s, &endp, 0);
+ if (errno != 0 || *endp != '\0') {
+ error(1, errno, "couldn't parse %s '%s'", what, s);
+ }
+ if (result > INT_MAX || result < INT_MIN) {
+ error(1, errno, "%s '%s' out of range", what, s);
+ }
+ return result;
+}
+
+int ioctl_main(int argc, char* argv[]) {
+ int read_only = 0;
+ int length = -1;
+ int arg_size = 4;
+ int direct_arg = 0;
+
+ void *ioctl_args = NULL;
+ uint8_t *ioctl_argp;
+ uint8_t *ioctl_argp_save = NULL;
+ int rem;
+
+ int c;
+ while ((c = getopt(argc, argv, "rdl:a:h")) != -1) {
+ switch (c) {
+ case 'r':
+ read_only = 1;
+ break;
+ case 'd':
+ direct_arg = 1;
+ break;
+ case 'l':
+ length = xstrtoi(optarg, "length");
+ break;
+ case 'a':
+ arg_size = xstrtoi(optarg, "argument size");
+ break;
+ case 'h':
+ usage();
+ break;
+ default:
+ error(1, 0, "invalid option -%c", optopt);
+ }
+ }
+
+ if (optind + 2 > argc) {
+ usage();
+ }
+
+ const char* device = argv[optind];
+ int fd;
+ if (strcmp(device, "-") == 0) {
+ fd = STDIN_FILENO;
+ } else {
+ fd = open(device, read_only ? O_RDONLY : (O_RDWR | O_SYNC));
+ if (fd == -1) {
+ error(1, errno, "cannot open %s", argv[optind]);
+ }
+ }
+ optind++;
+
+ // IOCTL(2) wants second parameter as a signed int.
+ // Let's let the user specify either negative numbers or large positive
+ // numbers, for the case where ioctl number is larger than INT_MAX.
+ errno = 0;
+ char* endp;
+ int ioctl_nr = UINT_MAX & strtoll(argv[optind], &endp, 0);
+ if (errno != 0 || *endp != '\0') {
+ error(1, errno, "couldn't parse ioctl number '%s'", argv[optind]);
+ }
+ optind++;
+
+ if(direct_arg) {
+ arg_size = 4;
+ length = 4;
+ }
+
+ if(length < 0) {
+ length = (argc - optind) * arg_size;
+ }
+ if(length) {
+ ioctl_args = calloc(1, length);
+
+ ioctl_argp_save = ioctl_argp = ioctl_args;
+ rem = length;
+ while (optind < argc) {
+ uint64_t tmp = strtoull(argv[optind], NULL, 0);
+ if (rem < arg_size) {
+ error(1, 0, "too many arguments");
+ }
+ memcpy(ioctl_argp, &tmp, arg_size);
+ ioctl_argp += arg_size;
+ rem -= arg_size;
+ optind++;
+ }
+ }
+ printf("sending ioctl 0x%x", ioctl_nr);
+ rem = length;
+ while(rem--) {
+ printf(" 0x%02x", *ioctl_argp_save++);
+ }
+ printf(" to %s\n", device);
+
+ int res;
+ if(direct_arg)
+ res = ioctl(fd, ioctl_nr, *(uint32_t*)ioctl_args);
+ else if(length)
+ res = ioctl(fd, ioctl_nr, ioctl_args);
+ else
+ res = ioctl(fd, ioctl_nr, 0);
+ if (res < 0) {
+ free(ioctl_args);
+ error(1, errno, "ioctl 0x%x failed (returned %d)", ioctl_nr, res);
+ }
+
+ if (length) {
+ printf("return buf:");
+ ioctl_argp = ioctl_args;
+ rem = length;
+ while(rem--) {
+ printf(" %02x", *ioctl_argp++);
+ }
+ printf("\n");
+ }
+ free(ioctl_args);
+ close(fd);
+ return 0;
+}
diff --git a/toolbox/ionice.c b/toolbox/ionice.c
new file mode 100644
index 0000000..7abc261
--- /dev/null
+++ b/toolbox/ionice.c
@@ -0,0 +1,58 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <cutils/iosched_policy.h>
+
+static char *classes[] = {"none", "rt", "be", "idle", NULL};
+
+int ionice_main(int argc, char *argv[])
+{
+ IoSchedClass clazz = IoSchedClass_NONE;
+ int ioprio = 0;
+ int pid;
+
+ if(argc != 2 && argc != 4) {
+ fprintf(stderr, "usage: ionice <pid> [none|rt|be|idle] [prio]\n");
+ return 1;
+ }
+
+ if (!(pid = atoi(argv[1]))) {
+ fprintf(stderr, "Invalid pid specified\n");
+ return 1;
+ }
+
+ if (argc == 2) {
+ if (android_get_ioprio(pid, &clazz, &ioprio)) {
+ fprintf(stderr, "Failed to read priority (%s)\n", strerror(errno));
+ return 1;
+ }
+ fprintf(stdout, "Pid %d, class %s (%d), prio %d\n", pid, classes[clazz], clazz, ioprio);
+ return 0;
+ }
+
+ if (!strcmp(argv[2], "none")) {
+ clazz = IoSchedClass_NONE;
+ } else if (!strcmp(argv[2], "rt")) {
+ clazz = IoSchedClass_RT;
+ } else if (!strcmp(argv[2], "be")) {
+ clazz = IoSchedClass_BE;
+ } else if (!strcmp(argv[2], "idle")) {
+ clazz = IoSchedClass_IDLE;
+ } else {
+ fprintf(stderr, "Unsupported class '%s'\n", argv[2]);
+ return 1;
+ }
+
+ ioprio = atoi(argv[3]);
+
+ printf("Setting pid %d i/o class to %d, prio %d\n", pid, clazz, ioprio);
+ if (android_set_ioprio(pid, clazz, ioprio)) {
+ fprintf(stderr, "Failed to set priority (%s)\n", strerror(errno));
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/toolbox/log.c b/toolbox/log.c
new file mode 100644
index 0000000..2f020a8
--- /dev/null
+++ b/toolbox/log.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2008, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <log/logd.h>
+#include <ctype.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <cutils/sockets.h>
+#include <unistd.h>
+
+/*
+ * Note: also accepts 0-9 priorities
+ * returns ANDROID_LOG_UNKNOWN if the character is unrecognized
+ */
+static android_LogPriority filterCharToPri (char c)
+{
+ android_LogPriority pri;
+
+ c = tolower(c);
+
+ if (c >= '0' && c <= '9') {
+ if (c >= ('0'+ANDROID_LOG_SILENT)) {
+ pri = ANDROID_LOG_VERBOSE;
+ } else {
+ pri = (android_LogPriority)(c - '0');
+ }
+ } else if (c == 'v') {
+ pri = ANDROID_LOG_VERBOSE;
+ } else if (c == 'd') {
+ pri = ANDROID_LOG_DEBUG;
+ } else if (c == 'i') {
+ pri = ANDROID_LOG_INFO;
+ } else if (c == 'w') {
+ pri = ANDROID_LOG_WARN;
+ } else if (c == 'e') {
+ pri = ANDROID_LOG_ERROR;
+ } else if (c == 'f') {
+ pri = ANDROID_LOG_FATAL;
+ } else if (c == 's') {
+ pri = ANDROID_LOG_SILENT;
+ } else if (c == '*') {
+ pri = ANDROID_LOG_DEFAULT;
+ } else {
+ pri = ANDROID_LOG_UNKNOWN;
+ }
+
+ return pri;
+}
+
+static int usage(const char *s)
+{
+ fprintf(stderr, "USAGE: %s [-p priorityChar] [-t tag] message\n", s);
+
+ fprintf(stderr, "\tpriorityChar should be one of:\n"
+ "\t\tv,d,i,w,e\n");
+ exit(-1);
+}
+
+
+int log_main(int argc, char *argv[])
+{
+ android_LogPriority priority;
+ const char *tag = "log";
+ char buffer[4096];
+ int i;
+
+ priority = ANDROID_LOG_INFO;
+
+ for (;;) {
+ int ret;
+
+ ret = getopt(argc, argv, "t:p:h");
+
+ if (ret < 0) {
+ break;
+ }
+
+ switch(ret) {
+ case 't':
+ tag = optarg;
+ break;
+
+ case 'p':
+ priority = filterCharToPri(optarg[0]);
+ if (priority == ANDROID_LOG_UNKNOWN) {
+ usage(argv[0]);
+ }
+ break;
+
+ case 'h':
+ usage(argv[0]);
+ break;
+ }
+ }
+
+ if (optind == argc) {
+ usage(argv[0]);
+ }
+
+ buffer[0] = '\0';
+
+ for (i = optind ; i < argc ; i++) {
+ strlcat(buffer, argv[i], sizeof(buffer)-1);
+ strlcat(buffer, " ", sizeof(buffer)-1);
+ }
+
+ if(buffer[0] == 0) {
+ usage(argv[0]);
+ }
+
+ __android_log_print(priority, tag, "%s", buffer);
+
+ return 0;
+}
+
diff --git a/toolbox/ls.c b/toolbox/ls.c
new file mode 100644
index 0000000..9a89dd4
--- /dev/null
+++ b/toolbox/ls.c
@@ -0,0 +1,588 @@
+#include <dirent.h>
+#include <errno.h>
+#include <grp.h>
+#include <limits.h>
+#include <pwd.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <selinux/selinux.h>
+
+// simple dynamic array of strings.
+typedef struct {
+ int count;
+ int capacity;
+ void** items;
+} strlist_t;
+
+#define STRLIST_INITIALIZER { 0, 0, NULL }
+
+/* Used to iterate over a strlist_t
+ * _list :: pointer to strlist_t object
+ * _item :: name of local variable name defined within the loop with
+ * type 'char*'
+ * _stmnt :: C statement executed in each iteration
+ *
+ * This macro is only intended for simple uses. Do not add or remove items
+ * to/from the list during iteration.
+ */
+#define STRLIST_FOREACH(_list,_item,_stmnt) \
+ do { \
+ int _nn_##__LINE__ = 0; \
+ for (;_nn_##__LINE__ < (_list)->count; ++ _nn_##__LINE__) { \
+ char* _item = (char*)(_list)->items[_nn_##__LINE__]; \
+ _stmnt; \
+ } \
+ } while (0)
+
+static void dynarray_reserve_more( strlist_t *a, int count ) {
+ int old_cap = a->capacity;
+ int new_cap = old_cap;
+ const int max_cap = INT_MAX/sizeof(void*);
+ void** new_items;
+ int new_count = a->count + count;
+
+ if (count <= 0)
+ return;
+
+ if (count > max_cap - a->count)
+ abort();
+
+ new_count = a->count + count;
+
+ while (new_cap < new_count) {
+ old_cap = new_cap;
+ new_cap += (new_cap >> 2) + 4;
+ if (new_cap < old_cap || new_cap > max_cap) {
+ new_cap = max_cap;
+ }
+ }
+ new_items = realloc(a->items, new_cap*sizeof(void*));
+ if (new_items == NULL)
+ abort();
+
+ a->items = new_items;
+ a->capacity = new_cap;
+}
+
+void strlist_init( strlist_t *list ) {
+ list->count = list->capacity = 0;
+ list->items = NULL;
+}
+
+// append a new string made of the first 'slen' characters from 'str'
+// followed by a trailing zero.
+void strlist_append_b( strlist_t *list, const void* str, size_t slen ) {
+ char *copy = malloc(slen+1);
+ memcpy(copy, str, slen);
+ copy[slen] = '\0';
+ if (list->count >= list->capacity)
+ dynarray_reserve_more(list, 1);
+ list->items[list->count++] = copy;
+}
+
+// append the copy of a given input string to a strlist_t.
+void strlist_append_dup( strlist_t *list, const char *str) {
+ strlist_append_b(list, str, strlen(str));
+}
+
+// note: strlist_done will free all the strings owned by the list.
+void strlist_done( strlist_t *list ) {
+ STRLIST_FOREACH(list, string, free(string));
+ free(list->items);
+ list->items = NULL;
+ list->count = list->capacity = 0;
+}
+
+static int strlist_compare_strings(const void* a, const void* b) {
+ const char *sa = *(const char **)a;
+ const char *sb = *(const char **)b;
+ return strcmp(sa, sb);
+}
+
+/* sort the strings in a given list (using strcmp) */
+void strlist_sort( strlist_t *list ) {
+ if (list->count > 0) {
+ qsort(list->items, (size_t)list->count, sizeof(void*), strlist_compare_strings);
+ }
+}
+
+
+// bits for flags argument
+#define LIST_LONG (1 << 0)
+#define LIST_ALL (1 << 1)
+#define LIST_RECURSIVE (1 << 2)
+#define LIST_DIRECTORIES (1 << 3)
+#define LIST_SIZE (1 << 4)
+#define LIST_LONG_NUMERIC (1 << 5)
+#define LIST_CLASSIFY (1 << 6)
+#define LIST_MACLABEL (1 << 7)
+#define LIST_INODE (1 << 8)
+
+// fwd
+static int listpath(const char *name, int flags);
+
+static char mode2kind(mode_t mode)
+{
+ switch(mode & S_IFMT){
+ case S_IFSOCK: return 's';
+ case S_IFLNK: return 'l';
+ case S_IFREG: return '-';
+ case S_IFDIR: return 'd';
+ case S_IFBLK: return 'b';
+ case S_IFCHR: return 'c';
+ case S_IFIFO: return 'p';
+ default: return '?';
+ }
+}
+
+void strmode(mode_t mode, char *out)
+{
+ *out++ = mode2kind(mode);
+
+ *out++ = (mode & 0400) ? 'r' : '-';
+ *out++ = (mode & 0200) ? 'w' : '-';
+ if(mode & 04000) {
+ *out++ = (mode & 0100) ? 's' : 'S';
+ } else {
+ *out++ = (mode & 0100) ? 'x' : '-';
+ }
+ *out++ = (mode & 040) ? 'r' : '-';
+ *out++ = (mode & 020) ? 'w' : '-';
+ if(mode & 02000) {
+ *out++ = (mode & 010) ? 's' : 'S';
+ } else {
+ *out++ = (mode & 010) ? 'x' : '-';
+ }
+ *out++ = (mode & 04) ? 'r' : '-';
+ *out++ = (mode & 02) ? 'w' : '-';
+ if(mode & 01000) {
+ *out++ = (mode & 01) ? 't' : 'T';
+ } else {
+ *out++ = (mode & 01) ? 'x' : '-';
+ }
+ *out = 0;
+}
+
+static void user2str(uid_t uid, char *out, size_t out_size)
+{
+ struct passwd *pw = getpwuid(uid);
+ if(pw) {
+ strlcpy(out, pw->pw_name, out_size);
+ } else {
+ snprintf(out, out_size, "%d", uid);
+ }
+}
+
+static void group2str(gid_t gid, char *out, size_t out_size)
+{
+ struct group *gr = getgrgid(gid);
+ if(gr) {
+ strlcpy(out, gr->gr_name, out_size);
+ } else {
+ snprintf(out, out_size, "%d", gid);
+ }
+}
+
+static int show_total_size(const char *dirname, DIR *d, int flags)
+{
+ struct dirent *de;
+ char tmp[1024];
+ struct stat s;
+ int sum = 0;
+
+ /* run through the directory and sum up the file block sizes */
+ while ((de = readdir(d)) != 0) {
+ if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
+ continue;
+ if (de->d_name[0] == '.' && (flags & LIST_ALL) == 0)
+ continue;
+
+ if (strcmp(dirname, "/") == 0)
+ snprintf(tmp, sizeof(tmp), "/%s", de->d_name);
+ else
+ snprintf(tmp, sizeof(tmp), "%s/%s", dirname, de->d_name);
+
+ if (lstat(tmp, &s) < 0) {
+ fprintf(stderr, "stat failed on %s: %s\n", tmp, strerror(errno));
+ rewinddir(d);
+ return -1;
+ }
+
+ sum += s.st_blocks / 2;
+ }
+
+ printf("total %d\n", sum);
+ rewinddir(d);
+ return 0;
+}
+
+static int listfile_size(const char *path, const char *filename, struct stat *s,
+ int flags)
+{
+ if(!s || !path) {
+ return -1;
+ }
+
+ /* blocks are 512 bytes, we want output to be KB */
+ if ((flags & LIST_SIZE) != 0) {
+ printf("%lld ", (long long)s->st_blocks / 2);
+ }
+
+ if ((flags & LIST_CLASSIFY) != 0) {
+ char filetype = mode2kind(s->st_mode);
+ if (filetype != 'l') {
+ printf("%c ", filetype);
+ } else {
+ struct stat link_dest;
+ if (!stat(path, &link_dest)) {
+ printf("l%c ", mode2kind(link_dest.st_mode));
+ } else {
+ fprintf(stderr, "stat '%s' failed: %s\n", path, strerror(errno));
+ printf("l? ");
+ }
+ }
+ }
+
+ printf("%s\n", filename);
+
+ return 0;
+}
+
+static int listfile_long(const char *path, struct stat *s, int flags)
+{
+ char date[32];
+ char mode[16];
+ char user[32];
+ char group[32];
+ const char *name;
+
+ if(!s || !path) {
+ return -1;
+ }
+
+ /* name is anything after the final '/', or the whole path if none*/
+ name = strrchr(path, '/');
+ if(name == 0) {
+ name = path;
+ } else {
+ name++;
+ }
+
+ strmode(s->st_mode, mode);
+ if (flags & LIST_LONG_NUMERIC) {
+ snprintf(user, sizeof(user), "%u", s->st_uid);
+ snprintf(group, sizeof(group), "%u", s->st_gid);
+ } else {
+ user2str(s->st_uid, user, sizeof(user));
+ group2str(s->st_gid, group, sizeof(group));
+ }
+
+ strftime(date, 32, "%Y-%m-%d %H:%M", localtime((const time_t*)&s->st_mtime));
+ date[31] = 0;
+
+// 12345678901234567890123456789012345678901234567890123456789012345678901234567890
+// MMMMMMMM UUUUUUUU GGGGGGGGG XXXXXXXX YYYY-MM-DD HH:MM NAME (->LINK)
+
+ switch(s->st_mode & S_IFMT) {
+ case S_IFBLK:
+ case S_IFCHR:
+ printf("%s %-8s %-8s %3d, %3d %s %s\n",
+ mode, user, group,
+ major(s->st_rdev), minor(s->st_rdev),
+ date, name);
+ break;
+ case S_IFREG:
+ printf("%s %-8s %-8s %8lld %s %s\n",
+ mode, user, group, (long long)s->st_size, date, name);
+ break;
+ case S_IFLNK: {
+ char linkto[256];
+ ssize_t len;
+
+ len = readlink(path, linkto, 256);
+ if(len < 0) return -1;
+
+ if(len > 255) {
+ linkto[252] = '.';
+ linkto[253] = '.';
+ linkto[254] = '.';
+ linkto[255] = 0;
+ } else {
+ linkto[len] = 0;
+ }
+
+ printf("%s %-8s %-8s %s %s -> %s\n",
+ mode, user, group, date, name, linkto);
+ break;
+ }
+ default:
+ printf("%s %-8s %-8s %s %s\n",
+ mode, user, group, date, name);
+
+ }
+ return 0;
+}
+
+static int listfile_maclabel(const char *path, struct stat *s)
+{
+ char mode[16];
+ char user[32];
+ char group[32];
+ char *maclabel = NULL;
+ const char *name;
+
+ if(!s || !path) {
+ return -1;
+ }
+
+ /* name is anything after the final '/', or the whole path if none*/
+ name = strrchr(path, '/');
+ if(name == 0) {
+ name = path;
+ } else {
+ name++;
+ }
+
+ lgetfilecon(path, &maclabel);
+ if (!maclabel) {
+ return -1;
+ }
+
+ strmode(s->st_mode, mode);
+ user2str(s->st_uid, user, sizeof(user));
+ group2str(s->st_gid, group, sizeof(group));
+
+ switch(s->st_mode & S_IFMT) {
+ case S_IFLNK: {
+ char linkto[256];
+ ssize_t len;
+
+ len = readlink(path, linkto, sizeof(linkto));
+ if(len < 0) return -1;
+
+ if((size_t)len > sizeof(linkto)-1) {
+ linkto[sizeof(linkto)-4] = '.';
+ linkto[sizeof(linkto)-3] = '.';
+ linkto[sizeof(linkto)-2] = '.';
+ linkto[sizeof(linkto)-1] = 0;
+ } else {
+ linkto[len] = 0;
+ }
+
+ printf("%s %-8s %-8s %s %s -> %s\n",
+ mode, user, group, maclabel, name, linkto);
+ break;
+ }
+ default:
+ printf("%s %-8s %-8s %s %s\n",
+ mode, user, group, maclabel, name);
+
+ }
+
+ free(maclabel);
+
+ return 0;
+}
+
+static int listfile(const char *dirname, const char *filename, int flags)
+{
+ struct stat s;
+
+ if ((flags & (LIST_LONG | LIST_SIZE | LIST_CLASSIFY | LIST_MACLABEL | LIST_INODE)) == 0) {
+ printf("%s\n", filename);
+ return 0;
+ }
+
+ char tmp[4096];
+ const char* pathname = filename;
+
+ if (dirname != NULL) {
+ snprintf(tmp, sizeof(tmp), "%s/%s", dirname, filename);
+ pathname = tmp;
+ } else {
+ pathname = filename;
+ }
+
+ if(lstat(pathname, &s) < 0) {
+ fprintf(stderr, "lstat '%s' failed: %s\n", pathname, strerror(errno));
+ return -1;
+ }
+
+ if(flags & LIST_INODE) {
+ printf("%8llu ", (unsigned long long)s.st_ino);
+ }
+
+ if ((flags & LIST_MACLABEL) != 0) {
+ return listfile_maclabel(pathname, &s);
+ } else if ((flags & LIST_LONG) != 0) {
+ return listfile_long(pathname, &s, flags);
+ } else /*((flags & LIST_SIZE) != 0)*/ {
+ return listfile_size(pathname, filename, &s, flags);
+ }
+}
+
+static int listdir(const char *name, int flags)
+{
+ char tmp[4096];
+ DIR *d;
+ struct dirent *de;
+ strlist_t files = STRLIST_INITIALIZER;
+
+ d = opendir(name);
+ if(d == 0) {
+ fprintf(stderr, "opendir failed, %s\n", strerror(errno));
+ return -1;
+ }
+
+ if ((flags & LIST_SIZE) != 0) {
+ show_total_size(name, d, flags);
+ }
+
+ while((de = readdir(d)) != 0){
+ if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue;
+ if(de->d_name[0] == '.' && (flags & LIST_ALL) == 0) continue;
+
+ strlist_append_dup(&files, de->d_name);
+ }
+
+ strlist_sort(&files);
+ STRLIST_FOREACH(&files, filename, listfile(name, filename, flags));
+ strlist_done(&files);
+
+ if (flags & LIST_RECURSIVE) {
+ strlist_t subdirs = STRLIST_INITIALIZER;
+
+ rewinddir(d);
+
+ while ((de = readdir(d)) != 0) {
+ struct stat s;
+ int err;
+
+ if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+ continue;
+ if (de->d_name[0] == '.' && (flags & LIST_ALL) == 0)
+ continue;
+
+ if (!strcmp(name, "/"))
+ snprintf(tmp, sizeof(tmp), "/%s", de->d_name);
+ else
+ snprintf(tmp, sizeof(tmp), "%s/%s", name, de->d_name);
+
+ /*
+ * If the name ends in a '/', use stat() so we treat it like a
+ * directory even if it's a symlink.
+ */
+ if (tmp[strlen(tmp)-1] == '/')
+ err = stat(tmp, &s);
+ else
+ err = lstat(tmp, &s);
+
+ if (err < 0) {
+ perror(tmp);
+ closedir(d);
+ return -1;
+ }
+
+ if (S_ISDIR(s.st_mode)) {
+ strlist_append_dup(&subdirs, tmp);
+ }
+ }
+ strlist_sort(&subdirs);
+ STRLIST_FOREACH(&subdirs, path, {
+ printf("\n%s:\n", path);
+ listdir(path, flags);
+ });
+ strlist_done(&subdirs);
+ }
+
+ closedir(d);
+ return 0;
+}
+
+static int listpath(const char *name, int flags)
+{
+ struct stat s;
+ int err;
+
+ /*
+ * If the name ends in a '/', use stat() so we treat it like a
+ * directory even if it's a symlink.
+ */
+ if (name[strlen(name)-1] == '/')
+ err = stat(name, &s);
+ else
+ err = lstat(name, &s);
+
+ if (err < 0) {
+ perror(name);
+ return -1;
+ }
+
+ if ((flags & LIST_DIRECTORIES) == 0 && S_ISDIR(s.st_mode)) {
+ if (flags & LIST_RECURSIVE)
+ printf("\n%s:\n", name);
+ return listdir(name, flags);
+ } else {
+ /* yeah this calls stat() again*/
+ return listfile(NULL, name, flags);
+ }
+}
+
+int ls_main(int argc, char **argv)
+{
+ int flags = 0;
+
+ if(argc > 1) {
+ int i;
+ int err = 0;
+ strlist_t files = STRLIST_INITIALIZER;
+
+ for (i = 1; i < argc; i++) {
+ if (argv[i][0] == '-') {
+ /* an option ? */
+ const char *arg = argv[i]+1;
+ while (arg[0]) {
+ switch (arg[0]) {
+ case 'l': flags |= LIST_LONG; break;
+ case 'n': flags |= LIST_LONG | LIST_LONG_NUMERIC; break;
+ case 's': flags |= LIST_SIZE; break;
+ case 'R': flags |= LIST_RECURSIVE; break;
+ case 'd': flags |= LIST_DIRECTORIES; break;
+ case 'Z': flags |= LIST_MACLABEL; break;
+ case 'a': flags |= LIST_ALL; break;
+ case 'F': flags |= LIST_CLASSIFY; break;
+ case 'i': flags |= LIST_INODE; break;
+ default:
+ fprintf(stderr, "%s: Unknown option '-%c'. Aborting.\n", "ls", arg[0]);
+ exit(1);
+ }
+ arg++;
+ }
+ } else {
+ /* not an option ? */
+ strlist_append_dup(&files, argv[i]);
+ }
+ }
+
+ if (files.count > 0) {
+ STRLIST_FOREACH(&files, path, {
+ if (listpath(path, flags) != 0) {
+ err = EXIT_FAILURE;
+ }
+ });
+ strlist_done(&files);
+ return err;
+ }
+ }
+
+ // list working directory if no files or directories were specified
+ return listpath(".", flags);
+}
diff --git a/toolbox/lsof.c b/toolbox/lsof.c
new file mode 100644
index 0000000..982f5aa
--- /dev/null
+++ b/toolbox/lsof.c
@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) 2010, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <pwd.h>
+#include <sys/stat.h>
+
+#define BUF_MAX 1024
+#define CMD_DISPLAY_MAX (9 + 1)
+#define USER_DISPLAY_MAX (10 + 1)
+
+struct pid_info_t {
+ pid_t pid;
+ char user[USER_DISPLAY_MAX];
+
+ char cmdline[CMD_DISPLAY_MAX];
+
+ char path[PATH_MAX];
+ ssize_t parent_length;
+};
+
+static void print_header()
+{
+ printf("%-9s %5s %10s %4s %9s %18s %9s %10s %s\n",
+ "COMMAND",
+ "PID",
+ "USER",
+ "FD",
+ "TYPE",
+ "DEVICE",
+ "SIZE/OFF",
+ "NODE",
+ "NAME");
+}
+
+static void print_type(char *type, struct pid_info_t* info)
+{
+ static ssize_t link_dest_size;
+ static char link_dest[PATH_MAX];
+
+ strlcat(info->path, type, sizeof(info->path));
+ if ((link_dest_size = readlink(info->path, link_dest, sizeof(link_dest)-1)) < 0) {
+ if (errno == ENOENT)
+ goto out;
+
+ snprintf(link_dest, sizeof(link_dest), "%s (readlink: %s)", info->path, strerror(errno));
+ } else {
+ link_dest[link_dest_size] = '\0';
+ }
+
+ // Things that are just the root filesystem are uninteresting (we already know)
+ if (!strcmp(link_dest, "/"))
+ goto out;
+
+ printf("%-9s %5d %10s %4s %9s %18s %9s %10s %s\n",
+ info->cmdline, info->pid, info->user, type,
+ "???", "???", "???", "???", link_dest);
+
+out:
+ info->path[info->parent_length] = '\0';
+}
+
+// Prints out all file that have been memory mapped
+static void print_maps(struct pid_info_t* info)
+{
+ FILE *maps;
+ size_t offset;
+ char device[10];
+ long int inode;
+ char file[PATH_MAX];
+
+ strlcat(info->path, "maps", sizeof(info->path));
+
+ maps = fopen(info->path, "r");
+ if (!maps)
+ goto out;
+
+ while (fscanf(maps, "%*x-%*x %*s %zx %s %ld %s\n", &offset, device, &inode,
+ file) == 4) {
+ // We don't care about non-file maps
+ if (inode == 0 || !strcmp(device, "00:00"))
+ continue;
+
+ printf("%-9s %5d %10s %4s %9s %18s %9zd %10ld %s\n",
+ info->cmdline, info->pid, info->user, "mem",
+ "???", device, offset, inode, file);
+ }
+
+ fclose(maps);
+
+out:
+ info->path[info->parent_length] = '\0';
+}
+
+// Prints out all open file descriptors
+static void print_fds(struct pid_info_t* info)
+{
+ static char* fd_path = "fd/";
+ strlcat(info->path, fd_path, sizeof(info->path));
+
+ int previous_length = info->parent_length;
+ info->parent_length += strlen(fd_path);
+
+ DIR *dir = opendir(info->path);
+ if (dir == NULL) {
+ char msg[BUF_MAX];
+ snprintf(msg, sizeof(msg), "%s (opendir: %s)", info->path, strerror(errno));
+ printf("%-9s %5d %10s %4s %9s %18s %9s %10s %s\n",
+ info->cmdline, info->pid, info->user, "FDS",
+ "", "", "", "", msg);
+ goto out;
+ }
+
+ struct dirent* de;
+ while ((de = readdir(dir))) {
+ if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+ continue;
+
+ print_type(de->d_name, info);
+ }
+ closedir(dir);
+
+out:
+ info->parent_length = previous_length;
+ info->path[info->parent_length] = '\0';
+}
+
+static void lsof_dumpinfo(pid_t pid)
+{
+ int fd;
+ struct pid_info_t info;
+ struct stat pidstat;
+ struct passwd *pw;
+
+ info.pid = pid;
+ snprintf(info.path, sizeof(info.path), "/proc/%d/", pid);
+ info.parent_length = strlen(info.path);
+
+ // Get the UID by calling stat on the proc/pid directory.
+ if (!stat(info.path, &pidstat)) {
+ pw = getpwuid(pidstat.st_uid);
+ if (pw) {
+ strlcpy(info.user, pw->pw_name, sizeof(info.user));
+ } else {
+ snprintf(info.user, USER_DISPLAY_MAX, "%d", (int)pidstat.st_uid);
+ }
+ } else {
+ strcpy(info.user, "???");
+ }
+
+ // Read the command line information; each argument is terminated with NULL.
+ strlcat(info.path, "cmdline", sizeof(info.path));
+ fd = open(info.path, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "Couldn't read %s\n", info.path);
+ return;
+ }
+
+ char cmdline[PATH_MAX];
+ int numRead = read(fd, cmdline, sizeof(cmdline) - 1);
+ close(fd);
+
+ if (numRead < 0) {
+ fprintf(stderr, "Error reading cmdline: %s: %s\n", info.path, strerror(errno));
+ return;
+ }
+
+ cmdline[numRead] = '\0';
+
+ // We only want the basename of the cmdline
+ strlcpy(info.cmdline, basename(cmdline), sizeof(info.cmdline));
+
+ // Read each of these symlinks
+ print_type("cwd", &info);
+ print_type("exe", &info);
+ print_type("root", &info);
+
+ print_fds(&info);
+ print_maps(&info);
+}
+
+int lsof_main(int argc, char *argv[])
+{
+ long int pid = 0;
+ char* endptr;
+ if (argc == 2) {
+ pid = strtol(argv[1], &endptr, 10);
+ }
+
+ print_header();
+
+ if (pid) {
+ lsof_dumpinfo(pid);
+ } else {
+ DIR *dir = opendir("/proc");
+ if (dir == NULL) {
+ fprintf(stderr, "Couldn't open /proc\n");
+ return -1;
+ }
+
+ struct dirent* de;
+ while ((de = readdir(dir))) {
+ if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+ continue;
+
+ // Only inspect directories that are PID numbers
+ pid = strtol(de->d_name, &endptr, 10);
+ if (*endptr != '\0')
+ continue;
+
+ lsof_dumpinfo(pid);
+ }
+ closedir(dir);
+ }
+
+ return 0;
+}
diff --git a/toolbox/nandread.c b/toolbox/nandread.c
new file mode 100644
index 0000000..bd19942
--- /dev/null
+++ b/toolbox/nandread.c
@@ -0,0 +1,287 @@
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <mtd/mtd-user.h>
+#include <sys/ioctl.h>
+
+static int test_empty(const char *buf, size_t size)
+{
+ while(size--) {
+ if (*buf++ != (char) 0xff)
+ return 0;
+ }
+ return 1;
+}
+
+int nandread_main(int argc, char **argv)
+{
+ char *devname = NULL;
+ char *filename = NULL;
+ char *statusfilename = NULL;
+ char *statusext = ".stat";
+ int fd;
+ int outfd = -1;
+ FILE *statusfile = NULL;
+ int ret;
+ int verbose = 0;
+ void *buffer;
+ loff_t pos, opos, end, bpos;
+ loff_t start = 0, len = 0;
+ int c;
+ int i;
+ int empty_pages = 0;
+ int page_count = 0;
+ int bad_block;
+ int rawmode = 0;
+ uint32_t *oob_data;
+ uint8_t *oob_fixed;
+ size_t spare_size = 64;
+ struct mtd_info_user mtdinfo;
+ struct mtd_ecc_stats initial_ecc, last_ecc, ecc;
+ struct mtd_oob_buf oobbuf;
+ nand_ecclayout_t ecclayout;
+
+ do {
+ c = getopt(argc, argv, "d:f:s:S:L:Rhv");
+ if (c == EOF)
+ break;
+ switch (c) {
+ case 'd':
+ devname = optarg;
+ break;
+ case 'f':
+ filename = optarg;
+ break;
+ case 's':
+ spare_size = atoi(optarg);
+ break;
+ case 'S':
+ start = strtoll(optarg, NULL, 0);
+ break;
+ case 'L':
+ len = strtoll(optarg, NULL, 0);
+ break;
+ case 'R':
+ rawmode = 1;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'h':
+ fprintf(stderr, "%s [-d <dev>] [-f file] [-s sparesize] [-vh]\n"
+ " -d <dev> Read from <dev>\n"
+ " -f <file> Write to <file>\n"
+ " -s <size> Number of spare bytes in file (default 64)\n"
+ " -R Raw mode\n"
+ " -S <start> Start offset (default 0)\n"
+ " -L <len> Length (default 0)\n"
+ " -v Print info\n"
+ " -h Print help\n", argv[0]);
+ return -1;
+ case '?':
+ fprintf(stderr, "%s: invalid option -%c\n",
+ argv[0], optopt);
+ exit(1);
+ }
+ } while (1);
+
+ if (optind < argc) {
+ fprintf(stderr, "%s: extra arguments\n", argv[0]);
+ return 1;
+ }
+ if (!devname) {
+ fprintf(stderr, "%s: specify device name\n", argv[0]);
+ return 1;
+ }
+
+ fd = open(devname, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "cannot open %s, %s\n", devname, strerror(errno));
+ return 1;
+ }
+
+ if (filename) {
+ outfd = creat(filename, 0666);
+ if (outfd < 0) {
+ fprintf(stderr, "cannot open %s, %s\n", filename, strerror(errno));
+ return 1;
+ }
+ statusfilename = malloc(strlen(filename) + strlen(statusext) + 1);
+ strcpy(statusfilename, filename);
+ strcat(statusfilename, statusext);
+ statusfile = fopen(statusfilename, "w+");
+ if (!statusfile) {
+ fprintf(stderr, "cannot open %s, %s\n", statusfilename, strerror(errno));
+ return 1;
+ }
+ }
+
+ ret = ioctl(fd, MEMGETINFO, &mtdinfo);
+ if (ret) {
+ fprintf(stderr, "failed get mtd info for %s, %s\n",
+ devname, strerror(errno));
+ return 1;
+ }
+
+ if (verbose) {
+ printf("size: %u\n", mtdinfo.size);
+ printf("erase size: %u\n", mtdinfo.erasesize);
+ printf("write size: %u\n", mtdinfo.writesize);
+ printf("oob size: %u\n", mtdinfo.oobsize);
+ }
+
+ buffer = malloc(mtdinfo.writesize + mtdinfo.oobsize + spare_size);
+ if (!buffer) {
+ fprintf(stderr, "failed allocate readbuffer size %u\n",
+ mtdinfo.writesize + mtdinfo.oobsize);
+ return 1;
+ }
+
+ oobbuf.length = mtdinfo.oobsize;
+ oob_data = (uint32_t *)((uint8_t *)buffer + mtdinfo.writesize);
+ memset(oob_data, 0xff, mtdinfo.oobsize + spare_size);
+ oobbuf.ptr = (uint8_t *)oob_data + spare_size;
+
+ ret = ioctl(fd, ECCGETLAYOUT, &ecclayout);
+ if (ret) {
+ fprintf(stderr, "failed get ecc layout for %s, %s\n",
+ devname, strerror(errno));
+ return 1;
+ }
+ if (verbose) {
+ printf("ecc bytes: %u\n", ecclayout.eccbytes);
+ printf("oobavail: %u\n", ecclayout.oobavail);
+ }
+ if (ecclayout.oobavail > spare_size)
+ printf("oobavail, %d > image spare size, %zu\n", ecclayout.oobavail, spare_size);
+
+ ret = ioctl(fd, ECCGETSTATS, &initial_ecc);
+ if (ret) {
+ fprintf(stderr, "failed get ecc stats for %s, %s\n",
+ devname, strerror(errno));
+ return 1;
+ }
+ last_ecc = initial_ecc;
+
+ if (verbose) {
+ printf("initial ecc corrected: %u\n", initial_ecc.corrected);
+ printf("initial ecc failed: %u\n", initial_ecc.failed);
+ printf("initial ecc badblocks: %u\n", initial_ecc.badblocks);
+ printf("initial ecc bbtblocks: %u\n", initial_ecc.bbtblocks);
+ }
+
+ if (rawmode) {
+ rawmode = mtdinfo.oobsize;
+ ret = ioctl(fd, MTDFILEMODE, MTD_FILE_MODE_RAW);
+ if (ret) {
+ fprintf(stderr, "failed set raw mode for %s, %s\n",
+ devname, strerror(errno));
+ return 1;
+ }
+ }
+
+ end = len ? (start + len) : mtdinfo.size;
+ for (pos = start, opos = 0; pos < end; pos += mtdinfo.writesize) {
+ bad_block = 0;
+ if (verbose > 3)
+ printf("reading at %" PRIx64 "\n", pos);
+ lseek64(fd, pos, SEEK_SET);
+ ret = read(fd, buffer, mtdinfo.writesize + rawmode);
+ if (ret < (int)mtdinfo.writesize) {
+ fprintf(stderr, "short read at %" PRIx64 ", %d\n", pos, ret);
+ bad_block = 2;
+ }
+ if (!rawmode) {
+ oobbuf.start = pos;
+ ret = ioctl(fd, MEMREADOOB, &oobbuf);
+ if (ret) {
+ fprintf(stderr, "failed to read oob data at %" PRIx64 ", %d\n", pos, ret);
+ bad_block = 2;
+ }
+ }
+ ret = ioctl(fd, ECCGETSTATS, &ecc);
+ if (ret) {
+ fprintf(stderr, "failed get ecc stats for %s, %s\n",
+ devname, strerror(errno));
+ return 1;
+ }
+ bpos = pos / mtdinfo.erasesize * mtdinfo.erasesize;
+ ret = ioctl(fd, MEMGETBADBLOCK, &bpos);
+ if (ret && errno != EOPNOTSUPP) {
+ printf("badblock at %" PRIx64 "\n", pos);
+ bad_block = 1;
+ }
+ if (ecc.corrected != last_ecc.corrected)
+ printf("ecc corrected, %u, at %" PRIx64 "\n", ecc.corrected - last_ecc.corrected, pos);
+ if (ecc.failed != last_ecc.failed)
+ printf("ecc failed, %u, at %" PRIx64 "\n", ecc.failed - last_ecc.failed, pos);
+ if (ecc.badblocks != last_ecc.badblocks)
+ printf("ecc badblocks, %u, at %" PRIx64 "\n", ecc.badblocks - last_ecc.badblocks, pos);
+ if (ecc.bbtblocks != last_ecc.bbtblocks)
+ printf("ecc bbtblocks, %u, at %" PRIx64 "\n", ecc.bbtblocks - last_ecc.bbtblocks, pos);
+
+ if (!rawmode) {
+ oob_fixed = (uint8_t *)oob_data;
+ for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES; i++) {
+ int len = ecclayout.oobfree[i].length;
+ if (oob_fixed + len > oobbuf.ptr)
+ len = oobbuf.ptr - oob_fixed;
+ if (len) {
+ memcpy(oob_fixed, oobbuf.ptr + ecclayout.oobfree[i].offset, len);
+ oob_fixed += len;
+ }
+ }
+ }
+
+ if (outfd >= 0) {
+ ret = write(outfd, buffer, mtdinfo.writesize + spare_size);
+ if (ret < (int)(mtdinfo.writesize + spare_size)) {
+ fprintf(stderr, "short write at %" PRIx64 ", %d\n", pos, ret);
+ close(outfd);
+ outfd = -1;
+ }
+ if (ecc.corrected != last_ecc.corrected)
+ fprintf(statusfile, "%08" PRIx64 ": ecc corrected\n", opos);
+ if (ecc.failed != last_ecc.failed)
+ fprintf(statusfile, "%08" PRIx64 ": ecc failed\n", opos);
+ if (bad_block == 1)
+ fprintf(statusfile, "%08" PRIx64 ": badblock\n", opos);
+ if (bad_block == 2)
+ fprintf(statusfile, "%08" PRIx64 ": read error\n", opos);
+ opos += mtdinfo.writesize + spare_size;
+ }
+
+ last_ecc = ecc;
+ page_count++;
+ if (test_empty(buffer, mtdinfo.writesize + mtdinfo.oobsize + spare_size))
+ empty_pages++;
+ else if (verbose > 2 || (verbose > 1 && !(pos & (mtdinfo.erasesize - 1))))
+ printf("page at %" PRIx64 " (%d oobbytes): %08x %08x %08x %08x "
+ "%08x %08x %08x %08x\n", pos, oobbuf.start,
+ oob_data[0], oob_data[1], oob_data[2], oob_data[3],
+ oob_data[4], oob_data[5], oob_data[6], oob_data[7]);
+ }
+
+ if (outfd >= 0) {
+ fprintf(statusfile, "read %d pages, %d empty\n", page_count, empty_pages);
+ fprintf(statusfile, "total ecc corrected, %u\n", ecc.corrected - initial_ecc.corrected);
+ fprintf(statusfile, "total ecc failed, %u\n", ecc.failed - initial_ecc.failed);
+ fprintf(statusfile, "total ecc badblocks, %u\n", ecc.badblocks - initial_ecc.badblocks);
+ fprintf(statusfile, "total ecc bbtblocks, %u\n", ecc.bbtblocks - initial_ecc.bbtblocks);
+ }
+ if (verbose) {
+ printf("total ecc corrected, %u\n", ecc.corrected - initial_ecc.corrected);
+ printf("total ecc failed, %u\n", ecc.failed - initial_ecc.failed);
+ printf("total ecc badblocks, %u\n", ecc.badblocks - initial_ecc.badblocks);
+ printf("total ecc bbtblocks, %u\n", ecc.bbtblocks - initial_ecc.bbtblocks);
+ }
+ printf("read %d pages, %d empty\n", page_count, empty_pages);
+
+ return 0;
+}
diff --git a/toolbox/newfs_msdos.c b/toolbox/newfs_msdos.c
new file mode 100644
index 0000000..5b98a01
--- /dev/null
+++ b/toolbox/newfs_msdos.c
@@ -0,0 +1,1098 @@
+/*
+ * Copyright (c) 1998 Robert Nordier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD: src/sbin/newfs_msdos/newfs_msdos.c,v 1.33 2009/04/11 14:56:29 ed Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+
+#ifndef ANDROID
+#include <sys/fdcio.h>
+#include <sys/disk.h>
+#include <sys/disklabel.h>
+#include <sys/mount.h>
+#else
+#include <stdarg.h>
+#include <linux/fs.h>
+#include <linux/hdreg.h>
+#endif
+
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#define MAXU16 0xffff /* maximum unsigned 16-bit quantity */
+#define BPN 4 /* bits per nibble */
+#define NPB 2 /* nibbles per byte */
+
+#define DOSMAGIC 0xaa55 /* DOS magic number */
+#define MINBPS 512 /* minimum bytes per sector */
+#define MAXSPC 128 /* maximum sectors per cluster */
+#define MAXNFT 16 /* maximum number of FATs */
+#define DEFBLK 4096 /* default block size */
+#define DEFBLK16 2048 /* default block size FAT16 */
+#define DEFRDE 512 /* default root directory entries */
+#define RESFTE 2 /* reserved FAT entries */
+#define MINCLS12 1 /* minimum FAT12 clusters */
+#define MINCLS16 0x1000 /* minimum FAT16 clusters */
+#define MINCLS32 2 /* minimum FAT32 clusters */
+#define MAXCLS12 0xfed /* maximum FAT12 clusters */
+#define MAXCLS16 0xfff5 /* maximum FAT16 clusters */
+#define MAXCLS32 0xffffff5 /* maximum FAT32 clusters */
+
+#define mincls(fat) ((fat) == 12 ? MINCLS12 : \
+ (fat) == 16 ? MINCLS16 : \
+ MINCLS32)
+
+#define maxcls(fat) ((fat) == 12 ? MAXCLS12 : \
+ (fat) == 16 ? MAXCLS16 : \
+ MAXCLS32)
+
+#define mk1(p, x) \
+ (p) = (u_int8_t)(x)
+
+#define mk2(p, x) \
+ (p)[0] = (u_int8_t)(x), \
+ (p)[1] = (u_int8_t)((x) >> 010)
+
+#define mk4(p, x) \
+ (p)[0] = (u_int8_t)(x), \
+ (p)[1] = (u_int8_t)((x) >> 010), \
+ (p)[2] = (u_int8_t)((x) >> 020), \
+ (p)[3] = (u_int8_t)((x) >> 030)
+
+#define argto1(arg, lo, msg) argtou(arg, lo, 0xff, msg)
+#define argto2(arg, lo, msg) argtou(arg, lo, 0xffff, msg)
+#define argto4(arg, lo, msg) argtou(arg, lo, 0xffffffff, msg)
+#define argtox(arg, lo, msg) argtou(arg, lo, UINT_MAX, msg)
+
+struct bs {
+ u_int8_t jmp[3]; /* bootstrap entry point */
+ u_int8_t oem[8]; /* OEM name and version */
+};
+
+struct bsbpb {
+ u_int8_t bps[2]; /* bytes per sector */
+ u_int8_t spc; /* sectors per cluster */
+ u_int8_t res[2]; /* reserved sectors */
+ u_int8_t nft; /* number of FATs */
+ u_int8_t rde[2]; /* root directory entries */
+ u_int8_t sec[2]; /* total sectors */
+ u_int8_t mid; /* media descriptor */
+ u_int8_t spf[2]; /* sectors per FAT */
+ u_int8_t spt[2]; /* sectors per track */
+ u_int8_t hds[2]; /* drive heads */
+ u_int8_t hid[4]; /* hidden sectors */
+ u_int8_t bsec[4]; /* big total sectors */
+};
+
+struct bsxbpb {
+ u_int8_t bspf[4]; /* big sectors per FAT */
+ u_int8_t xflg[2]; /* FAT control flags */
+ u_int8_t vers[2]; /* file system version */
+ u_int8_t rdcl[4]; /* root directory start cluster */
+ u_int8_t infs[2]; /* file system info sector */
+ u_int8_t bkbs[2]; /* backup boot sector */
+ u_int8_t rsvd[12]; /* reserved */
+};
+
+struct bsx {
+ u_int8_t drv; /* drive number */
+ u_int8_t rsvd; /* reserved */
+ u_int8_t sig; /* extended boot signature */
+ u_int8_t volid[4]; /* volume ID number */
+ u_int8_t label[11]; /* volume label */
+ u_int8_t type[8]; /* file system type */
+};
+
+struct de {
+ u_int8_t namext[11]; /* name and extension */
+ u_int8_t attr; /* attributes */
+ u_int8_t rsvd[10]; /* reserved */
+ u_int8_t time[2]; /* creation time */
+ u_int8_t date[2]; /* creation date */
+ u_int8_t clus[2]; /* starting cluster */
+ u_int8_t size[4]; /* size */
+};
+
+struct bpb {
+ u_int bps; /* bytes per sector */
+ u_int spc; /* sectors per cluster */
+ u_int res; /* reserved sectors */
+ u_int nft; /* number of FATs */
+ u_int rde; /* root directory entries */
+ u_int sec; /* total sectors */
+ u_int mid; /* media descriptor */
+ u_int spf; /* sectors per FAT */
+ u_int spt; /* sectors per track */
+ u_int hds; /* drive heads */
+ u_int hid; /* hidden sectors */
+ u_int bsec; /* big total sectors */
+ u_int bspf; /* big sectors per FAT */
+ u_int rdcl; /* root directory start cluster */
+ u_int infs; /* file system info sector */
+ u_int bkbs; /* backup boot sector */
+};
+
+#define BPBGAP 0, 0, 0, 0, 0, 0
+
+static struct {
+ const char *name;
+ struct bpb bpb;
+} const stdfmt[] = {
+ {"160", {512, 1, 1, 2, 64, 320, 0xfe, 1, 8, 1, BPBGAP}},
+ {"180", {512, 1, 1, 2, 64, 360, 0xfc, 2, 9, 1, BPBGAP}},
+ {"320", {512, 2, 1, 2, 112, 640, 0xff, 1, 8, 2, BPBGAP}},
+ {"360", {512, 2, 1, 2, 112, 720, 0xfd, 2, 9, 2, BPBGAP}},
+ {"640", {512, 2, 1, 2, 112, 1280, 0xfb, 2, 8, 2, BPBGAP}},
+ {"720", {512, 2, 1, 2, 112, 1440, 0xf9, 3, 9, 2, BPBGAP}},
+ {"1200", {512, 1, 1, 2, 224, 2400, 0xf9, 7, 15, 2, BPBGAP}},
+ {"1232", {1024,1, 1, 2, 192, 1232, 0xfe, 2, 8, 2, BPBGAP}},
+ {"1440", {512, 1, 1, 2, 224, 2880, 0xf0, 9, 18, 2, BPBGAP}},
+ {"2880", {512, 2, 1, 2, 240, 5760, 0xf0, 9, 36, 2, BPBGAP}}
+};
+
+static const u_int8_t bootcode[] = {
+ 0xfa, /* cli */
+ 0x31, 0xc0, /* xor ax,ax */
+ 0x8e, 0xd0, /* mov ss,ax */
+ 0xbc, 0x00, 0x7c, /* mov sp,7c00h */
+ 0xfb, /* sti */
+ 0x8e, 0xd8, /* mov ds,ax */
+ 0xe8, 0x00, 0x00, /* call $ + 3 */
+ 0x5e, /* pop si */
+ 0x83, 0xc6, 0x19, /* add si,+19h */
+ 0xbb, 0x07, 0x00, /* mov bx,0007h */
+ 0xfc, /* cld */
+ 0xac, /* lodsb */
+ 0x84, 0xc0, /* test al,al */
+ 0x74, 0x06, /* jz $ + 8 */
+ 0xb4, 0x0e, /* mov ah,0eh */
+ 0xcd, 0x10, /* int 10h */
+ 0xeb, 0xf5, /* jmp $ - 9 */
+ 0x30, 0xe4, /* xor ah,ah */
+ 0xcd, 0x16, /* int 16h */
+ 0xcd, 0x19, /* int 19h */
+ 0x0d, 0x0a,
+ 'N', 'o', 'n', '-', 's', 'y', 's', 't',
+ 'e', 'm', ' ', 'd', 'i', 's', 'k',
+ 0x0d, 0x0a,
+ 'P', 'r', 'e', 's', 's', ' ', 'a', 'n',
+ 'y', ' ', 'k', 'e', 'y', ' ', 't', 'o',
+ ' ', 'r', 'e', 'b', 'o', 'o', 't',
+ 0x0d, 0x0a,
+ 0
+};
+
+static void check_mounted(const char *, mode_t);
+static void getstdfmt(const char *, struct bpb *);
+static void getdiskinfo(int, const char *, const char *, int, struct bpb *);
+static void print_bpb(struct bpb *);
+static u_int ckgeom(const char *, u_int, const char *);
+static u_int argtou(const char *, u_int, u_int, const char *);
+static off_t argtooff(const char *, const char *);
+static int oklabel(const char *);
+static void mklabel(u_int8_t *, const char *);
+static void setstr(u_int8_t *, const char *, size_t);
+static void usage(void);
+
+/*
+ * Construct a FAT12, FAT16, or FAT32 file system.
+ */
+int newfs_msdos_main(int argc, char *argv[])
+{
+ static const char opts[] = "@:NAB:C:F:I:L:O:S:a:b:c:e:f:h:i:k:m:n:o:r:s:u:";
+ const char *opt_B = NULL, *opt_L = NULL, *opt_O = NULL, *opt_f = NULL;
+ u_int opt_F = 0, opt_I = 0, opt_S = 0, opt_a = 0, opt_b = 0, opt_c = 0;
+ u_int opt_e = 0, opt_h = 0, opt_i = 0, opt_k = 0, opt_m = 0, opt_n = 0;
+ u_int opt_o = 0, opt_r = 0, opt_s = 0, opt_u = 0;
+ u_int opt_A = 0;
+ int opt_N = 0;
+ int Iflag = 0, mflag = 0, oflag = 0;
+ char buf[MAXPATHLEN];
+ struct stat sb;
+ struct timeval tv;
+ struct bpb bpb;
+ struct tm *tm;
+ struct bs *bs;
+ struct bsbpb *bsbpb;
+ struct bsxbpb *bsxbpb;
+ struct bsx *bsx;
+ struct de *de;
+ u_int8_t *img;
+ const char *fname, *dtype, *bname;
+ ssize_t n;
+ time_t now;
+ u_int fat, bss, rds, cls, dir, lsn, x, x1, x2;
+ u_int extra_res, alignment=0, set_res, set_spf, set_spc, tempx, attempts=0;
+ int ch, fd, fd1;
+ off_t opt_create = 0, opt_ofs = 0;
+
+ while ((ch = getopt(argc, argv, opts)) != -1)
+ switch (ch) {
+ case '@':
+ opt_ofs = argtooff(optarg, "offset");
+ break;
+ case 'N':
+ opt_N = 1;
+ break;
+ case 'A':
+ opt_A = 1;
+ break;
+ case 'B':
+ opt_B = optarg;
+ break;
+ case 'C':
+ opt_create = argtooff(optarg, "create size");
+ break;
+ case 'F':
+ if (strcmp(optarg, "12") && strcmp(optarg, "16") && strcmp(optarg, "32"))
+ errx(1, "%s: bad FAT type", optarg);
+ opt_F = atoi(optarg);
+ break;
+ case 'I':
+ opt_I = argto4(optarg, 0, "volume ID");
+ Iflag = 1;
+ break;
+ case 'L':
+ if (!oklabel(optarg))
+ errx(1, "%s: bad volume label", optarg);
+ opt_L = optarg;
+ break;
+ case 'O':
+ if (strlen(optarg) > 8)
+ errx(1, "%s: bad OEM string", optarg);
+ opt_O = optarg;
+ break;
+ case 'S':
+ opt_S = argto2(optarg, 1, "bytes/sector");
+ break;
+ case 'a':
+ opt_a = argto4(optarg, 1, "sectors/FAT");
+ break;
+ case 'b':
+ opt_b = argtox(optarg, 1, "block size");
+ opt_c = 0;
+ break;
+ case 'c':
+ opt_c = argto1(optarg, 1, "sectors/cluster");
+ opt_b = 0;
+ break;
+ case 'e':
+ opt_e = argto2(optarg, 1, "directory entries");
+ break;
+ case 'f':
+ opt_f = optarg;
+ break;
+ case 'h':
+ opt_h = argto2(optarg, 1, "drive heads");
+ break;
+ case 'i':
+ opt_i = argto2(optarg, 1, "info sector");
+ break;
+ case 'k':
+ opt_k = argto2(optarg, 1, "backup sector");
+ break;
+ case 'm':
+ opt_m = argto1(optarg, 0, "media descriptor");
+ mflag = 1;
+ break;
+ case 'n':
+ opt_n = argto1(optarg, 1, "number of FATs");
+ break;
+ case 'o':
+ opt_o = argto4(optarg, 0, "hidden sectors");
+ oflag = 1;
+ break;
+ case 'r':
+ opt_r = argto2(optarg, 1, "reserved sectors");
+ break;
+ case 's':
+ opt_s = argto4(optarg, 1, "file system size");
+ break;
+ case 'u':
+ opt_u = argto2(optarg, 1, "sectors/track");
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc < 1 || argc > 2)
+ usage();
+ fname = *argv++;
+ if (!opt_create && !strchr(fname, '/')) {
+ snprintf(buf, sizeof(buf), "%s%s", _PATH_DEV, fname);
+ if (!(fname = strdup(buf)))
+ err(1, "%s", buf);
+ }
+ dtype = *argv;
+ if (opt_A) {
+ if (opt_r)
+ errx(1, "align (-A) is incompatible with -r");
+ if (opt_N)
+ errx(1, "align (-A) is incompatible with -N");
+ }
+ if (opt_create) {
+ if (opt_N)
+ errx(1, "create (-C) is incompatible with -N");
+ fd = open(fname, O_RDWR | O_CREAT | O_TRUNC, 0644);
+ if (fd == -1)
+ errx(1, "failed to create %s", fname);
+ if (ftruncate(fd, opt_create))
+ errx(1, "failed to initialize %jd bytes", (intmax_t)opt_create);
+ } else if ((fd = open(fname, opt_N ? O_RDONLY : O_RDWR)) == -1)
+ err(1, "%s", fname);
+ if (fstat(fd, &sb))
+ err(1, "%s", fname);
+ if (opt_create) {
+ if (!S_ISREG(sb.st_mode))
+ warnx("warning, %s is not a regular file", fname);
+ } else {
+ if (!S_ISCHR(sb.st_mode))
+ warnx("warning, %s is not a character device", fname);
+ }
+ if (!opt_N)
+ check_mounted(fname, sb.st_mode);
+ if (opt_ofs && opt_ofs != lseek(fd, opt_ofs, SEEK_SET))
+ errx(1, "cannot seek to %jd", (intmax_t)opt_ofs);
+ memset(&bpb, 0, sizeof(bpb));
+ if (opt_f) {
+ getstdfmt(opt_f, &bpb);
+ bpb.bsec = bpb.sec;
+ bpb.sec = 0;
+ bpb.bspf = bpb.spf;
+ bpb.spf = 0;
+ }
+ if (opt_h)
+ bpb.hds = opt_h;
+ if (opt_u)
+ bpb.spt = opt_u;
+ if (opt_S)
+ bpb.bps = opt_S;
+ if (opt_s)
+ bpb.bsec = opt_s;
+ if (oflag)
+ bpb.hid = opt_o;
+ if (!(opt_f || (opt_h && opt_u && opt_S && opt_s && oflag))) {
+ off_t delta;
+ getdiskinfo(fd, fname, dtype, oflag, &bpb);
+ if (opt_s) {
+ bpb.bsec = opt_s;
+ }
+ bpb.bsec -= (opt_ofs / bpb.bps);
+ delta = bpb.bsec % bpb.spt;
+ if (delta != 0) {
+ warnx("trim %d sectors from %d to adjust to a multiple of %d",
+ (int)delta, bpb.bsec, bpb.spt);
+ bpb.bsec -= delta;
+ }
+ if (bpb.spc == 0) { /* set defaults */
+ if (bpb.bsec <= 6000) /* about 3MB -> 512 bytes */
+ bpb.spc = 1;
+ else if (bpb.bsec <= (1<<17)) /* 64M -> 4k */
+ bpb.spc = 8;
+ else if (bpb.bsec <= (1<<19)) /* 256M -> 8k */
+ bpb.spc = 16;
+ else if (bpb.bsec <= (1<<22)) /* 2G -> 16k, some versions of windows
+ require a minimum of 65527 clusters */
+ bpb.spc = 32;
+ else
+ bpb.spc = 64; /* otherwise 32k */
+ }
+ }
+ if (!powerof2(bpb.bps))
+ errx(1, "bytes/sector (%u) is not a power of 2", bpb.bps);
+ if (bpb.bps < MINBPS)
+ errx(1, "bytes/sector (%u) is too small; minimum is %u",
+ bpb.bps, MINBPS);
+ if (!(fat = opt_F)) {
+ if (opt_f)
+ fat = 12;
+ else if (!opt_e && (opt_i || opt_k))
+ fat = 32;
+ }
+ if ((fat == 32 && opt_e) || (fat != 32 && (opt_i || opt_k)))
+ errx(1, "-%c is not a legal FAT%s option",
+ fat == 32 ? 'e' : opt_i ? 'i' : 'k',
+ fat == 32 ? "32" : "12/16");
+ if (opt_f && fat == 32)
+ bpb.rde = 0;
+ if (opt_b) {
+ if (!powerof2(opt_b))
+ errx(1, "block size (%u) is not a power of 2", opt_b);
+ if (opt_b < bpb.bps)
+ errx(1, "block size (%u) is too small; minimum is %u",
+ opt_b, bpb.bps);
+ if (opt_b > bpb.bps * MAXSPC)
+ errx(1, "block size (%u) is too large; maximum is %u", opt_b, bpb.bps * MAXSPC);
+ bpb.spc = opt_b / bpb.bps;
+ }
+ if (opt_c) {
+ if (!powerof2(opt_c))
+ errx(1, "sectors/cluster (%u) is not a power of 2", opt_c);
+ bpb.spc = opt_c;
+ }
+ if (opt_r)
+ bpb.res = opt_r;
+ if (opt_n) {
+ if (opt_n > MAXNFT)
+ errx(1, "number of FATs (%u) is too large; maximum is %u", opt_n, MAXNFT);
+ bpb.nft = opt_n;
+ }
+ if (opt_e)
+ bpb.rde = opt_e;
+ if (mflag) {
+ if (opt_m < 0xf0)
+ errx(1, "illegal media descriptor (%#x)", opt_m);
+ bpb.mid = opt_m;
+ }
+ if (opt_a)
+ bpb.bspf = opt_a;
+ if (opt_i)
+ bpb.infs = opt_i;
+ if (opt_k)
+ bpb.bkbs = opt_k;
+ bss = 1;
+ bname = NULL;
+ fd1 = -1;
+ if (opt_B) {
+ bname = opt_B;
+ if (!strchr(bname, '/')) {
+ snprintf(buf, sizeof(buf), "/boot/%s", bname);
+ if (!(bname = strdup(buf)))
+ err(1, "%s", buf);
+ }
+ if ((fd1 = open(bname, O_RDONLY)) == -1 || fstat(fd1, &sb))
+ err(1, "%s", bname);
+ if (!S_ISREG(sb.st_mode) || sb.st_size % bpb.bps ||
+ sb.st_size < bpb.bps || sb.st_size > bpb.bps * MAXU16)
+ errx(1, "%s: inappropriate file type or format", bname);
+ bss = sb.st_size / bpb.bps;
+ }
+ if (!bpb.nft)
+ bpb.nft = 2;
+ if (!fat) {
+ if (bpb.bsec < (bpb.res ? bpb.res : bss) +
+ howmany((RESFTE + (bpb.spc ? MINCLS16 : MAXCLS12 + 1)) *
+ ((bpb.spc ? 16 : 12) / BPN), bpb.bps * NPB) *
+ bpb.nft +
+ howmany(bpb.rde ? bpb.rde : DEFRDE,
+ bpb.bps / sizeof(struct de)) +
+ (bpb.spc ? MINCLS16 : MAXCLS12 + 1) *
+ (bpb.spc ? bpb.spc : howmany(DEFBLK, bpb.bps)))
+ fat = 12;
+ else if (bpb.rde || bpb.bsec <
+ (bpb.res ? bpb.res : bss) +
+ howmany((RESFTE + MAXCLS16) * 2, bpb.bps) * bpb.nft +
+ howmany(DEFRDE, bpb.bps / sizeof(struct de)) +
+ (MAXCLS16 + 1) *
+ (bpb.spc ? bpb.spc : howmany(8192, bpb.bps)))
+ fat = 16;
+ else
+ fat = 32;
+ }
+ x = bss;
+ if (fat == 32) {
+ if (!bpb.infs) {
+ if (x == MAXU16 || x == bpb.bkbs)
+ errx(1, "no room for info sector");
+ bpb.infs = x;
+ }
+ if (bpb.infs != MAXU16 && x <= bpb.infs)
+ x = bpb.infs + 1;
+ if (!bpb.bkbs) {
+ if (x == MAXU16)
+ errx(1, "no room for backup sector");
+ bpb.bkbs = x;
+ } else if (bpb.bkbs != MAXU16 && bpb.bkbs == bpb.infs)
+ errx(1, "backup sector would overwrite info sector");
+ if (bpb.bkbs != MAXU16 && x <= bpb.bkbs)
+ x = bpb.bkbs + 1;
+ }
+
+ extra_res = 0;
+ set_res = !bpb.res;
+ set_spf = !bpb.bspf;
+ set_spc = !bpb.spc;
+ tempx = x;
+ /*
+ * Attempt to align if opt_A is set. This is done by increasing the number
+ * of reserved blocks. This can cause other factors to change, which can in
+ * turn change the alignment. This should take at most 2 iterations, as
+ * increasing the reserved amount may cause the FAT size to decrease by 1,
+ * requiring another nft reserved blocks. If spc changes, it will
+ * be half of its previous size, and thus will not throw off alignment.
+ */
+ do {
+ x = tempx;
+ if (set_res)
+ bpb.res = (fat == 32 ? MAX(x, MAX(16384 / bpb.bps, 4)) : x) + extra_res;
+ else if (bpb.res < x)
+ errx(1, "too few reserved sectors");
+ if (fat != 32 && !bpb.rde)
+ bpb.rde = DEFRDE;
+ rds = howmany(bpb.rde, bpb.bps / sizeof(struct de));
+ if (set_spc)
+ for (bpb.spc = howmany(fat == 16 ? DEFBLK16 : DEFBLK, bpb.bps);
+ bpb.spc < MAXSPC &&
+ bpb.res +
+ howmany((RESFTE + maxcls(fat)) * (fat / BPN),
+ bpb.bps * NPB) * bpb.nft +
+ rds +
+ (u_int64_t)(maxcls(fat) + 1) * bpb.spc <= bpb.bsec;
+ bpb.spc <<= 1);
+ if (fat != 32 && bpb.bspf > MAXU16)
+ errx(1, "too many sectors/FAT for FAT12/16");
+ x1 = bpb.res + rds;
+ x = bpb.bspf ? bpb.bspf : 1;
+ if (x1 + (u_int64_t)x * bpb.nft > bpb.bsec)
+ errx(1, "meta data exceeds file system size");
+ x1 += x * bpb.nft;
+ x = (u_int64_t)(bpb.bsec - x1) * bpb.bps * NPB /
+ (bpb.spc * bpb.bps * NPB + fat / BPN * bpb.nft);
+ x2 = howmany((RESFTE + MIN(x, maxcls(fat))) * (fat / BPN), bpb.bps * NPB);
+ if (set_spf) {
+ if (!bpb.bspf) {
+ bpb.bspf = x2;
+ }
+ x1 += (bpb.bspf - 1) * bpb.nft;
+ }
+ if(set_res) {
+ /* attempt to align root directory */
+ alignment = (bpb.res + bpb.bspf * bpb.nft) % bpb.spc;
+ extra_res += bpb.spc - alignment;
+ }
+ attempts++;
+ } while(opt_A && alignment != 0 && attempts < 2);
+ if (alignment != 0)
+ warnx("warning: Alignment failed.");
+
+ cls = (bpb.bsec - x1) / bpb.spc;
+ x = (u_int64_t)bpb.bspf * bpb.bps * NPB / (fat / BPN) - RESFTE;
+ if (cls > x)
+ cls = x;
+ if (bpb.bspf < x2)
+ warnx("warning: sectors/FAT limits file system to %u clusters", cls);
+ if (cls < mincls(fat))
+ errx(1, "%u clusters too few clusters for FAT%u, need %u", cls, fat, mincls(fat));
+ if (cls > maxcls(fat)) {
+ cls = maxcls(fat);
+ bpb.bsec = x1 + (cls + 1) * bpb.spc - 1;
+ warnx("warning: FAT type limits file system to %u sectors", bpb.bsec);
+ }
+ printf("%s: %u sector%s in %u FAT%u cluster%s (%u bytes/cluster)\n",
+ fname, cls * bpb.spc, cls * bpb.spc == 1 ? "" : "s", cls, fat,
+ cls == 1 ? "" : "s", bpb.bps * bpb.spc);
+ if (!bpb.mid)
+ bpb.mid = !bpb.hid ? 0xf0 : 0xf8;
+ if (fat == 32)
+ bpb.rdcl = RESFTE;
+ if (bpb.hid + bpb.bsec <= MAXU16) {
+ bpb.sec = bpb.bsec;
+ bpb.bsec = 0;
+ }
+ if (fat != 32) {
+ bpb.spf = bpb.bspf;
+ bpb.bspf = 0;
+ }
+ print_bpb(&bpb);
+ if (!opt_N) {
+ gettimeofday(&tv, NULL);
+ now = tv.tv_sec;
+ tm = localtime(&now);
+ if (!(img = malloc(bpb.bps)))
+ err(1, "%u", bpb.bps);
+ dir = bpb.res + (bpb.spf ? bpb.spf : bpb.bspf) * bpb.nft;
+ for (lsn = 0; lsn < dir + (fat == 32 ? bpb.spc : rds); lsn++) {
+ x = lsn;
+ if (opt_B && fat == 32 && bpb.bkbs != MAXU16 && bss <= bpb.bkbs && x >= bpb.bkbs) {
+ x -= bpb.bkbs;
+ if (!x && lseek(fd1, opt_ofs, SEEK_SET))
+ err(1, "%s", bname);
+ }
+ if (opt_B && x < bss) {
+ if ((n = read(fd1, img, bpb.bps)) == -1)
+ err(1, "%s", bname);
+ if ((unsigned)n != bpb.bps)
+ errx(1, "%s: can't read sector %u", bname, x);
+ } else
+ memset(img, 0, bpb.bps);
+ if (!lsn || (fat == 32 && bpb.bkbs != MAXU16 && lsn == bpb.bkbs)) {
+ x1 = sizeof(struct bs);
+ bsbpb = (struct bsbpb *)(img + x1);
+ mk2(bsbpb->bps, bpb.bps);
+ mk1(bsbpb->spc, bpb.spc);
+ mk2(bsbpb->res, bpb.res);
+ mk1(bsbpb->nft, bpb.nft);
+ mk2(bsbpb->rde, bpb.rde);
+ mk2(bsbpb->sec, bpb.sec);
+ mk1(bsbpb->mid, bpb.mid);
+ mk2(bsbpb->spf, bpb.spf);
+ mk2(bsbpb->spt, bpb.spt);
+ mk2(bsbpb->hds, bpb.hds);
+ mk4(bsbpb->hid, bpb.hid);
+ mk4(bsbpb->bsec, bpb.bsec);
+ x1 += sizeof(struct bsbpb);
+ if (fat == 32) {
+ bsxbpb = (struct bsxbpb *)(img + x1);
+ mk4(bsxbpb->bspf, bpb.bspf);
+ mk2(bsxbpb->xflg, 0);
+ mk2(bsxbpb->vers, 0);
+ mk4(bsxbpb->rdcl, bpb.rdcl);
+ mk2(bsxbpb->infs, bpb.infs);
+ mk2(bsxbpb->bkbs, bpb.bkbs);
+ x1 += sizeof(struct bsxbpb);
+ }
+ bsx = (struct bsx *)(img + x1);
+ mk1(bsx->sig, 0x29);
+ if (Iflag)
+ x = opt_I;
+ else
+ x = (((u_int)(1 + tm->tm_mon) << 8 |
+ (u_int)tm->tm_mday) +
+ ((u_int)tm->tm_sec << 8 |
+ (u_int)(tv.tv_usec / 10))) << 16 |
+ ((u_int)(1900 + tm->tm_year) +
+ ((u_int)tm->tm_hour << 8 |
+ (u_int)tm->tm_min));
+ mk4(bsx->volid, x);
+ mklabel(bsx->label, opt_L ? opt_L : "NO NAME");
+ sprintf(buf, "FAT%u", fat);
+ setstr(bsx->type, buf, sizeof(bsx->type));
+ if (!opt_B) {
+ x1 += sizeof(struct bsx);
+ bs = (struct bs *)img;
+ mk1(bs->jmp[0], 0xeb);
+ mk1(bs->jmp[1], x1 - 2);
+ mk1(bs->jmp[2], 0x90);
+ setstr(bs->oem, opt_O ? opt_O : "BSD 4.4",
+ sizeof(bs->oem));
+ memcpy(img + x1, bootcode, sizeof(bootcode));
+ mk2(img + MINBPS - 2, DOSMAGIC);
+ }
+ } else if (fat == 32 && bpb.infs != MAXU16 &&
+ (lsn == bpb.infs || (bpb.bkbs != MAXU16 &&
+ lsn == bpb.bkbs + bpb.infs))) {
+ mk4(img, 0x41615252);
+ mk4(img + MINBPS - 28, 0x61417272);
+ mk4(img + MINBPS - 24, 0xffffffff);
+ mk4(img + MINBPS - 20, bpb.rdcl);
+ mk2(img + MINBPS - 2, DOSMAGIC);
+ } else if (lsn >= bpb.res && lsn < dir &&
+ !((lsn - bpb.res) % (bpb.spf ? bpb.spf : bpb.bspf))) {
+ mk1(img[0], bpb.mid);
+ for (x = 1; x < fat * (fat == 32 ? 3 : 2) / 8; x++)
+ mk1(img[x], fat == 32 && x % 4 == 3 ? 0x0f : 0xff);
+ } else if (lsn == dir && opt_L) {
+ de = (struct de *)img;
+ mklabel(de->namext, opt_L);
+ mk1(de->attr, 050);
+ x = (u_int)tm->tm_hour << 11 |
+ (u_int)tm->tm_min << 5 |
+ (u_int)tm->tm_sec >> 1;
+ mk2(de->time, x);
+ x = (u_int)(tm->tm_year - 80) << 9 |
+ (u_int)(tm->tm_mon + 1) << 5 |
+ (u_int)tm->tm_mday;
+ mk2(de->date, x);
+ }
+ if ((n = write(fd, img, bpb.bps)) == -1)
+ err(1, "%s", fname);
+ if ((unsigned)n != bpb.bps) {
+ errx(1, "%s: can't write sector %u", fname, lsn);
+ exit(1);
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+ * Exit with error if file system is mounted.
+ */
+static void check_mounted(const char *fname, mode_t mode)
+{
+#ifdef ANDROID
+ warnx("Skipping mount checks");
+#else
+ struct statfs *mp;
+ const char *s1, *s2;
+ size_t len;
+ int n, r;
+
+ if (!(n = getmntinfo(&mp, MNT_NOWAIT)))
+ err(1, "getmntinfo");
+ len = strlen(_PATH_DEV);
+ s1 = fname;
+ if (!strncmp(s1, _PATH_DEV, len))
+ s1 += len;
+ r = S_ISCHR(mode) && s1 != fname && *s1 == 'r';
+ for (; n--; mp++) {
+ s2 = mp->f_mntfromname;
+ if (!strncmp(s2, _PATH_DEV, len))
+ s2 += len;
+ if ((r && s2 != mp->f_mntfromname && !strcmp(s1 + 1, s2)) || !strcmp(s1, s2))
+ errx(1, "%s is mounted on %s", fname, mp->f_mntonname);
+ }
+#endif
+}
+
+/*
+ * Get a standard format.
+ */
+static void getstdfmt(const char *fmt, struct bpb *bpb)
+{
+ u_int x, i;
+
+ x = sizeof(stdfmt) / sizeof(stdfmt[0]);
+ for (i = 0; i < x && strcmp(fmt, stdfmt[i].name); i++);
+ if (i == x)
+ errx(1, "%s: unknown standard format", fmt);
+ *bpb = stdfmt[i].bpb;
+}
+
+/*
+ * Get disk slice, partition, and geometry information.
+ */
+
+#ifdef ANDROID
+static void getdiskinfo(int fd, const char *fname, const char *dtype,
+ __unused int oflag,struct bpb *bpb)
+{
+ struct hd_geometry geom;
+ u_long block_size;
+
+ if (ioctl(fd, BLKSSZGET, &bpb->bps)) {
+ fprintf(stderr, "Error getting bytes / sector (%s)\n", strerror(errno));
+ exit(1);
+ }
+
+ ckgeom(fname, bpb->bps, "bytes/sector");
+
+ if (ioctl(fd, BLKGETSIZE, &block_size)) {
+ fprintf(stderr, "Error getting blocksize (%s)\n", strerror(errno));
+ exit(1);
+ }
+
+ if (block_size > UINT32_MAX) {
+ fprintf(stderr, "Error blocksize too large: %lu\n", block_size);
+ exit(1);
+ }
+
+ bpb->bsec = (u_int)block_size;
+
+ if (ioctl(fd, HDIO_GETGEO, &geom)) {
+ fprintf(stderr, "Error getting gemoetry (%s) - trying sane values\n", strerror(errno));
+ geom.heads = 64;
+ geom.sectors = 63;
+ }
+
+ if (!geom.heads) {
+ printf("Bogus heads from kernel - setting sane value\n");
+ geom.heads = 64;
+ }
+
+ if (!geom.sectors) {
+ printf("Bogus sectors from kernel - setting sane value\n");
+ geom.sectors = 63;
+ }
+
+ bpb->spt = geom.sectors;
+ ckgeom(fname, bpb->spt, "sectors/track");
+
+ bpb->hds = geom.heads;
+ ckgeom(fname, bpb->hds, "drive heads");
+}
+
+#else
+
+static void getdiskinfo(int fd, const char *fname, const char *dtype,
+ __unused int oflag, struct bpb *bpb)
+{
+ struct disklabel *lp, dlp;
+ struct fd_type type;
+ off_t ms, hs = 0;
+
+ lp = NULL;
+
+ /* If the user specified a disk type, try to use that */
+ if (dtype != NULL) {
+ lp = getdiskbyname(dtype);
+ }
+
+ /* Maybe it's a floppy drive */
+ if (lp == NULL) {
+ if (ioctl(fd, DIOCGMEDIASIZE, &ms) == -1) {
+ struct stat st;
+
+ if (fstat(fd, &st))
+ err(1, "Cannot get disk size");
+ /* create a fake geometry for a file image */
+ ms = st.st_size;
+ dlp.d_secsize = 512;
+ dlp.d_nsectors = 63;
+ dlp.d_ntracks = 255;
+ dlp.d_secperunit = ms / dlp.d_secsize;
+ lp = &dlp;
+ } else if (ioctl(fd, FD_GTYPE, &type) != -1) {
+ dlp.d_secsize = 128 << type.secsize;
+ dlp.d_nsectors = type.sectrac;
+ dlp.d_ntracks = type.heads;
+ dlp.d_secperunit = ms / dlp.d_secsize;
+ lp = &dlp;
+ }
+ }
+
+ /* Maybe it's a fixed drive */
+ if (lp == NULL) {
+ if (ioctl(fd, DIOCGDINFO, &dlp) == -1) {
+ if (bpb->bps == 0 && ioctl(fd, DIOCGSECTORSIZE, &dlp.d_secsize) == -1)
+ errx(1, "Cannot get sector size, %s", strerror(errno));
+
+ /* XXX Should we use bpb->bps if it's set? */
+ dlp.d_secperunit = ms / dlp.d_secsize;
+
+ if (bpb->spt == 0 && ioctl(fd, DIOCGFWSECTORS, &dlp.d_nsectors) == -1) {
+ warnx("Cannot get number of sectors per track, %s", strerror(errno));
+ dlp.d_nsectors = 63;
+ }
+ if (bpb->hds == 0 && ioctl(fd, DIOCGFWHEADS, &dlp.d_ntracks) == -1) {
+ warnx("Cannot get number of heads, %s", strerror(errno));
+ if (dlp.d_secperunit <= 63*1*1024)
+ dlp.d_ntracks = 1;
+ else if (dlp.d_secperunit <= 63*16*1024)
+ dlp.d_ntracks = 16;
+ else
+ dlp.d_ntracks = 255;
+ }
+ }
+
+ hs = (ms / dlp.d_secsize) - dlp.d_secperunit;
+ lp = &dlp;
+ }
+
+ if (bpb->bps == 0)
+ bpb->bps = ckgeom(fname, lp->d_secsize, "bytes/sector");
+ if (bpb->spt == 0)
+ bpb->spt = ckgeom(fname, lp->d_nsectors, "sectors/track");
+ if (bpb->hds == 0)
+ bpb->hds = ckgeom(fname, lp->d_ntracks, "drive heads");
+ if (bpb->bsec == 0)
+ bpb->bsec = lp->d_secperunit;
+ if (bpb->hid == 0)
+ bpb->hid = hs;
+}
+#endif
+
+/*
+ * Print out BPB values.
+ */
+static void print_bpb(struct bpb *bpb)
+{
+ printf("bps=%u spc=%u res=%u nft=%u", bpb->bps, bpb->spc, bpb->res,
+ bpb->nft);
+ if (bpb->rde)
+ printf(" rde=%u", bpb->rde);
+ if (bpb->sec)
+ printf(" sec=%u", bpb->sec);
+ printf(" mid=%#x", bpb->mid);
+ if (bpb->spf)
+ printf(" spf=%u", bpb->spf);
+ printf(" spt=%u hds=%u hid=%u", bpb->spt, bpb->hds, bpb->hid);
+ if (bpb->bsec)
+ printf(" bsec=%u", bpb->bsec);
+ if (!bpb->spf) {
+ printf(" bspf=%u rdcl=%u", bpb->bspf, bpb->rdcl);
+ printf(" infs=");
+ printf(bpb->infs == MAXU16 ? "%#x" : "%u", bpb->infs);
+ printf(" bkbs=");
+ printf(bpb->bkbs == MAXU16 ? "%#x" : "%u", bpb->bkbs);
+ }
+ printf("\n");
+}
+
+/*
+ * Check a disk geometry value.
+ */
+static u_int ckgeom(const char *fname, u_int val, const char *msg)
+{
+ if (!val)
+ errx(1, "%s: no default %s", fname, msg);
+ if (val > MAXU16)
+ errx(1, "%s: illegal %s %d", fname, msg, val);
+ return val;
+}
+
+/*
+ * Convert and check a numeric option argument.
+ */
+static u_int argtou(const char *arg, u_int lo, u_int hi, const char *msg)
+{
+ char *s;
+ u_long x;
+
+ errno = 0;
+ x = strtoul(arg, &s, 0);
+ if (errno || !*arg || *s || x < lo || x > hi)
+ errx(1, "%s: bad %s", arg, msg);
+ return x;
+}
+
+/*
+ * Same for off_t, with optional skmgpP suffix
+ */
+static off_t argtooff(const char *arg, const char *msg)
+{
+ char *s;
+ off_t x;
+
+ x = strtoll(arg, &s, 0);
+ /* allow at most one extra char */
+ if (errno || x < 0 || (s[0] && s[1]) )
+ errx(1, "%s: bad %s", arg, msg);
+ if (*s) { /* the extra char is the multiplier */
+ switch (*s) {
+ default:
+ errx(1, "%s: bad %s", arg, msg);
+ /* notreached */
+
+ case 's': /* sector */
+ case 'S':
+ x <<= 9; /* times 512 */
+ break;
+
+ case 'k': /* kilobyte */
+ case 'K':
+ x <<= 10; /* times 1024 */
+ break;
+
+ case 'm': /* megabyte */
+ case 'M':
+ x <<= 20; /* times 1024*1024 */
+ break;
+
+ case 'g': /* gigabyte */
+ case 'G':
+ x <<= 30; /* times 1024*1024*1024 */
+ break;
+
+ case 'p': /* partition start */
+ case 'P': /* partition start */
+ case 'l': /* partition length */
+ case 'L': /* partition length */
+ errx(1, "%s: not supported yet %s", arg, msg);
+ /* notreached */
+ }
+ }
+ return x;
+}
+
+/*
+ * Check a volume label.
+ */
+static int oklabel(const char *src)
+{
+ int c, i;
+
+ for (i = 0; i <= 11; i++) {
+ c = (u_char)*src++;
+ if (c < ' ' + !i || strchr("\"*+,./:;<=>?[\\]|", c))
+ break;
+ }
+ return i && !c;
+}
+
+/*
+ * Make a volume label.
+ */
+static void mklabel(u_int8_t *dest, const char *src)
+{
+ int c, i;
+
+ for (i = 0; i < 11; i++) {
+ c = *src ? toupper(*src++) : ' ';
+ *dest++ = !i && c == '\xe5' ? 5 : c;
+ }
+}
+
+/*
+ * Copy string, padding with spaces.
+ */
+static void setstr(u_int8_t *dest, const char *src, size_t len)
+{
+ while (len--)
+ *dest++ = *src ? *src++ : ' ';
+}
+
+/*
+ * Print usage message.
+ */
+static void usage(void)
+{
+ fprintf(stderr,
+ "usage: newfs_msdos [ -options ] special [disktype]\n"
+ "where the options are:\n"
+ "\t-@ create file system at specified offset\n"
+ "\t-A Attempt to cluster align root directory\n"
+ "\t-B get bootstrap from file\n"
+ "\t-C create image file with specified size\n"
+ "\t-F FAT type (12, 16, or 32)\n"
+ "\t-I volume ID\n"
+ "\t-L volume label\n"
+ "\t-N don't create file system: just print out parameters\n"
+ "\t-O OEM string\n"
+ "\t-S bytes/sector\n"
+ "\t-a sectors/FAT\n"
+ "\t-b block size\n"
+ "\t-c sectors/cluster\n"
+ "\t-e root directory entries\n"
+ "\t-f standard format\n"
+ "\t-h drive heads\n"
+ "\t-i file system info sector\n"
+ "\t-k backup boot sector\n"
+ "\t-m media descriptor\n"
+ "\t-n number of FATs\n"
+ "\t-o hidden sectors\n"
+ "\t-r reserved sectors\n"
+ "\t-s file system size (sectors)\n"
+ "\t-u sectors/track\n");
+ exit(1);
+}
diff --git a/toolbox/prlimit.c b/toolbox/prlimit.c
new file mode 100644
index 0000000..8cf202a
--- /dev/null
+++ b/toolbox/prlimit.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2014, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+static void
+usage(const char *s)
+{
+ fprintf(stderr, "usage: %s pid resource cur max\n", s);
+ exit(EXIT_FAILURE);
+}
+
+int prlimit_main(int argc, char *argv[])
+{
+ pid_t pid;
+ struct rlimit64 rl;
+ int resource;
+ int rc;
+
+ if (argc != 5)
+ usage(*argv);
+
+ if (sscanf(argv[1], "%d", &pid) != 1)
+ usage(*argv);
+
+ if (sscanf(argv[2], "%d", &resource) != 1)
+ usage(*argv);
+
+ if (sscanf(argv[3], "%llu", &rl.rlim_cur) != 1)
+ usage(*argv);
+
+ if (sscanf(argv[4], "%llu", &rl.rlim_max) != 1)
+ usage(*argv);
+
+ printf("setting resource %d of pid %d to [%llu,%llu]\n", resource, pid,
+ rl.rlim_cur, rl.rlim_max);
+ rc = prlimit64(pid, resource, &rl, NULL);
+ if (rc < 0) {
+ perror("prlimit");
+ exit(EXIT_FAILURE);
+ }
+
+ return 0;
+}
diff --git a/toolbox/ps.c b/toolbox/ps.c
new file mode 100644
index 0000000..cf3f05a
--- /dev/null
+++ b/toolbox/ps.c
@@ -0,0 +1,326 @@
+#include <ctype.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <cutils/sched_policy.h>
+
+static char *nexttoksep(char **strp, char *sep)
+{
+ char *p = strsep(strp,sep);
+ return (p == 0) ? "" : p;
+}
+static char *nexttok(char **strp)
+{
+ return nexttoksep(strp, " ");
+}
+
+#define SHOW_PRIO 1
+#define SHOW_TIME 2
+#define SHOW_POLICY 4
+#define SHOW_CPU 8
+#define SHOW_MACLABEL 16
+#define SHOW_NUMERIC_UID 32
+#define SHOW_ABI 64
+
+#if __LP64__
+#define PC_WIDTH 10 /* Realistically, the top bits will be 0, so don't waste space. */
+#else
+#define PC_WIDTH (2*sizeof(uintptr_t))
+#endif
+
+static int display_flags = 0;
+static int ppid_filter = 0;
+
+static void print_exe_abi(int pid);
+
+static int ps_line(int pid, int tid, char *namefilter)
+{
+ char statline[1024];
+ char cmdline[1024];
+ char macline[1024];
+ char user[32];
+ struct stat stats;
+ int fd, r;
+ char *ptr, *name, *state;
+ int ppid;
+ unsigned rss, vss;
+ uintptr_t eip;
+ unsigned utime, stime;
+ int prio, nice, rtprio, sched, psr;
+ struct passwd *pw;
+
+ sprintf(statline, "/proc/%d", pid);
+ stat(statline, &stats);
+
+ if(tid) {
+ sprintf(statline, "/proc/%d/task/%d/stat", pid, tid);
+ cmdline[0] = 0;
+ snprintf(macline, sizeof(macline), "/proc/%d/task/%d/attr/current", pid, tid);
+ } else {
+ sprintf(statline, "/proc/%d/stat", pid);
+ sprintf(cmdline, "/proc/%d/cmdline", pid);
+ snprintf(macline, sizeof(macline), "/proc/%d/attr/current", pid);
+ fd = open(cmdline, O_RDONLY);
+ if(fd == 0) {
+ r = 0;
+ } else {
+ r = read(fd, cmdline, 1023);
+ close(fd);
+ if(r < 0) r = 0;
+ }
+ cmdline[r] = 0;
+ }
+
+ fd = open(statline, O_RDONLY);
+ if(fd == 0) return -1;
+ r = read(fd, statline, 1023);
+ close(fd);
+ if(r < 0) return -1;
+ statline[r] = 0;
+
+ ptr = statline;
+ nexttok(&ptr); // skip pid
+ ptr++; // skip "("
+
+ name = ptr;
+ ptr = strrchr(ptr, ')'); // Skip to *last* occurence of ')',
+ *ptr++ = '\0'; // and null-terminate name.
+
+ ptr++; // skip " "
+ state = nexttok(&ptr);
+ ppid = atoi(nexttok(&ptr));
+ nexttok(&ptr); // pgrp
+ nexttok(&ptr); // sid
+ nexttok(&ptr); // tty
+ nexttok(&ptr); // tpgid
+ nexttok(&ptr); // flags
+ nexttok(&ptr); // minflt
+ nexttok(&ptr); // cminflt
+ nexttok(&ptr); // majflt
+ nexttok(&ptr); // cmajflt
+#if 1
+ utime = atoi(nexttok(&ptr));
+ stime = atoi(nexttok(&ptr));
+#else
+ nexttok(&ptr); // utime
+ nexttok(&ptr); // stime
+#endif
+ nexttok(&ptr); // cutime
+ nexttok(&ptr); // cstime
+ prio = atoi(nexttok(&ptr));
+ nice = atoi(nexttok(&ptr));
+ nexttok(&ptr); // threads
+ nexttok(&ptr); // itrealvalue
+ nexttok(&ptr); // starttime
+ vss = strtoul(nexttok(&ptr), 0, 10); // vsize
+ rss = strtoul(nexttok(&ptr), 0, 10); // rss
+ nexttok(&ptr); // rlim
+ nexttok(&ptr); // startcode
+ nexttok(&ptr); // endcode
+ nexttok(&ptr); // startstack
+ nexttok(&ptr); // kstkesp
+ eip = strtoul(nexttok(&ptr), 0, 10); // kstkeip
+ nexttok(&ptr); // signal
+ nexttok(&ptr); // blocked
+ nexttok(&ptr); // sigignore
+ nexttok(&ptr); // sigcatch
+ nexttok(&ptr); // wchan
+ nexttok(&ptr); // nswap
+ nexttok(&ptr); // cnswap
+ nexttok(&ptr); // exit signal
+ psr = atoi(nexttok(&ptr)); // processor
+ rtprio = atoi(nexttok(&ptr)); // rt_priority
+ sched = atoi(nexttok(&ptr)); // scheduling policy
+
+ nexttok(&ptr); // tty
+
+ if(tid != 0) {
+ ppid = pid;
+ pid = tid;
+ }
+
+ pw = getpwuid(stats.st_uid);
+ if(pw == 0 || (display_flags & SHOW_NUMERIC_UID)) {
+ sprintf(user,"%d",(int)stats.st_uid);
+ } else {
+ strcpy(user,pw->pw_name);
+ }
+
+ if(ppid_filter != 0 && ppid != ppid_filter) {
+ return 0;
+ }
+
+ if(!namefilter || !strncmp(cmdline[0] ? cmdline : name, namefilter, strlen(namefilter))) {
+ if (display_flags & SHOW_MACLABEL) {
+ fd = open(macline, O_RDONLY);
+ strcpy(macline, "-");
+ if (fd >= 0) {
+ r = read(fd, macline, sizeof(macline)-1);
+ close(fd);
+ if (r > 0)
+ macline[r] = 0;
+ }
+ printf("%-30s %-9s %-5d %-5d %s\n", macline, user, pid, ppid, cmdline[0] ? cmdline : name);
+ return 0;
+ }
+
+ printf("%-9s %-5d %-5d %-6d %-5d", user, pid, ppid, vss / 1024, rss * 4);
+ if (display_flags & SHOW_CPU)
+ printf(" %-2d", psr);
+ if (display_flags & SHOW_PRIO)
+ printf(" %-5d %-5d %-5d %-5d", prio, nice, rtprio, sched);
+ if (display_flags & SHOW_POLICY) {
+ SchedPolicy p;
+ if (get_sched_policy(pid, &p) < 0)
+ printf(" un ");
+ else
+ printf(" %.2s ", get_sched_policy_name(p));
+ }
+ char path[PATH_MAX];
+ snprintf(path, sizeof(path), "/proc/%d/wchan", pid);
+ char wchan[10];
+ int fd = open(path, O_RDONLY);
+ ssize_t wchan_len = read(fd, wchan, sizeof(wchan));
+ if (wchan_len == -1) {
+ wchan[wchan_len = 0] = '\0';
+ }
+ close(fd);
+ printf(" %10.*s %0*" PRIxPTR " %s ", (int) wchan_len, wchan, (int) PC_WIDTH, eip, state);
+ if (display_flags & SHOW_ABI) {
+ print_exe_abi(pid);
+ }
+ printf("%s", cmdline[0] ? cmdline : name);
+ if(display_flags&SHOW_TIME)
+ printf(" (u:%d, s:%d)", utime, stime);
+
+ printf("\n");
+ }
+ return 0;
+}
+
+static void print_exe_abi(int pid)
+{
+ int fd, r;
+ char exeline[1024];
+
+ sprintf(exeline, "/proc/%d/exe", pid);
+ fd = open(exeline, O_RDONLY);
+ if(fd == 0) {
+ printf(" ");
+ return;
+ }
+ r = read(fd, exeline, 5 /* 4 byte ELFMAG + 1 byte EI_CLASS */);
+ close(fd);
+ if(r < 0) {
+ printf(" ");
+ return;
+ }
+ if (memcmp("\177ELF", exeline, 4) != 0) {
+ printf("?? ");
+ return;
+ }
+ switch (exeline[4]) {
+ case 1:
+ printf("32 ");
+ return;
+ case 2:
+ printf("64 ");
+ return;
+ default:
+ printf("?? ");
+ return;
+ }
+}
+
+void ps_threads(int pid, char *namefilter)
+{
+ char tmp[128];
+ DIR *d;
+ struct dirent *de;
+
+ sprintf(tmp,"/proc/%d/task",pid);
+ d = opendir(tmp);
+ if(d == 0) return;
+
+ while((de = readdir(d)) != 0){
+ if(isdigit(de->d_name[0])){
+ int tid = atoi(de->d_name);
+ if(tid == pid) continue;
+ ps_line(pid, tid, namefilter);
+ }
+ }
+ closedir(d);
+}
+
+int ps_main(int argc, char **argv)
+{
+ DIR *d;
+ struct dirent *de;
+ char *namefilter = 0;
+ int pidfilter = 0;
+ int threads = 0;
+
+ d = opendir("/proc");
+ if(d == 0) return -1;
+
+ while(argc > 1){
+ if(!strcmp(argv[1],"-t")) {
+ threads = 1;
+ } else if(!strcmp(argv[1],"-n")) {
+ display_flags |= SHOW_NUMERIC_UID;
+ } else if(!strcmp(argv[1],"-x")) {
+ display_flags |= SHOW_TIME;
+ } else if(!strcmp(argv[1], "-Z")) {
+ display_flags |= SHOW_MACLABEL;
+ } else if(!strcmp(argv[1],"-P")) {
+ display_flags |= SHOW_POLICY;
+ } else if(!strcmp(argv[1],"-p")) {
+ display_flags |= SHOW_PRIO;
+ } else if(!strcmp(argv[1],"-c")) {
+ display_flags |= SHOW_CPU;
+ } else if(!strcmp(argv[1],"--abi")) {
+ display_flags |= SHOW_ABI;
+ } else if(!strcmp(argv[1],"--ppid")) {
+ ppid_filter = atoi(argv[2]);
+ argc--;
+ argv++;
+ } else if(isdigit(argv[1][0])){
+ pidfilter = atoi(argv[1]);
+ } else {
+ namefilter = argv[1];
+ }
+ argc--;
+ argv++;
+ }
+
+ if (display_flags & SHOW_MACLABEL) {
+ printf("LABEL USER PID PPID NAME\n");
+ } else {
+ printf("USER PID PPID VSIZE RSS %s%s %sWCHAN %*s %sNAME\n",
+ (display_flags&SHOW_CPU)?"CPU ":"",
+ (display_flags&SHOW_PRIO)?"PRIO NICE RTPRI SCHED ":"",
+ (display_flags&SHOW_POLICY)?"PCY " : "",
+ (int) PC_WIDTH, "PC",
+ (display_flags&SHOW_ABI)?"ABI " : "");
+ }
+ while((de = readdir(d)) != 0){
+ if(isdigit(de->d_name[0])){
+ int pid = atoi(de->d_name);
+ if(!pidfilter || (pidfilter == pid)) {
+ ps_line(pid, 0, namefilter);
+ if(threads) ps_threads(pid, namefilter);
+ }
+ }
+ }
+ closedir(d);
+ return 0;
+}
+
diff --git a/toolbox/r.c b/toolbox/r.c
new file mode 100644
index 0000000..b96cdb2
--- /dev/null
+++ b/toolbox/r.c
@@ -0,0 +1,102 @@
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#if __LP64__
+#define strtoptr strtoull
+#else
+#define strtoptr strtoul
+#endif
+
+static int usage()
+{
+ fprintf(stderr,"r [-b|-s] <address> [<value>]\n");
+ return -1;
+}
+
+int main(int argc, char *argv[])
+{
+ if(argc < 2) return usage();
+
+ int width = 4;
+ if(!strcmp(argv[1], "-b")) {
+ width = 1;
+ argc--;
+ argv++;
+ } else if(!strcmp(argv[1], "-s")) {
+ width = 2;
+ argc--;
+ argv++;
+ }
+
+ if(argc < 2) return usage();
+ uintptr_t addr = strtoptr(argv[1], 0, 16);
+
+ uintptr_t endaddr = 0;
+ char* end = strchr(argv[1], '-');
+ if (end)
+ endaddr = strtoptr(end + 1, 0, 16);
+
+ if (!endaddr)
+ endaddr = addr + width - 1;
+
+ if (endaddr <= addr) {
+ fprintf(stderr, "end address <= start address\n");
+ return -1;
+ }
+
+ bool set = false;
+ uint32_t value = 0;
+ if(argc > 2) {
+ set = true;
+ value = strtoul(argv[2], 0, 16);
+ }
+
+ int fd = open("/dev/mem", O_RDWR | O_SYNC);
+ if(fd < 0) {
+ fprintf(stderr,"cannot open /dev/mem\n");
+ return -1;
+ }
+
+ off64_t mmap_start = addr & ~(PAGE_SIZE - 1);
+ size_t mmap_size = endaddr - mmap_start + 1;
+ mmap_size = (mmap_size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
+
+ void* page = mmap64(0, mmap_size, PROT_READ | PROT_WRITE,
+ MAP_SHARED, fd, mmap_start);
+
+ if(page == MAP_FAILED){
+ fprintf(stderr,"cannot mmap region\n");
+ return -1;
+ }
+
+ while (addr <= endaddr) {
+ switch(width){
+ case 4: {
+ uint32_t* x = (uint32_t*) (((uintptr_t) page) + (addr & 4095));
+ if(set) *x = value;
+ fprintf(stderr,"%08"PRIxPTR": %08x\n", addr, *x);
+ break;
+ }
+ case 2: {
+ uint16_t* x = (uint16_t*) (((uintptr_t) page) + (addr & 4095));
+ if(set) *x = value;
+ fprintf(stderr,"%08"PRIxPTR": %04x\n", addr, *x);
+ break;
+ }
+ case 1: {
+ uint8_t* x = (uint8_t*) (((uintptr_t) page) + (addr & 4095));
+ if(set) *x = value;
+ fprintf(stderr,"%08"PRIxPTR": %02x\n", addr, *x);
+ break;
+ }
+ }
+ addr += width;
+ }
+ return 0;
+}
diff --git a/toolbox/renice.c b/toolbox/renice.c
new file mode 100644
index 0000000..99a06f4
--- /dev/null
+++ b/toolbox/renice.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2008, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sched.h>
+#include <getopt.h>
+
+static void
+usage(const char *s)
+{
+ fprintf(stderr, "USAGE: %s [[-r] [-t TYPE] priority pids ...] [-g pid]\n", s);
+ exit(EXIT_FAILURE);
+}
+
+void print_prio(pid_t pid)
+{
+ int sched;
+ struct sched_param sp;
+
+ printf("pid %d's priority: %d\n", pid, getpriority(PRIO_PROCESS, pid));
+
+ printf("scheduling class: ");
+ sched = sched_getscheduler(pid);
+ switch (sched) {
+ case SCHED_FIFO:
+ printf("FIFO\n");
+ break;
+ case SCHED_RR:
+ printf("RR\n");
+ break;
+ case SCHED_OTHER:
+ printf("Normal\n");
+ break;
+ case -1:
+ perror("sched_getscheduler");
+ break;
+ default:
+ printf("Unknown\n");
+ }
+
+ sched_getparam(pid, &sp);
+ printf("RT prio: %d (of %d to %d)\n", sp.sched_priority,
+ sched_get_priority_min(sched), sched_get_priority_max(sched));
+}
+
+int get_sched(char *str)
+{
+ if (strcasecmp(str, "RR") == 0)
+ return SCHED_RR;
+ else if (strcasecmp(str, "FIFO") == 0)
+ return SCHED_FIFO;
+ else if (strcasecmp(str, "NORMAL") == 0)
+ return SCHED_OTHER;
+ else if (strcasecmp(str, "OTHER") == 0)
+ return SCHED_OTHER;
+ return SCHED_RR;
+}
+
+int renice_main(int argc, char *argv[])
+{
+ int prio;
+ int realtime = 0;
+ int opt;
+ int sched = SCHED_RR;
+ char *cmd = argv[0];
+
+ do {
+ opt = getopt(argc, argv, "rt:g:");
+ if (opt == -1)
+ break;
+ switch (opt) {
+ case 'r':
+ // do realtime priority adjustment
+ realtime = 1;
+ break;
+ case 't':
+ sched = get_sched(optarg);
+ break;
+ case 'g':
+ print_prio(atoi(optarg));
+ return 0;
+ default:
+ usage(cmd);
+ }
+ } while (1);
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1)
+ usage(cmd);
+
+ prio = atoi(argv[0]);
+ argc--;
+ argv++;
+
+ if (argc < 1)
+ usage(cmd);
+
+ while(argc) {
+ pid_t pid;
+
+ pid = atoi(argv[0]);
+ argc--;
+ argv++;
+
+ if (realtime) {
+ struct sched_param sp = { .sched_priority = prio };
+ int ret;
+
+ ret = sched_setscheduler(pid, sched, &sp);
+ if (ret) {
+ perror("sched_set_scheduler");
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ int ret;
+
+ ret = setpriority(PRIO_PROCESS, pid, prio);
+ if (ret) {
+ perror("setpriority");
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/toolbox/sendevent.c b/toolbox/sendevent.c
new file mode 100644
index 0000000..4d0ca17
--- /dev/null
+++ b/toolbox/sendevent.c
@@ -0,0 +1,42 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/input.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+int sendevent_main(int argc, char *argv[])
+{
+ int fd;
+ ssize_t ret;
+ int version;
+ struct input_event event;
+
+ if(argc != 5) {
+ fprintf(stderr, "use: %s device type code value\n", argv[0]);
+ return 1;
+ }
+
+ fd = open(argv[1], O_RDWR);
+ if(fd < 0) {
+ fprintf(stderr, "could not open %s, %s\n", argv[optind], strerror(errno));
+ return 1;
+ }
+ if (ioctl(fd, EVIOCGVERSION, &version)) {
+ fprintf(stderr, "could not get driver version for %s, %s\n", argv[optind], strerror(errno));
+ return 1;
+ }
+ memset(&event, 0, sizeof(event));
+ event.type = atoi(argv[2]);
+ event.code = atoi(argv[3]);
+ event.value = atoi(argv[4]);
+ ret = write(fd, &event, sizeof(event));
+ if(ret < (ssize_t) sizeof(event)) {
+ fprintf(stderr, "write event failed, %s\n", strerror(errno));
+ return -1;
+ }
+ return 0;
+}
diff --git a/toolbox/start.c b/toolbox/start.c
new file mode 100644
index 0000000..cca5fef
--- /dev/null
+++ b/toolbox/start.c
@@ -0,0 +1 @@
+/* Needed by Android.mk. Actual code in start_stop.cpp. */
diff --git a/toolbox/start_stop.cpp b/toolbox/start_stop.cpp
new file mode 100644
index 0000000..dc48c0c
--- /dev/null
+++ b/toolbox/start_stop.cpp
@@ -0,0 +1,43 @@
+#include <error.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <cutils/properties.h>
+
+static const char* services[] = {
+ "netd",
+ "surfaceflinger",
+ "zygote",
+ "zygote_secondary",
+};
+
+static int start_stop(bool start, int argc, char* argv[]) {
+ if (getuid() != 0) error(1, 0, "must be root");
+ const char* property = start ? "ctl.start" : "ctl.stop";
+ if (argc > 2) {
+ error(1, 0, "usage: %s [SERVICE]\n", argv[0]);
+ } else if (argc == 2) {
+ property_set(property, argv[1]);
+ } else {
+ if (start) {
+ for (size_t i = 0; i < sizeof(services)/sizeof(services[0]); ++i) {
+ property_set(property, services[i]);
+ }
+ } else {
+ for (int i = sizeof(services)/sizeof(services[0]) - 1; i >= 0; --i) {
+ property_set(property, services[i]);
+ }
+ }
+ }
+ return 0;
+}
+
+extern "C" int start_main(int argc, char* argv[]) {
+ return start_stop(true, argc, argv);
+}
+
+extern "C" int stop_main(int argc, char* argv[]) {
+ return start_stop(false, argc, argv);
+}
diff --git a/toolbox/stop.c b/toolbox/stop.c
new file mode 100644
index 0000000..cca5fef
--- /dev/null
+++ b/toolbox/stop.c
@@ -0,0 +1 @@
+/* Needed by Android.mk. Actual code in start_stop.cpp. */
diff --git a/toolbox/toolbox.c b/toolbox/toolbox.c
new file mode 100644
index 0000000..915da44
--- /dev/null
+++ b/toolbox/toolbox.c
@@ -0,0 +1,72 @@
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+int main(int, char **);
+
+static int toolbox_main(int argc, char **argv)
+{
+ // "toolbox foo ..." is equivalent to "foo ..."
+ if (argc > 1) {
+ return main(argc - 1, argv + 1);
+ } else {
+ printf("Toolbox!\n");
+ return 0;
+ }
+}
+
+#define TOOL(name) int name##_main(int, char**);
+#include "tools.h"
+#undef TOOL
+
+static struct
+{
+ const char *name;
+ int (*func)(int, char**);
+} tools[] = {
+ { "toolbox", toolbox_main },
+#define TOOL(name) { #name, name##_main },
+#include "tools.h"
+#undef TOOL
+ { 0, 0 },
+};
+
+static void SIGPIPE_handler(int signal) {
+ // Those desktop Linux tools that catch SIGPIPE seem to agree that it's
+ // a successful way to exit, not a failure. (Which makes sense --- we were
+ // told to stop by a reader, rather than failing to continue ourselves.)
+ _exit(0);
+}
+
+int main(int argc, char **argv)
+{
+ int i;
+ char *name = argv[0];
+
+ // Let's assume that none of this code handles broken pipes. At least ls,
+ // ps, and top were broken (though I'd previously added this fix locally
+ // to top). We exit rather than use SIG_IGN because tools like top will
+ // just keep on writing to nowhere forever if we don't stop them.
+ signal(SIGPIPE, SIGPIPE_handler);
+
+ if((argc > 1) && (argv[1][0] == '@')) {
+ name = argv[1] + 1;
+ argc--;
+ argv++;
+ } else {
+ char *cmd = strrchr(argv[0], '/');
+ if (cmd)
+ name = cmd + 1;
+ }
+
+ for(i = 0; tools[i].name; i++){
+ if(!strcmp(tools[i].name, name)){
+ return tools[i].func(argc, argv);
+ }
+ }
+
+ printf("%s: no such tool\n", argv[0]);
+ return -1;
+}
diff --git a/toolbox/top.c b/toolbox/top.c
new file mode 100644
index 0000000..1e99d4c
--- /dev/null
+++ b/toolbox/top.c
@@ -0,0 +1,572 @@
+/*
+ * Copyright (c) 2008, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <ctype.h>
+#include <dirent.h>
+#include <grp.h>
+#include <inttypes.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <cutils/sched_policy.h>
+
+struct cpu_info {
+ long unsigned utime, ntime, stime, itime;
+ long unsigned iowtime, irqtime, sirqtime;
+};
+
+#define PROC_NAME_LEN 64
+#define THREAD_NAME_LEN 32
+#define POLICY_NAME_LEN 4
+
+struct proc_info {
+ struct proc_info *next;
+ pid_t pid;
+ pid_t tid;
+ uid_t uid;
+ gid_t gid;
+ char name[PROC_NAME_LEN];
+ char tname[THREAD_NAME_LEN];
+ char state;
+ uint64_t utime;
+ uint64_t stime;
+ uint64_t delta_utime;
+ uint64_t delta_stime;
+ uint64_t delta_time;
+ uint64_t vss;
+ uint64_t rss;
+ int prs;
+ int num_threads;
+ char policy[POLICY_NAME_LEN];
+};
+
+struct proc_list {
+ struct proc_info **array;
+ int size;
+};
+
+#define die(...) { fprintf(stderr, __VA_ARGS__); exit(EXIT_FAILURE); }
+
+#define INIT_PROCS 50
+#define THREAD_MULT 8
+static struct proc_info **old_procs, **new_procs;
+static int num_old_procs, num_new_procs;
+static struct proc_info *free_procs;
+static int num_used_procs, num_free_procs;
+
+static int max_procs, delay, iterations, threads;
+
+static struct cpu_info old_cpu, new_cpu;
+
+static struct proc_info *alloc_proc(void);
+static void free_proc(struct proc_info *proc);
+static void read_procs(void);
+static int read_stat(char *filename, struct proc_info *proc);
+static void read_policy(int pid, struct proc_info *proc);
+static void add_proc(int proc_num, struct proc_info *proc);
+static int read_cmdline(char *filename, struct proc_info *proc);
+static int read_status(char *filename, struct proc_info *proc);
+static void print_procs(void);
+static struct proc_info *find_old_proc(pid_t pid, pid_t tid);
+static void free_old_procs(void);
+static int (*proc_cmp)(const void *a, const void *b);
+static int proc_cpu_cmp(const void *a, const void *b);
+static int proc_vss_cmp(const void *a, const void *b);
+static int proc_rss_cmp(const void *a, const void *b);
+static int proc_thr_cmp(const void *a, const void *b);
+static int numcmp(long long a, long long b);
+static void usage(char *cmd);
+
+int top_main(int argc, char *argv[]) {
+ num_used_procs = num_free_procs = 0;
+
+ max_procs = 0;
+ delay = 3;
+ iterations = -1;
+ proc_cmp = &proc_cpu_cmp;
+ for (int i = 1; i < argc; i++) {
+ if (!strcmp(argv[i], "-m")) {
+ if (i + 1 >= argc) {
+ fprintf(stderr, "Option -m expects an argument.\n");
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ max_procs = atoi(argv[++i]);
+ continue;
+ }
+ if (!strcmp(argv[i], "-n")) {
+ if (i + 1 >= argc) {
+ fprintf(stderr, "Option -n expects an argument.\n");
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ iterations = atoi(argv[++i]);
+ continue;
+ }
+ if (!strcmp(argv[i], "-d")) {
+ if (i + 1 >= argc) {
+ fprintf(stderr, "Option -d expects an argument.\n");
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ delay = atoi(argv[++i]);
+ continue;
+ }
+ if (!strcmp(argv[i], "-s")) {
+ if (i + 1 >= argc) {
+ fprintf(stderr, "Option -s expects an argument.\n");
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ ++i;
+ if (!strcmp(argv[i], "cpu")) { proc_cmp = &proc_cpu_cmp; continue; }
+ if (!strcmp(argv[i], "vss")) { proc_cmp = &proc_vss_cmp; continue; }
+ if (!strcmp(argv[i], "rss")) { proc_cmp = &proc_rss_cmp; continue; }
+ if (!strcmp(argv[i], "thr")) { proc_cmp = &proc_thr_cmp; continue; }
+ fprintf(stderr, "Invalid argument \"%s\" for option -s.\n", argv[i]);
+ exit(EXIT_FAILURE);
+ }
+ if (!strcmp(argv[i], "-t")) { threads = 1; continue; }
+ if (!strcmp(argv[i], "-h")) {
+ usage(argv[0]);
+ exit(EXIT_SUCCESS);
+ }
+ fprintf(stderr, "Invalid argument \"%s\".\n", argv[i]);
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ if (threads && proc_cmp == &proc_thr_cmp) {
+ fprintf(stderr, "Sorting by threads per thread makes no sense!\n");
+ exit(EXIT_FAILURE);
+ }
+
+ free_procs = NULL;
+
+ num_new_procs = num_old_procs = 0;
+ new_procs = old_procs = NULL;
+
+ read_procs();
+ while ((iterations == -1) || (iterations-- > 0)) {
+ old_procs = new_procs;
+ num_old_procs = num_new_procs;
+ memcpy(&old_cpu, &new_cpu, sizeof(old_cpu));
+ sleep(delay);
+ read_procs();
+ print_procs();
+ free_old_procs();
+ }
+
+ return 0;
+}
+
+static struct proc_info *alloc_proc(void) {
+ struct proc_info *proc;
+
+ if (free_procs) {
+ proc = free_procs;
+ free_procs = free_procs->next;
+ num_free_procs--;
+ } else {
+ proc = malloc(sizeof(*proc));
+ if (!proc) die("Could not allocate struct process_info.\n");
+ }
+
+ num_used_procs++;
+
+ return proc;
+}
+
+static void free_proc(struct proc_info *proc) {
+ proc->next = free_procs;
+ free_procs = proc;
+
+ num_used_procs--;
+ num_free_procs++;
+}
+
+#define MAX_LINE 256
+
+static void read_procs(void) {
+ DIR *proc_dir, *task_dir;
+ struct dirent *pid_dir, *tid_dir;
+ char filename[64];
+ FILE *file;
+ int proc_num;
+ struct proc_info *proc;
+ pid_t pid, tid;
+
+ int i;
+
+ proc_dir = opendir("/proc");
+ if (!proc_dir) die("Could not open /proc.\n");
+
+ new_procs = calloc(INIT_PROCS * (threads ? THREAD_MULT : 1), sizeof(struct proc_info *));
+ num_new_procs = INIT_PROCS * (threads ? THREAD_MULT : 1);
+
+ file = fopen("/proc/stat", "r");
+ if (!file) die("Could not open /proc/stat.\n");
+ fscanf(file, "cpu %lu %lu %lu %lu %lu %lu %lu", &new_cpu.utime, &new_cpu.ntime, &new_cpu.stime,
+ &new_cpu.itime, &new_cpu.iowtime, &new_cpu.irqtime, &new_cpu.sirqtime);
+ fclose(file);
+
+ proc_num = 0;
+ while ((pid_dir = readdir(proc_dir))) {
+ if (!isdigit(pid_dir->d_name[0]))
+ continue;
+
+ pid = atoi(pid_dir->d_name);
+
+ struct proc_info cur_proc;
+
+ if (!threads) {
+ proc = alloc_proc();
+
+ proc->pid = proc->tid = pid;
+
+ sprintf(filename, "/proc/%d/stat", pid);
+ read_stat(filename, proc);
+
+ sprintf(filename, "/proc/%d/cmdline", pid);
+ read_cmdline(filename, proc);
+
+ sprintf(filename, "/proc/%d/status", pid);
+ read_status(filename, proc);
+
+ read_policy(pid, proc);
+
+ proc->num_threads = 0;
+ } else {
+ sprintf(filename, "/proc/%d/cmdline", pid);
+ read_cmdline(filename, &cur_proc);
+
+ sprintf(filename, "/proc/%d/status", pid);
+ read_status(filename, &cur_proc);
+
+ proc = NULL;
+ }
+
+ sprintf(filename, "/proc/%d/task", pid);
+ task_dir = opendir(filename);
+ if (!task_dir) continue;
+
+ while ((tid_dir = readdir(task_dir))) {
+ if (!isdigit(tid_dir->d_name[0]))
+ continue;
+
+ if (threads) {
+ tid = atoi(tid_dir->d_name);
+
+ proc = alloc_proc();
+
+ proc->pid = pid; proc->tid = tid;
+
+ sprintf(filename, "/proc/%d/task/%d/stat", pid, tid);
+ read_stat(filename, proc);
+
+ read_policy(tid, proc);
+
+ strcpy(proc->name, cur_proc.name);
+ proc->uid = cur_proc.uid;
+ proc->gid = cur_proc.gid;
+
+ add_proc(proc_num++, proc);
+ } else {
+ proc->num_threads++;
+ }
+ }
+
+ closedir(task_dir);
+
+ if (!threads)
+ add_proc(proc_num++, proc);
+ }
+
+ for (i = proc_num; i < num_new_procs; i++)
+ new_procs[i] = NULL;
+
+ closedir(proc_dir);
+}
+
+static int read_stat(char *filename, struct proc_info *proc) {
+ FILE *file;
+ char buf[MAX_LINE], *open_paren, *close_paren;
+
+ file = fopen(filename, "r");
+ if (!file) return 1;
+ fgets(buf, MAX_LINE, file);
+ fclose(file);
+
+ /* Split at first '(' and last ')' to get process name. */
+ open_paren = strchr(buf, '(');
+ close_paren = strrchr(buf, ')');
+ if (!open_paren || !close_paren) return 1;
+
+ *open_paren = *close_paren = '\0';
+ strncpy(proc->tname, open_paren + 1, THREAD_NAME_LEN);
+ proc->tname[THREAD_NAME_LEN-1] = 0;
+
+ /* Scan rest of string. */
+ sscanf(close_paren + 1,
+ " %c " "%*d %*d %*d %*d %*d %*d %*d %*d %*d %*d "
+ "%" SCNu64
+ "%" SCNu64 "%*d %*d %*d %*d %*d %*d %*d "
+ "%" SCNu64
+ "%" SCNu64 "%*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d "
+ "%d",
+ &proc->state,
+ &proc->utime,
+ &proc->stime,
+ &proc->vss,
+ &proc->rss,
+ &proc->prs);
+
+ return 0;
+}
+
+static void add_proc(int proc_num, struct proc_info *proc) {
+ int i;
+
+ if (proc_num >= num_new_procs) {
+ new_procs = realloc(new_procs, 2 * num_new_procs * sizeof(struct proc_info *));
+ if (!new_procs) die("Could not expand procs array.\n");
+ for (i = num_new_procs; i < 2 * num_new_procs; i++)
+ new_procs[i] = NULL;
+ num_new_procs = 2 * num_new_procs;
+ }
+ new_procs[proc_num] = proc;
+}
+
+static int read_cmdline(char *filename, struct proc_info *proc) {
+ FILE *file;
+ char line[MAX_LINE];
+
+ line[0] = '\0';
+ file = fopen(filename, "r");
+ if (!file) return 1;
+ fgets(line, MAX_LINE, file);
+ fclose(file);
+ if (strlen(line) > 0) {
+ strncpy(proc->name, line, PROC_NAME_LEN);
+ proc->name[PROC_NAME_LEN-1] = 0;
+ } else
+ proc->name[0] = 0;
+ return 0;
+}
+
+static void read_policy(int pid, struct proc_info *proc) {
+ SchedPolicy p;
+ if (get_sched_policy(pid, &p) < 0)
+ strlcpy(proc->policy, "unk", POLICY_NAME_LEN);
+ else {
+ strlcpy(proc->policy, get_sched_policy_name(p), POLICY_NAME_LEN);
+ proc->policy[2] = '\0';
+ }
+}
+
+static int read_status(char *filename, struct proc_info *proc) {
+ FILE *file;
+ char line[MAX_LINE];
+ unsigned int uid, gid;
+
+ file = fopen(filename, "r");
+ if (!file) return 1;
+ while (fgets(line, MAX_LINE, file)) {
+ sscanf(line, "Uid: %u", &uid);
+ sscanf(line, "Gid: %u", &gid);
+ }
+ fclose(file);
+ proc->uid = uid; proc->gid = gid;
+ return 0;
+}
+
+static void print_procs(void) {
+ int i;
+ struct proc_info *old_proc, *proc;
+ long unsigned total_delta_time;
+ struct passwd *user;
+ char *user_str, user_buf[20];
+
+ for (i = 0; i < num_new_procs; i++) {
+ if (new_procs[i]) {
+ old_proc = find_old_proc(new_procs[i]->pid, new_procs[i]->tid);
+ if (old_proc) {
+ new_procs[i]->delta_utime = new_procs[i]->utime - old_proc->utime;
+ new_procs[i]->delta_stime = new_procs[i]->stime - old_proc->stime;
+ } else {
+ new_procs[i]->delta_utime = 0;
+ new_procs[i]->delta_stime = 0;
+ }
+ new_procs[i]->delta_time = new_procs[i]->delta_utime + new_procs[i]->delta_stime;
+ }
+ }
+
+ total_delta_time = (new_cpu.utime + new_cpu.ntime + new_cpu.stime + new_cpu.itime
+ + new_cpu.iowtime + new_cpu.irqtime + new_cpu.sirqtime)
+ - (old_cpu.utime + old_cpu.ntime + old_cpu.stime + old_cpu.itime
+ + old_cpu.iowtime + old_cpu.irqtime + old_cpu.sirqtime);
+
+ qsort(new_procs, num_new_procs, sizeof(struct proc_info *), proc_cmp);
+
+ printf("\n\n\n");
+ printf("User %ld%%, System %ld%%, IOW %ld%%, IRQ %ld%%\n",
+ ((new_cpu.utime + new_cpu.ntime) - (old_cpu.utime + old_cpu.ntime)) * 100 / total_delta_time,
+ ((new_cpu.stime ) - (old_cpu.stime)) * 100 / total_delta_time,
+ ((new_cpu.iowtime) - (old_cpu.iowtime)) * 100 / total_delta_time,
+ ((new_cpu.irqtime + new_cpu.sirqtime)
+ - (old_cpu.irqtime + old_cpu.sirqtime)) * 100 / total_delta_time);
+ printf("User %ld + Nice %ld + Sys %ld + Idle %ld + IOW %ld + IRQ %ld + SIRQ %ld = %ld\n",
+ new_cpu.utime - old_cpu.utime,
+ new_cpu.ntime - old_cpu.ntime,
+ new_cpu.stime - old_cpu.stime,
+ new_cpu.itime - old_cpu.itime,
+ new_cpu.iowtime - old_cpu.iowtime,
+ new_cpu.irqtime - old_cpu.irqtime,
+ new_cpu.sirqtime - old_cpu.sirqtime,
+ total_delta_time);
+ printf("\n");
+ if (!threads)
+ printf("%5s %2s %4s %1s %5s %7s %7s %3s %-8s %s\n", "PID", "PR", "CPU%", "S", "#THR", "VSS", "RSS", "PCY", "UID", "Name");
+ else
+ printf("%5s %5s %2s %4s %1s %7s %7s %3s %-8s %-15s %s\n", "PID", "TID", "PR", "CPU%", "S", "VSS", "RSS", "PCY", "UID", "Thread", "Proc");
+
+ for (i = 0; i < num_new_procs; i++) {
+ proc = new_procs[i];
+
+ if (!proc || (max_procs && (i >= max_procs)))
+ break;
+ user = getpwuid(proc->uid);
+ if (user && user->pw_name) {
+ user_str = user->pw_name;
+ } else {
+ snprintf(user_buf, 20, "%d", proc->uid);
+ user_str = user_buf;
+ }
+ if (!threads) {
+ printf("%5d %2d %3" PRIu64 "%% %c %5d %6" PRIu64 "K %6" PRIu64 "K %3s %-8.8s %s\n",
+ proc->pid, proc->prs, proc->delta_time * 100 / total_delta_time, proc->state, proc->num_threads,
+ proc->vss / 1024, proc->rss * getpagesize() / 1024, proc->policy, user_str, proc->name[0] != 0 ? proc->name : proc->tname);
+ } else {
+ printf("%5d %5d %2d %3" PRIu64 "%% %c %6" PRIu64 "K %6" PRIu64 "K %3s %-8.8s %-15s %s\n",
+ proc->pid, proc->tid, proc->prs, proc->delta_time * 100 / total_delta_time, proc->state,
+ proc->vss / 1024, proc->rss * getpagesize() / 1024, proc->policy, user_str, proc->tname, proc->name);
+ }
+ }
+}
+
+static struct proc_info *find_old_proc(pid_t pid, pid_t tid) {
+ int i;
+
+ for (i = 0; i < num_old_procs; i++)
+ if (old_procs[i] && (old_procs[i]->pid == pid) && (old_procs[i]->tid == tid))
+ return old_procs[i];
+
+ return NULL;
+}
+
+static void free_old_procs(void) {
+ int i;
+
+ for (i = 0; i < num_old_procs; i++)
+ if (old_procs[i])
+ free_proc(old_procs[i]);
+
+ free(old_procs);
+}
+
+static int proc_cpu_cmp(const void *a, const void *b) {
+ struct proc_info *pa, *pb;
+
+ pa = *((struct proc_info **)a); pb = *((struct proc_info **)b);
+
+ if (!pa && !pb) return 0;
+ if (!pa) return 1;
+ if (!pb) return -1;
+
+ return -numcmp(pa->delta_time, pb->delta_time);
+}
+
+static int proc_vss_cmp(const void *a, const void *b) {
+ struct proc_info *pa, *pb;
+
+ pa = *((struct proc_info **)a); pb = *((struct proc_info **)b);
+
+ if (!pa && !pb) return 0;
+ if (!pa) return 1;
+ if (!pb) return -1;
+
+ return -numcmp(pa->vss, pb->vss);
+}
+
+static int proc_rss_cmp(const void *a, const void *b) {
+ struct proc_info *pa, *pb;
+
+ pa = *((struct proc_info **)a); pb = *((struct proc_info **)b);
+
+ if (!pa && !pb) return 0;
+ if (!pa) return 1;
+ if (!pb) return -1;
+
+ return -numcmp(pa->rss, pb->rss);
+}
+
+static int proc_thr_cmp(const void *a, const void *b) {
+ struct proc_info *pa, *pb;
+
+ pa = *((struct proc_info **)a); pb = *((struct proc_info **)b);
+
+ if (!pa && !pb) return 0;
+ if (!pa) return 1;
+ if (!pb) return -1;
+
+ return -numcmp(pa->num_threads, pb->num_threads);
+}
+
+static int numcmp(long long a, long long b) {
+ if (a < b) return -1;
+ if (a > b) return 1;
+ return 0;
+}
+
+static void usage(char *cmd) {
+ fprintf(stderr, "Usage: %s [ -m max_procs ] [ -n iterations ] [ -d delay ] [ -s sort_column ] [ -t ] [ -h ]\n"
+ " -m num Maximum number of processes to display.\n"
+ " -n num Updates to show before exiting.\n"
+ " -d num Seconds to wait between updates.\n"
+ " -s col Column to sort by (cpu,vss,rss,thr).\n"
+ " -t Show threads instead of processes.\n"
+ " -h Display this help screen.\n",
+ cmd);
+}
diff --git a/toolbox/upstream-netbsd/bin/dd/args.c b/toolbox/upstream-netbsd/bin/dd/args.c
new file mode 100644
index 0000000..207e300
--- /dev/null
+++ b/toolbox/upstream-netbsd/bin/dd/args.c
@@ -0,0 +1,391 @@
+/* $NetBSD: args.c,v 1.38 2013/07/17 12:55:48 christos Exp $ */
+
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego and Lance
+ * Visser of Convex Computer Corporation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)args.c 8.3 (Berkeley) 4/2/94";
+#else
+__RCSID("$NetBSD: args.c,v 1.38 2013/07/17 12:55:48 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "dd.h"
+#include "extern.h"
+
+static int c_arg(const void *, const void *);
+
+#ifdef NO_MSGFMT
+static void f_msgfmt(char *) __dead;
+#else
+static void f_msgfmt(char *);
+#endif /* NO_MSGFMT */
+
+#ifdef NO_CONV
+static void f_conv(char *) __dead;
+#else
+static void f_conv(char *);
+static int c_conv(const void *, const void *);
+#endif /* NO_CONV */
+
+static void f_bs(char *);
+static void f_cbs(char *);
+static void f_count(char *);
+static void f_files(char *);
+static void f_ibs(char *);
+static void f_if(char *);
+static void f_obs(char *);
+static void f_of(char *);
+static void f_seek(char *);
+static void f_skip(char *);
+static void f_progress(char *);
+
+static const struct arg {
+ const char *name;
+ void (*f)(char *);
+ u_int set, noset;
+} args[] = {
+ /* the array needs to be sorted by the first column so
+ bsearch() can be used to find commands quickly */
+ { "bs", f_bs, C_BS, C_BS|C_IBS|C_OBS|C_OSYNC },
+ { "cbs", f_cbs, C_CBS, C_CBS },
+ { "conv", f_conv, 0, 0 },
+ { "count", f_count, C_COUNT, C_COUNT },
+ { "files", f_files, C_FILES, C_FILES },
+ { "ibs", f_ibs, C_IBS, C_BS|C_IBS },
+ { "if", f_if, C_IF, C_IF },
+ { "iseek", f_skip, C_SKIP, C_SKIP },
+ { "msgfmt", f_msgfmt, 0, 0 },
+ { "obs", f_obs, C_OBS, C_BS|C_OBS },
+ { "of", f_of, C_OF, C_OF },
+ { "oseek", f_seek, C_SEEK, C_SEEK },
+ { "progress", f_progress, 0, 0 },
+ { "seek", f_seek, C_SEEK, C_SEEK },
+ { "skip", f_skip, C_SKIP, C_SKIP },
+};
+
+/*
+ * args -- parse JCL syntax of dd.
+ */
+void
+jcl(char **argv)
+{
+ struct arg *ap, tmp;
+ char *oper, *arg;
+
+ in.dbsz = out.dbsz = 512;
+
+ while ((oper = *++argv) != NULL) {
+ if ((oper = strdup(oper)) == NULL) {
+ errx(EXIT_FAILURE,
+ "unable to allocate space for the argument %s",
+ *argv);
+ /* NOTREACHED */
+ }
+ if ((arg = strchr(oper, '=')) == NULL) {
+ errx(EXIT_FAILURE, "unknown operand %s", oper);
+ /* NOTREACHED */
+ }
+ *arg++ = '\0';
+ if (!*arg) {
+ errx(EXIT_FAILURE, "no value specified for %s", oper);
+ /* NOTREACHED */
+ }
+ tmp.name = oper;
+ if (!(ap = bsearch(&tmp, args,
+ __arraycount(args), sizeof(*args), c_arg))) {
+ errx(EXIT_FAILURE, "unknown operand %s", tmp.name);
+ /* NOTREACHED */
+ }
+ if (ddflags & ap->noset) {
+ errx(EXIT_FAILURE,
+ "%s: illegal argument combination or already set",
+ tmp.name);
+ /* NOTREACHED */
+ }
+ ddflags |= ap->set;
+ ap->f(arg);
+ }
+
+ /* Final sanity checks. */
+
+ if (ddflags & C_BS) {
+ /*
+ * Bs is turned off by any conversion -- we assume the user
+ * just wanted to set both the input and output block sizes
+ * and didn't want the bs semantics, so we don't warn.
+ */
+ if (ddflags & (C_BLOCK | C_LCASE | C_SWAB | C_UCASE |
+ C_UNBLOCK | C_OSYNC | C_ASCII | C_EBCDIC | C_SPARSE)) {
+ ddflags &= ~C_BS;
+ ddflags |= C_IBS|C_OBS;
+ }
+
+ /* Bs supersedes ibs and obs. */
+ if (ddflags & C_BS && ddflags & (C_IBS|C_OBS))
+ warnx("bs supersedes ibs and obs");
+ }
+
+ /*
+ * Ascii/ebcdic and cbs implies block/unblock.
+ * Block/unblock requires cbs and vice-versa.
+ */
+ if (ddflags & (C_BLOCK|C_UNBLOCK)) {
+ if (!(ddflags & C_CBS)) {
+ errx(EXIT_FAILURE, "record operations require cbs");
+ /* NOTREACHED */
+ }
+ cfunc = ddflags & C_BLOCK ? block : unblock;
+ } else if (ddflags & C_CBS) {
+ if (ddflags & (C_ASCII|C_EBCDIC)) {
+ if (ddflags & C_ASCII) {
+ ddflags |= C_UNBLOCK;
+ cfunc = unblock;
+ } else {
+ ddflags |= C_BLOCK;
+ cfunc = block;
+ }
+ } else {
+ errx(EXIT_FAILURE,
+ "cbs meaningless if not doing record operations");
+ /* NOTREACHED */
+ }
+ } else
+ cfunc = def;
+
+ /* Read, write and seek calls take off_t as arguments.
+ *
+ * The following check is not done because an off_t is a quad
+ * for current NetBSD implementations.
+ *
+ * if (in.offset > INT_MAX/in.dbsz || out.offset > INT_MAX/out.dbsz)
+ * errx(1, "seek offsets cannot be larger than %d", INT_MAX);
+ */
+}
+
+static int
+c_arg(const void *a, const void *b)
+{
+
+ return (strcmp(((const struct arg *)a)->name,
+ ((const struct arg *)b)->name));
+}
+
+static void
+f_bs(char *arg)
+{
+
+ in.dbsz = out.dbsz = strsuftoll("block size", arg, 1, UINT_MAX);
+}
+
+static void
+f_cbs(char *arg)
+{
+
+ cbsz = strsuftoll("conversion record size", arg, 1, UINT_MAX);
+}
+
+static void
+f_count(char *arg)
+{
+
+ cpy_cnt = strsuftoll("block count", arg, 0, LLONG_MAX);
+ if (!cpy_cnt)
+ terminate(0);
+}
+
+static void
+f_files(char *arg)
+{
+
+ files_cnt = (u_int)strsuftoll("file count", arg, 0, UINT_MAX);
+ if (!files_cnt)
+ terminate(0);
+}
+
+static void
+f_ibs(char *arg)
+{
+
+ if (!(ddflags & C_BS))
+ in.dbsz = strsuftoll("input block size", arg, 1, UINT_MAX);
+}
+
+static void
+f_if(char *arg)
+{
+
+ in.name = arg;
+}
+
+#ifdef NO_MSGFMT
+/* Build a small version (i.e. for a ramdisk root) */
+static void
+f_msgfmt(char *arg)
+{
+
+ errx(EXIT_FAILURE, "msgfmt option disabled");
+ /* NOTREACHED */
+}
+#else /* NO_MSGFMT */
+static void
+f_msgfmt(char *arg)
+{
+
+ /*
+ * If the format string is not valid, dd_write_msg() will print
+ * an error and exit.
+ */
+ dd_write_msg(arg, 0);
+
+ msgfmt = arg;
+}
+#endif /* NO_MSGFMT */
+
+static void
+f_obs(char *arg)
+{
+
+ if (!(ddflags & C_BS))
+ out.dbsz = strsuftoll("output block size", arg, 1, UINT_MAX);
+}
+
+static void
+f_of(char *arg)
+{
+
+ out.name = arg;
+}
+
+static void
+f_seek(char *arg)
+{
+
+ out.offset = strsuftoll("seek blocks", arg, 0, LLONG_MAX);
+}
+
+static void
+f_skip(char *arg)
+{
+
+ in.offset = strsuftoll("skip blocks", arg, 0, LLONG_MAX);
+}
+
+static void
+f_progress(char *arg)
+{
+
+ progress = strsuftoll("progress blocks", arg, 0, LLONG_MAX);
+}
+
+#ifdef NO_CONV
+/* Build a small version (i.e. for a ramdisk root) */
+static void
+f_conv(char *arg)
+{
+
+ errx(EXIT_FAILURE, "conv option disabled");
+ /* NOTREACHED */
+}
+#else /* NO_CONV */
+
+static const struct conv {
+ const char *name;
+ u_int set, noset;
+ const u_char *ctab;
+} clist[] = {
+ { "ascii", C_ASCII, C_EBCDIC, e2a_POSIX },
+ { "block", C_BLOCK, C_UNBLOCK, NULL },
+ { "ebcdic", C_EBCDIC, C_ASCII, a2e_POSIX },
+ { "ibm", C_EBCDIC, C_ASCII, a2ibm_POSIX },
+ { "lcase", C_LCASE, C_UCASE, NULL },
+ { "noerror", C_NOERROR, 0, NULL },
+ { "notrunc", C_NOTRUNC, 0, NULL },
+ { "oldascii", C_ASCII, C_EBCDIC, e2a_32V },
+ { "oldebcdic", C_EBCDIC, C_ASCII, a2e_32V },
+ { "oldibm", C_EBCDIC, C_ASCII, a2ibm_32V },
+ { "osync", C_OSYNC, C_BS, NULL },
+ { "sparse", C_SPARSE, 0, NULL },
+ { "swab", C_SWAB, 0, NULL },
+ { "sync", C_SYNC, 0, NULL },
+ { "ucase", C_UCASE, C_LCASE, NULL },
+ { "unblock", C_UNBLOCK, C_BLOCK, NULL },
+ /* If you add items to this table, be sure to add the
+ * conversions to the C_BS check in the jcl routine above.
+ */
+};
+
+static void
+f_conv(char *arg)
+{
+ struct conv *cp, tmp;
+
+ while (arg != NULL) {
+ tmp.name = strsep(&arg, ",");
+ if (!(cp = bsearch(&tmp, clist,
+ __arraycount(clist), sizeof(*clist), c_conv))) {
+ errx(EXIT_FAILURE, "unknown conversion %s", tmp.name);
+ /* NOTREACHED */
+ }
+ if (ddflags & cp->noset) {
+ errx(EXIT_FAILURE,
+ "%s: illegal conversion combination", tmp.name);
+ /* NOTREACHED */
+ }
+ ddflags |= cp->set;
+ if (cp->ctab)
+ ctab = cp->ctab;
+ }
+}
+
+static int
+c_conv(const void *a, const void *b)
+{
+
+ return (strcmp(((const struct conv *)a)->name,
+ ((const struct conv *)b)->name));
+}
+
+#endif /* NO_CONV */
diff --git a/toolbox/upstream-netbsd/bin/dd/conv.c b/toolbox/upstream-netbsd/bin/dd/conv.c
new file mode 100644
index 0000000..d4a8a09
--- /dev/null
+++ b/toolbox/upstream-netbsd/bin/dd/conv.c
@@ -0,0 +1,283 @@
+/* $NetBSD: conv.c,v 1.17 2003/08/07 09:05:10 agc Exp $ */
+
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego and Lance
+ * Visser of Convex Computer Corporation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)conv.c 8.3 (Berkeley) 4/2/94";
+#else
+__RCSID("$NetBSD: conv.c,v 1.17 2003/08/07 09:05:10 agc Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+
+#include <err.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "dd.h"
+#include "extern.h"
+
+/*
+ * def --
+ * Copy input to output. Input is buffered until reaches obs, and then
+ * output until less than obs remains. Only a single buffer is used.
+ * Worst case buffer calculation is (ibs + obs - 1).
+ */
+void
+def(void)
+{
+ uint64_t cnt;
+ u_char *inp;
+ const u_char *t;
+
+ if ((t = ctab) != NULL)
+ for (inp = in.dbp - (cnt = in.dbrcnt); cnt--; ++inp)
+ *inp = t[*inp];
+
+ /* Make the output buffer look right. */
+ out.dbp = in.dbp;
+ out.dbcnt = in.dbcnt;
+
+ if (in.dbcnt >= out.dbsz) {
+ /* If the output buffer is full, write it. */
+ dd_out(0);
+
+ /*
+ * Ddout copies the leftover output to the beginning of
+ * the buffer and resets the output buffer. Reset the
+ * input buffer to match it.
+ */
+ in.dbp = out.dbp;
+ in.dbcnt = out.dbcnt;
+ }
+}
+
+void
+def_close(void)
+{
+
+ /* Just update the count, everything is already in the buffer. */
+ if (in.dbcnt)
+ out.dbcnt = in.dbcnt;
+}
+
+#ifdef NO_CONV
+/* Build a smaller version (i.e. for a miniroot) */
+/* These can not be called, but just in case... */
+static const char no_block[] = "unblock and -DNO_CONV?";
+void block(void) { errx(EXIT_FAILURE, "%s", no_block + 2); }
+void block_close(void) { errx(EXIT_FAILURE, "%s", no_block + 2); }
+void unblock(void) { errx(EXIT_FAILURE, "%s", no_block); }
+void unblock_close(void) { errx(EXIT_FAILURE, "%s", no_block); }
+#else /* NO_CONV */
+
+/*
+ * Copy variable length newline terminated records with a max size cbsz
+ * bytes to output. Records less than cbs are padded with spaces.
+ *
+ * max in buffer: MAX(ibs, cbsz)
+ * max out buffer: obs + cbsz
+ */
+void
+block(void)
+{
+ static int intrunc;
+ int ch = 0; /* pacify gcc */
+ uint64_t cnt, maxlen;
+ u_char *inp, *outp;
+ const u_char *t;
+
+ /*
+ * Record truncation can cross block boundaries. If currently in a
+ * truncation state, keep tossing characters until reach a newline.
+ * Start at the beginning of the buffer, as the input buffer is always
+ * left empty.
+ */
+ if (intrunc) {
+ for (inp = in.db, cnt = in.dbrcnt;
+ cnt && *inp++ != '\n'; --cnt);
+ if (!cnt) {
+ in.dbcnt = 0;
+ in.dbp = in.db;
+ return;
+ }
+ intrunc = 0;
+ /* Adjust the input buffer numbers. */
+ in.dbcnt = cnt - 1;
+ in.dbp = inp + cnt - 1;
+ }
+
+ /*
+ * Copy records (max cbsz size chunks) into the output buffer. The
+ * translation is done as we copy into the output buffer.
+ */
+ for (inp = in.dbp - in.dbcnt, outp = out.dbp; in.dbcnt;) {
+ maxlen = MIN(cbsz, in.dbcnt);
+ if ((t = ctab) != NULL)
+ for (cnt = 0;
+ cnt < maxlen && (ch = *inp++) != '\n'; ++cnt)
+ *outp++ = t[ch];
+ else
+ for (cnt = 0;
+ cnt < maxlen && (ch = *inp++) != '\n'; ++cnt)
+ *outp++ = ch;
+ /*
+ * Check for short record without a newline. Reassemble the
+ * input block.
+ */
+ if (ch != '\n' && in.dbcnt < cbsz) {
+ (void)memmove(in.db, in.dbp - in.dbcnt, in.dbcnt);
+ break;
+ }
+
+ /* Adjust the input buffer numbers. */
+ in.dbcnt -= cnt;
+ if (ch == '\n')
+ --in.dbcnt;
+
+ /* Pad short records with spaces. */
+ if (cnt < cbsz)
+ (void)memset(outp, ctab ? ctab[' '] : ' ', cbsz - cnt);
+ else {
+ /*
+ * If the next character wouldn't have ended the
+ * block, it's a truncation.
+ */
+ if (!in.dbcnt || *inp != '\n')
+ ++st.trunc;
+
+ /* Toss characters to a newline. */
+ for (; in.dbcnt && *inp++ != '\n'; --in.dbcnt);
+ if (!in.dbcnt)
+ intrunc = 1;
+ else
+ --in.dbcnt;
+ }
+
+ /* Adjust output buffer numbers. */
+ out.dbp += cbsz;
+ if ((out.dbcnt += cbsz) >= out.dbsz)
+ dd_out(0);
+ outp = out.dbp;
+ }
+ in.dbp = in.db + in.dbcnt;
+}
+
+void
+block_close(void)
+{
+
+ /*
+ * Copy any remaining data into the output buffer and pad to a record.
+ * Don't worry about truncation or translation, the input buffer is
+ * always empty when truncating, and no characters have been added for
+ * translation. The bottom line is that anything left in the input
+ * buffer is a truncated record. Anything left in the output buffer
+ * just wasn't big enough.
+ */
+ if (in.dbcnt) {
+ ++st.trunc;
+ (void)memmove(out.dbp, in.dbp - in.dbcnt, in.dbcnt);
+ (void)memset(out.dbp + in.dbcnt,
+ ctab ? ctab[' '] : ' ', cbsz - in.dbcnt);
+ out.dbcnt += cbsz;
+ }
+}
+
+/*
+ * Convert fixed length (cbsz) records to variable length. Deletes any
+ * trailing blanks and appends a newline.
+ *
+ * max in buffer: MAX(ibs, cbsz) + cbsz
+ * max out buffer: obs + cbsz
+ */
+void
+unblock(void)
+{
+ uint64_t cnt;
+ u_char *inp;
+ const u_char *t;
+
+ /* Translation and case conversion. */
+ if ((t = ctab) != NULL)
+ for (cnt = in.dbrcnt, inp = in.dbp - 1; cnt--; inp--)
+ *inp = t[*inp];
+ /*
+ * Copy records (max cbsz size chunks) into the output buffer. The
+ * translation has to already be done or we might not recognize the
+ * spaces.
+ */
+ for (inp = in.db; in.dbcnt >= cbsz; inp += cbsz, in.dbcnt -= cbsz) {
+ for (t = inp + cbsz - 1; t >= inp && *t == ' '; --t);
+ if (t >= inp) {
+ cnt = t - inp + 1;
+ (void)memmove(out.dbp, inp, cnt);
+ out.dbp += cnt;
+ out.dbcnt += cnt;
+ }
+ ++out.dbcnt;
+ *out.dbp++ = '\n';
+ if (out.dbcnt >= out.dbsz)
+ dd_out(0);
+ }
+ if (in.dbcnt)
+ (void)memmove(in.db, in.dbp - in.dbcnt, in.dbcnt);
+ in.dbp = in.db + in.dbcnt;
+}
+
+void
+unblock_close(void)
+{
+ uint64_t cnt;
+ u_char *t;
+
+ if (in.dbcnt) {
+ warnx("%s: short input record", in.name);
+ for (t = in.db + in.dbcnt - 1; t >= in.db && *t == ' '; --t);
+ if (t >= in.db) {
+ cnt = t - in.db + 1;
+ (void)memmove(out.dbp, in.db, cnt);
+ out.dbp += cnt;
+ out.dbcnt += cnt;
+ }
+ ++out.dbcnt;
+ *out.dbp++ = '\n';
+ }
+}
+
+#endif /* NO_CONV */
diff --git a/toolbox/upstream-netbsd/bin/dd/dd.c b/toolbox/upstream-netbsd/bin/dd/dd.c
new file mode 100644
index 0000000..03d080c
--- /dev/null
+++ b/toolbox/upstream-netbsd/bin/dd/dd.c
@@ -0,0 +1,598 @@
+/* $NetBSD: dd.c,v 1.49 2012/02/21 01:49:01 matt Exp $ */
+
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego and Lance
+ * Visser of Convex Computer Corporation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__COPYRIGHT("@(#) Copyright (c) 1991, 1993, 1994\
+ The Regents of the University of California. All rights reserved.");
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)dd.c 8.5 (Berkeley) 4/2/94";
+#else
+__RCSID("$NetBSD: dd.c,v 1.49 2012/02/21 01:49:01 matt Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/mtio.h>
+#include <sys/time.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <locale.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "dd.h"
+#include "extern.h"
+
+static void dd_close(void);
+static void dd_in(void);
+static void getfdtype(IO *);
+static void redup_clean_fd(IO *);
+static void setup(void);
+
+int main(int, char *[]);
+
+IO in, out; /* input/output state */
+STAT st; /* statistics */
+void (*cfunc)(void); /* conversion function */
+uint64_t cpy_cnt; /* # of blocks to copy */
+static off_t pending = 0; /* pending seek if sparse */
+u_int ddflags; /* conversion options */
+uint64_t cbsz; /* conversion block size */
+u_int files_cnt = 1; /* # of files to copy */
+uint64_t progress = 0; /* display sign of life */
+const u_char *ctab; /* conversion table */
+sigset_t infoset; /* a set blocking SIGINFO */
+const char *msgfmt = "posix"; /* default summary() message format */
+
+/*
+ * Ops for stdin/stdout and crunch'd dd. These are always host ops.
+ */
+static const struct ddfops ddfops_stdfd = {
+ .op_open = open,
+ .op_close = close,
+ .op_fcntl = fcntl,
+ .op_ioctl = ioctl,
+ .op_fstat = fstat,
+ .op_fsync = fsync,
+ .op_ftruncate = ftruncate,
+ .op_lseek = lseek,
+ .op_read = read,
+ .op_write = write,
+};
+extern const struct ddfops ddfops_prog;
+
+int
+main(int argc, char *argv[])
+{
+ int ch;
+
+ setprogname(argv[0]);
+ (void)setlocale(LC_ALL, "");
+
+ while ((ch = getopt(argc, argv, "")) != -1) {
+ switch (ch) {
+ default:
+ errx(EXIT_FAILURE, "usage: dd [operand ...]");
+ /* NOTREACHED */
+ }
+ }
+ argc -= (optind - 1);
+ argv += (optind - 1);
+
+ jcl(argv);
+#ifndef CRUNCHOPS
+ if (ddfops_prog.op_init && ddfops_prog.op_init() == -1)
+ err(1, "prog init");
+#endif
+ setup();
+
+ (void)signal(SIGINFO, summaryx);
+ (void)signal(SIGINT, terminate);
+ (void)sigemptyset(&infoset);
+ (void)sigaddset(&infoset, SIGINFO);
+
+ (void)atexit(summary);
+
+ while (files_cnt--)
+ dd_in();
+
+ dd_close();
+ exit(0);
+ /* NOTREACHED */
+}
+
+static void
+setup(void)
+{
+#ifdef CRUNCHOPS
+ const struct ddfops *prog_ops = &ddfops_stdfd;
+#else
+ const struct ddfops *prog_ops = &ddfops_prog;
+#endif
+
+ if (in.name == NULL) {
+ in.name = "stdin";
+ in.fd = STDIN_FILENO;
+ in.ops = &ddfops_stdfd;
+ } else {
+ in.ops = prog_ops;
+ in.fd = ddop_open(in, in.name, O_RDONLY, 0);
+ if (in.fd < 0)
+ err(EXIT_FAILURE, "%s", in.name);
+ /* NOTREACHED */
+
+ /* Ensure in.fd is outside the stdio descriptor range */
+ redup_clean_fd(&in);
+ }
+
+ getfdtype(&in);
+
+ if (files_cnt > 1 && !(in.flags & ISTAPE)) {
+ errx(EXIT_FAILURE, "files is not supported for non-tape devices");
+ /* NOTREACHED */
+ }
+
+ if (out.name == NULL) {
+ /* No way to check for read access here. */
+ out.fd = STDOUT_FILENO;
+ out.name = "stdout";
+ out.ops = &ddfops_stdfd;
+ } else {
+ out.ops = prog_ops;
+#define OFLAGS \
+ (O_CREAT | (ddflags & (C_SEEK | C_NOTRUNC) ? 0 : O_TRUNC))
+ out.fd = ddop_open(out, out.name, O_RDWR | OFLAGS, DEFFILEMODE);
+ /*
+ * May not have read access, so try again with write only.
+ * Without read we may have a problem if output also does
+ * not support seeks.
+ */
+ if (out.fd < 0) {
+ out.fd = ddop_open(out, out.name, O_WRONLY | OFLAGS,
+ DEFFILEMODE);
+ out.flags |= NOREAD;
+ }
+ if (out.fd < 0) {
+ err(EXIT_FAILURE, "%s", out.name);
+ /* NOTREACHED */
+ }
+
+ /* Ensure out.fd is outside the stdio descriptor range */
+ redup_clean_fd(&out);
+ }
+
+ getfdtype(&out);
+
+ /*
+ * Allocate space for the input and output buffers. If not doing
+ * record oriented I/O, only need a single buffer.
+ */
+ if (!(ddflags & (C_BLOCK|C_UNBLOCK))) {
+ size_t dbsz = out.dbsz;
+ if (!(ddflags & C_BS))
+ dbsz += in.dbsz - 1;
+ if ((in.db = malloc(dbsz)) == NULL) {
+ err(EXIT_FAILURE, NULL);
+ /* NOTREACHED */
+ }
+ out.db = in.db;
+ } else if ((in.db =
+ malloc((u_int)(MAX(in.dbsz, cbsz) + cbsz))) == NULL ||
+ (out.db = malloc((u_int)(out.dbsz + cbsz))) == NULL) {
+ err(EXIT_FAILURE, NULL);
+ /* NOTREACHED */
+ }
+ in.dbp = in.db;
+ out.dbp = out.db;
+
+ /* Position the input/output streams. */
+ if (in.offset)
+ pos_in();
+ if (out.offset)
+ pos_out();
+
+ /*
+ * Truncate the output file; ignore errors because it fails on some
+ * kinds of output files, tapes, for example.
+ */
+ if ((ddflags & (C_OF | C_SEEK | C_NOTRUNC)) == (C_OF | C_SEEK))
+ (void)ddop_ftruncate(out, out.fd, (off_t)out.offset * out.dbsz);
+
+ /*
+ * If converting case at the same time as another conversion, build a
+ * table that does both at once. If just converting case, use the
+ * built-in tables.
+ */
+ if (ddflags & (C_LCASE|C_UCASE)) {
+#ifdef NO_CONV
+ /* Should not get here, but just in case... */
+ errx(EXIT_FAILURE, "case conv and -DNO_CONV");
+ /* NOTREACHED */
+#else /* NO_CONV */
+ u_int cnt;
+
+ if (ddflags & C_ASCII || ddflags & C_EBCDIC) {
+ if (ddflags & C_LCASE) {
+ for (cnt = 0; cnt < 256; ++cnt)
+ casetab[cnt] = tolower(ctab[cnt]);
+ } else {
+ for (cnt = 0; cnt < 256; ++cnt)
+ casetab[cnt] = toupper(ctab[cnt]);
+ }
+ } else {
+ if (ddflags & C_LCASE) {
+ for (cnt = 0; cnt < 256; ++cnt)
+ casetab[cnt] = tolower(cnt);
+ } else {
+ for (cnt = 0; cnt < 256; ++cnt)
+ casetab[cnt] = toupper(cnt);
+ }
+ }
+
+ ctab = casetab;
+#endif /* NO_CONV */
+ }
+
+ (void)gettimeofday(&st.start, NULL); /* Statistics timestamp. */
+}
+
+static void
+getfdtype(IO *io)
+{
+ struct mtget mt;
+ struct stat sb;
+
+ if (io->ops->op_fstat(io->fd, &sb)) {
+ err(EXIT_FAILURE, "%s", io->name);
+ /* NOTREACHED */
+ }
+ if (S_ISCHR(sb.st_mode))
+ io->flags |= io->ops->op_ioctl(io->fd, MTIOCGET, &mt)
+ ? ISCHR : ISTAPE;
+ else if (io->ops->op_lseek(io->fd, (off_t)0, SEEK_CUR) == -1
+ && errno == ESPIPE)
+ io->flags |= ISPIPE; /* XXX fixed in 4.4BSD */
+}
+
+/*
+ * Move the parameter file descriptor to a descriptor that is outside the
+ * stdio descriptor range, if necessary. This is required to avoid
+ * accidentally outputting completion or error messages into the
+ * output file that were intended for the tty.
+ */
+static void
+redup_clean_fd(IO *io)
+{
+ int fd = io->fd;
+ int newfd;
+
+ if (fd != STDIN_FILENO && fd != STDOUT_FILENO &&
+ fd != STDERR_FILENO)
+ /* File descriptor is ok, return immediately. */
+ return;
+
+ /*
+ * 3 is the first descriptor greater than STD*_FILENO. Any
+ * free descriptor valued 3 or above is acceptable...
+ */
+ newfd = io->ops->op_fcntl(fd, F_DUPFD, 3);
+ if (newfd < 0) {
+ err(EXIT_FAILURE, "dupfd IO");
+ /* NOTREACHED */
+ }
+
+ io->ops->op_close(fd);
+ io->fd = newfd;
+}
+
+static void
+dd_in(void)
+{
+ int flags;
+ int64_t n;
+
+ for (flags = ddflags;;) {
+ if (cpy_cnt && (st.in_full + st.in_part) >= cpy_cnt)
+ return;
+
+ /*
+ * Clear the buffer first if doing "sync" on input.
+ * If doing block operations use spaces. This will
+ * affect not only the C_NOERROR case, but also the
+ * last partial input block which should be padded
+ * with zero and not garbage.
+ */
+ if (flags & C_SYNC) {
+ if (flags & (C_BLOCK|C_UNBLOCK))
+ (void)memset(in.dbp, ' ', in.dbsz);
+ else
+ (void)memset(in.dbp, 0, in.dbsz);
+ }
+
+ n = ddop_read(in, in.fd, in.dbp, in.dbsz);
+ if (n == 0) {
+ in.dbrcnt = 0;
+ return;
+ }
+
+ /* Read error. */
+ if (n < 0) {
+
+ /*
+ * If noerror not specified, die. POSIX requires that
+ * the warning message be followed by an I/O display.
+ */
+ if (!(flags & C_NOERROR)) {
+ err(EXIT_FAILURE, "%s", in.name);
+ /* NOTREACHED */
+ }
+ warn("%s", in.name);
+ summary();
+
+ /*
+ * If it's not a tape drive or a pipe, seek past the
+ * error. If your OS doesn't do the right thing for
+ * raw disks this section should be modified to re-read
+ * in sector size chunks.
+ */
+ if (!(in.flags & (ISPIPE|ISTAPE)) &&
+ ddop_lseek(in, in.fd, (off_t)in.dbsz, SEEK_CUR))
+ warn("%s", in.name);
+
+ /* If sync not specified, omit block and continue. */
+ if (!(ddflags & C_SYNC))
+ continue;
+
+ /* Read errors count as full blocks. */
+ in.dbcnt += in.dbrcnt = in.dbsz;
+ ++st.in_full;
+
+ /* Handle full input blocks. */
+ } else if ((uint64_t)n == in.dbsz) {
+ in.dbcnt += in.dbrcnt = n;
+ ++st.in_full;
+
+ /* Handle partial input blocks. */
+ } else {
+ /* If sync, use the entire block. */
+ if (ddflags & C_SYNC)
+ in.dbcnt += in.dbrcnt = in.dbsz;
+ else
+ in.dbcnt += in.dbrcnt = n;
+ ++st.in_part;
+ }
+
+ /*
+ * POSIX states that if bs is set and no other conversions
+ * than noerror, notrunc or sync are specified, the block
+ * is output without buffering as it is read.
+ */
+ if (ddflags & C_BS) {
+ out.dbcnt = in.dbcnt;
+ dd_out(1);
+ in.dbcnt = 0;
+ continue;
+ }
+
+ if (ddflags & C_SWAB) {
+ if ((n = in.dbrcnt) & 1) {
+ ++st.swab;
+ --n;
+ }
+ swab(in.dbp, in.dbp, n);
+ }
+
+ in.dbp += in.dbrcnt;
+ (*cfunc)();
+ }
+}
+
+/*
+ * Cleanup any remaining I/O and flush output. If necessary, output file
+ * is truncated.
+ */
+static void
+dd_close(void)
+{
+
+ if (cfunc == def)
+ def_close();
+ else if (cfunc == block)
+ block_close();
+ else if (cfunc == unblock)
+ unblock_close();
+ if (ddflags & C_OSYNC && out.dbcnt < out.dbsz) {
+ (void)memset(out.dbp, 0, out.dbsz - out.dbcnt);
+ out.dbcnt = out.dbsz;
+ }
+ /* If there are pending sparse blocks, make sure
+ * to write out the final block un-sparse
+ */
+ if ((out.dbcnt == 0) && pending) {
+ memset(out.db, 0, out.dbsz);
+ out.dbcnt = out.dbsz;
+ out.dbp = out.db + out.dbcnt;
+ pending -= out.dbsz;
+ }
+ if (out.dbcnt)
+ dd_out(1);
+
+ /*
+ * Reporting nfs write error may be deferred until next
+ * write(2) or close(2) system call. So, we need to do an
+ * extra check. If an output is stdout, the file structure
+ * may be shared with other processes and close(2) just
+ * decreases the reference count.
+ */
+ if (out.fd == STDOUT_FILENO && ddop_fsync(out, out.fd) == -1
+ && errno != EINVAL) {
+ err(EXIT_FAILURE, "fsync stdout");
+ /* NOTREACHED */
+ }
+ if (ddop_close(out, out.fd) == -1) {
+ err(EXIT_FAILURE, "close");
+ /* NOTREACHED */
+ }
+}
+
+void
+dd_out(int force)
+{
+ static int warned;
+ int64_t cnt, n, nw;
+ u_char *outp;
+
+ /*
+ * Write one or more blocks out. The common case is writing a full
+ * output block in a single write; increment the full block stats.
+ * Otherwise, we're into partial block writes. If a partial write,
+ * and it's a character device, just warn. If a tape device, quit.
+ *
+ * The partial writes represent two cases. 1: Where the input block
+ * was less than expected so the output block was less than expected.
+ * 2: Where the input block was the right size but we were forced to
+ * write the block in multiple chunks. The original versions of dd(1)
+ * never wrote a block in more than a single write, so the latter case
+ * never happened.
+ *
+ * One special case is if we're forced to do the write -- in that case
+ * we play games with the buffer size, and it's usually a partial write.
+ */
+ outp = out.db;
+ for (n = force ? out.dbcnt : out.dbsz;; n = out.dbsz) {
+ for (cnt = n;; cnt -= nw) {
+
+ if (!force && ddflags & C_SPARSE) {
+ int sparse, i;
+ sparse = 1; /* Is buffer sparse? */
+ for (i = 0; i < cnt; i++)
+ if (outp[i] != 0) {
+ sparse = 0;
+ break;
+ }
+ if (sparse) {
+ pending += cnt;
+ outp += cnt;
+ nw = 0;
+ break;
+ }
+ }
+ if (pending != 0) {
+ if (ddop_lseek(out,
+ out.fd, pending, SEEK_CUR) == -1)
+ err(EXIT_FAILURE, "%s: seek error creating sparse file",
+ out.name);
+ }
+ nw = bwrite(&out, outp, cnt);
+ if (nw <= 0) {
+ if (nw == 0)
+ errx(EXIT_FAILURE,
+ "%s: end of device", out.name);
+ /* NOTREACHED */
+ if (errno != EINTR)
+ err(EXIT_FAILURE, "%s", out.name);
+ /* NOTREACHED */
+ nw = 0;
+ }
+ if (pending) {
+ st.bytes += pending;
+ st.sparse += pending/out.dbsz;
+ st.out_full += pending/out.dbsz;
+ pending = 0;
+ }
+ outp += nw;
+ st.bytes += nw;
+ if (nw == n) {
+ if ((uint64_t)n != out.dbsz)
+ ++st.out_part;
+ else
+ ++st.out_full;
+ break;
+ }
+ ++st.out_part;
+ if (nw == cnt)
+ break;
+ if (out.flags & ISCHR && !warned) {
+ warned = 1;
+ warnx("%s: short write on character device", out.name);
+ }
+ if (out.flags & ISTAPE)
+ errx(EXIT_FAILURE,
+ "%s: short write on tape device", out.name);
+ /* NOTREACHED */
+
+ }
+ if ((out.dbcnt -= n) < out.dbsz)
+ break;
+ }
+
+ /* Reassemble the output block. */
+ if (out.dbcnt)
+ (void)memmove(out.db, out.dbp - out.dbcnt, out.dbcnt);
+ out.dbp = out.db + out.dbcnt;
+
+ if (progress && (st.out_full + st.out_part) % progress == 0)
+ (void)write(STDERR_FILENO, ".", 1);
+}
+
+/*
+ * A protected against SIGINFO write
+ */
+ssize_t
+bwrite(IO *io, const void *buf, size_t len)
+{
+ sigset_t oset;
+ ssize_t rv;
+ int oerrno;
+
+ (void)sigprocmask(SIG_BLOCK, &infoset, &oset);
+ rv = io->ops->op_write(io->fd, buf, len);
+ oerrno = errno;
+ (void)sigprocmask(SIG_SETMASK, &oset, NULL);
+ errno = oerrno;
+ return (rv);
+}
diff --git a/toolbox/upstream-netbsd/bin/dd/dd.h b/toolbox/upstream-netbsd/bin/dd/dd.h
new file mode 100644
index 0000000..b01c7b3
--- /dev/null
+++ b/toolbox/upstream-netbsd/bin/dd/dd.h
@@ -0,0 +1,127 @@
+/* $NetBSD: dd.h,v 1.15 2011/02/04 19:42:12 pooka Exp $ */
+
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego and Lance
+ * Visser of Convex Computer Corporation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)dd.h 8.3 (Berkeley) 4/2/94
+ */
+
+#include <sys/stat.h>
+
+struct ddfops {
+ int (*op_init)(void);
+
+ int (*op_open)(const char *, int, ...);
+ int (*op_close)(int);
+
+ int (*op_fcntl)(int, int, ...);
+#ifdef __ANDROID__
+ int (*op_ioctl)(int, int, ...);
+#else
+ int (*op_ioctl)(int, unsigned long, ...);
+#endif
+
+ int (*op_fstat)(int, struct stat *);
+ int (*op_fsync)(int);
+ int (*op_ftruncate)(int, off_t);
+
+ off_t (*op_lseek)(int, off_t, int);
+
+ ssize_t (*op_read)(int, void *, size_t);
+ ssize_t (*op_write)(int, const void *, size_t);
+};
+
+#define ddop_open(dir, a1, a2, ...) dir.ops->op_open(a1, a2, __VA_ARGS__)
+#define ddop_close(dir, a1) dir.ops->op_close(a1)
+#define ddop_fcntl(dir, a1, a2, ...) dir.ops->op_fcntl(a1, a2, __VA_ARGS__)
+#define ddop_ioctl(dir, a1, a2, ...) dir.ops->op_ioctl(a1, a2, __VA_ARGS__)
+#define ddop_fsync(dir, a1) dir.ops->op_fsync(a1)
+#define ddop_ftruncate(dir, a1, a2) dir.ops->op_ftruncate(a1, a2)
+#define ddop_lseek(dir, a1, a2, a3) dir.ops->op_lseek(a1, a2, a3)
+#define ddop_read(dir, a1, a2, a3) dir.ops->op_read(a1, a2, a3)
+#define ddop_write(dir, a1, a2, a3) dir.ops->op_write(a1, a2, a3)
+
+/* Input/output stream state. */
+typedef struct {
+ u_char *db; /* buffer address */
+ u_char *dbp; /* current buffer I/O address */
+ uint64_t dbcnt; /* current buffer byte count */
+ int64_t dbrcnt; /* last read byte count */
+ uint64_t dbsz; /* buffer size */
+
+#define ISCHR 0x01 /* character device (warn on short) */
+#define ISPIPE 0x02 /* pipe (not truncatable) */
+#define ISTAPE 0x04 /* tape (not seekable) */
+#define NOREAD 0x08 /* not readable */
+ u_int flags;
+
+ const char *name; /* name */
+ int fd; /* file descriptor */
+ uint64_t offset; /* # of blocks to skip */
+ struct ddfops const *ops; /* ops to use with fd */
+} IO;
+
+typedef struct {
+ uint64_t in_full; /* # of full input blocks */
+ uint64_t in_part; /* # of partial input blocks */
+ uint64_t out_full; /* # of full output blocks */
+ uint64_t out_part; /* # of partial output blocks */
+ uint64_t trunc; /* # of truncated records */
+ uint64_t swab; /* # of odd-length swab blocks */
+ uint64_t sparse; /* # of sparse output blocks */
+ uint64_t bytes; /* # of bytes written */
+ struct timeval start; /* start time of dd */
+} STAT;
+
+/* Flags (in ddflags). */
+#define C_ASCII 0x00001
+#define C_BLOCK 0x00002
+#define C_BS 0x00004
+#define C_CBS 0x00008
+#define C_COUNT 0x00010
+#define C_EBCDIC 0x00020
+#define C_FILES 0x00040
+#define C_IBS 0x00080
+#define C_IF 0x00100
+#define C_LCASE 0x00200
+#define C_NOERROR 0x00400
+#define C_NOTRUNC 0x00800
+#define C_OBS 0x01000
+#define C_OF 0x02000
+#define C_SEEK 0x04000
+#define C_SKIP 0x08000
+#define C_SWAB 0x10000
+#define C_SYNC 0x20000
+#define C_UCASE 0x40000
+#define C_UNBLOCK 0x80000
+#define C_OSYNC 0x100000
+#define C_SPARSE 0x200000
diff --git a/toolbox/upstream-netbsd/bin/dd/dd_hostops.c b/toolbox/upstream-netbsd/bin/dd/dd_hostops.c
new file mode 100644
index 0000000..d6e7a89
--- /dev/null
+++ b/toolbox/upstream-netbsd/bin/dd/dd_hostops.c
@@ -0,0 +1,53 @@
+/* $NetBSD: dd_hostops.c,v 1.1 2011/02/04 19:42:12 pooka Exp $ */
+
+/*-
+ * Copyright (c) 2010 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: dd_hostops.c,v 1.1 2011/02/04 19:42:12 pooka Exp $");
+#endif /* !lint */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "dd.h"
+
+const struct ddfops ddfops_prog = {
+ .op_open = open,
+ .op_close = close,
+ .op_fcntl = fcntl,
+ .op_ioctl = ioctl,
+ .op_fstat = fstat,
+ .op_fsync = fsync,
+ .op_ftruncate = ftruncate,
+ .op_lseek = lseek,
+ .op_read = read,
+ .op_write = write,
+};
diff --git a/toolbox/upstream-netbsd/bin/dd/extern.h b/toolbox/upstream-netbsd/bin/dd/extern.h
new file mode 100644
index 0000000..9c59021
--- /dev/null
+++ b/toolbox/upstream-netbsd/bin/dd/extern.h
@@ -0,0 +1,82 @@
+/* $NetBSD: extern.h,v 1.22 2011/11/07 22:24:23 jym Exp $ */
+
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego and Lance
+ * Visser of Convex Computer Corporation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.3 (Berkeley) 4/2/94
+ */
+
+#include <sys/cdefs.h>
+
+#ifdef NO_CONV
+__dead void block(void);
+__dead void block_close(void);
+__dead void unblock(void);
+__dead void unblock_close(void);
+#else
+void block(void);
+void block_close(void);
+void unblock(void);
+void unblock_close(void);
+#endif
+
+#ifndef NO_MSGFMT
+int dd_write_msg(const char *, int);
+#endif
+
+void dd_out(int);
+void def(void);
+void def_close(void);
+void jcl(char **);
+void pos_in(void);
+void pos_out(void);
+void summary(void);
+void summaryx(int);
+__dead void terminate(int);
+void unblock(void);
+void unblock_close(void);
+ssize_t bwrite(IO *, const void *, size_t);
+
+extern IO in, out;
+extern STAT st;
+extern void (*cfunc)(void);
+extern uint64_t cpy_cnt;
+extern uint64_t cbsz;
+extern u_int ddflags;
+extern u_int files_cnt;
+extern uint64_t progress;
+extern const u_char *ctab;
+extern const u_char a2e_32V[], a2e_POSIX[];
+extern const u_char e2a_32V[], e2a_POSIX[];
+extern const u_char a2ibm_32V[], a2ibm_POSIX[];
+extern u_char casetab[];
+extern const char *msgfmt;
diff --git a/toolbox/upstream-netbsd/bin/dd/misc.c b/toolbox/upstream-netbsd/bin/dd/misc.c
new file mode 100644
index 0000000..0fac98b
--- /dev/null
+++ b/toolbox/upstream-netbsd/bin/dd/misc.c
@@ -0,0 +1,342 @@
+/* $NetBSD: misc.c,v 1.23 2011/11/07 22:24:23 jym Exp $ */
+
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego and Lance
+ * Visser of Convex Computer Corporation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)misc.c 8.3 (Berkeley) 4/2/94";
+#else
+__RCSID("$NetBSD: misc.c,v 1.23 2011/11/07 22:24:23 jym Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <util.h>
+#include <inttypes.h>
+
+#include "dd.h"
+#include "extern.h"
+
+#define tv2mS(tv) ((tv).tv_sec * 1000LL + ((tv).tv_usec + 500) / 1000)
+
+static void posix_summary(void);
+#ifndef NO_MSGFMT
+static void custom_summary(void);
+static void human_summary(void);
+static void quiet_summary(void);
+
+static void buffer_write(const char *, size_t, int);
+#endif /* NO_MSGFMT */
+
+void
+summary(void)
+{
+
+ if (progress)
+ (void)write(STDERR_FILENO, "\n", 1);
+
+#ifdef NO_MSGFMT
+ return posix_summary();
+#else /* NO_MSGFMT */
+ if (strncmp(msgfmt, "human", sizeof("human")) == 0)
+ return human_summary();
+
+ if (strncmp(msgfmt, "posix", sizeof("posix")) == 0)
+ return posix_summary();
+
+ if (strncmp(msgfmt, "quiet", sizeof("quiet")) == 0)
+ return quiet_summary();
+
+ return custom_summary();
+#endif /* NO_MSGFMT */
+}
+
+static void
+posix_summary(void)
+{
+ char buf[100];
+ int64_t mS;
+ struct timeval tv;
+
+ if (progress)
+ (void)write(STDERR_FILENO, "\n", 1);
+
+ (void)gettimeofday(&tv, NULL);
+ mS = tv2mS(tv) - tv2mS(st.start);
+ if (mS == 0)
+ mS = 1;
+
+ /* Use snprintf(3) so that we don't reenter stdio(3). */
+ (void)snprintf(buf, sizeof(buf),
+ "%llu+%llu records in\n%llu+%llu records out\n",
+ (unsigned long long)st.in_full, (unsigned long long)st.in_part,
+ (unsigned long long)st.out_full, (unsigned long long)st.out_part);
+ (void)write(STDERR_FILENO, buf, strlen(buf));
+ if (st.swab) {
+ (void)snprintf(buf, sizeof(buf), "%llu odd length swab %s\n",
+ (unsigned long long)st.swab,
+ (st.swab == 1) ? "block" : "blocks");
+ (void)write(STDERR_FILENO, buf, strlen(buf));
+ }
+ if (st.trunc) {
+ (void)snprintf(buf, sizeof(buf), "%llu truncated %s\n",
+ (unsigned long long)st.trunc,
+ (st.trunc == 1) ? "block" : "blocks");
+ (void)write(STDERR_FILENO, buf, strlen(buf));
+ }
+ if (st.sparse) {
+ (void)snprintf(buf, sizeof(buf), "%llu sparse output %s\n",
+ (unsigned long long)st.sparse,
+ (st.sparse == 1) ? "block" : "blocks");
+ (void)write(STDERR_FILENO, buf, strlen(buf));
+ }
+ (void)snprintf(buf, sizeof(buf),
+ "%llu bytes transferred in %lu.%03d secs (%llu bytes/sec)\n",
+ (unsigned long long) st.bytes,
+ (long) (mS / 1000),
+ (int) (mS % 1000),
+ (unsigned long long) (st.bytes * 1000LL / mS));
+ (void)write(STDERR_FILENO, buf, strlen(buf));
+}
+
+/* ARGSUSED */
+void
+summaryx(int notused)
+{
+
+ summary();
+}
+
+/* ARGSUSED */
+void
+terminate(int signo)
+{
+
+ summary();
+ (void)raise_default_signal(signo);
+ _exit(127);
+}
+
+#ifndef NO_MSGFMT
+/*
+ * Buffer write(2) calls
+ */
+static void
+buffer_write(const char *str, size_t size, int flush)
+{
+ static char wbuf[128];
+ static size_t cnt = 0; /* Internal counter to allow wbuf to wrap */
+
+ unsigned int i;
+
+ for (i = 0; i < size; i++) {
+ if (str != NULL) {
+ wbuf[cnt++] = str[i];
+ }
+ if (cnt >= sizeof(wbuf)) {
+ (void)write(STDERR_FILENO, wbuf, cnt);
+ cnt = 0;
+ }
+ }
+
+ if (flush != 0) {
+ (void)write(STDERR_FILENO, wbuf, cnt);
+ cnt = 0;
+ }
+}
+
+/*
+ * Write summary to stderr according to format 'fmt'. If 'enable' is 0, it
+ * will not attempt to write anything. Can be used to validate the
+ * correctness of the 'fmt' string.
+ */
+int
+dd_write_msg(const char *fmt, int enable)
+{
+ char hbuf[7], nbuf[32];
+ const char *ptr;
+ int64_t mS;
+ struct timeval tv;
+
+ (void)gettimeofday(&tv, NULL);
+ mS = tv2mS(tv) - tv2mS(st.start);
+ if (mS == 0)
+ mS = 1;
+
+#define ADDC(c) do { if (enable != 0) buffer_write(&c, 1, 0); } \
+ while (/*CONSTCOND*/0)
+#define ADDS(p) do { if (enable != 0) buffer_write(p, strlen(p), 0); } \
+ while (/*CONSTCOND*/0)
+
+ for (ptr = fmt; *ptr; ptr++) {
+ if (*ptr != '%') {
+ ADDC(*ptr);
+ continue;
+ }
+
+ switch (*++ptr) {
+ case 'b':
+ (void)snprintf(nbuf, sizeof(nbuf), "%llu",
+ (unsigned long long)st.bytes);
+ ADDS(nbuf);
+ break;
+ case 'B':
+ if (humanize_number(hbuf, sizeof(hbuf),
+ st.bytes, "B",
+ HN_AUTOSCALE, HN_DECIMAL) == -1)
+ warnx("humanize_number (bytes transferred)");
+ ADDS(hbuf);
+ break;
+ case 'e':
+ (void)snprintf(nbuf, sizeof(nbuf), "%llu",
+ (unsigned long long) (st.bytes * 1000LL / mS));
+ ADDS(nbuf);
+ break;
+ case 'E':
+ if (humanize_number(hbuf, sizeof(hbuf),
+ st.bytes * 1000LL / mS, "B",
+ HN_AUTOSCALE, HN_DECIMAL) == -1)
+ warnx("humanize_number (bytes per second)");
+ ADDS(hbuf); ADDS("/sec");
+ break;
+ case 'i':
+ (void)snprintf(nbuf, sizeof(nbuf), "%llu",
+ (unsigned long long)st.in_part);
+ ADDS(nbuf);
+ break;
+ case 'I':
+ (void)snprintf(nbuf, sizeof(nbuf), "%llu",
+ (unsigned long long)st.in_full);
+ ADDS(nbuf);
+ break;
+ case 'o':
+ (void)snprintf(nbuf, sizeof(nbuf), "%llu",
+ (unsigned long long)st.out_part);
+ ADDS(nbuf);
+ break;
+ case 'O':
+ (void)snprintf(nbuf, sizeof(nbuf), "%llu",
+ (unsigned long long)st.out_full);
+ ADDS(nbuf);
+ break;
+ case 's':
+ (void)snprintf(nbuf, sizeof(nbuf), "%li.%03d",
+ (long) (mS / 1000), (int) (mS % 1000));
+ ADDS(nbuf);
+ break;
+ case 'p':
+ (void)snprintf(nbuf, sizeof(nbuf), "%llu",
+ (unsigned long long)st.sparse);
+ ADDS(nbuf);
+ break;
+ case 't':
+ (void)snprintf(nbuf, sizeof(nbuf), "%llu",
+ (unsigned long long)st.trunc);
+ ADDS(nbuf);
+ break;
+ case 'w':
+ (void)snprintf(nbuf, sizeof(nbuf), "%llu",
+ (unsigned long long)st.swab);
+ ADDS(nbuf);
+ break;
+ case 'P':
+ ADDS("block");
+ if (st.sparse != 1) ADDS("s");
+ break;
+ case 'T':
+ ADDS("block");
+ if (st.trunc != 1) ADDS("s");
+ break;
+ case 'W':
+ ADDS("block");
+ if (st.swab != 1) ADDS("s");
+ break;
+ case '%':
+ ADDC(*ptr);
+ break;
+ default:
+ if (*ptr == '\0')
+ goto done;
+ errx(EXIT_FAILURE, "unknown specifier '%c' in "
+ "msgfmt string", *ptr);
+ /* NOTREACHED */
+ }
+ }
+
+done:
+ /* flush buffer */
+ buffer_write(NULL, 0, 1);
+ return 0;
+}
+
+static void
+custom_summary(void)
+{
+
+ dd_write_msg(msgfmt, 1);
+}
+
+static void
+human_summary(void)
+{
+ (void)dd_write_msg("%I+%i records in\n%O+%o records out\n", 1);
+ if (st.swab) {
+ (void)dd_write_msg("%w odd length swab %W\n", 1);
+ }
+ if (st.trunc) {
+ (void)dd_write_msg("%t truncated %T\n", 1);
+ }
+ if (st.sparse) {
+ (void)dd_write_msg("%p sparse output %P\n", 1);
+ }
+ (void)dd_write_msg("%b bytes (%B) transferred in %s secs "
+ "(%e bytes/sec - %E)\n", 1);
+}
+
+static void
+quiet_summary(void)
+{
+
+ /* stay quiet */
+}
+#endif /* NO_MSGFMT */
diff --git a/toolbox/upstream-netbsd/bin/dd/position.c b/toolbox/upstream-netbsd/bin/dd/position.c
new file mode 100644
index 0000000..36dd580
--- /dev/null
+++ b/toolbox/upstream-netbsd/bin/dd/position.c
@@ -0,0 +1,185 @@
+/* $NetBSD: position.c,v 1.18 2010/11/22 21:04:28 pooka Exp $ */
+
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego and Lance
+ * Visser of Convex Computer Corporation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)position.c 8.3 (Berkeley) 4/2/94";
+#else
+__RCSID("$NetBSD: position.c,v 1.18 2010/11/22 21:04:28 pooka Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/mtio.h>
+#include <sys/time.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "dd.h"
+#include "extern.h"
+
+/*
+ * Position input/output data streams before starting the copy. Device type
+ * dependent. Seekable devices use lseek, and the rest position by reading.
+ * Seeking past the end of file can cause null blocks to be written to the
+ * output.
+ */
+void
+pos_in(void)
+{
+ int bcnt, cnt, nr, warned;
+
+ /* If not a pipe or tape device, try to seek on it. */
+ if (!(in.flags & (ISPIPE|ISTAPE))) {
+ if (ddop_lseek(in, in.fd,
+ (off_t)in.offset * (off_t)in.dbsz, SEEK_CUR) == -1) {
+ err(EXIT_FAILURE, "%s", in.name);
+ /* NOTREACHED */
+ }
+ return;
+ /* NOTREACHED */
+ }
+
+ /*
+ * Read the data. If a pipe, read until satisfy the number of bytes
+ * being skipped. No differentiation for reading complete and partial
+ * blocks for other devices.
+ */
+ for (bcnt = in.dbsz, cnt = in.offset, warned = 0; cnt;) {
+ if ((nr = ddop_read(in, in.fd, in.db, bcnt)) > 0) {
+ if (in.flags & ISPIPE) {
+ if (!(bcnt -= nr)) {
+ bcnt = in.dbsz;
+ --cnt;
+ }
+ } else
+ --cnt;
+ continue;
+ }
+
+ if (nr == 0) {
+ if (files_cnt > 1) {
+ --files_cnt;
+ continue;
+ }
+ errx(EXIT_FAILURE, "skip reached end of input");
+ /* NOTREACHED */
+ }
+
+ /*
+ * Input error -- either EOF with no more files, or I/O error.
+ * If noerror not set die. POSIX requires that the warning
+ * message be followed by an I/O display.
+ */
+ if (ddflags & C_NOERROR) {
+ if (!warned) {
+
+ warn("%s", in.name);
+ warned = 1;
+ summary();
+ }
+ continue;
+ }
+ err(EXIT_FAILURE, "%s", in.name);
+ /* NOTREACHED */
+ }
+}
+
+void
+pos_out(void)
+{
+ struct mtop t_op;
+ int n;
+ uint64_t cnt;
+
+ /*
+ * If not a tape, try seeking on the file. Seeking on a pipe is
+ * going to fail, but don't protect the user -- they shouldn't
+ * have specified the seek operand.
+ */
+ if (!(out.flags & ISTAPE)) {
+ if (ddop_lseek(out, out.fd,
+ (off_t)out.offset * (off_t)out.dbsz, SEEK_SET) == -1)
+ err(EXIT_FAILURE, "%s", out.name);
+ /* NOTREACHED */
+ return;
+ }
+
+ /* If no read access, try using mtio. */
+ if (out.flags & NOREAD) {
+ t_op.mt_op = MTFSR;
+ t_op.mt_count = out.offset;
+
+ if (ddop_ioctl(out, out.fd, MTIOCTOP, &t_op) < 0)
+ err(EXIT_FAILURE, "%s", out.name);
+ /* NOTREACHED */
+ return;
+ }
+
+ /* Read it. */
+ for (cnt = 0; cnt < out.offset; ++cnt) {
+ if ((n = ddop_read(out, out.fd, out.db, out.dbsz)) > 0)
+ continue;
+
+ if (n < 0)
+ err(EXIT_FAILURE, "%s", out.name);
+ /* NOTREACHED */
+
+ /*
+ * If reach EOF, fill with NUL characters; first, back up over
+ * the EOF mark. Note, cnt has not yet been incremented, so
+ * the EOF read does not count as a seek'd block.
+ */
+ t_op.mt_op = MTBSR;
+ t_op.mt_count = 1;
+ if (ddop_ioctl(out, out.fd, MTIOCTOP, &t_op) == -1)
+ err(EXIT_FAILURE, "%s", out.name);
+ /* NOTREACHED */
+
+ while (cnt++ < out.offset)
+ if ((uint64_t)(n = bwrite(&out,
+ out.db, out.dbsz)) != out.dbsz)
+ err(EXIT_FAILURE, "%s", out.name);
+ /* NOTREACHED */
+ break;
+ }
+}
diff --git a/toolbox/upstream-netbsd/include/namespace.h b/toolbox/upstream-netbsd/include/namespace.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/toolbox/upstream-netbsd/include/namespace.h
diff --git a/toolbox/upstream-netbsd/include/sys/extattr.h b/toolbox/upstream-netbsd/include/sys/extattr.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/toolbox/upstream-netbsd/include/sys/extattr.h
diff --git a/toolbox/upstream-netbsd/include/sys/mtio.h b/toolbox/upstream-netbsd/include/sys/mtio.h
new file mode 100644
index 0000000..8fb5655
--- /dev/null
+++ b/toolbox/upstream-netbsd/include/sys/mtio.h
@@ -0,0 +1 @@
+#include <linux/mtio.h>
diff --git a/toolbox/upstream-netbsd/lib/libc/gen/getbsize.c b/toolbox/upstream-netbsd/lib/libc/gen/getbsize.c
new file mode 100644
index 0000000..a9ce2c1
--- /dev/null
+++ b/toolbox/upstream-netbsd/lib/libc/gen/getbsize.c
@@ -0,0 +1,118 @@
+/* $NetBSD: getbsize.c,v 1.17 2012/06/25 22:32:43 abs Exp $ */
+
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#if defined(LIBC_SCCS) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)getbsize.c 8.1 (Berkeley) 6/4/93";
+#else
+__RCSID("$NetBSD: getbsize.c,v 1.17 2012/06/25 22:32:43 abs Exp $");
+#endif
+#endif /* not lint */
+
+#include "namespace.h"
+
+#include <assert.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef __weak_alias
+__weak_alias(getbsize,_getbsize)
+#endif
+
+char *
+getbsize(int *headerlenp, long *blocksizep)
+{
+ static char header[20];
+ long n, max, mul, blocksize;
+ char *ep, *p;
+ const char *form;
+
+#define KB (1024L)
+#define MB (1024L * 1024L)
+#define GB (1024L * 1024L * 1024L)
+#define MAXB GB /* No tera, peta, nor exa. */
+ form = "";
+ if ((p = getenv("BLOCKSIZE")) != NULL && *p != '\0') {
+ if ((n = strtol(p, &ep, 10)) < 0)
+ goto underflow;
+ if (n == 0)
+ n = 1;
+ if (*ep && ep[1])
+ goto fmterr;
+ switch (*ep) {
+ case 'G': case 'g':
+ form = "G";
+ max = MAXB / GB;
+ mul = GB;
+ break;
+ case 'K': case 'k':
+ form = "K";
+ max = MAXB / KB;
+ mul = KB;
+ break;
+ case 'M': case 'm':
+ form = "M";
+ max = MAXB / MB;
+ mul = MB;
+ break;
+ case '\0':
+ max = MAXB;
+ mul = 1;
+ break;
+ default:
+fmterr: warnx("%s: unknown blocksize", p);
+ n = 512;
+ mul = 1;
+ max = 0;
+ break;
+ }
+ if (n > max) {
+ warnx("maximum blocksize is %ldG", MAXB / GB);
+ n = max;
+ }
+ if ((blocksize = n * mul) < 512) {
+underflow: warnx("%s: minimum blocksize is 512", p);
+ form = "";
+ blocksize = n = 512;
+ }
+ } else
+ blocksize = n = 512;
+
+ if (headerlenp)
+ *headerlenp =
+ snprintf(header, sizeof(header), "%ld%s-blocks", n, form);
+ if (blocksizep)
+ *blocksizep = blocksize;
+ return (header);
+}
diff --git a/toolbox/upstream-netbsd/lib/libc/gen/humanize_number.c b/toolbox/upstream-netbsd/lib/libc/gen/humanize_number.c
new file mode 100644
index 0000000..533560f
--- /dev/null
+++ b/toolbox/upstream-netbsd/lib/libc/gen/humanize_number.c
@@ -0,0 +1,161 @@
+/* $NetBSD: humanize_number.c,v 1.16 2012/03/17 20:01:14 christos Exp $ */
+
+/*
+ * Copyright (c) 1997, 1998, 1999, 2002 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
+ * NASA Ames Research Center, by Luke Mewburn and by Tomas Svensson.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#if defined(LIBC_SCCS) && !defined(lint)
+__RCSID("$NetBSD: humanize_number.c,v 1.16 2012/03/17 20:01:14 christos Exp $");
+#endif /* LIBC_SCCS and not lint */
+
+#include "namespace.h"
+#include <assert.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <locale.h>
+
+int
+humanize_number(char *buf, size_t len, int64_t bytes,
+ const char *suffix, int scale, int flags)
+{
+ const char *prefixes, *sep;
+ int b, r, s1, s2, sign;
+ int64_t divisor, max, post = 1;
+ size_t i, baselen, maxscale;
+
+ _DIAGASSERT(buf != NULL);
+ _DIAGASSERT(suffix != NULL);
+ _DIAGASSERT(scale >= 0);
+
+ if (flags & HN_DIVISOR_1000) {
+ /* SI for decimal multiplies */
+ divisor = 1000;
+ if (flags & HN_B)
+ prefixes = "B\0k\0M\0G\0T\0P\0E";
+ else
+ prefixes = "\0\0k\0M\0G\0T\0P\0E";
+ } else {
+ /*
+ * binary multiplies
+ * XXX IEC 60027-2 recommends Ki, Mi, Gi...
+ */
+ divisor = 1024;
+ if (flags & HN_B)
+ prefixes = "B\0K\0M\0G\0T\0P\0E";
+ else
+ prefixes = "\0\0K\0M\0G\0T\0P\0E";
+ }
+
+#define SCALE2PREFIX(scale) (&prefixes[(scale) << 1])
+ maxscale = 7;
+
+ if ((size_t)scale >= maxscale &&
+ (scale & (HN_AUTOSCALE | HN_GETSCALE)) == 0)
+ return (-1);
+
+ if (buf == NULL || suffix == NULL)
+ return (-1);
+
+ if (len > 0)
+ buf[0] = '\0';
+ if (bytes < 0) {
+ sign = -1;
+ baselen = 3; /* sign, digit, prefix */
+ if (-bytes < INT64_MAX / 100)
+ bytes *= -100;
+ else {
+ bytes = -bytes;
+ post = 100;
+ baselen += 2;
+ }
+ } else {
+ sign = 1;
+ baselen = 2; /* digit, prefix */
+ if (bytes < INT64_MAX / 100)
+ bytes *= 100;
+ else {
+ post = 100;
+ baselen += 2;
+ }
+ }
+ if (flags & HN_NOSPACE)
+ sep = "";
+ else {
+ sep = " ";
+ baselen++;
+ }
+ baselen += strlen(suffix);
+
+ /* Check if enough room for `x y' + suffix + `\0' */
+ if (len < baselen + 1)
+ return (-1);
+
+ if (scale & (HN_AUTOSCALE | HN_GETSCALE)) {
+ /* See if there is additional columns can be used. */
+ for (max = 100, i = len - baselen; i-- > 0;)
+ max *= 10;
+
+ /*
+ * Divide the number until it fits the given column.
+ * If there will be an overflow by the rounding below,
+ * divide once more.
+ */
+ for (i = 0; bytes >= max - 50 && i < maxscale; i++)
+ bytes /= divisor;
+
+ if (scale & HN_GETSCALE) {
+ _DIAGASSERT(__type_fit(int, i));
+ return (int)i;
+ }
+ } else
+ for (i = 0; i < (size_t)scale && i < maxscale; i++)
+ bytes /= divisor;
+ bytes *= post;
+
+ /* If a value <= 9.9 after rounding and ... */
+ if (bytes < 995 && i > 0 && flags & HN_DECIMAL) {
+ /* baselen + \0 + .N */
+ if (len < baselen + 1 + 2)
+ return (-1);
+ b = ((int)bytes + 5) / 10;
+ s1 = b / 10;
+ s2 = b % 10;
+ r = snprintf(buf, len, "%d%s%d%s%s%s",
+ sign * s1, localeconv()->decimal_point, s2,
+ sep, SCALE2PREFIX(i), suffix);
+ } else
+ r = snprintf(buf, len, "%" PRId64 "%s%s%s",
+ sign * ((bytes + 50) / 100),
+ sep, SCALE2PREFIX(i), suffix);
+
+ return (r);
+}
diff --git a/toolbox/upstream-netbsd/lib/libc/stdlib/strsuftoll.c b/toolbox/upstream-netbsd/lib/libc/stdlib/strsuftoll.c
new file mode 100644
index 0000000..80fc52f
--- /dev/null
+++ b/toolbox/upstream-netbsd/lib/libc/stdlib/strsuftoll.c
@@ -0,0 +1,249 @@
+/* $NetBSD: strsuftoll.c,v 1.9 2011/10/22 22:08:47 christos Exp $ */
+/*-
+ * Copyright (c) 2001-2002,2004 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Luke Mewburn.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego and Lance
+ * Visser of Convex Computer Corporation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+
+#if defined(LIBC_SCCS) && !defined(lint)
+__RCSID("$NetBSD: strsuftoll.c,v 1.9 2011/10/22 22:08:47 christos Exp $");
+#endif /* LIBC_SCCS and not lint */
+
+#ifdef _LIBC
+#include "namespace.h"
+#endif
+
+#if !HAVE_STRSUFTOLL
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef _LIBC
+# ifdef __weak_alias
+__weak_alias(strsuftoll, _strsuftoll)
+__weak_alias(strsuftollx, _strsuftollx)
+# endif
+#endif /* LIBC */
+
+/*
+ * Convert an expression of the following forms to a (u)int64_t.
+ * 1) A positive decimal number.
+ * 2) A positive decimal number followed by a b (mult by 512).
+ * 3) A positive decimal number followed by a k (mult by 1024).
+ * 4) A positive decimal number followed by a m (mult by 1048576).
+ * 5) A positive decimal number followed by a g (mult by 1073741824).
+ * 6) A positive decimal number followed by a t (mult by 1099511627776).
+ * 7) A positive decimal number followed by a w (mult by sizeof int)
+ * 8) Two or more positive decimal numbers (with/without k,b or w).
+ * separated by x (also * for backwards compatibility), specifying
+ * the product of the indicated values.
+ * Returns the result upon successful conversion, or exits with an
+ * appropriate error.
+ *
+ */
+/* LONGLONG */
+long long
+strsuftoll(const char *desc, const char *val,
+ long long min, long long max)
+{
+ long long result;
+ char errbuf[100];
+
+ result = strsuftollx(desc, val, min, max, errbuf, sizeof(errbuf));
+ if (*errbuf != '\0')
+ errx(EXIT_FAILURE, "%s", errbuf);
+ return result;
+}
+
+/*
+ * As strsuftoll(), but returns the error message into the provided buffer
+ * rather than exiting with it.
+ */
+/* LONGLONG */
+static long long
+__strsuftollx(const char *desc, const char *val,
+ long long min, long long max, char *ebuf, size_t ebuflen, size_t depth)
+{
+ long long num, t;
+ char *expr;
+
+ _DIAGASSERT(desc != NULL);
+ _DIAGASSERT(val != NULL);
+ _DIAGASSERT(ebuf != NULL);
+
+ if (depth > 16) {
+ snprintf(ebuf, ebuflen, "%s: Recursion limit exceeded", desc);
+ return 0;
+ }
+
+ while (isspace((unsigned char)*val)) /* Skip leading space */
+ val++;
+
+ errno = 0;
+ num = strtoll(val, &expr, 10);
+ if (errno == ERANGE)
+ goto erange; /* Overflow */
+
+ if (expr == val) /* No digits */
+ goto badnum;
+
+ switch (*expr) {
+ case 'b':
+ t = num;
+ num *= 512; /* 1 block */
+ if (t > num)
+ goto erange;
+ ++expr;
+ break;
+ case 'k':
+ t = num;
+ num *= 1024; /* 1 kibibyte */
+ if (t > num)
+ goto erange;
+ ++expr;
+ break;
+ case 'm':
+ t = num;
+ num *= 1048576; /* 1 mebibyte */
+ if (t > num)
+ goto erange;
+ ++expr;
+ break;
+ case 'g':
+ t = num;
+ num *= 1073741824; /* 1 gibibyte */
+ if (t > num)
+ goto erange;
+ ++expr;
+ break;
+ case 't':
+ t = num;
+ num *= 1099511627776LL; /* 1 tebibyte */
+ if (t > num)
+ goto erange;
+ ++expr;
+ break;
+ case 'w':
+ t = num;
+ num *= sizeof(int); /* 1 word */
+ if (t > num)
+ goto erange;
+ ++expr;
+ break;
+ }
+
+ switch (*expr) {
+ case '\0':
+ break;
+ case '*': /* Backward compatible */
+ case 'x':
+ t = num;
+ num *= __strsuftollx(desc, expr + 1, min, max, ebuf, ebuflen,
+ depth + 1);
+ if (*ebuf != '\0')
+ return 0;
+ if (t > num) {
+ erange:
+ errno = ERANGE;
+ snprintf(ebuf, ebuflen, "%s: %s", desc, strerror(errno));
+ return 0;
+ }
+ break;
+ default:
+ badnum:
+ snprintf(ebuf, ebuflen, "%s `%s': illegal number", desc, val);
+ return 0;
+ }
+ if (num < min) {
+ /* LONGLONG */
+ snprintf(ebuf, ebuflen, "%s %lld is less than %lld.",
+ desc, (long long)num, (long long)min);
+ return 0;
+ }
+ if (num > max) {
+ /* LONGLONG */
+ snprintf(ebuf, ebuflen, "%s %lld is greater than %lld.",
+ desc, (long long)num, (long long)max);
+ return 0;
+ }
+ *ebuf = '\0';
+ return num;
+}
+
+long long
+strsuftollx(const char *desc, const char *val,
+ long long min, long long max, char *ebuf, size_t ebuflen)
+{
+ return __strsuftollx(desc, val, min, max, ebuf, ebuflen, 0);
+}
+#endif /* !HAVE_STRSUFTOLL */
diff --git a/toolbox/upstream-netbsd/lib/libc/string/swab.c b/toolbox/upstream-netbsd/lib/libc/string/swab.c
new file mode 100644
index 0000000..392b186
--- /dev/null
+++ b/toolbox/upstream-netbsd/lib/libc/string/swab.c
@@ -0,0 +1,80 @@
+/* $NetBSD: swab.c,v 1.18 2011/01/04 17:14:07 martin Exp $ */
+
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jeffrey Mogul.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#if defined(LIBC_SCCS) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)swab.c 8.1 (Berkeley) 6/4/93";
+#else
+__RCSID("$NetBSD: swab.c,v 1.18 2011/01/04 17:14:07 martin Exp $");
+#endif
+#endif /* LIBC_SCCS and not lint */
+
+#include <assert.h>
+#include <unistd.h>
+
+void
+swab(const void * __restrict from, void * __restrict to, ssize_t len)
+{
+ char temp;
+ const char *fp;
+ char *tp;
+
+ if (len <= 1)
+ return;
+
+ _DIAGASSERT(from != NULL);
+ _DIAGASSERT(to != NULL);
+
+ len /= 2;
+ fp = (const char *)from;
+ tp = (char *)to;
+#define STEP temp = *fp++,*tp++ = *fp++,*tp++ = temp
+
+ if (__predict_false(len == 1)) {
+ STEP;
+ return;
+ }
+
+ /* round to multiple of 8 */
+ while ((--len % 8) != 0)
+ STEP;
+ len /= 8;
+ if (len == 0)
+ return;
+ while (len-- != 0) {
+ STEP; STEP; STEP; STEP;
+ STEP; STEP; STEP; STEP;
+ }
+}
diff --git a/toolbox/upstream-netbsd/lib/libutil/raise_default_signal.c b/toolbox/upstream-netbsd/lib/libutil/raise_default_signal.c
new file mode 100644
index 0000000..50cffd4
--- /dev/null
+++ b/toolbox/upstream-netbsd/lib/libutil/raise_default_signal.c
@@ -0,0 +1,117 @@
+/* $NetBSD: raise_default_signal.c,v 1.3 2008/04/28 20:23:03 martin Exp $ */
+
+/*-
+ * Copyright (c) 2007 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Luke Mewburn.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+#if defined(LIBC_SCCS) && !defined(lint)
+__RCSID("$NetBSD: raise_default_signal.c,v 1.3 2008/04/28 20:23:03 martin Exp $");
+#endif
+
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <util.h>
+
+#if ! HAVE_RAISE_DEFAULT_SIGNAL
+/*
+ * raise_default_signal sig
+ * Raise the default signal handler for sig, by
+ * - block all signals
+ * - set the signal handler to SIG_DFL
+ * - raise the signal
+ * - unblock the signal to deliver it
+ *
+ * The original signal mask and signal handler is restored on exit
+ * (whether successful or not).
+ *
+ * Returns 0 on success, or -1 on failure with errno set to
+ * on of the values for sigemptyset(), sigaddset(), sigprocmask(),
+ * sigaction(), or raise().
+ */
+int
+raise_default_signal(int sig)
+{
+ struct sigaction origact, act;
+ sigset_t origmask, fullmask, mask;
+ int retval, oerrno;
+
+ retval = -1;
+
+ /* Setup data structures */
+ /* XXX memset(3) isn't async-safe according to signal(7) */
+ (void)memset(&act, 0, sizeof(act));
+ act.sa_handler = SIG_DFL;
+ act.sa_flags = 0;
+ if ((sigemptyset(&act.sa_mask) == -1) ||
+ (sigfillset(&fullmask) == -1) ||
+ (sigemptyset(&mask) == -1) ||
+ (sigaddset(&mask, sig) == -1))
+ goto restore_none;
+
+ /* Block all signals */
+ if (sigprocmask(SIG_BLOCK, &fullmask, &origmask) == -1)
+ goto restore_none;
+ /* (use 'goto restore_mask' to restore state) */
+
+ /* Enable the SIG_DFL handler */
+ if (sigaction(sig, &act, &origact) == -1)
+ goto restore_mask;
+ /* (use 'goto restore_act' to restore state) */
+
+ /* Raise the signal, and unblock the signal to deliver it */
+ if ((raise(sig) == -1) ||
+ (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1))
+ goto restore_act;
+
+ /* Flag successful raise() */
+ retval = 0;
+
+ /* Restore the original handler */
+ restore_act:
+ oerrno = errno;
+ (void)sigaction(sig, &origact, NULL);
+ errno = oerrno;
+
+ /* Restore the original mask */
+ restore_mask:
+ oerrno = errno;
+ (void)sigprocmask(SIG_SETMASK, &origmask, NULL);
+ errno = oerrno;
+
+ restore_none:
+ return retval;
+}
+
+#endif /* ! HAVE_RAISE_DEFAULT_SIGNAL */
diff --git a/toolbox/upstream-netbsd/usr.bin/du/du.c b/toolbox/upstream-netbsd/usr.bin/du/du.c
new file mode 100644
index 0000000..086ac4a
--- /dev/null
+++ b/toolbox/upstream-netbsd/usr.bin/du/du.c
@@ -0,0 +1,364 @@
+/* $NetBSD: du.c,v 1.36 2012/03/11 11:23:20 shattered Exp $ */
+
+/*
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Newcomb.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__COPYRIGHT("@(#) Copyright (c) 1989, 1993, 1994\
+ The Regents of the University of California. All rights reserved.");
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)du.c 8.5 (Berkeley) 5/4/95";
+#else
+__RCSID("$NetBSD: du.c,v 1.36 2012/03/11 11:23:20 shattered Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <fts.h>
+#include <inttypes.h>
+#include <util.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+
+/* Count inodes or file size */
+#define COUNT (iflag ? 1 : p->fts_statp->st_blocks)
+
+static int linkchk(dev_t, ino_t);
+static void prstat(const char *, int64_t);
+__dead static void usage(void);
+
+static int hflag, iflag;
+static long blocksize;
+
+int
+main(int argc, char *argv[])
+{
+ FTS *fts;
+ FTSENT *p;
+ int64_t totalblocks;
+ int ftsoptions, listfiles;
+ int depth;
+ int Hflag, Lflag, aflag, ch, cflag, dflag, gkmflag, nflag, rval, sflag;
+ const char *noargv[2];
+
+ Hflag = Lflag = aflag = cflag = dflag = gkmflag = nflag = sflag = 0;
+ totalblocks = 0;
+ ftsoptions = FTS_PHYSICAL;
+ depth = INT_MAX;
+ while ((ch = getopt(argc, argv, "HLPacd:ghikmnrsx")) != -1)
+ switch (ch) {
+ case 'H':
+ Hflag = 1;
+ Lflag = 0;
+ break;
+ case 'L':
+ Lflag = 1;
+ Hflag = 0;
+ break;
+ case 'P':
+ Hflag = Lflag = 0;
+ break;
+ case 'a':
+ aflag = 1;
+ break;
+ case 'c':
+ cflag = 1;
+ break;
+ case 'd':
+ dflag = 1;
+ depth = atoi(optarg);
+ if (depth < 0 || depth > SHRT_MAX) {
+ warnx("invalid argument to option d: %s",
+ optarg);
+ usage();
+ }
+ break;
+ case 'g':
+ blocksize = 1024 * 1024 * 1024;
+ gkmflag = 1;
+ break;
+ case 'h':
+ hflag = 1;
+ break;
+ case 'i':
+ iflag = 1;
+ break;
+ case 'k':
+ blocksize = 1024;
+ gkmflag = 1;
+ break;
+ case 'm':
+ blocksize = 1024 * 1024;
+ gkmflag = 1;
+ break;
+ case 'n':
+ nflag = 1;
+ break;
+ case 'r':
+ break;
+ case 's':
+ sflag = 1;
+ break;
+ case 'x':
+ ftsoptions |= FTS_XDEV;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ /*
+ * XXX
+ * Because of the way that fts(3) works, logical walks will not count
+ * the blocks actually used by symbolic links. We rationalize this by
+ * noting that users computing logical sizes are likely to do logical
+ * copies, so not counting the links is correct. The real reason is
+ * that we'd have to re-implement the kernel's symbolic link traversing
+ * algorithm to get this right. If, for example, you have relative
+ * symbolic links referencing other relative symbolic links, it gets
+ * very nasty, very fast. The bottom line is that it's documented in
+ * the man page, so it's a feature.
+ */
+ if (Hflag)
+ ftsoptions |= FTS_COMFOLLOW;
+ if (Lflag) {
+ ftsoptions &= ~FTS_PHYSICAL;
+ ftsoptions |= FTS_LOGICAL;
+ }
+
+ listfiles = 0;
+ if (aflag) {
+ if (sflag || dflag)
+ usage();
+ listfiles = 1;
+ } else if (sflag) {
+ if (dflag)
+ usage();
+ depth = 0;
+ }
+
+ if (!*argv) {
+ noargv[0] = ".";
+ noargv[1] = NULL;
+ argv = __UNCONST(noargv);
+ }
+
+ if (!gkmflag)
+ (void)getbsize(NULL, &blocksize);
+ blocksize /= 512;
+
+ if ((fts = fts_open(argv, ftsoptions, NULL)) == NULL)
+ err(1, "fts_open `%s'", *argv);
+
+ for (rval = 0; (p = fts_read(fts)) != NULL;) {
+#ifndef __ANDROID__
+ if (nflag) {
+ switch (p->fts_info) {
+ case FTS_NS:
+ case FTS_SLNONE:
+ /* nothing */
+ break;
+ default:
+ if (p->fts_statp->st_flags & UF_NODUMP) {
+ fts_set(fts, p, FTS_SKIP);
+ continue;
+ }
+ }
+ }
+#endif
+ switch (p->fts_info) {
+ case FTS_D: /* Ignore. */
+ break;
+ case FTS_DP:
+ p->fts_parent->fts_number +=
+ p->fts_number += COUNT;
+ if (cflag)
+ totalblocks += COUNT;
+ /*
+ * If listing each directory, or not listing files
+ * or directories and this is post-order of the
+ * root of a traversal, display the total.
+ */
+ if (p->fts_level <= depth
+ || (!listfiles && !p->fts_level))
+ prstat(p->fts_path, p->fts_number);
+ break;
+ case FTS_DC: /* Ignore. */
+ break;
+ case FTS_DNR: /* Warn, continue. */
+ case FTS_ERR:
+ case FTS_NS:
+ warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
+ rval = 1;
+ break;
+ default:
+ if (p->fts_statp->st_nlink > 1 &&
+ linkchk(p->fts_statp->st_dev, p->fts_statp->st_ino))
+ break;
+ /*
+ * If listing each file, or a non-directory file was
+ * the root of a traversal, display the total.
+ */
+ if (listfiles || !p->fts_level)
+ prstat(p->fts_path, COUNT);
+ p->fts_parent->fts_number += COUNT;
+ if (cflag)
+ totalblocks += COUNT;
+ }
+ }
+ if (errno)
+ err(1, "fts_read");
+ if (cflag)
+ prstat("total", totalblocks);
+ exit(rval);
+}
+
+static void
+prstat(const char *fname, int64_t blocks)
+{
+ if (iflag) {
+ (void)printf("%" PRId64 "\t%s\n", blocks, fname);
+ return;
+ }
+
+ if (hflag) {
+ char buf[5];
+ int64_t sz = blocks * 512;
+
+ humanize_number(buf, sizeof(buf), sz, "", HN_AUTOSCALE,
+ HN_B | HN_NOSPACE | HN_DECIMAL);
+
+ (void)printf("%s\t%s\n", buf, fname);
+ } else
+ (void)printf("%" PRId64 "\t%s\n",
+ howmany(blocks, (int64_t)blocksize),
+ fname);
+}
+
+static int
+linkchk(dev_t dev, ino_t ino)
+{
+ static struct entry {
+ dev_t dev;
+ ino_t ino;
+ } *htable;
+ static int htshift; /* log(allocated size) */
+ static int htmask; /* allocated size - 1 */
+ static int htused; /* 2*number of insertions */
+ static int sawzero; /* Whether zero is in table or not */
+ int h, h2;
+ uint64_t tmp;
+ /* this constant is (1<<64)/((1+sqrt(5))/2)
+ * aka (word size)/(golden ratio)
+ */
+ const uint64_t HTCONST = 11400714819323198485ULL;
+ const int HTBITS = CHAR_BIT * sizeof(tmp);
+
+ /* Never store zero in hashtable */
+ if (dev == 0 && ino == 0) {
+ h = sawzero;
+ sawzero = 1;
+ return h;
+ }
+
+ /* Extend hash table if necessary, keep load under 0.5 */
+ if (htused<<1 >= htmask) {
+ struct entry *ohtable;
+
+ if (!htable)
+ htshift = 10; /* starting hashtable size */
+ else
+ htshift++; /* exponential hashtable growth */
+
+ htmask = (1 << htshift) - 1;
+ htused = 0;
+
+ ohtable = htable;
+ htable = calloc(htmask+1, sizeof(*htable));
+ if (!htable)
+ err(1, "calloc");
+
+ /* populate newly allocated hashtable */
+ if (ohtable) {
+ int i;
+ for (i = 0; i <= htmask>>1; i++)
+ if (ohtable[i].ino || ohtable[i].dev)
+ linkchk(ohtable[i].dev, ohtable[i].ino);
+ free(ohtable);
+ }
+ }
+
+ /* multiplicative hashing */
+ tmp = dev;
+ tmp <<= HTBITS>>1;
+ tmp |= ino;
+ tmp *= HTCONST;
+ h = tmp >> (HTBITS - htshift);
+ h2 = 1 | ( tmp >> (HTBITS - (htshift<<1) - 1)); /* must be odd */
+
+ /* open address hashtable search with double hash probing */
+ while (htable[h].ino || htable[h].dev) {
+ if ((htable[h].ino == ino) && (htable[h].dev == dev))
+ return 1;
+ h = (h + h2) & htmask;
+ }
+
+ /* Insert the current entry into hashtable */
+ htable[h].dev = dev;
+ htable[h].ino = ino;
+ htused++;
+ return 0;
+}
+
+static void
+usage(void)
+{
+
+ (void)fprintf(stderr,
+ "usage: du [-H | -L | -P] [-a | -d depth | -s] [-cghikmnrx] [file ...]\n");
+ exit(1);
+}
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/fastgrep.c b/toolbox/upstream-netbsd/usr.bin/grep/fastgrep.c
new file mode 100644
index 0000000..2fcd864
--- /dev/null
+++ b/toolbox/upstream-netbsd/usr.bin/grep/fastgrep.c
@@ -0,0 +1,336 @@
+/* $OpenBSD: util.c,v 1.36 2007/10/02 17:59:18 otto Exp $ */
+/* $FreeBSD: head/usr.bin/grep/fastgrep.c 211496 2010-08-19 09:28:59Z des $ */
+
+/*-
+ * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
+ * Copyright (C) 2008 Gabor Kovesdan <gabor@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * XXX: This file is a speed up for grep to cover the defects of the
+ * regex library. These optimizations should practically be implemented
+ * there keeping this code clean. This is a future TODO, but for the
+ * meantime, we need to use this workaround.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: fastgrep.c,v 1.5 2011/04/18 03:27:40 joerg Exp $");
+
+#include <limits.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#include "grep.h"
+
+static inline int grep_cmp(const unsigned char *, const unsigned char *, size_t);
+static inline void grep_revstr(unsigned char *, int);
+
+void
+fgrepcomp(fastgrep_t *fg, const char *pat)
+{
+ unsigned int i;
+
+ /* Initialize. */
+ fg->len = strlen(pat);
+ fg->bol = false;
+ fg->eol = false;
+ fg->reversed = false;
+
+ fg->pattern = (unsigned char *)grep_strdup(pat);
+
+ /* Preprocess pattern. */
+ for (i = 0; i <= UCHAR_MAX; i++)
+ fg->qsBc[i] = fg->len;
+ for (i = 1; i < fg->len; i++)
+ fg->qsBc[fg->pattern[i]] = fg->len - i;
+}
+
+/*
+ * Returns: -1 on failure, 0 on success
+ */
+int
+fastcomp(fastgrep_t *fg, const char *pat)
+{
+ unsigned int i;
+ int firstHalfDot = -1;
+ int firstLastHalfDot = -1;
+ int hasDot = 0;
+ int lastHalfDot = 0;
+ int shiftPatternLen;
+
+ /* Initialize. */
+ fg->len = strlen(pat);
+ fg->bol = false;
+ fg->eol = false;
+ fg->reversed = false;
+ fg->word = wflag;
+
+ /* Remove end-of-line character ('$'). */
+ if (fg->len > 0 && pat[fg->len - 1] == '$') {
+ fg->eol = true;
+ fg->len--;
+ }
+
+ /* Remove beginning-of-line character ('^'). */
+ if (pat[0] == '^') {
+ fg->bol = true;
+ fg->len--;
+ pat++;
+ }
+
+ if (fg->len >= 14 &&
+ memcmp(pat, "[[:<:]]", 7) == 0 &&
+ memcmp(pat + fg->len - 7, "[[:>:]]", 7) == 0) {
+ fg->len -= 14;
+ pat += 7;
+ /* Word boundary is handled separately in util.c */
+ fg->word = true;
+ }
+
+ /*
+ * pat has been adjusted earlier to not include '^', '$' or
+ * the word match character classes at the beginning and ending
+ * of the string respectively.
+ */
+ fg->pattern = grep_malloc(fg->len + 1);
+ memcpy(fg->pattern, pat, fg->len);
+ fg->pattern[fg->len] = '\0';
+
+ /* Look for ways to cheat...er...avoid the full regex engine. */
+ for (i = 0; i < fg->len; i++) {
+ /* Can still cheat? */
+ if (fg->pattern[i] == '.') {
+ hasDot = i;
+ if (i < fg->len / 2) {
+ if (firstHalfDot < 0)
+ /* Closest dot to the beginning */
+ firstHalfDot = i;
+ } else {
+ /* Closest dot to the end of the pattern. */
+ lastHalfDot = i;
+ if (firstLastHalfDot < 0)
+ firstLastHalfDot = i;
+ }
+ } else {
+ /* Free memory and let others know this is empty. */
+ free(fg->pattern);
+ fg->pattern = NULL;
+ return (-1);
+ }
+ }
+
+ /*
+ * Determine if a reverse search would be faster based on the placement
+ * of the dots.
+ */
+ if ((!(lflag || cflag)) && ((!(fg->bol || fg->eol)) &&
+ ((lastHalfDot) && ((firstHalfDot < 0) ||
+ ((fg->len - (lastHalfDot + 1)) < (size_t)firstHalfDot)))) &&
+ !oflag && !color) {
+ fg->reversed = true;
+ hasDot = fg->len - (firstHalfDot < 0 ?
+ firstLastHalfDot : firstHalfDot) - 1;
+ grep_revstr(fg->pattern, fg->len);
+ }
+
+ /*
+ * Normal Quick Search would require a shift based on the position the
+ * next character after the comparison is within the pattern. With
+ * wildcards, the position of the last dot effects the maximum shift
+ * distance.
+ * The closer to the end the wild card is the slower the search. A
+ * reverse version of this algorithm would be useful for wildcards near
+ * the end of the string.
+ *
+ * Examples:
+ * Pattern Max shift
+ * ------- ---------
+ * this 5
+ * .his 4
+ * t.is 3
+ * th.s 2
+ * thi. 1
+ */
+
+ /* Adjust the shift based on location of the last dot ('.'). */
+ shiftPatternLen = fg->len - hasDot;
+
+ /* Preprocess pattern. */
+ for (i = 0; i <= (signed)UCHAR_MAX; i++)
+ fg->qsBc[i] = shiftPatternLen;
+ for (i = hasDot + 1; i < fg->len; i++) {
+ fg->qsBc[fg->pattern[i]] = fg->len - i;
+ }
+
+ /*
+ * Put pattern back to normal after pre-processing to allow for easy
+ * comparisons later.
+ */
+ if (fg->reversed)
+ grep_revstr(fg->pattern, fg->len);
+
+ return (0);
+}
+
+int
+grep_search(fastgrep_t *fg, const unsigned char *data, size_t len, regmatch_t *pmatch)
+{
+ unsigned int j;
+ int ret = REG_NOMATCH;
+
+ if (pmatch->rm_so == (ssize_t)len)
+ return (ret);
+
+ if (fg->bol && pmatch->rm_so != 0) {
+ pmatch->rm_so = len;
+ pmatch->rm_eo = len;
+ return (ret);
+ }
+
+ /* No point in going farther if we do not have enough data. */
+ if (len < fg->len)
+ return (ret);
+
+ /* Only try once at the beginning or ending of the line. */
+ if (fg->bol || fg->eol) {
+ /* Simple text comparison. */
+ /* Verify data is >= pattern length before searching on it. */
+ if (len >= fg->len) {
+ /* Determine where in data to start search at. */
+ j = fg->eol ? len - fg->len : 0;
+ if (!((fg->bol && fg->eol) && (len != fg->len)))
+ if (grep_cmp(fg->pattern, data + j,
+ fg->len) == -1) {
+ pmatch->rm_so = j;
+ pmatch->rm_eo = j + fg->len;
+ ret = 0;
+ }
+ }
+ } else if (fg->reversed) {
+ /* Quick Search algorithm. */
+ j = len;
+ do {
+ if (grep_cmp(fg->pattern, data + j - fg->len,
+ fg->len) == -1) {
+ pmatch->rm_so = j - fg->len;
+ pmatch->rm_eo = j;
+ ret = 0;
+ break;
+ }
+ /* Shift if within bounds, otherwise, we are done. */
+ if (j == fg->len)
+ break;
+ j -= fg->qsBc[data[j - fg->len - 1]];
+ } while (j >= fg->len);
+ } else {
+ /* Quick Search algorithm. */
+ j = pmatch->rm_so;
+ do {
+ if (grep_cmp(fg->pattern, data + j, fg->len) == -1) {
+ pmatch->rm_so = j;
+ pmatch->rm_eo = j + fg->len;
+ ret = 0;
+ break;
+ }
+
+ /* Shift if within bounds, otherwise, we are done. */
+ if (j + fg->len == len)
+ break;
+ else
+ j += fg->qsBc[data[j + fg->len]];
+ } while (j <= (len - fg->len));
+ }
+
+ return (ret);
+}
+
+/*
+ * Returns: i >= 0 on failure (position that it failed)
+ * -1 on success
+ */
+static inline int
+grep_cmp(const unsigned char *pat, const unsigned char *data, size_t len)
+{
+ size_t size;
+ wchar_t *wdata, *wpat;
+ unsigned int i;
+
+ if (iflag) {
+ if ((size = mbstowcs(NULL, (const char *)data, 0)) ==
+ ((size_t) - 1))
+ return (-1);
+
+ wdata = grep_malloc(size * sizeof(wint_t));
+
+ if (mbstowcs(wdata, (const char *)data, size) ==
+ ((size_t) - 1))
+ return (-1);
+
+ if ((size = mbstowcs(NULL, (const char *)pat, 0)) ==
+ ((size_t) - 1))
+ return (-1);
+
+ wpat = grep_malloc(size * sizeof(wint_t));
+
+ if (mbstowcs(wpat, (const char *)pat, size) == ((size_t) - 1))
+ return (-1);
+ for (i = 0; i < len; i++) {
+ if ((towlower(wpat[i]) == towlower(wdata[i])) ||
+ ((grepbehave != GREP_FIXED) && wpat[i] == L'.'))
+ continue;
+ free(wpat);
+ free(wdata);
+ return (i);
+ }
+ } else {
+ for (i = 0; i < len; i++) {
+ if ((pat[i] == data[i]) || ((grepbehave != GREP_FIXED) &&
+ pat[i] == '.'))
+ continue;
+ return (i);
+ }
+ }
+ return (-1);
+}
+
+static inline void
+grep_revstr(unsigned char *str, int len)
+{
+ int i;
+ char c;
+
+ for (i = 0; i < len / 2; i++) {
+ c = str[i];
+ str[i] = str[len - i - 1];
+ str[len - i - 1] = c;
+ }
+}
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/file.c b/toolbox/upstream-netbsd/usr.bin/grep/file.c
new file mode 100644
index 0000000..cf4a0fa
--- /dev/null
+++ b/toolbox/upstream-netbsd/usr.bin/grep/file.c
@@ -0,0 +1,271 @@
+/* $NetBSD: file.c,v 1.7 2011/04/18 22:46:48 joerg Exp $ */
+/* $FreeBSD: head/usr.bin/grep/file.c 211496 2010-08-19 09:28:59Z des $ */
+/* $OpenBSD: file.c,v 1.11 2010/07/02 20:48:48 nicm Exp $ */
+
+/*-
+ * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
+ * Copyright (C) 2008-2010 Gabor Kovesdan <gabor@FreeBSD.org>
+ * Copyright (C) 2010 Dimitry Andric <dimitry@andric.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: file.c,v 1.7 2011/04/18 22:46:48 joerg Exp $");
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifndef __ANDROID__
+#include <bzlib.h>
+#endif
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <wctype.h>
+#ifndef __ANDROID__
+#include <zlib.h>
+#endif
+
+#include "grep.h"
+
+#define MAXBUFSIZ (32 * 1024)
+#define LNBUFBUMP 80
+
+#ifndef __ANDROID__
+static gzFile gzbufdesc;
+static BZFILE* bzbufdesc;
+#endif
+
+static unsigned char buffer[MAXBUFSIZ];
+static unsigned char *bufpos;
+static size_t bufrem;
+
+static unsigned char *lnbuf;
+static size_t lnbuflen;
+
+static inline int
+grep_refill(struct file *f)
+{
+ ssize_t nr;
+#ifndef __ANDROID__
+ int bzerr;
+#endif
+
+ bufpos = buffer;
+ bufrem = 0;
+
+#ifndef __ANDROID__
+ if (filebehave == FILE_GZIP)
+ nr = gzread(gzbufdesc, buffer, MAXBUFSIZ);
+ else if (filebehave == FILE_BZIP && bzbufdesc != NULL) {
+ nr = BZ2_bzRead(&bzerr, bzbufdesc, buffer, MAXBUFSIZ);
+ switch (bzerr) {
+ case BZ_OK:
+ case BZ_STREAM_END:
+ /* No problem, nr will be okay */
+ break;
+ case BZ_DATA_ERROR_MAGIC:
+ /*
+ * As opposed to gzread(), which simply returns the
+ * plain file data, if it is not in the correct
+ * compressed format, BZ2_bzRead() instead aborts.
+ *
+ * So, just restart at the beginning of the file again,
+ * and use plain reads from now on.
+ */
+ BZ2_bzReadClose(&bzerr, bzbufdesc);
+ bzbufdesc = NULL;
+ if (lseek(f->fd, 0, SEEK_SET) == -1)
+ return (-1);
+ nr = read(f->fd, buffer, MAXBUFSIZ);
+ break;
+ default:
+ /* Make sure we exit with an error */
+ nr = -1;
+ }
+ } else
+#endif
+ nr = read(f->fd, buffer, MAXBUFSIZ);
+
+ if (nr < 0)
+ return (-1);
+
+ bufrem = nr;
+ return (0);
+}
+
+static inline int
+grep_lnbufgrow(size_t newlen)
+{
+
+ if (lnbuflen < newlen) {
+ lnbuf = grep_realloc(lnbuf, newlen);
+ lnbuflen = newlen;
+ }
+
+ return (0);
+}
+
+char *
+grep_fgetln(struct file *f, size_t *lenp)
+{
+ unsigned char *p;
+ char *ret;
+ size_t len;
+ size_t off;
+ ptrdiff_t diff;
+
+ /* Fill the buffer, if necessary */
+ if (bufrem == 0 && grep_refill(f) != 0)
+ goto error;
+
+ if (bufrem == 0) {
+ /* Return zero length to indicate EOF */
+ *lenp = 0;
+ return ((char *)bufpos);
+ }
+
+ /* Look for a newline in the remaining part of the buffer */
+ if ((p = memchr(bufpos, line_sep, bufrem)) != NULL) {
+ ++p; /* advance over newline */
+ ret = (char *)bufpos;
+ len = p - bufpos;
+ bufrem -= len;
+ bufpos = p;
+ *lenp = len;
+ return (ret);
+ }
+
+ /* We have to copy the current buffered data to the line buffer */
+ for (len = bufrem, off = 0; ; len += bufrem) {
+ /* Make sure there is room for more data */
+ if (grep_lnbufgrow(len + LNBUFBUMP))
+ goto error;
+ memcpy(lnbuf + off, bufpos, len - off);
+ off = len;
+ if (grep_refill(f) != 0)
+ goto error;
+ if (bufrem == 0)
+ /* EOF: return partial line */
+ break;
+ if ((p = memchr(bufpos, line_sep, bufrem)) == NULL)
+ continue;
+ /* got it: finish up the line (like code above) */
+ ++p;
+ diff = p - bufpos;
+ len += diff;
+ if (grep_lnbufgrow(len))
+ goto error;
+ memcpy(lnbuf + off, bufpos, diff);
+ bufrem -= diff;
+ bufpos = p;
+ break;
+ }
+ *lenp = len;
+ return ((char *)lnbuf);
+
+error:
+ *lenp = 0;
+ return (NULL);
+}
+
+static inline struct file *
+grep_file_init(struct file *f)
+{
+
+#ifndef __ANDROID__
+ if (filebehave == FILE_GZIP &&
+ (gzbufdesc = gzdopen(f->fd, "r")) == NULL)
+ goto error;
+
+ if (filebehave == FILE_BZIP &&
+ (bzbufdesc = BZ2_bzdopen(f->fd, "r")) == NULL)
+ goto error;
+#endif
+
+ /* Fill read buffer, also catches errors early */
+ if (grep_refill(f) != 0)
+ goto error;
+
+ /* Check for binary stuff, if necessary */
+ if (!nulldataflag && binbehave != BINFILE_TEXT &&
+ memchr(bufpos, '\0', bufrem) != NULL)
+ f->binary = true;
+
+ return (f);
+error:
+ close(f->fd);
+ free(f);
+ return (NULL);
+}
+
+/*
+ * Opens a file for processing.
+ */
+struct file *
+grep_open(const char *path)
+{
+ struct file *f;
+
+ f = grep_malloc(sizeof *f);
+ memset(f, 0, sizeof *f);
+ if (path == NULL) {
+ /* Processing stdin implies --line-buffered. */
+ lbflag = true;
+ f->fd = STDIN_FILENO;
+ } else if ((f->fd = open(path, O_RDONLY)) == -1) {
+ free(f);
+ return (NULL);
+ }
+
+ return (grep_file_init(f));
+}
+
+/*
+ * Closes a file.
+ */
+void
+grep_close(struct file *f)
+{
+
+ close(f->fd);
+
+ /* Reset read buffer and line buffer */
+ bufpos = buffer;
+ bufrem = 0;
+
+ free(lnbuf);
+ lnbuf = NULL;
+ lnbuflen = 0;
+}
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/grep.c b/toolbox/upstream-netbsd/usr.bin/grep/grep.c
new file mode 100644
index 0000000..1ea6ed3
--- /dev/null
+++ b/toolbox/upstream-netbsd/usr.bin/grep/grep.c
@@ -0,0 +1,707 @@
+/* $NetBSD: grep.c,v 1.12 2014/07/11 16:30:45 christos Exp $ */
+/* $FreeBSD: head/usr.bin/grep/grep.c 211519 2010-08-19 22:55:17Z delphij $ */
+/* $OpenBSD: grep.c,v 1.42 2010/07/02 22:18:03 tedu Exp $ */
+
+/*-
+ * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
+ * Copyright (C) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: grep.c,v 1.12 2014/07/11 16:30:45 christos Exp $");
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <getopt.h>
+#include <limits.h>
+#include <libgen.h>
+#include <locale.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "grep.h"
+
+#ifndef WITHOUT_NLS
+#include <nl_types.h>
+nl_catd catalog;
+#endif
+
+/*
+ * Default messags to use when NLS is disabled or no catalogue
+ * is found.
+ */
+const char *errstr[] = {
+ "",
+/* 1*/ "(standard input)",
+/* 2*/ "cannot read bzip2 compressed file",
+/* 3*/ "unknown %s option",
+/* 4*/ "usage: %s [-abcDEFGHhIiJLlmnOoPqRSsUVvwxZz] [-A num] [-B num] [-C[num]]\n",
+/* 5*/ "\t[-e pattern] [-f file] [--binary-files=value] [--color=when]\n",
+/* 6*/ "\t[--context[=num]] [--directories=action] [--label] [--line-buffered]\n",
+/* 7*/ "\t[pattern] [file ...]\n",
+/* 8*/ "Binary file %s matches\n",
+/* 9*/ "%s (BSD grep) %s\n",
+};
+
+/* Flags passed to regcomp() and regexec() */
+int cflags = 0;
+int eflags = REG_STARTEND;
+
+/* Searching patterns */
+unsigned int patterns, pattern_sz;
+char **pattern;
+regex_t *r_pattern;
+fastgrep_t *fg_pattern;
+
+/* Filename exclusion/inclusion patterns */
+unsigned int fpatterns, fpattern_sz;
+unsigned int dpatterns, dpattern_sz;
+struct epat *dpattern, *fpattern;
+
+/* For regex errors */
+char re_error[RE_ERROR_BUF + 1];
+
+/* Command-line flags */
+unsigned long long Aflag; /* -A x: print x lines trailing each match */
+unsigned long long Bflag; /* -B x: print x lines leading each match */
+bool Hflag; /* -H: always print file name */
+bool Lflag; /* -L: only show names of files with no matches */
+bool bflag; /* -b: show block numbers for each match */
+bool cflag; /* -c: only show a count of matching lines */
+bool hflag; /* -h: don't print filename headers */
+bool iflag; /* -i: ignore case */
+bool lflag; /* -l: only show names of files with matches */
+bool mflag; /* -m x: stop reading the files after x matches */
+unsigned long long mcount; /* count for -m */
+bool nflag; /* -n: show line numbers in front of matching lines */
+bool oflag; /* -o: print only matching part */
+bool qflag; /* -q: quiet mode (don't output anything) */
+bool sflag; /* -s: silent mode (ignore errors) */
+bool vflag; /* -v: only show non-matching lines */
+bool wflag; /* -w: pattern must start and end on word boundaries */
+bool xflag; /* -x: pattern must match entire line */
+bool lbflag; /* --line-buffered */
+bool nullflag; /* --null */
+bool nulldataflag; /* --null-data */
+unsigned char line_sep = '\n'; /* 0 for --null-data */
+char *label; /* --label */
+const char *color; /* --color */
+int grepbehave = GREP_BASIC; /* -EFGP: type of the regex */
+int binbehave = BINFILE_BIN; /* -aIU: handling of binary files */
+int filebehave = FILE_STDIO; /* -JZ: normal, gzip or bzip2 file */
+int devbehave = DEV_READ; /* -D: handling of devices */
+int dirbehave = DIR_READ; /* -dRr: handling of directories */
+int linkbehave = LINK_READ; /* -OpS: handling of symlinks */
+
+bool dexclude, dinclude; /* --exclude-dir and --include-dir */
+bool fexclude, finclude; /* --exclude and --include */
+
+enum {
+ BIN_OPT = CHAR_MAX + 1,
+ COLOR_OPT,
+ DECOMPRESS_OPT,
+ HELP_OPT,
+ MMAP_OPT,
+ LINEBUF_OPT,
+ LABEL_OPT,
+ R_EXCLUDE_OPT,
+ R_INCLUDE_OPT,
+ R_DEXCLUDE_OPT,
+ R_DINCLUDE_OPT
+};
+
+static inline const char *init_color(const char *);
+
+/* Housekeeping */
+int tail; /* lines left to print */
+bool notfound; /* file not found */
+
+extern char *__progname;
+
+/*
+ * Prints usage information and returns 2.
+ */
+__dead static void
+usage(void)
+{
+ fprintf(stderr, getstr(4), __progname);
+ fprintf(stderr, "%s", getstr(5));
+ fprintf(stderr, "%s", getstr(6));
+ fprintf(stderr, "%s", getstr(7));
+ exit(2);
+}
+
+static const char optstr[] =
+ "0123456789A:B:C:D:EFGHIJLOPSRUVZabcd:e:f:hilm:nopqrsuvwxyz";
+
+struct option long_options[] =
+{
+ {"binary-files", required_argument, NULL, BIN_OPT},
+ {"decompress", no_argument, NULL, DECOMPRESS_OPT},
+ {"help", no_argument, NULL, HELP_OPT},
+ {"mmap", no_argument, NULL, MMAP_OPT},
+ {"line-buffered", no_argument, NULL, LINEBUF_OPT},
+ {"label", required_argument, NULL, LABEL_OPT},
+ {"color", optional_argument, NULL, COLOR_OPT},
+ {"colour", optional_argument, NULL, COLOR_OPT},
+ {"exclude", required_argument, NULL, R_EXCLUDE_OPT},
+ {"include", required_argument, NULL, R_INCLUDE_OPT},
+ {"exclude-dir", required_argument, NULL, R_DEXCLUDE_OPT},
+ {"include-dir", required_argument, NULL, R_DINCLUDE_OPT},
+ {"after-context", required_argument, NULL, 'A'},
+ {"text", no_argument, NULL, 'a'},
+ {"before-context", required_argument, NULL, 'B'},
+ {"byte-offset", no_argument, NULL, 'b'},
+ {"context", optional_argument, NULL, 'C'},
+ {"count", no_argument, NULL, 'c'},
+ {"devices", required_argument, NULL, 'D'},
+ {"directories", required_argument, NULL, 'd'},
+ {"extended-regexp", no_argument, NULL, 'E'},
+ {"regexp", required_argument, NULL, 'e'},
+ {"fixed-strings", no_argument, NULL, 'F'},
+ {"file", required_argument, NULL, 'f'},
+ {"basic-regexp", no_argument, NULL, 'G'},
+ {"no-filename", no_argument, NULL, 'h'},
+ {"with-filename", no_argument, NULL, 'H'},
+ {"ignore-case", no_argument, NULL, 'i'},
+ {"bz2decompress", no_argument, NULL, 'J'},
+ {"files-with-matches", no_argument, NULL, 'l'},
+ {"files-without-match", no_argument, NULL, 'L'},
+ {"max-count", required_argument, NULL, 'm'},
+ {"line-number", no_argument, NULL, 'n'},
+ {"only-matching", no_argument, NULL, 'o'},
+ {"quiet", no_argument, NULL, 'q'},
+ {"silent", no_argument, NULL, 'q'},
+ {"recursive", no_argument, NULL, 'r'},
+ {"no-messages", no_argument, NULL, 's'},
+ {"binary", no_argument, NULL, 'U'},
+ {"unix-byte-offsets", no_argument, NULL, 'u'},
+ {"invert-match", no_argument, NULL, 'v'},
+ {"version", no_argument, NULL, 'V'},
+ {"word-regexp", no_argument, NULL, 'w'},
+ {"line-regexp", no_argument, NULL, 'x'},
+ {"null", no_argument, NULL, 'Z'},
+ {"null-data", no_argument, NULL, 'z'},
+ {NULL, no_argument, NULL, 0}
+};
+
+/*
+ * Adds a searching pattern to the internal array.
+ */
+static void
+add_pattern(char *pat, size_t len)
+{
+
+ /* TODO: Check for empty patterns and shortcut */
+
+ /* Increase size if necessary */
+ if (patterns == pattern_sz) {
+ pattern_sz *= 2;
+ pattern = grep_realloc(pattern, ++pattern_sz *
+ sizeof(*pattern));
+ }
+ if (len > 0 && pat[len - 1] == '\n')
+ --len;
+ /* pat may not be NUL-terminated */
+ pattern[patterns] = grep_malloc(len + 1);
+ memcpy(pattern[patterns], pat, len);
+ pattern[patterns][len] = '\0';
+ ++patterns;
+}
+
+/*
+ * Adds a file include/exclude pattern to the internal array.
+ */
+static void
+add_fpattern(const char *pat, int mode)
+{
+
+ /* Increase size if necessary */
+ if (fpatterns == fpattern_sz) {
+ fpattern_sz *= 2;
+ fpattern = grep_realloc(fpattern, ++fpattern_sz *
+ sizeof(struct epat));
+ }
+ fpattern[fpatterns].pat = grep_strdup(pat);
+ fpattern[fpatterns].mode = mode;
+ ++fpatterns;
+}
+
+/*
+ * Adds a directory include/exclude pattern to the internal array.
+ */
+static void
+add_dpattern(const char *pat, int mode)
+{
+
+ /* Increase size if necessary */
+ if (dpatterns == dpattern_sz) {
+ dpattern_sz *= 2;
+ dpattern = grep_realloc(dpattern, ++dpattern_sz *
+ sizeof(struct epat));
+ }
+ dpattern[dpatterns].pat = grep_strdup(pat);
+ dpattern[dpatterns].mode = mode;
+ ++dpatterns;
+}
+
+/*
+ * Reads searching patterns from a file and adds them with add_pattern().
+ */
+static void
+read_patterns(const char *fn)
+{
+ FILE *f;
+ char *line;
+ size_t len;
+ ssize_t rlen;
+
+ if ((f = fopen(fn, "r")) == NULL)
+ err(2, "%s", fn);
+ line = NULL;
+ len = 0;
+ while ((rlen = getline(&line, &len, f)) != -1)
+ add_pattern(line, *line == '\n' ? 0 : (size_t)rlen);
+ free(line);
+ if (ferror(f))
+ err(2, "%s", fn);
+ fclose(f);
+}
+
+static inline const char *
+init_color(const char *d)
+{
+ char *c;
+
+ c = getenv("GREP_COLOR");
+ return (c != NULL ? c : d);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char **aargv, **eargv, *eopts;
+ char *ep;
+ unsigned long long l;
+ unsigned int aargc, eargc, i, j;
+ int c, lastc, needpattern, newarg, prevoptind;
+
+ setlocale(LC_ALL, "");
+
+#ifndef WITHOUT_NLS
+ catalog = catopen("grep", NL_CAT_LOCALE);
+#endif
+
+ /* Check what is the program name of the binary. In this
+ way we can have all the funcionalities in one binary
+ without the need of scripting and using ugly hacks. */
+ switch (__progname[0]) {
+ case 'e':
+ grepbehave = GREP_EXTENDED;
+ break;
+ case 'f':
+ grepbehave = GREP_FIXED;
+ break;
+ case 'g':
+ grepbehave = GREP_BASIC;
+ break;
+ case 'z':
+ filebehave = FILE_GZIP;
+ switch(__progname[1]) {
+ case 'e':
+ grepbehave = GREP_EXTENDED;
+ break;
+ case 'f':
+ grepbehave = GREP_FIXED;
+ break;
+ case 'g':
+ grepbehave = GREP_BASIC;
+ break;
+ }
+ break;
+ }
+
+ lastc = '\0';
+ newarg = 1;
+ prevoptind = 1;
+ needpattern = 1;
+
+ eopts = getenv("GREP_OPTIONS");
+
+ /* support for extra arguments in GREP_OPTIONS */
+ eargc = 0;
+ if (eopts != NULL) {
+ char *str;
+
+ /* make an estimation of how many extra arguments we have */
+ for (j = 0; j < strlen(eopts); j++)
+ if (eopts[j] == ' ')
+ eargc++;
+
+ eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1));
+
+ eargc = 0;
+ /* parse extra arguments */
+ while ((str = strsep(&eopts, " ")) != NULL)
+ eargv[eargc++] = grep_strdup(str);
+
+ aargv = (char **)grep_calloc(eargc + argc + 1,
+ sizeof(char *));
+
+ aargv[0] = argv[0];
+ for (i = 0; i < eargc; i++)
+ aargv[i + 1] = eargv[i];
+ for (j = 1; j < (unsigned int)argc; j++, i++)
+ aargv[i + 1] = argv[j];
+
+ aargc = eargc + argc;
+ } else {
+ aargv = argv;
+ aargc = argc;
+ }
+
+ while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) !=
+ -1)) {
+ switch (c) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ if (newarg || !isdigit(lastc))
+ Aflag = 0;
+ else if (Aflag > LLONG_MAX / 10) {
+ errno = ERANGE;
+ err(2, NULL);
+ }
+ Aflag = Bflag = (Aflag * 10) + (c - '0');
+ break;
+ case 'C':
+ if (optarg == NULL) {
+ Aflag = Bflag = 2;
+ break;
+ }
+ /* FALLTHROUGH */
+ case 'A':
+ /* FALLTHROUGH */
+ case 'B':
+ errno = 0;
+ l = strtoull(optarg, &ep, 10);
+ if (((errno == ERANGE) && (l == ULLONG_MAX)) ||
+ ((errno == EINVAL) && (l == 0)))
+ err(2, NULL);
+ else if (ep[0] != '\0') {
+ errno = EINVAL;
+ err(2, NULL);
+ }
+ if (c == 'A')
+ Aflag = l;
+ else if (c == 'B')
+ Bflag = l;
+ else
+ Aflag = Bflag = l;
+ break;
+ case 'a':
+ binbehave = BINFILE_TEXT;
+ break;
+ case 'b':
+ bflag = true;
+ break;
+ case 'c':
+ cflag = true;
+ break;
+ case 'D':
+ if (strcasecmp(optarg, "skip") == 0)
+ devbehave = DEV_SKIP;
+ else if (strcasecmp(optarg, "read") == 0)
+ devbehave = DEV_READ;
+ else
+ errx(2, getstr(3), "--devices");
+ break;
+ case 'd':
+ if (strcasecmp("recurse", optarg) == 0) {
+ Hflag = true;
+ dirbehave = DIR_RECURSE;
+ } else if (strcasecmp("skip", optarg) == 0)
+ dirbehave = DIR_SKIP;
+ else if (strcasecmp("read", optarg) == 0)
+ dirbehave = DIR_READ;
+ else
+ errx(2, getstr(3), "--directories");
+ break;
+ case 'E':
+ grepbehave = GREP_EXTENDED;
+ break;
+ case 'e':
+ add_pattern(optarg, strlen(optarg));
+ needpattern = 0;
+ break;
+ case 'F':
+ grepbehave = GREP_FIXED;
+ break;
+ case 'f':
+ read_patterns(optarg);
+ needpattern = 0;
+ break;
+ case 'G':
+ grepbehave = GREP_BASIC;
+ break;
+ case 'H':
+ Hflag = true;
+ break;
+ case 'h':
+ Hflag = false;
+ hflag = true;
+ break;
+ case 'I':
+ binbehave = BINFILE_SKIP;
+ break;
+ case 'i':
+ case 'y':
+ iflag = true;
+ cflags |= REG_ICASE;
+ break;
+ case 'J':
+ filebehave = FILE_BZIP;
+ break;
+ case 'L':
+ lflag = false;
+ Lflag = true;
+ break;
+ case 'l':
+ Lflag = false;
+ lflag = true;
+ break;
+ case 'm':
+ mflag = true;
+ errno = 0;
+ mcount = strtoull(optarg, &ep, 10);
+ if (((errno == ERANGE) && (mcount == ULLONG_MAX)) ||
+ ((errno == EINVAL) && (mcount == 0)))
+ err(2, NULL);
+ else if (ep[0] != '\0') {
+ errno = EINVAL;
+ err(2, NULL);
+ }
+ break;
+ case 'n':
+ nflag = true;
+ break;
+ case 'O':
+ linkbehave = LINK_EXPLICIT;
+ break;
+ case 'o':
+ oflag = true;
+ break;
+ case 'p':
+ linkbehave = LINK_SKIP;
+ break;
+ case 'q':
+ qflag = true;
+ break;
+ case 'S':
+ linkbehave = LINK_READ;
+ break;
+ case 'R':
+ case 'r':
+ dirbehave = DIR_RECURSE;
+ Hflag = true;
+ break;
+ case 's':
+ sflag = true;
+ break;
+ case 'U':
+ binbehave = BINFILE_BIN;
+ break;
+ case 'u':
+ case MMAP_OPT:
+ /* noop, compatibility */
+ break;
+ case 'V':
+ printf(getstr(9), __progname, VERSION);
+ exit(0);
+ case 'v':
+ vflag = true;
+ break;
+ case 'w':
+ wflag = true;
+ break;
+ case 'x':
+ xflag = true;
+ break;
+ case 'Z':
+ nullflag = true;
+ break;
+ case 'z':
+ nulldataflag = true;
+ line_sep = '\0';
+ break;
+ case BIN_OPT:
+ if (strcasecmp("binary", optarg) == 0)
+ binbehave = BINFILE_BIN;
+ else if (strcasecmp("without-match", optarg) == 0)
+ binbehave = BINFILE_SKIP;
+ else if (strcasecmp("text", optarg) == 0)
+ binbehave = BINFILE_TEXT;
+ else
+ errx(2, getstr(3), "--binary-files");
+ break;
+ case COLOR_OPT:
+ color = NULL;
+ if (optarg == NULL || strcasecmp("auto", optarg) == 0 ||
+ strcasecmp("tty", optarg) == 0 ||
+ strcasecmp("if-tty", optarg) == 0) {
+ char *term;
+
+ term = getenv("TERM");
+ if (isatty(STDOUT_FILENO) && term != NULL &&
+ strcasecmp(term, "dumb") != 0)
+ color = init_color("01;31");
+ } else if (strcasecmp("always", optarg) == 0 ||
+ strcasecmp("yes", optarg) == 0 ||
+ strcasecmp("force", optarg) == 0) {
+ color = init_color("01;31");
+ } else if (strcasecmp("never", optarg) != 0 &&
+ strcasecmp("none", optarg) != 0 &&
+ strcasecmp("no", optarg) != 0)
+ errx(2, getstr(3), "--color");
+ break;
+ case DECOMPRESS_OPT:
+ filebehave = FILE_GZIP;
+ break;
+ case LABEL_OPT:
+ label = optarg;
+ break;
+ case LINEBUF_OPT:
+ lbflag = true;
+ break;
+ case R_INCLUDE_OPT:
+ finclude = true;
+ add_fpattern(optarg, INCL_PAT);
+ break;
+ case R_EXCLUDE_OPT:
+ fexclude = true;
+ add_fpattern(optarg, EXCL_PAT);
+ break;
+ case R_DINCLUDE_OPT:
+ dinclude = true;
+ add_dpattern(optarg, INCL_PAT);
+ break;
+ case R_DEXCLUDE_OPT:
+ dexclude = true;
+ add_dpattern(optarg, EXCL_PAT);
+ break;
+ case HELP_OPT:
+ default:
+ usage();
+ }
+ lastc = c;
+ newarg = optind != prevoptind;
+ prevoptind = optind;
+ }
+ aargc -= optind;
+ aargv += optind;
+
+ /* Fail if we don't have any pattern */
+ if (aargc == 0 && needpattern)
+ usage();
+
+ /* Process patterns from command line */
+ if (aargc != 0 && needpattern) {
+ add_pattern(*aargv, strlen(*aargv));
+ --aargc;
+ ++aargv;
+ }
+
+ switch (grepbehave) {
+ case GREP_FIXED:
+ case GREP_BASIC:
+ break;
+ case GREP_EXTENDED:
+ cflags |= REG_EXTENDED;
+ break;
+ default:
+ /* NOTREACHED */
+ usage();
+ }
+
+ fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern));
+ r_pattern = grep_calloc(patterns, sizeof(*r_pattern));
+/*
+ * XXX: fgrepcomp() and fastcomp() are workarounds for regexec() performance.
+ * Optimizations should be done there.
+ */
+ /* Check if cheating is allowed (always is for fgrep). */
+ if (grepbehave == GREP_FIXED) {
+ for (i = 0; i < patterns; ++i)
+ fgrepcomp(&fg_pattern[i], pattern[i]);
+ } else {
+ for (i = 0; i < patterns; ++i) {
+ if (fastcomp(&fg_pattern[i], pattern[i])) {
+ /* Fall back to full regex library */
+ c = regcomp(&r_pattern[i], pattern[i], cflags);
+ if (c != 0) {
+ regerror(c, &r_pattern[i], re_error,
+ RE_ERROR_BUF);
+ errx(2, "%s", re_error);
+ }
+ }
+ }
+ }
+
+ if (lbflag)
+ setlinebuf(stdout);
+
+ if ((aargc == 0 || aargc == 1) && !Hflag)
+ hflag = true;
+
+ if (aargc == 0)
+ exit(!procfile("-"));
+
+ if (dirbehave == DIR_RECURSE)
+ c = grep_tree(aargv);
+ else
+ for (c = 0; aargc--; ++aargv) {
+ if ((finclude || fexclude) && !file_matching(*aargv))
+ continue;
+ c+= procfile(*aargv);
+ }
+
+#ifndef WITHOUT_NLS
+ catclose(catalog);
+#endif
+
+ /* Find out the correct return value according to the
+ results and the command line option. */
+ exit(c ? (notfound ? (qflag ? 0 : 2) : 0) : (notfound ? 2 : 1));
+}
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/grep.h b/toolbox/upstream-netbsd/usr.bin/grep/grep.h
new file mode 100644
index 0000000..fa2a3e3
--- /dev/null
+++ b/toolbox/upstream-netbsd/usr.bin/grep/grep.h
@@ -0,0 +1,162 @@
+/* $NetBSD: grep.h,v 1.8 2012/05/06 22:27:00 joerg Exp $ */
+/* $OpenBSD: grep.h,v 1.15 2010/04/05 03:03:55 tedu Exp $ */
+/* $FreeBSD: head/usr.bin/grep/grep.h 211496 2010-08-19 09:28:59Z des $ */
+
+/*-
+ * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
+ * Copyright (c) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef __ANDROID__
+#include <bzlib.h>
+#endif
+#include <limits.h>
+#include <regex.h>
+#include <stdbool.h>
+#include <stdio.h>
+#ifndef __ANDROID__
+#include <zlib.h>
+#endif
+
+#ifdef WITHOUT_NLS
+#define getstr(n) errstr[n]
+#else
+#include <nl_types.h>
+
+extern nl_catd catalog;
+#define getstr(n) catgets(catalog, 1, n, errstr[n])
+#endif
+
+extern const char *errstr[];
+
+#define VERSION "2.5.1-FreeBSD"
+
+#define GREP_FIXED 0
+#define GREP_BASIC 1
+#define GREP_EXTENDED 2
+
+#define BINFILE_BIN 0
+#define BINFILE_SKIP 1
+#define BINFILE_TEXT 2
+
+#define FILE_STDIO 0
+#define FILE_GZIP 1
+#define FILE_BZIP 2
+
+#define DIR_READ 0
+#define DIR_SKIP 1
+#define DIR_RECURSE 2
+
+#define DEV_READ 0
+#define DEV_SKIP 1
+
+#define LINK_READ 0
+#define LINK_EXPLICIT 1
+#define LINK_SKIP 2
+
+#define EXCL_PAT 0
+#define INCL_PAT 1
+
+#define MAX_LINE_MATCHES 32
+
+struct file {
+ int fd;
+ bool binary;
+};
+
+struct str {
+ off_t off;
+ size_t len;
+ char *dat;
+ char *file;
+ int line_no;
+};
+
+struct epat {
+ char *pat;
+ int mode;
+};
+
+typedef struct {
+ size_t len;
+ unsigned char *pattern;
+ int qsBc[UCHAR_MAX + 1];
+ /* flags */
+ bool bol;
+ bool eol;
+ bool reversed;
+ bool word;
+} fastgrep_t;
+
+/* Flags passed to regcomp() and regexec() */
+extern int cflags, eflags;
+
+/* Command line flags */
+extern bool Eflag, Fflag, Gflag, Hflag, Lflag,
+ bflag, cflag, hflag, iflag, lflag, mflag, nflag, oflag,
+ qflag, sflag, vflag, wflag, xflag;
+extern bool dexclude, dinclude, fexclude, finclude, lbflag, nullflag, nulldataflag;
+extern unsigned char line_sep;
+extern unsigned long long Aflag, Bflag, mcount;
+extern char *label;
+extern const char *color;
+extern int binbehave, devbehave, dirbehave, filebehave, grepbehave, linkbehave;
+
+extern bool notfound;
+extern int tail;
+extern unsigned int dpatterns, fpatterns, patterns;
+extern char **pattern;
+extern struct epat *dpattern, *fpattern;
+extern regex_t *er_pattern, *r_pattern;
+extern fastgrep_t *fg_pattern;
+
+/* For regex errors */
+#define RE_ERROR_BUF 512
+extern char re_error[RE_ERROR_BUF + 1]; /* Seems big enough */
+
+/* util.c */
+bool file_matching(const char *fname);
+int procfile(const char *fn);
+int grep_tree(char **argv);
+void *grep_malloc(size_t size);
+void *grep_calloc(size_t nmemb, size_t size);
+void *grep_realloc(void *ptr, size_t size);
+char *grep_strdup(const char *str);
+void printline(struct str *line, int sep, regmatch_t *matches, int m);
+
+/* queue.c */
+void enqueue(struct str *x);
+void printqueue(void);
+void clearqueue(void);
+
+/* file.c */
+void grep_close(struct file *f);
+struct file *grep_open(const char *path);
+char *grep_fgetln(struct file *f, size_t *len);
+
+/* fastgrep.c */
+int fastcomp(fastgrep_t *, const char *);
+void fgrepcomp(fastgrep_t *, const char *);
+int grep_search(fastgrep_t *, const unsigned char *, size_t, regmatch_t *);
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/queue.c b/toolbox/upstream-netbsd/usr.bin/grep/queue.c
new file mode 100644
index 0000000..e3c6be1
--- /dev/null
+++ b/toolbox/upstream-netbsd/usr.bin/grep/queue.c
@@ -0,0 +1,116 @@
+/* $NetBSD: queue.c,v 1.5 2011/08/31 16:24:57 plunky Exp $ */
+/* $FreeBSD: head/usr.bin/grep/queue.c 211496 2010-08-19 09:28:59Z des $ */
+/*-
+ * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * A really poor man's queue. It does only what it has to and gets out of
+ * Dodge. It is used in place of <sys/queue.h> to get a better performance.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: queue.c,v 1.5 2011/08/31 16:24:57 plunky Exp $");
+
+#include <sys/param.h>
+#include <sys/queue.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "grep.h"
+
+struct qentry {
+ STAILQ_ENTRY(qentry) list;
+ struct str data;
+};
+
+static STAILQ_HEAD(, qentry) queue = STAILQ_HEAD_INITIALIZER(queue);
+static unsigned long long count;
+
+static struct qentry *dequeue(void);
+
+void
+enqueue(struct str *x)
+{
+ struct qentry *item;
+
+ item = grep_malloc(sizeof(struct qentry));
+ item->data.dat = grep_malloc(sizeof(char) * x->len);
+ item->data.len = x->len;
+ item->data.line_no = x->line_no;
+ item->data.off = x->off;
+ memcpy(item->data.dat, x->dat, x->len);
+ item->data.file = x->file;
+
+ STAILQ_INSERT_TAIL(&queue, item, list);
+
+ if (++count > Bflag) {
+ item = dequeue();
+ free(item->data.dat);
+ free(item);
+ }
+}
+
+static struct qentry *
+dequeue(void)
+{
+ struct qentry *item;
+
+ item = STAILQ_FIRST(&queue);
+ if (item == NULL)
+ return (NULL);
+
+ STAILQ_REMOVE_HEAD(&queue, list);
+ --count;
+ return (item);
+}
+
+void
+printqueue(void)
+{
+ struct qentry *item;
+
+ while ((item = dequeue()) != NULL) {
+ printline(&item->data, '-', NULL, 0);
+ free(item->data.dat);
+ free(item);
+ }
+}
+
+void
+clearqueue(void)
+{
+ struct qentry *item;
+
+ while ((item = dequeue()) != NULL) {
+ free(item->data.dat);
+ free(item);
+ }
+}
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/util.c b/toolbox/upstream-netbsd/usr.bin/grep/util.c
new file mode 100644
index 0000000..ecd948d
--- /dev/null
+++ b/toolbox/upstream-netbsd/usr.bin/grep/util.c
@@ -0,0 +1,499 @@
+/* $NetBSD: util.c,v 1.17 2013/01/21 03:24:43 msaitoh Exp $ */
+/* $FreeBSD: head/usr.bin/grep/util.c 211496 2010-08-19 09:28:59Z des $ */
+/* $OpenBSD: util.c,v 1.39 2010/07/02 22:18:03 tedu Exp $ */
+
+/*-
+ * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
+ * Copyright (C) 2008-2010 Gabor Kovesdan <gabor@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: util.c,v 1.17 2013/01/21 03:24:43 msaitoh Exp $");
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fnmatch.h>
+#include <fts.h>
+#include <libgen.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#include "grep.h"
+
+static bool first, first_global = true;
+static unsigned long long since_printed;
+
+static int procline(struct str *l, int);
+
+bool
+file_matching(const char *fname)
+{
+ char *fname_base, *fname_copy;
+ unsigned int i;
+ bool ret;
+
+ ret = finclude ? false : true;
+ fname_copy = grep_strdup(fname);
+ fname_base = basename(fname_copy);
+
+ for (i = 0; i < fpatterns; ++i) {
+ if (fnmatch(fpattern[i].pat, fname, 0) == 0 ||
+ fnmatch(fpattern[i].pat, fname_base, 0) == 0) {
+ if (fpattern[i].mode == EXCL_PAT) {
+ free(fname_copy);
+ return (false);
+ } else
+ ret = true;
+ }
+ }
+ free(fname_copy);
+ return (ret);
+}
+
+static inline bool
+dir_matching(const char *dname)
+{
+ unsigned int i;
+ bool ret;
+
+ ret = dinclude ? false : true;
+
+ for (i = 0; i < dpatterns; ++i) {
+ if (dname != NULL &&
+ fnmatch(dname, dpattern[i].pat, 0) == 0) {
+ if (dpattern[i].mode == EXCL_PAT)
+ return (false);
+ else
+ ret = true;
+ }
+ }
+ return (ret);
+}
+
+/*
+ * Processes a directory when a recursive search is performed with
+ * the -R option. Each appropriate file is passed to procfile().
+ */
+int
+grep_tree(char **argv)
+{
+ FTS *fts;
+ FTSENT *p;
+ char *d, *dir = NULL;
+ int c, fts_flags;
+ bool ok;
+
+ c = fts_flags = 0;
+
+ switch(linkbehave) {
+ case LINK_EXPLICIT:
+ fts_flags = FTS_COMFOLLOW;
+ break;
+ case LINK_SKIP:
+ fts_flags = FTS_PHYSICAL;
+ break;
+ default:
+ fts_flags = FTS_LOGICAL;
+
+ }
+
+ fts_flags |= FTS_NOSTAT | FTS_NOCHDIR;
+
+ if (!(fts = fts_open(argv, fts_flags, NULL)))
+ err(2, "fts_open");
+ while ((p = fts_read(fts)) != NULL) {
+ switch (p->fts_info) {
+ case FTS_DNR:
+ /* FALLTHROUGH */
+ case FTS_ERR:
+ errx(2, "%s: %s", p->fts_path, strerror(p->fts_errno));
+ break;
+ case FTS_D:
+ /* FALLTHROUGH */
+ case FTS_DP:
+ break;
+ case FTS_DC:
+ /* Print a warning for recursive directory loop */
+ warnx("warning: %s: recursive directory loop",
+ p->fts_path);
+ break;
+ default:
+ /* Check for file exclusion/inclusion */
+ ok = true;
+ if (dexclude || dinclude) {
+ if ((d = strrchr(p->fts_path, '/')) != NULL) {
+ dir = grep_malloc(sizeof(char) *
+ (d - p->fts_path + 1));
+ memcpy(dir, p->fts_path,
+ d - p->fts_path);
+ dir[d - p->fts_path] = '\0';
+ }
+ ok = dir_matching(dir);
+ free(dir);
+ dir = NULL;
+ }
+ if (fexclude || finclude)
+ ok &= file_matching(p->fts_path);
+
+ if (ok)
+ c += procfile(p->fts_path);
+ break;
+ }
+ }
+
+ fts_close(fts);
+ return (c);
+}
+
+/*
+ * Opens a file and processes it. Each file is processed line-by-line
+ * passing the lines to procline().
+ */
+int
+procfile(const char *fn)
+{
+ struct file *f;
+ struct stat sb;
+ struct str ln;
+ mode_t s;
+ int c, t;
+
+ if (mflag && (mcount <= 0))
+ return (0);
+
+ if (strcmp(fn, "-") == 0) {
+ fn = label != NULL ? label : getstr(1);
+ f = grep_open(NULL);
+ } else {
+ if (!stat(fn, &sb)) {
+ /* Check if we need to process the file */
+ s = sb.st_mode & S_IFMT;
+ if (s == S_IFDIR && dirbehave == DIR_SKIP)
+ return (0);
+ if ((s == S_IFIFO || s == S_IFCHR || s == S_IFBLK
+ || s == S_IFSOCK) && devbehave == DEV_SKIP)
+ return (0);
+ }
+ f = grep_open(fn);
+ }
+ if (f == NULL) {
+ if (!sflag)
+ warn("%s", fn);
+ if (errno == ENOENT)
+ notfound = true;
+ return (0);
+ }
+
+ ln.file = grep_malloc(strlen(fn) + 1);
+ strcpy(ln.file, fn);
+ ln.line_no = 0;
+ ln.len = 0;
+ tail = 0;
+ ln.off = -1;
+
+ for (first = true, c = 0; c == 0 || !(lflag || qflag); ) {
+ ln.off += ln.len + 1;
+ if ((ln.dat = grep_fgetln(f, &ln.len)) == NULL || ln.len == 0)
+ break;
+ if (ln.len > 0 && ln.dat[ln.len - 1] == line_sep)
+ --ln.len;
+ ln.line_no++;
+
+ /* Return if we need to skip a binary file */
+ if (f->binary && binbehave == BINFILE_SKIP) {
+ grep_close(f);
+ free(ln.file);
+ free(f);
+ return (0);
+ }
+ /* Process the file line-by-line */
+ t = procline(&ln, f->binary);
+ c += t;
+
+ /* Count the matches if we have a match limit */
+ if (mflag) {
+ mcount -= t;
+ if (mcount <= 0)
+ break;
+ }
+ }
+ if (Bflag > 0)
+ clearqueue();
+ grep_close(f);
+
+ if (cflag) {
+ if (!hflag)
+ printf("%s:", ln.file);
+ printf("%u%c", c, line_sep);
+ }
+ if (lflag && !qflag && c != 0)
+ printf("%s%c", fn, line_sep);
+ if (Lflag && !qflag && c == 0)
+ printf("%s%c", fn, line_sep);
+ if (c && !cflag && !lflag && !Lflag &&
+ binbehave == BINFILE_BIN && f->binary && !qflag)
+ printf(getstr(8), fn);
+
+ free(ln.file);
+ free(f);
+ return (c);
+}
+
+#define iswword(x) (iswalnum((x)) || (x) == L'_')
+
+/*
+ * Processes a line comparing it with the specified patterns. Each pattern
+ * is looped to be compared along with the full string, saving each and every
+ * match, which is necessary to colorize the output and to count the
+ * matches. The matching lines are passed to printline() to display the
+ * appropriate output.
+ */
+static int
+procline(struct str *l, int nottext)
+{
+ regmatch_t matches[MAX_LINE_MATCHES];
+ regmatch_t pmatch;
+ size_t st = 0;
+ unsigned int i;
+ int c = 0, m = 0, r = 0;
+
+ /* Loop to process the whole line */
+ while (st <= l->len) {
+ pmatch.rm_so = st;
+ pmatch.rm_eo = l->len;
+
+ /* Loop to compare with all the patterns */
+ for (i = 0; i < patterns; i++) {
+/*
+ * XXX: grep_search() is a workaround for speed up and should be
+ * removed in the future. See fastgrep.c.
+ */
+ if (fg_pattern[i].pattern) {
+ r = grep_search(&fg_pattern[i],
+ (unsigned char *)l->dat,
+ l->len, &pmatch);
+ r = (r == 0) ? 0 : REG_NOMATCH;
+ st = pmatch.rm_eo;
+ } else {
+ r = regexec(&r_pattern[i], l->dat, 1,
+ &pmatch, eflags);
+ r = (r == 0) ? 0 : REG_NOMATCH;
+ st = pmatch.rm_eo;
+ }
+ if (r == REG_NOMATCH)
+ continue;
+ /* Check for full match */
+ if (xflag &&
+ (pmatch.rm_so != 0 ||
+ (size_t)pmatch.rm_eo != l->len))
+ continue;
+ /* Check for whole word match */
+ if (fg_pattern[i].word && pmatch.rm_so != 0) {
+ wchar_t wbegin, wend;
+
+ wbegin = wend = L' ';
+ if (pmatch.rm_so != 0 &&
+ sscanf(&l->dat[pmatch.rm_so - 1],
+ "%lc", &wbegin) != 1)
+ continue;
+ if ((size_t)pmatch.rm_eo != l->len &&
+ sscanf(&l->dat[pmatch.rm_eo],
+ "%lc", &wend) != 1)
+ continue;
+ if (iswword(wbegin) || iswword(wend))
+ continue;
+ }
+ c = 1;
+ if (m < MAX_LINE_MATCHES)
+ matches[m++] = pmatch;
+ /* matches - skip further patterns */
+ if ((color != NULL && !oflag) || qflag || lflag)
+ break;
+ }
+
+ if (vflag) {
+ c = !c;
+ break;
+ }
+ /* One pass if we are not recording matches */
+ if ((color != NULL && !oflag) || qflag || lflag)
+ break;
+
+ if (st == (size_t)pmatch.rm_so)
+ break; /* No matches */
+ }
+
+ if (c && binbehave == BINFILE_BIN && nottext)
+ return (c); /* Binary file */
+
+ /* Dealing with the context */
+ if ((tail || c) && !cflag && !qflag && !lflag && !Lflag) {
+ if (c) {
+ if ((Aflag || Bflag) && !first_global &&
+ (first || since_printed > Bflag))
+ printf("--\n");
+ tail = Aflag;
+ if (Bflag > 0)
+ printqueue();
+ printline(l, ':', matches, m);
+ } else {
+ printline(l, '-', matches, m);
+ tail--;
+ }
+ first = false;
+ first_global = false;
+ since_printed = 0;
+ } else {
+ if (Bflag)
+ enqueue(l);
+ since_printed++;
+ }
+ return (c);
+}
+
+/*
+ * Safe malloc() for internal use.
+ */
+void *
+grep_malloc(size_t size)
+{
+ void *ptr;
+
+ if ((ptr = malloc(size)) == NULL)
+ err(2, "malloc");
+ return (ptr);
+}
+
+/*
+ * Safe calloc() for internal use.
+ */
+void *
+grep_calloc(size_t nmemb, size_t size)
+{
+ void *ptr;
+
+ if ((ptr = calloc(nmemb, size)) == NULL)
+ err(2, "calloc");
+ return (ptr);
+}
+
+/*
+ * Safe realloc() for internal use.
+ */
+void *
+grep_realloc(void *ptr, size_t size)
+{
+
+ if ((ptr = realloc(ptr, size)) == NULL)
+ err(2, "realloc");
+ return (ptr);
+}
+
+/*
+ * Safe strdup() for internal use.
+ */
+char *
+grep_strdup(const char *str)
+{
+ char *ret;
+
+ if ((ret = strdup(str)) == NULL)
+ err(2, "strdup");
+ return (ret);
+}
+
+/*
+ * Prints a matching line according to the command line options.
+ */
+void
+printline(struct str *line, int sep, regmatch_t *matches, int m)
+{
+ size_t a = 0;
+ int i, n = 0;
+
+ if (!hflag) {
+ if (nullflag == 0)
+ fputs(line->file, stdout);
+ else {
+ printf("%s", line->file);
+ putchar(0);
+ }
+ ++n;
+ }
+ if (nflag) {
+ if (n > 0)
+ putchar(sep);
+ printf("%d", line->line_no);
+ ++n;
+ }
+ if (bflag) {
+ if (n > 0)
+ putchar(sep);
+ printf("%lld", (long long)line->off);
+ ++n;
+ }
+ if (n)
+ putchar(sep);
+ /* --color and -o */
+ if ((oflag || color) && m > 0) {
+ for (i = 0; i < m; i++) {
+ if (!oflag)
+ fwrite(line->dat + a, matches[i].rm_so - a, 1,
+ stdout);
+ if (color)
+ fprintf(stdout, "\33[%sm\33[K", color);
+
+ fwrite(line->dat + matches[i].rm_so,
+ matches[i].rm_eo - matches[i].rm_so, 1,
+ stdout);
+ if (color)
+ fprintf(stdout, "\33[m\33[K");
+ a = matches[i].rm_eo;
+ if (oflag)
+ putchar('\n');
+ }
+ if (!oflag) {
+ if (line->len - a > 0)
+ fwrite(line->dat + a, line->len - a, 1, stdout);
+ putchar(line_sep);
+ }
+ } else {
+ fwrite(line->dat, line->len, 1, stdout);
+ putchar(line_sep);
+ }
+}
diff --git a/toolbox/uptime.c b/toolbox/uptime.c
new file mode 100644
index 0000000..ebfb15e
--- /dev/null
+++ b/toolbox/uptime.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2010, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+static void format_time(int time, char* buffer) {
+ int seconds = time % 60;
+ time /= 60;
+ int minutes = time % 60;
+ time /= 60;
+ int hours = time % 24;
+ int days = time / 24;
+
+ if (days > 0) {
+ sprintf(buffer, "%d day%s, %02d:%02d:%02d", days, (days == 1) ? "" : "s", hours, minutes, seconds);
+ } else {
+ sprintf(buffer, "%02d:%02d:%02d", hours, minutes, seconds);
+ }
+}
+
+int uptime_main(int argc __attribute__((unused)), char *argv[] __attribute__((unused))) {
+ FILE* file = fopen("/proc/uptime", "r");
+ if (!file) {
+ fprintf(stderr, "Could not open /proc/uptime\n");
+ return -1;
+ }
+ float idle_time;
+ if (fscanf(file, "%*f %f", &idle_time) != 1) {
+ fprintf(stderr, "Could not parse /proc/uptime\n");
+ fclose(file);
+ return -1;
+ }
+ fclose(file);
+
+ struct timespec up_timespec;
+ if (clock_gettime(CLOCK_MONOTONIC, &up_timespec) == -1) {
+ fprintf(stderr, "Could not get monotonic time: %s\n", strerror(errno));
+ return -1;
+ }
+ float up_time = up_timespec.tv_sec + up_timespec.tv_nsec / 1e9;
+
+ struct timespec elapsed_timespec;
+ if (clock_gettime(CLOCK_BOOTTIME, &elapsed_timespec) == -1) {
+ fprintf(stderr, "Could not get boot time: %s\n", strerror(errno));
+ return -1;
+ }
+ int elapsed = elapsed_timespec.tv_sec;
+
+ char up_string[100], idle_string[100], sleep_string[100];
+ format_time(elapsed, up_string);
+ format_time((int)idle_time, idle_string);
+ format_time((int)(elapsed - up_time), sleep_string);
+ printf("up time: %s, idle time: %s, sleep time: %s\n", up_string, idle_string, sleep_string);
+
+ return 0;
+}
diff --git a/toolbox/watchprops.c b/toolbox/watchprops.c
new file mode 100644
index 0000000..cd62922
--- /dev/null
+++ b/toolbox/watchprops.c
@@ -0,0 +1,92 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+
+#include <cutils/properties.h>
+#include <cutils/hashmap.h>
+
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+
+static int str_hash(void *key)
+{
+ return hashmapHash(key, strlen(key));
+}
+
+static bool str_equals(void *keyA, void *keyB)
+{
+ return strcmp(keyA, keyB) == 0;
+}
+
+static void announce(char *name, char *value)
+{
+ unsigned char *x;
+
+ for(x = (unsigned char *)value; *x; x++) {
+ if((*x < 32) || (*x > 127)) *x = '.';
+ }
+
+ fprintf(stderr,"%10d %s = '%s'\n", (int) time(0), name, value);
+}
+
+static void add_to_watchlist(Hashmap *watchlist, const char *name,
+ const prop_info *pi)
+{
+ char *key = strdup(name);
+ unsigned *value = malloc(sizeof(unsigned));
+ if (!key || !value)
+ exit(1);
+
+ *value = __system_property_serial(pi);
+ hashmapPut(watchlist, key, value);
+}
+
+static void populate_watchlist(const prop_info *pi, void *cookie)
+{
+ Hashmap *watchlist = cookie;
+ char name[PROP_NAME_MAX];
+ char value_unused[PROP_VALUE_MAX];
+
+ __system_property_read(pi, name, value_unused);
+ add_to_watchlist(watchlist, name, pi);
+}
+
+static void update_watchlist(const prop_info *pi, void *cookie)
+{
+ Hashmap *watchlist = cookie;
+ char name[PROP_NAME_MAX];
+ char value[PROP_VALUE_MAX];
+ unsigned *serial;
+
+ __system_property_read(pi, name, value);
+ serial = hashmapGet(watchlist, name);
+ if (!serial) {
+ add_to_watchlist(watchlist, name, pi);
+ announce(name, value);
+ } else {
+ unsigned tmp = __system_property_serial(pi);
+ if (*serial != tmp) {
+ *serial = tmp;
+ announce(name, value);
+ }
+ }
+}
+
+int watchprops_main(int argc, char *argv[])
+{
+ unsigned serial;
+
+ Hashmap *watchlist = hashmapCreate(1024, str_hash, str_equals);
+ if (!watchlist)
+ exit(1);
+
+ __system_property_foreach(populate_watchlist, watchlist);
+
+ for(serial = 0;;) {
+ serial = __system_property_wait_any(serial);
+ __system_property_foreach(update_watchlist, watchlist);
+ }
+ return 0;
+}
diff --git a/tzdatacheck/Android.mk b/tzdatacheck/Android.mk
new file mode 100644
index 0000000..0e25f7d
--- /dev/null
+++ b/tzdatacheck/Android.mk
@@ -0,0 +1,21 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# ========================================================
+# Executable
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES:= tzdatacheck.cpp
+LOCAL_MODULE := tzdatacheck
+LOCAL_SHARED_LIBRARIES := libbase libcutils liblog
+LOCAL_CFLAGS := -Werror
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES:= tzdatacheck.cpp
+LOCAL_MODULE := tzdatacheck
+LOCAL_SHARED_LIBRARIES := libbase libcutils liblog
+LOCAL_CFLAGS := -Werror
+include $(BUILD_HOST_EXECUTABLE)
+
diff --git a/tzdatacheck/tzdatacheck.cpp b/tzdatacheck/tzdatacheck.cpp
new file mode 100644
index 0000000..31f7b55
--- /dev/null
+++ b/tzdatacheck/tzdatacheck.cpp
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <ftw.h>
+#include <libgen.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/logging.h"
+
+static const char* TZDATA_FILENAME = "/tzdata";
+// tzdata file header (as much as we need for the version):
+// byte[11] tzdata_version -- e.g. "tzdata2012f"
+static const int TZ_HEADER_LENGTH = 11;
+
+static void usage() {
+ std::cerr << "Usage: tzdatacheck SYSTEM_TZ_DIR DATA_TZ_DIR\n"
+ "\n"
+ "Compares the headers of two tzdata files. If the one in SYSTEM_TZ_DIR is the\n"
+ "same or a higher version than the one in DATA_TZ_DIR the DATA_TZ_DIR is renamed\n"
+ "and then deleted.\n";
+ exit(1);
+}
+
+/*
+ * Opens a file and fills headerBytes with the first byteCount bytes from the file. It is a fatal
+ * error if the file is too small or cannot be opened. If the file does not exist false is returned.
+ * If the bytes were read successfully then true is returned.
+ */
+static bool readHeader(const std::string& tzDataFileName, char* headerBytes, size_t byteCount) {
+ FILE* tzDataFile = fopen(tzDataFileName.c_str(), "r");
+ if (tzDataFile == nullptr) {
+ if (errno == ENOENT) {
+ return false;
+ } else {
+ PLOG(FATAL) << "Error opening tzdata file " << tzDataFileName;
+ }
+ }
+ size_t bytesRead = fread(headerBytes, 1, byteCount, tzDataFile);
+ if (bytesRead != byteCount) {
+ LOG(FATAL) << tzDataFileName << " is too small. " << byteCount << " bytes required";
+ }
+ fclose(tzDataFile);
+ return true;
+}
+
+/* Checks the contents of headerBytes. It is a fatal error if it not a tzdata header. */
+static void checkValidHeader(const std::string& fileName, char* headerBytes) {
+ if (strncmp("tzdata", headerBytes, 6) != 0) {
+ LOG(FATAL) << fileName << " does not start with the expected bytes (tzdata)";
+ }
+}
+
+/* Return the parent directory of dirName. */
+static std::string getParentDir(const std::string& dirName) {
+ std::unique_ptr<char> mutable_dirname(strdup(dirName.c_str()));
+ return dirname(mutable_dirname.get());
+}
+
+/* Deletes a single file, symlink or directory. Called from nftw(). */
+static int deleteFn(const char* fpath, const struct stat*, int typeflag, struct FTW*) {
+ LOG(DEBUG) << "Inspecting " << fpath;
+ switch (typeflag) {
+ case FTW_F:
+ case FTW_SL:
+ LOG(DEBUG) << "Unlinking " << fpath;
+ if (unlink(fpath)) {
+ PLOG(WARNING) << "Failed to unlink file/symlink " << fpath;
+ }
+ break;
+ case FTW_D:
+ case FTW_DP:
+ LOG(DEBUG) << "Removing dir " << fpath;
+ if (rmdir(fpath)) {
+ PLOG(WARNING) << "Failed to remove dir " << fpath;
+ }
+ break;
+ default:
+ LOG(WARNING) << "Unsupported file type " << fpath << ": " << typeflag;
+ break;
+ }
+ return 0;
+}
+
+/*
+ * Deletes dirToDelete and returns true if it is successful in removing or moving the directory out
+ * of the way. If dirToDelete does not exist this function does nothing and returns true.
+ *
+ * During deletion, this function first renames the directory to a temporary name. If the temporary
+ * directory cannot be created, or the directory cannot be renamed, false is returned. After the
+ * rename, deletion of files and subdirs beneath the directory is performed on a "best effort"
+ * basis. Symlinks beneath the directory are not followed.
+ */
+static bool deleteDir(const std::string& dirToDelete) {
+ // Check whether the dir exists.
+ struct stat buf;
+ if (stat(dirToDelete.c_str(), &buf) == 0) {
+ if (!S_ISDIR(buf.st_mode)) {
+ LOG(WARNING) << dirToDelete << " is not a directory";
+ return false;
+ }
+ } else {
+ if (errno == ENOENT) {
+ PLOG(INFO) << "Directory does not exist: " << dirToDelete;
+ return true;
+ } else {
+ PLOG(WARNING) << "Unable to stat " << dirToDelete;
+ return false;
+ }
+ }
+
+ // First, rename dirToDelete.
+ std::string tempDirNameTemplate = getParentDir(dirToDelete);
+ tempDirNameTemplate += "/tempXXXXXX";
+
+ // Create an empty directory with the temporary name. For this we need a non-const char*.
+ std::vector<char> tempDirName(tempDirNameTemplate.length() + 1);
+ strcpy(&tempDirName[0], tempDirNameTemplate.c_str());
+ if (mkdtemp(&tempDirName[0]) == nullptr) {
+ PLOG(WARNING) << "Unable to create a temporary directory: " << tempDirNameTemplate;
+ return false;
+ }
+
+ // Rename dirToDelete to tempDirName.
+ int rc = rename(dirToDelete.c_str(), &tempDirName[0]);
+ if (rc == -1) {
+ PLOG(WARNING) << "Unable to rename directory from " << dirToDelete << " to "
+ << &tempDirName[0];
+ return false;
+ }
+
+ // Recursively delete contents of tempDirName.
+ rc = nftw(&tempDirName[0], deleteFn, 10 /* openFiles */,
+ FTW_DEPTH | FTW_MOUNT | FTW_PHYS);
+ if (rc == -1) {
+ LOG(INFO) << "Could not delete directory: " << &tempDirName[0];
+ }
+ return true;
+}
+
+/*
+ * After a platform update it is likely that timezone data found on the system partition will be
+ * newer than the version found in the data partition. This tool detects this case and removes the
+ * version in /data along with any update metadata.
+ *
+ * Note: This code is related to code in com.android.server.updates.TzDataInstallReceiver. The
+ * paths for the metadata and current timezone data must match.
+ *
+ * Typically on device the two args will be:
+ * /system/usr/share/zoneinfo /data/misc/zoneinfo
+ *
+ * See usage() for usage notes.
+ */
+int main(int argc, char* argv[]) {
+ if (argc != 3) {
+ usage();
+ }
+
+ const char* systemZoneInfoDir = argv[1];
+ const char* dataZoneInfoDir = argv[2];
+
+ std::string dataCurrentDirName(dataZoneInfoDir);
+ dataCurrentDirName += "/current";
+ std::string dataTzDataFileName(dataCurrentDirName);
+ dataTzDataFileName += TZDATA_FILENAME;
+
+ std::vector<char> dataTzDataHeader;
+ dataTzDataHeader.reserve(TZ_HEADER_LENGTH);
+
+ bool dataFileExists = readHeader(dataTzDataFileName, dataTzDataHeader.data(), TZ_HEADER_LENGTH);
+ if (!dataFileExists) {
+ LOG(INFO) << "tzdata file " << dataTzDataFileName << " does not exist. No action required.";
+ return 0;
+ }
+ checkValidHeader(dataTzDataFileName, dataTzDataHeader.data());
+
+ std::string systemTzDataFileName(systemZoneInfoDir);
+ systemTzDataFileName += TZDATA_FILENAME;
+ std::vector<char> systemTzDataHeader;
+ systemTzDataHeader.reserve(TZ_HEADER_LENGTH);
+ bool systemFileExists =
+ readHeader(systemTzDataFileName, systemTzDataHeader.data(), TZ_HEADER_LENGTH);
+ if (!systemFileExists) {
+ LOG(FATAL) << systemTzDataFileName << " does not exist or could not be opened";
+ }
+ checkValidHeader(systemTzDataFileName, systemTzDataHeader.data());
+
+ if (strncmp(&systemTzDataHeader[0], &dataTzDataHeader[0], TZ_HEADER_LENGTH) < 0) {
+ LOG(INFO) << "tzdata file " << dataTzDataFileName << " is the newer than "
+ << systemTzDataFileName << ". No action required.";
+ } else {
+ // We have detected the case this tool is intended to prevent. Go fix it.
+ LOG(INFO) << "tzdata file " << dataTzDataFileName << " is the same as or older than "
+ << systemTzDataFileName << "; fixing...";
+
+ // Delete the update metadata
+ std::string dataUpdatesDirName(dataZoneInfoDir);
+ dataUpdatesDirName += "/updates";
+ LOG(INFO) << "Removing: " << dataUpdatesDirName;
+ bool deleted = deleteDir(dataUpdatesDirName);
+ if (!deleted) {
+ LOG(WARNING) << "Deletion of install metadata " << dataUpdatesDirName
+ << " was not successful";
+ }
+
+ // Delete the TZ data
+ LOG(INFO) << "Removing: " << dataCurrentDirName;
+ deleted = deleteDir(dataCurrentDirName);
+ if (!deleted) {
+ LOG(WARNING) << "Deletion of tzdata " << dataCurrentDirName << " was not successful";
+ }
+ }
+
+ return 0;
+}