summaryrefslogtreecommitdiffstats
path: root/libs/utils
diff options
context:
space:
mode:
Diffstat (limited to 'libs/utils')
-rw-r--r--libs/utils/Android.mk148
-rw-r--r--libs/utils/Asset.cpp813
-rw-r--r--libs/utils/AssetDir.cpp66
-rw-r--r--libs/utils/AssetManager.cpp1637
-rw-r--r--libs/utils/Binder.cpp242
-rw-r--r--libs/utils/BpBinder.cpp348
-rw-r--r--libs/utils/BufferedTextOutput.cpp279
-rw-r--r--libs/utils/CallStack.cpp335
-rw-r--r--libs/utils/Debug.cpp318
-rw-r--r--libs/utils/FileMap.cpp222
-rw-r--r--libs/utils/IDataConnection.cpp89
-rw-r--r--libs/utils/IInterface.cpp35
-rw-r--r--libs/utils/IMemory.cpp486
-rw-r--r--libs/utils/IPCThreadState.cpp1007
-rw-r--r--libs/utils/IPermissionController.cpp86
-rw-r--r--libs/utils/IServiceManager.cpp230
-rw-r--r--libs/utils/InetAddress.cpp236
-rw-r--r--libs/utils/LogSocket.cpp129
-rw-r--r--libs/utils/MODULE_LICENSE_APACHE20
-rw-r--r--libs/utils/MemoryBase.cpp46
-rw-r--r--libs/utils/MemoryDealer.cpp407
-rw-r--r--libs/utils/MemoryHeapBase.cpp178
-rw-r--r--libs/utils/MemoryHeapPmem.cpp226
-rw-r--r--libs/utils/NOTICE190
-rw-r--r--libs/utils/Parcel.cpp1311
-rw-r--r--libs/utils/Pipe.cpp465
-rw-r--r--libs/utils/ProcessState.cpp398
-rw-r--r--libs/utils/README14
-rw-r--r--libs/utils/RefBase.cpp534
-rw-r--r--libs/utils/ResourceTypes.cpp3969
-rw-r--r--libs/utils/SharedBuffer.cpp113
-rw-r--r--libs/utils/Socket.cpp388
-rw-r--r--libs/utils/Static.cpp120
-rw-r--r--libs/utils/StopWatch.cpp79
-rw-r--r--libs/utils/String16.cpp609
-rw-r--r--libs/utils/String8.cpp602
-rw-r--r--libs/utils/SystemClock.cpp139
-rw-r--r--libs/utils/TextOutput.cpp146
-rw-r--r--libs/utils/Threads.cpp1126
-rw-r--r--libs/utils/TimerProbe.cpp131
-rw-r--r--libs/utils/Timers.cpp240
-rw-r--r--libs/utils/Unicode.cpp193
-rw-r--r--libs/utils/VectorImpl.cpp611
-rw-r--r--libs/utils/ZipEntry.cpp696
-rw-r--r--libs/utils/ZipFile.cpp1296
-rw-r--r--libs/utils/ZipFileCRO.cpp54
-rw-r--r--libs/utils/ZipFileRO.cpp724
-rw-r--r--libs/utils/ZipUtils.cpp344
-rw-r--r--libs/utils/characterData.h730
-rw-r--r--libs/utils/executablepath_darwin.cpp31
-rw-r--r--libs/utils/executablepath_linux.cpp30
-rw-r--r--libs/utils/futex_synchro.c175
-rw-r--r--libs/utils/misc.cpp185
-rw-r--r--libs/utils/ported.cpp106
54 files changed, 23312 insertions, 0 deletions
diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk
new file mode 100644
index 0000000..4a68dc1
--- /dev/null
+++ b/libs/utils/Android.mk
@@ -0,0 +1,148 @@
+# 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
+
+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 0000000..91203dd
--- /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 <utils/Asset.h>
+#include <utils/Atomic.h>
+#include <utils/FileMap.h>
+#include <utils/ZipUtils.h>
+#include <utils/ZipFileRO.h>
+#include <utils/Log.h>
+
+#include <string.h>
+#include <memory.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <assert.h>
+
+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 0000000..c5f664e
--- /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 <utils/AssetDir.h>
+
+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<FileInfo>* 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 0000000..447b801
--- /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 <utils/AssetManager.h>
+#include <utils/AssetDir.h>
+#include <utils/Asset.h>
+#include <utils/Atomic.h>
+#include <utils/String8.h>
+#include <utils/ResourceTypes.h>
+#include <utils/String8.h>
+#include <utils/ZipFileRO.h>
+#include <utils/Log.h>
+#include <utils/Timers.h>
+#include <utils/threads.h>
+
+#include <dirent.h>
+#include <errno.h>
+#include <assert.h>
+
+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(); i++) {
+ if (mAssetPaths[i].path == ap.path) {
+ if (cookie) {
+ *cookie = (void*)(i+1);
+ }
+ return true;
+ }
+ }
+
+ LOGV("In %p Asset %s path: %s", this,
+ ap.type == kFileTypeDirectory ? "dir" : "zip", ap.path.string());
+
+ mAssetPaths.add(ap);
+
+ // new paths are always added at the end
+ if (cookie) {
+ *cookie = (void*)mAssetPaths.size();
+ }
+
+ return true;
+}
+
+bool AssetManager::addDefaultAssets()
+{
+ const char* root = getenv("ANDROID_ROOT");
+ LOG_ALWAYS_FATAL_IF(root == NULL, "ANDROID_ROOT not set");
+
+ String8 path(root);
+ path.appendPath(kSystemAssets);
+
+ return addAssetPath(path, NULL);
+}
+
+void* AssetManager::nextAssetPath(void* cookie) const
+{
+ AutoMutex _l(mLock);
+ size_t next = ((size_t)cookie)+1;
+ return next > 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<AssetManager*>(this)->loadFileNameCacheLocked();
+
+ const size_t N = mAssetPaths.size();
+ for (size_t i=0; i<N; i++) {
+ Asset* ass = NULL;
+ bool shared = true;
+ const asset_path& ap = mAssetPaths.itemAt(i);
+ LOGV("Looking for resource asset in '%s'\n", ap.path.string());
+ if (ap.type != kFileTypeDirectory) {
+ ass = const_cast<AssetManager*>(this)->
+ mZipSet.getZipResourceTable(ap.path);
+ if (ass == NULL) {
+ LOGV("loading resource table %s\n", ap.path.string());
+ ass = const_cast<AssetManager*>(this)->
+ openNonAssetInPathLocked("resources.arsc",
+ Asset::ACCESS_BUFFER,
+ ap);
+ if (ass != NULL && ass != kExcludedAsset) {
+ ass = const_cast<AssetManager*>(this)->
+ mZipSet.setZipResourceTable(ap.path, ass);
+ }
+ }
+ } else {
+ LOGV("loading resource table %s\n", ap.path.string());
+ Asset* ass = const_cast<AssetManager*>(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<String8>* 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<AssetDir::FileInfo>* 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<AssetDir::FileInfo>;
+
+ 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<AssetDir::FileInfo>* pMergedInfo,
+ const asset_path& ap, const char* rootDir, const char* dirName)
+{
+ SortedVector<AssetDir::FileInfo>* 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<AssetDir::FileInfo>;
+
+ /*
+ * 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<AssetDir::FileInfo>* AssetManager::scanDirLocked(const String8& path)
+{
+ SortedVector<AssetDir::FileInfo>* 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<AssetDir::FileInfo>;
+
+ 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<AssetDir::FileInfo>* pMergedInfo,
+ const asset_path& ap, const char* rootDir, const char* baseDirName)
+{
+ ZipFileRO* pZip;
+ Vector<String8> dirs;
+ AssetDir::FileInfo info;
+ SortedVector<AssetDir::FileInfo> 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<AssetDir::FileInfo>* pMergedInfo,
+ const SortedVector<AssetDir::FileInfo>* 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<AssetDir::FileInfo>* pNewSorted;
+ int mergeMax, contMax;
+ int mergeIdx, contIdx;
+
+ pNewSorted = new SortedVector<AssetDir::FileInfo>;
+ 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<AssetDir::FileInfo>* 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<AssetDir::FileInfo>* pMergedInfo,
+ const asset_path& ap, const char* locale, const char* vendor,
+ const char* dirName)
+{
+ SortedVector<AssetDir::FileInfo>* 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<String8, wp<AssetManager::SharedZip> > 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> AssetManager::SharedZip::get(const String8& path)
+{
+ AutoMutex _l(gLock);
+ time_t modWhen = getFileModDate(path);
+ sp<SharedZip> 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<SharedZip> 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<SharedZip> 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<SharedZip> 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; i<N; i++) {
+ if (mZipFile[i] != NULL && !mZipFile[i]->isUpToDate()) {
+ 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<N; i++) {
+ if (mZipPath[i] == zip) {
+ return i;
+ }
+ }
+
+ mZipPath.add(zip);
+ mZipFile.add(NULL);
+
+ return mZipPath.size()-1;
+}
+
diff --git a/libs/utils/Binder.cpp b/libs/utils/Binder.cpp
new file mode 100644
index 0000000..37e4685
--- /dev/null
+++ b/libs/utils/Binder.cpp
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <utils/Binder.h>
+
+#include <utils/Atomic.h>
+#include <utils/BpBinder.h>
+#include <utils/IInterface.h>
+#include <utils/Parcel.h>
+
+#include <stdio.h>
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+sp<IInterface> 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<DeathRecipient>& recipient, void* cookie, uint32_t flags)
+{
+ return INVALID_OPERATION;
+}
+
+status_t BBinder::unlinkToDeath(
+ const wp<DeathRecipient>& recipient, void* cookie, uint32_t flags,
+ wp<DeathRecipient>* outRecipient)
+{
+ return INVALID_OPERATION;
+}
+
+status_t BBinder::dump(int fd, const Vector<String16>& 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<int32_t>(e),
+ reinterpret_cast<volatile int32_t*>(&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<String16> 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<IBinder>& 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 0000000..69ab195
--- /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 <utils/BpBinder.h>
+
+#include <utils/IPCThreadState.h>
+#include <utils/Log.h>
+
+#include <stdio.h>
+
+//#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; i<N; i++) {
+ const entry_t& e = mObjects.valueAt(i);
+ if (e.func != NULL) {
+ e.func(mObjects.keyAt(i), e.object, e.cleanupCookie);
+ }
+ }
+
+ mObjects.clear();
+}
+
+// ---------------------------------------------------------------------------
+
+BpBinder::BpBinder(int32_t handle)
+ : mHandle(handle)
+ , mAlive(1)
+ , mObitsSent(0)
+ , mObituaries(NULL)
+{
+ LOGV("Creating BpBinder %p handle %d\n", this, mHandle);
+
+ extendObjectLifetime(OBJECT_LIFETIME_WEAK);
+ IPCThreadState::self()->incWeakHandle(handle);
+}
+
+String16 BpBinder::getInterfaceDescriptor() const
+{
+ String16 res;
+ Parcel send, reply;
+ status_t err = const_cast<BpBinder*>(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<String16>& 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<DeathRecipient>& 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<Obituary>;
+ 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<DeathRecipient>& recipient, void* cookie, uint32_t flags,
+ wp<DeathRecipient>* outRecipient)
+{
+ AutoMutex _l(mLock);
+
+ if (mObitsSent) {
+ return DEAD_OBJECT;
+ }
+
+ const size_t N = mObituaries ? mObituaries->size() : 0;
+ for (size_t i=0; i<N; i++) {
+ const Obituary& obit = mObituaries->itemAt(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<Obituary>* 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; i<N; i++) {
+ reportOneDeath(obits->itemAt(i));
+ }
+
+ delete obits;
+ }
+}
+
+void BpBinder::reportOneDeath(const Obituary& obit)
+{
+ sp<DeathRecipient> 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<Obituary>* 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 0000000..989662e
--- /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 <utils/BufferedTextOutput.h>
+
+#include <utils/Atomic.h>
+#include <utils/Debug.h>
+#include <utils/Log.h>
+#include <utils/RefBase.h>
+#include <utils/Vector.h>
+#include <cutils/threads.h>
+
+#include <private/utils/Static.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+// ---------------------------------------------------------------------------
+
+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<sp<BufferedTextOutput::BufferState> > 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 0000000..4968666
--- /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 <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#if HAVE_DLADDR
+#include <dlfcn.h>
+#endif
+
+#if HAVE_CXXABI
+#include <cxxabi.h>
+#endif
+
+#include <unwind.h>
+
+#include <utils/Log.h>
+#include <utils/Errors.h>
+#include <utils/CallStack.h>
+#include <utils/threads.h>
+
+
+/*****************************************************************************/
+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, uint32_t *offset, char* name, size_t bufSize)
+{
+#if HAVE_DLADDR
+ Dl_info info;
+ if (dladdr(addr, &info)) {
+ *offset = (uint32_t)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;
+ unsigned start;
+ unsigned end;
+ char name[];
+ };
+
+ const char *map_to_name(unsigned 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 = strtoul(line, 0, 16);
+ mi->end = strtoul(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((unsigned)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];
+ uint32_t 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%08x: <", (size_t)ip);
+ snprintf(tmp2, 32, ">+0x%08x", offs);
+ res.append(tmp1);
+ res.append(name);
+ res.append(tmp2);
+ } else {
+ name = MapInfo::mapAddressToName(ip, "<unknown>");
+ snprintf(tmp, 256, "pc %08x %s", (size_t)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<int(mCount); i++) {
+ LOGD("%s", toStringSingleLevel(prefix, i).string());
+ }
+}
+
+// Return a string (possibly very long) containing the complete stack trace
+String8 CallStack::toString(const char* prefix) const
+{
+ String8 res;
+
+ for (int i=0; i<int(mCount); i++) {
+ res.append(toStringSingleLevel(prefix, i).string());
+ }
+
+ return res;
+}
+
+/*****************************************************************************/
+
+}; // namespace android
diff --git a/libs/utils/Debug.cpp b/libs/utils/Debug.cpp
new file mode 100644
index 0000000..f7988ec
--- /dev/null
+++ b/libs/utils/Debug.cpp
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <utils/Debug.h>
+
+#include <utils/misc.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+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 0000000..e1ba9b2
--- /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 <utils/FileMap.h>
+#include <utils/Log.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_POSIX_FILEMAP
+#include <sys/mman.h>
+#endif
+
+#include <string.h>
+#include <memory.h>
+#include <errno.h>
+#include <assert.h>
+
+using namespace android;
+
+/*static*/ long FileMap::mPageSize = -1;
+
+
+/*
+ * Constructor. Create an empty object.
+ */
+FileMap::FileMap(void)
+ : mRefCount(1), mFileName(NULL), mBasePtr(NULL), mBaseLength(0),
+ mDataPtr(NULL), mDataLength(0)
+{
+}
+
+/*
+ * Destructor.
+ */
+FileMap::~FileMap(void)
+{
+ assert(mRefCount == 0);
+
+ //printf("+++ removing FileMap %p %u\n", mDataPtr, mDataLength);
+
+ mRefCount = -100; // help catch double-free
+ if (mFileName != NULL) {
+ free(mFileName);
+ }
+#ifdef HAVE_POSIX_FILEMAP
+ if (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 0000000..c6d49aa
--- /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 <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Parcel.h>
+
+#include <utils/IDataConnection.h>
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+enum
+{
+ CONNECT_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
+ DISCONNECT_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION + 1
+};
+
+class BpDataConnection : public BpInterface<IDataConnection>
+{
+public:
+ BpDataConnection::BpDataConnection(const sp<IBinder>& impl)
+ : BpInterface<IDataConnection>(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 0000000..6ea8178
--- /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 <utils/IInterface.h>
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+sp<IBinder> IInterface::asBinder()
+{
+ return this ? onAsBinder() : NULL;
+}
+
+sp<const IBinder> IInterface::asBinder() const
+{
+ return this ? const_cast<IInterface*>(this)->onAsBinder() : NULL;
+}
+
+// ---------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/libs/utils/IMemory.cpp b/libs/utils/IMemory.cpp
new file mode 100644
index 0000000..429bc2b
--- /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 <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#include <utils/IMemory.h>
+#include <utils/KeyedVector.h>
+#include <utils/threads.h>
+#include <utils/Atomic.h>
+#include <utils/Parcel.h>
+#include <utils/CallStack.h>
+
+#define VERBOSE 0
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+class HeapCache : public IBinder::DeathRecipient
+{
+public:
+ HeapCache();
+ virtual ~HeapCache();
+
+ virtual void binderDied(const wp<IBinder>& who);
+
+ sp<IMemoryHeap> find_heap(const sp<IBinder>& binder);
+ void pin_heap(const sp<IBinder>& binder);
+ void free_heap(const sp<IBinder>& binder);
+ sp<IMemoryHeap> get_heap(const sp<IBinder>& binder);
+ void dump_heaps();
+
+private:
+ // For IMemory.cpp
+ struct heap_info_t {
+ sp<IMemoryHeap> heap;
+ int32_t count;
+ };
+
+ void free_heap(const wp<IBinder>& binder);
+
+ Mutex mHeapCacheLock;
+ KeyedVector< wp<IBinder>, heap_info_t > mHeapCache;
+};
+
+static sp<HeapCache> gHeapCache = new HeapCache();
+
+/******************************************************************************/
+
+enum {
+ HEAP_ID = IBinder::FIRST_CALL_TRANSACTION
+};
+
+class BpMemoryHeap : public BpInterface<IMemoryHeap>
+{
+public:
+ BpMemoryHeap(const sp<IBinder>& 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<IMemoryHeap> find_heap(const sp<IBinder>& binder) {
+ return gHeapCache->find_heap(binder);
+ }
+ static inline void free_heap(const sp<IBinder>& binder) {
+ gHeapCache->free_heap(binder);
+ }
+ static inline sp<IMemoryHeap> get_heap(const sp<IBinder>& binder) {
+ return gHeapCache->get_heap(binder);
+ }
+ static inline void dump_heaps() {
+ gHeapCache->dump_heaps();
+ }
+ void inline pin_heap() const {
+ gHeapCache->pin_heap(const_cast<BpMemoryHeap*>(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<IMemory>
+{
+public:
+ BpMemory(const sp<IBinder>& impl);
+ virtual ~BpMemory();
+ virtual sp<IMemoryHeap> getMemory(ssize_t* offset=0, size_t* size=0) const;
+
+private:
+ mutable sp<IMemoryHeap> mHeap;
+ mutable ssize_t mOffset;
+ mutable size_t mSize;
+};
+
+/******************************************************************************/
+
+void* IMemory::fastPointer(const sp<IBinder>& binder, ssize_t offset) const
+{
+ sp<IMemoryHeap> realHeap = BpMemoryHeap::get_heap(binder);
+ void* const base = realHeap->base();
+ if (base == MAP_FAILED)
+ return 0;
+ return static_cast<char*>(base) + offset;
+}
+
+void* IMemory::pointer() const {
+ ssize_t offset;
+ sp<IMemoryHeap> heap = getMemory(&offset);
+ void* const base = heap!=0 ? heap->base() : MAP_FAILED;
+ if (base == MAP_FAILED)
+ return 0;
+ return static_cast<char*>(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<IBinder>& impl)
+ : BpInterface<IMemory>(impl), mOffset(0), mSize(0)
+{
+}
+
+BpMemory::~BpMemory()
+{
+}
+
+sp<IMemoryHeap> 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<IBinder> heap = reply.readStrongBinder();
+ ssize_t o = reply.readInt32();
+ size_t s = reply.readInt32();
+ if (heap != 0) {
+ mHeap = interface_cast<IMemoryHeap>(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<IBinder>& impl)
+ : BpInterface<IMemoryHeap>(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<IBinder> binder = const_cast<BpMemoryHeap*>(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<IBinder> binder = const_cast<BpMemoryHeap*>(this)->asBinder();
+ free_heap(binder);
+ }
+ }
+}
+
+void BpMemoryHeap::assertMapped() const
+{
+ if (mHeapId == -1) {
+ sp<IBinder> binder(const_cast<BpMemoryHeap*>(this)->asBinder());
+ sp<BpMemoryHeap> heap(static_cast<BpMemoryHeap*>(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<IBinder>& binder)
+{
+ //LOGD("binderDied binder=%p", binder.unsafe_get());
+ free_heap(binder);
+}
+
+sp<IMemoryHeap> HeapCache::find_heap(const sp<IBinder>& 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<BpMemoryHeap*>(info.heap.get())->mSize,
+ static_cast<BpMemoryHeap*>(info.heap.get())->mHeapId,
+ info.count);
+ android_atomic_inc(&info.count);
+ return info.heap;
+ } else {
+ heap_info_t info;
+ info.heap = interface_cast<IMemoryHeap>(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<IBinder>& 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<IBinder>& binder) {
+ free_heap( wp<IBinder>(binder) );
+}
+
+void HeapCache::free_heap(const wp<IBinder>& binder)
+{
+ sp<IMemoryHeap> 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<BpMemoryHeap*>(info.heap.get())->mSize,
+ static_cast<BpMemoryHeap*>(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<IMemoryHeap> HeapCache::get_heap(const sp<IBinder>& binder)
+{
+ sp<IMemoryHeap> realHeap;
+ Mutex::Autolock _l(mHeapCacheLock);
+ ssize_t i = mHeapCache.indexOfKey(binder);
+ if (i>=0) realHeap = mHeapCache.valueAt(i).heap;
+ else realHeap = interface_cast<IMemoryHeap>(binder);
+ return realHeap;
+}
+
+void HeapCache::dump_heaps()
+{
+ Mutex::Autolock _l(mHeapCacheLock);
+ int c = mHeapCache.size();
+ for (int i=0 ; i<c ; i++) {
+ const heap_info_t& info = mHeapCache.valueAt(i);
+ BpMemoryHeap const* h(static_cast<BpMemoryHeap const *>(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 0000000..ca49d9a
--- /dev/null
+++ b/libs/utils/IPCThreadState.cpp
@@ -0,0 +1,1007 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <utils/IPCThreadState.h>
+
+#include <utils/Binder.h>
+#include <utils/BpBinder.h>
+#include <utils/Debug.h>
+#include <utils/Log.h>
+#include <utils/TextOutput.h>
+#include <utils/threads.h>
+
+#include <private/utils/binder_module.h>
+#include <private/utils/Static.h>
+
+#include <sys/ioctl.h>
+#include <signal.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#ifdef HAVE_PTHREADS
+#include <pthread.h>
+#include <sched.h>
+#include <sys/resource.h>
+#endif
+#ifdef HAVE_WIN32_THREADS
+#include <windows.h>
+#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<ProcessState> 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;
+ 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<const uint8_t*>(tr.data.ptr.buffer),
+ tr.data_size,
+ reinterpret_cast<const size_t*>(tr.data.ptr.offsets),
+ tr.offsets_size/sizeof(size_t),
+ freeBuffer, this);
+ } else {
+ err = *static_cast<const status_t*>(tr.data.ptr.buffer);
+ freeBuffer(NULL,
+ reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
+ tr.data_size,
+ reinterpret_cast<const size_t*>(tr.data.ptr.offsets),
+ tr.offsets_size/sizeof(size_t), this);
+ }
+ } else {
+ freeBuffer(NULL,
+ reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
+ tr.data_size,
+ reinterpret_cast<const size_t*>(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<BBinder> the_context_object;
+
+void setTheContextObject(sp<BBinder> 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();
+ }
+ obj->decStrong(mProcess.get());
+ 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());
+ refs->decWeak(mProcess.get());
+ 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<const uint8_t*>(tr.data.ptr.buffer),
+ tr.data_size,
+ reinterpret_cast<const size_t*>(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<const uint8_t*>(tr.data.ptr.buffer)
+ << ", offsets addr="
+ << reinterpret_cast<const size_t*>(tr.data.ptr.offsets) << endl;
+ }
+ if (tr.target.ptr) {
+ sp<BBinder> 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<IPCThreadState*>(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 0000000..f01d38f
--- /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 <utils/IPermissionController.h>
+
+#include <utils/Debug.h>
+#include <utils/Log.h>
+#include <utils/Parcel.h>
+#include <utils/String8.h>
+
+#include <private/utils/Static.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------
+
+class BpPermissionController : public BpInterface<IPermissionController>
+{
+public:
+ BpPermissionController(const sp<IBinder>& impl)
+ : BpInterface<IPermissionController>(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 0000000..9beeadd
--- /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 <utils/IServiceManager.h>
+
+#include <utils/Debug.h>
+#include <utils/IPCThreadState.h>
+#include <utils/Log.h>
+#include <utils/Parcel.h>
+#include <utils/String8.h>
+#include <utils/SystemClock.h>
+
+#include <private/utils/Static.h>
+
+#include <unistd.h>
+
+namespace android {
+
+sp<IServiceManager> defaultServiceManager()
+{
+ if (gDefaultServiceManager != NULL) return gDefaultServiceManager;
+
+ {
+ AutoMutex _l(gDefaultServiceManagerLock);
+ if (gDefaultServiceManager == NULL) {
+ gDefaultServiceManager = interface_cast<IServiceManager>(
+ 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<IPermissionController> 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<IBinder> 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<IPermissionController>(binder);
+ // Install the new permission controller, and try again.
+ gDefaultServiceManagerLock.lock();
+ gPermissionController = pc;
+ gDefaultServiceManagerLock.unlock();
+ }
+ }
+}
+
+// ----------------------------------------------------------------------
+
+class BpServiceManager : public BpInterface<IServiceManager>
+{
+public:
+ BpServiceManager(const sp<IBinder>& impl)
+ : BpInterface<IServiceManager>(impl)
+ {
+ }
+
+ virtual sp<IBinder> getService(const String16& name) const
+ {
+ unsigned n;
+ for (n = 0; n < 5; n++){
+ sp<IBinder> svc = checkService(name);
+ if (svc != NULL) return svc;
+ LOGI("Waiting for sevice %s...\n", String8(name).string());
+ sleep(1);
+ }
+ return NULL;
+ }
+
+ virtual sp<IBinder> 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<IBinder>& 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<String16> listServices()
+ {
+ Vector<String16> 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<IBinder> b = const_cast<BnServiceManager*>(this)->getService(which);
+ reply->writeStrongBinder(b);
+ return NO_ERROR;
+ } break;
+ case CHECK_SERVICE_TRANSACTION: {
+ CHECK_INTERFACE(IServiceManager, data, reply);
+ String16 which = data.readString16();
+ sp<IBinder> b = const_cast<BnServiceManager*>(this)->checkService(which);
+ reply->writeStrongBinder(b);
+ return NO_ERROR;
+ } break;
+ case ADD_SERVICE_TRANSACTION: {
+ CHECK_INTERFACE(IServiceManager, data, reply);
+ String16 which = data.readString16();
+ sp<IBinder> 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<String16> list = listServices();
+ const size_t N = list.size();
+ reply->writeInt32(N);
+ for (size_t i=0; i<N; i++) {
+ reply->writeString16(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 0000000..39a0a68
--- /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 <winsock2.h>
+#else
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <netinet/in.h>
+//# include <arpa/inet.h>
+# include <netdb.h>
+#endif
+
+#include <utils/inet_address.h>
+#include <utils/threads.h>
+#include <utils/Log.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+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 0000000..e64f794
--- /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 <string.h>
+#include <cutils/log.h>
+#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 0000000..e69de29
--- /dev/null
+++ b/libs/utils/MODULE_LICENSE_APACHE2
diff --git a/libs/utils/MemoryBase.cpp b/libs/utils/MemoryBase.cpp
new file mode 100644
index 0000000..f25e11c
--- /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 <stdlib.h>
+#include <stdint.h>
+
+#include <utils/MemoryBase.h>
+
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+MemoryBase::MemoryBase(const sp<IMemoryHeap>& heap,
+ ssize_t offset, size_t size)
+ : mSize(size), mOffset(offset), mHeap(heap)
+{
+}
+
+sp<IMemoryHeap> 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 0000000..e6d1d18
--- /dev/null
+++ b/libs/utils/MemoryDealer.cpp
@@ -0,0 +1,407 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MemoryDealer"
+
+#include <utils/MemoryDealer.h>
+
+#include <utils/Log.h>
+#include <utils/IPCThreadState.h>
+#include <utils/SortedVector.h>
+#include <utils/String8.h>
+#include <utils/MemoryBase.h>
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/file.h>
+
+namespace android {
+
+
+// ----------------------------------------------------------------------------
+
+class SimpleMemory : public MemoryBase {
+public:
+ SimpleMemory(const sp<IMemoryHeap>& heap, ssize_t offset, size_t size);
+ virtual ~SimpleMemory();
+};
+
+
+// ----------------------------------------------------------------------------
+
+MemoryDealer::Allocation::Allocation(
+ const sp<MemoryDealer>& dealer, ssize_t offset, size_t size,
+ const sp<IMemory>& 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<IMemoryHeap> 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<HeapInterface>& heap)
+ : mHeap(heap),
+ mAllocator(new SimpleBestFitAllocator(heap->virtualSize()))
+{
+}
+
+MemoryDealer::MemoryDealer( const sp<HeapInterface>& heap,
+ const sp<AllocatorInterface>& allocator)
+ : mHeap(heap), mAllocator(allocator)
+{
+}
+
+MemoryDealer::~MemoryDealer()
+{
+}
+
+sp<IMemory> MemoryDealer::allocate(size_t size, uint32_t flags)
+{
+ sp<IMemory> memory;
+ const ssize_t offset = allocator()->allocate(size, flags);
+ if (offset >= 0) {
+ sp<IMemory> 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<HeapInterface>& MemoryDealer::heap() const {
+ return mHeap;
+}
+
+const sp<AllocatorInterface>& 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<IMemory> SharedHeap::mapMemory(size_t offset, size_t size)
+{
+ return new SimpleMemory(this, offset, size);
+}
+
+
+SimpleMemory::SimpleMemory(const sp<IMemoryHeap>& 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);
+
+ 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 0000000..59963c9
--- /dev/null
+++ b/libs/utils/MemoryHeapBase.cpp
@@ -0,0 +1,178 @@
+/*
+ * 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 <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+
+#include <cutils/log.h>
+#include <cutils/ashmem.h>
+#include <cutils/atomic.h>
+
+#include <utils/MemoryHeapBase.h>
+
+#if HAVE_ANDROID_OS
+#include <linux/android_pmem.h>
+#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, &reg);
+ 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.
+ }
+
+ 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);
+ mFD = fd;
+ mBase = base;
+ mSize = size;
+ mNeedUnmap = true;
+ 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 0000000..1e5a1cc
--- /dev/null
+++ b/libs/utils/MemoryHeapPmem.cpp
@@ -0,0 +1,226 @@
+/*
+ * 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 <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+
+#include <cutils/log.h>
+
+#include <utils/MemoryHeapPmem.h>
+#include <utils/MemoryHeapBase.h>
+
+#if HAVE_ANDROID_OS
+#include <linux/android_pmem.h>
+#endif
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+class MemoryHeapPmem;
+
+class SubRegionMemory : public BnMemory {
+public:
+ SubRegionMemory(const sp<MemoryHeapPmem>& heap, ssize_t offset, size_t size);
+ virtual ~SubRegionMemory();
+ virtual sp<IMemoryHeap> getMemory(ssize_t* offset, size_t* size) const;
+private:
+ friend class MemoryHeapPmem;
+ void revoke();
+ size_t mSize;
+ ssize_t mOffset;
+ sp<MemoryHeapPmem> mClientHeap;
+};
+
+SubRegionMemory::SubRegionMemory(const sp<MemoryHeapPmem>& heap,
+ ssize_t offset, size_t size)
+ : mSize(size), mOffset(offset), mClientHeap(heap)
+{
+#ifndef NDEBUG
+ void* const start_ptr = (void*)(intptr_t(mClientHeap->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<IMemoryHeap> SubRegionMemory::getMemory(ssize_t* offset, size_t* size) const
+{
+ if (offset) *offset = mOffset;
+ if (size) *size = mSize;
+ return mClientHeap;
+}
+
+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 (mClientHeap != NULL) {
+ int our_fd = mClientHeap->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);
+ mClientHeap.clear();
+ }
+#endif
+}
+
+// ---------------------------------------------------------------------------
+
+MemoryHeapPmem::MemoryHeapPmem(const sp<MemoryHeapBase>& 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<IMemory> MemoryHeapPmem::mapMemory(size_t offset, size_t size)
+{
+ sp<SubRegionMemory> memory;
+ if (heapID() > 0)
+ memory = new SubRegionMemory(this, offset, size);
+
+ if (memory != 0) {
+ Mutex::Autolock _l(mLock);
+ mAllocations.add(memory);
+ }
+ 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()
+{
+ Vector< wp<SubRegionMemory> > allocations;
+
+ { // scope for lock
+ Mutex::Autolock _l(mLock);
+ allocations = mAllocations;
+ mAllocations.clear();
+ }
+
+ ssize_t count = allocations.size();
+ for (ssize_t i=0 ; i<count ; i++) {
+ sp<SubRegionMemory> memory(allocations[i].promote());
+ if (memory != 0)
+ memory->revoke();
+ }
+}
+
+// ---------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/utils/NOTICE b/libs/utils/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /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 0000000..3eca4b0
--- /dev/null
+++ b/libs/utils/Parcel.cpp
@@ -0,0 +1,1311 @@
+/*
+ * 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 <utils/Parcel.h>
+
+#include <utils/Binder.h>
+#include <utils/BpBinder.h>
+#include <utils/Debug.h>
+#include <utils/ProcessState.h>
+#include <utils/Log.h>
+#include <utils/String8.h>
+#include <utils/String16.h>
+#include <utils/TextOutput.h>
+#include <utils/misc.h>
+
+#include <private/utils/binder_module.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#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<ProcessState>& 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<IBinder*>(obj.cookie)->incStrong(who);
+ }
+ return;
+ case BINDER_TYPE_WEAK_BINDER:
+ if (obj.binder)
+ static_cast<RefBase::weakref_type*>(obj.binder)->incWeak(who);
+ return;
+ case BINDER_TYPE_HANDLE: {
+ const sp<IBinder> 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<IBinder> 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<ProcessState>& 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<IBinder*>(obj.cookie)->decStrong(who);
+ }
+ return;
+ case BINDER_TYPE_WEAK_BINDER:
+ if (obj.binder)
+ static_cast<RefBase::weakref_type*>(obj.binder)->decWeak(who);
+ return;
+ case BINDER_TYPE_HANDLE: {
+ const sp<IBinder> 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<IBinder> 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<IBinder>& binder, const flat_binder_object& flat, Parcel* out)
+{
+ return out->writeObject(flat, false);
+}
+
+status_t flatten_binder(const sp<ProcessState>& proc,
+ const sp<IBinder>& 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<ProcessState>& proc,
+ const wp<IBinder>& binder, Parcel* out)
+{
+ flat_binder_object obj;
+
+ obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
+ if (binder != NULL) {
+ sp<IBinder> 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<ProcessState>& proc,
+ const Parcel& in, sp<IBinder>* out)
+{
+ const flat_binder_object* flat = in.readObject(false);
+
+ if (flat) {
+ switch (flat->type) {
+ case BINDER_TYPE_BINDER:
+ *out = static_cast<IBinder*>(flat->cookie);
+ return finish_unflatten_binder(NULL, *flat, in);
+ case BINDER_TYPE_HANDLE:
+ *out = proc->getStrongProxyForHandle(flat->handle);
+ return finish_unflatten_binder(
+ static_cast<BpBinder*>(out->get()), *flat, in);
+ }
+ }
+ return BAD_TYPE;
+}
+
+status_t unflatten_binder(const sp<ProcessState>& proc,
+ const Parcel& in, wp<IBinder>* out)
+{
+ const flat_binder_object* flat = in.readObject(false);
+
+ if (flat) {
+ switch (flat->type) {
+ case BINDER_TYPE_BINDER:
+ *out = static_cast<IBinder*>(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<IBinder*>(flat->cookie),
+ static_cast<RefBase::weakref_type*>(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<BpBinder*>(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<uint8_t*>(data()), buffer, len);
+ mDataSize = len;
+ mFdsKnown = false;
+ }
+ return err;
+}
+
+status_t Parcel::appendFrom(Parcel *parcel, size_t offset, size_t len)
+{
+ const sp<ProcessState> 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<flat_binder_object*>(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<void**>(data+padded-4));
+ *reinterpret_cast<uint32_t*>(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<int32_t*>(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<int64_t*>(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<float*>(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<double*>(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<char16_t*>(data+len) = 0;
+ return NO_ERROR;
+ }
+ err = mError;
+ }
+ return err;
+}
+
+status_t Parcel::writeStrongBinder(const sp<IBinder>& val)
+{
+ return flatten_binder(ProcessState::self(), val, this);
+}
+
+status_t Parcel::writeWeakBinder(const wp<IBinder>& val)
+{
+ return flatten_binder(ProcessState::self(), val, this);
+}
+
+status_t Parcel::writeFileDescriptor(int fd)
+{
+ flat_binder_object obj;
+ obj.type = BINDER_TYPE_FD;
+ obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
+ obj.handle = fd;
+ obj.cookie = (void*)0;
+ return writeObject(obj, true);
+}
+
+status_t Parcel::writeDupFileDescriptor(int fd)
+{
+ flat_binder_object obj;
+ obj.type = BINDER_TYPE_FD;
+ obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
+ obj.handle = dup(fd);
+ obj.cookie = (void*)1;
+ return writeObject(obj, true);
+}
+
+status_t Parcel::writeObject(const flat_binder_object& val, bool nullMetaData)
+{
+ const bool enoughData = (mDataPos+sizeof(val)) <= mDataCapacity;
+ const bool enoughObjects = mObjectsSize < mObjectsCapacity;
+ if (enoughData && enoughObjects) {
+restart_write:
+ *reinterpret_cast<flat_binder_object*>(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<const int32_t*>(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<const int32_t*>(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<const int64_t*>(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<const int64_t*>(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<const float*>(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<const float*>(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<const double*>(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<const double*>(data);
+ }
+ return 0;
+}
+
+
+const char* Parcel::readCString() const
+{
+ const size_t avail = mDataSize-mDataPos;
+ if (avail > 0) {
+ const char* str = reinterpret_cast<const char*>(mData+mDataPos);
+ // is the string's trailing NUL within the parcel's valid bounds?
+ const char* eos = reinterpret_cast<const char*>(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<IBinder> Parcel::readStrongBinder() const
+{
+ sp<IBinder> val;
+ unflatten_binder(ProcessState::self(), *this, &val);
+ return val;
+}
+
+wp<IBinder> Parcel::readWeakBinder() const
+{
+ wp<IBinder> val;
+ unflatten_binder(ProcessState::self(), *this, &val);
+ return val;
+}
+
+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<const flat_binder_object*>(mData+DPOS);
+ mDataPos = DPOS + sizeof(flat_binder_object);
+ if (!nullMetaData && (obj->cookie == NULL && obj->binder == NULL)) {
+ // When transfering 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<flat_binder_object*>(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<uint8_t*>(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<size_t*>(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<N; i++) {
+ const flat_binder_object* flat
+ = reinterpret_cast<const flat_binder_object*>(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<ProcessState> 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<flat_binder_object*>(data+objects[i]);
+ release_object(proc, *flat, this);
+ }
+}
+
+void Parcel::acquireObjects()
+{
+ const sp<ProcessState> 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<flat_binder_object*>(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<ProcessState> proc(ProcessState::self());
+ for (size_t i=objectsSize; i<mObjectsSize; i++) {
+ const flat_binder_object* flat
+ = reinterpret_cast<flat_binder_object*>(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<mObjectsSize; i++) {
+ const flat_binder_object* flat
+ = reinterpret_cast<const flat_binder_object*>(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 0000000..613906b
--- /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 <utils/Pipe.h>
+#include <utils/Log.h>
+
+#if defined(HAVE_WIN32_IPC)
+# include <windows.h>
+#else
+# include <fcntl.h>
+# include <unistd.h>
+# include <errno.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+
+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 0000000..4567df6
--- /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 <cutils/process_name.h>
+
+#include <utils/ProcessState.h>
+
+#include <utils/Atomic.h>
+#include <utils/BpBinder.h>
+#include <utils/IPCThreadState.h>
+#include <utils/Log.h>
+#include <utils/String8.h>
+#include <utils/IServiceManager.h>
+#include <utils/String8.h>
+#include <utils/threads.h>
+
+#include <private/utils/binder_module.h>
+#include <private/utils/Static.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#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> 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<IBinder>& object)
+{
+ setContextObject(object, String16("default"));
+}
+
+sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& caller)
+{
+ if (supportsProcesses()) {
+ return getStrongProxyForHandle(0);
+ } else {
+ return getContextObject(String16("default"), caller);
+ }
+}
+
+void ProcessState::setContextObject(const sp<IBinder>& object, const String16& name)
+{
+ AutoMutex _l(mLock);
+ mContexts.add(name, object);
+}
+
+sp<IBinder> ProcessState::getContextObject(const String16& name, const sp<IBinder>& caller)
+{
+ mLock.lock();
+ sp<IBinder> 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<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
+{
+ sp<IBinder> 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<IBinder> ProcessState::getWeakProxyForHandle(int32_t handle)
+{
+ wp<IBinder> 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<argc; i++) {
+ mArgLen += strlen(argv[i]) + 1;
+ }
+ mArgLen--;
+}
+
+int ProcessState::getArgC() const
+{
+ return mArgC;
+}
+
+const char* const* ProcessState::getArgV() const
+{
+ return mArgV;
+}
+
+void ProcessState::setArgV0(const char* txt)
+{
+ if (mArgV != NULL) {
+ strncpy((char*)mArgV[0], txt, mArgLen);
+ set_process_name(txt);
+ }
+}
+
+void ProcessState::spawnPooledThread(bool isMain)
+{
+ if (mThreadPoolStarted) {
+ int32_t s = android_atomic_add(1, &mThreadPoolSeq);
+ char buf[32];
+ sprintf(buf, "Binder Thread #%d", s);
+ LOGV("Spawning new pooled thread, name=%s\n", buf);
+ sp<Thread> 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 0000000..36a706d
--- /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 0000000..0bd1af4
--- /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 <utils/RefBase.h>
+
+#include <utils/Atomic.h>
+#include <utils/CallStack.h>
+#include <utils/KeyedVector.h>
+#include <utils/Log.h>
+#include <utils/threads.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <typeinfo>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+// compile with refcounting debugging enabled
+#define DEBUG_REFS 0
+#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<weakref_impl*>(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<const void*, int32_t>* 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<const void*, int32_t>* 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<const void*, int32_t>& refs)
+ {
+ const size_t N=refs.size();
+ for (size_t i=0; i<N; i++) {
+ printf("\tID %p: %d remain\n", refs.keyAt(i), refs.valueAt(i));
+ }
+ }
+
+ mutable Mutex mMutex;
+ KeyedVector<const void*, int32_t> mStrongRefs;
+ KeyedVector<const void*, int32_t> 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<RefBase*>(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<RefBase*>(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<RefBase*>(this)->onFirstRef();
+ }
+}
+
+int32_t RefBase::getStrongCount() const
+{
+ return mRefs->mStrong;
+}
+
+
+
+RefBase* RefBase::weakref_type::refBase() const
+{
+ return static_cast<const weakref_impl*>(this)->mBase;
+}
+
+void RefBase::weakref_type::incWeak(const void* id)
+{
+ weakref_impl* const impl = static_cast<weakref_impl*>(this);
+ impl->addWeakRef(id);
+ const int32_t c = android_atomic_inc(&impl->mWeak);
+ 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<weakref_impl*>(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<weakref_impl*>(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<weakref_impl*>(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<const weakref_impl*>(this)->mWeak;
+}
+
+void RefBase::weakref_type::printRefs() const
+{
+ static_cast<const weakref_impl*>(this)->printRefs();
+}
+
+void RefBase::weakref_type::trackMe(bool enable, bool retain)
+{
+ static_cast<const weakref_impl*>(this)->trackMe(enable, retain);
+}
+
+RefBase::weakref_type* RefBase::createWeak(const void* id) const
+{
+ mRefs->incWeak(id);
+ return mRefs;
+}
+
+RefBase::weakref_type* RefBase::getWeakRefs() const
+{
+ return mRefs;
+}
+
+RefBase::RefBase()
+ : mRefs(new weakref_impl(this))
+{
+// 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 0000000..a5fe9fb
--- /dev/null
+++ b/libs/utils/ResourceTypes.cpp
@@ -0,0 +1,3969 @@
+/*
+ * 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 <utils/Atomic.h>
+#include <utils/ByteOrder.h>
+#include <utils/Debug.h>
+#include <utils/ResourceTypes.h>
+#include <utils/String16.h>
+#include <utils/String8.h>
+#include <utils/TextOutput.h>
+#include <utils/Log.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <memory.h>
+#include <ctype.h>
+#include <stdint.h>
+
+#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; i<numColors; i++) {
+ colors[i] = htonl(colors[i]);
+ }
+}
+
+void Res_png_9patch::fileToDevice()
+{
+ for (int i = 0; i < numXDivs; i++) {
+ xDivs[i] = ntohl(xDivs[i]);
+ }
+ for (int i = 0; i < numYDivs; i++) {
+ yDivs[i] = ntohl(yDivs[i]);
+ }
+ paddingLeft = ntohl(paddingLeft);
+ paddingRight = ntohl(paddingRight);
+ paddingTop = ntohl(paddingTop);
+ paddingBottom = ntohl(paddingBottom);
+ for (int i=0; i<numColors; i++) {
+ colors[i] = ntohl(colors[i]);
+ }
+}
+
+size_t Res_png_9patch::serializedSize()
+{
+ return sizeof(Res_png_9patch)
+ + numXDivs * sizeof(int32_t)
+ + numYDivs * sizeof(int32_t)
+ + numColors * sizeof(uint32_t);
+}
+
+void* Res_png_9patch::serialize()
+{
+ void* newData = malloc(serializedSize());
+ serialize(newData);
+ return newData;
+}
+
+void Res_png_9patch::serialize(void * outData)
+{
+ char* data = (char*) outData;
+ memmove(data, this, sizeof(Res_png_9patch));
+ data += sizeof(Res_png_9patch);
+ memmove(data, this->xDivs, 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));
+}
+
+Res_png_9patch* Res_png_9patch::deserialize(const void* inData)
+{
+ deserialize(inData, (Res_png_9patch*) inData);
+ return (Res_png_9patch*) inData;
+}
+
+void Res_png_9patch::deserialize(const void* inData, Res_png_9patch* outData) {
+ Res_png_9patch* patch = (Res_png_9patch*) inData;
+ if (inData != outData) {
+ memcpy(outData, inData, patch->serializedSize());
+ }
+ outData->wasDeserialized = true;
+ char* data = (char*)outData;
+ data += sizeof(Res_png_9patch);
+ outData->xDivs = (int32_t*) data;
+ data += patch->numXDivs * sizeof(int32_t);
+ outData->yDivs = (int32_t*) data;
+ data += patch->numYDivs * sizeof(int32_t);
+ outData->colors = (uint32_t*) data;
+}
+
+// --------------------------------------------------------------------
+// --------------------------------------------------------------------
+// --------------------------------------------------------------------
+
+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<ResStringPool_header*>(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<uint32_t*>(mEntries);
+ for (i=0; i<mHeader->stringCount; i++) {
+ e[i] = dtohl(mEntries[i]);
+ }
+ char16_t* s = const_cast<char16_t*>(mStrings);
+ for (i=0; i<mStringPoolSize; i++) {
+ s[i] = dtohs(mStrings[i]);
+ }
+ }
+
+ if (mStrings[mStringPoolSize-1] != 0) {
+ LOGW("Bad string block: last string is not 0-terminated\n");
+ return (mError=BAD_TYPE);
+ }
+ } else {
+ mStrings = NULL;
+ mStringPoolSize = 0;
+ }
+
+ if (mHeader->styleCount > 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<uint32_t*>(mEntryStyles);
+ for (i=0; i<mHeader->styleCount; i++) {
+ e[i] = dtohl(mEntryStyles[i]);
+ }
+ uint32_t* s = const_cast<uint32_t*>(mStyles);
+ for (i=0; i<mStylePoolSize; i++) {
+ s[i] = dtohl(mStyles[i]);
+ }
+ }
+
+ const ResStringPool_span endSpan = {
+ { htodl(ResStringPool_span::END) },
+ htodl(ResStringPool_span::END), htodl(ResStringPool_span::END)
+ };
+ if (memcmp(&mStyles[mStylePoolSize-(sizeof(endSpan)/sizeof(uint32_t))],
+ &endSpan, sizeof(endSpan)) != 0) {
+ LOGW("Bad string block: last style is not 0xFFFFFFFF-terminated\n");
+ return (mError=BAD_TYPE);
+ }
+ } else {
+ mEntryStyles = NULL;
+ mStyles = NULL;
+ mStylePoolSize = 0;
+ }
+
+ return (mError=NO_ERROR);
+}
+
+status_t ResStringPool::getError() const
+{
+ return mError;
+}
+
+void ResStringPool::uninit()
+{
+ mError = NO_INIT;
+ if (mOwnedData) {
+ free(mOwnedData);
+ mOwnedData = NULL;
+ }
+}
+
+const uint16_t* ResStringPool::stringAt(size_t idx, size_t* outLen) const
+{
+ if (mError == NO_ERROR && idx < mHeader->stringCount) {
+ 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<N; i++) {
+ size_t curNsLen, curAttrLen;
+ const char16_t* curNs = getAttributeNamespace(i, &curNsLen);
+ const char16_t* curAttr = getAttributeName(i, &curAttrLen);
+ //printf("%d: ns=%p attr=%p curNs=%p curAttr=%p\n",
+ // i, ns, attr, curNs, curAttr);
+ //printf(" --> 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<const ResTable_type*> 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<Type*> 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; i<N; i++) {
+ delete packages[i];
+ }
+ }
+
+ void clearBagCache() {
+ if (bags) {
+ TABLE_NOISY(printf("bags=%p\n", bags));
+ Package* pkg = packages[0];
+ TABLE_NOISY(printf("typeCount=%x\n", typeCount));
+ for (size_t i=0; i<typeCount; i++) {
+ TABLE_NOISY(printf("type=%d\n", i));
+ const Type* type = pkg->getType(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<N; j++) {
+ if (typeBags[j] && typeBags[j] != (bag_set*)0xFFFFFFFF)
+ free(typeBags[j]);
+ }
+ free(typeBags);
+ }
+ }
+ }
+ free(bags);
+ bags = NULL;
+ }
+ }
+
+ String16 const name;
+ uint32_t const id;
+ Vector<Package*> 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; i<Res_MAXPACKAGE; i++) {
+ package_info* pi = mPackages[i];
+ if (pi != NULL) {
+ free_package(pi);
+ }
+ }
+}
+
+void ResTable::Theme::free_package(package_info* pi)
+{
+ for (size_t j=0; j<pi->numTypes; 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; j<newpi->numTypes; 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<Res_MAXPACKAGE; i++) {
+ if (mPackages[i] != NULL) {
+ free_package(mPackages[i]);
+ }
+ if (other.mPackages[i] != NULL) {
+ mPackages[i] = copy_package(other.mPackages[i]);
+ } else {
+ mPackages[i] = NULL;
+ }
+ }
+ } else {
+ // @todo: need to really implement this, not just copy
+ // the system package (which is still wrong because it isn't
+ // fixing up resource references).
+ for (size_t i=0; i<Res_MAXPACKAGE; i++) {
+ if (mPackages[i] != NULL) {
+ free_package(mPackages[i]);
+ }
+ if (i == 0 && other.mPackages[i] != NULL) {
+ mPackages[i] = copy_package(other.mPackages[i]);
+ } else {
+ mPackages[i] = NULL;
+ }
+ }
+ }
+
+ //LOGI("Final theme:");
+ //dumpToLog();
+
+ return NO_ERROR;
+}
+
+ssize_t ResTable::Theme::getAttribute(uint32_t resID, Res_value* outValue,
+ uint32_t* outTypeSpecFlags) const
+{
+ int cnt = 20;
+
+ if (outTypeSpecFlags != NULL) *outTypeSpecFlags = 0;
+
+ do {
+ const ssize_t p = mTable.getResourcePackageIndex(resID);
+ const uint32_t t = Res_GETTYPE(resID);
+ const uint32_t e = Res_GETENTRY(resID);
+
+ TABLE_NOISY(LOGV("Looking up attr 0x%08x in theme %p", resID, this));
+
+ if (p >= 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; i<Res_MAXPACKAGE; i++) {
+ package_info* pi = mPackages[i];
+ if (pi == NULL) continue;
+
+ LOGI(" Package #0x%02x:\n", (int)(i+1));
+ for (size_t j=0; j<pi->numTypes; 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; k<ti.numEntries; k++) {
+ theme_entry& te = ti.entries[k];
+ if (te.value.dataType == Res_value::TYPE_NULL) continue;
+ LOGI(" 0x%08x: t=0x%x, d=0x%08x (block=%d)\n",
+ (int)Res_MAKEID(i, j, k),
+ te.value.dataType, (int)te.value.data, (int)te.stringBlock);
+ }
+ }
+ }
+}
+
+ResTable::ResTable()
+ : mError(NO_INIT)
+{
+ memset(&mParams, 0, sizeof(mParams));
+ memset(mPackageMap, 0, sizeof(mPackageMap));
+ //LOGI("Creating ResTable %p\n", this);
+}
+
+ResTable::ResTable(const void* data, size_t size, void* cookie, bool copyData)
+ : mError(NO_INIT)
+{
+ memset(&mParams, 0, sizeof(mParams));
+ memset(mPackageMap, 0, sizeof(mPackageMap));
+ add(data, size, cookie, copyData);
+ LOG_FATAL_IF(mError != NO_ERROR, "Error parsing resource table");
+ //LOGI("Creating ResTable %p\n", this);
+}
+
+ResTable::~ResTable()
+{
+ //LOGI("Destroying ResTable in %p\n", this);
+ uninit();
+}
+
+inline ssize_t ResTable::getResourcePackageIndex(uint32_t resID) const
+{
+ return ((ssize_t)mPackageMap[Res_GETPACKAGE(resID)+1])-1;
+}
+
+status_t ResTable::add(const void* data, size_t size, void* cookie, bool copyData)
+{
+ return add(data, size, cookie, NULL, copyData);
+}
+
+status_t ResTable::add(Asset* asset, void* cookie, bool copyData)
+{
+ const void* data = asset->getBuffer(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; i<N; i++) {
+ PackageGroup* g = mPackageGroups[i];
+ delete g;
+ }
+ N = mHeaders.size();
+ for (size_t i=0; i<N; i++) {
+ Header* header = mHeaders[i];
+ if (header->ownedData) {
+ 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) 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);
+ 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; i<mPackageGroups.size(); i++) {
+ TABLE_NOISY(LOGI("CLEARING BAGS FOR GROUP %d!", i));
+ mPackageGroups[i]->clearBagCache();
+ }
+ 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; i<N; i++) {
+ const id_name_map* m = ID_NAMES + i;
+ len = m->len;
+ if (len != nameLen) {
+ continue;
+ }
+ for (size_t j=1; j<len; j++) {
+ if (m->name[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; ig<NG; ig++) {
+ const PackageGroup* group = mPackageGroups[ig];
+
+ if (strzcmp16(package, packageLen,
+ group->name.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; tci<NTC; tci++) {
+ const ResTable_type* const ty = typeConfigs->configs[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<NE; i++) {
+ uint32_t offset = dtohl(eindex[i]);
+ if (offset == ResTable_type::NO_ENTRY) {
+ continue;
+ }
+
+ offset += typeOffset;
+
+ if (offset > (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<<Res_value::COMPLEX_RADIX_SHIFT)
+ | (mantissa<<Res_value::COMPLEX_MANTISSA_SHIFT);
+ //printf("Input value: %f 0x%016Lx, mult: %f, radix: %d, shift: %d, final: 0x%08x\n",
+ // f * (neg ? -1 : 1), bits, f*(1<<23),
+ // radix, shift, outValue->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; i<cnt; i++, bagi++) {
+ if (!Res_INTERNALID(bagi->map.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<ResTable_config>* configs) const
+{
+ const size_t I = mPackageGroups.size();
+ for (size_t i=0; i<I; i++) {
+ const PackageGroup* packageGroup = mPackageGroups[i];
+ const size_t J = packageGroup->packages.size();
+ for (size_t j=0; j<J; j++) {
+ const Package* package = packageGroup->packages[j];
+ const size_t K = package->types.size();
+ for (size_t k=0; k<K; k++) {
+ const Type* type = package->types[k];
+ if (type == NULL) continue;
+ const size_t L = type->configs.size();
+ for (size_t l=0; l<L; l++) {
+ const ResTable_type* config = type->configs[l];
+ const ResTable_config* cfg = &config->config;
+ // only insert unique
+ const size_t M = configs->size();
+ size_t m;
+ for (m=0; m<M; m++) {
+ if (0 == (*configs)[m].compare(*cfg)) {
+ break;
+ }
+ }
+ // if we didn't find it
+ if (m == M) {
+ configs->add(*cfg);
+ }
+ }
+ }
+ }
+ }
+}
+
+void ResTable::getLocales(Vector<String8>* locales) const
+{
+ Vector<ResTable_config> 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; i<I; i++) {
+ char locale[6];
+ configs[i].getLocale(locale);
+ const size_t J = locales->size();
+ size_t j;
+ for (j=0; j<J; j++) {
+ if (0 == strcmp(locale, (*locales)[j].string())) {
+ break;
+ }
+ }
+ if (j == J) {
+ locales->add(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; i<NT; i++) {
+ const ResTable_type* const thisType = allTypes->configs[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; pgIndex<pgCount; pgIndex++) {
+ const PackageGroup* pg = mPackageGroups[pgIndex];
+ printf("Package Group %d id=%d packageCount=%d name=%s\n",
+ (int)pgIndex, pg->id, (int)pg->packages.size(),
+ String8(pg->name).string());
+
+ size_t pkgCount = pg->packages.size();
+ for (size_t pkgIndex=0; pkgIndex<pkgCount; pkgIndex++) {
+ const Package* pkg = pg->packages[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; typeIndex<typeCount; typeIndex++) {
+ const Type* typeConfigs = pkg->getType(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; entryIndex<typeConfigs->entryCount; 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; configIndex<NTC; configIndex++) {
+ const ResTable_type* type = typeConfigs->configs[configIndex];
+ if ((((int)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; entryIndex<entryCount; entryIndex++) {
+
+ const uint8_t* const end = ((const uint8_t*)type)
+ + dtohl(type->header.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("<bag>");
+ } 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 0000000..3555fb7
--- /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 <stdlib.h>
+#include <string.h>
+
+#include <utils/SharedBuffer.h>
+#include <utils/Atomic.h>
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+SharedBuffer* SharedBuffer::alloc(size_t size)
+{
+ SharedBuffer* sb = static_cast<SharedBuffer *>(malloc(sizeof(SharedBuffer) + size));
+ if (sb) {
+ sb->mRefs = 1;
+ sb->mSize = size;
+ }
+ return sb;
+}
+
+
+ssize_t SharedBuffer::dealloc(const SharedBuffer* released)
+{
+ if (released->mRefs != 0) return -1; // XXX: invalid operation
+ free(const_cast<SharedBuffer*>(released));
+ return 0;
+}
+
+SharedBuffer* SharedBuffer::edit() const
+{
+ if (onlyOwner()) {
+ return const_cast<SharedBuffer*>(this);
+ }
+ SharedBuffer* sb = alloc(mSize);
+ if (sb) {
+ memcpy(sb->data(), data(), size());
+ release();
+ }
+ return sb;
+}
+
+SharedBuffer* SharedBuffer::editResize(size_t newSize) const
+{
+ if (onlyOwner()) {
+ SharedBuffer* buf = const_cast<SharedBuffer*>(this);
+ if (buf->mSize == newSize) return buf;
+ buf = (SharedBuffer*)realloc(buf, sizeof(SharedBuffer) + newSize);
+ if (buf != NULL) {
+ buf->mSize = newSize;
+ return buf;
+ }
+ }
+ SharedBuffer* sb = alloc(newSize);
+ if (sb) {
+ const size_t mySize = mSize;
+ memcpy(sb->data(), data(), newSize < mySize ? newSize : mySize);
+ release();
+ }
+ return sb;
+}
+
+SharedBuffer* SharedBuffer::attemptEdit() const
+{
+ if (onlyOwner()) {
+ return const_cast<SharedBuffer*>(this);
+ }
+ return 0;
+}
+
+SharedBuffer* SharedBuffer::reset(size_t new_size) const
+{
+ // cheap-o-reset.
+ SharedBuffer* sb = alloc(new_size);
+ if (sb) {
+ release();
+ }
+ return sb;
+}
+
+void SharedBuffer::acquire() const {
+ android_atomic_inc(&mRefs);
+}
+
+int32_t SharedBuffer::release(uint32_t flags) const
+{
+ int32_t prev = 1;
+ if (onlyOwner() || ((prev = android_atomic_dec(&mRefs)) == 1)) {
+ mRefs = 0;
+ if ((flags & eKeepStorage) == 0) {
+ free(const_cast<SharedBuffer*>(this));
+ }
+ }
+ return prev;
+}
+
+
+}; // namespace android
diff --git a/libs/utils/Socket.cpp b/libs/utils/Socket.cpp
new file mode 100644
index 0000000..51509a3
--- /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 <sys/types.h>.
+# include <winsock2.h>
+#endif
+
+#include <utils/Socket.h>
+#include <utils/inet_address.h>
+#include <utils/Log.h>
+#include <utils/Timers.h>
+
+#ifndef HAVE_WINSOCK
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <netinet/in.h>
+# include <arpa/inet.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+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 0000000..93f7e4f
--- /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 <private/utils/Static.h>
+
+#include <utils/BufferedTextOutput.h>
+#include <utils/IPCThreadState.h>
+#include <utils/Log.h>
+
+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<int32_t> 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<ProcessState> gProcess;
+
+class LibUtilsIPCtStatics
+{
+public:
+ LibUtilsIPCtStatics()
+ {
+ }
+
+ ~LibUtilsIPCtStatics()
+ {
+ IPCThreadState::shutdown();
+ }
+};
+
+static LibUtilsIPCtStatics gIPCStatics;
+
+// ------------ ServiceManager.cpp
+
+Mutex gDefaultServiceManagerLock;
+sp<IServiceManager> gDefaultServiceManager;
+sp<IPermissionController> gPermissionController;
+
+#endif
+
+} // namespace android
diff --git a/libs/utils/StopWatch.cpp b/libs/utils/StopWatch.cpp
new file mode 100644
index 0000000..68a1c52
--- /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 <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <utils/Log.h>
+#include <utils/Errors.h>
+#include <utils/StopWatch.h>
+
+/*****************************************************************************/
+
+namespace android {
+
+
+StopWatch::StopWatch(const char *name, int clock, uint32_t flags)
+ : mName(name), mClock(clock), mFlags(flags),
+ 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<n ; i++) {
+ const nsecs_t soFar = mLaps[i].soFar;
+ const nsecs_t thisLap = mLaps[i].thisLap;
+ LOGD(" [%d: %lld, %lld]", i, ns2us(soFar), ns2us(thisLap));
+ }
+}
+
+const char* StopWatch::name() const
+{
+ return mName;
+}
+
+nsecs_t StopWatch::lap()
+{
+ nsecs_t elapsed = elapsedTime();
+ if (mNumLaps >= 8) {
+ elapsed = 0;
+ } else {
+ const int n = mNumLaps;
+ mLaps[n].soFar = elapsed;
+ mLaps[n].thisLap = n ? (elapsed - mLaps[n-1].soFar) : elapsed;
+ mNumLaps = n+1;
+ }
+ return elapsed;
+}
+
+nsecs_t StopWatch::elapsedTime() const
+{
+ return systemTime(mClock) - mStartTime;
+}
+
+
+/*****************************************************************************/
+
+}; // namespace android
+
diff --git a/libs/utils/String16.cpp b/libs/utils/String16.cpp
new file mode 100644
index 0000000..1f81cad
--- /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 <utils/String16.h>
+
+#include <utils/Debug.h>
+#include <utils/Log.h>
+#include <utils/String8.h>
+#include <utils/TextOutput.h>
+#include <utils/threads.h>
+
+#include <private/utils/Static.h>
+
+#ifdef HAVE_WINSOCK
+# undef nhtol
+# undef htonl
+# undef nhtos
+# undef htons
+
+# ifdef HAVE_LITTLE_ENDIAN
+# define ntohl(x) ( ((x) << 24) | (((x) >> 24) & 255) | (((x) << 8) & 0xff0000) | (((x) >> 8) & 0xff00) )
+# define htonl(x) ntohl(x)
+# define ntohs(x) ( (((x) << 8) & 0xff00) | (((x) >> 8) & 255) )
+# define htons(x) ntohs(x)
+# else
+# define ntohl(x) (x)
+# define htonl(x) (x)
+# define ntohs(x) (x)
+# define htons(x) (x)
+# endif
+#else
+# include <netinet/in.h>
+#endif
+
+#include <memory.h>
+#include <stdio.h>
+#include <ctype.h>
+
+// ---------------------------------------------------------------------------
+
+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<N; i++) {
+ const char16_t v = str[i];
+ if (v >= 'A' && v <= 'Z') {
+ if (!edit) {
+ SharedBuffer* buf = SharedBuffer::bufferFromData(mString)->edit();
+ if (!buf) {
+ return NO_MEMORY;
+ }
+ edit = (char16_t*)buf->data();
+ mString = str = edit;
+ }
+ edit[i] = tolower((char)v);
+ }
+ }
+ return NO_ERROR;
+}
+
+status_t String16::replaceAll(char16_t replaceThis, char16_t withThis)
+{
+ const size_t N = size();
+ const char16_t* str = string();
+ char16_t* edit = NULL;
+ for (size_t i=0; i<N; i++) {
+ if (str[i] == replaceThis) {
+ if (!edit) {
+ SharedBuffer* buf = SharedBuffer::bufferFromData(mString)->edit();
+ if (!buf) {
+ return NO_MEMORY;
+ }
+ edit = (char16_t*)buf->data();
+ mString = str = edit;
+ }
+ edit[i] = withThis;
+ }
+ }
+ return NO_ERROR;
+}
+
+status_t String16::remove(size_t len, size_t begin)
+{
+ const size_t N = size();
+ if (begin >= N) {
+ SharedBuffer::bufferFromData(mString)->release();
+ mString = getEmptyString();
+ return NO_ERROR;
+ }
+ if ((begin+len) > N) len = N-begin;
+ if (begin == 0 && len == N) {
+ return NO_ERROR;
+ }
+
+ if (begin > 0) {
+ SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
+ ->editResize((N+1)*sizeof(char16_t));
+ if (!buf) {
+ return NO_MEMORY;
+ }
+ char16_t* str = (char16_t*)buf->data();
+ memmove(str, str+begin, (N-begin+1)*sizeof(char16_t));
+ mString = str;
+ }
+ SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
+ ->editResize((len+1)*sizeof(char16_t));
+ if (buf) {
+ char16_t* str = (char16_t*)buf->data();
+ str[len] = 0;
+ mString = str;
+ return NO_ERROR;
+ }
+ return NO_MEMORY;
+}
+
+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 0000000..ab843f6
--- /dev/null
+++ b/libs/utils/String8.cpp
@@ -0,0 +1,602 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <utils/String8.h>
+
+#include <utils/Log.h>
+#include <utils/String16.h>
+#include <utils/TextOutput.h>
+#include <utils/threads.h>
+
+#include <private/utils/Static.h>
+
+#include <ctype.h>
+
+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 <dstP>.
+
+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();
+ memcpy(str+myLen, other, otherLen+1);
+ mString = str;
+ 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<char*>(lastDot);
+}
+
+String8 String8::getPathExtension(void) const
+{
+ char* ext;
+
+ ext = find_extension();
+ if (ext != NULL)
+ return String8(ext);
+ else
+ return String8("");
+}
+
+String8 String8::getBasePath(void) const
+{
+ char* ext;
+ const char* const str = mString;
+
+ ext = find_extension();
+ if (ext == NULL)
+ return String8(*this);
+ else
+ return String8(str, ext - str);
+}
+
+String8& String8::appendPath(const char* name)
+{
+ // TODO: The test below will fail for Win32 paths. Fix later or ignore.
+ if (name[0] != OS_PATH_SEPARATOR) {
+ if (*name == '\0') {
+ // nothing to do
+ return *this;
+ }
+
+ size_t len = length();
+ if (len == 0) {
+ // no existing filename, just use the new one
+ setPathName(name);
+ return *this;
+ }
+
+ // make room for oldPath + '/' + newPath
+ int newlen = strlen(name);
+
+ char* buf = lockBuffer(len+1+newlen);
+
+ // insert a '/' if needed
+ if (buf[len-1] != OS_PATH_SEPARATOR)
+ buf[len++] = OS_PATH_SEPARATOR;
+
+ memcpy(buf+len, name, newlen+1);
+ len += newlen;
+
+ unlockBuffer(len);
+
+ return *this;
+ } else {
+ setPathName(name);
+ return *this;
+ }
+}
+
+String8& String8::convertToResPath()
+{
+#if OS_PATH_SEPARATOR != RES_PATH_SEPARATOR
+ size_t len = length();
+ if (len > 0) {
+ char * buf = lockBuffer(len);
+ for (char * end = buf + len; buf < end; ++buf) {
+ if (*buf == OS_PATH_SEPARATOR)
+ *buf = RES_PATH_SEPARATOR;
+ }
+ unlockBuffer(len);
+ }
+#endif
+ return *this;
+}
+
+
+}; // namespace android
diff --git a/libs/utils/SystemClock.cpp b/libs/utils/SystemClock.cpp
new file mode 100644
index 0000000..2bdc0ce
--- /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 <linux/ioctl.h>
+#include <linux/rtc.h>
+#include <utils/Atomic.h>
+#include <linux/android_alarm.h>
+#endif
+
+#include <sys/time.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+
+#include <utils/SystemClock.h>
+#include <utils/Timers.h>
+
+#define LOG_TAG "SystemClock"
+#include "utils/Log.h"
+
+namespace android {
+
+/*
+ * 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 0000000..cebee99
--- /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 <utils/TextOutput.h>
+
+#include <utils/Debug.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+// ---------------------------------------------------------------------------
+
+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 0000000..74271ba
--- /dev/null
+++ b/libs/utils/Threads.cpp
@@ -0,0 +1,1126 @@
+/*
+ * 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 <utils/threads.h>
+#include <utils/Log.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <memory.h>
+#include <errno.h>
+#include <assert.h>
+#include <unistd.h>
+
+#if defined(HAVE_PTHREADS)
+# include <pthread.h>
+# include <sched.h>
+# include <sys/resource.h>
+#elif defined(HAVE_WIN32_THREADS)
+# include <windows.h>
+# include <stdint.h>
+# include <process.h>
+# define HAVE_CREATETHREAD // Cygwin, vs. HAVE__BEGINTHREADEX for MinGW
+#endif
+
+#if defined(HAVE_FUTEX)
+#include <private/utils/futex_synchro.h>
+#endif
+
+#if defined(HAVE_PRCTL)
+#include <sys/prctl.h>
+#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) {
+ 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) {
+ 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<Thread*>(user);
+ sp<Thread> strong(self->mHoldSelf);
+ wp<Thread> 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 0000000..835480d
--- /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 <utils/TimerProbe.h>
+
+#if ENABLE_TIMER_PROBE
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+#define LOG_TAG "time"
+
+namespace android {
+
+Vector<TimerProbe::Bucket> 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 0000000..2abc811
--- /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 <utils/Timers.h>
+#include <utils/ported.h> // may need usleep
+#include <utils/Log.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <time.h>
+#include <errno.h>
+
+#ifdef HAVE_WIN32_THREADS
+#include <windows.h>
+#endif
+
+nsecs_t systemTime(int clock)
+{
+#if defined(HAVE_POSIX_CLOCKS)
+ static const clockid_t clocks[] = {
+ CLOCK_REALTIME,
+ CLOCK_MONOTONIC,
+ CLOCK_PROCESS_CPUTIME_ID,
+ CLOCK_THREAD_CPUTIME_ID
+ };
+ 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 0000000..33f535f
--- /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 <unicode/utf16.h>
+
+#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 0000000..2c2d667
--- /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 <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <utils/Log.h>
+#include <utils/Errors.h>
+#include <utils/SharedBuffer.h>
+#include <utils/VectorImpl.h>
+
+/*****************************************************************************/
+
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+const size_t kMinVectorCapacity = 4;
+
+static inline size_t max(size_t a, size_t b) {
+ return a>b ? a : b;
+}
+
+// ----------------------------------------------------------------------------
+
+VectorImpl::VectorImpl(size_t itemSize, uint32_t flags)
+ : mStorage(0), mCount(0), mFlags(flags), mItemSize(itemSize)
+{
+}
+
+VectorImpl::VectorImpl(const VectorImpl& rhs)
+ : mStorage(rhs.mStorage), mCount(rhs.mCount),
+ mFlags(rhs.mFlags), mItemSize(rhs.mItemSize)
+{
+ if (mStorage) {
+ SharedBuffer::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<void*>(arrayImpl());
+ void* temp = 0;
+ ssize_t i = 1;
+ while (i < count) {
+ void* item = reinterpret_cast<char*>(array) + mItemSize*(i);
+ void* curr = reinterpret_cast<char*>(array) + mItemSize*(i-1);
+ if (cmp(curr, item, state) > 0) {
+
+ if (!temp) {
+ // we're going to have to modify the array...
+ array = editArrayImpl();
+ if (!array) return NO_MEMORY;
+ temp = malloc(mItemSize);
+ if (!temp) return NO_MEMORY;
+ _do_construct(temp, 1);
+ item = reinterpret_cast<char*>(array) + mItemSize*(i);
+ curr = reinterpret_cast<char*>(array) + mItemSize*(i-1);
+ }
+
+ _do_copy(temp, item, 1);
+
+ ssize_t j = i-1;
+ void* next = reinterpret_cast<char*>(array) + mItemSize*(i);
+ do {
+ _do_copy(next, curr, 1);
+ next = curr;
+ --j;
+ curr = reinterpret_cast<char*>(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(),
+ "[%p] replace: index=%d, size=%d", this, (int)index, (int)size());
+
+ void* item = editItemLocation(index);
+ if (item == 0)
+ return NO_MEMORY;
+ _do_destroy(item, 1);
+ if (prototype == 0) {
+ _do_construct(item, 1);
+ } else {
+ _do_copy(item, prototype, 1);
+ }
+ return ssize_t(index);
+}
+
+ssize_t VectorImpl::removeItemsAt(size_t index, size_t count)
+{
+ LOG_ASSERT((index+count)<=size(),
+ "[%p] remove: index=%d, count=%d, size=%d",
+ this, (int)index, (int)count, (int)size());
+
+ if ((index+count) > size())
+ return BAD_VALUE;
+ _shrink(index, count);
+ return index;
+}
+
+void VectorImpl::finish_vector()
+{
+ release_storage();
+ mStorage = 0;
+ mCount = 0;
+}
+
+void VectorImpl::clear()
+{
+ _shrink(0, mCount);
+}
+
+void* VectorImpl::editItemLocation(size_t index)
+{
+ LOG_ASSERT(index<capacity(),
+ "[%p] itemLocation: index=%d, capacity=%d, count=%d",
+ this, (int)index, (int)capacity(), (int)mCount);
+
+ void* buffer = editArrayImpl();
+ if (buffer)
+ return reinterpret_cast<char*>(buffer) + index*mItemSize;
+ return 0;
+}
+
+const void* VectorImpl::itemLocation(size_t index) const
+{
+ LOG_ASSERT(index<capacity(),
+ "[%p] editItemLocation: index=%d, capacity=%d, count=%d",
+ this, (int)index, (int)capacity(), (int)mCount);
+
+ const void* buffer = arrayImpl();
+ if (buffer)
+ return reinterpret_cast<const char*>(buffer) + index*mItemSize;
+ return 0;
+}
+
+ssize_t VectorImpl::setCapacity(size_t new_capacity)
+{
+ size_t current_capacity = capacity();
+ ssize_t amount = new_capacity - size();
+ if (amount <= 0) {
+ // we can't reduce the capacity
+ return current_capacity;
+ }
+ SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize);
+ if (sb) {
+ void* array = sb->data();
+ _do_copy(array, mStorage, size());
+ release_storage();
+ mStorage = const_cast<void*>(array);
+ } else {
+ return NO_MEMORY;
+ }
+ return new_capacity;
+}
+
+void VectorImpl::release_storage()
+{
+ if (mStorage) {
+ const SharedBuffer* sb = SharedBuffer::sharedBuffer(mStorage);
+ if (sb->release(SharedBuffer::eKeepStorage) == 1) {
+ _do_destroy(mStorage, mCount);
+ SharedBuffer::dealloc(sb);
+ }
+ }
+}
+
+void* VectorImpl::_grow(size_t where, size_t amount)
+{
+// 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<const uint8_t *>(mStorage) + where*mItemSize;
+ void* dest = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize;
+ _do_copy(dest, from, mCount-where);
+ }
+ release_storage();
+ mStorage = const_cast<void*>(array);
+ }
+ }
+ } else {
+ ssize_t s = mCount-where;
+ if (s>0) {
+ void* array = editArrayImpl();
+ void* to = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize;
+ const void* from = reinterpret_cast<const uint8_t *>(array) + where*mItemSize;
+ _do_move_forward(to, from, s);
+ }
+ }
+ mCount += amount;
+ void* free_space = const_cast<void*>(itemLocation(where));
+ return free_space;
+}
+
+void VectorImpl::_shrink(size_t where, size_t amount)
+{
+ if (!mStorage)
+ return;
+
+// 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<const uint8_t *>(mStorage) + (where+amount)*mItemSize;
+ void* dest = reinterpret_cast<uint8_t *>(array) + where*mItemSize;
+ _do_copy(dest, from, mCount-(where+amount));
+ }
+ release_storage();
+ mStorage = const_cast<void*>(array);
+ }
+ }
+ } else {
+ void* array = editArrayImpl();
+ void* to = reinterpret_cast<uint8_t *>(array) + where*mItemSize;
+ _do_destroy(to, amount);
+ ssize_t s = mCount-(where+amount);
+ if (s>0) {
+ const void* from = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize;
+ _do_move_backward(to, from, s);
+ }
+ }
+
+ // adjust the number of items...
+ mCount -= amount;
+}
+
+size_t VectorImpl::itemSize() const {
+ return mItemSize;
+}
+
+void VectorImpl::_do_construct(void* storage, size_t num) const
+{
+ if (!(mFlags & HAS_TRIVIAL_CTOR)) {
+ do_construct(storage, num);
+ }
+}
+
+void VectorImpl::_do_destroy(void* storage, size_t num) const
+{
+ if (!(mFlags & HAS_TRIVIAL_DTOR)) {
+ do_destroy(storage, num);
+ }
+}
+
+void VectorImpl::_do_copy(void* dest, const void* from, size_t num) const
+{
+ if (!(mFlags & HAS_TRIVIAL_COPY)) {
+ do_copy(dest, from, num);
+ } else {
+ memcpy(dest, from, num*itemSize());
+ }
+}
+
+void VectorImpl::_do_splat(void* dest, const void* item, size_t num) const {
+ do_splat(dest, item, num);
+}
+
+void VectorImpl::_do_move_forward(void* dest, const void* from, size_t num) const {
+ do_move_forward(dest, from, num);
+}
+
+void VectorImpl::_do_move_backward(void* dest, const void* from, size_t num) const {
+ do_move_backward(dest, from, num);
+}
+
+void VectorImpl::reservedVectorImpl1() { }
+void VectorImpl::reservedVectorImpl2() { }
+void VectorImpl::reservedVectorImpl3() { }
+void VectorImpl::reservedVectorImpl4() { }
+void VectorImpl::reservedVectorImpl5() { }
+void VectorImpl::reservedVectorImpl6() { }
+void VectorImpl::reservedVectorImpl7() { }
+void VectorImpl::reservedVectorImpl8() { }
+
+/*****************************************************************************/
+
+SortedVectorImpl::SortedVectorImpl(size_t itemSize, uint32_t flags)
+ : VectorImpl(itemSize, flags)
+{
+}
+
+SortedVectorImpl::SortedVectorImpl(const VectorImpl& rhs)
+: VectorImpl(rhs)
+{
+}
+
+SortedVectorImpl::~SortedVectorImpl()
+{
+}
+
+SortedVectorImpl& SortedVectorImpl::operator = (const SortedVectorImpl& rhs)
+{
+ return static_cast<SortedVectorImpl&>( VectorImpl::operator = (static_cast<const VectorImpl&>(rhs)) );
+}
+
+ssize_t SortedVectorImpl::indexOf(const void* item) const
+{
+ return _indexOrderOf(item);
+}
+
+size_t SortedVectorImpl::orderOf(const void* item) const
+{
+ size_t o;
+ _indexOrderOf(item, &o);
+ return o;
+}
+
+ssize_t SortedVectorImpl::_indexOrderOf(const void* item, size_t* order) const
+{
+ // binary search
+ ssize_t err = NAME_NOT_FOUND;
+ ssize_t l = 0;
+ ssize_t h = size()-1;
+ ssize_t mid;
+ const void* a = arrayImpl();
+ const size_t s = itemSize();
+ while (l <= h) {
+ mid = l + (h - l)/2;
+ const void* const curr = reinterpret_cast<const char *>(a) + (mid*s);
+ const int c = do_compare(curr, item);
+ if (c == 0) {
+ err = l = mid;
+ break;
+ } else if (c < 0) {
+ l = mid + 1;
+ } else {
+ h = mid - 1;
+ }
+ }
+ if (order) *order = l;
+ return err;
+}
+
+ssize_t SortedVectorImpl::add(const void* item)
+{
+ size_t order;
+ ssize_t index = _indexOrderOf(item, &order);
+ if (index < 0) {
+ index = VectorImpl::insertAt(item, order, 1);
+ } else {
+ index = VectorImpl::replaceAt(item, index);
+ }
+ return index;
+}
+
+ssize_t SortedVectorImpl::merge(const VectorImpl& vector)
+{
+ // naive merge...
+ if (!vector.isEmpty()) {
+ const void* buffer = vector.arrayImpl();
+ const size_t is = itemSize();
+ size_t s = vector.size();
+ for (size_t i=0 ; i<s ; i++) {
+ ssize_t err = add( reinterpret_cast<const char*>(buffer) + i*is );
+ if (err<0) {
+ return err;
+ }
+ }
+ }
+ return NO_ERROR;
+}
+
+ssize_t SortedVectorImpl::merge(const SortedVectorImpl& vector)
+{
+ // we've merging a sorted vector... nice!
+ ssize_t err = NO_ERROR;
+ if (!vector.isEmpty()) {
+ // first take care of the case where the vectors are sorted together
+ if (do_compare(vector.itemLocation(vector.size()-1), arrayImpl()) <= 0) {
+ err = VectorImpl::insertVectorAt(static_cast<const VectorImpl&>(vector), 0);
+ } else if (do_compare(vector.arrayImpl(), itemLocation(size()-1)) >= 0) {
+ err = VectorImpl::appendVector(static_cast<const VectorImpl&>(vector));
+ } else {
+ // this could be made a little better
+ err = merge(static_cast<const VectorImpl&>(vector));
+ }
+ }
+ return err;
+}
+
+ssize_t SortedVectorImpl::remove(const void* item)
+{
+ ssize_t i = indexOf(item);
+ if (i>=0) {
+ VectorImpl::removeItemsAt(i, 1);
+ }
+ return i;
+}
+
+void SortedVectorImpl::reservedSortedVectorImpl1() { };
+void SortedVectorImpl::reservedSortedVectorImpl2() { };
+void SortedVectorImpl::reservedSortedVectorImpl3() { };
+void SortedVectorImpl::reservedSortedVectorImpl4() { };
+void SortedVectorImpl::reservedSortedVectorImpl5() { };
+void SortedVectorImpl::reservedSortedVectorImpl6() { };
+void SortedVectorImpl::reservedSortedVectorImpl7() { };
+void SortedVectorImpl::reservedSortedVectorImpl8() { };
+
+
+/*****************************************************************************/
+
+}; // namespace android
+
diff --git a/libs/utils/ZipEntry.cpp b/libs/utils/ZipEntry.cpp
new file mode 100644
index 0000000..fbc9e67
--- /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 <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+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 0000000..89aa874
--- /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 <zlib.h>
+#define DEF_MEM_LEVEL 8 // normally in zutil.h?
+
+#include <memory.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <assert.h>
+
+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 0000000..d312daf
--- /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 0000000..ae8c719
--- /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 <zlib.h>
+
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <assert.h>
+
+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 0000000..bfbacfe
--- /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 <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include <zlib.h>
+
+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 0000000..e931d99
--- /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 0000000..2e3c3a0
--- /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 <utils/executablepath.h>
+#import <Carbon/Carbon.h>
+#include <unistd.h>
+
+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 0000000..b8d2a3d
--- /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 <utils/executablepath.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <limits.h>
+#include <stdio.h>
+
+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 0000000..c13760d
--- /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 <stdio.h>
+#include <limits.h>
+
+#include <sys/time.h>
+#include <sched.h>
+
+#include <errno.h>
+
+#include <private/utils/futex_synchro.h>
+
+
+// This futex glue code is need on desktop linux, but is part of klibc on ARM
+#if !defined(__arm__)
+
+#include <sys/syscall.h>
+typedef unsigned int u32;
+#define asmlinkage
+#define __user
+#include <linux/futex.h>
+#include <utils/Atomic.h>
+
+
+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(__arm__)
+
+
+// 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 0000000..dc89d15
--- /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 <utils/misc.h>
+
+#include <sys/stat.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <stdio.h>
+
+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 <stdio.h>
+/*
+ * 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 0000000..656e46f
--- /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 <utils/ported.h>
+
+#if defined(NEED_GETTIMEOFDAY) || defined(NEED_USLEEP)
+# include <sys/time.h>
+# include <windows.h>
+#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