From cbb1011c95e0c25c29e40e203a6a31bccd029da3 Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Tue, 3 Mar 2009 19:31:44 -0800 Subject: [PATCH] auto import from //depot/cupcake/@135843 --- MODULE_LICENSE_APACHE2 | 0 NOTICE | 222 ++ include/utils.h | 33 + include/utils/AndroidUnicode.h | 255 ++ include/utils/Asset.h | 315 ++ include/utils/AssetDir.h | 145 + include/utils/AssetManager.h | 323 ++ include/utils/Atomic.h | 22 + include/utils/Binder.h | 103 + include/utils/BpBinder.h | 122 + include/utils/Buffer.h | 107 + include/utils/BufferedTextOutput.h | 67 + include/utils/ByteOrder.h | 69 + include/utils/CallStack.h | 76 + include/utils/Debug.h | 45 + include/utils/Endian.h | 40 + include/utils/Errors.h | 87 + include/utils/FileMap.h | 134 + include/utils/IBinder.h | 159 + include/utils/IInterface.h | 135 + include/utils/IMemory.h | 94 + include/utils/IPCThreadState.h | 110 + include/utils/IPermissionController.h | 56 + include/utils/IServiceManager.h | 98 + include/utils/KeyedVector.h | 201 ++ include/utils/List.h | 280 ++ include/utils/Log.h | 33 + include/utils/LogSocket.h | 20 + include/utils/MemoryBase.h | 51 + include/utils/MemoryDealer.h | 238 ++ include/utils/MemoryHeapBase.h | 98 + include/utils/MemoryHeapPmem.h | 80 + include/utils/Parcel.h | 209 ++ include/utils/Pipe.h | 108 + include/utils/ProcessState.h | 117 + include/utils/RefBase.h | 550 ++++ include/utils/ResourceTypes.h | 1720 +++++++++++ include/utils/SharedBuffer.h | 146 + include/utils/Socket.h | 80 + include/utils/SortedVector.h | 282 ++ include/utils/StopWatch.h | 62 + include/utils/String16.h | 260 ++ include/utils/String8.h | 353 +++ include/utils/SystemClock.h | 32 + include/utils/TextOutput.h | 190 ++ include/utils/TimeUtils.h | 89 + include/utils/TimerProbe.h | 72 + include/utils/Timers.h | 137 + include/utils/TypeHelpers.h | 254 ++ include/utils/Vector.h | 359 +++ include/utils/VectorImpl.h | 199 ++ include/utils/ZipEntry.h | 345 +++ include/utils/ZipFile.h | 269 ++ include/utils/ZipFileCRO.h | 59 + include/utils/ZipFileRO.h | 222 ++ include/utils/ZipUtils.h | 67 + include/utils/ashmem.h | 41 + include/utils/executablepath.h | 28 + include/utils/inet_address.h | 103 + include/utils/misc.h | 93 + include/utils/ported.h | 50 + include/utils/string_array.h | 135 + include/utils/threads.h | 347 +++ libs/utils/Android.mk | 156 + libs/utils/Asset.cpp | 813 +++++ libs/utils/AssetDir.cpp | 66 + libs/utils/AssetManager.cpp | 1637 ++++++++++ libs/utils/Binder.cpp | 242 ++ libs/utils/BpBinder.cpp | 348 +++ libs/utils/BufferedTextOutput.cpp | 279 ++ libs/utils/CallStack.cpp | 335 +++ libs/utils/Debug.cpp | 318 ++ libs/utils/FileMap.cpp | 222 ++ libs/utils/IDataConnection.cpp | 89 + libs/utils/IInterface.cpp | 35 + libs/utils/IMemory.cpp | 486 +++ libs/utils/IPCThreadState.cpp | 1030 +++++++ libs/utils/IPermissionController.cpp | 86 + libs/utils/IServiceManager.cpp | 230 ++ libs/utils/InetAddress.cpp | 236 ++ libs/utils/LogSocket.cpp | 129 + libs/utils/MODULE_LICENSE_APACHE2 | 0 libs/utils/MemoryBase.cpp | 46 + libs/utils/MemoryDealer.cpp | 409 +++ libs/utils/MemoryHeapBase.cpp | 183 ++ libs/utils/MemoryHeapPmem.cpp | 248 ++ libs/utils/NOTICE | 190 ++ libs/utils/Parcel.cpp | 1377 +++++++++ libs/utils/Pipe.cpp | 465 +++ libs/utils/ProcessState.cpp | 398 +++ libs/utils/README | 14 + libs/utils/RefBase.cpp | 534 ++++ libs/utils/ResourceTypes.cpp | 3983 +++++++++++++++++++++++++ libs/utils/SharedBuffer.cpp | 113 + libs/utils/Socket.cpp | 388 +++ libs/utils/Static.cpp | 120 + libs/utils/StopWatch.cpp | 79 + libs/utils/String16.cpp | 609 ++++ libs/utils/String8.cpp | 604 ++++ libs/utils/SystemClock.cpp | 139 + libs/utils/TextOutput.cpp | 146 + libs/utils/Threads.cpp | 1128 +++++++ libs/utils/TimerProbe.cpp | 131 + libs/utils/Timers.cpp | 240 ++ libs/utils/Unicode.cpp | 193 ++ libs/utils/VectorImpl.cpp | 611 ++++ libs/utils/ZipEntry.cpp | 696 +++++ libs/utils/ZipFile.cpp | 1296 ++++++++ libs/utils/ZipFileCRO.cpp | 54 + libs/utils/ZipFileRO.cpp | 724 +++++ libs/utils/ZipUtils.cpp | 344 +++ libs/utils/characterData.h | 730 +++++ libs/utils/executablepath_darwin.cpp | 31 + libs/utils/executablepath_linux.cpp | 30 + libs/utils/futex_synchro.c | 175 ++ libs/utils/misc.cpp | 185 ++ libs/utils/ported.cpp | 106 + 117 files changed, 34252 insertions(+) create mode 100644 MODULE_LICENSE_APACHE2 create mode 100644 NOTICE create mode 100644 include/utils.h create mode 100644 include/utils/AndroidUnicode.h create mode 100644 include/utils/Asset.h create mode 100644 include/utils/AssetDir.h create mode 100644 include/utils/AssetManager.h create mode 100644 include/utils/Atomic.h create mode 100644 include/utils/Binder.h create mode 100644 include/utils/BpBinder.h create mode 100644 include/utils/Buffer.h create mode 100644 include/utils/BufferedTextOutput.h create mode 100644 include/utils/ByteOrder.h create mode 100644 include/utils/CallStack.h create mode 100644 include/utils/Debug.h create mode 100644 include/utils/Endian.h create mode 100644 include/utils/Errors.h create mode 100644 include/utils/FileMap.h create mode 100644 include/utils/IBinder.h create mode 100644 include/utils/IInterface.h create mode 100644 include/utils/IMemory.h create mode 100644 include/utils/IPCThreadState.h create mode 100644 include/utils/IPermissionController.h create mode 100644 include/utils/IServiceManager.h create mode 100644 include/utils/KeyedVector.h create mode 100644 include/utils/List.h create mode 100644 include/utils/Log.h create mode 100644 include/utils/LogSocket.h create mode 100644 include/utils/MemoryBase.h create mode 100644 include/utils/MemoryDealer.h create mode 100644 include/utils/MemoryHeapBase.h create mode 100644 include/utils/MemoryHeapPmem.h create mode 100644 include/utils/Parcel.h create mode 100644 include/utils/Pipe.h create mode 100644 include/utils/ProcessState.h create mode 100644 include/utils/RefBase.h create mode 100644 include/utils/ResourceTypes.h create mode 100644 include/utils/SharedBuffer.h create mode 100644 include/utils/Socket.h create mode 100644 include/utils/SortedVector.h create mode 100644 include/utils/StopWatch.h create mode 100644 include/utils/String16.h create mode 100644 include/utils/String8.h create mode 100644 include/utils/SystemClock.h create mode 100644 include/utils/TextOutput.h create mode 100644 include/utils/TimeUtils.h create mode 100644 include/utils/TimerProbe.h create mode 100644 include/utils/Timers.h create mode 100644 include/utils/TypeHelpers.h create mode 100644 include/utils/Vector.h create mode 100644 include/utils/VectorImpl.h create mode 100644 include/utils/ZipEntry.h create mode 100644 include/utils/ZipFile.h create mode 100644 include/utils/ZipFileCRO.h create mode 100644 include/utils/ZipFileRO.h create mode 100644 include/utils/ZipUtils.h create mode 100644 include/utils/ashmem.h create mode 100644 include/utils/executablepath.h create mode 100644 include/utils/inet_address.h create mode 100644 include/utils/misc.h create mode 100644 include/utils/ported.h create mode 100644 include/utils/string_array.h create mode 100644 include/utils/threads.h create mode 100644 libs/utils/Android.mk create mode 100644 libs/utils/Asset.cpp create mode 100644 libs/utils/AssetDir.cpp create mode 100644 libs/utils/AssetManager.cpp create mode 100644 libs/utils/Binder.cpp create mode 100644 libs/utils/BpBinder.cpp create mode 100644 libs/utils/BufferedTextOutput.cpp create mode 100644 libs/utils/CallStack.cpp create mode 100644 libs/utils/Debug.cpp create mode 100644 libs/utils/FileMap.cpp create mode 100644 libs/utils/IDataConnection.cpp create mode 100644 libs/utils/IInterface.cpp create mode 100644 libs/utils/IMemory.cpp create mode 100644 libs/utils/IPCThreadState.cpp create mode 100644 libs/utils/IPermissionController.cpp create mode 100644 libs/utils/IServiceManager.cpp create mode 100644 libs/utils/InetAddress.cpp create mode 100644 libs/utils/LogSocket.cpp create mode 100644 libs/utils/MODULE_LICENSE_APACHE2 create mode 100644 libs/utils/MemoryBase.cpp create mode 100644 libs/utils/MemoryDealer.cpp create mode 100644 libs/utils/MemoryHeapBase.cpp create mode 100644 libs/utils/MemoryHeapPmem.cpp create mode 100644 libs/utils/NOTICE create mode 100644 libs/utils/Parcel.cpp create mode 100644 libs/utils/Pipe.cpp create mode 100644 libs/utils/ProcessState.cpp create mode 100644 libs/utils/README create mode 100644 libs/utils/RefBase.cpp create mode 100644 libs/utils/ResourceTypes.cpp create mode 100644 libs/utils/SharedBuffer.cpp create mode 100644 libs/utils/Socket.cpp create mode 100644 libs/utils/Static.cpp create mode 100644 libs/utils/StopWatch.cpp create mode 100644 libs/utils/String16.cpp create mode 100644 libs/utils/String8.cpp create mode 100644 libs/utils/SystemClock.cpp create mode 100644 libs/utils/TextOutput.cpp create mode 100644 libs/utils/Threads.cpp create mode 100644 libs/utils/TimerProbe.cpp create mode 100644 libs/utils/Timers.cpp create mode 100644 libs/utils/Unicode.cpp create mode 100644 libs/utils/VectorImpl.cpp create mode 100644 libs/utils/ZipEntry.cpp create mode 100644 libs/utils/ZipFile.cpp create mode 100644 libs/utils/ZipFileCRO.cpp create mode 100644 libs/utils/ZipFileRO.cpp create mode 100644 libs/utils/ZipUtils.cpp create mode 100644 libs/utils/characterData.h create mode 100644 libs/utils/executablepath_darwin.cpp create mode 100644 libs/utils/executablepath_linux.cpp create mode 100644 libs/utils/futex_synchro.c create mode 100644 libs/utils/misc.cpp create mode 100644 libs/utils/ported.cpp diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2 new file mode 100644 index 000000000..e69de29bb diff --git a/NOTICE b/NOTICE new file mode 100644 index 000000000..267a6aafd --- /dev/null +++ b/NOTICE @@ -0,0 +1,222 @@ + ========================================================================= + == 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-2004 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. + + 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/include/utils.h b/include/utils.h new file mode 100644 index 000000000..30648b185 --- /dev/null +++ b/include/utils.h @@ -0,0 +1,33 @@ +/* + * 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. This file includes all +// of the generally-useful headers in the "utils" directory. +// +#ifndef _LIBS_UTILS_H +#define _LIBS_UTILS_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#endif // _LIBS_UTILS_H diff --git a/include/utils/AndroidUnicode.h b/include/utils/AndroidUnicode.h new file mode 100644 index 000000000..563fcd019 --- /dev/null +++ b/include/utils/AndroidUnicode.h @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// + +#ifndef ANDROID_UNICODE_H +#define ANDROID_UNICODE_H + +#include +#include + +#define REPLACEMENT_CHAR (0xFFFD) + +// this part of code is copied from umachine.h under ICU +/** + * Define UChar32 as a type for single Unicode code points. + * UChar32 is a signed 32-bit integer (same as int32_t). + * + * The Unicode code point range is 0..0x10ffff. + * All other values (negative or >=0x110000) are illegal as Unicode code points. + * They may be used as sentinel values to indicate "done", "error" + * or similar non-code point conditions. + * + * @stable ICU 2.4 + */ +typedef int32_t UChar32; + +namespace android { + + class Encoding; + /** + * \class Unicode + * + * Helper class for getting properties of Unicode characters. Characters + * can have one of the types listed in CharType and each character can have the + * directionality of Direction. + */ + class Unicode + { + public: + /** + * Directions specified in the Unicode standard. These directions map directly + * to java.lang.Character. + */ + enum Direction { + DIRECTIONALITY_UNDEFINED = -1, + DIRECTIONALITY_LEFT_TO_RIGHT, + DIRECTIONALITY_RIGHT_TO_LEFT, + DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC, + DIRECTIONALITY_EUROPEAN_NUMBER, + DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR, + DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR, + DIRECTIONALITY_ARABIC_NUMBER, + DIRECTIONALITY_COMMON_NUMBER_SEPARATOR, + DIRECTIONALITY_NONSPACING_MARK, + DIRECTIONALITY_BOUNDARY_NEUTRAL, + DIRECTIONALITY_PARAGRAPH_SEPARATOR, + DIRECTIONALITY_SEGMENT_SEPARATOR, + DIRECTIONALITY_WHITESPACE, + DIRECTIONALITY_OTHER_NEUTRALS, + DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING, + DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE, + DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING, + DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE, + DIRECTIONALITY_POP_DIRECTIONAL_FORMAT + }; + + /** + * Character types as specified in the Unicode standard. These map directly to + * java.lang.Character. + */ + enum CharType { + CHARTYPE_UNASSIGNED = 0, + CHARTYPE_UPPERCASE_LETTER, + CHARTYPE_LOWERCASE_LETTER, + CHARTYPE_TITLECASE_LETTER, + CHARTYPE_MODIFIER_LETTER, + CHARTYPE_OTHER_LETTER, + CHARTYPE_NON_SPACING_MARK, + CHARTYPE_ENCLOSING_MARK, + CHARTYPE_COMBINING_SPACING_MARK, + CHARTYPE_DECIMAL_DIGIT_NUMBER, + CHARTYPE_LETTER_NUMBER, + CHARTYPE_OTHER_NUMBER, + CHARTYPE_SPACE_SEPARATOR, + CHARTYPE_LINE_SEPARATOR, + CHARTYPE_PARAGRAPH_SEPARATOR, + CHARTYPE_CONTROL, + CHARTYPE_FORMAT, + CHARTYPE_MISSING_VALUE_FOR_JAVA, /* This is the mysterious missing 17 value from the java constants */ + CHARTYPE_PRIVATE_USE, + CHARTYPE_SURROGATE, + CHARTYPE_DASH_PUNCTUATION, + CHARTYPE_START_PUNCTUATION, + CHARTYPE_END_PUNCTUATION, + CHARTYPE_CONNECTOR_PUNCTUATION, + CHARTYPE_OTHER_PUNCTUATION, + CHARTYPE_MATH_SYMBOL, + CHARTYPE_CURRENCY_SYMBOL, + CHARTYPE_MODIFIER_SYMBOL, + CHARTYPE_OTHER_SYMBOL, + CHARTYPE_INITIAL_QUOTE_PUNCTUATION, + CHARTYPE_FINAL_QUOTE_PUNCTUATION + }; + + /** + * Decomposition types as described by the unicode standard. These values map to + * the same values in uchar.h in ICU. + */ + enum DecompositionType { + DECOMPOSITION_NONE = 0, + DECOMPOSITION_CANONICAL, + DECOMPOSITION_COMPAT, + DECOMPOSITION_CIRCLE, + DECOMPOSITION_FINAL, + DECOMPOSITION_FONT, + DECOMPOSITION_FRACTION, + DECOMPOSITION_INITIAL, + DECOMPOSITION_ISOLATED, + DECOMPOSITION_MEDIAL, + DECOMPOSITION_NARROW, + DECOMPOSITION_NOBREAK, + DECOMPOSITION_SMALL, + DECOMPOSITION_SQUARE, + DECOMPOSITION_SUB, + DECOMPOSITION_SUPER, + DECOMPOSITION_VERTICAL, + DECOMPOSITION_WIDE + }; + + /** + * Returns the packed data for java calls + * @param c The unicode character. + * @return The packed data for the character. + * + * Copied from java.lang.Character implementation: + * 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + * F E D C B A 9 8 7 6 5 4 3 2 1 0 F E D C B A 9 8 7 6 5 4 3 2 1 0 + * + * 31 types --------- + * 18 directionalities --------- + * 2 mirroreds - + * ----------- 56 toupper diffs + * ----------- 48 tolower diffs + * --- 4 totitlecase diffs + * ------------- 84 numeric values + * --------- 24 mirror char diffs + */ + static uint32_t getPackedData(UChar32 c); + + /** + * Get the Character type. + * @param c The unicode character. + * @return The character's type or CHARTYPE_UNASSIGNED if the character is invalid + * or has an unassigned class. + */ + static CharType getType(UChar32 c); + + /** + * Get the Character's decomposition type. + * @param c The unicode character. + * @return The character's decomposition type or DECOMPOSITION_NONE is there + * is no decomposition. + */ + static DecompositionType getDecompositionType(UChar32 c); + + /** + * Returns the digit value of a character or -1 if the character + * is not within the specified radix. + * + * The digit value is computed for integer characters and letters + * within the given radix. This function does not handle Roman Numerals, + * fractions, or any other characters that may represent numbers. + * + * @param c The unicode character + * @param radix The intended radix. + * @return The digit value or -1 if there is no digit value or if the value is outside the radix. + */ + static int getDigitValue(UChar32 c, int radix = 10); + + /** + * Return the numeric value of a character + * + * @param c The unicode character. + * @return The numeric value of the character. -1 if the character has no numeric value, + * -2 if the character has a numeric value that is not representable by an integer. + */ + static int getNumericValue(UChar32 c); + + /** + * Convert the character to lowercase + * @param c The unicode character. + * @return The lowercase character equivalent of c. If c does not have a lowercase equivalent, + * the original character is returned. + */ + static UChar32 toLower(UChar32 c); + + /** + * Convert the character to uppercase + * @param c The unicode character. + * @return The uppercase character equivalent of c. If c does not have an uppercase equivalent, + * the original character is returned. + */ + static UChar32 toUpper(UChar32 c); + + /** + * Get the directionality of the character. + * @param c The unicode character. + * @return The direction of the character or DIRECTIONALITY_UNDEFINED. + */ + static Direction getDirectionality(UChar32 c); + + /** + * Check if the character is a mirrored character. This means that the character + * has an equivalent character that is the mirror image of itself. + * @param c The unicode character. + * @return True iff c has a mirror equivalent. + */ + static bool isMirrored(UChar32 c); + + /** + * Return the mirror of the given character. + * @param c The unicode character. + * @return The mirror equivalent of c. If c does not have a mirror equivalent, + * the original character is returned. + * @see isMirrored + */ + static UChar32 toMirror(UChar32 c); + + /** + * Convert the character to title case. + * @param c The unicode character. + * @return The titlecase equivalent of c. If c does not have a titlecase equivalent, + * the original character is returned. + */ + static UChar32 toTitle(UChar32 c); + + }; + +} + +#endif diff --git a/include/utils/Asset.h b/include/utils/Asset.h new file mode 100644 index 000000000..453a2049a --- /dev/null +++ b/include/utils/Asset.h @@ -0,0 +1,315 @@ +/* + * 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. + */ + +// +// Class providing access to a read-only asset. Asset objects are NOT +// thread-safe, and should not be shared across threads. +// +#ifndef __LIBS_ASSET_H +#define __LIBS_ASSET_H + +#include +#include +#include "FileMap.h" +#include "String8.h" +#include "Errors.h" + +namespace android { + +/* + * Instances of this class provide read-only operations on a byte stream. + * + * Access may be optimized for streaming, random, or whole buffer modes. All + * operations are supported regardless of how the file was opened, but some + * things will be less efficient. [pass that in??] + * + * "Asset" is the base class for all types of assets. The classes below + * provide most of the implementation. The AssetManager uses one of the + * static "create" functions defined here to create a new instance. + */ +class Asset { +public: + virtual ~Asset(void); + + static int32_t getGlobalCount(); + + /* used when opening an asset */ + typedef enum AccessMode { + ACCESS_UNKNOWN = 0, + + /* read chunks, and seek forward and backward */ + ACCESS_RANDOM, + + /* read sequentially, with an occasional forward seek */ + ACCESS_STREAMING, + + /* caller plans to ask for a read-only buffer with all data */ + ACCESS_BUFFER, + } AccessMode; + + enum { + /* data larger than this does not get uncompressed into a buffer */ +#ifdef HAVE_ANDROID_OS + UNCOMPRESS_DATA_MAX = 1 * 1024 * 1024 +#else + UNCOMPRESS_DATA_MAX = 2 * 1024 * 1024 +#endif + }; + + /* + * Read data from the current offset. Returns the actual number of + * bytes read, 0 on EOF, or -1 on error. + */ + virtual ssize_t read(void* buf, size_t count) = 0; + + /* + * Seek to the specified offset. "whence" uses the same values as + * lseek/fseek. Returns the new position on success, or (off_t) -1 + * on failure. + */ + virtual off_t seek(off_t offset, int whence) = 0; + + /* + * Close the asset, freeing all associated resources. + */ + virtual void close(void) = 0; + + /* + * Get a pointer to a buffer with the entire contents of the file. + */ + virtual const void* getBuffer(bool wordAligned) = 0; + + /* + * Get the total amount of data that can be read. + */ + virtual off_t getLength(void) const = 0; + + /* + * Get the total amount of data that can be read from the current position. + */ + virtual off_t getRemainingLength(void) const = 0; + + /* + * Open a new file descriptor that can be used to read this asset. + * Returns -1 if you can not use the file descriptor (for example if the + * asset is compressed). + */ + virtual int openFileDescriptor(off_t* outStart, off_t* outLength) const = 0; + + /* + * Get a string identifying the asset's source. This might be a full + * path, it might be a colon-separated list of identifiers. + * + * This is NOT intended to be used for anything except debug output. + * DO NOT try to parse this or use it to open a file. + */ + const char* getAssetSource(void) const { return mAssetSource.string(); } + +protected: + Asset(void); // constructor; only invoked indirectly + + /* handle common seek() housekeeping */ + off_t handleSeek(off_t offset, int whence, off_t curPosn, off_t maxPosn); + + /* set the asset source string */ + void setAssetSource(const String8& path) { mAssetSource = path; } + + AccessMode getAccessMode(void) const { return mAccessMode; } + +private: + /* these operations are not implemented */ + Asset(const Asset& src); + Asset& operator=(const Asset& src); + + /* AssetManager needs access to our "create" functions */ + friend class AssetManager; + + /* + * Create the asset from a named file on disk. + */ + static Asset* createFromFile(const char* fileName, AccessMode mode); + + /* + * Create the asset from a named, compressed file on disk (e.g. ".gz"). + */ + static Asset* createFromCompressedFile(const char* fileName, + AccessMode mode); + +#if 0 + /* + * Create the asset from a segment of an open file. This will fail + * if "offset" and "length" don't fit within the bounds of the file. + * + * The asset takes ownership of the file descriptor. + */ + static Asset* createFromFileSegment(int fd, off_t offset, size_t length, + AccessMode mode); + + /* + * Create from compressed data. "fd" should be seeked to the start of + * the compressed data. This could be inside a gzip file or part of a + * Zip archive. + * + * The asset takes ownership of the file descriptor. + * + * This may not verify the validity of the compressed data until first + * use. + */ + static Asset* createFromCompressedData(int fd, off_t offset, + int compressionMethod, size_t compressedLength, + size_t uncompressedLength, AccessMode mode); +#endif + + /* + * Create the asset from a memory-mapped file segment. + * + * The asset takes ownership of the FileMap. + */ + static Asset* createFromUncompressedMap(FileMap* dataMap, AccessMode mode); + + /* + * Create the asset from a memory-mapped file segment with compressed + * data. "method" is a Zip archive compression method constant. + * + * The asset takes ownership of the FileMap. + */ + static Asset* createFromCompressedMap(FileMap* dataMap, int method, + size_t uncompressedLen, AccessMode mode); + + + /* + * Create from a reference-counted chunk of shared memory. + */ + // TODO + + AccessMode mAccessMode; // how the asset was opened + String8 mAssetSource; // debug string +}; + + +/* + * =========================================================================== + * + * Innards follow. Do not use these classes directly. + */ + +/* + * An asset based on an uncompressed file on disk. It may encompass the + * entire file or just a piece of it. Access is through fread/fseek. + */ +class _FileAsset : public Asset { +public: + _FileAsset(void); + virtual ~_FileAsset(void); + + /* + * Use a piece of an already-open file. + * + * On success, the object takes ownership of "fd". + */ + status_t openChunk(const char* fileName, int fd, off_t offset, size_t length); + + /* + * Use a memory-mapped region. + * + * On success, the object takes ownership of "dataMap". + */ + status_t openChunk(FileMap* dataMap); + + /* + * Standard Asset interfaces. + */ + virtual ssize_t read(void* buf, size_t count); + virtual off_t seek(off_t offset, int whence); + virtual void close(void); + virtual const void* getBuffer(bool wordAligned); + virtual off_t getLength(void) const { return mLength; } + virtual off_t getRemainingLength(void) const { return mLength-mOffset; } + virtual int openFileDescriptor(off_t* outStart, off_t* outLength) const; + +private: + off_t mStart; // absolute file offset of start of chunk + off_t mLength; // length of the chunk + off_t mOffset; // current local offset, 0 == mStart + FILE* mFp; // for read/seek + char* mFileName; // for opening + + /* + * To support getBuffer() we either need to read the entire thing into + * a buffer or memory-map it. For small files it's probably best to + * just read them in. + */ + enum { kReadVsMapThreshold = 4096 }; + + FileMap* mMap; // for memory map + unsigned char* mBuf; // for read + + const void* ensureAlignment(FileMap* map); +}; + + +/* + * An asset based on compressed data in a file. + */ +class _CompressedAsset : public Asset { +public: + _CompressedAsset(void); + virtual ~_CompressedAsset(void); + + /* + * Use a piece of an already-open file. + * + * On success, the object takes ownership of "fd". + */ + status_t openChunk(int fd, off_t offset, int compressionMethod, + size_t uncompressedLen, size_t compressedLen); + + /* + * Use a memory-mapped region. + * + * On success, the object takes ownership of "fd". + */ + status_t openChunk(FileMap* dataMap, int compressionMethod, + size_t uncompressedLen); + + /* + * Standard Asset interfaces. + */ + virtual ssize_t read(void* buf, size_t count); + virtual off_t seek(off_t offset, int whence); + virtual void close(void); + virtual const void* getBuffer(bool wordAligned); + virtual off_t getLength(void) const { return mUncompressedLen; } + virtual off_t getRemainingLength(void) const { return mUncompressedLen-mOffset; } + virtual int openFileDescriptor(off_t* outStart, off_t* outLength) const { return -1; } + +private: + off_t mStart; // offset to start of compressed data + off_t mCompressedLen; // length of the compressed data + off_t mUncompressedLen; // length of the uncompressed data + off_t mOffset; // current offset, 0 == start of uncomp data + + FileMap* mMap; // for memory-mapped input + int mFd; // for file input + + unsigned char* mBuf; // for getBuffer() +}; + +// need: shared mmap version? + +}; // namespace android + +#endif // __LIBS_ASSET_H diff --git a/include/utils/AssetDir.h b/include/utils/AssetDir.h new file mode 100644 index 000000000..abf8a3595 --- /dev/null +++ b/include/utils/AssetDir.h @@ -0,0 +1,145 @@ +/* + * 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. + */ + +// +// Access a chunk of the asset hierarchy as if it were a single directory. +// +#ifndef __LIBS_ASSETDIR_H +#define __LIBS_ASSETDIR_H + +#include +#include +#include +#include +#include + +namespace android { + +/* + * This provides vector-style access to a directory. We do this rather + * than modeling opendir/readdir access because it's simpler and the + * nature of the operation requires us to have all data on hand anyway. + * + * The list of files will be sorted in ascending order by ASCII value. + * + * The contents are populated by our friend, the AssetManager. + */ +class AssetDir { +public: + AssetDir(void) + : mFileInfo(NULL) + {} + virtual ~AssetDir(void) { + delete mFileInfo; + } + + /* + * Vector-style access. + */ + size_t getFileCount(void) { return mFileInfo->size(); } + const String8& getFileName(int idx) { + return mFileInfo->itemAt(idx).getFileName(); + } + const String8& getSourceName(int idx) { + return mFileInfo->itemAt(idx).getSourceName(); + } + + /* + * Get the type of a file (usually regular or directory). + */ + FileType getFileType(int idx) { + return mFileInfo->itemAt(idx).getFileType(); + } + +private: + /* these operations are not implemented */ + AssetDir(const AssetDir& src); + const AssetDir& operator=(const AssetDir& src); + + friend class AssetManager; + + /* + * This holds information about files in the asset hierarchy. + */ + class FileInfo { + public: + FileInfo(void) {} + FileInfo(const String8& path) // useful for e.g. svect.indexOf + : mFileName(path), mFileType(kFileTypeUnknown) + {} + ~FileInfo(void) {} + FileInfo(const FileInfo& src) { + copyMembers(src); + } + const FileInfo& operator= (const FileInfo& src) { + if (this != &src) + copyMembers(src); + return *this; + } + + void copyMembers(const FileInfo& src) { + mFileName = src.mFileName; + mFileType = src.mFileType; + mSourceName = src.mSourceName; + } + + /* need this for SortedVector; must compare only on file name */ + bool operator< (const FileInfo& rhs) const { + return mFileName < rhs.mFileName; + } + + /* used by AssetManager */ + bool operator== (const FileInfo& rhs) const { + return mFileName == rhs.mFileName; + } + + void set(const String8& path, FileType type) { + mFileName = path; + mFileType = type; + } + + const String8& getFileName(void) const { return mFileName; } + void setFileName(const String8& path) { mFileName = path; } + + FileType getFileType(void) const { return mFileType; } + void setFileType(FileType type) { mFileType = type; } + + const String8& getSourceName(void) const { return mSourceName; } + void setSourceName(const String8& path) { mSourceName = path; } + + /* + * Handy utility for finding an entry in a sorted vector of FileInfo. + * Returns the index of the matching entry, or -1 if none found. + */ + static int findEntry(const SortedVector* pVector, + const String8& fileName); + + private: + String8 mFileName; // filename only + FileType mFileType; // regular, directory, etc + + String8 mSourceName; // currently debug-only + }; + + /* AssetManager uses this to initialize us */ + void setFileList(SortedVector* list) { mFileInfo = list; } + + SortedVector* mFileInfo; +}; + +}; // namespace android + +#endif // __LIBS_ASSETDIR_H diff --git a/include/utils/AssetManager.h b/include/utils/AssetManager.h new file mode 100644 index 000000000..e94c0e8fe --- /dev/null +++ b/include/utils/AssetManager.h @@ -0,0 +1,323 @@ +/* + * 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. + */ + +// +// Asset management class. AssetManager objects are thread-safe. +// +#ifndef __LIBS_ASSETMANAGER_H +#define __LIBS_ASSETMANAGER_H + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace android { + +class Asset; // fwd decl for things that include Asset.h first +class ResTable; +struct ResTable_config; + +/* + * Every application that uses assets needs one instance of this. A + * single instance may be shared across multiple threads, and a single + * thread may have more than one instance (the latter is discouraged). + * + * The purpose of the AssetManager is to create Asset objects. To do + * this efficiently it may cache information about the locations of + * files it has seen. This can be controlled with the "cacheMode" + * argument. + * + * The asset hierarchy may be examined like a filesystem, using + * AssetDir objects to peruse a single directory. + */ +class AssetManager { +public: + typedef enum CacheMode { + CACHE_UNKNOWN = 0, + CACHE_OFF, // don't try to cache file locations + CACHE_DEFER, // construct cache as pieces are needed + //CACHE_SCAN, // scan full(!) asset hierarchy at init() time + } CacheMode; + + AssetManager(CacheMode cacheMode = CACHE_OFF); + virtual ~AssetManager(void); + + static int32_t getGlobalCount(); + + /* + * Add a new source for assets. This can be called multiple times to + * look in multiple places for assets. It can be either a directory (for + * finding assets as raw files on the disk) or a ZIP file. This newly + * added asset path will be examined first when searching for assets, + * before any that were previously added. + * + * Returns "true" on success, "false" on failure. If 'cookie' is non-NULL, + * then on success, *cookie is set to the value corresponding to the + * newly-added asset source. + */ + bool addAssetPath(const String8& path, void** cookie); + + /* + * Convenience for adding the standard system assets. Uses the + * ANDROID_ROOT environment variable to find them. + */ + bool addDefaultAssets(); + + /* + * Iterate over the asset paths in this manager. (Previously + * added via addAssetPath() and addDefaultAssets().) On first call, + * 'cookie' must be NULL, resulting in the first cookie being returned. + * Each next cookie will be returned there-after, until NULL indicating + * the end has been reached. + */ + void* nextAssetPath(void* cookie) const; + + /* + * Return an asset path in the manager. 'which' must be between 0 and + * countAssetPaths(). + */ + String8 getAssetPath(void* cookie) const; + + /* + * Set the current locale and vendor. The locale can change during + * the lifetime of an AssetManager if the user updates the device's + * language setting. The vendor is less likely to change. + * + * Pass in NULL to indicate no preference. + */ + void setLocale(const char* locale); + void setVendor(const char* vendor); + + /* + * Choose screen orientation for resources values returned. + */ + void setConfiguration(const ResTable_config& config, const char* locale = NULL); + + typedef Asset::AccessMode AccessMode; // typing shortcut + + /* + * Open an asset. + * + * This will search through locale-specific and vendor-specific + * directories and packages to find the file. + * + * The object returned does not depend on the AssetManager. It should + * be freed by calling Asset::close(). + */ + Asset* open(const char* fileName, AccessMode mode); + + /* + * Open a non-asset file as an asset. + * + * This is for opening files that are included in an asset package + * but aren't assets. These sit outside the usual "locale/vendor" + * path hierarchy, and will not be seen by "AssetDir" or included + * in our filename cache. + */ + Asset* openNonAsset(const char* fileName, AccessMode mode); + + /* + * Explicit non-asset file. The file explicitly named by the cookie (the + * resource set to look in) and fileName will be opened and returned. + */ + Asset* openNonAsset(void* cookie, const char* fileName, AccessMode mode); + + /* + * Open a directory within the asset hierarchy. + * + * The contents of the directory are an amalgam of vendor-specific, + * locale-specific, and generic assets stored loosely or in asset + * packages. Depending on the cache setting and previous accesses, + * this call may incur significant disk overhead. + * + * To open the top-level directory, pass in "". + */ + AssetDir* openDir(const char* dirName); + + /* + * Get the type of a file in the asset hierarchy. They will either + * be "regular" or "directory". [Currently only works for "regular".] + * + * Can also be used as a quick test for existence of a file. + */ + FileType getFileType(const char* fileName); + + /* + * Return the complete resource table to find things in the package. + */ + const ResTable& getResources(bool required = true) const; + + /* + * Discard cached filename information. This only needs to be called + * if somebody has updated the set of "loose" files, and we want to + * discard our cached notion of what's where. + */ + void purge(void) { purgeFileNameCacheLocked(); } + + /* + * Return true if the files this AssetManager references are all + * up-to-date (have not been changed since it was created). If false + * is returned, you will need to create a new AssetManager to get + * the current data. + */ + bool isUpToDate(); + + /** + * Get the known locales for this asset manager object. + */ + void getLocales(Vector* locales) const; + +private: + struct asset_path + { + String8 path; + FileType type; + }; + + Asset* openInPathLocked(const char* fileName, AccessMode mode, + const asset_path& path); + Asset* openNonAssetInPathLocked(const char* fileName, AccessMode mode, + const asset_path& path); + Asset* openInLocaleVendorLocked(const char* fileName, AccessMode mode, + const asset_path& path, const char* locale, const char* vendor); + String8 createPathNameLocked(const asset_path& path, const char* locale, + const char* vendor); + String8 createPathNameLocked(const asset_path& path, const char* rootDir); + String8 createZipSourceNameLocked(const String8& zipFileName, + const String8& dirName, const String8& fileName); + + ZipFileRO* getZipFileLocked(const asset_path& path); + Asset* openAssetFromFileLocked(const String8& fileName, AccessMode mode); + Asset* openAssetFromZipLocked(const ZipFileRO* pZipFile, + const ZipEntryRO entry, AccessMode mode, const String8& entryName); + + bool scanAndMergeDirLocked(SortedVector* pMergedInfo, + const asset_path& path, const char* rootDir, const char* dirName); + SortedVector* scanDirLocked(const String8& path); + bool scanAndMergeZipLocked(SortedVector* pMergedInfo, + const asset_path& path, const char* rootDir, const char* dirName); + void mergeInfoLocked(SortedVector* pMergedInfo, + const SortedVector* pContents); + + void loadFileNameCacheLocked(void); + void fncScanLocked(SortedVector* pMergedInfo, + const char* dirName); + bool fncScanAndMergeDirLocked( + SortedVector* pMergedInfo, + const asset_path& path, const char* locale, const char* vendor, + const char* dirName); + void purgeFileNameCacheLocked(void); + + const ResTable* getResTable(bool required = true) const; + void setLocaleLocked(const char* locale); + void updateResourceParamsLocked() const; + + class SharedZip : public RefBase { + public: + static sp get(const String8& path); + + ZipFileRO* getZip(); + + Asset* getResourceTableAsset(); + Asset* setResourceTableAsset(Asset* asset); + + bool isUpToDate(); + + protected: + ~SharedZip(); + + private: + SharedZip(const String8& path, time_t modWhen); + SharedZip(); // <-- not implemented + + String8 mPath; + ZipFileRO* mZipFile; + time_t mModWhen; + + Asset* mResourceTableAsset; + + static Mutex gLock; + static DefaultKeyedVector > gOpen; + }; + + /* + * Manage a set of Zip files. For each file we need a pointer to the + * ZipFile and a time_t with the file's modification date. + * + * We currently only have two zip files (current app, "common" app). + * (This was originally written for 8, based on app/locale/vendor.) + */ + class ZipSet { + public: + ZipSet(void); + ~ZipSet(void); + + /* + * Return a ZipFileRO structure for a ZipFileRO with the specified + * parameters. + */ + ZipFileRO* getZip(const String8& path); + + Asset* getZipResourceTable(const String8& path); + Asset* setZipResourceTable(const String8& path, Asset* asset); + + // generate path, e.g. "common/en-US-noogle.zip" + static String8 getPathName(const char* path); + + bool isUpToDate(); + + private: + void closeZip(int idx); + + int getIndex(const String8& zip) const; + mutable Vector mZipPath; + mutable Vector > mZipFile; + }; + + // Protect all internal state. + mutable Mutex mLock; + + ZipSet mZipSet; + + Vector mAssetPaths; + char* mLocale; + char* mVendor; + + mutable ResTable* mResources; + ResTable_config* mConfig; + + /* + * Cached data for "loose" files. This lets us avoid poking at the + * filesystem when searching for loose assets. Each entry is the + * "extended partial" path, e.g. "default/default/foo/bar.txt". The + * full set of files is present, including ".EXCLUDE" entries. + * + * We do not cache directory names. We don't retain the ".gz", + * because to our clients "foo" and "foo.gz" both look like "foo". + */ + CacheMode mCacheMode; // is the cache enabled? + bool mCacheValid; // clear when locale or vendor changes + SortedVector mCache; +}; + +}; // namespace android + +#endif // __LIBS_ASSETMANAGER_H diff --git a/include/utils/Atomic.h b/include/utils/Atomic.h new file mode 100644 index 000000000..7eb476c94 --- /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 + +#endif // ANDROID_UTILS_ATOMIC_H diff --git a/include/utils/Binder.h b/include/utils/Binder.h new file mode 100644 index 000000000..b5b8d9851 --- /dev/null +++ b/include/utils/Binder.h @@ -0,0 +1,103 @@ +/* + * 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_BINDER_H +#define ANDROID_BINDER_H + +#include + +// --------------------------------------------------------------------------- +namespace android { + +class BBinder : public IBinder +{ +public: + BBinder(); + + virtual String16 getInterfaceDescriptor() const; + virtual bool isBinderAlive() const; + virtual status_t pingBinder(); + virtual status_t dump(int fd, const Vector& args); + + virtual status_t transact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); + + virtual status_t linkToDeath(const sp& recipient, + void* cookie = NULL, + uint32_t flags = 0); + + virtual status_t unlinkToDeath( const wp& recipient, + void* cookie = NULL, + uint32_t flags = 0, + wp* outRecipient = NULL); + + virtual void attachObject( const void* objectID, + void* object, + void* cleanupCookie, + object_cleanup_func func); + virtual void* findObject(const void* objectID) const; + virtual void detachObject(const void* objectID); + + virtual BBinder* localBinder(); + +protected: + virtual ~BBinder(); + + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); + +private: + BBinder(const BBinder& o); + BBinder& operator=(const BBinder& o); + + class Extras; + + Extras* mExtras; + void* mReserved0; +}; + +// --------------------------------------------------------------------------- + +class BpRefBase : public virtual RefBase +{ +protected: + BpRefBase(const sp& o); + virtual ~BpRefBase(); + virtual void onFirstRef(); + virtual void onLastStrongRef(const void* id); + virtual bool onIncStrongAttempted(uint32_t flags, const void* id); + + inline IBinder* remote() { return mRemote; } + inline IBinder* remote() const { return mRemote; } + +private: + BpRefBase(const BpRefBase& o); + BpRefBase& operator=(const BpRefBase& o); + + IBinder* const mRemote; + RefBase::weakref_type* mRefs; + volatile int32_t mState; +}; + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_BINDER_H diff --git a/include/utils/BpBinder.h b/include/utils/BpBinder.h new file mode 100644 index 000000000..7b96e296f --- /dev/null +++ b/include/utils/BpBinder.h @@ -0,0 +1,122 @@ +/* + * 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_BPBINDER_H +#define ANDROID_BPBINDER_H + +#include +#include +#include + +// --------------------------------------------------------------------------- +namespace android { + +class BpBinder : public IBinder +{ +public: + BpBinder(int32_t handle); + + inline int32_t handle() const { return mHandle; } + + virtual String16 getInterfaceDescriptor() const; + virtual bool isBinderAlive() const; + virtual status_t pingBinder(); + virtual status_t dump(int fd, const Vector& args); + + virtual status_t transact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); + + virtual status_t linkToDeath(const sp& recipient, + void* cookie = NULL, + uint32_t flags = 0); + virtual status_t unlinkToDeath( const wp& recipient, + void* cookie = NULL, + uint32_t flags = 0, + wp* outRecipient = NULL); + + virtual void attachObject( const void* objectID, + void* object, + void* cleanupCookie, + object_cleanup_func func); + virtual void* findObject(const void* objectID) const; + virtual void detachObject(const void* objectID); + + virtual BpBinder* remoteBinder(); + + status_t setConstantData(const void* data, size_t size); + void sendObituary(); + + class ObjectManager + { + public: + ObjectManager(); + ~ObjectManager(); + + void attach( const void* objectID, + void* object, + void* cleanupCookie, + IBinder::object_cleanup_func func); + void* find(const void* objectID) const; + void detach(const void* objectID); + + void kill(); + + private: + ObjectManager(const ObjectManager&); + ObjectManager& operator=(const ObjectManager&); + + struct entry_t + { + void* object; + void* cleanupCookie; + IBinder::object_cleanup_func func; + }; + + KeyedVector mObjects; + }; + +protected: + virtual ~BpBinder(); + virtual void onFirstRef(); + virtual void onLastStrongRef(const void* id); + virtual bool onIncStrongAttempted(uint32_t flags, const void* id); + +private: + const int32_t mHandle; + + struct Obituary { + wp recipient; + void* cookie; + uint32_t flags; + }; + + void reportOneDeath(const Obituary& obit); + + mutable Mutex mLock; + volatile int32_t mAlive; + volatile int32_t mObitsSent; + Vector* mObituaries; + ObjectManager mObjects; + Parcel* mConstantData; +}; + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_BPBINDER_H diff --git a/include/utils/Buffer.h b/include/utils/Buffer.h new file mode 100644 index 000000000..8e22b0f21 --- /dev/null +++ b/include/utils/Buffer.h @@ -0,0 +1,107 @@ +/* + * 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 __UTILS_BUFFER_H__ +#define __UTILS_BUFFER_H__ 1 + +#include +#include +#include + +namespace android { + +class Buffer +{ +private: + char *buf; + int bufsiz; + int used; + void ensureCapacity(int len); + + void + makeRoomFor(int len) + { + if (len + used >= bufsiz) { + bufsiz = (len + used) * 3/2 + 2; + char *blah = new char[bufsiz]; + + memcpy(blah, buf, used); + delete[] buf; + buf = blah; + } + } + +public: + Buffer() + { + bufsiz = 16; + buf = new char[bufsiz]; + clear(); + } + + ~Buffer() + { + delete[] buf; + } + + void + clear() + { + buf[0] = '\0'; + used = 0; + } + + int + length() + { + return used; + } + + void + append(const char c) + { + makeRoomFor(1); + buf[used] = c; + used++; + buf[used] = '\0'; + } + + void + append(const char *s, int len) + { + makeRoomFor(len); + + memcpy(buf + used, s, len); + used += len; + buf[used] = '\0'; + } + + void + append(const char *s) + { + append(s, strlen(s)); + } + + char * + getBytes() + { + return buf; + } +}; + +}; // namespace android + +#endif diff --git a/include/utils/BufferedTextOutput.h b/include/utils/BufferedTextOutput.h new file mode 100644 index 000000000..69c624037 --- /dev/null +++ b/include/utils/BufferedTextOutput.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_BUFFEREDTEXTOUTPUT_H +#define ANDROID_BUFFEREDTEXTOUTPUT_H + +#include +#include +#include + +// --------------------------------------------------------------------------- +namespace android { + +class BufferedTextOutput : public TextOutput +{ +public: + //** Flags for constructor */ + enum { + MULTITHREADED = 0x0001 + }; + + BufferedTextOutput(uint32_t flags = 0); + virtual ~BufferedTextOutput(); + + virtual status_t print(const char* txt, size_t len); + virtual void moveIndent(int delta); + + virtual void pushBundle(); + virtual void popBundle(); + +protected: + virtual status_t writeLines(const struct iovec& vec, size_t N) = 0; + +private: + struct BufferState; + struct ThreadState; + + static ThreadState*getThreadState(); + static void threadDestructor(void *st); + + BufferState*getBuffer() const; + + uint32_t mFlags; + const int32_t mSeq; + const int32_t mIndex; + + Mutex mLock; + BufferState* mGlobalState; +}; + +// --------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_BUFFEREDTEXTOUTPUT_H diff --git a/include/utils/ByteOrder.h b/include/utils/ByteOrder.h new file mode 100644 index 000000000..4c0606763 --- /dev/null +++ b/include/utils/ByteOrder.h @@ -0,0 +1,69 @@ +/* + * 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 +#include +#ifdef HAVE_WINSOCK +#include +#else +#include +#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. + */ + +#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 + +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 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 + +#endif // _LIBS_UTILS_BYTE_ORDER_H diff --git a/include/utils/CallStack.h b/include/utils/CallStack.h new file mode 100644 index 000000000..c2c8ce514 --- /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 +#include + +#include + +// --------------------------------------------------------------------------- + +namespace android { + +class CallStack +{ +public: + enum { + MAX_DEPTH = 31 + }; + + CallStack(); + 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=0, int32_t maxDepth=MAX_DEPTH); + + // Dump a stack trace to the log + void dump(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: + // Internal helper function + String8 toStringSingleLevel(const char* prefix, int32_t level) const; + + size_t mCount; + const void* mStack[MAX_DEPTH]; +}; + +}; // namespace android + + +// --------------------------------------------------------------------------- + +#endif // ANDROID_CALLSTACK_H diff --git a/include/utils/Debug.h b/include/utils/Debug.h new file mode 100644 index 000000000..a662b9cc2 --- /dev/null +++ b/include/utils/Debug.h @@ -0,0 +1,45 @@ +/* + * 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. + */ + +// +// Debugging tools. These should be able to be stripped +// in release builds. +// +#ifndef ANDROID_DEBUG_H +#define ANDROID_DEBUG_H + +#include +#include + +namespace android { + +template struct CompileTimeAssert; +template<> struct CompileTimeAssert {}; + +const char* stringForIndent(int32_t indentLevel); + +typedef void (*debugPrintFunc)(void* cookie, const char* txt); + +void printTypeCode(uint32_t typeCode, + debugPrintFunc func = 0, void* cookie = 0); +void printHexData(int32_t indent, const void *buf, size_t length, + size_t bytesPerLine=16, int32_t singleLineBytesCutoff=16, + size_t alignment=0, bool cArrayStyle=false, + debugPrintFunc func = 0, void* cookie = 0); + +}; // namespace android + +#endif // ANDROID_DEBUG_H diff --git a/include/utils/Endian.h b/include/utils/Endian.h new file mode 100644 index 000000000..19f250494 --- /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 + +#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 000000000..1bf9e6f2b --- /dev/null +++ b/include/utils/Errors.h @@ -0,0 +1,87 @@ +/* + * 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 +#include + +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 = -ETIME, + UNKNOWN_TRANSACTION = -EBADMSG, +#else + BAD_INDEX = -E2BIG, + NOT_ENOUGH_DATA = 0x80000003, + WOULD_BLOCK = 0x80000004, + TIMED_OUT = 0x80000005, + UNKNOWN_TRANSACTION = 0x80000006, +#endif +}; + +// 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 000000000..8dfd3bea6 --- /dev/null +++ b/include/utils/FileMap.h @@ -0,0 +1,134 @@ +/* + * 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 + +#ifdef HAVE_WIN32_FILEMAP +#include +#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, + off_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. + */ + off_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 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" + off_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/IBinder.h b/include/utils/IBinder.h new file mode 100644 index 000000000..737033090 --- /dev/null +++ b/include/utils/IBinder.h @@ -0,0 +1,159 @@ +/* + * 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_IBINDER_H +#define ANDROID_IBINDER_H + +#include +#include +#include +#include + + +#define B_PACK_CHARS(c1, c2, c3, c4) \ + ((((c1)<<24)) | (((c2)<<16)) | (((c3)<<8)) | (c4)) + +// --------------------------------------------------------------------------- +namespace android { + +class BBinder; +class BpBinder; +class IInterface; +class Parcel; + +/** + * Base class and low-level protocol for a remotable object. + * You can derive from this class to create an object for which other + * processes can hold references to it. Communication between processes + * (method calls, property get and set) is down through a low-level + * protocol implemented on top of the transact() API. + */ +class IBinder : public virtual RefBase +{ +public: + enum { + FIRST_CALL_TRANSACTION = 0x00000001, + LAST_CALL_TRANSACTION = 0x00ffffff, + + PING_TRANSACTION = B_PACK_CHARS('_','P','N','G'), + DUMP_TRANSACTION = B_PACK_CHARS('_','D','M','P'), + INTERFACE_TRANSACTION = B_PACK_CHARS('_', 'N', 'T', 'F'), + + // Corresponds to tfOneWay -- an asynchronous call. + FLAG_ONEWAY = 0x00000001 + }; + + inline IBinder() { } + + /** + * Check if this IBinder implements the interface named by + * @a descriptor. If it does, the base pointer to it is returned, + * which you can safely static_cast<> to the concrete C++ interface. + */ + virtual sp queryLocalInterface(const String16& descriptor); + + /** + * Return the canonical name of the interface provided by this IBinder + * object. + */ + virtual String16 getInterfaceDescriptor() const = 0; + + virtual bool isBinderAlive() const = 0; + virtual status_t pingBinder() = 0; + virtual status_t dump(int fd, const Vector& args) = 0; + + virtual status_t transact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0) = 0; + + /** + * This method allows you to add data that is transported through + * IPC along with your IBinder pointer. When implementing a Binder + * object, override it to write your desired data in to @a outData. + * You can then call getConstantData() on your IBinder to retrieve + * that data, from any process. You MUST return the number of bytes + * written in to the parcel (including padding). + */ + class DeathRecipient : public virtual RefBase + { + public: + virtual void binderDied(const wp& who) = 0; + }; + + /** + * Register the @a recipient for a notification if this binder + * goes away. If this binder object unexpectedly goes away + * (typically because its hosting process has been killed), + * then DeathRecipient::binderDied() will be called with a referene + * to this. + * + * The @a cookie is optional -- if non-NULL, it should be a + * memory address that you own (that is, you know it is unique). + * + * @note You will only receive death notifications for remote binders, + * as local binders by definition can't die without you dying as well. + * Trying to use this function on a local binder will result in an + * INVALID_OPERATION code being returned and nothing happening. + * + * @note This link always holds a weak reference to its recipient. + * + * @note You will only receive a weak reference to the dead + * binder. You should not try to promote this to a strong reference. + * (Nor should you need to, as there is nothing useful you can + * directly do with it now that it has passed on.) + */ + virtual status_t linkToDeath(const sp& recipient, + void* cookie = NULL, + uint32_t flags = 0) = 0; + + /** + * Remove a previously registered death notification. + * The @a recipient will no longer be called if this object + * dies. The @a cookie is optional. If non-NULL, you can + * supply a NULL @a recipient, and the recipient previously + * added with that cookie will be unlinked. + */ + virtual status_t unlinkToDeath( const wp& recipient, + void* cookie = NULL, + uint32_t flags = 0, + wp* outRecipient = NULL) = 0; + + virtual bool checkSubclass(const void* subclassID) const; + + typedef void (*object_cleanup_func)(const void* id, void* obj, void* cleanupCookie); + + virtual void attachObject( const void* objectID, + void* object, + void* cleanupCookie, + object_cleanup_func func) = 0; + virtual void* findObject(const void* objectID) const = 0; + virtual void detachObject(const void* objectID) = 0; + + virtual BBinder* localBinder(); + virtual BpBinder* remoteBinder(); + +protected: + inline virtual ~IBinder() { } + +private: +}; + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_IBINDER_H diff --git a/include/utils/IInterface.h b/include/utils/IInterface.h new file mode 100644 index 000000000..959722a4d --- /dev/null +++ b/include/utils/IInterface.h @@ -0,0 +1,135 @@ +/* + * 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_IINTERFACE_H +#define ANDROID_IINTERFACE_H + +#include + +namespace android { + +// ---------------------------------------------------------------------- + +class IInterface : public virtual RefBase +{ +public: + sp asBinder(); + sp asBinder() const; + +protected: + virtual IBinder* onAsBinder() = 0; +}; + +// ---------------------------------------------------------------------- + +template +inline sp interface_cast(const sp& obj) +{ + return INTERFACE::asInterface(obj); +} + +// ---------------------------------------------------------------------- + +template +class BnInterface : public INTERFACE, public BBinder +{ +public: + virtual sp queryLocalInterface(const String16& _descriptor); + virtual String16 getInterfaceDescriptor() const; + +protected: + virtual IBinder* onAsBinder(); +}; + +// ---------------------------------------------------------------------- + +template +class BpInterface : public INTERFACE, public BpRefBase +{ +public: + BpInterface(const sp& remote); + +protected: + virtual IBinder* onAsBinder(); +}; + +// ---------------------------------------------------------------------- + +#define DECLARE_META_INTERFACE(INTERFACE) \ + static const String16 descriptor; \ + static sp asInterface(const sp& obj); \ + virtual String16 getInterfaceDescriptor() const; \ + +#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \ + const String16 I##INTERFACE::descriptor(NAME); \ + String16 I##INTERFACE::getInterfaceDescriptor() const { \ + return I##INTERFACE::descriptor; \ + } \ + sp I##INTERFACE::asInterface(const sp& obj) \ + { \ + sp intr; \ + if (obj != NULL) { \ + intr = static_cast( \ + obj->queryLocalInterface( \ + I##INTERFACE::descriptor).get()); \ + if (intr == NULL) { \ + intr = new Bp##INTERFACE(obj); \ + } \ + } \ + return intr; \ + } \ + +// ---------------------------------------------------------------------- +// No user-servicable parts after this... + +template +inline sp BnInterface::queryLocalInterface( + const String16& _descriptor) +{ + if (_descriptor == INTERFACE::descriptor) return this; + return NULL; +} + +template +inline String16 BnInterface::getInterfaceDescriptor() const +{ + return INTERFACE::getInterfaceDescriptor(); +} + +template +IBinder* BnInterface::onAsBinder() +{ + return this; +} + +template +inline BpInterface::BpInterface(const sp& remote) + : BpRefBase(remote) +{ +} + +template +inline IBinder* BpInterface::onAsBinder() +{ + return remote(); +} + +// ---------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_IINTERFACE_H diff --git a/include/utils/IMemory.h b/include/utils/IMemory.h new file mode 100644 index 000000000..35a3fd7da --- /dev/null +++ b/include/utils/IMemory.h @@ -0,0 +1,94 @@ +/* + * 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_IMEMORY_H +#define ANDROID_IMEMORY_H + +#include +#include +#include + +#include +#include +#include + +namespace android { + +// ---------------------------------------------------------------------------- + +class IMemoryHeap : public IInterface +{ +public: + DECLARE_META_INTERFACE(MemoryHeap); + + // flags returned by getFlags() + enum { + READ_ONLY = 0x00000001, + MAP_ONCE = 0x00000002 + }; + + virtual int getHeapID() const = 0; + virtual void* getBase() const = 0; + virtual size_t getSize() const = 0; + virtual uint32_t getFlags() const = 0; + + // these are there just for backward source compatibility + int32_t heapID() const { return getHeapID(); } + void* base() const { return getBase(); } + size_t virtualSize() const { return getSize(); } +}; + +class BnMemoryHeap : public BnInterface +{ +public: + virtual status_t onTransact( + uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +// ---------------------------------------------------------------------------- + +class IMemory : public IInterface +{ +public: + DECLARE_META_INTERFACE(Memory); + + virtual sp getMemory(ssize_t* offset=0, size_t* size=0) const = 0; + + // helpers + void* fastPointer(const sp& heap, ssize_t offset) const; + void* pointer() const; + size_t size() const; + ssize_t offset() const; +}; + +class BnMemory : public BnInterface +{ +public: + virtual status_t onTransact( + uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +// ---------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_IMEMORY_H diff --git a/include/utils/IPCThreadState.h b/include/utils/IPCThreadState.h new file mode 100644 index 000000000..0490fd3ec --- /dev/null +++ b/include/utils/IPCThreadState.h @@ -0,0 +1,110 @@ +/* + * 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_IPC_THREAD_STATE_H +#define ANDROID_IPC_THREAD_STATE_H + +#include +#include +#include +#include + +#ifdef HAVE_WIN32_PROC +typedef int uid_t; +#endif + +// --------------------------------------------------------------------------- +namespace android { + +class IPCThreadState +{ +public: + static IPCThreadState* self(); + + sp process(); + + status_t clearLastError(); + + int getCallingPid(); + int getCallingUid(); + + int64_t clearCallingIdentity(); + void restoreCallingIdentity(int64_t token); + + void flushCommands(); + + void joinThreadPool(bool isMain = true); + + // Stop the local process. + void stopProcess(bool immediate = true); + + status_t transact(int32_t handle, + uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags); + + void incStrongHandle(int32_t handle); + void decStrongHandle(int32_t handle); + void incWeakHandle(int32_t handle); + void decWeakHandle(int32_t handle); + status_t attemptIncStrongHandle(int32_t handle); + static void expungeHandle(int32_t handle, IBinder* binder); + status_t requestDeathNotification( int32_t handle, + BpBinder* proxy); + status_t clearDeathNotification( int32_t handle, + BpBinder* proxy); + + static void shutdown(); + +private: + IPCThreadState(); + ~IPCThreadState(); + + status_t sendReply(const Parcel& reply, uint32_t flags); + status_t waitForResponse(Parcel *reply, + status_t *acquireResult=NULL); + status_t talkWithDriver(bool doReceive=true); + status_t writeTransactionData(int32_t cmd, + uint32_t binderFlags, + int32_t handle, + uint32_t code, + const Parcel& data, + status_t* statusBuffer); + status_t executeCommand(int32_t command); + + void clearCaller(); + + static void threadDestructor(void *st); + static void freeBuffer(Parcel* parcel, + const uint8_t* data, size_t dataSize, + const size_t* objects, size_t objectsSize, + void* cookie); + + const sp mProcess; + Vector mPendingStrongDerefs; + Vector mPendingWeakDerefs; + + Parcel mIn; + Parcel mOut; + status_t mLastError; + pid_t mCallingPid; + uid_t mCallingUid; +}; + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_IPC_THREAD_STATE_H diff --git a/include/utils/IPermissionController.h b/include/utils/IPermissionController.h new file mode 100644 index 000000000..cb1dd345d --- /dev/null +++ b/include/utils/IPermissionController.h @@ -0,0 +1,56 @@ +/* + * 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_IPERMISSION_CONTROLLER_H +#define ANDROID_IPERMISSION_CONTROLLER_H + +#include + +namespace android { + +// ---------------------------------------------------------------------- + +class IPermissionController : public IInterface +{ +public: + DECLARE_META_INTERFACE(PermissionController); + + virtual bool checkPermission(const String16& permission, + int32_t pid, int32_t uid) = 0; + + enum { + CHECK_PERMISSION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION + }; +}; + +// ---------------------------------------------------------------------- + +class BnPermissionController : public BnInterface +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +// ---------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_IPERMISSION_CONTROLLER_H + diff --git a/include/utils/IServiceManager.h b/include/utils/IServiceManager.h new file mode 100644 index 000000000..e3d99fe7e --- /dev/null +++ b/include/utils/IServiceManager.h @@ -0,0 +1,98 @@ +/* + * 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_ISERVICE_MANAGER_H +#define ANDROID_ISERVICE_MANAGER_H + +#include +#include +#include +#include + +namespace android { + +// ---------------------------------------------------------------------- + +class IServiceManager : public IInterface +{ +public: + DECLARE_META_INTERFACE(ServiceManager); + + /** + * Retrieve an existing service, blocking for a few seconds + * if it doesn't yet exist. + */ + virtual sp getService( const String16& name) const = 0; + + /** + * Retrieve an existing service, non-blocking. + */ + virtual sp checkService( const String16& name) const = 0; + + /** + * Register a service. + */ + virtual status_t addService( const String16& name, + const sp& service) = 0; + + /** + * Return list of all existing services. + */ + virtual Vector listServices() = 0; + + enum { + GET_SERVICE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, + CHECK_SERVICE_TRANSACTION, + ADD_SERVICE_TRANSACTION, + LIST_SERVICES_TRANSACTION, + }; +}; + +sp defaultServiceManager(); + +template +status_t getService(const String16& name, sp* outService) +{ + const sp sm = defaultServiceManager(); + if (sm != NULL) { + *outService = interface_cast(sm->getService(name)); + if ((*outService) != NULL) return NO_ERROR; + } + return NAME_NOT_FOUND; +} + +bool checkCallingPermission(const String16& permission); +bool checkCallingPermission(const String16& permission, + int32_t* outPid, int32_t* outUid); + +// ---------------------------------------------------------------------- + +class BnServiceManager : public BnInterface +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +// ---------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_ISERVICE_MANAGER_H + diff --git a/include/utils/KeyedVector.h b/include/utils/KeyedVector.h new file mode 100644 index 000000000..f4513ee20 --- /dev/null +++ b/include/utils/KeyedVector.h @@ -0,0 +1,201 @@ +/* + * 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 +#include +#include + +#include +#include +#include + +// --------------------------------------------------------------------------- + +namespace android { + +template +class KeyedVector +{ +public: + typedef KEY key_type; + typedef VALUE value_type; + + inline KeyedVector(); + + /* + * empty the vector + */ + + inline void clear() { mVector.clear(); } + + /*! + * vector stats + */ + + //! returns number of items in the vector + inline size_t size() const { return mVector.size(); } + //! returns wether or not the vector is empty + inline bool isEmpty() const { return mVector.isEmpty(); } + //! returns how many items can be stored without reallocating the backing store + inline size_t capacity() const { return mVector.capacity(); } + //! setst the capacity. capacity can never be reduced less than size() + inline ssize_t setCapacity(size_t size) { return mVector.setCapacity(size); } + + /*! + * accessors + */ + const VALUE& valueFor(const KEY& key) const; + const VALUE& valueAt(size_t index) const; + const KEY& keyAt(size_t index) const; + ssize_t indexOfKey(const KEY& key) const; + + /*! + * modifing the array + */ + + VALUE& editValueFor(const KEY& key); + VALUE& editValueAt(size_t index); + + /*! + * add/insert/replace items + */ + + ssize_t add(const KEY& key, const VALUE& item); + ssize_t replaceValueFor(const KEY& key, const VALUE& item); + ssize_t replaceValueAt(size_t index, const VALUE& item); + + /*! + * remove items + */ + + ssize_t removeItem(const KEY& key); + ssize_t removeItemsAt(size_t index, size_t count = 1); + +private: + SortedVector< key_value_pair_t > mVector; +}; + +// --------------------------------------------------------------------------- + +/** + * Variation of KeyedVector that holds a default value to return when + * valueFor() is called with a key that doesn't exist. + */ +template +class DefaultKeyedVector : public KeyedVector +{ +public: + inline DefaultKeyedVector(const VALUE& defValue = VALUE()); + const VALUE& valueFor(const KEY& key) const; + +private: + VALUE mDefault; +}; + +// --------------------------------------------------------------------------- + +template inline +KeyedVector::KeyedVector() +{ +} + +template inline +ssize_t KeyedVector::indexOfKey(const KEY& key) const { + return mVector.indexOf( key_value_pair_t(key) ); +} + +template inline +const VALUE& KeyedVector::valueFor(const KEY& key) const { + ssize_t i = indexOfKey(key); + assert(i>=0); + return mVector.itemAt(i).value; +} + +template inline +const VALUE& KeyedVector::valueAt(size_t index) const { + return mVector.itemAt(index).value; +} + +template inline +const KEY& KeyedVector::keyAt(size_t index) const { + return mVector.itemAt(index).key; +} + +template inline +VALUE& KeyedVector::editValueFor(const KEY& key) { + ssize_t i = indexOfKey(key); + assert(i>=0); + return mVector.editItemAt(i).value; +} + +template inline +VALUE& KeyedVector::editValueAt(size_t index) { + return mVector.editItemAt(index).value; +} + +template inline +ssize_t KeyedVector::add(const KEY& key, const VALUE& value) { + return mVector.add( key_value_pair_t(key, value) ); +} + +template inline +ssize_t KeyedVector::replaceValueFor(const KEY& key, const VALUE& value) { + key_value_pair_t pair(key, value); + mVector.remove(pair); + return mVector.add(pair); +} + +template inline +ssize_t KeyedVector::replaceValueAt(size_t index, const VALUE& item) { + if (index inline +ssize_t KeyedVector::removeItem(const KEY& key) { + return mVector.remove(key_value_pair_t(key)); +} + +template inline +ssize_t KeyedVector::removeItemsAt(size_t index, size_t count) { + return mVector.removeItemsAt(index, count); +} + +// --------------------------------------------------------------------------- + +template inline +DefaultKeyedVector::DefaultKeyedVector(const VALUE& defValue) + : mDefault(defValue) +{ +} + +template inline +const VALUE& DefaultKeyedVector::valueFor(const KEY& key) const { + ssize_t i = indexOfKey(key); + return i >= 0 ? KeyedVector::valueAt(i) : mDefault; +} + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_KEYED_VECTOR_H diff --git a/include/utils/List.h b/include/utils/List.h new file mode 100644 index 000000000..1a6be9ac9 --- /dev/null +++ b/include/utils/List.h @@ -0,0 +1,280 @@ +/* + * 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". Do not use classes +// starting with "_" directly. +// +#ifndef _LIBS_UTILS_LIST_H +#define _LIBS_UTILS_LIST_H + +namespace android { + +/* + * One element in the list. + */ +template class _ListNode { +public: + typedef _ListNode _Node; + + _ListNode(const T& val) : mVal(val) {} + ~_ListNode(void) {} + + T& getRef(void) { return mVal; } + void setVal(const T& val) { mVal = val; } + + _Node* getPrev(void) const { return mpPrev; } + void setPrev(_Node* ptr) { mpPrev = ptr; } + _Node* getNext(void) const { return mpNext; } + void setNext(_Node* ptr) { mpNext = ptr; } + +private: + T mVal; + _Node* mpPrev; + _Node* mpNext; +}; + +/* + * Iterator for walking through the list. + */ +template class _ListIterator { +public: + typedef _ListIterator _Iter; + typedef _ListNode _Node; + + _ListIterator(void) {} + _ListIterator(_Node* ptr) : mpNode(ptr) {} + ~_ListIterator(void) {} + + /* + * Dereference operator. Used to get at the juicy insides. + */ + Tref operator*() const { return mpNode->getRef(); } + + /* + * Iterator comparison. + */ + bool operator==(const _Iter& right) const { return mpNode == right.mpNode; } + bool operator!=(const _Iter& right) const { return mpNode != right.mpNode; } + + /* + * Incr/decr, used to move through the list. + */ + _Iter& operator++(void) { // pre-increment + mpNode = mpNode->getNext(); + return *this; + } + _Iter operator++(int) { // post-increment + _Iter tmp = *this; + ++*this; + return tmp; + } + _Iter& operator--(void) { // pre-increment + mpNode = mpNode->getPrev(); + return *this; + } + _Iter operator--(int) { // post-increment + _Iter tmp = *this; + --*this; + return tmp; + } + + _Node* getNode(void) const { return mpNode; } + +private: + _Node* mpNode; +}; + + +/* + * Doubly-linked list. Instantiate with "List myList". + * + * Objects added to the list are copied using the assignment operator, + * so this must be defined. + */ +template class List { +public: + typedef _ListNode _Node; + + List(void) { + prep(); + } + List(const List& src) { // copy-constructor + prep(); + insert(begin(), src.begin(), src.end()); + } + virtual ~List(void) { + clear(); + delete[] (unsigned char*) mpMiddle; + } + + typedef _ListIterator iterator; + typedef _ListIterator const_iterator; + + List& operator=(const List& right); + + /* returns true if the list is empty */ + bool empty(void) const { return mpMiddle->getNext() == mpMiddle; } + + /* return #of elements in list */ + unsigned int size(void) const { + return distance(begin(), end()); + } + + /* + * Return the first element or one past the last element. The + * _ListNode* we're returning is converted to an "iterator" by a + * constructor in _ListIterator. + */ + iterator begin() { return mpMiddle->getNext(); } + const_iterator begin() const { return mpMiddle->getNext(); } + iterator end() { return mpMiddle; } + const_iterator end() const { return 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 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 pNext; + } + + /* remove a range of elements */ + iterator erase(iterator first, iterator last) { + while (first != last) + erase(first++); // don't erase than incr later! + return last; + } + + /* remove all contents of the list */ + void clear(void) { + _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. + * + * (This is actually a generic iterator function. It should be part + * of some other class, possibly an iterator base class. It needs to + * know the difference between a list, which has to march through, + * and a vector, which can just do pointer math.) + */ + unsigned int distance(iterator first, iterator last) { + unsigned int count = 0; + while (first != last) { + ++first; + ++count; + } + return count; + } + unsigned int distance(const_iterator first, const_iterator last) const { + unsigned int count = 0; + while (first != last) { + ++first; + ++count; + } + return count; + } + +private: + /* + * I want a _ListNode 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(void) { + 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 +List& List::operator=(const List& 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 000000000..3c6cc8bdc --- /dev/null +++ b/include/utils/Log.h @@ -0,0 +1,33 @@ +/* + * 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 + +#endif // _LIBS_UTILS_LOG_H diff --git a/include/utils/LogSocket.h b/include/utils/LogSocket.h new file mode 100644 index 000000000..01fbfb50e --- /dev/null +++ b/include/utils/LogSocket.h @@ -0,0 +1,20 @@ +/* utils/LogSocket.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_LOGSOCKET_H +#define _UTILS_LOGSOCKET_H + +#define SOCKET_CLOSE_LOCAL 0 + +void add_send_stats(int fd, int send); +void add_recv_stats(int fd, int recv); +void log_socket_close(int fd, short reason); +void log_socket_connect(int fd, unsigned int ip, unsigned short port); + +#endif /* _UTILS_LOGSOCKET_H */ diff --git a/include/utils/MemoryBase.h b/include/utils/MemoryBase.h new file mode 100644 index 000000000..eb5a9d275 --- /dev/null +++ b/include/utils/MemoryBase.h @@ -0,0 +1,51 @@ +/* + * 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_MEMORY_BASE_H +#define ANDROID_MEMORY_BASE_H + +#include +#include + +#include + + +namespace android { + +// --------------------------------------------------------------------------- + +class MemoryBase : public BnMemory +{ +public: + MemoryBase(const sp& heap, ssize_t offset, size_t size); + virtual ~MemoryBase(); + virtual sp getMemory(ssize_t* offset, size_t* size) const; + +protected: + size_t getSize() const { return mSize; } + ssize_t getOffset() const { return mOffset; } + const sp& getHeap() const { return mHeap; } + +private: + size_t mSize; + ssize_t mOffset; + sp mHeap; +}; + +// --------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_MEMORY_BASE_H diff --git a/include/utils/MemoryDealer.h b/include/utils/MemoryDealer.h new file mode 100644 index 000000000..454b6270e --- /dev/null +++ b/include/utils/MemoryDealer.h @@ -0,0 +1,238 @@ +/* + * 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_MEMORY_DEALER_H +#define ANDROID_MEMORY_DEALER_H + + +#include +#include + +#include +#include +#include + +namespace android { +// ---------------------------------------------------------------------------- +class String8; + +/* + * interface for implementing a "heap". A heap basically provides + * the IMemoryHeap interface for cross-process sharing and the + * ability to map/unmap pages within the heap. + */ +class HeapInterface : public virtual BnMemoryHeap +{ +public: + // all values must be page-aligned + virtual sp mapMemory(size_t offset, size_t size) = 0; +}; + +// ---------------------------------------------------------------------------- + +/* + * interface for implementing an allocator. An allocator provides + * methods for allocating and freeing memory blocks and dumping + * its state. + */ +class AllocatorInterface : public RefBase +{ +public: + enum { + PAGE_ALIGNED = 0x00000001 + }; + + virtual size_t allocate(size_t size, uint32_t flags = 0) = 0; + virtual status_t deallocate(size_t offset) = 0; + virtual size_t size() const = 0; + virtual void dump(const char* what, uint32_t flags = 0) const = 0; + virtual void dump(String8& res, + const char* what, uint32_t flags = 0) const = 0; +}; + +// ---------------------------------------------------------------------------- + +/* + * concrete implementation of HeapInterface on top of mmap() + */ +class SharedHeap : public HeapInterface, public MemoryHeapBase +{ +public: + SharedHeap(size_t size, uint32_t flags = 0, char const * name = NULL); + virtual ~SharedHeap(); + virtual sp mapMemory(size_t offset, size_t size); +}; + +// ---------------------------------------------------------------------------- + +/* + * A simple templatized doubly linked-list implementation + */ + +template +class LinkedList +{ + NODE* mFirst; + NODE* mLast; + +public: + LinkedList() : mFirst(0), mLast(0) { } + bool isEmpty() const { return mFirst == 0; } + NODE const* head() const { return mFirst; } + NODE* head() { return mFirst; } + NODE const* tail() const { return mLast; } + NODE* tail() { return mLast; } + + void insertAfter(NODE* node, NODE* newNode) { + newNode->prev = node; + newNode->next = node->next; + if (node->next == 0) mLast = newNode; + else node->next->prev = newNode; + node->next = newNode; + } + + void insertBefore(NODE* node, NODE* newNode) { + newNode->prev = node->prev; + newNode->next = node; + if (node->prev == 0) mFirst = newNode; + else node->prev->next = newNode; + node->prev = newNode; + } + + void insertHead(NODE* newNode) { + if (mFirst == 0) { + mFirst = mLast = newNode; + newNode->prev = newNode->next = 0; + } else { + insertBefore(mFirst, newNode); + } + } + + void insertTail(NODE* newNode) { + if (mLast == 0) insertBeginning(newNode); + else insertAfter(mLast, newNode); + } + + NODE* remove(NODE* node) { + if (node->prev == 0) mFirst = node->next; + else node->prev->next = node->next; + if (node->next == 0) mLast = node->prev; + else node->next->prev = node->prev; + return node; + } +}; + + +/* + * concrete implementation of AllocatorInterface using a simple + * best-fit allocation scheme + */ +class SimpleBestFitAllocator : public AllocatorInterface +{ +public: + + SimpleBestFitAllocator(size_t size); + virtual ~SimpleBestFitAllocator(); + + virtual size_t allocate(size_t size, uint32_t flags = 0); + virtual status_t deallocate(size_t offset); + virtual size_t size() const; + virtual void dump(const char* what, uint32_t flags = 0) const; + virtual void dump(String8& res, + const char* what, uint32_t flags = 0) const; + +private: + + struct chunk_t { + chunk_t(size_t start, size_t size) + : start(start), size(size), free(1), prev(0), next(0) { + } + size_t start; + size_t size : 28; + int free : 4; + mutable chunk_t* prev; + mutable chunk_t* next; + }; + + ssize_t alloc(size_t size, uint32_t flags); + chunk_t* dealloc(size_t start); + void dump_l(const char* what, uint32_t flags = 0) const; + void dump_l(String8& res, const char* what, uint32_t flags = 0) const; + + static const int kMemoryAlign; + mutable Mutex mLock; + LinkedList mList; + size_t mHeapSize; +}; + +// ---------------------------------------------------------------------------- + +class MemoryDealer : public RefBase +{ +public: + + enum { + READ_ONLY = MemoryHeapBase::READ_ONLY, + PAGE_ALIGNED = AllocatorInterface::PAGE_ALIGNED + }; + + // creates a memory dealer with the SharedHeap and SimpleBestFitAllocator + MemoryDealer(size_t size, uint32_t flags = 0, const char* name = 0); + + // provide a custom heap but use the SimpleBestFitAllocator + MemoryDealer(const sp& heap); + + // provide both custom heap and allocotar + MemoryDealer( + const sp& heap, + const sp& allocator); + + virtual ~MemoryDealer(); + + virtual sp allocate(size_t size, uint32_t flags = 0); + virtual void deallocate(size_t offset); + virtual void dump(const char* what, uint32_t flags = 0) const; + + + sp getMemoryHeap() const { return heap(); } + sp getAllocator() const { return allocator(); } + +private: + const sp& heap() const; + const sp& allocator() const; + + class Allocation : public BnMemory { + public: + Allocation(const sp& dealer, + ssize_t offset, size_t size, const sp& memory); + virtual ~Allocation(); + virtual sp getMemory(ssize_t* offset, size_t* size) const; + private: + sp mDealer; + ssize_t mOffset; + size_t mSize; + sp mMemory; + }; + + sp mHeap; + sp mAllocator; +}; + + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_MEMORY_DEALER_H diff --git a/include/utils/MemoryHeapBase.h b/include/utils/MemoryHeapBase.h new file mode 100644 index 000000000..574acf4f9 --- /dev/null +++ b/include/utils/MemoryHeapBase.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_MEMORY_HEAP_BASE_H +#define ANDROID_MEMORY_HEAP_BASE_H + +#include +#include + +#include + + +namespace android { + +// --------------------------------------------------------------------------- + +class MemoryHeapBase : public virtual BnMemoryHeap +{ +public: + enum { + READ_ONLY = IMemoryHeap::READ_ONLY, + MAP_ONCE = IMemoryHeap::MAP_ONCE, + // memory won't be mapped locally, but will be mapped in the remote + // process. + DONT_MAP_LOCALLY = 0x00000100 + }; + + /* + * maps the memory referenced by fd. but DOESN'T take ownership + * of the filedescriptor (it makes a copy with dup() + */ + MemoryHeapBase(int fd, size_t size, uint32_t flags = 0); + + /* + * maps memory from the given device + */ + MemoryHeapBase(const char* device, size_t size = 0, uint32_t flags = 0); + + /* + * maps memory from ashmem, with the given name for debugging + */ + MemoryHeapBase(size_t size, uint32_t flags = 0, char const* name = NULL); + + virtual ~MemoryHeapBase(); + + /* implement IMemoryHeap interface */ + virtual int getHeapID() const; + virtual void* getBase() const; + virtual size_t getSize() const; + virtual uint32_t getFlags() const; + + const char* getDevice() const; + + /* this closes this heap -- use carefully */ + void dispose(); + + /* this is only needed as a workaround, use only if you know + * what you are doing */ + status_t setDevice(const char* device) { + if (mDevice == 0) + mDevice = device; + return mDevice ? NO_ERROR : ALREADY_EXISTS; + } + +protected: + MemoryHeapBase(); + // init() takes ownership of fd + status_t init(int fd, void *base, int size, + int flags = 0, const char* device = NULL); + +private: + status_t mapfd(int fd, size_t size); + + int mFD; + size_t mSize; + void* mBase; + uint32_t mFlags; + const char* mDevice; + bool mNeedUnmap; +}; + +// --------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_MEMORY_HEAP_BASE_H diff --git a/include/utils/MemoryHeapPmem.h b/include/utils/MemoryHeapPmem.h new file mode 100644 index 000000000..60335adae --- /dev/null +++ b/include/utils/MemoryHeapPmem.h @@ -0,0 +1,80 @@ +/* + * 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_MEMORY_HEAP_PMEM_H +#define ANDROID_MEMORY_HEAP_PMEM_H + +#include +#include + +#include +#include +#include +#include + +namespace android { + +class MemoryHeapBase; + +// --------------------------------------------------------------------------- + +class MemoryHeapPmem : public HeapInterface, public MemoryHeapBase +{ +public: + class MemoryPmem : public BnMemory { + public: + MemoryPmem(const sp& heap); + ~MemoryPmem(); + protected: + const sp& getHeap() const { return mClientHeap; } + private: + friend class MemoryHeapPmem; + virtual void revoke() = 0; + sp mClientHeap; + }; + + MemoryHeapPmem(const sp& pmemHeap, + uint32_t flags = IMemoryHeap::MAP_ONCE); + ~MemoryHeapPmem(); + + /* HeapInterface additions */ + virtual sp mapMemory(size_t offset, size_t size); + + /* make the whole heap visible (you know who you are) */ + virtual status_t slap(); + + /* hide (revoke) the whole heap (the client will see the garbage page) */ + virtual status_t unslap(); + + /* revoke all allocations made by this heap */ + virtual void revoke(); + +private: + /* use this to create your own IMemory for mapMemory */ + virtual sp createMemory(size_t offset, size_t size); + void remove(const wp& memory); + +private: + sp mParentHeap; + mutable Mutex mLock; + SortedVector< wp > mAllocations; +}; + + +// --------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_MEMORY_HEAP_PMEM_H diff --git a/include/utils/Parcel.h b/include/utils/Parcel.h new file mode 100644 index 000000000..9087c4465 --- /dev/null +++ b/include/utils/Parcel.h @@ -0,0 +1,209 @@ +/* + * 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_PARCEL_H +#define ANDROID_PARCEL_H + +#include +#include +#include +#include +#include + +// --------------------------------------------------------------------------- +namespace android { + +class IBinder; +class ProcessState; +class String8; +class TextOutput; + +struct flat_binder_object; // defined in support_p/binder_module.h + +class Parcel +{ +public: + Parcel(); + ~Parcel(); + + const uint8_t* data() const; + size_t dataSize() const; + size_t dataAvail() const; + size_t dataPosition() const; + size_t dataCapacity() const; + + status_t setDataSize(size_t size); + void setDataPosition(size_t pos) const; + status_t setDataCapacity(size_t size); + + status_t setData(const uint8_t* buffer, size_t len); + + status_t appendFrom(Parcel *parcel, size_t start, size_t len); + + bool hasFileDescriptors() const; + + status_t writeInterfaceToken(const String16& interface); + bool enforceInterface(const String16& interface) const; + + void freeData(); + + const size_t* objects() const; + size_t objectsCount() const; + + status_t errorCheck() const; + void setError(status_t err); + + status_t write(const void* data, size_t len); + void* writeInplace(size_t len); + status_t writeUnpadded(const void* data, size_t len); + status_t writeInt32(int32_t val); + status_t writeInt64(int64_t val); + status_t writeFloat(float val); + status_t writeDouble(double val); + status_t writeCString(const char* str); + status_t writeString8(const String8& str); + status_t writeString16(const String16& str); + status_t writeString16(const char16_t* str, size_t len); + status_t writeStrongBinder(const sp& val); + status_t writeWeakBinder(const wp& val); + + // doesn't take ownership of the native_handle + status_t writeNativeHandle(const native_handle& handle); + + // Place a file descriptor into the parcel. The given fd must remain + // valid for the lifetime of the parcel. + status_t writeFileDescriptor(int fd); + + // Place a file descriptor into the parcel. A dup of the fd is made, which + // will be closed once the parcel is destroyed. + status_t writeDupFileDescriptor(int fd); + + status_t writeObject(const flat_binder_object& val, bool nullMetaData); + + void remove(size_t start, size_t amt); + + status_t read(void* outData, size_t len) const; + const void* readInplace(size_t len) const; + int32_t readInt32() const; + status_t readInt32(int32_t *pArg) const; + int64_t readInt64() const; + status_t readInt64(int64_t *pArg) const; + float readFloat() const; + status_t readFloat(float *pArg) const; + double readDouble() const; + status_t readDouble(double *pArg) const; + + const char* readCString() const; + String8 readString8() const; + String16 readString16() const; + const char16_t* readString16Inplace(size_t* outLen) const; + sp readStrongBinder() const; + wp readWeakBinder() const; + + + // if alloc is NULL, native_handle is allocated with malloc(), otherwise + // alloc is used. If the function fails, the effects of alloc() must be + // reverted by the caller. + native_handle* readNativeHandle( + native_handle* (*alloc)(void* cookie, int numFds, int ints), + void* cookie) const; + + + // Retrieve a file descriptor from the parcel. This returns the raw fd + // in the parcel, which you do not own -- use dup() to get your own copy. + int readFileDescriptor() const; + + const flat_binder_object* readObject(bool nullMetaData) const; + + // Explicitly close all file descriptors in the parcel. + void closeFileDescriptors(); + + typedef void (*release_func)(Parcel* parcel, + const uint8_t* data, size_t dataSize, + const size_t* objects, size_t objectsSize, + void* cookie); + + const uint8_t* ipcData() const; + size_t ipcDataSize() const; + const size_t* ipcObjects() const; + size_t ipcObjectsCount() const; + void ipcSetDataReference(const uint8_t* data, size_t dataSize, + const size_t* objects, size_t objectsCount, + release_func relFunc, void* relCookie); + + void print(TextOutput& to, uint32_t flags = 0) const; + +private: + Parcel(const Parcel& o); + Parcel& operator=(const Parcel& o); + + status_t finishWrite(size_t len); + void releaseObjects(); + void acquireObjects(); + status_t growData(size_t len); + status_t restartWrite(size_t desired); + status_t continueWrite(size_t desired); + void freeDataNoInit(); + void initState(); + void scanForFds() const; + + status_t mError; + uint8_t* mData; + size_t mDataSize; + size_t mDataCapacity; + mutable size_t mDataPos; + size_t* mObjects; + size_t mObjectsSize; + size_t mObjectsCapacity; + mutable size_t mNextObjectHint; + + mutable bool mFdsKnown; + mutable bool mHasFds; + + release_func mOwner; + void* mOwnerCookie; +}; + +// --------------------------------------------------------------------------- + +inline TextOutput& operator<<(TextOutput& to, const Parcel& parcel) +{ + parcel.print(to); + return to; +} + +// --------------------------------------------------------------------------- + +// Generic acquire and release of objects. +void acquire_object(const sp& proc, + const flat_binder_object& obj, const void* who); +void release_object(const sp& proc, + const flat_binder_object& obj, const void* who); + +void flatten_binder(const sp& proc, + const sp& binder, flat_binder_object* out); +void flatten_binder(const sp& proc, + const wp& binder, flat_binder_object* out); +status_t unflatten_binder(const sp& proc, + const flat_binder_object& flat, sp* out); +status_t unflatten_binder(const sp& proc, + const flat_binder_object& flat, wp* out); + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_PARCEL_H diff --git a/include/utils/Pipe.h b/include/utils/Pipe.h new file mode 100644 index 000000000..6404168a2 --- /dev/null +++ b/include/utils/Pipe.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2005 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// FIFO I/O. +// +#ifndef _LIBS_UTILS_PIPE_H +#define _LIBS_UTILS_PIPE_H + +#ifdef HAVE_ANDROID_OS +#error DO NOT USE THIS FILE IN THE DEVICE BUILD +#endif + +namespace android { + +/* + * Simple anonymous unidirectional pipe. + * + * The primary goal is to create an implementation with minimal overhead + * under Linux. Making Windows, Mac OS X, and Linux all work the same way + * is a secondary goal. Part of this goal is to have something that can + * be fed to a select() call, so that the application can sleep in the + * kernel until something interesting happens. + */ +class Pipe { +public: + Pipe(void); + virtual ~Pipe(void); + + /* Create the pipe */ + bool create(void); + + /* Create a read-only pipe, using the supplied handle as read handle */ + bool createReader(unsigned long handle); + /* Create a write-only pipe, using the supplied handle as write handle */ + bool createWriter(unsigned long handle); + + /* Is this object ready to go? */ + bool isCreated(void); + + /* + * Read "count" bytes from the pipe. Returns the amount of data read, + * or 0 if no data available and we're non-blocking. + * Returns -1 on error. + */ + int read(void* buf, int count); + + /* + * Write "count" bytes into the pipe. Returns number of bytes written, + * or 0 if there's no room for more data and we're non-blocking. + * Returns -1 on error. + */ + int write(const void* buf, int count); + + /* Returns "true" if data is available to read */ + bool readReady(void); + + /* Enable or disable non-blocking I/O for reads */ + bool setReadNonBlocking(bool val); + /* Enable or disable non-blocking I/O for writes. Only works on Linux. */ + bool setWriteNonBlocking(bool val); + + /* + * Get the handle. Only useful in some platform-specific situations. + */ + unsigned long getReadHandle(void); + unsigned long getWriteHandle(void); + + /* + * Modify inheritance, i.e. whether or not a child process will get + * copies of the descriptors. Systems with fork+exec allow us to close + * the descriptors before launching the child process, but Win32 + * doesn't allow it. + */ + bool disallowReadInherit(void); + bool disallowWriteInherit(void); + + /* + * Close one side or the other. Useful in the parent after launching + * a child process. + */ + bool closeRead(void); + bool closeWrite(void); + +private: + bool mReadNonBlocking; + bool mWriteNonBlocking; + + unsigned long mReadHandle; + unsigned long mWriteHandle; +}; + +}; // android + +#endif // _LIBS_UTILS_PIPE_H diff --git a/include/utils/ProcessState.h b/include/utils/ProcessState.h new file mode 100644 index 000000000..39584f42c --- /dev/null +++ b/include/utils/ProcessState.h @@ -0,0 +1,117 @@ +/* + * 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_PROCESS_STATE_H +#define ANDROID_PROCESS_STATE_H + +#include +#include +#include +#include + +#include + +// --------------------------------------------------------------------------- +namespace android { + +// Global variables +extern int mArgC; +extern const char* const* mArgV; +extern int mArgLen; + +class IPCThreadState; + +class ProcessState : public virtual RefBase +{ +public: + static sp self(); + + static void setSingleProcess(bool singleProcess); + + void setContextObject(const sp& object); + sp getContextObject(const sp& caller); + + void setContextObject(const sp& object, + const String16& name); + sp getContextObject(const String16& name, + const sp& caller); + + bool supportsProcesses() const; + + void startThreadPool(); + + typedef bool (*context_check_func)(const String16& name, + const sp& caller, + void* userData); + + bool isContextManager(void) const; + bool becomeContextManager( + context_check_func checkFunc, + void* userData); + + sp getStrongProxyForHandle(int32_t handle); + wp getWeakProxyForHandle(int32_t handle); + void expungeHandle(int32_t handle, IBinder* binder); + + void setArgs(int argc, const char* const argv[]); + int getArgC() const; + const char* const* getArgV() const; + + void setArgV0(const char* txt); + + void spawnPooledThread(bool isMain); + +private: + friend class IPCThreadState; + + ProcessState(); + ~ProcessState(); + + ProcessState(const ProcessState& o); + ProcessState& operator=(const ProcessState& o); + + struct handle_entry { + IBinder* binder; + RefBase::weakref_type* refs; + }; + + handle_entry* lookupHandleLocked(int32_t handle); + + int mDriverFD; + void* mVMStart; + + mutable Mutex mLock; // protects everything below. + + VectormHandleToObject; + + bool mManagesContexts; + context_check_func mBinderContextCheckFunc; + void* mBinderContextUserData; + + KeyedVector > + mContexts; + + + String8 mRootDir; + bool mThreadPoolStarted; + volatile int32_t mThreadPoolSeq; +}; + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_PROCESS_STATE_H diff --git a/include/utils/RefBase.h b/include/utils/RefBase.h new file mode 100644 index 000000000..cbda0fd80 --- /dev/null +++ b/include/utils/RefBase.h @@ -0,0 +1,550 @@ +/* + * 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 +#include + +#include +#include +#include + +// --------------------------------------------------------------------------- +namespace android { + +template class wp; + +// --------------------------------------------------------------------------- + +#define COMPARE(_op_) \ +inline bool operator _op_ (const sp& o) const { \ + return m_ptr _op_ o.m_ptr; \ +} \ +inline bool operator _op_ (const wp& o) const { \ + return m_ptr _op_ o.m_ptr; \ +} \ +inline bool operator _op_ (const T* o) const { \ + return m_ptr _op_ o; \ +} \ +template \ +inline bool operator _op_ (const sp& o) const { \ + return m_ptr _op_ o.m_ptr; \ +} \ +template \ +inline bool operator _op_ (const wp& o) const { \ + return m_ptr _op_ o.m_ptr; \ +} \ +template \ +inline bool operator _op_ (const U* o) const { \ + return m_ptr _op_ o; \ +} + +// --------------------------------------------------------------------------- + +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); + + bool attemptIncStrong(const void* id); + + //! This is only safe if you have set OBJECT_LIFETIME_FOREVER. + 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); + } + +protected: + RefBase(); + virtual ~RefBase(); + + //! Flags for extendObjectLifetime() + enum { + OBJECT_LIFETIME_WEAK = 0x0001, + OBJECT_LIFETIME_FOREVER = 0x0003 + }; + + 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); + + weakref_impl* const mRefs; +}; + +// --------------------------------------------------------------------------- + +template +class LightRefBase +{ +public: + inline LightRefBase() : mCount(0) { } + inline void incStrong(const void* id) const { + android_atomic_inc(&mCount); + } + inline void decStrong(const void* id) const { + if (android_atomic_dec(&mCount) == 1) { + delete static_cast(this); + } + } + +protected: + inline ~LightRefBase() { } + +private: + mutable volatile int32_t mCount; +}; + +// --------------------------------------------------------------------------- + +template +class sp +{ +public: + typedef typename RefBase::weakref_type weakref_type; + + inline sp() : m_ptr(0) { } + + sp(T* other); + sp(const sp& other); + template sp(U* other); + template sp(const sp& other); + + ~sp(); + + // Assignment + + sp& operator = (T* other); + sp& operator = (const sp& other); + + template sp& operator = (const sp& other); + template 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 friend class sp; + template friend class wp; + + // Optimization for wp::promote(). + sp(T* p, weakref_type* refs); + + T* m_ptr; +}; + +template +TextOutput& operator<<(TextOutput& to, const sp& val); + +// --------------------------------------------------------------------------- + +template +class wp +{ +public: + typedef typename RefBase::weakref_type weakref_type; + + inline wp() : m_ptr(0) { } + + wp(T* other); + wp(const wp& other); + wp(const sp& other); + template wp(U* other); + template wp(const sp& other); + template wp(const wp& other); + + ~wp(); + + // Assignment + + wp& operator = (T* other); + wp& operator = (const wp& other); + wp& operator = (const sp& other); + + template wp& operator = (U* other); + template wp& operator = (const wp& other); + template wp& operator = (const sp& other); + + void set_object_and_refs(T* other, weakref_type* refs); + + // promotion to sp + + sp 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(==) + COMPARE(!=) + COMPARE(>) + COMPARE(<) + COMPARE(<=) + COMPARE(>=) + +private: + template friend class sp; + template friend class wp; + + T* m_ptr; + weakref_type* m_refs; +}; + +template +TextOutput& operator<<(TextOutput& to, const wp& val); + +#undef COMPARE + +// --------------------------------------------------------------------------- +// No user serviceable parts below here. + +template +sp::sp(T* other) + : m_ptr(other) +{ + if (other) other->incStrong(this); +} + +template +sp::sp(const sp& other) + : m_ptr(other.m_ptr) +{ + if (m_ptr) m_ptr->incStrong(this); +} + +template template +sp::sp(U* other) : m_ptr(other) +{ + if (other) other->incStrong(this); +} + +template template +sp::sp(const sp& other) + : m_ptr(other.m_ptr) +{ + if (m_ptr) m_ptr->incStrong(this); +} + +template +sp::~sp() +{ + if (m_ptr) m_ptr->decStrong(this); +} + +template +sp& sp::operator = (const sp& other) { + if (other.m_ptr) other.m_ptr->incStrong(this); + if (m_ptr) m_ptr->decStrong(this); + m_ptr = other.m_ptr; + return *this; +} + +template +sp& sp::operator = (T* other) +{ + if (other) other->incStrong(this); + if (m_ptr) m_ptr->decStrong(this); + m_ptr = other; + return *this; +} + +template template +sp& sp::operator = (const sp& other) +{ + if (other.m_ptr) other.m_ptr->incStrong(this); + if (m_ptr) m_ptr->decStrong(this); + m_ptr = other.m_ptr; + return *this; +} + +template template +sp& sp::operator = (U* other) +{ + if (other) other->incStrong(this); + if (m_ptr) m_ptr->decStrong(this); + m_ptr = other; + return *this; +} + +template +void sp::force_set(T* other) +{ + other->forceIncStrong(this); + m_ptr = other; +} + +template +void sp::clear() +{ + if (m_ptr) { + m_ptr->decStrong(this); + m_ptr = 0; + } +} + +template +sp::sp(T* p, weakref_type* refs) + : m_ptr((p && refs->attemptIncStrong(this)) ? p : 0) +{ +} + +template +inline TextOutput& operator<<(TextOutput& to, const sp& val) +{ + to << "sp<>(" << val.get() << ")"; + return to; +} + +// --------------------------------------------------------------------------- + +template +wp::wp(T* other) + : m_ptr(other) +{ + if (other) m_refs = other->createWeak(this); +} + +template +wp::wp(const wp& other) + : m_ptr(other.m_ptr), m_refs(other.m_refs) +{ + if (m_ptr) m_refs->incWeak(this); +} + +template +wp::wp(const sp& other) + : m_ptr(other.m_ptr) +{ + if (m_ptr) { + m_refs = m_ptr->createWeak(this); + } +} + +template template +wp::wp(U* other) + : m_ptr(other) +{ + if (other) m_refs = other->createWeak(this); +} + +template template +wp::wp(const wp& other) + : m_ptr(other.m_ptr) +{ + if (m_ptr) { + m_refs = other.m_refs; + m_refs->incWeak(this); + } +} + +template template +wp::wp(const sp& other) + : m_ptr(other.m_ptr) +{ + if (m_ptr) { + m_refs = m_ptr->createWeak(this); + } +} + +template +wp::~wp() +{ + if (m_ptr) m_refs->decWeak(this); +} + +template +wp& wp::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 +wp& wp::operator = (const wp& other) +{ + if (other.m_ptr) other.m_refs->incWeak(this); + if (m_ptr) m_refs->decWeak(this); + m_ptr = other.m_ptr; + m_refs = other.m_refs; + return *this; +} + +template +wp& wp::operator = (const sp& other) +{ + weakref_type* newRefs = + other != NULL ? other->createWeak(this) : 0; + if (m_ptr) m_refs->decWeak(this); + m_ptr = other.get(); + m_refs = newRefs; + return *this; +} + +template template +wp& wp::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 template +wp& wp::operator = (const wp& other) +{ + if (other.m_ptr) other.m_refs->incWeak(this); + if (m_ptr) m_refs->decWeak(this); + m_ptr = other.m_ptr; + m_refs = other.m_refs; + return *this; +} + +template template +wp& wp::operator = (const sp& other) +{ + weakref_type* newRefs = + other != NULL ? other->createWeak(this) : 0; + if (m_ptr) m_refs->decWeak(this); + m_ptr = other.get(); + m_refs = newRefs; + return *this; +} + +template +void wp::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 +sp wp::promote() const +{ + return sp(m_ptr, m_refs); +} + +template +void wp::clear() +{ + if (m_ptr) { + m_refs->decWeak(this); + m_ptr = 0; + } +} + +template +inline TextOutput& operator<<(TextOutput& to, const wp& val) +{ + to << "wp<>(" << val.unsafe_get() << ")"; + return to; +} + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_REF_BASE_H diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h new file mode 100644 index 000000000..7d3fcf2a8 --- /dev/null +++ b/include/utils/ResourceTypes.h @@ -0,0 +1,1720 @@ +/* + * 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. + */ + +// +// Definitions of resource data structures. +// +#ifndef _LIBS_UTILS_RESOURCE_TYPES_H +#define _LIBS_UTILS_RESOURCE_TYPES_H + +#include +#include +#include +#include +#include + +#include + +#include +#include + +namespace android { + +/** ******************************************************************** + * PNG Extensions + * + * New private chunks that may be placed in PNG images. + * + *********************************************************************** */ + +/** + * This chunk specifies how to split an image into segments for + * scaling. + * + * There are J horizontal and K vertical segments. These segments divide + * the image into J*K regions as follows (where J=4 and K=3): + * + * F0 S0 F1 S1 + * +-----+----+------+-------+ + * S2| 0 | 1 | 2 | 3 | + * +-----+----+------+-------+ + * | | | | | + * | | | | | + * F2| 4 | 5 | 6 | 7 | + * | | | | | + * | | | | | + * +-----+----+------+-------+ + * S3| 8 | 9 | 10 | 11 | + * +-----+----+------+-------+ + * + * Each horizontal and vertical segment is considered to by either + * stretchable (marked by the Sx labels) or fixed (marked by the Fy + * labels), in the horizontal or vertical axis, respectively. In the + * above example, the first is horizontal segment (F0) is fixed, the + * next is stretchable and then they continue to alternate. Note that + * the segment list for each axis can begin or end with a stretchable + * or fixed segment. + * + * The relative sizes of the stretchy segments indicates the relative + * amount of stretchiness of the regions bordered by the segments. For + * example, regions 3, 7 and 11 above will take up more horizontal space + * than regions 1, 5 and 9 since the horizonal segment associated with + * the first set of regions is larger than the other set of regions. The + * ratios of the amount of horizontal (or vertical) space taken by any + * two stretchable slices is exactly the ratio of their corresponding + * segment lengths. + * + * xDivs and yDivs point to arrays of horizontal and vertical pixel + * indices. The first pair of Divs (in either array) indicate the + * starting and ending points of the first stretchable segment in that + * axis. The next pair specifies the next stretchable segment, etc. So + * in the above example xDiv[0] and xDiv[1] specify the horizontal + * coordinates for the regions labeled 1, 5 and 9. xDiv[2] and + * xDiv[3] specify the coordinates for regions 3, 7 and 11. Note that + * the leftmost slices always start at x=0 and the rightmost slices + * always end at the end of the image. So, for example, the regions 0, + * 4 and 8 (which are fixed along the X axis) start at x value 0 and + * go to xDiv[0] amd slices 2, 6 and 10 start at xDiv[1] and end at + * xDiv[2]. + * + * The array pointed to by the colors field lists contains hints for + * each of the regions. They are ordered according left-to-right and + * top-to-bottom as indicated above. For each segment that is a solid + * color the array entry will contain that color value; otherwise it + * will contain NO_COLOR. Segments that are completely transparent + * will always have the value TRANSPARENT_COLOR. + * + * The PNG chunk type is "npTc". + */ +struct Res_png_9patch +{ + Res_png_9patch() : wasDeserialized(false), xDivs(NULL), + yDivs(NULL), colors(NULL) { } + + int8_t wasDeserialized; + int8_t numXDivs; + int8_t numYDivs; + int8_t numColors; + + // These tell where the next section of a patch starts. + // For example, the first patch includes the pixels from + // 0 to xDivs[0]-1 and the second patch includes the pixels + // from xDivs[0] to xDivs[1]-1. + // Note: allocation/free of these pointers is left to the caller. + int32_t* xDivs; + int32_t* yDivs; + + int32_t paddingLeft, paddingRight; + int32_t paddingTop, paddingBottom; + + enum { + // The 9 patch segment is not a solid color. + NO_COLOR = 0x00000001, + + // The 9 patch segment is completely transparent. + TRANSPARENT_COLOR = 0x00000000 + }; + // Note: allocation/free of this pointer is left to the caller. + uint32_t* colors; + + // Convert data from device representation to PNG file representation. + void deviceToFile(); + // Convert data from PNG file representation to device representation. + void fileToDevice(); + // Serialize/Marshall the patch data into a newly malloc-ed block + void* serialize(); + // Serialize/Marshall the patch data + void serialize(void* outData); + // Deserialize/Unmarshall the patch data + static Res_png_9patch* deserialize(const void* data); + // Compute the size of the serialized data structure + size_t serializedSize(); +}; + +/** ******************************************************************** + * Base Types + * + * These are standard types that are shared between multiple specific + * resource types. + * + *********************************************************************** */ + +/** + * Header that appears at the front of every data chunk in a resource. + */ +struct ResChunk_header +{ + // Type identifier for this chunk. The meaning of this value depends + // on the containing chunk. + uint16_t type; + + // Size of the chunk header (in bytes). Adding this value to + // the address of the chunk allows you to find its associated data + // (if any). + uint16_t headerSize; + + // Total size of this chunk (in bytes). This is the chunkSize plus + // the size of any data associated with the chunk. Adding this value + // to the chunk allows you to completely skip its contents (including + // any child chunks). If this value is the same as chunkSize, there is + // no data associated with the chunk. + uint32_t size; +}; + +enum { + RES_NULL_TYPE = 0x0000, + RES_STRING_POOL_TYPE = 0x0001, + RES_TABLE_TYPE = 0x0002, + RES_XML_TYPE = 0x0003, + + // Chunk types in RES_XML_TYPE + RES_XML_FIRST_CHUNK_TYPE = 0x0100, + RES_XML_START_NAMESPACE_TYPE= 0x0100, + RES_XML_END_NAMESPACE_TYPE = 0x0101, + RES_XML_START_ELEMENT_TYPE = 0x0102, + RES_XML_END_ELEMENT_TYPE = 0x0103, + RES_XML_CDATA_TYPE = 0x0104, + RES_XML_LAST_CHUNK_TYPE = 0x017f, + // This contains a uint32_t array mapping strings in the string + // pool back to resource identifiers. It is optional. + RES_XML_RESOURCE_MAP_TYPE = 0x0180, + + // Chunk types in RES_TABLE_TYPE + RES_TABLE_PACKAGE_TYPE = 0x0200, + RES_TABLE_TYPE_TYPE = 0x0201, + RES_TABLE_TYPE_SPEC_TYPE = 0x0202 +}; + +/** + * Macros for building/splitting resource identifiers. + */ +#define Res_VALIDID(resid) (resid != 0) +#define Res_CHECKID(resid) ((resid&0xFFFF0000) != 0) +#define Res_MAKEID(package, type, entry) \ + (((package+1)<<24) | (((type+1)&0xFF)<<16) | (entry&0xFFFF)) +#define Res_GETPACKAGE(id) ((id>>24)-1) +#define Res_GETTYPE(id) (((id>>16)&0xFF)-1) +#define Res_GETENTRY(id) (id&0xFFFF) + +#define Res_INTERNALID(resid) ((resid&0xFFFF0000) != 0 && (resid&0xFF0000) == 0) +#define Res_MAKEINTERNAL(entry) (0x01000000 | (entry&0xFFFF)) +#define Res_MAKEARRAY(entry) (0x02000000 | (entry&0xFFFF)) + +#define Res_MAXPACKAGE 255 + +/** + * Representation of a value in a resource, supplying type + * information. + */ +struct Res_value +{ + // Number of bytes in this structure. + uint16_t size; + + // Always set to 0. + uint8_t res0; + + // Type of the data value. + enum { + // Contains no data. + TYPE_NULL = 0x00, + // The 'data' holds a ResTable_ref, a reference to another resource + // table entry. + TYPE_REFERENCE = 0x01, + // The 'data' holds an attribute resource identifier. + TYPE_ATTRIBUTE = 0x02, + // The 'data' holds an index into the containing resource table's + // global value string pool. + TYPE_STRING = 0x03, + // The 'data' holds a single-precision floating point number. + TYPE_FLOAT = 0x04, + // The 'data' holds a complex number encoding a dimension value, + // such as "100in". + TYPE_DIMENSION = 0x05, + // The 'data' holds a complex number encoding a fraction of a + // container. + TYPE_FRACTION = 0x06, + + // Beginning of integer flavors... + TYPE_FIRST_INT = 0x10, + + // The 'data' is a raw integer value of the form n..n. + TYPE_INT_DEC = 0x10, + // The 'data' is a raw integer value of the form 0xn..n. + TYPE_INT_HEX = 0x11, + // The 'data' is either 0 or 1, for input "false" or "true" respectively. + TYPE_INT_BOOLEAN = 0x12, + + // Beginning of color integer flavors... + TYPE_FIRST_COLOR_INT = 0x1c, + + // The 'data' is a raw integer value of the form #aarrggbb. + TYPE_INT_COLOR_ARGB8 = 0x1c, + // The 'data' is a raw integer value of the form #rrggbb. + TYPE_INT_COLOR_RGB8 = 0x1d, + // The 'data' is a raw integer value of the form #argb. + TYPE_INT_COLOR_ARGB4 = 0x1e, + // The 'data' is a raw integer value of the form #rgb. + TYPE_INT_COLOR_RGB4 = 0x1f, + + // ...end of integer flavors. + TYPE_LAST_COLOR_INT = 0x1f, + + // ...end of integer flavors. + TYPE_LAST_INT = 0x1f + }; + uint8_t dataType; + + // Structure of complex data values (TYPE_UNIT and TYPE_FRACTION) + enum { + // Where the unit type information is. This gives us 16 possible + // types, as defined below. + COMPLEX_UNIT_SHIFT = 0, + COMPLEX_UNIT_MASK = 0xf, + + // TYPE_DIMENSION: Value is raw pixels. + COMPLEX_UNIT_PX = 0, + // TYPE_DIMENSION: Value is Device Independent Pixels. + COMPLEX_UNIT_DIP = 1, + // TYPE_DIMENSION: Value is a Scaled device independent Pixels. + COMPLEX_UNIT_SP = 2, + // TYPE_DIMENSION: Value is in points. + COMPLEX_UNIT_PT = 3, + // TYPE_DIMENSION: Value is in inches. + COMPLEX_UNIT_IN = 4, + // TYPE_DIMENSION: Value is in millimeters. + COMPLEX_UNIT_MM = 5, + + // TYPE_FRACTION: A basic fraction of the overall size. + COMPLEX_UNIT_FRACTION = 0, + // TYPE_FRACTION: A fraction of the parent size. + COMPLEX_UNIT_FRACTION_PARENT = 1, + + // Where the radix information is, telling where the decimal place + // appears in the mantissa. This give us 4 possible fixed point + // representations as defined below. + COMPLEX_RADIX_SHIFT = 4, + COMPLEX_RADIX_MASK = 0x3, + + // The mantissa is an integral number -- i.e., 0xnnnnnn.0 + COMPLEX_RADIX_23p0 = 0, + // The mantissa magnitude is 16 bits -- i.e, 0xnnnn.nn + COMPLEX_RADIX_16p7 = 1, + // The mantissa magnitude is 8 bits -- i.e, 0xnn.nnnn + COMPLEX_RADIX_8p15 = 2, + // The mantissa magnitude is 0 bits -- i.e, 0x0.nnnnnn + COMPLEX_RADIX_0p23 = 3, + + // Where the actual value is. This gives us 23 bits of + // precision. The top bit is the sign. + COMPLEX_MANTISSA_SHIFT = 8, + COMPLEX_MANTISSA_MASK = 0xffffff + }; + + // The data for this item, as interpreted according to dataType. + uint32_t data; + + void copyFrom_dtoh(const Res_value& src); +}; + +/** + * This is a reference to a unique entry (a ResTable_entry structure) + * in a resource table. The value is structured as: 0xpptteeee, + * where pp is the package index, tt is the type index in that + * package, and eeee is the entry index in that type. The package + * and type values start at 1 for the first item, to help catch cases + * where they have not been supplied. + */ +struct ResTable_ref +{ + uint32_t ident; +}; + +/** + * Reference to a string in a string pool. + */ +struct ResStringPool_ref +{ + // Index into the string pool table (uint32_t-offset from the indices + // immediately after ResStringPool_header) at which to find the location + // of the string data in the pool. + uint32_t index; +}; + +/** ******************************************************************** + * String Pool + * + * A set of strings that can be references by others through a + * ResStringPool_ref. + * + *********************************************************************** */ + +/** + * Definition for a pool of strings. The data of this chunk is an + * array of uint32_t providing indices into the pool, relative to + * stringsStart. At stringsStart are all of the UTF-16 strings + * concatenated together; each starts with a uint16_t of the string's + * length and each ends with a 0x0000 terminator. If a string is > + * 32767 characters, the high bit of the length is set meaning to take + * those 15 bits as a high word and it will be followed by another + * uint16_t containing the low word. + * + * If styleCount is not zero, then immediately following the array of + * uint32_t indices into the string table is another array of indices + * into a style table starting at stylesStart. Each entry in the + * style table is an array of ResStringPool_span structures. + */ +struct ResStringPool_header +{ + struct ResChunk_header header; + + // Number of strings in this pool (number of uint32_t indices that follow + // in the data). + uint32_t stringCount; + + // Number of style span arrays in the pool (number of uint32_t indices + // follow the string indices). + uint32_t styleCount; + + // Flags. + enum { + // If set, the string index is sorted by the string values (based + // on strcmp16()). + SORTED_FLAG = 1<<0 + }; + uint32_t flags; + + // Index from header of the string data. + uint32_t stringsStart; + + // Index from header of the style data. + uint32_t stylesStart; +}; + +/** + * This structure defines a span of style information associated with + * a string in the pool. + */ +struct ResStringPool_span +{ + enum { + END = 0xFFFFFFFF + }; + + // This is the name of the span -- that is, the name of the XML + // tag that defined it. The special value END (0xFFFFFFFF) indicates + // the end of an array of spans. + ResStringPool_ref name; + + // The range of characters in the string that this span applies to. + uint32_t firstChar, lastChar; +}; + +/** + * Convenience class for accessing data in a ResStringPool resource. + */ +class ResStringPool +{ +public: + ResStringPool(); + ResStringPool(const void* data, size_t size, bool copyData=false); + ~ResStringPool(); + + status_t setTo(const void* data, size_t size, bool copyData=false); + + status_t getError() const; + + void uninit(); + + inline const char16_t* stringAt(const ResStringPool_ref& ref, size_t* outLen) const { + return stringAt(ref.index, outLen); + } + const char16_t* stringAt(size_t idx, size_t* outLen) const; + + const ResStringPool_span* styleAt(const ResStringPool_ref& ref) const; + const ResStringPool_span* styleAt(size_t idx) const; + + ssize_t indexOfString(const char16_t* str, size_t strLen) const; + + size_t size() const; + +private: + status_t mError; + void* mOwnedData; + const ResStringPool_header* mHeader; + size_t mSize; + const uint32_t* mEntries; + const uint32_t* mEntryStyles; + const char16_t* mStrings; + uint32_t mStringPoolSize; // number of uint16_t + const uint32_t* mStyles; + uint32_t mStylePoolSize; // number of uint32_t +}; + +/** ******************************************************************** + * XML Tree + * + * Binary representation of an XML document. This is designed to + * express everything in an XML document, in a form that is much + * easier to parse on the device. + * + *********************************************************************** */ + +/** + * XML tree header. This appears at the front of an XML tree, + * describing its content. It is followed by a flat array of + * ResXMLTree_node structures; the hierarchy of the XML document + * is described by the occurrance of RES_XML_START_ELEMENT_TYPE + * and corresponding RES_XML_END_ELEMENT_TYPE nodes in the array. + */ +struct ResXMLTree_header +{ + struct ResChunk_header header; +}; + +/** + * Basic XML tree node. A single item in the XML document. Extended info + * about the node can be found after header.headerSize. + */ +struct ResXMLTree_node +{ + struct ResChunk_header header; + + // Line number in original source file at which this element appeared. + uint32_t lineNumber; + + // Optional XML comment that was associated with this element; -1 if none. + struct ResStringPool_ref comment; +}; + +/** + * Extended XML tree node for CDATA tags -- includes the CDATA string. + * Appears header.headerSize bytes after a ResXMLTree_node. + */ +struct ResXMLTree_cdataExt +{ + // The raw CDATA character data. + struct ResStringPool_ref data; + + // The typed value of the character data if this is a CDATA node. + struct Res_value typedData; +}; + +/** + * Extended XML tree node for namespace start/end nodes. + * Appears header.headerSize bytes after a ResXMLTree_node. + */ +struct ResXMLTree_namespaceExt +{ + // The prefix of the namespace. + struct ResStringPool_ref prefix; + + // The URI of the namespace. + struct ResStringPool_ref uri; +}; + +/** + * Extended XML tree node for element start/end nodes. + * Appears header.headerSize bytes after a ResXMLTree_node. + */ +struct ResXMLTree_endElementExt +{ + // String of the full namespace of this element. + struct ResStringPool_ref ns; + + // String name of this node if it is an ELEMENT; the raw + // character data if this is a CDATA node. + struct ResStringPool_ref name; +}; + +/** + * Extended XML tree node for start tags -- includes attribute + * information. + * Appears header.headerSize bytes after a ResXMLTree_node. + */ +struct ResXMLTree_attrExt +{ + // String of the full namespace of this element. + struct ResStringPool_ref ns; + + // String name of this node if it is an ELEMENT; the raw + // character data if this is a CDATA node. + struct ResStringPool_ref name; + + // Byte offset from the start of this structure where the attributes start. + uint16_t attributeStart; + + // Size of the ResXMLTree_attribute structures that follow. + uint16_t attributeSize; + + // Number of attributes associated with an ELEMENT. These are + // available as an array of ResXMLTree_attribute structures + // immediately following this node. + uint16_t attributeCount; + + // Index (1-based) of the "id" attribute. 0 if none. + uint16_t idIndex; + + // Index (1-based) of the "class" attribute. 0 if none. + uint16_t classIndex; + + // Index (1-based) of the "style" attribute. 0 if none. + uint16_t styleIndex; +}; + +struct ResXMLTree_attribute +{ + // Namespace of this attribute. + struct ResStringPool_ref ns; + + // Name of this attribute. + struct ResStringPool_ref name; + + // The original raw string value of this attribute. + struct ResStringPool_ref rawValue; + + // Processesd typed value of this attribute. + struct Res_value typedValue; +}; + +class ResXMLTree; + +class ResXMLParser +{ +public: + ResXMLParser(const ResXMLTree& tree); + + enum event_code_t { + BAD_DOCUMENT = -1, + START_DOCUMENT = 0, + END_DOCUMENT = 1, + + FIRST_CHUNK_CODE = RES_XML_FIRST_CHUNK_TYPE, + + START_NAMESPACE = RES_XML_START_NAMESPACE_TYPE, + END_NAMESPACE = RES_XML_END_NAMESPACE_TYPE, + START_TAG = RES_XML_START_ELEMENT_TYPE, + END_TAG = RES_XML_END_ELEMENT_TYPE, + TEXT = RES_XML_CDATA_TYPE + }; + + struct ResXMLPosition + { + event_code_t eventCode; + const ResXMLTree_node* curNode; + const void* curExt; + }; + + void restart(); + + event_code_t getEventType() const; + // Note, unlike XmlPullParser, the first call to next() will return + // START_TAG of the first element. + event_code_t next(); + + // These are available for all nodes: + const int32_t getCommentID() const; + const uint16_t* getComment(size_t* outLen) const; + const uint32_t getLineNumber() const; + + // This is available for TEXT: + const int32_t getTextID() const; + const uint16_t* getText(size_t* outLen) const; + ssize_t getTextValue(Res_value* outValue) const; + + // These are available for START_NAMESPACE and END_NAMESPACE: + const int32_t getNamespacePrefixID() const; + const uint16_t* getNamespacePrefix(size_t* outLen) const; + const int32_t getNamespaceUriID() const; + const uint16_t* getNamespaceUri(size_t* outLen) const; + + // These are available for START_TAG and END_TAG: + const int32_t getElementNamespaceID() const; + const uint16_t* getElementNamespace(size_t* outLen) const; + const int32_t getElementNameID() const; + const uint16_t* getElementName(size_t* outLen) const; + + // Remaining methods are for retrieving information about attributes + // associated with a START_TAG: + + size_t getAttributeCount() const; + + // Returns -1 if no namespace, -2 if idx out of range. + const int32_t getAttributeNamespaceID(size_t idx) const; + const uint16_t* getAttributeNamespace(size_t idx, size_t* outLen) const; + + const int32_t getAttributeNameID(size_t idx) const; + const uint16_t* getAttributeName(size_t idx, size_t* outLen) const; + const uint32_t getAttributeNameResID(size_t idx) const; + + const int32_t getAttributeValueStringID(size_t idx) const; + const uint16_t* getAttributeStringValue(size_t idx, size_t* outLen) const; + + int32_t getAttributeDataType(size_t idx) const; + int32_t getAttributeData(size_t idx) const; + ssize_t getAttributeValue(size_t idx, Res_value* outValue) const; + + ssize_t indexOfAttribute(const char* ns, const char* attr) const; + ssize_t indexOfAttribute(const char16_t* ns, size_t nsLen, + const char16_t* attr, size_t attrLen) const; + + ssize_t indexOfID() const; + ssize_t indexOfClass() const; + ssize_t indexOfStyle() const; + + void getPosition(ResXMLPosition* pos) const; + void setPosition(const ResXMLPosition& pos); + +private: + friend class ResXMLTree; + + event_code_t nextNode(); + + const ResXMLTree& mTree; + event_code_t mEventCode; + const ResXMLTree_node* mCurNode; + const void* mCurExt; +}; + +/** + * Convenience class for accessing data in a ResXMLTree resource. + */ +class ResXMLTree : public ResXMLParser +{ +public: + ResXMLTree(); + ResXMLTree(const void* data, size_t size, bool copyData=false); + ~ResXMLTree(); + + status_t setTo(const void* data, size_t size, bool copyData=false); + + status_t getError() const; + + void uninit(); + + const ResStringPool& getStrings() const; + +private: + friend class ResXMLParser; + + status_t validateNode(const ResXMLTree_node* node) const; + + status_t mError; + void* mOwnedData; + const ResXMLTree_header* mHeader; + size_t mSize; + const uint8_t* mDataEnd; + ResStringPool mStrings; + const uint32_t* mResIds; + size_t mNumResIds; + const ResXMLTree_node* mRootNode; + const void* mRootExt; + event_code_t mRootCode; +}; + +/** ******************************************************************** + * RESOURCE TABLE + * + *********************************************************************** */ + +/** + * Header for a resource table. Its data contains a series of + * additional chunks: + * * A ResStringPool_header containing all table values. + * * One or more ResTable_package chunks. + * + * Specific entries within a resource table can be uniquely identified + * with a single integer as defined by the ResTable_ref structure. + */ +struct ResTable_header +{ + struct ResChunk_header header; + + // The number of ResTable_package structures. + uint32_t packageCount; +}; + +/** + * A collection of resource data types within a package. Followed by + * one or more ResTable_type and ResTable_typeSpec structures containing the + * entry values for each resource type. + */ +struct ResTable_package +{ + struct ResChunk_header header; + + // If this is a base package, its ID. Package IDs start + // at 1 (corresponding to the value of the package bits in a + // resource identifier). 0 means this is not a base package. + uint32_t id; + + // Actual name of this package, \0-terminated. + char16_t name[128]; + + // Offset to a ResStringPool_header defining the resource + // type symbol table. If zero, this package is inheriting from + // another base package (overriding specific values in it). + uint32_t typeStrings; + + // Last index into typeStrings that is for public use by others. + uint32_t lastPublicType; + + // Offset to a ResStringPool_header defining the resource + // key symbol table. If zero, this package is inheriting from + // another base package (overriding specific values in it). + uint32_t keyStrings; + + // Last index into keyStrings that is for public use by others. + uint32_t lastPublicKey; +}; + +/** + * Describes a particular resource configuration. + */ +struct ResTable_config +{ + // Number of bytes in this structure. + uint32_t size; + + union { + struct { + // Mobile country code (from SIM). 0 means "any". + uint16_t mcc; + // Mobile network code (from SIM). 0 means "any". + uint16_t mnc; + }; + uint32_t imsi; + }; + + union { + struct { + // \0\0 means "any". Otherwise, en, fr, etc. + char language[2]; + + // \0\0 means "any". Otherwise, US, CA, etc. + char country[2]; + }; + uint32_t locale; + }; + + enum { + ORIENTATION_ANY = 0x0000, + ORIENTATION_PORT = 0x0001, + ORIENTATION_LAND = 0x0002, + ORIENTATION_SQUARE = 0x0002, + }; + + enum { + TOUCHSCREEN_ANY = 0x0000, + TOUCHSCREEN_NOTOUCH = 0x0001, + TOUCHSCREEN_STYLUS = 0x0002, + TOUCHSCREEN_FINGER = 0x0003, + }; + + enum { + DENSITY_ANY = 0 + }; + + union { + struct { + uint8_t orientation; + uint8_t touchscreen; + uint16_t density; + }; + uint32_t screenType; + }; + + enum { + KEYBOARD_ANY = 0x0000, + KEYBOARD_NOKEYS = 0x0001, + KEYBOARD_QWERTY = 0x0002, + KEYBOARD_12KEY = 0x0003, + }; + + enum { + NAVIGATION_ANY = 0x0000, + NAVIGATION_NONAV = 0x0001, + NAVIGATION_DPAD = 0x0002, + NAVIGATION_TRACKBALL = 0x0003, + NAVIGATION_WHEEL = 0x0004, + }; + + enum { + MASK_KEYSHIDDEN = 0x0003, + SHIFT_KEYSHIDDEN = 0, + KEYSHIDDEN_ANY = 0x0000, + KEYSHIDDEN_NO = 0x0001, + KEYSHIDDEN_YES = 0x0002, + KEYSHIDDEN_SOFT = 0x0003, + }; + + union { + struct { + uint8_t keyboard; + uint8_t navigation; + uint8_t inputFlags; + uint8_t pad0; + }; + uint32_t input; + }; + + enum { + SCREENWIDTH_ANY = 0 + }; + + enum { + SCREENHEIGHT_ANY = 0 + }; + + union { + struct { + uint16_t screenWidth; + uint16_t screenHeight; + }; + uint32_t screenSize; + }; + + enum { + SDKVERSION_ANY = 0 + }; + + enum { + MINORVERSION_ANY = 0 + }; + + union { + struct { + uint16_t sdkVersion; + // For now minorVersion must always be 0!!! Its meaning + // is currently undefined. + uint16_t minorVersion; + }; + uint32_t version; + }; + + inline void copyFromDeviceNoSwap(const ResTable_config& o) { + const size_t size = dtohl(o.size); + if (size >= sizeof(ResTable_config)) { + *this = o; + } else { + memcpy(this, &o, size); + memset(((uint8_t*)this)+size, 0, sizeof(ResTable_config)-size); + } + } + + inline void copyFromDtoH(const ResTable_config& o) { + copyFromDeviceNoSwap(o); + size = sizeof(ResTable_config); + mcc = dtohs(mcc); + mnc = dtohs(mnc); + density = dtohs(density); + screenWidth = dtohs(screenWidth); + screenHeight = dtohs(screenHeight); + sdkVersion = dtohs(sdkVersion); + minorVersion = dtohs(minorVersion); + } + + inline void swapHtoD() { + size = htodl(size); + mcc = htods(mcc); + mnc = htods(mnc); + density = htods(density); + screenWidth = htods(screenWidth); + screenHeight = htods(screenHeight); + sdkVersion = htods(sdkVersion); + minorVersion = htods(minorVersion); + } + + inline int compare(const ResTable_config& o) const { + int32_t diff = (int32_t)(imsi - o.imsi); + if (diff != 0) return diff; + diff = (int32_t)(locale - o.locale); + if (diff != 0) return diff; + diff = (int32_t)(screenType - o.screenType); + if (diff != 0) return diff; + diff = (int32_t)(input - o.input); + if (diff != 0) return diff; + diff = (int32_t)(screenSize - o.screenSize); + if (diff != 0) return diff; + diff = (int32_t)(version - o.version); + return (int)diff; + } + + // Flags indicating a set of config values. These flag constants must + // match the corresponding ones in android.content.pm.ActivityInfo and + // attrs_manifest.xml. + enum { + CONFIG_MCC = 0x0001, + CONFIG_MNC = 0x0002, + CONFIG_LOCALE = 0x0004, + CONFIG_TOUCHSCREEN = 0x0008, + CONFIG_KEYBOARD = 0x0010, + CONFIG_KEYBOARD_HIDDEN = 0x0020, + CONFIG_NAVIGATION = 0x0040, + CONFIG_ORIENTATION = 0x0080, + CONFIG_DENSITY = 0x0100, + CONFIG_SCREEN_SIZE = 0x0200, + CONFIG_VERSION = 0x0400 + }; + + // Compare two configuration, returning CONFIG_* flags set for each value + // that is different. + inline int diff(const ResTable_config& o) const { + int diffs = 0; + if (mcc != o.mcc) diffs |= CONFIG_MCC; + if (mnc != o.mnc) diffs |= CONFIG_MNC; + if (locale != o.locale) diffs |= CONFIG_LOCALE; + if (orientation != o.orientation) diffs |= CONFIG_ORIENTATION; + if (density != o.density) diffs |= CONFIG_DENSITY; + if (touchscreen != o.touchscreen) diffs |= CONFIG_TOUCHSCREEN; + if (((inputFlags^o.inputFlags)&MASK_KEYSHIDDEN) != 0) diffs |= CONFIG_KEYBOARD_HIDDEN; + if (keyboard != o.keyboard) diffs |= CONFIG_KEYBOARD; + if (navigation != o.navigation) diffs |= CONFIG_NAVIGATION; + if (screenSize != o.screenSize) diffs |= CONFIG_SCREEN_SIZE; + if (version != o.version) diffs |= CONFIG_VERSION; + return diffs; + } + + // Return true if 'this' is more specific than 'o'. Optionally, if + // 'requested' is null, then they will also be compared against the + // requested configuration and true will only be returned if 'this' + // is a better candidate than 'o' for the configuration. This assumes that + // match() has already been used to remove any configurations that don't + // match the requested configuration at all; if they are not first filtered, + // non-matching results can be considered better than matching ones. + inline bool + isBetterThan(const ResTable_config& o, const ResTable_config* requested = NULL) const { + // The order of the following tests defines the importance of one + // configuration parameter over another. Those tests first are more + // important, trumping any values in those following them. + if (imsi != 0 && (!requested || requested->imsi != 0)) { + if (mcc != 0 && (!requested || requested->mcc != 0)) { + if (o.mcc == 0) { + return true; + } + } + if (mnc != 0 && (!requested || requested->mnc != 0)) { + if (o.mnc == 0) { + return true; + } + } + } + if (locale != 0 && (!requested || requested->locale != 0)) { + if (language[0] != 0 && (!requested || requested->language[0] != 0)) { + if (o.language[0] == 0) { + return true; + } + } + if (country[0] != 0 && (!requested || requested->country[0] != 0)) { + if (o.country[0] == 0) { + return true; + } + } + } + if (screenType != 0 && (!requested || requested->screenType != 0)) { + if (orientation != 0 && (!requested || requested->orientation != 0)) { + if (o.orientation == 0) { + return true; + } + } + if (density != 0 && (!requested || requested->density != 0)) { + if (o.density == 0) { + return true; + } + } + if (touchscreen != 0 && (!requested || requested->touchscreen != 0)) { + if (o.touchscreen == 0) { + return true; + } + } + } + if (input != 0 && (!requested || requested->input != 0)) { + const int keysHidden = inputFlags&MASK_KEYSHIDDEN; + const int reqKeysHidden = requested + ? requested->inputFlags&MASK_KEYSHIDDEN : 0; + if (keysHidden != 0 && reqKeysHidden != 0) { + const int oKeysHidden = o.inputFlags&MASK_KEYSHIDDEN; + //LOGI("isBetterThan keysHidden: cur=%d, given=%d, config=%d\n", + // keysHidden, oKeysHidden, reqKeysHidden); + if (oKeysHidden == 0) { + //LOGI("Better because 0!"); + return true; + } + // For compatibility, we count KEYSHIDDEN_NO as being + // the same as KEYSHIDDEN_SOFT. Here we disambiguate these + // may making an exact match more specific. + if (keysHidden == reqKeysHidden && oKeysHidden != reqKeysHidden) { + // The current configuration is an exact match, and + // the given one is not, so the current one is better. + //LOGI("Better because other not same!"); + return true; + } + } + if (keyboard != 0 && (!requested || requested->keyboard != 0)) { + if (o.keyboard == 0) { + return true; + } + } + if (navigation != 0 && (!requested || requested->navigation != 0)) { + if (o.navigation == 0) { + return true; + } + } + } + if (screenSize != 0 && (!requested || requested->screenSize != 0)) { + if (screenWidth != 0 && (!requested || requested->screenWidth != 0)) { + if (o.screenWidth == 0) { + return true; + } + } + if (screenHeight != 0 && (!requested || requested->screenHeight != 0)) { + if (o.screenHeight == 0) { + return true; + } + } + } + if (version != 0 && (!requested || requested->version != 0)) { + if (sdkVersion != 0 && (!requested || requested->sdkVersion != 0)) { + if (o.sdkVersion == 0) { + return true; + } + } + if (minorVersion != 0 && (!requested || requested->minorVersion != 0)) { + if (o.minorVersion == 0) { + return true; + } + } + } + return false; + } + + // Return true if 'this' can be considered a match for the parameters in + // 'settings'. + // Note this is asymetric. A default piece of data will match every request + // but a request for the default should not match odd specifics + // (ie, request with no mcc should not match a particular mcc's data) + // settings is the requested settings + inline bool match(const ResTable_config& settings) const { + if (imsi != 0) { + if ((settings.mcc != 0 && mcc != 0 + && mcc != settings.mcc) || + (settings.mcc == 0 && mcc != 0)) { + return false; + } + if ((settings.mnc != 0 && mnc != 0 + && mnc != settings.mnc) || + (settings.mnc == 0 && mnc != 0)) { + return false; + } + } + if (locale != 0) { + if (settings.language[0] != 0 && language[0] != 0 + && (language[0] != settings.language[0] + || language[1] != settings.language[1])) { + return false; + } + if (settings.country[0] != 0 && country[0] != 0 + && (country[0] != settings.country[0] + || country[1] != settings.country[1])) { + return false; + } + } + if (screenType != 0) { + if (settings.orientation != 0 && orientation != 0 + && orientation != settings.orientation) { + return false; + } + // Density not taken into account, always match, no matter what + // density is specified for the resource + if (settings.touchscreen != 0 && touchscreen != 0 + && touchscreen != settings.touchscreen) { + return false; + } + } + if (input != 0) { + const int keysHidden = inputFlags&MASK_KEYSHIDDEN; + const int setKeysHidden = settings.inputFlags&MASK_KEYSHIDDEN; + if (setKeysHidden != 0 && keysHidden != 0 + && keysHidden != setKeysHidden) { + // For compatibility, we count a request for KEYSHIDDEN_NO as also + // matching the more recent KEYSHIDDEN_SOFT. Basically + // KEYSHIDDEN_NO means there is some kind of keyboard available. + //LOGI("Matching keysHidden: have=%d, config=%d\n", keysHidden, setKeysHidden); + if (keysHidden != KEYSHIDDEN_NO || setKeysHidden != KEYSHIDDEN_SOFT) { + //LOGI("No match!"); + return false; + } + } + if (settings.keyboard != 0 && keyboard != 0 + && keyboard != settings.keyboard) { + return false; + } + if (settings.navigation != 0 && navigation != 0 + && navigation != settings.navigation) { + return false; + } + } + if (screenSize != 0) { + if (settings.screenWidth != 0 && screenWidth != 0 + && screenWidth != settings.screenWidth) { + return false; + } + if (settings.screenHeight != 0 && screenHeight != 0 + && screenHeight != settings.screenHeight) { + return false; + } + } + if (version != 0) { + if (settings.sdkVersion != 0 && sdkVersion != 0 + && sdkVersion != settings.sdkVersion) { + return false; + } + if (settings.minorVersion != 0 && minorVersion != 0 + && minorVersion != settings.minorVersion) { + return false; + } + } + return true; + } + + void getLocale(char str[6]) const { + memset(str, 0, 6); + if (language[0]) { + str[0] = language[0]; + str[1] = language[1]; + if (country[0]) { + str[2] = '_'; + str[3] = country[0]; + str[4] = country[1]; + } + } + } + + String8 toString() const { + char buf[200]; + sprintf(buf, "imsi=%d/%d lang=%c%c reg=%c%c orient=0x%02x touch=0x%02x dens=0x%02x " + "kbd=0x%02x nav=0x%02x input=0x%02x screenW=0x%04x screenH=0x%04x vers=%d.%d", + mcc, mnc, + language[0] ? language[0] : '-', language[1] ? language[1] : '-', + country[0] ? country[0] : '-', country[1] ? country[1] : '-', + orientation, touchscreen, density, keyboard, navigation, inputFlags, + screenWidth, screenHeight, sdkVersion, minorVersion); + return String8(buf); + } +}; + +/** + * A specification of the resources defined by a particular type. + * + * There should be one of these chunks for each resource type. + * + * This structure is followed by an array of integers providing the set of + * configuation change flags (ResTable_config::CONFIG_*) that have multiple + * resources for that configuration. In addition, the high bit is set if that + * resource has been made public. + */ +struct ResTable_typeSpec +{ + struct ResChunk_header header; + + // The type identifier this chunk is holding. Type IDs start + // at 1 (corresponding to the value of the type bits in a + // resource identifier). 0 is invalid. + uint8_t id; + + // Must be 0. + uint8_t res0; + // Must be 0. + uint16_t res1; + + // Number of uint32_t entry configuration masks that follow. + uint32_t entryCount; + + enum { + // Additional flag indicating an entry is public. + SPEC_PUBLIC = 0x40000000 + }; +}; + +/** + * A collection of resource entries for a particular resource data + * type. Followed by an array of uint32_t defining the resource + * values, corresponding to the array of type strings in the + * ResTable_package::typeStrings string block. Each of these hold an + * index from entriesStart; a value of NO_ENTRY means that entry is + * not defined. + * + * There may be multiple of these chunks for a particular resource type, + * supply different configuration variations for the resource values of + * that type. + * + * It would be nice to have an additional ordered index of entries, so + * we can do a binary search if trying to find a resource by string name. + */ +struct ResTable_type +{ + struct ResChunk_header header; + + enum { + NO_ENTRY = 0xFFFFFFFF + }; + + // The type identifier this chunk is holding. Type IDs start + // at 1 (corresponding to the value of the type bits in a + // resource identifier). 0 is invalid. + uint8_t id; + + // Must be 0. + uint8_t res0; + // Must be 0. + uint16_t res1; + + // Number of uint32_t entry indices that follow. + uint32_t entryCount; + + // Offset from header where ResTable_entry data starts. + uint32_t entriesStart; + + // Configuration this collection of entries is designed for. + ResTable_config config; +}; + +/** + * This is the beginning of information about an entry in the resource + * table. It holds the reference to the name of this entry, and is + * immediately followed by one of: + * * A Res_value structures, if FLAG_COMPLEX is -not- set. + * * An array of ResTable_map structures, if FLAG_COMPLEX is set. + * These supply a set of name/value mappings of data. + */ +struct ResTable_entry +{ + // Number of bytes in this structure. + uint16_t size; + + enum { + // If set, this is a complex entry, holding a set of name/value + // mappings. It is followed by an array of ResTable_map structures. + FLAG_COMPLEX = 0x0001, + // If set, this resource has been declared public, so libraries + // are allowed to reference it. + FLAG_PUBLIC = 0x0002 + }; + uint16_t flags; + + // Reference into ResTable_package::keyStrings identifying this entry. + struct ResStringPool_ref key; +}; + +/** + * Extended form of a ResTable_entry for map entries, defining a parent map + * resource from which to inherit values. + */ +struct ResTable_map_entry : public ResTable_entry +{ + // Resource identifier of the parent mapping, or 0 if there is none. + ResTable_ref parent; + // Number of name/value pairs that follow for FLAG_COMPLEX. + uint32_t count; +}; + +/** + * A single name/value mapping that is part of a complex resource + * entry. + */ +struct ResTable_map +{ + // The resource identifier defining this mapping's name. For attribute + // resources, 'name' can be one of the following special resource types + // to supply meta-data about the attribute; for all other resource types + // it must be an attribute resource. + ResTable_ref name; + + // Special values for 'name' when defining attribute resources. + enum { + // This entry holds the attribute's type code. + ATTR_TYPE = Res_MAKEINTERNAL(0), + + // For integral attributes, this is the minimum value it can hold. + ATTR_MIN = Res_MAKEINTERNAL(1), + + // For integral attributes, this is the maximum value it can hold. + ATTR_MAX = Res_MAKEINTERNAL(2), + + // Localization of this resource is can be encouraged or required with + // an aapt flag if this is set + ATTR_L10N = Res_MAKEINTERNAL(3), + + // for plural support, see android.content.res.PluralRules#attrForQuantity(int) + ATTR_OTHER = Res_MAKEINTERNAL(4), + ATTR_ZERO = Res_MAKEINTERNAL(5), + ATTR_ONE = Res_MAKEINTERNAL(6), + ATTR_TWO = Res_MAKEINTERNAL(7), + ATTR_FEW = Res_MAKEINTERNAL(8), + ATTR_MANY = Res_MAKEINTERNAL(9) + + }; + + // Bit mask of allowed types, for use with ATTR_TYPE. + enum { + // No type has been defined for this attribute, use generic + // type handling. The low 16 bits are for types that can be + // handled generically; the upper 16 require additional information + // in the bag so can not be handled generically for TYPE_ANY. + TYPE_ANY = 0x0000FFFF, + + // Attribute holds a references to another resource. + TYPE_REFERENCE = 1<<0, + + // Attribute holds a generic string. + TYPE_STRING = 1<<1, + + // Attribute holds an integer value. ATTR_MIN and ATTR_MIN can + // optionally specify a constrained range of possible integer values. + TYPE_INTEGER = 1<<2, + + // Attribute holds a boolean integer. + TYPE_BOOLEAN = 1<<3, + + // Attribute holds a color value. + TYPE_COLOR = 1<<4, + + // Attribute holds a floating point value. + TYPE_FLOAT = 1<<5, + + // Attribute holds a dimension value, such as "20px". + TYPE_DIMENSION = 1<<6, + + // Attribute holds a fraction value, such as "20%". + TYPE_FRACTION = 1<<7, + + // Attribute holds an enumeration. The enumeration values are + // supplied as additional entries in the map. + TYPE_ENUM = 1<<16, + + // Attribute holds a bitmaks of flags. The flag bit values are + // supplied as additional entries in the map. + TYPE_FLAGS = 1<<17 + }; + + // Enum of localization modes, for use with ATTR_L10N. + enum { + L10N_NOT_REQUIRED = 0, + L10N_SUGGESTED = 1 + }; + + // This mapping's value. + Res_value value; +}; + +/** + * Convenience class for accessing data in a ResTable resource. + */ +class ResTable +{ +public: + ResTable(); + ResTable(const void* data, size_t size, void* cookie, + bool copyData=false); + ~ResTable(); + + status_t add(const void* data, size_t size, void* cookie, + bool copyData=false); + status_t add(Asset* asset, void* cookie, + bool copyData=false); + + status_t getError() const; + + void uninit(); + + struct resource_name + { + const char16_t* package; + size_t packageLen; + const char16_t* type; + size_t typeLen; + const char16_t* name; + size_t nameLen; + }; + + bool getResourceName(uint32_t resID, resource_name* outName) const; + + /** + * Retrieve the value of a resource. If the resource is found, returns a + * value >= 0 indicating the table it is in (for use with + * getTableStringBlock() and getTableCookie()) and fills in 'outValue'. If + * not found, returns a negative error code. + * + * Note that this function does not do reference traversal. If you want + * to follow references to other resources to get the "real" value to + * use, you need to call resolveReference() after this function. + * + * @param resID The desired resoruce identifier. + * @param outValue Filled in with the resource data that was found. + * + * @return ssize_t Either a >= 0 table index or a negative error code. + */ + ssize_t getResource(uint32_t resID, Res_value* outValue, bool mayBeBag=false, + uint32_t* outSpecFlags=NULL, ResTable_config* outConfig=NULL) const; + + inline ssize_t getResource(const ResTable_ref& res, Res_value* outValue, + uint32_t* outSpecFlags=NULL) const { + return getResource(res.ident, outValue, false, outSpecFlags, NULL); + } + + ssize_t resolveReference(Res_value* inOutValue, + ssize_t blockIndex, + uint32_t* outLastRef = NULL, + uint32_t* inoutTypeSpecFlags = NULL) const; + + enum { + TMP_BUFFER_SIZE = 16 + }; + const char16_t* valueToString(const Res_value* value, size_t stringBlock, + char16_t tmpBuffer[TMP_BUFFER_SIZE], + size_t* outLen); + + struct bag_entry { + ssize_t stringBlock; + ResTable_map map; + }; + + /** + * Retrieve the bag of a resource. If the resoruce is found, returns the + * number of bags it contains and 'outBag' points to an array of their + * values. If not found, a negative error code is returned. + * + * Note that this function -does- do reference traversal of the bag data. + * + * @param resID The desired resource identifier. + * @param outBag Filled inm with a pointer to the bag mappings. + * + * @return ssize_t Either a >= 0 bag count of negative error code. + */ + ssize_t lockBag(uint32_t resID, const bag_entry** outBag) const; + + void unlockBag(const bag_entry* bag) const; + + void lock() const; + + ssize_t getBagLocked(uint32_t resID, const bag_entry** outBag, + uint32_t* outTypeSpecFlags=NULL) const; + + void unlock() const; + + class Theme { + public: + Theme(const ResTable& table); + ~Theme(); + + inline const ResTable& getResTable() const { return mTable; } + + status_t applyStyle(uint32_t resID, bool force=false); + status_t setTo(const Theme& other); + + /** + * Retrieve a value in the theme. If the theme defines this + * value, returns a value >= 0 indicating the table it is in + * (for use with getTableStringBlock() and getTableCookie) and + * fills in 'outValue'. If not found, returns a negative error + * code. + * + * Note that this function does not do reference traversal. If you want + * to follow references to other resources to get the "real" value to + * use, you need to call resolveReference() after this function. + * + * @param resID A resource identifier naming the desired theme + * attribute. + * @param outValue Filled in with the theme value that was + * found. + * + * @return ssize_t Either a >= 0 table index or a negative error code. + */ + ssize_t getAttribute(uint32_t resID, Res_value* outValue, + uint32_t* outTypeSpecFlags = NULL) const; + + /** + * This is like ResTable::resolveReference(), but also takes + * care of resolving attribute references to the theme. + */ + ssize_t resolveAttributeReference(Res_value* inOutValue, + ssize_t blockIndex, uint32_t* outLastRef = NULL, + uint32_t* inoutTypeSpecFlags = NULL) const; + + void dumpToLog() const; + + private: + Theme(const Theme&); + Theme& operator=(const Theme&); + + struct theme_entry { + ssize_t stringBlock; + uint32_t typeSpecFlags; + Res_value value; + }; + struct type_info { + size_t numEntries; + theme_entry* entries; + }; + struct package_info { + size_t numTypes; + type_info types[]; + }; + + void free_package(package_info* pi); + package_info* copy_package(package_info* pi); + + const ResTable& mTable; + package_info* mPackages[Res_MAXPACKAGE]; + }; + + void setParameters(const ResTable_config* params); + void getParameters(ResTable_config* params) const; + + // Retrieve an identifier (which can be passed to getResource) + // for a given resource name. The 'name' can be fully qualified + // (:.) or the package or type components + // can be dropped if default values are supplied here. + // + // Returns 0 if no such resource was found, else a valid resource ID. + uint32_t identifierForName(const char16_t* name, size_t nameLen, + const char16_t* type = 0, size_t typeLen = 0, + const char16_t* defPackage = 0, + size_t defPackageLen = 0, + uint32_t* outTypeSpecFlags = NULL) const; + + static bool expandResourceRef(const uint16_t* refStr, size_t refLen, + String16* outPackage, + String16* outType, + String16* outName, + const String16* defType = NULL, + const String16* defPackage = NULL, + const char** outErrorMsg = NULL); + + static bool stringToInt(const char16_t* s, size_t len, Res_value* outValue); + static bool stringToFloat(const char16_t* s, size_t len, Res_value* outValue); + + // Used with stringToValue. + class Accessor + { + public: + inline virtual ~Accessor() { } + + virtual uint32_t getCustomResource(const String16& package, + const String16& type, + const String16& name) const = 0; + virtual uint32_t getCustomResourceWithCreation(const String16& package, + const String16& type, + const String16& name, + const bool createIfNeeded = false) = 0; + virtual uint32_t getRemappedPackage(uint32_t origPackage) const = 0; + virtual bool getAttributeType(uint32_t attrID, uint32_t* outType) = 0; + virtual bool getAttributeMin(uint32_t attrID, uint32_t* outMin) = 0; + virtual bool getAttributeMax(uint32_t attrID, uint32_t* outMax) = 0; + virtual bool getAttributeEnum(uint32_t attrID, + const char16_t* name, size_t nameLen, + Res_value* outValue) = 0; + virtual bool getAttributeFlags(uint32_t attrID, + const char16_t* name, size_t nameLen, + Res_value* outValue) = 0; + virtual uint32_t getAttributeL10N(uint32_t attrID) = 0; + virtual bool getLocalizationSetting() = 0; + virtual void reportError(void* accessorCookie, const char* fmt, ...) = 0; + }; + + // Convert a string to a resource value. Handles standard "@res", + // "#color", "123", and "0x1bd" types; performs escaping of strings. + // The resulting value is placed in 'outValue'; if it is a string type, + // 'outString' receives the string. If 'attrID' is supplied, the value is + // type checked against this attribute and it is used to perform enum + // evaluation. If 'acccessor' is supplied, it will be used to attempt to + // resolve resources that do not exist in this ResTable. If 'attrType' is + // supplied, the value will be type checked for this format if 'attrID' + // is not supplied or found. + bool stringToValue(Res_value* outValue, String16* outString, + const char16_t* s, size_t len, + bool preserveSpaces, bool coerceType, + uint32_t attrID = 0, + const String16* defType = NULL, + const String16* defPackage = NULL, + Accessor* accessor = NULL, + void* accessorCookie = NULL, + uint32_t attrType = ResTable_map::TYPE_ANY, + bool enforcePrivate = true) const; + + // Perform processing of escapes and quotes in a string. + static bool collectString(String16* outString, + const char16_t* s, size_t len, + bool preserveSpaces, + const char** outErrorMsg = NULL, + bool append = false); + + size_t getBasePackageCount() const; + const char16_t* getBasePackageName(size_t idx) const; + uint32_t getBasePackageId(size_t idx) const; + + size_t getTableCount() const; + const ResStringPool* getTableStringBlock(size_t index) const; + void* getTableCookie(size_t index) const; + + // Return the configurations (ResTable_config) that we know about + void getConfigurations(Vector* configs) const; + + void getLocales(Vector* locales) const; + +#ifndef HAVE_ANDROID_OS + void print() const; +#endif + +private: + struct Header; + struct Type; + struct Package; + struct PackageGroup; + struct bag_set; + + status_t add(const void* data, size_t size, void* cookie, + Asset* asset, bool copyData); + + ssize_t getResourcePackageIndex(uint32_t resID) const; + ssize_t getEntry( + const Package* package, int typeIndex, int entryIndex, + const ResTable_config* config, + const ResTable_type** outType, const ResTable_entry** outEntry, + const Type** outTypeClass) const; + status_t parsePackage( + const ResTable_package* const pkg, const Header* const header); + + mutable Mutex mLock; + + status_t mError; + + ResTable_config mParams; + + // Array of all resource tables. + Vector mHeaders; + + // Array of packages in all resource tables. + Vector mPackageGroups; + + // Mapping from resource package IDs to indices into the internal + // package array. + uint8_t mPackageMap[256]; +}; + +} // namespace android + +#endif // _LIBS_UTILS_RESOURCE_TYPES_H diff --git a/include/utils/SharedBuffer.h b/include/utils/SharedBuffer.h new file mode 100644 index 000000000..24508b0f7 --- /dev/null +++ b/include/utils/SharedBuffer.h @@ -0,0 +1,146 @@ +/* + * 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 +#include + +// --------------------------------------------------------------------------- + +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); + + //! get the SharedBuffer from the data pointer + static inline const SharedBuffer* sharedBuffer(const void* data); + + //! access the data for read + inline const void* data() const; + + //! access the data for read/write + inline void* data(); + + //! get size of the buffer + inline size_t size() const; + + //! get back a SharedBuffer object from its data + static inline SharedBuffer* bufferFromData(void* data); + + //! get back a SharedBuffer object from its data + static inline const SharedBuffer* bufferFromData(const void* data); + + //! get the size of a SharedBuffer object from its data + static inline size_t sizeFromData(const void* data); + + //! edit the buffer (get a writtable, or non-const, version of it) + SharedBuffer* edit() const; + + //! edit the buffer, resizing if needed + SharedBuffer* editResize(size_t size) const; + + //! like edit() but fails if a copy is required + SharedBuffer* attemptEdit() const; + + //! resize and edit the buffer, loose it's content. + SharedBuffer* reset(size_t size) const; + + //! acquire/release a reference on this buffer + void acquire() const; + + /*! release a reference on this buffer, with the option of not + * freeing the memory associated with it if it was the last reference + * returns the previous reference count + */ + int32_t release(uint32_t flags = 0) const; + + //! returns wether or not we're the only owner + inline bool onlyOwner() const; + + +private: + inline SharedBuffer() { } + inline ~SharedBuffer() { } + inline SharedBuffer(const SharedBuffer&); + + // 16 bytes. must be sized to preserve correct alingment. + mutable int32_t mRefs; + size_t mSize; + uint32_t mReserved[2]; +}; + +// --------------------------------------------------------------------------- + +const SharedBuffer* SharedBuffer::sharedBuffer(const void* data) { + return data ? reinterpret_cast(data)-1 : 0; +} + +const void* SharedBuffer::data() const { + return this + 1; +} + +void* SharedBuffer::data() { + return this + 1; +} + +size_t SharedBuffer::size() const { + return mSize; +} + +SharedBuffer* SharedBuffer::bufferFromData(void* data) +{ + return ((SharedBuffer*)data)-1; +} + +const SharedBuffer* SharedBuffer::bufferFromData(const void* data) +{ + return ((const SharedBuffer*)data)-1; +} + +size_t SharedBuffer::sizeFromData(const void* data) +{ + return (((const SharedBuffer*)data)-1)->mSize; +} + +bool SharedBuffer::onlyOwner() const { + return (mRefs == 1); +} + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_VECTOR_H diff --git a/include/utils/Socket.h b/include/utils/Socket.h new file mode 100644 index 000000000..8b7f40617 --- /dev/null +++ b/include/utils/Socket.h @@ -0,0 +1,80 @@ +/* + * 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. + */ + +// +// Socket class. Modeled after Java classes. +// +#ifndef _RUNTIME_SOCKET_H +#define _RUNTIME_SOCKET_H + +#include +#include + +namespace android { + +/* + * Basic socket class, needed to abstract away the differences between + * BSD sockets and WinSock. This establishes a streaming network + * connection (TCP/IP) to somebody. + */ +class Socket { +public: + Socket(void); + ~Socket(void); + + // Create a connection to somewhere. + // Return 0 on success. + int connect(const char* host, int port); + int connect(const InetAddress* addr, int port); + + + // Close the socket. Don't try to use this object again after + // calling this. Returns false on failure. + bool close(void); + + // If we created the socket without an address, we can use these + // to finish the connection. Returns 0 on success. + int bind(const SocketAddress& bindPoint); + int connect(const SocketAddress& endPoint); + + // Here we deviate from the traditional object-oriented fanciness + // and just provide read/write operators instead of getters for + // objects that abstract a stream. + // + // Standard read/write semantics. + int read(void* buf, ssize_t len) const; + int write(const void* buf, ssize_t len) const; + + // This must be called once, at program startup. + static bool bootInit(void); + static void finalShutdown(void); + +private: + // Internal function that establishes a connection. + int doConnect(const InetSocketAddress& addr); + + unsigned long mSock; // holds SOCKET or int + + static bool mBootInitialized; +}; + + +// debug -- unit tests +void TestSockets(void); + +}; // namespace android + +#endif // _RUNTIME_SOCKET_H diff --git a/include/utils/SortedVector.h b/include/utils/SortedVector.h new file mode 100644 index 000000000..c8a61531f --- /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 +#include +#include + +#include +#include +#include + +// --------------------------------------------------------------------------- + +namespace android { + +template +class SortedVector : private SortedVectorImpl +{ +public: + typedef TYPE value_type; + + /*! + * Constructors and destructors + */ + + SortedVector(); + SortedVector(const SortedVector& rhs); + virtual ~SortedVector(); + + /*! copy operator */ + const SortedVector& operator = (const SortedVector& rhs) const; + SortedVector& operator = (const SortedVector& rhs); + + /* + * empty the vector + */ + + inline void clear() { VectorImpl::clear(); } + + /*! + * vector stats + */ + + //! returns number of items in the vector + inline size_t size() const { return VectorImpl::size(); } + //! returns wether or not the vector is empty + inline bool isEmpty() const { return VectorImpl::isEmpty(); } + //! returns how many items can be stored without reallocating the backing store + inline size_t capacity() const { return VectorImpl::capacity(); } + //! setst the capacity. capacity can never be reduced less than size() + inline ssize_t setCapacity(size_t size) { return VectorImpl::setCapacity(size); } + + /*! + * C-style array access + */ + + //! read-only C-style access + inline const TYPE* array() const; + + //! read-write C-style access. BE VERY CAREFUL when modifying the array + //! you ust keep it sorted! You usually don't use this function. + TYPE* editArray(); + + //! finds the index of an item + ssize_t indexOf(const TYPE& item) const; + + //! finds where this item should be inserted + size_t orderOf(const TYPE& item) const; + + + /*! + * accessors + */ + + //! read-only access to an item at a given index + inline const TYPE& operator [] (size_t index) const; + //! alternate name for operator [] + inline const TYPE& itemAt(size_t index) const; + //! stack-usage of the vector. returns the top of the stack (last element) + const TYPE& top() const; + //! same as operator [], but allows to access the vector backward (from the end) with a negative index + const TYPE& mirrorItemAt(ssize_t index) const; + + /*! + * modifing the array + */ + + //! add an item in the right place (and replace the one that is there) + ssize_t add(const TYPE& item); + + //! editItemAt() MUST NOT change the order of this item + TYPE& editItemAt(size_t index) { + return *( static_cast(VectorImpl::editItemLocation(index)) ); + } + + //! merges a vector into this one + ssize_t merge(const Vector& vector); + ssize_t merge(const SortedVector& vector); + + //! removes an item + ssize_t remove(const TYPE&); + + //! remove several items + inline ssize_t removeItemsAt(size_t index, size_t count = 1); + //! remove one item + inline ssize_t removeAt(size_t index) { return removeItemsAt(index); } + +protected: + virtual void do_construct(void* storage, size_t num) const; + virtual void do_destroy(void* storage, size_t num) const; + virtual void do_copy(void* dest, const void* from, size_t num) const; + virtual void do_splat(void* dest, const void* item, size_t num) const; + virtual void do_move_forward(void* dest, const void* from, size_t num) const; + virtual void do_move_backward(void* dest, const void* from, size_t num) const; + virtual int do_compare(const void* lhs, const void* rhs) const; +}; + + +// --------------------------------------------------------------------------- +// No user serviceable parts from here... +// --------------------------------------------------------------------------- + +template inline +SortedVector::SortedVector() + : SortedVectorImpl(sizeof(TYPE), + ((traits::has_trivial_ctor ? HAS_TRIVIAL_CTOR : 0) + |(traits::has_trivial_dtor ? HAS_TRIVIAL_DTOR : 0) + |(traits::has_trivial_copy ? HAS_TRIVIAL_COPY : 0) + |(traits::has_trivial_assign ? HAS_TRIVIAL_ASSIGN : 0)) + ) +{ +} + +template inline +SortedVector::SortedVector(const SortedVector& rhs) + : SortedVectorImpl(rhs) { +} + +template inline +SortedVector::~SortedVector() { + finish_vector(); +} + +template inline +SortedVector& SortedVector::operator = (const SortedVector& rhs) { + SortedVectorImpl::operator = (rhs); + return *this; +} + +template inline +const SortedVector& SortedVector::operator = (const SortedVector& rhs) const { + SortedVectorImpl::operator = (rhs); + return *this; +} + +template inline +const TYPE* SortedVector::array() const { + return static_cast(arrayImpl()); +} + +template inline +TYPE* SortedVector::editArray() { + return static_cast(editArrayImpl()); +} + + +template inline +const TYPE& SortedVector::operator[](size_t index) const { + assert( index inline +const TYPE& SortedVector::itemAt(size_t index) const { + return operator[](index); +} + +template inline +const TYPE& SortedVector::mirrorItemAt(ssize_t index) const { + assert( (index>0 ? index : -index) inline +const TYPE& SortedVector::top() const { + return *(array() + size() - 1); +} + +template inline +ssize_t SortedVector::add(const TYPE& item) { + return SortedVectorImpl::add(&item); +} + +template inline +ssize_t SortedVector::indexOf(const TYPE& item) const { + return SortedVectorImpl::indexOf(&item); +} + +template inline +size_t SortedVector::orderOf(const TYPE& item) const { + return SortedVectorImpl::orderOf(&item); +} + +template inline +ssize_t SortedVector::merge(const Vector& vector) { + return SortedVectorImpl::merge(reinterpret_cast(vector)); +} + +template inline +ssize_t SortedVector::merge(const SortedVector& vector) { + return SortedVectorImpl::merge(reinterpret_cast(vector)); +} + +template inline +ssize_t SortedVector::remove(const TYPE& item) { + return SortedVectorImpl::remove(&item); +} + +template inline +ssize_t SortedVector::removeItemsAt(size_t index, size_t count) { + return VectorImpl::removeItemsAt(index, count); +} + +// --------------------------------------------------------------------------- + +template +void SortedVector::do_construct(void* storage, size_t num) const { + construct_type( reinterpret_cast(storage), num ); +} + +template +void SortedVector::do_destroy(void* storage, size_t num) const { + destroy_type( reinterpret_cast(storage), num ); +} + +template +void SortedVector::do_copy(void* dest, const void* from, size_t num) const { + copy_type( reinterpret_cast(dest), reinterpret_cast(from), num ); +} + +template +void SortedVector::do_splat(void* dest, const void* item, size_t num) const { + splat_type( reinterpret_cast(dest), reinterpret_cast(item), num ); +} + +template +void SortedVector::do_move_forward(void* dest, const void* from, size_t num) const { + move_forward_type( reinterpret_cast(dest), reinterpret_cast(from), num ); +} + +template +void SortedVector::do_move_backward(void* dest, const void* from, size_t num) const { + move_backward_type( reinterpret_cast(dest), reinterpret_cast(from), num ); +} + +template +int SortedVector::do_compare(const void* lhs, const void* rhs) const { + return compare_type( *reinterpret_cast(lhs), *reinterpret_cast(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 000000000..cc0bebc40 --- /dev/null +++ b/include/utils/StopWatch.h @@ -0,0 +1,62 @@ +/* + * 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 +#include + +#include + +// --------------------------------------------------------------------------- + +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; + +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 000000000..a2d22eea9 --- /dev/null +++ b/include/utils/String16.h @@ -0,0 +1,260 @@ +/* + * 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 +#include + +#include +#include + +// --------------------------------------------------------------------------- + +extern "C" { + +typedef uint16_t char16_t; + +// Standard string functions on char16 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); + +} + +// --------------------------------------------------------------------------- + +namespace android { + +class String8; +class TextOutput; + +//! This is a string holding UTF-16 characters. +class String16 +{ +public: + String16(); + 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; +}; + +TextOutput& operator<<(TextOutput& to, const String16& val); + +// --------------------------------------------------------------------------- +// 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; + 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 000000000..c49faf6fe --- /dev/null +++ b/include/utils/String8.h @@ -0,0 +1,353 @@ +/* + * 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 + +// Need this for the char16_t type; String8.h should not +// be depedent on the String16 class. +#include + +#include +#include +#include + +// --------------------------------------------------------------------------- + +namespace android { + +class TextOutput; + +//! This is a string holding UTF-8 characters. +class String8 +{ +public: + String8(); + 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); + + ~String8(); + + inline const char* string() const; + inline size_t size() const; + inline size_t length() const; + inline size_t bytes() const; + + inline const SharedBuffer* sharedBuffer() const; + + 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 append(const String8& other); + status_t append(const char* other); + status_t append(const char* other, size_t numChars); + + 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 up to + * four 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; +}; + +TextOutput& operator<<(TextOutput& to, const String16& val); + +// --------------------------------------------------------------------------- +// 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 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 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; + 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; + 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/SystemClock.h b/include/utils/SystemClock.h new file mode 100644 index 000000000..7c319be13 --- /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 +#include + +namespace android { + +int setCurrentTimeMillis(int64_t millis); +int64_t uptimeMillis(); +int64_t elapsedRealtime(); + +}; // namespace android + +#endif // ANDROID_UTILS_SYSTEMCLOCK_H + diff --git a/include/utils/TextOutput.h b/include/utils/TextOutput.h new file mode 100644 index 000000000..d8d86ba82 --- /dev/null +++ b/include/utils/TextOutput.h @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_TEXTOUTPUT_H +#define ANDROID_TEXTOUTPUT_H + +#include + +#include +#include + +// --------------------------------------------------------------------------- +namespace android { + +class TextOutput +{ +public: + TextOutput() { } + virtual ~TextOutput() { } + + virtual status_t print(const char* txt, size_t len) = 0; + virtual void moveIndent(int delta) = 0; + + class Bundle { + public: + inline Bundle(TextOutput& to) : mTO(to) { to.pushBundle(); } + inline ~Bundle() { mTO.popBundle(); } + private: + TextOutput& mTO; + }; + + virtual void pushBundle() = 0; + virtual void popBundle() = 0; +}; + +// --------------------------------------------------------------------------- + +// Text output stream for printing to the log (via utils/Log.h). +extern TextOutput& alog; + +// Text output stream for printing to stdout. +extern TextOutput& aout; + +// Text output stream for printing to stderr. +extern TextOutput& aerr; + +typedef TextOutput& (*TextOutputManipFunc)(TextOutput&); + +TextOutput& endl(TextOutput& to); +TextOutput& indent(TextOutput& to); +TextOutput& dedent(TextOutput& to); + +TextOutput& operator<<(TextOutput& to, const char* str); +TextOutput& operator<<(TextOutput& to, char); // writes raw character +TextOutput& operator<<(TextOutput& to, bool); +TextOutput& operator<<(TextOutput& to, int); +TextOutput& operator<<(TextOutput& to, long); +TextOutput& operator<<(TextOutput& to, unsigned int); +TextOutput& operator<<(TextOutput& to, unsigned long); +TextOutput& operator<<(TextOutput& to, long long); +TextOutput& operator<<(TextOutput& to, unsigned long long); +TextOutput& operator<<(TextOutput& to, float); +TextOutput& operator<<(TextOutput& to, double); +TextOutput& operator<<(TextOutput& to, TextOutputManipFunc func); +TextOutput& operator<<(TextOutput& to, const void*); + +class TypeCode +{ +public: + inline TypeCode(uint32_t code); + inline ~TypeCode(); + + inline uint32_t typeCode() const; + +private: + uint32_t mCode; +}; + +TextOutput& operator<<(TextOutput& to, const TypeCode& val); + +class HexDump +{ +public: + HexDump(const void *buf, size_t size, size_t bytesPerLine=16); + inline ~HexDump(); + + inline HexDump& setBytesPerLine(size_t bytesPerLine); + inline HexDump& setSingleLineCutoff(int32_t bytes); + inline HexDump& setAlignment(size_t alignment); + inline HexDump& setCArrayStyle(bool enabled); + + inline const void* buffer() const; + inline size_t size() const; + inline size_t bytesPerLine() const; + inline int32_t singleLineCutoff() const; + inline size_t alignment() const; + inline bool carrayStyle() const; + +private: + const void* mBuffer; + size_t mSize; + size_t mBytesPerLine; + int32_t mSingleLineCutoff; + size_t mAlignment; + bool mCArrayStyle; +}; + +TextOutput& operator<<(TextOutput& to, const HexDump& val); + +// --------------------------------------------------------------------------- +// No user servicable parts below. + +inline TextOutput& endl(TextOutput& to) +{ + to.print("\n", 1); + return to; +} + +inline TextOutput& indent(TextOutput& to) +{ + to.moveIndent(1); + return to; +} + +inline TextOutput& dedent(TextOutput& to) +{ + to.moveIndent(-1); + return to; +} + +inline TextOutput& operator<<(TextOutput& to, const char* str) +{ + to.print(str, strlen(str)); + return to; +} + +inline TextOutput& operator<<(TextOutput& to, char c) +{ + to.print(&c, 1); + return to; +} + +inline TextOutput& operator<<(TextOutput& to, TextOutputManipFunc func) +{ + return (*func)(to); +} + +inline TypeCode::TypeCode(uint32_t code) : mCode(code) { } +inline TypeCode::~TypeCode() { } +inline uint32_t TypeCode::typeCode() const { return mCode; } + +inline HexDump::~HexDump() { } + +inline HexDump& HexDump::setBytesPerLine(size_t bytesPerLine) { + mBytesPerLine = bytesPerLine; return *this; +} +inline HexDump& HexDump::setSingleLineCutoff(int32_t bytes) { + mSingleLineCutoff = bytes; return *this; +} +inline HexDump& HexDump::setAlignment(size_t alignment) { + mAlignment = alignment; return *this; +} +inline HexDump& HexDump::setCArrayStyle(bool enabled) { + mCArrayStyle = enabled; return *this; +} + +inline const void* HexDump::buffer() const { return mBuffer; } +inline size_t HexDump::size() const { return mSize; } +inline size_t HexDump::bytesPerLine() const { return mBytesPerLine; } +inline int32_t HexDump::singleLineCutoff() const { return mSingleLineCutoff; } +inline size_t HexDump::alignment() const { return mAlignment; } +inline bool HexDump::carrayStyle() const { return mCArrayStyle; } + +// --------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_TEXTOUTPUT_H diff --git a/include/utils/TimeUtils.h b/include/utils/TimeUtils.h new file mode 100644 index 000000000..b19e02126 --- /dev/null +++ b/include/utils/TimeUtils.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2005 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_TIME_H +#define ANDROID_TIME_H + +#include +#include +#include +#include +#include +#include +#include + +namespace android { + +/* + * This class is the core implementation of the android.util.Time java + * class. It doesn't implement some of the methods that are implemented + * in Java. They could be done here, but it's not expected that this class + * will be used. If that assumption is incorrect, feel free to update this + * file. The reason to do it here is to not mix the implementation of this + * class and the jni glue code. + */ +class Time +{ +public: + struct tm t; + + // this object doesn't own this string + const char *timezone; + + enum { + SEC = 1, + MIN = 2, + HOUR = 3, + MDAY = 4, + MON = 5, + YEAR = 6, + WDAY = 7, + YDAY = 8 + }; + + static int compare(Time& a, Time& b); + + Time(); + + void switchTimezone(const char *timezone); + String8 format(const char *format, const struct strftime_locale *locale) const; + void format2445(short* buf, bool hasTime) const; + String8 toString() const; + void setToNow(); + int64_t toMillis(bool ignoreDst); + void set(int64_t millis); + + inline void set(int sec, int min, int hour, int mday, int mon, int year, + int isdst) + { + this->t.tm_sec = sec; + this->t.tm_min = min; + this->t.tm_hour = hour; + this->t.tm_mday = mday; + this->t.tm_mon = mon; + this->t.tm_year = year; + this->t.tm_isdst = isdst; +#ifdef HAVE_TM_GMTOFF + this->t.tm_gmtoff = 0; +#endif + this->t.tm_wday = 0; + this->t.tm_yday = 0; + } +}; + +}; // namespace android + +#endif // ANDROID_TIME_H diff --git a/include/utils/TimerProbe.h b/include/utils/TimerProbe.h new file mode 100644 index 000000000..f2e32b21a --- /dev/null +++ b/include/utils/TimerProbe.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_TIMER_PROBE_H +#define ANDROID_TIMER_PROBE_H + +#if 0 && defined(HAVE_POSIX_CLOCKS) +#define ENABLE_TIMER_PROBE 1 +#else +#define ENABLE_TIMER_PROBE 0 +#endif + +#if ENABLE_TIMER_PROBE + +#include +#include +#include + +#define TIMER_PROBE(tag) \ + static int _timer_slot_; \ + android::TimerProbe probe(tag, &_timer_slot_) +#define TIMER_PROBE_END() probe.end() +#else +#define TIMER_PROBE(tag) +#define TIMER_PROBE_END() +#endif + +#if ENABLE_TIMER_PROBE +namespace android { + +class TimerProbe { +public: + TimerProbe(const char tag[], int* slot); + void end(); + ~TimerProbe(); +private: + struct Bucket { + int mStart, mReal, mProcess, mThread, mCount; + const char* mTag; + int* mSlotPtr; + int mIndent; + }; + static Vector gBuckets; + static TimerProbe* gExecuteChain; + static int gIndent; + static timespec gRealBase; + TimerProbe* mNext; + static uint32_t ElapsedTime(const timespec& start, const timespec& end); + void print(const timespec& r, const timespec& p, const timespec& t) const; + timespec mRealStart, mPStart, mTStart; + const char* mTag; + int mIndent; + int mBucket; +}; + +}; // namespace android + +#endif +#endif diff --git a/include/utils/Timers.h b/include/utils/Timers.h new file mode 100644 index 000000000..96103995b --- /dev/null +++ b/include/utils/Timers.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. + */ + +// +// Timer functions. +// +#ifndef _LIBS_UTILS_TIMERS_H +#define _LIBS_UTILS_TIMERS_H + +#include +#include +#include + +// ------------------------------------------------------------------ +// 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 +}; + +// 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 + +// return the system-time according to the specified clock +int sleepForInterval(long interval, struct timeval* pNextTick); + +#ifdef __cplusplus +} // extern "C" +#endif + +// ------------------------------------------------------------------ +// C++ API + +#ifdef __cplusplus + +namespace android { +/* + * Time the duration of something. + * + * Includes some timeval manipulation functions. + */ +class DurationTimer { +public: + DurationTimer(void) {} + ~DurationTimer(void) {} + + // Start the timer. + void start(void); + // Stop the timer. + void stop(void); + // Get the duration in microseconds. + long long durationUsecs(void) const; + + // Subtract two timevals. Returns the difference (ptv1-ptv2) in + // microseconds. + static long long subtractTimevals(const struct timeval* ptv1, + const struct timeval* ptv2); + + // Add the specified amount of time to the timeval. + static void addToTimeval(struct timeval* ptv, long usec); + +private: + struct timeval mStartWhen; + struct timeval mStopWhen; +}; + +}; // android +#endif // def __cplusplus + +#endif // _LIBS_UTILS_TIMERS_H diff --git a/include/utils/TypeHelpers.h b/include/utils/TypeHelpers.h new file mode 100644 index 000000000..c04c37fa9 --- /dev/null +++ b/include/utils/TypeHelpers.h @@ -0,0 +1,254 @@ +/* + * 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 +#include +#include +#include + +// --------------------------------------------------------------------------- + +namespace android { + +/* + * Types traits + */ + +template struct trait_trivial_ctor { enum { value = false }; }; +template struct trait_trivial_dtor { enum { value = false }; }; +template struct trait_trivial_copy { enum { value = false }; }; +template struct trait_trivial_assign{ enum { value = false }; }; + +template struct trait_pointer { enum { value = false }; }; +template struct trait_pointer { enum { value = true }; }; + +#define ANDROID_BASIC_TYPES_TRAITS( T ) \ + template<> struct trait_trivial_ctor< T > { enum { value = true }; }; \ + template<> struct trait_trivial_dtor< T > { enum { value = true }; }; \ + template<> struct trait_trivial_copy< T > { enum { value = true }; }; \ + template<> struct trait_trivial_assign< T >{ enum { value = true }; }; + +#define ANDROID_TYPE_TRAITS( T, ctor, dtor, copy, assign ) \ + template<> struct trait_trivial_ctor< T > { enum { value = ctor }; }; \ + template<> struct trait_trivial_dtor< T > { enum { value = dtor }; }; \ + template<> struct trait_trivial_copy< T > { enum { value = copy }; }; \ + template<> struct trait_trivial_assign< T >{ enum { value = assign }; }; + +template +struct traits { + enum { + is_pointer = trait_pointer::value, + has_trivial_ctor = is_pointer || trait_trivial_ctor::value, + has_trivial_dtor = is_pointer || trait_trivial_dtor::value, + has_trivial_copy = is_pointer || trait_trivial_copy::value, + has_trivial_assign = is_pointer || trait_trivial_assign::value + }; +}; + +template +struct aggregate_traits { + enum { + is_pointer = false, + has_trivial_ctor = traits::has_trivial_ctor && traits::has_trivial_ctor, + has_trivial_dtor = traits::has_trivial_dtor && traits::has_trivial_dtor, + has_trivial_copy = traits::has_trivial_copy && traits::has_trivial_copy, + has_trivial_assign = traits::has_trivial_assign && traits::has_trivial_assign + }; +}; + +// --------------------------------------------------------------------------- + +/* + * basic types traits + */ + +ANDROID_BASIC_TYPES_TRAITS( void ); +ANDROID_BASIC_TYPES_TRAITS( bool ); +ANDROID_BASIC_TYPES_TRAITS( char ); +ANDROID_BASIC_TYPES_TRAITS( unsigned char ); +ANDROID_BASIC_TYPES_TRAITS( short ); +ANDROID_BASIC_TYPES_TRAITS( unsigned short ); +ANDROID_BASIC_TYPES_TRAITS( int ); +ANDROID_BASIC_TYPES_TRAITS( unsigned int ); +ANDROID_BASIC_TYPES_TRAITS( long ); +ANDROID_BASIC_TYPES_TRAITS( unsigned long ); +ANDROID_BASIC_TYPES_TRAITS( long long ); +ANDROID_BASIC_TYPES_TRAITS( unsigned long long ); +ANDROID_BASIC_TYPES_TRAITS( float ); +ANDROID_BASIC_TYPES_TRAITS( double ); + +// --------------------------------------------------------------------------- + + +/* + * compare and order types + */ + +template inline +int strictly_order_type(const TYPE& lhs, const TYPE& rhs) { + return (lhs < rhs) ? 1 : 0; +} + +template inline +int compare_type(const TYPE& lhs, const TYPE& rhs) { + return strictly_order_type(rhs, lhs) - strictly_order_type(lhs, rhs); +} + +/* + * create, destroy, copy and assign types... + */ + +template inline +void construct_type(TYPE* p, size_t n) { + if (!traits::has_trivial_ctor) { + while (n--) { + new(p++) TYPE; + } + } +} + +template inline +void destroy_type(TYPE* p, size_t n) { + if (!traits::has_trivial_dtor) { + while (n--) { + p->~TYPE(); + p++; + } + } +} + +template inline +void copy_type(TYPE* d, const TYPE* s, size_t n) { + if (!traits::has_trivial_copy) { + while (n--) { + new(d) TYPE(*s); + d++, s++; + } + } else { + memcpy(d,s,n*sizeof(TYPE)); + } +} + +template inline +void assign_type(TYPE* d, const TYPE* s, size_t n) { + if (!traits::has_trivial_assign) { + while (n--) { + *d++ = *s++; + } + } else { + memcpy(d,s,n*sizeof(TYPE)); + } +} + +template inline +void splat_type(TYPE* where, const TYPE* what, size_t n) { + if (!traits::has_trivial_copy) { + while (n--) { + new(where) TYPE(*what); + where++; + } + } else { + while (n--) { + *where++ = *what; + } + } +} + +template inline +void move_forward_type(TYPE* d, const TYPE* s, size_t n = 1) { + if (!traits::has_trivial_copy || !traits::has_trivial_dtor) { + d += n; + s += n; + while (n--) { + --d, --s; + if (!traits::has_trivial_copy) { + new(d) TYPE(*s); + } else { + *d = *s; + } + if (!traits::has_trivial_dtor) { + s->~TYPE(); + } + } + } else { + memmove(d,s,n*sizeof(TYPE)); + } +} + +template inline +void move_backward_type(TYPE* d, const TYPE* s, size_t n = 1) { + if (!traits::has_trivial_copy || !traits::has_trivial_dtor) { + while (n--) { + if (!traits::has_trivial_copy) { + new(d) TYPE(*s); + } else { + *d = *s; + } + if (!traits::has_trivial_dtor) { + s->~TYPE(); + } + d++, s++; + } + } else { + memmove(d,s,n*sizeof(TYPE)); + } +} +// --------------------------------------------------------------------------- + +/* + * a key/value pair + */ + +template +struct key_value_pair_t { + KEY key; + VALUE value; + key_value_pair_t() { } + key_value_pair_t(const key_value_pair_t& o) : key(o.key), value(o.value) { } + key_value_pair_t(const KEY& k, const VALUE& v) : key(k), value(v) { } + key_value_pair_t(const KEY& k) : key(k) { } + inline bool operator < (const key_value_pair_t& o) const { + return strictly_order_type(key, o.key); + } +}; + +template<> +template +struct trait_trivial_ctor< key_value_pair_t > +{ enum { value = aggregate_traits::has_trivial_ctor }; }; +template<> +template +struct trait_trivial_dtor< key_value_pair_t > +{ enum { value = aggregate_traits::has_trivial_dtor }; }; +template<> +template +struct trait_trivial_copy< key_value_pair_t > +{ enum { value = aggregate_traits::has_trivial_copy }; }; +template<> +template +struct trait_trivial_assign< key_value_pair_t > +{ enum { value = aggregate_traits::has_trivial_assign};}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_TYPE_HELPERS_H diff --git a/include/utils/Vector.h b/include/utils/Vector.h new file mode 100644 index 000000000..be365d83e --- /dev/null +++ b/include/utils/Vector.h @@ -0,0 +1,359 @@ +/* + * 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 +#include +#include + +#include +#include +#include + +// --------------------------------------------------------------------------- + +namespace android { + +/*! + * The main templated vector class ensuring type safety + * while making use of VectorImpl. + * This is the class users want to use. + */ + +template +class Vector : private VectorImpl +{ +public: + typedef TYPE value_type; + + /*! + * Constructors and destructors + */ + + Vector(); + Vector(const Vector& rhs); + virtual ~Vector(); + + /*! copy operator */ + const Vector& operator = (const Vector& rhs) const; + Vector& operator = (const Vector& rhs); + + /* + * empty the vector + */ + + inline void clear() { VectorImpl::clear(); } + + /*! + * vector stats + */ + + //! returns number of items in the vector + inline size_t size() const { return VectorImpl::size(); } + //! returns wether or not the vector is empty + inline bool isEmpty() const { return VectorImpl::isEmpty(); } + //! returns how many items can be stored without reallocating the backing store + inline size_t capacity() const { return VectorImpl::capacity(); } + //! setst the capacity. capacity can never be reduced less than size() + inline ssize_t setCapacity(size_t size) { return VectorImpl::setCapacity(size); } + + /*! + * C-style array access + */ + + //! read-only C-style access + inline const TYPE* array() const; + //! read-write C-style access + TYPE* editArray(); + + /*! + * accessors + */ + + //! read-only access to an item at a given index + inline const TYPE& operator [] (size_t index) const; + //! alternate name for operator [] + inline const TYPE& itemAt(size_t index) const; + //! stack-usage of the vector. returns the top of the stack (last element) + const TYPE& top() const; + //! same as operator [], but allows to access the vector backward (from the end) with a negative index + const TYPE& mirrorItemAt(ssize_t index) const; + + /*! + * modifing the array + */ + + //! copy-on write support, grants write access to an item + TYPE& editItemAt(size_t index); + //! grants right acces to the top of the stack (last element) + TYPE& editTop(); + + /*! + * append/insert another vector + */ + + //! insert another vector at a given index + ssize_t insertVectorAt(const Vector& vector, size_t index); + + //! append another vector at the end of this one + ssize_t appendVector(const Vector& vector); + + + /*! + * add/insert/replace items + */ + + //! insert one or several items initialized with their default constructor + inline ssize_t insertAt(size_t index, size_t numItems = 1); + //! insert on onr several items initialized from a prototype item + ssize_t insertAt(const TYPE& prototype_item, size_t index, size_t numItems = 1); + //! pop the top of the stack (removes the last element). No-op if the stack's empty + inline void pop(); + //! pushes an item initialized with its default constructor + inline void push(); + //! pushes an item on the top of the stack + void push(const TYPE& item); + //! same as push() but returns the index the item was added at (or an error) + inline ssize_t add(); + //! same as push() but returns the index the item was added at (or an error) + ssize_t add(const TYPE& item); + //! replace an item with a new one initialized with its default constructor + inline ssize_t replaceAt(size_t index); + //! replace an item with a new one + ssize_t replaceAt(const TYPE& item, size_t index); + + /*! + * remove items + */ + + //! remove several items + inline ssize_t removeItemsAt(size_t index, size_t count = 1); + //! remove one item + inline ssize_t removeAt(size_t index) { return removeItemsAt(index); } + + /*! + * sort (stable) the array + */ + + typedef int (*compar_t)(const TYPE* lhs, const TYPE* rhs); + typedef int (*compar_r_t)(const TYPE* lhs, const TYPE* rhs, void* state); + + inline status_t sort(compar_t cmp); + inline status_t sort(compar_r_t cmp, void* state); + +protected: + virtual void do_construct(void* storage, size_t num) const; + virtual void do_destroy(void* storage, size_t num) const; + virtual void do_copy(void* dest, const void* from, size_t num) const; + virtual void do_splat(void* dest, const void* item, size_t num) const; + virtual void do_move_forward(void* dest, const void* from, size_t num) const; + virtual void do_move_backward(void* dest, const void* from, size_t num) const; +}; + + +// --------------------------------------------------------------------------- +// No user serviceable parts from here... +// --------------------------------------------------------------------------- + +template inline +Vector::Vector() + : VectorImpl(sizeof(TYPE), + ((traits::has_trivial_ctor ? HAS_TRIVIAL_CTOR : 0) + |(traits::has_trivial_dtor ? HAS_TRIVIAL_DTOR : 0) + |(traits::has_trivial_copy ? HAS_TRIVIAL_COPY : 0) + |(traits::has_trivial_assign ? HAS_TRIVIAL_ASSIGN : 0)) + ) +{ +} + +template inline +Vector::Vector(const Vector& rhs) + : VectorImpl(rhs) { +} + +template inline +Vector::~Vector() { + finish_vector(); +} + +template inline +Vector& Vector::operator = (const Vector& rhs) { + VectorImpl::operator = (rhs); + return *this; +} + +template inline +const Vector& Vector::operator = (const Vector& rhs) const { + VectorImpl::operator = (rhs); + return *this; +} + +template inline +const TYPE* Vector::array() const { + return static_cast(arrayImpl()); +} + +template inline +TYPE* Vector::editArray() { + return static_cast(editArrayImpl()); +} + + +template inline +const TYPE& Vector::operator[](size_t index) const { + LOG_FATAL_IF( index>=size(), + "itemAt: index %d is past size %d", (int)index, (int)size() ); + return *(array() + index); +} + +template inline +const TYPE& Vector::itemAt(size_t index) const { + return operator[](index); +} + +template inline +const TYPE& Vector::mirrorItemAt(ssize_t index) const { + LOG_FATAL_IF( (index>0 ? index : -index)>=size(), + "mirrorItemAt: index %d is past size %d", + (int)index, (int)size() ); + return *(array() + ((index<0) ? (size()-index) : index)); +} + +template inline +const TYPE& Vector::top() const { + return *(array() + size() - 1); +} + +template inline +TYPE& Vector::editItemAt(size_t index) { + return *( static_cast(editItemLocation(index)) ); +} + +template inline +TYPE& Vector::editTop() { + return *( static_cast(editItemLocation(size()-1)) ); +} + +template inline +ssize_t Vector::insertVectorAt(const Vector& vector, size_t index) { + return VectorImpl::insertVectorAt(reinterpret_cast(vector), index); +} + +template inline +ssize_t Vector::appendVector(const Vector& vector) { + return VectorImpl::appendVector(reinterpret_cast(vector)); +} + +template inline +ssize_t Vector::insertAt(const TYPE& item, size_t index, size_t numItems) { + return VectorImpl::insertAt(&item, index, numItems); +} + +template inline +void Vector::push(const TYPE& item) { + return VectorImpl::push(&item); +} + +template inline +ssize_t Vector::add(const TYPE& item) { + return VectorImpl::add(&item); +} + +template inline +ssize_t Vector::replaceAt(const TYPE& item, size_t index) { + return VectorImpl::replaceAt(&item, index); +} + +template inline +ssize_t Vector::insertAt(size_t index, size_t numItems) { + return VectorImpl::insertAt(index, numItems); +} + +template inline +void Vector::pop() { + VectorImpl::pop(); +} + +template inline +void Vector::push() { + VectorImpl::push(); +} + +template inline +ssize_t Vector::add() { + return VectorImpl::add(); +} + +template inline +ssize_t Vector::replaceAt(size_t index) { + return VectorImpl::replaceAt(index); +} + +template inline +ssize_t Vector::removeItemsAt(size_t index, size_t count) { + return VectorImpl::removeItemsAt(index, count); +} + +template inline +status_t Vector::sort(Vector::compar_t cmp) { + return VectorImpl::sort((VectorImpl::compar_t)cmp); +} + +template inline +status_t Vector::sort(Vector::compar_r_t cmp, void* state) { + return VectorImpl::sort((VectorImpl::compar_r_t)cmp, state); +} + +// --------------------------------------------------------------------------- + +template +void Vector::do_construct(void* storage, size_t num) const { + construct_type( reinterpret_cast(storage), num ); +} + +template +void Vector::do_destroy(void* storage, size_t num) const { + destroy_type( reinterpret_cast(storage), num ); +} + +template +void Vector::do_copy(void* dest, const void* from, size_t num) const { + copy_type( reinterpret_cast(dest), reinterpret_cast(from), num ); +} + +template +void Vector::do_splat(void* dest, const void* item, size_t num) const { + splat_type( reinterpret_cast(dest), reinterpret_cast(item), num ); +} + +template +void Vector::do_move_forward(void* dest, const void* from, size_t num) const { + move_forward_type( reinterpret_cast(dest), reinterpret_cast(from), num ); +} + +template +void Vector::do_move_backward(void* dest, const void* from, size_t num) const { + move_backward_type( reinterpret_cast(dest), reinterpret_cast(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 000000000..2525229be --- /dev/null +++ b/include/utils/VectorImpl.h @@ -0,0 +1,199 @@ +/* + * 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 +#include +#include +#include + +// --------------------------------------------------------------------------- +// 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, + HAS_TRIVIAL_ASSIGN = 0x00000008 + }; + + VectorImpl(size_t itemSize, uint32_t flags); + VectorImpl(const VectorImpl& rhs); + virtual ~VectorImpl(); + + /*! must be called from subclasses destructor */ + void finish_vector(); + + VectorImpl& operator = (const VectorImpl& rhs); + + /*! C-style array access */ + inline const void* arrayImpl() const { return mStorage; } + void* editArrayImpl(); + + /*! vector stats */ + inline size_t size() const { return mCount; } + inline bool isEmpty() const { return mCount == 0; } + size_t capacity() const; + ssize_t setCapacity(size_t size); + + /*! append/insert another vector */ + ssize_t insertVectorAt(const VectorImpl& vector, size_t index); + ssize_t appendVector(const VectorImpl& vector); + + /*! add/insert/replace items */ + ssize_t insertAt(size_t where, size_t numItems = 1); + ssize_t insertAt(const void* item, size_t where, size_t numItems = 1); + void pop(); + void push(); + void push(const void* item); + ssize_t add(); + ssize_t add(const void* item); + ssize_t replaceAt(size_t index); + ssize_t replaceAt(const void* item, size_t index); + + /*! remove items */ + ssize_t removeItemsAt(size_t index, size_t count = 1); + void clear(); + + const void* itemLocation(size_t index) const; + void* editItemLocation(size_t index); + + 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; + + // take care of FBC... + virtual void reservedVectorImpl1(); + virtual void reservedVectorImpl2(); + virtual void reservedVectorImpl3(); + virtual void reservedVectorImpl4(); + virtual void reservedVectorImpl5(); + virtual void reservedVectorImpl6(); + virtual void reservedVectorImpl7(); + virtual void reservedVectorImpl8(); + +private: + void* _grow(size_t where, size_t amount); + void _shrink(size_t where, size_t amount); + + inline void _do_construct(void* storage, size_t num) const; + inline void _do_destroy(void* storage, size_t num) const; + inline void _do_copy(void* dest, const void* from, size_t num) const; + inline void _do_splat(void* dest, const void* item, size_t num) const; + inline void _do_move_forward(void* dest, const void* from, size_t num) const; + inline void _do_move_backward(void* dest, const void* from, size_t num) const; + + // These 2 fields are exposed in the inlines below, + // so they're set in stone. + void * mStorage; // base address of the vector + size_t mCount; // number of items + + const uint32_t mFlags; + const size_t mItemSize; +}; + + + +class SortedVectorImpl : public VectorImpl +{ +public: + SortedVectorImpl(size_t itemSize, uint32_t flags); + SortedVectorImpl(const VectorImpl& rhs); + virtual ~SortedVectorImpl(); + + SortedVectorImpl& operator = (const SortedVectorImpl& rhs); + + //! finds the index of an item + ssize_t indexOf(const void* item) const; + + //! finds where this item should be inserted + size_t orderOf(const void* item) const; + + //! add an item in the right place (or replaces it if there is one) + ssize_t add(const void* item); + + //! merges a vector into this one + ssize_t merge(const VectorImpl& vector); + ssize_t merge(const SortedVectorImpl& vector); + + //! removes an item + ssize_t remove(const void* item); + +protected: + virtual int do_compare(const void* lhs, const void* rhs) const = 0; + + // take care of FBC... + virtual void reservedSortedVectorImpl1(); + virtual void reservedSortedVectorImpl2(); + virtual void reservedSortedVectorImpl3(); + virtual void reservedSortedVectorImpl4(); + virtual void reservedSortedVectorImpl5(); + virtual void reservedSortedVectorImpl6(); + virtual void reservedSortedVectorImpl7(); + virtual void reservedSortedVectorImpl8(); + +private: + ssize_t _indexOrderOf(const void* item, size_t* order = 0) const; + + // these are made private, because they can't be used on a SortedVector + // (they don't have an implementation either) + ssize_t add(); + void pop(); + void push(); + void push(const void* item); + ssize_t insertVectorAt(const VectorImpl& vector, size_t index); + ssize_t appendVector(const VectorImpl& vector); + ssize_t insertAt(size_t where, size_t numItems = 1); + ssize_t insertAt(const void* item, size_t where, size_t numItems = 1); + ssize_t replaceAt(size_t index); + ssize_t replaceAt(const void* item, size_t index); +}; + +}; // namespace android + + +// --------------------------------------------------------------------------- + +#endif // ANDROID_VECTOR_IMPL_H diff --git a/include/utils/ZipEntry.h b/include/utils/ZipEntry.h new file mode 100644 index 000000000..e4698dfbb --- /dev/null +++ b/include/utils/ZipEntry.h @@ -0,0 +1,345 @@ +/* + * 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. + */ + +// +// Zip archive entries. +// +// The ZipEntry class is tightly meshed with the ZipFile class. +// +#ifndef __LIBS_ZIPENTRY_H +#define __LIBS_ZIPENTRY_H + +#include "Errors.h" + +#include +#include + +namespace android { + +class ZipFile; + +/* + * ZipEntry objects represent a single entry in a Zip archive. + * + * You can use one of these to get or set information about an entry, but + * there are no functions here for accessing the data itself. (We could + * tuck a pointer to the ZipFile in here for convenience, but that raises + * the likelihood of using ZipEntry objects after discarding the ZipFile.) + * + * File information is stored in two places: next to the file data (the Local + * File Header, and possibly a Data Descriptor), and at the end of the file + * (the Central Directory Entry). The two must be kept in sync. + */ +class ZipEntry { +public: + friend class ZipFile; + + ZipEntry(void) + : mDeleted(false), mMarked(false) + {} + ~ZipEntry(void) {} + + /* + * Returns "true" if the data is compressed. + */ + bool isCompressed(void) const { + return mCDE.mCompressionMethod != kCompressStored; + } + int getCompressionMethod(void) const { return mCDE.mCompressionMethod; } + + /* + * Return the uncompressed length. + */ + off_t getUncompressedLen(void) const { return mCDE.mUncompressedSize; } + + /* + * Return the compressed length. For uncompressed data, this returns + * the same thing as getUncompresesdLen(). + */ + off_t getCompressedLen(void) const { return mCDE.mCompressedSize; } + + /* + * Return the absolute file offset of the start of the compressed or + * uncompressed data. + */ + off_t getFileOffset(void) const { + return mCDE.mLocalHeaderRelOffset + + LocalFileHeader::kLFHLen + + mLFH.mFileNameLength + + mLFH.mExtraFieldLength; + } + + /* + * Return the data CRC. + */ + unsigned long getCRC32(void) const { return mCDE.mCRC32; } + + /* + * Return file modification time in UNIX seconds-since-epoch. + */ + time_t getModWhen(void) const; + + /* + * Return the archived file name. + */ + const char* getFileName(void) const { return (const char*) mCDE.mFileName; } + + /* + * Application-defined "mark". Can be useful when synchronizing the + * contents of an archive with contents on disk. + */ + bool getMarked(void) const { return mMarked; } + void setMarked(bool val) { mMarked = val; } + + /* + * Some basic functions for raw data manipulation. "LE" means + * Little Endian. + */ + static inline unsigned short getShortLE(const unsigned char* buf) { + return buf[0] | (buf[1] << 8); + } + static inline unsigned long getLongLE(const unsigned char* buf) { + return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); + } + static inline void putShortLE(unsigned char* buf, short val) { + buf[0] = (unsigned char) val; + buf[1] = (unsigned char) (val >> 8); + } + static inline void putLongLE(unsigned char* buf, long val) { + buf[0] = (unsigned char) val; + buf[1] = (unsigned char) (val >> 8); + buf[2] = (unsigned char) (val >> 16); + buf[3] = (unsigned char) (val >> 24); + } + + /* defined for Zip archives */ + enum { + kCompressStored = 0, // no compression + // shrunk = 1, + // reduced 1 = 2, + // reduced 2 = 3, + // reduced 3 = 4, + // reduced 4 = 5, + // imploded = 6, + // tokenized = 7, + kCompressDeflated = 8, // standard deflate + // Deflate64 = 9, + // lib imploded = 10, + // reserved = 11, + // bzip2 = 12, + }; + + /* + * Deletion flag. If set, the entry will be removed on the next + * call to "flush". + */ + bool getDeleted(void) const { return mDeleted; } + +protected: + /* + * Initialize the structure from the file, which is pointing at + * our Central Directory entry. + */ + status_t initFromCDE(FILE* fp); + + /* + * Initialize the structure for a new file. We need the filename + * and comment so that we can properly size the LFH area. The + * filename is mandatory, the comment is optional. + */ + void initNew(const char* fileName, const char* comment); + + /* + * Initialize the structure with the contents of a ZipEntry from + * another file. + */ + status_t initFromExternal(const ZipFile* pZipFile, const ZipEntry* pEntry); + + /* + * Add some pad bytes to the LFH. We do this by adding or resizing + * the "extra" field. + */ + status_t addPadding(int padding); + + /* + * Set information about the data for this entry. + */ + void setDataInfo(long uncompLen, long compLen, unsigned long crc32, + int compressionMethod); + + /* + * Set the modification date. + */ + void setModWhen(time_t when); + + /* + * Return the offset of the local file header. + */ + off_t getLFHOffset(void) const { return mCDE.mLocalHeaderRelOffset; } + + /* + * Set the offset of the local file header, relative to the start of + * the current file. + */ + void setLFHOffset(off_t offset) { + mCDE.mLocalHeaderRelOffset = (long) offset; + } + + /* mark for deletion; used by ZipFile::remove() */ + void setDeleted(void) { mDeleted = true; } + +private: + /* these are private and not defined */ + ZipEntry(const ZipEntry& src); + ZipEntry& operator=(const ZipEntry& src); + + /* returns "true" if the CDE and the LFH agree */ + bool compareHeaders(void) const; + void copyCDEtoLFH(void); + + bool mDeleted; // set if entry is pending deletion + bool mMarked; // app-defined marker + + /* + * Every entry in the Zip archive starts off with one of these. + */ + class LocalFileHeader { + public: + LocalFileHeader(void) : + mVersionToExtract(0), + mGPBitFlag(0), + mCompressionMethod(0), + mLastModFileTime(0), + mLastModFileDate(0), + mCRC32(0), + mCompressedSize(0), + mUncompressedSize(0), + mFileNameLength(0), + mExtraFieldLength(0), + mFileName(NULL), + mExtraField(NULL) + {} + virtual ~LocalFileHeader(void) { + delete[] mFileName; + delete[] mExtraField; + } + + status_t read(FILE* fp); + status_t write(FILE* fp); + + // unsigned long mSignature; + unsigned short mVersionToExtract; + unsigned short mGPBitFlag; + unsigned short mCompressionMethod; + unsigned short mLastModFileTime; + unsigned short mLastModFileDate; + unsigned long mCRC32; + unsigned long mCompressedSize; + unsigned long mUncompressedSize; + unsigned short mFileNameLength; + unsigned short mExtraFieldLength; + unsigned char* mFileName; + unsigned char* mExtraField; + + enum { + kSignature = 0x04034b50, + kLFHLen = 30, // LocalFileHdr len, excl. var fields + }; + + void dump(void) const; + }; + + /* + * Every entry in the Zip archive has one of these in the "central + * directory" at the end of the file. + */ + class CentralDirEntry { + public: + CentralDirEntry(void) : + mVersionMadeBy(0), + mVersionToExtract(0), + mGPBitFlag(0), + mCompressionMethod(0), + mLastModFileTime(0), + mLastModFileDate(0), + mCRC32(0), + mCompressedSize(0), + mUncompressedSize(0), + mFileNameLength(0), + mExtraFieldLength(0), + mFileCommentLength(0), + mDiskNumberStart(0), + mInternalAttrs(0), + mExternalAttrs(0), + mLocalHeaderRelOffset(0), + mFileName(NULL), + mExtraField(NULL), + mFileComment(NULL) + {} + virtual ~CentralDirEntry(void) { + delete[] mFileName; + delete[] mExtraField; + delete[] mFileComment; + } + + status_t read(FILE* fp); + status_t write(FILE* fp); + + // unsigned long mSignature; + unsigned short mVersionMadeBy; + unsigned short mVersionToExtract; + unsigned short mGPBitFlag; + unsigned short mCompressionMethod; + unsigned short mLastModFileTime; + unsigned short mLastModFileDate; + unsigned long mCRC32; + unsigned long mCompressedSize; + unsigned long mUncompressedSize; + unsigned short mFileNameLength; + unsigned short mExtraFieldLength; + unsigned short mFileCommentLength; + unsigned short mDiskNumberStart; + unsigned short mInternalAttrs; + unsigned long mExternalAttrs; + unsigned long mLocalHeaderRelOffset; + unsigned char* mFileName; + unsigned char* mExtraField; + unsigned char* mFileComment; + + void dump(void) const; + + enum { + kSignature = 0x02014b50, + kCDELen = 46, // CentralDirEnt len, excl. var fields + }; + }; + + enum { + //kDataDescriptorSignature = 0x08074b50, // currently unused + kDataDescriptorLen = 16, // four 32-bit fields + + kDefaultVersion = 20, // need deflate, nothing much else + kDefaultMadeBy = 0x0317, // 03=UNIX, 17=spec v2.3 + kUsesDataDescr = 0x0008, // GPBitFlag bit 3 + }; + + LocalFileHeader mLFH; + CentralDirEntry mCDE; +}; + +}; // namespace android + +#endif // __LIBS_ZIPENTRY_H diff --git a/include/utils/ZipFile.h b/include/utils/ZipFile.h new file mode 100644 index 000000000..44df5bbaa --- /dev/null +++ b/include/utils/ZipFile.h @@ -0,0 +1,269 @@ +/* + * 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. + */ + +// +// General-purpose Zip archive access. This class allows both reading and +// writing to Zip archives, including deletion of existing entries. +// +#ifndef __LIBS_ZIPFILE_H +#define __LIBS_ZIPFILE_H + +#include "ZipEntry.h" +#include "Vector.h" +#include "Errors.h" +#include + +namespace android { + +/* + * Manipulate a Zip archive. + * + * Some changes will not be visible in the until until "flush" is called. + * + * The correct way to update a file archive is to make all changes to a + * copy of the archive in a temporary file, and then unlink/rename over + * the original after everything completes. Because we're only interested + * in using this for packaging, we don't worry about such things. Crashing + * after making changes and before flush() completes could leave us with + * an unusable Zip archive. + */ +class ZipFile { +public: + ZipFile(void) + : mZipFp(NULL), mReadOnly(false), mNeedCDRewrite(false) + {} + ~ZipFile(void) { + if (!mReadOnly) + flush(); + if (mZipFp != NULL) + fclose(mZipFp); + discardEntries(); + } + + /* + * Open a new or existing archive. + */ + typedef enum { + kOpenReadOnly = 0x01, + kOpenReadWrite = 0x02, + kOpenCreate = 0x04, // create if it doesn't exist + kOpenTruncate = 0x08, // if it exists, empty it + }; + status_t open(const char* zipFileName, int flags); + + /* + * Add a file to the end of the archive. Specify whether you want the + * library to try to store it compressed. + * + * If "storageName" is specified, the archive will use that instead + * of "fileName". + * + * If there is already an entry with the same name, the call fails. + * Existing entries with the same name must be removed first. + * + * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. + */ + status_t add(const char* fileName, int compressionMethod, + ZipEntry** ppEntry) + { + return add(fileName, fileName, compressionMethod, ppEntry); + } + status_t add(const char* fileName, const char* storageName, + int compressionMethod, ZipEntry** ppEntry) + { + return addCommon(fileName, NULL, 0, storageName, + ZipEntry::kCompressStored, + compressionMethod, ppEntry); + } + + /* + * Add a file that is already compressed with gzip. + * + * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. + */ + status_t addGzip(const char* fileName, const char* storageName, + ZipEntry** ppEntry) + { + return addCommon(fileName, NULL, 0, storageName, + ZipEntry::kCompressDeflated, + ZipEntry::kCompressDeflated, ppEntry); + } + + /* + * Add a file from an in-memory data buffer. + * + * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. + */ + status_t add(const void* data, size_t size, const char* storageName, + int compressionMethod, ZipEntry** ppEntry) + { + return addCommon(NULL, data, size, storageName, + ZipEntry::kCompressStored, + compressionMethod, ppEntry); + } + + /* + * Add an entry by copying it from another zip file. If "padding" is + * nonzero, the specified number of bytes will be added to the "extra" + * field in the header. + * + * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. + */ + status_t add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry, + int padding, ZipEntry** ppEntry); + + /* + * Mark an entry as having been removed. It is not actually deleted + * from the archive or our internal data structures until flush() is + * called. + */ + status_t remove(ZipEntry* pEntry); + + /* + * Flush changes. If mNeedCDRewrite is set, this writes the central dir. + */ + status_t flush(void); + + /* + * Expand the data into the buffer provided. The buffer must hold + * at least bytes. Variation expands directly + * to a file. + * + * Returns "false" if an error was encountered in the compressed data. + */ + //bool uncompress(const ZipEntry* pEntry, void* buf) const; + //bool uncompress(const ZipEntry* pEntry, FILE* fp) const; + void* uncompress(const ZipEntry* pEntry); + + /* + * Get an entry, by name. Returns NULL if not found. + * + * Does not return entries pending deletion. + */ + ZipEntry* getEntryByName(const char* fileName) const; + + /* + * Get the Nth entry in the archive. + * + * This will return an entry that is pending deletion. + */ + int getNumEntries(void) const { return mEntries.size(); } + ZipEntry* getEntryByIndex(int idx) const; + +private: + /* these are private and not defined */ + ZipFile(const ZipFile& src); + ZipFile& operator=(const ZipFile& src); + + class EndOfCentralDir { + public: + EndOfCentralDir(void) : + mDiskNumber(0), + mDiskWithCentralDir(0), + mNumEntries(0), + mTotalNumEntries(0), + mCentralDirSize(0), + mCentralDirOffset(0), + mCommentLen(0), + mComment(NULL) + {} + virtual ~EndOfCentralDir(void) { + delete[] mComment; + } + + status_t readBuf(const unsigned char* buf, int len); + status_t write(FILE* fp); + + //unsigned long mSignature; + unsigned short mDiskNumber; + unsigned short mDiskWithCentralDir; + unsigned short mNumEntries; + unsigned short mTotalNumEntries; + unsigned long mCentralDirSize; + unsigned long mCentralDirOffset; // offset from first disk + unsigned short mCommentLen; + unsigned char* mComment; + + enum { + kSignature = 0x06054b50, + kEOCDLen = 22, // EndOfCentralDir len, excl. comment + + kMaxCommentLen = 65535, // longest possible in ushort + kMaxEOCDSearch = kMaxCommentLen + EndOfCentralDir::kEOCDLen, + + }; + + void dump(void) const; + }; + + + /* read all entries in the central dir */ + status_t readCentralDir(void); + + /* crunch deleted entries out */ + status_t crunchArchive(void); + + /* clean up mEntries */ + void discardEntries(void); + + /* common handler for all "add" functions */ + status_t addCommon(const char* fileName, const void* data, size_t size, + const char* storageName, int sourceType, int compressionMethod, + ZipEntry** ppEntry); + + /* copy all of "srcFp" into "dstFp" */ + status_t copyFpToFp(FILE* dstFp, FILE* srcFp, unsigned long* pCRC32); + /* copy all of "data" into "dstFp" */ + status_t copyDataToFp(FILE* dstFp, + const void* data, size_t size, unsigned long* pCRC32); + /* copy some of "srcFp" into "dstFp" */ + status_t copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length, + unsigned long* pCRC32); + /* like memmove(), but on parts of a single file */ + status_t filemove(FILE* fp, off_t dest, off_t src, size_t n); + /* compress all of "srcFp" into "dstFp", using Deflate */ + status_t compressFpToFp(FILE* dstFp, FILE* srcFp, + const void* data, size_t size, unsigned long* pCRC32); + + /* get modification date from a file descriptor */ + time_t getModTime(int fd); + + /* + * We use stdio FILE*, which gives us buffering but makes dealing + * with files >2GB awkward. Until we support Zip64, we're fine. + */ + FILE* mZipFp; // Zip file pointer + + /* one of these per file */ + EndOfCentralDir mEOCD; + + /* did we open this read-only? */ + bool mReadOnly; + + /* set this when we trash the central dir */ + bool mNeedCDRewrite; + + /* + * One ZipEntry per entry in the zip file. I'm using pointers instead + * of objects because it's easier than making operator= work for the + * classes and sub-classes. + */ + Vector mEntries; +}; + +}; // namespace android + +#endif // __LIBS_ZIPFILE_H diff --git a/include/utils/ZipFileCRO.h b/include/utils/ZipFileCRO.h new file mode 100644 index 000000000..30e00368e --- /dev/null +++ b/include/utils/ZipFileCRO.h @@ -0,0 +1,59 @@ +/* + * 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. + */ + +// +// C API for ead-only access to Zip archives, with minimal heap allocation. +// +#ifndef __LIBS_ZIPFILECRO_H +#define __LIBS_ZIPFILECRO_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Trivial typedef to ensure that ZipFileCRO is not treated as a simple integer. + */ +typedef void* ZipFileCRO; + +/* + * Trivial typedef to ensure that ZipEntryCRO is not treated as a simple + * integer. We use NULL to indicate an invalid value. + */ +typedef void* ZipEntryCRO; + +extern ZipFileCRO ZipFileXRO_open(const char* path); + +extern void ZipFileCRO_destroy(ZipFileCRO zip); + +extern ZipEntryCRO ZipFileCRO_findEntryByName(ZipFileCRO zip, + const char* fileName); + +extern bool ZipFileCRO_getEntryInfo(ZipFileCRO zip, ZipEntryCRO entry, + int* pMethod, long* pUncompLen, + long* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32); + +extern bool ZipFileCRO_uncompressEntry(ZipFileCRO zip, ZipEntryCRO entry, int fd); + +#ifdef __cplusplus +} +#endif + +#endif /*__LIBS_ZIPFILECRO_H*/ diff --git a/include/utils/ZipFileRO.h b/include/utils/ZipFileRO.h new file mode 100644 index 000000000..51c4f2fb6 --- /dev/null +++ b/include/utils/ZipFileRO.h @@ -0,0 +1,222 @@ +/* + * 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. + */ + +// +// Read-only access to Zip archives, with minimal heap allocation. +// +// This is similar to the more-complete ZipFile class, but no attempt +// has been made to make them interchangeable. This class operates under +// a very different set of assumptions and constraints. +// +#ifndef __LIBS_ZIPFILERO_H +#define __LIBS_ZIPFILERO_H + +#include "Errors.h" +#include "FileMap.h" + +#include +#include +#include + +namespace android { + +/* + * Trivial typedef to ensure that ZipEntryRO is not treated as a simple + * integer. We use NULL to indicate an invalid value. + */ +typedef void* ZipEntryRO; + +/* + * Open a Zip archive for reading. + * + * We want "open" and "find entry by name" to be fast operations, and we + * want to use as little memory as possible. We memory-map the file, + * and load a hash table with pointers to the filenames (which aren't + * null-terminated). The other fields are at a fixed offset from the + * filename, so we don't need to extract those (but we do need to byte-read + * and endian-swap them every time we want them). + * + * To speed comparisons when doing a lookup by name, we could make the mapping + * "private" (copy-on-write) and null-terminate the filenames after verifying + * the record structure. However, this requires a private mapping of + * every page that the Central Directory touches. Easier to tuck a copy + * of the string length into the hash table entry. + */ +class ZipFileRO { +public: + ZipFileRO() + : mFd(-1), mFileMap(NULL), mHashTableSize(-1), mHashTable(NULL) + {} + ~ZipFileRO() { + free(mHashTable); + if (mFileMap) + mFileMap->release(); + if (mFd >= 0) + close(mFd); + } + + /* + * Open an archive. + */ + status_t open(const char* zipFileName); + + /* + * Find an entry, by name. Returns the entry identifier, or NULL if + * not found. + * + * If two entries have the same name, one will be chosen at semi-random. + */ + ZipEntryRO findEntryByName(const char* fileName) const; + + /* + * Return the #of entries in the Zip archive. + */ + int getNumEntries(void) const { + return mNumEntries; + } + + /* + * Return the Nth entry. Zip file entries are not stored in sorted + * order, and updated entries may appear at the end, so anyone walking + * the archive needs to avoid making ordering assumptions. We take + * that further by returning the Nth non-empty entry in the hash table + * rather than the Nth entry in the archive. + * + * Valid values are [0..numEntries). + * + * [This is currently O(n). If it needs to be fast we can allocate an + * additional data structure or provide an iterator interface.] + */ + ZipEntryRO findEntryByIndex(int idx) const; + + /* + * Copy the filename into the supplied buffer. Returns 0 on success, + * -1 if "entry" is invalid, or the filename length if it didn't fit. The + * length, and the returned string, include the null-termination. + */ + int getEntryFileName(ZipEntryRO entry, char* buffer, int bufLen) const; + + /* + * Get the vital stats for an entry. Pass in NULL pointers for anything + * you don't need. + * + * "*pOffset" holds the Zip file offset of the entry's data. + * + * Returns "false" if "entry" is bogus or if the data in the Zip file + * appears to be bad. + */ + bool getEntryInfo(ZipEntryRO entry, int* pMethod, long* pUncompLen, + long* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) const; + + /* + * Create a new FileMap object that maps a subset of the archive. For + * an uncompressed entry this effectively provides a pointer to the + * actual data, for a compressed entry this provides the input buffer + * for inflate(). + */ + FileMap* createEntryFileMap(ZipEntryRO entry) const; + + /* + * Uncompress the data into a buffer. Depending on the compression + * format, this is either an "inflate" operation or a memcpy. + * + * Use "uncompLen" from getEntryInfo() to determine the required + * buffer size. + * + * Returns "true" on success. + */ + bool uncompressEntry(ZipEntryRO entry, void* buffer) const; + + /* + * Uncompress the data to an open file descriptor. + */ + bool uncompressEntry(ZipEntryRO entry, int fd) const; + + /* Zip compression methods we support */ + enum { + kCompressStored = 0, // no compression + kCompressDeflated = 8, // standard deflate + }; + + /* + * Utility function: uncompress deflated data, buffer to buffer. + */ + static bool inflateBuffer(void* outBuf, const void* inBuf, + long uncompLen, long compLen); + + /* + * Utility function: uncompress deflated data, buffer to fd. + */ + static bool inflateBuffer(int fd, const void* inBuf, + long uncompLen, long compLen); + + /* + * Some basic functions for raw data manipulation. "LE" means + * Little Endian. + */ + static inline unsigned short get2LE(const unsigned char* buf) { + return buf[0] | (buf[1] << 8); + } + static inline unsigned long get4LE(const unsigned char* buf) { + return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); + } + +private: + /* these are private and not defined */ + ZipFileRO(const ZipFileRO& src); + ZipFileRO& operator=(const ZipFileRO& src); + + /* parse the archive, prepping internal structures */ + bool parseZipArchive(void); + + /* add a new entry to the hash table */ + void addToHash(const char* str, int strLen, unsigned int hash); + + /* compute string hash code */ + static unsigned int computeHash(const char* str, int len); + + /* convert a ZipEntryRO back to a hash table index */ + int entryToIndex(const ZipEntryRO entry) const; + + /* + * One entry in the hash table. + */ + typedef struct HashEntry { + const char* name; + unsigned short nameLen; + //unsigned int hash; + } HashEntry; + + /* open Zip archive */ + int mFd; + + /* mapped file */ + FileMap* mFileMap; + + /* number of entries in the Zip archive */ + int mNumEntries; + + /* + * We know how many entries are in the Zip archive, so we have a + * fixed-size hash table. We probe for an empty slot. + */ + int mHashTableSize; + HashEntry* mHashTable; +}; + +}; // namespace android + +#endif /*__LIBS_ZIPFILERO_H*/ diff --git a/include/utils/ZipUtils.h b/include/utils/ZipUtils.h new file mode 100644 index 000000000..42c42b6c0 --- /dev/null +++ b/include/utils/ZipUtils.h @@ -0,0 +1,67 @@ +/* + * 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. + */ + +// +// Miscellaneous zip/gzip utility functions. +// +#ifndef __LIBS_ZIPUTILS_H +#define __LIBS_ZIPUTILS_H + +#include + +namespace android { + +/* + * Container class for utility functions, primarily for namespace reasons. + */ +class ZipUtils { +public: + /* + * General utility function for uncompressing "deflate" data from a file + * to a buffer. + */ + static bool inflateToBuffer(int fd, void* buf, long uncompressedLen, + long compressedLen); + static bool inflateToBuffer(FILE* fp, void* buf, long uncompressedLen, + long compressedLen); + + /* + * Someday we might want to make this generic and handle bzip2 ".bz2" + * files too. + * + * We could declare gzip to be a sub-class of zip that has exactly + * one always-compressed entry, but we currently want to treat Zip + * and gzip as distinct, so there's no value. + * + * The zlib library has some gzip utilities, but it has no interface + * for extracting the uncompressed length of the file (you do *not* + * want to gzseek to the end). + * + * Pass in a seeked file pointer for the gzip file. If this is a gzip + * file, we set our return values appropriately and return "true" with + * the file seeked to the start of the compressed data. + */ + static bool examineGzip(FILE* fp, int* pCompressionMethod, + long* pUncompressedLen, long* pCompressedLen, unsigned long* pCRC32); + +private: + ZipUtils() {} + ~ZipUtils() {} +}; + +}; // namespace android + +#endif /*__LIBS_ZIPUTILS_H*/ diff --git a/include/utils/ashmem.h b/include/utils/ashmem.h new file mode 100644 index 000000000..085477578 --- /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 +#include + +#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/executablepath.h b/include/utils/executablepath.h new file mode 100644 index 000000000..c979432ba --- /dev/null +++ b/include/utils/executablepath.h @@ -0,0 +1,28 @@ +/* + * 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 _UTILS_EXECUTABLEPATH_H +#define _UTILS_EXECUTABLEPATH_H + +#include + +// returns the path to this executable +#if __cplusplus +extern "C" +#endif +void executablepath(char s[PATH_MAX]); + +#endif // _UTILS_EXECUTABLEPATH_H diff --git a/include/utils/inet_address.h b/include/utils/inet_address.h new file mode 100644 index 000000000..dbd8672e0 --- /dev/null +++ b/include/utils/inet_address.h @@ -0,0 +1,103 @@ +/* + * 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. + */ + +// +// Internet address classes. Modeled after Java classes. +// +#ifndef _RUNTIME_INET_ADDRESS_H +#define _RUNTIME_INET_ADDRESS_H + +#ifdef HAVE_ANDROID_OS +#error DO NOT USE THIS FILE IN THE DEVICE BUILD +#endif + + +namespace android { + +/* + * This class holds Internet addresses. Perhaps more useful is its + * ability to look up addresses by name. + * + * Invoke one of the static factory methods to create a new object. + */ +class InetAddress { +public: + virtual ~InetAddress(void); + + // create from w.x.y.z or foo.bar.com notation + static InetAddress* getByName(const char* host); + + // copy-construction + InetAddress(const InetAddress& orig); + + const void* getAddress(void) const { return mAddress; } + int getAddressLength(void) const { return mLength; } + const char* getHostName(void) const { return mName; } + +private: + InetAddress(void); + // assignment (private) + InetAddress& operator=(const InetAddress& addr); + + // use a void* here so we don't have to expose actual socket headers + void* mAddress; // this is really a ptr to sockaddr_in + int mLength; + char* mName; +}; + + +/* + * Base class for socket addresses. + */ +class SocketAddress { +public: + SocketAddress() {} + virtual ~SocketAddress() {} +}; + + +/* + * Internet address class. This combines an InetAddress with a port. + */ +class InetSocketAddress : public SocketAddress { +public: + InetSocketAddress() : + mAddress(0), mPort(-1) + {} + ~InetSocketAddress(void) { + delete mAddress; + } + + // Create an address with a host wildcard (useful for servers). + bool create(int port); + // Create an address with the specified host and port. + bool create(const InetAddress* addr, int port); + // Create an address with the specified host and port. Does the + // hostname lookup. + bool create(const char* host, int port); + + const InetAddress* getAddress(void) const { return mAddress; } + const int getPort(void) const { return mPort; } + const char* getHostName(void) const { return mAddress->getHostName(); } + +private: + InetAddress* mAddress; + int mPort; +}; + +}; // namespace android + +#endif // _RUNTIME_INET_ADDRESS_H diff --git a/include/utils/misc.h b/include/utils/misc.h new file mode 100644 index 000000000..62e84b489 --- /dev/null +++ b/include/utils/misc.h @@ -0,0 +1,93 @@ +/* + * 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 +#include "utils/Endian.h" + +namespace android { + +/* get #of elements in a static array */ +#ifndef NELEM +# define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0]))) +#endif + +/* + * Make a copy of the string, using "new[]" instead of "malloc". Free the + * string with delete[]. + * + * Returns NULL if "str" is NULL. + */ +char* strdupNew(const char* str); + +/* + * Concatenate an argument vector into a single string. If argc is >= 0 + * it will be used; if it's < 0 then the last element in the arg vector + * must be NULL. + * + * This inserts a space between each argument. + * + * This does not automatically add double quotes around arguments with + * spaces in them. This practice is necessary for Win32, because Win32's + * CreateProcess call is stupid. + * + * The caller should delete[] the returned string. + */ +char* concatArgv(int argc, const char* const argv[]); + +/* + * Count up the number of arguments in "argv". The count does not include + * the final NULL entry. + */ +int countArgv(const char* const argv[]); + +/* + * Some utility functions for working with files. These could be made + * part of a "File" class. + */ +typedef enum FileType { + kFileTypeUnknown = 0, + kFileTypeNonexistent, // i.e. ENOENT + kFileTypeRegular, + kFileTypeDirectory, + kFileTypeCharDev, + kFileTypeBlockDev, + kFileTypeFifo, + kFileTypeSymlink, + kFileTypeSocket, +} FileType; +/* get the file's type; follows symlinks */ +FileType getFileType(const char* fileName); +/* get the file's modification date; returns -1 w/errno set on failure */ +time_t getFileModDate(const char* fileName); + +/* + * Round up to the nearest power of 2. Handy for hash tables. + */ +unsigned int roundUpPower2(unsigned int val); + +void strreverse(char* begin, char* end); +void k_itoa(int value, char* str, int base); +char* itoa(int val, int base); + +}; // namespace android + +#endif // _LIBS_UTILS_MISC_H diff --git a/include/utils/ported.h b/include/utils/ported.h new file mode 100644 index 000000000..eb3be01e9 --- /dev/null +++ b/include/utils/ported.h @@ -0,0 +1,50 @@ +/* + * 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. + */ + +// +// Standard functions ported to the current platform. Note these are NOT +// in the "android" namespace. +// +#ifndef _LIBS_UTILS_PORTED_H +#define _LIBS_UTILS_PORTED_H + +#include // for timeval + +#ifdef __cplusplus +extern "C" { +#endif + +/* library replacement functions */ +#if defined(NEED_GETTIMEOFDAY) +int gettimeofday(struct timeval* tv, struct timezone* tz); +#endif +#if defined(NEED_USLEEP) +void usleep(unsigned long usec); +#endif +#if defined(NEED_PIPE) +int pipe(int filedes[2]); +#endif +#if defined(NEED_SETENV) +int setenv(const char* name, const char* value, int overwrite); +void unsetenv(const char* name); +char* getenv(const char* name); +#endif + +#ifdef __cplusplus +} +#endif + +#endif // _LIBS_UTILS_PORTED_H diff --git a/include/utils/string_array.h b/include/utils/string_array.h new file mode 100644 index 000000000..064dda224 --- /dev/null +++ b/include/utils/string_array.h @@ -0,0 +1,135 @@ +/* + * 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. + */ + +// +// Sortable array of strings. STL-ish, but STL-free. +// +#ifndef _LIBS_UTILS_STRING_ARRAY_H +#define _LIBS_UTILS_STRING_ARRAY_H + +#include +#include + +namespace android { + +// +// An expanding array of strings. Add, get, sort, delete. +// +class StringArray { +public: + StringArray() + : mMax(0), mCurrent(0), mArray(NULL) + {} + virtual ~StringArray() { + for (int i = 0; i < mCurrent; i++) + delete[] mArray[i]; + delete[] mArray; + } + + // + // Add a string. A copy of the string is made. + // + bool push_back(const char* str) { + if (mCurrent >= mMax) { + char** tmp; + + if (mMax == 0) + mMax = 16; // initial storage + else + mMax *= 2; + + tmp = new char*[mMax]; + if (tmp == NULL) + return false; + + memcpy(tmp, mArray, mCurrent * sizeof(char*)); + delete[] mArray; + mArray = tmp; + } + + int len = strlen(str); + mArray[mCurrent] = new char[len+1]; + memcpy(mArray[mCurrent], str, len+1); + mCurrent++; + + return true; + } + + // + // Delete an entry. + // + void erase(int idx) { + if (idx < 0 || idx >= mCurrent) + return; + delete[] mArray[idx]; + if (idx < mCurrent-1) { + memmove(&mArray[idx], &mArray[idx+1], + (mCurrent-1 - idx) * sizeof(char*)); + } + mCurrent--; + } + + // + // Sort the array. + // + void sort(int (*compare)(const void*, const void*)) { + qsort(mArray, mCurrent, sizeof(char*), compare); + } + + // + // Pass this to the sort routine to do an ascending alphabetical sort. + // + static int cmpAscendingAlpha(const void* pstr1, const void* pstr2) { + return strcmp(*(const char**)pstr1, *(const char**)pstr2); + } + + // + // Get the #of items in the array. + // + inline int size(void) const { return mCurrent; } + + // + // Return entry N. + // [should use operator[] here] + // + const char* getEntry(int idx) const { + if (idx < 0 || idx >= mCurrent) + return NULL; + return mArray[idx]; + } + + // + // Set entry N to specified string. + // [should use operator[] here] + // + void setEntry(int idx, const char* str) { + if (idx < 0 || idx >= mCurrent) + return; + delete[] mArray[idx]; + int len = strlen(str); + mArray[idx] = new char[len+1]; + memcpy(mArray[idx], str, len+1); + } + +private: + int mMax; + int mCurrent; + char** mArray; +}; + +}; // namespace android + +#endif // _LIBS_UTILS_STRING_ARRAY_H diff --git a/include/utils/threads.h b/include/utils/threads.h new file mode 100644 index 000000000..7dca81004 --- /dev/null +++ b/include/utils/threads.h @@ -0,0 +1,347 @@ +/* + * 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 + +#include +#include +#include + +// ------------------------------------------------------------------ +// C API + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void* android_thread_id_t; + +typedef int (*android_thread_func_t)(void*); + +enum { + /* + * *********************************************** + * ** Keep in sync with android.os.Process.java ** + * *********************************************** + * + * This maps directly to the "nice" priorites we use in Android. + * A thread priority should be chosen inverse-proportinally 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 = -8, + + /* 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, +}; + +// 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); + +// 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); + +#ifdef __cplusplus +} +#endif + +// ------------------------------------------------------------------ +// C++ API + +#ifdef __cplusplus + +#include +#include +#include + +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, +}; + +// 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(); +} + +/* + * 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: + Mutex(); + Mutex(const char* name); + ~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) : mpMutex(&mutex) { mutex.lock(); } + inline Autolock(Mutex* mutex) : mpMutex(mutex) { mutex->lock(); } + inline ~Autolock() { mpMutex->unlock(); } + private: + Mutex* mpMutex; + }; + +private: + friend class Condition; + + // A mutex cannot be copied + Mutex(const Mutex&); + Mutex& operator = (const Mutex&); + void _init(); + + void* mState; +}; + +/* + * 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; + + +/* + * 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: + Condition(); + ~Condition(); + // Wait on the condition variable. Lock the mutex before calling. + status_t wait(Mutex& mutex); + // Wait on the condition variable until the given time. Lock the mutex + // before calling. + status_t wait(Mutex& mutex, nsecs_t abstime); + // 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 all threads to continue. + void broadcast(); + +private: + void* mState; +}; + + +/* + * Read/write lock. The resource can have multiple readers or one writer, + * but can't be read and written at the same time. + * + * The same thread should not call a lock function while it already has + * a lock. (Should be okay for multiple readers.) + */ +class ReadWriteLock { +public: + ReadWriteLock() + : mNumReaders(0), mNumWriters(0) + {} + ~ReadWriteLock() {} + + void lockForRead(); + bool tryLockForRead(); + void unlockForRead(); + + void lockForWrite(); + bool tryLockForWrite(); + void unlockForWrite(); + +private: + int mNumReaders; + int mNumWriters; + + Mutex mLock; + Condition mReadWaiter; + Condition mWriteWaiter; +#if defined(PRINT_RENDER_TIMES) + DurationTimer mDebugTimer; +#endif +}; + + +/* + * This is our spiffy thread object! + */ + +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(); + +protected: + // exitPending() returns true if requestExit() has been called. + bool exitPending() const; + +private: + // Derived class must implemtent 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; + thread_id_t mThread; + Mutex mLock; + Condition mThreadExitedCondition; + status_t mStatus; + volatile bool mExitPending; + volatile bool mRunning; + sp mHoldSelf; +}; + + +}; // namespace android + +#endif // __cplusplus + +#endif // _LIBS_UTILS_THREADS_H diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk new file mode 100644 index 000000000..cdb8ca2d7 --- /dev/null +++ b/libs/utils/Android.mk @@ -0,0 +1,156 @@ +# 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:= \ + Asset.cpp \ + AssetDir.cpp \ + AssetManager.cpp \ + BufferedTextOutput.cpp \ + CallStack.cpp \ + Debug.cpp \ + FileMap.cpp \ + RefBase.cpp \ + ResourceTypes.cpp \ + SharedBuffer.cpp \ + Static.cpp \ + StopWatch.cpp \ + String8.cpp \ + String16.cpp \ + SystemClock.cpp \ + TextOutput.cpp \ + Threads.cpp \ + TimerProbe.cpp \ + Timers.cpp \ + VectorImpl.cpp \ + ZipFileCRO.cpp \ + ZipFileRO.cpp \ + ZipUtils.cpp \ + misc.cpp \ + ported.cpp \ + LogSocket.cpp + +# +# The cpp files listed here do not belong in the device +# build. Consult with the swetland before even thinking about +# putting them in commonSources. +# +# They're used by the simulator runtime and by host-side tools like +# aapt and the simulator front-end. +# +hostSources:= \ + InetAddress.cpp \ + Pipe.cpp \ + Socket.cpp \ + ZipEntry.cpp \ + ZipFile.cpp + +# For the host +# ===================================================== + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= $(commonSources) $(hostSources) + +ifeq ($(HOST_OS),linux) +# Use the futex based mutex and condition variable +# implementation from android-arm because it's shared mem safe + LOCAL_SRC_FILES += \ + futex_synchro.c \ + executablepath_linux.cpp +endif +ifeq ($(HOST_OS),darwin) + LOCAL_SRC_FILES += \ + executablepath_darwin.cpp +endif + +LOCAL_MODULE:= libutils + +LOCAL_CFLAGS += -DLIBUTILS_NATIVE=1 $(TOOL_CFLAGS) +LOCAL_C_INCLUDES += external/zlib + +ifeq ($(HOST_OS),windows) +ifeq ($(strip $(USE_CYGWIN),),) +# Under MinGW, ctype.h doesn't need multi-byte support +LOCAL_CFLAGS += -DMB_CUR_MAX=1 +endif +endif + +include $(BUILD_HOST_STATIC_LIBRARY) + + + +# For the device +# ===================================================== +include $(CLEAR_VARS) + + +# we have the common sources, plus some device-specific stuff +LOCAL_SRC_FILES:= \ + $(commonSources) \ + Binder.cpp \ + BpBinder.cpp \ + IInterface.cpp \ + IMemory.cpp \ + IPCThreadState.cpp \ + MemoryDealer.cpp \ + MemoryBase.cpp \ + MemoryHeapBase.cpp \ + MemoryHeapPmem.cpp \ + Parcel.cpp \ + ProcessState.cpp \ + IPermissionController.cpp \ + IServiceManager.cpp \ + Unicode.cpp + +ifeq ($(TARGET_SIMULATOR),true) +LOCAL_SRC_FILES += $(hostSources) +endif + +ifeq ($(TARGET_OS),linux) +# Use the futex based mutex and condition variable +# implementation from android-arm because it's shared mem safe +LOCAL_SRC_FILES += futex_synchro.c +LOCAL_LDLIBS += -lrt -ldl +endif + +LOCAL_C_INCLUDES += \ + external/zlib \ + external/icu4c/common +LOCAL_LDLIBS += -lpthread + +LOCAL_SHARED_LIBRARIES := \ + libz \ + liblog \ + libcutils + +ifneq ($(TARGET_SIMULATOR),true) +ifeq ($(TARGET_OS)-$(TARGET_ARCH),linux-x86) +# This is needed on x86 to bring in dl_iterate_phdr for CallStack.cpp +LOCAL_SHARED_LIBRARIES += \ + libdl +endif # linux-x86 +endif # sim + +LOCAL_MODULE:= libutils + +#LOCAL_CFLAGS+= +#LOCAL_LDFLAGS:= + +include $(BUILD_SHARED_LIBRARY) + diff --git a/libs/utils/Asset.cpp b/libs/utils/Asset.cpp new file mode 100644 index 000000000..91203ddb4 --- /dev/null +++ b/libs/utils/Asset.cpp @@ -0,0 +1,813 @@ +/* + * 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. + */ + +// +// Provide access to a read-only asset. +// + +#define LOG_TAG "asset" +//#define NDEBUG 0 + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace android; + +#ifndef O_BINARY +# define O_BINARY 0 +#endif + +static volatile int32_t gCount = 0; + +int32_t Asset::getGlobalCount() +{ + return gCount; +} + +Asset::Asset(void) + : mAccessMode(ACCESS_UNKNOWN) +{ + int count = android_atomic_inc(&gCount)+1; + //LOGI("Creating Asset %p #%d\n", this, count); +} + +Asset::~Asset(void) +{ + int count = android_atomic_dec(&gCount); + //LOGI("Destroying Asset in %p #%d\n", this, count); +} + +/* + * Create a new Asset from a file on disk. There is a fair chance that + * the file doesn't actually exist. + * + * We can use "mode" to decide how we want to go about it. + */ +/*static*/ Asset* Asset::createFromFile(const char* fileName, AccessMode mode) +{ + _FileAsset* pAsset; + status_t result; + off_t length; + int fd; + + fd = open(fileName, O_RDONLY | O_BINARY); + if (fd < 0) + return NULL; + + /* + * Under Linux, the lseek fails if we actually opened a directory. To + * be correct we should test the file type explicitly, but since we + * always open things read-only it doesn't really matter, so there's + * no value in incurring the extra overhead of an fstat() call. + */ + length = lseek(fd, 0, SEEK_END); + if (length < 0) { + ::close(fd); + return NULL; + } + (void) lseek(fd, 0, SEEK_SET); + + pAsset = new _FileAsset; + result = pAsset->openChunk(fileName, fd, 0, length); + if (result != NO_ERROR) { + delete pAsset; + return NULL; + } + + pAsset->mAccessMode = mode; + return pAsset; +} + + +/* + * Create a new Asset from a compressed file on disk. There is a fair chance + * that the file doesn't actually exist. + * + * We currently support gzip files. We might want to handle .bz2 someday. + */ +/*static*/ Asset* Asset::createFromCompressedFile(const char* fileName, + AccessMode mode) +{ + _CompressedAsset* pAsset; + status_t result; + off_t fileLen; + bool scanResult; + long offset; + int method; + long uncompressedLen, compressedLen; + int fd; + + fd = open(fileName, O_RDONLY | O_BINARY); + if (fd < 0) + return NULL; + + fileLen = lseek(fd, 0, SEEK_END); + if (fileLen < 0) { + ::close(fd); + return NULL; + } + (void) lseek(fd, 0, SEEK_SET); + + /* want buffered I/O for the file scan; must dup so fclose() is safe */ + FILE* fp = fdopen(dup(fd), "rb"); + if (fp == NULL) { + ::close(fd); + return NULL; + } + + unsigned long crc32; + scanResult = ZipUtils::examineGzip(fp, &method, &uncompressedLen, + &compressedLen, &crc32); + offset = ftell(fp); + fclose(fp); + if (!scanResult) { + LOGD("File '%s' is not in gzip format\n", fileName); + ::close(fd); + return NULL; + } + + pAsset = new _CompressedAsset; + result = pAsset->openChunk(fd, offset, method, uncompressedLen, + compressedLen); + if (result != NO_ERROR) { + delete pAsset; + return NULL; + } + + pAsset->mAccessMode = mode; + return pAsset; +} + + +#if 0 +/* + * Create a new Asset from part of an open file. + */ +/*static*/ Asset* Asset::createFromFileSegment(int fd, off_t offset, + size_t length, AccessMode mode) +{ + _FileAsset* pAsset; + status_t result; + + pAsset = new _FileAsset; + result = pAsset->openChunk(NULL, fd, offset, length); + if (result != NO_ERROR) + return NULL; + + pAsset->mAccessMode = mode; + return pAsset; +} + +/* + * Create a new Asset from compressed data in an open file. + */ +/*static*/ Asset* Asset::createFromCompressedData(int fd, off_t offset, + int compressionMethod, size_t uncompressedLen, size_t compressedLen, + AccessMode mode) +{ + _CompressedAsset* pAsset; + status_t result; + + pAsset = new _CompressedAsset; + result = pAsset->openChunk(fd, offset, compressionMethod, + uncompressedLen, compressedLen); + if (result != NO_ERROR) + return NULL; + + pAsset->mAccessMode = mode; + return pAsset; +} +#endif + +/* + * Create a new Asset from a memory mapping. + */ +/*static*/ Asset* Asset::createFromUncompressedMap(FileMap* dataMap, + AccessMode mode) +{ + _FileAsset* pAsset; + status_t result; + + pAsset = new _FileAsset; + result = pAsset->openChunk(dataMap); + if (result != NO_ERROR) + return NULL; + + pAsset->mAccessMode = mode; + return pAsset; +} + +/* + * Create a new Asset from compressed data in a memory mapping. + */ +/*static*/ Asset* Asset::createFromCompressedMap(FileMap* dataMap, + int method, size_t uncompressedLen, AccessMode mode) +{ + _CompressedAsset* pAsset; + status_t result; + + pAsset = new _CompressedAsset; + result = pAsset->openChunk(dataMap, method, uncompressedLen); + if (result != NO_ERROR) + return NULL; + + pAsset->mAccessMode = mode; + return pAsset; +} + + +/* + * Do generic seek() housekeeping. Pass in the offset/whence values from + * the seek request, along with the current chunk offset and the chunk + * length. + * + * Returns the new chunk offset, or -1 if the seek is illegal. + */ +off_t Asset::handleSeek(off_t offset, int whence, off_t curPosn, off_t maxPosn) +{ + off_t newOffset; + + switch (whence) { + case SEEK_SET: + newOffset = offset; + break; + case SEEK_CUR: + newOffset = curPosn + offset; + break; + case SEEK_END: + newOffset = maxPosn + offset; + break; + default: + LOGW("unexpected whence %d\n", whence); + // this was happening due to an off_t size mismatch + assert(false); + return (off_t) -1; + } + + if (newOffset < 0 || newOffset > maxPosn) { + LOGW("seek out of range: want %ld, end=%ld\n", + (long) newOffset, (long) maxPosn); + return (off_t) -1; + } + + return newOffset; +} + + +/* + * =========================================================================== + * _FileAsset + * =========================================================================== + */ + +/* + * Constructor. + */ +_FileAsset::_FileAsset(void) + : mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mMap(NULL), mBuf(NULL) +{ +} + +/* + * Destructor. Release resources. + */ +_FileAsset::~_FileAsset(void) +{ + close(); +} + +/* + * Operate on a chunk of an uncompressed file. + * + * Zero-length chunks are allowed. + */ +status_t _FileAsset::openChunk(const char* fileName, int fd, off_t offset, size_t length) +{ + assert(mFp == NULL); // no reopen + assert(mMap == NULL); + assert(fd >= 0); + assert(offset >= 0); + + /* + * Seek to end to get file length. + */ + off_t fileLength; + fileLength = lseek(fd, 0, SEEK_END); + if (fileLength == (off_t) -1) { + // probably a bad file descriptor + LOGD("failed lseek (errno=%d)\n", errno); + return UNKNOWN_ERROR; + } + + if ((off_t) (offset + length) > fileLength) { + LOGD("start (%ld) + len (%ld) > end (%ld)\n", + (long) offset, (long) length, (long) fileLength); + return BAD_INDEX; + } + + /* after fdopen, the fd will be closed on fclose() */ + mFp = fdopen(fd, "rb"); + if (mFp == NULL) + return UNKNOWN_ERROR; + + mStart = offset; + mLength = length; + assert(mOffset == 0); + + /* seek the FILE* to the start of chunk */ + if (fseek(mFp, mStart, SEEK_SET) != 0) { + assert(false); + } + + mFileName = fileName != NULL ? strdup(fileName) : NULL; + + return NO_ERROR; +} + +/* + * Create the chunk from the map. + */ +status_t _FileAsset::openChunk(FileMap* dataMap) +{ + assert(mFp == NULL); // no reopen + assert(mMap == NULL); + assert(dataMap != NULL); + + mMap = dataMap; + mStart = -1; // not used + mLength = dataMap->getDataLength(); + assert(mOffset == 0); + + return NO_ERROR; +} + +/* + * Read a chunk of data. + */ +ssize_t _FileAsset::read(void* buf, size_t count) +{ + size_t maxLen; + size_t actual; + + assert(mOffset >= 0 && mOffset <= mLength); + + if (getAccessMode() == ACCESS_BUFFER) { + /* + * On first access, read or map the entire file. The caller has + * requested buffer access, either because they're going to be + * using the buffer or because what they're doing has appropriate + * performance needs and access patterns. + */ + if (mBuf == NULL) + getBuffer(false); + } + + /* adjust count if we're near EOF */ + maxLen = mLength - mOffset; + if (count > maxLen) + count = maxLen; + + if (!count) + return 0; + + if (mMap != NULL) { + /* copy from mapped area */ + //printf("map read\n"); + memcpy(buf, (char*)mMap->getDataPtr() + mOffset, count); + actual = count; + } else if (mBuf != NULL) { + /* copy from buffer */ + //printf("buf read\n"); + memcpy(buf, (char*)mBuf + mOffset, count); + actual = count; + } else { + /* read from the file */ + //printf("file read\n"); + if (ftell(mFp) != mStart + mOffset) { + LOGE("Hosed: %ld != %ld+%ld\n", + ftell(mFp), (long) mStart, (long) mOffset); + assert(false); + } + + /* + * This returns 0 on error or eof. We need to use ferror() or feof() + * to tell the difference, but we don't currently have those on the + * device. However, we know how much data is *supposed* to be in the + * file, so if we don't read the full amount we know something is + * hosed. + */ + actual = fread(buf, 1, count, mFp); + if (actual == 0) // something failed -- I/O error? + return -1; + + assert(actual == count); + } + + mOffset += actual; + return actual; +} + +/* + * Seek to a new position. + */ +off_t _FileAsset::seek(off_t offset, int whence) +{ + off_t newPosn; + long actualOffset; + + // compute new position within chunk + newPosn = handleSeek(offset, whence, mOffset, mLength); + if (newPosn == (off_t) -1) + return newPosn; + + actualOffset = (long) (mStart + newPosn); + + if (mFp != NULL) { + if (fseek(mFp, (long) actualOffset, SEEK_SET) != 0) + return (off_t) -1; + } + + mOffset = actualOffset - mStart; + return mOffset; +} + +/* + * Close the asset. + */ +void _FileAsset::close(void) +{ + if (mMap != NULL) { + mMap->release(); + mMap = NULL; + } + if (mBuf != NULL) { + delete[] mBuf; + mBuf = NULL; + } + + if (mFileName != NULL) { + free(mFileName); + mFileName = NULL; + } + + if (mFp != NULL) { + // can only be NULL when called from destructor + // (otherwise we would never return this object) + fclose(mFp); + mFp = NULL; + } +} + +/* + * Return a read-only pointer to a buffer. + * + * We can either read the whole thing in or map the relevant piece of + * the source file. Ideally a map would be established at a higher + * level and we'd be using a different object, but we didn't, so we + * deal with it here. + */ +const void* _FileAsset::getBuffer(bool wordAligned) +{ + /* subsequent requests just use what we did previously */ + if (mBuf != NULL) + return mBuf; + if (mMap != NULL) { + if (!wordAligned) { + return mMap->getDataPtr(); + } + return ensureAlignment(mMap); + } + + assert(mFp != NULL); + + if (mLength < kReadVsMapThreshold) { + unsigned char* buf; + long allocLen; + + /* zero-length files are allowed; not sure about zero-len allocs */ + /* (works fine with gcc + x86linux) */ + allocLen = mLength; + if (mLength == 0) + allocLen = 1; + + buf = new unsigned char[allocLen]; + if (buf == NULL) { + LOGE("alloc of %ld bytes failed\n", (long) allocLen); + return NULL; + } + + LOGV("Asset %p allocating buffer size %d (smaller than threshold)", this, (int)allocLen); + if (mLength > 0) { + long oldPosn = ftell(mFp); + fseek(mFp, mStart, SEEK_SET); + if (fread(buf, 1, mLength, mFp) != (size_t) mLength) { + LOGE("failed reading %ld bytes\n", (long) mLength); + delete[] buf; + return NULL; + } + fseek(mFp, oldPosn, SEEK_SET); + } + + LOGV(" getBuffer: loaded into buffer\n"); + + mBuf = buf; + return mBuf; + } else { + FileMap* map; + + map = new FileMap; + if (!map->create(NULL, fileno(mFp), mStart, mLength, true)) { + map->release(); + return NULL; + } + + LOGV(" getBuffer: mapped\n"); + + mMap = map; + if (!wordAligned) { + return mMap->getDataPtr(); + } + return ensureAlignment(mMap); + } +} + +int _FileAsset::openFileDescriptor(off_t* outStart, off_t* outLength) const +{ + if (mMap != NULL) { + const char* fname = mMap->getFileName(); + if (fname == NULL) { + fname = mFileName; + } + if (fname == NULL) { + return -1; + } + *outStart = mMap->getDataOffset(); + *outLength = mMap->getDataLength(); + return open(fname, O_RDONLY | O_BINARY); + } + if (mFileName == NULL) { + return -1; + } + *outStart = mStart; + *outLength = mLength; + return open(mFileName, O_RDONLY | O_BINARY); +} + +const void* _FileAsset::ensureAlignment(FileMap* map) +{ + void* data = map->getDataPtr(); + if ((((size_t)data)&0x3) == 0) { + // We can return this directly if it is aligned on a word + // boundary. + return data; + } + // If not aligned on a word boundary, then we need to copy it into + // our own buffer. + LOGV("Copying FileAsset %p to buffer size %d to make it aligned.", this, (int)mLength); + unsigned char* buf = new unsigned char[mLength]; + if (buf == NULL) { + LOGE("alloc of %ld bytes failed\n", (long) mLength); + return NULL; + } + memcpy(buf, data, mLength); + mBuf = buf; + return buf; +} + +/* + * =========================================================================== + * _CompressedAsset + * =========================================================================== + */ + +/* + * Constructor. + */ +_CompressedAsset::_CompressedAsset(void) + : mStart(0), mCompressedLen(0), mUncompressedLen(0), mOffset(0), + mMap(NULL), mFd(-1), mBuf(NULL) +{ +} + +/* + * Destructor. Release resources. + */ +_CompressedAsset::~_CompressedAsset(void) +{ + close(); +} + +/* + * Open a chunk of compressed data inside a file. + * + * This currently just sets up some values and returns. On the first + * read, we expand the entire file into a buffer and return data from it. + */ +status_t _CompressedAsset::openChunk(int fd, off_t offset, + int compressionMethod, size_t uncompressedLen, size_t compressedLen) +{ + assert(mFd < 0); // no re-open + assert(mMap == NULL); + assert(fd >= 0); + assert(offset >= 0); + assert(compressedLen > 0); + + if (compressionMethod != ZipFileRO::kCompressDeflated) { + assert(false); + return UNKNOWN_ERROR; + } + + mStart = offset; + mCompressedLen = compressedLen; + mUncompressedLen = uncompressedLen; + assert(mOffset == 0); + mFd = fd; + assert(mBuf == NULL); + + return NO_ERROR; +} + +/* + * Open a chunk of compressed data in a mapped region. + * + * Nothing is expanded until the first read call. + */ +status_t _CompressedAsset::openChunk(FileMap* dataMap, int compressionMethod, + size_t uncompressedLen) +{ + assert(mFd < 0); // no re-open + assert(mMap == NULL); + assert(dataMap != NULL); + + if (compressionMethod != ZipFileRO::kCompressDeflated) { + assert(false); + return UNKNOWN_ERROR; + } + + mMap = dataMap; + mStart = -1; // not used + mCompressedLen = dataMap->getDataLength(); + mUncompressedLen = uncompressedLen; + assert(mOffset == 0); + + return NO_ERROR; +} + +/* + * Read data from a chunk of compressed data. + * + * [For now, that's just copying data out of a buffer.] + */ +ssize_t _CompressedAsset::read(void* buf, size_t count) +{ + size_t maxLen; + size_t actual; + + assert(mOffset >= 0 && mOffset <= mUncompressedLen); + + // TODO: if mAccessMode == ACCESS_STREAMING, use zlib more cleverly + + if (mBuf == NULL) { + if (getBuffer(false) == NULL) + return -1; + } + assert(mBuf != NULL); + + /* adjust count if we're near EOF */ + maxLen = mUncompressedLen - mOffset; + if (count > maxLen) + count = maxLen; + + if (!count) + return 0; + + /* copy from buffer */ + //printf("comp buf read\n"); + memcpy(buf, (char*)mBuf + mOffset, count); + actual = count; + + mOffset += actual; + return actual; +} + +/* + * Handle a seek request. + * + * If we're working in a streaming mode, this is going to be fairly + * expensive, because it requires plowing through a bunch of compressed + * data. + */ +off_t _CompressedAsset::seek(off_t offset, int whence) +{ + off_t newPosn; + + // compute new position within chunk + newPosn = handleSeek(offset, whence, mOffset, mUncompressedLen); + if (newPosn == (off_t) -1) + return newPosn; + + mOffset = newPosn; + return mOffset; +} + +/* + * Close the asset. + */ +void _CompressedAsset::close(void) +{ + if (mMap != NULL) { + mMap->release(); + mMap = NULL; + } + if (mBuf != NULL) { + delete[] mBuf; + mBuf = NULL; + } + + if (mFd > 0) { + ::close(mFd); + mFd = -1; + } +} + +/* + * Get a pointer to a read-only buffer of data. + * + * The first time this is called, we expand the compressed data into a + * buffer. + */ +const void* _CompressedAsset::getBuffer(bool wordAligned) +{ + unsigned char* buf = NULL; + + if (mBuf != NULL) + return mBuf; + + if (mUncompressedLen > UNCOMPRESS_DATA_MAX) { + LOGD("Data exceeds UNCOMPRESS_DATA_MAX (%ld vs %d)\n", + (long) mUncompressedLen, UNCOMPRESS_DATA_MAX); + goto bail; + } + + /* + * Allocate a buffer and read the file into it. + */ + buf = new unsigned char[mUncompressedLen]; + if (buf == NULL) { + LOGW("alloc %ld bytes failed\n", (long) mUncompressedLen); + goto bail; + } + + if (mMap != NULL) { + if (!ZipFileRO::inflateBuffer(buf, mMap->getDataPtr(), + mUncompressedLen, mCompressedLen)) + goto bail; + } else { + assert(mFd >= 0); + + /* + * Seek to the start of the compressed data. + */ + if (lseek(mFd, mStart, SEEK_SET) != mStart) + goto bail; + + /* + * Expand the data into it. + */ + if (!ZipUtils::inflateToBuffer(mFd, buf, mUncompressedLen, + mCompressedLen)) + goto bail; + } + + /* success! */ + mBuf = buf; + buf = NULL; + +bail: + delete[] buf; + return mBuf; +} + diff --git a/libs/utils/AssetDir.cpp b/libs/utils/AssetDir.cpp new file mode 100644 index 000000000..c5f664ecc --- /dev/null +++ b/libs/utils/AssetDir.cpp @@ -0,0 +1,66 @@ +/* + * 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. + */ + +// +// Provide access to a virtual directory in "asset space". Most of the +// implementation is in the header file or in friend functions in +// AssetManager. +// +#include + +using namespace android; + + +/* + * Find a matching entry in a vector of FileInfo. Because it's sorted, we + * can use a binary search. + * + * Assumes the vector is sorted in ascending order. + */ +/*static*/ int AssetDir::FileInfo::findEntry(const SortedVector* pVector, + const String8& fileName) +{ + FileInfo tmpInfo; + + tmpInfo.setFileName(fileName); + return pVector->indexOf(tmpInfo); + +#if 0 // don't need this after all (uses 1/2 compares of SortedVector though) + int lo, hi, cur; + + lo = 0; + hi = pVector->size() -1; + while (lo <= hi) { + int cmp; + + cur = (hi + lo) / 2; + cmp = strcmp(pVector->itemAt(cur).getFileName(), fileName); + if (cmp == 0) { + /* match, bail */ + return cur; + } else if (cmp < 0) { + /* too low */ + lo = cur + 1; + } else { + /* too high */ + hi = cur -1; + } + } + + return -1; +#endif +} + diff --git a/libs/utils/AssetManager.cpp b/libs/utils/AssetManager.cpp new file mode 100644 index 000000000..447b80193 --- /dev/null +++ b/libs/utils/AssetManager.cpp @@ -0,0 +1,1637 @@ +/* + * 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. + */ + +// +// Provide access to read-only assets. +// + +#define LOG_TAG "asset" +//#define LOG_NDEBUG 0 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace android; + +/* + * Names for default app, locale, and vendor. We might want to change + * these to be an actual locale, e.g. always use en-US as the default. + */ +static const char* kDefaultLocale = "default"; +static const char* kDefaultVendor = "default"; +static const char* kAssetsRoot = "assets"; +static const char* kAppZipName = NULL; //"classes.jar"; +static const char* kSystemAssets = "framework/framework-res.apk"; + +static const char* kExcludeExtension = ".EXCLUDE"; + +static Asset* const kExcludedAsset = (Asset*) 0xd000000d; + +static volatile int32_t gCount = 0; + + +/* + * =========================================================================== + * AssetManager + * =========================================================================== + */ + +int32_t AssetManager::getGlobalCount() +{ + return gCount; +} + +AssetManager::AssetManager(CacheMode cacheMode) + : mLocale(NULL), mVendor(NULL), + mResources(NULL), mConfig(new ResTable_config), + mCacheMode(cacheMode), mCacheValid(false) +{ + int count = android_atomic_inc(&gCount)+1; + //LOGI("Creating AssetManager %p #%d\n", this, count); + memset(mConfig, 0, sizeof(ResTable_config)); +} + +AssetManager::~AssetManager(void) +{ + int count = android_atomic_dec(&gCount); + //LOGI("Destroying AssetManager in %p #%d\n", this, count); + + delete mConfig; + delete mResources; + + // don't have a String class yet, so make sure we clean up + delete[] mLocale; + delete[] mVendor; +} + +bool AssetManager::addAssetPath(const String8& path, void** cookie) +{ + AutoMutex _l(mLock); + + asset_path ap; + + String8 realPath(path); + if (kAppZipName) { + realPath.appendPath(kAppZipName); + } + ap.type = ::getFileType(realPath.string()); + if (ap.type == kFileTypeRegular) { + ap.path = realPath; + } else { + ap.path = path; + ap.type = ::getFileType(path.string()); + if (ap.type != kFileTypeDirectory && ap.type != kFileTypeRegular) { + LOGW("Asset path %s is neither a directory nor file (type=%d).", + path.string(), (int)ap.type); + return false; + } + } + + // Skip if we have it already. + for (size_t i=0; i mAssetPaths.size() ? NULL : (void*)next; +} + +String8 AssetManager::getAssetPath(void* cookie) const +{ + AutoMutex _l(mLock); + const size_t which = ((size_t)cookie)-1; + if (which < mAssetPaths.size()) { + return mAssetPaths[which].path; + } + return String8(); +} + +/* + * Set the current locale. Use NULL to indicate no locale. + * + * Close and reopen Zip archives as appropriate, and reset cached + * information in the locale-specific sections of the tree. + */ +void AssetManager::setLocale(const char* locale) +{ + AutoMutex _l(mLock); + setLocaleLocked(locale); +} + +void AssetManager::setLocaleLocked(const char* locale) +{ + if (mLocale != NULL) { + /* previously set, purge cached data */ + purgeFileNameCacheLocked(); + //mZipSet.purgeLocale(); + delete[] mLocale; + } + mLocale = strdupNew(locale); + + updateResourceParamsLocked(); +} + +/* + * Set the current vendor. Use NULL to indicate no vendor. + * + * Close and reopen Zip archives as appropriate, and reset cached + * information in the vendor-specific sections of the tree. + */ +void AssetManager::setVendor(const char* vendor) +{ + AutoMutex _l(mLock); + + if (mVendor != NULL) { + /* previously set, purge cached data */ + purgeFileNameCacheLocked(); + //mZipSet.purgeVendor(); + delete[] mVendor; + } + mVendor = strdupNew(vendor); +} + +void AssetManager::setConfiguration(const ResTable_config& config, const char* locale) +{ + AutoMutex _l(mLock); + *mConfig = config; + if (locale) { + setLocaleLocked(locale); + } else if (config.language[0] != 0) { + char spec[9]; + spec[0] = config.language[0]; + spec[1] = config.language[1]; + if (config.country[0] != 0) { + spec[2] = '_'; + spec[3] = config.country[0]; + spec[4] = config.country[1]; + spec[5] = 0; + } else { + spec[3] = 0; + } + setLocaleLocked(spec); + } else { + updateResourceParamsLocked(); + } +} + +/* + * Open an asset. + * + * The data could be; + * - In a file on disk (assetBase + fileName). + * - In a compressed file on disk (assetBase + fileName.gz). + * - In a Zip archive, uncompressed or compressed. + * + * It can be in a number of different directories and Zip archives. + * The search order is: + * - [appname] + * - locale + vendor + * - "default" + vendor + * - locale + "default" + * - "default + "default" + * - "common" + * - (same as above) + * + * To find a particular file, we have to try up to eight paths with + * all three forms of data. + * + * We should probably reject requests for "illegal" filenames, e.g. those + * with illegal characters or "../" backward relative paths. + */ +Asset* AssetManager::open(const char* fileName, AccessMode mode) +{ + AutoMutex _l(mLock); + + LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); + + + if (mCacheMode != CACHE_OFF && !mCacheValid) + loadFileNameCacheLocked(); + + String8 assetName(kAssetsRoot); + assetName.appendPath(fileName); + + /* + * For each top-level asset path, search for the asset. + */ + + size_t i = mAssetPaths.size(); + while (i > 0) { + i--; + LOGV("Looking for asset '%s' in '%s'\n", + assetName.string(), mAssetPaths.itemAt(i).path.string()); + Asset* pAsset = openNonAssetInPathLocked(assetName.string(), mode, mAssetPaths.itemAt(i)); + if (pAsset != NULL) { + return pAsset != kExcludedAsset ? pAsset : NULL; + } + } + + return NULL; +} + +/* + * Open a non-asset file as if it were an asset. + * + * The "fileName" is the partial path starting from the application + * name. + */ +Asset* AssetManager::openNonAsset(const char* fileName, AccessMode mode) +{ + AutoMutex _l(mLock); + + LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); + + + if (mCacheMode != CACHE_OFF && !mCacheValid) + loadFileNameCacheLocked(); + + /* + * For each top-level asset path, search for the asset. + */ + + size_t i = mAssetPaths.size(); + while (i > 0) { + i--; + LOGV("Looking for non-asset '%s' in '%s'\n", fileName, mAssetPaths.itemAt(i).path.string()); + Asset* pAsset = openNonAssetInPathLocked( + fileName, mode, mAssetPaths.itemAt(i)); + if (pAsset != NULL) { + return pAsset != kExcludedAsset ? pAsset : NULL; + } + } + + return NULL; +} + +Asset* AssetManager::openNonAsset(void* cookie, const char* fileName, AccessMode mode) +{ + const size_t which = ((size_t)cookie)-1; + + AutoMutex _l(mLock); + + LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); + + + if (mCacheMode != CACHE_OFF && !mCacheValid) + loadFileNameCacheLocked(); + + if (which < mAssetPaths.size()) { + LOGV("Looking for non-asset '%s' in '%s'\n", fileName, + mAssetPaths.itemAt(which).path.string()); + Asset* pAsset = openNonAssetInPathLocked( + fileName, mode, mAssetPaths.itemAt(which)); + if (pAsset != NULL) { + return pAsset != kExcludedAsset ? pAsset : NULL; + } + } + + return NULL; +} + +/* + * Get the type of a file in the asset namespace. + * + * This currently only works for regular files. All others (including + * directories) will return kFileTypeNonexistent. + */ +FileType AssetManager::getFileType(const char* fileName) +{ + Asset* pAsset = NULL; + + /* + * Open the asset. This is less efficient than simply finding the + * file, but it's not too bad (we don't uncompress or mmap data until + * the first read() call). + */ + pAsset = open(fileName, Asset::ACCESS_STREAMING); + delete pAsset; + + if (pAsset == NULL) + return kFileTypeNonexistent; + else + return kFileTypeRegular; +} + +const ResTable* AssetManager::getResTable(bool required) const +{ + ResTable* rt = mResources; + if (rt) { + return rt; + } + + // Iterate through all asset packages, collecting resources from each. + + AutoMutex _l(mLock); + + if (mResources != NULL) { + return mResources; + } + + if (required) { + LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); + } + + if (mCacheMode != CACHE_OFF && !mCacheValid) + const_cast(this)->loadFileNameCacheLocked(); + + const size_t N = mAssetPaths.size(); + for (size_t i=0; i(this)-> + mZipSet.getZipResourceTable(ap.path); + if (ass == NULL) { + LOGV("loading resource table %s\n", ap.path.string()); + ass = const_cast(this)-> + openNonAssetInPathLocked("resources.arsc", + Asset::ACCESS_BUFFER, + ap); + if (ass != NULL && ass != kExcludedAsset) { + ass = const_cast(this)-> + mZipSet.setZipResourceTable(ap.path, ass); + } + } + } else { + LOGV("loading resource table %s\n", ap.path.string()); + Asset* ass = const_cast(this)-> + openNonAssetInPathLocked("resources.arsc", + Asset::ACCESS_BUFFER, + ap); + shared = false; + } + if (ass != NULL && ass != kExcludedAsset) { + if (rt == NULL) { + mResources = rt = new ResTable(); + updateResourceParamsLocked(); + } + LOGV("Installing resource asset %p in to table %p\n", ass, mResources); + rt->add(ass, (void*)(i+1), !shared); + + if (!shared) { + delete ass; + } + } + } + + if (required && !rt) LOGW("Unable to find resources file resources.arsc"); + if (!rt) { + mResources = rt = new ResTable(); + } + return rt; +} + +void AssetManager::updateResourceParamsLocked() const +{ + ResTable* res = mResources; + if (!res) { + return; + } + + size_t llen = mLocale ? strlen(mLocale) : 0; + mConfig->language[0] = 0; + mConfig->language[1] = 0; + mConfig->country[0] = 0; + mConfig->country[1] = 0; + if (llen >= 2) { + mConfig->language[0] = mLocale[0]; + mConfig->language[1] = mLocale[1]; + } + if (llen >= 5) { + mConfig->country[0] = mLocale[3]; + mConfig->country[1] = mLocale[4]; + } + mConfig->size = sizeof(*mConfig); + + res->setParameters(mConfig); +} + +const ResTable& AssetManager::getResources(bool required) const +{ + const ResTable* rt = getResTable(required); + return *rt; +} + +bool AssetManager::isUpToDate() +{ + AutoMutex _l(mLock); + return mZipSet.isUpToDate(); +} + +void AssetManager::getLocales(Vector* locales) const +{ + ResTable* res = mResources; + if (res != NULL) { + res->getLocales(locales); + } +} + +/* + * Open a non-asset file as if it were an asset, searching for it in the + * specified app. + * + * Pass in a NULL values for "appName" if the common app directory should + * be used. + */ +Asset* AssetManager::openNonAssetInPathLocked(const char* fileName, AccessMode mode, + const asset_path& ap) +{ + Asset* pAsset = NULL; + + /* look at the filesystem on disk */ + if (ap.type == kFileTypeDirectory) { + String8 path(ap.path); + path.appendPath(fileName); + + pAsset = openAssetFromFileLocked(path, mode); + + if (pAsset == NULL) { + /* try again, this time with ".gz" */ + path.append(".gz"); + pAsset = openAssetFromFileLocked(path, mode); + } + + if (pAsset != NULL) { + //printf("FOUND NA '%s' on disk\n", fileName); + pAsset->setAssetSource(path); + } + + /* look inside the zip file */ + } else { + String8 path(fileName); + + /* check the appropriate Zip file */ + ZipFileRO* pZip; + ZipEntryRO entry; + + pZip = getZipFileLocked(ap); + if (pZip != NULL) { + //printf("GOT zip, checking NA '%s'\n", (const char*) path); + entry = pZip->findEntryByName(path.string()); + if (entry != NULL) { + //printf("FOUND NA in Zip file for %s\n", appName ? appName : kAppCommon); + pAsset = openAssetFromZipLocked(pZip, entry, mode, path); + } + } + + if (pAsset != NULL) { + /* create a "source" name, for debug/display */ + pAsset->setAssetSource( + createZipSourceNameLocked(ZipSet::getPathName(ap.path.string()), String8(""), + String8(fileName))); + } + } + + return pAsset; +} + +/* + * Open an asset, searching for it in the directory hierarchy for the + * specified app. + * + * Pass in a NULL values for "appName" if the common app directory should + * be used. + */ +Asset* AssetManager::openInPathLocked(const char* fileName, AccessMode mode, + const asset_path& ap) +{ + Asset* pAsset = NULL; + + /* + * Try various combinations of locale and vendor. + */ + if (mLocale != NULL && mVendor != NULL) + pAsset = openInLocaleVendorLocked(fileName, mode, ap, mLocale, mVendor); + if (pAsset == NULL && mVendor != NULL) + pAsset = openInLocaleVendorLocked(fileName, mode, ap, NULL, mVendor); + if (pAsset == NULL && mLocale != NULL) + pAsset = openInLocaleVendorLocked(fileName, mode, ap, mLocale, NULL); + if (pAsset == NULL) + pAsset = openInLocaleVendorLocked(fileName, mode, ap, NULL, NULL); + + return pAsset; +} + +/* + * Open an asset, searching for it in the directory hierarchy for the + * specified locale and vendor. + * + * We also search in "app.jar". + * + * Pass in NULL values for "appName", "locale", and "vendor" if the + * defaults should be used. + */ +Asset* AssetManager::openInLocaleVendorLocked(const char* fileName, AccessMode mode, + const asset_path& ap, const char* locale, const char* vendor) +{ + Asset* pAsset = NULL; + + if (ap.type == kFileTypeDirectory) { + if (mCacheMode == CACHE_OFF) { + /* look at the filesystem on disk */ + String8 path(createPathNameLocked(ap, locale, vendor)); + path.appendPath(fileName); + + String8 excludeName(path); + excludeName.append(kExcludeExtension); + if (::getFileType(excludeName.string()) != kFileTypeNonexistent) { + /* say no more */ + //printf("+++ excluding '%s'\n", (const char*) excludeName); + return kExcludedAsset; + } + + pAsset = openAssetFromFileLocked(path, mode); + + if (pAsset == NULL) { + /* try again, this time with ".gz" */ + path.append(".gz"); + pAsset = openAssetFromFileLocked(path, mode); + } + + if (pAsset != NULL) + pAsset->setAssetSource(path); + } else { + /* find in cache */ + String8 path(createPathNameLocked(ap, locale, vendor)); + path.appendPath(fileName); + + AssetDir::FileInfo tmpInfo; + bool found = false; + + String8 excludeName(path); + excludeName.append(kExcludeExtension); + + if (mCache.indexOf(excludeName) != NAME_NOT_FOUND) { + /* go no farther */ + //printf("+++ Excluding '%s'\n", (const char*) excludeName); + return kExcludedAsset; + } + + /* + * File compression extensions (".gz") don't get stored in the + * name cache, so we have to try both here. + */ + if (mCache.indexOf(path) != NAME_NOT_FOUND) { + found = true; + pAsset = openAssetFromFileLocked(path, mode); + if (pAsset == NULL) { + /* try again, this time with ".gz" */ + path.append(".gz"); + pAsset = openAssetFromFileLocked(path, mode); + } + } + + if (pAsset != NULL) + pAsset->setAssetSource(path); + + /* + * Don't continue the search into the Zip files. Our cached info + * said it was a file on disk; to be consistent with openDir() + * we want to return the loose asset. If the cached file gets + * removed, we fail. + * + * The alternative is to update our cache when files get deleted, + * or make some sort of "best effort" promise, but for now I'm + * taking the hard line. + */ + if (found) { + if (pAsset == NULL) + LOGD("Expected file not found: '%s'\n", path.string()); + return pAsset; + } + } + } + + /* + * Either it wasn't found on disk or on the cached view of the disk. + * Dig through the currently-opened set of Zip files. If caching + * is disabled, the Zip file may get reopened. + */ + if (pAsset == NULL && ap.type == kFileTypeRegular) { + String8 path; + + path.appendPath((locale != NULL) ? locale : kDefaultLocale); + path.appendPath((vendor != NULL) ? vendor : kDefaultVendor); + path.appendPath(fileName); + + /* check the appropriate Zip file */ + ZipFileRO* pZip; + ZipEntryRO entry; + + pZip = getZipFileLocked(ap); + if (pZip != NULL) { + //printf("GOT zip, checking '%s'\n", (const char*) path); + entry = pZip->findEntryByName(path.string()); + if (entry != NULL) { + //printf("FOUND in Zip file for %s/%s-%s\n", + // appName, locale, vendor); + pAsset = openAssetFromZipLocked(pZip, entry, mode, path); + } + } + + if (pAsset != NULL) { + /* create a "source" name, for debug/display */ + pAsset->setAssetSource(createZipSourceNameLocked(ZipSet::getPathName(ap.path.string()), + String8(""), String8(fileName))); + } + } + + return pAsset; +} + +/* + * Create a "source name" for a file from a Zip archive. + */ +String8 AssetManager::createZipSourceNameLocked(const String8& zipFileName, + const String8& dirName, const String8& fileName) +{ + String8 sourceName("zip:"); + sourceName.append(zipFileName); + sourceName.append(":"); + if (dirName.length() > 0) { + sourceName.appendPath(dirName); + } + sourceName.appendPath(fileName); + return sourceName; +} + +/* + * Create a path to a loose asset (asset-base/app/locale/vendor). + */ +String8 AssetManager::createPathNameLocked(const asset_path& ap, const char* locale, + const char* vendor) +{ + String8 path(ap.path); + path.appendPath((locale != NULL) ? locale : kDefaultLocale); + path.appendPath((vendor != NULL) ? vendor : kDefaultVendor); + return path; +} + +/* + * Create a path to a loose asset (asset-base/app/rootDir). + */ +String8 AssetManager::createPathNameLocked(const asset_path& ap, const char* rootDir) +{ + String8 path(ap.path); + if (rootDir != NULL) path.appendPath(rootDir); + return path; +} + +/* + * Return a pointer to one of our open Zip archives. Returns NULL if no + * matching Zip file exists. + * + * Right now we have 2 possible Zip files (1 each in app/"common"). + * + * If caching is set to CACHE_OFF, to get the expected behavior we + * need to reopen the Zip file on every request. That would be silly + * and expensive, so instead we just check the file modification date. + * + * Pass in NULL values for "appName", "locale", and "vendor" if the + * generics should be used. + */ +ZipFileRO* AssetManager::getZipFileLocked(const asset_path& ap) +{ + LOGV("getZipFileLocked() in %p\n", this); + + return mZipSet.getZip(ap.path); +} + +/* + * Try to open an asset from a file on disk. + * + * If the file is compressed with gzip, we seek to the start of the + * deflated data and pass that in (just like we would for a Zip archive). + * + * For uncompressed data, we may already have an mmap()ed version sitting + * around. If so, we want to hand that to the Asset instead. + * + * This returns NULL if the file doesn't exist, couldn't be opened, or + * claims to be a ".gz" but isn't. + */ +Asset* AssetManager::openAssetFromFileLocked(const String8& pathName, + AccessMode mode) +{ + Asset* pAsset = NULL; + + if (strcasecmp(pathName.getPathExtension().string(), ".gz") == 0) { + //printf("TRYING '%s'\n", (const char*) pathName); + pAsset = Asset::createFromCompressedFile(pathName.string(), mode); + } else { + //printf("TRYING '%s'\n", (const char*) pathName); + pAsset = Asset::createFromFile(pathName.string(), mode); + } + + return pAsset; +} + +/* + * Given an entry in a Zip archive, create a new Asset object. + * + * If the entry is uncompressed, we may want to create or share a + * slice of shared memory. + */ +Asset* AssetManager::openAssetFromZipLocked(const ZipFileRO* pZipFile, + const ZipEntryRO entry, AccessMode mode, const String8& entryName) +{ + Asset* pAsset = NULL; + + // TODO: look for previously-created shared memory slice? + int method; + long uncompressedLen; + + //printf("USING Zip '%s'\n", pEntry->getFileName()); + + //pZipFile->getEntryInfo(entry, &method, &uncompressedLen, &compressedLen, + // &offset); + if (!pZipFile->getEntryInfo(entry, &method, &uncompressedLen, NULL, NULL, + NULL, NULL)) + { + LOGW("getEntryInfo failed\n"); + return NULL; + } + + FileMap* dataMap = pZipFile->createEntryFileMap(entry); + if (dataMap == NULL) { + LOGW("create map from entry failed\n"); + return NULL; + } + + if (method == ZipFileRO::kCompressStored) { + pAsset = Asset::createFromUncompressedMap(dataMap, mode); + LOGV("Opened uncompressed entry %s in zip %s mode %d: %p", entryName.string(), + dataMap->getFileName(), mode, pAsset); + } else { + pAsset = Asset::createFromCompressedMap(dataMap, method, + uncompressedLen, mode); + LOGV("Opened compressed entry %s in zip %s mode %d: %p", entryName.string(), + dataMap->getFileName(), mode, pAsset); + } + if (pAsset == NULL) { + /* unexpected */ + LOGW("create from segment failed\n"); + } + + return pAsset; +} + + + +/* + * Open a directory in the asset namespace. + * + * An "asset directory" is simply the combination of all files in all + * locations, with ".gz" stripped for loose files. With app, locale, and + * vendor defined, we have 8 directories and 2 Zip archives to scan. + * + * Pass in "" for the root dir. + */ +AssetDir* AssetManager::openDir(const char* dirName) +{ + AutoMutex _l(mLock); + + AssetDir* pDir = NULL; + SortedVector* pMergedInfo = NULL; + + LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); + assert(dirName != NULL); + + //printf("+++ openDir(%s) in '%s'\n", dirName, (const char*) mAssetBase); + + if (mCacheMode != CACHE_OFF && !mCacheValid) + loadFileNameCacheLocked(); + + pDir = new AssetDir; + + /* + * Scan the various directories, merging what we find into a single + * vector. We want to scan them in reverse priority order so that + * the ".EXCLUDE" processing works correctly. Also, if we decide we + * want to remember where the file is coming from, we'll get the right + * version. + * + * We start with Zip archives, then do loose files. + */ + pMergedInfo = new SortedVector; + + size_t i = mAssetPaths.size(); + while (i > 0) { + i--; + const asset_path& ap = mAssetPaths.itemAt(i); + if (ap.type == kFileTypeRegular) { + LOGV("Adding directory %s from zip %s", dirName, ap.path.string()); + scanAndMergeZipLocked(pMergedInfo, ap, kAssetsRoot, dirName); + } else { + LOGV("Adding directory %s from dir %s", dirName, ap.path.string()); + scanAndMergeDirLocked(pMergedInfo, ap, kAssetsRoot, dirName); + } + } + +#if 0 + printf("FILE LIST:\n"); + for (i = 0; i < (size_t) pMergedInfo->size(); i++) { + printf(" %d: (%d) '%s'\n", i, + pMergedInfo->itemAt(i).getFileType(), + (const char*) pMergedInfo->itemAt(i).getFileName()); + } +#endif + + pDir->setFileList(pMergedInfo); + return pDir; +} + +/* + * Scan the contents of the specified directory and merge them into the + * "pMergedInfo" vector, removing previous entries if we find "exclude" + * directives. + * + * Returns "false" if we found nothing to contribute. + */ +bool AssetManager::scanAndMergeDirLocked(SortedVector* pMergedInfo, + const asset_path& ap, const char* rootDir, const char* dirName) +{ + SortedVector* pContents; + String8 path; + + assert(pMergedInfo != NULL); + + //printf("scanAndMergeDir: %s %s %s %s\n", appName, locale, vendor,dirName); + + if (mCacheValid) { + int i, start, count; + + pContents = new SortedVector; + + /* + * Get the basic partial path and find it in the cache. That's + * the start point for the search. + */ + path = createPathNameLocked(ap, rootDir); + if (dirName[0] != '\0') + path.appendPath(dirName); + + start = mCache.indexOf(path); + if (start == NAME_NOT_FOUND) { + //printf("+++ not found in cache: dir '%s'\n", (const char*) path); + delete pContents; + return false; + } + + /* + * The match string looks like "common/default/default/foo/bar/". + * The '/' on the end ensures that we don't match on the directory + * itself or on ".../foo/barfy/". + */ + path.append("/"); + + count = mCache.size(); + + /* + * Pick out the stuff in the current dir by examining the pathname. + * It needs to match the partial pathname prefix, and not have a '/' + * (fssep) anywhere after the prefix. + */ + for (i = start+1; i < count; i++) { + if (mCache[i].getFileName().length() > path.length() && + strncmp(mCache[i].getFileName().string(), path.string(), path.length()) == 0) + { + const char* name = mCache[i].getFileName().string(); + // XXX THIS IS BROKEN! Looks like we need to store the full + // path prefix separately from the file path. + if (strchr(name + path.length(), '/') == NULL) { + /* grab it, reducing path to just the filename component */ + AssetDir::FileInfo tmp = mCache[i]; + tmp.setFileName(tmp.getFileName().getPathLeaf()); + pContents->add(tmp); + } + } else { + /* no longer in the dir or its subdirs */ + break; + } + + } + } else { + path = createPathNameLocked(ap, rootDir); + if (dirName[0] != '\0') + path.appendPath(dirName); + pContents = scanDirLocked(path); + if (pContents == NULL) + return false; + } + + // if we wanted to do an incremental cache fill, we would do it here + + /* + * Process "exclude" directives. If we find a filename that ends with + * ".EXCLUDE", we look for a matching entry in the "merged" set, and + * remove it if we find it. We also delete the "exclude" entry. + */ + int i, count, exclExtLen; + + count = pContents->size(); + exclExtLen = strlen(kExcludeExtension); + for (i = 0; i < count; i++) { + const char* name; + int nameLen; + + name = pContents->itemAt(i).getFileName().string(); + nameLen = strlen(name); + if (nameLen > exclExtLen && + strcmp(name + (nameLen - exclExtLen), kExcludeExtension) == 0) + { + String8 match(name, nameLen - exclExtLen); + int matchIdx; + + matchIdx = AssetDir::FileInfo::findEntry(pMergedInfo, match); + if (matchIdx > 0) { + LOGV("Excluding '%s' [%s]\n", + pMergedInfo->itemAt(matchIdx).getFileName().string(), + pMergedInfo->itemAt(matchIdx).getSourceName().string()); + pMergedInfo->removeAt(matchIdx); + } else { + //printf("+++ no match on '%s'\n", (const char*) match); + } + + LOGD("HEY: size=%d removing %d\n", (int)pContents->size(), i); + pContents->removeAt(i); + i--; // adjust "for" loop + count--; // and loop limit + } + } + + mergeInfoLocked(pMergedInfo, pContents); + + delete pContents; + + return true; +} + +/* + * Scan the contents of the specified directory, and stuff what we find + * into a newly-allocated vector. + * + * Files ending in ".gz" will have their extensions removed. + * + * We should probably think about skipping files with "illegal" names, + * e.g. illegal characters (/\:) or excessive length. + * + * Returns NULL if the specified directory doesn't exist. + */ +SortedVector* AssetManager::scanDirLocked(const String8& path) +{ + SortedVector* pContents = NULL; + DIR* dir; + struct dirent* entry; + FileType fileType; + + LOGV("Scanning dir '%s'\n", path.string()); + + dir = opendir(path.string()); + if (dir == NULL) + return NULL; + + pContents = new SortedVector; + + while (1) { + entry = readdir(dir); + if (entry == NULL) + break; + + if (strcmp(entry->d_name, ".") == 0 || + strcmp(entry->d_name, "..") == 0) + continue; + +#ifdef _DIRENT_HAVE_D_TYPE + if (entry->d_type == DT_REG) + fileType = kFileTypeRegular; + else if (entry->d_type == DT_DIR) + fileType = kFileTypeDirectory; + else + fileType = kFileTypeUnknown; +#else + // stat the file + fileType = ::getFileType(path.appendPathCopy(entry->d_name).string()); +#endif + + if (fileType != kFileTypeRegular && fileType != kFileTypeDirectory) + continue; + + AssetDir::FileInfo info; + info.set(String8(entry->d_name), fileType); + if (strcasecmp(info.getFileName().getPathExtension().string(), ".gz") == 0) + info.setFileName(info.getFileName().getBasePath()); + info.setSourceName(path.appendPathCopy(info.getFileName())); + pContents->add(info); + } + + closedir(dir); + return pContents; +} + +/* + * Scan the contents out of the specified Zip archive, and merge what we + * find into "pMergedInfo". If the Zip archive in question doesn't exist, + * we return immediately. + * + * Returns "false" if we found nothing to contribute. + */ +bool AssetManager::scanAndMergeZipLocked(SortedVector* pMergedInfo, + const asset_path& ap, const char* rootDir, const char* baseDirName) +{ + ZipFileRO* pZip; + Vector dirs; + AssetDir::FileInfo info; + SortedVector contents; + String8 sourceName, zipName, dirName; + + pZip = mZipSet.getZip(ap.path); + if (pZip == NULL) { + LOGW("Failure opening zip %s\n", ap.path.string()); + return false; + } + + zipName = ZipSet::getPathName(ap.path.string()); + + /* convert "sounds" to "rootDir/sounds" */ + if (rootDir != NULL) dirName = rootDir; + dirName.appendPath(baseDirName); + + /* + * Scan through the list of files, looking for a match. The files in + * the Zip table of contents are not in sorted order, so we have to + * process the entire list. We're looking for a string that begins + * with the characters in "dirName", is followed by a '/', and has no + * subsequent '/' in the stuff that follows. + * + * What makes this especially fun is that directories are not stored + * explicitly in Zip archives, so we have to infer them from context. + * When we see "sounds/foo.wav" we have to leave a note to ourselves + * to insert a directory called "sounds" into the list. We store + * these in temporary vector so that we only return each one once. + * + * Name comparisons are case-sensitive to match UNIX filesystem + * semantics. + */ + int dirNameLen = dirName.length(); + for (int i = 0; i < pZip->getNumEntries(); i++) { + ZipEntryRO entry; + char nameBuf[256]; + + entry = pZip->findEntryByIndex(i); + if (pZip->getEntryFileName(entry, nameBuf, sizeof(nameBuf)) != 0) { + // TODO: fix this if we expect to have long names + LOGE("ARGH: name too long?\n"); + continue; + } + if (dirNameLen == 0 || + (strncmp(nameBuf, dirName.string(), dirNameLen) == 0 && + nameBuf[dirNameLen] == '/')) + { + const char* cp; + const char* nextSlash; + + cp = nameBuf + dirNameLen; + if (dirNameLen != 0) + cp++; // advance past the '/' + + nextSlash = strchr(cp, '/'); +//xxx this may break if there are bare directory entries + if (nextSlash == NULL) { + /* this is a file in the requested directory */ + + info.set(String8(nameBuf).getPathLeaf(), kFileTypeRegular); + + info.setSourceName( + createZipSourceNameLocked(zipName, dirName, info.getFileName())); + + contents.add(info); + //printf("FOUND: file '%s'\n", (const char*) info.mFileName); + } else { + /* this is a subdir; add it if we don't already have it*/ + String8 subdirName(cp, nextSlash - cp); + size_t j; + size_t N = dirs.size(); + + for (j = 0; j < N; j++) { + if (subdirName == dirs[j]) { + break; + } + } + if (j == N) { + dirs.add(subdirName); + } + + //printf("FOUND: dir '%s'\n", (const char*) subdirName); + } + } + } + + /* + * Add the set of unique directories. + */ + for (int i = 0; i < (int) dirs.size(); i++) { + info.set(dirs[i], kFileTypeDirectory); + info.setSourceName( + createZipSourceNameLocked(zipName, dirName, info.getFileName())); + contents.add(info); + } + + mergeInfoLocked(pMergedInfo, &contents); + + return true; +} + + +/* + * Merge two vectors of FileInfo. + * + * The merged contents will be stuffed into *pMergedInfo. + * + * If an entry for a file exists in both "pMergedInfo" and "pContents", + * we use the newer "pContents" entry. + */ +void AssetManager::mergeInfoLocked(SortedVector* pMergedInfo, + const SortedVector* pContents) +{ + /* + * Merge what we found in this directory with what we found in + * other places. + * + * Two basic approaches: + * (1) Create a new array that holds the unique values of the two + * arrays. + * (2) Take the elements from pContents and shove them into pMergedInfo. + * + * Because these are vectors of complex objects, moving elements around + * inside the vector requires constructing new objects and allocating + * storage for members. With approach #1, we're always adding to the + * end, whereas with #2 we could be inserting multiple elements at the + * front of the vector. Approach #1 requires a full copy of the + * contents of pMergedInfo, but approach #2 requires the same copy for + * every insertion at the front of pMergedInfo. + * + * (We should probably use a SortedVector interface that allows us to + * just stuff items in, trusting us to maintain the sort order.) + */ + SortedVector* pNewSorted; + int mergeMax, contMax; + int mergeIdx, contIdx; + + pNewSorted = new SortedVector; + mergeMax = pMergedInfo->size(); + contMax = pContents->size(); + mergeIdx = contIdx = 0; + + while (mergeIdx < mergeMax || contIdx < contMax) { + if (mergeIdx == mergeMax) { + /* hit end of "merge" list, copy rest of "contents" */ + pNewSorted->add(pContents->itemAt(contIdx)); + contIdx++; + } else if (contIdx == contMax) { + /* hit end of "cont" list, copy rest of "merge" */ + pNewSorted->add(pMergedInfo->itemAt(mergeIdx)); + mergeIdx++; + } else if (pMergedInfo->itemAt(mergeIdx) == pContents->itemAt(contIdx)) + { + /* items are identical, add newer and advance both indices */ + pNewSorted->add(pContents->itemAt(contIdx)); + mergeIdx++; + contIdx++; + } else if (pMergedInfo->itemAt(mergeIdx) < pContents->itemAt(contIdx)) + { + /* "merge" is lower, add that one */ + pNewSorted->add(pMergedInfo->itemAt(mergeIdx)); + mergeIdx++; + } else { + /* "cont" is lower, add that one */ + assert(pContents->itemAt(contIdx) < pMergedInfo->itemAt(mergeIdx)); + pNewSorted->add(pContents->itemAt(contIdx)); + contIdx++; + } + } + + /* + * Overwrite the "merged" list with the new stuff. + */ + *pMergedInfo = *pNewSorted; + delete pNewSorted; + +#if 0 // for Vector, rather than SortedVector + int i, j; + for (i = pContents->size() -1; i >= 0; i--) { + bool add = true; + + for (j = pMergedInfo->size() -1; j >= 0; j--) { + /* case-sensitive comparisons, to behave like UNIX fs */ + if (strcmp(pContents->itemAt(i).mFileName, + pMergedInfo->itemAt(j).mFileName) == 0) + { + /* match, don't add this entry */ + add = false; + break; + } + } + + if (add) + pMergedInfo->add(pContents->itemAt(i)); + } +#endif +} + + +/* + * Load all files into the file name cache. We want to do this across + * all combinations of { appname, locale, vendor }, performing a recursive + * directory traversal. + * + * This is not the most efficient data structure. Also, gathering the + * information as we needed it (file-by-file or directory-by-directory) + * would be faster. However, on the actual device, 99% of the files will + * live in Zip archives, so this list will be very small. The trouble + * is that we have to check the "loose" files first, so it's important + * that we don't beat the filesystem silly looking for files that aren't + * there. + * + * Note on thread safety: this is the only function that causes updates + * to mCache, and anybody who tries to use it will call here if !mCacheValid, + * so we need to employ a mutex here. + */ +void AssetManager::loadFileNameCacheLocked(void) +{ + assert(!mCacheValid); + assert(mCache.size() == 0); + +#ifdef DO_TIMINGS // need to link against -lrt for this now + DurationTimer timer; + timer.start(); +#endif + + fncScanLocked(&mCache, ""); + +#ifdef DO_TIMINGS + timer.stop(); + LOGD("Cache scan took %.3fms\n", + timer.durationUsecs() / 1000.0); +#endif + +#if 0 + int i; + printf("CACHED FILE LIST (%d entries):\n", mCache.size()); + for (i = 0; i < (int) mCache.size(); i++) { + printf(" %d: (%d) '%s'\n", i, + mCache.itemAt(i).getFileType(), + (const char*) mCache.itemAt(i).getFileName()); + } +#endif + + mCacheValid = true; +} + +/* + * Scan up to 8 versions of the specified directory. + */ +void AssetManager::fncScanLocked(SortedVector* pMergedInfo, + const char* dirName) +{ + size_t i = mAssetPaths.size(); + while (i > 0) { + i--; + const asset_path& ap = mAssetPaths.itemAt(i); + fncScanAndMergeDirLocked(pMergedInfo, ap, NULL, NULL, dirName); + if (mLocale != NULL) + fncScanAndMergeDirLocked(pMergedInfo, ap, mLocale, NULL, dirName); + if (mVendor != NULL) + fncScanAndMergeDirLocked(pMergedInfo, ap, NULL, mVendor, dirName); + if (mLocale != NULL && mVendor != NULL) + fncScanAndMergeDirLocked(pMergedInfo, ap, mLocale, mVendor, dirName); + } +} + +/* + * Recursively scan this directory and all subdirs. + * + * This is similar to scanAndMergeDir, but we don't remove the .EXCLUDE + * files, and we prepend the extended partial path to the filenames. + */ +bool AssetManager::fncScanAndMergeDirLocked( + SortedVector* pMergedInfo, + const asset_path& ap, const char* locale, const char* vendor, + const char* dirName) +{ + SortedVector* pContents; + String8 partialPath; + String8 fullPath; + + // XXX This is broken -- the filename cache needs to hold the base + // asset path separately from its filename. + + partialPath = createPathNameLocked(ap, locale, vendor); + if (dirName[0] != '\0') { + partialPath.appendPath(dirName); + } + + fullPath = partialPath; + pContents = scanDirLocked(fullPath); + if (pContents == NULL) { + return false; // directory did not exist + } + + /* + * Scan all subdirectories of the current dir, merging what we find + * into "pMergedInfo". + */ + for (int i = 0; i < (int) pContents->size(); i++) { + if (pContents->itemAt(i).getFileType() == kFileTypeDirectory) { + String8 subdir(dirName); + subdir.appendPath(pContents->itemAt(i).getFileName()); + + fncScanAndMergeDirLocked(pMergedInfo, ap, locale, vendor, subdir.string()); + } + } + + /* + * To be consistent, we want entries for the root directory. If + * we're the root, add one now. + */ + if (dirName[0] == '\0') { + AssetDir::FileInfo tmpInfo; + + tmpInfo.set(String8(""), kFileTypeDirectory); + tmpInfo.setSourceName(createPathNameLocked(ap, locale, vendor)); + pContents->add(tmpInfo); + } + + /* + * We want to prepend the extended partial path to every entry in + * "pContents". It's the same value for each entry, so this will + * not change the sorting order of the vector contents. + */ + for (int i = 0; i < (int) pContents->size(); i++) { + const AssetDir::FileInfo& info = pContents->itemAt(i); + pContents->editItemAt(i).setFileName(partialPath.appendPathCopy(info.getFileName())); + } + + mergeInfoLocked(pMergedInfo, pContents); + return true; +} + +/* + * Trash the cache. + */ +void AssetManager::purgeFileNameCacheLocked(void) +{ + mCacheValid = false; + mCache.clear(); +} + +/* + * =========================================================================== + * AssetManager::SharedZip + * =========================================================================== + */ + + +Mutex AssetManager::SharedZip::gLock; +DefaultKeyedVector > AssetManager::SharedZip::gOpen; + +AssetManager::SharedZip::SharedZip(const String8& path, time_t modWhen) + : mPath(path), mZipFile(NULL), mModWhen(modWhen), mResourceTableAsset(NULL) +{ + //LOGI("Creating SharedZip %p %s\n", this, (const char*)mPath); + mZipFile = new ZipFileRO; + LOGV("+++ opening zip '%s'\n", mPath.string()); + if (mZipFile->open(mPath.string()) != NO_ERROR) { + LOGD("failed to open Zip archive '%s'\n", mPath.string()); + delete mZipFile; + mZipFile = NULL; + } +} + +sp AssetManager::SharedZip::get(const String8& path) +{ + AutoMutex _l(gLock); + time_t modWhen = getFileModDate(path); + sp zip = gOpen.valueFor(path).promote(); + if (zip != NULL && zip->mModWhen == modWhen) { + return zip; + } + zip = new SharedZip(path, modWhen); + gOpen.add(path, zip); + return zip; + +} + +ZipFileRO* AssetManager::SharedZip::getZip() +{ + return mZipFile; +} + +Asset* AssetManager::SharedZip::getResourceTableAsset() +{ + LOGV("Getting from SharedZip %p resource asset %p\n", this, mResourceTableAsset); + return mResourceTableAsset; +} + +Asset* AssetManager::SharedZip::setResourceTableAsset(Asset* asset) +{ + { + AutoMutex _l(gLock); + if (mResourceTableAsset == NULL) { + mResourceTableAsset = asset; + // This is not thread safe the first time it is called, so + // do it here with the global lock held. + asset->getBuffer(true); + return asset; + } + } + delete asset; + return mResourceTableAsset; +} + +bool AssetManager::SharedZip::isUpToDate() +{ + time_t modWhen = getFileModDate(mPath.string()); + return mModWhen == modWhen; +} + +AssetManager::SharedZip::~SharedZip() +{ + //LOGI("Destroying SharedZip %p %s\n", this, (const char*)mPath); + if (mResourceTableAsset != NULL) { + delete mResourceTableAsset; + } + if (mZipFile != NULL) { + delete mZipFile; + LOGV("Closed '%s'\n", mPath.string()); + } +} + +/* + * =========================================================================== + * AssetManager::ZipSet + * =========================================================================== + */ + +/* + * Constructor. + */ +AssetManager::ZipSet::ZipSet(void) +{ +} + +/* + * Destructor. Close any open archives. + */ +AssetManager::ZipSet::~ZipSet(void) +{ + size_t N = mZipFile.size(); + for (size_t i = 0; i < N; i++) + closeZip(i); +} + +/* + * Close a Zip file and reset the entry. + */ +void AssetManager::ZipSet::closeZip(int idx) +{ + mZipFile.editItemAt(idx) = NULL; +} + + +/* + * Retrieve the appropriate Zip file from the set. + */ +ZipFileRO* AssetManager::ZipSet::getZip(const String8& path) +{ + int idx = getIndex(path); + sp zip = mZipFile[idx]; + if (zip == NULL) { + zip = SharedZip::get(path); + mZipFile.editItemAt(idx) = zip; + } + return zip->getZip(); +} + +Asset* AssetManager::ZipSet::getZipResourceTable(const String8& path) +{ + int idx = getIndex(path); + sp zip = mZipFile[idx]; + if (zip == NULL) { + zip = SharedZip::get(path); + mZipFile.editItemAt(idx) = zip; + } + return zip->getResourceTableAsset(); +} + +Asset* AssetManager::ZipSet::setZipResourceTable(const String8& path, + Asset* asset) +{ + int idx = getIndex(path); + sp zip = mZipFile[idx]; + // doesn't make sense to call before previously accessing. + return zip->setResourceTableAsset(asset); +} + +/* + * Generate the partial pathname for the specified archive. The caller + * gets to prepend the asset root directory. + * + * Returns something like "common/en-US-noogle.jar". + */ +/*static*/ String8 AssetManager::ZipSet::getPathName(const char* zipPath) +{ + return String8(zipPath); +} + +bool AssetManager::ZipSet::isUpToDate() +{ + const size_t N = mZipFile.size(); + for (size_t i=0; iisUpToDate()) { + return false; + } + } + return true; +} + +/* + * Compute the zip file's index. + * + * "appName", "locale", and "vendor" should be set to NULL to indicate the + * default directory. + */ +int AssetManager::ZipSet::getIndex(const String8& zip) const +{ + const size_t N = mZipPath.size(); + for (size_t i=0; i + +#include +#include +#include +#include + +#include + +namespace android { + +// --------------------------------------------------------------------------- + +sp IBinder::queryLocalInterface(const String16& descriptor) +{ + return NULL; +} + +BBinder* IBinder::localBinder() +{ + return NULL; +} + +BpBinder* IBinder::remoteBinder() +{ + return NULL; +} + +bool IBinder::checkSubclass(const void* /*subclassID*/) const +{ + return false; +} + +// --------------------------------------------------------------------------- + +class BBinder::Extras +{ +public: + Mutex mLock; + BpBinder::ObjectManager mObjects; +}; + +// --------------------------------------------------------------------------- + +BBinder::BBinder() + : mExtras(NULL) +{ +} + +bool BBinder::isBinderAlive() const +{ + return true; +} + +status_t BBinder::pingBinder() +{ + return NO_ERROR; +} + +String16 BBinder::getInterfaceDescriptor() const +{ + LOGW("reached BBinder::getInterfaceDescriptor (this=%p)", this); + return String16(); +} + +status_t BBinder::transact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + data.setDataPosition(0); + + status_t err = NO_ERROR; + switch (code) { + case PING_TRANSACTION: + reply->writeInt32(pingBinder()); + break; + default: + err = onTransact(code, data, reply, flags); + break; + } + + if (reply != NULL) { + reply->setDataPosition(0); + } + + return err; +} + +status_t BBinder::linkToDeath( + const sp& recipient, void* cookie, uint32_t flags) +{ + return INVALID_OPERATION; +} + +status_t BBinder::unlinkToDeath( + const wp& recipient, void* cookie, uint32_t flags, + wp* outRecipient) +{ + return INVALID_OPERATION; +} + +status_t BBinder::dump(int fd, const Vector& args) +{ + return NO_ERROR; +} + +void BBinder::attachObject( + const void* objectID, void* object, void* cleanupCookie, + object_cleanup_func func) +{ + Extras* e = mExtras; + + if (!e) { + e = new Extras; + if (android_atomic_cmpxchg(0, reinterpret_cast(e), + reinterpret_cast(&mExtras)) != 0) { + delete e; + e = mExtras; + } + if (e == 0) return; // out of memory + } + + AutoMutex _l(e->mLock); + e->mObjects.attach(objectID, object, cleanupCookie, func); +} + +void* BBinder::findObject(const void* objectID) const +{ + Extras* e = mExtras; + if (!e) return NULL; + + AutoMutex _l(e->mLock); + return e->mObjects.find(objectID); +} + +void BBinder::detachObject(const void* objectID) +{ + Extras* e = mExtras; + if (!e) return; + + AutoMutex _l(e->mLock); + e->mObjects.detach(objectID); +} + +BBinder* BBinder::localBinder() +{ + return this; +} + +BBinder::~BBinder() +{ + if (mExtras) delete mExtras; +} + + +status_t BBinder::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch (code) { + case INTERFACE_TRANSACTION: + reply->writeString16(getInterfaceDescriptor()); + return NO_ERROR; + + case DUMP_TRANSACTION: { + int fd = data.readFileDescriptor(); + int argc = data.readInt32(); + Vector args; + for (int i = 0; i < argc && data.dataAvail() > 0; i++) { + args.add(data.readString16()); + } + return dump(fd, args); + } + default: + return UNKNOWN_TRANSACTION; + } +} + +// --------------------------------------------------------------------------- + +enum { + // This is used to transfer ownership of the remote binder from + // the BpRefBase object holding it (when it is constructed), to the + // owner of the BpRefBase object when it first acquires that BpRefBase. + kRemoteAcquired = 0x00000001 +}; + +BpRefBase::BpRefBase(const sp& o) + : mRemote(o.get()), mRefs(NULL), mState(0) +{ + extendObjectLifetime(OBJECT_LIFETIME_WEAK); + + if (mRemote) { + mRemote->incStrong(this); // Removed on first IncStrong(). + mRefs = mRemote->createWeak(this); // Held for our entire lifetime. + } +} + +BpRefBase::~BpRefBase() +{ + if (mRemote) { + if (!(mState&kRemoteAcquired)) { + mRemote->decStrong(this); + } + mRefs->decWeak(this); + } +} + +void BpRefBase::onFirstRef() +{ + android_atomic_or(kRemoteAcquired, &mState); +} + +void BpRefBase::onLastStrongRef(const void* id) +{ + if (mRemote) { + mRemote->decStrong(this); + } +} + +bool BpRefBase::onIncStrongAttempted(uint32_t flags, const void* id) +{ + return mRemote ? mRefs->attemptIncStrong(this) : false; +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/utils/BpBinder.cpp b/libs/utils/BpBinder.cpp new file mode 100644 index 000000000..69ab19574 --- /dev/null +++ b/libs/utils/BpBinder.cpp @@ -0,0 +1,348 @@ +/* + * 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 "BpBinder" +//#define LOG_NDEBUG 0 + +#include + +#include +#include + +#include + +//#undef LOGV +//#define LOGV(...) fprintf(stderr, __VA_ARGS__) + +namespace android { + +// --------------------------------------------------------------------------- + +BpBinder::ObjectManager::ObjectManager() +{ +} + +BpBinder::ObjectManager::~ObjectManager() +{ + kill(); +} + +void BpBinder::ObjectManager::attach( + const void* objectID, void* object, void* cleanupCookie, + IBinder::object_cleanup_func func) +{ + entry_t e; + e.object = object; + e.cleanupCookie = cleanupCookie; + e.func = func; + + if (mObjects.indexOfKey(objectID) >= 0) { + LOGE("Trying to attach object ID %p to binder ObjectManager %p with object %p, but object ID already in use", + objectID, this, object); + return; + } + + mObjects.add(objectID, e); +} + +void* BpBinder::ObjectManager::find(const void* objectID) const +{ + const ssize_t i = mObjects.indexOfKey(objectID); + if (i < 0) return NULL; + return mObjects.valueAt(i).object; +} + +void BpBinder::ObjectManager::detach(const void* objectID) +{ + mObjects.removeItem(objectID); +} + +void BpBinder::ObjectManager::kill() +{ + const size_t N = mObjects.size(); + LOGV("Killing %d objects in manager %p", N, this); + for (size_t i=0; iincWeakHandle(handle); +} + +String16 BpBinder::getInterfaceDescriptor() const +{ + String16 res; + Parcel send, reply; + status_t err = const_cast(this)->transact( + INTERFACE_TRANSACTION, send, &reply); + if (err == NO_ERROR) { + res = reply.readString16(); + } + return res; +} + +bool BpBinder::isBinderAlive() const +{ + return mAlive != 0; +} + +status_t BpBinder::pingBinder() +{ + Parcel send; + Parcel reply; + status_t err = transact(PING_TRANSACTION, send, &reply); + if (err != NO_ERROR) return err; + if (reply.dataSize() < sizeof(status_t)) return NOT_ENOUGH_DATA; + return (status_t)reply.readInt32(); +} + +status_t BpBinder::dump(int fd, const Vector& args) +{ + Parcel send; + Parcel reply; + send.writeFileDescriptor(fd); + const size_t numArgs = args.size(); + send.writeInt32(numArgs); + for (size_t i = 0; i < numArgs; i++) { + send.writeString16(args[i]); + } + status_t err = transact(DUMP_TRANSACTION, send, &reply); + return err; +} + +status_t BpBinder::transact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + // Once a binder has died, it will never come back to life. + if (mAlive) { + status_t status = IPCThreadState::self()->transact( + mHandle, code, data, reply, flags); + if (status == DEAD_OBJECT) mAlive = 0; + return status; + } + + return DEAD_OBJECT; +} + +status_t BpBinder::linkToDeath( + const sp& recipient, void* cookie, uint32_t flags) +{ + Obituary ob; + ob.recipient = recipient; + ob.cookie = cookie; + ob.flags = flags; + + LOG_ALWAYS_FATAL_IF(recipient == NULL, + "linkToDeath(): recipient must be non-NULL"); + + { + AutoMutex _l(mLock); + + if (!mObitsSent) { + if (!mObituaries) { + mObituaries = new Vector; + if (!mObituaries) { + return NO_MEMORY; + } + LOGV("Requesting death notification: %p handle %d\n", this, mHandle); + getWeakRefs()->incWeak(this); + IPCThreadState* self = IPCThreadState::self(); + self->requestDeathNotification(mHandle, this); + self->flushCommands(); + } + ssize_t res = mObituaries->add(ob); + return res >= (ssize_t)NO_ERROR ? (status_t)NO_ERROR : res; + } + } + + return DEAD_OBJECT; +} + +status_t BpBinder::unlinkToDeath( + const wp& recipient, void* cookie, uint32_t flags, + wp* outRecipient) +{ + AutoMutex _l(mLock); + + if (mObitsSent) { + return DEAD_OBJECT; + } + + const size_t N = mObituaries ? mObituaries->size() : 0; + for (size_t i=0; iitemAt(i); + if ((obit.recipient == recipient + || (recipient == NULL && obit.cookie == cookie)) + && obit.flags == flags) { + const uint32_t allFlags = obit.flags|flags; + if (outRecipient != NULL) { + *outRecipient = mObituaries->itemAt(i).recipient; + } + mObituaries->removeAt(i); + if (mObituaries->size() == 0) { + LOGV("Clearing death notification: %p handle %d\n", this, mHandle); + IPCThreadState* self = IPCThreadState::self(); + self->clearDeathNotification(mHandle, this); + self->flushCommands(); + delete mObituaries; + mObituaries = NULL; + } + return NO_ERROR; + } + } + + return NAME_NOT_FOUND; +} + +void BpBinder::sendObituary() +{ + LOGV("Sending obituary for proxy %p handle %d, mObitsSent=%s\n", + this, mHandle, mObitsSent ? "true" : "false"); + + mAlive = 0; + if (mObitsSent) return; + + mLock.lock(); + Vector* obits = mObituaries; + if(obits != NULL) { + LOGV("Clearing sent death notification: %p handle %d\n", this, mHandle); + IPCThreadState* self = IPCThreadState::self(); + self->clearDeathNotification(mHandle, this); + self->flushCommands(); + mObituaries = NULL; + } + mObitsSent = 1; + mLock.unlock(); + + LOGV("Reporting death of proxy %p for %d recipients\n", + this, obits ? obits->size() : 0); + + if (obits != NULL) { + const size_t N = obits->size(); + for (size_t i=0; iitemAt(i)); + } + + delete obits; + } +} + +void BpBinder::reportOneDeath(const Obituary& obit) +{ + sp recipient = obit.recipient.promote(); + LOGV("Reporting death to recipient: %p\n", recipient.get()); + if (recipient == NULL) return; + + recipient->binderDied(this); +} + + +void BpBinder::attachObject( + const void* objectID, void* object, void* cleanupCookie, + object_cleanup_func func) +{ + AutoMutex _l(mLock); + LOGV("Attaching object %p to binder %p (manager=%p)", object, this, &mObjects); + mObjects.attach(objectID, object, cleanupCookie, func); +} + +void* BpBinder::findObject(const void* objectID) const +{ + AutoMutex _l(mLock); + return mObjects.find(objectID); +} + +void BpBinder::detachObject(const void* objectID) +{ + AutoMutex _l(mLock); + mObjects.detach(objectID); +} + +BpBinder* BpBinder::remoteBinder() +{ + return this; +} + +BpBinder::~BpBinder() +{ + LOGV("Destroying BpBinder %p handle %d\n", this, mHandle); + + IPCThreadState* ipc = IPCThreadState::self(); + + mLock.lock(); + Vector* obits = mObituaries; + if(obits != NULL) { + if (ipc) ipc->clearDeathNotification(mHandle, this); + mObituaries = NULL; + } + mLock.unlock(); + + if (obits != NULL) { + // XXX Should we tell any remaining DeathRecipient + // objects that the last strong ref has gone away, so they + // are no longer linked? + delete obits; + } + + if (ipc) { + ipc->expungeHandle(mHandle, this); + ipc->decWeakHandle(mHandle); + } +} + +void BpBinder::onFirstRef() +{ + LOGV("onFirstRef BpBinder %p handle %d\n", this, mHandle); + IPCThreadState* ipc = IPCThreadState::self(); + if (ipc) ipc->incStrongHandle(mHandle); +} + +void BpBinder::onLastStrongRef(const void* id) +{ + LOGV("onLastStrongRef BpBinder %p handle %d\n", this, mHandle); + IF_LOGV() { + printRefs(); + } + IPCThreadState* ipc = IPCThreadState::self(); + if (ipc) ipc->decStrongHandle(mHandle); +} + +bool BpBinder::onIncStrongAttempted(uint32_t flags, const void* id) +{ + LOGV("onIncStrongAttempted BpBinder %p handle %d\n", this, mHandle); + IPCThreadState* ipc = IPCThreadState::self(); + return ipc ? ipc->attemptIncStrongHandle(mHandle) == NO_ERROR : false; +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/utils/BufferedTextOutput.cpp b/libs/utils/BufferedTextOutput.cpp new file mode 100644 index 000000000..989662e84 --- /dev/null +++ b/libs/utils/BufferedTextOutput.cpp @@ -0,0 +1,279 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +// --------------------------------------------------------------------------- + +namespace android { + +struct BufferedTextOutput::BufferState : public RefBase +{ + BufferState(int32_t _seq) + : seq(_seq) + , buffer(NULL) + , bufferPos(0) + , bufferSize(0) + , atFront(true) + , indent(0) + , bundle(0) { + } + ~BufferState() { + free(buffer); + } + + status_t append(const char* txt, size_t len) { + if ((len+bufferPos) > bufferSize) { + void* b = realloc(buffer, ((len+bufferPos)*3)/2); + if (!b) return NO_MEMORY; + buffer = (char*)b; + } + memcpy(buffer+bufferPos, txt, len); + bufferPos += len; + return NO_ERROR; + } + + void restart() { + bufferPos = 0; + atFront = true; + if (bufferSize > 256) { + void* b = realloc(buffer, 256); + if (b) { + buffer = (char*)b; + bufferSize = 256; + } + } + } + + const int32_t seq; + char* buffer; + size_t bufferPos; + size_t bufferSize; + bool atFront; + int32_t indent; + int32_t bundle; +}; + +struct BufferedTextOutput::ThreadState +{ + Vector > states; +}; + +static mutex_t gMutex; + +static thread_store_t tls; + +BufferedTextOutput::ThreadState* BufferedTextOutput::getThreadState() +{ + ThreadState* ts = (ThreadState*) thread_store_get( &tls ); + if (ts) return ts; + ts = new ThreadState; + thread_store_set( &tls, ts, threadDestructor ); + return ts; +} + +void BufferedTextOutput::threadDestructor(void *st) +{ + delete ((ThreadState*)st); +} + +static volatile int32_t gSequence = 0; + +static volatile int32_t gFreeBufferIndex = -1; + +static int32_t allocBufferIndex() +{ + int32_t res = -1; + + mutex_lock(&gMutex); + + if (gFreeBufferIndex >= 0) { + res = gFreeBufferIndex; + gFreeBufferIndex = gTextBuffers[res]; + gTextBuffers.editItemAt(res) = -1; + + } else { + res = gTextBuffers.size(); + gTextBuffers.add(-1); + } + + mutex_unlock(&gMutex); + + return res; +} + +static void freeBufferIndex(int32_t idx) +{ + mutex_lock(&gMutex); + gTextBuffers.editItemAt(idx) = gFreeBufferIndex; + gFreeBufferIndex = idx; + mutex_unlock(&gMutex); +} + +// --------------------------------------------------------------------------- + +BufferedTextOutput::BufferedTextOutput(uint32_t flags) + : mFlags(flags) + , mSeq(android_atomic_inc(&gSequence)) + , mIndex(allocBufferIndex()) +{ + mGlobalState = new BufferState(mSeq); + if (mGlobalState) mGlobalState->incStrong(this); +} + +BufferedTextOutput::~BufferedTextOutput() +{ + if (mGlobalState) mGlobalState->decStrong(this); + freeBufferIndex(mIndex); +} + +status_t BufferedTextOutput::print(const char* txt, size_t len) +{ + //printf("BufferedTextOutput: printing %d\n", len); + + AutoMutex _l(mLock); + BufferState* b = getBuffer(); + + const char* const end = txt+len; + + status_t err; + + while (txt < end) { + // Find the next line. + const char* first = txt; + while (txt < end && *txt != '\n') txt++; + + // Include this and all following empty lines. + while (txt < end && *txt == '\n') txt++; + + // Special cases for first data on a line. + if (b->atFront) { + if (b->indent > 0) { + // If this is the start of a line, add the indent. + const char* prefix = stringForIndent(b->indent); + err = b->append(prefix, strlen(prefix)); + if (err != NO_ERROR) return err; + + } else if (*(txt-1) == '\n' && !b->bundle) { + // Fast path: if we are not indenting or bundling, and + // have been given one or more complete lines, just write + // them out without going through the buffer. + + // Slurp up all of the lines. + const char* lastLine = txt+1; + while (txt < end) { + if (*txt++ == '\n') lastLine = txt; + } + struct iovec vec; + vec.iov_base = (void*)first; + vec.iov_len = lastLine-first; + //printf("Writing %d bytes of data!\n", vec.iov_len); + writeLines(vec, 1); + txt = lastLine; + continue; + } + } + + // Append the new text to the buffer. + err = b->append(first, txt-first); + if (err != NO_ERROR) return err; + b->atFront = *(txt-1) == '\n'; + + // If we have finished a line and are not bundling, write + // it out. + //printf("Buffer is now %d bytes\n", b->bufferPos); + if (b->atFront && !b->bundle) { + struct iovec vec; + vec.iov_base = b->buffer; + vec.iov_len = b->bufferPos; + //printf("Writing %d bytes of data!\n", vec.iov_len); + writeLines(vec, 1); + b->restart(); + } + } + + return NO_ERROR; +} + +void BufferedTextOutput::moveIndent(int delta) +{ + AutoMutex _l(mLock); + BufferState* b = getBuffer(); + b->indent += delta; + if (b->indent < 0) b->indent = 0; +} + +void BufferedTextOutput::pushBundle() +{ + AutoMutex _l(mLock); + BufferState* b = getBuffer(); + b->bundle++; +} + +void BufferedTextOutput::popBundle() +{ + AutoMutex _l(mLock); + BufferState* b = getBuffer(); + b->bundle--; + LOG_FATAL_IF(b->bundle < 0, + "TextOutput::popBundle() called more times than pushBundle()"); + if (b->bundle < 0) b->bundle = 0; + + if (b->bundle == 0) { + // Last bundle, write out data if it is complete. If it is not + // complete, don't write until the last line is done... this may + // or may not be the write thing to do, but it's the easiest. + if (b->bufferPos > 0 && b->atFront) { + struct iovec vec; + vec.iov_base = b->buffer; + vec.iov_len = b->bufferPos; + writeLines(vec, 1); + b->restart(); + } + } +} + +BufferedTextOutput::BufferState* BufferedTextOutput::getBuffer() const +{ + if ((mFlags&MULTITHREADED) != 0) { + ThreadState* ts = getThreadState(); + if (ts) { + while (ts->states.size() <= (size_t)mIndex) ts->states.add(NULL); + BufferState* bs = ts->states[mIndex].get(); + if (bs != NULL && bs->seq == mSeq) return bs; + + ts->states.editItemAt(mIndex) = new BufferState(mIndex); + bs = ts->states[mIndex].get(); + if (bs != NULL) return bs; + } + } + + return mGlobalState; +} + +}; // namespace android diff --git a/libs/utils/CallStack.cpp b/libs/utils/CallStack.cpp new file mode 100644 index 000000000..26fb22abc --- /dev/null +++ b/libs/utils/CallStack.cpp @@ -0,0 +1,335 @@ +/* + * 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 +#include +#include + +#if HAVE_DLADDR +#include +#endif + +#if HAVE_CXXABI +#include +#endif + +#include + +#include +#include +#include +#include + + +/*****************************************************************************/ +namespace android { + + +typedef struct { + size_t count; + size_t ignore; + const void** addrs; +} stack_crawl_state_t; + +static +_Unwind_Reason_Code trace_function(_Unwind_Context *context, void *arg) +{ + stack_crawl_state_t* state = (stack_crawl_state_t*)arg; + if (state->count) { + void* ip = (void*)_Unwind_GetIP(context); + if (ip) { + if (state->ignore) { + state->ignore--; + } else { + state->addrs[0] = ip; + state->addrs++; + state->count--; + } + } + } + return _URC_NO_REASON; +} + +static +int backtrace(const void** addrs, size_t ignore, size_t size) +{ + stack_crawl_state_t state; + state.count = size; + state.ignore = ignore; + state.addrs = addrs; + _Unwind_Backtrace(trace_function, (void*)&state); + return size - state.count; +} + +/*****************************************************************************/ + +static +const char *lookup_symbol(const void* addr, void **offset, char* name, size_t bufSize) +{ +#if HAVE_DLADDR + Dl_info info; + if (dladdr(addr, &info)) { + *offset = info.dli_saddr; + return info.dli_sname; + } +#endif + return NULL; +} + +static +int32_t linux_gcc_demangler(const char *mangled_name, char *unmangled_name, size_t buffersize) +{ + size_t out_len = 0; +#if HAVE_CXXABI + int status = 0; + char *demangled = abi::__cxa_demangle(mangled_name, 0, &out_len, &status); + if (status == 0) { + // OK + if (out_len < buffersize) memcpy(unmangled_name, demangled, out_len); + else out_len = 0; + free(demangled); + } else { + out_len = 0; + } +#endif + return out_len; +} + +/*****************************************************************************/ + +class MapInfo { + struct mapinfo { + struct mapinfo *next; + uint64_t start; + uint64_t end; + char name[]; + }; + + const char *map_to_name(uint64_t pc, const char* def) { + mapinfo* mi = getMapInfoList(); + while(mi) { + if ((pc >= mi->start) && (pc < mi->end)) + return mi->name; + mi = mi->next; + } + return def; + } + + mapinfo *parse_maps_line(char *line) { + mapinfo *mi; + int len = strlen(line); + if (len < 1) return 0; + line[--len] = 0; + if (len < 50) return 0; + if (line[20] != 'x') return 0; + mi = (mapinfo*)malloc(sizeof(mapinfo) + (len - 47)); + if (mi == 0) return 0; + mi->start = strtoull(line, 0, 16); + mi->end = strtoull(line + 9, 0, 16); + mi->next = 0; + strcpy(mi->name, line + 49); + return mi; + } + + mapinfo* getMapInfoList() { + Mutex::Autolock _l(mLock); + if (milist == 0) { + char data[1024]; + FILE *fp; + sprintf(data, "/proc/%d/maps", getpid()); + fp = fopen(data, "r"); + if (fp) { + while(fgets(data, 1024, fp)) { + mapinfo *mi = parse_maps_line(data); + if(mi) { + mi->next = milist; + milist = mi; + } + } + fclose(fp); + } + } + return milist; + } + mapinfo* milist; + Mutex mLock; + static MapInfo sMapInfo; + +public: + MapInfo() + : milist(0) { + } + + ~MapInfo() { + while (milist) { + mapinfo *next = milist->next; + free(milist); + milist = next; + } + } + + static const char *mapAddressToName(const void* pc, const char* def) { + return sMapInfo.map_to_name((uint64_t)pc, def); + } + +}; + +/*****************************************************************************/ + +MapInfo MapInfo::sMapInfo; + +/*****************************************************************************/ + +CallStack::CallStack() + : mCount(0) +{ +} + +CallStack::CallStack(const CallStack& rhs) + : mCount(rhs.mCount) +{ + if (mCount) { + memcpy(mStack, rhs.mStack, mCount*sizeof(void*)); + } +} + +CallStack::~CallStack() +{ +} + +CallStack& CallStack::operator = (const CallStack& rhs) +{ + mCount = rhs.mCount; + if (mCount) { + memcpy(mStack, rhs.mStack, mCount*sizeof(void*)); + } + return *this; +} + +bool CallStack::operator == (const CallStack& rhs) const { + if (mCount != rhs.mCount) + return false; + return !mCount || (memcmp(mStack, rhs.mStack, mCount*sizeof(void*)) == 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(void*)) < 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(void*)) > 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 mStack[index]; +} + + +void CallStack::clear() +{ + mCount = 0; +} + +void CallStack::update(int32_t ignoreDepth, int32_t maxDepth) +{ + if (maxDepth > MAX_DEPTH) + maxDepth = MAX_DEPTH; + mCount = backtrace(mStack, ignoreDepth, maxDepth); +} + +// Return the stack frame name on the designated level +String8 CallStack::toStringSingleLevel(const char* prefix, int32_t level) const +{ + String8 res; + char namebuf[1024]; + char tmp[256]; + char tmp1[32]; + char tmp2[32]; + void *offs; + + const void* ip = mStack[level]; + if (!ip) return res; + + if (prefix) res.append(prefix); + snprintf(tmp1, 32, "#%02d ", level); + res.append(tmp1); + + const char* name = lookup_symbol(ip, &offs, namebuf, sizeof(namebuf)); + if (name) { + if (linux_gcc_demangler(name, tmp, 256) != 0) + name = tmp; + snprintf(tmp1, 32, "0x%p: <", ip); + snprintf(tmp2, 32, ">+0x%p", offs); + res.append(tmp1); + res.append(name); + res.append(tmp2); + } else { + name = MapInfo::mapAddressToName(ip, ""); + snprintf(tmp, 256, "pc %p %s", ip, name); + res.append(tmp); + } + res.append("\n"); + + return res; +} + +// Dump a stack trace to the log +void CallStack::dump(const char* prefix) const +{ + /* + * Sending a single long log may be truncated since the stack levels can + * get very deep. So we request function names of each frame individually. + */ + for (int i=0; i + +#include + +#include +#include +#include + +namespace android { + +// --------------------------------------------------------------------- + +static const char indentStr[] = +" " +" "; + +const char* stringForIndent(int32_t indentLevel) +{ + ssize_t off = sizeof(indentStr)-1-(indentLevel*2); + return indentStr + (off < 0 ? 0 : off); +} + +// --------------------------------------------------------------------- + +static void defaultPrintFunc(void* cookie, const char* txt) +{ + printf("%s", txt); +} + +// --------------------------------------------------------------------- + +static inline int isident(int c) +{ + return isalnum(c) || c == '_'; +} + +static inline bool isasciitype(char c) +{ + if( c >= ' ' && c < 127 && c != '\'' && c != '\\' ) return true; + return false; +} + +static inline char makehexdigit(uint32_t val) +{ + return "0123456789abcdef"[val&0xF]; +} + +static char* appendhexnum(uint32_t val, char* out) +{ + for( int32_t i=28; i>=0; i-=4 ) { + *out++ = makehexdigit( val>>i ); + } + *out = 0; + return out; +} + +static inline char makeupperhexdigit(uint32_t val) +{ + return "0123456789ABCDEF"[val&0xF]; +} + +static char* appendupperhexnum(uint32_t val, char* out) +{ + for( int32_t i=28; i>=0; i-=4 ) { + *out++ = makeupperhexdigit( val>>i ); + } + *out = 0; + return out; +} + +static char* appendcharornum(char c, char* out, bool skipzero = true) +{ + if (skipzero && c == 0) return out; + + if (isasciitype(c)) { + *out++ = c; + return out; + } + + *out++ = '\\'; + *out++ = 'x'; + *out++ = makehexdigit(c>>4); + *out++ = makehexdigit(c); + return out; +} + +static char* typetostring(uint32_t type, char* out, + bool fullContext = true, + bool strict = false) +{ + char* pos = out; + char c[4]; + c[0] = (char)((type>>24)&0xFF); + c[1] = (char)((type>>16)&0xFF); + c[2] = (char)((type>>8)&0xFF); + c[3] = (char)(type&0xFF); + bool valid; + if( !strict ) { + // now even less strict! + // valid = isasciitype(c[3]); + valid = true; + int32_t i = 0; + bool zero = true; + while (valid && i<3) { + if (c[i] == 0) { + if (!zero) valid = false; + } else { + zero = false; + //if (!isasciitype(c[i])) valid = false; + } + i++; + } + // if all zeros, not a valid type code. + if (zero) valid = false; + } else { + valid = isident(c[3]) ? true : false; + int32_t i = 0; + bool zero = true; + while (valid && i<3) { + if (c[i] == 0) { + if (!zero) valid = false; + } else { + zero = false; + if (!isident(c[i])) valid = false; + } + i++; + } + } + if( valid && (!fullContext || c[0] != '0' || c[1] != 'x') ) { + if( fullContext ) *pos++ = '\''; + pos = appendcharornum(c[0], pos); + pos = appendcharornum(c[1], pos); + pos = appendcharornum(c[2], pos); + pos = appendcharornum(c[3], pos); + if( fullContext ) *pos++ = '\''; + *pos = 0; + return pos; + } + + if( fullContext ) { + *pos++ = '0'; + *pos++ = 'x'; + } + return appendhexnum(type, pos); +} + +void printTypeCode(uint32_t typeCode, debugPrintFunc func, void* cookie) +{ + char buffer[32]; + char* end = typetostring(typeCode, buffer); + *end = 0; + func ? (*func)(cookie, buffer) : defaultPrintFunc(cookie, buffer); +} + +void printHexData(int32_t indent, const void *buf, size_t length, + size_t bytesPerLine, int32_t singleLineBytesCutoff, + size_t alignment, bool cStyle, + debugPrintFunc func, void* cookie) +{ + if (alignment == 0) { + if (bytesPerLine >= 16) alignment = 4; + else if (bytesPerLine >= 8) alignment = 2; + else alignment = 1; + } + if (func == NULL) func = defaultPrintFunc; + + size_t offset; + + unsigned char *pos = (unsigned char *)buf; + + if (pos == NULL) { + if (singleLineBytesCutoff < 0) func(cookie, "\n"); + func(cookie, "(NULL)"); + return; + } + + if (length == 0) { + if (singleLineBytesCutoff < 0) func(cookie, "\n"); + func(cookie, "(empty)"); + return; + } + + if ((int32_t)length < 0) { + if (singleLineBytesCutoff < 0) func(cookie, "\n"); + char buf[64]; + sprintf(buf, "(bad length: %d)", length); + func(cookie, buf); + return; + } + + char buffer[256]; + static const size_t maxBytesPerLine = (sizeof(buffer)-1-11-4)/(3+1); + + if (bytesPerLine > maxBytesPerLine) bytesPerLine = maxBytesPerLine; + + const bool oneLine = (int32_t)length <= singleLineBytesCutoff; + bool newLine = false; + if (cStyle) { + indent++; + func(cookie, "{\n"); + newLine = true; + } else if (!oneLine) { + func(cookie, "\n"); + newLine = true; + } + + for (offset = 0; ; offset += bytesPerLine, pos += bytesPerLine) { + long remain = length; + + char* c = buffer; + if (!oneLine && !cStyle) { + sprintf(c, "0x%08x: ", (int)offset); + c += 12; + } + + size_t index; + size_t word; + + for (word = 0; word < bytesPerLine; ) { + +#ifdef HAVE_LITTLE_ENDIAN + const size_t startIndex = word+(alignment-(alignment?1:0)); + const ssize_t dir = -1; +#else + const size_t startIndex = word; + const ssize_t dir = 1; +#endif + + for (index = 0; index < alignment || (alignment == 0 && index < bytesPerLine); index++) { + + if (!cStyle) { + if (index == 0 && word > 0 && alignment > 0) { + *c++ = ' '; + } + + if (remain-- > 0) { + const unsigned char val = *(pos+startIndex+(index*dir)); + *c++ = makehexdigit(val>>4); + *c++ = makehexdigit(val); + } else if (!oneLine) { + *c++ = ' '; + *c++ = ' '; + } + } else { + if (remain > 0) { + if (index == 0 && word > 0) { + *c++ = ','; + *c++ = ' '; + } + if (index == 0) { + *c++ = '0'; + *c++ = 'x'; + } + const unsigned char val = *(pos+startIndex+(index*dir)); + *c++ = makehexdigit(val>>4); + *c++ = makehexdigit(val); + remain--; + } + } + } + + word += index; + } + + if (!cStyle) { + remain = length; + *c++ = ' '; + *c++ = '\''; + for (index = 0; index < bytesPerLine; index++) { + + if (remain-- > 0) { + const unsigned char val = pos[index]; + *c++ = (val >= ' ' && val < 127) ? val : '.'; + } else if (!oneLine) { + *c++ = ' '; + } + } + + *c++ = '\''; + if (length > bytesPerLine) *c++ = '\n'; + } else { + if (remain > 0) *c++ = ','; + *c++ = '\n'; + } + + if (newLine && indent) func(cookie, stringForIndent(indent)); + *c = 0; + func(cookie, buffer); + newLine = true; + + if (length <= bytesPerLine) break; + length -= bytesPerLine; + } + + if (cStyle) { + if (indent > 0) func(cookie, stringForIndent(indent-1)); + func(cookie, "};"); + } +} + +}; // namespace android + diff --git a/libs/utils/FileMap.cpp b/libs/utils/FileMap.cpp new file mode 100644 index 000000000..e1ba9b238 --- /dev/null +++ b/libs/utils/FileMap.cpp @@ -0,0 +1,222 @@ +/* + * 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 +#include + +#include +#include + +#ifdef HAVE_POSIX_FILEMAP +#include +#endif + +#include +#include +#include +#include + +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 (munmap(mBasePtr, mBaseLength) != 0) { + LOGD("munmap(%p, %d) failed\n", mBasePtr, (int) mBaseLength); + } +#endif +#ifdef HAVE_WIN32_FILEMAP + if ( UnmapViewOfFile(mBasePtr) == 0) { + LOGD("UnmapViewOfFile(%p) failed, error = %ld\n", mBasePtr, + GetLastError() ); + } + 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, off_t offset, size_t length, bool readOnly) +{ +#ifdef HAVE_WIN32_FILEMAP + int adjust; + off_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) { + LOGE("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) { + LOGE("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; + off_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) { + LOGE("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; + } + + LOGE("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); + + LOGV("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) + LOGW("madvise(%d) failed: %s\n", sysAdvice, strerror(errno)); + return cc; +#else + return -1; +#endif // HAVE_MADVISE +} diff --git a/libs/utils/IDataConnection.cpp b/libs/utils/IDataConnection.cpp new file mode 100644 index 000000000..c6d49aa48 --- /dev/null +++ b/libs/utils/IDataConnection.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include + +#include + +namespace android { + +// --------------------------------------------------------------------------- + +enum +{ + CONNECT_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, + DISCONNECT_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION + 1 +}; + +class BpDataConnection : public BpInterface +{ +public: + BpDataConnection::BpDataConnection(const sp& impl) + : BpInterface(impl) + { + } + + virtual void connect() + { + Parcel data, reply; + data.writeInterfaceToken(IDataConnection::descriptor()); + remote()->transact(CONNECT_TRANSACTION, data, &reply); + } + + virtual void disconnect() + { + Parcel data, reply; + remote()->transact(DISCONNECT_TRANSACTION, data, &reply); + } +}; + +IMPLEMENT_META_INTERFACE(DataConnection, "android.utils.IDataConnection"); + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnDataConnection::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) + { + case CONNECT_TRANSACTION: + { + CHECK_INTERFACE(IDataConnection, data, reply); + connect(); + return NO_ERROR; + } + + case DISCONNECT_TRANSACTION: + { + CHECK_INTERFACE(IDataConnection, data, reply); + disconnect(); + return NO_ERROR; + } + + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/utils/IInterface.cpp b/libs/utils/IInterface.cpp new file mode 100644 index 000000000..6ea817887 --- /dev/null +++ b/libs/utils/IInterface.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2005 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +namespace android { + +// --------------------------------------------------------------------------- + +sp IInterface::asBinder() +{ + return this ? onAsBinder() : NULL; +} + +sp IInterface::asBinder() const +{ + return this ? const_cast(this)->onAsBinder() : NULL; +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/utils/IMemory.cpp b/libs/utils/IMemory.cpp new file mode 100644 index 000000000..429bc2b94 --- /dev/null +++ b/libs/utils/IMemory.cpp @@ -0,0 +1,486 @@ +/* + * 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 "IMemory" + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#define VERBOSE 0 + +namespace android { +// --------------------------------------------------------------------------- + +class HeapCache : public IBinder::DeathRecipient +{ +public: + HeapCache(); + virtual ~HeapCache(); + + virtual void binderDied(const wp& who); + + sp find_heap(const sp& binder); + void pin_heap(const sp& binder); + void free_heap(const sp& binder); + sp get_heap(const sp& binder); + void dump_heaps(); + +private: + // For IMemory.cpp + struct heap_info_t { + sp heap; + int32_t count; + }; + + void free_heap(const wp& binder); + + Mutex mHeapCacheLock; + KeyedVector< wp, heap_info_t > mHeapCache; +}; + +static sp gHeapCache = new HeapCache(); + +/******************************************************************************/ + +enum { + HEAP_ID = IBinder::FIRST_CALL_TRANSACTION +}; + +class BpMemoryHeap : public BpInterface +{ +public: + BpMemoryHeap(const sp& impl); + virtual ~BpMemoryHeap(); + + virtual int getHeapID() const; + virtual void* getBase() const; + virtual size_t getSize() const; + virtual uint32_t getFlags() const; + +private: + friend class IMemory; + friend class HeapCache; + + // for debugging in this module + static inline sp find_heap(const sp& binder) { + return gHeapCache->find_heap(binder); + } + static inline void free_heap(const sp& binder) { + gHeapCache->free_heap(binder); + } + static inline sp get_heap(const sp& binder) { + return gHeapCache->get_heap(binder); + } + static inline void dump_heaps() { + gHeapCache->dump_heaps(); + } + void inline pin_heap() const { + gHeapCache->pin_heap(const_cast(this)->asBinder()); + } + + void assertMapped() const; + void assertReallyMapped() const; + void pinHeap() const; + + mutable volatile int32_t mHeapId; + mutable void* mBase; + mutable size_t mSize; + mutable uint32_t mFlags; + mutable bool mRealHeap; + mutable Mutex mLock; +}; + +// ---------------------------------------------------------------------------- + +enum { + GET_MEMORY = IBinder::FIRST_CALL_TRANSACTION +}; + +class BpMemory : public BpInterface +{ +public: + BpMemory(const sp& impl); + virtual ~BpMemory(); + virtual sp getMemory(ssize_t* offset=0, size_t* size=0) const; + +private: + mutable sp mHeap; + mutable ssize_t mOffset; + mutable size_t mSize; +}; + +/******************************************************************************/ + +void* IMemory::fastPointer(const sp& binder, ssize_t offset) const +{ + sp realHeap = BpMemoryHeap::get_heap(binder); + void* const base = realHeap->base(); + if (base == MAP_FAILED) + return 0; + return static_cast(base) + offset; +} + +void* IMemory::pointer() const { + ssize_t offset; + sp heap = getMemory(&offset); + void* const base = heap!=0 ? heap->base() : MAP_FAILED; + if (base == MAP_FAILED) + return 0; + return static_cast(base) + offset; +} + +size_t IMemory::size() const { + size_t size; + getMemory(NULL, &size); + return size; +} + +ssize_t IMemory::offset() const { + ssize_t offset; + getMemory(&offset); + return offset; +} + +/******************************************************************************/ + +BpMemory::BpMemory(const sp& impl) + : BpInterface(impl), mOffset(0), mSize(0) +{ +} + +BpMemory::~BpMemory() +{ +} + +sp BpMemory::getMemory(ssize_t* offset, size_t* size) const +{ + if (mHeap == 0) { + Parcel data, reply; + data.writeInterfaceToken(IMemory::getInterfaceDescriptor()); + if (remote()->transact(GET_MEMORY, data, &reply) == NO_ERROR) { + sp heap = reply.readStrongBinder(); + ssize_t o = reply.readInt32(); + size_t s = reply.readInt32(); + if (heap != 0) { + mHeap = interface_cast(heap); + if (mHeap != 0) { + mOffset = o; + mSize = s; + } + } + } + } + if (offset) *offset = mOffset; + if (size) *size = mSize; + return mHeap; +} + +// --------------------------------------------------------------------------- + +IMPLEMENT_META_INTERFACE(Memory, "android.utils.IMemory"); + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnMemory::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case GET_MEMORY: { + CHECK_INTERFACE(IMemory, data, reply); + ssize_t offset; + size_t size; + reply->writeStrongBinder( getMemory(&offset, &size)->asBinder() ); + reply->writeInt32(offset); + reply->writeInt32(size); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + + +/******************************************************************************/ + +BpMemoryHeap::BpMemoryHeap(const sp& impl) + : BpInterface(impl), + mHeapId(-1), mBase(MAP_FAILED), mSize(0), mFlags(0), mRealHeap(false) +{ +} + +BpMemoryHeap::~BpMemoryHeap() { + if (mHeapId != -1) { + close(mHeapId); + if (mRealHeap) { + // by construction we're the last one + if (mBase != MAP_FAILED) { + sp binder = const_cast(this)->asBinder(); + + if (VERBOSE) { + LOGD("UNMAPPING binder=%p, heap=%p, size=%d, fd=%d", + binder.get(), this, mSize, mHeapId); + CallStack stack; + stack.update(); + stack.dump("callstack"); + } + + munmap(mBase, mSize); + } + } else { + // remove from list only if it was mapped before + sp binder = const_cast(this)->asBinder(); + free_heap(binder); + } + } +} + +void BpMemoryHeap::assertMapped() const +{ + if (mHeapId == -1) { + sp binder(const_cast(this)->asBinder()); + sp heap(static_cast(find_heap(binder).get())); + heap->assertReallyMapped(); + if (heap->mBase != MAP_FAILED) { + Mutex::Autolock _l(mLock); + if (mHeapId == -1) { + mBase = heap->mBase; + mSize = heap->mSize; + android_atomic_write( dup( heap->mHeapId ), &mHeapId ); + } + } else { + // something went wrong + free_heap(binder); + } + } +} + +void BpMemoryHeap::assertReallyMapped() const +{ + if (mHeapId == -1) { + + // remote call without mLock held, worse case scenario, we end up + // calling transact() from multiple threads, but that's not a problem, + // only mmap below must be in the critical section. + + Parcel data, reply; + data.writeInterfaceToken(IMemoryHeap::getInterfaceDescriptor()); + status_t err = remote()->transact(HEAP_ID, data, &reply); + int parcel_fd = reply.readFileDescriptor(); + ssize_t size = reply.readInt32(); + uint32_t flags = reply.readInt32(); + + LOGE_IF(err, "binder=%p transaction failed fd=%d, size=%d, err=%d (%s)", + asBinder().get(), parcel_fd, size, err, strerror(-err)); + + int fd = dup( parcel_fd ); + LOGE_IF(fd==-1, "cannot dup fd=%d, size=%d, err=%d (%s)", + parcel_fd, size, err, strerror(errno)); + + int access = PROT_READ; + if (!(flags & READ_ONLY)) { + access |= PROT_WRITE; + } + + Mutex::Autolock _l(mLock); + if (mHeapId == -1) { + mRealHeap = true; + mBase = mmap(0, size, access, MAP_SHARED, fd, 0); + if (mBase == MAP_FAILED) { + LOGE("cannot map BpMemoryHeap (binder=%p), size=%d, fd=%d (%s)", + asBinder().get(), size, fd, strerror(errno)); + close(fd); + } else { + if (flags & MAP_ONCE) { + //LOGD("pinning heap (binder=%p, size=%d, fd=%d", + // asBinder().get(), size, fd); + pin_heap(); + } + mSize = size; + mFlags = flags; + android_atomic_write(fd, &mHeapId); + } + } + } +} + +int BpMemoryHeap::getHeapID() const { + assertMapped(); + return mHeapId; +} + +void* BpMemoryHeap::getBase() const { + assertMapped(); + return mBase; +} + +size_t BpMemoryHeap::getSize() const { + assertMapped(); + return mSize; +} + +uint32_t BpMemoryHeap::getFlags() const { + assertMapped(); + return mFlags; +} + +// --------------------------------------------------------------------------- + +IMPLEMENT_META_INTERFACE(MemoryHeap, "android.utils.IMemoryHeap"); + +status_t BnMemoryHeap::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case HEAP_ID: { + CHECK_INTERFACE(IMemoryHeap, data, reply); + reply->writeFileDescriptor(getHeapID()); + reply->writeInt32(getSize()); + reply->writeInt32(getFlags()); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +/*****************************************************************************/ + +HeapCache::HeapCache() + : DeathRecipient() +{ +} + +HeapCache::~HeapCache() +{ +} + +void HeapCache::binderDied(const wp& binder) +{ + //LOGD("binderDied binder=%p", binder.unsafe_get()); + free_heap(binder); +} + +sp HeapCache::find_heap(const sp& binder) +{ + Mutex::Autolock _l(mHeapCacheLock); + ssize_t i = mHeapCache.indexOfKey(binder); + if (i>=0) { + heap_info_t& info = mHeapCache.editValueAt(i); + LOGD_IF(VERBOSE, + "found binder=%p, heap=%p, size=%d, fd=%d, count=%d", + binder.get(), info.heap.get(), + static_cast(info.heap.get())->mSize, + static_cast(info.heap.get())->mHeapId, + info.count); + android_atomic_inc(&info.count); + return info.heap; + } else { + heap_info_t info; + info.heap = interface_cast(binder); + info.count = 1; + //LOGD("adding binder=%p, heap=%p, count=%d", + // binder.get(), info.heap.get(), info.count); + mHeapCache.add(binder, info); + return info.heap; + } +} + +void HeapCache::pin_heap(const sp& binder) +{ + Mutex::Autolock _l(mHeapCacheLock); + ssize_t i = mHeapCache.indexOfKey(binder); + if (i>=0) { + heap_info_t& info(mHeapCache.editValueAt(i)); + android_atomic_inc(&info.count); + binder->linkToDeath(this); + } else { + LOGE("pin_heap binder=%p not found!!!", binder.get()); + } +} + +void HeapCache::free_heap(const sp& binder) { + free_heap( wp(binder) ); +} + +void HeapCache::free_heap(const wp& binder) +{ + sp rel; + { + Mutex::Autolock _l(mHeapCacheLock); + ssize_t i = mHeapCache.indexOfKey(binder); + if (i>=0) { + heap_info_t& info(mHeapCache.editValueAt(i)); + int32_t c = android_atomic_dec(&info.count); + if (c == 1) { + LOGD_IF(VERBOSE, + "removing binder=%p, heap=%p, size=%d, fd=%d, count=%d", + binder.unsafe_get(), info.heap.get(), + static_cast(info.heap.get())->mSize, + static_cast(info.heap.get())->mHeapId, + info.count); + rel = mHeapCache.valueAt(i).heap; + mHeapCache.removeItemsAt(i); + } + } else { + LOGE("free_heap binder=%p not found!!!", binder.unsafe_get()); + } + } +} + +sp HeapCache::get_heap(const sp& binder) +{ + sp realHeap; + Mutex::Autolock _l(mHeapCacheLock); + ssize_t i = mHeapCache.indexOfKey(binder); + if (i>=0) realHeap = mHeapCache.valueAt(i).heap; + else realHeap = interface_cast(binder); + return realHeap; +} + +void HeapCache::dump_heaps() +{ + Mutex::Autolock _l(mHeapCacheLock); + int c = mHeapCache.size(); + for (int i=0 ; i(info.heap.get())); + LOGD("hey=%p, heap=%p, count=%d, (fd=%d, base=%p, size=%d)", + mHeapCache.keyAt(i).unsafe_get(), + info.heap.get(), info.count, + h->mHeapId, h->mBase, h->mSize); + } +} + + +// --------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/utils/IPCThreadState.cpp b/libs/utils/IPCThreadState.cpp new file mode 100644 index 000000000..04ae1424e --- /dev/null +++ b/libs/utils/IPCThreadState.cpp @@ -0,0 +1,1030 @@ +/* + * 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 + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#ifdef HAVE_PTHREADS +#include +#include +#include +#endif +#ifdef HAVE_WIN32_THREADS +#include +#endif + + +#if LOG_NDEBUG + +#define IF_LOG_TRANSACTIONS() if (false) +#define IF_LOG_COMMANDS() if (false) +#define LOG_REMOTEREFS(...) +#define IF_LOG_REMOTEREFS() if (false) +#define LOG_THREADPOOL(...) +#define LOG_ONEWAY(...) + +#else + +#define IF_LOG_TRANSACTIONS() IF_LOG(LOG_VERBOSE, "transact") +#define IF_LOG_COMMANDS() IF_LOG(LOG_VERBOSE, "ipc") +#define LOG_REMOTEREFS(...) LOG(LOG_DEBUG, "remoterefs", __VA_ARGS__) +#define IF_LOG_REMOTEREFS() IF_LOG(LOG_DEBUG, "remoterefs") +#define LOG_THREADPOOL(...) LOG(LOG_DEBUG, "threadpool", __VA_ARGS__) +#define LOG_ONEWAY(...) LOG(LOG_DEBUG, "ipc", __VA_ARGS__) + +#endif + +// --------------------------------------------------------------------------- + +namespace android { + +static const char* getReturnString(size_t idx); +static const char* getCommandString(size_t idx); +static const void* printReturnCommand(TextOutput& out, const void* _cmd); +static const void* printCommand(TextOutput& out, const void* _cmd); + +// This will result in a missing symbol failure if the IF_LOG_COMMANDS() +// conditionals don't get stripped... but that is probably what we want. +#if !LOG_NDEBUG +static const char *kReturnStrings[] = { +#if 1 /* TODO: error update strings */ + "unknown", +#else + "BR_OK", + "BR_TIMEOUT", + "BR_WAKEUP", + "BR_TRANSACTION", + "BR_REPLY", + "BR_ACQUIRE_RESULT", + "BR_DEAD_REPLY", + "BR_TRANSACTION_COMPLETE", + "BR_INCREFS", + "BR_ACQUIRE", + "BR_RELEASE", + "BR_DECREFS", + "BR_ATTEMPT_ACQUIRE", + "BR_EVENT_OCCURRED", + "BR_NOOP", + "BR_SPAWN_LOOPER", + "BR_FINISHED", + "BR_DEAD_BINDER", + "BR_CLEAR_DEATH_NOTIFICATION_DONE" +#endif +}; + +static const char *kCommandStrings[] = { +#if 1 /* TODO: error update strings */ + "unknown", +#else + "BC_NOOP", + "BC_TRANSACTION", + "BC_REPLY", + "BC_ACQUIRE_RESULT", + "BC_FREE_BUFFER", + "BC_TRANSACTION_COMPLETE", + "BC_INCREFS", + "BC_ACQUIRE", + "BC_RELEASE", + "BC_DECREFS", + "BC_INCREFS_DONE", + "BC_ACQUIRE_DONE", + "BC_ATTEMPT_ACQUIRE", + "BC_RETRIEVE_ROOT_OBJECT", + "BC_SET_THREAD_ENTRY", + "BC_REGISTER_LOOPER", + "BC_ENTER_LOOPER", + "BC_EXIT_LOOPER", + "BC_SYNC", + "BC_STOP_PROCESS", + "BC_STOP_SELF", + "BC_REQUEST_DEATH_NOTIFICATION", + "BC_CLEAR_DEATH_NOTIFICATION", + "BC_DEAD_BINDER_DONE" +#endif +}; + +static const char* getReturnString(size_t idx) +{ + if (idx < sizeof(kReturnStrings) / sizeof(kReturnStrings[0])) + return kReturnStrings[idx]; + else + return "unknown"; +} + +static const char* getCommandString(size_t idx) +{ + if (idx < sizeof(kCommandStrings) / sizeof(kCommandStrings[0])) + return kCommandStrings[idx]; + else + return "unknown"; +} + +static const void* printBinderTransactionData(TextOutput& out, const void* data) +{ + const binder_transaction_data* btd = + (const binder_transaction_data*)data; + out << "target=" << btd->target.ptr << " (cookie " << btd->cookie << ")" << endl + << "code=" << TypeCode(btd->code) << ", flags=" << (void*)btd->flags << endl + << "data=" << btd->data.ptr.buffer << " (" << (void*)btd->data_size + << " bytes)" << endl + << "offsets=" << btd->data.ptr.offsets << " (" << (void*)btd->offsets_size + << " bytes)" << endl; + return btd+1; +} + +static const void* printReturnCommand(TextOutput& out, const void* _cmd) +{ + static const int32_t N = sizeof(kReturnStrings)/sizeof(kReturnStrings[0]); + + const int32_t* cmd = (const int32_t*)_cmd; + int32_t code = *cmd++; + if (code == BR_ERROR) { + out << "BR_ERROR: " << (void*)(*cmd++) << endl; + return cmd; + } else if (code < 0 || code >= N) { + out << "Unknown reply: " << code << endl; + return cmd; + } + + out << kReturnStrings[code]; + switch (code) { + case BR_TRANSACTION: + case BR_REPLY: { + out << ": " << indent; + cmd = (const int32_t *)printBinderTransactionData(out, cmd); + out << dedent; + } break; + + case BR_ACQUIRE_RESULT: { + const int32_t res = *cmd++; + out << ": " << res << (res ? " (SUCCESS)" : " (FAILURE)"); + } break; + + case BR_INCREFS: + case BR_ACQUIRE: + case BR_RELEASE: + case BR_DECREFS: { + const int32_t b = *cmd++; + const int32_t c = *cmd++; + out << ": target=" << (void*)b << " (cookie " << (void*)c << ")"; + } break; + + case BR_ATTEMPT_ACQUIRE: { + const int32_t p = *cmd++; + const int32_t b = *cmd++; + const int32_t c = *cmd++; + out << ": target=" << (void*)b << " (cookie " << (void*)c + << "), pri=" << p; + } break; + + case BR_DEAD_BINDER: + case BR_CLEAR_DEATH_NOTIFICATION_DONE: { + const int32_t c = *cmd++; + out << ": death cookie " << (void*)c; + } break; + } + + out << endl; + return cmd; +} + +static const void* printCommand(TextOutput& out, const void* _cmd) +{ + static const int32_t N = sizeof(kCommandStrings)/sizeof(kCommandStrings[0]); + + const int32_t* cmd = (const int32_t*)_cmd; + int32_t code = *cmd++; + if (code < 0 || code >= N) { + out << "Unknown command: " << code << endl; + return cmd; + } + + out << kCommandStrings[code]; + switch (code) { + case BC_TRANSACTION: + case BC_REPLY: { + out << ": " << indent; + cmd = (const int32_t *)printBinderTransactionData(out, cmd); + out << dedent; + } break; + + case BC_ACQUIRE_RESULT: { + const int32_t res = *cmd++; + out << ": " << res << (res ? " (SUCCESS)" : " (FAILURE)"); + } break; + + case BC_FREE_BUFFER: { + const int32_t buf = *cmd++; + out << ": buffer=" << (void*)buf; + } break; + + case BC_INCREFS: + case BC_ACQUIRE: + case BC_RELEASE: + case BC_DECREFS: { + const int32_t d = *cmd++; + out << ": descriptor=" << (void*)d; + } break; + + case BC_INCREFS_DONE: + case BC_ACQUIRE_DONE: { + const int32_t b = *cmd++; + const int32_t c = *cmd++; + out << ": target=" << (void*)b << " (cookie " << (void*)c << ")"; + } break; + + case BC_ATTEMPT_ACQUIRE: { + const int32_t p = *cmd++; + const int32_t d = *cmd++; + out << ": decriptor=" << (void*)d << ", pri=" << p; + } break; + + case BC_REQUEST_DEATH_NOTIFICATION: + case BC_CLEAR_DEATH_NOTIFICATION: { + const int32_t h = *cmd++; + const int32_t c = *cmd++; + out << ": handle=" << h << " (death cookie " << (void*)c << ")"; + } break; + + case BC_DEAD_BINDER_DONE: { + const int32_t c = *cmd++; + out << ": death cookie " << (void*)c; + } break; + } + + out << endl; + return cmd; +} +#endif + +static pthread_mutex_t gTLSMutex = PTHREAD_MUTEX_INITIALIZER; +static bool gHaveTLS = false; +static pthread_key_t gTLS = 0; +static bool gShutdown = false; + +IPCThreadState* IPCThreadState::self() +{ + if (gHaveTLS) { +restart: + const pthread_key_t k = gTLS; + IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k); + if (st) return st; + return new IPCThreadState; + } + + if (gShutdown) return NULL; + + pthread_mutex_lock(&gTLSMutex); + if (!gHaveTLS) { + if (pthread_key_create(&gTLS, threadDestructor) != 0) { + pthread_mutex_unlock(&gTLSMutex); + return NULL; + } + gHaveTLS = true; + } + pthread_mutex_unlock(&gTLSMutex); + goto restart; +} + +void IPCThreadState::shutdown() +{ + gShutdown = true; + + if (gHaveTLS) { + // XXX Need to wait for all thread pool threads to exit! + IPCThreadState* st = (IPCThreadState*)pthread_getspecific(gTLS); + if (st) { + delete st; + pthread_setspecific(gTLS, NULL); + } + gHaveTLS = false; + } +} + +sp IPCThreadState::process() +{ + return mProcess; +} + +status_t IPCThreadState::clearLastError() +{ + const status_t err = mLastError; + mLastError = NO_ERROR; + return err; +} + +int IPCThreadState::getCallingPid() +{ + return mCallingPid; +} + +int IPCThreadState::getCallingUid() +{ + return mCallingUid; +} + +int64_t IPCThreadState::clearCallingIdentity() +{ + int64_t token = ((int64_t)mCallingUid<<32) | mCallingPid; + clearCaller(); + return token; +} + +void IPCThreadState::restoreCallingIdentity(int64_t token) +{ + mCallingUid = (int)(token>>32); + mCallingPid = (int)token; +} + +void IPCThreadState::clearCaller() +{ + if (mProcess->supportsProcesses()) { + mCallingPid = getpid(); + mCallingUid = getuid(); + } else { + mCallingPid = -1; + mCallingUid = -1; + } +} + +void IPCThreadState::flushCommands() +{ + if (mProcess->mDriverFD <= 0) + return; + talkWithDriver(false); +} + +void IPCThreadState::joinThreadPool(bool isMain) +{ + LOG_THREADPOOL("**** THREAD %p (PID %d) IS JOINING THE THREAD POOL\n", (void*)pthread_self(), getpid()); + + mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER); + + status_t result; + do { + int32_t cmd; + + // When we've cleared the incoming command queue, process any pending derefs + if (mIn.dataPosition() >= mIn.dataSize()) { + size_t numPending = mPendingWeakDerefs.size(); + if (numPending > 0) { + for (size_t i = 0; i < numPending; i++) { + RefBase::weakref_type* refs = mPendingWeakDerefs[i]; + refs->decWeak(mProcess.get()); + } + mPendingWeakDerefs.clear(); + } + + numPending = mPendingStrongDerefs.size(); + if (numPending > 0) { + for (size_t i = 0; i < numPending; i++) { + BBinder* obj = mPendingStrongDerefs[i]; + obj->decStrong(mProcess.get()); + } + mPendingStrongDerefs.clear(); + } + } + + // now get the next command to be processed, waiting if necessary + result = talkWithDriver(); + if (result >= NO_ERROR) { + size_t IN = mIn.dataAvail(); + if (IN < sizeof(int32_t)) continue; + cmd = mIn.readInt32(); + IF_LOG_COMMANDS() { + alog << "Processing top-level Command: " + << getReturnString(cmd) << endl; + } + result = executeCommand(cmd); + } + + // Let this thread exit the thread pool if it is no longer + // needed and it is not the main process thread. + if(result == TIMED_OUT && !isMain) { + break; + } + } while (result != -ECONNREFUSED && result != -EBADF); + + LOG_THREADPOOL("**** THREAD %p (PID %d) IS LEAVING THE THREAD POOL err=%p\n", + (void*)pthread_self(), getpid(), (void*)result); + + mOut.writeInt32(BC_EXIT_LOOPER); + talkWithDriver(false); +} + +void IPCThreadState::stopProcess(bool immediate) +{ + //LOGI("**** STOPPING PROCESS"); + flushCommands(); + int fd = mProcess->mDriverFD; + mProcess->mDriverFD = -1; + close(fd); + //kill(getpid(), SIGKILL); +} + +status_t IPCThreadState::transact(int32_t handle, + uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags) +{ + status_t err = data.errorCheck(); + + flags |= TF_ACCEPT_FDS; + + IF_LOG_TRANSACTIONS() { + TextOutput::Bundle _b(alog); + alog << "BC_TRANSACTION thr " << (void*)pthread_self() << " / hand " + << handle << " / code " << TypeCode(code) << ": " + << indent << data << dedent << endl; + } + + if (err == NO_ERROR) { + LOG_ONEWAY(">>>> SEND from pid %d uid %d %s", getpid(), getuid(), + (flags & TF_ONE_WAY) == 0 ? "READ REPLY" : "ONE WAY"); + err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL); + } + + if (err != NO_ERROR) { + if (reply) reply->setError(err); + return (mLastError = err); + } + + if ((flags & TF_ONE_WAY) == 0) { + if (reply) { + err = waitForResponse(reply); + } else { + Parcel fakeReply; + err = waitForResponse(&fakeReply); + } + + IF_LOG_TRANSACTIONS() { + TextOutput::Bundle _b(alog); + alog << "BR_REPLY thr " << (void*)pthread_self() << " / hand " + << handle << ": "; + if (reply) alog << indent << *reply << dedent << endl; + else alog << "(none requested)" << endl; + } + } else { + err = waitForResponse(NULL, NULL); + } + + return err; +} + +void IPCThreadState::incStrongHandle(int32_t handle) +{ + LOG_REMOTEREFS("IPCThreadState::incStrongHandle(%d)\n", handle); + mOut.writeInt32(BC_ACQUIRE); + mOut.writeInt32(handle); +} + +void IPCThreadState::decStrongHandle(int32_t handle) +{ + LOG_REMOTEREFS("IPCThreadState::decStrongHandle(%d)\n", handle); + mOut.writeInt32(BC_RELEASE); + mOut.writeInt32(handle); +} + +void IPCThreadState::incWeakHandle(int32_t handle) +{ + LOG_REMOTEREFS("IPCThreadState::incWeakHandle(%d)\n", handle); + mOut.writeInt32(BC_INCREFS); + mOut.writeInt32(handle); +} + +void IPCThreadState::decWeakHandle(int32_t handle) +{ + LOG_REMOTEREFS("IPCThreadState::decWeakHandle(%d)\n", handle); + mOut.writeInt32(BC_DECREFS); + mOut.writeInt32(handle); +} + +status_t IPCThreadState::attemptIncStrongHandle(int32_t handle) +{ + mOut.writeInt32(BC_ATTEMPT_ACQUIRE); + mOut.writeInt32(0); // xxx was thread priority + mOut.writeInt32(handle); + status_t result = UNKNOWN_ERROR; + + waitForResponse(NULL, &result); + +#if LOG_REFCOUNTS + printf("IPCThreadState::attemptIncStrongHandle(%ld) = %s\n", + handle, result == NO_ERROR ? "SUCCESS" : "FAILURE"); +#endif + + return result; +} + +void IPCThreadState::expungeHandle(int32_t handle, IBinder* binder) +{ +#if LOG_REFCOUNTS + printf("IPCThreadState::expungeHandle(%ld)\n", handle); +#endif + self()->mProcess->expungeHandle(handle, binder); +} + +status_t IPCThreadState::requestDeathNotification(int32_t handle, BpBinder* proxy) +{ + mOut.writeInt32(BC_REQUEST_DEATH_NOTIFICATION); + mOut.writeInt32((int32_t)handle); + mOut.writeInt32((int32_t)proxy); + return NO_ERROR; +} + +status_t IPCThreadState::clearDeathNotification(int32_t handle, BpBinder* proxy) +{ + mOut.writeInt32(BC_CLEAR_DEATH_NOTIFICATION); + mOut.writeInt32((int32_t)handle); + mOut.writeInt32((int32_t)proxy); + return NO_ERROR; +} + +IPCThreadState::IPCThreadState() + : mProcess(ProcessState::self()) +{ + pthread_setspecific(gTLS, this); + clearCaller(); + mIn.setDataCapacity(256); + mOut.setDataCapacity(256); +} + +IPCThreadState::~IPCThreadState() +{ +} + +status_t IPCThreadState::sendReply(const Parcel& reply, uint32_t flags) +{ + status_t err; + status_t statusBuffer; + err = writeTransactionData(BC_REPLY, flags, -1, 0, reply, &statusBuffer); + if (err < NO_ERROR) return err; + + return waitForResponse(NULL, NULL); +} + +status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult) +{ + int32_t cmd; + int32_t err; + + while (1) { + if ((err=talkWithDriver()) < NO_ERROR) break; + err = mIn.errorCheck(); + if (err < NO_ERROR) break; + if (mIn.dataAvail() == 0) continue; + + cmd = mIn.readInt32(); + + IF_LOG_COMMANDS() { + alog << "Processing waitForResponse Command: " + << getReturnString(cmd) << endl; + } + + switch (cmd) { + case BR_TRANSACTION_COMPLETE: + if (!reply && !acquireResult) goto finish; + break; + + case BR_DEAD_REPLY: + err = DEAD_OBJECT; + goto finish; + + case BR_FAILED_REPLY: + err = FAILED_TRANSACTION; + goto finish; + + case BR_ACQUIRE_RESULT: + { + LOG_ASSERT(acquireResult != NULL, "Unexpected brACQUIRE_RESULT"); + const int32_t result = mIn.readInt32(); + if (!acquireResult) continue; + *acquireResult = result ? NO_ERROR : INVALID_OPERATION; + } + goto finish; + + case BR_REPLY: + { + binder_transaction_data tr; + err = mIn.read(&tr, sizeof(tr)); + LOG_ASSERT(err == NO_ERROR, "Not enough command data for brREPLY"); + if (err != NO_ERROR) goto finish; + + if (reply) { + if ((tr.flags & TF_STATUS_CODE) == 0) { + reply->ipcSetDataReference( + reinterpret_cast(tr.data.ptr.buffer), + tr.data_size, + reinterpret_cast(tr.data.ptr.offsets), + tr.offsets_size/sizeof(size_t), + freeBuffer, this); + } else { + err = *static_cast(tr.data.ptr.buffer); + freeBuffer(NULL, + reinterpret_cast(tr.data.ptr.buffer), + tr.data_size, + reinterpret_cast(tr.data.ptr.offsets), + tr.offsets_size/sizeof(size_t), this); + } + } else { + freeBuffer(NULL, + reinterpret_cast(tr.data.ptr.buffer), + tr.data_size, + reinterpret_cast(tr.data.ptr.offsets), + tr.offsets_size/sizeof(size_t), this); + continue; + } + } + goto finish; + + default: + err = executeCommand(cmd); + if (err != NO_ERROR) goto finish; + break; + } + } + +finish: + if (err != NO_ERROR) { + if (acquireResult) *acquireResult = err; + if (reply) reply->setError(err); + mLastError = err; + } + + return err; +} + +status_t IPCThreadState::talkWithDriver(bool doReceive) +{ + LOG_ASSERT(mProcess->mDriverFD >= 0, "Binder driver is not opened"); + + binder_write_read bwr; + + // Is the read buffer empty? + const bool needRead = mIn.dataPosition() >= mIn.dataSize(); + + // We don't want to write anything if we are still reading + // from data left in the input buffer and the caller + // has requested to read the next data. + const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0; + + bwr.write_size = outAvail; + bwr.write_buffer = (long unsigned int)mOut.data(); + + // This is what we'll read. + if (doReceive && needRead) { + bwr.read_size = mIn.dataCapacity(); + bwr.read_buffer = (long unsigned int)mIn.data(); + } else { + bwr.read_size = 0; + } + + IF_LOG_COMMANDS() { + TextOutput::Bundle _b(alog); + if (outAvail != 0) { + alog << "Sending commands to driver: " << indent; + const void* cmds = (const void*)bwr.write_buffer; + const void* end = ((const uint8_t*)cmds)+bwr.write_size; + alog << HexDump(cmds, bwr.write_size) << endl; + while (cmds < end) cmds = printCommand(alog, cmds); + alog << dedent; + } + alog << "Size of receive buffer: " << bwr.read_size + << ", needRead: " << needRead << ", doReceive: " << doReceive << endl; + } + + // Return immediately if there is nothing to do. + if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR; + + bwr.write_consumed = 0; + bwr.read_consumed = 0; + status_t err; + do { + IF_LOG_COMMANDS() { + alog << "About to read/write, write size = " << mOut.dataSize() << endl; + } +#if defined(HAVE_ANDROID_OS) + if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0) + err = NO_ERROR; + else + err = -errno; +#else + err = INVALID_OPERATION; +#endif + IF_LOG_COMMANDS() { + alog << "Finished read/write, write size = " << mOut.dataSize() << endl; + } + } while (err == -EINTR); + + IF_LOG_COMMANDS() { + alog << "Our err: " << (void*)err << ", write consumed: " + << bwr.write_consumed << " (of " << mOut.dataSize() + << "), read consumed: " << bwr.read_consumed << endl; + } + + if (err >= NO_ERROR) { + if (bwr.write_consumed > 0) { + if (bwr.write_consumed < (ssize_t)mOut.dataSize()) + mOut.remove(0, bwr.write_consumed); + else + mOut.setDataSize(0); + } + if (bwr.read_consumed > 0) { + mIn.setDataSize(bwr.read_consumed); + mIn.setDataPosition(0); + } + IF_LOG_COMMANDS() { + TextOutput::Bundle _b(alog); + alog << "Remaining data size: " << mOut.dataSize() << endl; + alog << "Received commands from driver: " << indent; + const void* cmds = mIn.data(); + const void* end = mIn.data() + mIn.dataSize(); + alog << HexDump(cmds, mIn.dataSize()) << endl; + while (cmds < end) cmds = printReturnCommand(alog, cmds); + alog << dedent; + } + return NO_ERROR; + } + + return err; +} + +status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags, + int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer) +{ + binder_transaction_data tr; + + tr.target.handle = handle; + tr.code = code; + tr.flags = binderFlags; + + const status_t err = data.errorCheck(); + if (err == NO_ERROR) { + tr.data_size = data.ipcDataSize(); + tr.data.ptr.buffer = data.ipcData(); + tr.offsets_size = data.ipcObjectsCount()*sizeof(size_t); + tr.data.ptr.offsets = data.ipcObjects(); + } else if (statusBuffer) { + tr.flags |= TF_STATUS_CODE; + *statusBuffer = err; + tr.data_size = sizeof(status_t); + tr.data.ptr.buffer = statusBuffer; + tr.offsets_size = 0; + tr.data.ptr.offsets = NULL; + } else { + return (mLastError = err); + } + + mOut.writeInt32(cmd); + mOut.write(&tr, sizeof(tr)); + + return NO_ERROR; +} + +sp the_context_object; + +void setTheContextObject(sp obj) +{ + the_context_object = obj; +} + +status_t IPCThreadState::executeCommand(int32_t cmd) +{ + BBinder* obj; + RefBase::weakref_type* refs; + status_t result = NO_ERROR; + + switch (cmd) { + case BR_ERROR: + result = mIn.readInt32(); + break; + + case BR_OK: + break; + + case BR_ACQUIRE: + refs = (RefBase::weakref_type*)mIn.readInt32(); + obj = (BBinder*)mIn.readInt32(); + LOG_ASSERT(refs->refBase() == obj, + "BR_ACQUIRE: object %p does not match cookie %p (expected %p)", + refs, obj, refs->refBase()); + obj->incStrong(mProcess.get()); + IF_LOG_REMOTEREFS() { + LOG_REMOTEREFS("BR_ACQUIRE from driver on %p", obj); + obj->printRefs(); + } + mOut.writeInt32(BC_ACQUIRE_DONE); + mOut.writeInt32((int32_t)refs); + mOut.writeInt32((int32_t)obj); + break; + + case BR_RELEASE: + refs = (RefBase::weakref_type*)mIn.readInt32(); + obj = (BBinder*)mIn.readInt32(); + LOG_ASSERT(refs->refBase() == obj, + "BR_RELEASE: object %p does not match cookie %p (expected %p)", + refs, obj, refs->refBase()); + IF_LOG_REMOTEREFS() { + LOG_REMOTEREFS("BR_RELEASE from driver on %p", obj); + obj->printRefs(); + } + mPendingStrongDerefs.push(obj); + break; + + case BR_INCREFS: + refs = (RefBase::weakref_type*)mIn.readInt32(); + obj = (BBinder*)mIn.readInt32(); + refs->incWeak(mProcess.get()); + mOut.writeInt32(BC_INCREFS_DONE); + mOut.writeInt32((int32_t)refs); + mOut.writeInt32((int32_t)obj); + break; + + case BR_DECREFS: + refs = (RefBase::weakref_type*)mIn.readInt32(); + obj = (BBinder*)mIn.readInt32(); + // NOTE: This assertion is not valid, because the object may no + // longer exist (thus the (BBinder*)cast above resulting in a different + // memory address). + //LOG_ASSERT(refs->refBase() == obj, + // "BR_DECREFS: object %p does not match cookie %p (expected %p)", + // refs, obj, refs->refBase()); + mPendingWeakDerefs.push(refs); + break; + + case BR_ATTEMPT_ACQUIRE: + refs = (RefBase::weakref_type*)mIn.readInt32(); + obj = (BBinder*)mIn.readInt32(); + + { + const bool success = refs->attemptIncStrong(mProcess.get()); + LOG_ASSERT(success && refs->refBase() == obj, + "BR_ATTEMPT_ACQUIRE: object %p does not match cookie %p (expected %p)", + refs, obj, refs->refBase()); + + mOut.writeInt32(BC_ACQUIRE_RESULT); + mOut.writeInt32((int32_t)success); + } + break; + + case BR_TRANSACTION: + { + binder_transaction_data tr; + result = mIn.read(&tr, sizeof(tr)); + LOG_ASSERT(result == NO_ERROR, + "Not enough command data for brTRANSACTION"); + if (result != NO_ERROR) break; + + Parcel buffer; + buffer.ipcSetDataReference( + reinterpret_cast(tr.data.ptr.buffer), + tr.data_size, + reinterpret_cast(tr.data.ptr.offsets), + tr.offsets_size/sizeof(size_t), freeBuffer, this); + + const pid_t origPid = mCallingPid; + const uid_t origUid = mCallingUid; + + mCallingPid = tr.sender_pid; + mCallingUid = tr.sender_euid; + + //LOGI(">>>> TRANSACT from pid %d uid %d\n", mCallingPid, mCallingUid); + + Parcel reply; + IF_LOG_TRANSACTIONS() { + TextOutput::Bundle _b(alog); + alog << "BR_TRANSACTION thr " << (void*)pthread_self() + << " / obj " << tr.target.ptr << " / code " + << TypeCode(tr.code) << ": " << indent << buffer + << dedent << endl + << "Data addr = " + << reinterpret_cast(tr.data.ptr.buffer) + << ", offsets addr=" + << reinterpret_cast(tr.data.ptr.offsets) << endl; + } + if (tr.target.ptr) { + sp b((BBinder*)tr.cookie); + const status_t error = b->transact(tr.code, buffer, &reply, 0); + if (error < NO_ERROR) reply.setError(error); + + } else { + const status_t error = the_context_object->transact(tr.code, buffer, &reply, 0); + if (error < NO_ERROR) reply.setError(error); + } + + //LOGI("<<<< TRANSACT from pid %d restore pid %d uid %d\n", + // mCallingPid, origPid, origUid); + + if ((tr.flags & TF_ONE_WAY) == 0) { + LOG_ONEWAY("Sending reply to %d!", mCallingPid); + sendReply(reply, 0); + } else { + LOG_ONEWAY("NOT sending reply to %d!", mCallingPid); + } + + mCallingPid = origPid; + mCallingUid = origUid; + + IF_LOG_TRANSACTIONS() { + TextOutput::Bundle _b(alog); + alog << "BC_REPLY thr " << (void*)pthread_self() << " / obj " + << tr.target.ptr << ": " << indent << reply << dedent << endl; + } + + } + break; + + case BR_DEAD_BINDER: + { + BpBinder *proxy = (BpBinder*)mIn.readInt32(); + proxy->sendObituary(); + mOut.writeInt32(BC_DEAD_BINDER_DONE); + mOut.writeInt32((int32_t)proxy); + } break; + + case BR_CLEAR_DEATH_NOTIFICATION_DONE: + { + BpBinder *proxy = (BpBinder*)mIn.readInt32(); + proxy->getWeakRefs()->decWeak(proxy); + } break; + + case BR_FINISHED: + result = TIMED_OUT; + break; + + case BR_NOOP: + break; + + case BR_SPAWN_LOOPER: + mProcess->spawnPooledThread(false); + break; + + default: + printf("*** BAD COMMAND %d received from Binder driver\n", cmd); + result = UNKNOWN_ERROR; + break; + } + + if (result != NO_ERROR) { + mLastError = result; + } + + return result; +} + +void IPCThreadState::threadDestructor(void *st) +{ + IPCThreadState* const self = static_cast(st); + if (self) { + self->flushCommands(); +#if defined(HAVE_ANDROID_OS) + ioctl(self->mProcess->mDriverFD, BINDER_THREAD_EXIT, 0); +#endif + delete self; + } +} + + +void IPCThreadState::freeBuffer(Parcel* parcel, const uint8_t* data, size_t dataSize, + const size_t* objects, size_t objectsSize, + void* cookie) +{ + //LOGI("Freeing parcel %p", &parcel); + IF_LOG_COMMANDS() { + alog << "Writing BC_FREE_BUFFER for " << data << endl; + } + LOG_ASSERT(data != NULL, "Called with NULL data"); + if (parcel != NULL) parcel->closeFileDescriptors(); + IPCThreadState* state = self(); + state->mOut.writeInt32(BC_FREE_BUFFER); + state->mOut.writeInt32((int32_t)data); +} + +}; // namespace android diff --git a/libs/utils/IPermissionController.cpp b/libs/utils/IPermissionController.cpp new file mode 100644 index 000000000..f01d38fd1 --- /dev/null +++ b/libs/utils/IPermissionController.cpp @@ -0,0 +1,86 @@ +/* + * 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 "PermissionController" + +#include + +#include +#include +#include +#include + +#include + +namespace android { + +// ---------------------------------------------------------------------- + +class BpPermissionController : public BpInterface +{ +public: + BpPermissionController(const sp& impl) + : BpInterface(impl) + { + } + + virtual bool checkPermission(const String16& permission, int32_t pid, int32_t uid) + { + Parcel data, reply; + data.writeInterfaceToken(IPermissionController::getInterfaceDescriptor()); + data.writeString16(permission); + data.writeInt32(pid); + data.writeInt32(uid); + remote()->transact(CHECK_PERMISSION_TRANSACTION, data, &reply); + // fail on exception + if (reply.readInt32() != 0) return 0; + return reply.readInt32() != 0; + } +}; + +IMPLEMENT_META_INTERFACE(PermissionController, "android.os.IPermissionController"); + +// ---------------------------------------------------------------------- + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnPermissionController::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + //printf("PermissionController received: "); data.print(); + switch(code) { + case CHECK_PERMISSION_TRANSACTION: { + CHECK_INTERFACE(IPermissionController, data, reply); + String16 permission = data.readString16(); + int32_t pid = data.readInt32(); + int32_t uid = data.readInt32(); + bool res = checkPermission(permission, pid, uid); + // write exception + reply->writeInt32(0); + reply->writeInt32(res ? 1 : 0); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +}; // namespace android + diff --git a/libs/utils/IServiceManager.cpp b/libs/utils/IServiceManager.cpp new file mode 100644 index 000000000..9beeaddde --- /dev/null +++ b/libs/utils/IServiceManager.cpp @@ -0,0 +1,230 @@ +/* + * 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 "ServiceManager" + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include + +namespace android { + +sp defaultServiceManager() +{ + if (gDefaultServiceManager != NULL) return gDefaultServiceManager; + + { + AutoMutex _l(gDefaultServiceManagerLock); + if (gDefaultServiceManager == NULL) { + gDefaultServiceManager = interface_cast( + ProcessState::self()->getContextObject(NULL)); + } + } + + return gDefaultServiceManager; +} + +bool checkCallingPermission(const String16& permission) +{ + return checkCallingPermission(permission, NULL, NULL); +} + +static String16 _permission("permission"); + +bool checkCallingPermission(const String16& permission, int32_t* outPid, int32_t* outUid) +{ + IPCThreadState* ipcState = IPCThreadState::self(); + int32_t pid = ipcState->getCallingPid(); + int32_t uid = ipcState->getCallingUid(); + if (outPid) *outPid = pid; + if (outUid) *outUid= uid; + + sp pc; + gDefaultServiceManagerLock.lock(); + pc = gPermissionController; + gDefaultServiceManagerLock.unlock(); + + int64_t startTime = 0; + + while (true) { + if (pc != NULL) { + bool res = pc->checkPermission(permission, pid, uid); + if (res) { + if (startTime != 0) { + LOGI("Check passed after %d seconds for %s from uid=%d pid=%d", + (int)((uptimeMillis()-startTime)/1000), + String8(permission).string(), uid, pid); + } + return res; + } + + // Is this a permission failure, or did the controller go away? + if (pc->asBinder()->isBinderAlive()) { + LOGW("Permission failure: %s from uid=%d pid=%d", + String8(permission).string(), uid, pid); + return false; + } + + // Object is dead! + gDefaultServiceManagerLock.lock(); + if (gPermissionController == pc) { + gPermissionController = NULL; + } + gDefaultServiceManagerLock.unlock(); + } + + // Need to retrieve the permission controller. + sp binder = defaultServiceManager()->checkService(_permission); + if (binder == NULL) { + // Wait for the permission controller to come back... + if (startTime == 0) { + startTime = uptimeMillis(); + LOGI("Waiting to check permission %s from uid=%d pid=%d", + String8(permission).string(), uid, pid); + } + sleep(1); + } else { + pc = interface_cast(binder); + // Install the new permission controller, and try again. + gDefaultServiceManagerLock.lock(); + gPermissionController = pc; + gDefaultServiceManagerLock.unlock(); + } + } +} + +// ---------------------------------------------------------------------- + +class BpServiceManager : public BpInterface +{ +public: + BpServiceManager(const sp& impl) + : BpInterface(impl) + { + } + + virtual sp getService(const String16& name) const + { + unsigned n; + for (n = 0; n < 5; n++){ + sp svc = checkService(name); + if (svc != NULL) return svc; + LOGI("Waiting for sevice %s...\n", String8(name).string()); + sleep(1); + } + return NULL; + } + + virtual sp checkService( const String16& name) const + { + Parcel data, reply; + data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); + data.writeString16(name); + remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply); + return reply.readStrongBinder(); + } + + virtual status_t addService(const String16& name, const sp& service) + { + Parcel data, reply; + data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); + data.writeString16(name); + data.writeStrongBinder(service); + status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply); + return err == NO_ERROR ? reply.readInt32() : err; + } + + virtual Vector listServices() + { + Vector res; + int n = 0; + + for (;;) { + Parcel data, reply; + data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); + data.writeInt32(n++); + status_t err = remote()->transact(LIST_SERVICES_TRANSACTION, data, &reply); + if (err != NO_ERROR) + break; + res.add(reply.readString16()); + } + return res; + } +}; + +IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager"); + +// ---------------------------------------------------------------------- + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnServiceManager::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + //printf("ServiceManager received: "); data.print(); + switch(code) { + case GET_SERVICE_TRANSACTION: { + CHECK_INTERFACE(IServiceManager, data, reply); + String16 which = data.readString16(); + sp b = const_cast(this)->getService(which); + reply->writeStrongBinder(b); + return NO_ERROR; + } break; + case CHECK_SERVICE_TRANSACTION: { + CHECK_INTERFACE(IServiceManager, data, reply); + String16 which = data.readString16(); + sp b = const_cast(this)->checkService(which); + reply->writeStrongBinder(b); + return NO_ERROR; + } break; + case ADD_SERVICE_TRANSACTION: { + CHECK_INTERFACE(IServiceManager, data, reply); + String16 which = data.readString16(); + sp b = data.readStrongBinder(); + status_t err = addService(which, b); + reply->writeInt32(err); + return NO_ERROR; + } break; + case LIST_SERVICES_TRANSACTION: { + CHECK_INTERFACE(IServiceManager, data, reply); + Vector list = listServices(); + const size_t N = list.size(); + reply->writeInt32(N); + for (size_t i=0; iwriteString16(list[i]); + } + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +}; // namespace android + diff --git a/libs/utils/InetAddress.cpp b/libs/utils/InetAddress.cpp new file mode 100644 index 000000000..39a0a6839 --- /dev/null +++ b/libs/utils/InetAddress.cpp @@ -0,0 +1,236 @@ +/* + * 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. + */ + +// +// Internet address class. +// +#ifdef HAVE_WINSOCK +# include +#else +# include +# include +# include +//# include +# include +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace android; + + +/* + * =========================================================================== + * InetAddress + * =========================================================================== + */ + +// lock for the next couple of functions; could tuck into InetAddress +static Mutex* gGHBNLock; + +/* + * Lock/unlock access to the hostent struct returned by gethostbyname(). + */ +static inline void lock_gethostbyname(void) +{ + if (gGHBNLock == NULL) + gGHBNLock = new Mutex; + gGHBNLock->lock(); +} +static inline void unlock_gethostbyname(void) +{ + assert(gGHBNLock != NULL); + gGHBNLock->unlock(); +} + + +/* + * Constructor -- just init members. This is private so that callers + * are required to use getByName(). + */ +InetAddress::InetAddress(void) + : mAddress(NULL), mLength(-1), mName(NULL) +{ +} + +/* + * Destructor -- free address storage. + */ +InetAddress::~InetAddress(void) +{ + delete[] (char*) mAddress; + delete[] mName; +} + +/* + * Copy constructor. + */ +InetAddress::InetAddress(const InetAddress& orig) +{ + *this = orig; // use assignment code +} + +/* + * Assignment operator. + */ +InetAddress& InetAddress::operator=(const InetAddress& addr) +{ + // handle self-assignment + if (this == &addr) + return *this; + // copy mLength and mAddress + mLength = addr.mLength; + if (mLength > 0) { + mAddress = new char[mLength]; + memcpy(mAddress, addr.mAddress, mLength); + LOG(LOG_DEBUG, "socket", + "HEY: copied %d bytes in assignment operator\n", mLength); + } else { + mAddress = NULL; + } + // copy mName + mName = new char[strlen(addr.mName)+1]; + strcpy(mName, addr.mName); + + return *this; +} + +/* + * Create a new object from a name or a dotted-number IP notation. + * + * Returns NULL on failure. + */ +InetAddress* +InetAddress::getByName(const char* host) +{ + InetAddress* newAddr = NULL; + struct sockaddr_in addr; + struct hostent* he; + DurationTimer hostTimer, lockTimer; + + // gethostbyname() isn't reentrant, so we need to lock things until + // we can copy the data out. + lockTimer.start(); + lock_gethostbyname(); + hostTimer.start(); + + he = gethostbyname(host); + if (he == NULL) { + LOG(LOG_WARN, "socket", "WARNING: cannot resolve host %s\n", host); + unlock_gethostbyname(); + return NULL; + } + + memcpy(&addr.sin_addr, he->h_addr, he->h_length); + addr.sin_family = he->h_addrtype; + addr.sin_port = 0; + + // got it, unlock us + hostTimer.stop(); + he = NULL; + unlock_gethostbyname(); + + lockTimer.stop(); + if ((long) lockTimer.durationUsecs() > 100000) { + long lockTime = (long) lockTimer.durationUsecs(); + long hostTime = (long) hostTimer.durationUsecs(); + LOG(LOG_DEBUG, "socket", + "Lookup of %s took %.3fs (gethostbyname=%.3fs lock=%.3fs)\n", + host, lockTime / 1000000.0, hostTime / 1000000.0, + (lockTime - hostTime) / 1000000.0); + } + + // Alloc storage and copy it over. + newAddr = new InetAddress(); + if (newAddr == NULL) + return NULL; + + newAddr->mLength = sizeof(struct sockaddr_in); + newAddr->mAddress = new char[sizeof(struct sockaddr_in)]; + if (newAddr->mAddress == NULL) { + delete newAddr; + return NULL; + } + memcpy(newAddr->mAddress, &addr, newAddr->mLength); + + // Keep this for debug messages. + newAddr->mName = new char[strlen(host)+1]; + if (newAddr->mName == NULL) { + delete newAddr; + return NULL; + } + strcpy(newAddr->mName, host); + + return newAddr; +} + + +/* + * =========================================================================== + * InetSocketAddress + * =========================================================================== + */ + +/* + * Create an address with the host wildcard (INADDR_ANY). + */ +bool InetSocketAddress::create(int port) +{ + assert(mAddress == NULL); + + mAddress = InetAddress::getByName("0.0.0.0"); + if (mAddress == NULL) + return false; + mPort = port; + return true; +} + +/* + * Create address with host and port specified. + */ +bool InetSocketAddress::create(const InetAddress* addr, int port) +{ + assert(mAddress == NULL); + + mAddress = new InetAddress(*addr); // make a copy + if (mAddress == NULL) + return false; + mPort = port; + return true; +} + +/* + * Create address with host and port specified. + */ +bool InetSocketAddress::create(const char* host, int port) +{ + assert(mAddress == NULL); + + mAddress = InetAddress::getByName(host); + if (mAddress == NULL) + return false; + mPort = port; + return true; +} + diff --git a/libs/utils/LogSocket.cpp b/libs/utils/LogSocket.cpp new file mode 100644 index 000000000..55c1b99af --- /dev/null +++ b/libs/utils/LogSocket.cpp @@ -0,0 +1,129 @@ +/* + * 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 HAVE_WINSOCK +//#define SOCKETLOG +#endif + +#ifdef SOCKETLOG + +#define LOG_TAG "SOCKETLOG" + +#include +#include +#include "utils/LogSocket.h" +#include "utils/logger.h" +#include "cutils/hashmap.h" + +// defined in //device/data/etc/event-log-tags +#define SOCKET_CLOSE_LOG 51000 + +static Hashmap* statsMap = NULL; + +#define LOG_LIST_NUMBER 5 + +typedef struct SocketStats { + int fd; + unsigned int send; + unsigned int recv; + unsigned int ip; + unsigned short port; + short reason; +}SocketStats; + +SocketStats *get_socket_stats(int fd) { + if (statsMap == NULL) { + statsMap = hashmapCreate(8, &hashmapIntHash, &hashmapIntEquals); + } + + SocketStats *s = (SocketStats*) hashmapGet(statsMap, &fd); + if (s == NULL) { + // LOGD("create SocketStats for fd %d", fd); + s = (SocketStats*) malloc(sizeof(SocketStats)); + memset(s, 0, sizeof(SocketStats)); + s->fd = fd; + hashmapPut(statsMap, &s->fd, s); + } + return s; +} + +void log_socket_connect(int fd, unsigned int ip, unsigned short port) { + // LOGD("log_socket_connect for fd %d ip %d port%d", fd, ip, port); + SocketStats *s = get_socket_stats(fd); + s->ip = ip; + s->port = port; +} + +void add_send_stats(int fd, int send) { + if (send <=0) { + LOGE("add_send_stats send %d", send); + return; + } + SocketStats *s = get_socket_stats(fd); + s->send += send; + // LOGD("add_send_stats for fd %d ip %d port%d", fd, s->ip, s->port); +} + +void add_recv_stats(int fd, int recv) { + if (recv <=0) { + LOGE("add_recv_stats recv %d", recv); + return; + } + SocketStats *s = get_socket_stats(fd); + s->recv += recv; + // LOGD("add_recv_stats for fd %d ip %d port%d", fd, s->ip, s->port); +} + +char* put_int(char* buf, int value) { + *buf = EVENT_TYPE_INT; + buf++; + memcpy(buf, &value, sizeof(int)); + return buf + sizeof(int); +} + +void log_socket_close(int fd, short reason) { + if (statsMap) { + SocketStats *s = (SocketStats*) hashmapGet(statsMap, &fd); + if (s != NULL) { + if (s->send != 0 || s->recv != 0) { + s->reason = reason; + // 5 int + list type need 2 bytes + char buf[LOG_LIST_NUMBER * 5 + 2]; + buf[0] = EVENT_TYPE_LIST; + buf[1] = LOG_LIST_NUMBER; + char* writePos = buf + 2; + writePos = put_int(writePos, s->send); + writePos = put_int(writePos, s->recv); + writePos = put_int(writePos, s->ip); + writePos = put_int(writePos, s->port); + writePos = put_int(writePos, s->reason); + + android_bWriteLog(SOCKET_CLOSE_LOG, buf, sizeof(buf)); + // LOGD("send %d recv %d reason %d", s->send, s->recv, s->reason); + } + hashmapRemove(statsMap, &s->fd); + free(s); + } + } +} + +#else +void add_send_stats(int fd, int send) {} +void add_recv_stats(int fd, int recv) {} +void log_socket_close(int fd, short reason) {} +void log_socket_connect(int fd, unsigned int ip, unsigned short port) {} +#endif diff --git a/libs/utils/MODULE_LICENSE_APACHE2 b/libs/utils/MODULE_LICENSE_APACHE2 new file mode 100644 index 000000000..e69de29bb diff --git a/libs/utils/MemoryBase.cpp b/libs/utils/MemoryBase.cpp new file mode 100644 index 000000000..f25e11c6b --- /dev/null +++ b/libs/utils/MemoryBase.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include + +#include + + +namespace android { + +// --------------------------------------------------------------------------- + +MemoryBase::MemoryBase(const sp& heap, + ssize_t offset, size_t size) + : mSize(size), mOffset(offset), mHeap(heap) +{ +} + +sp MemoryBase::getMemory(ssize_t* offset, size_t* size) const +{ + if (offset) *offset = mOffset; + if (size) *size = mSize; + return mHeap; +} + +MemoryBase::~MemoryBase() +{ +} + +// --------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/utils/MemoryDealer.cpp b/libs/utils/MemoryDealer.cpp new file mode 100644 index 000000000..cf8201b85 --- /dev/null +++ b/libs/utils/MemoryDealer.cpp @@ -0,0 +1,409 @@ +/* + * 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 "MemoryDealer" + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace android { + + +// ---------------------------------------------------------------------------- + +class SimpleMemory : public MemoryBase { +public: + SimpleMemory(const sp& heap, ssize_t offset, size_t size); + virtual ~SimpleMemory(); +}; + + +// ---------------------------------------------------------------------------- + +MemoryDealer::Allocation::Allocation( + const sp& dealer, ssize_t offset, size_t size, + const sp& memory) + : mDealer(dealer), mOffset(offset), mSize(size), mMemory(memory) +{ +} + +MemoryDealer::Allocation::~Allocation() +{ + if (mSize) { + /* NOTE: it's VERY important to not free allocations of size 0 because + * they're special as they don't have any record in the allocator + * and could alias some real allocation (their offset is zero). */ + mDealer->deallocate(mOffset); + } +} + +sp MemoryDealer::Allocation::getMemory( + ssize_t* offset, size_t* size) const +{ + return mMemory->getMemory(offset, size); +} + +// ---------------------------------------------------------------------------- + +MemoryDealer::MemoryDealer(size_t size, uint32_t flags, const char* name) + : mHeap(new SharedHeap(size, flags, name)), + mAllocator(new SimpleBestFitAllocator(size)) +{ +} + +MemoryDealer::MemoryDealer(const sp& heap) + : mHeap(heap), + mAllocator(new SimpleBestFitAllocator(heap->virtualSize())) +{ +} + +MemoryDealer::MemoryDealer( const sp& heap, + const sp& allocator) + : mHeap(heap), mAllocator(allocator) +{ +} + +MemoryDealer::~MemoryDealer() +{ +} + +sp MemoryDealer::allocate(size_t size, uint32_t flags) +{ + sp memory; + const ssize_t offset = allocator()->allocate(size, flags); + if (offset >= 0) { + sp new_memory = heap()->mapMemory(offset, size); + if (new_memory != 0) { + memory = new Allocation(this, offset, size, new_memory); + } else { + LOGE("couldn't map [%8x, %d]", offset, size); + if (size) { + /* NOTE: it's VERY important to not free allocations of size 0 + * because they're special as they don't have any record in the + * allocator and could alias some real allocation + * (their offset is zero). */ + allocator()->deallocate(offset); + } + } + } + return memory; +} + +void MemoryDealer::deallocate(size_t offset) +{ + allocator()->deallocate(offset); +} + +void MemoryDealer::dump(const char* what, uint32_t flags) const +{ + allocator()->dump(what, flags); +} + +const sp& MemoryDealer::heap() const { + return mHeap; +} + +const sp& MemoryDealer::allocator() const { + return mAllocator; +} + +// ---------------------------------------------------------------------------- + +// align all the memory blocks on a cache-line boundary +const int SimpleBestFitAllocator::kMemoryAlign = 32; + +SimpleBestFitAllocator::SimpleBestFitAllocator(size_t size) +{ + size_t pagesize = getpagesize(); + mHeapSize = ((size + pagesize-1) & ~(pagesize-1)); + + chunk_t* node = new chunk_t(0, mHeapSize / kMemoryAlign); + mList.insertHead(node); +} + +SimpleBestFitAllocator::~SimpleBestFitAllocator() +{ + while(!mList.isEmpty()) { + delete mList.remove(mList.head()); + } +} + +size_t SimpleBestFitAllocator::size() const +{ + return mHeapSize; +} + +size_t SimpleBestFitAllocator::allocate(size_t size, uint32_t flags) +{ + Mutex::Autolock _l(mLock); + ssize_t offset = alloc(size, flags); + return offset; +} + +status_t SimpleBestFitAllocator::deallocate(size_t offset) +{ + Mutex::Autolock _l(mLock); + chunk_t const * const freed = dealloc(offset); + if (freed) { + return NO_ERROR; + } + return NAME_NOT_FOUND; +} + +ssize_t SimpleBestFitAllocator::alloc(size_t size, uint32_t flags) +{ + if (size == 0) { + return 0; + } + size = (size + kMemoryAlign-1) / kMemoryAlign; + chunk_t* free_chunk = 0; + chunk_t* cur = mList.head(); + + size_t pagesize = getpagesize(); + while (cur) { + int extra = 0; + if (flags & PAGE_ALIGNED) + extra = ( -cur->start & ((pagesize/kMemoryAlign)-1) ) ; + + // best fit + if (cur->free && (cur->size >= (size+extra))) { + if ((!free_chunk) || (cur->size < free_chunk->size)) { + free_chunk = cur; + } + if (cur->size == size) { + break; + } + } + cur = cur->next; + } + + if (free_chunk) { + const size_t free_size = free_chunk->size; + free_chunk->free = 0; + free_chunk->size = size; + if (free_size > size) { + int extra = 0; + if (flags & PAGE_ALIGNED) + extra = ( -free_chunk->start & ((pagesize/kMemoryAlign)-1) ) ; + if (extra) { + chunk_t* split = new chunk_t(free_chunk->start, extra); + free_chunk->start += extra; + mList.insertBefore(free_chunk, split); + } + + LOGE_IF((flags&PAGE_ALIGNED) && + ((free_chunk->start*kMemoryAlign)&(pagesize-1)), + "PAGE_ALIGNED requested, but page is not aligned!!!"); + + const ssize_t tail_free = free_size - (size+extra); + if (tail_free > 0) { + chunk_t* split = new chunk_t( + free_chunk->start + free_chunk->size, tail_free); + mList.insertAfter(free_chunk, split); + } + } + return (free_chunk->start)*kMemoryAlign; + } + return NO_MEMORY; +} + +SimpleBestFitAllocator::chunk_t* SimpleBestFitAllocator::dealloc(size_t start) +{ + start = start / kMemoryAlign; + chunk_t* cur = mList.head(); + while (cur) { + if (cur->start == start) { + LOG_FATAL_IF(cur->free, + "block at offset 0x%08lX of size 0x%08lX already freed", + cur->start*kMemoryAlign, cur->size*kMemoryAlign); + + // merge freed blocks together + chunk_t* freed = cur; + cur->free = 1; + do { + chunk_t* const p = cur->prev; + chunk_t* const n = cur->next; + if (p && (p->free || !cur->size)) { + freed = p; + p->size += cur->size; + mList.remove(cur); + delete cur; + } + cur = n; + } while (cur && cur->free); + + #ifndef NDEBUG + if (!freed->free) { + dump_l("dealloc (!freed->free)"); + } + #endif + LOG_FATAL_IF(!freed->free, + "freed block at offset 0x%08lX of size 0x%08lX is not free!", + freed->start * kMemoryAlign, freed->size * kMemoryAlign); + + return freed; + } + cur = cur->next; + } + return 0; +} + +void SimpleBestFitAllocator::dump(const char* what, uint32_t flags) const +{ + Mutex::Autolock _l(mLock); + dump_l(what, flags); +} + +void SimpleBestFitAllocator::dump_l(const char* what, uint32_t flags) const +{ + String8 result; + dump_l(result, what, flags); + LOGD("%s", result.string()); +} + +void SimpleBestFitAllocator::dump(String8& result, + const char* what, uint32_t flags) const +{ + Mutex::Autolock _l(mLock); + dump_l(result, what, flags); +} + +void SimpleBestFitAllocator::dump_l(String8& result, + const char* what, uint32_t flags) const +{ + size_t size = 0; + int32_t i = 0; + chunk_t const* cur = mList.head(); + + const size_t SIZE = 256; + char buffer[SIZE]; + snprintf(buffer, SIZE, " %s (%p, size=%u)\n", + what, this, (unsigned int)mHeapSize); + + result.append(buffer); + + while (cur) { + const char* errs[] = {"", "| link bogus NP", + "| link bogus PN", "| link bogus NP+PN" }; + int np = ((cur->next) && cur->next->prev != cur) ? 1 : 0; + int pn = ((cur->prev) && cur->prev->next != cur) ? 2 : 0; + + snprintf(buffer, SIZE, " %3u: %08x | 0x%08X | 0x%08X | %s %s\n", + i, int(cur), int(cur->start*kMemoryAlign), + int(cur->size*kMemoryAlign), + int(cur->free) ? "F" : "A", + errs[np|pn]); + + result.append(buffer); + + if (!cur->free) + size += cur->size*kMemoryAlign; + + i++; + cur = cur->next; + } + snprintf(buffer, SIZE, " size allocated: %u (%u KB)\n", int(size), int(size/1024)); + result.append(buffer); +} + +// ---------------------------------------------------------------------------- + + +SharedHeap::SharedHeap(size_t size, uint32_t flags, char const * name) + : MemoryHeapBase(size, flags, name) +{ +} + +SharedHeap::~SharedHeap() +{ +} + +sp SharedHeap::mapMemory(size_t offset, size_t size) +{ + return new SimpleMemory(this, offset, size); +} + + +SimpleMemory::SimpleMemory(const sp& heap, + ssize_t offset, size_t size) + : MemoryBase(heap, offset, size) +{ +#ifndef NDEBUG + void* const start_ptr = (void*)(intptr_t(heap->base()) + offset); + memset(start_ptr, 0xda, size); +#endif +} + +SimpleMemory::~SimpleMemory() +{ + size_t freedOffset = getOffset(); + size_t freedSize = getSize(); + + // keep the size to unmap in excess + size_t pagesize = getpagesize(); + size_t start = freedOffset; + size_t end = start + freedSize; + start &= ~(pagesize-1); + end = (end + pagesize-1) & ~(pagesize-1); + + // give back to the kernel the pages we don't need + size_t free_start = freedOffset; + size_t free_end = free_start + freedSize; + if (start < free_start) + start = free_start; + if (end > free_end) + end = free_end; + start = (start + pagesize-1) & ~(pagesize-1); + end &= ~(pagesize-1); + + if (start < end) { + void* const start_ptr = (void*)(intptr_t(getHeap()->base()) + start); + size_t size = end-start; + +#ifndef NDEBUG + memset(start_ptr, 0xdf, size); +#endif + + // MADV_REMOVE is not defined on Dapper based Goobuntu +#ifdef MADV_REMOVE + if (size) { + int err = madvise(start_ptr, size, MADV_REMOVE); + LOGW_IF(err, "madvise(%p, %u, MADV_REMOVE) returned %s", + start_ptr, size, err<0 ? strerror(errno) : "Ok"); + } +#endif + } +} + +}; // namespace android diff --git a/libs/utils/MemoryHeapBase.cpp b/libs/utils/MemoryHeapBase.cpp new file mode 100644 index 000000000..825172819 --- /dev/null +++ b/libs/utils/MemoryHeapBase.cpp @@ -0,0 +1,183 @@ +/* + * 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 "MemoryHeapBase" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#if HAVE_ANDROID_OS +#include +#endif + + +namespace android { + +// --------------------------------------------------------------------------- + +MemoryHeapBase::MemoryHeapBase() + : mFD(-1), mSize(0), mBase(MAP_FAILED), + mDevice(NULL), mNeedUnmap(false) +{ +} + +MemoryHeapBase::MemoryHeapBase(size_t size, uint32_t flags, char const * name) + : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags), + mDevice(0), mNeedUnmap(false) +{ + const size_t pagesize = getpagesize(); + size = ((size + pagesize-1) & ~(pagesize-1)); + int fd = ashmem_create_region(name == NULL ? "MemoryHeapBase" : name, size); + LOGE_IF(fd<0, "error creating ashmem region: %s", strerror(errno)); + if (fd >= 0) { + if (mapfd(fd, size) == NO_ERROR) { + if (flags & READ_ONLY) { + ashmem_set_prot_region(fd, PROT_READ); + } + } + } +} + +MemoryHeapBase::MemoryHeapBase(const char* device, size_t size, uint32_t flags) + : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags), + mDevice(0), mNeedUnmap(false) +{ + int fd = open(device, O_RDWR); + LOGE_IF(fd<0, "error opening %s: %s", device, strerror(errno)); + if (fd >= 0) { + const size_t pagesize = getpagesize(); + size = ((size + pagesize-1) & ~(pagesize-1)); + if (mapfd(fd, size) == NO_ERROR) { + mDevice = device; + } + } +} + +MemoryHeapBase::MemoryHeapBase(int fd, size_t size, uint32_t flags) + : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags), + mDevice(0), mNeedUnmap(false) +{ + const size_t pagesize = getpagesize(); + size = ((size + pagesize-1) & ~(pagesize-1)); + mapfd(dup(fd), size); +} + +status_t MemoryHeapBase::init(int fd, void *base, int size, int flags, const char* device) +{ + if (mFD != -1) { + return INVALID_OPERATION; + } + mFD = fd; + mBase = base; + mSize = size; + mFlags = flags; + mDevice = device; + return NO_ERROR; +} + +status_t MemoryHeapBase::mapfd(int fd, size_t size) +{ + if (size == 0) { + // try to figure out the size automatically +#if HAVE_ANDROID_OS + // first try the PMEM ioctl + pmem_region reg; + int err = ioctl(fd, PMEM_GET_TOTAL_SIZE, ®); + if (err == 0) + size = reg.len; +#endif + if (size == 0) { // try fstat + struct stat sb; + if (fstat(fd, &sb) == 0) + size = sb.st_size; + } + // if it didn't work, let mmap() fail. + } + + if ((mFlags & DONT_MAP_LOCALLY) == 0) { + void* base = (uint8_t*)mmap(0, size, + PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + if (base == MAP_FAILED) { + LOGE("mmap(fd=%d, size=%u) failed (%s)", + fd, uint32_t(size), strerror(errno)); + close(fd); + return -errno; + } + //LOGD("mmap(fd=%d, base=%p, size=%lu)", fd, base, size); + mBase = base; + mNeedUnmap = true; + } else { + mBase = 0; // not MAP_FAILED + mNeedUnmap = false; + } + mFD = fd; + mSize = size; + return NO_ERROR; +} + +MemoryHeapBase::~MemoryHeapBase() +{ + dispose(); +} + +void MemoryHeapBase::dispose() +{ + int fd = android_atomic_or(-1, &mFD); + if (fd >= 0) { + if (mNeedUnmap) { + //LOGD("munmap(fd=%d, base=%p, size=%lu)", fd, mBase, mSize); + munmap(mBase, mSize); + } + mBase = 0; + mSize = 0; + close(fd); + } +} + +int MemoryHeapBase::getHeapID() const { + return mFD; +} + +void* MemoryHeapBase::getBase() const { + return mBase; +} + +size_t MemoryHeapBase::getSize() const { + return mSize; +} + +uint32_t MemoryHeapBase::getFlags() const { + return mFlags; +} + +const char* MemoryHeapBase::getDevice() const { + return mDevice; +} + +// --------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/utils/MemoryHeapPmem.cpp b/libs/utils/MemoryHeapPmem.cpp new file mode 100644 index 000000000..eba2b3055 --- /dev/null +++ b/libs/utils/MemoryHeapPmem.cpp @@ -0,0 +1,248 @@ +/* + * 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 "MemoryHeapPmem" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#if HAVE_ANDROID_OS +#include +#endif + +namespace android { + +// --------------------------------------------------------------------------- + +MemoryHeapPmem::MemoryPmem::MemoryPmem(const sp& heap) + : BnMemory(), mClientHeap(heap) +{ +} + +MemoryHeapPmem::MemoryPmem::~MemoryPmem() { + if (mClientHeap != NULL) { + mClientHeap->remove(this); + } +} + +// --------------------------------------------------------------------------- + +class SubRegionMemory : public MemoryHeapPmem::MemoryPmem { +public: + SubRegionMemory(const sp& heap, ssize_t offset, size_t size); + virtual ~SubRegionMemory(); + virtual sp getMemory(ssize_t* offset, size_t* size) const; +private: + friend class MemoryHeapPmem; + void revoke(); + size_t mSize; + ssize_t mOffset; +}; + +SubRegionMemory::SubRegionMemory(const sp& heap, + ssize_t offset, size_t size) + : MemoryHeapPmem::MemoryPmem(heap), mSize(size), mOffset(offset) +{ +#ifndef NDEBUG + void* const start_ptr = (void*)(intptr_t(getHeap()->base()) + offset); + memset(start_ptr, 0xda, size); +#endif + +#if HAVE_ANDROID_OS + if (size > 0) { + const size_t pagesize = getpagesize(); + size = (size + pagesize-1) & ~(pagesize-1); + int our_fd = heap->heapID(); + struct pmem_region sub = { offset, size }; + int err = ioctl(our_fd, PMEM_MAP, &sub); + LOGE_IF(err<0, "PMEM_MAP failed (%s), " + "mFD=%d, sub.offset=%lu, sub.size=%lu", + strerror(errno), our_fd, sub.offset, sub.len); +} +#endif +} + +sp SubRegionMemory::getMemory(ssize_t* offset, size_t* size) const +{ + if (offset) *offset = mOffset; + if (size) *size = mSize; + return getHeap(); +} + +SubRegionMemory::~SubRegionMemory() +{ + revoke(); +} + + +void SubRegionMemory::revoke() +{ + // NOTE: revoke() doesn't need to be protected by a lock because it + // can only be called from MemoryHeapPmem::revoke(), which means + // that we can't be in ~SubRegionMemory(), or in ~SubRegionMemory(), + // which means MemoryHeapPmem::revoke() wouldn't have been able to + // promote() it. + +#if HAVE_ANDROID_OS + if (mSize != NULL) { + const sp& heap(getHeap()); + int our_fd = heap->heapID(); + struct pmem_region sub; + sub.offset = mOffset; + sub.len = mSize; + int err = ioctl(our_fd, PMEM_UNMAP, &sub); + LOGE_IF(err<0, "PMEM_UNMAP failed (%s), " + "mFD=%d, sub.offset=%lu, sub.size=%lu", + strerror(errno), our_fd, sub.offset, sub.len); + mSize = 0; + } +#endif +} + +// --------------------------------------------------------------------------- + +MemoryHeapPmem::MemoryHeapPmem(const sp& pmemHeap, + uint32_t flags) + : HeapInterface(), MemoryHeapBase() +{ + char const * const device = pmemHeap->getDevice(); +#if HAVE_ANDROID_OS + if (device) { + int fd = open(device, O_RDWR); + LOGE_IF(fd<0, "couldn't open %s (%s)", device, strerror(errno)); + if (fd >= 0) { + int err = ioctl(fd, PMEM_CONNECT, pmemHeap->heapID()); + if (err < 0) { + LOGE("PMEM_CONNECT failed (%s), mFD=%d, sub-fd=%d", + strerror(errno), fd, pmemHeap->heapID()); + close(fd); + } else { + // everything went well... + mParentHeap = pmemHeap; + MemoryHeapBase::init(fd, + pmemHeap->getBase(), + pmemHeap->getSize(), + pmemHeap->getFlags() | flags, + device); + } + } + } +#else + mParentHeap = pmemHeap; + MemoryHeapBase::init( + dup(pmemHeap->heapID()), + pmemHeap->getBase(), + pmemHeap->getSize(), + pmemHeap->getFlags() | flags, + device); +#endif +} + +MemoryHeapPmem::~MemoryHeapPmem() +{ +} + +sp MemoryHeapPmem::mapMemory(size_t offset, size_t size) +{ + sp memory = createMemory(offset, size); + if (memory != 0) { + Mutex::Autolock _l(mLock); + mAllocations.add(memory); + } + return memory; +} + +sp MemoryHeapPmem::createMemory( + size_t offset, size_t size) +{ + sp memory; + if (heapID() > 0) + memory = new SubRegionMemory(this, offset, size); + return memory; +} + +status_t MemoryHeapPmem::slap() +{ +#if HAVE_ANDROID_OS + size_t size = getSize(); + const size_t pagesize = getpagesize(); + size = (size + pagesize-1) & ~(pagesize-1); + int our_fd = getHeapID(); + struct pmem_region sub = { 0, size }; + int err = ioctl(our_fd, PMEM_MAP, &sub); + LOGE_IF(err<0, "PMEM_MAP failed (%s), " + "mFD=%d, sub.offset=%lu, sub.size=%lu", + strerror(errno), our_fd, sub.offset, sub.len); + return -errno; +#else + return NO_ERROR; +#endif +} + +status_t MemoryHeapPmem::unslap() +{ +#if HAVE_ANDROID_OS + size_t size = getSize(); + const size_t pagesize = getpagesize(); + size = (size + pagesize-1) & ~(pagesize-1); + int our_fd = getHeapID(); + struct pmem_region sub = { 0, size }; + int err = ioctl(our_fd, PMEM_UNMAP, &sub); + LOGE_IF(err<0, "PMEM_UNMAP failed (%s), " + "mFD=%d, sub.offset=%lu, sub.size=%lu", + strerror(errno), our_fd, sub.offset, sub.len); + return -errno; +#else + return NO_ERROR; +#endif +} + +void MemoryHeapPmem::revoke() +{ + SortedVector< wp > allocations; + + { // scope for lock + Mutex::Autolock _l(mLock); + allocations = mAllocations; + } + + ssize_t count = allocations.size(); + for (ssize_t i=0 ; i memory(allocations[i].promote()); + if (memory != 0) + memory->revoke(); + } +} + +void MemoryHeapPmem::remove(const wp& memory) +{ + Mutex::Autolock _l(mLock); + mAllocations.remove(memory); +} + +// --------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/utils/NOTICE b/libs/utils/NOTICE new file mode 100644 index 000000000..c5b1efa7a --- /dev/null +++ b/libs/utils/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/libs/utils/Parcel.cpp b/libs/utils/Parcel.cpp new file mode 100644 index 000000000..0f4b64730 --- /dev/null +++ b/libs/utils/Parcel.cpp @@ -0,0 +1,1377 @@ +/* + * 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 "Parcel" +//#define LOG_NDEBUG 0 + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#ifndef INT32_MAX +#define INT32_MAX ((int32_t)(2147483647)) +#endif + +#define LOG_REFS(...) +//#define LOG_REFS(...) LOG(LOG_DEBUG, "Parcel", __VA_ARGS__) + +// --------------------------------------------------------------------------- + +#define PAD_SIZE(s) (((s)+3)&~3) + +// XXX This can be made public if we want to provide +// support for typed data. +struct small_flat_data +{ + uint32_t type; + uint32_t data; +}; + +namespace android { + +void acquire_object(const sp& proc, + const flat_binder_object& obj, const void* who) +{ + switch (obj.type) { + case BINDER_TYPE_BINDER: + if (obj.binder) { + LOG_REFS("Parcel %p acquiring reference on local %p", who, obj.cookie); + static_cast(obj.cookie)->incStrong(who); + } + return; + case BINDER_TYPE_WEAK_BINDER: + if (obj.binder) + static_cast(obj.binder)->incWeak(who); + return; + case BINDER_TYPE_HANDLE: { + const sp b = proc->getStrongProxyForHandle(obj.handle); + if (b != NULL) { + LOG_REFS("Parcel %p acquiring reference on remote %p", who, b.get()); + b->incStrong(who); + } + return; + } + case BINDER_TYPE_WEAK_HANDLE: { + const wp b = proc->getWeakProxyForHandle(obj.handle); + if (b != NULL) b.get_refs()->incWeak(who); + return; + } + case BINDER_TYPE_FD: { + // intentionally blank -- nothing to do to acquire this, but we do + // recognize it as a legitimate object type. + return; + } + } + + LOGD("Invalid object type 0x%08lx", obj.type); +} + +void release_object(const sp& proc, + const flat_binder_object& obj, const void* who) +{ + switch (obj.type) { + case BINDER_TYPE_BINDER: + if (obj.binder) { + LOG_REFS("Parcel %p releasing reference on local %p", who, obj.cookie); + static_cast(obj.cookie)->decStrong(who); + } + return; + case BINDER_TYPE_WEAK_BINDER: + if (obj.binder) + static_cast(obj.binder)->decWeak(who); + return; + case BINDER_TYPE_HANDLE: { + const sp b = proc->getStrongProxyForHandle(obj.handle); + if (b != NULL) { + LOG_REFS("Parcel %p releasing reference on remote %p", who, b.get()); + b->decStrong(who); + } + return; + } + case BINDER_TYPE_WEAK_HANDLE: { + const wp b = proc->getWeakProxyForHandle(obj.handle); + if (b != NULL) b.get_refs()->decWeak(who); + return; + } + case BINDER_TYPE_FD: { + if (obj.cookie != (void*)0) close(obj.handle); + return; + } + } + + LOGE("Invalid object type 0x%08lx", obj.type); +} + +inline static status_t finish_flatten_binder( + const sp& binder, const flat_binder_object& flat, Parcel* out) +{ + return out->writeObject(flat, false); +} + +status_t flatten_binder(const sp& proc, + const sp& binder, Parcel* out) +{ + flat_binder_object obj; + + obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; + if (binder != NULL) { + IBinder *local = binder->localBinder(); + if (!local) { + BpBinder *proxy = binder->remoteBinder(); + if (proxy == NULL) { + LOGE("null proxy"); + } + const int32_t handle = proxy ? proxy->handle() : 0; + obj.type = BINDER_TYPE_HANDLE; + obj.handle = handle; + obj.cookie = NULL; + } else { + obj.type = BINDER_TYPE_BINDER; + obj.binder = local->getWeakRefs(); + obj.cookie = local; + } + } else { + obj.type = BINDER_TYPE_BINDER; + obj.binder = NULL; + obj.cookie = NULL; + } + + return finish_flatten_binder(binder, obj, out); +} + +status_t flatten_binder(const sp& proc, + const wp& binder, Parcel* out) +{ + flat_binder_object obj; + + obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; + if (binder != NULL) { + sp real = binder.promote(); + if (real != NULL) { + IBinder *local = real->localBinder(); + if (!local) { + BpBinder *proxy = real->remoteBinder(); + if (proxy == NULL) { + LOGE("null proxy"); + } + const int32_t handle = proxy ? proxy->handle() : 0; + obj.type = BINDER_TYPE_WEAK_HANDLE; + obj.handle = handle; + obj.cookie = NULL; + } else { + obj.type = BINDER_TYPE_WEAK_BINDER; + obj.binder = binder.get_refs(); + obj.cookie = binder.unsafe_get(); + } + return finish_flatten_binder(real, obj, out); + } + + // XXX How to deal? In order to flatten the given binder, + // we need to probe it for information, which requires a primary + // reference... but we don't have one. + // + // The OpenBinder implementation uses a dynamic_cast<> here, + // but we can't do that with the different reference counting + // implementation we are using. + LOGE("Unable to unflatten Binder weak reference!"); + obj.type = BINDER_TYPE_BINDER; + obj.binder = NULL; + obj.cookie = NULL; + return finish_flatten_binder(NULL, obj, out); + + } else { + obj.type = BINDER_TYPE_BINDER; + obj.binder = NULL; + obj.cookie = NULL; + return finish_flatten_binder(NULL, obj, out); + } +} + +inline static status_t finish_unflatten_binder( + BpBinder* proxy, const flat_binder_object& flat, const Parcel& in) +{ + return NO_ERROR; +} + +status_t unflatten_binder(const sp& proc, + const Parcel& in, sp* out) +{ + const flat_binder_object* flat = in.readObject(false); + + if (flat) { + switch (flat->type) { + case BINDER_TYPE_BINDER: + *out = static_cast(flat->cookie); + return finish_unflatten_binder(NULL, *flat, in); + case BINDER_TYPE_HANDLE: + *out = proc->getStrongProxyForHandle(flat->handle); + return finish_unflatten_binder( + static_cast(out->get()), *flat, in); + } + } + return BAD_TYPE; +} + +status_t unflatten_binder(const sp& proc, + const Parcel& in, wp* out) +{ + const flat_binder_object* flat = in.readObject(false); + + if (flat) { + switch (flat->type) { + case BINDER_TYPE_BINDER: + *out = static_cast(flat->cookie); + return finish_unflatten_binder(NULL, *flat, in); + case BINDER_TYPE_WEAK_BINDER: + if (flat->binder != NULL) { + out->set_object_and_refs( + static_cast(flat->cookie), + static_cast(flat->binder)); + } else { + *out = NULL; + } + return finish_unflatten_binder(NULL, *flat, in); + case BINDER_TYPE_HANDLE: + case BINDER_TYPE_WEAK_HANDLE: + *out = proc->getWeakProxyForHandle(flat->handle); + return finish_unflatten_binder( + static_cast(out->unsafe_get()), *flat, in); + } + } + return BAD_TYPE; +} + +// --------------------------------------------------------------------------- + +Parcel::Parcel() +{ + initState(); +} + +Parcel::~Parcel() +{ + freeDataNoInit(); +} + +const uint8_t* Parcel::data() const +{ + return mData; +} + +size_t Parcel::dataSize() const +{ + return (mDataSize > mDataPos ? mDataSize : mDataPos); +} + +size_t Parcel::dataAvail() const +{ + // TODO: decide what to do about the possibility that this can + // report an available-data size that exceeds a Java int's max + // positive value, causing havoc. Fortunately this will only + // happen if someone constructs a Parcel containing more than two + // gigabytes of data, which on typical phone hardware is simply + // not possible. + return dataSize() - dataPosition(); +} + +size_t Parcel::dataPosition() const +{ + return mDataPos; +} + +size_t Parcel::dataCapacity() const +{ + return mDataCapacity; +} + +status_t Parcel::setDataSize(size_t size) +{ + status_t err; + err = continueWrite(size); + if (err == NO_ERROR) { + mDataSize = size; + LOGV("setDataSize Setting data size of %p to %d\n", this, mDataSize); + } + return err; +} + +void Parcel::setDataPosition(size_t pos) const +{ + mDataPos = pos; + mNextObjectHint = 0; +} + +status_t Parcel::setDataCapacity(size_t size) +{ + if (size > mDataSize) return continueWrite(size); + return NO_ERROR; +} + +status_t Parcel::setData(const uint8_t* buffer, size_t len) +{ + status_t err = restartWrite(len); + if (err == NO_ERROR) { + memcpy(const_cast(data()), buffer, len); + mDataSize = len; + mFdsKnown = false; + } + return err; +} + +status_t Parcel::appendFrom(Parcel *parcel, size_t offset, size_t len) +{ + const sp proc(ProcessState::self()); + status_t err; + uint8_t *data = parcel->mData; + size_t *objects = parcel->mObjects; + size_t size = parcel->mObjectsSize; + int startPos = mDataPos; + int firstIndex = -1, lastIndex = -2; + + if (len == 0) { + return NO_ERROR; + } + + // range checks against the source parcel size + if ((offset > parcel->mDataSize) + || (len > parcel->mDataSize) + || (offset + len > parcel->mDataSize)) { + return BAD_VALUE; + } + + // Count objects in range + for (int i = 0; i < (int) size; i++) { + size_t off = objects[i]; + if ((off >= offset) && (off < offset + len)) { + if (firstIndex == -1) { + firstIndex = i; + } + lastIndex = i; + } + } + int numObjects = lastIndex - firstIndex + 1; + + // grow data + err = growData(len); + if (err != NO_ERROR) { + return err; + } + + // append data + memcpy(mData + mDataPos, data + offset, len); + mDataPos += len; + mDataSize += len; + + if (numObjects > 0) { + // grow objects + if (mObjectsCapacity < mObjectsSize + numObjects) { + int newSize = ((mObjectsSize + numObjects)*3)/2; + size_t *objects = + (size_t*)realloc(mObjects, newSize*sizeof(size_t)); + if (objects == (size_t*)0) { + return NO_MEMORY; + } + mObjects = objects; + mObjectsCapacity = newSize; + } + + // append and acquire objects + int idx = mObjectsSize; + for (int i = firstIndex; i <= lastIndex; i++) { + size_t off = objects[i] - offset + startPos; + mObjects[idx++] = off; + mObjectsSize++; + + const flat_binder_object* flat + = reinterpret_cast(mData + off); + acquire_object(proc, *flat, this); + + // take note if the object is a file descriptor + if (flat->type == BINDER_TYPE_FD) { + mHasFds = mFdsKnown = true; + } + } + } + + return NO_ERROR; +} + +bool Parcel::hasFileDescriptors() const +{ + if (!mFdsKnown) { + scanForFds(); + } + return mHasFds; +} + +status_t Parcel::writeInterfaceToken(const String16& interface) +{ + // currently the interface identification token is just its name as a string + return writeString16(interface); +} + +bool Parcel::enforceInterface(const String16& interface) const +{ + String16 str = readString16(); + if (str == interface) { + return true; + } else { + LOGW("**** enforceInterface() expected '%s' but read '%s'\n", + String8(interface).string(), String8(str).string()); + return false; + } +} + +const size_t* Parcel::objects() const +{ + return mObjects; +} + +size_t Parcel::objectsCount() const +{ + return mObjectsSize; +} + +status_t Parcel::errorCheck() const +{ + return mError; +} + +void Parcel::setError(status_t err) +{ + mError = err; +} + +status_t Parcel::finishWrite(size_t len) +{ + //printf("Finish write of %d\n", len); + mDataPos += len; + LOGV("finishWrite Setting data pos of %p to %d\n", this, mDataPos); + if (mDataPos > mDataSize) { + mDataSize = mDataPos; + LOGV("finishWrite Setting data size of %p to %d\n", this, mDataSize); + } + //printf("New pos=%d, size=%d\n", mDataPos, mDataSize); + return NO_ERROR; +} + +status_t Parcel::writeUnpadded(const void* data, size_t len) +{ + size_t end = mDataPos + len; + if (end < mDataPos) { + // integer overflow + return BAD_VALUE; + } + + if (end <= mDataCapacity) { +restart_write: + memcpy(mData+mDataPos, data, len); + return finishWrite(len); + } + + status_t err = growData(len); + if (err == NO_ERROR) goto restart_write; + return err; +} + +status_t Parcel::write(const void* data, size_t len) +{ + void* const d = writeInplace(len); + if (d) { + memcpy(d, data, len); + return NO_ERROR; + } + return mError; +} + +void* Parcel::writeInplace(size_t len) +{ + const size_t padded = PAD_SIZE(len); + + // sanity check for integer overflow + if (mDataPos+padded < mDataPos) { + return NULL; + } + + if ((mDataPos+padded) <= mDataCapacity) { +restart_write: + //printf("Writing %ld bytes, padded to %ld\n", len, padded); + uint8_t* const data = mData+mDataPos; + + // Need to pad at end? + if (padded != len) { +#if BYTE_ORDER == BIG_ENDIAN + static const uint32_t mask[4] = { + 0x00000000, 0xffffff00, 0xffff0000, 0xff000000 + }; +#endif +#if BYTE_ORDER == LITTLE_ENDIAN + static const uint32_t mask[4] = { + 0x00000000, 0x00ffffff, 0x0000ffff, 0x000000ff + }; +#endif + //printf("Applying pad mask: %p to %p\n", (void*)mask[padded-len], + // *reinterpret_cast(data+padded-4)); + *reinterpret_cast(data+padded-4) &= mask[padded-len]; + } + + finishWrite(padded); + return data; + } + + status_t err = growData(padded); + if (err == NO_ERROR) goto restart_write; + return NULL; +} + +status_t Parcel::writeInt32(int32_t val) +{ + if ((mDataPos+sizeof(val)) <= mDataCapacity) { +restart_write: + *reinterpret_cast(mData+mDataPos) = val; + return finishWrite(sizeof(val)); + } + + status_t err = growData(sizeof(val)); + if (err == NO_ERROR) goto restart_write; + return err; +} + +status_t Parcel::writeInt64(int64_t val) +{ + if ((mDataPos+sizeof(val)) <= mDataCapacity) { +restart_write: + *reinterpret_cast(mData+mDataPos) = val; + return finishWrite(sizeof(val)); + } + + status_t err = growData(sizeof(val)); + if (err == NO_ERROR) goto restart_write; + return err; +} + +status_t Parcel::writeFloat(float val) +{ + if ((mDataPos+sizeof(val)) <= mDataCapacity) { +restart_write: + *reinterpret_cast(mData+mDataPos) = val; + return finishWrite(sizeof(val)); + } + + status_t err = growData(sizeof(val)); + if (err == NO_ERROR) goto restart_write; + return err; +} + +status_t Parcel::writeDouble(double val) +{ + if ((mDataPos+sizeof(val)) <= mDataCapacity) { +restart_write: + *reinterpret_cast(mData+mDataPos) = val; + return finishWrite(sizeof(val)); + } + + status_t err = growData(sizeof(val)); + if (err == NO_ERROR) goto restart_write; + return err; +} + +status_t Parcel::writeCString(const char* str) +{ + return write(str, strlen(str)+1); +} + +status_t Parcel::writeString8(const String8& str) +{ + status_t err = writeInt32(str.bytes()); + if (err == NO_ERROR) { + err = write(str.string(), str.bytes()+1); + } + return err; +} + +status_t Parcel::writeString16(const String16& str) +{ + return writeString16(str.string(), str.size()); +} + +status_t Parcel::writeString16(const char16_t* str, size_t len) +{ + if (str == NULL) return writeInt32(-1); + + status_t err = writeInt32(len); + if (err == NO_ERROR) { + len *= sizeof(char16_t); + uint8_t* data = (uint8_t*)writeInplace(len+sizeof(char16_t)); + if (data) { + memcpy(data, str, len); + *reinterpret_cast(data+len) = 0; + return NO_ERROR; + } + err = mError; + } + return err; +} + +status_t Parcel::writeStrongBinder(const sp& val) +{ + return flatten_binder(ProcessState::self(), val, this); +} + +status_t Parcel::writeWeakBinder(const wp& val) +{ + return flatten_binder(ProcessState::self(), val, this); +} + +status_t Parcel::writeNativeHandle(const native_handle& handle) +{ + if (handle.version != sizeof(native_handle)) + return BAD_TYPE; + + status_t err; + err = writeInt32(handle.numFds); + if (err != NO_ERROR) return err; + + err = writeInt32(handle.numInts); + if (err != NO_ERROR) return err; + + for (int i=0 ; err==NO_ERROR && i(mData+mDataPos) = val; + + // Need to write meta-data? + if (nullMetaData || val.binder != NULL) { + mObjects[mObjectsSize] = mDataPos; + acquire_object(ProcessState::self(), val, this); + mObjectsSize++; + } + + // remember if it's a file descriptor + if (val.type == BINDER_TYPE_FD) { + mHasFds = mFdsKnown = true; + } + + return finishWrite(sizeof(flat_binder_object)); + } + + if (!enoughData) { + const status_t err = growData(sizeof(val)); + if (err != NO_ERROR) return err; + } + if (!enoughObjects) { + size_t newSize = ((mObjectsSize+2)*3)/2; + size_t* objects = (size_t*)realloc(mObjects, newSize*sizeof(size_t)); + if (objects == NULL) return NO_MEMORY; + mObjects = objects; + mObjectsCapacity = newSize; + } + + goto restart_write; +} + + +void Parcel::remove(size_t start, size_t amt) +{ + LOG_ALWAYS_FATAL("Parcel::remove() not yet implemented!"); +} + +status_t Parcel::read(void* outData, size_t len) const +{ + if ((mDataPos+PAD_SIZE(len)) >= mDataPos && (mDataPos+PAD_SIZE(len)) <= mDataSize) { + memcpy(outData, mData+mDataPos, len); + mDataPos += PAD_SIZE(len); + LOGV("read Setting data pos of %p to %d\n", this, mDataPos); + return NO_ERROR; + } + return NOT_ENOUGH_DATA; +} + +const void* Parcel::readInplace(size_t len) const +{ + if ((mDataPos+PAD_SIZE(len)) >= mDataPos && (mDataPos+PAD_SIZE(len)) <= mDataSize) { + const void* data = mData+mDataPos; + mDataPos += PAD_SIZE(len); + LOGV("readInplace Setting data pos of %p to %d\n", this, mDataPos); + return data; + } + return NULL; +} + +status_t Parcel::readInt32(int32_t *pArg) const +{ + if ((mDataPos+sizeof(int32_t)) <= mDataSize) { + const void* data = mData+mDataPos; + mDataPos += sizeof(int32_t); + *pArg = *reinterpret_cast(data); + return NO_ERROR; + } else { + return NOT_ENOUGH_DATA; + } +} + +int32_t Parcel::readInt32() const +{ + if ((mDataPos+sizeof(int32_t)) <= mDataSize) { + const void* data = mData+mDataPos; + mDataPos += sizeof(int32_t); + LOGV("readInt32 Setting data pos of %p to %d\n", this, mDataPos); + return *reinterpret_cast(data); + } + return 0; +} + + +status_t Parcel::readInt64(int64_t *pArg) const +{ + if ((mDataPos+sizeof(int64_t)) <= mDataSize) { + const void* data = mData+mDataPos; + mDataPos += sizeof(int64_t); + *pArg = *reinterpret_cast(data); + LOGV("readInt64 Setting data pos of %p to %d\n", this, mDataPos); + return NO_ERROR; + } else { + return NOT_ENOUGH_DATA; + } +} + + +int64_t Parcel::readInt64() const +{ + if ((mDataPos+sizeof(int64_t)) <= mDataSize) { + const void* data = mData+mDataPos; + mDataPos += sizeof(int64_t); + LOGV("readInt64 Setting data pos of %p to %d\n", this, mDataPos); + return *reinterpret_cast(data); + } + return 0; +} + +status_t Parcel::readFloat(float *pArg) const +{ + if ((mDataPos+sizeof(float)) <= mDataSize) { + const void* data = mData+mDataPos; + mDataPos += sizeof(float); + LOGV("readFloat Setting data pos of %p to %d\n", this, mDataPos); + *pArg = *reinterpret_cast(data); + return NO_ERROR; + } else { + return NOT_ENOUGH_DATA; + } +} + + +float Parcel::readFloat() const +{ + if ((mDataPos+sizeof(float)) <= mDataSize) { + const void* data = mData+mDataPos; + mDataPos += sizeof(float); + LOGV("readFloat Setting data pos of %p to %d\n", this, mDataPos); + return *reinterpret_cast(data); + } + return 0; +} + +status_t Parcel::readDouble(double *pArg) const +{ + if ((mDataPos+sizeof(double)) <= mDataSize) { + const void* data = mData+mDataPos; + mDataPos += sizeof(double); + LOGV("readDouble Setting data pos of %p to %d\n", this, mDataPos); + *pArg = *reinterpret_cast(data); + return NO_ERROR; + } else { + return NOT_ENOUGH_DATA; + } +} + + +double Parcel::readDouble() const +{ + if ((mDataPos+sizeof(double)) <= mDataSize) { + const void* data = mData+mDataPos; + mDataPos += sizeof(double); + LOGV("readDouble Setting data pos of %p to %d\n", this, mDataPos); + return *reinterpret_cast(data); + } + return 0; +} + + +const char* Parcel::readCString() const +{ + const size_t avail = mDataSize-mDataPos; + if (avail > 0) { + const char* str = reinterpret_cast(mData+mDataPos); + // is the string's trailing NUL within the parcel's valid bounds? + const char* eos = reinterpret_cast(memchr(str, 0, avail)); + if (eos) { + const size_t len = eos - str; + mDataPos += PAD_SIZE(len+1); + LOGV("readCString Setting data pos of %p to %d\n", this, mDataPos); + return str; + } + } + return NULL; +} + +String8 Parcel::readString8() const +{ + int32_t size = readInt32(); + // watch for potential int overflow adding 1 for trailing NUL + if (size > 0 && size < INT32_MAX) { + const char* str = (const char*)readInplace(size+1); + if (str) return String8(str, size); + } + return String8(); +} + +String16 Parcel::readString16() const +{ + size_t len; + const char16_t* str = readString16Inplace(&len); + if (str) return String16(str, len); + LOGE("Reading a NULL string not supported here."); + return String16(); +} + +const char16_t* Parcel::readString16Inplace(size_t* outLen) const +{ + int32_t size = readInt32(); + // watch for potential int overflow from size+1 + if (size >= 0 && size < INT32_MAX) { + *outLen = size; + const char16_t* str = (const char16_t*)readInplace((size+1)*sizeof(char16_t)); + if (str != NULL) { + return str; + } + } + *outLen = 0; + return NULL; +} + +sp Parcel::readStrongBinder() const +{ + sp val; + unflatten_binder(ProcessState::self(), *this, &val); + return val; +} + +wp Parcel::readWeakBinder() const +{ + wp val; + unflatten_binder(ProcessState::self(), *this, &val); + return val; +} + + +native_handle* Parcel::readNativeHandle(native_handle* (*alloc)(void*, int, int), void* cookie) const +{ + int numFds, numInts; + status_t err; + err = readInt32(&numFds); + if (err != NO_ERROR) return 0; + err = readInt32(&numInts); + if (err != NO_ERROR) return 0; + + native_handle* h; + if (alloc == 0) { + size_t size = sizeof(native_handle) + sizeof(int)*(numFds + numInts); + h = (native_handle*)malloc(size); + h->version = sizeof(native_handle); + h->numFds = numFds; + h->numInts = numInts; + } else { + h = alloc(cookie, numFds, numInts); + if (h->version != sizeof(native_handle)) { + return 0; + } + } + + for (int i=0 ; err==NO_ERROR && idata[i] = dup(readFileDescriptor()); + if (h->data[i] < 0) err = BAD_VALUE; + } + + err = read(h->data + numFds, sizeof(int)*numInts); + + if (err != NO_ERROR) { + if (alloc == 0) { + free(h); + } + h = 0; + } + return h; +} + + +int Parcel::readFileDescriptor() const +{ + const flat_binder_object* flat = readObject(true); + if (flat) { + switch (flat->type) { + case BINDER_TYPE_FD: + //LOGI("Returning file descriptor %ld from parcel %p\n", flat->handle, this); + return flat->handle; + } + } + return BAD_TYPE; +} + +const flat_binder_object* Parcel::readObject(bool nullMetaData) const +{ + const size_t DPOS = mDataPos; + if ((DPOS+sizeof(flat_binder_object)) <= mDataSize) { + const flat_binder_object* obj + = reinterpret_cast(mData+DPOS); + mDataPos = DPOS + sizeof(flat_binder_object); + if (!nullMetaData && (obj->cookie == NULL && obj->binder == NULL)) { + // When transferring a NULL object, we don't write it into + // the object list, so we don't want to check for it when + // reading. + LOGV("readObject Setting data pos of %p to %d\n", this, mDataPos); + return obj; + } + + // Ensure that this object is valid... + size_t* const OBJS = mObjects; + const size_t N = mObjectsSize; + size_t opos = mNextObjectHint; + + if (N > 0) { + LOGV("Parcel %p looking for obj at %d, hint=%d\n", + this, DPOS, opos); + + // Start at the current hint position, looking for an object at + // the current data position. + if (opos < N) { + while (opos < (N-1) && OBJS[opos] < DPOS) { + opos++; + } + } else { + opos = N-1; + } + if (OBJS[opos] == DPOS) { + // Found it! + LOGV("Parcel found obj %d at index %d with forward search", + this, DPOS, opos); + mNextObjectHint = opos+1; + LOGV("readObject Setting data pos of %p to %d\n", this, mDataPos); + return obj; + } + + // Look backwards for it... + while (opos > 0 && OBJS[opos] > DPOS) { + opos--; + } + if (OBJS[opos] == DPOS) { + // Found it! + LOGV("Parcel found obj %d at index %d with backward search", + this, DPOS, opos); + mNextObjectHint = opos+1; + LOGV("readObject Setting data pos of %p to %d\n", this, mDataPos); + return obj; + } + } + LOGW("Attempt to read object from Parcel %p at offset %d that is not in the object list", + this, DPOS); + } + return NULL; +} + +void Parcel::closeFileDescriptors() +{ + size_t i = mObjectsSize; + if (i > 0) { + //LOGI("Closing file descriptors for %d objects...", mObjectsSize); + } + while (i > 0) { + i--; + const flat_binder_object* flat + = reinterpret_cast(mData+mObjects[i]); + if (flat->type == BINDER_TYPE_FD) { + //LOGI("Closing fd: %ld\n", flat->handle); + close(flat->handle); + } + } +} + +const uint8_t* Parcel::ipcData() const +{ + return mData; +} + +size_t Parcel::ipcDataSize() const +{ + return (mDataSize > mDataPos ? mDataSize : mDataPos); +} + +const size_t* Parcel::ipcObjects() const +{ + return mObjects; +} + +size_t Parcel::ipcObjectsCount() const +{ + return mObjectsSize; +} + +void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize, + const size_t* objects, size_t objectsCount, release_func relFunc, void* relCookie) +{ + freeDataNoInit(); + mError = NO_ERROR; + mData = const_cast(data); + mDataSize = mDataCapacity = dataSize; + //LOGI("setDataReference Setting data size of %p to %lu (pid=%d)\n", this, mDataSize, getpid()); + mDataPos = 0; + LOGV("setDataReference Setting data pos of %p to %d\n", this, mDataPos); + mObjects = const_cast(objects); + mObjectsSize = mObjectsCapacity = objectsCount; + mNextObjectHint = 0; + mOwner = relFunc; + mOwnerCookie = relCookie; + scanForFds(); +} + +void Parcel::print(TextOutput& to, uint32_t flags) const +{ + to << "Parcel("; + + if (errorCheck() != NO_ERROR) { + const status_t err = errorCheck(); + to << "Error: " << (void*)err << " \"" << strerror(-err) << "\""; + } else if (dataSize() > 0) { + const uint8_t* DATA = data(); + to << indent << HexDump(DATA, dataSize()) << dedent; + const size_t* OBJS = objects(); + const size_t N = objectsCount(); + for (size_t i=0; i(DATA+OBJS[i]); + to << endl << "Object #" << i << " @ " << (void*)OBJS[i] << ": " + << TypeCode(flat->type & 0x7f7f7f00) + << " = " << flat->binder; + } + } else { + to << "NULL"; + } + + to << ")"; +} + +void Parcel::releaseObjects() +{ + const sp proc(ProcessState::self()); + size_t i = mObjectsSize; + uint8_t* const data = mData; + size_t* const objects = mObjects; + while (i > 0) { + i--; + const flat_binder_object* flat + = reinterpret_cast(data+objects[i]); + release_object(proc, *flat, this); + } +} + +void Parcel::acquireObjects() +{ + const sp proc(ProcessState::self()); + size_t i = mObjectsSize; + uint8_t* const data = mData; + size_t* const objects = mObjects; + while (i > 0) { + i--; + const flat_binder_object* flat + = reinterpret_cast(data+objects[i]); + acquire_object(proc, *flat, this); + } +} + +void Parcel::freeData() +{ + freeDataNoInit(); + initState(); +} + +void Parcel::freeDataNoInit() +{ + if (mOwner) { + //LOGI("Freeing data ref of %p (pid=%d)\n", this, getpid()); + mOwner(this, mData, mDataSize, mObjects, mObjectsSize, mOwnerCookie); + } else { + releaseObjects(); + if (mData) free(mData); + if (mObjects) free(mObjects); + } +} + +status_t Parcel::growData(size_t len) +{ + size_t newSize = ((mDataSize+len)*3)/2; + return (newSize <= mDataSize) + ? (status_t) NO_MEMORY + : continueWrite(newSize); +} + +status_t Parcel::restartWrite(size_t desired) +{ + if (mOwner) { + freeData(); + return continueWrite(desired); + } + + uint8_t* data = (uint8_t*)realloc(mData, desired); + if (!data && desired > mDataCapacity) { + mError = NO_MEMORY; + return NO_MEMORY; + } + + releaseObjects(); + + if (data) { + mData = data; + mDataCapacity = desired; + } + + mDataSize = mDataPos = 0; + LOGV("restartWrite Setting data size of %p to %d\n", this, mDataSize); + LOGV("restartWrite Setting data pos of %p to %d\n", this, mDataPos); + + free(mObjects); + mObjects = NULL; + mObjectsSize = mObjectsCapacity = 0; + mNextObjectHint = 0; + mHasFds = false; + mFdsKnown = true; + + return NO_ERROR; +} + +status_t Parcel::continueWrite(size_t desired) +{ + // If shrinking, first adjust for any objects that appear + // after the new data size. + size_t objectsSize = mObjectsSize; + if (desired < mDataSize) { + if (desired == 0) { + objectsSize = 0; + } else { + while (objectsSize > 0) { + if (mObjects[objectsSize-1] < desired) + break; + objectsSize--; + } + } + } + + if (mOwner) { + // If the size is going to zero, just release the owner's data. + if (desired == 0) { + freeData(); + return NO_ERROR; + } + + // If there is a different owner, we need to take + // posession. + uint8_t* data = (uint8_t*)malloc(desired); + if (!data) { + mError = NO_MEMORY; + return NO_MEMORY; + } + size_t* objects = NULL; + + if (objectsSize) { + objects = (size_t*)malloc(objectsSize*sizeof(size_t)); + if (!objects) { + mError = NO_MEMORY; + return NO_MEMORY; + } + + // Little hack to only acquire references on objects + // we will be keeping. + size_t oldObjectsSize = mObjectsSize; + mObjectsSize = objectsSize; + acquireObjects(); + mObjectsSize = oldObjectsSize; + } + + if (mData) { + memcpy(data, mData, mDataSize < desired ? mDataSize : desired); + } + if (objects && mObjects) { + memcpy(objects, mObjects, objectsSize*sizeof(size_t)); + } + //LOGI("Freeing data ref of %p (pid=%d)\n", this, getpid()); + mOwner(this, mData, mDataSize, mObjects, mObjectsSize, mOwnerCookie); + mOwner = NULL; + + mData = data; + mObjects = objects; + mDataSize = (mDataSize < desired) ? mDataSize : desired; + LOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize); + mDataCapacity = desired; + mObjectsSize = mObjectsCapacity = objectsSize; + mNextObjectHint = 0; + + } else if (mData) { + if (objectsSize < mObjectsSize) { + // Need to release refs on any objects we are dropping. + const sp proc(ProcessState::self()); + for (size_t i=objectsSize; i(mData+mObjects[i]); + if (flat->type == BINDER_TYPE_FD) { + // will need to rescan because we may have lopped off the only FDs + mFdsKnown = false; + } + release_object(proc, *flat, this); + } + size_t* objects = + (size_t*)realloc(mObjects, objectsSize*sizeof(size_t)); + if (objects) { + mObjects = objects; + } + mObjectsSize = objectsSize; + mNextObjectHint = 0; + } + + // We own the data, so we can just do a realloc(). + if (desired > mDataCapacity) { + uint8_t* data = (uint8_t*)realloc(mData, desired); + if (data) { + mData = data; + mDataCapacity = desired; + } else if (desired > mDataCapacity) { + mError = NO_MEMORY; + return NO_MEMORY; + } + } else { + mDataSize = desired; + LOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize); + if (mDataPos > desired) { + mDataPos = desired; + LOGV("continueWrite Setting data pos of %p to %d\n", this, mDataPos); + } + } + + } else { + // This is the first data. Easy! + uint8_t* data = (uint8_t*)malloc(desired); + if (!data) { + mError = NO_MEMORY; + return NO_MEMORY; + } + + if(!(mDataCapacity == 0 && mObjects == NULL + && mObjectsCapacity == 0)) { + LOGE("continueWrite: %d/%p/%d/%d", mDataCapacity, mObjects, mObjectsCapacity, desired); + } + + mData = data; + mDataSize = mDataPos = 0; + LOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize); + LOGV("continueWrite Setting data pos of %p to %d\n", this, mDataPos); + mDataCapacity = desired; + } + + return NO_ERROR; +} + +void Parcel::initState() +{ + mError = NO_ERROR; + mData = 0; + mDataSize = 0; + mDataCapacity = 0; + mDataPos = 0; + LOGV("initState Setting data size of %p to %d\n", this, mDataSize); + LOGV("initState Setting data pos of %p to %d\n", this, mDataPos); + mObjects = NULL; + mObjectsSize = 0; + mObjectsCapacity = 0; + mNextObjectHint = 0; + mHasFds = false; + mFdsKnown = true; + mOwner = NULL; +} + +void Parcel::scanForFds() const +{ + bool hasFds = false; + for (size_t i=0; i(mData + mObjects[i]); + if (flat->type == BINDER_TYPE_FD) { + hasFds = true; + break; + } + } + mHasFds = hasFds; + mFdsKnown = true; +} + +}; // namespace android diff --git a/libs/utils/Pipe.cpp b/libs/utils/Pipe.cpp new file mode 100644 index 000000000..613906bed --- /dev/null +++ b/libs/utils/Pipe.cpp @@ -0,0 +1,465 @@ +/* + * 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. + */ + +// +// Unidirectional pipe. +// + +#include +#include + +#if defined(HAVE_WIN32_IPC) +# include +#else +# include +# include +# include +#endif + +#include +#include +#include +#include + +using namespace android; + +const unsigned long kInvalidHandle = (unsigned long) -1; + + +/* + * Constructor. Do little. + */ +Pipe::Pipe(void) + : mReadNonBlocking(false), mReadHandle(kInvalidHandle), + mWriteHandle(kInvalidHandle) +{ +} + +/* + * Destructor. Use the system-appropriate close call. + */ +Pipe::~Pipe(void) +{ +#if defined(HAVE_WIN32_IPC) + if (mReadHandle != kInvalidHandle) { + if (!CloseHandle((HANDLE)mReadHandle)) + LOG(LOG_WARN, "pipe", "failed closing read handle (%ld)\n", + mReadHandle); + } + if (mWriteHandle != kInvalidHandle) { + FlushFileBuffers((HANDLE)mWriteHandle); + if (!CloseHandle((HANDLE)mWriteHandle)) + LOG(LOG_WARN, "pipe", "failed closing write handle (%ld)\n", + mWriteHandle); + } +#else + if (mReadHandle != kInvalidHandle) { + if (close((int) mReadHandle) != 0) + LOG(LOG_WARN, "pipe", "failed closing read fd (%d)\n", + (int) mReadHandle); + } + if (mWriteHandle != kInvalidHandle) { + if (close((int) mWriteHandle) != 0) + LOG(LOG_WARN, "pipe", "failed closing write fd (%d)\n", + (int) mWriteHandle); + } +#endif +} + +/* + * Create the pipe. + * + * Use the POSIX stuff for everything but Windows. + */ +bool Pipe::create(void) +{ + assert(mReadHandle == kInvalidHandle); + assert(mWriteHandle == kInvalidHandle); + +#if defined(HAVE_WIN32_IPC) + /* we use this across processes, so they need to be inheritable */ + HANDLE handles[2]; + SECURITY_ATTRIBUTES saAttr; + + saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + saAttr.bInheritHandle = TRUE; + saAttr.lpSecurityDescriptor = NULL; + + if (!CreatePipe(&handles[0], &handles[1], &saAttr, 0)) { + LOG(LOG_ERROR, "pipe", "unable to create pipe\n"); + return false; + } + mReadHandle = (unsigned long) handles[0]; + mWriteHandle = (unsigned long) handles[1]; + return true; +#else + int fds[2]; + + if (pipe(fds) != 0) { + LOG(LOG_ERROR, "pipe", "unable to create pipe\n"); + return false; + } + mReadHandle = fds[0]; + mWriteHandle = fds[1]; + return true; +#endif +} + +/* + * Create a "half pipe". Please, no Segway riding. + */ +bool Pipe::createReader(unsigned long handle) +{ + mReadHandle = handle; + assert(mWriteHandle == kInvalidHandle); + return true; +} + +/* + * Create a "half pipe" for writing. + */ +bool Pipe::createWriter(unsigned long handle) +{ + mWriteHandle = handle; + assert(mReadHandle == kInvalidHandle); + return true; +} + +/* + * Return "true" if create() has been called successfully. + */ +bool Pipe::isCreated(void) +{ + // one or the other should be open + return (mReadHandle != kInvalidHandle || mWriteHandle != kInvalidHandle); +} + + +/* + * Read data from the pipe. + * + * For Linux and Darwin, just call read(). For Windows, implement + * non-blocking reads by calling PeekNamedPipe first. + */ +int Pipe::read(void* buf, int count) +{ + assert(mReadHandle != kInvalidHandle); + +#if defined(HAVE_WIN32_IPC) + DWORD totalBytesAvail = count; + DWORD bytesRead; + + if (mReadNonBlocking) { + // use PeekNamedPipe to adjust read count expectations + if (!PeekNamedPipe((HANDLE) mReadHandle, NULL, 0, NULL, + &totalBytesAvail, NULL)) + { + LOG(LOG_ERROR, "pipe", "PeekNamedPipe failed\n"); + return -1; + } + + if (totalBytesAvail == 0) + return 0; + } + + if (!ReadFile((HANDLE) mReadHandle, buf, totalBytesAvail, &bytesRead, + NULL)) + { + DWORD err = GetLastError(); + if (err == ERROR_HANDLE_EOF || err == ERROR_BROKEN_PIPE) + return 0; + LOG(LOG_ERROR, "pipe", "ReadFile failed (err=%ld)\n", err); + return -1; + } + + return (int) bytesRead; +#else + int cc; + cc = ::read(mReadHandle, buf, count); + if (cc < 0 && errno == EAGAIN) + return 0; + return cc; +#endif +} + +/* + * Write data to the pipe. + * + * POSIX systems are trivial, Windows uses a different call and doesn't + * handle non-blocking writes. + * + * If we add non-blocking support here, we probably want to make it an + * all-or-nothing write. + * + * DO NOT use LOG() here, we could be writing a log message. + */ +int Pipe::write(const void* buf, int count) +{ + assert(mWriteHandle != kInvalidHandle); + +#if defined(HAVE_WIN32_IPC) + DWORD bytesWritten; + + if (mWriteNonBlocking) { + // BUG: can't use PeekNamedPipe() to get the amount of space + // left. Looks like we need to use "overlapped I/O" functions. + // I just don't care that much. + } + + if (!WriteFile((HANDLE) mWriteHandle, buf, count, &bytesWritten, NULL)) { + // can't LOG, use stderr + fprintf(stderr, "WriteFile failed (err=%ld)\n", GetLastError()); + return -1; + } + + return (int) bytesWritten; +#else + int cc; + cc = ::write(mWriteHandle, buf, count); + if (cc < 0 && errno == EAGAIN) + return 0; + return cc; +#endif +} + +/* + * Figure out if there is data available on the read fd. + * + * We return "true" on error because we want the caller to try to read + * from the pipe. They'll notice the read failure and do something + * appropriate. + */ +bool Pipe::readReady(void) +{ + assert(mReadHandle != kInvalidHandle); + +#if defined(HAVE_WIN32_IPC) + DWORD totalBytesAvail; + + if (!PeekNamedPipe((HANDLE) mReadHandle, NULL, 0, NULL, + &totalBytesAvail, NULL)) + { + LOG(LOG_ERROR, "pipe", "PeekNamedPipe failed\n"); + return true; + } + + return (totalBytesAvail != 0); +#else + errno = 0; + fd_set readfds; + struct timeval tv = { 0, 0 }; + int cc; + + FD_ZERO(&readfds); + FD_SET(mReadHandle, &readfds); + + cc = select(mReadHandle+1, &readfds, NULL, NULL, &tv); + if (cc < 0) { + LOG(LOG_ERROR, "pipe", "select() failed\n"); + return true; + } else if (cc == 0) { + /* timed out, nothing available */ + return false; + } else if (cc == 1) { + /* our fd is ready */ + return true; + } else { + LOG(LOG_ERROR, "pipe", "HUH? select() returned > 1\n"); + return true; + } +#endif +} + +/* + * Enable or disable non-blocking mode for the read descriptor. + * + * NOTE: the calls succeed under Mac OS X, but the pipe doesn't appear to + * actually be in non-blocking mode. If this matters -- i.e. you're not + * using a select() call -- put a call to readReady() in front of the + * ::read() call, with a PIPE_NONBLOCK_BROKEN #ifdef in the Makefile for + * Darwin. + */ +bool Pipe::setReadNonBlocking(bool val) +{ + assert(mReadHandle != kInvalidHandle); + +#if defined(HAVE_WIN32_IPC) + // nothing to do +#else + int flags; + + if (fcntl(mReadHandle, F_GETFL, &flags) == -1) { + LOG(LOG_ERROR, "pipe", "couldn't get flags for pipe read fd\n"); + return false; + } + if (val) + flags |= O_NONBLOCK; + else + flags &= ~(O_NONBLOCK); + if (fcntl(mReadHandle, F_SETFL, &flags) == -1) { + LOG(LOG_ERROR, "pipe", "couldn't set flags for pipe read fd\n"); + return false; + } +#endif + + mReadNonBlocking = val; + return true; +} + +/* + * Enable or disable non-blocking mode for the write descriptor. + * + * As with setReadNonBlocking(), this does not work on the Mac. + */ +bool Pipe::setWriteNonBlocking(bool val) +{ + assert(mWriteHandle != kInvalidHandle); + +#if defined(HAVE_WIN32_IPC) + // nothing to do +#else + int flags; + + if (fcntl(mWriteHandle, F_GETFL, &flags) == -1) { + LOG(LOG_WARN, "pipe", + "Warning: couldn't get flags for pipe write fd (errno=%d)\n", + errno); + return false; + } + if (val) + flags |= O_NONBLOCK; + else + flags &= ~(O_NONBLOCK); + if (fcntl(mWriteHandle, F_SETFL, &flags) == -1) { + LOG(LOG_WARN, "pipe", + "Warning: couldn't set flags for pipe write fd (errno=%d)\n", + errno); + return false; + } +#endif + + mWriteNonBlocking = val; + return true; +} + +/* + * Specify whether a file descriptor can be inherited by a child process. + * Under Linux this means setting the close-on-exec flag, under Windows + * this is SetHandleInformation(HANDLE_FLAG_INHERIT). + */ +bool Pipe::disallowReadInherit(void) +{ + if (mReadHandle == kInvalidHandle) + return false; + +#if defined(HAVE_WIN32_IPC) + if (SetHandleInformation((HANDLE) mReadHandle, HANDLE_FLAG_INHERIT, 0) == 0) + return false; +#else + if (fcntl((int) mReadHandle, F_SETFD, FD_CLOEXEC) != 0) + return false; +#endif + return true; +} +bool Pipe::disallowWriteInherit(void) +{ + if (mWriteHandle == kInvalidHandle) + return false; + +#if defined(HAVE_WIN32_IPC) + if (SetHandleInformation((HANDLE) mWriteHandle, HANDLE_FLAG_INHERIT, 0) == 0) + return false; +#else + if (fcntl((int) mWriteHandle, F_SETFD, FD_CLOEXEC) != 0) + return false; +#endif + return true; +} + +/* + * Close read descriptor. + */ +bool Pipe::closeRead(void) +{ + if (mReadHandle == kInvalidHandle) + return false; + +#if defined(HAVE_WIN32_IPC) + if (mReadHandle != kInvalidHandle) { + if (!CloseHandle((HANDLE)mReadHandle)) { + LOG(LOG_WARN, "pipe", "failed closing read handle\n"); + return false; + } + } +#else + if (mReadHandle != kInvalidHandle) { + if (close((int) mReadHandle) != 0) { + LOG(LOG_WARN, "pipe", "failed closing read fd\n"); + return false; + } + } +#endif + mReadHandle = kInvalidHandle; + return true; +} + +/* + * Close write descriptor. + */ +bool Pipe::closeWrite(void) +{ + if (mWriteHandle == kInvalidHandle) + return false; + +#if defined(HAVE_WIN32_IPC) + if (mWriteHandle != kInvalidHandle) { + if (!CloseHandle((HANDLE)mWriteHandle)) { + LOG(LOG_WARN, "pipe", "failed closing write handle\n"); + return false; + } + } +#else + if (mWriteHandle != kInvalidHandle) { + if (close((int) mWriteHandle) != 0) { + LOG(LOG_WARN, "pipe", "failed closing write fd\n"); + return false; + } + } +#endif + mWriteHandle = kInvalidHandle; + return true; +} + +/* + * Get the read handle. + */ +unsigned long Pipe::getReadHandle(void) +{ + assert(mReadHandle != kInvalidHandle); + + return mReadHandle; +} + +/* + * Get the write handle. + */ +unsigned long Pipe::getWriteHandle(void) +{ + assert(mWriteHandle != kInvalidHandle); + + return mWriteHandle; +} + diff --git a/libs/utils/ProcessState.cpp b/libs/utils/ProcessState.cpp new file mode 100644 index 000000000..4567df60f --- /dev/null +++ b/libs/utils/ProcessState.cpp @@ -0,0 +1,398 @@ +/* + * 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 "ProcessState" + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define BINDER_VM_SIZE (1*1024*1024) + +static bool gSingleProcess = false; + + +// --------------------------------------------------------------------------- + +namespace android { + +// Global variables +int mArgC; +const char* const* mArgV; +int mArgLen; + +class PoolThread : public Thread +{ +public: + PoolThread(bool isMain) + : mIsMain(isMain) + { + } + +protected: + virtual bool threadLoop() + { + IPCThreadState::self()->joinThreadPool(mIsMain); + return false; + } + + const bool mIsMain; +}; + +sp ProcessState::self() +{ + if (gProcess != NULL) return gProcess; + + AutoMutex _l(gProcessMutex); + if (gProcess == NULL) gProcess = new ProcessState; + return gProcess; +} + +void ProcessState::setSingleProcess(bool singleProcess) +{ + gSingleProcess = singleProcess; +} + + +void ProcessState::setContextObject(const sp& object) +{ + setContextObject(object, String16("default")); +} + +sp ProcessState::getContextObject(const sp& caller) +{ + if (supportsProcesses()) { + return getStrongProxyForHandle(0); + } else { + return getContextObject(String16("default"), caller); + } +} + +void ProcessState::setContextObject(const sp& object, const String16& name) +{ + AutoMutex _l(mLock); + mContexts.add(name, object); +} + +sp ProcessState::getContextObject(const String16& name, const sp& caller) +{ + mLock.lock(); + sp object( + mContexts.indexOfKey(name) >= 0 ? mContexts.valueFor(name) : NULL); + mLock.unlock(); + + //printf("Getting context object %s for %p\n", String8(name).string(), caller.get()); + + if (object != NULL) return object; + + // Don't attempt to retrieve contexts if we manage them + if (mManagesContexts) { + LOGE("getContextObject(%s) failed, but we manage the contexts!\n", + String8(name).string()); + return NULL; + } + + IPCThreadState* ipc = IPCThreadState::self(); + { + Parcel data, reply; + // no interface token on this magic transaction + data.writeString16(name); + data.writeStrongBinder(caller); + status_t result = ipc->transact(0 /*magic*/, 0, data, &reply, 0); + if (result == NO_ERROR) { + object = reply.readStrongBinder(); + } + } + + ipc->flushCommands(); + + if (object != NULL) setContextObject(object, name); + return object; +} + +bool ProcessState::supportsProcesses() const +{ + return mDriverFD >= 0; +} + +void ProcessState::startThreadPool() +{ + AutoMutex _l(mLock); + if (!mThreadPoolStarted) { + mThreadPoolStarted = true; + spawnPooledThread(true); + } +} + +bool ProcessState::isContextManager(void) const +{ + return mManagesContexts; +} + +bool ProcessState::becomeContextManager(context_check_func checkFunc, void* userData) +{ + if (!mManagesContexts) { + AutoMutex _l(mLock); + mBinderContextCheckFunc = checkFunc; + mBinderContextUserData = userData; + if (mDriverFD >= 0) { + int dummy = 0; +#if defined(HAVE_ANDROID_OS) + status_t result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &dummy); +#else + status_t result = INVALID_OPERATION; +#endif + if (result == 0) { + mManagesContexts = true; + } else if (result == -1) { + mBinderContextCheckFunc = NULL; + mBinderContextUserData = NULL; + LOGE("Binder ioctl to become context manager failed: %s\n", strerror(errno)); + } + } else { + // If there is no driver, our only world is the local + // process so we can always become the context manager there. + mManagesContexts = true; + } + } + return mManagesContexts; +} + +ProcessState::handle_entry* ProcessState::lookupHandleLocked(int32_t handle) +{ + const size_t N=mHandleToObject.size(); + if (N <= (size_t)handle) { + handle_entry e; + e.binder = NULL; + e.refs = NULL; + status_t err = mHandleToObject.insertAt(e, N, handle+1-N); + if (err < NO_ERROR) return NULL; + } + return &mHandleToObject.editItemAt(handle); +} + +sp ProcessState::getStrongProxyForHandle(int32_t handle) +{ + sp result; + + AutoMutex _l(mLock); + + handle_entry* e = lookupHandleLocked(handle); + + if (e != NULL) { + // We need to create a new BpBinder if there isn't currently one, OR we + // are unable to acquire a weak reference on this current one. See comment + // in getWeakProxyForHandle() for more info about this. + IBinder* b = e->binder; + if (b == NULL || !e->refs->attemptIncWeak(this)) { + b = new BpBinder(handle); + e->binder = b; + if (b) e->refs = b->getWeakRefs(); + result = b; + } else { + // This little bit of nastyness is to allow us to add a primary + // reference to the remote proxy when this team doesn't have one + // but another team is sending the handle to us. + result.force_set(b); + e->refs->decWeak(this); + } + } + + return result; +} + +wp ProcessState::getWeakProxyForHandle(int32_t handle) +{ + wp result; + + AutoMutex _l(mLock); + + handle_entry* e = lookupHandleLocked(handle); + + if (e != NULL) { + // We need to create a new BpBinder if there isn't currently one, OR we + // are unable to acquire a weak reference on this current one. The + // attemptIncWeak() is safe because we know the BpBinder destructor will always + // call expungeHandle(), which acquires the same lock we are holding now. + // We need to do this because there is a race condition between someone + // releasing a reference on this BpBinder, and a new reference on its handle + // arriving from the driver. + IBinder* b = e->binder; + if (b == NULL || !e->refs->attemptIncWeak(this)) { + b = new BpBinder(handle); + result = b; + e->binder = b; + if (b) e->refs = b->getWeakRefs(); + } else { + result = b; + e->refs->decWeak(this); + } + } + + return result; +} + +void ProcessState::expungeHandle(int32_t handle, IBinder* binder) +{ + AutoMutex _l(mLock); + + handle_entry* e = lookupHandleLocked(handle); + + // This handle may have already been replaced with a new BpBinder + // (if someone failed the AttemptIncWeak() above); we don't want + // to overwrite it. + if (e && e->binder == binder) e->binder = NULL; +} + +void ProcessState::setArgs(int argc, const char* const argv[]) +{ + mArgC = argc; + mArgV = (const char **)argv; + + mArgLen = 0; + for (int i=0; i t = new PoolThread(isMain); + t->run(buf); + } +} + +static int open_driver() +{ + if (gSingleProcess) { + return -1; + } + + int fd = open("/dev/binder", O_RDWR); + if (fd >= 0) { + fcntl(fd, F_SETFD, FD_CLOEXEC); + int vers; +#if defined(HAVE_ANDROID_OS) + status_t result = ioctl(fd, BINDER_VERSION, &vers); +#else + status_t result = -1; + errno = EPERM; +#endif + if (result == -1) { + LOGE("Binder ioctl to obtain version failed: %s", strerror(errno)); + close(fd); + fd = -1; + } + if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) { + LOGE("Binder driver protocol does not match user space protocol!"); + close(fd); + fd = -1; + } +#if defined(HAVE_ANDROID_OS) + size_t maxThreads = 15; + result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads); + if (result == -1) { + LOGE("Binder ioctl to set max threads failed: %s", strerror(errno)); + } +#endif + + } else { + LOGW("Opening '/dev/binder' failed: %s\n", strerror(errno)); + } + return fd; +} + +ProcessState::ProcessState() + : mDriverFD(open_driver()) + , mVMStart(MAP_FAILED) + , mManagesContexts(false) + , mBinderContextCheckFunc(NULL) + , mBinderContextUserData(NULL) + , mThreadPoolStarted(false) + , mThreadPoolSeq(1) +{ + if (mDriverFD >= 0) { + // XXX Ideally, there should be a specific define for whether we + // have mmap (or whether we could possibly have the kernel module + // availabla). +#if !defined(HAVE_WIN32_IPC) + // mmap the binder, providing a chunk of virtual address space to receive transactions. + mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0); + if (mVMStart == MAP_FAILED) { + // *sigh* + LOGE("Using /dev/binder failed: unable to mmap transaction memory.\n"); + close(mDriverFD); + mDriverFD = -1; + } +#else + mDriverFD = -1; +#endif + } + if (mDriverFD < 0) { + // Need to run without the driver, starting our own thread pool. + } +} + +ProcessState::~ProcessState() +{ +} + +}; // namespace android diff --git a/libs/utils/README b/libs/utils/README new file mode 100644 index 000000000..36a706d5c --- /dev/null +++ b/libs/utils/README @@ -0,0 +1,14 @@ +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. + diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp new file mode 100644 index 000000000..0bd1af4eb --- /dev/null +++ b/libs/utils/RefBase.cpp @@ -0,0 +1,534 @@ +/* + * 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" + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +// compile with refcounting debugging enabled +#define DEBUG_REFS 0 +#define DEBUG_REFS_ENABLED_BY_DEFAULT 1 +#define DEBUG_REFS_CALLSTACK_ENABLED 1 + +// 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 addWeakRef(const void* /*id*/) { } + void removeWeakRef(const void* /*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) + { + //LOGI("NEW weakref_impl %p for RefBase %p", this, base); + } + + ~weakref_impl() + { + LOG_ALWAYS_FATAL_IF(!mRetain && mStrongRefs != NULL, "Strong references remain!"); + LOG_ALWAYS_FATAL_IF(!mRetain && mWeakRefs != NULL, "Weak references remain!"); + } + + void addStrongRef(const void* id) + { + addRef(&mStrongRefs, id, mStrong); + } + + void removeStrongRef(const void* id) + { + if (!mRetain) + removeRef(&mStrongRefs, id); + else + addRef(&mStrongRefs, id, -mStrong); + } + + 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 trackMe(bool track, bool retain) + { + mTrackEnabled = track; + mRetain = retain; + } + + void printRefs() const + { + String8 text; + + { + AutoMutex _l(const_cast(this)->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, "/data/%p.stack", this); + int rc = open(name, O_RDWR | O_CREAT | O_APPEND); + if (rc >= 0) { + write(rc, text.string(), text.length()); + close(rc); + LOGD("STACK TRACE for %p saved in %s", this, name); + } + else LOGE("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* ref = *refs; + while (ref != NULL) { + if (ref->id == id) { + *refs = ref->next; + delete ref; + return; + } + + refs = &ref->next; + ref = *refs; + } + + LOG_ALWAYS_FATAL("RefBase: removing id %p on RefBase %p (weakref_type %p) that doesn't exist!", + id, mBase, this); + } + } + + 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; + } + } + + 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; + +#if 0 + void addRef(KeyedVector* refs, const void* id) + { + AutoMutex _l(mMutex); + ssize_t i = refs->indexOfKey(id); + if (i >= 0) { + ++(refs->editValueAt(i)); + } else { + i = refs->add(id, 1); + } + } + + void removeRef(KeyedVector* refs, const void* id) + { + AutoMutex _l(mMutex); + ssize_t i = refs->indexOfKey(id); + LOG_ALWAYS_FATAL_IF(i < 0, "RefBase: removing id %p that doesn't exist!", id); + if (i >= 0) { + int32_t val = --(refs->editValueAt(i)); + if (val == 0) { + refs->removeItemsAt(i); + } + } + } + + void printRefs(const KeyedVector& refs) + { + const size_t N=refs.size(); + for (size_t i=0; i mStrongRefs; + KeyedVector mWeakRefs; +#endif + +#endif +}; + +// --------------------------------------------------------------------------- + +void RefBase::incStrong(const void* id) const +{ + weakref_impl* const refs = mRefs; + refs->addWeakRef(id); + refs->incWeak(id); + + refs->addStrongRef(id); + const int32_t c = android_atomic_inc(&refs->mStrong); + LOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs); +#if PRINT_REFS + LOGD("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); + const_cast(this)->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 + LOGD("decStrong of %p from %p: cnt=%d\n", this, id, c); +#endif + LOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs); + if (c == 1) { + const_cast(this)->onLastStrongRef(id); + if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) { + delete this; + } + } + refs->removeWeakRef(id); + refs->decWeak(id); +} + +void RefBase::forceIncStrong(const void* id) const +{ + weakref_impl* const refs = mRefs; + refs->addWeakRef(id); + refs->incWeak(id); + + refs->addStrongRef(id); + const int32_t c = android_atomic_inc(&refs->mStrong); + LOG_ASSERT(c >= 0, "forceIncStrong called on %p after ref count underflow", + refs); +#if PRINT_REFS + LOGD("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: + const_cast(this)->onFirstRef(); + } +} + +int32_t RefBase::getStrongCount() const +{ + return mRefs->mStrong; +} + + + +RefBase* RefBase::weakref_type::refBase() const +{ + return static_cast(this)->mBase; +} + +void RefBase::weakref_type::incWeak(const void* id) +{ + weakref_impl* const impl = static_cast(this); + impl->addWeakRef(id); + const int32_t c = android_atomic_inc(&impl->mWeak); + LOG_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(this); + impl->removeWeakRef(id); + const int32_t c = android_atomic_dec(&impl->mWeak); + LOG_ASSERT(c >= 1, "decWeak called on %p too many times", this); + if (c != 1) return; + + if ((impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) { + if (impl->mStrong == INITIAL_STRONG_VALUE) + delete impl->mBase; + else { +// LOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase); + delete impl; + } + } else { + impl->mBase->onLastWeakRef(id); + if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) { + delete impl->mBase; + } + } +} + +bool RefBase::weakref_type::attemptIncStrong(const void* id) +{ + incWeak(id); + + weakref_impl* const impl = static_cast(this); + + int32_t curCount = impl->mStrong; + LOG_ASSERT(curCount >= 0, "attemptIncStrong called on %p after underflow", + this); + while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) { + if (android_atomic_cmpxchg(curCount, curCount+1, &impl->mStrong) == 0) { + break; + } + curCount = impl->mStrong; + } + + if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) { + bool allow; + if (curCount == INITIAL_STRONG_VALUE) { + // Attempting to acquire first strong reference... this is allowed + // if the object does NOT have a longer lifetime (meaning the + // implementation doesn't need to see this), or if the implementation + // allows it to happen. + allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK + || impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id); + } else { + // Attempting to revive the object... this is allowed + // if the object DOES have a longer lifetime (so we can safely + // call the object with only a weak ref) and the implementation + // allows it to happen. + allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_WEAK + && impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id); + } + if (!allow) { + decWeak(id); + return false; + } + 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->addWeakRef(id); + impl->addStrongRef(id); + +#if PRINT_REFS + LOGD("attemptIncStrong of %p from %p: cnt=%d\n", this, id, curCount); +#endif + + if (curCount == INITIAL_STRONG_VALUE) { + android_atomic_add(-INITIAL_STRONG_VALUE, &impl->mStrong); + impl->mBase->onFirstRef(); + } + + return true; +} + +bool RefBase::weakref_type::attemptIncWeak(const void* id) +{ + weakref_impl* const impl = static_cast(this); + + int32_t curCount = impl->mWeak; + LOG_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(this)->mWeak; +} + +void RefBase::weakref_type::printRefs() const +{ + static_cast(this)->printRefs(); +} + +void RefBase::weakref_type::trackMe(bool enable, bool retain) +{ + static_cast(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)) +{ +// LOGV("Creating refs %p with RefBase %p\n", mRefs, this); +} + +RefBase::~RefBase() +{ +// LOGV("Destroying RefBase %p (refs %p)\n", this, mRefs); + if (mRefs->mWeak == 0) { +// LOGV("Freeing refs %p of old RefBase %p\n", mRefs, this); + delete mRefs; + } +} + +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*/) +{ +} + +}; // namespace android diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp new file mode 100644 index 000000000..71e7cd722 --- /dev/null +++ b/libs/utils/ResourceTypes.cpp @@ -0,0 +1,3983 @@ +/* + * 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 "ResourceType" +//#define LOG_NDEBUG 0 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifndef INT32_MAX +#define INT32_MAX ((int32_t)(2147483647)) +#endif + +#define POOL_NOISY(x) //x +#define XML_NOISY(x) //x +#define TABLE_NOISY(x) //x +#define TABLE_GETENTRY(x) //x +#define TABLE_SUPER_NOISY(x) //x +#define LOAD_TABLE_NOISY(x) //x + +namespace android { + +#ifdef HAVE_WINSOCK +#undef nhtol +#undef htonl + +#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 +#endif + +static void printToLogFunc(void* cookie, const char* txt) +{ + LOGV("%s", txt); +} + +// Standard C isspace() is only required to look at the low byte of its input, so +// produces incorrect results for UTF-16 characters. For safety's sake, assume that +// any high-byte UTF-16 code point is not whitespace. +inline int isspace16(char16_t c) { + return (c < 0x0080 && isspace(c)); +} + +// range checked; guaranteed to NUL-terminate within the stated number of available slots +// NOTE: if this truncates the dst string due to running out of space, no attempt is +// made to avoid splitting surrogate pairs. +static void strcpy16_dtoh(uint16_t* dst, const uint16_t* src, size_t avail) +{ + uint16_t* last = dst + avail - 1; + while (*src && (dst < last)) { + char16_t s = dtohs(*src); + *dst++ = s; + src++; + } + *dst = 0; +} + +static status_t validate_chunk(const ResChunk_header* chunk, + size_t minSize, + const uint8_t* dataEnd, + const char* name) +{ + const uint16_t headerSize = dtohs(chunk->headerSize); + const uint32_t size = dtohl(chunk->size); + + if (headerSize >= minSize) { + if (headerSize <= size) { + if (((headerSize|size)&0x3) == 0) { + if ((ssize_t)size <= (dataEnd-((const uint8_t*)chunk))) { + return NO_ERROR; + } + LOGW("%s data size %p extends beyond resource end %p.", + name, (void*)size, + (void*)(dataEnd-((const uint8_t*)chunk))); + return BAD_TYPE; + } + LOGW("%s size 0x%x or headerSize 0x%x is not on an integer boundary.", + name, (int)size, (int)headerSize); + return BAD_TYPE; + } + LOGW("%s size %p is smaller than header size %p.", + name, (void*)size, (void*)(int)headerSize); + return BAD_TYPE; + } + LOGW("%s header size %p is too small.", + name, (void*)(int)headerSize); + return BAD_TYPE; +} + +inline void Res_value::copyFrom_dtoh(const Res_value& src) +{ + size = dtohs(src.size); + res0 = src.res0; + dataType = src.dataType; + data = dtohl(src.data); +} + +void Res_png_9patch::deviceToFile() +{ + for (int i = 0; i < numXDivs; i++) { + xDivs[i] = htonl(xDivs[i]); + } + for (int i = 0; i < numYDivs; i++) { + yDivs[i] = htonl(yDivs[i]); + } + paddingLeft = htonl(paddingLeft); + paddingRight = htonl(paddingRight); + paddingTop = htonl(paddingTop); + paddingBottom = htonl(paddingBottom); + for (int i=0; ixDivs, numXDivs * sizeof(int32_t)); + data += numXDivs * sizeof(int32_t); + memmove(data, this->yDivs, numYDivs * sizeof(int32_t)); + data += numYDivs * sizeof(int32_t); + memmove(data, this->colors, numColors * sizeof(uint32_t)); +} + +static void deserializeInternal(const void* inData, Res_png_9patch* outData) { + char* patch = (char*) inData; + if (inData != outData) { + memmove(&outData->wasDeserialized, patch, 4); // copy wasDeserialized, numXDivs, numYDivs, numColors + memmove(&outData->paddingLeft, patch + 12, 4); // copy wasDeserialized, numXDivs, numYDivs, numColors + } + outData->wasDeserialized = true; + char* data = (char*)outData; + data += sizeof(Res_png_9patch); + outData->xDivs = (int32_t*) data; + data += outData->numXDivs * sizeof(int32_t); + outData->yDivs = (int32_t*) data; + data += outData->numYDivs * sizeof(int32_t); + outData->colors = (uint32_t*) data; +} + +Res_png_9patch* Res_png_9patch::deserialize(const void* inData) +{ + if (sizeof(void*) != sizeof(int32_t)) { + LOGE("Cannot deserialize on non 32-bit system\n"); + return NULL; + } + deserializeInternal(inData, (Res_png_9patch*) inData); + return (Res_png_9patch*) inData; +} + +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- + +ResStringPool::ResStringPool() + : mError(NO_INIT), mOwnedData(NULL) +{ +} + +ResStringPool::ResStringPool(const void* data, size_t size, bool copyData) + : mError(NO_INIT), mOwnedData(NULL) +{ + setTo(data, size, copyData); +} + +ResStringPool::~ResStringPool() +{ + uninit(); +} + +status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) +{ + if (!data || !size) { + return (mError=BAD_TYPE); + } + + uninit(); + + const bool notDeviceEndian = htods(0xf0) != 0xf0; + + if (copyData || notDeviceEndian) { + mOwnedData = malloc(size); + if (mOwnedData == NULL) { + return (mError=NO_MEMORY); + } + memcpy(mOwnedData, data, size); + data = mOwnedData; + } + + mHeader = (const ResStringPool_header*)data; + + if (notDeviceEndian) { + ResStringPool_header* h = const_cast(mHeader); + h->header.headerSize = dtohs(mHeader->header.headerSize); + h->header.type = dtohs(mHeader->header.type); + h->header.size = dtohl(mHeader->header.size); + h->stringCount = dtohl(mHeader->stringCount); + h->styleCount = dtohl(mHeader->styleCount); + h->flags = dtohl(mHeader->flags); + h->stringsStart = dtohl(mHeader->stringsStart); + h->stylesStart = dtohl(mHeader->stylesStart); + } + + if (mHeader->header.headerSize > mHeader->header.size + || mHeader->header.size > size) { + LOGW("Bad string block: header size %d or total size %d is larger than data size %d\n", + (int)mHeader->header.headerSize, (int)mHeader->header.size, (int)size); + return (mError=BAD_TYPE); + } + mSize = mHeader->header.size; + mEntries = (const uint32_t*) + (((const uint8_t*)data)+mHeader->header.headerSize); + + if (mHeader->stringCount > 0) { + if ((mHeader->stringCount*sizeof(uint32_t) < mHeader->stringCount) // uint32 overflow? + || (mHeader->header.headerSize+(mHeader->stringCount*sizeof(uint32_t))) + > size) { + LOGW("Bad string block: entry of %d items extends past data size %d\n", + (int)(mHeader->header.headerSize+(mHeader->stringCount*sizeof(uint32_t))), + (int)size); + return (mError=BAD_TYPE); + } + mStrings = (const char16_t*) + (((const uint8_t*)data)+mHeader->stringsStart); + if (mHeader->stringsStart >= (mHeader->header.size-sizeof(uint16_t))) { + LOGW("Bad string block: string pool starts at %d, after total size %d\n", + (int)mHeader->stringsStart, (int)mHeader->header.size); + return (mError=BAD_TYPE); + } + if (mHeader->styleCount == 0) { + mStringPoolSize = + (mHeader->header.size-mHeader->stringsStart)/sizeof(uint16_t); + } else { + // check invariant: styles follow the strings + if (mHeader->stylesStart <= mHeader->stringsStart) { + LOGW("Bad style block: style block starts at %d, before strings at %d\n", + (int)mHeader->stylesStart, (int)mHeader->stringsStart); + return (mError=BAD_TYPE); + } + mStringPoolSize = + (mHeader->stylesStart-mHeader->stringsStart)/sizeof(uint16_t); + } + + // check invariant: stringCount > 0 requires a string pool to exist + if (mStringPoolSize == 0) { + LOGW("Bad string block: stringCount is %d but pool size is 0\n", (int)mHeader->stringCount); + return (mError=BAD_TYPE); + } + + if (notDeviceEndian) { + size_t i; + uint32_t* e = const_cast(mEntries); + for (i=0; istringCount; i++) { + e[i] = dtohl(mEntries[i]); + } + char16_t* s = const_cast(mStrings); + for (i=0; istyleCount > 0) { + mEntryStyles = mEntries + mHeader->stringCount; + // invariant: integer overflow in calculating mEntryStyles + if (mEntryStyles < mEntries) { + LOGW("Bad string block: integer overflow finding styles\n"); + return (mError=BAD_TYPE); + } + + if (((const uint8_t*)mEntryStyles-(const uint8_t*)mHeader) > (int)size) { + LOGW("Bad string block: entry of %d styles extends past data size %d\n", + (int)((const uint8_t*)mEntryStyles-(const uint8_t*)mHeader), + (int)size); + return (mError=BAD_TYPE); + } + mStyles = (const uint32_t*) + (((const uint8_t*)data)+mHeader->stylesStart); + if (mHeader->stylesStart >= mHeader->header.size) { + LOGW("Bad string block: style pool starts %d, after total size %d\n", + (int)mHeader->stylesStart, (int)mHeader->header.size); + return (mError=BAD_TYPE); + } + mStylePoolSize = + (mHeader->header.size-mHeader->stylesStart)/sizeof(uint32_t); + + if (notDeviceEndian) { + size_t i; + uint32_t* e = const_cast(mEntryStyles); + for (i=0; istyleCount; i++) { + e[i] = dtohl(mEntryStyles[i]); + } + uint32_t* s = const_cast(mStyles); + for (i=0; istringCount) { + const uint32_t off = (mEntries[idx]/sizeof(uint16_t)); + if (off < (mStringPoolSize-1)) { + const char16_t* str = mStrings+off; + *outLen = *str; + if ((*str)&0x8000) { + str++; + *outLen = (((*outLen)&0x7fff)<<16) + *str; + } + if ((uint32_t)(str+1+*outLen-mStrings) < mStringPoolSize) { + return str+1; + } else { + LOGW("Bad string block: string #%d extends to %d, past end at %d\n", + (int)idx, (int)(str+1+*outLen-mStrings), (int)mStringPoolSize); + } + } else { + LOGW("Bad string block: string #%d entry is at %d, past end at %d\n", + (int)idx, (int)(off*sizeof(uint16_t)), + (int)(mStringPoolSize*sizeof(uint16_t))); + } + } + return NULL; +} + +const ResStringPool_span* ResStringPool::styleAt(const ResStringPool_ref& ref) const +{ + return styleAt(ref.index); +} + +const ResStringPool_span* ResStringPool::styleAt(size_t idx) const +{ + if (mError == NO_ERROR && idx < mHeader->styleCount) { + const uint32_t off = (mEntryStyles[idx]/sizeof(uint32_t)); + if (off < mStylePoolSize) { + return (const ResStringPool_span*)(mStyles+off); + } else { + LOGW("Bad string block: style #%d entry is at %d, past end at %d\n", + (int)idx, (int)(off*sizeof(uint32_t)), + (int)(mStylePoolSize*sizeof(uint32_t))); + } + } + return NULL; +} + +ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const +{ + if (mError != NO_ERROR) { + return mError; + } + + size_t len; + + if (mHeader->flags&ResStringPool_header::SORTED_FLAG) { + // Do a binary search for the string... + ssize_t l = 0; + ssize_t h = mHeader->stringCount-1; + + ssize_t mid; + while (l <= h) { + mid = l + (h - l)/2; + const char16_t* s = stringAt(mid, &len); + int c = s ? strzcmp16(s, len, str, strLen) : -1; + POOL_NOISY(printf("Looking for %s, at %s, cmp=%d, l/mid/h=%d/%d/%d\n", + String8(str).string(), + String8(s).string(), + c, (int)l, (int)mid, (int)h)); + if (c == 0) { + return mid; + } else if (c < 0) { + l = mid + 1; + } else { + h = mid - 1; + } + } + } else { + // It is unusual to get the ID from an unsorted string block... + // most often this happens because we want to get IDs for style + // span tags; since those always appear at the end of the string + // block, start searching at the back. + for (int i=mHeader->stringCount-1; i>=0; i--) { + const char16_t* s = stringAt(i, &len); + POOL_NOISY(printf("Looking for %s, at %s, i=%d\n", + String8(str, strLen).string(), + String8(s).string(), + i)); + if (s && strzcmp16(s, len, str, strLen) == 0) { + return i; + } + } + } + + return NAME_NOT_FOUND; +} + +size_t ResStringPool::size() const +{ + return (mError == NO_ERROR) ? mHeader->stringCount : 0; +} + +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- + +ResXMLParser::ResXMLParser(const ResXMLTree& tree) + : mTree(tree), mEventCode(BAD_DOCUMENT) +{ +} + +void ResXMLParser::restart() +{ + mCurNode = NULL; + mEventCode = mTree.mError == NO_ERROR ? START_DOCUMENT : BAD_DOCUMENT; +} + +ResXMLParser::event_code_t ResXMLParser::getEventType() const +{ + return mEventCode; +} + +ResXMLParser::event_code_t ResXMLParser::next() +{ + if (mEventCode == START_DOCUMENT) { + mCurNode = mTree.mRootNode; + mCurExt = mTree.mRootExt; + return (mEventCode=mTree.mRootCode); + } else if (mEventCode >= FIRST_CHUNK_CODE) { + return nextNode(); + } + return mEventCode; +} + +const int32_t ResXMLParser::getCommentID() const +{ + return mCurNode != NULL ? dtohl(mCurNode->comment.index) : -1; +} + +const uint16_t* ResXMLParser::getComment(size_t* outLen) const +{ + int32_t id = getCommentID(); + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; +} + +const uint32_t ResXMLParser::getLineNumber() const +{ + return mCurNode != NULL ? dtohl(mCurNode->lineNumber) : -1; +} + +const int32_t ResXMLParser::getTextID() const +{ + if (mEventCode == TEXT) { + return dtohl(((const ResXMLTree_cdataExt*)mCurExt)->data.index); + } + return -1; +} + +const uint16_t* ResXMLParser::getText(size_t* outLen) const +{ + int32_t id = getTextID(); + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; +} + +ssize_t ResXMLParser::getTextValue(Res_value* outValue) const +{ + if (mEventCode == TEXT) { + outValue->copyFrom_dtoh(((const ResXMLTree_cdataExt*)mCurExt)->typedData); + return sizeof(Res_value); + } + return BAD_TYPE; +} + +const int32_t ResXMLParser::getNamespacePrefixID() const +{ + if (mEventCode == START_NAMESPACE || mEventCode == END_NAMESPACE) { + return dtohl(((const ResXMLTree_namespaceExt*)mCurExt)->prefix.index); + } + return -1; +} + +const uint16_t* ResXMLParser::getNamespacePrefix(size_t* outLen) const +{ + int32_t id = getNamespacePrefixID(); + //printf("prefix=%d event=%p\n", id, mEventCode); + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; +} + +const int32_t ResXMLParser::getNamespaceUriID() const +{ + if (mEventCode == START_NAMESPACE || mEventCode == END_NAMESPACE) { + return dtohl(((const ResXMLTree_namespaceExt*)mCurExt)->uri.index); + } + return -1; +} + +const uint16_t* ResXMLParser::getNamespaceUri(size_t* outLen) const +{ + int32_t id = getNamespaceUriID(); + //printf("uri=%d event=%p\n", id, mEventCode); + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; +} + +const int32_t ResXMLParser::getElementNamespaceID() const +{ + if (mEventCode == START_TAG) { + return dtohl(((const ResXMLTree_attrExt*)mCurExt)->ns.index); + } + if (mEventCode == END_TAG) { + return dtohl(((const ResXMLTree_endElementExt*)mCurExt)->ns.index); + } + return -1; +} + +const uint16_t* ResXMLParser::getElementNamespace(size_t* outLen) const +{ + int32_t id = getElementNamespaceID(); + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; +} + +const int32_t ResXMLParser::getElementNameID() const +{ + if (mEventCode == START_TAG) { + return dtohl(((const ResXMLTree_attrExt*)mCurExt)->name.index); + } + if (mEventCode == END_TAG) { + return dtohl(((const ResXMLTree_endElementExt*)mCurExt)->name.index); + } + return -1; +} + +const uint16_t* ResXMLParser::getElementName(size_t* outLen) const +{ + int32_t id = getElementNameID(); + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; +} + +size_t ResXMLParser::getAttributeCount() const +{ + if (mEventCode == START_TAG) { + return dtohs(((const ResXMLTree_attrExt*)mCurExt)->attributeCount); + } + return 0; +} + +const int32_t ResXMLParser::getAttributeNamespaceID(size_t idx) const +{ + if (mEventCode == START_TAG) { + const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; + if (idx < dtohs(tag->attributeCount)) { + const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*) + (((const uint8_t*)tag) + + dtohs(tag->attributeStart) + + (dtohs(tag->attributeSize)*idx)); + return dtohl(attr->ns.index); + } + } + return -2; +} + +const uint16_t* ResXMLParser::getAttributeNamespace(size_t idx, size_t* outLen) const +{ + int32_t id = getAttributeNamespaceID(idx); + //printf("attribute namespace=%d idx=%d event=%p\n", id, idx, mEventCode); + //XML_NOISY(printf("getAttributeNamespace 0x%x=0x%x\n", idx, id)); + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; +} + +const int32_t ResXMLParser::getAttributeNameID(size_t idx) const +{ + if (mEventCode == START_TAG) { + const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; + if (idx < dtohs(tag->attributeCount)) { + const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*) + (((const uint8_t*)tag) + + dtohs(tag->attributeStart) + + (dtohs(tag->attributeSize)*idx)); + return dtohl(attr->name.index); + } + } + return -1; +} + +const uint16_t* ResXMLParser::getAttributeName(size_t idx, size_t* outLen) const +{ + int32_t id = getAttributeNameID(idx); + //printf("attribute name=%d idx=%d event=%p\n", id, idx, mEventCode); + //XML_NOISY(printf("getAttributeName 0x%x=0x%x\n", idx, id)); + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; +} + +const uint32_t ResXMLParser::getAttributeNameResID(size_t idx) const +{ + int32_t id = getAttributeNameID(idx); + if (id >= 0 && (size_t)id < mTree.mNumResIds) { + return dtohl(mTree.mResIds[id]); + } + return 0; +} + +const int32_t ResXMLParser::getAttributeValueStringID(size_t idx) const +{ + if (mEventCode == START_TAG) { + const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; + if (idx < dtohs(tag->attributeCount)) { + const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*) + (((const uint8_t*)tag) + + dtohs(tag->attributeStart) + + (dtohs(tag->attributeSize)*idx)); + return dtohl(attr->rawValue.index); + } + } + return -1; +} + +const uint16_t* ResXMLParser::getAttributeStringValue(size_t idx, size_t* outLen) const +{ + int32_t id = getAttributeValueStringID(idx); + //XML_NOISY(printf("getAttributeValue 0x%x=0x%x\n", idx, id)); + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; +} + +int32_t ResXMLParser::getAttributeDataType(size_t idx) const +{ + if (mEventCode == START_TAG) { + const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; + if (idx < dtohs(tag->attributeCount)) { + const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*) + (((const uint8_t*)tag) + + dtohs(tag->attributeStart) + + (dtohs(tag->attributeSize)*idx)); + return attr->typedValue.dataType; + } + } + return Res_value::TYPE_NULL; +} + +int32_t ResXMLParser::getAttributeData(size_t idx) const +{ + if (mEventCode == START_TAG) { + const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; + if (idx < dtohs(tag->attributeCount)) { + const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*) + (((const uint8_t*)tag) + + dtohs(tag->attributeStart) + + (dtohs(tag->attributeSize)*idx)); + return dtohl(attr->typedValue.data); + } + } + return 0; +} + +ssize_t ResXMLParser::getAttributeValue(size_t idx, Res_value* outValue) const +{ + if (mEventCode == START_TAG) { + const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; + if (idx < dtohs(tag->attributeCount)) { + const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*) + (((const uint8_t*)tag) + + dtohs(tag->attributeStart) + + (dtohs(tag->attributeSize)*idx)); + outValue->copyFrom_dtoh(attr->typedValue); + return sizeof(Res_value); + } + } + return BAD_TYPE; +} + +ssize_t ResXMLParser::indexOfAttribute(const char* ns, const char* attr) const +{ + String16 nsStr(ns != NULL ? ns : ""); + String16 attrStr(attr); + return indexOfAttribute(ns ? nsStr.string() : NULL, ns ? nsStr.size() : 0, + attrStr.string(), attrStr.size()); +} + +ssize_t ResXMLParser::indexOfAttribute(const char16_t* ns, size_t nsLen, + const char16_t* attr, size_t attrLen) const +{ + if (mEventCode == START_TAG) { + const size_t N = getAttributeCount(); + for (size_t i=0; i attr=%s, curAttr=%s\n", + // String8(attr).string(), String8(curAttr).string()); + if (attr && curAttr && (strzcmp16(attr, attrLen, curAttr, curAttrLen) == 0)) { + if (ns == NULL) { + if (curNs == NULL) return i; + } else if (curNs != NULL) { + //printf(" --> ns=%s, curNs=%s\n", + // String8(ns).string(), String8(curNs).string()); + if (strzcmp16(ns, nsLen, curNs, curNsLen) == 0) return i; + } + } + } + } + + return NAME_NOT_FOUND; +} + +ssize_t ResXMLParser::indexOfID() const +{ + if (mEventCode == START_TAG) { + const ssize_t idx = dtohs(((const ResXMLTree_attrExt*)mCurExt)->idIndex); + if (idx > 0) return (idx-1); + } + return NAME_NOT_FOUND; +} + +ssize_t ResXMLParser::indexOfClass() const +{ + if (mEventCode == START_TAG) { + const ssize_t idx = dtohs(((const ResXMLTree_attrExt*)mCurExt)->classIndex); + if (idx > 0) return (idx-1); + } + return NAME_NOT_FOUND; +} + +ssize_t ResXMLParser::indexOfStyle() const +{ + if (mEventCode == START_TAG) { + const ssize_t idx = dtohs(((const ResXMLTree_attrExt*)mCurExt)->styleIndex); + if (idx > 0) return (idx-1); + } + return NAME_NOT_FOUND; +} + +ResXMLParser::event_code_t ResXMLParser::nextNode() +{ + if (mEventCode < 0) { + return mEventCode; + } + + do { + const ResXMLTree_node* next = (const ResXMLTree_node*) + (((const uint8_t*)mCurNode) + dtohl(mCurNode->header.size)); + //LOGW("Next node: prev=%p, next=%p\n", mCurNode, next); + + if (((const uint8_t*)next) >= mTree.mDataEnd) { + mCurNode = NULL; + return (mEventCode=END_DOCUMENT); + } + + if (mTree.validateNode(next) != NO_ERROR) { + mCurNode = NULL; + return (mEventCode=BAD_DOCUMENT); + } + + mCurNode = next; + const uint16_t headerSize = dtohs(next->header.headerSize); + const uint32_t totalSize = dtohl(next->header.size); + mCurExt = ((const uint8_t*)next) + headerSize; + size_t minExtSize = 0; + event_code_t eventCode = (event_code_t)dtohs(next->header.type); + switch ((mEventCode=eventCode)) { + case RES_XML_START_NAMESPACE_TYPE: + case RES_XML_END_NAMESPACE_TYPE: + minExtSize = sizeof(ResXMLTree_namespaceExt); + break; + case RES_XML_START_ELEMENT_TYPE: + minExtSize = sizeof(ResXMLTree_attrExt); + break; + case RES_XML_END_ELEMENT_TYPE: + minExtSize = sizeof(ResXMLTree_endElementExt); + break; + case RES_XML_CDATA_TYPE: + minExtSize = sizeof(ResXMLTree_cdataExt); + break; + default: + LOGW("Unknown XML block: header type %d in node at %d\n", + (int)dtohs(next->header.type), + (int)(((const uint8_t*)next)-((const uint8_t*)mTree.mHeader))); + continue; + } + + if ((totalSize-headerSize) < minExtSize) { + LOGW("Bad XML block: header type 0x%x in node at 0x%x has size %d, need %d\n", + (int)dtohs(next->header.type), + (int)(((const uint8_t*)next)-((const uint8_t*)mTree.mHeader)), + (int)(totalSize-headerSize), (int)minExtSize); + return (mEventCode=BAD_DOCUMENT); + } + + //printf("CurNode=%p, CurExt=%p, headerSize=%d, minExtSize=%d\n", + // mCurNode, mCurExt, headerSize, minExtSize); + + return eventCode; + } while (true); +} + +void ResXMLParser::getPosition(ResXMLParser::ResXMLPosition* pos) const +{ + pos->eventCode = mEventCode; + pos->curNode = mCurNode; + pos->curExt = mCurExt; +} + +void ResXMLParser::setPosition(const ResXMLParser::ResXMLPosition& pos) +{ + mEventCode = pos.eventCode; + mCurNode = pos.curNode; + mCurExt = pos.curExt; +} + + +// -------------------------------------------------------------------- + +static volatile int32_t gCount = 0; + +ResXMLTree::ResXMLTree() + : ResXMLParser(*this) + , mError(NO_INIT), mOwnedData(NULL) +{ + //LOGI("Creating ResXMLTree %p #%d\n", this, android_atomic_inc(&gCount)+1); + restart(); +} + +ResXMLTree::ResXMLTree(const void* data, size_t size, bool copyData) + : ResXMLParser(*this) + , mError(NO_INIT), mOwnedData(NULL) +{ + //LOGI("Creating ResXMLTree %p #%d\n", this, android_atomic_inc(&gCount)+1); + setTo(data, size, copyData); +} + +ResXMLTree::~ResXMLTree() +{ + //LOGI("Destroying ResXMLTree in %p #%d\n", this, android_atomic_dec(&gCount)-1); + uninit(); +} + +status_t ResXMLTree::setTo(const void* data, size_t size, bool copyData) +{ + uninit(); + mEventCode = START_DOCUMENT; + + if (copyData) { + mOwnedData = malloc(size); + if (mOwnedData == NULL) { + return (mError=NO_MEMORY); + } + memcpy(mOwnedData, data, size); + data = mOwnedData; + } + + mHeader = (const ResXMLTree_header*)data; + mSize = dtohl(mHeader->header.size); + if (dtohs(mHeader->header.headerSize) > mSize || mSize > size) { + LOGW("Bad XML block: header size %d or total size %d is larger than data size %d\n", + (int)dtohs(mHeader->header.headerSize), + (int)dtohl(mHeader->header.size), (int)size); + mError = BAD_TYPE; + restart(); + return mError; + } + mDataEnd = ((const uint8_t*)mHeader) + mSize; + + mStrings.uninit(); + mRootNode = NULL; + mResIds = NULL; + mNumResIds = 0; + + // First look for a couple interesting chunks: the string block + // and first XML node. + const ResChunk_header* chunk = + (const ResChunk_header*)(((const uint8_t*)mHeader) + dtohs(mHeader->header.headerSize)); + const ResChunk_header* lastChunk = chunk; + while (((const uint8_t*)chunk) < (mDataEnd-sizeof(ResChunk_header)) && + ((const uint8_t*)chunk) < (mDataEnd-dtohl(chunk->size))) { + status_t err = validate_chunk(chunk, sizeof(ResChunk_header), mDataEnd, "XML"); + if (err != NO_ERROR) { + mError = err; + goto done; + } + const uint16_t type = dtohs(chunk->type); + const size_t size = dtohl(chunk->size); + XML_NOISY(printf("Scanning @ %p: type=0x%x, size=0x%x\n", + (void*)(((uint32_t)chunk)-((uint32_t)mHeader)), type, size)); + if (type == RES_STRING_POOL_TYPE) { + mStrings.setTo(chunk, size); + } else if (type == RES_XML_RESOURCE_MAP_TYPE) { + mResIds = (const uint32_t*) + (((const uint8_t*)chunk)+dtohs(chunk->headerSize)); + mNumResIds = (dtohl(chunk->size)-dtohs(chunk->headerSize))/sizeof(uint32_t); + } else if (type >= RES_XML_FIRST_CHUNK_TYPE + && type <= RES_XML_LAST_CHUNK_TYPE) { + if (validateNode((const ResXMLTree_node*)chunk) != NO_ERROR) { + mError = BAD_TYPE; + goto done; + } + mCurNode = (const ResXMLTree_node*)lastChunk; + if (nextNode() == BAD_DOCUMENT) { + mError = BAD_TYPE; + goto done; + } + mRootNode = mCurNode; + mRootExt = mCurExt; + mRootCode = mEventCode; + break; + } else { + XML_NOISY(printf("Skipping unknown chunk!\n")); + } + lastChunk = chunk; + chunk = (const ResChunk_header*) + (((const uint8_t*)chunk) + size); + } + + if (mRootNode == NULL) { + LOGW("Bad XML block: no root element node found\n"); + mError = BAD_TYPE; + goto done; + } + + mError = mStrings.getError(); + +done: + restart(); + return mError; +} + +status_t ResXMLTree::getError() const +{ + return mError; +} + +void ResXMLTree::uninit() +{ + mError = NO_INIT; + if (mOwnedData) { + free(mOwnedData); + mOwnedData = NULL; + } + restart(); +} + +const ResStringPool& ResXMLTree::getStrings() const +{ + return mStrings; +} + +status_t ResXMLTree::validateNode(const ResXMLTree_node* node) const +{ + const uint16_t eventCode = dtohs(node->header.type); + + status_t err = validate_chunk( + &node->header, sizeof(ResXMLTree_node), + mDataEnd, "ResXMLTree_node"); + + if (err >= NO_ERROR) { + // Only perform additional validation on START nodes + if (eventCode != RES_XML_START_ELEMENT_TYPE) { + return NO_ERROR; + } + + const uint16_t headerSize = dtohs(node->header.headerSize); + const uint32_t size = dtohl(node->header.size); + const ResXMLTree_attrExt* attrExt = (const ResXMLTree_attrExt*) + (((const uint8_t*)node) + headerSize); + // check for sensical values pulled out of the stream so far... + if ((size >= headerSize + sizeof(ResXMLTree_attrExt)) + && ((void*)attrExt > (void*)node)) { + const size_t attrSize = ((size_t)dtohs(attrExt->attributeSize)) + * dtohs(attrExt->attributeCount); + if ((dtohs(attrExt->attributeStart)+attrSize) <= (size-headerSize)) { + return NO_ERROR; + } + LOGW("Bad XML block: node attributes use 0x%x bytes, only have 0x%x bytes\n", + (unsigned int)(dtohs(attrExt->attributeStart)+attrSize), + (unsigned int)(size-headerSize)); + } + else { + LOGW("Bad XML start block: node header size 0x%x, size 0x%x\n", + (unsigned int)headerSize, (unsigned int)size); + } + return BAD_TYPE; + } + + return err; + +#if 0 + const bool isStart = dtohs(node->header.type) == RES_XML_START_ELEMENT_TYPE; + + const uint16_t headerSize = dtohs(node->header.headerSize); + const uint32_t size = dtohl(node->header.size); + + if (headerSize >= (isStart ? sizeof(ResXMLTree_attrNode) : sizeof(ResXMLTree_node))) { + if (size >= headerSize) { + if (((const uint8_t*)node) <= (mDataEnd-size)) { + if (!isStart) { + return NO_ERROR; + } + if ((((size_t)dtohs(node->attributeSize))*dtohs(node->attributeCount)) + <= (size-headerSize)) { + return NO_ERROR; + } + LOGW("Bad XML block: node attributes use 0x%x bytes, only have 0x%x bytes\n", + ((int)dtohs(node->attributeSize))*dtohs(node->attributeCount), + (int)(size-headerSize)); + return BAD_TYPE; + } + LOGW("Bad XML block: node at 0x%x extends beyond data end 0x%x\n", + (int)(((const uint8_t*)node)-((const uint8_t*)mHeader)), (int)mSize); + return BAD_TYPE; + } + LOGW("Bad XML block: node at 0x%x header size 0x%x smaller than total size 0x%x\n", + (int)(((const uint8_t*)node)-((const uint8_t*)mHeader)), + (int)headerSize, (int)size); + return BAD_TYPE; + } + LOGW("Bad XML block: node at 0x%x header size 0x%x too small\n", + (int)(((const uint8_t*)node)-((const uint8_t*)mHeader)), + (int)headerSize); + return BAD_TYPE; +#endif +} + +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- + +struct ResTable::Header +{ + Header() : ownedData(NULL), header(NULL) { } + + void* ownedData; + const ResTable_header* header; + size_t size; + const uint8_t* dataEnd; + size_t index; + void* cookie; + + ResStringPool values; +}; + +struct ResTable::Type +{ + Type(const Header* _header, const Package* _package, size_t count) + : header(_header), package(_package), entryCount(count), + typeSpec(NULL), typeSpecFlags(NULL) { } + const Header* const header; + const Package* const package; + const size_t entryCount; + const ResTable_typeSpec* typeSpec; + const uint32_t* typeSpecFlags; + Vector configs; +}; + +struct ResTable::Package +{ + Package(const Header* _header, const ResTable_package* _package) + : header(_header), package(_package) { } + ~Package() + { + size_t i = types.size(); + while (i > 0) { + i--; + delete types[i]; + } + } + + const Header* const header; + const ResTable_package* const package; + Vector types; + + const Type* getType(size_t idx) const { + return idx < types.size() ? types[idx] : NULL; + } +}; + +// A group of objects describing a particular resource package. +// The first in 'package' is always the root object (from the resource +// table that defined the package); the ones after are skins on top of it. +struct ResTable::PackageGroup +{ + PackageGroup(const String16& _name, uint32_t _id) + : name(_name), id(_id), typeCount(0), bags(NULL) { } + ~PackageGroup() { + clearBagCache(); + const size_t N = packages.size(); + for (size_t i=0; igetType(i); + if (type != NULL) { + bag_set** typeBags = bags[i]; + TABLE_NOISY(printf("typeBags=%p\n", typeBags)); + if (typeBags) { + TABLE_NOISY(printf("type->entryCount=%x\n", type->entryCount)); + const size_t N = type->entryCount; + for (size_t j=0; j packages; + + // Taken from the root package. + ResStringPool typeStrings; + ResStringPool keyStrings; + size_t typeCount; + + // Computed attribute bags, first indexed by the type and second + // by the entry in that type. + bag_set*** bags; +}; + +struct ResTable::bag_set +{ + size_t numAttrs; // number in array + size_t availAttrs; // total space in array + uint32_t typeSpecFlags; + // Followed by 'numAttr' bag_entry structures. +}; + +ResTable::Theme::Theme(const ResTable& table) + : mTable(table) +{ + memset(mPackages, 0, sizeof(mPackages)); +} + +ResTable::Theme::~Theme() +{ + for (size_t i=0; inumTypes; j++) { + theme_entry* te = pi->types[j].entries; + if (te != NULL) { + free(te); + } + } + free(pi); +} + +ResTable::Theme::package_info* ResTable::Theme::copy_package(package_info* pi) +{ + package_info* newpi = (package_info*)malloc( + sizeof(package_info) + (pi->numTypes*sizeof(type_info))); + newpi->numTypes = pi->numTypes; + for (size_t j=0; jnumTypes; j++) { + size_t cnt = pi->types[j].numEntries; + newpi->types[j].numEntries = cnt; + theme_entry* te = pi->types[j].entries; + if (te != NULL) { + theme_entry* newte = (theme_entry*)malloc(cnt*sizeof(theme_entry)); + newpi->types[j].entries = newte; + memcpy(newte, te, cnt*sizeof(theme_entry)); + } else { + newpi->types[j].entries = NULL; + } + } + return newpi; +} + +status_t ResTable::Theme::applyStyle(uint32_t resID, bool force) +{ + const bag_entry* bag; + uint32_t bagTypeSpecFlags = 0; + mTable.lock(); + const ssize_t N = mTable.getBagLocked(resID, &bag, &bagTypeSpecFlags); + TABLE_NOISY(LOGV("Applying style 0x%08x to theme %p, count=%d", resID, this, N)); + if (N < 0) { + mTable.unlock(); + return N; + } + + uint32_t curPackage = 0xffffffff; + ssize_t curPackageIndex = 0; + package_info* curPI = NULL; + uint32_t curType = 0xffffffff; + size_t numEntries = 0; + theme_entry* curEntries = NULL; + + const bag_entry* end = bag + N; + while (bag < end) { + const uint32_t attrRes = bag->map.name.ident; + const uint32_t p = Res_GETPACKAGE(attrRes); + const uint32_t t = Res_GETTYPE(attrRes); + const uint32_t e = Res_GETENTRY(attrRes); + + if (curPackage != p) { + const ssize_t pidx = mTable.getResourcePackageIndex(attrRes); + if (pidx < 0) { + LOGE("Style contains key with bad package: 0x%08x\n", attrRes); + bag++; + continue; + } + curPackage = p; + curPackageIndex = pidx; + curPI = mPackages[pidx]; + if (curPI == NULL) { + PackageGroup* const grp = mTable.mPackageGroups[pidx]; + int cnt = grp->typeCount; + curPI = (package_info*)malloc( + sizeof(package_info) + (cnt*sizeof(type_info))); + curPI->numTypes = cnt; + memset(curPI->types, 0, cnt*sizeof(type_info)); + mPackages[pidx] = curPI; + } + curType = 0xffffffff; + } + if (curType != t) { + if (t >= curPI->numTypes) { + LOGE("Style contains key with bad type: 0x%08x\n", attrRes); + bag++; + continue; + } + curType = t; + curEntries = curPI->types[t].entries; + if (curEntries == NULL) { + PackageGroup* const grp = mTable.mPackageGroups[curPackageIndex]; + const Type* type = grp->packages[0]->getType(t); + int cnt = type != NULL ? type->entryCount : 0; + curEntries = (theme_entry*)malloc(cnt*sizeof(theme_entry)); + memset(curEntries, Res_value::TYPE_NULL, cnt*sizeof(theme_entry)); + curPI->types[t].numEntries = cnt; + curPI->types[t].entries = curEntries; + } + numEntries = curPI->types[t].numEntries; + } + if (e >= numEntries) { + LOGE("Style contains key with bad entry: 0x%08x\n", attrRes); + bag++; + continue; + } + theme_entry* curEntry = curEntries + e; + TABLE_NOISY(LOGV("Attr 0x%08x: type=0x%x, data=0x%08x; curType=0x%x", + attrRes, bag->map.value.dataType, bag->map.value.data, + curEntry->value.dataType)); + if (force || curEntry->value.dataType == Res_value::TYPE_NULL) { + curEntry->stringBlock = bag->stringBlock; + curEntry->typeSpecFlags |= bagTypeSpecFlags; + curEntry->value = bag->map.value; + } + + bag++; + } + + mTable.unlock(); + + //LOGI("Applying style 0x%08x (force=%d) theme %p...\n", resID, force, this); + //dumpToLog(); + + return NO_ERROR; +} + +status_t ResTable::Theme::setTo(const Theme& other) +{ + //LOGI("Setting theme %p from theme %p...\n", this, &other); + //dumpToLog(); + //other.dumpToLog(); + + if (&mTable == &other.mTable) { + for (size_t i=0; i= 0) { + const package_info* const pi = mPackages[p]; + if (pi != NULL) { + if (t < pi->numTypes) { + const type_info& ti = pi->types[t]; + if (e < ti.numEntries) { + const theme_entry& te = ti.entries[e]; + if (outTypeSpecFlags != NULL) { + *outTypeSpecFlags |= te.typeSpecFlags; + } + const uint8_t type = te.value.dataType; + if (type == Res_value::TYPE_ATTRIBUTE) { + if (cnt > 0) { + cnt--; + resID = te.value.data; + continue; + } + LOGW("Too many attribute references, stopped at: 0x%08x\n", resID); + return BAD_INDEX; + } else if (type != Res_value::TYPE_NULL) { + *outValue = te.value; + return te.stringBlock; + } + return BAD_INDEX; + } + } + } + } + break; + + } while (true); + + return BAD_INDEX; +} + +ssize_t ResTable::Theme::resolveAttributeReference(Res_value* inOutValue, + ssize_t blockIndex, uint32_t* outLastRef, + uint32_t* inoutTypeSpecFlags) const +{ + //printf("Resolving type=0x%x\n", inOutValue->dataType); + if (inOutValue->dataType == Res_value::TYPE_ATTRIBUTE) { + uint32_t newTypeSpecFlags; + blockIndex = getAttribute(inOutValue->data, inOutValue, &newTypeSpecFlags); + if (inoutTypeSpecFlags != NULL) *inoutTypeSpecFlags |= newTypeSpecFlags; + //printf("Retrieved attribute new type=0x%x\n", inOutValue->dataType); + if (blockIndex < 0) { + return blockIndex; + } + } + return mTable.resolveReference(inOutValue, blockIndex, outLastRef); +} + +void ResTable::Theme::dumpToLog() const +{ + LOGI("Theme %p:\n", this); + for (size_t i=0; inumTypes; j++) { + type_info& ti = pi->types[j]; + if (ti.numEntries == 0) continue; + + LOGI(" Type #0x%02x:\n", (int)(j+1)); + for (size_t k=0; kgetBuffer(true); + if (data == NULL) { + LOGW("Unable to get buffer of resource asset file"); + return UNKNOWN_ERROR; + } + size_t size = (size_t)asset->getLength(); + return add(data, size, cookie, asset, copyData); +} + +status_t ResTable::add(const void* data, size_t size, void* cookie, + Asset* asset, bool copyData) +{ + if (!data) return NO_ERROR; + Header* header = new Header; + header->index = mHeaders.size(); + header->cookie = cookie; + mHeaders.add(header); + + const bool notDeviceEndian = htods(0xf0) != 0xf0; + + LOAD_TABLE_NOISY( + LOGV("Adding resources to ResTable: data=%p, size=0x%x, cookie=%p, asset=%p, copy=%d\n", + data, size, cookie, asset, copyData)); + + if (copyData || notDeviceEndian) { + header->ownedData = malloc(size); + if (header->ownedData == NULL) { + return (mError=NO_MEMORY); + } + memcpy(header->ownedData, data, size); + data = header->ownedData; + } + + header->header = (const ResTable_header*)data; + header->size = dtohl(header->header->header.size); + //LOGI("Got size 0x%x, again size 0x%x, raw size 0x%x\n", header->size, + // dtohl(header->header->header.size), header->header->header.size); + LOAD_TABLE_NOISY(LOGV("Loading ResTable @%p:\n", header->header)); + LOAD_TABLE_NOISY(printHexData(2, header->header, header->size < 256 ? header->size : 256, + 16, 16, 0, false, printToLogFunc)); + if (dtohs(header->header->header.headerSize) > header->size + || header->size > size) { + LOGW("Bad resource table: header size 0x%x or total size 0x%x is larger than data size 0x%x\n", + (int)dtohs(header->header->header.headerSize), + (int)header->size, (int)size); + return (mError=BAD_TYPE); + } + if (((dtohs(header->header->header.headerSize)|header->size)&0x3) != 0) { + LOGW("Bad resource table: header size 0x%x or total size 0x%x is not on an integer boundary\n", + (int)dtohs(header->header->header.headerSize), + (int)header->size); + return (mError=BAD_TYPE); + } + header->dataEnd = ((const uint8_t*)header->header) + header->size; + + // Iterate through all chunks. + size_t curPackage = 0; + + const ResChunk_header* chunk = + (const ResChunk_header*)(((const uint8_t*)header->header) + + dtohs(header->header->header.headerSize)); + while (((const uint8_t*)chunk) <= (header->dataEnd-sizeof(ResChunk_header)) && + ((const uint8_t*)chunk) <= (header->dataEnd-dtohl(chunk->size))) { + status_t err = validate_chunk(chunk, sizeof(ResChunk_header), header->dataEnd, "ResTable"); + if (err != NO_ERROR) { + return (mError=err); + } + TABLE_NOISY(LOGV("Chunk: type=0x%x, headerSize=0x%x, size=0x%x, pos=%p\n", + dtohs(chunk->type), dtohs(chunk->headerSize), dtohl(chunk->size), + (void*)(((const uint8_t*)chunk) - ((const uint8_t*)header->header)))); + const size_t csize = dtohl(chunk->size); + const uint16_t ctype = dtohs(chunk->type); + if (ctype == RES_STRING_POOL_TYPE) { + if (header->values.getError() != NO_ERROR) { + // Only use the first string chunk; ignore any others that + // may appear. + status_t err = header->values.setTo(chunk, csize); + if (err != NO_ERROR) { + return (mError=err); + } + } else { + LOGW("Multiple string chunks found in resource table."); + } + } else if (ctype == RES_TABLE_PACKAGE_TYPE) { + if (curPackage >= dtohl(header->header->packageCount)) { + LOGW("More package chunks were found than the %d declared in the header.", + dtohl(header->header->packageCount)); + return (mError=BAD_TYPE); + } + if (parsePackage((ResTable_package*)chunk, header) != NO_ERROR) { + return mError; + } + curPackage++; + } else { + LOGW("Unknown chunk type %p in table at %p.\n", + (void*)(int)(ctype), + (void*)(((const uint8_t*)chunk) - ((const uint8_t*)header->header))); + } + chunk = (const ResChunk_header*) + (((const uint8_t*)chunk) + csize); + } + + if (curPackage < dtohl(header->header->packageCount)) { + LOGW("Fewer package chunks (%d) were found than the %d declared in the header.", + (int)curPackage, dtohl(header->header->packageCount)); + return (mError=BAD_TYPE); + } + mError = header->values.getError(); + if (mError != NO_ERROR) { + LOGW("No string values found in resource table!"); + } + TABLE_NOISY(LOGV("Returning from add with mError=%d\n", mError)); + return mError; +} + +status_t ResTable::getError() const +{ + return mError; +} + +void ResTable::uninit() +{ + mError = NO_INIT; + size_t N = mPackageGroups.size(); + for (size_t i=0; iownedData) { + free(header->ownedData); + } + delete header; + } + + mPackageGroups.clear(); + mHeaders.clear(); +} + +bool ResTable::getResourceName(uint32_t resID, resource_name* outName) const +{ + if (mError != NO_ERROR) { + return false; + } + + const ssize_t p = getResourcePackageIndex(resID); + const int t = Res_GETTYPE(resID); + const int e = Res_GETENTRY(resID); + + if (p < 0) { + LOGW("No package identifier when getting name for resource number 0x%08x", resID); + return false; + } + if (t < 0) { + LOGW("No type identifier when getting name for resource number 0x%08x", resID); + return false; + } + + const PackageGroup* const grp = mPackageGroups[p]; + if (grp == NULL) { + LOGW("Bad identifier when getting name for resource number 0x%08x", resID); + return false; + } + if (grp->packages.size() > 0) { + const Package* const package = grp->packages[0]; + + const ResTable_type* type; + const ResTable_entry* entry; + ssize_t offset = getEntry(package, t, e, NULL, &type, &entry, NULL); + if (offset <= 0) { + return false; + } + + outName->package = grp->name.string(); + outName->packageLen = grp->name.size(); + outName->type = grp->typeStrings.stringAt(t, &outName->typeLen); + outName->name = grp->keyStrings.stringAt( + dtohl(entry->key.index), &outName->nameLen); + return true; + } + + return false; +} + +ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag, + uint32_t* outSpecFlags, ResTable_config* outConfig) const +{ + if (mError != NO_ERROR) { + return mError; + } + + const ssize_t p = getResourcePackageIndex(resID); + const int t = Res_GETTYPE(resID); + const int e = Res_GETENTRY(resID); + + if (p < 0) { + LOGW("No package identifier when getting value for resource number 0x%08x", resID); + return BAD_INDEX; + } + if (t < 0) { + LOGW("No type identifier when getting value for resource number 0x%08x", resID); + return BAD_INDEX; + } + + const Res_value* bestValue = NULL; + const Package* bestPackage = NULL; + ResTable_config bestItem; + memset(&bestItem, 0, sizeof(bestItem)); // make the compiler shut up + + if (outSpecFlags != NULL) *outSpecFlags = 0; + + // Look through all resource packages, starting with the most + // recently added. + const PackageGroup* const grp = mPackageGroups[p]; + if (grp == NULL) { + LOGW("Bad identifier when getting value for resource number 0x%08x", resID); + return false; + } + size_t ip = grp->packages.size(); + while (ip > 0) { + ip--; + + const Package* const package = grp->packages[ip]; + + const ResTable_type* type; + const ResTable_entry* entry; + const Type* typeClass; + ssize_t offset = getEntry(package, t, e, &mParams, &type, &entry, &typeClass); + if (offset <= 0) { + if (offset < 0) { + LOGW("Failure getting entry for 0x%08x (t=%d e=%d) in package %d: 0x%08x\n", + resID, t, e, (int)ip, (int)offset); + return offset; + } + continue; + } + + if ((dtohs(entry->flags)&entry->FLAG_COMPLEX) != 0) { + if (!mayBeBag) { + LOGW("Requesting resource %p failed because it is complex\n", + (void*)resID); + } + continue; + } + + TABLE_NOISY(aout << "Resource type data: " + << HexDump(type, dtohl(type->header.size)) << endl); + + if ((size_t)offset > (dtohl(type->header.size)-sizeof(Res_value))) { + LOGW("ResTable_item at %d is beyond type chunk data %d", + (int)offset, dtohl(type->header.size)); + return BAD_TYPE; + } + + const Res_value* item = + (const Res_value*)(((const uint8_t*)type) + offset); + ResTable_config thisConfig; + thisConfig.copyFromDtoH(type->config); + + if (outSpecFlags != NULL) { + if (typeClass->typeSpecFlags != NULL) { + *outSpecFlags |= dtohl(typeClass->typeSpecFlags[e]); + } else { + *outSpecFlags = -1; + } + } + + if (bestPackage != NULL && bestItem.isBetterThan(thisConfig)) { + continue; + } + + bestItem = thisConfig; + bestValue = item; + bestPackage = package; + } + + TABLE_NOISY(printf("Found result: package %p\n", bestPackage)); + + if (bestValue) { + outValue->size = dtohs(bestValue->size); + outValue->res0 = bestValue->res0; + outValue->dataType = bestValue->dataType; + outValue->data = dtohl(bestValue->data); + if (outConfig != NULL) { + *outConfig = bestItem; + } + TABLE_NOISY(size_t len; + printf("Found value: pkg=%d, type=%d, str=%s, int=%d\n", + bestPackage->header->index, + outValue->dataType, + outValue->dataType == bestValue->TYPE_STRING + ? String8(bestPackage->header->values.stringAt( + outValue->data, &len)).string() + : "", + outValue->data)); + return bestPackage->header->index; + } + + return BAD_INDEX; +} + +ssize_t ResTable::resolveReference(Res_value* value, ssize_t blockIndex, + uint32_t* outLastRef, uint32_t* inoutTypeSpecFlags) const +{ + int count=0; + while (blockIndex >= 0 && value->dataType == value->TYPE_REFERENCE + && value->data != 0 && count < 20) { + if (outLastRef) *outLastRef = value->data; + uint32_t lastRef = value->data; + uint32_t newFlags = 0; + const ssize_t newIndex = getResource(value->data, value, true, &newFlags); + //LOGI("Resolving reference d=%p: newIndex=%d, t=0x%02x, d=%p\n", + // (void*)lastRef, (int)newIndex, (int)value->dataType, (void*)value->data); + //printf("Getting reference 0x%08x: newIndex=%d\n", value->data, newIndex); + if (inoutTypeSpecFlags != NULL) *inoutTypeSpecFlags |= newFlags; + if (newIndex < 0) { + // This can fail if the resource being referenced is a style... + // in this case, just return the reference, and expect the + // caller to deal with. + return blockIndex; + } + blockIndex = newIndex; + count++; + } + return blockIndex; +} + +const char16_t* ResTable::valueToString( + const Res_value* value, size_t stringBlock, + char16_t tmpBuffer[TMP_BUFFER_SIZE], size_t* outLen) +{ + if (!value) { + return NULL; + } + if (value->dataType == value->TYPE_STRING) { + return getTableStringBlock(stringBlock)->stringAt(value->data, outLen); + } + // XXX do int to string conversions. + return NULL; +} + +ssize_t ResTable::lockBag(uint32_t resID, const bag_entry** outBag) const +{ + mLock.lock(); + ssize_t err = getBagLocked(resID, outBag); + if (err < NO_ERROR) { + //printf("*** get failed! unlocking\n"); + mLock.unlock(); + } + return err; +} + +void ResTable::unlockBag(const bag_entry* bag) const +{ + //printf("<<< unlockBag %p\n", this); + mLock.unlock(); +} + +void ResTable::lock() const +{ + mLock.lock(); +} + +void ResTable::unlock() const +{ + mLock.unlock(); +} + +ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, + uint32_t* outTypeSpecFlags) const +{ + if (mError != NO_ERROR) { + return mError; + } + + const ssize_t p = getResourcePackageIndex(resID); + const int t = Res_GETTYPE(resID); + const int e = Res_GETENTRY(resID); + + if (p < 0) { + LOGW("Invalid package identifier when getting bag for resource number 0x%08x", resID); + return BAD_INDEX; + } + if (t < 0) { + LOGW("No type identifier when getting bag for resource number 0x%08x", resID); + return BAD_INDEX; + } + + //printf("Get bag: id=0x%08x, p=%d, t=%d\n", resID, p, t); + PackageGroup* const grp = mPackageGroups[p]; + if (grp == NULL) { + LOGW("Bad identifier when getting bag for resource number 0x%08x", resID); + return false; + } + + if (t >= (int)grp->typeCount) { + LOGW("Type identifier 0x%x is larger than type count 0x%x", + t+1, (int)grp->typeCount); + return BAD_INDEX; + } + + const Package* const basePackage = grp->packages[0]; + + const Type* const typeConfigs = basePackage->getType(t); + + const size_t NENTRY = typeConfigs->entryCount; + if (e >= (int)NENTRY) { + LOGW("Entry identifier 0x%x is larger than entry count 0x%x", + e, (int)typeConfigs->entryCount); + return BAD_INDEX; + } + + // First see if we've already computed this bag... + if (grp->bags) { + bag_set** typeSet = grp->bags[t]; + if (typeSet) { + bag_set* set = typeSet[e]; + if (set) { + if (set != (bag_set*)0xFFFFFFFF) { + if (outTypeSpecFlags != NULL) { + *outTypeSpecFlags = set->typeSpecFlags; + } + *outBag = (bag_entry*)(set+1); + //LOGI("Found existing bag for: %p\n", (void*)resID); + return set->numAttrs; + } + LOGW("Attempt to retrieve bag 0x%08x which is invalid or in a cycle.", + resID); + return BAD_INDEX; + } + } + } + + // Bag not found, we need to compute it! + if (!grp->bags) { + grp->bags = (bag_set***)malloc(sizeof(bag_set*)*grp->typeCount); + if (!grp->bags) return NO_MEMORY; + memset(grp->bags, 0, sizeof(bag_set*)*grp->typeCount); + } + + bag_set** typeSet = grp->bags[t]; + if (!typeSet) { + typeSet = (bag_set**)malloc(sizeof(bag_set*)*NENTRY); + if (!typeSet) return NO_MEMORY; + memset(typeSet, 0, sizeof(bag_set*)*NENTRY); + grp->bags[t] = typeSet; + } + + // Mark that we are currently working on this one. + typeSet[e] = (bag_set*)0xFFFFFFFF; + + // This is what we are building. + bag_set* set = NULL; + + TABLE_NOISY(LOGI("Building bag: %p\n", (void*)resID)); + + // Now collect all bag attributes from all packages. + size_t ip = grp->packages.size(); + while (ip > 0) { + ip--; + + const Package* const package = grp->packages[ip]; + + const ResTable_type* type; + const ResTable_entry* entry; + const Type* typeClass; + LOGV("Getting entry pkg=%p, t=%d, e=%d\n", package, t, e); + ssize_t offset = getEntry(package, t, e, &mParams, &type, &entry, &typeClass); + LOGV("Resulting offset=%d\n", offset); + if (offset <= 0) { + if (offset < 0) { + if (set) free(set); + return offset; + } + continue; + } + + if ((dtohs(entry->flags)&entry->FLAG_COMPLEX) == 0) { + LOGW("Skipping entry %p in package table %d because it is not complex!\n", + (void*)resID, (int)ip); + continue; + } + + const uint16_t entrySize = dtohs(entry->size); + const uint32_t parent = entrySize >= sizeof(ResTable_map_entry) + ? dtohl(((const ResTable_map_entry*)entry)->parent.ident) : 0; + const uint32_t count = entrySize >= sizeof(ResTable_map_entry) + ? dtohl(((const ResTable_map_entry*)entry)->count) : 0; + + size_t N = count; + + TABLE_NOISY(LOGI("Found map: size=%p parent=%p count=%d\n", + entrySize, parent, count)); + + if (set == NULL) { + // If this map inherits from another, we need to start + // with its parent's values. Otherwise start out empty. + TABLE_NOISY(printf("Creating new bag, entrySize=0x%08x, parent=0x%08x\n", + entrySize, parent)); + if (parent) { + const bag_entry* parentBag; + uint32_t parentTypeSpecFlags = 0; + const ssize_t NP = getBagLocked(parent, &parentBag, &parentTypeSpecFlags); + const size_t NT = ((NP >= 0) ? NP : 0) + N; + set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*NT); + if (set == NULL) { + return NO_MEMORY; + } + if (NP > 0) { + memcpy(set+1, parentBag, NP*sizeof(bag_entry)); + set->numAttrs = NP; + TABLE_NOISY(LOGI("Initialized new bag with %d inherited attributes.\n", NP)); + } else { + TABLE_NOISY(LOGI("Initialized new bag with no inherited attributes.\n")); + set->numAttrs = 0; + } + set->availAttrs = NT; + set->typeSpecFlags = parentTypeSpecFlags; + } else { + set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*N); + if (set == NULL) { + return NO_MEMORY; + } + set->numAttrs = 0; + set->availAttrs = N; + set->typeSpecFlags = 0; + } + } + + if (typeClass->typeSpecFlags != NULL) { + set->typeSpecFlags |= dtohl(typeClass->typeSpecFlags[e]); + } else { + set->typeSpecFlags = -1; + } + + // Now merge in the new attributes... + ssize_t curOff = offset; + const ResTable_map* map; + bag_entry* entries = (bag_entry*)(set+1); + size_t curEntry = 0; + uint32_t pos = 0; + TABLE_NOISY(LOGI("Starting with set %p, entries=%p, avail=%d\n", + set, entries, set->availAttrs)); + while (pos < count) { + TABLE_NOISY(printf("Now at %p\n", (void*)curOff)); + + if ((size_t)curOff > (dtohl(type->header.size)-sizeof(ResTable_map))) { + LOGW("ResTable_map at %d is beyond type chunk data %d", + (int)curOff, dtohl(type->header.size)); + return BAD_TYPE; + } + map = (const ResTable_map*)(((const uint8_t*)type) + curOff); + N++; + + const uint32_t newName = htodl(map->name.ident); + bool isInside; + uint32_t oldName = 0; + while ((isInside=(curEntry < set->numAttrs)) + && (oldName=entries[curEntry].map.name.ident) < newName) { + TABLE_NOISY(printf("#%d: Keeping existing attribute: 0x%08x\n", + curEntry, entries[curEntry].map.name.ident)); + curEntry++; + } + + if ((!isInside) || oldName != newName) { + // This is a new attribute... figure out what to do with it. + if (set->numAttrs >= set->availAttrs) { + // Need to alloc more memory... + const size_t newAvail = set->availAttrs+N; + set = (bag_set*)realloc(set, + sizeof(bag_set) + + sizeof(bag_entry)*newAvail); + if (set == NULL) { + return NO_MEMORY; + } + set->availAttrs = newAvail; + entries = (bag_entry*)(set+1); + TABLE_NOISY(printf("Reallocated set %p, entries=%p, avail=%d\n", + set, entries, set->availAttrs)); + } + if (isInside) { + // Going in the middle, need to make space. + memmove(entries+curEntry+1, entries+curEntry, + sizeof(bag_entry)*(set->numAttrs-curEntry)); + set->numAttrs++; + } + TABLE_NOISY(printf("#%d: Inserting new attribute: 0x%08x\n", + curEntry, newName)); + } else { + TABLE_NOISY(printf("#%d: Replacing existing attribute: 0x%08x\n", + curEntry, oldName)); + } + + bag_entry* cur = entries+curEntry; + + cur->stringBlock = package->header->index; + cur->map.name.ident = newName; + cur->map.value.copyFrom_dtoh(map->value); + TABLE_NOISY(printf("Setting entry #%d %p: block=%d, name=0x%08x, type=%d, data=0x%08x\n", + curEntry, cur, cur->stringBlock, cur->map.name.ident, + cur->map.value.dataType, cur->map.value.data)); + + // On to the next! + curEntry++; + pos++; + const size_t size = dtohs(map->value.size); + curOff += size + sizeof(*map)-sizeof(map->value); + }; + if (curEntry > set->numAttrs) { + set->numAttrs = curEntry; + } + } + + // And this is it... + typeSet[e] = set; + if (set) { + if (outTypeSpecFlags != NULL) { + *outTypeSpecFlags = set->typeSpecFlags; + } + *outBag = (bag_entry*)(set+1); + TABLE_NOISY(LOGI("Returning %d attrs\n", set->numAttrs)); + return set->numAttrs; + } + return BAD_INDEX; +} + +void ResTable::setParameters(const ResTable_config* params) +{ + mLock.lock(); + TABLE_GETENTRY(LOGI("Setting parameters: imsi:%d/%d lang:%c%c cnt:%c%c " + "orien:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n", + params->mcc, params->mnc, + params->language[0] ? params->language[0] : '-', + params->language[1] ? params->language[1] : '-', + params->country[0] ? params->country[0] : '-', + params->country[1] ? params->country[1] : '-', + params->orientation, + params->touchscreen, + params->density, + params->keyboard, + params->inputFlags, + params->navigation, + params->screenWidth, + params->screenHeight)); + mParams = *params; + for (size_t i=0; iclearBagCache(); + } + mLock.unlock(); +} + +void ResTable::getParameters(ResTable_config* params) const +{ + mLock.lock(); + *params = mParams; + mLock.unlock(); +} + +struct id_name_map { + uint32_t id; + size_t len; + char16_t name[6]; +}; + +const static id_name_map ID_NAMES[] = { + { ResTable_map::ATTR_TYPE, 5, { '^', 't', 'y', 'p', 'e' } }, + { ResTable_map::ATTR_L10N, 5, { '^', 'l', '1', '0', 'n' } }, + { ResTable_map::ATTR_MIN, 4, { '^', 'm', 'i', 'n' } }, + { ResTable_map::ATTR_MAX, 4, { '^', 'm', 'a', 'x' } }, + { ResTable_map::ATTR_OTHER, 6, { '^', 'o', 't', 'h', 'e', 'r' } }, + { ResTable_map::ATTR_ZERO, 5, { '^', 'z', 'e', 'r', 'o' } }, + { ResTable_map::ATTR_ONE, 4, { '^', 'o', 'n', 'e' } }, + { ResTable_map::ATTR_TWO, 4, { '^', 't', 'w', 'o' } }, + { ResTable_map::ATTR_FEW, 4, { '^', 'f', 'e', 'w' } }, + { ResTable_map::ATTR_MANY, 5, { '^', 'm', 'a', 'n', 'y' } }, +}; + +uint32_t ResTable::identifierForName(const char16_t* name, size_t nameLen, + const char16_t* type, size_t typeLen, + const char16_t* package, + size_t packageLen, + uint32_t* outTypeSpecFlags) const +{ + TABLE_SUPER_NOISY(printf("Identifier for name: error=%d\n", mError)); + + // Check for internal resource identifier as the very first thing, so + // that we will always find them even when there are no resources. + if (name[0] == '^') { + const int N = (sizeof(ID_NAMES)/sizeof(ID_NAMES[0])); + size_t len; + for (int i=0; ilen; + if (len != nameLen) { + continue; + } + for (size_t j=1; jname[j] != name[j]) { + goto nope; + } + } + return m->id; +nope: + ; + } + if (nameLen > 7) { + if (name[1] == 'i' && name[2] == 'n' + && name[3] == 'd' && name[4] == 'e' && name[5] == 'x' + && name[6] == '_') { + int index = atoi(String8(name + 7, nameLen - 7).string()); + if (Res_CHECKID(index)) { + LOGW("Array resource index: %d is too large.", + index); + return 0; + } + return Res_MAKEARRAY(index); + } + } + return 0; + } + + if (mError != NO_ERROR) { + return 0; + } + + // Figure out the package and type we are looking in... + + const char16_t* packageEnd = NULL; + const char16_t* typeEnd = NULL; + const char16_t* const nameEnd = name+nameLen; + const char16_t* p = name; + while (p < nameEnd) { + if (*p == ':') packageEnd = p; + else if (*p == '/') typeEnd = p; + p++; + } + if (*name == '@') name++; + if (name >= nameEnd) { + return 0; + } + + if (packageEnd) { + package = name; + packageLen = packageEnd-name; + name = packageEnd+1; + } else if (!package) { + return 0; + } + + if (typeEnd) { + type = name; + typeLen = typeEnd-name; + name = typeEnd+1; + } else if (!type) { + return 0; + } + + if (name >= nameEnd) { + return 0; + } + nameLen = nameEnd-name; + + TABLE_NOISY(printf("Looking for identifier: type=%s, name=%s, package=%s\n", + String8(type, typeLen).string(), + String8(name, nameLen).string(), + String8(package, packageLen).string())); + + const size_t NG = mPackageGroups.size(); + for (size_t ig=0; igname.string(), group->name.size())) { + TABLE_NOISY(printf("Skipping package group: %s\n", String8(group->name).string())); + continue; + } + + const ssize_t ti = group->typeStrings.indexOfString(type, typeLen); + if (ti < 0) { + TABLE_NOISY(printf("Type not found in package %s\n", String8(group->name).string())); + continue; + } + + const ssize_t ei = group->keyStrings.indexOfString(name, nameLen); + if (ei < 0) { + TABLE_NOISY(printf("Name not found in package %s\n", String8(group->name).string())); + continue; + } + + TABLE_NOISY(printf("Search indices: type=%d, name=%d\n", ti, ei)); + + const Type* const typeConfigs = group->packages[0]->getType(ti); + if (typeConfigs == NULL || typeConfigs->configs.size() <= 0) { + TABLE_NOISY(printf("Expected type structure not found in package %s for idnex %d\n", + String8(group->name).string(), ti)); + } + + size_t NTC = typeConfigs->configs.size(); + for (size_t tci=0; tciconfigs[tci]; + const uint32_t typeOffset = dtohl(ty->entriesStart); + + const uint8_t* const end = ((const uint8_t*)ty) + dtohl(ty->header.size); + const uint32_t* const eindex = (const uint32_t*) + (((const uint8_t*)ty) + dtohs(ty->header.headerSize)); + + const size_t NE = dtohl(ty->entryCount); + for (size_t i=0; i (dtohl(ty->header.size)-sizeof(ResTable_entry))) { + LOGW("ResTable_entry at %d is beyond type chunk data %d", + offset, dtohl(ty->header.size)); + return 0; + } + if ((offset&0x3) != 0) { + LOGW("ResTable_entry at %d (pkg=%d type=%d ent=%d) is not on an integer boundary when looking for %s:%s/%s", + (int)offset, (int)group->id, (int)ti+1, (int)i, + String8(package, packageLen).string(), + String8(type, typeLen).string(), + String8(name, nameLen).string()); + return 0; + } + + const ResTable_entry* const entry = (const ResTable_entry*) + (((const uint8_t*)ty) + offset); + if (dtohs(entry->size) < sizeof(*entry)) { + LOGW("ResTable_entry size %d is too small", dtohs(entry->size)); + return BAD_TYPE; + } + + TABLE_SUPER_NOISY(printf("Looking at entry #%d: want str %d, have %d\n", + i, ei, dtohl(entry->key.index))); + if (dtohl(entry->key.index) == (size_t)ei) { + if (outTypeSpecFlags) { + *outTypeSpecFlags = typeConfigs->typeSpecFlags[i]; + } + return Res_MAKEID(group->id-1, ti, i); + } + } + } + } + + return 0; +} + +bool ResTable::expandResourceRef(const uint16_t* refStr, size_t refLen, + String16* outPackage, + String16* outType, + String16* outName, + const String16* defType, + const String16* defPackage, + const char** outErrorMsg) +{ + const char16_t* packageEnd = NULL; + const char16_t* typeEnd = NULL; + const char16_t* p = refStr; + const char16_t* const end = p + refLen; + while (p < end) { + if (*p == ':') packageEnd = p; + else if (*p == '/') { + typeEnd = p; + break; + } + p++; + } + p = refStr; + if (*p == '@') p++; + + if (packageEnd) { + *outPackage = String16(p, packageEnd-p); + p = packageEnd+1; + } else { + if (!defPackage) { + if (outErrorMsg) { + *outErrorMsg = "No resource package specified"; + } + return false; + } + *outPackage = *defPackage; + } + if (typeEnd) { + *outType = String16(p, typeEnd-p); + p = typeEnd+1; + } else { + if (!defType) { + if (outErrorMsg) { + *outErrorMsg = "No resource type specified"; + } + return false; + } + *outType = *defType; + } + *outName = String16(p, end-p); + return true; +} + +static uint32_t get_hex(char c, bool* outError) +{ + if (c >= '0' && c <= '9') { + return c - '0'; + } else if (c >= 'a' && c <= 'f') { + return c - 'a' + 0xa; + } else if (c >= 'A' && c <= 'F') { + return c - 'A' + 0xa; + } + *outError = true; + return 0; +} + +struct unit_entry +{ + const char* name; + size_t len; + uint8_t type; + uint32_t unit; + float scale; +}; + +static const unit_entry unitNames[] = { + { "px", strlen("px"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_PX, 1.0f }, + { "dip", strlen("dip"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_DIP, 1.0f }, + { "dp", strlen("dp"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_DIP, 1.0f }, + { "sp", strlen("sp"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_SP, 1.0f }, + { "pt", strlen("pt"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_PT, 1.0f }, + { "in", strlen("in"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_IN, 1.0f }, + { "mm", strlen("mm"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_MM, 1.0f }, + { "%", strlen("%"), Res_value::TYPE_FRACTION, Res_value::COMPLEX_UNIT_FRACTION, 1.0f/100 }, + { "%p", strlen("%p"), Res_value::TYPE_FRACTION, Res_value::COMPLEX_UNIT_FRACTION_PARENT, 1.0f/100 }, + { NULL, 0, 0, 0, 0 } +}; + +static bool parse_unit(const char* str, Res_value* outValue, + float* outScale, const char** outEnd) +{ + const char* end = str; + while (*end != 0 && !isspace((unsigned char)*end)) { + end++; + } + const size_t len = end-str; + + const char* realEnd = end; + while (*realEnd != 0 && isspace((unsigned char)*realEnd)) { + realEnd++; + } + if (*realEnd != 0) { + return false; + } + + const unit_entry* cur = unitNames; + while (cur->name) { + if (len == cur->len && strncmp(cur->name, str, len) == 0) { + outValue->dataType = cur->type; + outValue->data = cur->unit << Res_value::COMPLEX_UNIT_SHIFT; + *outScale = cur->scale; + *outEnd = end; + //printf("Found unit %s for %s\n", cur->name, str); + return true; + } + cur++; + } + + return false; +} + + +bool ResTable::stringToInt(const char16_t* s, size_t len, Res_value* outValue) +{ + while (len > 0 && isspace16(*s)) { + s++; + len--; + } + + if (len <= 0) { + return false; + } + + size_t i = 0; + int32_t val = 0; + bool neg = false; + + if (*s == '-') { + neg = true; + i++; + } + + if (s[i] < '0' || s[i] > '9') { + return false; + } + + // Decimal or hex? + if (s[i] == '0' && s[i+1] == 'x') { + if (outValue) + outValue->dataType = outValue->TYPE_INT_HEX; + i += 2; + bool error = false; + while (i < len && !error) { + val = (val*16) + get_hex(s[i], &error); + i++; + } + if (error) { + return false; + } + } else { + if (outValue) + outValue->dataType = outValue->TYPE_INT_DEC; + while (i < len) { + if (s[i] < '0' || s[i] > '9') { + return false; + } + val = (val*10) + s[i]-'0'; + i++; + } + } + + if (neg) val = -val; + + while (i < len && isspace16(s[i])) { + i++; + } + + if (i == len) { + if (outValue) + outValue->data = val; + return true; + } + + return false; +} + +bool ResTable::stringToFloat(const char16_t* s, size_t len, Res_value* outValue) +{ + while (len > 0 && isspace16(*s)) { + s++; + len--; + } + + if (len <= 0) { + return false; + } + + char buf[128]; + int i=0; + while (len > 0 && *s != 0 && i < 126) { + if (*s > 255) { + return false; + } + buf[i++] = *s++; + len--; + } + + if (len > 0) { + return false; + } + if (buf[0] < '0' && buf[0] > '9' && buf[0] != '.') { + return false; + } + + buf[i] = 0; + const char* end; + float f = strtof(buf, (char**)&end); + + if (*end != 0 && !isspace((unsigned char)*end)) { + // Might be a unit... + float scale; + if (parse_unit(end, outValue, &scale, &end)) { + f *= scale; + const bool neg = f < 0; + if (neg) f = -f; + uint64_t bits = (uint64_t)(f*(1<<23)+.5f); + uint32_t radix; + uint32_t shift; + if ((bits&0x7fffff) == 0) { + // Always use 23p0 if there is no fraction, just to make + // things easier to read. + radix = Res_value::COMPLEX_RADIX_23p0; + shift = 23; + } else if ((bits&0xffffffffff800000LL) == 0) { + // Magnitude is zero -- can fit in 0 bits of precision. + radix = Res_value::COMPLEX_RADIX_0p23; + shift = 0; + } else if ((bits&0xffffffff80000000LL) == 0) { + // Magnitude can fit in 8 bits of precision. + radix = Res_value::COMPLEX_RADIX_8p15; + shift = 8; + } else if ((bits&0xffffff8000000000LL) == 0) { + // Magnitude can fit in 16 bits of precision. + radix = Res_value::COMPLEX_RADIX_16p7; + shift = 16; + } else { + // Magnitude needs entire range, so no fractional part. + radix = Res_value::COMPLEX_RADIX_23p0; + shift = 23; + } + int32_t mantissa = (int32_t)( + (bits>>shift) & Res_value::COMPLEX_MANTISSA_MASK); + if (neg) { + mantissa = (-mantissa) & Res_value::COMPLEX_MANTISSA_MASK; + } + outValue->data |= + (radix<data); + return true; + } + return false; + } + + while (*end != 0 && isspace((unsigned char)*end)) { + end++; + } + + if (*end == 0) { + if (outValue) { + outValue->dataType = outValue->TYPE_FLOAT; + *(float*)(&outValue->data) = f; + return true; + } + } + + return false; +} + +bool ResTable::stringToValue(Res_value* outValue, String16* outString, + const char16_t* s, size_t len, + bool preserveSpaces, bool coerceType, + uint32_t attrID, + const String16* defType, + const String16* defPackage, + Accessor* accessor, + void* accessorCookie, + uint32_t attrType, + bool enforcePrivate) const +{ + bool localizationSetting = accessor != NULL && accessor->getLocalizationSetting(); + const char* errorMsg = NULL; + + outValue->size = sizeof(Res_value); + outValue->res0 = 0; + + // First strip leading/trailing whitespace. Do this before handling + // escapes, so they can be used to force whitespace into the string. + if (!preserveSpaces) { + while (len > 0 && isspace16(*s)) { + s++; + len--; + } + while (len > 0 && isspace16(s[len-1])) { + len--; + } + // If the string ends with '\', then we keep the space after it. + if (len > 0 && s[len-1] == '\\' && s[len] != 0) { + len++; + } + } + + //printf("Value for: %s\n", String8(s, len).string()); + + uint32_t l10nReq = ResTable_map::L10N_NOT_REQUIRED; + uint32_t attrMin = 0x80000000, attrMax = 0x7fffffff; + bool fromAccessor = false; + if (attrID != 0 && !Res_INTERNALID(attrID)) { + const ssize_t p = getResourcePackageIndex(attrID); + const bag_entry* bag; + ssize_t cnt = p >= 0 ? lockBag(attrID, &bag) : -1; + //printf("For attr 0x%08x got bag of %d\n", attrID, cnt); + if (cnt >= 0) { + while (cnt > 0) { + //printf("Entry 0x%08x = 0x%08x\n", bag->map.name.ident, bag->map.value.data); + switch (bag->map.name.ident) { + case ResTable_map::ATTR_TYPE: + attrType = bag->map.value.data; + break; + case ResTable_map::ATTR_MIN: + attrMin = bag->map.value.data; + break; + case ResTable_map::ATTR_MAX: + attrMax = bag->map.value.data; + break; + case ResTable_map::ATTR_L10N: + l10nReq = bag->map.value.data; + break; + } + bag++; + cnt--; + } + unlockBag(bag); + } else if (accessor && accessor->getAttributeType(attrID, &attrType)) { + fromAccessor = true; + if (attrType == ResTable_map::TYPE_ENUM + || attrType == ResTable_map::TYPE_FLAGS + || attrType == ResTable_map::TYPE_INTEGER) { + accessor->getAttributeMin(attrID, &attrMin); + accessor->getAttributeMax(attrID, &attrMax); + } + if (localizationSetting) { + l10nReq = accessor->getAttributeL10N(attrID); + } + } + } + + const bool canStringCoerce = + coerceType && (attrType&ResTable_map::TYPE_STRING) != 0; + + if (*s == '@') { + outValue->dataType = outValue->TYPE_REFERENCE; + + // Note: we don't check attrType here because the reference can + // be to any other type; we just need to count on the client making + // sure the referenced type is correct. + + //printf("Looking up ref: %s\n", String8(s, len).string()); + + // It's a reference! + if (len == 5 && s[1]=='n' && s[2]=='u' && s[3]=='l' && s[4]=='l') { + outValue->data = 0; + return true; + } else { + bool createIfNotFound = false; + const char16_t* resourceRefName; + int resourceNameLen; + if (len > 2 && s[1] == '+') { + createIfNotFound = true; + resourceRefName = s + 2; + resourceNameLen = len - 2; + } else if (len > 2 && s[1] == '*') { + enforcePrivate = false; + resourceRefName = s + 2; + resourceNameLen = len - 2; + } else { + createIfNotFound = false; + resourceRefName = s + 1; + resourceNameLen = len - 1; + } + String16 package, type, name; + if (!expandResourceRef(resourceRefName,resourceNameLen, &package, &type, &name, + defType, defPackage, &errorMsg)) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, errorMsg); + } + return false; + } + + uint32_t specFlags = 0; + uint32_t rid = identifierForName(name.string(), name.size(), type.string(), + type.size(), package.string(), package.size(), &specFlags); + if (rid != 0) { + if (enforcePrivate) { + if ((specFlags&ResTable_typeSpec::SPEC_PUBLIC) == 0) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "Resource is not public."); + } + return false; + } + } + if (!accessor) { + outValue->data = rid; + return true; + } + rid = Res_MAKEID( + accessor->getRemappedPackage(Res_GETPACKAGE(rid)), + Res_GETTYPE(rid), Res_GETENTRY(rid)); + TABLE_NOISY(printf("Incl %s:%s/%s: 0x%08x\n", + String8(package).string(), String8(type).string(), + String8(name).string(), rid)); + outValue->data = rid; + return true; + } + + if (accessor) { + uint32_t rid = accessor->getCustomResourceWithCreation(package, type, name, + createIfNotFound); + if (rid != 0) { + TABLE_NOISY(printf("Pckg %s:%s/%s: 0x%08x\n", + String8(package).string(), String8(type).string(), + String8(name).string(), rid)); + outValue->data = rid; + return true; + } + } + } + + if (accessor != NULL) { + accessor->reportError(accessorCookie, "No resource found that matches the given name"); + } + return false; + } + + // if we got to here, and localization is required and it's not a reference, + // complain and bail. + if (l10nReq == ResTable_map::L10N_SUGGESTED) { + if (localizationSetting) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "This attribute must be localized."); + } + } + } + + if (*s == '#') { + // It's a color! Convert to an integer of the form 0xaarrggbb. + uint32_t color = 0; + bool error = false; + if (len == 4) { + outValue->dataType = outValue->TYPE_INT_COLOR_RGB4; + color |= 0xFF000000; + color |= get_hex(s[1], &error) << 20; + color |= get_hex(s[1], &error) << 16; + color |= get_hex(s[2], &error) << 12; + color |= get_hex(s[2], &error) << 8; + color |= get_hex(s[3], &error) << 4; + color |= get_hex(s[3], &error); + } else if (len == 5) { + outValue->dataType = outValue->TYPE_INT_COLOR_ARGB4; + color |= get_hex(s[1], &error) << 28; + color |= get_hex(s[1], &error) << 24; + color |= get_hex(s[2], &error) << 20; + color |= get_hex(s[2], &error) << 16; + color |= get_hex(s[3], &error) << 12; + color |= get_hex(s[3], &error) << 8; + color |= get_hex(s[4], &error) << 4; + color |= get_hex(s[4], &error); + } else if (len == 7) { + outValue->dataType = outValue->TYPE_INT_COLOR_RGB8; + color |= 0xFF000000; + color |= get_hex(s[1], &error) << 20; + color |= get_hex(s[2], &error) << 16; + color |= get_hex(s[3], &error) << 12; + color |= get_hex(s[4], &error) << 8; + color |= get_hex(s[5], &error) << 4; + color |= get_hex(s[6], &error); + } else if (len == 9) { + outValue->dataType = outValue->TYPE_INT_COLOR_ARGB8; + color |= get_hex(s[1], &error) << 28; + color |= get_hex(s[2], &error) << 24; + color |= get_hex(s[3], &error) << 20; + color |= get_hex(s[4], &error) << 16; + color |= get_hex(s[5], &error) << 12; + color |= get_hex(s[6], &error) << 8; + color |= get_hex(s[7], &error) << 4; + color |= get_hex(s[8], &error); + } else { + error = true; + } + if (!error) { + if ((attrType&ResTable_map::TYPE_COLOR) == 0) { + if (!canStringCoerce) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, + "Color types not allowed"); + } + return false; + } + } else { + outValue->data = color; + //printf("Color input=%s, output=0x%x\n", String8(s, len).string(), color); + return true; + } + } else { + if ((attrType&ResTable_map::TYPE_COLOR) != 0) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "Color value not valid --" + " must be #rgb, #argb, #rrggbb, or #aarrggbb"); + } + #if 0 + fprintf(stderr, "%s: Color ID %s value %s is not valid\n", + "Resource File", //(const char*)in->getPrintableSource(), + String8(*curTag).string(), + String8(s, len).string()); + #endif + return false; + } + } + } + + if (*s == '?') { + outValue->dataType = outValue->TYPE_ATTRIBUTE; + + // Note: we don't check attrType here because the reference can + // be to any other type; we just need to count on the client making + // sure the referenced type is correct. + + //printf("Looking up attr: %s\n", String8(s, len).string()); + + static const String16 attr16("attr"); + String16 package, type, name; + if (!expandResourceRef(s+1, len-1, &package, &type, &name, + &attr16, defPackage, &errorMsg)) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, errorMsg); + } + return false; + } + + //printf("Pkg: %s, Type: %s, Name: %s\n", + // String8(package).string(), String8(type).string(), + // String8(name).string()); + uint32_t specFlags = 0; + uint32_t rid = + identifierForName(name.string(), name.size(), + type.string(), type.size(), + package.string(), package.size(), &specFlags); + if (rid != 0) { + if (enforcePrivate) { + if ((specFlags&ResTable_typeSpec::SPEC_PUBLIC) == 0) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "Attribute is not public."); + } + return false; + } + } + if (!accessor) { + outValue->data = rid; + return true; + } + rid = Res_MAKEID( + accessor->getRemappedPackage(Res_GETPACKAGE(rid)), + Res_GETTYPE(rid), Res_GETENTRY(rid)); + //printf("Incl %s:%s/%s: 0x%08x\n", + // String8(package).string(), String8(type).string(), + // String8(name).string(), rid); + outValue->data = rid; + return true; + } + + if (accessor) { + uint32_t rid = accessor->getCustomResource(package, type, name); + if (rid != 0) { + //printf("Mine %s:%s/%s: 0x%08x\n", + // String8(package).string(), String8(type).string(), + // String8(name).string(), rid); + outValue->data = rid; + return true; + } + } + + if (accessor != NULL) { + accessor->reportError(accessorCookie, "No resource found that matches the given name"); + } + return false; + } + + if (stringToInt(s, len, outValue)) { + if ((attrType&ResTable_map::TYPE_INTEGER) == 0) { + // If this type does not allow integers, but does allow floats, + // fall through on this error case because the float type should + // be able to accept any integer value. + if (!canStringCoerce && (attrType&ResTable_map::TYPE_FLOAT) == 0) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "Integer types not allowed"); + } + return false; + } + } else { + if (((int32_t)outValue->data) < ((int32_t)attrMin) + || ((int32_t)outValue->data) > ((int32_t)attrMax)) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "Integer value out of range"); + } + return false; + } + return true; + } + } + + if (stringToFloat(s, len, outValue)) { + if (outValue->dataType == Res_value::TYPE_DIMENSION) { + if ((attrType&ResTable_map::TYPE_DIMENSION) != 0) { + return true; + } + if (!canStringCoerce) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "Dimension types not allowed"); + } + return false; + } + } else if (outValue->dataType == Res_value::TYPE_FRACTION) { + if ((attrType&ResTable_map::TYPE_FRACTION) != 0) { + return true; + } + if (!canStringCoerce) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "Fraction types not allowed"); + } + return false; + } + } else if ((attrType&ResTable_map::TYPE_FLOAT) == 0) { + if (!canStringCoerce) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "Float types not allowed"); + } + return false; + } + } else { + return true; + } + } + + if (len == 4) { + if ((s[0] == 't' || s[0] == 'T') && + (s[1] == 'r' || s[1] == 'R') && + (s[2] == 'u' || s[2] == 'U') && + (s[3] == 'e' || s[3] == 'E')) { + if ((attrType&ResTable_map::TYPE_BOOLEAN) == 0) { + if (!canStringCoerce) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "Boolean types not allowed"); + } + return false; + } + } else { + outValue->dataType = outValue->TYPE_INT_BOOLEAN; + outValue->data = (uint32_t)-1; + return true; + } + } + } + + if (len == 5) { + if ((s[0] == 'f' || s[0] == 'F') && + (s[1] == 'a' || s[1] == 'A') && + (s[2] == 'l' || s[2] == 'L') && + (s[3] == 's' || s[3] == 'S') && + (s[4] == 'e' || s[4] == 'E')) { + if ((attrType&ResTable_map::TYPE_BOOLEAN) == 0) { + if (!canStringCoerce) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "Boolean types not allowed"); + } + return false; + } + } else { + outValue->dataType = outValue->TYPE_INT_BOOLEAN; + outValue->data = 0; + return true; + } + } + } + + if ((attrType&ResTable_map::TYPE_ENUM) != 0) { + const ssize_t p = getResourcePackageIndex(attrID); + const bag_entry* bag; + ssize_t cnt = p >= 0 ? lockBag(attrID, &bag) : -1; + //printf("Got %d for enum\n", cnt); + if (cnt >= 0) { + resource_name rname; + while (cnt > 0) { + if (!Res_INTERNALID(bag->map.name.ident)) { + //printf("Trying attr #%08x\n", bag->map.name.ident); + if (getResourceName(bag->map.name.ident, &rname)) { + #if 0 + printf("Matching %s against %s (0x%08x)\n", + String8(s, len).string(), + String8(rname.name, rname.nameLen).string(), + bag->map.name.ident); + #endif + if (strzcmp16(s, len, rname.name, rname.nameLen) == 0) { + outValue->dataType = bag->map.value.dataType; + outValue->data = bag->map.value.data; + unlockBag(bag); + return true; + } + } + + } + bag++; + cnt--; + } + unlockBag(bag); + } + + if (fromAccessor) { + if (accessor->getAttributeEnum(attrID, s, len, outValue)) { + return true; + } + } + } + + if ((attrType&ResTable_map::TYPE_FLAGS) != 0) { + const ssize_t p = getResourcePackageIndex(attrID); + const bag_entry* bag; + ssize_t cnt = p >= 0 ? lockBag(attrID, &bag) : -1; + //printf("Got %d for flags\n", cnt); + if (cnt >= 0) { + bool failed = false; + resource_name rname; + outValue->dataType = Res_value::TYPE_INT_HEX; + outValue->data = 0; + const char16_t* end = s + len; + const char16_t* pos = s; + while (pos < end && !failed) { + const char16_t* start = pos; + end++; + while (pos < end && *pos != '|') { + pos++; + } + //printf("Looking for: %s\n", String8(start, pos-start).string()); + const bag_entry* bagi = bag; + ssize_t i; + for (i=0; imap.name.ident)) { + //printf("Trying attr #%08x\n", bagi->map.name.ident); + if (getResourceName(bagi->map.name.ident, &rname)) { + #if 0 + printf("Matching %s against %s (0x%08x)\n", + String8(start,pos-start).string(), + String8(rname.name, rname.nameLen).string(), + bagi->map.name.ident); + #endif + if (strzcmp16(start, pos-start, rname.name, rname.nameLen) == 0) { + outValue->data |= bagi->map.value.data; + break; + } + } + } + } + if (i >= cnt) { + // Didn't find this flag identifier. + failed = true; + } + if (pos < end) { + pos++; + } + } + unlockBag(bag); + if (!failed) { + //printf("Final flag value: 0x%lx\n", outValue->data); + return true; + } + } + + + if (fromAccessor) { + if (accessor->getAttributeFlags(attrID, s, len, outValue)) { + //printf("Final flag value: 0x%lx\n", outValue->data); + return true; + } + } + } + + if ((attrType&ResTable_map::TYPE_STRING) == 0) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "String types not allowed"); + } + return false; + } + + // Generic string handling... + outValue->dataType = outValue->TYPE_STRING; + if (outString) { + bool failed = collectString(outString, s, len, preserveSpaces, &errorMsg); + if (accessor != NULL) { + accessor->reportError(accessorCookie, errorMsg); + } + return failed; + } + + return true; +} + +bool ResTable::collectString(String16* outString, + const char16_t* s, size_t len, + bool preserveSpaces, + const char** outErrorMsg, + bool append) +{ + String16 tmp; + + char quoted = 0; + const char16_t* p = s; + while (p < (s+len)) { + while (p < (s+len)) { + const char16_t c = *p; + if (c == '\\') { + break; + } + if (!preserveSpaces) { + if (quoted == 0 && isspace16(c) + && (c != ' ' || isspace16(*(p+1)))) { + break; + } + if (c == '"' && (quoted == 0 || quoted == '"')) { + break; + } + if (c == '\'' && (quoted == 0 || quoted == '\'')) { + break; + } + } + p++; + } + if (p < (s+len)) { + if (p > s) { + tmp.append(String16(s, p-s)); + } + if (!preserveSpaces && (*p == '"' || *p == '\'')) { + if (quoted == 0) { + quoted = *p; + } else { + quoted = 0; + } + p++; + } else if (!preserveSpaces && isspace16(*p)) { + // Space outside of a quote -- consume all spaces and + // leave a single plain space char. + tmp.append(String16(" ")); + p++; + while (p < (s+len) && isspace16(*p)) { + p++; + } + } else if (*p == '\\') { + p++; + if (p < (s+len)) { + switch (*p) { + case 't': + tmp.append(String16("\t")); + break; + case 'n': + tmp.append(String16("\n")); + break; + case '#': + tmp.append(String16("#")); + break; + case '@': + tmp.append(String16("@")); + break; + case '?': + tmp.append(String16("?")); + break; + case '"': + tmp.append(String16("\"")); + break; + case '\'': + tmp.append(String16("'")); + break; + case '\\': + tmp.append(String16("\\")); + break; + case 'u': + { + char16_t chr = 0; + int i = 0; + while (i < 4 && p[1] != 0) { + p++; + i++; + int c; + if (*p >= '0' && *p <= '9') { + c = *p - '0'; + } else if (*p >= 'a' && *p <= 'f') { + c = *p - 'a' + 10; + } else if (*p >= 'A' && *p <= 'F') { + c = *p - 'A' + 10; + } else { + if (outErrorMsg) { + *outErrorMsg = "Bad character in \\u unicode escape sequence"; + } + return false; + } + chr = (chr<<4) | c; + } + tmp.append(String16(&chr, 1)); + } break; + default: + // ignore unknown escape chars. + break; + } + p++; + } + } + len -= (p-s); + s = p; + } + } + + if (tmp.size() != 0) { + if (len > 0) { + tmp.append(String16(s, len)); + } + if (append) { + outString->append(tmp); + } else { + outString->setTo(tmp); + } + } else { + if (append) { + outString->append(String16(s, len)); + } else { + outString->setTo(s, len); + } + } + + return true; +} + +size_t ResTable::getBasePackageCount() const +{ + if (mError != NO_ERROR) { + return 0; + } + return mPackageGroups.size(); +} + +const char16_t* ResTable::getBasePackageName(size_t idx) const +{ + if (mError != NO_ERROR) { + return 0; + } + LOG_FATAL_IF(idx >= mPackageGroups.size(), + "Requested package index %d past package count %d", + (int)idx, (int)mPackageGroups.size()); + return mPackageGroups[idx]->name.string(); +} + +uint32_t ResTable::getBasePackageId(size_t idx) const +{ + if (mError != NO_ERROR) { + return 0; + } + LOG_FATAL_IF(idx >= mPackageGroups.size(), + "Requested package index %d past package count %d", + (int)idx, (int)mPackageGroups.size()); + return mPackageGroups[idx]->id; +} + +size_t ResTable::getTableCount() const +{ + return mHeaders.size(); +} + +const ResStringPool* ResTable::getTableStringBlock(size_t index) const +{ + return &mHeaders[index]->values; +} + +void* ResTable::getTableCookie(size_t index) const +{ + return mHeaders[index]->cookie; +} + +void ResTable::getConfigurations(Vector* configs) const +{ + const size_t I = mPackageGroups.size(); + for (size_t i=0; ipackages.size(); + for (size_t j=0; jpackages[j]; + const size_t K = package->types.size(); + for (size_t k=0; ktypes[k]; + if (type == NULL) continue; + const size_t L = type->configs.size(); + for (size_t l=0; lconfigs[l]; + const ResTable_config* cfg = &config->config; + // only insert unique + const size_t M = configs->size(); + size_t m; + for (m=0; madd(*cfg); + } + } + } + } + } +} + +void ResTable::getLocales(Vector* locales) const +{ + Vector configs; + LOGD("calling getConfigurations"); + getConfigurations(&configs); + LOGD("called getConfigurations size=%d", (int)configs.size()); + const size_t I = configs.size(); + for (size_t i=0; isize(); + size_t j; + for (j=0; jadd(String8(locale)); + } + } +} + +ssize_t ResTable::getEntry( + const Package* package, int typeIndex, int entryIndex, + const ResTable_config* config, + const ResTable_type** outType, const ResTable_entry** outEntry, + const Type** outTypeClass) const +{ + LOGV("Getting entry from package %p\n", package); + const ResTable_package* const pkg = package->package; + + const Type* allTypes = package->getType(typeIndex); + LOGV("allTypes=%p\n", allTypes); + if (allTypes == NULL) { + LOGV("Skipping entry type index 0x%02x because type is NULL!\n", typeIndex); + return 0; + } + + if ((size_t)entryIndex >= allTypes->entryCount) { + LOGW("getEntry failing because entryIndex %d is beyond type entryCount %d", + entryIndex, (int)allTypes->entryCount); + return BAD_TYPE; + } + + const ResTable_type* type = NULL; + uint32_t offset = ResTable_type::NO_ENTRY; + ResTable_config bestConfig; + memset(&bestConfig, 0, sizeof(bestConfig)); // make the compiler shut up + + const size_t NT = allTypes->configs.size(); + for (size_t i=0; iconfigs[i]; + if (thisType == NULL) continue; + + ResTable_config thisConfig; + thisConfig.copyFromDtoH(thisType->config); + + TABLE_GETENTRY(LOGI("Match entry 0x%x in type 0x%x (sz 0x%x): imsi:%d/%d=%d/%d lang:%c%c=%c%c cnt:%c%c=%c%c " + "orien:%d=%d touch:%d=%d density:%d=%d key:%d=%d inp:%d=%d nav:%d=%d w:%d=%d h:%d=%d\n", + entryIndex, typeIndex+1, dtohl(thisType->config.size), + thisConfig.mcc, thisConfig.mnc, + config ? config->mcc : 0, config ? config->mnc : 0, + thisConfig.language[0] ? thisConfig.language[0] : '-', + thisConfig.language[1] ? thisConfig.language[1] : '-', + config && config->language[0] ? config->language[0] : '-', + config && config->language[1] ? config->language[1] : '-', + thisConfig.country[0] ? thisConfig.country[0] : '-', + thisConfig.country[1] ? thisConfig.country[1] : '-', + config && config->country[0] ? config->country[0] : '-', + config && config->country[1] ? config->country[1] : '-', + thisConfig.orientation, + config ? config->orientation : 0, + thisConfig.touchscreen, + config ? config->touchscreen : 0, + thisConfig.density, + config ? config->density : 0, + thisConfig.keyboard, + config ? config->keyboard : 0, + thisConfig.inputFlags, + config ? config->inputFlags : 0, + thisConfig.navigation, + config ? config->navigation : 0, + thisConfig.screenWidth, + config ? config->screenWidth : 0, + thisConfig.screenHeight, + config ? config->screenHeight : 0)); + + // Check to make sure this one is valid for the current parameters. + if (config && !thisConfig.match(*config)) { + TABLE_GETENTRY(LOGI("Does not match config!\n")); + continue; + } + + // Check if there is the desired entry in this type. + + const uint8_t* const end = ((const uint8_t*)thisType) + + dtohl(thisType->header.size); + const uint32_t* const eindex = (const uint32_t*) + (((const uint8_t*)thisType) + dtohs(thisType->header.headerSize)); + + uint32_t thisOffset = dtohl(eindex[entryIndex]); + if (thisOffset == ResTable_type::NO_ENTRY) { + TABLE_GETENTRY(LOGI("Skipping because it is not defined!\n")); + continue; + } + + if (type != NULL) { + // Check if this one is less specific than the last found. If so, + // we will skip it. We check starting with things we most care + // about to those we least care about. + if (!thisConfig.isBetterThan(bestConfig, config)) { + TABLE_GETENTRY(LOGI("This config is worse than last!\n")); + continue; + } + } + + type = thisType; + offset = thisOffset; + bestConfig = thisConfig; + TABLE_GETENTRY(LOGI("Best entry so far -- using it!\n")); + if (!config) break; + } + + if (type == NULL) { + TABLE_GETENTRY(LOGI("No value found for requested entry!\n")); + return BAD_INDEX; + } + + offset += dtohl(type->entriesStart); + TABLE_NOISY(aout << "Looking in resource table " << package->header->header + << ", typeOff=" + << (void*)(((const char*)type)-((const char*)package->header->header)) + << ", offset=" << (void*)offset << endl); + + if (offset > (dtohl(type->header.size)-sizeof(ResTable_entry))) { + LOGW("ResTable_entry at 0x%x is beyond type chunk data 0x%x", + offset, dtohl(type->header.size)); + return BAD_TYPE; + } + if ((offset&0x3) != 0) { + LOGW("ResTable_entry at 0x%x is not on an integer boundary", + offset); + return BAD_TYPE; + } + + const ResTable_entry* const entry = (const ResTable_entry*) + (((const uint8_t*)type) + offset); + if (dtohs(entry->size) < sizeof(*entry)) { + LOGW("ResTable_entry size 0x%x is too small", dtohs(entry->size)); + return BAD_TYPE; + } + + *outType = type; + *outEntry = entry; + if (outTypeClass != NULL) { + *outTypeClass = allTypes; + } + return offset + dtohs(entry->size); +} + +status_t ResTable::parsePackage(const ResTable_package* const pkg, + const Header* const header) +{ + const uint8_t* base = (const uint8_t*)pkg; + status_t err = validate_chunk(&pkg->header, sizeof(*pkg), + header->dataEnd, "ResTable_package"); + if (err != NO_ERROR) { + return (mError=err); + } + + const size_t pkgSize = dtohl(pkg->header.size); + + if (dtohl(pkg->typeStrings) >= pkgSize) { + LOGW("ResTable_package type strings at %p are past chunk size %p.", + (void*)dtohl(pkg->typeStrings), (void*)pkgSize); + return (mError=BAD_TYPE); + } + if ((dtohl(pkg->typeStrings)&0x3) != 0) { + LOGW("ResTable_package type strings at %p is not on an integer boundary.", + (void*)dtohl(pkg->typeStrings)); + return (mError=BAD_TYPE); + } + if (dtohl(pkg->keyStrings) >= pkgSize) { + LOGW("ResTable_package key strings at %p are past chunk size %p.", + (void*)dtohl(pkg->keyStrings), (void*)pkgSize); + return (mError=BAD_TYPE); + } + if ((dtohl(pkg->keyStrings)&0x3) != 0) { + LOGW("ResTable_package key strings at %p is not on an integer boundary.", + (void*)dtohl(pkg->keyStrings)); + return (mError=BAD_TYPE); + } + + Package* package = NULL; + PackageGroup* group = NULL; + uint32_t id = dtohl(pkg->id); + if (id != 0 && id < 256) { + size_t idx = mPackageMap[id]; + if (idx == 0) { + idx = mPackageGroups.size()+1; + + char16_t tmpName[sizeof(pkg->name)/sizeof(char16_t)]; + strcpy16_dtoh(tmpName, pkg->name, sizeof(pkg->name)/sizeof(char16_t)); + group = new PackageGroup(String16(tmpName), id); + if (group == NULL) { + return (mError=NO_MEMORY); + } + + err = group->typeStrings.setTo(base+dtohl(pkg->typeStrings), + header->dataEnd-(base+dtohl(pkg->typeStrings))); + if (err != NO_ERROR) { + return (mError=err); + } + err = group->keyStrings.setTo(base+dtohl(pkg->keyStrings), + header->dataEnd-(base+dtohl(pkg->keyStrings))); + if (err != NO_ERROR) { + return (mError=err); + } + + //printf("Adding new package id %d at index %d\n", id, idx); + err = mPackageGroups.add(group); + if (err < NO_ERROR) { + return (mError=err); + } + mPackageMap[id] = (uint8_t)idx; + } else { + group = mPackageGroups.itemAt(idx-1); + if (group == NULL) { + return (mError=UNKNOWN_ERROR); + } + } + package = new Package(header, pkg); + if (package == NULL) { + return (mError=NO_MEMORY); + } + err = group->packages.add(package); + if (err < NO_ERROR) { + return (mError=err); + } + } else { + LOG_ALWAYS_FATAL("Skins not supported!"); + return NO_ERROR; + } + + + // Iterate through all chunks. + size_t curPackage = 0; + + const ResChunk_header* chunk = + (const ResChunk_header*)(((const uint8_t*)pkg) + + dtohs(pkg->header.headerSize)); + const uint8_t* endPos = ((const uint8_t*)pkg) + dtohs(pkg->header.size); + while (((const uint8_t*)chunk) <= (endPos-sizeof(ResChunk_header)) && + ((const uint8_t*)chunk) <= (endPos-dtohl(chunk->size))) { + TABLE_NOISY(LOGV("PackageChunk: type=0x%x, headerSize=0x%x, size=0x%x, pos=%p\n", + dtohs(chunk->type), dtohs(chunk->headerSize), dtohl(chunk->size), + (void*)(((const uint8_t*)chunk) - ((const uint8_t*)header->header)))); + const size_t csize = dtohl(chunk->size); + const uint16_t ctype = dtohs(chunk->type); + if (ctype == RES_TABLE_TYPE_SPEC_TYPE) { + const ResTable_typeSpec* typeSpec = (const ResTable_typeSpec*)(chunk); + err = validate_chunk(&typeSpec->header, sizeof(*typeSpec), + endPos, "ResTable_typeSpec"); + if (err != NO_ERROR) { + return (mError=err); + } + + const size_t typeSpecSize = dtohl(typeSpec->header.size); + + LOAD_TABLE_NOISY(printf("TypeSpec off %p: type=0x%x, headerSize=0x%x, size=%p\n", + (void*)(base-(const uint8_t*)chunk), + dtohs(typeSpec->header.type), + dtohs(typeSpec->header.headerSize), + (void*)typeSize)); + // look for block overrun or int overflow when multiplying by 4 + if ((dtohl(typeSpec->entryCount) > (INT32_MAX/sizeof(uint32_t)) + || dtohs(typeSpec->header.headerSize)+(sizeof(uint32_t)*dtohl(typeSpec->entryCount)) + > typeSpecSize)) { + LOGW("ResTable_typeSpec entry index to %p extends beyond chunk end %p.", + (void*)(dtohs(typeSpec->header.headerSize) + +(sizeof(uint32_t)*dtohl(typeSpec->entryCount))), + (void*)typeSpecSize); + return (mError=BAD_TYPE); + } + + if (typeSpec->id == 0) { + LOGW("ResTable_type has an id of 0."); + return (mError=BAD_TYPE); + } + + while (package->types.size() < typeSpec->id) { + package->types.add(NULL); + } + Type* t = package->types[typeSpec->id-1]; + if (t == NULL) { + t = new Type(header, package, dtohl(typeSpec->entryCount)); + package->types.editItemAt(typeSpec->id-1) = t; + } else if (dtohl(typeSpec->entryCount) != t->entryCount) { + LOGW("ResTable_typeSpec entry count inconsistent: given %d, previously %d", + (int)dtohl(typeSpec->entryCount), (int)t->entryCount); + return (mError=BAD_TYPE); + } + t->typeSpecFlags = (const uint32_t*)( + ((const uint8_t*)typeSpec) + dtohs(typeSpec->header.headerSize)); + t->typeSpec = typeSpec; + + } else if (ctype == RES_TABLE_TYPE_TYPE) { + const ResTable_type* type = (const ResTable_type*)(chunk); + err = validate_chunk(&type->header, sizeof(*type)-sizeof(ResTable_config)+4, + endPos, "ResTable_type"); + if (err != NO_ERROR) { + return (mError=err); + } + + const size_t typeSize = dtohl(type->header.size); + + LOAD_TABLE_NOISY(printf("Type off %p: type=0x%x, headerSize=0x%x, size=%p\n", + (void*)(base-(const uint8_t*)chunk), + dtohs(type->header.type), + dtohs(type->header.headerSize), + (void*)typeSize)); + if (dtohs(type->header.headerSize)+(sizeof(uint32_t)*dtohl(type->entryCount)) + > typeSize) { + LOGW("ResTable_type entry index to %p extends beyond chunk end %p.", + (void*)(dtohs(type->header.headerSize) + +(sizeof(uint32_t)*dtohl(type->entryCount))), + (void*)typeSize); + return (mError=BAD_TYPE); + } + if (dtohl(type->entryCount) != 0 + && dtohl(type->entriesStart) > (typeSize-sizeof(ResTable_entry))) { + LOGW("ResTable_type entriesStart at %p extends beyond chunk end %p.", + (void*)dtohl(type->entriesStart), (void*)typeSize); + return (mError=BAD_TYPE); + } + if (type->id == 0) { + LOGW("ResTable_type has an id of 0."); + return (mError=BAD_TYPE); + } + + while (package->types.size() < type->id) { + package->types.add(NULL); + } + Type* t = package->types[type->id-1]; + if (t == NULL) { + t = new Type(header, package, dtohl(type->entryCount)); + package->types.editItemAt(type->id-1) = t; + } else if (dtohl(type->entryCount) != t->entryCount) { + LOGW("ResTable_type entry count inconsistent: given %d, previously %d", + (int)dtohl(type->entryCount), (int)t->entryCount); + return (mError=BAD_TYPE); + } + + TABLE_GETENTRY( + ResTable_config thisConfig; + thisConfig.copyFromDtoH(type->config); + LOGI("Adding config to type %d: imsi:%d/%d lang:%c%c cnt:%c%c " + "orien:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n", + type->id, + thisConfig.mcc, thisConfig.mnc, + thisConfig.language[0] ? thisConfig.language[0] : '-', + thisConfig.language[1] ? thisConfig.language[1] : '-', + thisConfig.country[0] ? thisConfig.country[0] : '-', + thisConfig.country[1] ? thisConfig.country[1] : '-', + thisConfig.orientation, + thisConfig.touchscreen, + thisConfig.density, + thisConfig.keyboard, + thisConfig.inputFlags, + thisConfig.navigation, + thisConfig.screenWidth, + thisConfig.screenHeight)); + t->configs.add(type); + } else { + status_t err = validate_chunk(chunk, sizeof(ResChunk_header), + endPos, "ResTable_package:unknown"); + if (err != NO_ERROR) { + return (mError=err); + } + } + chunk = (const ResChunk_header*) + (((const uint8_t*)chunk) + csize); + } + + if (group->typeCount == 0) { + group->typeCount = package->types.size(); + } + + return NO_ERROR; +} + +#ifndef HAVE_ANDROID_OS +#define CHAR16_TO_CSTR(c16, len) (String8(String16(c16,len)).string()) + +#define CHAR16_ARRAY_EQ(constant, var, len) \ + ((len == (sizeof(constant)/sizeof(constant[0]))) && (0 == memcmp((var), (constant), (len)))) + +void ResTable::print() const +{ + printf("mError=0x%x (%s)\n", mError, strerror(mError)); +#if 0 + printf("mParams=%c%c-%c%c,\n", + mParams.language[0], mParams.language[1], + mParams.country[0], mParams.country[1]); +#endif + size_t pgCount = mPackageGroups.size(); + printf("Package Groups (%d)\n", (int)pgCount); + for (size_t pgIndex=0; pgIndexid, (int)pg->packages.size(), + String8(pg->name).string()); + + size_t pkgCount = pg->packages.size(); + for (size_t pkgIndex=0; pkgIndexpackages[pkgIndex]; + size_t typeCount = pkg->types.size(); + printf(" Package %d id=%d name=%s typeCount=%d\n", (int)pkgIndex, + pkg->package->id, String8(String16(pkg->package->name)).string(), + (int)typeCount); + for (size_t typeIndex=0; typeIndexgetType(typeIndex); + if (typeConfigs == NULL) { + printf(" type %d NULL\n", (int)typeIndex); + continue; + } + const size_t NTC = typeConfigs->configs.size(); + printf(" type %d configCount=%d entryCount=%d\n", + (int)typeIndex, (int)NTC, (int)typeConfigs->entryCount); + if (typeConfigs->typeSpecFlags != NULL) { + for (size_t entryIndex=0; entryIndexentryCount; entryIndex++) { + uint32_t resID = (0xff000000 & ((pkg->package->id)<<24)) + | (0x00ff0000 & ((typeIndex+1)<<16)) + | (0x0000ffff & (entryIndex)); + resource_name resName; + this->getResourceName(resID, &resName); + printf(" spec resource 0x%08x %s:%s/%s: flags=0x%08x\n", + resID, + CHAR16_TO_CSTR(resName.package, resName.packageLen), + CHAR16_TO_CSTR(resName.type, resName.typeLen), + CHAR16_TO_CSTR(resName.name, resName.nameLen), + dtohl(typeConfigs->typeSpecFlags[entryIndex])); + } + } + for (size_t configIndex=0; configIndexconfigs[configIndex]; + if ((((uint64_t)type)&0x3) != 0) { + printf(" NON-INTEGER ResTable_type ADDRESS: %p\n", type); + continue; + } + printf(" config %d lang=%c%c cnt=%c%c orien=%d touch=%d density=%d key=%d infl=%d nav=%d w=%d h=%d\n", + (int)configIndex, + type->config.language[0] ? type->config.language[0] : '-', + type->config.language[1] ? type->config.language[1] : '-', + type->config.country[0] ? type->config.country[0] : '-', + type->config.country[1] ? type->config.country[1] : '-', + type->config.orientation, + type->config.touchscreen, + dtohs(type->config.density), + type->config.keyboard, + type->config.inputFlags, + type->config.navigation, + dtohs(type->config.screenWidth), + dtohs(type->config.screenHeight)); + size_t entryCount = dtohl(type->entryCount); + uint32_t entriesStart = dtohl(type->entriesStart); + if ((entriesStart&0x3) != 0) { + printf(" NON-INTEGER ResTable_type entriesStart OFFSET: %p\n", (void*)entriesStart); + continue; + } + uint32_t typeSize = dtohl(type->header.size); + if ((typeSize&0x3) != 0) { + printf(" NON-INTEGER ResTable_type header.size: %p\n", (void*)typeSize); + continue; + } + for (size_t entryIndex=0; entryIndexheader.size); + const uint32_t* const eindex = (const uint32_t*) + (((const uint8_t*)type) + dtohs(type->header.headerSize)); + + uint32_t thisOffset = dtohl(eindex[entryIndex]); + if (thisOffset == ResTable_type::NO_ENTRY) { + continue; + } + + uint32_t resID = (0xff000000 & ((pkg->package->id)<<24)) + | (0x00ff0000 & ((typeIndex+1)<<16)) + | (0x0000ffff & (entryIndex)); + resource_name resName; + this->getResourceName(resID, &resName); + printf(" resource 0x%08x %s:%s/%s: ", resID, + CHAR16_TO_CSTR(resName.package, resName.packageLen), + CHAR16_TO_CSTR(resName.type, resName.typeLen), + CHAR16_TO_CSTR(resName.name, resName.nameLen)); + if ((thisOffset&0x3) != 0) { + printf("NON-INTEGER OFFSET: %p\n", (void*)thisOffset); + continue; + } + if ((thisOffset+sizeof(ResTable_entry)) > typeSize) { + printf("OFFSET OUT OF BOUNDS: %p+%p (size is %p)\n", + (void*)entriesStart, (void*)thisOffset, + (void*)typeSize); + continue; + } + + const ResTable_entry* ent = (const ResTable_entry*) + (((const uint8_t*)type) + entriesStart + thisOffset); + if (((entriesStart + thisOffset)&0x3) != 0) { + printf("NON-INTEGER ResTable_entry OFFSET: %p\n", + (void*)(entriesStart + thisOffset)); + continue; + } + if ((dtohs(ent->flags)&ResTable_entry::FLAG_COMPLEX) != 0) { + printf(""); + } else { + uint16_t esize = dtohs(ent->size); + if ((esize&0x3) != 0) { + printf("NON-INTEGER ResTable_entry SIZE: %p\n", (void*)esize); + continue; + } + if ((thisOffset+esize) > typeSize) { + printf("ResTable_entry OUT OF BOUNDS: %p+%p+%p (size is %p)\n", + (void*)entriesStart, (void*)thisOffset, + (void*)esize, (void*)typeSize); + continue; + } + + const Res_value* value = (const Res_value*) + (((const uint8_t*)ent) + esize); + printf("t=0x%02x d=0x%08x (s=0x%04x r=0x%02x)", + (int)value->dataType, (int)dtohl(value->data), + (int)dtohs(value->size), (int)value->res0); + } + + if ((dtohs(ent->flags)&ResTable_entry::FLAG_PUBLIC) != 0) { + printf(" (PUBLIC)"); + } + printf("\n"); + } + } + } + } + } +} + +#endif // HAVE_ANDROID_OS + +} // namespace android diff --git a/libs/utils/SharedBuffer.cpp b/libs/utils/SharedBuffer.cpp new file mode 100644 index 000000000..3555fb712 --- /dev/null +++ b/libs/utils/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 +#include + +#include +#include + +// --------------------------------------------------------------------------- + +namespace android { + +SharedBuffer* SharedBuffer::alloc(size_t size) +{ + SharedBuffer* sb = static_cast(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(released)); + return 0; +} + +SharedBuffer* SharedBuffer::edit() const +{ + if (onlyOwner()) { + return const_cast(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(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(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(this)); + } + } + return prev; +} + + +}; // namespace android diff --git a/libs/utils/Socket.cpp b/libs/utils/Socket.cpp new file mode 100644 index 000000000..51509a304 --- /dev/null +++ b/libs/utils/Socket.cpp @@ -0,0 +1,388 @@ +/* + * 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. + */ + +// +// Internet address class. +// + +#ifdef HAVE_WINSOCK +// This needs to come first, or Cygwin gets concerned about a potential +// clash between WinSock and . +# include +#endif + +#include +#include +#include +#include + +#ifndef HAVE_WINSOCK +# include +# include +# include +# include +#endif + +#include +#include +#include +#include +#include +#include + +using namespace android; + + +/* + * =========================================================================== + * Socket + * =========================================================================== + */ + +#ifndef INVALID_SOCKET +# define INVALID_SOCKET (-1) +#endif +#define UNDEF_SOCKET ((unsigned long) INVALID_SOCKET) + +/*static*/ bool Socket::mBootInitialized = false; + +/* + * Extract system-dependent error code. + */ +static inline int getSocketError(void) { +#ifdef HAVE_WINSOCK + return WSAGetLastError(); +#else + return errno; +#endif +} + +/* + * One-time initialization for socket code. + */ +/*static*/ bool Socket::bootInit(void) +{ +#ifdef HAVE_WINSOCK + WSADATA wsaData; + int err; + + err = WSAStartup(MAKEWORD(2, 0), &wsaData); + if (err != 0) { + LOG(LOG_ERROR, "socket", "Unable to start WinSock\n"); + return false; + } + + LOG(LOG_INFO, "socket", "Using WinSock v%d.%d\n", + LOBYTE(wsaData.wVersion), HIBYTE(wsaData.wVersion)); +#endif + + mBootInitialized = true; + return true; +} + +/* + * One-time shutdown for socket code. + */ +/*static*/ void Socket::finalShutdown(void) +{ +#ifdef HAVE_WINSOCK + WSACleanup(); +#endif + mBootInitialized = false; +} + + +/* + * Simple constructor. Allow the application to create us and then make + * bind/connect calls. + */ +Socket::Socket(void) + : mSock(UNDEF_SOCKET) +{ + if (!mBootInitialized) + LOG(LOG_WARN, "socket", "WARNING: sockets not initialized\n"); +} + +/* + * Destructor. Closes the socket and resets our storage. + */ +Socket::~Socket(void) +{ + close(); +} + + +/* + * Create a socket and connect to the specified host and port. + */ +int Socket::connect(const char* host, int port) +{ + if (mSock != UNDEF_SOCKET) { + LOG(LOG_WARN, "socket", "Socket already connected\n"); + return -1; + } + + InetSocketAddress sockAddr; + if (!sockAddr.create(host, port)) + return -1; + + //return doConnect(sockAddr); + int foo; + foo = doConnect(sockAddr); + return foo; +} + +/* + * Create a socket and connect to the specified host and port. + */ +int Socket::connect(const InetAddress* addr, int port) +{ + if (mSock != UNDEF_SOCKET) { + LOG(LOG_WARN, "socket", "Socket already connected\n"); + return -1; + } + + InetSocketAddress sockAddr; + if (!sockAddr.create(addr, port)) + return -1; + + return doConnect(sockAddr); +} + +/* + * Finish creating a socket by connecting to the remote host. + * + * Returns 0 on success. + */ +int Socket::doConnect(const InetSocketAddress& sockAddr) +{ +#ifdef HAVE_WINSOCK + SOCKET sock; +#else + int sock; +#endif + const InetAddress* addr = sockAddr.getAddress(); + int port = sockAddr.getPort(); + struct sockaddr_in inaddr; + DurationTimer connectTimer; + + assert(sizeof(struct sockaddr_in) == addr->getAddressLength()); + memcpy(&inaddr, addr->getAddress(), addr->getAddressLength()); + inaddr.sin_port = htons(port); + + //fprintf(stderr, "--- connecting to %s:%d\n", + // sockAddr.getHostName(), port); + + sock = ::socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sock == INVALID_SOCKET) { + int err = getSocketError(); + LOG(LOG_ERROR, "socket", "Unable to create socket (err=%d)\n", err); + return (err != 0) ? err : -1; + } + + connectTimer.start(); + + if (::connect(sock, (struct sockaddr*) &inaddr, sizeof(inaddr)) != 0) { + int err = getSocketError(); + LOG(LOG_WARN, "socket", "Connect to %s:%d failed: %d\n", + sockAddr.getHostName(), port, err); + return (err != 0) ? err : -1; + } + + connectTimer.stop(); + if ((long) connectTimer.durationUsecs() > 100000) { + LOG(LOG_INFO, "socket", + "Connect to %s:%d took %.3fs\n", sockAddr.getHostName(), + port, ((long) connectTimer.durationUsecs()) / 1000000.0); + } + + mSock = (unsigned long) sock; + LOG(LOG_VERBOSE, "socket", + "--- connected to %s:%d\n", sockAddr.getHostName(), port); + return 0; +} + + +/* + * Close the socket if it needs closing. + */ +bool Socket::close(void) +{ + if (mSock != UNDEF_SOCKET) { + //fprintf(stderr, "--- closing socket %lu\n", mSock); +#ifdef HAVE_WINSOCK + if (::closesocket((SOCKET) mSock) != 0) + return false; +#else + if (::close((int) mSock) != 0) + return false; +#endif + } + + mSock = UNDEF_SOCKET; + + return true; +} + +/* + * Read data from socket. + * + * Standard semantics: read up to "len" bytes into "buf". Returns the + * number of bytes read, or less than zero on error. + */ +int Socket::read(void* buf, ssize_t len) const +{ + if (mSock == UNDEF_SOCKET) { + LOG(LOG_ERROR, "socket", "ERROR: read on invalid socket\n"); + return -500; + } + +#ifdef HAVE_WINSOCK + SOCKET sock = (SOCKET) mSock; +#else + int sock = (int) mSock; +#endif + int cc; + + cc = recv(sock, (char*)buf, len, 0); + if (cc < 0) { + int err = getSocketError(); + return (err > 0) ? -err : -1; + } + + return cc; +} + +/* + * Write data to a socket. + * + * Standard semantics: write up to "len" bytes into "buf". Returns the + * number of bytes written, or less than zero on error. + */ +int Socket::write(const void* buf, ssize_t len) const +{ + if (mSock == UNDEF_SOCKET) { + LOG(LOG_ERROR, "socket", "ERROR: write on invalid socket\n"); + return -500; + } + +#ifdef HAVE_WINSOCK + SOCKET sock = (SOCKET) mSock; +#else + int sock = (int) mSock; +#endif + int cc; + + cc = send(sock, (const char*)buf, len, 0); + if (cc < 0) { + int err = getSocketError(); + return (err > 0) ? -err : -1; + } + + return cc; +} + + +/* + * =========================================================================== + * Socket tests + * =========================================================================== + */ + +/* + * Read all data from the socket. The data is read into a buffer that + * expands as needed. + * + * On exit, the buffer is returned, and the length of the data is stored + * in "*sz". A null byte is added to the end, but is not included in + * the length. + */ +static char* socketReadAll(const Socket& s, int *sz) +{ + int max, r; + char *data, *ptr, *tmp; + + data = (char*) malloc(max = 32768); + if (data == NULL) + return NULL; + + ptr = data; + + for (;;) { + if ((ptr - data) == max) { + tmp = (char*) realloc(data, max *= 2); + if(tmp == 0) { + free(data); + return 0; + } + } + r = s.read(ptr, max - (ptr - data)); + if (r == 0) + break; + if (r < 0) { + LOG(LOG_WARN, "socket", "WARNING: socket read failed (res=%d)\n",r); + break; + } + ptr += r; + } + + if ((ptr - data) == max) { + tmp = (char*) realloc(data, max + 1); + if (tmp == NULL) { + free(data); + return NULL; + } + } + *ptr = '\0'; + *sz = (ptr - data); + return data; +} + +/* + * Exercise the Socket class. + */ +void android::TestSockets(void) +{ + printf("----- SOCKET TEST ------\n"); + Socket::bootInit(); + + char* buf = NULL; + int len, cc; + const char* kTestStr = + "GET / HTTP/1.0\n" + "Connection: close\n" + "\n"; + + Socket sock; + if (sock.connect("www.google.com", 80) != 0) { + fprintf(stderr, "socket connected failed\n"); + goto bail; + } + + cc = sock.write(kTestStr, strlen(kTestStr)); + if (cc != (int) strlen(kTestStr)) { + fprintf(stderr, "write failed, res=%d\n", cc); + goto bail; + } + buf = socketReadAll(sock, &len); + + printf("GOT '%s'\n", buf); + +bail: + sock.close(); + free(buf); +} + diff --git a/libs/utils/Static.cpp b/libs/utils/Static.cpp new file mode 100644 index 000000000..93f7e4f0c --- /dev/null +++ b/libs/utils/Static.cpp @@ -0,0 +1,120 @@ +/* + * 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. + +#include + +#include +#include +#include + +namespace android { + +class LibUtilsFirstStatics +{ +public: + LibUtilsFirstStatics() + { + initialize_string8(); + initialize_string16(); + } + + ~LibUtilsFirstStatics() + { + terminate_string16(); + terminate_string8(); + } +}; + +static LibUtilsFirstStatics gFirstStatics; +int gDarwinCantLoadAllObjects = 1; + +// ------------ Text output streams + +Vector gTextBuffers; + +class LogTextOutput : public BufferedTextOutput +{ +public: + LogTextOutput() : BufferedTextOutput(MULTITHREADED) { } + virtual ~LogTextOutput() { }; + +protected: + virtual status_t writeLines(const struct iovec& vec, size_t N) + { + android_writevLog(&vec, N); + return NO_ERROR; + } +}; + +class FdTextOutput : public BufferedTextOutput +{ +public: + FdTextOutput(int fd) : BufferedTextOutput(MULTITHREADED), mFD(fd) { } + virtual ~FdTextOutput() { }; + +protected: + virtual status_t writeLines(const struct iovec& vec, size_t N) + { + writev(mFD, &vec, N); + return NO_ERROR; + } + +private: + int mFD; +}; + +static LogTextOutput gLogTextOutput; +static FdTextOutput gStdoutTextOutput(STDOUT_FILENO); +static FdTextOutput gStderrTextOutput(STDERR_FILENO); + +TextOutput& alog(gLogTextOutput); +TextOutput& aout(gStdoutTextOutput); +TextOutput& aerr(gStderrTextOutput); + +#ifndef LIBUTILS_NATIVE + +// ------------ ProcessState.cpp + +Mutex gProcessMutex; +sp gProcess; + +class LibUtilsIPCtStatics +{ +public: + LibUtilsIPCtStatics() + { + } + + ~LibUtilsIPCtStatics() + { + IPCThreadState::shutdown(); + } +}; + +static LibUtilsIPCtStatics gIPCStatics; + +// ------------ ServiceManager.cpp + +Mutex gDefaultServiceManagerLock; +sp gDefaultServiceManager; +sp gPermissionController; + +#endif + +} // namespace android diff --git a/libs/utils/StopWatch.cpp b/libs/utils/StopWatch.cpp new file mode 100644 index 000000000..68a1c5217 --- /dev/null +++ b/libs/utils/StopWatch.cpp @@ -0,0 +1,79 @@ +/* + * 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 +#include +#include + +#include +#include +#include + +/*****************************************************************************/ + +namespace android { + + +StopWatch::StopWatch(const char *name, int clock, uint32_t flags) + : mName(name), mClock(clock), mFlags(flags), + mStartTime(0), mNumLaps(0) +{ + mStartTime = systemTime(mClock); +} + +StopWatch::~StopWatch() +{ + nsecs_t elapsed = elapsedTime(); + const int n = mNumLaps; + LOGD("StopWatch %s (us): %lld ", mName, ns2us(elapsed)); + for (int i=0 ; i= 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; +} + + +/*****************************************************************************/ + +}; // namespace android + diff --git a/libs/utils/String16.cpp b/libs/utils/String16.cpp new file mode 100644 index 000000000..1f81cadb7 --- /dev/null +++ b/libs/utils/String16.cpp @@ -0,0 +1,609 @@ +/* + * 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 + +#include +#include +#include +#include +#include + +#include + +#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 +#endif + +#include +#include +#include + +// --------------------------------------------------------------------------- + +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); +} + +// --------------------------------------------------------------------------- + +namespace android { + +static inline size_t +utf8_char_len(uint8_t ch) +{ + return ((0xe5000000 >> ((ch >> 3) & 0x1e)) & 3) + 1; +} + +#define UTF8_SHIFT_AND_MASK(unicode, byte) (unicode)<<=6; (unicode) |= (0x3f & (byte)); + +static inline uint32_t +utf8_to_utf32(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); +} + +// --------------------------------------------------------------------------- + +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; +} + +// --------------------------------------------------------------------------- + +// Note: not dealing with generating surrogate pairs. +static char16_t* allocFromUTF8(const char* in, size_t len) +{ + if (len == 0) return getEmptyString(); + + size_t chars = 0; + const char* end = in+len; + const char* p = in; + + while (p < end) { + chars++; + p += utf8_char_len(*p); + } + + SharedBuffer* buf = SharedBuffer::alloc((chars+1)*sizeof(char16_t)); + if (buf) { + p = in; + char16_t* str = (char16_t*)buf->data(); + char16_t* d = str; + while (p < end) { + size_t len = utf8_char_len(*p); + *d++ = (char16_t)utf8_to_utf32((const uint8_t*)p, len); + p += len; + } + *d = 0; + + //printf("Created UTF-16 string from UTF-8 \"%s\":", in); + //printHexData(1, str, buf->size(), 16, 1); + //printf("\n"); + + return str; + } + + return getEmptyString(); +} + +// --------------------------------------------------------------------------- + +String16::String16() + : mString(getEmptyString()) +{ +} + +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)); + LOG_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)); + LOG_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(); + memcpy(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= '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; iedit(); + 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; +} + +TextOutput& operator<<(TextOutput& to, const String16& val) +{ + to << String8(val).string(); + return to; +} + +}; // namespace android diff --git a/libs/utils/String8.cpp b/libs/utils/String8.cpp new file mode 100644 index 000000000..c50d343a7 --- /dev/null +++ b/libs/utils/String8.cpp @@ -0,0 +1,604 @@ +/* + * 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 + +#include +#include +#include +#include + +#include + +#include + +namespace android { + +// --------------------------------------------------------------------------- + +static const uint32_t kByteMask = 0x000000BF; +static const uint32_t kByteMark = 0x00000080; + +// Surrogates aren't valid for UTF-32 characters, so define some +// constants that will let us screen them out. +static const uint32_t kUnicodeSurrogateHighStart = 0x0000D800; +static const uint32_t kUnicodeSurrogateHighEnd = 0x0000DBFF; +static const uint32_t kUnicodeSurrogateLowStart = 0x0000DC00; +static const uint32_t kUnicodeSurrogateLowEnd = 0x0000DFFF; +static const uint32_t kUnicodeSurrogateStart = kUnicodeSurrogateHighStart; +static const uint32_t kUnicodeSurrogateEnd = kUnicodeSurrogateLowEnd; + +// Mask used to set appropriate bits in first byte of UTF-8 sequence, +// indexed by number of bytes in the sequence. +static const uint32_t kFirstByteMark[] = { + 0x00000000, 0x00000000, 0x000000C0, 0x000000E0, 0x000000F0 +}; + +// Separator used by resource paths. This is not platform dependent contrary +// to OS_PATH_SEPARATOR. +#define RES_PATH_SEPARATOR '/' + +// Return number of utf8 bytes required for the character. +static size_t utf32_to_utf8_bytes(uint32_t srcChar) +{ + size_t bytesToWrite; + + // Figure out how many bytes the result will require. + if (srcChar < 0x00000080) + { + bytesToWrite = 1; + } + else if (srcChar < 0x00000800) + { + bytesToWrite = 2; + } + else if (srcChar < 0x00010000) + { + if ((srcChar < kUnicodeSurrogateStart) + || (srcChar > kUnicodeSurrogateEnd)) + { + bytesToWrite = 3; + } + else + { + // Surrogates are invalid UTF-32 characters. + return 0; + } + } + // Max code point for Unicode is 0x0010FFFF. + else if (srcChar < 0x00110000) + { + bytesToWrite = 4; + } + else + { + // Invalid UTF-32 character. + return 0; + } + + return bytesToWrite; +} + +// Write out the source character to . + +static void utf32_to_utf8(uint8_t* dstP, uint32_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]); + } +} + +// --------------------------------------------------------------------------- + +static SharedBuffer* gEmptyStringBuf = NULL; +static char* gEmptyString = NULL; + +extern int gDarwinCantLoadAllObjects; +int gDarwinIsReallyAnnoying; + +static inline char* getEmptyString() +{ + gEmptyStringBuf->acquire(); + return gEmptyString; +} + +void initialize_string8() +{ +#ifdef LIBUTILS_NATIVE + // Bite me, Darwin! + gDarwinIsReallyAnnoying = gDarwinCantLoadAllObjects; +#endif + + 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); + LOG_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(); +} + +// Note: not dealing with expanding surrogate pairs. +static char* allocFromUTF16(const char16_t* in, size_t len) +{ + if (len == 0) return getEmptyString(); + + size_t bytes = 0; + const char16_t* end = in+len; + const char16_t* p = in; + + while (p < end) { + bytes += utf32_to_utf8_bytes(*p); + p++; + } + + SharedBuffer* buf = SharedBuffer::alloc(bytes+1); + LOG_ASSERT(buf, "Unable to allocate shared buffer"); + if (buf) { + p = in; + char* str = (char*)buf->data(); + char* d = str; + while (p < end) { + uint32_t c = *p++; + size_t len = utf32_to_utf8_bytes(c); + utf32_to_utf8((uint8_t*)d, c, len); + d += len; + } + *d = 0; + + return str; + } + + return getEmptyString(); +} + +// --------------------------------------------------------------------------- + +String8::String8() + : mString(getEmptyString()) +{ +} + +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() +{ + SharedBuffer::bufferFromData(mString)->release(); +} + +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) +{ + SharedBuffer::bufferFromData(mString)->release(); + mString = allocFromUTF8(other, strlen(other)); + if (mString) return NO_ERROR; + + mString = getEmptyString(); + return NO_MEMORY; +} + +status_t String8::setTo(const char* other, size_t len) +{ + SharedBuffer::bufferFromData(mString)->release(); + mString = allocFromUTF8(other, len); + if (mString) return NO_ERROR; + + mString = getEmptyString(); + return NO_MEMORY; +} + +status_t String8::setTo(const char16_t* other, size_t len) +{ + SharedBuffer::bufferFromData(mString)->release(); + mString = allocFromUTF16(other, len); + 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::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) { + char* str = (char*)buf->data(); + str[size] = 0; + mString = str; + return NO_ERROR; + } + } + + return NO_MEMORY; +} + +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); +} + +TextOutput& operator<<(TextOutput& to, const String8& val) +{ + to << val.string(); + return to; +} + +// --------------------------------------------------------------------------- +// 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(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/libs/utils/SystemClock.cpp b/libs/utils/SystemClock.cpp new file mode 100644 index 000000000..2bdc0ce27 --- /dev/null +++ b/libs/utils/SystemClock.cpp @@ -0,0 +1,139 @@ +/* + * 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. + */ + +#if HAVE_ANDROID_OS +#include +#include +#include +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include + +#define LOG_TAG "SystemClock" +#include "utils/Log.h" + +namespace android { + +/* + * Set the current time. This only works when running as root. + */ +int setCurrentTimeMillis(int64_t millis) +{ +#if WIN32 + // not implemented + return -1; +#else + struct timeval tv; +#if HAVE_ANDROID_OS + struct timespec ts; + int fd; + int res; +#endif + int ret = 0; + + if (millis <= 0 || millis / 1000LL >= INT_MAX) { + return -1; + } + + tv.tv_sec = (time_t) (millis / 1000LL); + tv.tv_usec = (suseconds_t) ((millis % 1000LL) * 1000LL); + + LOGD("Setting time of day to sec=%d\n", (int) tv.tv_sec); + +#if HAVE_ANDROID_OS + fd = open("/dev/alarm", O_RDWR); + if(fd < 0) { + LOGW("Unable to open alarm driver: %s\n", strerror(errno)); + return -1; + } + ts.tv_sec = tv.tv_sec; + ts.tv_nsec = tv.tv_usec * 1000; + res = ioctl(fd, ANDROID_ALARM_SET_RTC, &ts); + if(res < 0) { + LOGW("Unable to set rtc to %ld: %s\n", tv.tv_sec, strerror(errno)); + ret = -1; + } + close(fd); +#else + if (settimeofday(&tv, NULL) != 0) { + LOGW("Unable to set clock to %d.%d: %s\n", + (int) tv.tv_sec, (int) tv.tv_usec, strerror(errno)); + ret = -1; + } +#endif + + return ret; +#endif // WIN32 +} + +/* + * 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() +{ +#if HAVE_ANDROID_OS + 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); + } + } + + struct timespec ts; + int result = ioctl(s_fd, + ANDROID_ALARM_GET_TIME(ANDROID_ALARM_ELAPSED_REALTIME), &ts); + + if (result == 0) { + int64_t when = seconds_to_nanoseconds(ts.tv_sec) + ts.tv_nsec; + return (int64_t) nanoseconds_to_milliseconds(when); + } else { + // XXX: there was an error, probably because the driver didn't + // exist ... this should return + // a real error, like an exception! + int64_t when = systemTime(SYSTEM_TIME_MONOTONIC); + return (int64_t) nanoseconds_to_milliseconds(when); + } +#else + int64_t when = systemTime(SYSTEM_TIME_MONOTONIC); + return (int64_t) nanoseconds_to_milliseconds(when); +#endif +} + +}; // namespace android diff --git a/libs/utils/TextOutput.cpp b/libs/utils/TextOutput.cpp new file mode 100644 index 000000000..cebee99e5 --- /dev/null +++ b/libs/utils/TextOutput.cpp @@ -0,0 +1,146 @@ +/* + * 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 + +#include + +#include +#include +#include + +// --------------------------------------------------------------------------- + +namespace android { + +TextOutput& operator<<(TextOutput& to, bool val) +{ + if (val) to.print("true", 4); + else to.print("false", 5); + return to; +} + +TextOutput& operator<<(TextOutput& to, int val) +{ + char buf[16]; + sprintf(buf, "%d", val); + to.print(buf, strlen(buf)); + return to; +} + +TextOutput& operator<<(TextOutput& to, long val) +{ + char buf[16]; + sprintf(buf, "%ld", val); + to.print(buf, strlen(buf)); + return to; +} + +TextOutput& operator<<(TextOutput& to, unsigned int val) +{ + char buf[16]; + sprintf(buf, "%u", val); + to.print(buf, strlen(buf)); + return to; +} + +TextOutput& operator<<(TextOutput& to, unsigned long val) +{ + char buf[16]; + sprintf(buf, "%lu", val); + to.print(buf, strlen(buf)); + return to; +} + +TextOutput& operator<<(TextOutput& to, long long val) +{ + char buf[32]; + sprintf(buf, "%Ld", val); + to.print(buf, strlen(buf)); + return to; +} + +TextOutput& operator<<(TextOutput& to, unsigned long long val) +{ + char buf[32]; + sprintf(buf, "%Lu", val); + to.print(buf, strlen(buf)); + return to; +} + +static TextOutput& print_float(TextOutput& to, double value) +{ + char buf[64]; + sprintf(buf, "%g", value); + if( !strchr(buf, '.') && !strchr(buf, 'e') && + !strchr(buf, 'E') ) { + strncat(buf, ".0", sizeof(buf)-1); + } + to.print(buf, strlen(buf)); + return to; +} + +TextOutput& operator<<(TextOutput& to, float val) +{ + return print_float(to,val); +} + +TextOutput& operator<<(TextOutput& to, double val) +{ + return print_float(to,val); +} + +TextOutput& operator<<(TextOutput& to, const void* val) +{ + char buf[16]; + sprintf(buf, "%p", val); + to.print(buf, strlen(buf)); + return to; +} + +static void textOutputPrinter(void* cookie, const char* txt) +{ + ((TextOutput*)cookie)->print(txt, strlen(txt)); +} + +TextOutput& operator<<(TextOutput& to, const TypeCode& val) +{ + printTypeCode(val.typeCode(), textOutputPrinter, (void*)&to); + return to; +} + +HexDump::HexDump(const void *buf, size_t size, size_t bytesPerLine) + : mBuffer(buf) + , mSize(size) + , mBytesPerLine(bytesPerLine) + , mSingleLineCutoff(16) + , mAlignment(4) + , mCArrayStyle(false) +{ + if (bytesPerLine >= 16) mAlignment = 4; + else if (bytesPerLine >= 8) mAlignment = 2; + else mAlignment = 1; +} + +TextOutput& operator<<(TextOutput& to, const HexDump& val) +{ + printHexData(0, val.buffer(), val.size(), val.bytesPerLine(), + val.singleLineCutoff(), val.alignment(), val.carrayStyle(), + textOutputPrinter, (void*)&to); + return to; +} + +}; // namespace android diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp new file mode 100644 index 000000000..5f407a990 --- /dev/null +++ b/libs/utils/Threads.cpp @@ -0,0 +1,1128 @@ +/* + * 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 "libutils.threads" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#if defined(HAVE_PTHREADS) +# include +# include +# include +#elif defined(HAVE_WIN32_THREADS) +# include +# include +# include +# define HAVE_CREATETHREAD // Cygwin, vs. HAVE__BEGINTHREADEX for MinGW +#endif + +#if defined(HAVE_FUTEX) +#include +#endif + +#if defined(HAVE_PRCTL) +#include +#endif + +/* + * =========================================================================== + * Thread wrappers + * =========================================================================== + */ + +using namespace android; + +// ---------------------------------------------------------------------------- +#if defined(HAVE_PTHREADS) +#if 0 +#pragma mark - +#pragma mark PTHREAD +#endif +// ---------------------------------------------------------------------------- + +/* + * Create and run a new thead. + * + * 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. + 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 (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; + 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 + free(name); + } + return f(u); + } +}; + +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) { + // We could avoid the trampoline if there was a way to get to the + // android_thread_id_t (pid) from pthread_t + 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); + if (result != 0) { + LOGE("androidCreateRawThreadEtc failed (entry=%p, res=%d, errno=%d)\n" + "(android threadPriority=%d)", + entryFunction, result, errno, threadPriority); + return 0; + } + + if (threadId != NULL) { + *threadId = (android_thread_id_t)thread; // XXX: this is not portable + } + return 1; +} + +android_thread_id_t androidGetThreadId() +{ + return (android_thread_id_t)pthread_self(); +} + +// ---------------------------------------------------------------------------- +#elif defined(HAVE_WIN32_THREADS) +#if 0 +#pragma mark - +#pragma mark WIN32_THREADS +#endif +// ---------------------------------------------------------------------------- + +/* + * 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; + + LOG(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 + { + LOG(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 + +// ---------------------------------------------------------------------------- + +#if 0 +#pragma mark - +#pragma mark Common Thread functions +#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; +} + +namespace android { + +/* + * =========================================================================== + * Mutex class + * =========================================================================== + */ + +#if 0 +#pragma mark - +#pragma mark Mutex +#endif + +#if defined(HAVE_PTHREADS) && !defined(HAVE_FUTEX) +/* + * Simple pthread wrapper. + */ + +Mutex::Mutex() +{ + _init(); +} + +Mutex::Mutex(const char* name) +{ + // XXX: name not used for now + _init(); +} + +void Mutex::_init() +{ + pthread_mutex_t* pMutex = new pthread_mutex_t; + pthread_mutex_init(pMutex, NULL); + mState = pMutex; +} + +Mutex::~Mutex() +{ + delete (pthread_mutex_t*) mState; +} + +status_t Mutex::lock() +{ + int res; + while ((res=pthread_mutex_lock((pthread_mutex_t*) mState)) == EINTR) ; + return -res; +} + +void Mutex::unlock() +{ + pthread_mutex_unlock((pthread_mutex_t*) mState); +} + +status_t Mutex::tryLock() +{ + int res; + while ((res=pthread_mutex_trylock((pthread_mutex_t*) mState)) == EINTR) ; + return -res; +} + +#elif defined(HAVE_FUTEX) +#if 0 +#pragma mark - +#endif + +#define STATE ((futex_mutex_t*) (&mState)) + +Mutex::Mutex() +{ + _init(); +} + +Mutex::Mutex(const char* name) +{ + _init(); +} + +void +Mutex::_init() +{ + futex_mutex_init(STATE); +} + +Mutex::~Mutex() +{ +} + +status_t Mutex::lock() +{ + int res; + while ((res=futex_mutex_lock(STATE, FUTEX_WAIT_INFINITE)) == EINTR) ; + return -res; +} + +void Mutex::unlock() +{ + futex_mutex_unlock(STATE); +} + +status_t Mutex::tryLock() +{ + int res; + while ((res=futex_mutex_trylock(STATE)) == EINTR) ; + return -res; +} +#undef STATE + +#elif defined(HAVE_WIN32_THREADS) +#if 0 +#pragma mark - +#endif + +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; + + 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)) + LOG(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) + LOG(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 0 +#pragma mark - +#pragma mark Condition +#endif + +#if defined(HAVE_PTHREADS) && !defined(HAVE_FUTEX) + +/* + * Constructor. This is a simple pthread wrapper. + */ +Condition::Condition() +{ + pthread_cond_t* pCond = new pthread_cond_t; + + pthread_cond_init(pCond, NULL); + mState = pCond; +} + +/* + * Destructor. + */ +Condition::~Condition() +{ + pthread_cond_destroy((pthread_cond_t*) mState); + delete (pthread_cond_t*) mState; +} + +/* + * Wait on a condition variable. Lock the mutex before calling. + */ + +status_t Condition::wait(Mutex& mutex) +{ + assert(mutex.mState != NULL); + + int cc; + while ((cc = pthread_cond_wait((pthread_cond_t*)mState, + (pthread_mutex_t*) mutex.mState)) == EINTR) ; + return -cc; +} + +status_t Condition::wait(Mutex& mutex, nsecs_t abstime) +{ + assert(mutex.mState != NULL); + + struct timespec ts; + ts.tv_sec = abstime/1000000000; + ts.tv_nsec = abstime-(ts.tv_sec*1000000000); + + int cc; + while ((cc = pthread_cond_timedwait((pthread_cond_t*)mState, + (pthread_mutex_t*) mutex.mState, &ts)) == EINTR) ; + return -cc; +} + +status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) +{ + return wait(mutex, systemTime()+reltime); +} + +/* + * Signal the condition variable, allowing one thread to continue. + */ +void Condition::signal() +{ + pthread_cond_signal((pthread_cond_t*) mState); +} + +/* + * Signal the condition variable, allowing all threads to continue. + */ +void Condition::broadcast() +{ + pthread_cond_broadcast((pthread_cond_t*) mState); +} + +#elif defined(HAVE_FUTEX) +#if 0 +#pragma mark - +#endif + +#define STATE ((futex_cond_t*) (&mState)) + +/* + * Constructor. This is a simple pthread wrapper. + */ +Condition::Condition() +{ + futex_cond_init(STATE); +} + +/* + * Destructor. + */ +Condition::~Condition() +{ +} + +/* + * Wait on a condition variable. Lock the mutex before calling. + */ + +status_t Condition::wait(Mutex& mutex) +{ + assert(mutex.mState != NULL); + + int res; + while ((res = futex_cond_wait(STATE, + (futex_mutex_t*)(&mutex.mState), FUTEX_WAIT_INFINITE)) == -EINTR) ; + + return -res; +} + +status_t Condition::wait(Mutex& mutex, nsecs_t abstime) +{ + nsecs_t reltime = abstime - systemTime(); + if (reltime <= 0) return true; + return waitRelative(mutex, reltime); +} + +status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) +{ + assert(mutex.mState != NULL); + int res; + unsigned msec = ns2ms(reltime); + if(msec == 0) + return true; + // This code will not time out at the correct time if interrupted by signals + while ((res = futex_cond_wait(STATE, + (futex_mutex_t*)(&mutex.mState), msec)) == -EINTR) ; + return res; +} + +/* + * Signal the condition variable, allowing one thread to continue. + */ +void Condition::signal() +{ + futex_cond_signal(STATE); +} + +/* + * Signal the condition variable, allowing all threads to continue. + */ +void Condition::broadcast() +{ + futex_cond_broadcast(STATE); +} + +#undef STATE + +#elif defined(HAVE_WIN32_THREADS) +#if 0 +#pragma mark - +#endif + +/* + * 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::wait(Mutex& mutex, nsecs_t abstime) +{ + WinCondition* condState = (WinCondition*) mState; + HANDLE hMutex = (HANDLE) mutex.mState; + + return ((WinCondition*)mState)->wait(condState, hMutex, &abstime); +} + +status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) +{ + return wait(mutex, systemTime()+reltime); +} + +/* + * 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 + + +/* + * =========================================================================== + * ReadWriteLock class + * =========================================================================== + */ + +#if 0 +#pragma mark - +#pragma mark ReadWriteLock +#endif + +/* + * Add a reader. Readers are nice. They share. + */ +void ReadWriteLock::lockForRead() +{ + mLock.lock(); + while (mNumWriters > 0) { + LOG(LOG_DEBUG, "thread", "+++ lockForRead: waiting\n"); + mReadWaiter.wait(mLock); + } + assert(mNumWriters == 0); + mNumReaders++; +#if defined(PRINT_RENDER_TIMES) + if (mNumReaders == 1) + mDebugTimer.start(); +#endif + mLock.unlock(); +} + +/* + * Try to add a reader. If it doesn't work right away, return "false". + */ +bool ReadWriteLock::tryLockForRead() +{ + mLock.lock(); + if (mNumWriters > 0) { + mLock.unlock(); + return false; + } + assert(mNumWriters == 0); + mNumReaders++; +#if defined(PRINT_RENDER_TIMES) + if (mNumReaders == 1) + mDebugTimer.start(); +#endif + mLock.unlock(); + return true; +} + +/* + * Remove a reader. + */ +void ReadWriteLock::unlockForRead() +{ + mLock.lock(); + if (mNumReaders == 0) { + mLock.unlock(); + LOG(LOG_WARN, "thread", + "WARNING: unlockForRead requested, but not locked\n"); + return; + } + assert(mNumReaders > 0); + assert(mNumWriters == 0); + mNumReaders--; + if (mNumReaders == 0) { // last reader? +#if defined(PRINT_RENDER_TIMES) + mDebugTimer.stop(); + printf(" rdlk held %.3f msec\n", + (double) mDebugTimer.durationUsecs() / 1000.0); +#endif + //printf("+++ signaling writers (if any)\n"); + mWriteWaiter.signal(); // wake one writer (if any) + } + mLock.unlock(); +} + +/* + * Add a writer. This requires exclusive access to the object. + */ +void ReadWriteLock::lockForWrite() +{ + mLock.lock(); + while (mNumReaders > 0 || mNumWriters > 0) { + LOG(LOG_DEBUG, "thread", "+++ lockForWrite: waiting\n"); + mWriteWaiter.wait(mLock); + } + assert(mNumReaders == 0); + assert(mNumWriters == 0); + mNumWriters++; +#if defined(PRINT_RENDER_TIMES) + mDebugTimer.start(); +#endif + mLock.unlock(); +} + +/* + * Try to add a writer. If it doesn't work right away, return "false". + */ +bool ReadWriteLock::tryLockForWrite() +{ + mLock.lock(); + if (mNumReaders > 0 || mNumWriters > 0) { + mLock.unlock(); + return false; + } + assert(mNumReaders == 0); + assert(mNumWriters == 0); + mNumWriters++; +#if defined(PRINT_RENDER_TIMES) + mDebugTimer.start(); +#endif + mLock.unlock(); + return true; +} + +/* + * Remove a writer. + */ +void ReadWriteLock::unlockForWrite() +{ + mLock.lock(); + if (mNumWriters == 0) { + mLock.unlock(); + LOG(LOG_WARN, "thread", + "WARNING: unlockForWrite requested, but not locked\n"); + return; + } + assert(mNumWriters == 1); + mNumWriters--; +#if defined(PRINT_RENDER_TIMES) + mDebugTimer.stop(); + //printf(" wrlk held %.3f msec\n", + // (double) mDebugTimer.durationUsecs() / 1000.0); +#endif + mWriteWaiter.signal(); // should other writers get first dibs? + //printf("+++ signaling readers (if any)\n"); + mReadWaiter.broadcast(); // wake all readers (if any) + mLock.unlock(); +} + +// ---------------------------------------------------------------------------- + +#if 0 +#pragma mark - +#pragma mark Thread::Thread +#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) +{ +} + +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; + + 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); + } + + if (mStatus < 0) { + // something happened, don't leak + mHoldSelf.clear(); + } + + return mStatus; +} + +int Thread::_threadLoop(void* user) +{ + Thread* const self = static_cast(user); + sp strong(self->mHoldSelf); + wp weak(strong); + self->mHoldSelf.clear(); + + // we're about to run... + self->mStatus = self->readyToRun(); + if (self->mStatus!=NO_ERROR || self->mExitPending) { + // pretend the thread never started... + self->mExitPending = false; + self->mRunning = false; + return 0; + } + + // thread is running now + self->mRunning = true; + + do { + bool result = self->threadLoop(); + if (result == false || self->mExitPending) { + self->mExitPending = true; + self->mLock.lock(); + self->mRunning = false; + self->mThreadExitedCondition.signal(); + self->mLock.unlock(); + break; + } + + // Release our strong reference, to let a chance to the thread + // to die a peaceful death. + strong.clear(); + // And immediately, reacquire a strong reference for the next loop + strong = weak.promote(); + } while(strong != 0); + + return 0; +} + +void Thread::requestExit() +{ + mExitPending = true; +} + +status_t Thread::requestExitAndWait() +{ + if (mStatus == OK) { + + if (mThread == getThreadId()) { + LOGW( + "Thread (this=%p): don't call waitForExit() from this " + "Thread object's thread. It's a guaranteed deadlock!", + this); + return WOULD_BLOCK; + } + + requestExit(); + + Mutex::Autolock _l(mLock); + while (mRunning == true) { + mThreadExitedCondition.wait(mLock); + } + mExitPending = false; + } + return mStatus; +} + +bool Thread::exitPending() const +{ + return mExitPending; +} + + + +}; // namespace android diff --git a/libs/utils/TimerProbe.cpp b/libs/utils/TimerProbe.cpp new file mode 100644 index 000000000..835480d36 --- /dev/null +++ b/libs/utils/TimerProbe.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#if ENABLE_TIMER_PROBE + +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "time" + +namespace android { + +Vector TimerProbe::gBuckets; +TimerProbe* TimerProbe::gExecuteChain; +int TimerProbe::gIndent; +timespec TimerProbe::gRealBase; + +TimerProbe::TimerProbe(const char tag[], int* slot) : mTag(tag) +{ + mNext = gExecuteChain; + gExecuteChain = this; + mIndent = gIndent; + gIndent += 1; + if (mIndent > 0) { + if (*slot == 0) { + int count = gBuckets.add(); + *slot = count; + Bucket& bucket = gBuckets.editItemAt(count); + memset(&bucket, 0, sizeof(Bucket)); + bucket.mTag = tag; + bucket.mSlotPtr = slot; + bucket.mIndent = mIndent; + } + mBucket = *slot; + } + clock_gettime(CLOCK_REALTIME, &mRealStart); + if (gRealBase.tv_sec == 0) + gRealBase = mRealStart; + clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &mPStart); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &mTStart); +} + +void TimerProbe::end() +{ + timespec realEnd, pEnd, tEnd; + clock_gettime(CLOCK_REALTIME, &realEnd); + clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &pEnd); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tEnd); + print(realEnd, pEnd, tEnd); + mTag = NULL; +} + +TimerProbe::~TimerProbe() +{ + if (mTag != NULL) + end(); + gExecuteChain = mNext; + gIndent--; +} + + +uint32_t TimerProbe::ElapsedTime(const timespec& start, const timespec& end) +{ + int sec = end.tv_sec - start.tv_sec; + int nsec = end.tv_nsec - start.tv_nsec; + if (nsec < 0) { + sec--; + nsec += 1000000000; + } + return sec * 1000000 + nsec / 1000; +} + +void TimerProbe::print(const timespec& r, const timespec& p, + const timespec& t) const +{ + uint32_t es = ElapsedTime(gRealBase, mRealStart); + uint32_t er = ElapsedTime(mRealStart, r); + uint32_t ep = ElapsedTime(mPStart, p); + uint32_t et = ElapsedTime(mTStart, t); + if (mIndent > 0) { + Bucket& bucket = gBuckets.editItemAt(mBucket); + if (bucket.mStart == 0) + bucket.mStart = es; + bucket.mReal += er; + bucket.mProcess += ep; + bucket.mThread += et; + bucket.mCount++; + return; + } + int index = 0; + int buckets = gBuckets.size(); + int count = 1; + const char* tag = mTag; + int indent = mIndent; + do { + LOGD("%-30.30s: (%3d) %-5.*s time=%-10.3f real=%7dus process=%7dus (%3d%%) thread=%7dus (%3d%%)\n", + tag, count, indent > 5 ? 5 : indent, "+++++", es / 1000000.0, + er, ep, ep * 100 / er, et, et * 100 / er); + if (index >= buckets) + break; + Bucket& bucket = gBuckets.editItemAt(index); + count = bucket.mCount; + es = bucket.mStart; + er = bucket.mReal; + ep = bucket.mProcess; + et = bucket.mThread; + tag = bucket.mTag; + indent = bucket.mIndent; + *bucket.mSlotPtr = 0; + } while (++index); // always true + gBuckets.clear(); +} + +}; // namespace android + +#endif diff --git a/libs/utils/Timers.cpp b/libs/utils/Timers.cpp new file mode 100644 index 000000000..2abc811a0 --- /dev/null +++ b/libs/utils/Timers.cpp @@ -0,0 +1,240 @@ +/* + * 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 +#include // may need usleep +#include + +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_WIN32_THREADS +#include +#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 + }; + 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 +} + +//#define MONITOR_USLEEP + +/* + * Sleep long enough that we'll wake up "interval" milliseconds after + * the previous snooze. + * + * The "nextTick" argument is updated on each call, and should be passed + * in every time. Set its fields to zero on the first call. + * + * Returns the #of intervals we have overslept, which will be zero if we're + * on time. [Currently just returns 0 or 1.] + */ +int sleepForInterval(long interval, struct timeval* pNextTick) +{ + struct timeval now; + long long timeBeforeNext; + long sleepTime = 0; + bool overSlept = false; + //int usleepBias = 0; + +#ifdef USLEEP_BIAS + /* + * Linux likes to add 9000ms or so. + * [not using this for now] + */ + //usleepBias = USLEEP_BIAS; +#endif + + gettimeofday(&now, NULL); + + if (pNextTick->tv_sec == 0) { + /* special-case for first time through */ + *pNextTick = now; + sleepTime = interval; + android::DurationTimer::addToTimeval(pNextTick, interval); + } else { + /* + * Compute how much time there is before the next tick. If this + * value is negative, we've run over. If we've run over a little + * bit we can shorten the next frame to keep the pace steady, but + * if we've dramatically overshot we need to re-sync. + */ + timeBeforeNext = android::DurationTimer::subtractTimevals(pNextTick, &now); + //printf("TOP: now=%ld.%ld next=%ld.%ld diff=%ld\n", + // now.tv_sec, now.tv_usec, pNextTick->tv_sec, pNextTick->tv_usec, + // (long) timeBeforeNext); + if (timeBeforeNext < -interval) { + /* way over */ + overSlept = true; + sleepTime = 0; + *pNextTick = now; + } else if (timeBeforeNext <= 0) { + /* slightly over, keep the pace steady */ + overSlept = true; + sleepTime = 0; + } else if (timeBeforeNext <= interval) { + /* right on schedule */ + sleepTime = timeBeforeNext; + } else if (timeBeforeNext > interval && timeBeforeNext <= 2*interval) { + /* sleep call returned early; do a longer sleep this time */ + sleepTime = timeBeforeNext; + } else if (timeBeforeNext > interval) { + /* we went back in time -- somebody updated system clock? */ + /* (could also be a *seriously* broken usleep()) */ + LOG(LOG_DEBUG, "", + " Impossible: timeBeforeNext = %ld\n", (long)timeBeforeNext); + sleepTime = 0; + *pNextTick = now; + } + android::DurationTimer::addToTimeval(pNextTick, interval); + } + //printf(" Before sleep: now=%ld.%ld next=%ld.%ld sleepTime=%ld\n", + // now.tv_sec, now.tv_usec, pNextTick->tv_sec, pNextTick->tv_usec, + // sleepTime); + + /* + * Sleep for the designated period of time. + * + * Linux tends to sleep for longer than requested, often by 17-18ms. + * MinGW tends to sleep for less than requested, by as much as 14ms, + * but occasionally oversleeps for 40+ms (looks like some external + * factors plus round-off on a 64Hz clock). Cygwin is pretty steady. + * + * If you start the MinGW version, and then launch the Cygwin version, + * the MinGW clock becomes more erratic. Not entirely sure why. + * + * (There's a lot of stuff here; it's really just a usleep() call with + * a bunch of instrumentation.) + */ + if (sleepTime > 0) { +#if defined(MONITOR_USLEEP) + struct timeval before, after; + long long actual; + + gettimeofday(&before, NULL); + usleep((long) sleepTime); + gettimeofday(&after, NULL); + + /* check usleep() accuracy; default Linux threads are pretty sloppy */ + actual = android::DurationTimer::subtractTimevals(&after, &before); + if ((long) actual < sleepTime - 14000 /*(sleepTime/10)*/ || + (long) actual > sleepTime + 20000 /*(sleepTime/10)*/) + { + LOG(LOG_DEBUG, "", " Odd usleep: req=%ld, actual=%ld\n", sleepTime, + (long) actual); + } +#else +#ifdef HAVE_WIN32_THREADS + Sleep( sleepTime/1000 ); +#else + usleep((long) sleepTime); +#endif +#endif + } + + //printf("slept %d\n", sleepTime); + + if (overSlept) + return 1; // close enough + else + return 0; +} + + +/* + * =========================================================================== + * DurationTimer + * =========================================================================== + */ + +using namespace android; + +// Start the timer. +void DurationTimer::start(void) +{ + gettimeofday(&mStartWhen, NULL); +} + +// Stop the timer. +void DurationTimer::stop(void) +{ + gettimeofday(&mStopWhen, NULL); +} + +// Get the duration in microseconds. +long long DurationTimer::durationUsecs(void) const +{ + return (long) subtractTimevals(&mStopWhen, &mStartWhen); +} + +// Subtract two timevals. Returns the difference (ptv1-ptv2) in +// microseconds. +/*static*/ long long DurationTimer::subtractTimevals(const struct timeval* ptv1, + const struct timeval* ptv2) +{ + long long stop = ((long long) ptv1->tv_sec) * 1000000LL + + ((long long) ptv1->tv_usec); + long long start = ((long long) ptv2->tv_sec) * 1000000LL + + ((long long) ptv2->tv_usec); + return stop - start; +} + +// Add the specified amount of time to the timeval. +/*static*/ void DurationTimer::addToTimeval(struct timeval* ptv, long usec) +{ + if (usec < 0) { + LOG(LOG_WARN, "", "Negative values not supported in addToTimeval\n"); + return; + } + + // normalize tv_usec if necessary + if (ptv->tv_usec >= 1000000) { + ptv->tv_sec += ptv->tv_usec / 1000000; + ptv->tv_usec %= 1000000; + } + + ptv->tv_usec += usec % 1000000; + if (ptv->tv_usec >= 1000000) { + ptv->tv_usec -= 1000000; + ptv->tv_sec++; + } + ptv->tv_sec += usec / 1000000; +} + diff --git a/libs/utils/Unicode.cpp b/libs/utils/Unicode.cpp new file mode 100644 index 000000000..33f535fd1 --- /dev/null +++ b/libs/utils/Unicode.cpp @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "utils/AndroidUnicode.h" +#include "characterData.h" + +#define LOG_TAG "Unicode" +#include "utils/Log.h" + +// ICU headers for using macros +#include + +#define MIN_RADIX 2 +#define MAX_RADIX 36 + +#define TYPE_SHIFT 0 +#define TYPE_MASK ((1<<5)-1) + +#define DIRECTION_SHIFT (TYPE_SHIFT+5) +#define DIRECTION_MASK ((1<<5)-1) + +#define MIRRORED_SHIFT (DIRECTION_SHIFT+5) +#define MIRRORED_MASK ((1<<1)-1) + +#define TOUPPER_SHIFT (MIRRORED_SHIFT+1) +#define TOUPPER_MASK ((1<<6)-1) + +#define TOLOWER_SHIFT (TOUPPER_SHIFT+6) +#define TOLOWER_MASK ((1<<6)-1) + +#define TOTITLE_SHIFT (TOLOWER_SHIFT+6) +#define TOTITLE_MASK ((1<<2)-1) + +#define MIRROR_SHIFT (TOTITLE_SHIFT+2) +#define MIRROR_MASK ((1<<5)-1) + +#define NUMERIC_SHIFT (TOTITLE_SHIFT+2) +#define NUMERIC_MASK ((1<<7)-1) + +#define DECOMPOSITION_SHIFT (11) +#define DECOMPOSITION_MASK ((1<<5)-1) + +/* + * Returns the value stored in the CharacterData tables that contains + * an index into the packed data table and the decomposition type. + */ +static uint16_t findCharacterValue(UChar32 c) +{ + LOG_ASSERT(c >= 0 && c <= 0x10FFFF, "findCharacterValue received an invalid codepoint"); + if (c < 256) + return CharacterData::LATIN1_DATA[c]; + + // Rotate the bits because the tables are separated into even and odd codepoints + c = (c >> 1) | ((c & 1) << 20); + + CharacterData::Range search = CharacterData::FULL_DATA[c >> 16]; + const uint32_t* array = search.array; + + // This trick is so that that compare in the while loop does not + // need to shift the array entry down by 16 + c <<= 16; + c |= 0xFFFF; + + int high = (int)search.length - 1; + int low = 0; + + if (high < 0) + return 0; + + while (low < high - 1) + { + int probe = (high + low) >> 1; + + // The entries contain the codepoint in the high 16 bits and the index + // into PACKED_DATA in the low 16. + if (array[probe] > (unsigned)c) + high = probe; + else + low = probe; + } + + LOG_ASSERT((array[low] <= (unsigned)c), "A suitable range was not found"); + return array[low] & 0xFFFF; +} + +uint32_t android::Unicode::getPackedData(UChar32 c) +{ + // findCharacterValue returns a 16-bit value with the top 5 bits containing a decomposition type + // and the remaining bits containing an index. + return CharacterData::PACKED_DATA[findCharacterValue(c) & 0x7FF]; +} + +android::Unicode::CharType android::Unicode::getType(UChar32 c) +{ + if (c < 0 || c >= 0x10FFFF) + return CHARTYPE_UNASSIGNED; + return (CharType)((getPackedData(c) >> TYPE_SHIFT) & TYPE_MASK); +} + +android::Unicode::DecompositionType android::Unicode::getDecompositionType(UChar32 c) +{ + // findCharacterValue returns a 16-bit value with the top 5 bits containing a decomposition type + // and the remaining bits containing an index. + return (DecompositionType)((findCharacterValue(c) >> DECOMPOSITION_SHIFT) & DECOMPOSITION_MASK); +} + +int android::Unicode::getDigitValue(UChar32 c, int radix) +{ + if (radix < MIN_RADIX || radix > MAX_RADIX) + return -1; + + int tempValue = radix; + + if (c >= '0' && c <= '9') + tempValue = c - '0'; + else if (c >= 'a' && c <= 'z') + tempValue = c - 'a' + 10; + else if (c >= 'A' && c <= 'Z') + tempValue = c - 'A' + 10; + + return tempValue < radix ? tempValue : -1; +} + +int android::Unicode::getNumericValue(UChar32 c) +{ + if (isMirrored(c)) + return -1; + + return (int) CharacterData::NUMERICS[((getPackedData(c) >> NUMERIC_SHIFT) & NUMERIC_MASK)]; +} + +UChar32 android::Unicode::toLower(UChar32 c) +{ + return c + CharacterData::LCDIFF[(getPackedData(c) >> TOLOWER_SHIFT) & TOLOWER_MASK]; +} + +UChar32 android::Unicode::toUpper(UChar32 c) +{ + return c + CharacterData::UCDIFF[(getPackedData(c) >> TOUPPER_SHIFT) & TOUPPER_MASK]; +} + +android::Unicode::Direction android::Unicode::getDirectionality(UChar32 c) +{ + uint32_t data = getPackedData(c); + + if (0 == data) + return DIRECTIONALITY_UNDEFINED; + + Direction d = (Direction) ((data >> DIRECTION_SHIFT) & DIRECTION_MASK); + + if (DIRECTION_MASK == d) + return DIRECTIONALITY_UNDEFINED; + + return d; +} + +bool android::Unicode::isMirrored(UChar32 c) +{ + return ((getPackedData(c) >> MIRRORED_SHIFT) & MIRRORED_MASK) != 0; +} + +UChar32 android::Unicode::toMirror(UChar32 c) +{ + if (!isMirrored(c)) + return c; + + return c + CharacterData::MIRROR_DIFF[(getPackedData(c) >> MIRROR_SHIFT) & MIRROR_MASK]; +} + +UChar32 android::Unicode::toTitle(UChar32 c) +{ + int32_t diff = CharacterData::TCDIFF[(getPackedData(c) >> TOTITLE_SHIFT) & TOTITLE_MASK]; + + if (TOTITLE_MASK == diff) + return toUpper(c); + + return c + diff; +} + + diff --git a/libs/utils/VectorImpl.cpp b/libs/utils/VectorImpl.cpp new file mode 100644 index 000000000..2c2d6675c --- /dev/null +++ b/libs/utils/VectorImpl.cpp @@ -0,0 +1,611 @@ +/* + * 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 +#include +#include + +#include +#include +#include +#include + +/*****************************************************************************/ + + +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::sharedBuffer(mStorage)->acquire(); + } +} + +VectorImpl::~VectorImpl() +{ + LOG_ASSERT(!mCount, + "[%p] " + "subclasses of VectorImpl must call finish_vector()" + " in their destructor. Leaking %d bytes.", + this, (int)(mCount*mItemSize)); + // We can't call _do_destroy() here because the vtable is already gone. +} + +VectorImpl& VectorImpl::operator = (const VectorImpl& rhs) +{ + LOG_ASSERT(mItemSize == rhs.mItemSize, + "Vector<> have different types (this=%p, rhs=%p)", this, &rhs); + if (this != &rhs) { + release_storage(); + if (rhs.mCount) { + mStorage = rhs.mStorage; + mCount = rhs.mCount; + SharedBuffer::sharedBuffer(mStorage)->acquire(); + } else { + mStorage = 0; + mCount = 0; + } + } + return *this; +} + +void* VectorImpl::editArrayImpl() +{ + if (mStorage) { + SharedBuffer* sb = SharedBuffer::sharedBuffer(mStorage)->attemptEdit(); + if (sb == 0) { + sb = SharedBuffer::alloc(capacity() * mItemSize); + if (sb) { + _do_copy(sb->data(), mStorage, mCount); + release_storage(); + mStorage = sb->data(); + } + } + } + return mStorage; +} + +size_t VectorImpl::capacity() const +{ + if (mStorage) { + return SharedBuffer::sharedBuffer(mStorage)->size() / mItemSize; + } + return 0; +} + +ssize_t VectorImpl::insertVectorAt(const VectorImpl& vector, size_t index) +{ + if (index > size()) + return BAD_INDEX; + void* where = _grow(index, vector.size()); + if (where) { + _do_copy(where, vector.arrayImpl(), vector.size()); + } + return where ? index : (ssize_t)NO_MEMORY; +} + +ssize_t VectorImpl::appendVector(const VectorImpl& vector) +{ + return insertVectorAt(vector, size()); +} + +ssize_t VectorImpl::insertAt(size_t index, size_t numItems) +{ + return insertAt(0, index, numItems); +} + +ssize_t VectorImpl::insertAt(const void* item, size_t index, size_t numItems) +{ + if (index > size()) + return BAD_INDEX; + void* where = _grow(index, numItems); + if (where) { + if (item) { + _do_splat(where, item, numItems); + } else { + _do_construct(where, numItems); + } + } + return where ? index : (ssize_t)NO_MEMORY; +} + +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(arrayImpl()); + void* temp = 0; + ssize_t i = 1; + while (i < count) { + void* item = reinterpret_cast(array) + mItemSize*(i); + void* curr = reinterpret_cast(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; + _do_construct(temp, 1); + item = reinterpret_cast(array) + mItemSize*(i); + curr = reinterpret_cast(array) + mItemSize*(i-1); + } + + _do_copy(temp, item, 1); + + ssize_t j = i-1; + void* next = reinterpret_cast(array) + mItemSize*(i); + do { + _do_copy(next, curr, 1); + next = curr; + --j; + curr = reinterpret_cast(array) + mItemSize*(j); + } while (j>=0 && (cmp(curr, temp, state) > 0)); + + _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) +{ + LOG_ASSERT(index 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) +{ + LOG_ASSERT(index(buffer) + index*mItemSize; + return 0; +} + +const void* VectorImpl::itemLocation(size_t index) const +{ + LOG_ASSERT(index(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(array); + } else { + return NO_MEMORY; + } + return new_capacity; +} + +void VectorImpl::release_storage() +{ + if (mStorage) { + const SharedBuffer* sb = SharedBuffer::sharedBuffer(mStorage); + if (sb->release(SharedBuffer::eKeepStorage) == 1) { + _do_destroy(mStorage, mCount); + SharedBuffer::dealloc(sb); + } + } +} + +void* VectorImpl::_grow(size_t where, size_t amount) +{ +// LOGV("_grow(this=%p, where=%d, amount=%d) count=%d, capacity=%d", +// this, (int)where, (int)amount, (int)mCount, (int)capacity()); + + if (where > mCount) + where = mCount; + + const size_t new_size = mCount + amount; + if (capacity() < new_size) { + const size_t new_capacity = max(kMinVectorCapacity, ((new_size*3)+1)/2); +// LOGV("grow vector %p, new_capacity=%d", this, (int)new_capacity); + if ((mStorage) && + (mCount==where) && + (mFlags & HAS_TRIVIAL_COPY) && + (mFlags & HAS_TRIVIAL_DTOR)) + { + const SharedBuffer* cur_sb = SharedBuffer::sharedBuffer(mStorage); + SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize); + mStorage = sb->data(); + } else { + SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize); + if (sb) { + void* array = sb->data(); + if (where>0) { + _do_copy(array, mStorage, where); + } + if (mCount>where) { + const void* from = reinterpret_cast(mStorage) + where*mItemSize; + void* dest = reinterpret_cast(array) + (where+amount)*mItemSize; + _do_copy(dest, from, mCount-where); + } + release_storage(); + mStorage = const_cast(array); + } + } + } else { + ssize_t s = mCount-where; + if (s>0) { + void* array = editArrayImpl(); + void* to = reinterpret_cast(array) + (where+amount)*mItemSize; + const void* from = reinterpret_cast(array) + where*mItemSize; + _do_move_forward(to, from, s); + } + } + mCount += amount; + void* free_space = const_cast(itemLocation(where)); + return free_space; +} + +void VectorImpl::_shrink(size_t where, size_t amount) +{ + if (!mStorage) + return; + +// LOGV("_shrink(this=%p, where=%d, amount=%d) count=%d, capacity=%d", +// this, (int)where, (int)amount, (int)mCount, (int)capacity()); + + if (where >= mCount) + where = mCount - amount; + + const size_t new_size = mCount - amount; + if (new_size*3 < capacity()) { + const size_t new_capacity = max(kMinVectorCapacity, new_size*2); +// LOGV("shrink vector %p, new_capacity=%d", this, (int)new_capacity); + if ((where == mCount-amount) && + (mFlags & HAS_TRIVIAL_COPY) && + (mFlags & HAS_TRIVIAL_DTOR)) + { + const SharedBuffer* cur_sb = SharedBuffer::sharedBuffer(mStorage); + SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize); + mStorage = sb->data(); + } else { + SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize); + if (sb) { + void* array = sb->data(); + if (where>0) { + _do_copy(array, mStorage, where); + } + if (mCount > where+amount) { + const void* from = reinterpret_cast(mStorage) + (where+amount)*mItemSize; + void* dest = reinterpret_cast(array) + where*mItemSize; + _do_copy(dest, from, mCount-(where+amount)); + } + release_storage(); + mStorage = const_cast(array); + } + } + } else { + void* array = editArrayImpl(); + void* to = reinterpret_cast(array) + where*mItemSize; + _do_destroy(to, amount); + ssize_t s = mCount-(where+amount); + if (s>0) { + const void* from = reinterpret_cast(array) + (where+amount)*mItemSize; + _do_move_backward(to, from, s); + } + } + + // adjust the number of items... + mCount -= amount; +} + +size_t VectorImpl::itemSize() const { + return mItemSize; +} + +void VectorImpl::_do_construct(void* storage, size_t num) const +{ + if (!(mFlags & HAS_TRIVIAL_CTOR)) { + do_construct(storage, num); + } +} + +void VectorImpl::_do_destroy(void* storage, size_t num) const +{ + if (!(mFlags & HAS_TRIVIAL_DTOR)) { + do_destroy(storage, num); + } +} + +void VectorImpl::_do_copy(void* dest, const void* from, size_t num) const +{ + if (!(mFlags & HAS_TRIVIAL_COPY)) { + do_copy(dest, from, num); + } else { + memcpy(dest, from, num*itemSize()); + } +} + +void VectorImpl::_do_splat(void* dest, const void* item, size_t num) const { + do_splat(dest, item, num); +} + +void VectorImpl::_do_move_forward(void* dest, const void* from, size_t num) const { + do_move_forward(dest, from, num); +} + +void VectorImpl::_do_move_backward(void* dest, const void* from, size_t num) const { + do_move_backward(dest, from, num); +} + +void VectorImpl::reservedVectorImpl1() { } +void VectorImpl::reservedVectorImpl2() { } +void VectorImpl::reservedVectorImpl3() { } +void VectorImpl::reservedVectorImpl4() { } +void VectorImpl::reservedVectorImpl5() { } +void VectorImpl::reservedVectorImpl6() { } +void VectorImpl::reservedVectorImpl7() { } +void VectorImpl::reservedVectorImpl8() { } + +/*****************************************************************************/ + +SortedVectorImpl::SortedVectorImpl(size_t itemSize, uint32_t flags) + : VectorImpl(itemSize, flags) +{ +} + +SortedVectorImpl::SortedVectorImpl(const VectorImpl& rhs) +: VectorImpl(rhs) +{ +} + +SortedVectorImpl::~SortedVectorImpl() +{ +} + +SortedVectorImpl& SortedVectorImpl::operator = (const SortedVectorImpl& rhs) +{ + return static_cast( VectorImpl::operator = (static_cast(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(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(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(vector), 0); + } else if (do_compare(vector.arrayImpl(), itemLocation(size()-1)) >= 0) { + err = VectorImpl::appendVector(static_cast(vector)); + } else { + // this could be made a little better + err = merge(static_cast(vector)); + } + } + return err; +} + +ssize_t SortedVectorImpl::remove(const void* item) +{ + ssize_t i = indexOf(item); + if (i>=0) { + VectorImpl::removeItemsAt(i, 1); + } + return i; +} + +void SortedVectorImpl::reservedSortedVectorImpl1() { }; +void SortedVectorImpl::reservedSortedVectorImpl2() { }; +void SortedVectorImpl::reservedSortedVectorImpl3() { }; +void SortedVectorImpl::reservedSortedVectorImpl4() { }; +void SortedVectorImpl::reservedSortedVectorImpl5() { }; +void SortedVectorImpl::reservedSortedVectorImpl6() { }; +void SortedVectorImpl::reservedSortedVectorImpl7() { }; +void SortedVectorImpl::reservedSortedVectorImpl8() { }; + + +/*****************************************************************************/ + +}; // namespace android + diff --git a/libs/utils/ZipEntry.cpp b/libs/utils/ZipEntry.cpp new file mode 100644 index 000000000..fbc9e6784 --- /dev/null +++ b/libs/utils/ZipEntry.cpp @@ -0,0 +1,696 @@ +/* + * 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. + */ + +// +// Access to entries in a Zip archive. +// + +#define LOG_TAG "zip" + +#include "utils/ZipEntry.h" +#include "utils/Log.h" + +#include +#include +#include + +using namespace android; + +/* + * Initialize a new ZipEntry structure from a FILE* positioned at a + * CentralDirectoryEntry. + * + * On exit, the file pointer will be at the start of the next CDE or + * at the EOCD. + */ +status_t ZipEntry::initFromCDE(FILE* fp) +{ + status_t result; + long posn; + bool hasDD; + + //LOGV("initFromCDE ---\n"); + + /* read the CDE */ + result = mCDE.read(fp); + if (result != NO_ERROR) { + LOGD("mCDE.read failed\n"); + return result; + } + + //mCDE.dump(); + + /* using the info in the CDE, go load up the LFH */ + posn = ftell(fp); + if (fseek(fp, mCDE.mLocalHeaderRelOffset, SEEK_SET) != 0) { + LOGD("local header seek failed (%ld)\n", + mCDE.mLocalHeaderRelOffset); + return UNKNOWN_ERROR; + } + + result = mLFH.read(fp); + if (result != NO_ERROR) { + LOGD("mLFH.read failed\n"); + return result; + } + + if (fseek(fp, posn, SEEK_SET) != 0) + return UNKNOWN_ERROR; + + //mLFH.dump(); + + /* + * We *might* need to read the Data Descriptor at this point and + * integrate it into the LFH. If this bit is set, the CRC-32, + * compressed size, and uncompressed size will be zero. In practice + * these seem to be rare. + */ + hasDD = (mLFH.mGPBitFlag & kUsesDataDescr) != 0; + if (hasDD) { + // do something clever + //LOGD("+++ has data descriptor\n"); + } + + /* + * Sanity-check the LFH. Note that this will fail if the "kUsesDataDescr" + * flag is set, because the LFH is incomplete. (Not a problem, since we + * prefer the CDE values.) + */ + if (!hasDD && !compareHeaders()) { + LOGW("WARNING: header mismatch\n"); + // keep going? + } + + /* + * If the mVersionToExtract is greater than 20, we may have an + * issue unpacking the record -- could be encrypted, compressed + * with something we don't support, or use Zip64 extensions. We + * can defer worrying about that to when we're extracting data. + */ + + return NO_ERROR; +} + +/* + * Initialize a new entry. Pass in the file name and an optional comment. + * + * Initializes the CDE and the LFH. + */ +void ZipEntry::initNew(const char* fileName, const char* comment) +{ + assert(fileName != NULL && *fileName != '\0'); // name required + + /* most fields are properly initialized by constructor */ + mCDE.mVersionMadeBy = kDefaultMadeBy; + mCDE.mVersionToExtract = kDefaultVersion; + mCDE.mCompressionMethod = kCompressStored; + mCDE.mFileNameLength = strlen(fileName); + if (comment != NULL) + mCDE.mFileCommentLength = strlen(comment); + mCDE.mExternalAttrs = 0x81b60020; // matches what WinZip does + + if (mCDE.mFileNameLength > 0) { + mCDE.mFileName = new unsigned char[mCDE.mFileNameLength+1]; + strcpy((char*) mCDE.mFileName, fileName); + } + if (mCDE.mFileCommentLength > 0) { + /* TODO: stop assuming null-terminated ASCII here? */ + mCDE.mFileComment = new unsigned char[mCDE.mFileCommentLength+1]; + strcpy((char*) mCDE.mFileComment, comment); + } + + copyCDEtoLFH(); +} + +/* + * Initialize a new entry, starting with the ZipEntry from a different + * archive. + * + * Initializes the CDE and the LFH. + */ +status_t ZipEntry::initFromExternal(const ZipFile* pZipFile, + const ZipEntry* pEntry) +{ + /* + * Copy everything in the CDE over, then fix up the hairy bits. + */ + memcpy(&mCDE, &pEntry->mCDE, sizeof(mCDE)); + + if (mCDE.mFileNameLength > 0) { + mCDE.mFileName = new unsigned char[mCDE.mFileNameLength+1]; + if (mCDE.mFileName == NULL) + return NO_MEMORY; + strcpy((char*) mCDE.mFileName, (char*)pEntry->mCDE.mFileName); + } + if (mCDE.mFileCommentLength > 0) { + mCDE.mFileComment = new unsigned char[mCDE.mFileCommentLength+1]; + if (mCDE.mFileComment == NULL) + return NO_MEMORY; + strcpy((char*) mCDE.mFileComment, (char*)pEntry->mCDE.mFileComment); + } + if (mCDE.mExtraFieldLength > 0) { + /* we null-terminate this, though it may not be a string */ + mCDE.mExtraField = new unsigned char[mCDE.mExtraFieldLength+1]; + if (mCDE.mExtraField == NULL) + return NO_MEMORY; + memcpy(mCDE.mExtraField, pEntry->mCDE.mExtraField, + mCDE.mExtraFieldLength+1); + } + + /* construct the LFH from the CDE */ + copyCDEtoLFH(); + + /* + * The LFH "extra" field is independent of the CDE "extra", so we + * handle it here. + */ + assert(mLFH.mExtraField == NULL); + mLFH.mExtraFieldLength = pEntry->mLFH.mExtraFieldLength; + if (mLFH.mExtraFieldLength > 0) { + mLFH.mExtraField = new unsigned char[mLFH.mExtraFieldLength+1]; + if (mLFH.mExtraField == NULL) + return NO_MEMORY; + memcpy(mLFH.mExtraField, pEntry->mLFH.mExtraField, + mLFH.mExtraFieldLength+1); + } + + return NO_ERROR; +} + +/* + * Insert pad bytes in the LFH by tweaking the "extra" field. This will + * potentially confuse something that put "extra" data in here earlier, + * but I can't find an actual problem. + */ +status_t ZipEntry::addPadding(int padding) +{ + if (padding <= 0) + return INVALID_OPERATION; + + //LOGI("HEY: adding %d pad bytes to existing %d in %s\n", + // padding, mLFH.mExtraFieldLength, mCDE.mFileName); + + if (mLFH.mExtraFieldLength > 0) { + /* extend existing field */ + unsigned char* newExtra; + + newExtra = new unsigned char[mLFH.mExtraFieldLength + padding]; + if (newExtra == NULL) + return NO_MEMORY; + memset(newExtra + mLFH.mExtraFieldLength, 0, padding); + memcpy(newExtra, mLFH.mExtraField, mLFH.mExtraFieldLength); + + delete[] mLFH.mExtraField; + mLFH.mExtraField = newExtra; + mLFH.mExtraFieldLength += padding; + } else { + /* create new field */ + mLFH.mExtraField = new unsigned char[padding]; + memset(mLFH.mExtraField, 0, padding); + mLFH.mExtraFieldLength = padding; + } + + return NO_ERROR; +} + +/* + * Set the fields in the LFH equal to the corresponding fields in the CDE. + * + * This does not touch the LFH "extra" field. + */ +void ZipEntry::copyCDEtoLFH(void) +{ + mLFH.mVersionToExtract = mCDE.mVersionToExtract; + mLFH.mGPBitFlag = mCDE.mGPBitFlag; + mLFH.mCompressionMethod = mCDE.mCompressionMethod; + mLFH.mLastModFileTime = mCDE.mLastModFileTime; + mLFH.mLastModFileDate = mCDE.mLastModFileDate; + mLFH.mCRC32 = mCDE.mCRC32; + mLFH.mCompressedSize = mCDE.mCompressedSize; + mLFH.mUncompressedSize = mCDE.mUncompressedSize; + mLFH.mFileNameLength = mCDE.mFileNameLength; + // the "extra field" is independent + + delete[] mLFH.mFileName; + if (mLFH.mFileNameLength > 0) { + mLFH.mFileName = new unsigned char[mLFH.mFileNameLength+1]; + strcpy((char*) mLFH.mFileName, (const char*) mCDE.mFileName); + } else { + mLFH.mFileName = NULL; + } +} + +/* + * Set some information about a file after we add it. + */ +void ZipEntry::setDataInfo(long uncompLen, long compLen, unsigned long crc32, + int compressionMethod) +{ + mCDE.mCompressionMethod = compressionMethod; + mCDE.mCRC32 = crc32; + mCDE.mCompressedSize = compLen; + mCDE.mUncompressedSize = uncompLen; + mCDE.mCompressionMethod = compressionMethod; + if (compressionMethod == kCompressDeflated) { + mCDE.mGPBitFlag |= 0x0002; // indicates maximum compression used + } + copyCDEtoLFH(); +} + +/* + * See if the data in mCDE and mLFH match up. This is mostly useful for + * debugging these classes, but it can be used to identify damaged + * archives. + * + * Returns "false" if they differ. + */ +bool ZipEntry::compareHeaders(void) const +{ + if (mCDE.mVersionToExtract != mLFH.mVersionToExtract) { + LOGV("cmp: VersionToExtract\n"); + return false; + } + if (mCDE.mGPBitFlag != mLFH.mGPBitFlag) { + LOGV("cmp: GPBitFlag\n"); + return false; + } + if (mCDE.mCompressionMethod != mLFH.mCompressionMethod) { + LOGV("cmp: CompressionMethod\n"); + return false; + } + if (mCDE.mLastModFileTime != mLFH.mLastModFileTime) { + LOGV("cmp: LastModFileTime\n"); + return false; + } + if (mCDE.mLastModFileDate != mLFH.mLastModFileDate) { + LOGV("cmp: LastModFileDate\n"); + return false; + } + if (mCDE.mCRC32 != mLFH.mCRC32) { + LOGV("cmp: CRC32\n"); + return false; + } + if (mCDE.mCompressedSize != mLFH.mCompressedSize) { + LOGV("cmp: CompressedSize\n"); + return false; + } + if (mCDE.mUncompressedSize != mLFH.mUncompressedSize) { + LOGV("cmp: UncompressedSize\n"); + return false; + } + if (mCDE.mFileNameLength != mLFH.mFileNameLength) { + LOGV("cmp: FileNameLength\n"); + return false; + } +#if 0 // this seems to be used for padding, not real data + if (mCDE.mExtraFieldLength != mLFH.mExtraFieldLength) { + LOGV("cmp: ExtraFieldLength\n"); + return false; + } +#endif + if (mCDE.mFileName != NULL) { + if (strcmp((char*) mCDE.mFileName, (char*) mLFH.mFileName) != 0) { + LOGV("cmp: FileName\n"); + return false; + } + } + + return true; +} + + +/* + * Convert the DOS date/time stamp into a UNIX time stamp. + */ +time_t ZipEntry::getModWhen(void) const +{ + struct tm parts; + + parts.tm_sec = (mCDE.mLastModFileTime & 0x001f) << 1; + parts.tm_min = (mCDE.mLastModFileTime & 0x07e0) >> 5; + parts.tm_hour = (mCDE.mLastModFileTime & 0xf800) >> 11; + parts.tm_mday = (mCDE.mLastModFileDate & 0x001f); + parts.tm_mon = ((mCDE.mLastModFileDate & 0x01e0) >> 5) -1; + parts.tm_year = ((mCDE.mLastModFileDate & 0xfe00) >> 9) + 80; + parts.tm_wday = parts.tm_yday = 0; + parts.tm_isdst = -1; // DST info "not available" + + return mktime(&parts); +} + +/* + * Set the CDE/LFH timestamp from UNIX time. + */ +void ZipEntry::setModWhen(time_t when) +{ +#ifdef HAVE_LOCALTIME_R + struct tm tmResult; +#endif + time_t even; + unsigned short zdate, ztime; + + struct tm* ptm; + + /* round up to an even number of seconds */ + even = (time_t)(((unsigned long)(when) + 1) & (~1)); + + /* expand */ +#ifdef HAVE_LOCALTIME_R + ptm = localtime_r(&even, &tmResult); +#else + ptm = localtime(&even); +#endif + + int year; + year = ptm->tm_year; + if (year < 80) + year = 80; + + zdate = (year - 80) << 9 | (ptm->tm_mon+1) << 5 | ptm->tm_mday; + ztime = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1; + + mCDE.mLastModFileTime = mLFH.mLastModFileTime = ztime; + mCDE.mLastModFileDate = mLFH.mLastModFileDate = zdate; +} + + +/* + * =========================================================================== + * ZipEntry::LocalFileHeader + * =========================================================================== + */ + +/* + * Read a local file header. + * + * On entry, "fp" points to the signature at the start of the header. + * On exit, "fp" points to the start of data. + */ +status_t ZipEntry::LocalFileHeader::read(FILE* fp) +{ + status_t result = NO_ERROR; + unsigned char buf[kLFHLen]; + + assert(mFileName == NULL); + assert(mExtraField == NULL); + + if (fread(buf, 1, kLFHLen, fp) != kLFHLen) { + result = UNKNOWN_ERROR; + goto bail; + } + + if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) { + LOGD("whoops: didn't find expected signature\n"); + result = UNKNOWN_ERROR; + goto bail; + } + + mVersionToExtract = ZipEntry::getShortLE(&buf[0x04]); + mGPBitFlag = ZipEntry::getShortLE(&buf[0x06]); + mCompressionMethod = ZipEntry::getShortLE(&buf[0x08]); + mLastModFileTime = ZipEntry::getShortLE(&buf[0x0a]); + mLastModFileDate = ZipEntry::getShortLE(&buf[0x0c]); + mCRC32 = ZipEntry::getLongLE(&buf[0x0e]); + mCompressedSize = ZipEntry::getLongLE(&buf[0x12]); + mUncompressedSize = ZipEntry::getLongLE(&buf[0x16]); + mFileNameLength = ZipEntry::getShortLE(&buf[0x1a]); + mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1c]); + + // TODO: validate sizes + + /* grab filename */ + if (mFileNameLength != 0) { + mFileName = new unsigned char[mFileNameLength+1]; + if (mFileName == NULL) { + result = NO_MEMORY; + goto bail; + } + if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) { + result = UNKNOWN_ERROR; + goto bail; + } + mFileName[mFileNameLength] = '\0'; + } + + /* grab extra field */ + if (mExtraFieldLength != 0) { + mExtraField = new unsigned char[mExtraFieldLength+1]; + if (mExtraField == NULL) { + result = NO_MEMORY; + goto bail; + } + if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) { + result = UNKNOWN_ERROR; + goto bail; + } + mExtraField[mExtraFieldLength] = '\0'; + } + +bail: + return result; +} + +/* + * Write a local file header. + */ +status_t ZipEntry::LocalFileHeader::write(FILE* fp) +{ + unsigned char buf[kLFHLen]; + + ZipEntry::putLongLE(&buf[0x00], kSignature); + ZipEntry::putShortLE(&buf[0x04], mVersionToExtract); + ZipEntry::putShortLE(&buf[0x06], mGPBitFlag); + ZipEntry::putShortLE(&buf[0x08], mCompressionMethod); + ZipEntry::putShortLE(&buf[0x0a], mLastModFileTime); + ZipEntry::putShortLE(&buf[0x0c], mLastModFileDate); + ZipEntry::putLongLE(&buf[0x0e], mCRC32); + ZipEntry::putLongLE(&buf[0x12], mCompressedSize); + ZipEntry::putLongLE(&buf[0x16], mUncompressedSize); + ZipEntry::putShortLE(&buf[0x1a], mFileNameLength); + ZipEntry::putShortLE(&buf[0x1c], mExtraFieldLength); + + if (fwrite(buf, 1, kLFHLen, fp) != kLFHLen) + return UNKNOWN_ERROR; + + /* write filename */ + if (mFileNameLength != 0) { + if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength) + return UNKNOWN_ERROR; + } + + /* write "extra field" */ + if (mExtraFieldLength != 0) { + if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) + return UNKNOWN_ERROR; + } + + return NO_ERROR; +} + + +/* + * Dump the contents of a LocalFileHeader object. + */ +void ZipEntry::LocalFileHeader::dump(void) const +{ + LOGD(" LocalFileHeader contents:\n"); + LOGD(" versToExt=%u gpBits=0x%04x compression=%u\n", + mVersionToExtract, mGPBitFlag, mCompressionMethod); + LOGD(" modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n", + mLastModFileTime, mLastModFileDate, mCRC32); + LOGD(" compressedSize=%lu uncompressedSize=%lu\n", + mCompressedSize, mUncompressedSize); + LOGD(" filenameLen=%u extraLen=%u\n", + mFileNameLength, mExtraFieldLength); + if (mFileName != NULL) + LOGD(" filename: '%s'\n", mFileName); +} + + +/* + * =========================================================================== + * ZipEntry::CentralDirEntry + * =========================================================================== + */ + +/* + * Read the central dir entry that appears next in the file. + * + * On entry, "fp" should be positioned on the signature bytes for the + * entry. On exit, "fp" will point at the signature word for the next + * entry or for the EOCD. + */ +status_t ZipEntry::CentralDirEntry::read(FILE* fp) +{ + status_t result = NO_ERROR; + unsigned char buf[kCDELen]; + + /* no re-use */ + assert(mFileName == NULL); + assert(mExtraField == NULL); + assert(mFileComment == NULL); + + if (fread(buf, 1, kCDELen, fp) != kCDELen) { + result = UNKNOWN_ERROR; + goto bail; + } + + if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) { + LOGD("Whoops: didn't find expected signature\n"); + result = UNKNOWN_ERROR; + goto bail; + } + + mVersionMadeBy = ZipEntry::getShortLE(&buf[0x04]); + mVersionToExtract = ZipEntry::getShortLE(&buf[0x06]); + mGPBitFlag = ZipEntry::getShortLE(&buf[0x08]); + mCompressionMethod = ZipEntry::getShortLE(&buf[0x0a]); + mLastModFileTime = ZipEntry::getShortLE(&buf[0x0c]); + mLastModFileDate = ZipEntry::getShortLE(&buf[0x0e]); + mCRC32 = ZipEntry::getLongLE(&buf[0x10]); + mCompressedSize = ZipEntry::getLongLE(&buf[0x14]); + mUncompressedSize = ZipEntry::getLongLE(&buf[0x18]); + mFileNameLength = ZipEntry::getShortLE(&buf[0x1c]); + mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1e]); + mFileCommentLength = ZipEntry::getShortLE(&buf[0x20]); + mDiskNumberStart = ZipEntry::getShortLE(&buf[0x22]); + mInternalAttrs = ZipEntry::getShortLE(&buf[0x24]); + mExternalAttrs = ZipEntry::getLongLE(&buf[0x26]); + mLocalHeaderRelOffset = ZipEntry::getLongLE(&buf[0x2a]); + + // TODO: validate sizes and offsets + + /* grab filename */ + if (mFileNameLength != 0) { + mFileName = new unsigned char[mFileNameLength+1]; + if (mFileName == NULL) { + result = NO_MEMORY; + goto bail; + } + if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) { + result = UNKNOWN_ERROR; + goto bail; + } + mFileName[mFileNameLength] = '\0'; + } + + /* read "extra field" */ + if (mExtraFieldLength != 0) { + mExtraField = new unsigned char[mExtraFieldLength+1]; + if (mExtraField == NULL) { + result = NO_MEMORY; + goto bail; + } + if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) { + result = UNKNOWN_ERROR; + goto bail; + } + mExtraField[mExtraFieldLength] = '\0'; + } + + + /* grab comment, if any */ + if (mFileCommentLength != 0) { + mFileComment = new unsigned char[mFileCommentLength+1]; + if (mFileComment == NULL) { + result = NO_MEMORY; + goto bail; + } + if (fread(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength) + { + result = UNKNOWN_ERROR; + goto bail; + } + mFileComment[mFileCommentLength] = '\0'; + } + +bail: + return result; +} + +/* + * Write a central dir entry. + */ +status_t ZipEntry::CentralDirEntry::write(FILE* fp) +{ + unsigned char buf[kCDELen]; + + ZipEntry::putLongLE(&buf[0x00], kSignature); + ZipEntry::putShortLE(&buf[0x04], mVersionMadeBy); + ZipEntry::putShortLE(&buf[0x06], mVersionToExtract); + ZipEntry::putShortLE(&buf[0x08], mGPBitFlag); + ZipEntry::putShortLE(&buf[0x0a], mCompressionMethod); + ZipEntry::putShortLE(&buf[0x0c], mLastModFileTime); + ZipEntry::putShortLE(&buf[0x0e], mLastModFileDate); + ZipEntry::putLongLE(&buf[0x10], mCRC32); + ZipEntry::putLongLE(&buf[0x14], mCompressedSize); + ZipEntry::putLongLE(&buf[0x18], mUncompressedSize); + ZipEntry::putShortLE(&buf[0x1c], mFileNameLength); + ZipEntry::putShortLE(&buf[0x1e], mExtraFieldLength); + ZipEntry::putShortLE(&buf[0x20], mFileCommentLength); + ZipEntry::putShortLE(&buf[0x22], mDiskNumberStart); + ZipEntry::putShortLE(&buf[0x24], mInternalAttrs); + ZipEntry::putLongLE(&buf[0x26], mExternalAttrs); + ZipEntry::putLongLE(&buf[0x2a], mLocalHeaderRelOffset); + + if (fwrite(buf, 1, kCDELen, fp) != kCDELen) + return UNKNOWN_ERROR; + + /* write filename */ + if (mFileNameLength != 0) { + if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength) + return UNKNOWN_ERROR; + } + + /* write "extra field" */ + if (mExtraFieldLength != 0) { + if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) + return UNKNOWN_ERROR; + } + + /* write comment */ + if (mFileCommentLength != 0) { + if (fwrite(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength) + return UNKNOWN_ERROR; + } + + return NO_ERROR; +} + +/* + * Dump the contents of a CentralDirEntry object. + */ +void ZipEntry::CentralDirEntry::dump(void) const +{ + LOGD(" CentralDirEntry contents:\n"); + LOGD(" versMadeBy=%u versToExt=%u gpBits=0x%04x compression=%u\n", + mVersionMadeBy, mVersionToExtract, mGPBitFlag, mCompressionMethod); + LOGD(" modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n", + mLastModFileTime, mLastModFileDate, mCRC32); + LOGD(" compressedSize=%lu uncompressedSize=%lu\n", + mCompressedSize, mUncompressedSize); + LOGD(" filenameLen=%u extraLen=%u commentLen=%u\n", + mFileNameLength, mExtraFieldLength, mFileCommentLength); + LOGD(" diskNumStart=%u intAttr=0x%04x extAttr=0x%08lx relOffset=%lu\n", + mDiskNumberStart, mInternalAttrs, mExternalAttrs, + mLocalHeaderRelOffset); + + if (mFileName != NULL) + LOGD(" filename: '%s'\n", mFileName); + if (mFileComment != NULL) + LOGD(" comment: '%s'\n", mFileComment); +} + diff --git a/libs/utils/ZipFile.cpp b/libs/utils/ZipFile.cpp new file mode 100644 index 000000000..89aa874b4 --- /dev/null +++ b/libs/utils/ZipFile.cpp @@ -0,0 +1,1296 @@ +/* + * 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. + */ + +// +// Access to Zip archives. +// + +#define LOG_TAG "zip" + +#include "utils/ZipFile.h" +#include "utils/ZipUtils.h" +#include "utils/Log.h" + +#include +#define DEF_MEM_LEVEL 8 // normally in zutil.h? + +#include +#include +#include +#include + +using namespace android; + +/* + * Some environments require the "b", some choke on it. + */ +#define FILE_OPEN_RO "rb" +#define FILE_OPEN_RW "r+b" +#define FILE_OPEN_RW_CREATE "w+b" + +/* should live somewhere else? */ +static status_t errnoToStatus(int err) +{ + if (err == ENOENT) + return NAME_NOT_FOUND; + else if (err == EACCES) + return PERMISSION_DENIED; + else + return UNKNOWN_ERROR; +} + +/* + * Open a file and parse its guts. + */ +status_t ZipFile::open(const char* zipFileName, int flags) +{ + bool newArchive = false; + + assert(mZipFp == NULL); // no reopen + + if ((flags & kOpenTruncate)) + flags |= kOpenCreate; // trunc implies create + + if ((flags & kOpenReadOnly) && (flags & kOpenReadWrite)) + return INVALID_OPERATION; // not both + if (!((flags & kOpenReadOnly) || (flags & kOpenReadWrite))) + return INVALID_OPERATION; // not neither + if ((flags & kOpenCreate) && !(flags & kOpenReadWrite)) + return INVALID_OPERATION; // create requires write + + if (flags & kOpenTruncate) { + newArchive = true; + } else { + newArchive = (access(zipFileName, F_OK) != 0); + if (!(flags & kOpenCreate) && newArchive) { + /* not creating, must already exist */ + LOGD("File %s does not exist", zipFileName); + return NAME_NOT_FOUND; + } + } + + /* open the file */ + const char* openflags; + if (flags & kOpenReadWrite) { + if (newArchive) + openflags = FILE_OPEN_RW_CREATE; + else + openflags = FILE_OPEN_RW; + } else { + openflags = FILE_OPEN_RO; + } + mZipFp = fopen(zipFileName, openflags); + if (mZipFp == NULL) { + int err = errno; + LOGD("fopen failed: %d\n", err); + return errnoToStatus(err); + } + + status_t result; + if (!newArchive) { + /* + * Load the central directory. If that fails, then this probably + * isn't a Zip archive. + */ + result = readCentralDir(); + } else { + /* + * Newly-created. The EndOfCentralDir constructor actually + * sets everything to be the way we want it (all zeroes). We + * set mNeedCDRewrite so that we create *something* if the + * caller doesn't add any files. (We could also just unlink + * the file if it's brand new and nothing was added, but that's + * probably doing more than we really should -- the user might + * have a need for empty zip files.) + */ + mNeedCDRewrite = true; + result = NO_ERROR; + } + + if (flags & kOpenReadOnly) + mReadOnly = true; + else + assert(!mReadOnly); + + return result; +} + +/* + * Return the Nth entry in the archive. + */ +ZipEntry* ZipFile::getEntryByIndex(int idx) const +{ + if (idx < 0 || idx >= (int) mEntries.size()) + return NULL; + + return mEntries[idx]; +} + +/* + * Find an entry by name. + */ +ZipEntry* ZipFile::getEntryByName(const char* fileName) const +{ + /* + * Do a stupid linear string-compare search. + * + * There are various ways to speed this up, especially since it's rare + * to intermingle changes to the archive with "get by name" calls. We + * don't want to sort the mEntries vector itself, however, because + * it's used to recreate the Central Directory. + * + * (Hash table works, parallel list of pointers in sorted order is good.) + */ + int idx; + + for (idx = mEntries.size()-1; idx >= 0; idx--) { + ZipEntry* pEntry = mEntries[idx]; + if (!pEntry->getDeleted() && + strcmp(fileName, pEntry->getFileName()) == 0) + { + return pEntry; + } + } + + return NULL; +} + +/* + * Empty the mEntries vector. + */ +void ZipFile::discardEntries(void) +{ + int count = mEntries.size(); + + while (--count >= 0) + delete mEntries[count]; + + mEntries.clear(); +} + + +/* + * Find the central directory and read the contents. + * + * The fun thing about ZIP archives is that they may or may not be + * readable from start to end. In some cases, notably for archives + * that were written to stdout, the only length information is in the + * central directory at the end of the file. + * + * Of course, the central directory can be followed by a variable-length + * comment field, so we have to scan through it backwards. The comment + * is at most 64K, plus we have 18 bytes for the end-of-central-dir stuff + * itself, plus apparently sometimes people throw random junk on the end + * just for the fun of it. + * + * This is all a little wobbly. If the wrong value ends up in the EOCD + * area, we're hosed. This appears to be the way that everbody handles + * it though, so we're in pretty good company if this fails. + */ +status_t ZipFile::readCentralDir(void) +{ + status_t result = NO_ERROR; + unsigned char* buf = NULL; + off_t fileLength, seekStart; + long readAmount; + int i; + + fseek(mZipFp, 0, SEEK_END); + fileLength = ftell(mZipFp); + rewind(mZipFp); + + /* too small to be a ZIP archive? */ + if (fileLength < EndOfCentralDir::kEOCDLen) { + LOGD("Length is %ld -- too small\n", (long)fileLength); + result = INVALID_OPERATION; + goto bail; + } + + buf = new unsigned char[EndOfCentralDir::kMaxEOCDSearch]; + if (buf == NULL) { + LOGD("Failure allocating %d bytes for EOCD search", + EndOfCentralDir::kMaxEOCDSearch); + result = NO_MEMORY; + goto bail; + } + + if (fileLength > EndOfCentralDir::kMaxEOCDSearch) { + seekStart = fileLength - EndOfCentralDir::kMaxEOCDSearch; + readAmount = EndOfCentralDir::kMaxEOCDSearch; + } else { + seekStart = 0; + readAmount = (long) fileLength; + } + if (fseek(mZipFp, seekStart, SEEK_SET) != 0) { + LOGD("Failure seeking to end of zip at %ld", (long) seekStart); + result = UNKNOWN_ERROR; + goto bail; + } + + /* read the last part of the file into the buffer */ + if (fread(buf, 1, readAmount, mZipFp) != (size_t) readAmount) { + LOGD("short file? wanted %ld\n", readAmount); + result = UNKNOWN_ERROR; + goto bail; + } + + /* find the end-of-central-dir magic */ + for (i = readAmount - 4; i >= 0; i--) { + if (buf[i] == 0x50 && + ZipEntry::getLongLE(&buf[i]) == EndOfCentralDir::kSignature) + { + LOGV("+++ Found EOCD at buf+%d\n", i); + break; + } + } + if (i < 0) { + LOGD("EOCD not found, not Zip\n"); + result = INVALID_OPERATION; + goto bail; + } + + /* extract eocd values */ + result = mEOCD.readBuf(buf + i, readAmount - i); + if (result != NO_ERROR) { + LOGD("Failure reading %ld bytes of EOCD values", readAmount - i); + goto bail; + } + //mEOCD.dump(); + + if (mEOCD.mDiskNumber != 0 || mEOCD.mDiskWithCentralDir != 0 || + mEOCD.mNumEntries != mEOCD.mTotalNumEntries) + { + LOGD("Archive spanning not supported\n"); + result = INVALID_OPERATION; + goto bail; + } + + /* + * So far so good. "mCentralDirSize" is the size in bytes of the + * central directory, so we can just seek back that far to find it. + * We can also seek forward mCentralDirOffset bytes from the + * start of the file. + * + * We're not guaranteed to have the rest of the central dir in the + * buffer, nor are we guaranteed that the central dir will have any + * sort of convenient size. We need to skip to the start of it and + * read the header, then the other goodies. + * + * The only thing we really need right now is the file comment, which + * we're hoping to preserve. + */ + if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) { + LOGD("Failure seeking to central dir offset %ld\n", + mEOCD.mCentralDirOffset); + result = UNKNOWN_ERROR; + goto bail; + } + + /* + * Loop through and read the central dir entries. + */ + LOGV("Scanning %d entries...\n", mEOCD.mTotalNumEntries); + int entry; + for (entry = 0; entry < mEOCD.mTotalNumEntries; entry++) { + ZipEntry* pEntry = new ZipEntry; + + result = pEntry->initFromCDE(mZipFp); + if (result != NO_ERROR) { + LOGD("initFromCDE failed\n"); + delete pEntry; + goto bail; + } + + mEntries.add(pEntry); + } + + + /* + * If all went well, we should now be back at the EOCD. + */ + { + unsigned char checkBuf[4]; + if (fread(checkBuf, 1, 4, mZipFp) != 4) { + LOGD("EOCD check read failed\n"); + result = INVALID_OPERATION; + goto bail; + } + if (ZipEntry::getLongLE(checkBuf) != EndOfCentralDir::kSignature) { + LOGD("EOCD read check failed\n"); + result = UNKNOWN_ERROR; + goto bail; + } + LOGV("+++ EOCD read check passed\n"); + } + +bail: + delete[] buf; + return result; +} + + +/* + * Add a new file to the archive. + * + * This requires creating and populating a ZipEntry structure, and copying + * the data into the file at the appropriate position. The "appropriate + * position" is the current location of the central directory, which we + * casually overwrite (we can put it back later). + * + * If we were concerned about safety, we would want to make all changes + * in a temp file and then overwrite the original after everything was + * safely written. Not really a concern for us. + */ +status_t ZipFile::addCommon(const char* fileName, const void* data, size_t size, + const char* storageName, int sourceType, int compressionMethod, + ZipEntry** ppEntry) +{ + ZipEntry* pEntry = NULL; + status_t result = NO_ERROR; + long lfhPosn, startPosn, endPosn, uncompressedLen; + FILE* inputFp = NULL; + unsigned long crc; + time_t modWhen; + + if (mReadOnly) + return INVALID_OPERATION; + + assert(compressionMethod == ZipEntry::kCompressDeflated || + compressionMethod == ZipEntry::kCompressStored); + + /* make sure we're in a reasonable state */ + assert(mZipFp != NULL); + assert(mEntries.size() == mEOCD.mTotalNumEntries); + + /* make sure it doesn't already exist */ + if (getEntryByName(storageName) != NULL) + return ALREADY_EXISTS; + + if (!data) { + inputFp = fopen(fileName, FILE_OPEN_RO); + if (inputFp == NULL) + return errnoToStatus(errno); + } + + if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) { + result = UNKNOWN_ERROR; + goto bail; + } + + pEntry = new ZipEntry; + pEntry->initNew(storageName, NULL); + + /* + * From here on out, failures are more interesting. + */ + mNeedCDRewrite = true; + + /* + * Write the LFH, even though it's still mostly blank. We need it + * as a place-holder. In theory the LFH isn't necessary, but in + * practice some utilities demand it. + */ + lfhPosn = ftell(mZipFp); + pEntry->mLFH.write(mZipFp); + startPosn = ftell(mZipFp); + + /* + * Copy the data in, possibly compressing it as we go. + */ + if (sourceType == ZipEntry::kCompressStored) { + if (compressionMethod == ZipEntry::kCompressDeflated) { + bool failed = false; + result = compressFpToFp(mZipFp, inputFp, data, size, &crc); + if (result != NO_ERROR) { + LOGD("compression failed, storing\n"); + failed = true; + } else { + /* + * Make sure it has compressed "enough". This probably ought + * to be set through an API call, but I don't expect our + * criteria to change over time. + */ + long src = inputFp ? ftell(inputFp) : size; + long dst = ftell(mZipFp) - startPosn; + if (dst + (dst / 10) > src) { + LOGD("insufficient compression (src=%ld dst=%ld), storing\n", + src, dst); + failed = true; + } + } + + if (failed) { + compressionMethod = ZipEntry::kCompressStored; + if (inputFp) rewind(inputFp); + fseek(mZipFp, startPosn, SEEK_SET); + /* fall through to kCompressStored case */ + } + } + /* handle "no compression" request, or failed compression from above */ + if (compressionMethod == ZipEntry::kCompressStored) { + if (inputFp) { + result = copyFpToFp(mZipFp, inputFp, &crc); + } else { + result = copyDataToFp(mZipFp, data, size, &crc); + } + if (result != NO_ERROR) { + // don't need to truncate; happens in CDE rewrite + LOGD("failed copying data in\n"); + goto bail; + } + } + + // currently seeked to end of file + uncompressedLen = inputFp ? ftell(inputFp) : size; + } else if (sourceType == ZipEntry::kCompressDeflated) { + /* we should support uncompressed-from-compressed, but it's not + * important right now */ + assert(compressionMethod == ZipEntry::kCompressDeflated); + + bool scanResult; + int method; + long compressedLen; + + scanResult = ZipUtils::examineGzip(inputFp, &method, &uncompressedLen, + &compressedLen, &crc); + if (!scanResult || method != ZipEntry::kCompressDeflated) { + LOGD("this isn't a deflated gzip file?"); + result = UNKNOWN_ERROR; + goto bail; + } + + result = copyPartialFpToFp(mZipFp, inputFp, compressedLen, NULL); + if (result != NO_ERROR) { + LOGD("failed copying gzip data in\n"); + goto bail; + } + } else { + assert(false); + result = UNKNOWN_ERROR; + goto bail; + } + + /* + * We could write the "Data Descriptor", but there doesn't seem to + * be any point since we're going to go back and write the LFH. + * + * Update file offsets. + */ + endPosn = ftell(mZipFp); // seeked to end of compressed data + + /* + * Success! Fill out new values. + */ + pEntry->setDataInfo(uncompressedLen, endPosn - startPosn, crc, + compressionMethod); + modWhen = getModTime(inputFp ? fileno(inputFp) : fileno(mZipFp)); + pEntry->setModWhen(modWhen); + pEntry->setLFHOffset(lfhPosn); + mEOCD.mNumEntries++; + mEOCD.mTotalNumEntries++; + mEOCD.mCentralDirSize = 0; // mark invalid; set by flush() + mEOCD.mCentralDirOffset = endPosn; + + /* + * Go back and write the LFH. + */ + if (fseek(mZipFp, lfhPosn, SEEK_SET) != 0) { + result = UNKNOWN_ERROR; + goto bail; + } + pEntry->mLFH.write(mZipFp); + + /* + * Add pEntry to the list. + */ + mEntries.add(pEntry); + if (ppEntry != NULL) + *ppEntry = pEntry; + pEntry = NULL; + +bail: + if (inputFp != NULL) + fclose(inputFp); + delete pEntry; + return result; +} + +/* + * Add an entry by copying it from another zip file. If "padding" is + * nonzero, the specified number of bytes will be added to the "extra" + * field in the header. + * + * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. + */ +status_t ZipFile::add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry, + int padding, ZipEntry** ppEntry) +{ + ZipEntry* pEntry = NULL; + status_t result; + long lfhPosn, endPosn; + + if (mReadOnly) + return INVALID_OPERATION; + + /* make sure we're in a reasonable state */ + assert(mZipFp != NULL); + assert(mEntries.size() == mEOCD.mTotalNumEntries); + + if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) { + result = UNKNOWN_ERROR; + goto bail; + } + + pEntry = new ZipEntry; + if (pEntry == NULL) { + result = NO_MEMORY; + goto bail; + } + + result = pEntry->initFromExternal(pSourceZip, pSourceEntry); + if (result != NO_ERROR) + goto bail; + if (padding != 0) { + result = pEntry->addPadding(padding); + if (result != NO_ERROR) + goto bail; + } + + /* + * From here on out, failures are more interesting. + */ + mNeedCDRewrite = true; + + /* + * Write the LFH. Since we're not recompressing the data, we already + * have all of the fields filled out. + */ + lfhPosn = ftell(mZipFp); + pEntry->mLFH.write(mZipFp); + + /* + * Copy the data over. + * + * If the "has data descriptor" flag is set, we want to copy the DD + * fields as well. This is a fixed-size area immediately following + * the data. + */ + if (fseek(pSourceZip->mZipFp, pSourceEntry->getFileOffset(), SEEK_SET) != 0) + { + result = UNKNOWN_ERROR; + goto bail; + } + + off_t copyLen; + copyLen = pSourceEntry->getCompressedLen(); + if ((pSourceEntry->mLFH.mGPBitFlag & ZipEntry::kUsesDataDescr) != 0) + copyLen += ZipEntry::kDataDescriptorLen; + + if (copyPartialFpToFp(mZipFp, pSourceZip->mZipFp, copyLen, NULL) + != NO_ERROR) + { + LOGW("copy of '%s' failed\n", pEntry->mCDE.mFileName); + result = UNKNOWN_ERROR; + goto bail; + } + + /* + * Update file offsets. + */ + endPosn = ftell(mZipFp); + + /* + * Success! Fill out new values. + */ + pEntry->setLFHOffset(lfhPosn); // sets mCDE.mLocalHeaderRelOffset + mEOCD.mNumEntries++; + mEOCD.mTotalNumEntries++; + mEOCD.mCentralDirSize = 0; // mark invalid; set by flush() + mEOCD.mCentralDirOffset = endPosn; + + /* + * Add pEntry to the list. + */ + mEntries.add(pEntry); + if (ppEntry != NULL) + *ppEntry = pEntry; + pEntry = NULL; + + result = NO_ERROR; + +bail: + delete pEntry; + return result; +} + +/* + * Copy all of the bytes in "src" to "dst". + * + * On exit, "srcFp" will be seeked to the end of the file, and "dstFp" + * will be seeked immediately past the data. + */ +status_t ZipFile::copyFpToFp(FILE* dstFp, FILE* srcFp, unsigned long* pCRC32) +{ + unsigned char tmpBuf[32768]; + size_t count; + + *pCRC32 = crc32(0L, Z_NULL, 0); + + while (1) { + count = fread(tmpBuf, 1, sizeof(tmpBuf), srcFp); + if (ferror(srcFp) || ferror(dstFp)) + return errnoToStatus(errno); + if (count == 0) + break; + + *pCRC32 = crc32(*pCRC32, tmpBuf, count); + + if (fwrite(tmpBuf, 1, count, dstFp) != count) { + LOGD("fwrite %d bytes failed\n", (int) count); + return UNKNOWN_ERROR; + } + } + + return NO_ERROR; +} + +/* + * Copy all of the bytes in "src" to "dst". + * + * On exit, "dstFp" will be seeked immediately past the data. + */ +status_t ZipFile::copyDataToFp(FILE* dstFp, + const void* data, size_t size, unsigned long* pCRC32) +{ + size_t count; + + *pCRC32 = crc32(0L, Z_NULL, 0); + if (size > 0) { + *pCRC32 = crc32(*pCRC32, (const unsigned char*)data, size); + if (fwrite(data, 1, size, dstFp) != size) { + LOGD("fwrite %d bytes failed\n", (int) size); + return UNKNOWN_ERROR; + } + } + + return NO_ERROR; +} + +/* + * Copy some of the bytes in "src" to "dst". + * + * If "pCRC32" is NULL, the CRC will not be computed. + * + * On exit, "srcFp" will be seeked to the end of the file, and "dstFp" + * will be seeked immediately past the data just written. + */ +status_t ZipFile::copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length, + unsigned long* pCRC32) +{ + unsigned char tmpBuf[32768]; + size_t count; + + if (pCRC32 != NULL) + *pCRC32 = crc32(0L, Z_NULL, 0); + + while (length) { + long readSize; + + readSize = sizeof(tmpBuf); + if (readSize > length) + readSize = length; + + count = fread(tmpBuf, 1, readSize, srcFp); + if ((long) count != readSize) { // error or unexpected EOF + LOGD("fread %d bytes failed\n", (int) readSize); + return UNKNOWN_ERROR; + } + + if (pCRC32 != NULL) + *pCRC32 = crc32(*pCRC32, tmpBuf, count); + + if (fwrite(tmpBuf, 1, count, dstFp) != count) { + LOGD("fwrite %d bytes failed\n", (int) count); + return UNKNOWN_ERROR; + } + + length -= readSize; + } + + return NO_ERROR; +} + +/* + * Compress all of the data in "srcFp" and write it to "dstFp". + * + * On exit, "srcFp" will be seeked to the end of the file, and "dstFp" + * will be seeked immediately past the compressed data. + */ +status_t ZipFile::compressFpToFp(FILE* dstFp, FILE* srcFp, + const void* data, size_t size, unsigned long* pCRC32) +{ + status_t result = NO_ERROR; + const size_t kBufSize = 32768; + unsigned char* inBuf = NULL; + unsigned char* outBuf = NULL; + z_stream zstream; + bool atEof = false; // no feof() aviailable yet + unsigned long crc; + int zerr; + + /* + * Create an input buffer and an output buffer. + */ + inBuf = new unsigned char[kBufSize]; + outBuf = new unsigned char[kBufSize]; + if (inBuf == NULL || outBuf == NULL) { + result = NO_MEMORY; + goto bail; + } + + /* + * Initialize the zlib stream. + */ + memset(&zstream, 0, sizeof(zstream)); + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + zstream.next_in = NULL; + zstream.avail_in = 0; + zstream.next_out = outBuf; + zstream.avail_out = kBufSize; + zstream.data_type = Z_UNKNOWN; + + zerr = deflateInit2(&zstream, Z_BEST_COMPRESSION, + Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY); + if (zerr != Z_OK) { + result = UNKNOWN_ERROR; + if (zerr == Z_VERSION_ERROR) { + LOGE("Installed zlib is not compatible with linked version (%s)\n", + ZLIB_VERSION); + } else { + LOGD("Call to deflateInit2 failed (zerr=%d)\n", zerr); + } + goto bail; + } + + crc = crc32(0L, Z_NULL, 0); + + /* + * Loop while we have data. + */ + do { + size_t getSize; + int flush; + + /* only read if the input buffer is empty */ + if (zstream.avail_in == 0 && !atEof) { + LOGV("+++ reading %d bytes\n", (int)kBufSize); + if (data) { + getSize = size > kBufSize ? kBufSize : size; + memcpy(inBuf, data, getSize); + data = ((const char*)data) + getSize; + size -= getSize; + } else { + getSize = fread(inBuf, 1, kBufSize, srcFp); + if (ferror(srcFp)) { + LOGD("deflate read failed (errno=%d)\n", errno); + goto z_bail; + } + } + if (getSize < kBufSize) { + LOGV("+++ got %d bytes, EOF reached\n", + (int)getSize); + atEof = true; + } + + crc = crc32(crc, inBuf, getSize); + + zstream.next_in = inBuf; + zstream.avail_in = getSize; + } + + if (atEof) + flush = Z_FINISH; /* tell zlib that we're done */ + else + flush = Z_NO_FLUSH; /* more to come! */ + + zerr = deflate(&zstream, flush); + if (zerr != Z_OK && zerr != Z_STREAM_END) { + LOGD("zlib deflate call failed (zerr=%d)\n", zerr); + result = UNKNOWN_ERROR; + goto z_bail; + } + + /* write when we're full or when we're done */ + if (zstream.avail_out == 0 || + (zerr == Z_STREAM_END && zstream.avail_out != (uInt) kBufSize)) + { + LOGV("+++ writing %d bytes\n", (int) (zstream.next_out - outBuf)); + if (fwrite(outBuf, 1, zstream.next_out - outBuf, dstFp) != + (size_t)(zstream.next_out - outBuf)) + { + LOGD("write %d failed in deflate\n", + (int) (zstream.next_out - outBuf)); + goto z_bail; + } + + zstream.next_out = outBuf; + zstream.avail_out = kBufSize; + } + } while (zerr == Z_OK); + + assert(zerr == Z_STREAM_END); /* other errors should've been caught */ + + *pCRC32 = crc; + +z_bail: + deflateEnd(&zstream); /* free up any allocated structures */ + +bail: + delete[] inBuf; + delete[] outBuf; + + return result; +} + +/* + * Mark an entry as deleted. + * + * We will eventually need to crunch the file down, but if several files + * are being removed (perhaps as part of an "update" process) we can make + * things considerably faster by deferring the removal to "flush" time. + */ +status_t ZipFile::remove(ZipEntry* pEntry) +{ + /* + * Should verify that pEntry is actually part of this archive, and + * not some stray ZipEntry from a different file. + */ + + /* mark entry as deleted, and mark archive as dirty */ + pEntry->setDeleted(); + mNeedCDRewrite = true; + return NO_ERROR; +} + +/* + * Flush any pending writes. + * + * In particular, this will crunch out deleted entries, and write the + * Central Directory and EOCD if we have stomped on them. + */ +status_t ZipFile::flush(void) +{ + status_t result = NO_ERROR; + long eocdPosn; + int i, count; + + if (mReadOnly) + return INVALID_OPERATION; + if (!mNeedCDRewrite) + return NO_ERROR; + + assert(mZipFp != NULL); + + result = crunchArchive(); + if (result != NO_ERROR) + return result; + + if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) + return UNKNOWN_ERROR; + + count = mEntries.size(); + for (i = 0; i < count; i++) { + ZipEntry* pEntry = mEntries[i]; + pEntry->mCDE.write(mZipFp); + } + + eocdPosn = ftell(mZipFp); + mEOCD.mCentralDirSize = eocdPosn - mEOCD.mCentralDirOffset; + + mEOCD.write(mZipFp); + + /* + * If we had some stuff bloat up during compression and get replaced + * with plain files, or if we deleted some entries, there's a lot + * of wasted space at the end of the file. Remove it now. + */ + if (ftruncate(fileno(mZipFp), ftell(mZipFp)) != 0) { + LOGW("ftruncate failed %ld: %s\n", ftell(mZipFp), strerror(errno)); + // not fatal + } + + /* should we clear the "newly added" flag in all entries now? */ + + mNeedCDRewrite = false; + return NO_ERROR; +} + +/* + * Crunch deleted files out of an archive by shifting the later files down. + * + * Because we're not using a temp file, we do the operation inside the + * current file. + */ +status_t ZipFile::crunchArchive(void) +{ + status_t result = NO_ERROR; + int i, count; + long delCount, adjust; + +#if 0 + printf("CONTENTS:\n"); + for (i = 0; i < (int) mEntries.size(); i++) { + printf(" %d: lfhOff=%ld del=%d\n", + i, mEntries[i]->getLFHOffset(), mEntries[i]->getDeleted()); + } + printf(" END is %ld\n", (long) mEOCD.mCentralDirOffset); +#endif + + /* + * Roll through the set of files, shifting them as appropriate. We + * could probably get a slight performance improvement by sliding + * multiple files down at once (because we could use larger reads + * when operating on batches of small files), but it's not that useful. + */ + count = mEntries.size(); + delCount = adjust = 0; + for (i = 0; i < count; i++) { + ZipEntry* pEntry = mEntries[i]; + long span; + + if (pEntry->getLFHOffset() != 0) { + long nextOffset; + + /* Get the length of this entry by finding the offset + * of the next entry. Directory entries don't have + * file offsets, so we need to find the next non-directory + * entry. + */ + nextOffset = 0; + for (int ii = i+1; nextOffset == 0 && ii < count; ii++) + nextOffset = mEntries[ii]->getLFHOffset(); + if (nextOffset == 0) + nextOffset = mEOCD.mCentralDirOffset; + span = nextOffset - pEntry->getLFHOffset(); + + assert(span >= ZipEntry::LocalFileHeader::kLFHLen); + } else { + /* This is a directory entry. It doesn't have + * any actual file contents, so there's no need to + * move anything. + */ + span = 0; + } + + //printf("+++ %d: off=%ld span=%ld del=%d [count=%d]\n", + // i, pEntry->getLFHOffset(), span, pEntry->getDeleted(), count); + + if (pEntry->getDeleted()) { + adjust += span; + delCount++; + + delete pEntry; + mEntries.removeAt(i); + + /* adjust loop control */ + count--; + i--; + } else if (span != 0 && adjust > 0) { + /* shuffle this entry back */ + //printf("+++ Shuffling '%s' back %ld\n", + // pEntry->getFileName(), adjust); + result = filemove(mZipFp, pEntry->getLFHOffset() - adjust, + pEntry->getLFHOffset(), span); + if (result != NO_ERROR) { + /* this is why you use a temp file */ + LOGE("error during crunch - archive is toast\n"); + return result; + } + + pEntry->setLFHOffset(pEntry->getLFHOffset() - adjust); + } + } + + /* + * Fix EOCD info. We have to wait until the end to do some of this + * because we use mCentralDirOffset to determine "span" for the + * last entry. + */ + mEOCD.mCentralDirOffset -= adjust; + mEOCD.mNumEntries -= delCount; + mEOCD.mTotalNumEntries -= delCount; + mEOCD.mCentralDirSize = 0; // mark invalid; set by flush() + + assert(mEOCD.mNumEntries == mEOCD.mTotalNumEntries); + assert(mEOCD.mNumEntries == count); + + return result; +} + +/* + * Works like memmove(), but on pieces of a file. + */ +status_t ZipFile::filemove(FILE* fp, off_t dst, off_t src, size_t n) +{ + if (dst == src || n <= 0) + return NO_ERROR; + + unsigned char readBuf[32768]; + + if (dst < src) { + /* shift stuff toward start of file; must read from start */ + while (n != 0) { + size_t getSize = sizeof(readBuf); + if (getSize > n) + getSize = n; + + if (fseek(fp, (long) src, SEEK_SET) != 0) { + LOGD("filemove src seek %ld failed\n", (long) src); + return UNKNOWN_ERROR; + } + + if (fread(readBuf, 1, getSize, fp) != getSize) { + LOGD("filemove read %ld off=%ld failed\n", + (long) getSize, (long) src); + return UNKNOWN_ERROR; + } + + if (fseek(fp, (long) dst, SEEK_SET) != 0) { + LOGD("filemove dst seek %ld failed\n", (long) dst); + return UNKNOWN_ERROR; + } + + if (fwrite(readBuf, 1, getSize, fp) != getSize) { + LOGD("filemove write %ld off=%ld failed\n", + (long) getSize, (long) dst); + return UNKNOWN_ERROR; + } + + src += getSize; + dst += getSize; + n -= getSize; + } + } else { + /* shift stuff toward end of file; must read from end */ + assert(false); // write this someday, maybe + return UNKNOWN_ERROR; + } + + return NO_ERROR; +} + + +/* + * Get the modification time from a file descriptor. + */ +time_t ZipFile::getModTime(int fd) +{ + struct stat sb; + + if (fstat(fd, &sb) < 0) { + LOGD("HEY: fstat on fd %d failed\n", fd); + return (time_t) -1; + } + + return sb.st_mtime; +} + + +#if 0 /* this is a bad idea */ +/* + * Get a copy of the Zip file descriptor. + * + * We don't allow this if the file was opened read-write because we tend + * to leave the file contents in an uncertain state between calls to + * flush(). The duplicated file descriptor should only be valid for reads. + */ +int ZipFile::getZipFd(void) const +{ + if (!mReadOnly) + return INVALID_OPERATION; + assert(mZipFp != NULL); + + int fd; + fd = dup(fileno(mZipFp)); + if (fd < 0) { + LOGD("didn't work, errno=%d\n", errno); + } + + return fd; +} +#endif + + +#if 0 +/* + * Expand data. + */ +bool ZipFile::uncompress(const ZipEntry* pEntry, void* buf) const +{ + return false; +} +#endif + +// free the memory when you're done +void* ZipFile::uncompress(const ZipEntry* entry) +{ + size_t unlen = entry->getUncompressedLen(); + size_t clen = entry->getCompressedLen(); + + void* buf = malloc(unlen); + if (buf == NULL) { + return NULL; + } + + fseek(mZipFp, 0, SEEK_SET); + + off_t offset = entry->getFileOffset(); + if (fseek(mZipFp, offset, SEEK_SET) != 0) { + goto bail; + } + + switch (entry->getCompressionMethod()) + { + case ZipEntry::kCompressStored: { + ssize_t amt = fread(buf, 1, unlen, mZipFp); + if (amt != (ssize_t)unlen) { + goto bail; + } +#if 0 + printf("data...\n"); + const unsigned char* p = (unsigned char*)buf; + const unsigned char* end = p+unlen; + for (int i=0; i<32 && p < end; i++) { + printf("0x%08x ", (int)(offset+(i*0x10))); + for (int j=0; j<0x10 && p < end; j++) { + printf(" %02x", *p); + p++; + } + printf("\n"); + } +#endif + + } + break; + case ZipEntry::kCompressDeflated: { + if (!ZipUtils::inflateToBuffer(mZipFp, buf, unlen, clen)) { + goto bail; + } + } + break; + default: + goto bail; + } + return buf; + +bail: + free(buf); + return NULL; +} + + +/* + * =========================================================================== + * ZipFile::EndOfCentralDir + * =========================================================================== + */ + +/* + * Read the end-of-central-dir fields. + * + * "buf" should be positioned at the EOCD signature, and should contain + * the entire EOCD area including the comment. + */ +status_t ZipFile::EndOfCentralDir::readBuf(const unsigned char* buf, int len) +{ + /* don't allow re-use */ + assert(mComment == NULL); + + if (len < kEOCDLen) { + /* looks like ZIP file got truncated */ + LOGD(" Zip EOCD: expected >= %d bytes, found %d\n", + kEOCDLen, len); + return INVALID_OPERATION; + } + + /* this should probably be an assert() */ + if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) + return UNKNOWN_ERROR; + + mDiskNumber = ZipEntry::getShortLE(&buf[0x04]); + mDiskWithCentralDir = ZipEntry::getShortLE(&buf[0x06]); + mNumEntries = ZipEntry::getShortLE(&buf[0x08]); + mTotalNumEntries = ZipEntry::getShortLE(&buf[0x0a]); + mCentralDirSize = ZipEntry::getLongLE(&buf[0x0c]); + mCentralDirOffset = ZipEntry::getLongLE(&buf[0x10]); + mCommentLen = ZipEntry::getShortLE(&buf[0x14]); + + // TODO: validate mCentralDirOffset + + if (mCommentLen > 0) { + if (kEOCDLen + mCommentLen > len) { + LOGD("EOCD(%d) + comment(%d) exceeds len (%d)\n", + kEOCDLen, mCommentLen, len); + return UNKNOWN_ERROR; + } + mComment = new unsigned char[mCommentLen]; + memcpy(mComment, buf + kEOCDLen, mCommentLen); + } + + return NO_ERROR; +} + +/* + * Write an end-of-central-directory section. + */ +status_t ZipFile::EndOfCentralDir::write(FILE* fp) +{ + unsigned char buf[kEOCDLen]; + + ZipEntry::putLongLE(&buf[0x00], kSignature); + ZipEntry::putShortLE(&buf[0x04], mDiskNumber); + ZipEntry::putShortLE(&buf[0x06], mDiskWithCentralDir); + ZipEntry::putShortLE(&buf[0x08], mNumEntries); + ZipEntry::putShortLE(&buf[0x0a], mTotalNumEntries); + ZipEntry::putLongLE(&buf[0x0c], mCentralDirSize); + ZipEntry::putLongLE(&buf[0x10], mCentralDirOffset); + ZipEntry::putShortLE(&buf[0x14], mCommentLen); + + if (fwrite(buf, 1, kEOCDLen, fp) != kEOCDLen) + return UNKNOWN_ERROR; + if (mCommentLen > 0) { + assert(mComment != NULL); + if (fwrite(mComment, mCommentLen, 1, fp) != mCommentLen) + return UNKNOWN_ERROR; + } + + return NO_ERROR; +} + +/* + * Dump the contents of an EndOfCentralDir object. + */ +void ZipFile::EndOfCentralDir::dump(void) const +{ + LOGD(" EndOfCentralDir contents:\n"); + LOGD(" diskNum=%u diskWCD=%u numEnt=%u totalNumEnt=%u\n", + mDiskNumber, mDiskWithCentralDir, mNumEntries, mTotalNumEntries); + LOGD(" centDirSize=%lu centDirOff=%lu commentLen=%u\n", + mCentralDirSize, mCentralDirOffset, mCommentLen); +} + diff --git a/libs/utils/ZipFileCRO.cpp b/libs/utils/ZipFileCRO.cpp new file mode 100644 index 000000000..d312dafad --- /dev/null +++ b/libs/utils/ZipFileCRO.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "utils/ZipFileCRO.h" +#include "utils/ZipFileRO.h" + +using namespace android; + +ZipFileCRO ZipFileXRO_open(const char* path) { + ZipFileRO* zip = new ZipFileRO(); + if (zip->open(path) == NO_ERROR) { + return (ZipFileCRO)zip; + } + return NULL; +} + +void ZipFileCRO_destroy(ZipFileCRO zipToken) { + ZipFileRO* zip = (ZipFileRO*)zipToken; + delete zip; +} + +ZipEntryCRO ZipFileCRO_findEntryByName(ZipFileCRO zipToken, + const char* fileName) { + ZipFileRO* zip = (ZipFileRO*)zipToken; + return (ZipEntryCRO)zip->findEntryByName(fileName); +} + +bool ZipFileCRO_getEntryInfo(ZipFileCRO zipToken, ZipEntryRO entryToken, + int* pMethod, long* pUncompLen, + long* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) { + ZipFileRO* zip = (ZipFileRO*)zipToken; + ZipEntryRO entry = (ZipEntryRO)entryToken; + return zip->getEntryInfo(entry, pMethod, pUncompLen, pCompLen, pOffset, + pModWhen, pCrc32); +} + +bool ZipFileCRO_uncompressEntry(ZipFileCRO zipToken, ZipEntryRO entryToken, int fd) { + ZipFileRO* zip = (ZipFileRO*)zipToken; + ZipEntryRO entry = (ZipEntryRO)entryToken; + return zip->uncompressEntry(entry, fd); +} diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp new file mode 100644 index 000000000..ae8c71972 --- /dev/null +++ b/libs/utils/ZipFileRO.cpp @@ -0,0 +1,724 @@ +/* + * 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. + */ + +// +// Read-only access to Zip archives, with minimal heap allocation. +// +#define LOG_TAG "zipro" +//#define LOG_NDEBUG 0 +#include "utils/ZipFileRO.h" +#include "utils/Log.h" +#include "utils/misc.h" + +#include + +#include +#include +#include +#include + +using namespace android; + +/* + * Zip file constants. + */ +#define kEOCDSignature 0x06054b50 +#define kEOCDLen 22 +#define kEOCDNumEntries 8 // offset to #of entries in file +#define kEOCDFileOffset 16 // offset to central directory + +#define kMaxCommentLen 65535 // longest possible in ushort +#define kMaxEOCDSearch (kMaxCommentLen + kEOCDLen) + +#define kLFHSignature 0x04034b50 +#define kLFHLen 30 // excluding variable-len fields +#define kLFHNameLen 26 // offset to filename length +#define kLFHExtraLen 28 // offset to extra length + +#define kCDESignature 0x02014b50 +#define kCDELen 46 // excluding variable-len fields +#define kCDEMethod 10 // offset to compression method +#define kCDEModWhen 12 // offset to modification timestamp +#define kCDECRC 16 // offset to entry CRC +#define kCDECompLen 20 // offset to compressed length +#define kCDEUncompLen 24 // offset to uncompressed length +#define kCDENameLen 28 // offset to filename length +#define kCDEExtraLen 30 // offset to extra length +#define kCDECommentLen 32 // offset to comment length +#define kCDELocalOffset 42 // offset to local hdr + +/* + * The values we return for ZipEntryRO use 0 as an invalid value, so we + * want to adjust the hash table index by a fixed amount. Using a large + * value helps insure that people don't mix & match arguments, e.g. to + * findEntryByIndex(). + */ +#define kZipEntryAdj 10000 + +/* + * Convert a ZipEntryRO to a hash table index, verifying that it's in a + * valid range. + */ +int ZipFileRO::entryToIndex(const ZipEntryRO entry) const +{ + long ent = ((long) entry) - kZipEntryAdj; + if (ent < 0 || ent >= mHashTableSize || mHashTable[ent].name == NULL) { + LOGW("Invalid ZipEntryRO %p (%ld)\n", entry, ent); + return -1; + } + return ent; +} + + +/* + * Open the specified file read-only. We memory-map the entire thing and + * close the file before returning. + */ +status_t ZipFileRO::open(const char* zipFileName) +{ + int fd = -1; + off_t length; + + assert(mFileMap == NULL); + + /* + * Open and map the specified file. + */ + fd = ::open(zipFileName, O_RDONLY); + if (fd < 0) { + LOGW("Unable to open zip '%s': %s\n", zipFileName, strerror(errno)); + return NAME_NOT_FOUND; + } + + length = lseek(fd, 0, SEEK_END); + if (length < 0) { + close(fd); + return UNKNOWN_ERROR; + } + + mFileMap = new FileMap(); + if (mFileMap == NULL) { + close(fd); + return NO_MEMORY; + } + if (!mFileMap->create(zipFileName, fd, 0, length, true)) { + LOGW("Unable to map '%s': %s\n", zipFileName, strerror(errno)); + close(fd); + return UNKNOWN_ERROR; + } + + mFd = fd; + + /* + * Got it mapped, verify it and create data structures for fast access. + */ + if (!parseZipArchive()) { + mFileMap->release(); + mFileMap = NULL; + return UNKNOWN_ERROR; + } + + return OK; +} + +/* + * Parse the Zip archive, verifying its contents and initializing internal + * data structures. + */ +bool ZipFileRO::parseZipArchive(void) +{ +#define CHECK_OFFSET(_off) { \ + if ((unsigned int) (_off) >= maxOffset) { \ + LOGE("ERROR: bad offset %u (max %d): %s\n", \ + (unsigned int) (_off), maxOffset, #_off); \ + goto bail; \ + } \ + } + const unsigned char* basePtr = (const unsigned char*)mFileMap->getDataPtr(); + const unsigned char* ptr; + size_t length = mFileMap->getDataLength(); + bool result = false; + unsigned int i, numEntries, cdOffset; + unsigned int val; + + /* + * The first 4 bytes of the file will either be the local header + * signature for the first file (kLFHSignature) or, if the archive doesn't + * have any files in it, the end-of-central-directory signature + * (kEOCDSignature). + */ + val = get4LE(basePtr); + if (val == kEOCDSignature) { + LOGI("Found Zip archive, but it looks empty\n"); + goto bail; + } else if (val != kLFHSignature) { + LOGV("Not a Zip archive (found 0x%08x)\n", val); + goto bail; + } + + /* + * Find the EOCD. We'll find it immediately unless they have a file + * comment. + */ + ptr = basePtr + length - kEOCDLen; + + while (ptr >= basePtr) { + if (*ptr == (kEOCDSignature & 0xff) && get4LE(ptr) == kEOCDSignature) + break; + ptr--; + } + if (ptr < basePtr) { + LOGI("Could not find end-of-central-directory in Zip\n"); + goto bail; + } + + /* + * There are two interesting items in the EOCD block: the number of + * entries in the file, and the file offset of the start of the + * central directory. + * + * (There's actually a count of the #of entries in this file, and for + * all files which comprise a spanned archive, but for our purposes + * we're only interested in the current file. Besides, we expect the + * two to be equivalent for our stuff.) + */ + numEntries = get2LE(ptr + kEOCDNumEntries); + cdOffset = get4LE(ptr + kEOCDFileOffset); + + /* valid offsets are [0,EOCD] */ + unsigned int maxOffset; + maxOffset = (ptr - basePtr) +1; + + LOGV("+++ numEntries=%d cdOffset=%d\n", numEntries, cdOffset); + if (numEntries == 0 || cdOffset >= length) { + LOGW("Invalid entries=%d offset=%d (len=%zd)\n", + numEntries, cdOffset, length); + goto bail; + } + + /* + * Create hash table. We have a minimum 75% load factor, possibly as + * low as 50% after we round off to a power of 2. + */ + mNumEntries = numEntries; + mHashTableSize = roundUpPower2(1 + ((numEntries * 4) / 3)); + mHashTable = (HashEntry*) calloc(1, sizeof(HashEntry) * mHashTableSize); + + /* + * Walk through the central directory, adding entries to the hash + * table. + */ + ptr = basePtr + cdOffset; + for (i = 0; i < numEntries; i++) { + unsigned int fileNameLen, extraLen, commentLen, localHdrOffset; + const unsigned char* localHdr; + unsigned int hash; + + if (get4LE(ptr) != kCDESignature) { + LOGW("Missed a central dir sig (at %d)\n", i); + goto bail; + } + if (ptr + kCDELen > basePtr + length) { + LOGW("Ran off the end (at %d)\n", i); + goto bail; + } + + localHdrOffset = get4LE(ptr + kCDELocalOffset); + CHECK_OFFSET(localHdrOffset); + fileNameLen = get2LE(ptr + kCDENameLen); + extraLen = get2LE(ptr + kCDEExtraLen); + commentLen = get2LE(ptr + kCDECommentLen); + + //LOGV("+++ %d: localHdr=%d fnl=%d el=%d cl=%d\n", + // i, localHdrOffset, fileNameLen, extraLen, commentLen); + //LOGV(" '%.*s'\n", fileNameLen, ptr + kCDELen); + + /* add the CDE filename to the hash table */ + hash = computeHash((const char*)ptr + kCDELen, fileNameLen); + addToHash((const char*)ptr + kCDELen, fileNameLen, hash); + + localHdr = basePtr + localHdrOffset; + if (get4LE(localHdr) != kLFHSignature) { + LOGW("Bad offset to local header: %d (at %d)\n", + localHdrOffset, i); + goto bail; + } + + ptr += kCDELen + fileNameLen + extraLen + commentLen; + CHECK_OFFSET(ptr - basePtr); + } + + result = true; + +bail: + return result; +#undef CHECK_OFFSET +} + + +/* + * Simple string hash function for non-null-terminated strings. + */ +/*static*/ unsigned int ZipFileRO::computeHash(const char* str, int len) +{ + unsigned int hash = 0; + + while (len--) + hash = hash * 31 + *str++; + + return hash; +} + +/* + * Add a new entry to the hash table. + */ +void ZipFileRO::addToHash(const char* str, int strLen, unsigned int hash) +{ + int ent = hash & (mHashTableSize-1); + + /* + * We over-allocate the table, so we're guaranteed to find an empty slot. + */ + while (mHashTable[ent].name != NULL) + ent = (ent + 1) & (mHashTableSize-1); + + mHashTable[ent].name = str; + mHashTable[ent].nameLen = strLen; +} + +/* + * Find a matching entry. + * + * Returns 0 if not found. + */ +ZipEntryRO ZipFileRO::findEntryByName(const char* fileName) const +{ + int nameLen = strlen(fileName); + unsigned int hash = computeHash(fileName, nameLen); + int ent = hash & (mHashTableSize-1); + + while (mHashTable[ent].name != NULL) { + if (mHashTable[ent].nameLen == nameLen && + memcmp(mHashTable[ent].name, fileName, nameLen) == 0) + { + /* match */ + return (ZipEntryRO) (ent + kZipEntryAdj); + } + + ent = (ent + 1) & (mHashTableSize-1); + } + + return NULL; +} + +/* + * Find the Nth entry. + * + * This currently involves walking through the sparse hash table, counting + * non-empty entries. If we need to speed this up we can either allocate + * a parallel lookup table or (perhaps better) provide an iterator interface. + */ +ZipEntryRO ZipFileRO::findEntryByIndex(int idx) const +{ + if (idx < 0 || idx >= mNumEntries) { + LOGW("Invalid index %d\n", idx); + return NULL; + } + + for (int ent = 0; ent < mHashTableSize; ent++) { + if (mHashTable[ent].name != NULL) { + if (idx-- == 0) + return (ZipEntryRO) (ent + kZipEntryAdj); + } + } + + return NULL; +} + +/* + * Get the useful fields from the zip entry. + * + * Returns "false" if the offsets to the fields or the contents of the fields + * appear to be bogus. + */ +bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, long* pUncompLen, + long* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) const +{ + int ent = entryToIndex(entry); + if (ent < 0) + return false; + + /* + * Recover the start of the central directory entry from the filename + * pointer. + */ + const unsigned char* basePtr = (const unsigned char*)mFileMap->getDataPtr(); + const unsigned char* ptr = (const unsigned char*) mHashTable[ent].name; + size_t zipLength = mFileMap->getDataLength(); + + ptr -= kCDELen; + + int method = get2LE(ptr + kCDEMethod); + if (pMethod != NULL) + *pMethod = method; + + if (pModWhen != NULL) + *pModWhen = get4LE(ptr + kCDEModWhen); + if (pCrc32 != NULL) + *pCrc32 = get4LE(ptr + kCDECRC); + + /* + * We need to make sure that the lengths are not so large that somebody + * trying to map the compressed or uncompressed data runs off the end + * of the mapped region. + */ + unsigned long localHdrOffset = get4LE(ptr + kCDELocalOffset); + if (localHdrOffset + kLFHLen >= zipLength) { + LOGE("ERROR: bad local hdr offset in zip\n"); + return false; + } + const unsigned char* localHdr = basePtr + localHdrOffset; + off_t dataOffset = localHdrOffset + kLFHLen + + get2LE(localHdr + kLFHNameLen) + get2LE(localHdr + kLFHExtraLen); + if ((unsigned long) dataOffset >= zipLength) { + LOGE("ERROR: bad data offset in zip\n"); + return false; + } + + if (pCompLen != NULL) { + *pCompLen = get4LE(ptr + kCDECompLen); + if (*pCompLen < 0 || (size_t)(dataOffset + *pCompLen) >= zipLength) { + LOGE("ERROR: bad compressed length in zip\n"); + return false; + } + } + if (pUncompLen != NULL) { + *pUncompLen = get4LE(ptr + kCDEUncompLen); + if (*pUncompLen < 0) { + LOGE("ERROR: negative uncompressed length in zip\n"); + return false; + } + if (method == kCompressStored && + (size_t)(dataOffset + *pUncompLen) >= zipLength) + { + LOGE("ERROR: bad uncompressed length in zip\n"); + return false; + } + } + + if (pOffset != NULL) { + *pOffset = dataOffset; + } + return true; +} + +/* + * Copy the entry's filename to the buffer. + */ +int ZipFileRO::getEntryFileName(ZipEntryRO entry, char* buffer, int bufLen) + const +{ + int ent = entryToIndex(entry); + if (ent < 0) + return -1; + + int nameLen = mHashTable[ent].nameLen; + if (bufLen < nameLen+1) + return nameLen+1; + + memcpy(buffer, mHashTable[ent].name, nameLen); + buffer[nameLen] = '\0'; + return 0; +} + +/* + * Create a new FileMap object that spans the data in "entry". + */ +FileMap* ZipFileRO::createEntryFileMap(ZipEntryRO entry) const +{ + /* + * TODO: the efficient way to do this is to modify FileMap to allow + * sub-regions of a file to be mapped. A reference-counting scheme + * can manage the base memory mapping. For now, we just create a brand + * new mapping off of the Zip archive file descriptor. + */ + + FileMap* newMap; + long compLen; + off_t offset; + + if (!getEntryInfo(entry, NULL, NULL, &compLen, &offset, NULL, NULL)) + return NULL; + + newMap = new FileMap(); + if (!newMap->create(mFileMap->getFileName(), mFd, offset, compLen, true)) { + newMap->release(); + return NULL; + } + + return newMap; +} + +/* + * Uncompress an entry, in its entirety, into the provided output buffer. + * + * This doesn't verify the data's CRC, which might be useful for + * uncompressed data. The caller should be able to manage it. + */ +bool ZipFileRO::uncompressEntry(ZipEntryRO entry, void* buffer) const +{ + const int kSequentialMin = 32768; + bool result = false; + int ent = entryToIndex(entry); + if (ent < 0) + return -1; + + const unsigned char* basePtr = (const unsigned char*)mFileMap->getDataPtr(); + int method; + long uncompLen, compLen; + off_t offset; + + getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL); + + /* + * Experiment with madvise hint. When we want to uncompress a file, + * we pull some stuff out of the central dir entry and then hit a + * bunch of compressed or uncompressed data sequentially. The CDE + * visit will cause a limited amount of read-ahead because it's at + * the end of the file. We could end up doing lots of extra disk + * access if the file we're prying open is small. Bottom line is we + * probably don't want to turn MADV_SEQUENTIAL on and leave it on. + * + * So, if the compressed size of the file is above a certain minimum + * size, temporarily boost the read-ahead in the hope that the extra + * pair of system calls are negated by a reduction in page faults. + */ + if (compLen > kSequentialMin) + mFileMap->advise(FileMap::SEQUENTIAL); + + if (method == kCompressStored) { + memcpy(buffer, basePtr + offset, uncompLen); + } else { + if (!inflateBuffer(buffer, basePtr + offset, uncompLen, compLen)) + goto bail; + } + + if (compLen > kSequentialMin) + mFileMap->advise(FileMap::NORMAL); + + result = true; + +bail: + return result; +} + +/* + * Uncompress an entry, in its entirety, to an open file descriptor. + * + * This doesn't verify the data's CRC, but probably should. + */ +bool ZipFileRO::uncompressEntry(ZipEntryRO entry, int fd) const +{ + bool result = false; + int ent = entryToIndex(entry); + if (ent < 0) + return -1; + + const unsigned char* basePtr = (const unsigned char*)mFileMap->getDataPtr(); + int method; + long uncompLen, compLen; + off_t offset; + + getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL); + + if (method == kCompressStored) { + ssize_t actual; + + actual = write(fd, basePtr + offset, uncompLen); + if (actual < 0) { + LOGE("Write failed: %s\n", strerror(errno)); + goto bail; + } else if (actual != uncompLen) { + LOGE("Partial write during uncompress (%d of %ld)\n", + (int)actual, uncompLen); + goto bail; + } else { + LOGI("+++ successful write\n"); + } + } else { + if (!inflateBuffer(fd, basePtr+offset, uncompLen, compLen)) + goto bail; + } + + result = true; + +bail: + return result; +} + +/* + * Uncompress "deflate" data from one buffer to another. + */ +/*static*/ bool ZipFileRO::inflateBuffer(void* outBuf, const void* inBuf, + long uncompLen, long compLen) +{ + bool result = false; + z_stream zstream; + int zerr; + + /* + * Initialize the zlib stream struct. + */ + memset(&zstream, 0, sizeof(zstream)); + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + zstream.next_in = (Bytef*)inBuf; + zstream.avail_in = compLen; + zstream.next_out = (Bytef*) outBuf; + zstream.avail_out = uncompLen; + zstream.data_type = Z_UNKNOWN; + + /* + * Use the undocumented "negative window bits" feature to tell zlib + * that there's no zlib header waiting for it. + */ + zerr = inflateInit2(&zstream, -MAX_WBITS); + if (zerr != Z_OK) { + if (zerr == Z_VERSION_ERROR) { + LOGE("Installed zlib is not compatible with linked version (%s)\n", + ZLIB_VERSION); + } else { + LOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); + } + goto bail; + } + + /* + * Expand data. + */ + zerr = inflate(&zstream, Z_FINISH); + if (zerr != Z_STREAM_END) { + LOGW("Zip inflate failed, zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)\n", + zerr, zstream.next_in, zstream.avail_in, + zstream.next_out, zstream.avail_out); + goto z_bail; + } + + /* paranoia */ + if ((long) zstream.total_out != uncompLen) { + LOGW("Size mismatch on inflated file (%ld vs %ld)\n", + zstream.total_out, uncompLen); + goto z_bail; + } + + result = true; + +z_bail: + inflateEnd(&zstream); /* free up any allocated structures */ + +bail: + return result; +} + +/* + * Uncompress "deflate" data from one buffer to an open file descriptor. + */ +/*static*/ bool ZipFileRO::inflateBuffer(int fd, const void* inBuf, + long uncompLen, long compLen) +{ + bool result = false; + const int kWriteBufSize = 32768; + unsigned char writeBuf[kWriteBufSize]; + z_stream zstream; + int zerr; + + /* + * Initialize the zlib stream struct. + */ + memset(&zstream, 0, sizeof(zstream)); + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + zstream.next_in = (Bytef*)inBuf; + zstream.avail_in = compLen; + zstream.next_out = (Bytef*) writeBuf; + zstream.avail_out = sizeof(writeBuf); + zstream.data_type = Z_UNKNOWN; + + /* + * Use the undocumented "negative window bits" feature to tell zlib + * that there's no zlib header waiting for it. + */ + zerr = inflateInit2(&zstream, -MAX_WBITS); + if (zerr != Z_OK) { + if (zerr == Z_VERSION_ERROR) { + LOGE("Installed zlib is not compatible with linked version (%s)\n", + ZLIB_VERSION); + } else { + LOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); + } + goto bail; + } + + /* + * Loop while we have more to do. + */ + do { + /* + * Expand data. + */ + zerr = inflate(&zstream, Z_NO_FLUSH); + if (zerr != Z_OK && zerr != Z_STREAM_END) { + LOGW("zlib inflate: zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)\n", + zerr, zstream.next_in, zstream.avail_in, + zstream.next_out, zstream.avail_out); + goto z_bail; + } + + /* write when we're full or when we're done */ + if (zstream.avail_out == 0 || + (zerr == Z_STREAM_END && zstream.avail_out != sizeof(writeBuf))) + { + long writeSize = zstream.next_out - writeBuf; + int cc = write(fd, writeBuf, writeSize); + if (cc != (int) writeSize) { + LOGW("write failed in inflate (%d vs %ld)\n", cc, writeSize); + goto z_bail; + } + + zstream.next_out = writeBuf; + zstream.avail_out = sizeof(writeBuf); + } + } while (zerr == Z_OK); + + assert(zerr == Z_STREAM_END); /* other errors should've been caught */ + + /* paranoia */ + if ((long) zstream.total_out != uncompLen) { + LOGW("Size mismatch on inflated file (%ld vs %ld)\n", + zstream.total_out, uncompLen); + goto z_bail; + } + + result = true; + +z_bail: + inflateEnd(&zstream); /* free up any allocated structures */ + +bail: + return result; +} diff --git a/libs/utils/ZipUtils.cpp b/libs/utils/ZipUtils.cpp new file mode 100644 index 000000000..bfbacfecd --- /dev/null +++ b/libs/utils/ZipUtils.cpp @@ -0,0 +1,344 @@ +/* + * 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. + */ + +// +// Misc zip/gzip utility functions. +// + +#define LOG_TAG "ziputil" + +#include "utils/ZipUtils.h" +#include "utils/ZipFileRO.h" +#include "utils/Log.h" + +#include +#include +#include + +#include + +using namespace android; + +/* + * Utility function that expands zip/gzip "deflate" compressed data + * into a buffer. + * + * "fd" is an open file positioned at the start of the "deflate" data + * "buf" must hold at least "uncompressedLen" bytes. + */ +/*static*/ bool ZipUtils::inflateToBuffer(int fd, void* buf, + long uncompressedLen, long compressedLen) +{ + bool result = false; + const unsigned long kReadBufSize = 32768; + unsigned char* readBuf = NULL; + z_stream zstream; + int zerr; + unsigned long compRemaining; + + assert(uncompressedLen >= 0); + assert(compressedLen >= 0); + + readBuf = new unsigned char[kReadBufSize]; + if (readBuf == NULL) + goto bail; + compRemaining = compressedLen; + + /* + * Initialize the zlib stream. + */ + memset(&zstream, 0, sizeof(zstream)); + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + zstream.next_in = NULL; + zstream.avail_in = 0; + zstream.next_out = (Bytef*) buf; + zstream.avail_out = uncompressedLen; + zstream.data_type = Z_UNKNOWN; + + /* + * Use the undocumented "negative window bits" feature to tell zlib + * that there's no zlib header waiting for it. + */ + zerr = inflateInit2(&zstream, -MAX_WBITS); + if (zerr != Z_OK) { + if (zerr == Z_VERSION_ERROR) { + LOGE("Installed zlib is not compatible with linked version (%s)\n", + ZLIB_VERSION); + } else { + LOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); + } + goto bail; + } + + /* + * Loop while we have data. + */ + do { + unsigned long getSize; + + /* read as much as we can */ + if (zstream.avail_in == 0) { + getSize = (compRemaining > kReadBufSize) ? + kReadBufSize : compRemaining; + LOGV("+++ reading %ld bytes (%ld left)\n", + getSize, compRemaining); + + int cc = read(fd, readBuf, getSize); + if (cc != (int) getSize) { + LOGD("inflate read failed (%d vs %ld)\n", + cc, getSize); + goto z_bail; + } + + compRemaining -= getSize; + + zstream.next_in = readBuf; + zstream.avail_in = getSize; + } + + /* uncompress the data */ + zerr = inflate(&zstream, Z_NO_FLUSH); + if (zerr != Z_OK && zerr != Z_STREAM_END) { + LOGD("zlib inflate call failed (zerr=%d)\n", zerr); + goto z_bail; + } + + /* output buffer holds all, so no need to write the output */ + } while (zerr == Z_OK); + + assert(zerr == Z_STREAM_END); /* other errors should've been caught */ + + if ((long) zstream.total_out != uncompressedLen) { + LOGW("Size mismatch on inflated file (%ld vs %ld)\n", + zstream.total_out, uncompressedLen); + goto z_bail; + } + + // success! + result = true; + +z_bail: + inflateEnd(&zstream); /* free up any allocated structures */ + +bail: + delete[] readBuf; + return result; +} + +/* + * Utility function that expands zip/gzip "deflate" compressed data + * into a buffer. + * + * (This is a clone of the previous function, but it takes a FILE* instead + * of an fd. We could pass fileno(fd) to the above, but we can run into + * trouble when "fp" has a different notion of what fd's file position is.) + * + * "fp" is an open file positioned at the start of the "deflate" data + * "buf" must hold at least "uncompressedLen" bytes. + */ +/*static*/ bool ZipUtils::inflateToBuffer(FILE* fp, void* buf, + long uncompressedLen, long compressedLen) +{ + bool result = false; + const unsigned long kReadBufSize = 32768; + unsigned char* readBuf = NULL; + z_stream zstream; + int zerr; + unsigned long compRemaining; + + assert(uncompressedLen >= 0); + assert(compressedLen >= 0); + + readBuf = new unsigned char[kReadBufSize]; + if (readBuf == NULL) + goto bail; + compRemaining = compressedLen; + + /* + * Initialize the zlib stream. + */ + memset(&zstream, 0, sizeof(zstream)); + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + zstream.next_in = NULL; + zstream.avail_in = 0; + zstream.next_out = (Bytef*) buf; + zstream.avail_out = uncompressedLen; + zstream.data_type = Z_UNKNOWN; + + /* + * Use the undocumented "negative window bits" feature to tell zlib + * that there's no zlib header waiting for it. + */ + zerr = inflateInit2(&zstream, -MAX_WBITS); + if (zerr != Z_OK) { + if (zerr == Z_VERSION_ERROR) { + LOGE("Installed zlib is not compatible with linked version (%s)\n", + ZLIB_VERSION); + } else { + LOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); + } + goto bail; + } + + /* + * Loop while we have data. + */ + do { + unsigned long getSize; + + /* read as much as we can */ + if (zstream.avail_in == 0) { + getSize = (compRemaining > kReadBufSize) ? + kReadBufSize : compRemaining; + LOGV("+++ reading %ld bytes (%ld left)\n", + getSize, compRemaining); + + int cc = fread(readBuf, getSize, 1, fp); + if (cc != (int) getSize) { + LOGD("inflate read failed (%d vs %ld)\n", + cc, getSize); + goto z_bail; + } + + compRemaining -= getSize; + + zstream.next_in = readBuf; + zstream.avail_in = getSize; + } + + /* uncompress the data */ + zerr = inflate(&zstream, Z_NO_FLUSH); + if (zerr != Z_OK && zerr != Z_STREAM_END) { + LOGD("zlib inflate call failed (zerr=%d)\n", zerr); + goto z_bail; + } + + /* output buffer holds all, so no need to write the output */ + } while (zerr == Z_OK); + + assert(zerr == Z_STREAM_END); /* other errors should've been caught */ + + if ((long) zstream.total_out != uncompressedLen) { + LOGW("Size mismatch on inflated file (%ld vs %ld)\n", + zstream.total_out, uncompressedLen); + goto z_bail; + } + + // success! + result = true; + +z_bail: + inflateEnd(&zstream); /* free up any allocated structures */ + +bail: + delete[] readBuf; + return result; +} + +/* + * Look at the contents of a gzip archive. We want to know where the + * data starts, and how long it will be after it is uncompressed. + * + * We expect to find the CRC and length as the last 8 bytes on the file. + * This is a pretty reasonable thing to expect for locally-compressed + * files, but there's a small chance that some extra padding got thrown + * on (the man page talks about compressed data written to tape). We + * don't currently deal with that here. If "gzip -l" whines, we're going + * to fail too. + * + * On exit, "fp" is pointing at the start of the compressed data. + */ +/*static*/ bool ZipUtils::examineGzip(FILE* fp, int* pCompressionMethod, + long* pUncompressedLen, long* pCompressedLen, unsigned long* pCRC32) +{ + enum { // flags + FTEXT = 0x01, + FHCRC = 0x02, + FEXTRA = 0x04, + FNAME = 0x08, + FCOMMENT = 0x10, + }; + int ic; + int method, flags; + int i; + + ic = getc(fp); + if (ic != 0x1f || getc(fp) != 0x8b) + return false; // not gzip + method = getc(fp); + flags = getc(fp); + + /* quick sanity checks */ + if (method == EOF || flags == EOF) + return false; + if (method != ZipFileRO::kCompressDeflated) + return false; + + /* skip over 4 bytes of mod time, 1 byte XFL, 1 byte OS */ + for (i = 0; i < 6; i++) + (void) getc(fp); + /* consume "extra" field, if present */ + if ((flags & FEXTRA) != 0) { + int len; + + len = getc(fp); + len |= getc(fp) << 8; + while (len-- && getc(fp) != EOF) + ; + } + /* consume filename, if present */ + if ((flags & FNAME) != 0) { + do { + ic = getc(fp); + } while (ic != 0 && ic != EOF); + } + /* consume comment, if present */ + if ((flags & FCOMMENT) != 0) { + do { + ic = getc(fp); + } while (ic != 0 && ic != EOF); + } + /* consume 16-bit header CRC, if present */ + if ((flags & FHCRC) != 0) { + (void) getc(fp); + (void) getc(fp); + } + + if (feof(fp) || ferror(fp)) + return false; + + /* seek to the end; CRC and length are in the last 8 bytes */ + long curPosn = ftell(fp); + unsigned char buf[8]; + fseek(fp, -8, SEEK_END); + *pCompressedLen = ftell(fp) - curPosn; + + if (fread(buf, 1, 8, fp) != 8) + return false; + /* seek back to start of compressed data */ + fseek(fp, curPosn, SEEK_SET); + + *pCompressionMethod = method; + *pCRC32 = ZipFileRO::get4LE(&buf[0]); + *pUncompressedLen = ZipFileRO::get4LE(&buf[4]); + + return true; +} + diff --git a/libs/utils/characterData.h b/libs/utils/characterData.h new file mode 100644 index 000000000..e931d995e --- /dev/null +++ b/libs/utils/characterData.h @@ -0,0 +1,730 @@ +/* + * 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. + */ + +// Automatically generated on 07-11-2006 by make-CharacterDataC +// DO NOT EDIT DIRECTLY +namespace CharacterData { + + // Structure containing an array of ranges + struct Range { + int length; + const uint32_t* array; + }; + + // For Latin1 characters just index into this array to get the index and decomposition + static const uint16_t LATIN1_DATA[] = { + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0002, 0x0003, 0x0002, 0x0004, 0x0003, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0003, 0x0003, 0x0003, 0x0002, + 0x0005, 0x0006, 0x0006, 0x0007, 0x0008, 0x0007, 0x0006, 0x0006, + 0x0009, 0x000A, 0x0006, 0x000B, 0x000C, 0x000D, 0x000C, 0x000C, + 0x000E, 0x000F, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, + 0x0016, 0x0017, 0x000C, 0x0006, 0x0018, 0x0019, 0x001A, 0x0006, + 0x0006, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, 0x0020, 0x0021, + 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, + 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, 0x0030, 0x0031, + 0x0032, 0x0033, 0x0034, 0x0035, 0x0006, 0x0036, 0x0037, 0x0038, + 0x0037, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0035, 0x0019, 0x0036, 0x0019, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0003, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x5853, 0x0006, 0x0008, 0x0008, 0x0008, 0x0008, 0x0054, 0x0054, + 0x1037, 0x0054, 0x7855, 0x0056, 0x0019, 0x0057, 0x0054, 0x1037, + 0x0058, 0x0059, 0x785A, 0x785B, 0x1037, 0x105C, 0x0054, 0x0006, + 0x1037, 0x785D, 0x7855, 0x005E, 0x305F, 0x305F, 0x305F, 0x0006, + 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0060, 0x0860, + 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, + 0x0060, 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0019, + 0x0060, 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0060, 0x0055, + 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0061, 0x0861, + 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, + 0x0061, 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0019, + 0x0061, 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0061, 0x0862 + }; + + // Each of these arrays is stripped into ranges. In order to build the arrays, each + // codepoint was bit-shifted so that even and odd characters were separated into different + // arrays. The identifier of each array is the top byte after bit-shifting. + // The numbers stored in the array are the bit-shifted codepoint, the decomposition, and an + // index into another array of all possible packed data values. The top 16 bits are the + // codepoint and the bottom 16 are the decomposition and index. The top 5 bits for the decomposition + // and the rest for the index. + static const uint32_t a0[] = { + 0x00800863, 0x00880063, 0x00890863, 0x00930063, 0x00940863, 0x00980864, 0x00991063, 0x009A0863, + 0x009C0055, 0x009D0865, 0x00A01065, 0x00A10065, 0x00A20865, 0x00A50063, 0x00A60863, 0x00A90063, + 0x00AA0863, 0x00B30063, 0x00B40863, 0x00BC0866, 0x00BD0865, 0x00C00055, 0x00C10063, 0x00C30067, + 0x00C40065, 0x00C50068, 0x00C60065, 0x00C70069, 0x00C8006A, 0x00C90065, 0x00CA006B, 0x00CB006C, + 0x00CC0063, 0x00CD006D, 0x00CE006C, 0x00CF006E, 0x00D00863, 0x00D10063, 0x00D3006F, 0x00D40065, + 0x00D50055, 0x00D60063, 0x00D7006F, 0x00D80865, 0x00D90070, 0x00DA0065, 0x00DC0063, 0x00DD0055, + 0x00DE0063, 0x00DF0055, 0x00E00071, 0x00E21072, 0x00E31073, 0x00E41074, 0x00E51072, 0x00E61073, + 0x00E70865, 0x00EF0863, 0x00F20063, 0x00F30863, 0x00F80855, 0x00F91074, 0x00FA0863, 0x00FB0075, + 0x00FC0863, 0x010E0063, 0x010F0863, 0x01100076, 0x01110063, 0x01130863, 0x011A0055, 0x011D0077, + 0x011E0065, 0x011F0077, 0x01200055, 0x01210078, 0x01280055, 0x012A0079, 0x012B007A, 0x012C0055, + 0x0130007A, 0x01310055, 0x0134007B, 0x01350055, 0x0139007C, 0x013A0055, 0x0140007D, 0x01410055, + 0x0144007D, 0x0145007E, 0x01460055, 0x0149007F, 0x014A0080, 0x014B0055, 0x01587881, 0x015D0082, + 0x015E0081, 0x01610037, 0x01630082, 0x01680081, 0x01690037, 0x016C1037, 0x016F0037, 0x01707881, + 0x01730037, 0x01770081, 0x01780037, 0x01800083, 0x01A00883, 0x01A10083, 0x01A20883, 0x01A30083, + 0x01B80078, 0x01BA0837, 0x01BB0078, 0x01BD1081, 0x01BE0078, 0x01BF0806, 0x01C00078, 0x01C21037, + 0x01C30884, 0x01C40885, 0x01C60886, 0x01C70887, 0x01C80855, 0x01C90060, 0x01D10078, 0x01D20060, + 0x01D50860, 0x01D60888, 0x01D70889, 0x01D80855, 0x01D90061, 0x01E1008A, 0x01E20061, 0x01E50861, + 0x01E6088B, 0x01E7088C, 0x01E8108D, 0x01E91077, 0x01EA0877, 0x01EB108E, 0x01EC0063, 0x01F8108F, + 0x01F91090, 0x01FA1091, 0x01FB0019, 0x01FC0065, 0x01FD0063, 0x01FE0055, 0x01FF0077, 0x02000892, + 0x02010092, 0x02060892, 0x02080060, 0x02180061, 0x02280893, 0x02290093, 0x022E0893, 0x02300063, + 0x023B0863, 0x023C0063, 0x02410094, 0x02420083, 0x02440095, 0x02450063, 0x02600077, 0x02610865, + 0x02620065, 0x02680863, 0x026A0063, 0x026B0863, 0x026C0063, 0x026D0863, 0x02700063, 0x02710863, + 0x02740063, 0x02750863, 0x027B0063, 0x027C0863, 0x027D0078, 0x02800063, 0x02880078, 0x02990096, + 0x02AC0078, 0x02AD0097, 0x02B00078, 0x02B10098, 0x02C40078, 0x02C50099, 0x02C60078, 0x02C90083, + 0x02DD0078, 0x02DE0083, 0x02DF009A, 0x02E10083, 0x02E3009A, 0x02E40078, 0x02E8009B, 0x02F60078, + 0x02F8009B, 0x02FA009A, 0x02FB0078, 0x0300009C, 0x03020078, 0x0306000C, 0x03070054, 0x03080083, + 0x030B0078, 0x030F009D, 0x03100078, 0x0311089E, 0x0314009E, 0x031E0078, 0x0320009F, 0x0321009E, + 0x03260083, 0x033000A0, 0x033100A1, 0x033200A2, 0x033300A3, 0x033400A4, 0x03350007, 0x033600A5, + 0x0337009E, 0x03380083, 0x0339009E, 0x033B109E, 0x033D009E, 0x0360089E, 0x0362009E, 0x036A009D, + 0x036B0083, 0x036F0095, 0x03700083, 0x0373009F, 0x03740083, 0x0377009E, 0x0378000E, 0x03790010, + 0x037A0012, 0x037B0014, 0x037C0016, 0x037D009E, 0x037F00A6, 0x0380009D, 0x03870078, 0x0388009E, + 0x03980083, 0x03A60078, 0x03A7009E, 0x03B70078, 0x03C0009E, 0x03D30083, 0x03D90078, 0x04810083, + 0x04820071, 0x049A0871, 0x049B0071, 0x049D0078, 0x049E0083, 0x049F00A7, 0x04A10083, 0x04A500A7, + 0x04A70078, 0x04A80071, 0x04A90083, 0x04AB0078, 0x04AC0871, 0x04B00071, 0x04B10083, 0x04B20097, + 0x04B300A8, 0x04B400A9, 0x04B500AA, 0x04B600AB, 0x04B700AC, 0x04B80097, 0x04B90078, 0x04C100A7, + 0x04C20078, 0x04C30071, 0x04C70078, 0x04C80071, 0x04C90078, 0x04CA0071, 0x04DA0078, 0x04DB0071, + 0x04DD0078, 0x04DE0083, 0x04DF00A7, 0x04E10083, 0x04E30078, 0x04E400A7, 0x04E50078, 0x04E608A7, + 0x04E70071, 0x04E80078, 0x04EE0871, 0x04EF0078, 0x04F00071, 0x04F10083, 0x04F20078, 0x04F300A8, + 0x04F400A9, 0x04F500AA, 0x04F600AB, 0x04F700AC, 0x04F80071, 0x04F90008, 0x04FA00AD, 0x04FB00AE, + 0x04FC00AF, 0x04FD0094, 0x04FE0078, 0x05010083, 0x05020078, 0x05030071, 0x05060078, 0x05080071, + 0x05090078, 0x050A0071, 0x051A0078, 0x051B0871, 0x051C0071, 0x051D0078, 0x051E0083, 0x051F00A7, + 0x05210083, 0x05220078, 0x05240083, 0x05250078, 0x05260083, 0x05270078, 0x052D0871, 0x052E0071, + 0x052F0871, 0x05300078, 0x053300A8, 0x053400A9, 0x053500AA, 0x053600AB, 0x053700AC, 0x05380083, + 0x05390071, 0x053B0078, 0x05410083, 0x05420078, 0x05430071, 0x05470078, 0x05480071, 0x05490078, + 0x054A0071, 0x055A0078, 0x055B0071, 0x055D0078, 0x055E0083, 0x055F00A7, 0x05610083, 0x05630078, + 0x05640083, 0x05650078, 0x056600A7, 0x05670078, 0x05680071, 0x05690078, 0x05700071, 0x05710083, + 0x05720078, 0x057300A8, 0x057400A9, 0x057500AA, 0x057600AB, 0x057700AC, 0x05780078, 0x058100A7, + 0x05820078, 0x05830071, 0x05870078, 0x05880071, 0x05890078, 0x058A0071, 0x059A0078, 0x059B0071, + 0x059D0078, 0x059E0083, 0x059F00A7, 0x05A10083, 0x05A20078, 0x05A408A7, 0x05A50078, 0x05A608A7, + 0x05A70078, 0x05AB0083, 0x05AC0078, 0x05AE0871, 0x05AF0078, 0x05B00071, 0x05B10078, 0x05B300A8, + 0x05B400A9, 0x05B500AA, 0x05B600AB, 0x05B700AC, 0x05B80094, 0x05B90078, 0x05C10083, 0x05C20078, + 0x05C30071, 0x05C60078, 0x05C70071, 0x05CA0871, 0x05CB0078, 0x05CD0071, 0x05D00078, 0x05D20071, + 0x05D30078, 0x05D40071, 0x05D60078, 0x05D70071, 0x05DD0078, 0x05DF00A7, 0x05E00083, 0x05E100A7, + 0x05E20078, 0x05E300A7, 0x05E508A7, 0x05E70078, 0x05F300A8, 0x05F400A9, 0x05F500AA, 0x05F600AB, + 0x05F700AC, 0x05F800B0, 0x05F900B1, 0x05FA0054, 0x05FE0078, 0x060100A7, 0x06020078, 0x06030071, + 0x061A0078, 0x061B0071, 0x061D0078, 0x061F0083, 0x062100A7, 0x06230083, 0x06240883, 0x06250083, + 0x06270078, 0x062B0083, 0x062C0078, 0x06300071, 0x06310078, 0x063300A8, 0x063400A9, 0x063500AA, + 0x063600AB, 0x063700AC, 0x06380078, 0x064100A7, 0x06420078, 0x06430071, 0x065A0078, 0x065B0071, + 0x065D0078, 0x065E0083, 0x065F00A7, 0x066008A7, 0x066100A7, 0x066300B2, 0x066408A7, 0x06660083, + 0x06670078, 0x066B00A7, 0x066C0078, 0x066F0071, 0x06710078, 0x067300A8, 0x067400A9, 0x067500AA, + 0x067600AB, 0x067700AC, 0x06780078, 0x068100A7, 0x06820078, 0x06830071, 0x069D0078, 0x069F00A7, + 0x06A10083, 0x06A20078, 0x06A300A7, 0x06A508A7, 0x06A70078, 0x06B00071, 0x06B10078, 0x06B300A8, + 0x06B400A9, 0x06B500AA, 0x06B600AB, 0x06B700AC, 0x06B80078, 0x06C100A7, 0x06C20078, 0x06C30071, + 0x06CC0078, 0x06CD0071, 0x06D90078, 0x06DA0071, 0x06DE0078, 0x06E00071, 0x06E40078, 0x06E50083, + 0x06E60078, 0x06E800A7, 0x06E90083, 0x06EC00A7, 0x06ED08A7, 0x06F00078, 0x06F900A7, 0x06FA0097, + 0x06FB0078, 0x07010071, 0x071A0083, 0x071E0078, 0x07200071, 0x07230081, 0x07240083, 0x072800A8, + 0x072900A9, 0x072A00AA, 0x072B00AB, 0x072C00AC, 0x072D0097, 0x072E0078, 0x07410071, 0x07430078, + 0x07440071, 0x07460078, 0x074A0071, 0x074C0078, 0x074D0071, 0x07500078, 0x07510071, 0x07520078, + 0x07550071, 0x07560078, 0x07570071, 0x075A0083, 0x075D0078, 0x075E0083, 0x075F0078, 0x07600071, + 0x07630081, 0x07640083, 0x07670078, 0x076800A8, 0x076900A9, 0x076A00AA, 0x076B00AB, 0x076C00AC, + 0x076D0078, 0x076E1071, 0x076F0078, 0x07800071, 0x07810094, 0x07820097, 0x07865897, 0x07870097, + 0x078A0094, 0x078C0083, 0x078D0094, 0x079000A8, 0x079100A9, 0x079200AA, 0x079300AB, 0x079400AC, + 0x079500B3, 0x079A0094, 0x079D00B4, 0x079F00A7, 0x07A00071, 0x07A40078, 0x07A50071, 0x07A90871, + 0x07AA0071, 0x07AE0871, 0x07AF0071, 0x07B60078, 0x07B90083, 0x07BB0883, 0x07BD0083, 0x07C40071, + 0x07C60078, 0x07C80083, 0x07CC0078, 0x07CD0083, 0x07D10883, 0x07D20083, 0x07D60883, 0x07D70083, + 0x07DF0094, 0x07E30083, 0x07E40094, 0x07E70078, 0x07E80097, 0x07E90078, 0x08000071, 0x08110078, + 0x08120071, 0x08130871, 0x08140078, 0x08150071, 0x081600A7, 0x08170083, 0x081A0078, 0x081B0083, + 0x081C00A7, 0x081D0078, 0x082000A8, 0x082100A9, 0x082200AA, 0x082300AB, 0x082400AC, 0x08250097, + 0x08280071, 0x082B00A7, 0x082C0083, 0x082D0078, 0x085000B5, 0x08630078, 0x08680071, 0x087E7881, + 0x087F0078, 0x08800071, 0x08AD0078, 0x08B00071, 0x08D20078, 0x08D40071, 0x08FD0078, 0x09000071, + 0x09270078, 0x09280071, 0x092F0078, 0x09300071, 0x09470078, 0x09480071, 0x095B0078, 0x095C0071, + 0x09630078, 0x09640071, 0x098B0078, 0x098C0071, 0x09AE0078, 0x09B00094, 0x09B10097, 0x09B500B6, + 0x09B600B7, 0x09B700B8, 0x09B800B9, 0x09B900B0, 0x09BA00BA, 0x09BB00BB, 0x09BC00BC, 0x09BD00BD, + 0x09BE00BE, 0x09BF0078, 0x09C00071, 0x09C80054, 0x09CD0078, 0x09D00071, 0x09FB0078, 0x0A010071, + 0x0B370097, 0x0B380071, 0x0B3C0078, 0x0B400005, 0x0B410071, 0x0B4E00BF, 0x0B4F0078, 0x0B500071, + 0x0B760097, 0x0B7700C0, 0x0B7800C1, 0x0B790078, 0x0B800071, 0x0B890083, 0x0B8B0078, 0x0B900071, + 0x0B990083, 0x0B9B0097, 0x0B9C0078, 0x0BA00071, 0x0BA90083, 0x0BAA0078, 0x0BB00071, 0x0BB90083, + 0x0BBA0078, 0x0BC00071, 0x0BDA00C2, 0x0BDB00A7, 0x0BDC0083, 0x0BDF00A7, 0x0BE30083, 0x0BE400A7, + 0x0BE50083, 0x0BEA0097, 0x0BEE0071, 0x0BEF0078, 0x0BF000A8, 0x0BF100A9, 0x0BF200AA, 0x0BF300AB, + 0x0BF400AC, 0x0BF50078, 0x0BF800C3, 0x0BF900C4, 0x0BFA00C5, 0x0BFB00C6, 0x0BFC00C7, 0x0BFD0078, + 0x0C000006, 0x0C030099, 0x0C040006, 0x0C060083, 0x0C070005, 0x0C0800A8, 0x0C0900A9, 0x0C0A00AA, + 0x0C0B00AB, 0x0C0C00AC, 0x0C0D0078, 0x0C100071, 0x0C3C0078, 0x0C400071, 0x0C550078, 0x0C800071, + 0x0C8F0078, 0x0C900083, 0x0C9200A7, 0x0C940083, 0x0C9500C8, 0x0C960078, 0x0C9800A7, 0x0C990083, + 0x0C9A00A7, 0x0C9D0083, 0x0C9E0078, 0x0CA00054, 0x0CA10078, 0x0CA20006, 0x0CA300A8, 0x0CA400A9, + 0x0CA500AA, 0x0CA600AB, 0x0CA700AC, 0x0CA80071, 0x0CB70078, 0x0CB80071, 0x0CBB0078, 0x0CC00071, + 0x0CD50078, 0x0CD800A7, 0x0CE10071, 0x0CE400A7, 0x0CE50078, 0x0CE800A8, 0x0CE900A9, 0x0CEA00AA, + 0x0CEB00AB, 0x0CEC00AC, 0x0CED0078, 0x0CEF0006, 0x0CF00054, 0x0D000071, 0x0D0C0083, 0x0D0D00A7, + 0x0D0E0078, 0x0D0F0097, 0x0D100078, 0x0E800055, 0x0E967881, 0x0EA70081, 0x0EA87881, 0x0EB17055, + 0x0EB60055, 0x0EBC7881, 0x0EBD0055, 0x0ECE7881, 0x0EE00083, 0x0EE20078, 0x0F000863, 0x0F4B0855, + 0x0F4D1055, 0x0F4E0078, 0x0F500863, 0x0F7D0078, 0x0F8008C9, 0x0F8408CA, 0x0F8808C9, 0x0F8B0078, + 0x0F8C08CA, 0x0F8F0078, 0x0F9008C9, 0x0F9408CA, 0x0F9808C9, 0x0F9C08CA, 0x0FA008C9, 0x0FA30078, + 0x0FA408CA, 0x0FA70078, 0x0FA80855, 0x0FAC0078, 0x0FB008C9, 0x0FB408CA, 0x0FB808CB, 0x0FB908CC, + 0x0FBB08CD, 0x0FBC08CE, 0x0FBD08CF, 0x0FBE08D0, 0x0FBF0078, 0x0FC008C9, 0x0FC408D1, 0x0FC808C9, + 0x0FCC08D1, 0x0FD008C9, 0x0FD408D1, 0x0FD808C9, 0x0FD90855, 0x0FDC08CA, 0x0FDD08D2, 0x0FDE08D3, + 0x0FDF08D4, 0x0FE01037, 0x0FE10855, 0x0FE408D5, 0x0FE608D3, 0x0FE70837, 0x0FE808C9, 0x0FE90855, + 0x0FEA0078, 0x0FEB0855, 0x0FEC08CA, 0x0FED08D6, 0x0FEE0078, 0x0FEF0837, 0x0FF008C9, 0x0FF10855, + 0x0FF408CA, 0x0FF508D7, 0x0FF608D8, 0x0FF70837, 0x0FF80078, 0x0FF90855, 0x0FFC08D9, 0x0FFD08DA, + 0x0FFE08D3, 0x0FFF1037, 0x10000805, 0x10011005, 0x10060057, 0x100700C2, 0x10080099, 0x100B0006, + 0x100C00DB, 0x100D00B4, 0x100E00DB, 0x100F00B4, 0x10100006, 0x10121006, 0x101400DC, 0x101500DD, + 0x101600DE, 0x101700DF, 0x10180007, 0x101A1007, 0x101B1006, 0x101C0006, 0x101D00E0, 0x101E1006, + 0x10200038, 0x10210006, 0x102200E1, 0x1023000A, 0x10241006, 0x10250006, 0x10290019, 0x102A0038, + 0x102B0006, 0x10300057, 0x10320078, 0x10350057, 0x103878E2, 0x10390078, 0x103A78E3, 0x103B78E4, + 0x103C78E5, 0x103D780B, 0x103E7819, 0x103F780A, 0x104070E2, 0x1041705A, 0x104270E3, 0x104370E4, + 0x104470E5, 0x1045700B, 0x10467019, 0x1047700A, 0x10487081, 0x104B0078, 0x10500008, 0x10541008, + 0x10550008, 0x105B0078, 0x10680083, 0x106F0095, 0x10730083, 0x10760078, 0x10801054, 0x10812877, + 0x10820054, 0x10831054, 0x10840054, 0x10852855, 0x10862877, 0x10872855, 0x10882877, 0x108A0054, + 0x108B1054, 0x108C0054, 0x108D2877, 0x108F0054, 0x10907854, 0x10922877, 0x109308E6, 0x10942877, + 0x109508E7, 0x10962877, 0x10970058, 0x10982877, 0x10990054, 0x109A2855, 0x109B1071, 0x109D0054, + 0x109E2855, 0x109F2877, 0x10A028E8, 0x10A10019, 0x10A32855, 0x10A50054, 0x10A70078, 0x10AA305F, + 0x10B010E9, 0x10B110EA, 0x10B210EB, 0x10B310EC, 0x10B410ED, 0x10B510EE, 0x10B610EF, 0x10B710F0, + 0x10B810F1, 0x10B910F2, 0x10BA10F3, 0x10BB10F4, 0x10BC10F5, 0x10BD10F6, 0x10BE10F7, 0x10BF10F8, + 0x10C000F9, 0x10C100FA, 0x10C20078, 0x10C80019, 0x10CB0054, 0x10CD0819, 0x10CE0054, 0x10D00019, + 0x10D10054, 0x10D30019, 0x10D40054, 0x10D70819, 0x10D80054, 0x10E70819, 0x10E80054, 0x10E90019, + 0x10EB0054, 0x10FA0019, 0x110100E8, 0x110208E8, 0x11030019, 0x110400FB, 0x110608FC, 0x11070019, + 0x1109000B, 0x110A0019, 0x110B00E8, 0x110C0019, 0x110D00E8, 0x110F0019, 0x111000E8, 0x111208E8, + 0x11140019, 0x111610E8, 0x111700E8, 0x111810E8, 0x111900E8, 0x111A0019, 0x111E00FD, 0x111F00E8, + 0x112208E8, 0x112300E8, 0x11270019, 0x112900FD, 0x112B0019, 0x113008E8, 0x113200FD, 0x11360019, + 0x113708FD, 0x113900FD, 0x113A08FD, 0x113B00FD, 0x113C08FD, 0x113D00FD, 0x114008FD, 0x114100FD, + 0x114208FD, 0x114300FD, 0x114408FD, 0x114500FD, 0x114600E8, 0x11470019, 0x114800FE, 0x114A0019, + 0x114C00FF, 0x114D0019, 0x115100FD, 0x11520019, 0x11530100, 0x11540101, 0x115500E8, 0x115608E8, + 0x115800FD, 0x115C00E8, 0x115D0019, 0x115F00E8, 0x11600019, 0x116500FE, 0x11670019, 0x116800FD, + 0x11690019, 0x116B00FD, 0x117008FD, 0x117200FD, 0x117508FD, 0x11770019, 0x117800FD, 0x11790102, + 0x117B0103, 0x117C00E8, 0x117D0104, 0x117F0105, 0x11800054, 0x118400FD, 0x11860054, 0x119000E8, + 0x11910054, 0x1195080A, 0x11960054, 0x119B0094, 0x11BE0019, 0x11BF0054, 0x11CE0019, 0x11DA00B4, + 0x11DB0006, 0x11DC0054, 0x11EE0078, 0x12000054, 0x12140078, 0x12200054, 0x12260078, 0x12301906, + 0x12311907, 0x12321908, 0x12331909, 0x1234190A, 0x1235190B, 0x1236190C, 0x1237190D, 0x1238190E, + 0x1239190F, 0x123A1106, 0x123B1107, 0x123C1108, 0x123D1109, 0x123E110A, 0x123F110B, 0x1240110C, + 0x1241110D, 0x1242110E, 0x1243110F, 0x1244105D, 0x1245105B, 0x12461110, 0x12471111, 0x12481112, + 0x12491113, 0x124A1114, 0x124B1115, 0x124C1116, 0x124D1117, 0x124E1094, 0x125B1918, 0x12681919, + 0x127518C3, 0x1276011A, 0x1277011B, 0x1278011C, 0x1279011D, 0x127A011E, 0x127B00C4, 0x127C00C5, + 0x127D00C6, 0x127E00C7, 0x127F011F, 0x12800054, 0x12FC0019, 0x13000054, 0x134F0078, 0x13500054, + 0x13560094, 0x13570054, 0x13590078, 0x13810054, 0x13850078, 0x13860054, 0x13940078, 0x13950054, + 0x13A60078, 0x13A80054, 0x13AA0078, 0x13AB0054, 0x13B00078, 0x13B10054, 0x13B40009, 0x13BB0106, + 0x13BC0107, 0x13BD0108, 0x13BE0109, 0x13BF010A, 0x13C00106, 0x13C10107, 0x13C20108, 0x13C30109, + 0x13C4010A, 0x13C50106, 0x13C60107, 0x13C70108, 0x13C80109, 0x13C9010A, 0x13CA0054, 0x13CB0078, + 0x13CC0054, 0x13D80078, 0x13D90054, 0x13E000E8, 0x13E10019, 0x13E200FE, 0x13E3000A, 0x13E40078, + 0x13E80019, 0x13EA00E8, 0x13EB00FE, 0x13EC0019, 0x13EE00E8, 0x13EF00FE, 0x13F00019, 0x13F100FD, + 0x13F30009, 0x13F60078, 0x13F80019, 0x14000094, 0x14800019, 0x14C2000A, 0x14C70120, 0x14C80121, + 0x14C9000A, 0x14CD0019, 0x14CE00E8, 0x14D80019, 0x14DC0122, 0x14DD0019, 0x14E000FD, 0x14E100E8, + 0x14E200FD, 0x14E30019, 0x14E700E8, 0x14E800FE, 0x14EA00FD, 0x14EB0019, 0x14EC0009, 0x14EE00E8, + 0x14EF0019, 0x14F200E8, 0x14F30019, 0x14F400E8, 0x14F50019, 0x14FA00E8, 0x14FC00FD, 0x14FD0019, + 0x14FE0009, 0x14FF0019, 0x150500E8, 0x150610E8, 0x150700E8, 0x15110019, 0x151200E8, 0x15140019, + 0x151600FE, 0x15180019, 0x151A00FD, 0x151B0019, 0x151E00FD, 0x151F00E8, 0x15200019, 0x152C00E8, + 0x152D0019, 0x153200FD, 0x15330019, 0x153500E8, 0x15370019, 0x153800E8, 0x15390019, 0x153A10E8, + 0x153B1019, 0x153C0019, 0x153D00FE, 0x153E00E8, 0x153F00FE, 0x154300E8, 0x154600FE, 0x154700E8, + 0x154900FE, 0x154F00E8, 0x155100FE, 0x15520019, 0x155300FD, 0x15570019, 0x155800FE, 0x155900E8, + 0x155A00FE, 0x155B00E8, 0x155E00FE, 0x156400E8, 0x156700FE, 0x156C0019, 0x156E08E8, 0x156F0123, + 0x15700019, 0x157100E8, 0x15720124, 0x157300E8, 0x15740019, 0x157600FD, 0x157700E8, 0x15780019, + 0x157C00FE, 0x157E0019, 0x15800054, 0x158A0078, 0x16000096, 0x16180098, 0x16300078, 0x16400063, + 0x16720055, 0x16730054, 0x16760078, 0x167D0006, 0x16800125, 0x16930078, 0x16980071, 0x16B30078, + 0x16C00071, 0x16CC0078, 0x16D00071, 0x16F00078, 0x17000006, 0x17010126, 0x17030006, 0x170500E0, + 0x17060126, 0x17070006, 0x170C0078, 0x170E0126, 0x170F0078, 0x17400054, 0x174D0078, 0x174E0054, + 0x177A0078, 0x17801054, 0x17EB0078, 0x17F80054, 0x17FE0078, 0x18008805, 0x18010006, 0x18020054, + 0x18030071, 0x18040009, 0x18090054, 0x180A0009, 0x180E0099, 0x180F00BF, 0x18100054, 0x18110127, + 0x18120128, 0x18130129, 0x1814012A, 0x18150083, 0x18180099, 0x18190081, 0x181B1054, 0x181C112B, + 0x181D112C, 0x181E0071, 0x181F0054, 0x18200078, 0x18210071, 0x18260871, 0x18320071, 0x18380871, + 0x18390071, 0x183A0871, 0x183C0071, 0x183D0871, 0x183F0071, 0x184A0871, 0x184B0071, 0x184C0078, + 0x184D0083, 0x184E1037, 0x184F0881, 0x18500099, 0x18510071, 0x18560871, 0x18620071, 0x18680871, + 0x18690071, 0x186A0871, 0x186C0071, 0x186D0871, 0x186F0071, 0x187A0871, 0x187B0071, 0x187C0871, + 0x187E0081, 0x187F0881, 0x18800078, 0x18830071, 0x18970078, 0x18991071, 0x18C80094, 0x18C978AD, + 0x18CA78AE, 0x18CB7894, 0x18D00071, 0x18DC0078, 0x18E00054, 0x18E80078, 0x18F80071, 0x19001094, + 0x190F1054, 0x191010AD, 0x191110AE, 0x1912112D, 0x1913112E, 0x1914112F, 0x19151094, 0x19220078, + 0x19286854, 0x19291930, 0x192A1931, 0x192B1932, 0x192C1933, 0x192D1934, 0x192E1935, 0x192F1936, + 0x19301894, 0x193E1854, 0x194018AD, 0x194118AE, 0x1942192D, 0x1943192E, 0x1944192F, 0x19451894, + 0x19591937, 0x195A1938, 0x195B1939, 0x195C193A, 0x195D193B, 0x195E193C, 0x195F193D, 0x19601094, + 0x19666854, 0x19681894, 0x19806894, 0x19AC1094, 0x19B96894, 0x19BC6854, 0x19BE6894, 0x19EF6854, + 0x19F01094, 0x1A000071, 0x26DB0078, 0x26E00054, 0x27000071, 0x4FDE0078, 0x50000071, 0x52470078, + 0x52480054, 0x52640078, 0x53800037, 0x538C0078, 0x54000071, 0x540100C8, 0x54020071, 0x54030083, + 0x54040071, 0x541200A7, 0x54130083, 0x54140054, 0x54160078, 0x56000071, 0x6BD20078, 0x6C00013E, + 0x7000013F, 0x7C800871, 0x7D070071, 0x7D080871, 0x7D0A0071, 0x7D0B0871, 0x7D120071, 0x7D130871, + 0x7D140071, 0x7D150871, 0x7D170078, 0x7D180871, 0x7D360078, 0x7D380871, 0x7D6D0078, 0x7D801055, + 0x7D840078, 0x7D8A1055, 0x7D8C0078, 0x7D8F0083, 0x7D90289B, 0x7D95089B, 0x7DA10078, 0x7DA2089B, + 0x7DA8409E, 0x7DAA389E, 0x7DAB409E, 0x7DAC389E, 0x7DAD409E, 0x7DAE389E, 0x7DAF409E, 0x7DB0389E, + 0x7DB1409E, 0x7DB2389E, 0x7DB3409E, 0x7DB4389E, 0x7DB5409E, 0x7DB6389E, 0x7DB7409E, 0x7DB8389E, + 0x7DB9409E, 0x7DBA389E, 0x7DBB409E, 0x7DBC389E, 0x7DBD409E, 0x7DBE389E, 0x7DBF409E, 0x7DC0389E, + 0x7DC1409E, 0x7DC8389E, 0x7DC9409E, 0x7DCA389E, 0x7DCB409E, 0x7DCC389E, 0x7DCD409E, 0x7DCE389E, + 0x7DCF409E, 0x7DD1389E, 0x7DD2409E, 0x7DD4389E, 0x7DD5409E, 0x7DD6389E, 0x7DD7409E, 0x7DD90078, + 0x7DEA209E, 0x7DEB489E, 0x7DEC209E, 0x7DEF409E, 0x7DF3389E, 0x7DF5409E, 0x7DFC389E, 0x7DFD209E, + 0x7DFE409E, 0x7DFF389E, 0x7E00409E, 0x7E32209E, 0x7E4C389E, 0x7E70489E, 0x7E7B409E, 0x7E89209E, + 0x7E97389E, 0x7E9A489E, 0x7E9E209E, 0x7E9F00B4, 0x7EA00078, 0x7EA8389E, 0x7EAC209E, 0x7EAE389E, + 0x7EAF209E, 0x7EB0389E, 0x7EB1209E, 0x7EB4389E, 0x7EB5209E, 0x7EB8389E, 0x7EBA209E, 0x7EC3389E, + 0x7EC80078, 0x7EC9389E, 0x7ECB209E, 0x7ECC389E, 0x7ECD209E, 0x7EDA389E, 0x7EDB209E, 0x7EDC389E, + 0x7EDE209E, 0x7EE2389E, 0x7EE3209E, 0x7EE40078, 0x7EF8409E, 0x7EFE4140, 0x7EFF0078, 0x7F000083, + 0x7F088006, 0x7F0C80BF, 0x7F0D0078, 0x7F100083, 0x7F120078, 0x7F188006, 0x7F198099, 0x7F1A8038, + 0x7F1B80BF, 0x7F230006, 0x7F2480BF, 0x7F251006, 0x7F271038, 0x7F28600C, 0x7F2A6006, 0x7F2C6099, + 0x7F2D60BF, 0x7F306006, 0x7F31600B, 0x7F326019, 0x7F346006, 0x7F356007, 0x7F360078, 0x7F38409E, + 0x7F41209E, 0x7F46489E, 0x7F47209E, 0x7F49489E, 0x7F4A209E, 0x7F4C489E, 0x7F4D209E, 0x7F4E489E, + 0x7F4F209E, 0x7F50489E, 0x7F51209E, 0x7F52489E, 0x7F53209E, 0x7F54489E, 0x7F55209E, 0x7F5A489E, + 0x7F5B209E, 0x7F5C489E, 0x7F5D209E, 0x7F5E489E, 0x7F5F209E, 0x7F60489E, 0x7F61209E, 0x7F62489E, + 0x7F63209E, 0x7F64489E, 0x7F65209E, 0x7F66489E, 0x7F67209E, 0x7F68489E, 0x7F69209E, 0x7F6A489E, + 0x7F6B209E, 0x7F6C489E, 0x7F6D209E, 0x7F6E489E, 0x7F6F209E, 0x7F70489E, 0x7F71209E, 0x7F72489E, + 0x7F73209E, 0x7F74489E, 0x7F75209E, 0x7F76489E, 0x7F77209E, 0x7F7A489E, 0x7F7B209E, 0x7F7F0078, + 0x7F818806, 0x7F828808, 0x7F838806, 0x7F848809, 0x7F858806, 0x7F86880C, 0x7F88880E, 0x7F898810, + 0x7F8A8812, 0x7F8B8814, 0x7F8C8816, 0x7F8D880C, 0x7F8E8818, 0x7F8F881A, 0x7F908806, 0x7F918860, + 0x7F9E8806, 0x7F9F8837, 0x7FA18861, 0x7FAE8819, 0x7FB0880A, 0x7FB15009, 0x7FB25006, 0x7FB35071, + 0x7FB85081, 0x7FB95071, 0x7FCF5081, 0x7FD05071, 0x7FE00078, 0x7FE15071, 0x7FE40078, 0x7FE55071, + 0x7FE80078, 0x7FE95071, 0x7FEC0078, 0x7FED5071, 0x7FEF0078, 0x7FF08808, 0x7FF18819, 0x7FF28854, + 0x7FF38808, 0x7FF45054, 0x7FF55019, 0x7FF75054, 0x7FF80078, 0x7FFD0141, 0x7FFE0054, 0x7FFF0078, + 0x80000071, 0x80060078, 0x80070071, 0x801F0078, 0x80200071, 0x80270078, 0x80280071, 0x802F0078, + 0x80400071, 0x807E0078, 0x80800097, 0x80810094, 0x80820078, 0x808400B6, 0x808500B7, 0x808600B8, + 0x808700B9, 0x808800B0, 0x808900BA, 0x808A00BB, 0x808B00BC, 0x808C00BD, 0x808D0142, 0x808E0143, + 0x808F0144, 0x80900145, 0x809100B1, 0x80920146, 0x80930147, 0x80940148, 0x80950149, 0x8096014A, + 0x8097014B, 0x8098014C, 0x8099014D, 0x809A0078, 0x809C0094, 0x80A0014E, 0x80A1014F, 0x80A20150, + 0x80A30151, 0x80A40152, 0x80A50150, 0x80A60153, 0x80A70151, 0x80A80154, 0x80A90155, 0x80AA0156, + 0x80AB0157, 0x80AC014F, 0x80AE0158, 0x80B00154, 0x80B30150, 0x80B50155, 0x80B60153, 0x80B90151, + 0x80BA0150, 0x80BB005F, 0x80BD0054, 0x80C500C3, 0x80C60078, 0x81800071, 0x819000AD, 0x819100B0, + 0x81920078, 0x81980071, 0x81A50159, 0x81A60078, 0x81C00071, 0x81CF0078, 0x81D00071, 0x81E20078, + 0x81E40071, 0x81E80094, 0x81E90158, 0x81EA015A, 0x81EB0078, 0x8200015B, 0x8214015C, 0x82280071, + 0x824F0078, 0x825000A8, 0x825100A9, 0x825200AA, 0x825300AB, 0x825400AC, 0x82550078, 0x8400009B, + 0x84030078, 0x8404009B, 0x841B0078, 0x841C009B, 0x841D0078, 0x841E009B, 0x841F0078, 0x8500009B, + 0x85010083, 0x85020078, 0x85030083, 0x85040078, 0x85060083, 0x8508009B, 0x850A0078, 0x850B009B, + 0x850C0078, 0x850D009B, 0x851A0078, 0x851C0083, 0x851E0078, 0x8520015D, 0x8521015E, 0x8522015F, + 0x85230160, 0x85240078, 0x8528009A, 0x852D0078, 0xE8000094, 0xE87B0078, 0xE8800094, 0xE8940078, + 0xE8950094, 0xE8AF0894, 0xE8B300A7, 0xE8B40083, 0xE8B50094, 0xE8B700A7, 0xE8BA0057, 0xE8BE0083, + 0xE8C20094, 0xE8C30083, 0xE8C60094, 0xE8D50083, 0xE8D70094, 0xE8DE0894, 0xE8E10094, 0xE8EF0078, + 0xE9000054, 0xE9210083, 0xE9230078, 0xE9800054, 0xE9AC0078, 0xEA002877, 0xEA0D2855, 0xEA1A2877, + 0xEA272855, 0xEA342877, 0xEA412855, 0xEA4E2877, 0xEA500078, 0xEA512877, 0xEA520078, 0xEA532877, + 0xEA540078, 0xEA552877, 0xEA5B2855, 0xEA5D0078, 0xEA5F2855, 0xEA620078, 0xEA632855, 0xEA682877, + 0xEA752855, 0xEA822877, 0xEA830078, 0xEA842877, 0xEA860078, 0xEA872877, 0xEA8F2855, 0xEA9C2877, + 0xEA9D0078, 0xEA9E2877, 0xEAA40078, 0xEAA52877, 0xEAA92855, 0xEAB62877, 0xEAC32855, 0xEAD02877, + 0xEADD2855, 0xEAEA2877, 0xEAF72855, 0xEB042877, 0xEB112855, 0xEB1E2877, 0xEB2B2855, 0xEB382877, + 0xEB452855, 0xEB530078, 0xEB542877, 0xEB612855, 0xEB712877, 0xEB7E2855, 0xEB8E2877, 0xEB9B2855, + 0xEBAB2877, 0xEBB82855, 0xEBC82877, 0xEBD52855, 0xEBE50078, 0xEBE7280E, 0xEBE82810, 0xEBE92812, + 0xEBEA2814, 0xEBEB2816, 0xEBEC280E, 0xEBED2810, 0xEBEE2812, 0xEBEF2814, 0xEBF02816, 0xEBF1280E, + 0xEBF22810, 0xEBF32812, 0xEBF42814, 0xEBF52816, 0xEBF6280E, 0xEBF72810, 0xEBF82812, 0xEBF92814, + 0xEBFA2816, 0xEBFB280E, 0xEBFC2810, 0xEBFD2812, 0xEBFE2814, 0xEBFF2816, 0xEC000078 + }; + + static const uint32_t a1[] = { + 0x00000071, 0x536C0078, 0x7C000871, 0x7D0F0078 + }; + + static const uint32_t a7[] = { + 0x00100057, 0x00400078, 0x00800083, 0x00F80078, 0x8000013F, 0xFFFF0078 + }; + + static const uint32_t a8[] = { + 0x0000013F, 0x7FFF0078 + }; + + static const uint32_t a16[] = { + 0x00800865, 0x00880065, 0x00890865, 0x00930065, 0x00940865, 0x00980161, 0x00991065, 0x009A0865, + 0x009C0863, 0x009F1063, 0x00A00063, 0x00A10863, 0x00A41055, 0x00A50065, 0x00A60865, 0x00A90065, + 0x00AA0865, 0x00B30065, 0x00B40865, 0x00BC0863, 0x00BF1162, 0x00C00163, 0x00C10065, 0x00C30063, + 0x00C40068, 0x00C50063, 0x00C60055, 0x00C70164, 0x00C80063, 0x00C90068, 0x00CA0165, 0x00CB0166, + 0x00CC0065, 0x00CD0055, 0x00CE0167, 0x00CF0168, 0x00D00865, 0x00D10065, 0x00D30063, 0x00D4006F, + 0x00D50055, 0x00D60065, 0x00D70863, 0x00D80070, 0x00D90063, 0x00DB0169, 0x00DC0065, 0x00DD0071, + 0x00DE0065, 0x00DF016A, 0x00E00071, 0x00E21074, 0x00E31072, 0x00E41073, 0x00E51074, 0x00E60863, + 0x00EE016B, 0x00EF0865, 0x00F20065, 0x00F30865, 0x00F81072, 0x00F91073, 0x00FA0865, 0x00FB016C, + 0x00FC0865, 0x010E0065, 0x010F0865, 0x01100055, 0x01110065, 0x01130865, 0x011A0055, 0x011D0063, + 0x011E016D, 0x011F0055, 0x0120016E, 0x01210078, 0x01280055, 0x0129016F, 0x012A0055, 0x012B007A, + 0x012C0170, 0x012D0171, 0x012E0055, 0x01310172, 0x01320055, 0x01340173, 0x01350055, 0x01370173, + 0x01380055, 0x013A0174, 0x013B0055, 0x0141007D, 0x01420055, 0x0145007E, 0x01460055, 0x01587881, + 0x015C0082, 0x015D0081, 0x01610037, 0x01630082, 0x01680081, 0x01690037, 0x016C1037, 0x016F0037, + 0x01707881, 0x01720037, 0x01800083, 0x01A00883, 0x01A20175, 0x01A30083, 0x01B80078, 0x01BA0037, + 0x01BB0078, 0x01C20837, 0x01C30806, 0x01C40885, 0x01C50078, 0x01C70887, 0x01C80060, 0x01D50860, + 0x01D60889, 0x01D80061, 0x01E50861, 0x01E6088C, 0x01E70078, 0x01E81176, 0x01E90877, 0x01EA1177, + 0x01EB0055, 0x01EC0065, 0x01F81093, 0x01F90055, 0x01FA1178, 0x01FB0063, 0x01FC10D8, 0x01FD0065, + 0x01FE0077, 0x02000892, 0x02020092, 0x02030892, 0x02040092, 0x02060892, 0x02070092, 0x02080060, + 0x020C0860, 0x020D0060, 0x02180061, 0x021C0861, 0x021D0061, 0x02280893, 0x022A0093, 0x022B0893, + 0x022C0093, 0x022E0893, 0x022F0093, 0x02300065, 0x023B0865, 0x023C0065, 0x02410083, 0x02430078, + 0x02440095, 0x02450065, 0x02600863, 0x02610063, 0x02670078, 0x02680865, 0x026A0065, 0x026B0865, + 0x026C0065, 0x026D0865, 0x02700065, 0x02710865, 0x02740065, 0x02750865, 0x027B0065, 0x027C0865, + 0x027D0078, 0x02800065, 0x02880078, 0x02980096, 0x02AB0078, 0x02AC0081, 0x02AD0097, 0x02B00098, + 0x02C31055, 0x02C40097, 0x02C50078, 0x02C80083, 0x02E1009A, 0x02E20083, 0x02E40078, 0x02E8009B, + 0x02F50078, 0x02F8009B, 0x02F9009A, 0x02FA0078, 0x0300009C, 0x03020078, 0x03050140, 0x0306009D, + 0x03070054, 0x03080083, 0x030B0078, 0x030D009D, 0x030E0078, 0x030F009D, 0x0310009E, 0x0311089E, + 0x0313009E, 0x031D0078, 0x0320009E, 0x03250083, 0x032F0078, 0x03300179, 0x0331017A, 0x0332017B, + 0x0333017C, 0x0334017D, 0x033500A5, 0x0336009D, 0x0337009E, 0x033A109E, 0x033C009E, 0x0369089E, + 0x036A009E, 0x036B0083, 0x036E009C, 0x036F0083, 0x0372009F, 0x03730083, 0x03740054, 0x03750083, + 0x0377009E, 0x0378000F, 0x03790011, 0x037A0013, 0x037B0015, 0x037C0017, 0x037D009E, 0x037E00A6, + 0x037F009E, 0x0380009D, 0x03870057, 0x03880083, 0x0389009E, 0x03980083, 0x03A50078, 0x03A6009E, + 0x03B70078, 0x03C0009E, 0x03D30083, 0x03D8009E, 0x03D90078, 0x04800083, 0x048100A7, 0x04820071, + 0x04940871, 0x04950071, 0x04980871, 0x04990071, 0x049D0078, 0x049E0071, 0x049F00A7, 0x04A00083, + 0x04A400A7, 0x04A60083, 0x04A70078, 0x04A80083, 0x04AA0078, 0x04AC0871, 0x04B00071, 0x04B10083, + 0x04B20097, 0x04B3017E, 0x04B4017F, 0x04B50180, 0x04B60181, 0x04B70182, 0x04B80078, 0x04BE0071, + 0x04BF0078, 0x04C00083, 0x04C100A7, 0x04C20071, 0x04C60078, 0x04C70071, 0x04C80078, 0x04C90071, + 0x04D40078, 0x04D50071, 0x04D80078, 0x04DB0071, 0x04DD0078, 0x04DE0071, 0x04DF00A7, 0x04E00083, + 0x04E20078, 0x04E300A7, 0x04E40078, 0x04E508A7, 0x04E60083, 0x04E70078, 0x04EB00A7, 0x04EC0078, + 0x04EE0871, 0x04F00071, 0x04F10083, 0x04F20078, 0x04F3017E, 0x04F4017F, 0x04F50180, 0x04F60181, + 0x04F70182, 0x04F80071, 0x04F90008, 0x04FA00B6, 0x04FB00B7, 0x04FC0183, 0x04FD0078, 0x05000083, + 0x050100A7, 0x05020071, 0x05050078, 0x05070071, 0x05080078, 0x05090071, 0x05140078, 0x05150071, + 0x05180078, 0x05190871, 0x051A0071, 0x051B0078, 0x051C0071, 0x051D0078, 0x051F00A7, 0x05200083, + 0x05210078, 0x05230083, 0x05240078, 0x05250083, 0x05270078, 0x052C0871, 0x052E0078, 0x0533017E, + 0x0534017F, 0x05350180, 0x05360181, 0x05370182, 0x05380083, 0x05390071, 0x053A0078, 0x05400083, + 0x054100A7, 0x05420071, 0x05540078, 0x05550071, 0x05580078, 0x05590071, 0x055D0078, 0x055E0071, + 0x055F00A7, 0x05600083, 0x056400A7, 0x05660083, 0x05670078, 0x05700071, 0x05710083, 0x05720078, + 0x0573017E, 0x0574017F, 0x05750180, 0x05760181, 0x05770182, 0x05780008, 0x05790078, 0x05800083, + 0x058100A7, 0x05820071, 0x05860078, 0x05870071, 0x05880078, 0x05890071, 0x05940078, 0x05950071, + 0x05980078, 0x05990071, 0x059D0078, 0x059E0071, 0x059F0083, 0x05A20078, 0x05A300A7, 0x05A40078, + 0x05A508A7, 0x05A60083, 0x05A70078, 0x05AB00A7, 0x05AC0078, 0x05AE0871, 0x05AF0071, 0x05B10078, + 0x05B3017E, 0x05B4017F, 0x05B50180, 0x05B60181, 0x05B70182, 0x05B80071, 0x05B90078, 0x05C10071, + 0x05C50078, 0x05C70071, 0x05C80078, 0x05C90071, 0x05CB0078, 0x05CC0071, 0x05CD0078, 0x05CF0071, + 0x05D00078, 0x05D10071, 0x05D20078, 0x05D40071, 0x05D50078, 0x05D70071, 0x05DD0078, 0x05DF00A7, + 0x05E10078, 0x05E300A7, 0x05E40078, 0x05E508A7, 0x05E60083, 0x05E70078, 0x05EB00A7, 0x05EC0078, + 0x05F3017E, 0x05F4017F, 0x05F50180, 0x05F60181, 0x05F70182, 0x05F80184, 0x05F90054, 0x05FC0008, + 0x05FD0078, 0x060000A7, 0x06020071, 0x06060078, 0x06070071, 0x06080078, 0x06090071, 0x06140078, + 0x06150071, 0x061D0078, 0x061F0083, 0x062000A7, 0x06220078, 0x06230083, 0x06240078, 0x06250083, + 0x06270078, 0x062A0083, 0x062B0078, 0x06300071, 0x06310078, 0x0633017E, 0x0634017F, 0x06350180, + 0x06360181, 0x06370182, 0x06380078, 0x064100A7, 0x06420071, 0x06460078, 0x06470071, 0x06480078, + 0x06490071, 0x06540078, 0x06550071, 0x065D0078, 0x065E0071, 0x065F00B2, 0x066000A7, 0x06620078, + 0x066308A7, 0x06640078, 0x066508A7, 0x06660083, 0x06670078, 0x066A00A7, 0x066B0078, 0x06700071, + 0x06710078, 0x0673017E, 0x0674017F, 0x06750180, 0x06760181, 0x06770182, 0x06780078, 0x068100A7, + 0x06820071, 0x06860078, 0x06870071, 0x06880078, 0x06890071, 0x06940078, 0x06950071, 0x069D0078, + 0x069F00A7, 0x06A00083, 0x06A20078, 0x06A300A7, 0x06A40078, 0x06A508A7, 0x06A60083, 0x06A70078, + 0x06AB00A7, 0x06AC0078, 0x06B00071, 0x06B10078, 0x06B3017E, 0x06B4017F, 0x06B50180, 0x06B60181, + 0x06B70182, 0x06B80078, 0x06C100A7, 0x06C20071, 0x06CB0078, 0x06CD0071, 0x06DF0078, 0x06E00071, + 0x06E30078, 0x06E700A7, 0x06E90083, 0x06EA0078, 0x06EC00A7, 0x06EE08A7, 0x06EF00A7, 0x06F00078, + 0x06F900A7, 0x06FA0078, 0x07000071, 0x07180083, 0x07191071, 0x071A0083, 0x071D0078, 0x071F0008, + 0x07200071, 0x07230083, 0x07270097, 0x0728017E, 0x0729017F, 0x072A0180, 0x072B0181, 0x072C0182, + 0x072D0097, 0x072E0078, 0x07400071, 0x07410078, 0x07430071, 0x07440078, 0x07460071, 0x07470078, + 0x074A0071, 0x07540078, 0x07550071, 0x07580083, 0x07591071, 0x075A0083, 0x075E0071, 0x075F0078, + 0x07600071, 0x07620078, 0x07640083, 0x07670078, 0x0768017E, 0x0769017F, 0x076A0180, 0x076B0181, + 0x076C0182, 0x076D0078, 0x076E1071, 0x076F0078, 0x07800094, 0x07820097, 0x07890094, 0x078C0083, + 0x078D0094, 0x0790017E, 0x0791017F, 0x07920180, 0x07930181, 0x07940182, 0x079500B3, 0x079A0083, + 0x079D00BF, 0x079F00A7, 0x07A00071, 0x07A10871, 0x07A20071, 0x07A60871, 0x07A70071, 0x07AB0871, + 0x07AC0071, 0x07B40871, 0x07B50078, 0x07B80083, 0x07B90883, 0x07BB1083, 0x07BD0083, 0x07BF00A7, + 0x07C00883, 0x07C10083, 0x07C20097, 0x07C30083, 0x07C40071, 0x07C60078, 0x07C80083, 0x07C90883, + 0x07CA0083, 0x07CE0883, 0x07CF0083, 0x07D30883, 0x07D40083, 0x07DC0883, 0x07DD0083, 0x07DE0078, + 0x07DF0094, 0x07E60078, 0x07E70094, 0x07E80097, 0x07E90078, 0x08000071, 0x08150078, 0x08160083, + 0x081800A7, 0x08190078, 0x081B0083, 0x081D0078, 0x0820017E, 0x0821017F, 0x08220180, 0x08230181, + 0x08240182, 0x08250097, 0x08280071, 0x082B00A7, 0x082C0083, 0x082D0078, 0x085000B5, 0x08630078, + 0x08680071, 0x087D0097, 0x087E0078, 0x08800071, 0x08AD0078, 0x08AF0071, 0x08D10078, 0x08D40071, + 0x08FD0078, 0x09000071, 0x09240078, 0x09250071, 0x09270078, 0x09280071, 0x092B0078, 0x092D0071, + 0x092F0078, 0x09300071, 0x09440078, 0x09450071, 0x09470078, 0x09480071, 0x09580078, 0x09590071, + 0x095B0078, 0x095C0071, 0x095F0078, 0x09610071, 0x09630078, 0x09640071, 0x096B0078, 0x096C0071, + 0x09880078, 0x09890071, 0x098B0078, 0x098C0071, 0x09AD0078, 0x09AF0083, 0x09B00097, 0x09B400AD, + 0x09B500AE, 0x09B6012D, 0x09B7012E, 0x09B8012F, 0x09B90185, 0x09BA0186, 0x09BB0187, 0x09BC0188, + 0x09BD0184, 0x09BE0078, 0x09C00071, 0x09C80054, 0x09CD0078, 0x09D00071, 0x09FA0078, 0x0A000071, + 0x0B360097, 0x0B370071, 0x0B3B0078, 0x0B400071, 0x0B4D00B4, 0x0B4E0078, 0x0B500071, 0x0B750097, + 0x0B770189, 0x0B780078, 0x0B800071, 0x0B860078, 0x0B870071, 0x0B890083, 0x0B8A0078, 0x0B900071, + 0x0B990083, 0x0B9A0097, 0x0B9B0078, 0x0BA00071, 0x0BA90083, 0x0BAA0078, 0x0BB00071, 0x0BB60078, + 0x0BB70071, 0x0BB80078, 0x0BB90083, 0x0BBA0078, 0x0BC00071, 0x0BDA00C2, 0x0BDB0083, 0x0BDF00A7, + 0x0BE40083, 0x0BEA0097, 0x0BEB0081, 0x0BEC0097, 0x0BED0008, 0x0BEE0083, 0x0BEF0078, 0x0BF0017E, + 0x0BF1017F, 0x0BF20180, 0x0BF30181, 0x0BF40182, 0x0BF50078, 0x0BF80106, 0x0BF90107, 0x0BFA0108, + 0x0BFB0109, 0x0BFC010A, 0x0BFD0078, 0x0C000006, 0x0C050083, 0x0C070078, 0x0C08017E, 0x0C09017F, + 0x0C0A0180, 0x0C0B0181, 0x0C0C0182, 0x0C0D0078, 0x0C100071, 0x0C210081, 0x0C220071, 0x0C3C0078, + 0x0C400071, 0x0C540083, 0x0C550078, 0x0C800071, 0x0C8E0078, 0x0C900083, 0x0C9100A7, 0x0C930083, + 0x0C9400C8, 0x0C960078, 0x0C9800A7, 0x0C9C0083, 0x0C9E0078, 0x0CA20006, 0x0CA3017E, 0x0CA4017F, + 0x0CA50180, 0x0CA60181, 0x0CA70182, 0x0CA80071, 0x0CB70078, 0x0CB80071, 0x0CBA0078, 0x0CC00071, + 0x0CD50078, 0x0CD800A7, 0x0CE00071, 0x0CE400A7, 0x0CE50078, 0x0CE8017E, 0x0CE9017F, 0x0CEA0180, + 0x0CEB0181, 0x0CEC0182, 0x0CED0078, 0x0CEF0006, 0x0CF00054, 0x0D000071, 0x0D0B0083, 0x0D0C00A7, + 0x0D0E0078, 0x0D0F0097, 0x0D100078, 0x0E800055, 0x0E967881, 0x0E970081, 0x0E987881, 0x0E9D0081, + 0x0E9E7881, 0x0EB17055, 0x0EB50055, 0x0ECD7881, 0x0EE00083, 0x0EE20078, 0x0F000865, 0x0F4B0855, + 0x0F4D098A, 0x0F4E0078, 0x0F500865, 0x0F7D0078, 0x0F8008C9, 0x0F8408CA, 0x0F8808C9, 0x0F8B0078, + 0x0F8C08CA, 0x0F8F0078, 0x0F9008C9, 0x0F9408CA, 0x0F9808C9, 0x0F9C08CA, 0x0FA008C9, 0x0FA30078, + 0x0FA408CA, 0x0FA70078, 0x0FA808C9, 0x0FAC08CA, 0x0FB008C9, 0x0FB408CA, 0x0FB808CB, 0x0FB908CC, + 0x0FBB08CD, 0x0FBC08CE, 0x0FBD08CF, 0x0FBE08D0, 0x0FBF0078, 0x0FC008C9, 0x0FC408D1, 0x0FC808C9, + 0x0FCC08D1, 0x0FD008C9, 0x0FD408D1, 0x0FD808C9, 0x0FD9098B, 0x0FDA0078, 0x0FDB0855, 0x0FDC08CA, + 0x0FDD08D2, 0x0FDE1037, 0x0FE00837, 0x0FE1098B, 0x0FE20078, 0x0FE30855, 0x0FE408D5, 0x0FE60837, + 0x0FE808C9, 0x0FE90855, 0x0FEA0078, 0x0FEB0855, 0x0FEC08CA, 0x0FED08D6, 0x0FEE0837, 0x0FF008C9, + 0x0FF10855, 0x0FF20890, 0x0FF30855, 0x0FF408CA, 0x0FF508D7, 0x0FF60837, 0x0FF80078, 0x0FF9098B, + 0x0FFA0078, 0x0FFB0855, 0x0FFC08D9, 0x0FFD08DA, 0x0FFE0837, 0x0FFF0078, 0x10000805, 0x10011005, + 0x10035805, 0x10041005, 0x10050057, 0x1007018C, 0x10085899, 0x10090099, 0x100B1006, 0x100C018D, + 0x100D00DB, 0x100E018D, 0x100F00DB, 0x10100006, 0x10121006, 0x10130006, 0x1014018E, 0x1015018F, + 0x10160190, 0x10175853, 0x10180007, 0x10191007, 0x101A0006, 0x101B1006, 0x101C0126, 0x101D0006, + 0x101F0038, 0x10200006, 0x10220009, 0x10231006, 0x10250006, 0x102B1006, 0x102C0006, 0x102F1005, + 0x10300057, 0x10320078, 0x10350057, 0x10387855, 0x10390078, 0x103A7910, 0x103B7911, 0x103C7912, + 0x103D780B, 0x103E7809, 0x103F7855, 0x1040705D, 0x1041705B, 0x10427110, 0x10437111, 0x10447112, + 0x1045700B, 0x10467009, 0x10470078, 0x10487081, 0x104A0078, 0x10500008, 0x105B0078, 0x10680083, + 0x106E0095, 0x10700083, 0x10710095, 0x10720083, 0x10760078, 0x10801054, 0x10831077, 0x10841054, + 0x10852877, 0x10872855, 0x10882877, 0x10892855, 0x108A2877, 0x108B0054, 0x108C2877, 0x108F0054, + 0x10901054, 0x10910054, 0x10950991, 0x10962877, 0x10972855, 0x10982877, 0x109A1071, 0x109C2855, + 0x109D1054, 0x109E2855, 0x109F2877, 0x10A00019, 0x10A22877, 0x10A32855, 0x10A50019, 0x10A60078, + 0x10A9305F, 0x10AF3106, 0x10B01192, 0x10B11193, 0x10B21194, 0x10B31195, 0x10B41196, 0x10B51197, + 0x10B61198, 0x10B71199, 0x10B8119A, 0x10B9119B, 0x10BA119C, 0x10BB119D, 0x10BC119E, 0x10BD119F, + 0x10BE11A0, 0x10BF11A1, 0x10C001A2, 0x10C101A3, 0x10C20078, 0x10C80019, 0x10CA0054, 0x10CD0819, + 0x10CE0054, 0x10D10019, 0x10D20054, 0x10E60854, 0x10E70819, 0x10E80054, 0x10FA0019, 0x110000E8, + 0x11020019, 0x110408FB, 0x110500FC, 0x11070019, 0x110800E8, 0x11090059, 0x110A01A4, 0x110B0019, + 0x110D00E8, 0x11110019, 0x111500E8, 0x111610E8, 0x111800E8, 0x111A0019, 0x111C00E8, 0x111E00FE, + 0x111F00E8, 0x112008E8, 0x112101A5, 0x112200E8, 0x112308E8, 0x112500E8, 0x11260019, 0x112900FE, + 0x112B0019, 0x112F00E8, 0x11300019, 0x113200FE, 0x11360819, 0x113708FE, 0x113900FE, 0x113A08FE, + 0x113B00FE, 0x113C08FE, 0x113D00FE, 0x114008FE, 0x114100FE, 0x114208FE, 0x114300FE, 0x114408FE, + 0x114500FE, 0x11460019, 0x114700FD, 0x11490019, 0x115100FE, 0x11520019, 0x115300E8, 0x115401A6, + 0x115608E8, 0x115800FE, 0x115C0019, 0x115F00E8, 0x11600019, 0x116400FD, 0x116601A7, 0x11670019, + 0x116800FE, 0x11690019, 0x116B00FE, 0x117008FE, 0x117200FE, 0x117508FE, 0x11770019, 0x117800FE, + 0x11790102, 0x117A00E8, 0x117B0103, 0x117C00E8, 0x117D0104, 0x117E0105, 0x117F00E8, 0x11800054, + 0x118400FE, 0x11860054, 0x119000E8, 0x11910054, 0x11940809, 0x11950054, 0x119B0094, 0x11BD0054, + 0x11CA0094, 0x11CB0054, 0x11CD0019, 0x11DA00BF, 0x11DB0054, 0x11EE0078, 0x12000054, 0x12130078, + 0x12200054, 0x12250078, 0x123018C4, 0x123118C5, 0x123218C6, 0x123318C7, 0x1234191F, 0x1235191A, + 0x1236191B, 0x1237191C, 0x1238191D, 0x1239191E, 0x123A10C4, 0x123B10C5, 0x123C10C6, 0x123D10C7, + 0x123E111F, 0x123F111A, 0x1240111B, 0x1241111C, 0x1242111D, 0x1243111E, 0x1244105A, 0x124510E3, + 0x124610E4, 0x124710E5, 0x124811A8, 0x124911A9, 0x124A11AA, 0x124B11AB, 0x124C11AC, 0x124D11AD, + 0x124E1094, 0x125B1918, 0x12681919, 0x1275010B, 0x1276010C, 0x1277010D, 0x1278010E, 0x1279010F, + 0x127A0106, 0x127B0107, 0x127C0108, 0x127D0109, 0x127E010A, 0x127F00C3, 0x12800054, 0x12DB0019, + 0x12DC0054, 0x12E00019, 0x12E10054, 0x12FC0019, 0x13000054, 0x13370019, 0x13380054, 0x134E0078, + 0x13500054, 0x13590078, 0x13800054, 0x13820078, 0x13830054, 0x13850078, 0x13860054, 0x13A90078, + 0x13AC0054, 0x13AF0078, 0x13B00054, 0x13B4000A, 0x13BB00C4, 0x13BC00C5, 0x13BD00C6, 0x13BE00C7, + 0x13BF011F, 0x13C000C4, 0x13C100C5, 0x13C200C6, 0x13C300C7, 0x13C4011F, 0x13C500C4, 0x13C600C5, + 0x13C700C6, 0x13C800C7, 0x13C9011F, 0x13CA0078, 0x13CC0054, 0x13DF0078, 0x13E00019, 0x13E100FD, + 0x13E20009, 0x13E30078, 0x13E80019, 0x13E900E8, 0x13EA00FD, 0x13EB0019, 0x13EE00FD, 0x13EF0019, + 0x13F100FE, 0x13F3000A, 0x13F60078, 0x13F80019, 0x14000094, 0x14800019, 0x14C10009, 0x14C601AE, + 0x14C701AF, 0x14C80009, 0x14CC0019, 0x14CD00E8, 0x14D80019, 0x14E000FE, 0x14E100E8, 0x14E200FE, + 0x14E30019, 0x14E400E8, 0x14E50019, 0x14E700FD, 0x14E90019, 0x14EA00FE, 0x14EB0019, 0x14EC000A, + 0x14EE0019, 0x14F000E8, 0x14F30019, 0x14F400E8, 0x14F50019, 0x14FA01B0, 0x14FB00E8, 0x14FC00FE, + 0x14FD0019, 0x14FE000A, 0x14FF0019, 0x150500E8, 0x150E0019, 0x150F00E8, 0x15110019, 0x151400E8, + 0x151500FD, 0x15170019, 0x151A00FE, 0x151B0019, 0x151E00FE, 0x151F0019, 0x152B00E8, 0x152C0019, + 0x153200FE, 0x15330019, 0x153500E8, 0x15380019, 0x153900E8, 0x153A1019, 0x153B0019, 0x153C00FD, + 0x153D00E8, 0x153E00FD, 0x154200E8, 0x154500FD, 0x154600E8, 0x154800FD, 0x154E00E8, 0x155000FD, + 0x155100E8, 0x15520019, 0x155300FE, 0x155700FD, 0x155800E8, 0x155900FD, 0x155A00E8, 0x155D00FD, + 0x156300E8, 0x156600FD, 0x156B0019, 0x157101B1, 0x15730019, 0x157600FE, 0x15770019, 0x157900E8, + 0x157A0019, 0x157B00FD, 0x157D00E8, 0x157F0019, 0x15800054, 0x158A0078, 0x16000096, 0x16170078, + 0x16180098, 0x162F0078, 0x16400065, 0x16720054, 0x16750078, 0x167C0006, 0x167E005F, 0x167F0006, + 0x16800125, 0x16930078, 0x16980071, 0x16B30078, 0x16B77881, 0x16B80078, 0x16C00071, 0x16CB0078, + 0x16D00071, 0x16D30078, 0x16D40071, 0x16D70078, 0x16D80071, 0x16DB0078, 0x16DC0071, 0x16DF0078, + 0x16E00071, 0x16E30078, 0x16E40071, 0x16E70078, 0x16E80071, 0x16EB0078, 0x16EC0071, 0x16EF0078, + 0x17000006, 0x170100E0, 0x17030006, 0x17040126, 0x17050006, 0x170600E0, 0x17070006, 0x170B0099, + 0x170C0078, 0x170E00E0, 0x170F0078, 0x17400054, 0x174F1054, 0x17500054, 0x17791054, 0x177A0078, + 0x17801054, 0x17EB0078, 0x17F80054, 0x17FE0078, 0x18000006, 0x18020081, 0x180301B2, 0x1804000A, + 0x18090054, 0x180A000A, 0x180E00B4, 0x180F00BF, 0x181001B3, 0x181101B4, 0x181201B5, 0x181301B6, + 0x181401B7, 0x18150083, 0x18180081, 0x181B0054, 0x181C11B8, 0x181D0081, 0x181E0006, 0x181F0054, + 0x18200071, 0x18320871, 0x18350071, 0x18380871, 0x183A0071, 0x183B0871, 0x183D0071, 0x183E0871, + 0x183F0071, 0x184B0078, 0x184C0083, 0x184D1037, 0x184E0081, 0x184F8071, 0x18500071, 0x18620871, + 0x18650071, 0x18680871, 0x186A0071, 0x186B0871, 0x186D0071, 0x186E0871, 0x186F0071, 0x187B0871, + 0x187D0006, 0x187E0081, 0x187F8071, 0x18800078, 0x18820071, 0x18960078, 0x18981071, 0x18C70078, + 0x18C80094, 0x18C978B6, 0x18CA78B7, 0x18CB7894, 0x18D00071, 0x18DC0078, 0x18E00054, 0x18E80078, + 0x18F80071, 0x19001094, 0x190E1054, 0x190F0078, 0x191010B6, 0x191110B7, 0x191210B8, 0x191310B9, + 0x191410B0, 0x19151094, 0x19220078, 0x192819B9, 0x192919BA, 0x192A19BB, 0x192B19BC, 0x192C19BD, + 0x192D19BE, 0x192E19BF, 0x192F19C0, 0x19301894, 0x193E1854, 0x193F0094, 0x194018B6, 0x194118B7, + 0x194218B8, 0x194318B9, 0x194418B0, 0x19451894, 0x195819C1, 0x195919C2, 0x195A19C3, 0x195B19C4, + 0x195C19C5, 0x195D19C6, 0x195E19C7, 0x195F19C8, 0x19601094, 0x19666854, 0x19681894, 0x197F0078, + 0x19806894, 0x19AC1094, 0x19B86894, 0x19BB6854, 0x19BD6894, 0x19EF6854, 0x19F01094, 0x19FF6854, + 0x1A000071, 0x26DB0078, 0x26E00054, 0x27000071, 0x4FDE0078, 0x50000071, 0x500A0081, 0x500B0071, + 0x52460078, 0x52480054, 0x52630078, 0x53800037, 0x538B0078, 0x54000071, 0x54050083, 0x54060071, + 0x541100A7, 0x54120083, 0x541300A7, 0x54140054, 0x54160078, 0x56000071, 0x6BD20078, 0x6C00013E, + 0x7000013F, 0x7C800871, 0x7D070071, 0x7D0A0871, 0x7D0F0071, 0x7D120871, 0x7D130071, 0x7D150871, + 0x7D170078, 0x7D180871, 0x7D350078, 0x7D380871, 0x7D6D0078, 0x7D801055, 0x7D830078, 0x7D891055, + 0x7D8C0078, 0x7D8E089B, 0x7D90289B, 0x7D94280B, 0x7D95089B, 0x7D9B0078, 0x7D9C089B, 0x7D9E0078, + 0x7DA0089B, 0x7DA20078, 0x7DA3089B, 0x7DA7109B, 0x7DA8209E, 0x7DAA489E, 0x7DAB209E, 0x7DAC489E, + 0x7DAD209E, 0x7DAE489E, 0x7DAF209E, 0x7DB0489E, 0x7DB1209E, 0x7DB2489E, 0x7DB3209E, 0x7DB4489E, + 0x7DB5209E, 0x7DB6489E, 0x7DB7209E, 0x7DB8489E, 0x7DB9209E, 0x7DBA489E, 0x7DBB209E, 0x7DBC489E, + 0x7DBD209E, 0x7DBE489E, 0x7DBF209E, 0x7DC0489E, 0x7DC1209E, 0x7DC8489E, 0x7DC9209E, 0x7DCA489E, + 0x7DCB209E, 0x7DCC489E, 0x7DCD209E, 0x7DCE489E, 0x7DCF209E, 0x7DD1489E, 0x7DD2209E, 0x7DD4489E, + 0x7DD5209E, 0x7DD6489E, 0x7DD7209E, 0x7DD90078, 0x7DE9409E, 0x7DEA389E, 0x7DEB409E, 0x7DEF209E, + 0x7DF3489E, 0x7DF5209E, 0x7DFC409E, 0x7DFD389E, 0x7DFE209E, 0x7DFF489E, 0x7E00409E, 0x7E32209E, + 0x7E4B389E, 0x7E6F489E, 0x7E7A409E, 0x7E88209E, 0x7E96389E, 0x7E9A489E, 0x7E9E409E, 0x7E9F00BF, + 0x7EA00078, 0x7EA8209E, 0x7EA9389E, 0x7EAD209E, 0x7EAE389E, 0x7EAF209E, 0x7EB0389E, 0x7EB3209E, + 0x7EB5389E, 0x7EB7209E, 0x7EB9389E, 0x7EBA209E, 0x7EBB389E, 0x7EBC209E, 0x7EBE389E, 0x7EBF209E, + 0x7EC1389E, 0x7EC2209E, 0x7EC4389E, 0x7EC5209E, 0x7EC6389E, 0x7EC80078, 0x7EC9389E, 0x7ECB209E, + 0x7ECE389E, 0x7ECF209E, 0x7EDA389E, 0x7EDB209E, 0x7EE1389E, 0x7EE3209E, 0x7EE40078, 0x7EF8409E, + 0x7EFE0054, 0x7EFF0078, 0x7F000083, 0x7F088006, 0x7F0B80B4, 0x7F0C8006, 0x7F0D0078, 0x7F100083, + 0x7F120078, 0x7F188099, 0x7F198038, 0x7F1A80B4, 0x7F220006, 0x7F2380B4, 0x7F241006, 0x7F261038, + 0x7F286006, 0x7F290078, 0x7F2A600C, 0x7F2B6006, 0x7F2C60B4, 0x7F2F6007, 0x7F306006, 0x7F31600D, + 0x7F326019, 0x7F330078, 0x7F346008, 0x7F356006, 0x7F360078, 0x7F38489E, 0x7F39009E, 0x7F3A0078, + 0x7F3B489E, 0x7F40409E, 0x7F45389E, 0x7F46409E, 0x7F48389E, 0x7F49409E, 0x7F4B389E, 0x7F4C409E, + 0x7F4D389E, 0x7F4E409E, 0x7F4F389E, 0x7F50409E, 0x7F51389E, 0x7F52409E, 0x7F53389E, 0x7F54409E, + 0x7F59389E, 0x7F5A409E, 0x7F5B389E, 0x7F5C409E, 0x7F5D389E, 0x7F5E409E, 0x7F5F389E, 0x7F60409E, + 0x7F61389E, 0x7F62409E, 0x7F63389E, 0x7F64409E, 0x7F65389E, 0x7F66409E, 0x7F67389E, 0x7F68409E, + 0x7F69389E, 0x7F6A409E, 0x7F6B389E, 0x7F6C409E, 0x7F6D389E, 0x7F6E409E, 0x7F6F389E, 0x7F70409E, + 0x7F71389E, 0x7F72409E, 0x7F73389E, 0x7F74409E, 0x7F75389E, 0x7F76409E, 0x7F79389E, 0x7F7A409E, + 0x7F7E0078, 0x7F7F0057, 0x7F808806, 0x7F818807, 0x7F838806, 0x7F84880A, 0x7F85880B, 0x7F86880D, + 0x7F87880C, 0x7F88880F, 0x7F898811, 0x7F8A8813, 0x7F8B8815, 0x7F8C8817, 0x7F8D8806, 0x7F8E8819, + 0x7F8F8806, 0x7F908860, 0x7F9D8835, 0x7F9E8836, 0x7F9F8838, 0x7FA08861, 0x7FAD8835, 0x7FAE8836, + 0x7FAF8809, 0x7FB05006, 0x7FB1500A, 0x7FB25006, 0x7FB35071, 0x7FCF5081, 0x7FD05071, 0x7FDF0078, + 0x7FE15071, 0x7FE40078, 0x7FE55071, 0x7FE80078, 0x7FE95071, 0x7FEC0078, 0x7FED5071, 0x7FEE0078, + 0x7FF08808, 0x7FF18837, 0x7FF28808, 0x7FF30078, 0x7FF45019, 0x7FF65054, 0x7FF70078, 0x7FFC0141, + 0x7FFE0054, 0x7FFF0078, 0x80000071, 0x80130078, 0x80140071, 0x801D0078, 0x801E0071, 0x80270078, + 0x80280071, 0x802F0078, 0x80400071, 0x807D0078, 0x80800006, 0x80810078, 0x808300AD, 0x808400AE, + 0x8085012D, 0x8086012E, 0x8087012F, 0x80880185, 0x80890186, 0x808A0187, 0x808B0188, 0x808C0184, + 0x808D01C9, 0x808E01CA, 0x808F01CB, 0x809001CC, 0x809101CD, 0x809201CE, 0x809301CF, 0x809401D0, + 0x809500BE, 0x809601D1, 0x809701D2, 0x809801D3, 0x809901D4, 0x809A0078, 0x809B0094, 0x80A0014E, + 0x80A10152, 0x80A20153, 0x80A30157, 0x80A40154, 0x80A50155, 0x80A60156, 0x80A70152, 0x80A80150, + 0x80A90153, 0x80AA01D5, 0x80AB0154, 0x80AC014F, 0x80AD0158, 0x80AF0152, 0x80B00154, 0x80B201D6, + 0x80B30150, 0x80B501D7, 0x80B60153, 0x80B80156, 0x80B90152, 0x80BA005F, 0x80BC0054, 0x80C50078, + 0x81800071, 0x818F0078, 0x8190012D, 0x819100BB, 0x81920078, 0x81980071, 0x81A50078, 0x81C00071, + 0x81CF0097, 0x81D00071, 0x81E20078, 0x81E40071, 0x81E8014F, 0x81E90154, 0x81EA0155, 0x81EB0078, + 0x8200015B, 0x8214015C, 0x82280071, 0x824F0078, 0x8250017E, 0x8251017F, 0x82520180, 0x82530181, + 0x82540182, 0x82550078, 0x8400009B, 0x84030078, 0x8405009B, 0x841C0078, 0x841F009B, 0x84200078, + 0x85000083, 0x85030078, 0x85060083, 0x8508009B, 0x851A0078, 0x851C0083, 0x851D0078, 0x851F0083, + 0x852001D8, 0x852101D9, 0x852201DA, 0x852301DB, 0x85240078, 0x8528009A, 0x852C0078, 0xE8000094, + 0xE87B0078, 0xE8800094, 0xE8930078, 0xE8950094, 0xE8AF0894, 0xE8B200A7, 0xE8B30083, 0xE8B50094, + 0xE8B600A7, 0xE8B90057, 0xE8BD0083, 0xE8C10094, 0xE8C20083, 0xE8C60094, 0xE8D50083, 0xE8D70094, + 0xE8DD0894, 0xE8E00094, 0xE8EF0078, 0xE9000054, 0xE9210083, 0xE9220054, 0xE9230078, 0xE9800054, + 0xE9AB0078, 0xEA002877, 0xEA0D2855, 0xEA1A2877, 0xEA272855, 0xEA2A0078, 0xEA2B2855, 0xEA342877, + 0xEA412855, 0xEA4E0078, 0xEA4F2877, 0xEA500078, 0xEA522877, 0xEA530078, 0xEA542877, 0xEA560078, + 0xEA572877, 0xEA5B2855, 0xEA682877, 0xEA752855, 0xEA822877, 0xEA850078, 0xEA862877, 0xEA8A0078, + 0xEA8B2877, 0xEA8E0078, 0xEA8F2855, 0xEA9C2877, 0xEA9F0078, 0xEAA02877, 0xEAA20078, 0xEAA52877, + 0xEAA80078, 0xEAA92855, 0xEAB62877, 0xEAC32855, 0xEAD02877, 0xEADD2855, 0xEAEA2877, 0xEAF72855, + 0xEB042877, 0xEB112855, 0xEB1E2877, 0xEB2B2855, 0xEB382877, 0xEB452855, 0xEB530078, 0xEB542877, + 0xEB6029DC, 0xEB612855, 0xEB6D29DC, 0xEB6E2855, 0xEB712877, 0xEB7D29DC, 0xEB7E2855, 0xEB8A29DC, + 0xEB8B2855, 0xEB8E2877, 0xEB9A29DC, 0xEB9B2855, 0xEBA729DC, 0xEBA82855, 0xEBAB2877, 0xEBB729DC, + 0xEBB82855, 0xEBC429DC, 0xEBC52855, 0xEBC82877, 0xEBD429DC, 0xEBD52855, 0xEBE129DC, 0xEBE22855, + 0xEBE50078, 0xEBE7280F, 0xEBE82811, 0xEBE92813, 0xEBEA2815, 0xEBEB2817, 0xEBEC280F, 0xEBED2811, + 0xEBEE2813, 0xEBEF2815, 0xEBF02817, 0xEBF1280F, 0xEBF22811, 0xEBF32813, 0xEBF42815, 0xEBF52817, + 0xEBF6280F, 0xEBF72811, 0xEBF82813, 0xEBF92815, 0xEBFA2817, 0xEBFB280F, 0xEBFC2811, 0xEBFD2813, + 0xEBFE2815, 0xEBFF2817, 0xEC000078 + }; + + static const uint32_t a17[] = { + 0x00000071, 0x536B0078, 0x7C000871, 0x7D0F0078 + }; + + static const uint32_t a23[] = { + 0x00000057, 0x00010078, 0x00100057, 0x00400078, 0x00800083, 0x00F80078, 0x8000013F, 0xFFFF0078 + }; + + static const uint32_t a24[] = { + 0x0000013F, 0x7FFF0078 + }; + + + // The full set of all arrays to be searched. + static const Range FULL_DATA[] = { + {sizeof(a0)/sizeof(uint32_t), a0}, + {sizeof(a1)/sizeof(uint32_t), a1}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {sizeof(a7)/sizeof(uint32_t), a7}, + {sizeof(a8)/sizeof(uint32_t), a8}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {sizeof(a16)/sizeof(uint32_t), a16}, + {sizeof(a17)/sizeof(uint32_t), a17}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {sizeof(a23)/sizeof(uint32_t), a23}, + {sizeof(a24)/sizeof(uint32_t), a24}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0} + }; + + // Array of uppercase differences + static const short UCDIFF[] = { + 0, -32, 743, 121, -1, -232, -300, 97, + 163, 130, 56, -2, -79, -210, -206, -205, + -202, -203, -207, -209, -211, -213, -214, -218, + -217, -219, -83, 84, -38, -37, -31, -64, + -63, -62, -57, -47, -54, -86, -80, 7, + -96, -48, -59, 8, 74, 86, 100, 128, + 112, 126, 9, -7205, -16, -26, -7264, -40 + }; + + // Array of lowercase differences + static const short LCDIFF[] = { + 0, 32, 1, -199, -121, 210, 206, 205, + 79, 202, 203, 207, 211, 209, 213, 214, + 218, 217, 219, 2, -97, -56, -130, -163, + 83, 38, 37, 64, 63, -60, -7, 80, + 48, 7264, -8, -74, -9, -86, -100, -112, + -128, -126, -7517, -8383, -8262, 16, 26, 40 + }; + + // Array of titlecase differences + static const short TCDIFF[] = { + 3, 1, 0, -1 + }; + + // Array of mirrored character differences + static const short MIRROR_DIFF[] = { + 0, 1, -1, 2, -2, 16, -16, 3, + -3, 2016, 138, 1824, 2104, 2108, 2106, -138, + 8, 7, -8, -7, -1824, -2016, -2104, -2106, + -2108 + }; + + // Array of all possible numeric values + static const int NUMERICS[] = { + -1, 0, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 33, 34, 35, -2, 100, 1000, + 40, 50, 60, 70, 80, 90, 10000, 500, + 5000, 36, 37, 38, 39, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 200, 300, + 400, 600, 700, 800, 900, 2000, 3000, 4000, + 6000, 7000, 8000, 9000, 20000, 30000, 40000, 50000, + 60000, 70000, 80000, 90000 + }; + + // All possible packed data values, no duplicates + static const uint32_t PACKED_DATA[] = { + 0x00000000, 0x0000012F, 0x0000016F, 0x0000014F, 0x0000018F, 0x0000018C, 0x000001B8, 0x000000B8, + 0x000000BA, 0x020005B5, 0x040005B6, 0x00000099, 0x000000F8, 0x00000094, 0x02000069, 0x04000069, + 0x06000069, 0x08000069, 0x0A000069, 0x0C000069, 0x0E000069, 0x10000069, 0x12000069, 0x14000069, + 0x060005B9, 0x000001B9, 0x080005B9, 0x16020001, 0x18020001, 0x1A020001, 0x1C020001, 0x1E020001, + 0x20020001, 0x22020001, 0x24020001, 0x26020001, 0x28020001, 0x2A020001, 0x2C020001, 0x2E020001, + 0x30020001, 0x32020001, 0x34020001, 0x36020001, 0x38020001, 0x3A020001, 0x3C020001, 0x3E020001, + 0x40020001, 0x42020001, 0x44020001, 0x46020001, 0x48020001, 0x060005B5, 0x080005B6, 0x000001BB, + 0x000001B7, 0x16000802, 0x18000802, 0x1A000802, 0x1C000802, 0x1E000802, 0x20000802, 0x22000802, + 0x24000802, 0x26000802, 0x28000802, 0x2A000802, 0x2C000802, 0x2E000802, 0x30000802, 0x32000802, + 0x34000802, 0x36000802, 0x38000802, 0x3A000802, 0x3C000802, 0x3E000802, 0x40000802, 0x42000802, + 0x44000802, 0x46000802, 0x48000802, 0x000000EC, 0x000001BC, 0x00000002, 0x0A0005BD, 0x00000130, + 0x000000BC, 0x000000B9, 0x0600006B, 0x0800006B, 0x00001002, 0x0400006B, 0x0C0005BE, 0x4A0001AB, + 0x00020001, 0x00000802, 0x00001802, 0x00040001, 0x00060001, 0x00002002, 0x00080001, 0x000C0001, + 0x000E0001, 0x00100001, 0x00140001, 0x00160001, 0x00180001, 0x00004002, 0x00004802, 0x00200001, + 0x00220001, 0x00000005, 0x00A60001, 0x01805802, 0x01042003, 0x00280001, 0x002C0001, 0x00000001, + 0x00000000, 0x00007002, 0x00007802, 0x00009802, 0x0000A802, 0x0000B802, 0x0000C002, 0x0000C802, + 0x0000D002, 0x00000004, 0x000001A4, 0x00000106, 0x00320001, 0x00340001, 0x00360001, 0x00380001, + 0x0000E002, 0x0000E802, 0x0000F002, 0x0000F802, 0x00010002, 0x00010802, 0x00012002, 0x00012802, + 0x00013802, 0x003A0001, 0x003E0001, 0x00013002, 0x0000001C, 0x00000107, 0x00400001, 0x00000018, + 0x00014802, 0x000001B4, 0x00000038, 0x00000025, 0x00000050, 0x00000058, 0x00000045, 0x00000044, + 0x020000C9, 0x060000C9, 0x0A0000C9, 0x0E0000C9, 0x120000C9, 0x000000D8, 0x0000005C, 0x00000008, + 0x02000009, 0x06000009, 0x0A000009, 0x0E000009, 0x12000009, 0x0400000B, 0x0800000B, 0x0000000B, + 0x1600000B, 0x4E00000B, 0x00000006, 0x4A00000B, 0x000001B5, 0x00420001, 0x0600000B, 0x0A00000B, + 0x0E00000B, 0x1200000B, 0x3E00000B, 0x5200000B, 0x5600000B, 0x5A00000B, 0x5C00000B, 0x000001B6, + 0x2400000A, 0x2800000A, 0x00000010, 0x020001AB, 0x060001AB, 0x0A0001AB, 0x0E0001AB, 0x120001AB, + 0x00000108, 0x00015802, 0x00440001, 0x00016002, 0x00016802, 0x00017002, 0x00017802, 0x00018002, + 0x00018802, 0x00440003, 0x00460001, 0x00480003, 0x00019802, 0x004A0001, 0x004C0001, 0x004E0001, + 0x003C0001, 0x00500001, 0x00520001, 0x000001BD, 0x0000018D, 0x000001D0, 0x00000250, 0x00000230, + 0x040005BE, 0x000000F9, 0x0200006B, 0x0A00006B, 0x0E00006B, 0x1200006B, 0x00540001, 0x00560001, + 0x000005B9, 0x045A000A, 0x085A000A, 0x0C5A000A, 0x105A000A, 0x145A000A, 0x185A000A, 0x525A000A, + 0x5E5A000A, 0x0401A00A, 0x0801A00A, 0x0C01A00A, 0x1001A00A, 0x1401A00A, 0x1801A00A, 0x5201A00A, + 0x5E01A00A, 0x4E00000A, 0x5C00000A, 0x0E0005B9, 0x100005B9, 0x020005B9, 0x040005B9, 0x160005B9, + 0x180005B9, 0x1A0005B9, 0x200005B9, 0x220005B9, 0x240005B9, 0x260005B9, 0x040001AB, 0x080001AB, + 0x0C0001AB, 0x100001AB, 0x140001AB, 0x180001AB, 0x1C0001AB, 0x200001AB, 0x240001AB, 0x280001AB, + 0x0C00006B, 0x1000006B, 0x1400006B, 0x1800006B, 0x1C00006B, 0x2000006B, 0x2400006B, 0x2800006B, + 0x005C001C, 0x0001A81C, 0x1A0001AB, 0x1E0001AB, 0x220001AB, 0x260001AB, 0x2A0001AB, 0x160001AB, + 0x020005B6, 0x100005B6, 0x280005B9, 0x2C0005B9, 0x300005B9, 0x0001B002, 0x020005BD, 0x0600000A, + 0x0A00000A, 0x0E00000A, 0x1200000A, 0x1600000A, 0x3E00000A, 0x0C00000B, 0x1000000B, 0x1400000B, + 0x2E0001AB, 0x320001AB, 0x360001AB, 0x3A0001AB, 0x3E0001AB, 0x420001AB, 0x460001AB, 0x640001AB, + 0x680001AB, 0x6A0001AB, 0x6E0001AB, 0x720001AB, 0x760001AB, 0x7A0001AB, 0x00000013, 0x00000012, + 0x0000005A, 0x000001B0, 0x7C00000B, 0x8000000B, 0x8200000B, 0x8600000B, 0x8C00000B, 0x6000000B, + 0x9200000B, 0x9600000B, 0x9800000B, 0x9C00000B, 0xA000000B, 0xA400000B, 0x4A0001AA, 0x040001AA, + 0x520001AA, 0x600001AA, 0x0C0001AA, 0x5E0001AA, 0x160001AA, 0x4C0001AA, 0x4E0001AA, 0x9E0001AA, + 0x060001AA, 0x8800000A, 0x2A0001AA, 0x005E0001, 0x0001B802, 0x0400002B, 0x0800002B, 0x1600002B, + 0x4C00002B, 0x00002802, 0x00003002, 0x000A0001, 0x00120001, 0x00003802, 0x001A0001, 0x001C0001, + 0x001E0001, 0x00240001, 0x00005002, 0x00006002, 0x002A0001, 0x002E0001, 0x00300001, 0x00006802, + 0x00008002, 0x00008802, 0x00009002, 0x0000A002, 0x0000B002, 0x0000D906, 0x00011002, 0x00011802, + 0x00014002, 0x040000C9, 0x080000C9, 0x0C0000C9, 0x100000C9, 0x140000C9, 0x04000009, 0x08000009, + 0x0C000009, 0x10000009, 0x14000009, 0x2200000B, 0x4C00000B, 0x2A00000B, 0x5000000B, 0x5400000B, + 0x5800000B, 0x2600000A, 0x00015002, 0x00019002, 0x00000030, 0x000001BE, 0x0000014E, 0x00000210, + 0x000001F0, 0x00580001, 0x065A000A, 0x0A5A000A, 0x0E5A000A, 0x125A000A, 0x165A000A, 0x1A5A000A, + 0x4C5A000A, 0x4E5A000A, 0x0601A00A, 0x0A01A00A, 0x0E01A00A, 0x1201A00A, 0x1601A00A, 0x1A01A00A, + 0x4C01A00A, 0x4E01A00A, 0x6000000A, 0x0000000A, 0x120005B9, 0x140005B9, 0x1C0005B9, 0x1E0005B9, + 0x1600006B, 0x1A00006B, 0x1E00006B, 0x2200006B, 0x2600006B, 0x2A00006B, 0x0E0005B5, 0x040005B5, + 0x2A0005B9, 0x2E0005B9, 0x0200000A, 0x0400000A, 0x0800000A, 0x0C00000A, 0x1000000A, 0x1400000A, + 0x2A00000A, 0x2C0001AB, 0x300001AB, 0x340001AB, 0x380001AB, 0x3C0001AB, 0x400001AB, 0x440001AB, + 0x480001AB, 0x620001AB, 0x660001AB, 0x500001AB, 0x6C0001AB, 0x700001AB, 0x740001AB, 0x780001AB, + 0x520001AB, 0x7E00000B, 0x5E00000B, 0x8400000B, 0x8800000B, 0x8A00000B, 0x8E00000B, 0x9000000B, + 0x9400000B, 0x9A00000B, 0x9E00000B, 0xA200000B, 0xA600000B, 0x5C0001AA, 0x3E0001AA, 0x7E0001AA, + 0x0600002B, 0x0A00002B, 0x2A00002B, 0x4E00002B, 0x00000019 + }; +} diff --git a/libs/utils/executablepath_darwin.cpp b/libs/utils/executablepath_darwin.cpp new file mode 100644 index 000000000..2e3c3a01f --- /dev/null +++ b/libs/utils/executablepath_darwin.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#import +#include + +void executablepath(char s[PATH_MAX]) +{ + ProcessSerialNumber psn; + GetCurrentProcess(&psn); + CFDictionaryRef dict; + dict = ProcessInformationCopyDictionary(&psn, 0xffffffff); + CFStringRef value = (CFStringRef)CFDictionaryGetValue(dict, + CFSTR("CFBundleExecutable")); + CFStringGetCString(value, s, PATH_MAX+1, kCFStringEncodingUTF8); +} + diff --git a/libs/utils/executablepath_linux.cpp b/libs/utils/executablepath_linux.cpp new file mode 100644 index 000000000..b8d2a3d6f --- /dev/null +++ b/libs/utils/executablepath_linux.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +void executablepath(char exe[PATH_MAX]) +{ + char proc[100]; + sprintf(proc, "/proc/%d/exe", getpid()); + + int err = readlink(proc, exe, PATH_MAX); +} + diff --git a/libs/utils/futex_synchro.c b/libs/utils/futex_synchro.c new file mode 100644 index 000000000..ba1952033 --- /dev/null +++ b/libs/utils/futex_synchro.c @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include + +#include + +#include + + +// This futex glue code is need on desktop linux, but is already part of bionic. +#if !defined(HAVE_FUTEX_WRAPPERS) + +#include +typedef unsigned int u32; +#define asmlinkage +#define __user +#include +#include + + +int futex (int *uaddr, int op, int val, const struct timespec *timeout, int *uaddr2, int val3) +{ + int err = syscall(SYS_futex, uaddr, op, val, timeout, uaddr2, val3); + return err == 0 ? 0 : -errno; +} + +int __futex_wait(volatile void *ftx, int val, const struct timespec *timeout) +{ + return futex((int*)ftx, FUTEX_WAIT, val, timeout, NULL, 0); +} + +int __futex_wake(volatile void *ftx, int count) +{ + return futex((int*)ftx, FUTEX_WAKE, count, NULL, NULL, 0); +} + +int __atomic_cmpxchg(int old, int _new, volatile int *ptr) +{ + return android_atomic_cmpxchg(old, _new, ptr); +} + +int __atomic_swap(int _new, volatile int *ptr) +{ + return android_atomic_swap(_new, ptr); +} + +int __atomic_dec(volatile int *ptr) +{ + return android_atomic_dec(ptr); +} + +#else // !defined(__arm__) + +int __futex_wait(volatile void *ftx, int val, const struct timespec *timeout); +int __futex_wake(volatile void *ftx, int count); + +int __atomic_cmpxchg(int old, int _new, volatile int *ptr); +int __atomic_swap(int _new, volatile int *ptr); +int __atomic_dec(volatile int *ptr); + +#endif // !defined(HAVE_FUTEX_WRAPPERS) + + +// lock states +// +// 0: unlocked +// 1: locked, no waiters +// 2: locked, maybe waiters + +void futex_mutex_init(futex_mutex_t *m) +{ + m->value = 0; +} + +int futex_mutex_lock(futex_mutex_t *m, unsigned msec) +{ + if(__atomic_cmpxchg(0, 1, &m->value) == 0) { + return 0; + } + if(msec == FUTEX_WAIT_INFINITE) { + while(__atomic_swap(2, &m->value) != 0) { + __futex_wait(&m->value, 2, 0); + } + } else { + struct timespec ts; + ts.tv_sec = msec / 1000; + ts.tv_nsec = (msec % 1000) * 1000000; + while(__atomic_swap(2, &m->value) != 0) { + if(__futex_wait(&m->value, 2, &ts) == -ETIMEDOUT) { + return -1; + } + } + } + return 0; +} + +int futex_mutex_trylock(futex_mutex_t *m) +{ + if(__atomic_cmpxchg(0, 1, &m->value) == 0) { + return 0; + } + return -1; +} + +void futex_mutex_unlock(futex_mutex_t *m) +{ + if(__atomic_dec(&m->value) != 1) { + m->value = 0; + __futex_wake(&m->value, 1); + } +} + +/* XXX *technically* there is a race condition that could allow + * XXX a signal to be missed. If thread A is preempted in _wait() + * XXX after unlocking the mutex and before waiting, and if other + * XXX threads call signal or broadcast UINT_MAX times (exactly), + * XXX before thread A is scheduled again and calls futex_wait(), + * XXX then the signal will be lost. + */ + +void futex_cond_init(futex_cond_t *c) +{ + c->value = 0; +} + +int futex_cond_wait(futex_cond_t *c, futex_mutex_t *m, unsigned msec) +{ + if(msec == FUTEX_WAIT_INFINITE){ + int oldvalue = c->value; + futex_mutex_unlock(m); + __futex_wait(&c->value, oldvalue, 0); + futex_mutex_lock(m, FUTEX_WAIT_INFINITE); + return 0; + } else { + int oldvalue = c->value; + struct timespec ts; + ts.tv_sec = msec / 1000; + ts.tv_nsec = (msec % 1000) * 1000000; + futex_mutex_unlock(m); + const int err = __futex_wait(&c->value, oldvalue, &ts); + futex_mutex_lock(m, FUTEX_WAIT_INFINITE); + return err; + } +} + +void futex_cond_signal(futex_cond_t *c) +{ + __atomic_dec(&c->value); + __futex_wake(&c->value, 1); +} + +void futex_cond_broadcast(futex_cond_t *c) +{ + __atomic_dec(&c->value); + __futex_wake(&c->value, INT_MAX); +} + diff --git a/libs/utils/misc.cpp b/libs/utils/misc.cpp new file mode 100644 index 000000000..dc89d15b6 --- /dev/null +++ b/libs/utils/misc.cpp @@ -0,0 +1,185 @@ +/* + * 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. + */ + +// +// Miscellaneous utility functions. +// +#include + +#include +#include +#include +#include +#include + +using namespace android; + +namespace android { + +/* + * Like strdup(), but uses C++ "new" operator instead of malloc. + */ +char* strdupNew(const char* str) +{ + char* newStr; + int len; + + if (str == NULL) + return NULL; + + len = strlen(str); + newStr = new char[len+1]; + memcpy(newStr, str, len+1); + + return newStr; +} + +/* + * Concatenate an argument vector. + */ +char* concatArgv(int argc, const char* const argv[]) +{ + char* newStr = NULL; + int len, totalLen, posn, idx; + + /* + * First, figure out the total length. + */ + totalLen = idx = 0; + while (1) { + if (idx == argc || argv[idx] == NULL) + break; + if (idx) + totalLen++; // leave a space between args + totalLen += strlen(argv[idx]); + idx++; + } + + /* + * Alloc the string. + */ + newStr = new char[totalLen +1]; + if (newStr == NULL) + return NULL; + + /* + * Finally, allocate the string and copy data over. + */ + idx = posn = 0; + while (1) { + if (idx == argc || argv[idx] == NULL) + break; + if (idx) + newStr[posn++] = ' '; + + len = strlen(argv[idx]); + memcpy(&newStr[posn], argv[idx], len); + posn += len; + + idx++; + } + + assert(posn == totalLen); + newStr[posn] = '\0'; + + return newStr; +} + +/* + * Count the #of args in an argument vector. Don't count the final NULL. + */ +int countArgv(const char* const argv[]) +{ + int count = 0; + + while (argv[count] != NULL) + count++; + + return count; +} + + +#include +/* + * Get a file's type. + */ +FileType getFileType(const char* fileName) +{ + struct stat sb; + + if (stat(fileName, &sb) < 0) { + if (errno == ENOENT || errno == ENOTDIR) + return kFileTypeNonexistent; + else { + fprintf(stderr, "getFileType got errno=%d on '%s'\n", + errno, fileName); + return kFileTypeUnknown; + } + } else { + if (S_ISREG(sb.st_mode)) + return kFileTypeRegular; + else if (S_ISDIR(sb.st_mode)) + return kFileTypeDirectory; + else if (S_ISCHR(sb.st_mode)) + return kFileTypeCharDev; + else if (S_ISBLK(sb.st_mode)) + return kFileTypeBlockDev; + else if (S_ISFIFO(sb.st_mode)) + return kFileTypeFifo; +#ifdef HAVE_SYMLINKS + else if (S_ISLNK(sb.st_mode)) + return kFileTypeSymlink; + else if (S_ISSOCK(sb.st_mode)) + return kFileTypeSocket; +#endif + else + return kFileTypeUnknown; + } +} + +/* + * Get a file's modification date. + */ +time_t getFileModDate(const char* fileName) +{ + struct stat sb; + + if (stat(fileName, &sb) < 0) + return (time_t) -1; + + return sb.st_mtime; +} + +/* + * Round up to the next highest power of 2. + * + * Found on http://graphics.stanford.edu/~seander/bithacks.html. + */ +unsigned int roundUpPower2(unsigned int val) +{ + val--; + val |= val >> 1; + val |= val >> 2; + val |= val >> 4; + val |= val >> 8; + val |= val >> 16; + val++; + + return val; +} + +}; // namespace android + diff --git a/libs/utils/ported.cpp b/libs/utils/ported.cpp new file mode 100644 index 000000000..656e46f09 --- /dev/null +++ b/libs/utils/ported.cpp @@ -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. + */ + +// +// Ports of standard functions that don't exist on a specific platform. +// +// Note these are NOT in the "android" namespace. +// +#include + +#if defined(NEED_GETTIMEOFDAY) || defined(NEED_USLEEP) +# include +# include +#endif + + +#if defined(NEED_GETTIMEOFDAY) +/* + * Replacement gettimeofday() for Windows environments (primarily MinGW). + * + * Ignores "tz". + */ +int gettimeofday(struct timeval* ptv, struct timezone* tz) +{ + long long nsTime; // time in 100ns units since Jan 1 1601 + FILETIME ft; + + if (tz != NULL) { + // oh well + } + + ::GetSystemTimeAsFileTime(&ft); + nsTime = (long long) ft.dwHighDateTime << 32 | + (long long) ft.dwLowDateTime; + // convert to time in usec since Jan 1 1970 + ptv->tv_usec = (long) ((nsTime / 10LL) % 1000000LL); + ptv->tv_sec = (long) ((nsTime - 116444736000000000LL) / 10000000LL); + + return 0; +} +#endif + +#if defined(NEED_USLEEP) +// +// Replacement usleep for Windows environments (primarily MinGW). +// +void usleep(unsigned long usec) +{ + // Win32 API function Sleep() takes milliseconds + ::Sleep((usec + 500) / 1000); +} +#endif + +#if 0 //defined(NEED_PIPE) +// +// Replacement pipe() command for MinGW +// +// The _O_NOINHERIT flag sets bInheritHandle to FALSE in the +// SecurityAttributes argument to CreatePipe(). This means the handles +// aren't inherited when a new process is created. The examples I've seen +// use it, possibly because there's a lot of junk going on behind the +// scenes. (I'm assuming "process" and "thread" are different here, so +// we should be okay spinning up a thread.) The recommended practice is +// to dup() the descriptor you want the child to have. +// +// It appears that unnamed pipes can't do non-blocking ("overlapped") I/O. +// You can't use select() either, since that only works on sockets. The +// Windows API calls that are useful here all operate on a HANDLE, not +// an integer file descriptor, and I don't think you can get there from +// here. The "named pipe" stuff is insane. +// +int pipe(int filedes[2]) +{ + return _pipe(filedes, 0, _O_BINARY | _O_NOINHERIT); +} +#endif + +#if defined(NEED_SETENV) +/* + * MinGW lacks these. For now, just stub them out so the code compiles. + */ +int setenv(const char* name, const char* value, int overwrite) +{ + return 0; +} +void unsetenv(const char* name) +{ +} +char* getenv(const char* name) +{ + return NULL; +} +#endif