Merge "Add append2simg."
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/Android.mk b/adb/Android.mk
index 36f595b..721b48d 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -130,7 +130,7 @@
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN)
LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED)
-LOCAL_STATIC_LIBRARIES := libcutils libc libmincrypt
+LOCAL_STATIC_LIBRARIES := liblog libcutils libc libmincrypt
include $(BUILD_EXECUTABLE)
diff --git a/adb/log_service.c b/adb/log_service.c
index 6e9bdee..af24356 100644
--- a/adb/log_service.c
+++ b/adb/log_service.c
@@ -22,7 +22,7 @@
#include <fcntl.h>
#include <errno.h>
#include <sys/socket.h>
-#include <cutils/logger.h>
+#include <log/logger.h>
#include "sysdeps.h"
#include "adb.h"
diff --git a/adb/usb_vendors.c b/adb/usb_vendors.c
index e132c67..19b3022 100644
--- a/adb/usb_vendors.c
+++ b/adb/usb_vendors.c
@@ -153,7 +153,12 @@
#define VENDOR_ID_NOOK 0x2080
// Qisda's USB Vendor ID
#define VENDOR_ID_QISDA 0x1D45
-
+// ECS's USB Vendor ID
+#define VENDOR_ID_ECS 0x03fc
+// MSI's USB Vendor ID
+#define VENDOR_ID_MSI 0x0DB0
+// Wacom's USB Vendor ID
+#define VENDOR_ID_WACOM 0x0531
/** built-in vendor list */
int builtInVendorIds[] = {
@@ -216,6 +221,9 @@
VENDOR_ID_MTK,
VENDOR_ID_NOOK,
VENDOR_ID_QISDA,
+ VENDOR_ID_ECS,
+ VENDOR_ID_MSI,
+ VENDOR_ID_WACOM,
};
#define BUILT_IN_VENDOR_COUNT (sizeof(builtInVendorIds)/sizeof(builtInVendorIds[0]))
diff --git a/charger/Android.mk b/charger/Android.mk
deleted file mode 100644
index b9d3473..0000000
--- a/charger/Android.mk
+++ /dev/null
@@ -1,61 +0,0 @@
-# Copyright 2011 The Android Open Source Project
-
-ifneq ($(BUILD_TINY_ANDROID),true)
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
- charger.c
-
-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_MODULE := charger
-LOCAL_MODULE_TAGS := optional
-LOCAL_FORCE_STATIC_EXECUTABLE := true
-LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
-LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)
-
-LOCAL_C_INCLUDES := bootable/recovery
-
-LOCAL_STATIC_LIBRARIES := libminui libpixelflinger_static libpng
-ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
-LOCAL_STATIC_LIBRARIES += libsuspend
-endif
-LOCAL_STATIC_LIBRARIES += libz libstdc++ libcutils liblog libm libc
-
-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 :=
-
-endif
diff --git a/charger/charger.c b/charger/charger.c
deleted file mode 100644
index 66ddeaf..0000000
--- a/charger/charger.c
+++ /dev/null
@@ -1,1015 +0,0 @@
-/*
- * 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 DEBUG_UEVENTS
-#define CHARGER_KLOG_LEVEL 6
-
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <linux/input.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/poll.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 <cutils/android_reboot.h>
-#include <cutils/klog.h>
-#include <cutils/list.h>
-#include <cutils/misc.h>
-#include <cutils/uevent.h>
-
-#ifdef CHARGER_ENABLE_SUSPEND
-#include <suspend/autosuspend.h>
-#endif
-
-#include "minui/minui.h"
-
-#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_MAX_SZ (32 * 1024)
-
-#define LOGE(x...) do { KLOG_ERROR("charger", x); } while (0)
-#define LOGI(x...) do { KLOG_INFO("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 power_supply {
- struct listnode list;
- char name[256];
- char type[32];
- bool online;
- bool valid;
- char cap_path[PATH_MAX];
-};
-
-struct frame {
- const char *name;
- int disp_time;
- int min_capacity;
- bool level_only;
-
- gr_surface 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 {
- int64_t next_screen_transition;
- int64_t next_key_check;
- int64_t next_pwr_check;
-
- struct key_state keys[KEY_MAX + 1];
- int uevent_fd;
-
- struct listnode supplies;
- int num_supplies;
- int num_supplies_online;
-
- struct animation *batt_anim;
- gr_surface surf_unknown;
-
- struct power_supply *battery;
-};
-
-struct uevent {
- const char *action;
- const char *path;
- const char *subsystem;
- const char *ps_name;
- const char *ps_type;
- const char *ps_online;
-};
-
-static struct frame batt_anim_frames[] = {
- {
- .name = "charger/battery_0",
- .disp_time = 750,
- .min_capacity = 0,
- },
- {
- .name = "charger/battery_1",
- .disp_time = 750,
- .min_capacity = 20,
- },
- {
- .name = "charger/battery_2",
- .disp_time = 750,
- .min_capacity = 40,
- },
- {
- .name = "charger/battery_3",
- .disp_time = 750,
- .min_capacity = 60,
- },
- {
- .name = "charger/battery_4",
- .disp_time = 750,
- .min_capacity = 80,
- .level_only = true,
- },
- {
- .name = "charger/battery_5",
- .disp_time = 750,
- .min_capacity = BATTERY_FULL_THRESH,
- },
-};
-
-static struct animation battery_animation = {
- .frames = batt_anim_frames,
- .num_frames = ARRAY_SIZE(batt_anim_frames),
- .num_cycles = 3,
-};
-
-static struct charger charger_state = {
- .batt_anim = &battery_animation,
-};
-
-static int char_width;
-static int char_height;
-
-/* 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_fill(0, 0, gr_fb_width(), gr_fb_height());
-};
-
-#define MAX_KLOG_WRITE_BUF_SZ 256
-
-static void dump_last_kmsg(void)
-{
- char *buf;
- char *ptr;
- unsigned sz = 0;
- int len;
-
- LOGI("\n");
- LOGI("*************** LAST KMSG ***************\n");
- LOGI("\n");
- buf = load_file(LAST_KMSG_PATH, &sz);
- if (!buf || !sz) {
- LOGI("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 = memrchr(ptr, '\n', cnt - 1);
- if (nl)
- cnt = nl - ptr + 1;
-
- yoink = ptr[cnt];
- ptr[cnt] = '\0';
- klog_write(6, "<6>%s", ptr);
- ptr[cnt] = yoink;
-
- len -= cnt;
- ptr += cnt;
- }
-
- free(buf);
-
-out:
- LOGI("\n");
- LOGI("************* END LAST KMSG *************\n");
- LOGI("\n");
-}
-
-static int read_file(const char *path, char *buf, size_t sz)
-{
- int fd;
- size_t cnt;
-
- fd = open(path, O_RDONLY, 0);
- if (fd < 0)
- goto err;
-
- cnt = read(fd, buf, sz - 1);
- if (cnt <= 0)
- goto err;
- buf[cnt] = '\0';
- if (buf[cnt - 1] == '\n') {
- cnt--;
- buf[cnt] = '\0';
- }
-
- close(fd);
- return cnt;
-
-err:
- if (fd >= 0)
- close(fd);
- return -1;
-}
-
-static int read_file_int(const char *path, int *val)
-{
- char buf[32];
- int ret;
- int tmp;
- char *end;
-
- ret = read_file(path, buf, sizeof(buf));
- if (ret < 0)
- return -1;
-
- tmp = strtol(buf, &end, 0);
- if (end == buf ||
- ((end < buf+sizeof(buf)) && (*end != '\n' && *end != '\0')))
- goto err;
-
- *val = tmp;
- return 0;
-
-err:
- return -1;
-}
-
-static int get_battery_capacity(struct charger *charger)
-{
- int ret;
- int batt_cap = -1;
-
- if (!charger->battery)
- return -1;
-
- ret = read_file_int(charger->battery->cap_path, &batt_cap);
- if (ret < 0 || batt_cap > 100) {
- batt_cap = -1;
- }
-
- return batt_cap;
-}
-
-static struct power_supply *find_supply(struct charger *charger,
- const char *name)
-{
- struct listnode *node;
- struct power_supply *supply;
-
- list_for_each(node, &charger->supplies) {
- supply = node_to_item(node, struct power_supply, list);
- if (!strncmp(name, supply->name, sizeof(supply->name)))
- return supply;
- }
- return NULL;
-}
-
-static struct power_supply *add_supply(struct charger *charger,
- const char *name, const char *type,
- const char *path, bool online)
-{
- struct power_supply *supply;
-
- supply = calloc(1, sizeof(struct power_supply));
- if (!supply)
- return NULL;
-
- strlcpy(supply->name, name, sizeof(supply->name));
- strlcpy(supply->type, type, sizeof(supply->type));
- snprintf(supply->cap_path, sizeof(supply->cap_path),
- "/sys/%s/capacity", path);
- supply->online = online;
- list_add_tail(&charger->supplies, &supply->list);
- charger->num_supplies++;
- LOGV("... added %s %s %d\n", supply->name, supply->type, online);
- return supply;
-}
-
-static void remove_supply(struct charger *charger, struct power_supply *supply)
-{
- if (!supply)
- return;
- list_remove(&supply->list);
- charger->num_supplies--;
- free(supply);
-}
-
-#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 void parse_uevent(const char *msg, struct uevent *uevent)
-{
- uevent->action = "";
- uevent->path = "";
- uevent->subsystem = "";
- uevent->ps_name = "";
- uevent->ps_online = "";
- uevent->ps_type = "";
-
- /* currently ignoring SEQNUM */
- while (*msg) {
-#ifdef DEBUG_UEVENTS
- LOGV("uevent str: %s\n", msg);
-#endif
- 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, "POWER_SUPPLY_NAME=", 18)) {
- msg += 18;
- uevent->ps_name = msg;
- } else if (!strncmp(msg, "POWER_SUPPLY_ONLINE=", 20)) {
- msg += 20;
- uevent->ps_online = msg;
- } else if (!strncmp(msg, "POWER_SUPPLY_TYPE=", 18)) {
- msg += 18;
- uevent->ps_type = msg;
- }
-
- /* advance to after the next \0 */
- while (*msg++)
- ;
- }
-
- LOGV("event { '%s', '%s', '%s', '%s', '%s', '%s' }\n",
- uevent->action, uevent->path, uevent->subsystem,
- uevent->ps_name, uevent->ps_type, uevent->ps_online);
-}
-
-static void process_ps_uevent(struct charger *charger, struct uevent *uevent)
-{
- int online;
- char ps_type[32];
- struct power_supply *supply = NULL;
- int i;
- bool was_online = false;
- bool battery = false;
-
- if (uevent->ps_type[0] == '\0') {
- char *path;
- int ret;
-
- if (uevent->path[0] == '\0')
- return;
- ret = asprintf(&path, "/sys/%s/type", uevent->path);
- if (ret <= 0)
- return;
- ret = read_file(path, ps_type, sizeof(ps_type));
- free(path);
- if (ret < 0)
- return;
- } else {
- strlcpy(ps_type, uevent->ps_type, sizeof(ps_type));
- }
-
- if (!strncmp(ps_type, "Battery", 7))
- battery = true;
-
- online = atoi(uevent->ps_online);
- supply = find_supply(charger, uevent->ps_name);
- if (supply) {
- was_online = supply->online;
- supply->online = online;
- }
-
- if (!strcmp(uevent->action, "add")) {
- if (!supply) {
- supply = add_supply(charger, uevent->ps_name, ps_type, uevent->path,
- online);
- if (!supply) {
- LOGE("cannot add supply '%s' (%s %d)\n", uevent->ps_name,
- uevent->ps_type, online);
- return;
- }
- /* only pick up the first battery for now */
- if (battery && !charger->battery)
- charger->battery = supply;
- } else {
- LOGE("supply '%s' already exists..\n", uevent->ps_name);
- }
- } else if (!strcmp(uevent->action, "remove")) {
- if (supply) {
- if (charger->battery == supply)
- charger->battery = NULL;
- remove_supply(charger, supply);
- supply = NULL;
- }
- } else if (!strcmp(uevent->action, "change")) {
- if (!supply) {
- LOGE("power supply '%s' not found ('%s' %d)\n",
- uevent->ps_name, ps_type, online);
- return;
- }
- } else {
- return;
- }
-
- /* allow battery to be managed in the supply list but make it not
- * contribute to online power supplies. */
- if (!battery) {
- if (was_online && !online)
- charger->num_supplies_online--;
- else if (supply && !was_online && online)
- charger->num_supplies_online++;
- }
-
- LOGI("power supply %s (%s) %s (action=%s num_online=%d num_supplies=%d)\n",
- uevent->ps_name, ps_type, battery ? "" : online ? "online" : "offline",
- uevent->action, charger->num_supplies_online, charger->num_supplies);
-}
-
-static void process_uevent(struct charger *charger, struct uevent *uevent)
-{
- if (!strcmp(uevent->subsystem, "power_supply"))
- process_ps_uevent(charger, uevent);
-}
-
-#define UEVENT_MSG_LEN 1024
-static int handle_uevent_fd(struct charger *charger, int fd)
-{
- char msg[UEVENT_MSG_LEN+2];
- int n;
-
- if (fd < 0)
- return -1;
-
- while (true) {
- struct uevent uevent;
-
- n = uevent_kernel_multicast_recv(fd, msg, UEVENT_MSG_LEN);
- if (n <= 0)
- break;
- if (n >= UEVENT_MSG_LEN) /* overflow -- discard */
- continue;
-
- msg[n] = '\0';
- msg[n+1] = '\0';
-
- parse_uevent(msg, &uevent);
- process_uevent(charger, &uevent);
- }
-
- return 0;
-}
-
-static int uevent_callback(int fd, short revents, void *data)
-{
- struct charger *charger = data;
-
- if (!(revents & POLLIN))
- return -1;
- return handle_uevent_fd(charger, fd);
-}
-
-/* force the kernel to regenerate the change events for the existing
- * devices, if valid */
-static void do_coldboot(struct charger *charger, DIR *d, const char *event,
- bool follow_links, int max_depth)
-{
- struct dirent *de;
- int dfd, fd;
-
- dfd = dirfd(d);
-
- fd = openat(dfd, "uevent", O_WRONLY);
- if (fd >= 0) {
- write(fd, event, strlen(event));
- close(fd);
- handle_uevent_fd(charger, charger->uevent_fd);
- }
-
- while ((de = readdir(d)) && max_depth > 0) {
- DIR *d2;
-
- LOGV("looking at '%s'\n", de->d_name);
-
- if ((de->d_type != DT_DIR && !(de->d_type == DT_LNK && follow_links)) ||
- de->d_name[0] == '.') {
- LOGV("skipping '%s' type %d (depth=%d follow=%d)\n",
- de->d_name, de->d_type, max_depth, follow_links);
- continue;
- }
- LOGV("can descend into '%s'\n", de->d_name);
-
- fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
- if (fd < 0) {
- LOGE("cannot openat %d '%s' (%d: %s)\n", dfd, de->d_name,
- errno, strerror(errno));
- continue;
- }
-
- d2 = fdopendir(fd);
- if (d2 == 0)
- close(fd);
- else {
- LOGV("opened '%s'\n", de->d_name);
- do_coldboot(charger, d2, event, follow_links, max_depth - 1);
- closedir(d2);
- }
- }
-}
-
-static void coldboot(struct charger *charger, const char *path,
- const char *event)
-{
- char str[256];
-
- LOGV("doing coldboot '%s' in '%s'\n", event, path);
- DIR *d = opendir(path);
- if (d) {
- snprintf(str, sizeof(str), "%s\n", event);
- do_coldboot(charger, d, str, true, 1);
- closedir(d);
- }
-}
-
-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, gr_surface 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 name=%s min_cap=%d time=%d\n",
- batt_anim->cur_frame, frame->name, 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 cur_frame;
- int disp_time;
-
- if (!batt_anim->run || now < charger->next_screen_transition)
- return;
-
- /* 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("[%lld] animation done\n", now);
- if (charger->num_supplies_online > 0)
- 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) {
- int batt_cap;
- int ret;
-
- LOGV("[%lld] animation starting\n", now);
- batt_cap = get_battery_capacity(charger);
- if (batt_cap >= 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_cap < 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;
- }
-
- batt_anim->capacity = batt_cap;
- }
-
- /* 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("[%lld] 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
- * if necessary, advance cycle cntr, and reset frame cntr
- */
- 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
- */
- }
-}
-
-static int set_key_callback(int code, int value, void *data)
-{
- struct charger *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("[%lld] 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("[%lld] key[%d] up (was down for %lld.%lldsec)\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];
- int64_t next_key_check;
-
- if (code == KEY_POWER) {
- if (key->down) {
- int64_t reboot_timeout = key->timestamp + POWER_ON_KEY_TIME;
- if (now >= reboot_timeout) {
- LOGI("[%lld] 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->num_supplies_online == 0) {
- request_suspend(false);
- if (charger->next_pwr_check == -1) {
- charger->next_pwr_check = now + UNPLUGGED_SHUTDOWN_TIME;
- LOGI("[%lld] device unplugged: shutting down in %lld (@ %lld)\n",
- now, UNPLUGGED_SHUTDOWN_TIME, charger->next_pwr_check);
- } else if (now >= charger->next_pwr_check) {
- LOGI("[%lld] 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) {
- LOGI("[%lld] device plugged in: shutdown cancelled\n", now);
- kick_animation(charger->batt_anim);
- }
- charger->next_pwr_check = -1;
- }
-}
-
-static void wait_next_event(struct charger *charger, int64_t now)
-{
- int64_t next_event = INT64_MAX;
- int64_t timeout;
- struct input_event ev;
- int ret;
-
- LOGV("[%lld] next screen: %lld next key: %lld next pwr: %lld\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;
- LOGV("[%lld] blocking (%lld)\n", now, timeout);
- ret = ev_wait((int)timeout);
- if (!ret)
- ev_dispatch();
-}
-
-static int input_callback(int fd, short revents, void *data)
-{
- struct charger *charger = data;
- struct input_event ev;
- int ret;
-
- ret = ev_get_input(fd, revents, &ev);
- if (ret)
- return -1;
- update_input_state(charger, &ev);
- return 0;
-}
-
-static void event_loop(struct charger *charger)
-{
- int ret;
-
- while (true) {
- int64_t now = curr_time_ms();
-
- LOGV("[%lld] event_loop()\n", now);
- 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);
-
- wait_next_event(charger, now);
- }
-}
-
-int main(int argc, char **argv)
-{
- int ret;
- struct charger *charger = &charger_state;
- int64_t now = curr_time_ms() - 1;
- int fd;
- int i;
-
- list_init(&charger->supplies);
-
- klog_init();
- klog_set_level(CHARGER_KLOG_LEVEL);
-
- dump_last_kmsg();
-
- LOGI("--------------- STARTING CHARGER MODE ---------------\n");
-
- gr_init();
- gr_font_size(&char_width, &char_height);
-
- ev_init(input_callback, charger);
-
- fd = uevent_open_socket(64*1024, true);
- if (fd >= 0) {
- fcntl(fd, F_SETFL, O_NONBLOCK);
- ev_add_fd(fd, uevent_callback, charger);
- }
- charger->uevent_fd = fd;
- coldboot(charger, "/sys/class/power_supply", "add");
-
- ret = res_create_surface("charger/battery_fail", &charger->surf_unknown);
- if (ret < 0) {
- LOGE("Cannot load image\n");
- charger->surf_unknown = NULL;
- }
-
- for (i = 0; i < charger->batt_anim->num_frames; i++) {
- struct frame *frame = &charger->batt_anim->frames[i];
-
- ret = res_create_surface(frame->name, &frame->surface);
- if (ret < 0) {
- LOGE("Cannot load image %s\n", frame->name);
- /* TODO: free the already allocated surfaces... */
- charger->batt_anim->num_frames = 0;
- charger->batt_anim->num_cycles = 1;
- break;
- }
- }
-
- ev_sync_key_state(set_key_callback, charger);
-
-#ifndef CHARGER_DISABLE_INIT_BLANK
- gr_fb_blank(true);
-#endif
-
- charger->next_screen_transition = now - 1;
- charger->next_key_check = -1;
- charger->next_pwr_check = -1;
- reset_animation(charger->batt_anim);
- kick_animation(charger->batt_anim);
-
- event_loop(charger);
-
- return 0;
-}
diff --git a/debuggerd/debuggerd.c b/debuggerd/debuggerd.c
index 0028bda..756f7bb 100644
--- a/debuggerd/debuggerd.c
+++ b/debuggerd/debuggerd.c
@@ -31,9 +31,10 @@
#include <sys/stat.h>
#include <sys/poll.h>
+#include <log/logd.h>
+#include <log/logger.h>
+
#include <cutils/sockets.h>
-#include <cutils/logd.h>
-#include <cutils/logger.h>
#include <cutils/properties.h>
#include <cutils/debugger.h>
diff --git a/debuggerd/tombstone.c b/debuggerd/tombstone.c
index 77f6ef1..7009a8e 100644
--- a/debuggerd/tombstone.c
+++ b/debuggerd/tombstone.c
@@ -29,7 +29,7 @@
#include <private/android_filesystem_config.h>
-#include <cutils/logger.h>
+#include <log/logger.h>
#include <cutils/properties.h>
#include <corkscrew/demangle.h>
diff --git a/debuggerd/utility.c b/debuggerd/utility.c
index 9bf3c18..41be982 100644
--- a/debuggerd/utility.c
+++ b/debuggerd/utility.c
@@ -22,7 +22,7 @@
#include <errno.h>
#include <unistd.h>
#include <signal.h>
-#include <cutils/logd.h>
+#include <log/logd.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <arpa/inet.h>
diff --git a/fastboot/usb_linux.c b/fastboot/usb_linux.c
index b7a9ca3..9153c8d 100644
--- a/fastboot/usb_linux.c
+++ b/fastboot/usb_linux.c
@@ -75,10 +75,18 @@
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)
{
- while(*name) {
- if(!isdigit(*name++)) return 1;
+ if (!isdigit(*name))
+ return 1;
+ while(*++name) {
+ if(!isdigit(*name) && *name != '.' && *name != '-')
+ return 1;
}
return 0;
}
@@ -95,7 +103,8 @@
return 0;
}
-static int filter_usb_device(int fd, char *ptr, int len, int writable,
+static int filter_usb_device(int fd, char* sysfs_name,
+ char *ptr, int len, int writable,
ifc_match_func callback,
int *ept_in_id, int *ept_out_id, int *ifc_id)
{
@@ -131,69 +140,35 @@
info.dev_protocol = dev->bDeviceProtocol;
info.writable = writable;
- // read device serial number (if there is one)
- info.serial_number[0] = 0;
- if (dev->iSerialNumber) {
- struct usbdevfs_ctrltransfer ctrl;
- // Keep it short enough because some bootloaders are borked if the URB len is > 255
- // 128 is too big by 1.
- __u16 buffer[127];
+ snprintf(info.device_path, sizeof(info.device_path), "usb:%s", sysfs_name);
- memset(buffer, 0, sizeof(buffer));
-
- ctrl.bRequestType = USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE;
- ctrl.bRequest = USB_REQ_GET_DESCRIPTOR;
- ctrl.wValue = (USB_DT_STRING << 8) | dev->iSerialNumber;
- //language ID (en-us) for serial number string
- ctrl.wIndex = 0x0409;
- ctrl.wLength = sizeof(buffer);
- ctrl.data = buffer;
- ctrl.timeout = 50;
-
- result = ioctl(fd, USBDEVFS_CONTROL, &ctrl);
- if (result > 0) {
- int i;
- // skip first word, and copy the rest to the serial string, changing shorts to bytes.
- result /= 2;
- for (i = 1; i < result; i++)
- info.serial_number[i - 1] = buffer[i];
- info.serial_number[i - 1] = 0;
- }
- }
-
- /* We need to get a path that represents a particular port on a particular
- * hub. We are passed an fd that was obtained by opening an entry under
- * /dev/bus/usb. Unfortunately, the names of those entries change each
- * time devices are plugged and unplugged. So how to get a repeatable
- * path? udevadm provided the inspiration. We can get the major and
- * minor of the device file, read the symlink that can be found here:
- * /sys/dev/char/<major>:<minor>
- * and then use the last element of that path. As a concrete example, I
- * have an Android device at /dev/bus/usb/001/027 so working with bash:
- * $ ls -l /dev/bus/usb/001/027
- * crw-rw-r-- 1 root plugdev 189, 26 Apr 9 11:03 /dev/bus/usb/001/027
- * $ ls -l /sys/dev/char/189:26
- * lrwxrwxrwx 1 root root 0 Apr 9 11:03 /sys/dev/char/189:26 ->
- * ../../devices/pci0000:00/0000:00:1a.7/usb1/1-4/1-4.2/1-4.2.3
- * So our device_path would be 1-4.2.3 which says my device is connected
- * to port 3 of a hub on port 2 of a hub on port 4 of bus 1 (per
- * http://www.linux-usb.org/FAQ.html).
+ /* 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.device_path[0] = '\0';
- result = fstat(fd, &st);
- if (!result && S_ISCHR(st.st_mode)) {
- char cdev[128];
- char link[256];
- char *slash;
- ssize_t link_len;
- snprintf(cdev, sizeof(cdev), "/sys/dev/char/%d:%d",
- major(st.st_rdev), minor(st.st_rdev));
- link_len = readlink(cdev, link, sizeof(link) - 1);
- if (link_len > 0) {
- link[link_len] = '\0';
- slash = strrchr(link, '/');
- if (slash)
- snprintf(info.device_path, sizeof(info.device_path), "usb:%s", slash+1);
+ 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';
+ }
}
}
@@ -241,14 +216,73 @@
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 busname[64], devname[64];
+ char devname[64];
char desc[1024];
int n, in, out, ifc;
- DIR *busdir, *devdir;
+ DIR *busdir;
struct dirent *de;
int fd;
int writable;
@@ -259,15 +293,7 @@
while((de = readdir(busdir)) && (usb == 0)) {
if(badname(de->d_name)) continue;
- sprintf(busname, "%s/%s", base, de->d_name);
- devdir = opendir(busname);
- if(devdir == 0) continue;
-
-// DBG("[ scanning %s ]\n", busname);
- while((de = readdir(devdir)) && (usb == 0)) {
-
- if(badname(de->d_name)) continue;
- sprintf(devname, "%s/%s", busname, de->d_name);
+ if(!convert_to_devfs_name(de->d_name, devname, sizeof(devname))) {
// DBG("[ scanning %s ]\n", devname);
writable = 1;
@@ -282,7 +308,7 @@
n = read(fd, desc, sizeof(desc));
- if(filter_usb_device(fd, desc, n, writable, callback,
+ if(filter_usb_device(fd, de->d_name, desc, n, writable, callback,
&in, &out, &ifc) == 0) {
usb = calloc(1, sizeof(usb_handle));
strcpy(usb->fname, devname);
@@ -301,7 +327,6 @@
close(fd);
}
}
- closedir(devdir);
}
closedir(busdir);
@@ -431,5 +456,5 @@
usb_handle *usb_open(ifc_match_func callback)
{
- return find_usb_device("/dev/bus/usb", callback);
+ return find_usb_device("/sys/bus/usb/devices", callback);
}
diff --git a/fastbootd/Android.mk b/fastbootd/Android.mk
new file mode 100644
index 0000000..f7c67a9
--- /dev/null
+++ b/fastbootd/Android.mk
@@ -0,0 +1,97 @@
+# Copyright (C) 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_C_INCLUDES := \
+ external/openssl/include \
+ $(LOCAL_PATH)/include \
+ external/zlib/ \
+
+LOCAL_SRC_FILES := \
+ config.c \
+ commands.c \
+ commands/boot.c \
+ commands/flash.c \
+ commands/partitions.c \
+ commands/virtual_partitions.c \
+ fastbootd.c \
+ protocol.c \
+ transport.c \
+ trigger.c \
+ usb_linux_client.c \
+ utils.c
+
+LOCAL_MODULE := fastbootd
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter
+LOCAL_LDFLAGS := -ldl
+
+LOCAL_SHARED_LIBRARIES := \
+ libhardware \
+ libhardware_legacy
+
+LOCAL_STATIC_LIBRARIES := \
+ libsparse_static \
+ libc \
+ libcutils \
+ libz
+
+#LOCAL_FORCE_STATIC_EXECUTABLE := true
+
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_C_INCLUDES := \
+ external/zlib/
+
+LOCAL_SRC_FILES := \
+ commands/partitions.c \
+ other/gptedit.c \
+ utils.c
+
+LOCAL_MODULE := gptedit
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter
+
+LOCAL_STATIC_LIBRARIES := \
+ libsparse_static \
+ libc \
+ libcutils \
+ libz
+
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/include \
+
+LOCAL_STATIC_LIBRARIES := \
+ $(EXTRA_STATIC_LIBS) \
+ libcutils
+
+LOCAL_SRC_FILES := \
+ other/vendor_trigger.c
+
+LOCAL_MODULE := libvendortrigger.default
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter
+
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/fastbootd/bootimg.h b/fastbootd/bootimg.h
new file mode 100644
index 0000000..44fde92
--- /dev/null
+++ b/fastbootd/bootimg.h
@@ -0,0 +1,97 @@
+/*
+ * 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 _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
+
+struct boot_img_hdr
+{
+ unsigned char magic[BOOT_MAGIC_SIZE];
+
+ unsigned kernel_size; /* size in bytes */
+ unsigned kernel_addr; /* physical load addr */
+
+ unsigned ramdisk_size; /* size in bytes */
+ unsigned ramdisk_addr; /* physical load addr */
+
+ unsigned second_size; /* size in bytes */
+ unsigned second_addr; /* physical load addr */
+
+ unsigned tags_addr; /* physical addr for kernel tags */
+ unsigned page_size; /* flash page size we assume */
+ unsigned unused[2]; /* future expansion: should be 0 */
+
+ unsigned char name[BOOT_NAME_SIZE]; /* asciiz product name */
+
+ unsigned char cmdline[BOOT_ARGS_SIZE];
+
+ unsigned id[8]; /* timestamp / checksum / sha1 / etc */
+};
+
+/*
+** +-----------------+
+** | 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
+*/
+
+boot_img_hdr *mkbootimg(void *kernel, unsigned kernel_size,
+ void *ramdisk, unsigned ramdisk_size,
+ void *second, unsigned second_size,
+ unsigned page_size,
+ unsigned *bootimg_size);
+
+void bootimg_set_cmdline(boot_img_hdr *hdr, const char *cmdline);
+#endif
diff --git a/fastbootd/commands.c b/fastbootd/commands.c
new file mode 100644
index 0000000..83e86b0
--- /dev/null
+++ b/fastbootd/commands.c
@@ -0,0 +1,383 @@
+/*
+ * Copyright (c) 2009-2013, Google 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:
+ * * 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 <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/reboot.h>
+#include <fcntl.h>
+
+#include "bootimg.h"
+#include "commands/boot.h"
+#include "commands/flash.h"
+#include "commands/partitions.h"
+#include "commands/virtual_partitions.h"
+#include "debug.h"
+#include "protocol.h"
+#include "trigger.h"
+#include "utils.h"
+
+#define ATAGS_LOCATION "/proc/atags"
+
+static void cmd_boot(struct protocol_handle *phandle, const char *arg)
+{
+ unsigned kernel_actual;
+ unsigned ramdisk_actual;
+ unsigned second_actual;
+ void *kernel_ptr;
+ void *ramdisk_ptr;
+ void *second_ptr;
+ struct boot_img_hdr *hdr;
+ int sz, atags_sz, new_atags_sz;
+ int rv;
+ char *ptr = NULL, *atags_ptr = NULL, *new_atags = NULL;
+ D(DEBUG, "cmd_boot %s\n", arg);
+
+ if (phandle->download_fd < 0) {
+ fastboot_fail(phandle, "no kernel file");
+ return;
+ }
+
+ atags_ptr = read_atags(ATAGS_LOCATION, &atags_sz);
+ if (atags_ptr == NULL) {
+ fastboot_fail(phandle, "atags read error");
+ goto error;
+ }
+
+ sz = get_file_size(phandle->download_fd);
+ ptr = (char *) mmap(NULL, sz, PROT_READ,
+ MAP_POPULATE | MAP_PRIVATE, phandle->download_fd, 0);
+ hdr = (struct boot_img_hdr *) ptr;
+
+ if (ptr == MAP_FAILED) {
+ fastboot_fail(phandle, "internal fastbootd error");
+ goto error;
+ }
+
+ if ((size_t) sz < sizeof(*hdr)) {
+ fastboot_fail(phandle, "invalid bootimage header");
+ goto error;
+ }
+
+ kernel_actual = ROUND_TO_PAGE(hdr->kernel_size, hdr->page_size);
+ ramdisk_actual = ROUND_TO_PAGE(hdr->ramdisk_size, hdr->page_size);
+ second_actual = ROUND_TO_PAGE(hdr->second_size, hdr->page_size);
+
+ new_atags = (char *) create_atags((unsigned *) atags_ptr, atags_sz, hdr, &new_atags_sz);
+
+ if (new_atags == NULL) {
+ fastboot_fail(phandle, "atags generate error");
+ goto error;
+ }
+ if (new_atags_sz > 0x4000) {
+ fastboot_fail(phandle, "atags file to large");
+ goto error;
+ }
+
+ if ((int) (hdr->page_size + kernel_actual + ramdisk_actual) < sz) {
+ fastboot_fail(phandle, "incomplete bootimage");
+ goto error;
+ }
+
+ kernel_ptr = (void *)((unsigned) ptr + hdr->page_size);
+ ramdisk_ptr = (void *)((unsigned) kernel_ptr + kernel_actual);
+ second_ptr = (void *)((unsigned) ramdisk_ptr + ramdisk_actual);
+
+ D(INFO, "preparing to boot");
+ // Prepares boot physical addresses from header are ignored
+ rv = prepare_boot_linux(hdr->kernel_addr, kernel_ptr, kernel_actual,
+ hdr->ramdisk_addr, ramdisk_ptr, ramdisk_actual,
+ hdr->second_addr, second_ptr, second_actual,
+ hdr->tags_addr, new_atags, ROUND_TO_PAGE(new_atags_sz, hdr->page_size));
+ if (rv < 0) {
+ fastboot_fail(phandle, "kexec prepare failed");
+ goto error;
+ }
+
+ fastboot_okay(phandle, "");
+
+ free(atags_ptr);
+ munmap(ptr, sz);
+ free(new_atags);
+
+ D(INFO, "Kexec going to reboot");
+ reboot(LINUX_REBOOT_CMD_KEXEC);
+
+ fastboot_fail(phandle, "reboot error");
+
+ return;
+
+error:
+
+ if (atags_ptr != NULL)
+ free(atags_ptr);
+ if (ptr != NULL)
+ munmap(ptr, sz);
+
+}
+
+static void cmd_erase(struct protocol_handle *phandle, const char *arg)
+{
+ int partition_fd;
+ char path[PATH_MAX];
+ D(DEBUG, "cmd_erase %s\n", arg);
+
+ if (flash_find_entry(arg, path, PATH_MAX)) {
+ fastboot_fail(phandle, "partition table doesn't exist");
+ return;
+ }
+
+ if (path == NULL) {
+ fastboot_fail(phandle, "Couldn't find partition");
+ return;
+ }
+
+ partition_fd = flash_get_partiton(path);
+ if (partition_fd < 0) {
+ fastboot_fail(phandle, "partiton file does not exists");
+ }
+
+ if (flash_erase(partition_fd)) {
+ fastboot_fail(phandle, "failed to erase partition");
+ flash_close(partition_fd);
+ return;
+ }
+
+ if (flash_close(partition_fd) < 0) {
+ D(ERR, "could not close device %s", strerror(errno));
+ fastboot_fail(phandle, "failed to erase partition");
+ return;
+ }
+ fastboot_okay(phandle, "");
+}
+
+static int GPT_header_location() {
+ const char *location_str = fastboot_getvar("gpt_sector");
+ char *str;
+ int location;
+
+ if (!strcmp("", location_str)) {
+ D(INFO, "GPT location not specified using second sector");
+ return 1;
+ }
+ else {
+ location = strtoul(location_str, &str, 10);
+ D(INFO, "GPT location specified as %d", location);
+
+ if (*str != '\0')
+ return -1;
+
+ return location - 1;
+ }
+}
+
+static void cmd_gpt_layout(struct protocol_handle *phandle, const char *arg) {
+ struct GPT_entry_table *oldtable;
+ int location;
+ struct GPT_content content;
+ const char *device;
+ device = fastboot_getvar("blockdev");
+
+ if (!strcmp(device, "")) {
+ fastboot_fail(phandle, "blockdev not defined in config file");
+ return;
+ }
+
+ if (phandle->download_fd < 0) {
+ fastboot_fail(phandle, "no layout file");
+ return;
+ }
+
+ location = GPT_header_location();
+ oldtable = GPT_get_device(device, location);
+
+ GPT_default_content(&content, oldtable);
+ if (oldtable == NULL)
+ D(WARN, "Could not get old gpt table");
+ else
+ GPT_release_device(oldtable);
+
+ if (!GPT_parse_file(phandle->download_fd, &content)) {
+ fastboot_fail(phandle, "Could not parse partition config file");
+ return;
+ }
+
+ if (trigger_gpt_layout(&content)) {
+ fastboot_fail(phandle, "Vendor forbids this opperation");
+ GPT_release_content(&content);
+ return;
+ }
+
+ if (!GPT_write_content(device, &content)) {
+ fastboot_fail(phandle, "Unable to write gpt file");
+ GPT_release_content(&content);
+ return;
+ }
+
+ GPT_release_content(&content);
+ fastboot_okay(phandle, "");
+}
+
+static void cmd_flash(struct protocol_handle *phandle, const char *arg)
+{
+ int partition;
+ uint64_t sz;
+ char data[BOOT_MAGIC_SIZE];
+ char path[PATH_MAX];
+ ssize_t header_sz = 0;
+
+ D(DEBUG, "cmd_flash %s\n", arg);
+
+ if (try_handle_virtual_partition(phandle, arg)) {
+ return;
+ }
+
+ if (phandle->download_fd < 0) {
+ fastboot_fail(phandle, "no kernel file");
+ return;
+ }
+
+ if (flash_find_entry(arg, path, PATH_MAX)) {
+ fastboot_fail(phandle, "partition table doesn't exist");
+ return;
+ }
+
+ // TODO: Maybe its goot idea to check whether the partition is just bootable partition
+ if (!strcmp(arg, "boot") || !strcmp(arg, "recovery")) {
+ if (read_data_once(phandle->download_fd, data, BOOT_MAGIC_SIZE) < BOOT_MAGIC_SIZE) {
+ fastboot_fail(phandle, "incoming data read error, cannot read boot header");
+ return;
+ }
+ if (memcmp((void *)data, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
+ fastboot_fail(phandle, "image is not a boot image");
+ return;
+ }
+ }
+
+ partition = flash_get_partiton(path);
+
+ sz = get_file_size64(phandle->download_fd);
+ if (sz > get_file_size64(partition)) {
+ flash_close(partition);
+ D(WARN, "size of file too large");
+ fastboot_fail(phandle, "size of file too large");
+ return;
+ }
+
+ D(INFO, "writing %lld bytes to '%s'\n", sz, arg);
+
+ if (flash_write(partition, phandle->download_fd, sz, header_sz)) {
+ fastboot_fail(phandle, "flash write failure");
+ return;
+ }
+ D(INFO, "partition '%s' updated\n", arg);
+
+ flash_close(partition);
+
+ fastboot_okay(phandle, "");
+}
+
+static void cmd_continue(struct protocol_handle *phandle, const char *arg)
+{
+ fastboot_okay(phandle, "");
+#if 0
+ udc_stop();
+
+ boot_linux_from_flash();
+#endif
+}
+
+static void cmd_getvar(struct protocol_handle *phandle, const char *arg)
+{
+ const char *value;
+ D(DEBUG, "cmd_getvar %s\n", arg);
+
+ value = fastboot_getvar(arg);
+
+ fastboot_okay(phandle, value);
+}
+
+static void cmd_download(struct protocol_handle *phandle, const char *arg)
+{
+ unsigned len = strtoul(arg, NULL, 16);
+ int old_fd;
+
+ if (len > 256 * 1024 * 1024) {
+ fastboot_fail(phandle, "data too large");
+ return;
+ }
+
+ fastboot_data(phandle, len);
+
+ old_fd = protocol_get_download(phandle);
+ if (old_fd >= 0) {
+ off_t len = lseek(old_fd, 0, SEEK_END);
+ D(INFO, "disposing of unused fd %d, size %ld", old_fd, len);
+ close(old_fd);
+ }
+
+ phandle->download_fd = protocol_handle_download(phandle, len);
+ if (phandle->download_fd < 0) {
+ //handle->state = STATE_ERROR;
+ fastboot_fail(phandle, "download failed");
+ return;
+ }
+
+ fastboot_okay(phandle, "");
+}
+
+static void cmd_oem(struct protocol_handle *phandle, const char *arg) {
+ const char *response = "";
+
+ if (trigger_oem_cmd(arg, &response))
+ fastboot_fail(phandle, response);
+ else
+ fastboot_okay(phandle, response);
+}
+
+void commands_init()
+{
+ virtual_partition_register("partition-table", cmd_gpt_layout);
+
+ fastboot_register("boot", cmd_boot);
+ fastboot_register("erase:", cmd_erase);
+ fastboot_register("flash:", cmd_flash);
+ fastboot_register("continue", cmd_continue);
+ fastboot_register("getvar:", cmd_getvar);
+ fastboot_register("download:", cmd_download);
+ fastboot_register("oem", cmd_oem);
+ //fastboot_publish("version", "0.5");
+ //fastboot_publish("product", "swordfish");
+ //fastboot_publish("kernel", "lk");
+}
diff --git a/fastbootd/commands/boot.c b/fastbootd/commands/boot.c
new file mode 100644
index 0000000..8da9a28
--- /dev/null
+++ b/fastbootd/commands/boot.c
@@ -0,0 +1,254 @@
+/*
+ * Copyright (c) 2009-2013, Google 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:
+ * * 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 <sys/syscall.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "boot.h"
+#include "debug.h"
+#include "utils.h"
+#include "bootimg.h"
+
+
+#define KEXEC_ARM_ATAGS_OFFSET 0x1000
+#define KEXEC_ARM_ZIMAGE_OFFSET 0x8000
+
+#define MEMORY_SIZE 0x0800000
+#define START_ADDRESS 0x44000000
+#define KERNEL_START (START_ADDRESS + KEXEC_ARM_ZIMAGE_OFFSET)
+
+#define ATAG_NONE_TYPE 0x00000000
+#define ATAG_CORE_TYPE 0x54410001
+#define ATAG_RAMDISK_TYPE 0x54410004
+#define ATAG_INITRD2_TYPE 0x54420005
+#define ATAG_CMDLINE_TYPE 0x54410009
+
+#define MAX_ATAG_SIZE 0x4000
+
+struct atag_info {
+ unsigned size;
+ unsigned type;
+};
+
+struct atag_initrd2 {
+ unsigned start;
+ unsigned size;
+};
+
+struct atag_cmdline {
+ char cmdline[0];
+};
+
+struct atag {
+ struct atag_info info;
+ union {
+ struct atag_initrd2 initrd2;
+ struct atag_cmdline cmdline;
+ } data;
+};
+
+
+long kexec_load(unsigned int entry, unsigned long nr_segments,
+ struct kexec_segment *segment, unsigned long flags) {
+ return syscall(__NR_kexec_load, entry, nr_segments, segment, flags);
+}
+
+/*
+ * Prepares arguments for kexec
+ * Kernel address is not set into kernel_phys
+ * Ramdisk is set to position relative to kernel
+ */
+int prepare_boot_linux(unsigned kernel_phys, void *kernel_addr, int kernel_size,
+ unsigned ramdisk_phys, void *ramdisk_addr, int ramdisk_size,
+ unsigned second_phys, void *second_addr, int second_size,
+ unsigned atags_phys, void *atags_addr, int atags_size) {
+ struct kexec_segment segment[4];
+ int segment_count = 2;
+ unsigned entry = START_ADDRESS + KEXEC_ARM_ZIMAGE_OFFSET;
+ int rv;
+ int page_size = getpagesize();
+
+ segment[0].buf = kernel_addr;
+ segment[0].bufsz = kernel_size;
+ segment[0].mem = (void *) KERNEL_START;
+ segment[0].memsz = ROUND_TO_PAGE(kernel_size, page_size);
+
+ if (kernel_size > MEMORY_SIZE - KEXEC_ARM_ZIMAGE_OFFSET) {
+ D(INFO, "Kernel image too big");
+ return -1;
+ }
+
+ segment[1].buf = atags_addr;
+ segment[1].bufsz = atags_size;
+ segment[1].mem = (void *) (START_ADDRESS + KEXEC_ARM_ATAGS_OFFSET);
+ segment[1].memsz = ROUND_TO_PAGE(atags_size, page_size);
+
+ D(INFO, "Ramdisk size is %d", ramdisk_size);
+
+ if (ramdisk_size != 0) {
+ segment[segment_count].buf = ramdisk_addr;
+ segment[segment_count].bufsz = ramdisk_size;
+ segment[segment_count].mem = (void *) (KERNEL_START + ramdisk_phys - kernel_phys);
+ segment[segment_count].memsz = ROUND_TO_PAGE(ramdisk_phys, page_size);
+ ++segment_count;
+ }
+
+ D(INFO, "Ramdisk size is %d", ramdisk_size);
+ if (second_size != 0) {
+ segment[segment_count].buf = second_addr;
+ segment[segment_count].bufsz = second_size;
+ segment[segment_count].mem = (void *) (KERNEL_START + second_phys - kernel_phys);
+ segment[segment_count].memsz = ROUND_TO_PAGE(second_size, page_size);
+ entry = second_phys;
+ ++segment_count;
+ }
+
+ rv = kexec_load(entry, segment_count, segment, KEXEC_ARCH_DEFAULT);
+
+ if (rv != 0) {
+ D(INFO, "Kexec_load returned non-zero exit code: %s\n", strerror(errno));
+ return -1;
+ }
+
+ return 1;
+
+}
+
+unsigned *create_atags(unsigned *atags_position, int atag_size, const struct boot_img_hdr *hdr, int *size) {
+ struct atag *current_tag = (struct atag *) atags_position;
+ unsigned *current_tag_raw = atags_position;
+ unsigned *new_atags = malloc(ROUND_TO_PAGE(atag_size + BOOT_ARGS_SIZE * sizeof(char),
+ hdr->page_size));
+ //This pointer will point into the beggining of buffer free space
+ unsigned *natags_raw_buff = new_atags;
+ int new_atags_size = 0;
+ int current_size;
+ int cmdl_length;
+
+ // copy tags from current atag file
+ while (current_tag->info.type != ATAG_NONE_TYPE) {
+ switch (current_tag->info.type) {
+ case ATAG_CMDLINE_TYPE:
+ case ATAG_RAMDISK_TYPE:
+ case ATAG_INITRD2_TYPE: break;
+ default:
+ memcpy((void *)natags_raw_buff, (void *)current_tag_raw, current_tag->info.size * sizeof(unsigned));
+ natags_raw_buff += current_tag->info.size;
+ new_atags_size += current_tag->info.size;
+ }
+
+ current_tag_raw += current_tag->info.size;
+ current_tag = (struct atag *) current_tag_raw;
+
+ if (current_tag_raw >= atags_position + atag_size) {
+ D(ERR, "Critical error in atags");
+ return NULL;
+ }
+ }
+
+ // set INITRD2 tag
+ if (hdr->ramdisk_size > 0) {
+ current_size = (sizeof(struct atag_info) + sizeof(struct atag_initrd2)) / sizeof(unsigned);
+ *((struct atag *) natags_raw_buff) = (struct atag) {
+ .info = {
+ .size = current_size,
+ .type = ATAG_INITRD2_TYPE
+ },
+ .data = {
+ .initrd2 = (struct atag_initrd2) {
+ .start = hdr->ramdisk_addr,
+ .size = hdr->ramdisk_size
+ }
+ }
+ };
+
+ new_atags_size += current_size;
+ natags_raw_buff += current_size;
+ }
+
+ // set ATAG_CMDLINE
+ cmdl_length = strnlen((char *) hdr->cmdline, BOOT_ARGS_SIZE - 1);
+ current_size = sizeof(struct atag_info) + (1 + cmdl_length);
+ current_size = (current_size + sizeof(unsigned) - 1) / sizeof(unsigned);
+ *((struct atag *) natags_raw_buff) = (struct atag) {
+ .info = {
+ .size = current_size,
+ .type = ATAG_CMDLINE_TYPE
+ },
+ };
+
+ //copy cmdline and ensure that there is null character
+ memcpy(((struct atag *) natags_raw_buff)->data.cmdline.cmdline,
+ (char *) hdr->cmdline, cmdl_length);
+ ((struct atag *) natags_raw_buff)->data.cmdline.cmdline[cmdl_length] = '\0';
+
+ new_atags_size += current_size;
+ natags_raw_buff += current_size;
+
+ // set ATAG_NONE
+ *((struct atag *) natags_raw_buff) = (struct atag) {
+ .info = {
+ .size = 0,
+ .type = ATAG_NONE_TYPE
+ },
+ };
+ new_atags_size += sizeof(struct atag_info) / sizeof(unsigned);
+ natags_raw_buff += sizeof(struct atag_info) / sizeof(unsigned);
+
+ *size = new_atags_size * sizeof(unsigned);
+ return new_atags;
+}
+
+char *read_atags(const char * path, int *atags_sz) {
+ int afd = -1;
+ char *atags_ptr = NULL;
+
+ afd = open(path, O_RDONLY);
+ if (afd < 0) {
+ D(ERR, "wrong atags file");
+ return 0;
+ }
+
+ atags_ptr = (char *) malloc(MAX_ATAG_SIZE);
+ if (atags_ptr == NULL) {
+ D(ERR, "insufficient memory");
+ return 0;
+ }
+
+ *atags_sz = read(afd, atags_ptr, MAX_ATAG_SIZE);
+
+ close(afd);
+ return atags_ptr;
+}
+
diff --git a/fastbootd/commands/boot.h b/fastbootd/commands/boot.h
new file mode 100644
index 0000000..bfbf1de
--- /dev/null
+++ b/fastbootd/commands/boot.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2009-2013, Google 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:
+ * * 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.
+ */
+
+#ifndef __FASTBOOT_BOOT_H
+#define __FASTBOOT_BOOT_H
+
+#include <sys/cdefs.h>
+#include <uapi/linux/kexec.h>
+
+#include "bootimg.h"
+
+#define KEXEC_SEGMENT_MAX 16
+
+#define KEXEC_TYPE_DEFAULT 0
+#define KEXEC_TYPE_CRASH 1
+
+int prepare_boot_linux(unsigned, void *, int, unsigned, void *, int,
+ unsigned, void *, int, unsigned, void *, int);
+unsigned *create_atags(unsigned *, int, const struct boot_img_hdr *, int *);
+long kexec_load(unsigned int, unsigned long, struct kexec_segment *, unsigned long);
+char *read_atags(const char *, int *);
+
+#endif /* _SYS_KEXEC_H */
+
diff --git a/fastbootd/commands/flash.c b/fastbootd/commands/flash.c
new file mode 100644
index 0000000..5f8b931
--- /dev/null
+++ b/fastbootd/commands/flash.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2009-2013, Google 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:
+ * * 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 <sys/stat.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+
+#include "flash.h"
+#include "protocol.h"
+#include "debug.h"
+#include "utils.h"
+#include "commands/partitions.h"
+
+
+#define ALLOWED_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-."
+#define BUFFER_SIZE 1024 * 1024
+#define MIN(a, b) (a > b ? b : a)
+
+
+int flash_find_entry(const char *name, char *out, size_t outlen)
+{
+//TODO: Assumption: All the partitions has they unique name
+
+ const char *path = fastboot_getvar("device-directory");
+ size_t length;
+ if (strcmp(path, "") == 0) {
+ D(ERR, "device-directory: not defined in config file");
+ return -1;
+ }
+
+ length = strspn(name, ALLOWED_CHARS);
+ if (length != strlen(name)) {
+ D(ERR, "Not allowed char in name: %c", name[length]);
+ return -1;
+ }
+
+ if (snprintf(out, outlen, "%s%s", path, name) >= (int) outlen) {
+ D(ERR, "Too long path to partition file");
+ return -1;
+ }
+
+ if (access(out, F_OK ) == -1) {
+ D(ERR, "could not find partition file %s", name);
+ return -1;
+ }
+
+ return 0;
+}
+
+int flash_erase(int fd)
+{
+ int64_t size;
+ size = get_block_device_size(fd);
+ D(DEBUG, "erase %llu data from %d\n", size, fd);
+
+ return wipe_block_device(fd, size);
+}
+
+int flash_write(int partition_fd, int data_fd, ssize_t size, ssize_t skip)
+{
+ ssize_t written = 0;
+ struct GPT_mapping input;
+ struct GPT_mapping output;
+
+ while (written < size) {
+ int current_size = MIN(size - written, BUFFER_SIZE);
+
+ if (gpt_mmap(&input, written + skip, current_size, data_fd)) {
+ D(ERR, "Error in writing data, unable to map data file %d at %d size %d", size, skip, current_size);
+ return -1;
+ }
+ if (gpt_mmap(&output, written, current_size, partition_fd)) {
+ D(ERR, "Error in writing data, unable to map output partition");
+ return -1;
+ }
+
+ memcpy(output.ptr, input.ptr, current_size);
+
+ gpt_unmap(&input);
+ gpt_unmap(&output);
+
+ written += current_size;
+ }
+
+ return 0;
+}
diff --git a/fastbootd/commands/flash.h b/fastbootd/commands/flash.h
new file mode 100644
index 0000000..8ffd688
--- /dev/null
+++ b/fastbootd/commands/flash.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2009-2013, Google 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:
+ * * 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.
+ */
+
+#ifndef _FASTBOOTD_ERASE_H
+#define _FASTBOOTD_ERASE_H
+
+int flash_find_entry(const char *, char *, size_t);
+int flash_erase(int fd);
+
+static inline int flash_get_partiton(const char *path) {
+ return open(path, O_RDWR);
+}
+
+static inline int flash_close(int fd) {
+ return close(fd);
+}
+
+int flash_write(int partition, int data, ssize_t size, ssize_t skip);
+
+static inline ssize_t read_data_once(int fd, char *buffer, ssize_t size) {
+ ssize_t readcount = 0;
+ ssize_t len;
+
+ while ((len = TEMP_FAILURE_RETRY(read(fd, (void *) &buffer[readcount], size - readcount))) > 0) {
+ readcount -= len;
+ }
+ if (len < 0)
+ return len;
+
+ return readcount;
+}
+
+#endif
+
diff --git a/fastbootd/commands/partitions.c b/fastbootd/commands/partitions.c
new file mode 100644
index 0000000..3bfc298
--- /dev/null
+++ b/fastbootd/commands/partitions.c
@@ -0,0 +1,738 @@
+/*
+ * Copyright (c) 2009-2013, Google 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:
+ * * 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 <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <endian.h>
+#include <zlib.h>
+#include <linux/hdreg.h>
+#include <sys/ioctl.h>
+#include <stdlib.h>
+#include <cutils/config_utils.h>
+
+#include "partitions.h"
+#include "debug.h"
+#include "utils.h"
+#include "protocol.h"
+
+#define MMAP2_SHIFT 12
+
+#define BLKRRPART _IO(0x12,95) /* re-read partition table */
+#define BLKSSZGET _IO(0x12,104)
+
+#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)))
+
+
+const uint16_t partition_type_uuid[16] = {
+ 0xa2, 0xa0, 0xd0, 0xeb, 0xe5, 0xb9, 0x33, 0x44,
+ 0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7,
+};
+
+//TODO: If both blocks are invalid should I leave everything to vendor (through libvendor)
+
+static void GPT_entry_clear(struct GPT_entry_raw *entry)
+{
+ memset(entry, 0, sizeof(*entry));
+}
+
+extern void* __mmap2(void *, size_t, int, int, int, off_t);
+
+static inline void *mmap64(void *addr, size_t length, int prot,
+ int flags, int fd, uint64_t offset) {
+ return __mmap2(addr, length, prot, flags, fd, offset >> MMAP2_SHIFT);
+}
+
+/*
+ * returns mapped location to choosen area
+ * mapped_ptr is pointer to whole area mapped (it can be bigger then requested)
+ */
+int gpt_mmap(struct GPT_mapping *mapping, uint64_t location, int size, int fd)
+{
+ unsigned int location_diff = location & ~PAGE_MASK;
+
+ mapping->size = ALIGN(size + location_diff, PAGE_SIZE);
+
+ uint64_t sz = get_file_size64(fd);
+ if (sz < size + location) {
+ D(ERR, "the location of mapping area is outside of the device size %lld", sz);
+ return 1;
+ }
+
+ mapping->map_ptr = mmap64(NULL, mapping->size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, location);
+
+ if (mapping->map_ptr == MAP_FAILED) {
+ mapping->ptr = MAP_FAILED;
+ D(ERR, "map failed %d", (int) mapping->map_ptr);
+ return 1;
+ }
+
+ mapping->ptr = (void *)((char *) mapping->map_ptr + location_diff);
+ return 0;
+}
+
+void gpt_unmap(struct GPT_mapping *mapping) {
+ munmap(mapping->map_ptr, mapping->size);
+}
+
+
+#define LBA_ADDR(table, value) ((uint64_t) (table)->sector_size * (value))
+
+int GPT_map_from_content(struct GPT_entry_table *table, const struct GPT_content *content)
+{
+
+ // Mapping header
+ if (gpt_mmap(&table->header_map, LBA_ADDR(table, content->header.current_lba),
+ table->sector_size, table->fd)) {
+ D(ERR, "unable to map header:%s\n", strerror(errno));
+ goto error_header;
+ }
+
+ table->header = (struct GPT_header *) table->header_map.ptr;
+
+ table->partition_table_size = ROUND_UP(content->header.entries_count * sizeof(*table->entries),
+ table->sector_size);
+
+ // Mapping entry table
+ if (gpt_mmap(&table->entries_map, LBA_ADDR(table, content->header.entries_lba),
+ table->partition_table_size, table->fd)) {
+ D(ERR, "unable to map entries");
+ goto error_signature;
+ }
+
+ table->entries = (struct GPT_entry_raw *) table->entries_map.ptr;
+
+ // Mapping secondary header
+ if (gpt_mmap(&table->sec_header_map, LBA_ADDR(table, content->header.backup_lba),
+ table->sector_size, table->fd)) {
+ D(ERR, "unable to map backup gpt header");
+ goto error_sec_header;
+ }
+
+ // Mapping secondary entries table
+ if (gpt_mmap(&table->sec_entries_map,
+ LBA_ADDR(table, content->header.backup_lba) - table->partition_table_size,
+ table->partition_table_size, table->fd)) {
+ D(ERR, "unable to map secondary gpt table");
+ goto error_sec_entries;
+ }
+
+ table->second_header = (struct GPT_header *) table->sec_header_map.ptr;
+ table->second_entries = (struct GPT_entry_raw *) table->sec_entries_map.ptr;
+ table->second_valid = strcmp("EFI PART", (char *) table->second_header->signature) == 0;
+
+ return 0;
+
+error_sec_entries:
+ gpt_unmap(&table->sec_header_map);
+error_sec_header:
+ gpt_unmap(&table->entries_map);
+error_signature:
+ gpt_unmap(&table->header_map);
+error_header:
+ return 1;
+}
+
+int GPT_map(struct GPT_entry_table *table, unsigned header_lba)
+{
+ struct GPT_content content;
+ struct GPT_mapping mapping;
+ struct GPT_header *header;
+
+ if (gpt_mmap(&mapping, LBA_ADDR(table, header_lba), table->sector_size, table->fd)) {
+ D(ERR, "unable to map header: %s", strerror(errno));
+ goto error_header;
+ }
+
+ header = (struct GPT_header *) mapping.ptr;
+
+ if (strcmp("EFI PART", (char *) header->signature)) {
+ D(ERR, "GPT entry not valid");
+ goto error_signature;
+ }
+
+ content.header = *header;
+
+ gpt_unmap(&mapping);
+
+ return GPT_map_from_content(table, &content);
+
+error_signature:
+ gpt_unmap(&table->header_map);
+error_header:
+ return 1;
+}
+
+struct GPT_entry_table* GPT_get_device(const char *path, unsigned header_lba)
+{
+ struct GPT_entry_table *table;
+ size_t sector_bytes;
+
+ table = (struct GPT_entry_table *) malloc(sizeof(*table));
+ table->fd = open(path, O_RDWR);
+
+ if (table->fd < 0) {
+ D(ERR, "unable to open file %s:%s\n", path, strerror(errno));
+ return NULL;
+ }
+
+ if (!ioctl(table->fd, BLKSSZGET, §or_bytes)) {
+ table->sector_size = (unsigned) sector_bytes;
+ D(INFO, "Got sector size %d", table->sector_size);
+ } else {
+ D(WARN, "unable to get sector size, assuming 512");
+ table->sector_size = 512;
+ }
+
+ if (GPT_map(table, header_lba)) {
+ D(ERR, "Could not map gpt");
+ return NULL;
+ }
+
+ return table;
+}
+
+static struct GPT_entry_table* GPT_get_from_content(const char *path, const struct GPT_content *content)
+{
+ struct GPT_entry_table *table;
+ size_t sector_bytes;
+
+ table = (struct GPT_entry_table *) malloc(sizeof(*table));
+ table->fd = open(path, O_RDWR);
+
+ if (table->fd < 0) {
+ D(ERR, "unable to open file %s:%s\n", path, strerror(errno));
+ return NULL;
+ }
+
+ if (!ioctl(table->fd, BLKSSZGET, §or_bytes)) {
+ table->sector_size = (unsigned) sector_bytes;
+ D(INFO, "Got sector size %d", table->sector_size);
+ } else {
+ D(WARN, "unable to get sector size %s, assuming 512", strerror(errno));
+ table->sector_size = 512;
+ }
+
+ if (GPT_map_from_content(table, content)) {
+ D(ERR, "Could not map gpt");
+ return NULL;
+ }
+
+ return table;
+}
+
+
+void GPT_release_device(struct GPT_entry_table *table)
+{
+ gpt_unmap(&table->header_map);
+ gpt_unmap(&table->entries_map);
+ gpt_unmap(&table->sec_header_map);
+ gpt_unmap(&table->sec_entries_map);
+ close(table->fd);
+ free(table);
+}
+
+static int GPT_check_overlap(struct GPT_entry_table *table, struct GPT_entry_raw *entry);
+static int GPT_check_overlap_except(struct GPT_entry_table *table,
+ struct GPT_entry_raw *entry,
+ struct GPT_entry_raw *exclude);
+
+void GPT_edit_entry(struct GPT_entry_table *table,
+ struct GPT_entry_raw *old_entry,
+ struct GPT_entry_raw *new_entry)
+{
+ struct GPT_entry_raw *current_entry = GPT_get_pointer(table, old_entry);
+
+ if (GPT_check_overlap_except(table, new_entry, current_entry)) {
+ D(ERR, "Couldn't add overlaping partition");
+ return;
+ }
+
+ if (current_entry == NULL) {
+ D(ERR, "Couldn't find entry");
+ return;
+ }
+
+ *current_entry = *new_entry;
+}
+
+int GPT_delete_entry(struct GPT_entry_table *table, struct GPT_entry_raw *entry)
+{
+ struct GPT_entry_raw *raw = GPT_get_pointer(table, entry);
+
+ if (raw == NULL) {
+ D(ERR, "could not find entry");
+ return 1;
+ }
+ D(DEBUG, "Deleting gpt entry '%s'\n", raw->partition_guid);
+
+ // This entry can be empty in the middle
+ GPT_entry_clear(raw);
+
+ return 0;
+}
+
+void GPT_add_entry(struct GPT_entry_table *table, struct GPT_entry_raw *entry)
+{
+ unsigned i;
+ int inserted = 0;
+ if (GPT_check_overlap(table, entry)) {
+ D(ERR, "Couldn't add overlaping partition");
+ return;
+ }
+
+ if (GPT_get_pointer(table, entry) != NULL) {
+ D(WARN, "Add entry fault, this entry already exists");
+ return;
+ }
+
+ struct GPT_entry_raw *entries = table->entries;
+
+ for (i = 0; i < table->header->entries_count; ++i) {
+ if (!entries[i].type_guid[0]) {
+ inserted = 1;
+ D(DEBUG, "inserting");
+ memcpy(&entries[i], entry, sizeof(entries[i]));
+ break;
+ }
+ }
+
+ if (!inserted) {
+ D(ERR, "Unable to find empty partion entry");
+ }
+}
+
+struct GPT_entry_raw *GPT_get_pointer_by_UTFname(struct GPT_entry_table *table, const uint16_t *name);
+
+struct GPT_entry_raw *GPT_get_pointer(struct GPT_entry_table *table, struct GPT_entry_raw *entry)
+{
+ if (entry->partition_guid[0] != 0)
+ return GPT_get_pointer_by_guid(table, (const char *) entry->partition_guid);
+ else if (entry->name[0] != 0)
+ return GPT_get_pointer_by_UTFname(table, entry->name);
+
+ D(WARN, "Name or guid needed to find entry");
+ return NULL;
+}
+
+struct GPT_entry_raw *GPT_get_pointer_by_guid(struct GPT_entry_table *table, const char *name)
+{
+ int current = (int) table->header->entries_count;
+
+ for (current = current - 1; current >= 0; --current) {
+ if (strncmp((char *) name,
+ (char *) table->entries[current].partition_guid, 16) == 0) {
+ return &table->entries[current];
+ }
+ }
+
+ return NULL;
+}
+
+int strncmp_UTF16_char(const uint16_t *s1, const char *s2, size_t n)
+{
+ if (n == 0)
+ return (0);
+ do {
+ if (((*s1) & 127) != *s2++)
+ return (((unsigned char) ((*s1) & 127)) - *(unsigned char *)--s2);
+ if (*s1++ == 0)
+ break;
+ } while (--n != 0);
+ return (0);
+}
+
+int strncmp_UTF16(const uint16_t *s1, const uint16_t *s2, size_t n)
+{
+ if (n == 0)
+ return (0);
+ do {
+ if ((*s1) != *s2++)
+ return (*s1 - *--s2);
+ if (*s1++ == 0)
+ break;
+ } while (--n != 0);
+ return (0);
+}
+
+struct GPT_entry_raw *GPT_get_pointer_by_name(struct GPT_entry_table *table, const char *name)
+{
+ //TODO: reverse direction
+ int count = (int) table->header->entries_count;
+ int current;
+
+ for (current = 0; current < count; ++current) {
+ if (strncmp_UTF16_char(table->entries[current].name,
+ (char *) name, 16) == 0) {
+ return &table->entries[current];
+ }
+ }
+
+ return NULL;
+}
+
+struct GPT_entry_raw *GPT_get_pointer_by_UTFname(struct GPT_entry_table *table, const uint16_t *name)
+{
+ int count = (int) table->header->entries_count;
+ int current;
+
+ for (current = 0; current < count; ++current) {
+ if (strncmp_UTF16(table->entries[current].name,
+ name, GPT_NAMELEN) == 0) {
+ return &table->entries[current];
+ }
+ }
+
+ return NULL;
+}
+
+void GPT_sync(struct GPT_entry_table *table)
+{
+ uint32_t crc;
+
+ //calculate crc32
+ crc = crc32(0, Z_NULL, 0);
+ crc = crc32(crc, (void*) table->entries, table->header->entries_count * sizeof(*table->entries));
+ table->header->partition_array_checksum = crc;
+
+ table->header->header_checksum = 0;
+ crc = crc32(0, Z_NULL, 0);
+ crc = crc32(crc, (void*) table->header, sizeof(*table->header));
+ table->header->header_checksum = crc;
+
+ //sync secondary partion
+ if (table->second_valid) {
+ memcpy((void *)table->second_entries, (void *) table->entries, table->partition_table_size);
+ memcpy((void *)table->second_header, (void *)table->header, sizeof(*table->header));
+ }
+
+ if(!ioctl(table->fd, BLKRRPART, NULL)) {
+ D(WARN, "Unable to force kernel to refresh partition table");
+ }
+}
+
+void GPT_to_UTF16(uint16_t *to, const char *from, int n)
+{
+ int i;
+ for (i = 0; i < (n - 1) && (to[i] = from[i]) != '\0'; ++i);
+ to[i] = '\0';
+}
+
+void GPT_from_UTF16(char *to, const uint16_t *from, int n)
+{
+ int i;
+ for (i = 0; i < (n - 1) && (to[i] = from[i] & 127) != '\0'; ++i);
+ to[i] = '\0';
+}
+
+static int GPT_check_overlap_except(struct GPT_entry_table *table,
+ struct GPT_entry_raw *entry,
+ struct GPT_entry_raw *exclude) {
+ int current = (int) table->header->entries_count;
+ int dontcheck;
+ struct GPT_entry_raw *current_entry;
+ if (entry->last_lba < entry->first_lba) {
+ D(WARN, "Start address have to be less than end address");
+ return 1;
+ }
+
+ for (current = current - 1; current >= 0; --current) {
+ current_entry = &table->entries[current];
+ dontcheck = strncmp((char *) entry->partition_guid,
+ (char *) current_entry->partition_guid , 16) == 0;
+ dontcheck |= current_entry->type_guid[0] == 0;
+ dontcheck |= current_entry == exclude;
+
+ if (!dontcheck && ((entry->last_lba >= current_entry->first_lba &&
+ entry->first_lba < current_entry->last_lba ))) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int GPT_check_overlap(struct GPT_entry_table *table, struct GPT_entry_raw *entry)
+{
+ return GPT_check_overlap_except(table, entry, NULL);
+}
+
+static char *get_key_value(char *ptr, char **key, char **value)
+{
+ *key = ptr;
+ ptr = strchr(ptr, '=');
+
+ if (ptr == NULL)
+ return NULL;
+
+ *ptr++ = '\0';
+ *value = ptr;
+ ptr = strchr(ptr, ';');
+
+ if (ptr == NULL)
+ ptr = *value + strlen(*value);
+ else
+ *ptr = '\0';
+
+ *key = strip(*key);
+ *value = strip(*value);
+
+ return ptr;
+}
+
+//TODO: little endian?
+static int add_key_value(const char *key, const char *value, struct GPT_entry_raw *entry)
+{
+ char *endptr;
+ if (!strcmp(key, "type")) {
+ strncpy((char *) entry->type_guid, value, 16);
+ entry->type_guid[15] = 0;
+ }
+ else if (!strcmp(key, "guid")) {
+ strncpy((char *) entry->partition_guid, value, 16);
+ entry->type_guid[15] = 0;
+ }
+ else if (!strcmp(key, "firstlba")) {
+ entry->first_lba = strtoul(value, &endptr, 10);
+ if (*endptr != '\0') goto error;
+ }
+ else if (!strcmp(key, "lastlba")) {
+ entry->last_lba = strtoul(value, &endptr, 10);
+ if (*endptr != '\0') goto error;
+ }
+ else if (!strcmp(key, "flags")) {
+ entry->flags = strtoul(value, &endptr, 10);
+ if (*endptr != '\0') goto error;
+ }
+ else if (!strcmp(key, "name")) {
+ GPT_to_UTF16(entry->name, value, GPT_NAMELEN);
+ }
+ else {
+ goto error;
+ }
+
+ return 0;
+
+error:
+ D(ERR, "Could not find key or parse value: %s,%s", key, value);
+ return 1;
+}
+
+int GPT_parse_entry(char *string, struct GPT_entry_raw *entry)
+{
+ char *ptr = string;
+ char *key, *value;
+
+ while ((ptr = get_key_value(ptr, &key, &value)) != NULL) {
+ if (add_key_value(key, value, entry)) {
+ D(WARN, "key or value not valid: %s %s", key, value);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static void entry_set_guid(int n, uint8_t *guid)
+{
+ guid[0] = (uint8_t) (n + 1);
+ int fd;
+ fd = open("/dev/urandom", O_RDONLY);
+ read(fd, &guid[1], 15);
+ close(fd);
+}
+
+void GPT_default_content(struct GPT_content *content, struct GPT_entry_table *table)
+{
+ if (table != NULL) {
+ memcpy(&content->header, table->header, sizeof(content->header));
+ }
+ else {
+ D(WARN, "Could not locate old gpt table, using default values");
+ memset(&content->header, 0, sizeof(content->header) / sizeof(int));
+ content->header = (struct GPT_header) {
+ .revision = 0x0100,
+ .header_size = sizeof(content->header),
+ .header_checksum = 0,
+ .reserved_zeros = 0,
+ .current_lba = 1,
+ .backup_lba = 1,
+ .entry_size = sizeof(struct GPT_entry_raw),
+ .partition_array_checksum = 0
+ };
+ strncpy((char *)content->header.signature, "EFI PART", 8);
+ entry_set_guid(0, content->header.disk_guid);
+ }
+}
+
+static int get_config_uint64(cnode *node, uint64_t *ptr, const char *name)
+{
+ const char *tmp;
+ uint64_t val;
+ char *endptr;
+ if ((tmp = config_str(node, name, NULL))) {
+ val = strtoull(tmp, &endptr, 10);
+ if (*endptr != '\0') {
+ D(WARN, "Value for %s is not a number: %s", name, tmp);
+ return 1;
+ }
+ *ptr = val;
+ return 0;
+ }
+ return 1;
+}
+
+static void GPT_parse_header(cnode *node, struct GPT_content *content)
+{
+ get_config_uint64(node, &content->header.current_lba, "header_lba");
+ get_config_uint64(node, &content->header.backup_lba, "backup_lba");
+ get_config_uint64(node, &content->header.first_usable_lba, "first_lba");
+ get_config_uint64(node, &content->header.last_usable_lba, "last_lba");
+ get_config_uint64(node, &content->header.entries_lba, "entries_lba");
+}
+
+static int GPT_parse_partitions(cnode *node, struct GPT_content *content)
+{
+ cnode *current;
+ int i;
+ uint64_t partition_size;
+ for (i = 0, current = node->first_child; current; current = current->next, ++i) {
+ entry_set_guid(i, content->entries[i].partition_guid);
+ memcpy(&content->entries[i].type_guid, partition_type_uuid, 16);
+ if (get_config_uint64(current, &content->entries[i].first_lba, "first_lba")) {
+ D(ERR, "first_lba not specified");
+ return 1;
+ }
+ if (get_config_uint64(current, &partition_size, "partition_size")) {
+ D(ERR, "partition_size not specified");
+ return 1;
+ }
+ get_config_uint64(current, &content->entries[i].flags, "flags");
+ content->entries[i].last_lba = content->entries[i].first_lba + partition_size - 1;
+ GPT_to_UTF16(content->entries[i].name, current->name, 16);
+ }
+ return 0;
+}
+
+static inline int cnode_count(cnode *node)
+{
+ int i;
+ cnode *current;
+ for (i = 0, current = node->first_child; current; current = current->next, ++i)
+ ;
+ return i;
+}
+
+
+static int GPT_parse_cnode(cnode *root, struct GPT_content *content)
+{
+ cnode *partnode;
+
+ if (!(partnode = config_find(root, "partitions"))) {
+ D(ERR, "Could not find partition table");
+ return 0;
+ }
+
+ GPT_parse_header(root, content);
+
+ content->header.entries_count = cnode_count(partnode);
+ content->entries = malloc(content->header.entries_count * sizeof(struct GPT_entry_raw));
+
+ if (GPT_parse_partitions(partnode, content)) {
+ D(ERR, "Could not parse partitions");
+ return 0;
+ }
+
+ return 1;
+}
+
+int GPT_parse_file(int fd, struct GPT_content *content)
+{
+ char *data;
+ int size;
+ int ret;
+ cnode *root = config_node("", "");
+
+ size = get_file_size(fd);
+ data = (char *) mmap(NULL, size + 1, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+
+ if (data == NULL) {
+ if (size == 0)
+ D(ERR, "config file empty");
+ else
+ D(ERR, "Out of memory");
+ return 0;
+ }
+
+ data[size - 1] = 0;
+ config_load(root, data);
+
+ if (root->first_child == NULL) {
+ D(ERR, "Could not read config file");
+ return 0;
+ }
+
+ ret = GPT_parse_cnode(root, content);
+ munmap(data, size);
+ return ret;
+}
+
+void GPT_release_content(struct GPT_content *content)
+{
+ free(content->entries);
+}
+
+int GPT_write_content(const char *device, struct GPT_content *content)
+{
+ struct GPT_entry_table *maptable;
+
+ maptable = GPT_get_from_content(device, content);
+ if (maptable == NULL) {
+ D(ERR, "could not map device");
+ return 0;
+ }
+
+ memcpy(maptable->header, &content->header, sizeof(*maptable->header));
+ memcpy(maptable->entries, content->entries,
+ content->header.entries_count * sizeof(*maptable->entries));
+
+ //GPT_sync(maptable);
+ GPT_release_device(maptable);
+
+ return 1;
+}
+
diff --git a/fastbootd/commands/partitions.h b/fastbootd/commands/partitions.h
new file mode 100644
index 0000000..f2d590f
--- /dev/null
+++ b/fastbootd/commands/partitions.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2009-2013, Google 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:
+ * * 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.
+ */
+
+
+#ifndef __FASTBOOTD_PATITIONS_
+#define __FASTBOOTD_PATITIONS_
+
+#include <stdint.h>
+
+#define GPT_ENTRIES 128
+#define GPT_NAMELEN 36
+
+// it should be passed in little endian order
+struct GPT_entry_raw {
+ uint8_t type_guid[16];
+ uint8_t partition_guid[16];
+ uint64_t first_lba; // little endian
+ uint64_t last_lba;
+ uint64_t flags;
+ uint16_t name[GPT_NAMELEN]; // UTF-16 LE
+};
+
+struct GPT_mapping {
+ void *map_ptr;
+ void *ptr;
+ unsigned size;
+};
+
+struct GPT_entry_table {
+ int fd;
+
+ struct GPT_mapping header_map;
+ struct GPT_mapping entries_map;
+ struct GPT_mapping sec_header_map;
+ struct GPT_mapping sec_entries_map;
+
+ struct GPT_header *header;
+ struct GPT_entry_raw *entries;
+ struct GPT_header *second_header;
+ struct GPT_entry_raw *second_entries;
+
+ unsigned sector_size;
+ unsigned partition_table_size;
+ int second_valid;
+};
+
+struct GPT_header {
+ uint8_t signature[8];
+ uint32_t revision;
+ uint32_t header_size;
+ uint32_t header_checksum;
+ uint32_t reserved_zeros;
+ uint64_t current_lba;
+ uint64_t backup_lba;
+ uint64_t first_usable_lba;
+ uint64_t last_usable_lba;
+ uint8_t disk_guid[16];
+ uint64_t entries_lba;
+ uint32_t entries_count;
+ uint32_t entry_size;
+ uint32_t partition_array_checksum;
+ // the rest should be filled with zeros
+} __attribute__((packed));
+
+struct GPT_content {
+ struct GPT_header header;
+ struct GPT_entry_raw *entries;
+};
+
+
+struct GPT_entry_table* GPT_get_device(const char *, unsigned lba);
+
+void GPT_release_device(struct GPT_entry_table *);
+
+void GPT_edit_entry(struct GPT_entry_table *table,
+ struct GPT_entry_raw *old_entry,
+ struct GPT_entry_raw *new_entry);
+
+int GPT_delete_entry(struct GPT_entry_table *table, struct GPT_entry_raw *entry);
+
+void GPT_add_entry(struct GPT_entry_table *table, struct GPT_entry_raw *entry);
+
+struct GPT_entry_raw *GPT_get_pointer(struct GPT_entry_table *table, struct GPT_entry_raw *entry);
+struct GPT_entry_raw *GPT_get_pointer_by_guid(struct GPT_entry_table *, const char *);
+struct GPT_entry_raw *GPT_get_pointer_by_name(struct GPT_entry_table *, const char *);
+
+//Use after every edit operation
+void GPT_sync();
+
+void GPT_to_UTF16(uint16_t *, const char *, int );
+void GPT_from_UTF16(char *, const uint16_t *, int);
+
+int GPT_parse_entry(char *string, struct GPT_entry_raw *entry);
+
+void GPT_default_content(struct GPT_content *content, struct GPT_entry_table *table);
+
+void GPT_release_content(struct GPT_content *content);
+
+int GPT_parse_file(int fd, struct GPT_content *content);
+
+int GPT_write_content(const char *device, struct GPT_content *content);
+
+int gpt_mmap(struct GPT_mapping *mapping, uint64_t location, int size, int fd);
+
+void gpt_unmap(struct GPT_mapping *mapping);
+
+#endif
diff --git a/fastbootd/commands/virtual_partitions.c b/fastbootd/commands/virtual_partitions.c
new file mode 100644
index 0000000..813f485
--- /dev/null
+++ b/fastbootd/commands/virtual_partitions.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2009-2013, Google 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:
+ * * 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 "commands/virtual_partitions.h"
+#include "debug.h"
+
+static struct virtual_partition *partitions = NULL;
+
+int try_handle_virtual_partition(struct protocol_handle *handle, const char *arg)
+{
+ struct virtual_partition *current;
+
+ for (current = partitions; current != NULL; current = current->next) {
+ if (!strcmp(current->name, arg)) {
+ current->handler(handle, arg);
+ }
+ }
+
+ return 0;
+}
+
+void virtual_partition_register(
+ const char * name,
+ void (*handler)(struct protocol_handle *phandle, const char *arg))
+{
+ struct virtual_partition *new;
+ new = malloc(sizeof(*new));
+ if (new) {
+ new->name = name;
+ new->handler = handler;
+ new->next = partitions;
+ partitions = new;
+ }
+ else {
+ D(ERR, "Out of memory");
+ }
+}
diff --git a/fastbootd/commands/virtual_partitions.h b/fastbootd/commands/virtual_partitions.h
new file mode 100644
index 0000000..88df71e
--- /dev/null
+++ b/fastbootd/commands/virtual_partitions.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2009-2013, Google 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:
+ * * 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.
+ */
+
+#ifndef FASTBOOTD_VIRTUAL_PARTITIONS_H
+#define FASTBOOTD_VIRTUAL_PARTITIONS_H
+
+#include "protocol.h"
+
+struct virtual_partition {
+ struct virtual_partition *next;
+ const char *name;
+ void (*handler)(struct protocol_handle *phandle, const char *arg);
+};
+
+int try_handle_virtual_partition(struct protocol_handle *handle, const char *arg);
+
+void virtual_partition_register(
+ const char * name,
+ void (*handler)(struct protocol_handle *phandle, const char *arg));
+
+#endif
diff --git a/fastbootd/config.c b/fastbootd/config.c
new file mode 100644
index 0000000..fe6da69
--- /dev/null
+++ b/fastbootd/config.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2013, Google 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:
+ * * 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 <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "protocol.h"
+#include "utils.h"
+
+#include "debug.h"
+
+// TODO: change config path
+#define CONFIG_PATH "/data/fastboot.cfg"
+
+static int config_parse_line(char *line)
+{
+ char *c;
+ char *key;
+ char *value;
+
+ c = strchr(line, '#');
+ if (c)
+ *c = '\0';
+
+ if (strspn(line, " \t") == strlen(line))
+ return 0;
+
+ c = strchr(line, '=');
+ if (c == NULL)
+ return -1;
+
+ key = line;
+ *c = '\0';
+ value = c + 1;
+
+ key = strip(key);
+ value = strip(value);
+
+ key = strdup(key);
+ value = strdup(value);
+
+ fastboot_publish(key, value);
+
+ return 0;
+}
+
+static void config_parse(char *buffer)
+{
+ char *saveptr;
+ char *str = buffer;
+ char *line = buffer;
+ int c;
+ int ret;
+
+ for (c = 1; line != NULL; c++) {
+ line = strtok_r(str, "\r\n", &saveptr);
+ if (line != NULL) {
+ D(VERBOSE, "'%s'", line);
+ ret = config_parse_line(line);
+ if (ret < 0) {
+ D(WARN, "error parsing " CONFIG_PATH " line %d", c);
+ }
+ }
+ str = NULL;
+ }
+}
+
+void config_init()
+{
+ int fd;
+ off_t len;
+ ssize_t ret;
+ size_t count = 0;
+ char *buffer;
+
+ fd = open(CONFIG_PATH, O_RDONLY);
+ if (fd < 0) {
+ D(ERR, "failed to open " CONFIG_PATH);
+ return;
+ }
+
+ len = lseek(fd, 0, SEEK_END);
+ if (len < 0) {
+ D(ERR, "failed to seek to end of " CONFIG_PATH);
+ return;
+ }
+
+ lseek(fd, 0, SEEK_SET);
+
+ buffer = malloc(len + 1);
+ if (buffer == NULL) {
+ D(ERR, "failed to allocate %ld bytes", len);
+ return;
+ }
+
+ while (count < (size_t)len) {
+ ret = read(fd, buffer + count, len - count);
+ if (ret < 0 && errno != EINTR) {
+ D(ERR, "failed to read " CONFIG_PATH ": %d %s", errno, strerror(errno));
+ return;
+ }
+ if (ret == 0) {
+ D(ERR, "early EOF reading " CONFIG_PATH);
+ return;
+ }
+
+ count += ret;
+ }
+
+ buffer[len] = '\0';
+
+ config_parse(buffer);
+
+ free(buffer);
+}
diff --git a/fastbootd/debug.h b/fastbootd/debug.h
new file mode 100644
index 0000000..74620b8
--- /dev/null
+++ b/fastbootd/debug.h
@@ -0,0 +1,42 @@
+/*
+ * 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 _FASTBOOTD_DEBUG_H_
+#define _FASTBOOTD_DEBUG_H_
+
+#include <stdio.h>
+
+#include <cutils/klog.h>
+
+#define ERR 0
+#define WARN 1
+#define INFO 2
+#define VERBOSE 3
+#define DEBUG 4
+
+extern unsigned int debug_level;
+
+//#define DLOG(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#define DLOG(fmt, ...) KLOG_INFO("fastbootd", fmt, ##__VA_ARGS__)
+
+#define D(level, fmt, ...) \
+ do { \
+ if (debug_level == level || debug_level > level) { \
+ DLOG("%s:%d " fmt "\n", __BASE_FILE__, __LINE__, ##__VA_ARGS__); \
+ } \
+ } while (0)
+
+#endif
diff --git a/fastbootd/fastbootd.c b/fastbootd/fastbootd.c
new file mode 100644
index 0000000..90b9ef9
--- /dev/null
+++ b/fastbootd/fastbootd.c
@@ -0,0 +1,47 @@
+/*
+ * 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 <unistd.h>
+
+#include <cutils/klog.h>
+
+#include "debug.h"
+#include "trigger.h"
+
+unsigned int debug_level = DEBUG;
+
+void commands_init();
+void usb_init();
+void config_init();
+
+int main(int argc, char **argv)
+{
+ (void)argc;
+ (void)argv;
+
+ klog_init();
+ klog_set_level(6);
+
+ config_init();
+ load_trigger();
+ commands_init();
+ usb_init();
+ while (1) {
+ sleep(1);
+ }
+ return 0;
+}
diff --git a/fastbootd/include/vendor_trigger.h b/fastbootd/include/vendor_trigger.h
new file mode 100644
index 0000000..66f65c4
--- /dev/null
+++ b/fastbootd/include/vendor_trigger.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2009-2013, Google 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:
+ * * 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.
+ */
+
+#ifndef __VENDOR_TRIGGER_H_
+#define __VENDOR_TRIGGER_H_
+
+#define TRIGGER_MODULE_ID "vendortrigger"
+#include <hardware/hardware.h>
+
+__BEGIN_DECLS
+
+struct GPT_entry_raw;
+struct GPT_content;
+
+/*
+ * Structer with function pointers may become longer in the future
+ */
+
+struct vendor_trigger_t {
+ struct hw_device_t common;
+
+ /*
+ * This function runs at the beggining and shoud never be changed
+ *
+ * version is number parameter indicating version on the fastbootd side
+ * libversion is version indicateing version of the library version
+ *
+ * returns 0 if it can cooperate with the current version and 1 in opposite
+ */
+ int (*check_version)(const int version, int *libversion);
+
+
+ /*
+ * Return value 1 forbid the action from the vendor site and sets errno
+ */
+ int (* gpt_layout)(struct GPT_content *);
+ int (* oem_cmd)(const char *arg, const char **response);
+};
+
+__END_DECLS
+
+#endif
diff --git a/fastbootd/other/gptedit.c b/fastbootd/other/gptedit.c
new file mode 100644
index 0000000..07d01cb
--- /dev/null
+++ b/fastbootd/other/gptedit.c
@@ -0,0 +1,293 @@
+/*
+ * Copyright (c) 2009-2013, Google 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:
+ * * 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 <stdlib.h>
+#include <getopt.h>
+#include <unistd.h>
+
+#include <cutils/klog.h>
+
+#include "commands/partitions.h"
+#include "debug.h"
+
+unsigned int debug_level = DEBUG;
+//TODO: add tool to generate config file
+
+void usage() {
+ fprintf(stderr,
+ "usage: test_gpt [ <option> ] <file>\n"
+ "\n"
+ "options:\n"
+ " -p print partitions\n"
+ " -c print config file\n"
+ " -a adds new partition\n"
+ " -d deletes partition (-o needed)\n"
+ "\n"
+ " -n name@startlba,endlba new partition detail\n"
+ " -o old partition name\n"
+ " -t type guid\n"
+ " -g partition guid\n"
+ " -l gpt_location specyfies gpt secto\n"
+ );
+
+}
+
+void printGPT(struct GPT_entry_table *table);
+void addGPT(struct GPT_entry_table *table, const char *arg, const char *guid, const char *tguid);
+void deleteGPT(struct GPT_entry_table *table, const char *name);
+void configPrintGPT(struct GPT_entry_table *table);
+
+int main(int argc, char *argv[]) {
+ int print_cmd = 0;
+ int config_cmd = 0;
+ int add_cmd = 0;
+ int del_cmd = 0;
+ int sync_cmd = 0;
+ int c;
+ const char *new_partition = NULL;
+ const char *old_partition = NULL;
+ const char *type_guid = NULL;
+ const char *partition_guid = NULL;
+ unsigned gpt_location = 1;
+
+ klog_init();
+ klog_set_level(6);
+
+ const struct option longopts[] = {
+ {"print", no_argument, 0, 'p'},
+ {"config-print", no_argument, 0, 'c'},
+ {"add", no_argument, 0, 'a'},
+ {"del", no_argument, 0, 'd'},
+ {"new", required_argument, 0, 'n'},
+ {"old", required_argument, 0, 'o'},
+ {"type", required_argument, 0, 't'},
+ {"sync", required_argument, 0, 's'},
+ {"guid", required_argument, 0, 'g'},
+ {"location", required_argument, 0, 'l'},
+ {0, 0, 0, 0}
+ };
+
+ while (1) {
+ c = getopt_long(argc, argv, "pcadt:g:n:o:sl:", longopts, NULL);
+ /* Alphabetical cases */
+ if (c < 0)
+ break;
+ switch (c) {
+ case 'p':
+ print_cmd = 1;
+ break;
+ case 'c':
+ config_cmd = 1;
+ break;
+ case 'a':
+ add_cmd = 1;
+ break;
+ case 'd':
+ del_cmd = 1;
+ break;
+ case 'n':
+ new_partition = optarg;
+ break;
+ case 'o':
+ old_partition = optarg;
+ break;
+ case 't':
+ type_guid = optarg;
+ case 'g':
+ partition_guid = optarg;
+ break;
+ case 's':
+ sync_cmd = 1;
+ break;
+ case 'l':
+ gpt_location = strtoul(optarg, NULL, 10);
+ fprintf(stderr, "Got offset as %d", gpt_location);
+ break;
+ case '?':
+ return 1;
+ default:
+ abort();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ usage();
+ return 1;
+ }
+
+ const char *path = argv[0];
+ struct GPT_entry_table *table = GPT_get_device(path, gpt_location);
+ if (table == NULL) {
+ fprintf(stderr, "unable to get GPT table from %s\n", path);
+ return 1;
+ }
+
+ fprintf(stderr, "entries %d, name %s\n", table->header->entries_count, (char *) table->header->signature);
+
+
+ if (add_cmd)
+ addGPT(table, new_partition, partition_guid, type_guid);
+ if (del_cmd)
+ deleteGPT(table, old_partition);
+ if (print_cmd)
+ printGPT(table);
+ if (config_cmd)
+ configPrintGPT(table);
+ if (sync_cmd)
+ GPT_sync(table);
+
+ GPT_release_device(table);
+
+ return 0;
+}
+
+void printGPT(struct GPT_entry_table *table) {
+ struct GPT_entry_raw *entry = table->entries;
+ unsigned n, m;
+ char name[GPT_NAMELEN + 1];
+
+ printf("ptn start block end block name\n");
+ printf("---- ------------- -------------\n");
+
+ for (n = 0; n < table->header->entries_count; n++, entry++) {
+ if (entry->type_guid[0] == 0)
+ continue;
+ for (m = 0; m < GPT_NAMELEN; m++) {
+ name[m] = entry->name[m] & 127;
+ }
+ name[m] = 0;
+ printf("#%03d %13lld %13lld %s\n",
+ n + 1, entry->first_lba, entry->last_lba, name);
+ }
+}
+
+void configPrintGPT(struct GPT_entry_table *table) {
+ struct GPT_entry_raw *entry = table->entries;
+ unsigned n, m;
+ char name[GPT_NAMELEN + 1];
+
+ printf("header_lba %lld\n", table->header->current_lba);
+ printf("backup_lba %lld\n", table->header->backup_lba);
+ printf("first_lba %lld\n", table->header->first_usable_lba);
+ printf("last_lba %lld\n", table->header->last_usable_lba);
+ printf("entries_lba %lld\n", table->header->entries_lba);
+
+ printf("\npartitions {\n");
+
+ for (n = 0; n < table->header->entries_count; n++, entry++) {
+ uint64_t size = entry->last_lba - entry->first_lba + 1;
+
+ if (entry->type_guid[0] == 0)
+ continue;
+ for (m = 0; m < GPT_NAMELEN; m++) {
+ name[m] = entry->name[m] & 127;
+ }
+ name[m] = 0;
+
+ printf(" %s {\n", name);
+ printf(" first_lba %lld\n", entry->first_lba);
+ printf(" partition_size %lld\n", size);
+ if (entry->flags != 0)
+ printf(" flags %lld\n", entry->flags);
+ printf(" }\n\n");
+ }
+ printf("}\n");
+}
+
+void addGPT(struct GPT_entry_table *table, const char *str , const char *guid, const char *tguid) {
+ char *c, *c2;
+ char *arg = malloc(strlen(str));
+ char *name = arg;
+ unsigned start, end;
+ strcpy(arg, str);
+ if (guid == NULL || tguid == NULL) {
+ fprintf(stderr, "Type guid and partion guid needed");
+ free(arg);
+ return;
+ }
+
+ c = strchr(arg, '@');
+
+ if (c == NULL) {
+ fprintf(stderr, "Wrong entry format");
+ free(arg);
+ return;
+ }
+
+ *c++ = '\0';
+
+ c2 = strchr(c, ',');
+
+ if (c2 == NULL) {
+ fprintf(stderr, "Wrong entry format");
+ free(arg);
+ return;
+ }
+
+ start = strtoul(c, NULL, 10);
+ *c2++ = '\0';
+ end = strtoul(c2, NULL, 10);
+
+ struct GPT_entry_raw data;
+ strncpy((char *)data.partition_guid, guid, 15);
+ data.partition_guid[15] = '\0';
+ strncpy((char *)data.type_guid, tguid, 15);
+ data.type_guid[15] = '\0';
+ GPT_to_UTF16(data.name, name, GPT_NAMELEN);
+ data.first_lba = start;
+ data.last_lba = end;
+
+ fprintf(stderr, "Adding (%d,%d) %s as, [%s, %s]", start, end, name, (char *) data.type_guid, (char *) data.partition_guid);
+ GPT_add_entry(table, &data);
+ free(arg);
+}
+
+void deleteGPT(struct GPT_entry_table *table, const char *name) {
+ struct GPT_entry_raw *entry;
+
+ if (name == NULL) {
+ fprintf(stderr, "Need partition name");
+ return;
+ }
+
+ entry = GPT_get_pointer_by_name(table, name);
+
+ if (!entry) {
+ fprintf(stderr, "Unable to find partition: %s", name);
+ return;
+ }
+ GPT_delete_entry(table, entry);
+}
+
diff --git a/fastbootd/other/partitions.sample.cfg b/fastbootd/other/partitions.sample.cfg
new file mode 100644
index 0000000..49562cf
--- /dev/null
+++ b/fastbootd/other/partitions.sample.cfg
@@ -0,0 +1,60 @@
+
+header_lba 1
+backup_lba 101
+first_lba 43
+last_lba 100
+entries_lba 2
+
+partitions {
+ #You can generate this as output from gptedit -c
+ SOS {
+ first_lba 28672
+ partition_size 16384
+ }
+
+ DTB {
+ first_lba 45056
+ partition_size 8192
+ }
+
+ LNX {
+ first_lba 53248
+ partition_size 16384
+ }
+
+ APP {
+ first_lba 69632
+ partition_size 1048576
+ }
+
+ CAC {
+ first_lba 1118208
+ partition_size 1572864
+ }
+
+ MSC {
+ first_lba 2691072
+ partition_size 4096
+ }
+
+ USP {
+ first_lba 2695168
+ partition_size 65536
+ }
+
+ MDA {
+ first_lba 2760704
+ partition_size 4096
+ }
+
+ FCT {
+ first_lba 2764800
+ partition_size 32768
+ }
+
+ UDA {
+ first_lba 2797568
+ partition_size 27975680
+ }
+}
+
diff --git a/fastbootd/other/vendor_trigger.c b/fastbootd/other/vendor_trigger.c
new file mode 100644
index 0000000..9b7562c
--- /dev/null
+++ b/fastbootd/other/vendor_trigger.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2009-2013, Google 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:
+ * * 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 <stdlib.h>
+
+#include "vendor_trigger.h"
+#include "debug.h"
+
+unsigned int debug_level = DEBUG;
+
+static const int version = 1;
+
+int delete_partition(struct GPT_entry_raw *);
+int add_partition(struct GPT_entry_raw *);
+int modify_partition(struct GPT_entry_raw *, struct GPT_entry_raw *);
+
+int check_version(const int fastboot_version, int *libversion) {
+ *libversion = version;
+ return !(fastboot_version == version);
+}
+
+int delete_partition(struct GPT_entry_raw *entry) {
+ D(DEBUG, "message from libvendor");
+ return 0;
+}
+
+int add_partition(struct GPT_entry_raw *entry) {
+ D(DEBUG, "message from libvendor");
+ return 0;
+}
+
+int modify_partition(struct GPT_entry_raw *oldentry, struct GPT_entry_raw *newentry) {
+ D(DEBUG, "message from libvendor");
+ return 0;
+}
+
+int gpt_layout(struct GPT_content *table) {
+ D(DEBUG, "message from libvendor");
+ return 0;
+}
+
+int oem_cmd(const char *arg, const char **response) {
+ D(DEBUG, "message from libvendor, oem catched request %s", arg);
+ return 0;
+}
+
+static int close_triggers(struct vendor_trigger_t *dev)
+{
+ if (dev)
+ free(dev);
+
+ return 0;
+}
+
+static int open_triggers(const struct hw_module_t *module, char const *name,
+ struct hw_device_t **device) {
+ struct vendor_trigger_t *dev = malloc(sizeof(struct vendor_trigger_t));
+ klog_init();
+ klog_set_level(6);
+
+ memset(dev, 0, sizeof(*dev));
+ dev->common.module = (struct hw_module_t *) module;
+ dev->common.close = (int (*)(struct hw_device_t *)) close_triggers;
+
+ dev->gpt_layout = gpt_layout;
+ dev->oem_cmd = oem_cmd;
+
+ *device = (struct hw_device_t *) dev;
+
+ return 0;
+}
+
+
+static struct hw_module_methods_t trigger_module_methods = {
+ .open = open_triggers,
+};
+
+struct hw_module_t HAL_MODULE_INFO_SYM = {
+ .tag = HARDWARE_MODULE_TAG,
+ .version_major = 1,
+ .version_minor = 0,
+ .id = TRIGGER_MODULE_ID,
+ .name = "vendor trigger library for fastbootd",
+ .author = "Google, Inc.",
+ .methods = &trigger_module_methods,
+};
+
diff --git a/fastbootd/protocol.c b/fastbootd/protocol.c
new file mode 100644
index 0000000..0086b4a
--- /dev/null
+++ b/fastbootd/protocol.c
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2009-2013, Google 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:
+ * * 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 <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "debug.h"
+#include "protocol.h"
+#include "transport.h"
+
+#define STATE_OFFLINE 0
+#define STATE_COMMAND 1
+#define STATE_COMPLETE 2
+#define STATE_ERROR 3
+
+struct fastboot_cmd {
+ struct fastboot_cmd *next;
+ const char *prefix;
+ unsigned prefix_len;
+ void (*execute)(struct protocol_handle *phandle, const char *arg);
+};
+
+struct fastboot_var {
+ struct fastboot_var *next;
+ const char *name;
+ const char *value;
+};
+
+static struct fastboot_cmd *cmdlist;
+
+void fastboot_register(const char *prefix,
+ void (*phandle)(struct protocol_handle *phandle, const char *arg))
+{
+ struct fastboot_cmd *cmd;
+ cmd = malloc(sizeof(*cmd));
+ if (cmd) {
+ cmd->prefix = prefix;
+ cmd->prefix_len = strlen(prefix);
+ cmd->execute = phandle;
+ cmd->next = cmdlist;
+ cmdlist = cmd;
+ }
+}
+
+static struct fastboot_var *varlist;
+
+void fastboot_publish(const char *name, const char *value)
+{
+ struct fastboot_var *var;
+ var = malloc(sizeof(*var));
+ if (var) {
+ var->name = name;
+ var->value = value;
+ var->next = varlist;
+ varlist = var;
+ }
+}
+
+const char *fastboot_getvar(const char *name)
+{
+ struct fastboot_var *var;
+
+ for (var = varlist; var; var = var->next) {
+ if (!strcmp(var->name, name)) {
+ return var->value;
+ }
+ }
+
+ return "";
+}
+
+int protocol_handle_download(struct protocol_handle *phandle, size_t len)
+{
+ return transport_handle_download(phandle->transport_handle, len);
+}
+
+static ssize_t protocol_handle_write(struct protocol_handle *phandle,
+ char *buffer, size_t len)
+{
+ return transport_handle_write(phandle->transport_handle, buffer, len);
+}
+
+static void fastboot_ack(struct protocol_handle *phandle, const char *code,
+ const char *reason)
+{
+ char response[64];
+
+ if (phandle->state != STATE_COMMAND)
+ return;
+
+ if (reason == 0)
+ reason = "";
+
+ snprintf(response, 64, "%s%s", code, reason);
+ phandle->state = STATE_COMPLETE;
+
+ protocol_handle_write(phandle, response, strlen(response));
+}
+
+void fastboot_fail(struct protocol_handle *phandle, const char *reason)
+{
+ fastboot_ack(phandle, "FAIL", reason);
+}
+
+void fastboot_okay(struct protocol_handle *phandle, const char *info)
+{
+ fastboot_ack(phandle, "OKAY", info);
+}
+
+void fastboot_data(struct protocol_handle *phandle, size_t len)
+{
+ char response[64];
+ ssize_t ret;
+
+ snprintf(response, 64, "DATA%08x", len);
+ ret = protocol_handle_write(phandle, response, strlen(response));
+ if (ret < 0)
+ return;
+}
+
+void protocol_handle_command(struct protocol_handle *phandle, char *buffer)
+{
+ D(INFO,"fastboot: %s\n", buffer);
+
+ struct fastboot_cmd *cmd;
+
+ for (cmd = cmdlist; cmd; cmd = cmd->next) {
+ if (memcmp(buffer, cmd->prefix, cmd->prefix_len))
+ continue;
+ phandle->state = STATE_COMMAND;
+ cmd->execute(phandle, buffer + cmd->prefix_len);
+ if (phandle->state == STATE_COMMAND)
+ fastboot_fail(phandle, "unknown reason");
+ return;
+ }
+
+ fastboot_fail(phandle, "unknown command");
+}
+
+struct protocol_handle *create_protocol_handle(struct transport_handle *thandle)
+{
+ struct protocol_handle *phandle;
+
+ phandle = calloc(sizeof(struct protocol_handle), 1);
+
+ phandle->transport_handle = thandle;
+ phandle->state = STATE_OFFLINE;
+ phandle->download_fd = -1;
+
+ pthread_mutex_init(&phandle->lock, NULL);
+
+ return phandle;
+}
+
+int protocol_get_download(struct protocol_handle *phandle)
+{
+ int fd;
+
+ pthread_mutex_lock(&phandle->lock);
+ fd = phandle->download_fd;
+ phandle->download_fd = -1;
+ pthread_mutex_unlock(&phandle->lock);
+
+ return fd;
+}
diff --git a/fastbootd/protocol.h b/fastbootd/protocol.h
new file mode 100644
index 0000000..ea2a8df
--- /dev/null
+++ b/fastbootd/protocol.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2013, Google 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:
+ * * 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.
+ */
+
+#ifndef _FASTBOOTD_PROTOCOL_H_
+#define _FASTBOOTD_PROTOCOL_H_
+
+#include <pthread.h>
+#include <stddef.h>
+
+struct protocol_handle {
+ struct transport_handle *transport_handle;
+ unsigned int state;
+ int download_fd;
+
+ pthread_mutex_t lock;
+};
+
+void fastboot_register(const char *prefix,
+ void (*handle)(struct protocol_handle *handle, const char *arg));
+
+void fastboot_publish(const char *name, const char *value);
+const char *fastboot_getvar(const char *name);
+
+struct protocol_handle *create_protocol_handle(struct transport_handle *t);
+void protocol_handle_command(struct protocol_handle *handle, char *buffer);
+int protocol_handle_download(struct protocol_handle *phandle, size_t len);
+int protocol_get_download(struct protocol_handle *phandle);
+
+void fastboot_fail(struct protocol_handle *handle, const char *reason);
+void fastboot_okay(struct protocol_handle *handle, const char *reason);
+void fastboot_data(struct protocol_handle *handle, size_t len);
+
+#endif
diff --git a/fastbootd/transport.c b/fastbootd/transport.c
new file mode 100644
index 0000000..01a5a8a
--- /dev/null
+++ b/fastbootd/transport.c
@@ -0,0 +1,149 @@
+/*
+ * 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 <pthread.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+#include "debug.h"
+#include "protocol.h"
+#include "transport.h"
+
+#define COMMAND_BUF_SIZE 64
+
+ssize_t transport_handle_write(struct transport_handle *thandle, char *buffer, size_t len)
+{
+ return thandle->transport->write(thandle, buffer, len);
+}
+
+void transport_handle_close(struct transport_handle *thandle)
+{
+ thandle->transport->close(thandle);
+}
+
+int transport_handle_download(struct transport_handle *thandle, size_t len)
+{
+ ssize_t ret;
+ size_t n = 0;
+ int fd;
+ // TODO: move out of /dev
+ char tempname[] = "/dev/fastboot_download_XXXXXX";
+ char *buffer;
+
+ fd = mkstemp(tempname);
+ if (fd < 0)
+ return -1;
+
+ unlink(tempname);
+
+ ftruncate(fd, len);
+
+ buffer = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (buffer == NULL) {
+ D(ERR, "mmap(%u) failed: %d %s", len, errno, strerror(errno));
+ goto err;
+ }
+
+ while (n < len) {
+ ret = thandle->transport->read(thandle, buffer + n, len - n);
+ if (ret <= 0) {
+ D(WARN, "transport read failed, ret=%d %s", ret, strerror(-ret));
+ break;
+ }
+ n += ret;
+ }
+
+ munmap(buffer, len);
+
+ if (n != len)
+ goto err;
+
+ return fd;
+
+err:
+ close(fd);
+ transport_handle_close(thandle);
+ return -1;
+}
+
+static void *transport_data_thread(void *arg)
+{
+ struct transport_handle *thandle = arg;
+ struct protocol_handle *phandle = create_protocol_handle(thandle);
+
+ while (!thandle->stopped) {
+ int ret;
+ char buffer[COMMAND_BUF_SIZE + 1];
+ D(VERBOSE, "transport_data_thread\n");
+
+ ret = thandle->transport->read(thandle, buffer, COMMAND_BUF_SIZE);
+ if (ret <= 0) {
+ D(DEBUG, "ret = %d\n", ret);
+ break;
+ }
+ if (ret > 0) {
+ buffer[ret] = 0;
+ protocol_handle_command(phandle, buffer);
+ }
+ }
+
+ transport_handle_close(thandle);
+ free(thandle);
+
+ return NULL;
+}
+
+static void *transport_connect_thread(void *arg)
+{
+ struct transport *transport = arg;
+ while (!transport->stopped) {
+ struct transport_handle *thandle;
+ pthread_t thread;
+ pthread_attr_t attr;
+
+ D(VERBOSE, "transport_connect_thread\n");
+ thandle = transport->connect(transport);
+ if (thandle == NULL) {
+ D(ERR, "transport connect failed\n");
+ sleep(1);
+ continue;
+ }
+ thandle->transport = transport;
+
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+ pthread_create(&thread, &attr, transport_data_thread, thandle);
+
+ sleep(1);
+ }
+
+ return NULL;
+}
+
+void transport_register(struct transport *transport)
+{
+ pthread_t thread;
+ pthread_attr_t attr;
+
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+ pthread_create(&thread, &attr, transport_connect_thread, transport);
+}
diff --git a/fastbootd/transport.h b/fastbootd/transport.h
new file mode 100644
index 0000000..209340d
--- /dev/null
+++ b/fastbootd/transport.h
@@ -0,0 +1,41 @@
+/*
+ * 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 _FASTBOOTD_TRANSPORT_H_
+#define _FASTBOOTD_TRANSPORT_H_
+
+#include <stdbool.h>
+
+struct transport_handle {
+ struct transport *transport;
+
+ bool stopped;
+};
+
+struct transport {
+ void (*init)();
+ void (*close)(struct transport_handle *thandle);
+ ssize_t (*read)(struct transport_handle *thandle, void *data, size_t len);
+ ssize_t (*write)(struct transport_handle *thandle, const void *data, size_t len);
+ struct transport_handle *(*connect)(struct transport *transport);
+ bool stopped;
+};
+
+void transport_register(struct transport *transport);
+ssize_t transport_handle_write(struct transport_handle *handle, char *buffer, size_t len);
+int transport_handle_download(struct transport_handle *handle, size_t len);
+
+#endif
diff --git a/fastbootd/trigger.c b/fastbootd/trigger.c
new file mode 100644
index 0000000..e63e64d
--- /dev/null
+++ b/fastbootd/trigger.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2009-2013, Google 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:
+ * * 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 <dlfcn.h>
+
+#include <hardware/hardware.h>
+#include "debug.h"
+#include "trigger.h"
+#include "protocol.h"
+#include "vendor_trigger.h"
+
+static const int version = 1;
+
+static struct vendor_trigger_t *triggers = NULL;
+
+int load_trigger() {
+ int err;
+ hw_module_t* module;
+ hw_device_t* device;
+ int libversion;
+
+ err = hw_get_module(TRIGGER_MODULE_ID, (hw_module_t const**)&module);
+
+ if (err == 0) {
+ err = module->methods->open(module, NULL, &device);
+
+ if (err == 0) {
+ triggers = (struct vendor_trigger_t *) device;
+ } else {
+ D(WARN, "Libvendor load error");
+ return 1;
+ }
+ }
+ else {
+ D(WARN, "Libvendor not load: %s", strerror(-err));
+ return 0;
+ }
+
+ if (triggers->check_version != NULL &&
+ triggers->check_version(version, &libversion)) {
+
+ triggers = NULL;
+ D(ERR, "Library report incompability");
+ return 1;
+ }
+ D(INFO, "libvendortrigger loaded");
+
+ return 0;
+}
+
+int trigger_oem_cmd(const char *arg, const char **response) {
+ if (triggers != NULL && triggers->oem_cmd != NULL)
+ return triggers->oem_cmd(arg, response);
+ return 0;
+}
+
+int trigger_gpt_layout(struct GPT_content *table) {
+ if (triggers != NULL && triggers->gpt_layout != NULL)
+ return triggers->gpt_layout(table);
+ return 0;
+}
+
diff --git a/fastbootd/trigger.h b/fastbootd/trigger.h
new file mode 100644
index 0000000..404acb4
--- /dev/null
+++ b/fastbootd/trigger.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2009-2013, Google 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:
+ * * 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.
+ */
+
+#ifndef __FASTBOOTD_TRIGGER_H_
+#define __FASTBOOTD_TRIGGER_H_
+
+#include "commands/partitions.h"
+#include "vendor_trigger.h"
+
+int load_trigger();
+
+/* same as in struct triggers */
+
+int trigger_gpt_layout(struct GPT_content *table);
+int trigger_oem_cmd(const char *arg, const char **response);
+
+#endif
diff --git a/fastbootd/usb_linux_client.c b/fastbootd/usb_linux_client.c
new file mode 100644
index 0000000..111cf35
--- /dev/null
+++ b/fastbootd/usb_linux_client.c
@@ -0,0 +1,353 @@
+/*
+ * 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 <endian.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/functionfs.h>
+
+#include "debug.h"
+#include "transport.h"
+
+#define TRACE_TAG TRACE_USB
+
+#define MAX_PACKET_SIZE_FS 64
+#define MAX_PACKET_SIZE_HS 512
+
+#define cpu_to_le16(x) htole16(x)
+#define cpu_to_le32(x) htole32(x)
+
+#define FASTBOOT_CLASS 0xff
+#define FASTBOOT_SUBCLASS 0x42
+#define FASTBOOT_PROTOCOL 0x3
+
+#define USB_FFS_FASTBOOT_PATH "/dev/usb-ffs/adb/"
+#define USB_FFS_FASTBOOT_EP(x) USB_FFS_FASTBOOT_PATH#x
+
+#define USB_FFS_FASTBOOT_EP0 USB_FFS_FASTBOOT_EP(ep0)
+#define USB_FFS_FASTBOOT_OUT USB_FFS_FASTBOOT_EP(ep1)
+#define USB_FFS_FASTBOOT_IN USB_FFS_FASTBOOT_EP(ep2)
+
+#define READ_BUF_SIZE (16*1024)
+
+#define container_of(ptr, type, member) \
+ ((type*)((char*)(ptr) - offsetof(type, member)))
+
+struct usb_transport {
+ struct transport transport;
+
+ pthread_cond_t notify;
+ pthread_mutex_t lock;
+
+ int control;
+ int bulk_out; /* "out" from the host's perspective => source for fastbootd */
+ int bulk_in; /* "in" from the host's perspective => sink for fastbootd */
+};
+
+struct usb_handle {
+ struct transport_handle handle;
+};
+
+static const struct {
+ struct usb_functionfs_descs_head header;
+ struct {
+ struct usb_interface_descriptor intf;
+ struct usb_endpoint_descriptor_no_audio source;
+ struct usb_endpoint_descriptor_no_audio sink;
+ } __attribute__((packed)) fs_descs, hs_descs;
+} __attribute__((packed)) descriptors = {
+ .header = {
+ .magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC),
+ .length = cpu_to_le32(sizeof(descriptors)),
+ .fs_count = 3,
+ .hs_count = 3,
+ },
+ .fs_descs = {
+ .intf = {
+ .bLength = sizeof(descriptors.fs_descs.intf),
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = FASTBOOT_CLASS,
+ .bInterfaceSubClass = FASTBOOT_SUBCLASS,
+ .bInterfaceProtocol = FASTBOOT_PROTOCOL,
+ .iInterface = 1, /* first string from the provided table */
+ },
+ .source = {
+ .bLength = sizeof(descriptors.fs_descs.source),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 1 | USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = MAX_PACKET_SIZE_FS,
+ },
+ .sink = {
+ .bLength = sizeof(descriptors.fs_descs.sink),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 2 | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = MAX_PACKET_SIZE_FS,
+ },
+ },
+ .hs_descs = {
+ .intf = {
+ .bLength = sizeof(descriptors.hs_descs.intf),
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = FASTBOOT_CLASS,
+ .bInterfaceSubClass = FASTBOOT_SUBCLASS,
+ .bInterfaceProtocol = FASTBOOT_PROTOCOL,
+ .iInterface = 1, /* first string from the provided table */
+ },
+ .source = {
+ .bLength = sizeof(descriptors.hs_descs.source),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 1 | USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = MAX_PACKET_SIZE_HS,
+ },
+ .sink = {
+ .bLength = sizeof(descriptors.hs_descs.sink),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 2 | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = MAX_PACKET_SIZE_HS,
+ },
+ },
+};
+
+#define STR_INTERFACE_ "Fastboot 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 int init_functionfs(struct usb_transport *usb_transport)
+{
+ ssize_t ret;
+
+ D(VERBOSE, "OPENING %s", USB_FFS_FASTBOOT_EP0);
+ usb_transport->control = open(USB_FFS_FASTBOOT_EP0, O_RDWR);
+ if (usb_transport->control < 0) {
+ D(ERR, "[ %s: cannot open control endpoint: errno=%d]", USB_FFS_FASTBOOT_EP0, errno);
+ goto err;
+ }
+
+ ret = write(usb_transport->control, &descriptors, sizeof(descriptors));
+ if (ret < 0) {
+ D(ERR, "[ %s: write descriptors failed: errno=%d ]", USB_FFS_FASTBOOT_EP0, errno);
+ goto err;
+ }
+
+ ret = write(usb_transport->control, &strings, sizeof(strings));
+ if (ret < 0) {
+ D(ERR, "[ %s: writing strings failed: errno=%d]", USB_FFS_FASTBOOT_EP0, errno);
+ goto err;
+ }
+
+ usb_transport->bulk_out = open(USB_FFS_FASTBOOT_OUT, O_RDWR);
+ if (usb_transport->bulk_out < 0) {
+ D(ERR, "[ %s: cannot open bulk-out ep: errno=%d ]", USB_FFS_FASTBOOT_OUT, errno);
+ goto err;
+ }
+
+ usb_transport->bulk_in = open(USB_FFS_FASTBOOT_IN, O_RDWR);
+ if (usb_transport->bulk_in < 0) {
+ D(ERR, "[ %s: cannot open bulk-in ep: errno=%d ]", USB_FFS_FASTBOOT_IN, errno);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ if (usb_transport->bulk_in > 0) {
+ close(usb_transport->bulk_in);
+ usb_transport->bulk_in = -1;
+ }
+ if (usb_transport->bulk_out > 0) {
+ close(usb_transport->bulk_out);
+ usb_transport->bulk_out = -1;
+ }
+ if (usb_transport->control > 0) {
+ close(usb_transport->control);
+ usb_transport->control = -1;
+ }
+ return -1;
+}
+
+static ssize_t bulk_write(int bulk_in, const char *buf, size_t length)
+{
+ size_t count = 0;
+ ssize_t ret;
+
+ do {
+ ret = TEMP_FAILURE_RETRY(write(bulk_in, buf + count, length - count));
+ if (ret < 0) {
+ D(WARN, "[ bulk_read failed fd=%d length=%d errno=%d %s ]",
+ bulk_in, length, errno, strerror(errno));
+ return -1;
+ } else {
+ count += ret;
+ }
+ } while (count < length);
+
+ D(VERBOSE, "[ bulk_write done fd=%d ]", bulk_in);
+ return count;
+}
+
+static ssize_t usb_write(struct transport_handle *thandle, const void *data, size_t len)
+{
+ ssize_t ret;
+ struct transport *t = thandle->transport;
+ struct usb_transport *usb_transport = container_of(t, struct usb_transport, transport);
+
+ D(DEBUG, "about to write (fd=%d, len=%d)", usb_transport->bulk_in, len);
+ ret = bulk_write(usb_transport->bulk_in, data, len);
+ if (ret < 0) {
+ D(ERR, "ERROR: fd = %d, ret = %zd", usb_transport->bulk_in, ret);
+ return -1;
+ }
+ D(DEBUG, "[ usb_write done fd=%d ]", usb_transport->bulk_in);
+ return ret;
+}
+
+static ssize_t bulk_read(int bulk_out, char *buf, size_t length)
+{
+ ssize_t ret;
+ size_t n = 0;
+
+ while (n < length) {
+ size_t to_read = (length - n > READ_BUF_SIZE) ? READ_BUF_SIZE : length - n;
+ ret = TEMP_FAILURE_RETRY(read(bulk_out, buf + n, to_read));
+ if (ret < 0) {
+ D(WARN, "[ bulk_read failed fd=%d length=%d errno=%d %s ]",
+ bulk_out, length, errno, strerror(errno));
+ return ret;
+ }
+ n += ret;
+ if (ret < (ssize_t)to_read) {
+ D(VERBOSE, "bulk_read short read, ret=%zd to_read=%u n=%u length=%u",
+ ret, to_read, n, length);
+ break;
+ }
+ }
+
+ return n;
+}
+
+ssize_t usb_read(struct transport_handle *thandle, void *data, size_t len)
+{
+ ssize_t ret;
+ struct transport *t = thandle->transport;
+ struct usb_transport *usb_transport = container_of(t, struct usb_transport, transport);
+
+ D(DEBUG, "about to read (fd=%d, len=%d)", usb_transport->bulk_out, len);
+ ret = bulk_read(usb_transport->bulk_out, data, len);
+ if (ret < 0) {
+ D(ERR, "ERROR: fd = %d, ret = %zd", usb_transport->bulk_out, ret);
+ return -1;
+ }
+ D(DEBUG, "[ usb_read done fd=%d ret=%zd]", usb_transport->bulk_out, ret);
+ return ret;
+}
+
+void usb_close(struct transport_handle *thandle)
+{
+ int err;
+ struct transport *t = thandle->transport;
+ struct usb_transport *usb_transport = container_of(t, struct usb_transport, transport);
+
+ err = ioctl(usb_transport->bulk_in, FUNCTIONFS_CLEAR_HALT);
+ if (err < 0)
+ D(WARN, "[ kick: source (fd=%d) clear halt failed (%d) ]", usb_transport->bulk_in, errno);
+
+ err = ioctl(usb_transport->bulk_out, FUNCTIONFS_CLEAR_HALT);
+ if (err < 0)
+ D(WARN, "[ kick: sink (fd=%d) clear halt failed (%d) ]", usb_transport->bulk_out, errno);
+
+ pthread_mutex_lock(&usb_transport->lock);
+ close(usb_transport->control);
+ close(usb_transport->bulk_out);
+ close(usb_transport->bulk_in);
+ usb_transport->control = usb_transport->bulk_out = usb_transport->bulk_in = -1;
+
+ pthread_cond_signal(&usb_transport->notify);
+ pthread_mutex_unlock(&usb_transport->lock);
+}
+
+struct transport_handle *usb_connect(struct transport *transport)
+{
+ int ret;
+ struct usb_handle *usb_handle = calloc(sizeof(struct usb_handle), 1);
+ struct usb_transport *usb_transport = container_of(transport, struct usb_transport, transport);
+
+ pthread_mutex_lock(&usb_transport->lock);
+ while (usb_transport->control != -1)
+ pthread_cond_wait(&usb_transport->notify, &usb_transport->lock);
+ pthread_mutex_unlock(&usb_transport->lock);
+
+ ret = init_functionfs(usb_transport);
+ if (ret < 0) {
+ D(ERR, "usb connect: failed to initialize usb transport");
+ return NULL;
+ }
+
+ D(DEBUG, "[ usb_thread - registering device ]");
+ return &usb_handle->handle;
+}
+
+void usb_init()
+{
+ struct usb_transport *usb_transport = calloc(1, sizeof(struct usb_transport));
+
+ usb_transport->transport.connect = usb_connect;
+ usb_transport->transport.close = usb_close;
+ usb_transport->transport.read = usb_read;
+ usb_transport->transport.write = usb_write;
+ usb_transport->control = -1;
+ usb_transport->bulk_out = -1;
+ usb_transport->bulk_out = -1;
+
+ pthread_cond_init(&usb_transport->notify, NULL);
+ pthread_mutex_init(&usb_transport->lock, NULL);
+
+ transport_register(&usb_transport->transport);
+}
+
diff --git a/fastbootd/utils.c b/fastbootd/utils.c
new file mode 100644
index 0000000..16e1c09
--- /dev/null
+++ b/fastbootd/utils.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2009-2013, Google 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:
+ * * 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 <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <linux/fs.h>
+
+#include "utils.h"
+#include "debug.h"
+
+#ifndef BLKDISCARD
+#define BLKDISCARD _IO(0x12,119)
+#endif
+
+#ifndef BLKSECDISCARD
+#define BLKSECDISCARD _IO(0x12,125)
+#endif
+
+
+int get_stream_size(FILE *stream) {
+ int size;
+ fseek(stream, 0, SEEK_END);
+ size = ftell(stream);
+ fseek(stream, 0, SEEK_SET);
+ return size;
+}
+
+uint64_t get_block_device_size(int fd)
+{
+ uint64_t size = 0;
+ int ret;
+
+ ret = ioctl(fd, BLKGETSIZE64, &size);
+
+ if (ret)
+ return 0;
+
+ return size;
+}
+
+uint64_t get_file_size(int fd)
+{
+ struct stat buf;
+ int ret;
+ int64_t computed_size;
+
+ ret = fstat(fd, &buf);
+ if (ret)
+ return 0;
+
+ if (S_ISREG(buf.st_mode))
+ computed_size = buf.st_size;
+ else if (S_ISBLK(buf.st_mode))
+ computed_size = get_block_device_size(fd);
+ else
+ computed_size = 0;
+
+ return computed_size;
+}
+
+uint64_t get_file_size64(int fd)
+{
+ struct stat64 buf;
+ int ret;
+ uint64_t computed_size;
+
+ ret = fstat64(fd, &buf);
+ if (ret)
+ return 0;
+
+ if (S_ISREG(buf.st_mode))
+ computed_size = buf.st_size;
+ else if (S_ISBLK(buf.st_mode))
+ computed_size = get_block_device_size(fd);
+ else
+ computed_size = 0;
+
+ return computed_size;
+}
+
+
+char *strip(char *str)
+{
+ int n;
+
+ n = strspn(str, " \t");
+ str += n;
+ n = strcspn(str, " \t");
+ str[n] = '\0';
+
+ return str;
+}
+
+int wipe_block_device(int fd, int64_t len)
+{
+ uint64_t range[2];
+ int ret;
+
+ range[0] = 0;
+ range[1] = len;
+ ret = ioctl(fd, BLKSECDISCARD, &range);
+ if (ret < 0) {
+ range[0] = 0;
+ range[1] = len;
+ ret = ioctl(fd, BLKDISCARD, &range);
+ if (ret < 0) {
+ D(WARN, "Discard failed\n");
+ return 1;
+ } else {
+ D(WARN, "Wipe via secure discard failed, used discard instead\n");
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
diff --git a/fastbootd/utils.h b/fastbootd/utils.h
new file mode 100644
index 0000000..a553a25
--- /dev/null
+++ b/fastbootd/utils.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2009-2013, Google 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:
+ * * 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.
+ */
+
+#ifndef _FASTBOOT_UTLIS_H
+#define _FASTBOOT_UTILS_H
+
+#include <stdio.h>
+
+int get_stream_size(FILE *);
+
+char *strip(char *str);
+
+uint64_t get_file_size64(int fd);
+uint64_t get_file_size(int fd);
+uint64_t get_block_device_size(int fd);
+int wipe_block_device(int fd, int64_t len);
+
+#define ROUND_TO_PAGE(address,pagesize) ((address + pagesize - 1) & (~(pagesize - 1)))
+
+#define ROUND_UP(number,size) (((number + size - 1) / size) * size)
+
+#endif
diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c
index 82c5798..9a84a4e 100644
--- a/fs_mgr/fs_mgr.c
+++ b/fs_mgr/fs_mgr.c
@@ -235,74 +235,16 @@
return f;
}
-/* Read a line of text till the next newline character.
- * If no newline is found before the buffer is full, continue reading till a new line is seen,
- * then return an empty buffer. This effectively ignores lines that are too long.
- * On EOF, return null.
- */
-static char *fs_getline(char *buf, int size, FILE *file)
-{
- int cnt = 0;
- int eof = 0;
- int eol = 0;
- int c;
-
- if (size < 1) {
- return NULL;
- }
-
- while (cnt < (size - 1)) {
- c = getc(file);
- if (c == EOF) {
- eof = 1;
- break;
- }
-
- *(buf + cnt) = c;
- cnt++;
-
- if (c == '\n') {
- eol = 1;
- break;
- }
- }
-
- /* Null terminate what we've read */
- *(buf + cnt) = '\0';
-
- if (eof) {
- if (cnt) {
- return buf;
- } else {
- return NULL;
- }
- } else if (eol) {
- return buf;
- } else {
- /* The line is too long. Read till a newline or EOF.
- * If EOF, return null, if newline, return an empty buffer.
- */
- while(1) {
- c = getc(file);
- if (c == EOF) {
- return NULL;
- } else if (c == '\n') {
- *buf = '\0';
- return buf;
- }
- }
- }
-}
-
struct fstab *fs_mgr_read_fstab(const char *fstab_path)
{
FILE *fstab_file;
int cnt, entries;
- int len;
- char line[256];
+ ssize_t len;
+ size_t alloc_len = 0;
+ char *line = NULL;
const char *delim = " \t";
char *save_ptr, *p;
- struct fstab *fstab;
+ struct fstab *fstab = NULL;
struct fstab_rec *recs;
struct fs_mgr_flag_values flag_vals;
#define FS_OPTIONS_LEN 1024
@@ -315,9 +257,8 @@
}
entries = 0;
- while (fs_getline(line, sizeof(line), fstab_file)) {
+ while ((len = getline(&line, &alloc_len, fstab_file)) != -1) {
/* if the last character is a newline, shorten the string by 1 byte */
- len = strlen(line);
if (line[len - 1] == '\n') {
line[len - 1] = '\0';
}
@@ -334,7 +275,7 @@
if (!entries) {
ERROR("No entries found in fstab\n");
- return 0;
+ goto err;
}
/* Allocate and init the fstab structure */
@@ -346,9 +287,8 @@
fseek(fstab_file, 0, SEEK_SET);
cnt = 0;
- while (fs_getline(line, sizeof(line), fstab_file)) {
+ while ((len = getline(&line, &alloc_len, fstab_file)) != -1) {
/* if the last character is a newline, shorten the string by 1 byte */
- len = strlen(line);
if (line[len - 1] == '\n') {
line[len - 1] = '\0';
}
@@ -373,25 +313,25 @@
if (!(p = strtok_r(line, delim, &save_ptr))) {
ERROR("Error parsing mount source\n");
- return 0;
+ goto err;
}
fstab->recs[cnt].blk_device = strdup(p);
if (!(p = strtok_r(NULL, delim, &save_ptr))) {
ERROR("Error parsing mount_point\n");
- return 0;
+ goto err;
}
fstab->recs[cnt].mount_point = strdup(p);
if (!(p = strtok_r(NULL, delim, &save_ptr))) {
ERROR("Error parsing fs_type\n");
- return 0;
+ goto err;
}
fstab->recs[cnt].fs_type = strdup(p);
if (!(p = strtok_r(NULL, delim, &save_ptr))) {
ERROR("Error parsing mount_flags\n");
- return 0;
+ goto err;
}
tmp_fs_options[0] = '\0';
fstab->recs[cnt].flags = parse_flags(p, mount_flags, NULL,
@@ -406,7 +346,7 @@
if (!(p = strtok_r(NULL, delim, &save_ptr))) {
ERROR("Error parsing fs_mgr_options\n");
- return 0;
+ goto err;
}
fstab->recs[cnt].fs_mgr_flags = parse_flags(p, fs_mgr_flags,
&flag_vals, NULL, 0);
@@ -419,8 +359,15 @@
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)
@@ -435,7 +382,6 @@
free(fstab->recs[i].fs_options);
free(fstab->recs[i].key_loc);
free(fstab->recs[i].label);
- i++;
}
/* Free the fstab_recs array created by calloc(3) */
diff --git a/healthd/Android.mk b/healthd/Android.mk
new file mode 100644
index 0000000..4a853ee
--- /dev/null
+++ b/healthd/Android.mk
@@ -0,0 +1,92 @@
+# Copyright 2013 The Android Open Source Project
+
+ifneq ($(BUILD_TINY_ANDROID),true)
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := healthd_board_default.cpp
+LOCAL_MODULE := libhealthd.default
+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
+
+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 libpixelflinger_static 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
+
+include $(BUILD_EXECUTABLE)
+
+# Symlink /charger to /sbin/healthd
+SYMLINKS := \
+ $(TARGET_ROOT_OUT)/charger
+
+$(SYMLINKS): HEALTHD_BINARY := $(LOCAL_MODULE)
+$(SYMLINKS): $(LOCAL_INSTALLED_MODULE) $(LOCAL_PATH)/Android.mk
+ @echo "Symlink: $@ -> /sbin/$(HEALTHD_BINARY)"
+ @rm -rf $@
+ $(hide) ln -sf /sbin/$(HEALTHD_BINARY) $@
+
+ALL_DEFAULT_INSTALLED_MODULES += $(SYMLINKS)
+
+# We need this so that the installed files could be picked up based on the
+# local module name
+ALL_MODULES.$(LOCAL_MODULE).INSTALLED := \
+ $(ALL_MODULES.$(LOCAL_MODULE).INSTALLED) $(SYMLINKS)
+
+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 :=
+
+endif
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
new file mode 100644
index 0000000..f641077
--- /dev/null
+++ b/healthd/BatteryMonitor.cpp
@@ -0,0 +1,469 @@
+/*
+ * 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 <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
+
+namespace android {
+
+struct sysfsStringEnumMap {
+ 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) {
+ struct BatteryProperties props;
+ bool logthis;
+
+ props.chargerAcOnline = false;
+ props.chargerUsbOnline = false;
+ props.chargerWirelessOnline = false;
+ props.batteryStatus = BATTERY_STATUS_UNKNOWN;
+ props.batteryHealth = BATTERY_HEALTH_UNKNOWN;
+ props.batteryCurrentNow = INT_MIN;
+ props.batteryChargeCounter = INT_MIN;
+
+ if (!mHealthdConfig->batteryPresentPath.isEmpty())
+ props.batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);
+ else
+ props.batteryPresent = true;
+
+ props.batteryLevel = getIntField(mHealthdConfig->batteryCapacityPath);
+ props.batteryVoltage = getIntField(mHealthdConfig->batteryVoltagePath) / 1000;
+
+ if (!mHealthdConfig->batteryCurrentNowPath.isEmpty())
+ props.batteryCurrentNow = getIntField(mHealthdConfig->batteryCurrentNowPath);
+
+ if (!mHealthdConfig->batteryChargeCounterPath.isEmpty())
+ props.batteryChargeCounter = getIntField(mHealthdConfig->batteryChargeCounterPath);
+
+ props.batteryTemperature = 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];
+ 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()) {
+ char b[20];
+
+ snprintf(b, sizeof(b), " c=%d", props.batteryCurrentNow / 1000);
+ strlcat(dmesgline, b, sizeof(dmesgline));
+ }
+
+ KLOG_INFO(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;
+
+ switch(id) {
+ case BATTERY_PROP_CHARGE_COUNTER:
+ if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
+ val->valueInt =
+ getIntField(mHealthdConfig->batteryChargeCounterPath);
+ ret = NO_ERROR;
+ } else {
+ ret = NAME_NOT_FOUND;
+ }
+ break;
+
+ case BATTERY_PROP_CURRENT_NOW:
+ if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+ val->valueInt =
+ getIntField(mHealthdConfig->batteryCurrentNowPath);
+ ret = NO_ERROR;
+ } else {
+ ret = NAME_NOT_FOUND;
+ }
+ break;
+
+ case BATTERY_PROP_CURRENT_AVG:
+ if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
+ val->valueInt =
+ getIntField(mHealthdConfig->batteryCurrentAvgPath);
+ ret = NO_ERROR;
+ } else {
+ ret = NAME_NOT_FOUND;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (ret != NO_ERROR)
+ val->valueInt = INT_MIN;
+
+ return ret;
+}
+
+void BatteryMonitor::init(struct healthd_config *hc) {
+ String8 path;
+
+ 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;
+
+ char buf[20];
+ // 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:
+ 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 (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");
+}
+
+}; // namespace android
diff --git a/healthd/BatteryMonitor.h b/healthd/BatteryMonitor.h
new file mode 100644
index 0000000..3dd2ef0
--- /dev/null
+++ b/healthd/BatteryMonitor.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 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);
+
+ private:
+ struct healthd_config *mHealthdConfig;
+ Vector<String8> mChargerNames;
+
+ 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..f2edee4
--- /dev/null
+++ b/healthd/BatteryPropertiesRegistrar.cpp
@@ -0,0 +1,83 @@
+/*
+ * 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/IServiceManager.h>
+#include <utils/Errors.h>
+#include <utils/Mutex.h>
+#include <utils/String16.h>
+
+#include "healthd.h"
+
+namespace android {
+
+void BatteryPropertiesRegistrar::publish() {
+ defaultServiceManager()->addService(String16("batterypropreg"), 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) {
+ {
+ Mutex::Autolock _l(mRegistrationLock);
+ // check whether this is a duplicate
+ for (size_t i = 0; i < mListeners.size(); i++) {
+ if (mListeners[i]->asBinder() == listener->asBinder()) {
+ return;
+ }
+ }
+
+ mListeners.add(listener);
+ listener->asBinder()->linkToDeath(this);
+ }
+ healthd_battery_update();
+}
+
+void BatteryPropertiesRegistrar::unregisterListener(const sp<IBatteryPropertiesListener>& listener) {
+ Mutex::Autolock _l(mRegistrationLock);
+ for (size_t i = 0; i < mListeners.size(); i++) {
+ if (mListeners[i]->asBinder() == listener->asBinder()) {
+ mListeners[i]->asBinder()->unlinkToDeath(this);
+ mListeners.removeAt(i);
+ break;
+ }
+ }
+}
+
+status_t BatteryPropertiesRegistrar::getProperty(int id, struct BatteryProperty *val) {
+ return healthd_get_property(id, val);
+}
+
+void BatteryPropertiesRegistrar::binderDied(const wp<IBinder>& who) {
+ Mutex::Autolock _l(mRegistrationLock);
+
+ for (size_t i = 0; i < mListeners.size(); i++) {
+ if (mListeners[i]->asBinder() == who) {
+ mListeners.removeAt(i);
+ break;
+ }
+ }
+}
+
+} // namespace android
diff --git a/healthd/BatteryPropertiesRegistrar.h b/healthd/BatteryPropertiesRegistrar.h
new file mode 100644
index 0000000..3e86fdf
--- /dev/null
+++ b/healthd/BatteryPropertiesRegistrar.h
@@ -0,0 +1,47 @@
+/*
+ * 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/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);
+ 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..66b961d
--- /dev/null
+++ b/healthd/healthd.cpp
@@ -0,0 +1,355 @@
+/*
+ * 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),
+};
+
+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;
+static int binder_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;
+}
+
+static void periodic_chores() {
+ healthd_battery_update();
+}
+
+#define UEVENT_MSG_LEN 1024
+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_mode_ops->init(&healthd_config);
+ healthd_board_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", ch);
+ 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..fb9504a
--- /dev/null
+++ b/healthd/healthd.h
@@ -0,0 +1,118 @@
+/*
+ * 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 <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;
+};
+
+// 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);
+
+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..b2bb516
--- /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 *config)
+{
+ // use defaults
+}
+
+
+int healthd_board_battery_update(struct android::BatteryProperties *props)
+{
+ // 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..4887c8c
--- /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..fe96c86
--- /dev/null
+++ b/healthd/healthd_mode_charger.cpp
@@ -0,0 +1,679 @@
+/*
+ * 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 <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>
+
+#ifdef CHARGER_ENABLE_SUSPEND
+#include <suspend/autosuspend.h>
+#endif
+
+#include "minui/minui.h"
+
+#include "healthd.h"
+
+#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_MAX_SZ (32 * 1024)
+
+#define LOGE(x...) do { KLOG_ERROR("charger", x); } while (0)
+#define LOGI(x...) do { KLOG_INFO("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 {
+ const char *name;
+ int disp_time;
+ int min_capacity;
+ bool level_only;
+
+ gr_surface 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;
+ int capacity;
+ 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;
+ gr_surface surf_unknown;
+};
+
+static struct frame batt_anim_frames[] = {
+ {
+ .name = "charger/battery_0",
+ .disp_time = 750,
+ .min_capacity = 0,
+ .level_only = false,
+ .surface = NULL,
+ },
+ {
+ .name = "charger/battery_1",
+ .disp_time = 750,
+ .min_capacity = 20,
+ .level_only = false,
+ .surface = NULL,
+ },
+ {
+ .name = "charger/battery_2",
+ .disp_time = 750,
+ .min_capacity = 40,
+ .level_only = false,
+ .surface = NULL,
+ },
+ {
+ .name = "charger/battery_3",
+ .disp_time = 750,
+ .min_capacity = 60,
+ .level_only = false,
+ .surface = NULL,
+ },
+ {
+ .name = "charger/battery_4",
+ .disp_time = 750,
+ .min_capacity = 80,
+ .level_only = true,
+ .surface = NULL,
+ },
+ {
+ .name = "charger/battery_5",
+ .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 int char_width;
+static int char_height;
+
+/* 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_fill(0, 0, gr_fb_width(), gr_fb_height());
+};
+
+#define MAX_KLOG_WRITE_BUF_SZ 256
+
+static void dump_last_kmsg(void)
+{
+ char *buf;
+ char *ptr;
+ unsigned sz = 0;
+ int len;
+
+ LOGI("\n");
+ LOGI("*************** LAST KMSG ***************\n");
+ LOGI("\n");
+ buf = (char *)load_file(LAST_KMSG_PATH, &sz);
+ if (!buf || !sz) {
+ LOGI("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, "<6>%s", ptr);
+ ptr[cnt] = yoink;
+
+ len -= cnt;
+ ptr += cnt;
+ }
+
+ free(buf);
+
+out:
+ LOGI("\n");
+ LOGI("************* END LAST KMSG *************\n");
+ LOGI("\n");
+}
+
+static int get_battery_capacity()
+{
+ return charger_state.capacity;
+}
+
+#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, gr_surface 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 name=%s min_cap=%d time=%d\n",
+ batt_anim->cur_frame, frame->name, 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 cur_frame;
+ int disp_time;
+
+ if (!batt_anim->run || now < charger->next_screen_transition)
+ return;
+
+ /* 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("[%lld] 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) {
+ int batt_cap;
+ int ret;
+
+ LOGV("[%lld] animation starting\n", now);
+ batt_cap = get_battery_capacity();
+ if (batt_cap >= 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_cap < 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;
+ }
+
+ batt_anim->capacity = batt_cap;
+ }
+
+ /* 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("[%lld] 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
+ * if necessary, advance cycle cntr, and reset frame cntr
+ */
+ 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
+ */
+ }
+}
+
+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("[%lld] 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("[%lld] key[%d] up (was down for %lld.%lldsec)\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];
+ int64_t next_key_check;
+
+ if (code == KEY_POWER) {
+ if (key->down) {
+ int64_t reboot_timeout = key->timestamp + POWER_ON_KEY_TIME;
+ if (now >= reboot_timeout) {
+ LOGI("[%lld] 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;
+ LOGI("[%lld] device unplugged: shutting down in %lld (@ %lld)\n",
+ now, UNPLUGGED_SHUTDOWN_TIME, charger->next_pwr_check);
+ } else if (now >= charger->next_pwr_check) {
+ LOGI("[%lld] 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) {
+ LOGI("[%lld] 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();
+ int ret;
+
+ 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;
+ charger->capacity = props->batteryLevel;
+
+ 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);
+ }
+}
+
+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;
+ struct input_event ev;
+ int ret;
+
+ LOGV("[%lld] next screen: %lld next key: %lld next pwr: %lld\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();
+
+ LOGI("--------------- STARTING CHARGER MODE ---------------\n");
+
+ gr_init();
+ gr_font_size(&char_width, &char_height);
+
+ ret = ev_init(input_callback, charger);
+ if (!ret) {
+ epollfd = ev_get_epollfd();
+ healthd_register_event(epollfd, charger_event_handler);
+ }
+
+ ret = res_create_surface("charger/battery_fail", &charger->surf_unknown);
+ if (ret < 0) {
+ LOGE("Cannot load image\n");
+ charger->surf_unknown = NULL;
+ }
+
+ charger->batt_anim = &battery_animation;
+
+ for (i = 0; i < charger->batt_anim->num_frames; i++) {
+ struct frame *frame = &charger->batt_anim->frames[i];
+
+ ret = res_create_surface(frame->name, &frame->surface);
+ if (ret < 0) {
+ LOGE("Cannot load image %s\n", frame->name);
+ /* TODO: free the already allocated surfaces... */
+ charger->batt_anim->num_frames = 0;
+ charger->batt_anim->num_cycles = 1;
+ break;
+ }
+ }
+
+ ev_sync_key_state(set_key_callback, charger);
+
+#ifndef CHARGER_DISABLE_INIT_BLANK
+ gr_fb_blank(true);
+#endif
+
+ charger->next_screen_transition = -1;
+ charger->next_key_check = -1;
+ charger->next_pwr_check = -1;
+}
diff --git a/charger/images/battery_0.png b/healthd/images/battery_0.png
similarity index 100%
rename from charger/images/battery_0.png
rename to healthd/images/battery_0.png
Binary files differ
diff --git a/charger/images/battery_1.png b/healthd/images/battery_1.png
similarity index 100%
rename from charger/images/battery_1.png
rename to healthd/images/battery_1.png
Binary files differ
diff --git a/charger/images/battery_2.png b/healthd/images/battery_2.png
similarity index 100%
rename from charger/images/battery_2.png
rename to healthd/images/battery_2.png
Binary files differ
diff --git a/charger/images/battery_3.png b/healthd/images/battery_3.png
similarity index 100%
rename from charger/images/battery_3.png
rename to healthd/images/battery_3.png
Binary files differ
diff --git a/charger/images/battery_4.png b/healthd/images/battery_4.png
similarity index 100%
rename from charger/images/battery_4.png
rename to healthd/images/battery_4.png
Binary files differ
diff --git a/charger/images/battery_5.png b/healthd/images/battery_5.png
similarity index 100%
rename from charger/images/battery_5.png
rename to healthd/images/battery_5.png
Binary files differ
diff --git a/charger/images/battery_charge.png b/healthd/images/battery_charge.png
similarity index 100%
rename from charger/images/battery_charge.png
rename to healthd/images/battery_charge.png
Binary files differ
diff --git a/charger/images/battery_fail.png b/healthd/images/battery_fail.png
similarity index 100%
rename from charger/images/battery_fail.png
rename to healthd/images/battery_fail.png
Binary files differ
diff --git a/include/cutils/log.h b/include/cutils/log.h
index 8b045c7..0e0248e 100644
--- a/include/cutils/log.h
+++ b/include/cutils/log.h
@@ -1,563 +1 @@
-/*
- * 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_CUTILS_LOG_H
-#define _LIBS_CUTILS_LOG_H
-
-#include <stdio.h>
-#include <time.h>
-#include <sys/types.h>
-#include <unistd.h>
-#ifdef HAVE_PTHREADS
-#include <pthread.h>
-#endif
-#include <stdarg.h>
-
-#include <cutils/uio.h>
-#include <cutils/logd.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
-
-// ---------------------------------------------------------------------
-
-/*
- * Simplified macro to send a verbose log message using the current LOG_TAG.
- */
-#ifndef ALOGV
-#if LOG_NDEBUG
-#define ALOGV(...) ((void)0)
-#else
-#define ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
-#endif
-#endif
-
-#define CONDITION(cond) (__builtin_expect((cond)!=0, 0))
-
-#ifndef ALOGV_IF
-#if LOG_NDEBUG
-#define ALOGV_IF(cond, ...) ((void)0)
-#else
-#define ALOGV_IF(cond, ...) \
- ( (CONDITION(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, ...) \
- ( (CONDITION(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, ...) \
- ( (CONDITION(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, ...) \
- ( (CONDITION(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, ...) \
- ( (CONDITION(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
-#if LOG_NDEBUG
-#define SLOGV(...) ((void)0)
-#else
-#define SLOGV(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
-#endif
-#endif
-
-#define CONDITION(cond) (__builtin_expect((cond)!=0, 0))
-
-#ifndef SLOGV_IF
-#if LOG_NDEBUG
-#define SLOGV_IF(cond, ...) ((void)0)
-#else
-#define SLOGV_IF(cond, ...) \
- ( (CONDITION(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, ...) \
- ( (CONDITION(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, ...) \
- ( (CONDITION(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, ...) \
- ( (CONDITION(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, ...) \
- ( (CONDITION(cond)) \
- ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) \
- : (void)0 )
-#endif
-
-// ---------------------------------------------------------------------
-
-/*
- * Simplified macro to send a verbose radio log message using the current LOG_TAG.
- */
-#ifndef RLOGV
-#if LOG_NDEBUG
-#define RLOGV(...) ((void)0)
-#else
-#define RLOGV(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
-#endif
-#endif
-
-#define CONDITION(cond) (__builtin_expect((cond)!=0, 0))
-
-#ifndef RLOGV_IF
-#if LOG_NDEBUG
-#define RLOGV_IF(cond, ...) ((void)0)
-#else
-#define RLOGV_IF(cond, ...) \
- ( (CONDITION(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, ...) \
- ( (CONDITION(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, ...) \
- ( (CONDITION(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, ...) \
- ( (CONDITION(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, ...) \
- ( (CONDITION(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, ...) \
- ( (CONDITION(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,
-} AndroidEventLogType;
-
-
-#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_STRING
-#define LOG_EVENT_STRING(_tag, _value) \
- ((void) 0) /* not implemented -- must combine len with string */
-#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)
-
-// TODO: remove these prototypes and their users
-#define android_testLog(prio, tag) (1)
-#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_MAIN = 0,
- LOG_ID_RADIO = 1,
- LOG_ID_EVENTS = 2,
- LOG_ID_SYSTEM = 3,
-
- LOG_ID_MAX
-} log_id_t;
-
-/*
- * 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, ...);
-
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // _LIBS_CUTILS_LOG_H
+#include <log/log.h>
diff --git a/include/cutils/trace.h b/include/cutils/trace.h
index a0dd1e0..1c8f107 100644
--- a/include/cutils/trace.h
+++ b/include/cutils/trace.h
@@ -259,6 +259,23 @@
}
}
+/**
+ * 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))) {
+ char buf[ATRACE_MESSAGE_LENGTH];
+ size_t len;
+
+ len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "C|%d|%s|%lld",
+ getpid(), name, value);
+ write(atrace_marker_fd, buf, len);
+ }
+}
+
#else // not HAVE_ANDROID_OS
#define ATRACE_INIT()
diff --git a/include/cutils/event_tag_map.h b/include/log/event_tag_map.h
similarity index 100%
rename from include/cutils/event_tag_map.h
rename to include/log/event_tag_map.h
diff --git a/include/log/log.h b/include/log/log.h
new file mode 100644
index 0000000..7faddea
--- /dev/null
+++ b/include/log/log.h
@@ -0,0 +1,563 @@
+/*
+ * 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_LOG_LOG_H
+#define _LIBS_LOG_LOG_H
+
+#include <stdio.h>
+#include <time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#ifdef HAVE_PTHREADS
+#include <pthread.h>
+#endif
+#include <stdarg.h>
+
+#include <log/uio.h>
+#include <log/logd.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
+
+// ---------------------------------------------------------------------
+
+/*
+ * Simplified macro to send a verbose log message using the current LOG_TAG.
+ */
+#ifndef ALOGV
+#if LOG_NDEBUG
+#define ALOGV(...) ((void)0)
+#else
+#define ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
+#endif
+#endif
+
+#define CONDITION(cond) (__builtin_expect((cond)!=0, 0))
+
+#ifndef ALOGV_IF
+#if LOG_NDEBUG
+#define ALOGV_IF(cond, ...) ((void)0)
+#else
+#define ALOGV_IF(cond, ...) \
+ ( (CONDITION(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, ...) \
+ ( (CONDITION(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, ...) \
+ ( (CONDITION(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, ...) \
+ ( (CONDITION(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, ...) \
+ ( (CONDITION(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
+#if LOG_NDEBUG
+#define SLOGV(...) ((void)0)
+#else
+#define SLOGV(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
+#endif
+#endif
+
+#define CONDITION(cond) (__builtin_expect((cond)!=0, 0))
+
+#ifndef SLOGV_IF
+#if LOG_NDEBUG
+#define SLOGV_IF(cond, ...) ((void)0)
+#else
+#define SLOGV_IF(cond, ...) \
+ ( (CONDITION(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, ...) \
+ ( (CONDITION(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, ...) \
+ ( (CONDITION(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, ...) \
+ ( (CONDITION(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, ...) \
+ ( (CONDITION(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+// ---------------------------------------------------------------------
+
+/*
+ * Simplified macro to send a verbose radio log message using the current LOG_TAG.
+ */
+#ifndef RLOGV
+#if LOG_NDEBUG
+#define RLOGV(...) ((void)0)
+#else
+#define RLOGV(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
+#endif
+#endif
+
+#define CONDITION(cond) (__builtin_expect((cond)!=0, 0))
+
+#ifndef RLOGV_IF
+#if LOG_NDEBUG
+#define RLOGV_IF(cond, ...) ((void)0)
+#else
+#define RLOGV_IF(cond, ...) \
+ ( (CONDITION(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, ...) \
+ ( (CONDITION(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, ...) \
+ ( (CONDITION(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, ...) \
+ ( (CONDITION(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, ...) \
+ ( (CONDITION(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, ...) \
+ ( (CONDITION(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,
+} AndroidEventLogType;
+
+
+#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_STRING
+#define LOG_EVENT_STRING(_tag, _value) \
+ ((void) 0) /* not implemented -- must combine len with string */
+#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)
+
+// TODO: remove these prototypes and their users
+#define android_testLog(prio, tag) (1)
+#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_MAIN = 0,
+ LOG_ID_RADIO = 1,
+ LOG_ID_EVENTS = 2,
+ LOG_ID_SYSTEM = 3,
+
+ LOG_ID_MAX
+} log_id_t;
+
+/*
+ * 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, ...);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _LIBS_CUTILS_LOG_H
diff --git a/include/cutils/logd.h b/include/log/logd.h
similarity index 97%
rename from include/cutils/logd.h
rename to include/log/logd.h
index 8737639..379c373 100644
--- a/include/cutils/logd.h
+++ b/include/log/logd.h
@@ -31,7 +31,7 @@
#ifdef HAVE_PTHREADS
#include <pthread.h>
#endif
-#include <cutils/uio.h>
+#include <log/uio.h>
#include <stdarg.h>
#ifdef __cplusplus
diff --git a/include/cutils/logger.h b/include/log/logger.h
similarity index 100%
rename from include/cutils/logger.h
rename to include/log/logger.h
diff --git a/include/cutils/logprint.h b/include/log/logprint.h
similarity index 97%
rename from include/cutils/logprint.h
rename to include/log/logprint.h
index 2b1e1c5..481c96e 100644
--- a/include/cutils/logprint.h
+++ b/include/log/logprint.h
@@ -17,9 +17,9 @@
#ifndef _LOGPRINT_H
#define _LOGPRINT_H
-#include <cutils/log.h>
-#include <cutils/logger.h>
-#include <cutils/event_tag_map.h>
+#include <log/log.h>
+#include <log/logger.h>
+#include <log/event_tag_map.h>
#include <pthread.h>
#ifdef __cplusplus
diff --git a/include/cutils/uio.h b/include/log/uio.h
similarity index 100%
rename from include/cutils/uio.h
rename to include/log/uio.h
diff --git a/include/netutils/dhcp.h b/include/netutils/dhcp.h
index bd2c957..de6bc82 100644
--- a/include/netutils/dhcp.h
+++ b/include/netutils/dhcp.h
@@ -30,7 +30,9 @@
char *dns[],
char *server,
uint32_t *lease,
- char *vendorInfo);
+ char *vendorInfo,
+ char *domain,
+ char *mtu);
extern int dhcp_do_request_renew(const char *ifname,
char *ipaddr,
char *gateway,
@@ -38,7 +40,9 @@
char *dns[],
char *server,
uint32_t *lease,
- char *vendorInfo);
+ 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();
diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h
index 4a7d377..53619ac 100644
--- a/include/private/android_filesystem_config.h
+++ b/include/private/android_filesystem_config.h
@@ -34,8 +34,8 @@
#endif
/* This is the master Users and Groups config for the platform.
-** DO NOT EVER RENUMBER.
-*/
+ * DO NOT EVER RENUMBER
+ */
#define AID_ROOT 0 /* traditional unix root user */
@@ -72,6 +72,10 @@
#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_SHELL 2000 /* adb and debug shell user */
#define AID_CACHE 2001 /* cache access */
@@ -108,50 +112,61 @@
};
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, },
- { "dhcp", AID_DHCP, },
- { "adb", AID_ADB, },
- { "install", AID_INSTALL, },
- { "media", AID_MEDIA, },
- { "drm", AID_DRM, },
- { "mdnsr", AID_MDNSR, },
- { "nfc", AID_NFC, },
- { "drmrpc", AID_DRMRPC, },
- { "shell", AID_SHELL, },
- { "cache", AID_CACHE, },
- { "diag", AID_DIAG, },
- { "net_bt_admin", AID_NET_BT_ADMIN, },
- { "net_bt", AID_NET_BT, },
- { "net_bt_stack", AID_NET_BT_STACK, },
- { "sdcard_r", AID_SDCARD_R, },
- { "sdcard_rw", AID_SDCARD_RW, },
- { "media_rw", AID_MEDIA_RW, },
- { "vpn", AID_VPN, },
- { "keystore", AID_KEYSTORE, },
- { "usb", AID_USB, },
- { "mtp", AID_MTP, },
- { "gps", AID_GPS, },
- { "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, },
- { "loop_radio", AID_LOOP_RADIO, },
- { "misc", AID_MISC, },
- { "nobody", AID_NOBODY, },
- { "clat", AID_CLAT, },
- { "mediadrm", AID_MEDIA_DRM, },
+ { "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, },
+
+ { "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, },
+
+ { "misc", AID_MISC, },
+ { "nobody", AID_NOBODY, },
};
#define android_id_count \
@@ -240,7 +255,6 @@
{ 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, "charger*" },
{ 00750, AID_ROOT, AID_SHELL, 0, "sbin/fs_mgr" },
{ 00640, AID_ROOT, AID_SHELL, 0, "fstab.*" },
{ 00644, AID_ROOT, AID_ROOT, 0, 0 },
diff --git a/include/system/audio.h b/include/system/audio.h
index c49b0ee..aa7ac02 100644
--- a/include/system/audio.h
+++ b/include/system/audio.h
@@ -31,6 +31,9 @@
* frameworks/base/include/media/AudioSystem.h
*/
+/* device address used to refer to the standard remote submix */
+#define AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS "0"
+
typedef int audio_io_handle_t;
/* Audio stream types */
@@ -69,6 +72,11 @@
/* play the mix captured by this audio source. */
AUDIO_SOURCE_CNT,
AUDIO_SOURCE_MAX = AUDIO_SOURCE_CNT - 1,
+ 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;
/* special audio session values
@@ -389,6 +397,16 @@
AUDIO_OUTPUT_FLAG_NON_BLOCKING = 0x20 // use non-blocking write
} 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_flags_t;
+
/* Additional information about compressed streams offloaded to
* hardware playback
* The version and size fields must be initialized by the caller by using
@@ -471,24 +489,25 @@
static inline bool audio_is_remote_submix_device(audio_devices_t device)
{
- if ((popcount(device) == 1) && (device & AUDIO_DEVICE_OUT_REMOTE_SUBMIX))
+ if ((device & AUDIO_DEVICE_OUT_REMOTE_SUBMIX) == AUDIO_DEVICE_OUT_REMOTE_SUBMIX
+ || (device & AUDIO_DEVICE_IN_REMOTE_SUBMIX) == AUDIO_DEVICE_IN_REMOTE_SUBMIX)
return true;
else
return false;
}
-static inline bool audio_is_input_channel(uint32_t channel)
+static inline bool audio_is_input_channel(audio_channel_mask_t channel)
{
if ((channel & ~AUDIO_CHANNEL_IN_ALL) == 0)
- return true;
+ return channel != 0;
else
return false;
}
-static inline bool audio_is_output_channel(uint32_t channel)
+static inline bool audio_is_output_channel(audio_channel_mask_t channel)
{
if ((channel & ~AUDIO_CHANNEL_OUT_ALL) == 0)
- return true;
+ return channel != 0;
else
return false;
}
diff --git a/include/system/graphics.h b/include/system/graphics.h
index ed493f5..be86ae4 100644
--- a/include/system/graphics.h
+++ b/include/system/graphics.h
@@ -42,15 +42,40 @@
*/
enum {
+ /*
+ * "linear" color pixel formats:
+ *
+ * The pixel formats below contain sRGB data but are otherwise treated
+ * as linear formats, i.e.: no special operation is 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,
- HAL_PIXEL_FORMAT_RGBA_5551 = 6,
- HAL_PIXEL_FORMAT_RGBA_4444 = 7,
- /* 0x8 - 0xFF range unavailable */
+ /*
+ * sRGB color pixel formats:
+ *
+ * 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.
+ *
+ */
+ HAL_PIXEL_FORMAT_sRGB_A_8888 = 0xC,
+ HAL_PIXEL_FORMAT_sRGB_X_8888 = 0xD,
/*
* 0x100 - 0x1FF
@@ -268,6 +293,8 @@
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,
};
#ifdef __cplusplus
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
index b8a19c8..649bd71 100644
--- a/include/system/window.h
+++ b/include/system/window.h
@@ -230,7 +230,13 @@
* Boolean that indicates whether the consumer is running more than
* one buffer behind the producer.
*/
- NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND = 9
+ 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
};
/* Valid operations for the (*perform)() hook.
@@ -290,12 +296,15 @@
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 */
+ /* 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 */
diff --git a/include/sysutils/NetlinkEvent.h b/include/sysutils/NetlinkEvent.h
index 25a56f7..f3501cf 100644
--- a/include/sysutils/NetlinkEvent.h
+++ b/include/sysutils/NetlinkEvent.h
@@ -34,6 +34,8 @@
const static int NlActionChange;
const static int NlActionLinkDown;
const static int NlActionLinkUp;
+ const static int NlActionAddressUpdated;
+ const static int NlActionAddressRemoved;
NetlinkEvent();
virtual ~NetlinkEvent();
@@ -47,6 +49,7 @@
void dump();
protected:
+ bool parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr, int rtasize);
bool parseBinaryNetlinkMessage(char *buffer, int size);
bool parseAsciiNetlinkMessage(char *buffer, int size);
};
diff --git a/include/utils/AndroidThreads.h b/include/utils/AndroidThreads.h
new file mode 100644
index 0000000..4eee14d
--- /dev/null
+++ b/include/utils/AndroidThreads.h
@@ -0,0 +1,128 @@
+/*
+ * 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(HAVE_PTHREADS)
+# 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.
+
+// Get pid for the current thread.
+extern pid_t androidGetTid();
+
+#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..19c03d1
--- /dev/null
+++ b/include/utils/BitSet.h
@@ -0,0 +1,124 @@
+/*
+ * 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(0) { }
+ 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 0x80000000 >> n; }
+
+ // Clears the bit set.
+ inline void clear() { value = 0; }
+
+ // Returns the number of marked bits in the set.
+ inline uint32_t count() const { return __builtin_popcount(value); }
+
+ // Returns true if the bit set does not contain any marked bits.
+ inline bool isEmpty() const { return ! value; }
+
+ // Returns true if the bit set does not contain any unmarked bits.
+ inline bool isFull() const { return value == 0xffffffff; }
+
+ // Returns true if the specified bit is marked.
+ inline bool hasBit(uint32_t n) const { return value & valueForBit(n); }
+
+ // Marks the specified bit.
+ inline void markBit(uint32_t n) { value |= valueForBit(n); }
+
+ // Clears the specified bit.
+ inline void clearBit(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 __builtin_clz(value); }
+
+ // Finds the first unmarked bit in the set.
+ // Result is undefined if all bits are marked.
+ inline uint32_t firstUnmarkedBit() const { return __builtin_clz(~ value); }
+
+ // Finds the last marked bit in the set.
+ // Result is undefined if all bits are unmarked.
+ inline uint32_t lastMarkedBit() const { return 31 - __builtin_ctz(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() {
+ uint32_t n = firstMarkedBit();
+ clearBit(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() {
+ uint32_t n = firstUnmarkedBit();
+ markBit(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() {
+ uint32_t n = lastMarkedBit();
+ clearBit(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 __builtin_popcount(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;
+ }
+};
+
+ANDROID_BASIC_TYPES_TRAITS(BitSet32)
+
+} // 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..61dc832
--- /dev/null
+++ b/include/utils/CallStack.h
@@ -0,0 +1,76 @@
+/*
+ * 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 <stdint.h>
+#include <sys/types.h>
+
+#include <utils/String8.h>
+#include <corkscrew/backtrace.h>
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+class CallStack
+{
+public:
+ enum {
+ MAX_DEPTH = 31
+ };
+
+ CallStack();
+ CallStack(const char* logtag, int32_t ignoreDepth=1,
+ int32_t maxDepth=MAX_DEPTH);
+ CallStack(const CallStack& rhs);
+ ~CallStack();
+
+ CallStack& operator = (const CallStack& rhs);
+
+ bool operator == (const CallStack& rhs) const;
+ bool operator != (const CallStack& rhs) const;
+ bool operator < (const CallStack& rhs) const;
+ bool operator >= (const CallStack& rhs) const;
+ bool operator > (const CallStack& rhs) const;
+ bool operator <= (const CallStack& rhs) const;
+
+ const void* operator [] (int index) const;
+
+ void clear();
+
+ void update(int32_t ignoreDepth=1, int32_t maxDepth=MAX_DEPTH);
+
+ // Dump a stack trace to the log using the supplied logtag
+ void dump(const char* logtag, const char* prefix = 0) const;
+
+ // Return a string (possibly very long) containing the complete stack trace
+ String8 toString(const char* prefix = 0) const;
+
+ size_t size() const { return mCount; }
+
+private:
+ size_t mCount;
+ backtrace_frame_t mStack[MAX_DEPTH];
+};
+
+}; // namespace android
+
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_CALLSTACK_H
diff --git a/include/utils/Compat.h b/include/utils/Compat.h
new file mode 100644
index 0000000..fb7748e
--- /dev/null
+++ b/include/utils/Compat.h
@@ -0,0 +1,65 @@
+/*
+ * 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>
+
+/* Compatibility definitions for non-Linux (i.e., BSD-based) hosts. */
+#ifndef HAVE_OFF64_T
+#if _FILE_OFFSET_BITS < 64
+#error "_FILE_OFFSET_BITS < 64; large files are not supported on this platform"
+#endif /* _FILE_OFFSET_BITS < 64 */
+
+typedef off_t off64_t;
+
+static inline off64_t lseek64(int fd, off64_t offset, int whence) {
+ return lseek(fd, offset, whence);
+}
+
+#ifdef HAVE_PREAD
+static inline ssize_t pread64(int fd, void* buf, size_t nbytes, off64_t offset) {
+ return pread(fd, buf, nbytes, offset);
+}
+#endif
+
+#endif /* !HAVE_OFF64_T */
+
+#if HAVE_PRINTF_ZD
+# define ZD "%zd"
+# define ZD_TYPE ssize_t
+#else
+# define ZD "%ld"
+# define ZD_TYPE long
+#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..e63ba7e
--- /dev/null
+++ b/include/utils/Condition.h
@@ -0,0 +1,147 @@
+/*
+ * 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(HAVE_PTHREADS)
+# 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 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(HAVE_PTHREADS)
+ pthread_cond_t mCond;
+#else
+ void* mState;
+#endif
+};
+
+// ---------------------------------------------------------------------------
+
+#if defined(HAVE_PTHREADS)
+
+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(HAVE_POSIX_CLOCKS)
+ clock_gettime(CLOCK_REALTIME, &ts);
+#else // HAVE_POSIX_CLOCKS
+ // 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 // HAVE_POSIX_CLOCKS
+ 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() {
+ pthread_cond_signal(&mCond);
+}
+inline void Condition::broadcast() {
+ pthread_cond_broadcast(&mCond);
+}
+
+#endif // HAVE_PTHREADS
+
+// ---------------------------------------------------------------------------
+}; // 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..19f2504
--- /dev/null
+++ b/include/utils/Endian.h
@@ -0,0 +1,40 @@
+/*
+ * 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(HAVE_ENDIAN_H)
+
+#include <endian.h>
+
+#else /*not HAVE_ENDIAN_H*/
+
+#define __BIG_ENDIAN 0x1000
+#define __LITTLE_ENDIAN 0x0001
+
+#if defined(HAVE_LITTLE_ENDIAN)
+# define __BYTE_ORDER __LITTLE_ENDIAN
+#else
+# define __BYTE_ORDER __BIG_ENDIAN
+#endif
+
+#endif /*not HAVE_ENDIAN_H*/
+
+#endif /*_LIBS_UTILS_ENDIAN_H*/
diff --git a/include/utils/Errors.h b/include/utils/Errors.h
new file mode 100644
index 0000000..0b75b19
--- /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 = 0x80000000,
+
+ NO_MEMORY = -ENOMEM,
+ INVALID_OPERATION = -ENOSYS,
+ BAD_VALUE = -EINVAL,
+ BAD_TYPE = 0x80000001,
+ NAME_NOT_FOUND = -ENOENT,
+ PERMISSION_DENIED = -EPERM,
+ NO_INIT = -ENODEV,
+ ALREADY_EXISTS = -EEXIST,
+ DEAD_OBJECT = -EPIPE,
+ FAILED_TRANSACTION = 0x80000002,
+ 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 = 0x80000003,
+ WOULD_BLOCK = 0x80000004,
+ TIMED_OUT = 0x80000005,
+ UNKNOWN_TRANSACTION = 0x80000006,
+#endif
+ FDS_NOT_ALLOWED = 0x80000007,
+};
+
+// 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..dfe6d51
--- /dev/null
+++ b/include/utils/FileMap.h
@@ -0,0 +1,136 @@
+/*
+ * 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>
+
+#ifdef HAVE_WIN32_FILEMAP
+#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);
+
+ /*
+ * 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; }
+
+ /*
+ * Get a "copy" of the object.
+ */
+ FileMap* acquire(void) { mRefCount++; return this; }
+
+ /*
+ * Call this when mapping is no longer needed.
+ */
+ void release(void) {
+ if (--mRefCount <= 0)
+ delete this;
+ }
+
+ /*
+ * 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:
+ // don't delete objects; call release()
+ ~FileMap(void);
+
+private:
+ // these are not implemented
+ FileMap(const FileMap& src);
+ const FileMap& operator=(const FileMap& src);
+
+ int mRefCount; // reference count
+ 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"
+#ifdef HAVE_WIN32_FILEMAP
+ 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..e24ded4
--- /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..2e0651a
--- /dev/null
+++ b/include/utils/Looper.h
@@ -0,0 +1,388 @@
+/*
+ * 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 <android/looper.h>
+
+#include <sys/epoll.h>
+
+/*
+ * Declare a concrete type for the NDK's looper forward declaration.
+ */
+struct ALooper {
+};
+
+namespace android {
+
+/**
+ * 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 ALOOPER_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 ALooper_callbackFunc function pointer.
+ */
+class SimpleLooperCallback : public LooperCallback {
+protected:
+ virtual ~SimpleLooperCallback();
+
+public:
+ SimpleLooperCallback(ALooper_callbackFunc callback);
+ virtual int handleEvent(int fd, int events, void* data);
+
+private:
+ ALooper_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 ALooper, public RefBase {
+protected:
+ virtual ~Looper();
+
+public:
+ /**
+ * 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 ALOOPER_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 ALOOPER_POLL_CALLBACK if one or more callbacks were invoked.
+ *
+ * Returns ALOOPER_POLL_TIMEOUT if there was no data before the given
+ * timeout expired.
+ *
+ * Returns ALOOPER_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 ALOOPER_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 ALOOPER_POLL_CALLBACK if providing a non-NULL callback.
+ * "events" are the poll events to wake up on. Typically this is ALOOPER_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 ALooper_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, ALooper_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 ALOOPER_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..fa8f03f
--- /dev/null
+++ b/include/utils/LruCache.h
@@ -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.
+ */
+
+#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();
+
+ class Iterator {
+ public:
+ Iterator(const LruCache<TKey, TValue>& cache): mCache(cache), mIndex(-1) {
+ }
+
+ bool next() {
+ mIndex = mCache.mTable->next(mIndex);
+ return mIndex != -1;
+ }
+
+ size_t index() const {
+ return mIndex;
+ }
+
+ const TValue& value() const {
+ return mCache.mTable->entryAt(mIndex).value;
+ }
+
+ const TKey& key() const {
+ return mCache.mTable->entryAt(mIndex).key;
+ }
+ private:
+ const LruCache<TKey, TValue>& mCache;
+ size_t mIndex;
+ };
+
+private:
+ LruCache(const LruCache& that); // disallow copy constructor
+
+ struct Entry {
+ TKey key;
+ TValue value;
+ Entry* parent;
+ Entry* child;
+
+ Entry(TKey key_, TValue value_) : key(key_), value(value_), parent(NULL), child(NULL) {
+ }
+ const TKey& getKey() const { return key; }
+ };
+
+ void attachToCache(Entry& entry);
+ void detachFromCache(Entry& entry);
+ void rehash(size_t newCapacity);
+
+ UniquePtr<BasicHashtable<TKey, Entry> > mTable;
+ OnEntryRemoved<TKey, TValue>* mListener;
+ Entry* mOldest;
+ Entry* mYoungest;
+ uint32_t mMaxCapacity;
+ TValue mNullValue;
+};
+
+// Implementation is here, because it's fully templated
+template <typename TKey, typename TValue>
+LruCache<TKey, TValue>::LruCache(uint32_t maxCapacity): mMaxCapacity(maxCapacity),
+ mNullValue(NULL), mTable(new BasicHashtable<TKey, Entry>), mYoungest(NULL), mOldest(NULL),
+ mListener(NULL) {
+};
+
+template<typename K, typename V>
+void LruCache<K, V>::setOnEntryRemovedListener(OnEntryRemoved<K, V>* listener) {
+ mListener = listener;
+}
+
+template <typename TKey, typename TValue>
+size_t LruCache<TKey, TValue>::size() const {
+ return mTable->size();
+}
+
+template <typename TKey, typename TValue>
+const TValue& LruCache<TKey, TValue>::get(const TKey& key) {
+ hash_t hash = hash_type(key);
+ ssize_t index = mTable->find(-1, hash, key);
+ if (index == -1) {
+ return mNullValue;
+ }
+ Entry& entry = mTable->editEntryAt(index);
+ detachFromCache(entry);
+ attachToCache(entry);
+ return entry.value;
+}
+
+template <typename TKey, typename TValue>
+bool LruCache<TKey, TValue>::put(const TKey& key, const TValue& value) {
+ if (mMaxCapacity != kUnlimitedCapacity && size() >= mMaxCapacity) {
+ removeOldest();
+ }
+
+ hash_t hash = hash_type(key);
+ ssize_t index = mTable->find(-1, hash, key);
+ if (index >= 0) {
+ return false;
+ }
+ if (!mTable->hasMoreRoom()) {
+ rehash(mTable->capacity() * 2);
+ }
+
+ // Would it be better to initialize a blank entry and assign key, value?
+ Entry initEntry(key, value);
+ index = mTable->add(hash, initEntry);
+ Entry& entry = mTable->editEntryAt(index);
+ attachToCache(entry);
+ return true;
+}
+
+template <typename TKey, typename TValue>
+bool LruCache<TKey, TValue>::remove(const TKey& key) {
+ hash_t hash = hash_type(key);
+ ssize_t index = mTable->find(-1, hash, key);
+ if (index < 0) {
+ return false;
+ }
+ Entry& entry = mTable->editEntryAt(index);
+ if (mListener) {
+ (*mListener)(entry.key, entry.value);
+ }
+ detachFromCache(entry);
+ mTable->removeAt(index);
+ return true;
+}
+
+template <typename TKey, typename TValue>
+bool LruCache<TKey, TValue>::removeOldest() {
+ if (mOldest != NULL) {
+ return remove(mOldest->key);
+ // TODO: should probably abort if false
+ }
+ return false;
+}
+
+template <typename TKey, typename TValue>
+void LruCache<TKey, TValue>::clear() {
+ if (mListener) {
+ for (Entry* p = mOldest; p != NULL; p = p->child) {
+ (*mListener)(p->key, p->value);
+ }
+ }
+ mYoungest = NULL;
+ mOldest = NULL;
+ mTable->clear();
+}
+
+template <typename TKey, typename TValue>
+void LruCache<TKey, TValue>::attachToCache(Entry& entry) {
+ if (mYoungest == NULL) {
+ mYoungest = mOldest = &entry;
+ } else {
+ entry.parent = mYoungest;
+ mYoungest->child = &entry;
+ mYoungest = &entry;
+ }
+}
+
+template <typename TKey, typename TValue>
+void LruCache<TKey, TValue>::detachFromCache(Entry& entry) {
+ if (entry.parent != NULL) {
+ entry.parent->child = entry.child;
+ } else {
+ mOldest = entry.child;
+ }
+ if (entry.child != NULL) {
+ entry.child->parent = entry.parent;
+ } else {
+ mYoungest = entry.parent;
+ }
+
+ entry.parent = NULL;
+ entry.child = NULL;
+}
+
+template <typename TKey, typename TValue>
+void LruCache<TKey, TValue>::rehash(size_t newCapacity) {
+ UniquePtr<BasicHashtable<TKey, Entry> > oldTable(mTable.release());
+ Entry* oldest = mOldest;
+
+ mOldest = NULL;
+ mYoungest = NULL;
+ mTable.reset(new BasicHashtable<TKey, Entry>(newCapacity));
+ for (Entry* p = oldest; p != NULL; p = p->child) {
+ put(p->key, p->value);
+ }
+}
+
+}
+
+#endif // ANDROID_UTILS_LRU_CACHE_H
diff --git a/include/utils/Mutex.h b/include/utils/Mutex.h
new file mode 100644
index 0000000..dd201c8
--- /dev/null
+++ b/include/utils/Mutex.h
@@ -0,0 +1,137 @@
+/*
+ * 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(HAVE_PTHREADS)
+# include <pthread.h>
+#endif
+
+#include <utils/Errors.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();
+
+ // 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(HAVE_PTHREADS)
+ pthread_mutex_t mMutex;
+#else
+ void _init();
+ void* mState;
+#endif
+};
+
+// ---------------------------------------------------------------------------
+
+#if defined(HAVE_PTHREADS)
+
+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);
+}
+
+#endif // HAVE_PTHREADS
+
+// ---------------------------------------------------------------------------
+
+/*
+ * 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/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..90beb5f
--- /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(HAVE_PTHREADS)
+# include <pthread.h>
+#endif
+
+#include <utils/Errors.h>
+#include <utils/ThreadDefs.h>
+
+// ---------------------------------------------------------------------------
+namespace android {
+// ---------------------------------------------------------------------------
+
+#if defined(HAVE_PTHREADS)
+
+/*
+ * 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 // HAVE_PTHREADS
+
+// ---------------------------------------------------------------------------
+}; // 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..cbfe13a
--- /dev/null
+++ b/include/utils/RefBase.h
@@ -0,0 +1,546 @@
+/*
+ * 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;
+};
+
+// ---------------------------------------------------------------------------
+
+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) : s(s), d(d) { }
+ };
+
+ 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) : s(s), d(d) { }
+ };
+
+ 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..c60680e
--- /dev/null
+++ b/include/utils/Singleton.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 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<> Mutex Singleton< TYPE >::sLock(Mutex::PRIVATE); \
+ template<> TYPE* Singleton< TYPE >::sInstance(0); \
+ template class 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..ef59470
--- /dev/null
+++ b/include/utils/String8.h
@@ -0,0 +1,395 @@
+/*
+ * 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;
+
+ 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 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..df30611
--- /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(HAVE_PTHREADS)
+# 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
+ // androidGetTid(), 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..d015421
--- /dev/null
+++ b/include/utils/Timers.h
@@ -0,0 +1,106 @@
+/*
+ * 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>
+
+// ------------------------------------------------------------------
+// C API
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef int64_t nsecs_t; // nano-seconds
+
+static inline nsecs_t seconds_to_nanoseconds(nsecs_t secs)
+{
+ return secs*1000000000;
+}
+
+static inline nsecs_t milliseconds_to_nanoseconds(nsecs_t secs)
+{
+ return secs*1000000;
+}
+
+static inline nsecs_t microseconds_to_nanoseconds(nsecs_t secs)
+{
+ return secs*1000;
+}
+
+static inline nsecs_t nanoseconds_to_seconds(nsecs_t secs)
+{
+ return secs/1000000000;
+}
+
+static inline nsecs_t nanoseconds_to_milliseconds(nsecs_t secs)
+{
+ return secs/1000000;
+}
+
+static inline nsecs_t nanoseconds_to_microseconds(nsecs_t secs)
+{
+ return secs/1000;
+}
+
+static inline nsecs_t s2ns(nsecs_t v) {return seconds_to_nanoseconds(v);}
+static inline nsecs_t ms2ns(nsecs_t v) {return milliseconds_to_nanoseconds(v);}
+static inline nsecs_t us2ns(nsecs_t v) {return microseconds_to_nanoseconds(v);}
+static inline nsecs_t ns2s(nsecs_t v) {return nanoseconds_to_seconds(v);}
+static inline nsecs_t ns2ms(nsecs_t v) {return nanoseconds_to_milliseconds(v);}
+static inline nsecs_t ns2us(nsecs_t v) {return nanoseconds_to_microseconds(v);}
+
+static inline nsecs_t seconds(nsecs_t v) { return s2ns(v); }
+static inline nsecs_t milliseconds(nsecs_t v) { return ms2ns(v); }
+static 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..13c9081
--- /dev/null
+++ b/include/utils/TypeHelpers.h
@@ -0,0 +1,302 @@
+/*
+ * 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--) {
+ 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 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_dtor && traits<TYPE>::has_trivial_copy)
+ || traits<TYPE>::has_trivial_move)
+ {
+ memmove(d,s,n*sizeof(TYPE));
+ } else {
+ 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();
+ }
+ }
+ }
+}
+
+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--) {
+ 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..c8c87c3
--- /dev/null
+++ b/include/utils/Unicode.h
@@ -0,0 +1,175 @@
+/*
+ * 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" {
+
+typedef uint32_t char32_t;
+typedef uint16_t char16_t;
+
+// 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/UniquePtr.h b/include/utils/UniquePtr.h
new file mode 100644
index 0000000..bc62fe6
--- /dev/null
+++ b/include/utils/UniquePtr.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.
+ */
+
+/* === NOTE === NOTE === NOTE === NOTE === NOTE === NOTE === NOTE === NOTE ===
+ *
+ * THIS IS A COPY OF libcore/include/UniquePtr.h AND AS SUCH THAT IS THE
+ * CANONICAL SOURCE OF THIS FILE. PLEASE KEEP THEM IN SYNC.
+ *
+ * === NOTE === NOTE === NOTE === NOTE === NOTE === NOTE === NOTE === NOTE ===
+ */
+
+#ifndef UNIQUE_PTR_H_included
+#define UNIQUE_PTR_H_included
+
+#include <cstdlib> // For NULL.
+
+// Default deleter for pointer types.
+template <typename T>
+struct DefaultDelete {
+ enum { type_must_be_complete = sizeof(T) };
+ DefaultDelete() {}
+ void operator()(T* p) const {
+ delete p;
+ }
+};
+
+// Default deleter for array types.
+template <typename T>
+struct DefaultDelete<T[]> {
+ enum { type_must_be_complete = sizeof(T) };
+ void operator()(T* p) const {
+ delete[] p;
+ }
+};
+
+// A smart pointer that deletes the given pointer on destruction.
+// Equivalent to C++0x's std::unique_ptr (a combination of boost::scoped_ptr
+// and boost::scoped_array).
+// Named to be in keeping with Android style but also to avoid
+// collision with any other implementation, until we can switch over
+// to unique_ptr.
+// Use thus:
+// UniquePtr<C> c(new C);
+template <typename T, typename D = DefaultDelete<T> >
+class UniquePtr {
+public:
+ // Construct a new UniquePtr, taking ownership of the given raw pointer.
+ explicit UniquePtr(T* ptr = NULL) : mPtr(ptr) {
+ }
+
+ ~UniquePtr() {
+ reset();
+ }
+
+ // Accessors.
+ T& operator*() const { return *mPtr; }
+ T* operator->() const { return mPtr; }
+ T* get() const { return mPtr; }
+
+ // Returns the raw pointer and hands over ownership to the caller.
+ // The pointer will not be deleted by UniquePtr.
+ T* release() __attribute__((warn_unused_result)) {
+ T* result = mPtr;
+ mPtr = NULL;
+ return result;
+ }
+
+ // Takes ownership of the given raw pointer.
+ // If this smart pointer previously owned a different raw pointer, that
+ // raw pointer will be freed.
+ void reset(T* ptr = NULL) {
+ if (ptr != mPtr) {
+ D()(mPtr);
+ mPtr = ptr;
+ }
+ }
+
+private:
+ // The raw pointer.
+ T* mPtr;
+
+ // Comparing unique pointers is probably a mistake, since they're unique.
+ template <typename T2> bool operator==(const UniquePtr<T2>& p) const;
+ template <typename T2> bool operator!=(const UniquePtr<T2>& p) const;
+
+ // Disallow copy and assignment.
+ UniquePtr(const UniquePtr&);
+ void operator=(const UniquePtr&);
+};
+
+// Partial specialization for array types. Like std::unique_ptr, this removes
+// operator* and operator-> but adds operator[].
+template <typename T, typename D>
+class UniquePtr<T[], D> {
+public:
+ explicit UniquePtr(T* ptr = NULL) : mPtr(ptr) {
+ }
+
+ ~UniquePtr() {
+ reset();
+ }
+
+ T& operator[](size_t i) const {
+ return mPtr[i];
+ }
+ T* get() const { return mPtr; }
+
+ T* release() __attribute__((warn_unused_result)) {
+ T* result = mPtr;
+ mPtr = NULL;
+ return result;
+ }
+
+ void reset(T* ptr = NULL) {
+ if (ptr != mPtr) {
+ D()(mPtr);
+ mPtr = ptr;
+ }
+ }
+
+private:
+ T* mPtr;
+
+ // Disallow copy and assignment.
+ UniquePtr(const UniquePtr&);
+ void operator=(const UniquePtr&);
+};
+
+#if UNIQUE_PTR_TESTS
+
+// Run these tests with:
+// g++ -g -DUNIQUE_PTR_TESTS -x c++ UniquePtr.h && ./a.out
+
+#include <stdio.h>
+
+static void assert(bool b) {
+ if (!b) {
+ fprintf(stderr, "FAIL\n");
+ abort();
+ }
+ fprintf(stderr, "OK\n");
+}
+static int cCount = 0;
+struct C {
+ C() { ++cCount; }
+ ~C() { --cCount; }
+};
+static bool freed = false;
+struct Freer {
+ void operator()(int* p) {
+ assert(*p == 123);
+ free(p);
+ freed = true;
+ }
+};
+
+int main(int argc, char* argv[]) {
+ //
+ // UniquePtr<T> tests...
+ //
+
+ // Can we free a single object?
+ {
+ UniquePtr<C> c(new C);
+ assert(cCount == 1);
+ }
+ assert(cCount == 0);
+ // Does release work?
+ C* rawC;
+ {
+ UniquePtr<C> c(new C);
+ assert(cCount == 1);
+ rawC = c.release();
+ }
+ assert(cCount == 1);
+ delete rawC;
+ // Does reset work?
+ {
+ UniquePtr<C> c(new C);
+ assert(cCount == 1);
+ c.reset(new C);
+ assert(cCount == 1);
+ }
+ assert(cCount == 0);
+
+ //
+ // UniquePtr<T[]> tests...
+ //
+
+ // Can we free an array?
+ {
+ UniquePtr<C[]> cs(new C[4]);
+ assert(cCount == 4);
+ }
+ assert(cCount == 0);
+ // Does release work?
+ {
+ UniquePtr<C[]> c(new C[4]);
+ assert(cCount == 4);
+ rawC = c.release();
+ }
+ assert(cCount == 4);
+ delete[] rawC;
+ // Does reset work?
+ {
+ UniquePtr<C[]> c(new C[4]);
+ assert(cCount == 4);
+ c.reset(new C[2]);
+ assert(cCount == 2);
+ }
+ assert(cCount == 0);
+
+ //
+ // Custom deleter tests...
+ //
+ assert(!freed);
+ {
+ UniquePtr<int, Freer> i(reinterpret_cast<int*>(malloc(sizeof(int))));
+ *i = 123;
+ }
+ assert(freed);
+ return 0;
+}
+#endif
+
+#endif // UNIQUE_PTR_H_included
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/init/builtins.c b/init/builtins.c
index bfc0ddb..e8c8f91 100644
--- a/init/builtins.c
+++ b/init/builtins.c
@@ -57,7 +57,7 @@
{
int fd, ret, len;
- fd = open(path, O_WRONLY|O_CREAT, 0622);
+ fd = open(path, O_WRONLY|O_CREAT|O_NOFOLLOW, 0600);
if (fd < 0)
return -errno;
diff --git a/init/devices.c b/init/devices.c
index de27a7a..af88c5f 100644
--- a/init/devices.c
+++ b/init/devices.c
@@ -33,6 +33,7 @@
#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>
@@ -451,6 +452,8 @@
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
@@ -828,6 +831,15 @@
struct uevent uevent;
parse_event(msg, &uevent);
+ if (sehandle && 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);
}
@@ -894,6 +906,7 @@
sehandle = NULL;
if (is_selinux_enabled() > 0) {
sehandle = selinux_android_file_context_handle();
+ selinux_status_open(true);
}
/* is 256K enough? udev uses 16MB! */
diff --git a/init/init.c b/init/init.c
index b7e34d0..feac8ad 100644
--- a/init/init.c
+++ b/init/init.c
@@ -42,6 +42,7 @@
#include <cutils/android_reboot.h>
#include <cutils/sockets.h>
#include <cutils/iosched_policy.h>
+#include <cutils/fs.h>
#include <private/android_filesystem_config.h>
#include <termios.h>
@@ -249,14 +250,12 @@
for (ei = svc->envvars; ei; ei = ei->next)
add_environment(ei->name, ei->value);
- setsockcreatecon(scon);
-
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->perm, si->uid, si->gid, si->socketcon ?: scon);
if (s >= 0) {
publish_socket(si->name, s);
}
@@ -264,7 +263,6 @@
freecon(scon);
scon = NULL;
- setsockcreatecon(NULL);
if (svc->ioprio_class != IoSchedClass_NONE) {
if (android_set_ioprio(getpid(), svc->ioprio_class, svc->ioprio_pri)) {
@@ -559,6 +557,84 @@
return ret;
}
+/*
+ * 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));
+ 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));
+ 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 %d 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);
+ }
+ memset(buf, 0, sizeof(buf));
+ return result;
+}
+
static int keychord_init_action(int nargs, char **args)
{
keychord_init();
@@ -962,6 +1038,7 @@
action_for_each_trigger("early-init", action_add_queue_tail);
queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
+ 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");
@@ -976,6 +1053,11 @@
action_for_each_trigger("post-fs-data", 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");
+
queue_builtin_action(property_service_init_action, "property_service_init");
queue_builtin_action(signal_init_action, "signal_init");
queue_builtin_action(check_startup_action, "check_startup");
diff --git a/init/init.h b/init/init.h
index aa6a4ab..3928d52 100644
--- a/init/init.h
+++ b/init/init.h
@@ -55,6 +55,7 @@
uid_t uid;
gid_t gid;
int perm;
+ const char *socketcon;
};
struct svcenvinfo {
diff --git a/init/init_parser.c b/init/init_parser.c
index 776c699..7c2fa8c 100644
--- a/init/init_parser.c
+++ b/init/init_parser.c
@@ -771,7 +771,7 @@
svc->envvars = ei;
break;
}
- case K_socket: {/* name type perm [ uid gid ] */
+ case K_socket: {/* name type perm [ uid gid context ] */
struct socketinfo *si;
if (nargs < 4) {
parse_error(state, "socket option requires name, type, perm arguments\n");
@@ -794,6 +794,8 @@
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;
diff --git a/init/property_service.c b/init/property_service.c
index 9afc756..1b9327c 100644
--- a/init/property_service.c
+++ b/init/property_service.c
@@ -81,6 +81,7 @@
{ "sys.powerctl", AID_SHELL, 0 },
{ "service.", AID_SYSTEM, 0 },
{ "wlan.", AID_SYSTEM, 0 },
+ { "gps.", AID_GPS, 0 },
{ "bluetooth.", AID_BLUETOOTH, 0 },
{ "dhcp.", AID_SYSTEM, 0 },
{ "dhcp.", AID_DHCP, 0 },
@@ -92,6 +93,7 @@
{ "persist.sys.", AID_SYSTEM, 0 },
{ "persist.service.", AID_SYSTEM, 0 },
{ "persist.security.", AID_SYSTEM, 0 },
+ { "persist.gps.", AID_GPS, 0 },
{ "persist.service.bdroid.", AID_BLUETOOTH, 0 },
{ "selinux." , AID_SYSTEM, 0 },
{ NULL, 0, 0 }
@@ -276,6 +278,32 @@
}
}
+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;
+}
+
int property_set(const char *name, const char *value)
{
prop_info *pi;
@@ -284,9 +312,8 @@
size_t namelen = strlen(name);
size_t valuelen = strlen(value);
- if(namelen >= PROP_NAME_MAX) return -1;
- if(valuelen >= PROP_VALUE_MAX) return -1;
- if(namelen < 1) return -1;
+ if (!is_legal_property_name(name, namelen)) return -1;
+ if (valuelen >= PROP_VALUE_MAX) return -1;
pi = (prop_info*) __system_property_find(name);
@@ -298,7 +325,7 @@
} else {
ret = __system_property_add(name, namelen, value, valuelen);
if (ret < 0) {
- ERROR("Failed to set '%s'='%s'", name, value);
+ ERROR("Failed to set '%s'='%s'\n", name, value);
return ret;
}
}
@@ -364,6 +391,12 @@
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) {
@@ -404,10 +437,13 @@
*sz = pa_workspace.size;
}
-static void load_properties(char *data)
+static void load_properties(char *data, char *prefix)
{
char *key, *value, *eol, *sol, *tmp;
+ size_t plen;
+ if (prefix)
+ plen = strlen(prefix);
sol = data;
while((eol = strchr(sol, '\n'))) {
key = sol;
@@ -423,6 +459,9 @@
tmp = value - 2;
while((tmp > key) && isspace(*tmp)) *tmp-- = 0;
+ if (prefix && strncmp(key, prefix, plen))
+ continue;
+
while(isspace(*value)) value++;
tmp = eol - 2;
while((tmp > value) && isspace(*tmp)) *tmp-- = 0;
@@ -431,7 +470,7 @@
}
}
-static void load_properties_from_file(const char *fn)
+static void load_properties_from_file(const char *fn, char *prefix)
{
char *data;
unsigned sz;
@@ -439,7 +478,7 @@
data = read_file(fn, &sz);
if(data != 0) {
- load_properties(data);
+ load_properties(data, prefix);
free(data);
}
}
@@ -512,7 +551,7 @@
void property_load_boot_defaults(void)
{
- load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT);
+ load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT, NULL);
}
int properties_inited(void)
@@ -527,7 +566,7 @@
ret = property_get("ro.debuggable", debuggable);
if (ret && (strcmp(debuggable, "1") == 0)) {
- load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE);
+ load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE, NULL);
}
#endif /* ALLOW_LOCAL_PROP_OVERRIDE */
}
@@ -549,13 +588,14 @@
{
int fd;
- load_properties_from_file(PROP_PATH_SYSTEM_BUILD);
- load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT);
+ load_properties_from_file(PROP_PATH_SYSTEM_BUILD, NULL);
+ load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT, 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();
- fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0);
+ fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0, NULL);
if(fd < 0) return;
fcntl(fd, F_SETFD, FD_CLOEXEC);
fcntl(fd, F_SETFL, O_NONBLOCK);
diff --git a/init/readme.txt b/init/readme.txt
index 7a5997d..1e8c392 100644
--- a/init/readme.txt
+++ b/init/readme.txt
@@ -70,10 +70,13 @@
setenv <name> <value>
Set the environment variable <name> to <value> in the launched process.
-socket <name> <type> <perm> [ <user> [ <group> ] ]
+socket <name> <type> <perm> [ <user> [ <group> [ <context> ] ] ]
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.
+ Context 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.
diff --git a/init/util.c b/init/util.c
index fffc8a4..9aaa77d 100644
--- a/init/util.c
+++ b/init/util.c
@@ -84,11 +84,15 @@
* 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)
+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 *secon;
+ char *filecon;
+
+ if (socketcon)
+ setsockcreatecon(socketcon);
fd = socket(PF_UNIX, type, 0);
if (fd < 0) {
@@ -96,6 +100,9 @@
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",
@@ -107,11 +114,11 @@
goto out_close;
}
- secon = NULL;
+ filecon = NULL;
if (sehandle) {
- ret = selabel_lookup(sehandle, &secon, addr.sun_path, S_IFSOCK);
+ ret = selabel_lookup(sehandle, &filecon, addr.sun_path, S_IFSOCK);
if (ret == 0)
- setfscreatecon(secon);
+ setfscreatecon(filecon);
}
ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr));
@@ -121,7 +128,7 @@
}
setfscreatecon(NULL);
- freecon(secon);
+ freecon(filecon);
chown(addr.sun_path, uid, gid);
chmod(addr.sun_path, perm);
@@ -306,14 +313,27 @@
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 (isalnum(*s))
- s++;
- *s = 0;
+
+ for (; *s; s++) {
+ s += strspn(s, accept);
+ if (*s) *s = '_';
+ }
}
+
void make_link(const char *oldpath, const char *newpath)
{
int ret;
@@ -385,7 +405,9 @@
void get_hardware_name(char *hardware, unsigned int *revision)
{
- char data[1024];
+ const char *cpuinfo = "/proc/cpuinfo";
+ char *data = NULL;
+ size_t len = 0, limit = 1024;
int fd, n;
char *x, *hw, *rev;
@@ -393,14 +415,32 @@
if (hardware[0])
return;
- fd = open("/proc/cpuinfo", O_RDONLY);
+ fd = open(cpuinfo, O_RDONLY);
if (fd < 0) return;
- n = read(fd, data, 1023);
- close(fd);
- if (n < 0) return;
+ for (;;) {
+ x = realloc(data, limit);
+ if (!x) {
+ ERROR("Failed to allocate memory to read %s\n", cpuinfo);
+ goto done;
+ }
+ data = x;
- data[n] = 0;
+ n = read(fd, data + len, limit - len);
+ if (n < 0) {
+ ERROR("Failed reading %s: %s (%d)\n", cpuinfo, strerror(errno), errno);
+ goto done;
+ }
+ len += n;
+
+ if (len < limit)
+ break;
+
+ /* We filled the buffer, so increase size and loop to read more */
+ limit *= 2;
+ }
+
+ data[len] = 0;
hw = strstr(data, "\nHardware");
rev = strstr(data, "\nRevision");
@@ -425,18 +465,22 @@
*revision = strtoul(x + 2, 0, 16);
}
}
+
+done:
+ close(fd);
+ free(data);
}
void import_kernel_cmdline(int in_qemu,
void (*import_kernel_nv)(char *name, int in_qemu))
{
- char cmdline[1024];
+ char cmdline[2048];
char *ptr;
int fd;
fd = open("/proc/cmdline", O_RDONLY);
if (fd >= 0) {
- int n = read(fd, cmdline, 1023);
+ int n = read(fd, cmdline, sizeof(cmdline) - 1);
if (n < 0) n = 0;
/* get rid of trailing newline, it happens */
diff --git a/init/util.h b/init/util.h
index 6bca4e6..04b8129 100644
--- a/init/util.h
+++ b/init/util.h
@@ -26,7 +26,7 @@
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);
+ uid_t uid, gid_t gid, const char *socketcon);
void *read_file(const char *fn, unsigned *_sz);
time_t gettime(void);
unsigned int decode_uid(const char *s);
diff --git a/libcorkscrew/Android.mk b/libcorkscrew/Android.mk
index d62c2d5..e275317 100644
--- a/libcorkscrew/Android.mk
+++ b/libcorkscrew/Android.mk
@@ -51,7 +51,7 @@
LOCAL_SHARED_LIBRARIES += libdl libcutils liblog libgccdemangle
-LOCAL_CFLAGS += -std=gnu99 -Werror
+LOCAL_CFLAGS += -std=gnu99 -Werror -Wno-unused-parameter
LOCAL_MODULE := libcorkscrew
LOCAL_MODULE_TAGS := optional
@@ -81,7 +81,7 @@
LOCAL_SHARED_LIBRARIES += libgccdemangle # TODO: is this even needed on Linux?
LOCAL_LDLIBS += -lrt
endif
-LOCAL_CFLAGS += -std=gnu99 -Werror
+LOCAL_CFLAGS += -std=gnu99 -Werror -Wno-unused-parameter
LOCAL_MODULE := libcorkscrew
LOCAL_MODULE_TAGS := optional
include $(BUILD_HOST_SHARED_LIBRARY)
diff --git a/libcorkscrew/arch-x86/backtrace-x86.c b/libcorkscrew/arch-x86/backtrace-x86.c
index e133ab6..ef22821 100755
--- a/libcorkscrew/arch-x86/backtrace-x86.c
+++ b/libcorkscrew/arch-x86/backtrace-x86.c
@@ -380,7 +380,7 @@
case DW_CFA_offset_extended: // probably we don't have it on x86.
if (!try_get_uleb128(memory, ptr, ®, cursor)) return false;
if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false;
- if (reg > DWARF_REGISTERS) {
+ if (reg >= DWARF_REGISTERS) {
ALOGE("DW_CFA_offset_extended: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS);
return false;
}
@@ -390,39 +390,39 @@
break;
case DW_CFA_restore_extended: // probably we don't have it on x86.
if (!try_get_uleb128(memory, ptr, ®, cursor)) return false;
- dstate->regs[reg].rule = stack->regs[reg].rule;
- dstate->regs[reg].value = stack->regs[reg].value;
- if (reg > DWARF_REGISTERS) {
+ if (reg >= DWARF_REGISTERS) {
ALOGE("DW_CFA_restore_extended: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS);
return false;
}
+ dstate->regs[reg].rule = stack->regs[reg].rule;
+ dstate->regs[reg].value = stack->regs[reg].value;
ALOGV("DW_CFA_restore: r%d = %c(%d)", reg, dstate->regs[reg].rule, dstate->regs[reg].value);
break;
case DW_CFA_undefined: // probably we don't have it on x86.
if (!try_get_uleb128(memory, ptr, ®, cursor)) return false;
- dstate->regs[reg].rule = 'u';
- dstate->regs[reg].value = 0;
- if (reg > DWARF_REGISTERS) {
+ if (reg >= DWARF_REGISTERS) {
ALOGE("DW_CFA_undefined: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS);
return false;
}
+ dstate->regs[reg].rule = 'u';
+ dstate->regs[reg].value = 0;
ALOGV("DW_CFA_undefined: r%d", reg);
break;
case DW_CFA_same_value: // probably we don't have it on x86.
if (!try_get_uleb128(memory, ptr, ®, cursor)) return false;
- dstate->regs[reg].rule = 's';
- dstate->regs[reg].value = 0;
- if (reg > DWARF_REGISTERS) {
+ if (reg >= DWARF_REGISTERS) {
ALOGE("DW_CFA_undefined: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS);
return false;
}
+ dstate->regs[reg].rule = 's';
+ dstate->regs[reg].value = 0;
ALOGV("DW_CFA_same_value: r%d", reg);
break;
case DW_CFA_register: // probably we don't have it on x86.
if (!try_get_uleb128(memory, ptr, ®, cursor)) return false;
/* that's new register actually, not offset */
if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false;
- if (reg > DWARF_REGISTERS || offset > DWARF_REGISTERS) {
+ if (reg >= DWARF_REGISTERS || offset >= DWARF_REGISTERS) {
ALOGE("DW_CFA_register: r%d or r%d exceeds supported number of registers (%d)", reg, offset, DWARF_REGISTERS);
return false;
}
@@ -520,7 +520,7 @@
/* Updaing state based on dwarf state. */
static bool update_state(const memory_t* memory, unwind_state_t* state,
- dwarf_state_t* dstate, cie_info_t* cie_info) {
+ dwarf_state_t* dstate) {
unwind_state_t newstate;
/* We can restore more registers here if we need them. Meanwile doing minimal work here. */
/* Getting CFA. */
@@ -550,7 +550,6 @@
/* Execute CIE and FDE instructions for FDE found with find_fde. */
static bool execute_fde(const memory_t* memory,
- const map_info_t* map_info_list,
uintptr_t fde,
unwind_state_t* state) {
uint32_t fde_length = 0;
@@ -753,7 +752,7 @@
ALOGV("IP: %x, LOC: %x", state->reg[DWARF_EIP], dstate->loc);
}
- return update_state(memory, state, dstate, cie_info);
+ return update_state(memory, state, dstate);
}
static ssize_t unwind_backtrace_common(const memory_t* memory,
@@ -805,7 +804,7 @@
uint32_t stack_top = state->reg[DWARF_ESP];
- if (!execute_fde(memory, map_info_list, fde, state)) break;
+ if (!execute_fde(memory, fde, state)) break;
if (frame) {
frame->stack_top = stack_top;
diff --git a/libcutils/Android.mk b/libcutils/Android.mk
index e46216e..0fd5a57 100644
--- a/libcutils/Android.mk
+++ b/libcutils/Android.mk
@@ -65,10 +65,7 @@
WINDOWS_HOST_ONLY := 1
endif
-ifeq ($(WINDOWS_HOST_ONLY),1)
- commonSources += \
- uio.c
-else
+ifneq ($(WINDOWS_HOST_ONLY),1)
commonSources += \
fs.c \
multiuser.c
diff --git a/libcutils/ashmem-dev.c b/libcutils/ashmem-dev.c
index 8b71f87..3089a94 100644
--- a/libcutils/ashmem-dev.c
+++ b/libcutils/ashmem-dev.c
@@ -48,7 +48,7 @@
return fd;
if (name) {
- char buf[ASHMEM_NAME_LEN];
+ char buf[ASHMEM_NAME_LEN] = {0};
strlcpy(buf, name, sizeof(buf));
ret = ioctl(fd, ASHMEM_SET_NAME, buf);
diff --git a/liblog/Android.mk b/liblog/Android.mk
index be5cec2..6bfb119 100644
--- a/liblog/Android.mk
+++ b/liblog/Android.mk
@@ -36,6 +36,9 @@
liblog_sources += \
logprint.c \
event_tag_map.c
+else
+ liblog_sources += \
+ uio.c
endif
liblog_host_sources := $(liblog_sources) fake_log_device.c
diff --git a/liblog/event_tag_map.c b/liblog/event_tag_map.c
index e70754e..f3d1e2f 100644
--- a/liblog/event_tag_map.c
+++ b/liblog/event_tag_map.c
@@ -13,8 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#include "cutils/event_tag_map.h"
-#include "cutils/log.h"
+#include <log/event_tag_map.h>
+#include <log/log.h>
#include <stdlib.h>
#include <string.h>
diff --git a/liblog/fake_log_device.c b/liblog/fake_log_device.c
index df43299..5283619 100644
--- a/liblog/fake_log_device.c
+++ b/liblog/fake_log_device.c
@@ -19,7 +19,7 @@
* passed on to the underlying (fake) log device. When not in the
* simulator, messages are printed to stderr.
*/
-#include "cutils/logd.h"
+#include <log/logd.h>
#include <stdlib.h>
#include <string.h>
diff --git a/liblog/logd_write.c b/liblog/logd_write.c
index 3613d25..fff7cc4 100644
--- a/liblog/logd_write.c
+++ b/liblog/logd_write.c
@@ -27,9 +27,9 @@
#include <sys/types.h>
#include <sys/stat.h>
-#include <cutils/logger.h>
-#include <cutils/logd.h>
-#include <cutils/log.h>
+#include <log/logger.h>
+#include <log/logd.h>
+#include <log/log.h>
#define LOG_BUF_SIZE 1024
diff --git a/liblog/logprint.c b/liblog/logprint.c
index 6fac84b..508c825 100644
--- a/liblog/logprint.c
+++ b/liblog/logprint.c
@@ -26,8 +26,8 @@
#include <assert.h>
#include <arpa/inet.h>
-#include <cutils/logd.h>
-#include <cutils/logprint.h>
+#include <log/logd.h>
+#include <log/logprint.h>
typedef struct FilterInfo_t {
char *mTag;
diff --git a/libcutils/uio.c b/liblog/uio.c
similarity index 98%
rename from libcutils/uio.c
rename to liblog/uio.c
index baa8051..cfa4cb1 100644
--- a/libcutils/uio.c
+++ b/liblog/uio.c
@@ -16,7 +16,7 @@
#ifndef HAVE_SYS_UIO_H
-#include <cutils/uio.h>
+#include <log/uio.h>
#include <unistd.h>
int readv( int fd, struct iovec* vecs, int count )
diff --git a/libmemtrack/Android.mk b/libmemtrack/Android.mk
new file mode 100644
index 0000000..c23b6f4
--- /dev/null
+++ b/libmemtrack/Android.mk
@@ -0,0 +1,20 @@
+# Copyright 2013 The Android Open Source Project
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_SRC_FILES := memtrack.c
+LOCAL_MODULE := libmemtrack
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/include 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/include/memtrack.h b/libmemtrack/include/memtrack.h
new file mode 100644
index 0000000..d6b370b
--- /dev/null
+++ b/libmemtrack/include/memtrack.h
@@ -0,0 +1,138 @@
+/*
+ * 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>
+
+/**
+ * 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_gl_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_gl_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);
+
+#endif
diff --git a/libmemtrack/memtrack.c b/libmemtrack/memtrack.c
new file mode 100644
index 0000000..2b2651a
--- /dev/null
+++ b/libmemtrack/memtrack.c
@@ -0,0 +1,200 @@
+/*
+ * 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.h>
+
+#define LOG_TAG "memtrack"
+
+#include <log/log.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..f306f67
--- /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.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/%zd/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/libnetutils/dhcp_utils.c b/libnetutils/dhcp_utils.c
index da00f74..e1df874 100644
--- a/libnetutils/dhcp_utils.c
+++ b/libnetutils/dhcp_utils.c
@@ -92,7 +92,8 @@
char *server,
uint32_t *lease,
char *vendorInfo,
- char *domain)
+ char *domain,
+ char *mtu)
{
char prop_name[PROPERTY_KEY_MAX];
char prop_value[PROPERTY_VALUE_MAX];
@@ -158,6 +159,10 @@
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;
}
@@ -186,7 +191,8 @@
char *server,
uint32_t *lease,
char *vendorInfo,
- char *domain)
+ char *domain,
+ char *mtu)
{
char result_prop_name[PROPERTY_KEY_MAX];
char daemon_prop_name[PROPERTY_KEY_MAX];
@@ -238,7 +244,7 @@
if (strcmp(prop_value, "ok") == 0) {
char dns_prop_name[PROPERTY_KEY_MAX];
if (fill_ip_info(interface, ipaddr, gateway, prefixLength, dns,
- server, lease, vendorInfo, domain) == -1) {
+ server, lease, vendorInfo, domain, mtu) == -1) {
return -1;
}
return 0;
@@ -329,7 +335,8 @@
char *server,
uint32_t *lease,
char *vendorInfo,
- char *domain)
+ char *domain,
+ char *mtu)
{
char result_prop_name[PROPERTY_KEY_MAX];
char prop_value[PROPERTY_VALUE_MAX] = {'\0'};
@@ -366,7 +373,7 @@
}
if (strcmp(prop_value, "ok") == 0) {
return fill_ip_info(interface, ipaddr, gateway, prefixLength, dns,
- server, lease, vendorInfo, domain);
+ server, lease, vendorInfo, domain, mtu);
} else {
snprintf(errmsg, sizeof(errmsg), "DHCP Renew result was %s", prop_value);
return -1;
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index 4beebb7..aae2ae7 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -23,6 +23,10 @@
#include <sys/types.h>
#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+
#include <linux/if.h>
#include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter_ipv4/ipt_ULOG.h>
@@ -38,6 +42,8 @@
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;
NetlinkEvent::NetlinkEvent() {
mAction = NlActionUnknown;
@@ -70,13 +76,110 @@
}
/*
+ * Decode a RTM_NEWADDR or RTM_DELADDR message.
+ */
+bool NetlinkEvent::parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr,
+ int rtasize) {
+ struct rtattr *rta;
+ struct ifa_cacheinfo *cacheinfo = NULL;
+ char addrstr[INET6_ADDRSTRLEN] = "";
+
+ // Sanity check.
+ 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 = (type == RTM_NEWADDR) ? "RTM_NEWADDR" : "RTM_DELADDR";
+
+ for (rta = IFA_RTA(ifaddr); RTA_OK(rta, rtasize);
+ rta = RTA_NEXT(rta, rtasize)) {
+ if (rta->rta_type == IFA_ADDRESS) {
+ // Only look at the first address, because we only support notifying
+ // one change at a time.
+ if (*addrstr != '\0') {
+ SLOGE("Multiple IFA_ADDRESSes in %s, ignoring\n", 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 (%d 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 (%d 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.
+ char ifname[IFNAMSIZ + 1];
+ if (!if_indextoname(ifaddr->ifa_index, ifname)) {
+ SLOGE("Unknown ifindex %d in %s", ifaddr->ifa_index, msgtype);
+ return false;
+ }
+
+ // Fill in interface 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);
+ } else if (rta->rta_type == IFA_CACHEINFO) {
+ // Address lifetime information.
+ if (cacheinfo) {
+ // We only support one address.
+ SLOGE("Multiple IFA_CACHEINFOs in %s, ignoring\n", msgtype);
+ continue;
+ }
+
+ if (RTA_PAYLOAD(rta) < sizeof(*cacheinfo)) {
+ SLOGE("Short IFA_CACHEINFO (%d vs. %d bytes) in %s",
+ RTA_PAYLOAD(rta), sizeof(cacheinfo), msgtype);
+ continue;
+ }
+
+ cacheinfo = (struct ifa_cacheinfo *) RTA_DATA(rta);
+ 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);
+ }
+ }
+
+ if (addrstr[0] == '\0') {
+ SLOGE("No IFA_ADDRESS in %s\n", msgtype);
+ return false;
+ }
+
+ return true;
+}
+
+/*
* Parse an binary message from a NETLINK_ROUTE netlink socket.
*/
bool NetlinkEvent::parseBinaryNetlinkMessage(char *buffer, int size) {
- size_t sz = size;
- const struct nlmsghdr *nh = (struct nlmsghdr *) buffer;
+ const struct nlmsghdr *nh;
- while (NLMSG_OK(nh, sz) && (nh->nlmsg_type != NLMSG_DONE)) {
+ for (nh = (struct nlmsghdr *) buffer;
+ NLMSG_OK(nh, size) && (nh->nlmsg_type != NLMSG_DONE);
+ nh = NLMSG_NEXT(nh, size)) {
if (nh->nlmsg_type == RTM_NEWLINK) {
int len = nh->nlmsg_len - sizeof(*nh);
@@ -127,10 +230,24 @@
mSubsystem = strdup("qlog");
mAction = NlActionChange;
+ } else if (nh->nlmsg_type == RTM_NEWADDR ||
+ nh->nlmsg_type == RTM_DELADDR) {
+ int len = nh->nlmsg_len - sizeof(*nh);
+ struct ifaddrmsg *ifa;
+
+ if (sizeof(*ifa) > (size_t) len) {
+ SLOGE("Got a short RTM_xxxADDR message\n");
+ continue;
+ }
+
+ ifa = (ifaddrmsg *)NLMSG_DATA(nh);
+ size_t rtasize = IFA_PAYLOAD(nh);
+ if (!parseIfAddrMessage(nh->nlmsg_type, ifa, rtasize)) {
+ continue;
+ }
} else {
SLOGD("Unexpected netlink message. type=0x%x\n", nh->nlmsg_type);
}
- nh = NLMSG_NEXT(nh, size);
}
return true;
diff --git a/libusbhost/usbhost.c b/libusbhost/usbhost.c
index b967342..8be393e 100644
--- a/libusbhost/usbhost.c
+++ b/libusbhost/usbhost.c
@@ -51,7 +51,8 @@
#include "usbhost/usbhost.h"
#define DEV_DIR "/dev"
-#define USB_FS_DIR DEV_DIR "/bus/usb"
+#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"
@@ -68,6 +69,7 @@
void *data;
int wds[MAX_USBFS_WD_COUNT];
int wdd;
+ int wddbus;
};
struct usb_device {
@@ -195,6 +197,7 @@
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;
@@ -228,15 +231,25 @@
ret = read(context->fd, event_buf, sizeof(event_buf));
if (ret >= (int)sizeof(struct inotify_event)) {
- while (offset < ret) {
+ 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, "bus")) {
+ } 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]);
diff --git a/libutils/Android.mk b/libutils/Android.mk
new file mode 100644
index 0000000..7e6b1be
--- /dev/null
+++ b/libutils/Android.mk
@@ -0,0 +1,144 @@
+# 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)
+
+# libutils is a little unique: It's built twice, once for the host
+# and once for the device.
+
+commonSources:= \
+ BasicHashtable.cpp \
+ BlobCache.cpp \
+ CallStack.cpp \
+ FileMap.cpp \
+ JenkinsHash.cpp \
+ LinearAllocator.cpp \
+ LinearTransform.cpp \
+ Log.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)
+
+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
+
+host_commonLdlibs :=
+
+ifeq ($(TARGET_OS),linux)
+host_commonLdlibs += -lrt -ldl
+endif
+
+
+# For the host
+# =====================================================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES:= $(commonSources)
+ifeq ($(HOST_OS), linux)
+LOCAL_SRC_FILES += Looper.cpp
+endif
+LOCAL_MODULE:= libutils
+LOCAL_STATIC_LIBRARIES := liblog
+LOCAL_CFLAGS += $(host_commonCflags)
+LOCAL_LDLIBS += $(host_commonLdlibs)
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+
+# For the host, 64-bit
+# =====================================================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES:= $(commonSources)
+ifeq ($(HOST_OS), linux)
+LOCAL_SRC_FILES += Looper.cpp
+endif
+LOCAL_MODULE:= lib64utils
+LOCAL_STATIC_LIBRARIES := liblog
+LOCAL_CFLAGS += $(host_commonCflags) -m64
+LOCAL_LDLIBS += $(host_commonLdlibs)
+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_OS),linux)
+LOCAL_LDLIBS += -lrt -ldl
+endif
+
+ifeq ($(TARGET_ARCH),mips)
+LOCAL_CFLAGS += -DALIGN_DOUBLE
+endif
+
+LOCAL_C_INCLUDES += \
+ bionic/libc/private \
+ external/zlib
+
+LOCAL_LDLIBS += -lpthread
+
+LOCAL_STATIC_LIBRARIES := \
+ libcutils
+
+LOCAL_SHARED_LIBRARIES := \
+ libcorkscrew \
+ 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 := \
+ liblog \
+ libcutils \
+ libdl \
+ libcorkscrew
+
+include $(BUILD_SHARED_LIBRARY)
+
+# Include subdirectory makefiles
+# ============================================================
+
+# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework
+# team really wants is to build the stuff defined by this makefile.
+ifeq (,$(ONE_SHOT_MAKEFILE))
+include $(call first-makefiles-under,$(LOCAL_PATH))
+endif
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..0fb1d8e
--- /dev/null
+++ b/libutils/BlobCache.cpp
@@ -0,0 +1,362 @@
+/*
+ ** 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 <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 = '_Bb$';
+
+// BlobCache::Header::mBlobCacheVersion value
+static const uint32_t blobCacheVersion = 1;
+
+// 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", 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: %d (limit: %d)",
+ keySize, mMaxKeySize);
+ return;
+ }
+ if (mMaxValueSize < valueSize) {
+ ALOGV("set: not caching because the value is too large: %d (limit: %d)",
+ valueSize, mMaxValueSize);
+ return;
+ }
+ if (mMaxTotalSize < keySize + valueSize) {
+ ALOGV("set: not caching because the combined key/value size is too "
+ "large: %d (limit: %d)", 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: %d "
+ "(limit: %d)",
+ keySize + valueSize, mMaxTotalSize);
+ break;
+ }
+ }
+ mCacheEntries.add(CacheEntry(keyBlob, valueBlob));
+ mTotalSize = newTotalSize;
+ ALOGV("set: created new cache entry with %d byte key and %d 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: %d (limit: %d)",
+ keySize + valueSize, mMaxTotalSize);
+ break;
+ }
+ }
+ mCacheEntries.editItemAt(index).setValue(valueBlob);
+ mTotalSize = newTotalSize;
+ ALOGV("set: updated existing cache entry with %d byte key and %d 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: %d (limit %d)",
+ 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 %d", 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 %d bytes to caller's buffer", valueBlobSize);
+ memcpy(value, valueBlob->getData(), valueBlobSize);
+ } else {
+ ALOGV("get: caller's buffer is too small for value: %d (needs %d)",
+ valueSize, valueBlobSize);
+ }
+ return valueBlobSize;
+}
+
+static inline size_t align4(size_t size) {
+ return (size + 3) & ~3;
+}
+
+size_t BlobCache::getFlattenedSize() const {
+ size_t size = 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(size);
+ size += 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;
+ if (byteOffset + entrySize > 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);
+
+ byteOffset += align4(entrySize);
+ }
+
+ 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: %d", 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;
+
+ if (byteOffset + entrySize > 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 += align4(entrySize);
+ }
+
+ 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..e60f5d8
--- /dev/null
+++ b/libutils/CallStack.cpp
@@ -0,0 +1,138 @@
+/*
+ * 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 <string.h>
+
+#include <utils/Log.h>
+#include <utils/Errors.h>
+#include <utils/CallStack.h>
+#include <corkscrew/backtrace.h>
+
+/*****************************************************************************/
+namespace android {
+
+CallStack::CallStack() :
+ mCount(0) {
+}
+
+CallStack::CallStack(const char* logtag, int32_t ignoreDepth, int32_t maxDepth) {
+ this->update(ignoreDepth+1, maxDepth);
+ this->dump(logtag);
+}
+
+CallStack::CallStack(const CallStack& rhs) :
+ mCount(rhs.mCount) {
+ if (mCount) {
+ memcpy(mStack, rhs.mStack, mCount * sizeof(backtrace_frame_t));
+ }
+}
+
+CallStack::~CallStack() {
+}
+
+CallStack& CallStack::operator = (const CallStack& rhs) {
+ mCount = rhs.mCount;
+ if (mCount) {
+ memcpy(mStack, rhs.mStack, mCount * sizeof(backtrace_frame_t));
+ }
+ return *this;
+}
+
+bool CallStack::operator == (const CallStack& rhs) const {
+ if (mCount != rhs.mCount)
+ return false;
+ return !mCount || memcmp(mStack, rhs.mStack, mCount * sizeof(backtrace_frame_t)) == 0;
+}
+
+bool CallStack::operator != (const CallStack& rhs) const {
+ return !operator == (rhs);
+}
+
+bool CallStack::operator < (const CallStack& rhs) const {
+ if (mCount != rhs.mCount)
+ return mCount < rhs.mCount;
+ return memcmp(mStack, rhs.mStack, mCount * sizeof(backtrace_frame_t)) < 0;
+}
+
+bool CallStack::operator >= (const CallStack& rhs) const {
+ return !operator < (rhs);
+}
+
+bool CallStack::operator > (const CallStack& rhs) const {
+ if (mCount != rhs.mCount)
+ return mCount > rhs.mCount;
+ return memcmp(mStack, rhs.mStack, mCount * sizeof(backtrace_frame_t)) > 0;
+}
+
+bool CallStack::operator <= (const CallStack& rhs) const {
+ return !operator > (rhs);
+}
+
+const void* CallStack::operator [] (int index) const {
+ if (index >= int(mCount))
+ return 0;
+ return reinterpret_cast<const void*>(mStack[index].absolute_pc);
+}
+
+void CallStack::clear() {
+ mCount = 0;
+}
+
+void CallStack::update(int32_t ignoreDepth, int32_t maxDepth) {
+ if (maxDepth > MAX_DEPTH) {
+ maxDepth = MAX_DEPTH;
+ }
+ ssize_t count = unwind_backtrace(mStack, ignoreDepth + 1, maxDepth);
+ mCount = count > 0 ? count : 0;
+}
+
+void CallStack::dump(const char* logtag, const char* prefix) const {
+ backtrace_symbol_t symbols[mCount];
+
+ get_backtrace_symbols(mStack, mCount, symbols);
+ for (size_t i = 0; i < mCount; i++) {
+ char line[MAX_BACKTRACE_LINE_LENGTH];
+ format_backtrace_line(i, &mStack[i], &symbols[i],
+ line, MAX_BACKTRACE_LINE_LENGTH);
+ ALOG(LOG_DEBUG, logtag, "%s%s",
+ prefix ? prefix : "",
+ line);
+ }
+ free_backtrace_symbols(symbols, mCount);
+}
+
+String8 CallStack::toString(const char* prefix) const {
+ String8 str;
+ backtrace_symbol_t symbols[mCount];
+
+ get_backtrace_symbols(mStack, mCount, symbols);
+ for (size_t i = 0; i < mCount; i++) {
+ char line[MAX_BACKTRACE_LINE_LENGTH];
+ format_backtrace_line(i, &mStack[i], &symbols[i],
+ line, MAX_BACKTRACE_LINE_LENGTH);
+ if (prefix) {
+ str.append(prefix);
+ }
+ str.append(line);
+ str.append("\n");
+ }
+ free_backtrace_symbols(symbols, mCount);
+ return str;
+}
+
+}; // 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..9ce370e
--- /dev/null
+++ b/libutils/FileMap.cpp
@@ -0,0 +1,225 @@
+/*
+ * 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>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_POSIX_FILEMAP
+#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)
+ : mRefCount(1), mFileName(NULL), mBasePtr(NULL), mBaseLength(0),
+ mDataPtr(NULL), mDataLength(0)
+{
+}
+
+/*
+ * Destructor.
+ */
+FileMap::~FileMap(void)
+{
+ assert(mRefCount == 0);
+
+ //printf("+++ removing FileMap %p %u\n", mDataPtr, mDataLength);
+
+ mRefCount = -100; // help catch double-free
+ if (mFileName != NULL) {
+ free(mFileName);
+ }
+#ifdef HAVE_POSIX_FILEMAP
+ if (mBasePtr && munmap(mBasePtr, mBaseLength) != 0) {
+ ALOGD("munmap(%p, %d) failed\n", mBasePtr, (int) mBaseLength);
+ }
+#endif
+#ifdef HAVE_WIN32_FILEMAP
+ if (mBasePtr && UnmapViewOfFile(mBasePtr) == 0) {
+ ALOGD("UnmapViewOfFile(%p) failed, error = %ld\n", mBasePtr,
+ GetLastError() );
+ }
+ if (mFileMapping != INVALID_HANDLE_VALUE) {
+ CloseHandle(mFileMapping);
+ }
+ CloseHandle(mFileHandle);
+#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)
+{
+#ifdef HAVE_WIN32_FILEMAP
+ 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, %lx) failed with error %ld\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(%ld, %ld) failed with error %ld\n",
+ adjOffset, adjLength, GetLastError() );
+ CloseHandle(mFileMapping);
+ mFileMapping = INVALID_HANDLE_VALUE;
+ return false;
+ }
+#endif
+#ifdef HAVE_POSIX_FILEMAP
+ int prot, flags, adjust;
+ off64_t adjOffset;
+ size_t adjLength;
+
+ void* ptr;
+
+ assert(mRefCount == 1);
+ assert(fd >= 0);
+ assert(offset >= 0);
+ assert(length > 0);
+
+ /* init on first use */
+ if (mPageSize == -1) {
+#if NOT_USING_KLIBC
+ mPageSize = sysconf(_SC_PAGESIZE);
+ if (mPageSize == -1) {
+ ALOGE("could not get _SC_PAGESIZE\n");
+ return false;
+ }
+#else
+ /* this holds for Linux, Darwin, Cygwin, and doesn't pain the ARM */
+ mPageSize = 4096;
+#endif
+ }
+
+ adjust = offset % mPageSize;
+try_again:
+ 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) {
+ // Cygwin does not seem to like file mapping files from an offset.
+ // So if we fail, try again with offset zero
+ if (adjOffset > 0) {
+ adjust = offset;
+ goto try_again;
+ }
+
+ ALOGE("mmap(%ld,%ld) failed: %s\n",
+ (long) adjOffset, (long) adjLength, strerror(errno));
+ return false;
+ }
+ mBasePtr = ptr;
+#endif /* HAVE_POSIX_FILEMAP */
+
+ mFileName = origFileName != NULL ? strdup(origFileName) : NULL;
+ mBaseLength = adjLength;
+ mDataOffset = offset;
+ mDataPtr = (char*) mBasePtr + adjust;
+ mDataLength = length;
+
+ assert(mBasePtr != NULL);
+
+ ALOGV("MAP: base %p/%d data %p/%d\n",
+ mBasePtr, (int) mBaseLength, mDataPtr, (int) mDataLength);
+
+ return true;
+}
+
+/*
+ * Provide guidance to the system.
+ */
+int FileMap::advise(MapAdvice advice)
+{
+#if HAVE_MADVISE
+ 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
+ return -1;
+#endif // HAVE_MADVISE
+}
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..a07a291
--- /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..c51df2d
--- /dev/null
+++ b/libutils/Looper.cpp
@@ -0,0 +1,573 @@
+//
+// 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 <unistd.h>
+#include <fcntl.h>
+#include <limits.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(ALooper_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. errno=%d", 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. errno=%d",
+ errno);
+
+ result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
+ LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking. errno=%d",
+ 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. errno=%d", 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. errno=%d",
+ 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 & ALOOPER_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 "
+ "ALOOPER_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 = ALOOPER_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, errno=%d", errno);
+ result = ALOOPER_POLL_ERROR;
+ goto Done;
+ }
+
+ // Check for poll timeout.
+ if (eventCount == 0) {
+#if DEBUG_POLL_AND_WAKE
+ ALOGD("%p ~ pollOnce - timeout", this);
+#endif
+ result = ALOOPER_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 |= ALOOPER_EVENT_INPUT;
+ if (epollEvents & EPOLLOUT) events |= ALOOPER_EVENT_OUTPUT;
+ if (epollEvents & EPOLLERR) events |= ALOOPER_EVENT_ERROR;
+ if (epollEvents & EPOLLHUP) events |= ALOOPER_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 = ALOOPER_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 == ALOOPER_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 = ALOOPER_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 == ALOOPER_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 != ALOOPER_POLL_CALLBACK) {
+ return result;
+ }
+
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ timeoutMillis = toMillisecondTimeoutDelay(now, endTime);
+ if (timeoutMillis == 0) {
+ return ALOOPER_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, errno=%d", 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, ALooper_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 = ALOOPER_POLL_CALLBACK;
+ }
+
+ int epollEvents = 0;
+ if (events & ALOOPER_EVENT_INPUT) epollEvents |= EPOLLIN;
+ if (events & ALOOPER_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, errno=%d", fd, 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, errno=%d", fd, 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, errno=%d", fd, 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/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..f398a82
--- /dev/null
+++ b/libutils/RefBase.cpp
@@ -0,0 +1,650 @@
+/*
+ * 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 <utils/RefBase.h>
+
+#include <utils/Atomic.h>
+#include <utils/CallStack.h>
+#include <utils/Log.h>
+#include <utils/threads.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <typeinfo>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+// 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.dump(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.dump(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 = 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*/)
+{
+}
+
+// ---------------------------------------------------------------------------
+
+void RefBase::renameRefs(size_t n, const ReferenceRenamer& renamer) {
+#if DEBUG_REFS
+ for (size_t i=0 ; i<n ; i++) {
+ renamer(i);
+ }
+#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..b1708d6
--- /dev/null
+++ b/libutils/StopWatch.cpp
@@ -0,0 +1,88 @@
+/*
+ * 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 */
+#define __STDC_FORMAT_MACROS 1
+#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..b09b728
--- /dev/null
+++ b/libutils/String16.cpp
@@ -0,0 +1,422 @@
+/*
+ * 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/Debug.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();
+ }
+
+ const uint8_t* const u8end = u8cur + u8len;
+
+ 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..e852d77
--- /dev/null
+++ b/libutils/String8.cpp
@@ -0,0 +1,640 @@
+/*
+ * 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 result = NO_ERROR;
+ int n = vsnprintf(NULL, 0, fmt, 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;
+}
+
+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;
+ int extLen;
+ 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..4b74889
--- /dev/null
+++ b/libutils/SystemClock.cpp
@@ -0,0 +1,155 @@
+/*
+ * 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 <errno.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
+
+static const char *gettime_method_names[] = {
+ "clock_gettime",
+ "ioctl",
+ "systemTime",
+};
+
+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).
+ */
+#ifdef ARCH_ARM
+ int64_t prevTimestamp = *prevTimestampPtr;
+ int prevMethod = *prevMethodPtr;
+
+ if (timestamp < prevTimestamp) {
+ 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;
+#endif
+}
+
+/*
+ * native public static long elapsedRealtimeNano();
+ */
+int64_t elapsedRealtimeNano()
+{
+#ifdef HAVE_ANDROID_OS
+ struct timespec ts;
+ int result;
+ int64_t timestamp;
+ static volatile int64_t prevTimestamp;
+ static volatile int prevMethod;
+
+#if 0
+ /*
+ * b/7100774
+ * clock_gettime appears to have clock skews and can sometimes return
+ * backwards values. Disable its use until we find out what's wrong.
+ */
+ 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;
+ }
+#endif
+
+ // CLOCK_BOOTTIME doesn't exist, fallback to /dev/alarm
+ 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;
+ }
+
+ // 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..ff74914
--- /dev/null
+++ b/libutils/Threads.cpp
@@ -0,0 +1,878 @@
+/*
+ * 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 <utils/threads.h>
+#include <utils/Log.h>
+
+#include <cutils/sched_policy.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <memory.h>
+#include <errno.h>
+#include <assert.h>
+#include <unistd.h>
+
+#if defined(HAVE_PTHREADS)
+# include <pthread.h>
+# include <sched.h>
+# include <sys/resource.h>
+#ifdef HAVE_ANDROID_OS
+# include <bionic_pthread.h>
+#endif
+#elif defined(HAVE_WIN32_THREADS)
+# include <windows.h>
+# include <stdint.h>
+# include <process.h>
+# define HAVE_CREATETHREAD // Cygwin, vs. HAVE__BEGINTHREADEX for MinGW
+#endif
+
+#if defined(HAVE_PRCTL)
+#include <sys/prctl.h>
+#endif
+
+/*
+ * ===========================================================================
+ * Thread wrappers
+ * ===========================================================================
+ */
+
+using namespace android;
+
+// ----------------------------------------------------------------------------
+#if defined(HAVE_PTHREADS)
+// ----------------------------------------------------------------------------
+
+/*
+ * 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(HAVE_PRCTL)
+ // 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,
+ 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, errno=%d)\n"
+ "(android threadPriority=%d)",
+ entryFunction, result, 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();
+}
+
+// ----------------------------------------------------------------------------
+#elif defined(HAVE_WIN32_THREADS)
+// ----------------------------------------------------------------------------
+
+/*
+ * 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();
+}
+
+// ----------------------------------------------------------------------------
+#else
+#error "Threads not supported"
+#endif
+
+// ----------------------------------------------------------------------------
+
+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;
+}
+
+pid_t androidGetTid()
+{
+#ifdef HAVE_GETTID
+ return gettid();
+#else
+ return getpid();
+#endif
+}
+
+#ifdef HAVE_ANDROID_OS
+int androidSetThreadPriority(pid_t tid, int pri)
+{
+ int rc = 0;
+
+#if defined(HAVE_PTHREADS)
+ 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(HAVE_PTHREADS)
+ return getpriority(PRIO_PROCESS, tid);
+#else
+ return ANDROID_PRIORITY_NORMAL;
+#endif
+}
+
+#endif
+
+namespace android {
+
+/*
+ * ===========================================================================
+ * Mutex class
+ * ===========================================================================
+ */
+
+#if defined(HAVE_PTHREADS)
+// implemented as inlines in threads.h
+#elif defined(HAVE_WIN32_THREADS)
+
+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;
+}
+
+#else
+#error "Somebody forgot to implement threads for this platform."
+#endif
+
+
+/*
+ * ===========================================================================
+ * Condition class
+ * ===========================================================================
+ */
+
+#if defined(HAVE_PTHREADS)
+// implemented as inlines in threads.h
+#elif defined(HAVE_WIN32_THREADS)
+
+/*
+ * 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);
+}
+
+#else
+#error "condition variables not supported on this platform"
+#endif
+
+// ----------------------------------------------------------------------------
+
+/*
+ * 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(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..5293cd2
--- /dev/null
+++ b/libutils/Timers.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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 <utils/Log.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <time.h>
+#include <errno.h>
+#include <limits.h>
+
+#ifdef HAVE_WIN32_THREADS
+#include <windows.h>
+#endif
+
+nsecs_t systemTime(int clock)
+{
+#if defined(HAVE_POSIX_CLOCKS)
+ 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
+ // we don't support the clocks here.
+ 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..7067533
--- /dev/null
+++ b/libutils/Tokenizer.cpp
@@ -0,0 +1,175 @@
+/*
+ * 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() {
+ if (mFileMap) {
+ mFileMap->release();
+ }
+ 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 {
+ fileMap->release();
+ 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..a66e3bb
--- /dev/null
+++ b/libutils/Unicode.cpp
@@ -0,0 +1,606 @@
+/*
+ * 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
+
+# ifdef HAVE_LITTLE_ENDIAN
+# 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
+# define ntohl(x) (x)
+# define htonl(x) (x)
+# define ntohs(x) (x)
+# define htons(x) (x)
+# endif
+#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;
+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) {
+ 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 uint16_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..5a79647
--- /dev/null
+++ b/libutils/VectorImpl.cpp
@@ -0,0 +1,625 @@
+/*
+ * 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);
+ 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 (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 {
+ 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);
+ 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 (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 {
+ 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..58eb499
--- /dev/null
+++ b/libutils/misc.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 "misc"
+
+//
+// Miscellaneous utility functions.
+//
+#include <utils/misc.h>
+#include <utils/Log.h>
+
+#include <sys/stat.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+
+#if defined(HAVE_PTHREADS)
+# 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(HAVE_PTHREADS)
+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(HAVE_PTHREADS)
+ 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(HAVE_PTHREADS)
+ 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..caedaff
--- /dev/null
+++ b/libutils/tests/Android.mk
@@ -0,0 +1,34 @@
+# Build the unit tests.
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+# Build the unit tests.
+test_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
+
+shared_libraries := \
+ libz \
+ liblog \
+ libcutils \
+ libutils \
+ libstlport
+
+static_libraries := \
+ libgtest \
+ libgtest_main
+
+$(foreach file,$(test_src_files), \
+ $(eval include $(CLEAR_VARS)) \
+ $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
+ $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \
+ $(eval LOCAL_SRC_FILES := $(file)) \
+ $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
+ $(eval include $(BUILD_NATIVE_TEST)) \
+)
diff --git a/libutils/tests/BasicHashtable_test.cpp b/libutils/tests/BasicHashtable_test.cpp
new file mode 100644
index 0000000..7dcf750
--- /dev/null
+++ b/libutils/tests/BasicHashtable_test.cpp
@@ -0,0 +1,577 @@
+/*
+ * 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 android {
+
+typedef int SimpleKey;
+typedef int SimpleValue;
+typedef key_value_pair_t<SimpleKey, SimpleValue> SimpleEntry;
+typedef 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;
+
+template<> inline hash_t hash_type(const ComplexKey& value) {
+ return hash_type(value.k);
+}
+
+struct ComplexValue {
+ int v;
+
+ explicit ComplexValue(int v) : v(v) {
+ instanceCount += 1;
+ }
+
+ ComplexValue(const ComplexValue& other) : v(other.v) {
+ instanceCount += 1;
+ }
+
+ ~ComplexValue() {
+ instanceCount -= 1;
+ }
+
+ static ssize_t instanceCount;
+};
+
+ssize_t ComplexValue::instanceCount = 0;
+
+typedef key_value_pair_t<ComplexKey, ComplexValue> ComplexEntry;
+typedef BasicHashtable<ComplexKey, ComplexEntry> ComplexHashtable;
+
+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_EQ(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..752e56d
--- /dev/null
+++ b/libutils/tests/BitSet_test.cpp
@@ -0,0 +1,87 @@
+/*
+ * 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 BitSetTest : public testing::Test {
+protected:
+ BitSet32 b1;
+ BitSet32 b2;
+ virtual void TearDown() {
+ b1.clear();
+ b2.clear();
+ }
+};
+
+
+TEST_F(BitSetTest, 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(BitSetTest, 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(BitSetTest, 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));
+}
+} // namespace android
diff --git a/libutils/tests/BlobCache_test.cpp b/libutils/tests/BlobCache_test.cpp
new file mode 100644
index 0000000..7202123
--- /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) {
+ 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) {
+ 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) {
+ 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) {
+ 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) {
+ 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) {
+ 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];
+ 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];
+ 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) {
+ 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) {
+ 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) {
+ 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) {
+ 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) {
+ 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) {
+ 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..8bf2ba2
--- /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(ALOOPER_POLL_TIMEOUT, result)
+ << "pollOnce result should be ALOOPER_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(ALOOPER_POLL_WAKE, result)
+ << "pollOnce result should be ALOOPER_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(ALOOPER_POLL_WAKE, result)
+ << "pollOnce result should be ALOOPER_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(ALOOPER_POLL_TIMEOUT, result)
+ << "pollOnce result should be ALOOPER_POLL_TIMEOUT";
+}
+
+TEST_F(LooperTest, PollOnce_WhenZeroTimeoutAndNoSignalledFDs_ImmediatelyReturns) {
+ Pipe pipe;
+ StubCallbackHandler handler(true);
+
+ handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_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(ALOOPER_POLL_TIMEOUT, result)
+ << "pollOnce result should be ALOOPER_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, ALOOPER_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(ALOOPER_POLL_CALLBACK, result)
+ << "pollOnce result should be ALOOPER_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(ALOOPER_EVENT_INPUT, handler.events)
+ << "callback should have received ALOOPER_EVENT_INPUT as events";
+}
+
+TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndNoSignalledFDs_WaitsForTimeoutAndReturns) {
+ Pipe pipe;
+ StubCallbackHandler handler(true);
+
+ handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_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(ALOOPER_POLL_TIMEOUT, result)
+ << "pollOnce result should be ALOOPER_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, ALOOPER_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(ALOOPER_POLL_CALLBACK, result)
+ << "pollOnce result should be ALOOPER_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(ALOOPER_EVENT_INPUT, handler.events)
+ << "callback should have received ALOOPER_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, ALOOPER_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(ALOOPER_POLL_CALLBACK, result)
+ << "pollOnce result should be ALOOPER_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(ALOOPER_EVENT_INPUT, handler.events)
+ << "callback should have received ALOOPER_EVENT_INPUT as events";
+}
+
+TEST_F(LooperTest, PollOnce_WhenCallbackAddedThenRemoved_CallbackShouldNotBeInvoked) {
+ Pipe pipe;
+ StubCallbackHandler handler(true);
+
+ handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_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(ALOOPER_POLL_TIMEOUT, result)
+ << "pollOnce result should be ALOOPER_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, ALOOPER_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(ALOOPER_POLL_CALLBACK, result)
+ << "pollOnce result should be ALOOPER_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(ALOOPER_POLL_TIMEOUT, result)
+ << "pollOnce result should be ALOOPER_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, ALOOPER_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(ALOOPER_EVENT_INPUT, events)
+ << "pollOnce should have returned ALOOPER_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, ALOOPER_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, ALOOPER_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, ALOOPER_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, ALOOPER_EVENT_INPUT);
+ handler2.setCallback(mLooper, pipe.receiveFd, ALOOPER_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(ALOOPER_POLL_CALLBACK, result)
+ << "pollOnce result should be ALOOPER_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(ALOOPER_POLL_CALLBACK, result)
+ << "pollOnce result should be ALOOPER_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(ALOOPER_POLL_CALLBACK, result)
+ << "pollOnce result should be ALOOPER_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(ALOOPER_POLL_WAKE, result)
+ << "pollOnce result should be ALOOPER_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(ALOOPER_POLL_CALLBACK, result)
+ << "pollOnce result should be ALOOPER_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(ALOOPER_POLL_TIMEOUT, result)
+ << "pollOnce result should be ALOOPER_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(ALOOPER_POLL_CALLBACK, result)
+ << "pollOnce result should be ALOOPER_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(ALOOPER_POLL_CALLBACK, result)
+ << "pollOnce result should be ALOOPER_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(ALOOPER_POLL_WAKE, result)
+ << "pollOnce result should be ALOOPER_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(ALOOPER_POLL_CALLBACK, result)
+ << "pollOnce result should be ALOOPER_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(ALOOPER_POLL_TIMEOUT, result)
+ << "pollOnce result should be ALOOPER_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(ALOOPER_POLL_CALLBACK, result)
+ << "pollOnce result should be ALOOPER_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(ALOOPER_POLL_CALLBACK, result)
+ << "pollOnce result should be ALOOPER_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(ALOOPER_POLL_WAKE, result)
+ << "pollOnce result should be ALOOPER_POLL_WAKE because looper was awoken";
+ EXPECT_EQ(size_t(0), handler->messages.size())
+ << "no messages to handle";
+
+ result = mLooper->pollOnce(0);
+
+ EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result)
+ << "pollOnce result should be ALOOPER_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(ALOOPER_POLL_CALLBACK, result)
+ << "pollOnce result should be ALOOPER_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(ALOOPER_POLL_TIMEOUT, result)
+ << "pollOnce result should be ALOOPER_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..e573952
--- /dev/null
+++ b/libutils/tests/LruCache_test.cpp
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <utils/JenkinsHash.h>
+#include <utils/LruCache.h>
+#include <cutils/log.h>
+#include <gtest/gtest.h>
+
+namespace android {
+
+typedef int SimpleKey;
+typedef const char* StringValue;
+
+struct ComplexKey {
+ int k;
+
+ explicit ComplexKey(int k) : k(k) {
+ instanceCount += 1;
+ }
+
+ ComplexKey(const ComplexKey& other) : k(other.k) {
+ instanceCount += 1;
+ }
+
+ ~ComplexKey() {
+ instanceCount -= 1;
+ }
+
+ bool operator ==(const ComplexKey& other) const {
+ return k == other.k;
+ }
+
+ bool operator !=(const ComplexKey& other) const {
+ return k != other.k;
+ }
+
+ static ssize_t instanceCount;
+};
+
+ssize_t ComplexKey::instanceCount = 0;
+
+template<> inline hash_t hash_type(const ComplexKey& value) {
+ return hash_type(value.k);
+}
+
+struct ComplexValue {
+ int v;
+
+ explicit ComplexValue(int v) : v(v) {
+ instanceCount += 1;
+ }
+
+ ComplexValue(const ComplexValue& other) : v(other.v) {
+ instanceCount += 1;
+ }
+
+ ~ComplexValue() {
+ instanceCount -= 1;
+ }
+
+ static ssize_t instanceCount;
+};
+
+ssize_t ComplexValue::instanceCount = 0;
+
+typedef LruCache<ComplexKey, ComplexValue> ComplexCache;
+
+class EntryRemovedCallback : public OnEntryRemoved<SimpleKey, StringValue> {
+public:
+ EntryRemovedCallback() : callbackCount(0), lastKey(-1), lastValue(NULL) { }
+ ~EntryRemovedCallback() {}
+ void operator()(SimpleKey& k, StringValue& v) {
+ callbackCount += 1;
+ lastKey = k;
+ lastValue = v;
+ }
+ ssize_t callbackCount;
+ SimpleKey lastKey;
+ StringValue lastValue;
+};
+
+class LruCacheTest : public testing::Test {
+protected:
+ virtual void SetUp() {
+ ComplexKey::instanceCount = 0;
+ ComplexValue::instanceCount = 0;
+ }
+
+ virtual void TearDown() {
+ ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0));
+ }
+
+ void assertInstanceCount(ssize_t keys, ssize_t values) {
+ if (keys != ComplexKey::instanceCount || values != ComplexValue::instanceCount) {
+ FAIL() << "Expected " << keys << " keys and " << values << " values "
+ "but there were actually " << ComplexKey::instanceCount << " keys and "
+ << ComplexValue::instanceCount << " values";
+ }
+ }
+};
+
+TEST_F(LruCacheTest, Empty) {
+ LruCache<SimpleKey, StringValue> cache(100);
+
+ EXPECT_EQ(NULL, cache.get(0));
+ EXPECT_EQ(0u, cache.size());
+}
+
+TEST_F(LruCacheTest, Simple) {
+ LruCache<SimpleKey, StringValue> cache(100);
+
+ cache.put(1, "one");
+ cache.put(2, "two");
+ cache.put(3, "three");
+ EXPECT_STREQ("one", cache.get(1));
+ EXPECT_STREQ("two", cache.get(2));
+ EXPECT_STREQ("three", cache.get(3));
+ EXPECT_EQ(3u, cache.size());
+}
+
+TEST_F(LruCacheTest, MaxCapacity) {
+ LruCache<SimpleKey, StringValue> cache(2);
+
+ cache.put(1, "one");
+ cache.put(2, "two");
+ cache.put(3, "three");
+ EXPECT_EQ(NULL, cache.get(1));
+ EXPECT_STREQ("two", cache.get(2));
+ EXPECT_STREQ("three", cache.get(3));
+ EXPECT_EQ(2u, cache.size());
+}
+
+TEST_F(LruCacheTest, RemoveLru) {
+ LruCache<SimpleKey, StringValue> cache(100);
+
+ cache.put(1, "one");
+ cache.put(2, "two");
+ cache.put(3, "three");
+ cache.removeOldest();
+ EXPECT_EQ(NULL, cache.get(1));
+ EXPECT_STREQ("two", cache.get(2));
+ EXPECT_STREQ("three", cache.get(3));
+ EXPECT_EQ(2u, cache.size());
+}
+
+TEST_F(LruCacheTest, GetUpdatesLru) {
+ LruCache<SimpleKey, StringValue> cache(100);
+
+ cache.put(1, "one");
+ cache.put(2, "two");
+ cache.put(3, "three");
+ EXPECT_STREQ("one", cache.get(1));
+ cache.removeOldest();
+ EXPECT_STREQ("one", cache.get(1));
+ EXPECT_EQ(NULL, cache.get(2));
+ EXPECT_STREQ("three", cache.get(3));
+ EXPECT_EQ(2u, cache.size());
+}
+
+uint32_t hash_int(int x) {
+ return JenkinsHashWhiten(JenkinsHashMix(0, x));
+}
+
+TEST_F(LruCacheTest, StressTest) {
+ const size_t kCacheSize = 512;
+ LruCache<SimpleKey, StringValue> cache(512);
+ const size_t kNumKeys = 16 * 1024;
+ const size_t kNumIters = 100000;
+ char* strings[kNumKeys];
+
+ for (size_t i = 0; i < kNumKeys; i++) {
+ strings[i] = (char *)malloc(16);
+ sprintf(strings[i], "%d", i);
+ }
+
+ srandom(12345);
+ int hitCount = 0;
+ for (size_t i = 0; i < kNumIters; i++) {
+ int index = random() % kNumKeys;
+ uint32_t key = hash_int(index);
+ const char *val = cache.get(key);
+ if (val != NULL) {
+ EXPECT_EQ(strings[index], val);
+ hitCount++;
+ } else {
+ cache.put(key, strings[index]);
+ }
+ }
+ size_t expectedHitCount = kNumIters * kCacheSize / kNumKeys;
+ EXPECT_LT(int(expectedHitCount * 0.9), hitCount);
+ EXPECT_GT(int(expectedHitCount * 1.1), hitCount);
+ EXPECT_EQ(kCacheSize, cache.size());
+
+ for (size_t i = 0; i < kNumKeys; i++) {
+ free((void *)strings[i]);
+ }
+}
+
+TEST_F(LruCacheTest, NoLeak) {
+ ComplexCache cache(100);
+
+ cache.put(ComplexKey(0), ComplexValue(0));
+ cache.put(ComplexKey(1), ComplexValue(1));
+ EXPECT_EQ(2, cache.size());
+ assertInstanceCount(2, 3); // the null value counts as an instance
+}
+
+TEST_F(LruCacheTest, Clear) {
+ ComplexCache cache(100);
+
+ cache.put(ComplexKey(0), ComplexValue(0));
+ cache.put(ComplexKey(1), ComplexValue(1));
+ EXPECT_EQ(2, cache.size());
+ assertInstanceCount(2, 3);
+ cache.clear();
+ assertInstanceCount(0, 1);
+}
+
+TEST_F(LruCacheTest, ClearNoDoubleFree) {
+ {
+ ComplexCache cache(100);
+
+ cache.put(ComplexKey(0), ComplexValue(0));
+ cache.put(ComplexKey(1), ComplexValue(1));
+ EXPECT_EQ(2, cache.size());
+ assertInstanceCount(2, 3);
+ cache.removeOldest();
+ cache.clear();
+ assertInstanceCount(0, 1);
+ }
+ assertInstanceCount(0, 0);
+}
+
+TEST_F(LruCacheTest, ClearReuseOk) {
+ ComplexCache cache(100);
+
+ cache.put(ComplexKey(0), ComplexValue(0));
+ cache.put(ComplexKey(1), ComplexValue(1));
+ EXPECT_EQ(2, cache.size());
+ assertInstanceCount(2, 3);
+ cache.clear();
+ assertInstanceCount(0, 1);
+ cache.put(ComplexKey(0), ComplexValue(0));
+ cache.put(ComplexKey(1), ComplexValue(1));
+ EXPECT_EQ(2, cache.size());
+ assertInstanceCount(2, 3);
+}
+
+TEST_F(LruCacheTest, Callback) {
+ LruCache<SimpleKey, StringValue> cache(100);
+ EntryRemovedCallback callback;
+ cache.setOnEntryRemovedListener(&callback);
+
+ cache.put(1, "one");
+ cache.put(2, "two");
+ cache.put(3, "three");
+ EXPECT_EQ(3, cache.size());
+ cache.removeOldest();
+ EXPECT_EQ(1, callback.callbackCount);
+ EXPECT_EQ(1, callback.lastKey);
+ EXPECT_STREQ("one", callback.lastValue);
+}
+
+TEST_F(LruCacheTest, CallbackOnClear) {
+ LruCache<SimpleKey, StringValue> cache(100);
+ EntryRemovedCallback callback;
+ cache.setOnEntryRemovedListener(&callback);
+
+ cache.put(1, "one");
+ cache.put(2, "two");
+ cache.put(3, "three");
+ EXPECT_EQ(3, cache.size());
+ cache.clear();
+ EXPECT_EQ(3, callback.callbackCount);
+}
+
+}
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/lmkd/Android.mk b/lmkd/Android.mk
new file mode 100644
index 0000000..5d6d1d2
--- /dev/null
+++ b/lmkd/Android.mk
@@ -0,0 +1,10 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := lmkd.c
+LOCAL_STATIC_LIBRARIES := libcutils liblog libm libc
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+
+LOCAL_MODULE := lmkd
+
+include $(BUILD_EXECUTABLE)
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
new file mode 100644
index 0000000..376410b
--- /dev/null
+++ b/lmkd/lmkd.c
@@ -0,0 +1,735 @@
+/*
+ * 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 <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <cutils/log.h>
+#include <cutils/sockets.h>
+
+#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
+
+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;
+ 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 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 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_adj", pid);
+ snprintf(val, sizeof(val), "%d", 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->oomadj = oomadj;
+ proc_insert(procp);
+ } else {
+ proc_unslot(procp);
+ procp->oomadj = oomadj;
+ proc_slot(procp);
+ }
+}
+
+static void cmd_procremove(int pid) {
+ struct proc *procp;
+
+ 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 != 2)
+ goto wronglen;
+ cmd_procprio(ntohl(ibuf[1]), ntohl(ibuf[2]));
+ 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) {
+ 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;
+
+ if (*cp++ != '(')
+ return 0;
+
+ do {
+ zoneval = strtol(cp, &cp, 0);
+ if ((*cp != ',') && (*cp != ')'))
+ return 0;
+ if (zoneval > max)
+ max = zoneval;
+ } while (cp = strtok(NULL, " "));
+
+ return max;
+}
+
+static void zoneinfo_parse_line(char *line, struct sysmeminfo *mip) {
+ char *cp = line;
+ char *ap;
+
+ cp = strtok(line, " ");
+ if (!cp)
+ return;
+
+ ap = strtok(NULL, " ");
+ 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) {
+ FILE *f;
+ char *cp;
+ char line[LINE_MAX];
+
+ memset(mip, 0, sizeof(struct sysmeminfo));
+ f = fopen(ZONEINFO_PATH, "r");
+ if (!f) {
+ ALOGE("%s open: errno=%d", ZONEINFO_PATH, errno);
+ return -1;
+ }
+
+ while (fgets(line, LINE_MAX, f))
+ zoneinfo_parse_line(line, mip);
+
+ fclose(f);
+ return 0;
+}
+
+static int proc_get_size(int pid) {
+ char path[PATH_MAX];
+ char line[LINE_MAX];
+ FILE *f;
+ int rss = 0;
+ int total;
+
+ snprintf(path, PATH_MAX, "/proc/%d/statm", pid);
+ f = fopen(path, "r");
+ if (!f)
+ return -1;
+ if (!fgets(line, LINE_MAX, f)) {
+ fclose(f);
+ return -1;
+ }
+
+ sscanf(line, "%d %d ", &total, &rss);
+ fclose(f);
+ return rss;
+}
+
+static char *proc_get_name(int pid) {
+ char path[PATH_MAX];
+ static char line[LINE_MAX];
+ FILE *f;
+ char *cp;
+
+ snprintf(path, PATH_MAX, "/proc/%d/cmdline", pid);
+ f = fopen(path, "r");
+ if (!f)
+ return NULL;
+ if (!fgets(line, LINE_MAX, f)) {
+ fclose(f);
+ 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)]);
+}
+
+static void mp_event(uint32_t events) {
+ int i;
+ int ret;
+ unsigned long long evcount;
+ struct sysmeminfo mi;
+ int other_free;
+ int other_file;
+ int minfree = 0;
+ int min_score_adj = OOM_ADJUST_MAX + 1;
+
+ 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;
+
+ if (zoneinfo_parse(&mi) < 0)
+ return;
+
+ other_free = mi.nr_free_pages - mi.totalreserve_pages;
+ other_file = mi.nr_file_pages - mi.nr_shmem;
+
+ 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;
+
+ for (i = OOM_ADJUST_MAX; i >= min_score_adj; i--) {
+ struct proc *procp;
+
+ retry:
+ procp = proc_adj_lru(i);
+
+ if (procp) {
+ int pid = procp->pid;
+ char *taskname;
+ int tasksize;
+ int r;
+
+ taskname = proc_get_name(pid);
+ if (!taskname) {
+ pid_remove(pid);
+ goto retry;
+ }
+
+ tasksize = proc_get_size(pid);
+ if (tasksize < 0) {
+ pid_remove(pid);
+ goto retry;
+ }
+
+ ALOGI("Killing '%s' (%d), adj %d\n"
+ " to free %ldkB because cache %ldkB is below limit %ldkB for oom_adj %d\n"
+ " Free memory is %ldkB %s reserved",
+ taskname, pid, procp->oomadj, tasksize * page_k,
+ other_file * page_k, minfree * page_k, min_score_adj,
+ other_free * page_k, other_free >= 0 ? "above" : "below");
+ r = kill(pid, SIGKILL);
+ pid_remove(pid);
+
+ if (r) {
+ ALOGE("kill(%d): errno=%d", procp->pid, errno);
+ goto retry;
+ } else {
+ time(&kill_lasttime);
+ break;
+ }
+ }
+ }
+}
+
+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, char **argv) {
+ if (!init())
+ mainloop();
+
+ ALOGI("exiting");
+ return 0;
+}
diff --git a/logcat/event.logtags b/logcat/event.logtags
index 3a1b281..a325692 100644
--- a/logcat/event.logtags
+++ b/logcat/event.logtags
@@ -90,10 +90,6 @@
# Logged when the supplicant switches to a new state
50023 wifi_supplicant_state_changed (supplicant_state|1|5)
-# Do not change these names without updating tag in:
-#//device/dalvik/libcore/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.c
-51000 socket_stats (send|1|2),(recv|1|2),(ip|1|5),(port|1|5),(close|1|5)
-
# Database operation samples.
# db: the filename of the database
# sql: the executed query (without query args)
@@ -136,7 +132,7 @@
80310 bionic_event_resolver_wrong_query (uid|1)
# libcore failure logging
-90100 cert_pin_failure (certs|4)
+90100 exp_det_cert_pin_failure (certs|4)
# 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
index d3b5ed0..d44c679 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -1,10 +1,10 @@
// Copyright 2006 The Android Open Source Project
-#include <cutils/logger.h>
-#include <cutils/logd.h>
+#include <log/logger.h>
+#include <log/logd.h>
+#include <log/logprint.h>
+#include <log/event_tag_map.h>
#include <cutils/sockets.h>
-#include <cutils/logprint.h>
-#include <cutils/event_tag_map.h>
#include <stdio.h>
#include <stdlib.h>
diff --git a/mkbootimg/bootimg.h b/mkbootimg/bootimg.h
index 242ab35..9171d85 100644
--- a/mkbootimg/bootimg.h
+++ b/mkbootimg/bootimg.h
@@ -24,6 +24,7 @@
#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
{
@@ -43,10 +44,14 @@
unsigned unused[2]; /* future expansion: should be 0 */
unsigned char name[BOOT_NAME_SIZE]; /* asciiz product name */
-
+
unsigned char cmdline[BOOT_ARGS_SIZE];
unsigned id[8]; /* timestamp / checksum / sha1 / etc */
+
+ /* Supplemental command line data; kept here to maintain
+ * binary compatibility with older versions of mkbootimg */
+ unsigned char extra_cmdline[BOOT_EXTRA_ARGS_SIZE];
};
/*
diff --git a/mkbootimg/mkbootimg.c b/mkbootimg/mkbootimg.c
index 34a879b..d598f03 100644
--- a/mkbootimg/mkbootimg.c
+++ b/mkbootimg/mkbootimg.c
@@ -114,6 +114,7 @@
unsigned ramdisk_offset = 0x01000000;
unsigned second_offset = 0x00f00000;
unsigned tags_offset = 0x00000100;
+ size_t cmdlen;
argc--;
argv++;
@@ -192,11 +193,19 @@
memcpy(hdr.magic, BOOT_MAGIC, BOOT_MAGIC_SIZE);
- if(strlen(cmdline) > (BOOT_ARGS_SIZE - 1)) {
+ cmdlen = strlen(cmdline);
+ if(cmdlen > (BOOT_ARGS_SIZE + BOOT_EXTRA_ARGS_SIZE - 2)) {
fprintf(stderr,"error: kernel commandline too large\n");
return 1;
}
- strcpy((char*)hdr.cmdline, cmdline);
+ /* Even if we need to use the supplemental field, ensure we
+ * are still NULL-terminated */
+ strncpy((char *)hdr.cmdline, cmdline, BOOT_ARGS_SIZE - 1);
+ hdr.cmdline[BOOT_ARGS_SIZE - 1] = '\0';
+ if (cmdlen >= (BOOT_ARGS_SIZE - 1)) {
+ cmdline += (BOOT_ARGS_SIZE - 1);
+ strncpy((char *)hdr.extra_cmdline, cmdline, BOOT_EXTRA_ARGS_SIZE);
+ }
kernel_data = load_file(kernel_fn, &hdr.kernel_size);
if(kernel_data == 0) {
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 3417f54..2c16084 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -1,55 +1,38 @@
LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-# files that live under /system/etc/...
-
-copy_from := \
- etc/hosts
-
-
-copy_to := $(addprefix $(TARGET_OUT)/,$(copy_from))
-copy_from := $(addprefix $(LOCAL_PATH)/,$(copy_from))
-
-$(copy_to) : PRIVATE_MODULE := system_etcdir
-$(copy_to) : $(TARGET_OUT)/% : $(LOCAL_PATH)/% | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(copy_to)
-
-
-# files that live under /...
-
+#######################################
+# init.rc
# Only copy init.rc if the target doesn't have its own.
ifneq ($(TARGET_PROVIDES_INIT_RC),true)
-file := $(TARGET_ROOT_OUT)/init.rc
-$(file) : $(LOCAL_PATH)/init.rc | $(ACP)
- $(transform-prebuilt-to-target)
-ALL_PREBUILT += $(file)
-$(INSTALLED_RAMDISK_TARGET): $(file)
+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
-file := $(TARGET_ROOT_OUT)/ueventd.rc
-$(file) : $(LOCAL_PATH)/ueventd.rc | $(ACP)
- $(transform-prebuilt-to-target)
-ALL_PREBUILT += $(file)
-$(INSTALLED_RAMDISK_TARGET): $(file)
+include $(CLEAR_VARS)
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE := init.environ.rc
+LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
-# init.usb.rc is handled by build/target/product/core.rc
-
-
+# Put it here instead of in init.rc module definition,
+# because init.rc is conditionally included.
+#
# create some directories (some are mount points)
-DIRS := $(addprefix $(TARGET_ROOT_OUT)/, \
- sbin \
- dev \
- proc \
- sys \
- system \
- data \
- ) \
- $(TARGET_OUT_DATA)
+LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \
+ sbin dev proc sys system data)
-$(DIRS):
- @echo Directory: $@
- @mkdir -p $@
+include $(BUILD_SYSTEM)/base_rules.mk
-ALL_PREBUILT += $(DIRS)
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/init.environ.rc.in
+ @echo "Generate: $< -> $@"
+ @mkdir -p $(dir $@)
+ $(hide) sed -e 's?%BOOTCLASSPATH%?$(PRODUCT_BOOTCLASSPATH)?g' $< >$@
+
+#######################################
diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in
new file mode 100644
index 0000000..d2f74c0
--- /dev/null
+++ b/rootdir/init.environ.rc.in
@@ -0,0 +1,12 @@
+# set up the global environment
+on init
+ export PATH /sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin
+ export LD_LIBRARY_PATH /vendor/lib:/system/lib
+ 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%
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 1451953..3d17124 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -4,6 +4,7 @@
# This is a common source of Android security bugs.
#
+import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
import /init.trace.rc
@@ -27,18 +28,6 @@
loglevel 3
-# setup the global environment
- export PATH /sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin
- export LD_LIBRARY_PATH /vendor/lib:/system/lib
- 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 /system/framework/core.jar:/system/framework/conscrypt.jar:/system/framework/okhttp.jar:/system/framework/core-junit.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/telephony-common.jar:/system/framework/voip-common.jar:/system/framework/mms-common.jar:/system/framework/android.policy.jar:/system/framework/services.jar:/system/framework/apache-xml.jar:/system/framework/webviewchromium.jar
-
# Backward compatibility
symlink /system/etc /etc
symlink /sys/kernel/debug /d
@@ -53,13 +42,13 @@
mkdir /acct/uid
# Create cgroup mount point for memory
- mount tmpfs none /sys/fs/cgroup
- mkdir /sys/fs/cgroup/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
+ 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
@@ -94,6 +83,10 @@
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
@@ -188,6 +181,9 @@
# We restorecon /data in case the userdata partition has been reset.
restorecon /data
+ # Avoid predictable entropy pool. Carry over entropy from previous boot.
+ copy /data/system/entropy.dat /dev/urandom
+
# Create dump dir and collect dumps.
# Do this before we mount cache so eventually we can use cache for
# storing dumps on platforms which do not have a dedicated dump partition.
@@ -211,6 +207,7 @@
mkdir /data/misc/bluetooth 0770 system system
mkdir /data/misc/keystore 0700 keystore keystore
mkdir /data/misc/keychain 0771 system system
+ 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
@@ -352,7 +349,6 @@
chown root radio /proc/cmdline
# Set these so we can remotely update SELinux policy
- chown system system /sys/fs/selinux/load
chown system system /sys/fs/selinux/enforce
# Define TCP buffer sizes for various networks
@@ -401,6 +397,10 @@
on property:sys.powerctl=*
powerctl ${sys.powerctl}
+# system server cannot write to /proc/sys files, so proxy it through init
+on property:sys.sysctl.extra_free_kbytes=*
+ write /proc/sys/vm/extra_free_kbytes ${sys.sysctl.extra_free_kbytes}
+
## Daemon processes to be run by init.
##
service ueventd /sbin/ueventd
@@ -408,9 +408,10 @@
critical
seclabel u:r:ueventd:s0
-on property:selinux.reload_policy=1
- restart ueventd
- restart installd
+service healthd /sbin/healthd
+ class core
+ critical
+ seclabel u:r:healthd:s0
service console /system/bin/sh
class core
@@ -433,11 +434,17 @@
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
diff --git a/run-as/package.c b/run-as/package.c
index 27fc1eb..901e9e3 100644
--- a/run-as/package.c
+++ b/run-as/package.c
@@ -90,7 +90,7 @@
*/
oldegid = getegid();
- if (setegid(AID_SYSTEM) < 0) {
+ if (setegid(AID_PACKAGE_INFO) < 0) {
return NULL;
}
@@ -111,7 +111,7 @@
goto EXIT;
/* Ensure that the file is owned by the system user */
- if ((st.st_uid != AID_SYSTEM) || (st.st_gid != AID_SYSTEM)) {
+ if ((st.st_uid != AID_SYSTEM) || (st.st_gid != AID_PACKAGE_INFO)) {
goto EXIT;
}
diff --git a/run-as/run-as.c b/run-as/run-as.c
index 3c0ecc4..cc05e63 100644
--- a/run-as/run-as.c
+++ b/run-as/run-as.c
@@ -36,9 +36,9 @@
/*
* WARNING WARNING WARNING WARNING
*
- * This program runs as set-uid root on Android production devices.
- * Be very conservative when modifying it to avoid any serious
- * security issue. Keep in mind the following:
+ * 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
*
@@ -61,14 +61,19 @@
*
* run-as <package-name> <command> <args>
*
- * The 'run-as' binary is setuid, but will check the following:
+ * 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 cd to the package's data directory, drop to the application's
- * user id / group id then run the command there.
+ * 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:
*
@@ -141,19 +146,6 @@
return 1;
}
- /* then move to it */
- {
- int ret;
- do {
- ret = chdir(info.dataDir);
- } while (ret < 0 && errno == EINTR);
-
- if (ret < 0) {
- panic("Could not cd to package's data directory: %s\n", strerror(errno));
- return 1;
- }
- }
-
/* Ensure that we change all real/effective/saved IDs at the
* same time to avoid nasty surprises.
*/
@@ -168,6 +160,19 @@
return 1;
}
+ /* cd into the data directory */
+ {
+ int ret;
+ do {
+ ret = chdir(info.dataDir);
+ } while (ret < 0 && errno == EINTR);
+
+ if (ret < 0) {
+ panic("Could not cd to package's data directory: %s\n", strerror(errno));
+ return 1;
+ }
+ }
+
/* User specified command for exec. */
if (argc >= 3 ) {
if (execvp(argv[2], argv+2) < 0) {
diff --git a/sdcard/Android.mk b/sdcard/Android.mk
index fb04d6d..4630db9 100644
--- a/sdcard/Android.mk
+++ b/sdcard/Android.mk
@@ -6,6 +6,6 @@
LOCAL_MODULE:= sdcard
LOCAL_CFLAGS := -Wall -Wno-unused-parameter
-LOCAL_SHARED_LIBRARIES := libc
+LOCAL_SHARED_LIBRARIES := libc libcutils
include $(BUILD_EXECUTABLE)
diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c
index bff6e67..9a1dd17 100644
--- a/sdcard/sdcard.c
+++ b/sdcard/sdcard.c
@@ -30,6 +30,10 @@
#include <pthread.h>
#include <sys/time.h>
#include <sys/resource.h>
+#include <sys/inotify.h>
+
+#include <cutils/hashmap.h>
+#include <cutils/multiuser.h>
#include <private/android_filesystem_config.h>
@@ -57,6 +61,30 @@
* - 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
@@ -89,6 +117,39 @@
* 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/user" */
+ PERM_ANDROID_USER,
+} perm_t;
+
+/* Permissions structure to derive */
+typedef enum {
+ DERIVE_NONE,
+ DERIVE_LEGACY,
+ DERIVE_UNIFIED,
+} derive_t;
+
struct handle {
int fd;
};
@@ -102,6 +163,13 @@
__u64 nid;
__u64 gen;
+ /* 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 */
@@ -114,16 +182,42 @@
* 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;
};
+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 int int_hash(void *key) {
+ return (int) 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;
struct node root;
- char rootpath[PATH_MAX];
+ char obbpath[PATH_MAX];
+
+ Hashmap* package_to_appid;
+ Hashmap* appid_with_rw;
};
/* Private data used by a single fuse handler. */
@@ -208,15 +302,26 @@
* 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)
-{
- size_t namelen = node->namelen;
+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) {
+ if (node->parent && node->graft_path == NULL) {
pathlen = get_node_path_locked(node->parent, buf, bufsize - namelen - 2);
if (pathlen < 0) {
return -1;
@@ -224,7 +329,6 @@
buf[pathlen++] = '/';
}
- const char* name = node->actual_name ? node->actual_name : node->name;
memcpy(buf + pathlen, name, namelen + 1); /* include trailing \0 */
return pathlen + namelen;
}
@@ -258,7 +362,7 @@
struct dirent* entry;
DIR* dir = opendir(path);
if (!dir) {
- ERROR("opendir %s failed: %s", path, strerror(errno));
+ ERROR("opendir %s failed: %s\n", path, strerror(errno));
return actual;
}
while ((entry = readdir(dir))) {
@@ -273,9 +377,9 @@
return actual;
}
-static void attr_from_stat(struct fuse_attr *attr, const struct stat *s, __u64 nid)
+static void attr_from_stat(struct fuse_attr *attr, const struct stat *s, const struct node* node)
{
- attr->ino = nid;
+ attr->ino = node->nid;
attr->size = s->st_size;
attr->blocks = s->st_blocks;
attr->atime = s->st_atime;
@@ -287,19 +391,154 @@
attr->mode = s->st_mode;
attr->nlink = s->st_nlink;
- /* force permissions to something reasonable:
- * world readable
- * writable by the sdcard group
- */
- if (attr->mode & 0100) {
- attr->mode = (attr->mode & (~0777)) | 0775;
- } else {
- attr->mode = (attr->mode & (~0777)) | 0664;
+ 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 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;
}
- /* all files owned by root.sdcard */
- attr->uid = 0;
- attr->gid = AID_SDCARD_RW;
+ /* 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 (!strcmp(node->name, "Android")) {
+ /* App-specific directories inside; let anyone traverse */
+ node->perm = PERM_ANDROID;
+ node->mode = 0771;
+ } else if (fuse->split_perms) {
+ if (!strcmp(node->name, "DCIM")
+ || !strcmp(node->name, "Pictures")) {
+ node->gid = AID_SDCARD_PICS;
+ } else if (!strcmp(node->name, "Alarms")
+ || !strcmp(node->name, "Movies")
+ || !strcmp(node->name, "Music")
+ || !strcmp(node->name, "Notifications")
+ || !strcmp(node->name, "Podcasts")
+ || !strcmp(node->name, "Ringtones")) {
+ node->gid = AID_SDCARD_AV;
+ }
+ }
+ break;
+ case PERM_ANDROID:
+ if (!strcmp(node->name, "data")) {
+ /* App-specific directories inside; let anyone traverse */
+ node->perm = PERM_ANDROID_DATA;
+ node->mode = 0771;
+ } else if (!strcmp(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 (!strcmp(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:
+ appid = (appid_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*) 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 (!strcmp(name, "autorun.inf")
+ || !strcmp(name, ".android_secure")
+ || !strcmp(name, "android_secure")) {
+ return false;
+ }
+ }
+
+ /* No additional permissions enforcement */
+ if (fuse->derive == DERIVE_NONE) {
+ return true;
+ }
+
+ /* Root or shell always have access */
+ if (hdr->uid == 0 || hdr->uid == AID_SHELL) {
+ 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,
@@ -330,6 +569,8 @@
node->namelen = namelen;
node->nid = ptr_to_id(node);
node->gen = fuse->next_generation++;
+
+ derive_permissions_locked(fuse, parent, node);
acquire_node_locked(node);
add_node_to_parent_locked(node, parent);
return node;
@@ -422,18 +663,54 @@
return child;
}
-static void fuse_init(struct fuse *fuse, int fd, const char *source_path)
-{
+static void fuse_init(struct fuse *fuse, int fd, const char *source_path,
+ gid_t fs_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;
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 = fs_gid;
+ fuse->package_to_appid = hashmapCreate(256, str_hash, str_equals);
+ fuse->appid_with_rw = hashmapCreate(128, int_hash, int_equals);
+ snprintf(fuse->obbpath, sizeof(fuse->obbpath), "%s/obb", source_path);
+ 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 = fs_gid;
+ fuse->package_to_appid = hashmapCreate(256, str_hash, str_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)
@@ -475,7 +752,36 @@
struct stat s;
if (lstat(path, &s) < 0) {
- return -errno;
+ /* But wait! We'll automatically create a directory if its
+ * a valid package name under data or obb, since apps may not
+ * have enough permissions to create for themselves. */
+ if (errno == ENOENT && (parent->perm == PERM_ANDROID_DATA
+ || parent->perm == PERM_ANDROID_OBB)) {
+ TRACE("automatically creating %s\n", path);
+
+ pthread_mutex_lock(&fuse->lock);
+ bool validPackage = hashmapContainsKey(fuse->package_to_appid, (char*) name);
+ pthread_mutex_unlock(&fuse->lock);
+
+ if (!validPackage) {
+ return -ENOENT;
+ }
+ if (mkdir(path, 0775) == -1) {
+ /* We might have raced with ourselves and already created */
+ if (errno != EEXIST) {
+ ERROR("failed to mkdir(%s): %s\n", name, strerror(errno));
+ return -ENOENT;
+ }
+ }
+
+ /* It should exist this time around! */
+ if (lstat(path, &s) < 0) {
+ ERROR("failed to lstat(%s): %s\n", name, strerror(errno));
+ return -errno;
+ }
+ } else {
+ return -errno;
+ }
}
pthread_mutex_lock(&fuse->lock);
@@ -485,7 +791,7 @@
return -ENOMEM;
}
memset(&out, 0, sizeof(out));
- attr_from_stat(&out.attr, &s, node->nid);
+ attr_from_stat(&out.attr, &s, node);
out.attr_valid = 10;
out.entry_valid = 10;
out.nodeid = node->nid;
@@ -495,7 +801,7 @@
return NO_STATUS;
}
-static int fuse_reply_attr(struct fuse* fuse, __u64 unique, __u64 nid,
+static int fuse_reply_attr(struct fuse* fuse, __u64 unique, const struct node* node,
const char* path)
{
struct fuse_attr_out out;
@@ -505,7 +811,7 @@
return -errno;
}
memset(&out, 0, sizeof(out));
- attr_from_stat(&out.attr, &s, nid);
+ attr_from_stat(&out.attr, &s, node);
out.attr_valid = 10;
fuse_reply(fuse, unique, &out, sizeof(out));
return NO_STATUS;
@@ -530,6 +836,10 @@
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);
}
@@ -567,17 +877,23 @@
if (!node) {
return -ENOENT;
}
- return fuse_reply_attr(fuse, hdr->unique, hdr->nodeid, path);
+ 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=%llx valid=%x @ %llx (%s)\n", handler->token,
req->fh, req->valid, hdr->nodeid, node ? node->name : "?");
@@ -586,6 +902,9 @@
if (!node) {
return -ENOENT;
}
+ if (!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.*/
@@ -625,18 +944,20 @@
return -errno;
}
}
- return fuse_reply_attr(fuse, hdr->unique, hdr->nodeid, path);
+ 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 @ %llx (%s)\n", handler->token,
@@ -647,6 +968,9 @@
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;
@@ -657,12 +981,14 @@
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 @ %llx (%s)\n", handler->token,
@@ -673,6 +999,9 @@
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;
@@ -683,11 +1012,13 @@
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;
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 @ %llx (%s)\n", handler->token,
@@ -698,6 +1029,9 @@
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;
}
@@ -707,11 +1041,13 @@
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* 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 @ %llx (%s)\n", handler->token,
@@ -722,6 +1058,9 @@
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;
}
@@ -732,6 +1071,7 @@
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;
@@ -743,6 +1083,7 @@
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,
@@ -755,6 +1096,14 @@
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) {
@@ -800,15 +1149,28 @@
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 @ %llx (%s)\n", handler->token,
req->flags, hdr->nodeid, node ? node->name : "?");
@@ -817,6 +1179,10 @@
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;
@@ -961,6 +1327,9 @@
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;
@@ -972,6 +1341,8 @@
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;
}
@@ -1206,6 +1577,123 @@
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*) appid);
+
+ char* token = strtok(gids, ",");
+ while (token != NULL) {
+ if (strtoul(token, NULL, 10) == AID_SDCARD_RW) {
+ hashmapPut(fuse->appid_with_rw, (void*) appid, (void*) 1);
+ break;
+ }
+ token = strtok(NULL, ",");
+ }
+ }
+ }
+
+ TRACE("read_package_list: found %d packages, %d with sdcard_rw\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;
@@ -1213,7 +1701,7 @@
handlers = malloc(num_threads * sizeof(struct fuse_handler));
if (!handlers) {
- ERROR("cannot allocate storage for threads");
+ ERROR("cannot allocate storage for threads\n");
return -ENOMEM;
}
@@ -1222,16 +1710,25 @@
handlers[i].token = i;
}
- for (i = 1; i < num_threads; 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", i, res);
+ ERROR("failed to start thread #%d, error=%d\n", i, res);
goto quit;
}
}
- handle_fuse_requests(&handlers[0]);
- ERROR("terminated prematurely");
+
+ 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 */
@@ -1241,14 +1738,20 @@
static int usage()
{
- ERROR("usage: sdcard [-t<threads>] <source_path> <dest_path> <uid> <gid>\n"
- " -t<threads>: specify number of threads to use, default -t%d\n"
+ ERROR("usage: sdcard [OPTIONS] <source_path> <dest_path>\n"
+ " -u: specify UID to run as\n"
+ " -g: specify GID to run as\n"
+ " -G: specify default GID for files (default sdcard_r, 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,
- int num_threads) {
+static int run(const char* source_path, const char* dest_path, uid_t uid,
+ gid_t gid, gid_t fs_gid, int num_threads, derive_t derive, bool split_perms) {
int fd;
char opts[256];
int res;
@@ -1259,7 +1762,7 @@
fd = open("/dev/fuse", O_RDWR);
if (fd < 0){
- ERROR("cannot open fuse device (error %d)\n", errno);
+ ERROR("cannot open fuse device: %s\n", strerror(errno));
return -1;
}
@@ -1269,23 +1772,29 @@
res = mount("/dev/fuse", dest_path, "fuse", MS_NOSUID | MS_NODEV, opts);
if (res < 0) {
- ERROR("cannot mount fuse filesystem (error %d)\n", errno);
+ 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 (error %d)\n", errno);
+ ERROR("cannot setgid: %s\n", strerror(errno));
goto error;
}
res = setuid(uid);
if (res < 0) {
- ERROR("cannot setuid (error %d)\n", errno);
+ ERROR("cannot setuid: %s\n", strerror(errno));
goto error;
}
- fuse_init(&fuse, fd, source_path);
+ fuse_init(&fuse, fd, source_path, fs_gid, derive, split_perms);
umask(0);
res = ignite_fuse(&fuse, num_threads);
@@ -1305,34 +1814,53 @@
const char *dest_path = NULL;
uid_t uid = 0;
gid_t gid = 0;
+ gid_t fs_gid = AID_SDCARD_R;
int num_threads = DEFAULT_NUM_THREADS;
+ derive_t derive = DERIVE_NONE;
+ bool split_perms = false;
int i;
struct rlimit rlim;
- for (i = 1; i < argc; i++) {
+ int opt;
+ while ((opt = getopt(argc, argv, "u:g:G:t:dls")) != -1) {
+ switch (opt) {
+ case 'u':
+ uid = strtoul(optarg, NULL, 10);
+ break;
+ case 'g':
+ gid = strtoul(optarg, NULL, 10);
+ break;
+ case 'G':
+ fs_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 (!strncmp(arg, "-t", 2))
- num_threads = strtoul(arg + 2, 0, 10);
- else if (!source_path)
+ if (!source_path) {
source_path = arg;
- else if (!dest_path)
+ } else if (!dest_path) {
dest_path = arg;
- else if (!uid) {
- char* endptr = NULL;
- errno = 0;
- uid = strtoul(arg, &endptr, 10);
- if (*endptr != '\0' || errno != 0) {
- ERROR("Invalid uid");
- return usage();
- }
+ } else if (!uid) {
+ uid = strtoul(arg, NULL, 10);
} else if (!gid) {
- char* endptr = NULL;
- errno = 0;
- gid = strtoul(arg, &endptr, 10);
- if (*endptr != '\0' || errno != 0) {
- ERROR("Invalid gid");
- return usage();
- }
+ gid = strtoul(arg, NULL, 10);
} else {
ERROR("too many arguments\n");
return usage();
@@ -1355,6 +1883,10 @@
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;
@@ -1362,6 +1894,6 @@
ERROR("Error setting RLIMIT_NOFILE, errno = %d\n", errno);
}
- res = run(source_path, dest_path, uid, gid, num_threads);
+ res = run(source_path, dest_path, uid, gid, fs_gid, num_threads, derive, split_perms);
return res < 0 ? 1 : 0;
}
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
index f60037d..75ce53f 100644
--- a/toolbox/Android.mk
+++ b/toolbox/Android.mk
@@ -68,7 +68,8 @@
load_policy \
swapon \
swapoff \
- mkswap
+ mkswap \
+ readlink
ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
TOOLS += r
diff --git a/toolbox/log.c b/toolbox/log.c
index 0bee19e..2f020a8 100644
--- a/toolbox/log.c
+++ b/toolbox/log.c
@@ -30,7 +30,7 @@
*/
#include <stdio.h>
-#include <cutils/logd.h>
+#include <log/logd.h>
#include <ctype.h>
#include <sys/socket.h>
#include <sys/types.h>
diff --git a/toolbox/readlink.c b/toolbox/readlink.c
new file mode 100644
index 0000000..d114e20
--- /dev/null
+++ b/toolbox/readlink.c
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+static int skip_newline, quiet_errors, canonicalize;
+
+static void usage(char* name) {
+ fprintf(stderr, "Usage: %s [OPTION]... FILE\n", name);
+}
+
+int readlink_main(int argc, char* argv[]) {
+ int c;
+ while ((c = getopt(argc, argv, "nfqs")) != -1) {
+ switch (c) {
+ case 'n':
+ skip_newline = 1;
+ break;
+ case 'f':
+ canonicalize = 1;
+ break;
+ case 'q':
+ case 's':
+ quiet_errors = 1;
+ break;
+ case '?':
+ default:
+ usage(argv[0]);
+ return EXIT_FAILURE;
+ }
+ }
+ int index = optind;
+ if (argc - index != 1) {
+ usage(argv[0]);
+ return EXIT_FAILURE;
+ }
+
+ char name[PATH_MAX+1];
+ if (canonicalize) {
+ if(!realpath(argv[optind], name)) {
+ if (!quiet_errors) {
+ perror("readlink");
+ }
+ return EXIT_FAILURE;
+ }
+ } else {
+ ssize_t len = readlink(argv[1], name, PATH_MAX);
+
+ if (len < 0) {
+ if (!quiet_errors) {
+ perror("readlink");
+ }
+ return EXIT_FAILURE;
+ }
+ name[len] = '\0';
+ }
+
+ fputs(name, stdout);
+ if (!skip_newline) {
+ fputs("\n", stdout);
+ }
+
+ return EXIT_SUCCESS;
+}