diff options
Diffstat (limited to 'libs/utils')
-rw-r--r-- | libs/utils/Android.mk | 8 | ||||
-rw-r--r-- | libs/utils/Asset.cpp | 73 | ||||
-rw-r--r-- | libs/utils/FileMap.cpp | 15 | ||||
-rw-r--r-- | libs/utils/Looper.cpp | 212 | ||||
-rw-r--r-- | libs/utils/ObbFile.cpp | 29 | ||||
-rw-r--r-- | libs/utils/PropertyMap.cpp | 218 | ||||
-rw-r--r-- | libs/utils/RefBase.cpp | 217 | ||||
-rw-r--r-- | libs/utils/ResourceTypes.cpp | 216 | ||||
-rw-r--r-- | libs/utils/StreamingZipInflater.cpp | 4 | ||||
-rw-r--r-- | libs/utils/String16.cpp | 253 | ||||
-rw-r--r-- | libs/utils/String8.cpp | 432 | ||||
-rw-r--r-- | libs/utils/SystemClock.cpp | 8 | ||||
-rw-r--r-- | libs/utils/Threads.cpp | 29 | ||||
-rw-r--r-- | libs/utils/Tokenizer.cpp | 163 | ||||
-rw-r--r-- | libs/utils/Unicode.cpp | 575 | ||||
-rw-r--r-- | libs/utils/ZipFileCRO.cpp | 2 | ||||
-rw-r--r-- | libs/utils/ZipFileRO.cpp | 38 | ||||
-rw-r--r-- | libs/utils/tests/Android.mk | 3 | ||||
-rw-r--r-- | libs/utils/tests/Looper_test.cpp | 268 | ||||
-rw-r--r-- | libs/utils/tests/Unicode_test.cpp | 115 |
20 files changed, 2030 insertions, 848 deletions
diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index eb75ed8..e8d40ba 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -28,6 +28,7 @@ commonSources:= \ Flattenable.cpp \ ObbFile.cpp \ Pool.cpp \ + PropertyMap.cpp \ RefBase.cpp \ ResourceTypes.cpp \ SharedBuffer.cpp \ @@ -41,6 +42,8 @@ commonSources:= \ TextOutput.cpp \ Threads.cpp \ Timers.cpp \ + Tokenizer.cpp \ + Unicode.cpp \ VectorImpl.cpp \ ZipFileCRO.cpp \ ZipFileRO.cpp \ @@ -67,11 +70,6 @@ LOCAL_CFLAGS += -DMB_CUR_MAX=1 endif endif -ifeq ($(HOST_OS),darwin) -# MacOS doesn't have lseek64. However, off_t is 64-bit anyway. -LOCAL_CFLAGS += -DOFF_T_IS_64_BIT -endif - include $(BUILD_HOST_STATIC_LIBRARY) diff --git a/libs/utils/Asset.cpp b/libs/utils/Asset.cpp index cef7db4..a18294b 100644 --- a/libs/utils/Asset.cpp +++ b/libs/utils/Asset.cpp @@ -35,6 +35,9 @@ #include <fcntl.h> #include <errno.h> #include <assert.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/types.h> using namespace android; @@ -62,7 +65,7 @@ String8 Asset::getAssetAllocations() if (cur->isAllocated()) { res.append(" "); res.append(cur->getAssetSource()); - off_t size = (cur->getLength()+512)/1024; + off64_t size = (cur->getLength()+512)/1024; char buf[64]; sprintf(buf, ": %dK\n", (int)size); res.append(buf); @@ -119,7 +122,7 @@ Asset::~Asset(void) { _FileAsset* pAsset; status_t result; - off_t length; + off64_t length; int fd; fd = open(fileName, O_RDONLY | O_BINARY); @@ -132,12 +135,26 @@ Asset::~Asset(void) * 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); + // TODO(kroot): replace this with fstat despite the plea above. +#if 1 + length = lseek64(fd, 0, SEEK_END); if (length < 0) { ::close(fd); return NULL; } - (void) lseek(fd, 0, SEEK_SET); + (void) lseek64(fd, 0, SEEK_SET); +#else + struct stat st; + if (fstat(fd, &st) < 0) { + ::close(fd); + return NULL; + } + + if (!S_ISREG(st.st_mode)) { + ::close(fd); + return NULL; + } +#endif pAsset = new _FileAsset; result = pAsset->openChunk(fileName, fd, 0, length); @@ -162,7 +179,7 @@ Asset::~Asset(void) { _CompressedAsset* pAsset; status_t result; - off_t fileLen; + off64_t fileLen; bool scanResult; long offset; int method; @@ -215,7 +232,7 @@ Asset::~Asset(void) /* * Create a new Asset from part of an open file. */ -/*static*/ Asset* Asset::createFromFileSegment(int fd, off_t offset, +/*static*/ Asset* Asset::createFromFileSegment(int fd, off64_t offset, size_t length, AccessMode mode) { _FileAsset* pAsset; @@ -233,7 +250,7 @@ Asset::~Asset(void) /* * Create a new Asset from compressed data in an open file. */ -/*static*/ Asset* Asset::createFromCompressedData(int fd, off_t offset, +/*static*/ Asset* Asset::createFromCompressedData(int fd, off64_t offset, int compressionMethod, size_t uncompressedLen, size_t compressedLen, AccessMode mode) { @@ -295,9 +312,9 @@ Asset::~Asset(void) * * 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) +off64_t Asset::handleSeek(off64_t offset, int whence, off64_t curPosn, off64_t maxPosn) { - off_t newOffset; + off64_t newOffset; switch (whence) { case SEEK_SET: @@ -311,15 +328,15 @@ off_t Asset::handleSeek(off_t offset, int whence, off_t curPosn, off_t maxPosn) break; default: LOGW("unexpected whence %d\n", whence); - // this was happening due to an off_t size mismatch + // this was happening due to an off64_t size mismatch assert(false); - return (off_t) -1; + return (off64_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 (off64_t) -1; } return newOffset; @@ -353,7 +370,7 @@ _FileAsset::~_FileAsset(void) * * Zero-length chunks are allowed. */ -status_t _FileAsset::openChunk(const char* fileName, int fd, off_t offset, size_t length) +status_t _FileAsset::openChunk(const char* fileName, int fd, off64_t offset, size_t length) { assert(mFp == NULL); // no reopen assert(mMap == NULL); @@ -363,15 +380,15 @@ status_t _FileAsset::openChunk(const char* fileName, int fd, off_t offset, size_ /* * Seek to end to get file length. */ - off_t fileLength; - fileLength = lseek(fd, 0, SEEK_END); - if (fileLength == (off_t) -1) { + off64_t fileLength; + fileLength = lseek64(fd, 0, SEEK_END); + if (fileLength == (off64_t) -1) { // probably a bad file descriptor LOGD("failed lseek (errno=%d)\n", errno); return UNKNOWN_ERROR; } - if ((off_t) (offset + length) > fileLength) { + if ((off64_t) (offset + length) > fileLength) { LOGD("start (%ld) + len (%ld) > end (%ld)\n", (long) offset, (long) length, (long) fileLength); return BAD_INDEX; @@ -482,21 +499,21 @@ ssize_t _FileAsset::read(void* buf, size_t count) /* * Seek to a new position. */ -off_t _FileAsset::seek(off_t offset, int whence) +off64_t _FileAsset::seek(off64_t offset, int whence) { - off_t newPosn; - long actualOffset; + off64_t newPosn; + off64_t actualOffset; // compute new position within chunk newPosn = handleSeek(offset, whence, mOffset, mLength); - if (newPosn == (off_t) -1) + if (newPosn == (off64_t) -1) return newPosn; - actualOffset = (long) (mStart + newPosn); + actualOffset = mStart + newPosn; if (mFp != NULL) { if (fseek(mFp, (long) actualOffset, SEEK_SET) != 0) - return (off_t) -1; + return (off64_t) -1; } mOffset = actualOffset - mStart; @@ -603,7 +620,7 @@ const void* _FileAsset::getBuffer(bool wordAligned) } } -int _FileAsset::openFileDescriptor(off_t* outStart, off_t* outLength) const +int _FileAsset::openFileDescriptor(off64_t* outStart, off64_t* outLength) const { if (mMap != NULL) { const char* fname = mMap->getFileName(); @@ -678,7 +695,7 @@ _CompressedAsset::~_CompressedAsset(void) * 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, +status_t _CompressedAsset::openChunk(int fd, off64_t offset, int compressionMethod, size_t uncompressedLen, size_t compressedLen) { assert(mFd < 0); // no re-open @@ -782,13 +799,13 @@ ssize_t _CompressedAsset::read(void* buf, size_t count) * expensive, because it requires plowing through a bunch of compressed * data. */ -off_t _CompressedAsset::seek(off_t offset, int whence) +off64_t _CompressedAsset::seek(off64_t offset, int whence) { - off_t newPosn; + off64_t newPosn; // compute new position within chunk newPosn = handleSeek(offset, whence, mOffset, mUncompressedLen); - if (newPosn == (off_t) -1) + if (newPosn == (off64_t) -1) return newPosn; if (mZipInflater) { diff --git a/libs/utils/FileMap.cpp b/libs/utils/FileMap.cpp index e1ba9b2..c220a90 100644 --- a/libs/utils/FileMap.cpp +++ b/libs/utils/FileMap.cpp @@ -63,16 +63,18 @@ FileMap::~FileMap(void) free(mFileName); } #ifdef HAVE_POSIX_FILEMAP - if (munmap(mBasePtr, mBaseLength) != 0) { + if (mBasePtr && munmap(mBasePtr, mBaseLength) != 0) { LOGD("munmap(%p, %d) failed\n", mBasePtr, (int) mBaseLength); } #endif #ifdef HAVE_WIN32_FILEMAP - if ( UnmapViewOfFile(mBasePtr) == 0) { + if (mBasePtr && UnmapViewOfFile(mBasePtr) == 0) { LOGD("UnmapViewOfFile(%p) failed, error = %ld\n", mBasePtr, GetLastError() ); } - CloseHandle(mFileMapping); + if (mFileMapping != INVALID_HANDLE_VALUE) { + CloseHandle(mFileMapping); + } CloseHandle(mFileHandle); #endif } @@ -86,11 +88,12 @@ FileMap::~FileMap(void) * * Returns "false" on failure. */ -bool FileMap::create(const char* origFileName, int fd, off_t offset, size_t length, bool readOnly) +bool FileMap::create(const char* origFileName, int fd, off64_t offset, size_t length, + bool readOnly) { #ifdef HAVE_WIN32_FILEMAP int adjust; - off_t adjOffset; + off64_t adjOffset; size_t adjLength; if (mPageSize == -1) { @@ -129,7 +132,7 @@ bool FileMap::create(const char* origFileName, int fd, off_t offset, size_t leng #endif #ifdef HAVE_POSIX_FILEMAP int prot, flags, adjust; - off_t adjOffset; + off64_t adjOffset; size_t adjLength; void* ptr; diff --git a/libs/utils/Looper.cpp b/libs/utils/Looper.cpp index a5363d6..18f858b 100644 --- a/libs/utils/Looper.cpp +++ b/libs/utils/Looper.cpp @@ -19,10 +19,27 @@ #include <unistd.h> #include <fcntl.h> +#include <limits.h> namespace android { +// --- WeakMessageHandler --- + +WeakMessageHandler::WeakMessageHandler(const wp<MessageHandler>& handler) : + mHandler(handler) { +} + +void WeakMessageHandler::handleMessage(const Message& message) { + sp<MessageHandler> handler = mHandler.promote(); + if (handler != NULL) { + handler->handleMessage(message); + } +} + + +// --- Looper --- + #ifdef LOOPER_USES_EPOLL // Hint for number of file descriptors to be associated with the epoll instance. static const int EPOLL_SIZE_HINT = 8; @@ -35,8 +52,8 @@ static pthread_once_t gTLSOnce = PTHREAD_ONCE_INIT; static pthread_key_t gTLSKey = 0; Looper::Looper(bool allowNonCallbacks) : - mAllowNonCallbacks(allowNonCallbacks), - mResponseIndex(0) { + mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false), + mResponseIndex(0), mNextMessageUptime(LLONG_MAX) { int wakeFds[2]; int result = pipe(wakeFds); LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno); @@ -161,17 +178,21 @@ int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outDa for (;;) { while (mResponseIndex < mResponses.size()) { const Response& response = mResponses.itemAt(mResponseIndex++); - if (! response.request.callback) { + ALooper_callbackFunc callback = response.request.callback; + if (!callback) { + int ident = response.request.ident; + int fd = response.request.fd; + int events = response.events; + void* data = response.request.data; #if DEBUG_POLL_AND_WAKE LOGD("%p ~ pollOnce - returning signalled identifier %d: " - "fd=%d, events=0x%x, data=%p", this, - response.request.ident, response.request.fd, - response.events, response.request.data); + "fd=%d, events=0x%x, data=%p", + this, ident, fd, events, data); #endif - if (outFd != NULL) *outFd = response.request.fd; - if (outEvents != NULL) *outEvents = response.events; - if (outData != NULL) *outData = response.request.data; - return response.request.ident; + if (outFd != NULL) *outFd = fd; + if (outEvents != NULL) *outEvents = events; + if (outData != NULL) *outData = data; + return ident; } } @@ -194,6 +215,25 @@ int Looper::pollInner(int timeoutMillis) { LOGD("%p ~ pollOnce - waiting: timeoutMillis=%d", this, timeoutMillis); #endif + // Adjust the timeout based on when the next message is due. + if (timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + if (mNextMessageUptime <= now) { + timeoutMillis = 0; + } else { + uint64_t delay = (mNextMessageUptime - now + 999999LL) / 1000000LL; + if (delay < INT_MAX + && (timeoutMillis < 0 || int(delay) < timeoutMillis)) { + timeoutMillis = int(delay); + } + } +#if DEBUG_POLL_AND_WAKE + LOGD("%p ~ pollOnce - next message in %lldns, adjusted timeout: timeoutMillis=%d", + this, mNextMessageUptime - now, timeoutMillis); +#endif + } + + // Poll. int result = ALOOPER_POLL_WAKE; mResponses.clear(); mResponseIndex = 0; @@ -205,7 +245,6 @@ int Looper::pollInner(int timeoutMillis) { #ifdef LOOPER_USES_EPOLL struct epoll_event eventItems[EPOLL_MAX_EVENTS]; int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis); - bool acquiredLock = false; #else // Wait for wakeAndLock() waiters to run then set mPolling to true. mLock.lock(); @@ -219,16 +258,20 @@ int Looper::pollInner(int timeoutMillis) { int eventCount = poll(mRequestedFds.editArray(), requestedCount, timeoutMillis); #endif + // Acquire lock. + mLock.lock(); + + // Check for poll error. if (eventCount < 0) { if (errno == EINTR) { goto Done; } - LOGW("Poll failed with an unexpected error, errno=%d", errno); result = ALOOPER_POLL_ERROR; goto Done; } + // Check for poll timeout. if (eventCount == 0) { #if DEBUG_POLL_AND_WAKE LOGD("%p ~ pollOnce - timeout", this); @@ -237,6 +280,7 @@ int Looper::pollInner(int timeoutMillis) { goto Done; } + // Handle all events. #if DEBUG_POLL_AND_WAKE LOGD("%p ~ pollOnce - handling events from %d fds", this, eventCount); #endif @@ -252,11 +296,6 @@ int Looper::pollInner(int timeoutMillis) { LOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents); } } else { - if (! acquiredLock) { - mLock.lock(); - acquiredLock = true; - } - ssize_t requestIndex = mRequests.indexOfKey(fd); if (requestIndex >= 0) { int events = 0; @@ -271,9 +310,6 @@ int Looper::pollInner(int timeoutMillis) { } } } - if (acquiredLock) { - mLock.unlock(); - } Done: ; #else for (size_t i = 0; i < requestedCount; i++) { @@ -301,15 +337,12 @@ Done: ; } } } - Done: // Set mPolling to false and wake up the wakeAndLock() waiters. - mLock.lock(); mPolling = false; if (mWaiters != 0) { mAwake.broadcast(); } - mLock.unlock(); #endif #ifdef LOOPER_STATISTICS @@ -335,19 +368,59 @@ Done: } #endif + // Invoke pending message callbacks. + mNextMessageUptime = LLONG_MAX; + while (mMessageEnvelopes.size() != 0) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(0); + if (messageEnvelope.uptime <= now) { + // Remove the envelope from the list. + // We keep a strong reference to the handler until the call to handleMessage + // finishes. Then we drop it so that the handler can be deleted *before* + // we reacquire our lock. + { // obtain handler + sp<MessageHandler> handler = messageEnvelope.handler; + Message message = messageEnvelope.message; + mMessageEnvelopes.removeAt(0); + mSendingMessage = true; + mLock.unlock(); + +#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS + LOGD("%p ~ pollOnce - sending message: handler=%p, what=%d", + this, handler.get(), message.what); +#endif + handler->handleMessage(message); + } // release handler + + mLock.lock(); + mSendingMessage = false; + result = ALOOPER_POLL_CALLBACK; + } else { + // The last message left at the head of the queue determines the next wakeup time. + mNextMessageUptime = messageEnvelope.uptime; + break; + } + } + + // Release lock. + mLock.unlock(); + + // Invoke all response callbacks. for (size_t i = 0; i < mResponses.size(); i++) { const Response& response = mResponses.itemAt(i); - if (response.request.callback) { + ALooper_callbackFunc callback = response.request.callback; + if (callback) { + int fd = response.request.fd; + int events = response.events; + void* data = response.request.data; #if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS - LOGD("%p ~ pollOnce - invoking callback: fd=%d, events=0x%x, data=%p", this, - response.request.fd, response.events, response.request.data); + LOGD("%p ~ pollOnce - invoking fd event callback %p: fd=%d, events=0x%x, data=%p", + this, callback, fd, events, data); #endif - int callbackResult = response.request.callback( - response.request.fd, response.events, response.request.data); + int callbackResult = callback(fd, events, data); if (callbackResult == 0) { - removeFd(response.request.fd); + removeFd(fd); } - result = ALOOPER_POLL_CALLBACK; } } @@ -593,4 +666,83 @@ void Looper::wakeAndLock() { } #endif +void Looper::sendMessage(const sp<MessageHandler>& handler, const Message& message) { + sendMessageAtTime(LLONG_MIN, handler, message); +} + +void Looper::sendMessageDelayed(nsecs_t uptimeDelay, const sp<MessageHandler>& handler, + const Message& message) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + sendMessageAtTime(now + uptimeDelay, handler, message); +} + +void Looper::sendMessageAtTime(nsecs_t uptime, const sp<MessageHandler>& handler, + const Message& message) { +#if DEBUG_CALLBACKS + LOGD("%p ~ sendMessageAtTime - uptime=%lld, handler=%p, what=%d", + this, uptime, handler.get(), message.what); +#endif + + size_t i = 0; + { // acquire lock + AutoMutex _l(mLock); + + size_t messageCount = mMessageEnvelopes.size(); + while (i < messageCount && uptime >= mMessageEnvelopes.itemAt(i).uptime) { + i += 1; + } + + MessageEnvelope messageEnvelope(uptime, handler, message); + mMessageEnvelopes.insertAt(messageEnvelope, i, 1); + + // Optimization: If the Looper is currently sending a message, then we can skip + // the call to wake() because the next thing the Looper will do after processing + // messages is to decide when the next wakeup time should be. In fact, it does + // not even matter whether this code is running on the Looper thread. + if (mSendingMessage) { + return; + } + } // release lock + + // Wake the poll loop only when we enqueue a new message at the head. + if (i == 0) { + wake(); + } +} + +void Looper::removeMessages(const sp<MessageHandler>& handler) { +#if DEBUG_CALLBACKS + LOGD("%p ~ removeMessages - handler=%p", this, handler.get()); +#endif + + { // acquire lock + AutoMutex _l(mLock); + + for (size_t i = mMessageEnvelopes.size(); i != 0; ) { + const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(--i); + if (messageEnvelope.handler == handler) { + mMessageEnvelopes.removeAt(i); + } + } + } // release lock +} + +void Looper::removeMessages(const sp<MessageHandler>& handler, int what) { +#if DEBUG_CALLBACKS + LOGD("%p ~ removeMessages - handler=%p, what=%d", this, handler.get(), what); +#endif + + { // acquire lock + AutoMutex _l(mLock); + + for (size_t i = mMessageEnvelopes.size(); i != 0; ) { + const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(--i); + if (messageEnvelope.handler == handler + && messageEnvelope.message.what == what) { + mMessageEnvelopes.removeAt(i); + } + } + } // release lock +} + } // namespace android diff --git a/libs/utils/ObbFile.cpp b/libs/utils/ObbFile.cpp index 2c3724c..2907b56 100644 --- a/libs/utils/ObbFile.cpp +++ b/libs/utils/ObbFile.cpp @@ -22,6 +22,8 @@ #include <unistd.h> #define LOG_TAG "ObbFile" + +#include <utils/Compat.h> #include <utils/Log.h> #include <utils/ObbFile.h> @@ -67,17 +69,6 @@ _rc; }) #endif -/* - * Work around situations where off_t is 64-bit and use off64_t in - * situations where it's 32-bit. - */ -#ifdef OFF_T_IS_64_BIT -#define my_lseek64 lseek -typedef off_t my_off64_t; -#else -#define my_lseek64 lseek64 -typedef off64_t my_off64_t; -#endif namespace android { @@ -125,7 +116,7 @@ bool ObbFile::readFrom(int fd) bool ObbFile::parseObbFile(int fd) { - my_off64_t fileLength = my_lseek64(fd, 0, SEEK_END); + off64_t fileLength = lseek64(fd, 0, SEEK_END); if (fileLength < kFooterMinSize) { if (fileLength < 0) { @@ -140,7 +131,7 @@ bool ObbFile::parseObbFile(int fd) size_t footerSize; { - my_lseek64(fd, fileLength - kFooterTagSize, SEEK_SET); + lseek64(fd, fileLength - kFooterTagSize, SEEK_SET); char *footer = new char[kFooterTagSize]; actual = TEMP_FAILURE_RETRY(read(fd, footer, kFooterTagSize)); @@ -171,8 +162,8 @@ bool ObbFile::parseObbFile(int fd) } } - my_off64_t fileOffset = fileLength - footerSize - kFooterTagSize; - if (my_lseek64(fd, fileOffset, SEEK_SET) != fileOffset) { + off64_t fileOffset = fileLength - footerSize - kFooterTagSize; + if (lseek64(fd, fileOffset, SEEK_SET) != fileOffset) { LOGW("seek %lld failed: %s\n", fileOffset, strerror(errno)); return false; } @@ -211,10 +202,10 @@ bool ObbFile::parseObbFile(int fd) memcpy(&mSalt, (unsigned char*)scanBuf + kSaltOffset, sizeof(mSalt)); - uint32_t packageNameLen = get4LE((unsigned char*)scanBuf + kPackageNameLenOffset); - if (packageNameLen <= 0 + size_t packageNameLen = get4LE((unsigned char*)scanBuf + kPackageNameLenOffset); + if (packageNameLen == 0 || packageNameLen > (footerSize - kPackageNameOffset)) { - LOGW("bad ObbFile package name length (0x%04x; 0x%04x possible)\n", + LOGW("bad ObbFile package name length (0x%04zx; 0x%04zx possible)\n", packageNameLen, footerSize - kPackageNameOffset); free(scanBuf); return false; @@ -257,7 +248,7 @@ bool ObbFile::writeTo(int fd) return false; } - my_lseek64(fd, 0, SEEK_END); + lseek64(fd, 0, SEEK_END); if (mPackageName.size() == 0 || mVersion == -1) { LOGW("tried to write uninitialized ObbFile data\n"); diff --git a/libs/utils/PropertyMap.cpp b/libs/utils/PropertyMap.cpp new file mode 100644 index 0000000..d472d45 --- /dev/null +++ b/libs/utils/PropertyMap.cpp @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "PropertyMap" + +#include <stdlib.h> +#include <string.h> + +#include <utils/PropertyMap.h> +#include <utils/Log.h> + +// Enables debug output for the parser. +#define DEBUG_PARSER 0 + +// Enables debug output for parser performance. +#define DEBUG_PARSER_PERFORMANCE 0 + + +namespace android { + +static const char* WHITESPACE = " \t\r"; +static const char* WHITESPACE_OR_PROPERTY_DELIMITER = " \t\r="; + + +// --- PropertyMap --- + +PropertyMap::PropertyMap() { +} + +PropertyMap::~PropertyMap() { +} + +void PropertyMap::clear() { + mProperties.clear(); +} + +void PropertyMap::addProperty(const String8& key, const String8& value) { + mProperties.add(key, value); +} + +bool PropertyMap::hasProperty(const String8& key) const { + return mProperties.indexOfKey(key) >= 0; +} + +bool PropertyMap::tryGetProperty(const String8& key, String8& outValue) const { + ssize_t index = mProperties.indexOfKey(key); + if (index < 0) { + return false; + } + + outValue = mProperties.valueAt(index); + return true; +} + +bool PropertyMap::tryGetProperty(const String8& key, bool& outValue) const { + int32_t intValue; + if (!tryGetProperty(key, intValue)) { + return false; + } + + outValue = intValue; + return true; +} + +bool PropertyMap::tryGetProperty(const String8& key, int32_t& outValue) const { + String8 stringValue; + if (! tryGetProperty(key, stringValue) || stringValue.length() == 0) { + return false; + } + + char* end; + int value = strtol(stringValue.string(), & end, 10); + if (*end != '\0') { + LOGW("Property key '%s' has invalid value '%s'. Expected an integer.", + key.string(), stringValue.string()); + return false; + } + outValue = value; + return true; +} + +bool PropertyMap::tryGetProperty(const String8& key, float& outValue) const { + String8 stringValue; + if (! tryGetProperty(key, stringValue) || stringValue.length() == 0) { + return false; + } + + char* end; + float value = strtof(stringValue.string(), & end); + if (*end != '\0') { + LOGW("Property key '%s' has invalid value '%s'. Expected a float.", + key.string(), stringValue.string()); + return false; + } + outValue = value; + return true; +} + +void PropertyMap::addAll(const PropertyMap* map) { + for (size_t i = 0; i < map->mProperties.size(); i++) { + mProperties.add(map->mProperties.keyAt(i), map->mProperties.valueAt(i)); + } +} + +status_t PropertyMap::load(const String8& filename, PropertyMap** outMap) { + *outMap = NULL; + + Tokenizer* tokenizer; + status_t status = Tokenizer::open(filename, &tokenizer); + if (status) { + LOGE("Error %d opening property file %s.", status, filename.string()); + } else { + PropertyMap* map = new PropertyMap(); + if (!map) { + LOGE("Error allocating property map."); + status = NO_MEMORY; + } else { +#if DEBUG_PARSER_PERFORMANCE + nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); +#endif + Parser parser(map, tokenizer); + status = parser.parse(); +#if DEBUG_PARSER_PERFORMANCE + nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; + LOGD("Parsed property file '%s' %d lines in %0.3fms.", + tokenizer->getFilename().string(), tokenizer->getLineNumber(), + elapsedTime / 1000000.0); +#endif + if (status) { + delete map; + } else { + *outMap = map; + } + } + delete tokenizer; + } + return status; +} + + +// --- PropertyMap::Parser --- + +PropertyMap::Parser::Parser(PropertyMap* map, Tokenizer* tokenizer) : + mMap(map), mTokenizer(tokenizer) { +} + +PropertyMap::Parser::~Parser() { +} + +status_t PropertyMap::Parser::parse() { + while (!mTokenizer->isEof()) { +#if DEBUG_PARSER + LOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(), + mTokenizer->peekRemainderOfLine().string()); +#endif + + mTokenizer->skipDelimiters(WHITESPACE); + + if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { + String8 keyToken = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER); + if (keyToken.isEmpty()) { + LOGE("%s: Expected non-empty property key.", mTokenizer->getLocation().string()); + return BAD_VALUE; + } + + mTokenizer->skipDelimiters(WHITESPACE); + + if (mTokenizer->nextChar() != '=') { + LOGE("%s: Expected '=' between property key and value.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + + mTokenizer->skipDelimiters(WHITESPACE); + + String8 valueToken = mTokenizer->nextToken(WHITESPACE); + if (valueToken.find("\\", 0) >= 0 || valueToken.find("\"", 0) >= 0) { + LOGE("%s: Found reserved character '\\' or '\"' in property value.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + + mTokenizer->skipDelimiters(WHITESPACE); + if (!mTokenizer->isEol()) { + LOGE("%s: Expected end of line, got '%s'.", + mTokenizer->getLocation().string(), + mTokenizer->peekRemainderOfLine().string()); + return BAD_VALUE; + } + + if (mMap->hasProperty(keyToken)) { + LOGE("%s: Duplicate property value for key '%s'.", + mTokenizer->getLocation().string(), keyToken.string()); + return BAD_VALUE; + } + + mMap->addProperty(keyToken, valueToken); + } + + mTokenizer->nextLine(); + } + return NO_ERROR; +} + +} // namespace android diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp index 0bd1af4..bb6c125 100644 --- a/libs/utils/RefBase.cpp +++ b/libs/utils/RefBase.cpp @@ -20,9 +20,9 @@ #include <utils/Atomic.h> #include <utils/CallStack.h> -#include <utils/KeyedVector.h> #include <utils/Log.h> #include <utils/threads.h> +#include <utils/TextOutput.h> #include <stdlib.h> #include <stdio.h> @@ -34,6 +34,7 @@ // compile with refcounting debugging enabled #define DEBUG_REFS 0 +#define DEBUG_REFS_FATAL_SANITY_CHECKS 0 #define DEBUG_REFS_ENABLED_BY_DEFAULT 1 #define DEBUG_REFS_CALLSTACK_ENABLED 1 @@ -69,8 +70,10 @@ public: void addStrongRef(const void* /*id*/) { } void removeStrongRef(const void* /*id*/) { } + void renameStrongRefId(const void* /*old_id*/, const void* /*new_id*/) { } void addWeakRef(const void* /*id*/) { } void removeWeakRef(const void* /*id*/) { } + void renameWeakRefId(const void* /*old_id*/, const void* /*new_id*/) { } void printRefs() const { } void trackMe(bool, bool) { } @@ -86,39 +89,91 @@ public: , 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!"); + bool dumpStack = false; + if (!mRetain && mStrongRefs != NULL) { + dumpStack = true; +#if DEBUG_REFS_FATAL_SANITY_CHECKS + LOG_ALWAYS_FATAL("Strong references remain!"); +#else + LOGE("Strong references remain:"); +#endif + ref_entry* refs = mStrongRefs; + while (refs) { + char inc = refs->ref >= 0 ? '+' : '-'; + LOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref); +#if DEBUG_REFS_CALLSTACK_ENABLED + refs->stack.dump(); +#endif; + refs = refs->next; + } + } + + if (!mRetain && mWeakRefs != NULL) { + dumpStack = true; +#if DEBUG_REFS_FATAL_SANITY_CHECKS + LOG_ALWAYS_FATAL("Weak references remain:"); +#else + LOGE("Weak references remain!"); +#endif + ref_entry* refs = mWeakRefs; + while (refs) { + char inc = refs->ref >= 0 ? '+' : '-'; + LOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref); +#if DEBUG_REFS_CALLSTACK_ENABLED + refs->stack.dump(); +#endif; + refs = refs->next; + } + } + if (dumpStack) { + LOGE("above errors at:"); + CallStack stack; + stack.update(); + stack.dump(); + } } - void addStrongRef(const void* id) - { + void addStrongRef(const void* id) { + //LOGD_IF(mTrackEnabled, + // "addStrongRef: RefBase=%p, id=%p", mBase, id); addRef(&mStrongRefs, id, mStrong); } - void removeStrongRef(const void* id) - { - if (!mRetain) + void removeStrongRef(const void* id) { + //LOGD_IF(mTrackEnabled, + // "removeStrongRef: RefBase=%p, id=%p", mBase, id); + if (!mRetain) { removeRef(&mStrongRefs, id); - else + } else { addRef(&mStrongRefs, id, -mStrong); + } } - void addWeakRef(const void* id) - { + void renameStrongRefId(const void* old_id, const void* new_id) { + //LOGD_IF(mTrackEnabled, + // "renameStrongRefId: RefBase=%p, oid=%p, nid=%p", + // mBase, old_id, new_id); + renameRefsId(mStrongRefs, old_id, new_id); + } + + void addWeakRef(const void* id) { addRef(&mWeakRefs, id, mWeak); } - void removeWeakRef(const void* id) - { - if (!mRetain) + void removeWeakRef(const void* id) { + if (!mRetain) { removeRef(&mWeakRefs, id); - else + } else { addRef(&mWeakRefs, id, -mWeak); + } + } + + void renameWeakRefId(const void* old_id, const void* new_id) { + renameRefsId(mWeakRefs, old_id, new_id); } void trackMe(bool track, bool retain) @@ -132,8 +187,7 @@ public: String8 text; { - AutoMutex _l(const_cast<weakref_impl*>(this)->mMutex); - + Mutex::Autolock _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); @@ -172,6 +226,7 @@ private: { 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 @@ -181,7 +236,6 @@ private: #if DEBUG_REFS_CALLSTACK_ENABLED ref->stack.update(2); #endif - ref->next = *refs; *refs = ref; } @@ -192,20 +246,52 @@ private: if (mTrackEnabled) { AutoMutex _l(mMutex); - ref_entry* ref = *refs; + ref_entry* const head = *refs; + ref_entry* ref = head; while (ref != NULL) { if (ref->id == id) { *refs = ref->next; delete ref; return; } - refs = &ref->next; ref = *refs; } - - LOG_ALWAYS_FATAL("RefBase: removing id %p on RefBase %p (weakref_type %p) that doesn't exist!", - id, mBase, this); + +#if DEBUG_REFS_FATAL_SANITY_CHECKS + LOG_ALWAYS_FATAL("RefBase: removing id %p on RefBase %p" + "(weakref_type %p) that doesn't exist!", + id, mBase, this); +#endif + + LOGE("RefBase: removing id %p on RefBase %p" + "(weakref_type %p) that doesn't exist!", + id, mBase, this); + + ref = head; + while (ref) { + char inc = ref->ref >= 0 ? '+' : '-'; + LOGD("\t%c ID %p (ref %d):", inc, ref->id, ref->ref); + ref = ref->next; + } + + CallStack stack; + stack.update(); + stack.dump(); + } + } + + void renameRefsId(ref_entry* r, const void* old_id, const void* new_id) + { + if (mTrackEnabled) { + AutoMutex _l(mMutex); + ref_entry* ref = r; + while (ref != NULL) { + if (ref->id == old_id) { + ref->id = new_id; + } + ref = ref->next; + } } } @@ -235,44 +321,6 @@ private: // 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 }; @@ -281,7 +329,6 @@ private: void RefBase::incStrong(const void* id) const { weakref_impl* const refs = mRefs; - refs->addWeakRef(id); refs->incWeak(id); refs->addStrongRef(id); @@ -313,14 +360,12 @@ void RefBase::decStrong(const void* id) const 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); @@ -372,7 +417,7 @@ void RefBase::weakref_type::decWeak(const void* id) if (impl->mStrong == INITIAL_STRONG_VALUE) delete impl->mBase; else { -// LOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase); + // LOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase); delete impl; } } else { @@ -432,7 +477,6 @@ bool RefBase::weakref_type::attemptIncStrong(const void* id) } } - impl->addWeakRef(id); impl->addStrongRef(id); #if PRINT_REFS @@ -450,7 +494,7 @@ bool RefBase::weakref_type::attemptIncStrong(const void* id) 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); @@ -497,14 +541,11 @@ RefBase::weakref_type* RefBase::getWeakRefs() const 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; } } @@ -530,5 +571,37 @@ bool RefBase::onIncStrongAttempted(uint32_t flags, const void* id) void RefBase::onLastWeakRef(const void* /*id*/) { } - + +// --------------------------------------------------------------------------- + +void RefBase::moveReferences(void* dst, void const* src, size_t n, + const ReferenceConverterBase& caster) +{ +#if DEBUG_REFS + const size_t itemSize = caster.getReferenceTypeSize(); + for (size_t i=0 ; i<n ; i++) { + void* d = reinterpret_cast<void *>(intptr_t(dst) + i*itemSize); + void const* s = reinterpret_cast<void const*>(intptr_t(src) + i*itemSize); + RefBase* ref(reinterpret_cast<RefBase*>(caster.getReferenceBase(d))); + ref->mRefs->renameStrongRefId(s, d); + ref->mRefs->renameWeakRefId(s, d); + } +#endif +} + +// --------------------------------------------------------------------------- + +TextOutput& printStrongPointer(TextOutput& to, const void* val) +{ + to << "sp<>(" << val << ")"; + return to; +} + +TextOutput& printWeakPointer(TextOutput& to, const void* val) +{ + to << "wp<>(" << val << ")"; + return to; +} + + }; // namespace android diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 57aaf24..a6cdb23 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -396,6 +396,12 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) mStringPoolSize = (mHeader->header.size-mHeader->stringsStart)/charSize; } else { + // check invariant: styles starts before end of data + if (mHeader->stylesStart >= (mHeader->header.size-sizeof(uint16_t))) { + LOGW("Bad style block: style block starts at %d past data size of %d\n", + (int)mHeader->stylesStart, (int)mHeader->header.size); + return (mError=BAD_TYPE); + } // 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", @@ -517,15 +523,51 @@ void ResStringPool::uninit() } } -#define DECODE_LENGTH(str, chrsz, len) \ - len = *(str); \ - if (*(str)&(1<<(chrsz*8-1))) { \ - (str)++; \ - len = (((len)&((1<<(chrsz*8-1))-1))<<(chrsz*8)) + *(str); \ - } \ - (str)++; +/** + * Strings in UTF-16 format have length indicated by a length encoded in the + * stored data. It is either 1 or 2 characters of length data. This allows a + * maximum length of 0x7FFFFFF (2147483647 bytes), but if you're storing that + * much data in a string, you're abusing them. + * + * If the high bit is set, then there are two characters or 4 bytes of length + * data encoded. In that case, drop the high bit of the first character and + * add it together with the next character. + */ +static inline size_t +decodeLength(const char16_t** str) +{ + size_t len = **str; + if ((len & 0x8000) != 0) { + (*str)++; + len = ((len & 0x7FFF) << 16) | **str; + } + (*str)++; + return len; +} + +/** + * Strings in UTF-8 format have length indicated by a length encoded in the + * stored data. It is either 1 or 2 characters of length data. This allows a + * maximum length of 0x7FFF (32767 bytes), but you should consider storing + * text in another way if you're using that much data in a single string. + * + * If the high bit is set, then there are two characters or 2 bytes of length + * data encoded. In that case, drop the high bit of the first character and + * add it together with the next character. + */ +static inline size_t +decodeLength(const uint8_t** str) +{ + size_t len = **str; + if ((len & 0x80) != 0) { + (*str)++; + len = ((len & 0x7F) << 8) | **str; + } + (*str)++; + return len; +} -const uint16_t* ResStringPool::stringAt(size_t idx, size_t* outLen) const +const uint16_t* ResStringPool::stringAt(size_t idx, size_t* u16len) const { if (mError == NO_ERROR && idx < mHeader->stringCount) { const bool isUTF8 = (mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0; @@ -534,37 +576,51 @@ const uint16_t* ResStringPool::stringAt(size_t idx, size_t* outLen) const if (!isUTF8) { const char16_t* strings = (char16_t*)mStrings; const char16_t* str = strings+off; - DECODE_LENGTH(str, sizeof(char16_t), *outLen) - if ((uint32_t)(str+*outLen-strings) < mStringPoolSize) { + + *u16len = decodeLength(&str); + if ((uint32_t)(str+*u16len-strings) < mStringPoolSize) { return str; } else { LOGW("Bad string block: string #%d extends to %d, past end at %d\n", - (int)idx, (int)(str+*outLen-strings), (int)mStringPoolSize); + (int)idx, (int)(str+*u16len-strings), (int)mStringPoolSize); } } else { const uint8_t* strings = (uint8_t*)mStrings; - const uint8_t* str = strings+off; - DECODE_LENGTH(str, sizeof(uint8_t), *outLen) - size_t encLen; - DECODE_LENGTH(str, sizeof(uint8_t), encLen) - if ((uint32_t)(str+encLen-strings) < mStringPoolSize) { + const uint8_t* u8str = strings+off; + + *u16len = decodeLength(&u8str); + size_t u8len = decodeLength(&u8str); + + // encLen must be less than 0x7FFF due to encoding. + if ((uint32_t)(u8str+u8len-strings) < mStringPoolSize) { AutoMutex lock(mDecodeLock); + if (mCache[idx] != NULL) { return mCache[idx]; } - char16_t *u16str = (char16_t *)calloc(*outLen+1, sizeof(char16_t)); + + ssize_t actualLen = utf8_to_utf16_length(u8str, u8len); + if (actualLen < 0 || (size_t)actualLen != *u16len) { + LOGW("Bad string block: string #%lld decoded length is not correct " + "%lld vs %llu\n", + (long long)idx, (long long)actualLen, (long long)*u16len); + return NULL; + } + + char16_t *u16str = (char16_t *)calloc(*u16len+1, sizeof(char16_t)); if (!u16str) { LOGW("No memory when trying to allocate decode cache for string #%d\n", (int)idx); return NULL; } - const unsigned char *u8src = reinterpret_cast<const unsigned char *>(str); - utf8_to_utf16(u8src, encLen, u16str, *outLen); + + utf8_to_utf16(u8str, u8len, u16str); mCache[idx] = u16str; return u16str; } else { - LOGW("Bad string block: string #%d extends to %d, past end at %d\n", - (int)idx, (int)(str+encLen-strings), (int)mStringPoolSize); + LOGW("Bad string block: string #%lld extends to %lld, past end at %lld\n", + (long long)idx, (long long)(u8str+u8len-strings), + (long long)mStringPoolSize); } } } else { @@ -585,9 +641,8 @@ const char* ResStringPool::string8At(size_t idx, size_t* outLen) const if (isUTF8) { const uint8_t* strings = (uint8_t*)mStrings; const uint8_t* str = strings+off; - DECODE_LENGTH(str, sizeof(uint8_t), *outLen) - size_t encLen; - DECODE_LENGTH(str, sizeof(uint8_t), encLen) + *outLen = decodeLength(&str); + size_t encLen = decodeLength(&str); if ((uint32_t)(str+encLen-strings) < mStringPoolSize) { return (const char*)str; } else { @@ -1987,13 +2042,19 @@ bool ResTable::getResourceName(uint32_t resID, resource_name* outName) const outName->type = grp->basePackage->typeStrings.stringAt(t, &outName->typeLen); outName->name = grp->basePackage->keyStrings.stringAt( dtohl(entry->key.index), &outName->nameLen); + + // If we have a bad index for some reason, we should abort. + if (outName->type == NULL || outName->name == NULL) { + return false; + } + return true; } return false; } -ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag, +ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag, uint16_t density, uint32_t* outSpecFlags, ResTable_config* outConfig) const { if (mError != NO_ERROR) { @@ -2023,7 +2084,7 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag 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]; @@ -2031,6 +2092,22 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag LOGW("Bad identifier when getting value for resource number 0x%08x", resID); return BAD_INDEX; } + + // Allow overriding density + const ResTable_config* desiredConfig = &mParams; + ResTable_config* overrideConfig = NULL; + if (density > 0) { + overrideConfig = (ResTable_config*) malloc(sizeof(ResTable_config)); + if (overrideConfig == NULL) { + LOGE("Couldn't malloc ResTable_config for overrides: %s", strerror(errno)); + return BAD_INDEX; + } + memcpy(overrideConfig, &mParams, sizeof(ResTable_config)); + overrideConfig->density = density; + desiredConfig = overrideConfig; + } + + ssize_t rc = BAD_VALUE; size_t ip = grp->packages.size(); while (ip > 0) { ip--; @@ -2057,7 +2134,7 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag const ResTable_type* type; const ResTable_entry* entry; const Type* typeClass; - ssize_t offset = getEntry(package, T, E, &mParams, &type, &entry, &typeClass); + ssize_t offset = getEntry(package, T, E, desiredConfig, &type, &entry, &typeClass); if (offset <= 0) { // No {entry, appropriate config} pair found in package. If this // package is an overlay package (ip != 0), this simply means the @@ -2066,7 +2143,8 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag if (offset < 0 && ip == 0) { LOGW("Failure getting entry for 0x%08x (t=%d e=%d) in package %zd (error %d)\n", resID, T, E, ip, (int)offset); - return offset; + rc = offset; + goto out; } continue; } @@ -2081,13 +2159,14 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag 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; + rc = BAD_TYPE; + goto out; } - + const Res_value* item = (const Res_value*)(((const uint8_t*)type) + offset); ResTable_config thisConfig; @@ -2132,10 +2211,16 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag outValue->data, &len)).string() : "", outValue->data)); - return bestPackage->header->index; + rc = bestPackage->header->index; + goto out; + } + +out: + if (overrideConfig != NULL) { + free(overrideConfig); } - return BAD_VALUE; + return rc; } ssize_t ResTable::resolveReference(Res_value* value, ssize_t blockIndex, @@ -2148,7 +2233,7 @@ ssize_t ResTable::resolveReference(Res_value* value, ssize_t blockIndex, if (outLastRef) *outLastRef = value->data; uint32_t lastRef = value->data; uint32_t newFlags = 0; - const ssize_t newIndex = getResource(value->data, value, true, &newFlags, + const ssize_t newIndex = getResource(value->data, value, true, 0, &newFlags, outConfig); if (newIndex == BAD_INDEX) { return BAD_INDEX; @@ -2773,6 +2858,24 @@ bool ResTable::expandResourceRef(const uint16_t* refStr, size_t refLen, *outType = *defType; } *outName = String16(p, end-p); + if(**outPackage == 0) { + if(outErrorMsg) { + *outErrorMsg = "Resource package cannot be an empty string"; + } + return false; + } + if(**outType == 0) { + if(outErrorMsg) { + *outErrorMsg = "Resource type cannot be an empty string"; + } + return false; + } + if(**outName == 0) { + if(outErrorMsg) { + *outErrorMsg = "Resource id cannot be an empty string"; + } + return false; + } return true; } @@ -3764,9 +3867,9 @@ void ResTable::getConfigurations(Vector<ResTable_config>* configs) const void ResTable::getLocales(Vector<String8>* locales) const { Vector<ResTable_config> configs; - LOGD("calling getConfigurations"); + LOGV("calling getConfigurations"); getConfigurations(&configs); - LOGD("called getConfigurations size=%d", (int)configs.size()); + LOGV("called getConfigurations size=%d", (int)configs.size()); const size_t I = configs.size(); for (size_t i=0; i<I; i++) { char locale[6]; @@ -4457,13 +4560,16 @@ void ResTable::print(bool inclValues) const | (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])); + if (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])); + } else { + printf(" INVALID TYPE CONFIG FOR RESOURCE 0x%08x\n", resID); + } } } for (size_t configIndex=0; configIndex<NTC; configIndex++) { @@ -4670,11 +4776,14 @@ void ResTable::print(bool inclValues) const | (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 (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)); + } else { + printf(" INVALID RESOURCE 0x%08x: ", resID); + } if ((thisOffset&0x3) != 0) { printf("NON-INTEGER OFFSET: %p\n", (void*)thisOffset); continue; @@ -4732,18 +4841,19 @@ void ResTable::print(bool inclValues) const print_value(pkg, value); } else if (bagPtr != NULL) { const int N = dtohl(bagPtr->count); - const ResTable_map* mapPtr = (const ResTable_map*) - (((const uint8_t*)ent) + esize); + const uint8_t* baseMapPtr = (const uint8_t*)ent; + size_t mapOffset = esize; + const ResTable_map* mapPtr = (ResTable_map*)(baseMapPtr+mapOffset); printf(" Parent=0x%08x, Count=%d\n", dtohl(bagPtr->parent.ident), N); - for (int i=0; i<N; i++) { + for (int i=0; i<N && mapOffset < (typeSize-sizeof(ResTable_map)); i++) { printf(" #%i (Key=0x%08x): ", i, dtohl(mapPtr->name.ident)); value.copyFrom_dtoh(mapPtr->value); print_value(pkg, value); const size_t size = dtohs(mapPtr->value.size); - mapPtr = (ResTable_map*)(((const uint8_t*)mapPtr) - + size + sizeof(*mapPtr)-sizeof(mapPtr->value)); + mapOffset += size + sizeof(*mapPtr)-sizeof(mapPtr->value); + mapPtr = (ResTable_map*)(baseMapPtr+mapOffset); } } } diff --git a/libs/utils/StreamingZipInflater.cpp b/libs/utils/StreamingZipInflater.cpp index 1f62ac5..5a162cc 100644 --- a/libs/utils/StreamingZipInflater.cpp +++ b/libs/utils/StreamingZipInflater.cpp @@ -31,7 +31,7 @@ using namespace android; /* * Streaming access to compressed asset data in an open fd */ -StreamingZipInflater::StreamingZipInflater(int fd, off_t compDataStart, +StreamingZipInflater::StreamingZipInflater(int fd, off64_t compDataStart, size_t uncompSize, size_t compSize) { mFd = fd; mDataMap = NULL; @@ -210,7 +210,7 @@ int StreamingZipInflater::readNextChunk() { // seeking backwards requires uncompressing fom the beginning, so is very // expensive. seeking forwards only requires uncompressing from the current // position to the destination. -off_t StreamingZipInflater::seekAbsolute(off_t absoluteInputPosition) { +off64_t StreamingZipInflater::seekAbsolute(off64_t absoluteInputPosition) { if (absoluteInputPosition < mOutCurPosition) { // rewind and reprocess the data from the beginning if (!mStreamNeedsInit) { diff --git a/libs/utils/String16.cpp b/libs/utils/String16.cpp index eab7b2b..4ce1664 100644 --- a/libs/utils/String16.cpp +++ b/libs/utils/String16.cpp @@ -18,228 +18,17 @@ #include <utils/Debug.h> #include <utils/Log.h> +#include <utils/Unicode.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); -} - -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); -} - -void -utf8_to_utf16(const uint8_t *src, size_t srcLen, - char16_t* dst, const size_t dstLen) -{ - const uint8_t* const end = src + srcLen; - const char16_t* const dstEnd = dst + dstLen; - while (src < end && dst < dstEnd) { - size_t len = utf8_char_len(*src); - uint32_t codepoint = utf8_to_utf32((const uint8_t*)src, len); - - // Convert the UTF32 codepoint to one or more UTF16 codepoints - if (codepoint <= 0xFFFF) { - // Single UTF16 character - *dst++ = (char16_t) codepoint; - } else { - // Multiple UTF16 characters with surrogates - codepoint = codepoint - 0x10000; - *dst++ = (char16_t) ((codepoint >> 10) + 0xD800); - *dst++ = (char16_t) ((codepoint & 0x3FF) + 0xDC00); - } - - src += len; - } - if (dst < dstEnd) { - *dst = 0; - } -} - -// --------------------------------------------------------------------------- namespace android { @@ -270,37 +59,33 @@ void terminate_string16() // --------------------------------------------------------------------------- -static char16_t* allocFromUTF8(const char* in, size_t len) +static char16_t* allocFromUTF8(const char* u8str, size_t u8len) { - if (len == 0) return getEmptyString(); - - size_t chars = 0; - const char* end = in+len; - const char* p = in; - - while (p < end) { - chars++; - int utf8len = utf8_char_len(*p); - uint32_t codepoint = utf8_to_utf32((const uint8_t*)p, utf8len); - if (codepoint > 0xFFFF) chars++; // this will be a surrogate pair in utf16 - p += utf8len; + if (u8len == 0) return getEmptyString(); + + const uint8_t* u8cur = (const uint8_t*) u8str; + + const ssize_t u16len = utf8_to_utf16_length(u8cur, u8len); + if (u16len < 0) { + return getEmptyString(); } - - size_t bufSize = (chars+1)*sizeof(char16_t); - SharedBuffer* buf = SharedBuffer::alloc(bufSize); + + const uint8_t* const u8end = u8cur + u8len; + + SharedBuffer* buf = SharedBuffer::alloc(sizeof(char16_t)*(u16len+1)); if (buf) { - p = in; - char16_t* str = (char16_t*)buf->data(); - - utf8_to_utf16((const uint8_t*)p, len, str, bufSize); + u8cur = (const uint8_t*) u8str; + char16_t* u16str = (char16_t*)buf->data(); + + utf8_to_utf16(u8cur, u8len, u16str); //printf("Created UTF-16 string from UTF-8 \"%s\":", in); //printHexData(1, str, buf->size(), 16, 1); //printf("\n"); - return str; + return u16str; } - + return getEmptyString(); } diff --git a/libs/utils/String8.cpp b/libs/utils/String8.cpp index 1c4f80c..0bc5aff 100644 --- a/libs/utils/String8.cpp +++ b/libs/utils/String8.cpp @@ -17,6 +17,8 @@ #include <utils/String8.h> #include <utils/Log.h> +#include <utils/Unicode.h> +#include <utils/SharedBuffer.h> #include <utils/String16.h> #include <utils/TextOutput.h> #include <utils/threads.h> @@ -34,94 +36,10 @@ namespace android { -static const char32_t kByteMask = 0x000000BF; -static const char32_t kByteMark = 0x00000080; - -// Surrogates aren't valid for UTF-32 characters, so define some -// constants that will let us screen them out. -static const char32_t kUnicodeSurrogateHighStart = 0x0000D800; -static const char32_t kUnicodeSurrogateHighEnd = 0x0000DBFF; -static const char32_t kUnicodeSurrogateLowStart = 0x0000DC00; -static const char32_t kUnicodeSurrogateLowEnd = 0x0000DFFF; -static const char32_t kUnicodeSurrogateStart = kUnicodeSurrogateHighStart; -static const char32_t kUnicodeSurrogateEnd = kUnicodeSurrogateLowEnd; -static const char32_t kUnicodeMaxCodepoint = 0x0010FFFF; - -// Mask used to set appropriate bits in first byte of UTF-8 sequence, -// indexed by number of bytes in the sequence. -// 0xxxxxxx -// -> (00-7f) 7bit. Bit mask for the first byte is 0x00000000 -// 110yyyyx 10xxxxxx -// -> (c0-df)(80-bf) 11bit. Bit mask is 0x000000C0 -// 1110yyyy 10yxxxxx 10xxxxxx -// -> (e0-ef)(80-bf)(80-bf) 16bit. Bit mask is 0x000000E0 -// 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx -// -> (f0-f7)(80-bf)(80-bf)(80-bf) 21bit. Bit mask is 0x000000F0 -static const char32_t kFirstByteMark[] = { - 0x00000000, 0x00000000, 0x000000C0, 0x000000E0, 0x000000F0 -}; - // 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(char32_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 <= kUnicodeMaxCodepoint) - { - 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, char32_t srcChar, size_t bytes) -{ - dstP += bytes; - switch (bytes) - { /* note: everything falls through. */ - case 4: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; - case 3: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; - case 2: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; - case 1: *--dstP = (uint8_t)(srcChar | kFirstByteMark[bytes]); - } -} - -// --------------------------------------------------------------------------- - static SharedBuffer* gEmptyStringBuf = NULL; static char* gEmptyString = NULL; @@ -175,62 +93,47 @@ static char* allocFromUTF8(const char* in, size_t len) return getEmptyString(); } -template<typename T, typename L> -static char* allocFromUTF16OrUTF32(const T* in, L len) +static char* allocFromUTF16(const char16_t* in, size_t len) { if (len == 0) return getEmptyString(); - size_t bytes = 0; - const T* end = in+len; - const T* p = in; - - while (p < end) { - bytes += utf32_to_utf8_bytes(*p); - p++; + const ssize_t bytes = utf16_to_utf8_length(in, len); + if (bytes < 0) { + return getEmptyString(); } 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) { - const 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; + if (!buf) { + return getEmptyString(); } - return getEmptyString(); + char* str = (char*)buf->data(); + utf16_to_utf8(in, len, str); + return str; } -static char* allocFromUTF16(const char16_t* in, size_t len) +static char* allocFromUTF32(const char32_t* in, size_t len) { - if (len == 0) return getEmptyString(); + if (len == 0) { + return getEmptyString(); + } - const size_t bytes = utf8_length_from_utf16(in, len); + const ssize_t bytes = utf32_to_utf8_length(in, len); + if (bytes < 0) { + return getEmptyString(); + } SharedBuffer* buf = SharedBuffer::alloc(bytes+1); LOG_ASSERT(buf, "Unable to allocate shared buffer"); - if (buf) { - char* str = (char*)buf->data(); - - utf16_to_utf8(in, len, str, bytes+1); - - return str; + if (!buf) { + return getEmptyString(); } - return getEmptyString(); -} + char* str = (char*) buf->data(); + utf32_to_utf8(in, len, str); -static char* allocFromUTF32(const char32_t* in, size_t len) -{ - return allocFromUTF16OrUTF32<char32_t, size_t>(in, len); + return str; } // --------------------------------------------------------------------------- @@ -292,6 +195,29 @@ String8::~String8() SharedBuffer::bufferFromData(mString)->release(); } +String8 String8::format(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + + String8 result(formatV(fmt, args)); + + va_end(args); + return result; +} + +String8 String8::formatV(const char* fmt, va_list args) +{ + String8 result; + result.appendFormatV(fmt, args); + return result; +} + +void String8::clear() { + SharedBuffer::bufferFromData(mString)->release(); + mString = getEmptyString(); +} + void String8::setTo(const String8& other) { SharedBuffer::bufferFromData(other.mString)->acquire(); @@ -374,22 +300,28 @@ status_t String8::append(const char* other, size_t otherLen) status_t String8::appendFormat(const char* fmt, ...) { - va_list ap; - va_start(ap, fmt); + va_list args; + va_start(args, fmt); + + status_t result = appendFormatV(fmt, args); + va_end(args); + return result; +} + +status_t String8::appendFormatV(const char* fmt, va_list args) +{ int result = NO_ERROR; - int n = vsnprintf(NULL, 0, fmt, ap); + int n = vsnprintf(NULL, 0, fmt, args); if (n != 0) { size_t oldLength = length(); char* buf = lockBuffer(oldLength + n); if (buf) { - vsnprintf(buf + oldLength, n + 1, fmt, ap); + vsnprintf(buf + oldLength, n + 1, fmt, args); } else { result = NO_MEMORY; } } - - va_end(ap); return result; } @@ -505,17 +437,17 @@ void String8::toUpper(size_t start, size_t length) size_t String8::getUtf32Length() const { - return utf32_length(mString, length()); + return utf8_to_utf32_length(mString, length()); } int32_t String8::getUtf32At(size_t index, size_t *next_index) const { - return utf32_at(mString, length(), index, next_index); + return utf32_from_utf8_at(mString, length(), index, next_index); } -size_t String8::getUtf32(char32_t* dst, size_t dst_len) const +void String8::getUtf32(char32_t* dst) const { - return utf8_to_utf32(mString, length(), dst, dst_len); + utf8_to_utf32(mString, length(), dst); } TextOutput& operator<<(TextOutput& to, const String8& val) @@ -700,241 +632,3 @@ String8& String8::convertToResPath() } }; // namespace android - -// --------------------------------------------------------------------------- - -size_t strlen32(const char32_t *s) -{ - const char32_t *ss = s; - while ( *ss ) - ss++; - return ss-s; -} - -size_t strnlen32(const char32_t *s, size_t maxlen) -{ - const char32_t *ss = s; - while ((maxlen > 0) && *ss) { - ss++; - maxlen--; - } - return ss-s; -} - -size_t utf8_length(const char *src) -{ - const char *cur = src; - size_t ret = 0; - while (*cur != '\0') { - const char first_char = *cur++; - if ((first_char & 0x80) == 0) { // ASCII - ret += 1; - continue; - } - // (UTF-8's character must not be like 10xxxxxx, - // but 110xxxxx, 1110xxxx, ... or 1111110x) - if ((first_char & 0x40) == 0) { - return 0; - } - - int32_t mask, to_ignore_mask; - size_t num_to_read = 0; - char32_t utf32 = 0; - for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0x80; - num_to_read < 5 && (first_char & mask); - num_to_read++, to_ignore_mask |= mask, mask >>= 1) { - if ((*cur & 0xC0) != 0x80) { // must be 10xxxxxx - return 0; - } - // 0x3F == 00111111 - utf32 = (utf32 << 6) + (*cur++ & 0x3F); - } - // "first_char" must be (110xxxxx - 11110xxx) - if (num_to_read == 5) { - return 0; - } - to_ignore_mask |= mask; - utf32 |= ((~to_ignore_mask) & first_char) << (6 * (num_to_read - 1)); - if (utf32 > android::kUnicodeMaxCodepoint) { - return 0; - } - - ret += num_to_read; - } - return ret; -} - -size_t utf32_length(const char *src, size_t src_len) -{ - if (src == NULL || src_len == 0) { - return 0; - } - size_t ret = 0; - const char* cur; - const char* end; - size_t num_to_skip; - for (cur = src, end = src + src_len, num_to_skip = 1; - cur < end; - cur += num_to_skip, ret++) { - const char first_char = *cur; - num_to_skip = 1; - if ((first_char & 0x80) == 0) { // ASCII - continue; - } - int32_t mask; - - for (mask = 0x40; (first_char & mask); num_to_skip++, mask >>= 1) { - } - } - return ret; -} - -size_t utf8_length_from_utf32(const char32_t *src, size_t src_len) -{ - if (src == NULL || src_len == 0) { - return 0; - } - size_t ret = 0; - const char32_t *end = src + src_len; - while (src < end) { - ret += android::utf32_to_utf8_bytes(*src++); - } - return ret; -} - -size_t utf8_length_from_utf16(const char16_t *src, size_t src_len) -{ - if (src == NULL || src_len == 0) { - return 0; - } - size_t ret = 0; - const char16_t* const end = src + src_len; - while (src < end) { - if ((*src & 0xFC00) == 0xD800 && (src + 1) < end - && (*++src & 0xFC00) == 0xDC00) { - // surrogate pairs are always 4 bytes. - ret += 4; - src++; - } else { - ret += android::utf32_to_utf8_bytes((char32_t) *src++); - } - } - return ret; -} - -static int32_t utf32_at_internal(const char* cur, size_t *num_read) -{ - const char first_char = *cur; - if ((first_char & 0x80) == 0) { // ASCII - *num_read = 1; - return *cur; - } - cur++; - char32_t mask, to_ignore_mask; - size_t num_to_read = 0; - char32_t utf32 = first_char; - for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0xFFFFFF80; - (first_char & mask); - num_to_read++, to_ignore_mask |= mask, mask >>= 1) { - // 0x3F == 00111111 - utf32 = (utf32 << 6) + (*cur++ & 0x3F); - } - to_ignore_mask |= mask; - utf32 &= ~(to_ignore_mask << (6 * (num_to_read - 1))); - - *num_read = num_to_read; - return static_cast<int32_t>(utf32); -} - -int32_t utf32_at(const char *src, size_t src_len, - size_t index, size_t *next_index) -{ - if (index >= src_len) { - return -1; - } - size_t dummy_index; - if (next_index == NULL) { - next_index = &dummy_index; - } - size_t num_read; - int32_t ret = utf32_at_internal(src + index, &num_read); - if (ret >= 0) { - *next_index = index + num_read; - } - - return ret; -} - -size_t utf8_to_utf32(const char* src, size_t src_len, - char32_t* dst, size_t dst_len) -{ - if (src == NULL || src_len == 0 || dst == NULL || dst_len == 0) { - return 0; - } - - const char* cur = src; - const char* end = src + src_len; - char32_t* cur_utf32 = dst; - const char32_t* end_utf32 = dst + dst_len; - while (cur_utf32 < end_utf32 && cur < end) { - size_t num_read; - *cur_utf32++ = - static_cast<char32_t>(utf32_at_internal(cur, &num_read)); - cur += num_read; - } - if (cur_utf32 < end_utf32) { - *cur_utf32 = 0; - } - return static_cast<size_t>(cur_utf32 - dst); -} - -size_t utf32_to_utf8(const char32_t* src, size_t src_len, - char* dst, size_t dst_len) -{ - if (src == NULL || src_len == 0 || dst == NULL || dst_len == 0) { - return 0; - } - const char32_t *cur_utf32 = src; - const char32_t *end_utf32 = src + src_len; - char *cur = dst; - const char *end = dst + dst_len; - while (cur_utf32 < end_utf32 && cur < end) { - size_t len = android::utf32_to_utf8_bytes(*cur_utf32); - android::utf32_to_utf8((uint8_t *)cur, *cur_utf32++, len); - cur += len; - } - if (cur < end) { - *cur = '\0'; - } - return cur - dst; -} - -size_t utf16_to_utf8(const char16_t* src, size_t src_len, - char* dst, size_t dst_len) -{ - if (src == NULL || src_len == 0 || dst == NULL || dst_len == 0) { - return 0; - } - const char16_t* cur_utf16 = src; - const char16_t* const end_utf16 = src + src_len; - char *cur = dst; - const char* const end = dst + dst_len; - while (cur_utf16 < end_utf16 && cur < end) { - char32_t utf32; - // surrogate pairs - if ((*cur_utf16 & 0xFC00) == 0xD800 && (cur_utf16 + 1) < end_utf16) { - utf32 = (*cur_utf16++ - 0xD800) << 10; - utf32 |= *cur_utf16++ - 0xDC00; - utf32 += 0x10000; - } else { - utf32 = (char32_t) *cur_utf16++; - } - size_t len = android::utf32_to_utf8_bytes(utf32); - android::utf32_to_utf8((uint8_t*)cur, utf32, len); - cur += len; - } - if (cur < end) { - *cur = '\0'; - } - return cur - dst; -} diff --git a/libs/utils/SystemClock.cpp b/libs/utils/SystemClock.cpp index 2bdc0ce..062e6d7 100644 --- a/libs/utils/SystemClock.cpp +++ b/libs/utils/SystemClock.cpp @@ -19,7 +19,7 @@ * System clock functions. */ -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS #include <linux/ioctl.h> #include <linux/rtc.h> #include <utils/Atomic.h> @@ -50,7 +50,7 @@ int setCurrentTimeMillis(int64_t millis) return -1; #else struct timeval tv; -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS struct timespec ts; int fd; int res; @@ -66,7 +66,7 @@ int setCurrentTimeMillis(int64_t millis) LOGD("Setting time of day to sec=%d\n", (int) tv.tv_sec); -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS fd = open("/dev/alarm", O_RDWR); if(fd < 0) { LOGW("Unable to open alarm driver: %s\n", strerror(errno)); @@ -106,7 +106,7 @@ int64_t uptimeMillis() */ int64_t elapsedRealtime() { -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS static int s_fd = -1; if (s_fd == -1) { diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index b1bd828..8b5da0e 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -675,6 +675,9 @@ Thread::Thread(bool canCallJava) mLock("Thread::mLock"), mStatus(NO_ERROR), mExitPending(false), mRunning(false) +#ifdef HAVE_ANDROID_OS + , mTid(-1) +#endif { } @@ -715,6 +718,7 @@ status_t Thread::run(const char* name, int32_t priority, size_t stack) res = androidCreateRawThreadEtc(_threadLoop, this, name, priority, stack, &mThread); } + // The new thread wakes up at _threadLoop, but immediately blocks on mLock if (res == false) { mStatus = UNKNOWN_ERROR; // something happened! @@ -730,16 +734,24 @@ status_t Thread::run(const char* name, int32_t priority, size_t stack) // here merely indicates successfully starting the thread and does not // imply successful termination/execution. return NO_ERROR; + + // Exiting scope of mLock is a memory barrier and allows new thread to run } int Thread::_threadLoop(void* user) { Thread* const self = static_cast<Thread*>(user); + + // force a memory barrier before reading any fields, in particular mHoldSelf + { + Mutex::Autolock _l(self->mLock); + } + sp<Thread> strong(self->mHoldSelf); wp<Thread> weak(strong); self->mHoldSelf.clear(); -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS // this is very useful for debugging with gdb self->mTid = gettid(); #endif @@ -753,7 +765,7 @@ int Thread::_threadLoop(void* user) self->mStatus = self->readyToRun(); result = (self->mStatus == NO_ERROR); - if (result && !self->mExitPending) { + if (result && !self->exitPending()) { // Binder threads (and maybe others) rely on threadLoop // running at least once after a successful ::readyToRun() // (unless, of course, the thread has already been asked to exit @@ -770,18 +782,21 @@ int Thread::_threadLoop(void* user) result = self->threadLoop(); } + // establish a scope for mLock + { + Mutex::Autolock _l(self->mLock); if (result == false || self->mExitPending) { self->mExitPending = true; - self->mLock.lock(); self->mRunning = false; // clear thread ID so that requestExitAndWait() does not exit if // called by a new thread using the same thread ID as this one. self->mThread = thread_id_t(-1); + // note that interested observers blocked in requestExitAndWait are + // awoken by broadcast, but blocked on mLock until break exits scope self->mThreadExitedCondition.broadcast(); - self->mThread = thread_id_t(-1); // thread id could be reused - self->mLock.unlock(); break; } + } // Release our strong reference, to let a chance to the thread // to die a peaceful death. @@ -795,6 +810,7 @@ int Thread::_threadLoop(void* user) void Thread::requestExit() { + Mutex::Autolock _l(mLock); mExitPending = true; } @@ -815,6 +831,8 @@ status_t Thread::requestExitAndWait() while (mRunning == true) { mThreadExitedCondition.wait(mLock); } + // This next line is probably not needed any more, but is being left for + // historical reference. Note that each interested party will clear flag. mExitPending = false; return mStatus; @@ -822,6 +840,7 @@ status_t Thread::requestExitAndWait() bool Thread::exitPending() const { + Mutex::Autolock _l(mLock); return mExitPending; } diff --git a/libs/utils/Tokenizer.cpp b/libs/utils/Tokenizer.cpp new file mode 100644 index 0000000..b3445b7 --- /dev/null +++ b/libs/utils/Tokenizer.cpp @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "Tokenizer" + +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <utils/Log.h> +#include <utils/Tokenizer.h> + +// Enables debug output for the tokenizer. +#define DEBUG_TOKENIZER 0 + + +namespace android { + +static inline bool isDelimiter(char ch, const char* delimiters) { + return strchr(delimiters, ch) != NULL; +} + +Tokenizer::Tokenizer(const String8& filename, FileMap* fileMap, char* buffer, size_t length) : + mFilename(filename), mFileMap(fileMap), + mBuffer(buffer), mLength(length), mCurrent(buffer), mLineNumber(1) { +} + +Tokenizer::~Tokenizer() { + if (mFileMap) { + mFileMap->release(); + } else { + delete[] mBuffer; + } +} + +status_t Tokenizer::open(const String8& filename, Tokenizer** outTokenizer) { + *outTokenizer = NULL; + + int result = NO_ERROR; + int fd = ::open(filename.string(), O_RDONLY); + if (fd < 0) { + result = -errno; + LOGE("Error opening file '%s', %s.", filename.string(), strerror(errno)); + } else { + struct stat stat; + if (fstat(fd, &stat)) { + result = -errno; + LOGE("Error getting size of file '%s', %s.", filename.string(), strerror(errno)); + } else { + size_t length = size_t(stat.st_size); + + FileMap* fileMap = new FileMap(); + char* buffer; + if (fileMap->create(NULL, fd, 0, length, true)) { + fileMap->advise(FileMap::SEQUENTIAL); + buffer = static_cast<char*>(fileMap->getDataPtr()); + } else { + fileMap->release(); + fileMap = NULL; + + // Fall back to reading into a buffer since we can't mmap files in sysfs. + // The length we obtained from stat is wrong too (it will always be 4096) + // so we must trust that read will read the entire file. + buffer = new char[length]; + ssize_t nrd = read(fd, buffer, length); + if (nrd < 0) { + result = -errno; + LOGE("Error reading file '%s', %s.", filename.string(), strerror(errno)); + delete[] buffer; + buffer = NULL; + } else { + length = size_t(nrd); + } + } + + if (!result) { + *outTokenizer = new Tokenizer(filename, fileMap, buffer, length); + } + } + close(fd); + } + return result; +} + +String8 Tokenizer::getLocation() const { + String8 result; + result.appendFormat("%s:%d", mFilename.string(), mLineNumber); + return result; +} + +String8 Tokenizer::peekRemainderOfLine() const { + const char* end = getEnd(); + const char* eol = mCurrent; + while (eol != end) { + char ch = *eol; + if (ch == '\n') { + break; + } + eol += 1; + } + return String8(mCurrent, eol - mCurrent); +} + +String8 Tokenizer::nextToken(const char* delimiters) { +#if DEBUG_TOKENIZER + LOGD("nextToken"); +#endif + const char* end = getEnd(); + const char* tokenStart = mCurrent; + while (mCurrent != end) { + char ch = *mCurrent; + if (ch == '\n' || isDelimiter(ch, delimiters)) { + break; + } + mCurrent += 1; + } + return String8(tokenStart, mCurrent - tokenStart); +} + +void Tokenizer::nextLine() { +#if DEBUG_TOKENIZER + LOGD("nextLine"); +#endif + const char* end = getEnd(); + while (mCurrent != end) { + char ch = *(mCurrent++); + if (ch == '\n') { + mLineNumber += 1; + break; + } + } +} + +void Tokenizer::skipDelimiters(const char* delimiters) { +#if DEBUG_TOKENIZER + LOGD("skipDelimiters"); +#endif + const char* end = getEnd(); + while (mCurrent != end) { + char ch = *mCurrent; + if (ch == '\n' || !isDelimiter(ch, delimiters)) { + break; + } + mCurrent += 1; + } +} + +} // namespace android diff --git a/libs/utils/Unicode.cpp b/libs/utils/Unicode.cpp new file mode 100644 index 0000000..78c61b4 --- /dev/null +++ b/libs/utils/Unicode.cpp @@ -0,0 +1,575 @@ +/* + * Copyright (C) 2005 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <utils/Unicode.h> + +#include <stddef.h> + +#ifdef HAVE_WINSOCK +# undef nhtol +# undef htonl +# undef nhtos +# undef htons + +# ifdef HAVE_LITTLE_ENDIAN +# define ntohl(x) ( ((x) << 24) | (((x) >> 24) & 255) | (((x) << 8) & 0xff0000) | (((x) >> 8) & 0xff00) ) +# define htonl(x) ntohl(x) +# define ntohs(x) ( (((x) << 8) & 0xff00) | (((x) >> 8) & 255) ) +# define htons(x) ntohs(x) +# else +# define ntohl(x) (x) +# define htonl(x) (x) +# define ntohs(x) (x) +# define htons(x) (x) +# endif +#else +# include <netinet/in.h> +#endif + +extern "C" { + +static const char32_t kByteMask = 0x000000BF; +static const char32_t kByteMark = 0x00000080; + +// Surrogates aren't valid for UTF-32 characters, so define some +// constants that will let us screen them out. +static const char32_t kUnicodeSurrogateHighStart = 0x0000D800; +static const char32_t kUnicodeSurrogateHighEnd = 0x0000DBFF; +static const char32_t kUnicodeSurrogateLowStart = 0x0000DC00; +static const char32_t kUnicodeSurrogateLowEnd = 0x0000DFFF; +static const char32_t kUnicodeSurrogateStart = kUnicodeSurrogateHighStart; +static const char32_t kUnicodeSurrogateEnd = kUnicodeSurrogateLowEnd; +static const char32_t kUnicodeMaxCodepoint = 0x0010FFFF; + +// Mask used to set appropriate bits in first byte of UTF-8 sequence, +// indexed by number of bytes in the sequence. +// 0xxxxxxx +// -> (00-7f) 7bit. Bit mask for the first byte is 0x00000000 +// 110yyyyx 10xxxxxx +// -> (c0-df)(80-bf) 11bit. Bit mask is 0x000000C0 +// 1110yyyy 10yxxxxx 10xxxxxx +// -> (e0-ef)(80-bf)(80-bf) 16bit. Bit mask is 0x000000E0 +// 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx +// -> (f0-f7)(80-bf)(80-bf)(80-bf) 21bit. Bit mask is 0x000000F0 +static const char32_t kFirstByteMark[] = { + 0x00000000, 0x00000000, 0x000000C0, 0x000000E0, 0x000000F0 +}; + +// -------------------------------------------------------------------------- +// UTF-32 +// -------------------------------------------------------------------------- + +/** + * Return number of UTF-8 bytes required for the character. If the character + * is invalid, return size of 0. + */ +static inline size_t utf32_codepoint_utf8_length(char32_t srcChar) +{ + // Figure out how many bytes the result will require. + if (srcChar < 0x00000080) { + return 1; + } else if (srcChar < 0x00000800) { + return 2; + } else if (srcChar < 0x00010000) { + if ((srcChar < kUnicodeSurrogateStart) || (srcChar > kUnicodeSurrogateEnd)) { + return 3; + } else { + // Surrogates are invalid UTF-32 characters. + return 0; + } + } + // Max code point for Unicode is 0x0010FFFF. + else if (srcChar <= kUnicodeMaxCodepoint) { + return 4; + } else { + // Invalid UTF-32 character. + return 0; + } +} + +// Write out the source character to <dstP>. + +static inline void utf32_codepoint_to_utf8(uint8_t* dstP, char32_t srcChar, size_t bytes) +{ + dstP += bytes; + switch (bytes) + { /* note: everything falls through. */ + case 4: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; + case 3: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; + case 2: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; + case 1: *--dstP = (uint8_t)(srcChar | kFirstByteMark[bytes]); + } +} + +size_t strlen32(const char32_t *s) +{ + const char32_t *ss = s; + while ( *ss ) + ss++; + return ss-s; +} + +size_t strnlen32(const char32_t *s, size_t maxlen) +{ + const char32_t *ss = s; + while ((maxlen > 0) && *ss) { + ss++; + maxlen--; + } + return ss-s; +} + +static inline int32_t utf32_at_internal(const char* cur, size_t *num_read) +{ + const char first_char = *cur; + if ((first_char & 0x80) == 0) { // ASCII + *num_read = 1; + return *cur; + } + cur++; + char32_t mask, to_ignore_mask; + size_t num_to_read = 0; + char32_t utf32 = first_char; + for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0xFFFFFF80; + (first_char & mask); + num_to_read++, to_ignore_mask |= mask, mask >>= 1) { + // 0x3F == 00111111 + utf32 = (utf32 << 6) + (*cur++ & 0x3F); + } + to_ignore_mask |= mask; + utf32 &= ~(to_ignore_mask << (6 * (num_to_read - 1))); + + *num_read = num_to_read; + return static_cast<int32_t>(utf32); +} + +int32_t utf32_from_utf8_at(const char *src, size_t src_len, size_t index, size_t *next_index) +{ + if (index >= src_len) { + return -1; + } + size_t dummy_index; + if (next_index == NULL) { + next_index = &dummy_index; + } + size_t num_read; + int32_t ret = utf32_at_internal(src + index, &num_read); + if (ret >= 0) { + *next_index = index + num_read; + } + + return ret; +} + +ssize_t utf32_to_utf8_length(const char32_t *src, size_t src_len) +{ + if (src == NULL || src_len == 0) { + return -1; + } + + size_t ret = 0; + const char32_t *end = src + src_len; + while (src < end) { + ret += utf32_codepoint_utf8_length(*src++); + } + return ret; +} + +void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst) +{ + if (src == NULL || src_len == 0 || dst == NULL) { + return; + } + + const char32_t *cur_utf32 = src; + const char32_t *end_utf32 = src + src_len; + char *cur = dst; + while (cur_utf32 < end_utf32) { + size_t len = utf32_codepoint_utf8_length(*cur_utf32); + utf32_codepoint_to_utf8((uint8_t *)cur, *cur_utf32++, len); + cur += len; + } + *cur = '\0'; +} + +// -------------------------------------------------------------------------- +// UTF-16 +// -------------------------------------------------------------------------- + +int strcmp16(const char16_t *s1, const char16_t *s2) +{ + char16_t ch; + int d = 0; + + while ( 1 ) { + d = (int)(ch = *s1++) - (int)*s2++; + if ( d || !ch ) + break; + } + + return d; +} + +int strncmp16(const char16_t *s1, const char16_t *s2, size_t n) +{ + char16_t ch; + int d = 0; + + while ( n-- ) { + d = (int)(ch = *s1++) - (int)*s2++; + if ( d || !ch ) + break; + } + + return d; +} + +char16_t *strcpy16(char16_t *dst, const char16_t *src) +{ + char16_t *q = dst; + const char16_t *p = src; + char16_t ch; + + do { + *q++ = ch = *p++; + } while ( ch ); + + return dst; +} + +size_t strlen16(const char16_t *s) +{ + const char16_t *ss = s; + while ( *ss ) + ss++; + return ss-s; +} + + +char16_t *strncpy16(char16_t *dst, const char16_t *src, size_t n) +{ + char16_t *q = dst; + const char16_t *p = src; + char ch; + + while (n) { + n--; + *q++ = ch = *p++; + if ( !ch ) + break; + } + + *q = 0; + + return dst; +} + +size_t strnlen16(const char16_t *s, size_t maxlen) +{ + const char16_t *ss = s; + + /* Important: the maxlen test must precede the reference through ss; + since the byte beyond the maximum may segfault */ + while ((maxlen > 0) && *ss) { + ss++; + maxlen--; + } + return ss-s; +} + +int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2) +{ + const char16_t* e1 = s1+n1; + const char16_t* e2 = s2+n2; + + while (s1 < e1 && s2 < e2) { + const int d = (int)*s1++ - (int)*s2++; + if (d) { + return d; + } + } + + return n1 < n2 + ? (0 - (int)*s2) + : (n1 > n2 + ? ((int)*s1 - 0) + : 0); +} + +int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2) +{ + const char16_t* e1 = s1H+n1; + const char16_t* e2 = s2N+n2; + + while (s1H < e1 && s2N < e2) { + const char16_t c2 = ntohs(*s2N); + const int d = (int)*s1H++ - (int)c2; + s2N++; + if (d) { + return d; + } + } + + return n1 < n2 + ? (0 - (int)ntohs(*s2N)) + : (n1 > n2 + ? ((int)*s1H - 0) + : 0); +} + +void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst) +{ + if (src == NULL || src_len == 0 || dst == NULL) { + return; + } + + const char16_t* cur_utf16 = src; + const char16_t* const end_utf16 = src + src_len; + char *cur = dst; + while (cur_utf16 < end_utf16) { + char32_t utf32; + // surrogate pairs + if ((*cur_utf16 & 0xFC00) == 0xD800) { + utf32 = (*cur_utf16++ - 0xD800) << 10; + utf32 |= *cur_utf16++ - 0xDC00; + utf32 += 0x10000; + } else { + utf32 = (char32_t) *cur_utf16++; + } + const size_t len = utf32_codepoint_utf8_length(utf32); + utf32_codepoint_to_utf8((uint8_t*)cur, utf32, len); + cur += len; + } + *cur = '\0'; +} + +// -------------------------------------------------------------------------- +// UTF-8 +// -------------------------------------------------------------------------- + +ssize_t utf8_length(const char *src) +{ + const char *cur = src; + size_t ret = 0; + while (*cur != '\0') { + const char first_char = *cur++; + if ((first_char & 0x80) == 0) { // ASCII + ret += 1; + continue; + } + // (UTF-8's character must not be like 10xxxxxx, + // but 110xxxxx, 1110xxxx, ... or 1111110x) + if ((first_char & 0x40) == 0) { + return -1; + } + + int32_t mask, to_ignore_mask; + size_t num_to_read = 0; + char32_t utf32 = 0; + for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0x80; + num_to_read < 5 && (first_char & mask); + num_to_read++, to_ignore_mask |= mask, mask >>= 1) { + if ((*cur & 0xC0) != 0x80) { // must be 10xxxxxx + return -1; + } + // 0x3F == 00111111 + utf32 = (utf32 << 6) + (*cur++ & 0x3F); + } + // "first_char" must be (110xxxxx - 11110xxx) + if (num_to_read == 5) { + return -1; + } + to_ignore_mask |= mask; + utf32 |= ((~to_ignore_mask) & first_char) << (6 * (num_to_read - 1)); + if (utf32 > kUnicodeMaxCodepoint) { + return -1; + } + + ret += num_to_read; + } + return ret; +} + +ssize_t utf16_to_utf8_length(const char16_t *src, size_t src_len) +{ + if (src == NULL || src_len == 0) { + return -1; + } + + size_t ret = 0; + const char16_t* const end = src + src_len; + while (src < end) { + if ((*src & 0xFC00) == 0xD800 && (src + 1) < end + && (*++src & 0xFC00) == 0xDC00) { + // surrogate pairs are always 4 bytes. + ret += 4; + src++; + } else { + ret += utf32_codepoint_utf8_length((char32_t) *src++); + } + } + return ret; +} + +/** + * Returns 1-4 based on the number of leading bits. + * + * 1111 -> 4 + * 1110 -> 3 + * 110x -> 2 + * 10xx -> 1 + * 0xxx -> 1 + */ +static inline size_t utf8_codepoint_len(uint8_t ch) +{ + return ((0xe5000000 >> ((ch >> 3) & 0x1e)) & 3) + 1; +} + +static inline void utf8_shift_and_mask(uint32_t* codePoint, const uint8_t byte) +{ + *codePoint <<= 6; + *codePoint |= 0x3F & byte; +} + +size_t utf8_to_utf32_length(const char *src, size_t src_len) +{ + if (src == NULL || src_len == 0) { + return 0; + } + size_t ret = 0; + const char* cur; + const char* end; + size_t num_to_skip; + for (cur = src, end = src + src_len, num_to_skip = 1; + cur < end; + cur += num_to_skip, ret++) { + const char first_char = *cur; + num_to_skip = 1; + if ((first_char & 0x80) == 0) { // ASCII + continue; + } + int32_t mask; + + for (mask = 0x40; (first_char & mask); num_to_skip++, mask >>= 1) { + } + } + return ret; +} + +void utf8_to_utf32(const char* src, size_t src_len, char32_t* dst) +{ + if (src == NULL || src_len == 0 || dst == NULL) { + return; + } + + const char* cur = src; + const char* const end = src + src_len; + char32_t* cur_utf32 = dst; + while (cur < end) { + size_t num_read; + *cur_utf32++ = static_cast<char32_t>(utf32_at_internal(cur, &num_read)); + cur += num_read; + } + *cur_utf32 = 0; +} + +static inline uint32_t utf8_to_utf32_codepoint(const uint8_t *src, size_t length) +{ + uint32_t unicode; + + switch (length) + { + case 1: + return src[0]; + case 2: + unicode = src[0] & 0x1f; + utf8_shift_and_mask(&unicode, src[1]); + return unicode; + case 3: + unicode = src[0] & 0x0f; + utf8_shift_and_mask(&unicode, src[1]); + utf8_shift_and_mask(&unicode, src[2]); + return unicode; + case 4: + unicode = src[0] & 0x07; + utf8_shift_and_mask(&unicode, src[1]); + utf8_shift_and_mask(&unicode, src[2]); + utf8_shift_and_mask(&unicode, src[3]); + return unicode; + default: + return 0xffff; + } + + //printf("Char at %p: len=%d, utf-16=%p\n", src, length, (void*)result); +} + +ssize_t utf8_to_utf16_length(const uint8_t* u8str, size_t u8len) +{ + const uint8_t* const u8end = u8str + u8len; + const uint8_t* u8cur = u8str; + + /* Validate that the UTF-8 is the correct len */ + size_t u16measuredLen = 0; + while (u8cur < u8end) { + u16measuredLen++; + int u8charLen = utf8_codepoint_len(*u8cur); + uint32_t codepoint = utf8_to_utf32_codepoint(u8cur, u8charLen); + if (codepoint > 0xFFFF) u16measuredLen++; // this will be a surrogate pair in utf16 + u8cur += u8charLen; + } + + /** + * Make sure that we ended where we thought we would and the output UTF-16 + * will be exactly how long we were told it would be. + */ + if (u8cur != u8end) { + return -1; + } + + return u16measuredLen; +} + +/** + * Convert a UTF-8 string to UTF-16. The destination UTF-16 buffer must have + * space for NULL at the end. + */ +void utf8_to_utf16(const uint8_t* u8str, size_t u8len, char16_t* u16str) +{ + const uint8_t* const u8end = u8str + u8len; + const uint8_t* u8cur = u8str; + char16_t* u16cur = u16str; + + while (u8cur < u8end) { + size_t u8len = utf8_codepoint_len(*u8cur); + uint32_t codepoint = utf8_to_utf32_codepoint(u8cur, u8len); + + // Convert the UTF32 codepoint to one or more UTF16 codepoints + if (codepoint <= 0xFFFF) { + // Single UTF16 character + *u16cur++ = (char16_t) codepoint; + } else { + // Multiple UTF16 characters with surrogates + codepoint = codepoint - 0x10000; + *u16cur++ = (char16_t) ((codepoint >> 10) + 0xD800); + *u16cur++ = (char16_t) ((codepoint & 0x3FF) + 0xDC00); + } + + u8cur += u8len; + } + *u16cur = 0; +} + +} diff --git a/libs/utils/ZipFileCRO.cpp b/libs/utils/ZipFileCRO.cpp index 16b219c..55dfd9f 100644 --- a/libs/utils/ZipFileCRO.cpp +++ b/libs/utils/ZipFileCRO.cpp @@ -40,7 +40,7 @@ ZipEntryCRO ZipFileCRO_findEntryByName(ZipFileCRO zipToken, bool ZipFileCRO_getEntryInfo(ZipFileCRO zipToken, ZipEntryRO entryToken, int* pMethod, size_t* pUncompLen, - size_t* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) { + size_t* pCompLen, off64_t* pOffset, long* pModWhen, long* pCrc32) { ZipFileRO* zip = (ZipFileRO*)zipToken; ZipEntryRO entry = (ZipEntryRO)entryToken; return zip->getEntryInfo(entry, pMethod, pUncompLen, pCompLen, pOffset, diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp index 4261196..b18c383 100644 --- a/libs/utils/ZipFileRO.cpp +++ b/libs/utils/ZipFileRO.cpp @@ -146,7 +146,7 @@ status_t ZipFileRO::open(const char* zipFileName) return NAME_NOT_FOUND; } - mFileLength = lseek(fd, 0, SEEK_END); + mFileLength = lseek64(fd, 0, SEEK_END); if (mFileLength < kEOCDLen) { TEMP_FAILURE_RETRY(close(fd)); return UNKNOWN_ERROR; @@ -202,7 +202,7 @@ bool ZipFileRO::mapCentralDirectory(void) /* * Make sure this is a Zip archive. */ - if (lseek(mFd, 0, SEEK_SET) != 0) { + if (lseek64(mFd, 0, SEEK_SET) != 0) { LOGW("seek to start failed: %s", strerror(errno)); free(scanBuf); return false; @@ -240,9 +240,9 @@ bool ZipFileRO::mapCentralDirectory(void) * * We start by pulling in the last part of the file. */ - off_t searchStart = mFileLength - readAmount; + off64_t searchStart = mFileLength - readAmount; - if (lseek(mFd, searchStart, SEEK_SET) != searchStart) { + if (lseek64(mFd, searchStart, SEEK_SET) != searchStart) { LOGW("seek %ld failed: %s\n", (long) searchStart, strerror(errno)); free(scanBuf); return false; @@ -274,7 +274,7 @@ bool ZipFileRO::mapCentralDirectory(void) return false; } - off_t eocdOffset = searchStart + i; + off64_t eocdOffset = searchStart + i; const unsigned char* eocdPtr = scanBuf + i; assert(eocdOffset < mFileLength); @@ -473,7 +473,7 @@ ZipEntryRO ZipFileRO::findEntryByIndex(int idx) const * appear to be bogus. */ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, - size_t* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) const + size_t* pCompLen, off64_t* pOffset, long* pModWhen, long* pCrc32) const { bool ret = false; @@ -489,7 +489,7 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, * so we can just subtract back from that. */ const unsigned char* ptr = (const unsigned char*) hashEntry.name; - off_t cdOffset = mDirectoryOffset; + off64_t cdOffset = mDirectoryOffset; ptr -= kCDELen; @@ -536,12 +536,12 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, #ifdef HAVE_PREAD /* * This file descriptor might be from zygote's preloaded assets, - * so we need to do an pread() instead of a lseek() + read() to + * so we need to do an pread64() instead of a lseek64() + read() to * guarantee atomicity across the processes with the shared file * descriptors. */ ssize_t actual = - TEMP_FAILURE_RETRY(pread(mFd, lfhBuf, sizeof(lfhBuf), localHdrOffset)); + TEMP_FAILURE_RETRY(pread64(mFd, lfhBuf, sizeof(lfhBuf), localHdrOffset)); if (actual != sizeof(lfhBuf)) { LOGW("failed reading lfh from offset %ld\n", localHdrOffset); @@ -556,17 +556,17 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, } #else /* HAVE_PREAD */ /* - * For hosts don't have pread() we cannot guarantee atomic reads from + * For hosts don't have pread64() we cannot guarantee atomic reads from * an offset in a file. Android should never run on those platforms. * File descriptors inherited from a fork() share file offsets and * there would be nothing to protect from two different processes - * calling lseek() concurrently. + * calling lseek64() concurrently. */ { AutoMutex _l(mFdLock); - if (lseek(mFd, localHdrOffset, SEEK_SET) != localHdrOffset) { + if (lseek64(mFd, localHdrOffset, SEEK_SET) != localHdrOffset) { LOGW("failed seeking to lfh at offset %ld\n", localHdrOffset); return false; } @@ -579,7 +579,7 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, } if (get4LE(lfhBuf) != kLFHSignature) { - off_t actualOffset = lseek(mFd, 0, SEEK_CUR); + off64_t actualOffset = lseek64(mFd, 0, SEEK_CUR); LOGW("didn't find signature at start of lfh; wanted: offset=%ld data=0x%08x; " "got: offset=" ZD " data=0x%08lx\n", localHdrOffset, kLFHSignature, (ZD_TYPE) actualOffset, get4LE(lfhBuf)); @@ -588,7 +588,7 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, } #endif /* HAVE_PREAD */ - off_t dataOffset = localHdrOffset + kLFHLen + off64_t dataOffset = localHdrOffset + kLFHLen + get2LE(lfhBuf + kLFHNameLen) + get2LE(lfhBuf + kLFHExtraLen); if (dataOffset >= cdOffset) { LOGW("bad data offset %ld in zip\n", (long) dataOffset); @@ -596,14 +596,14 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, } /* check lengths */ - if ((off_t)(dataOffset + compLen) > cdOffset) { + if ((off64_t)(dataOffset + compLen) > cdOffset) { LOGW("bad compressed length in zip (%ld + " ZD " > %ld)\n", (long) dataOffset, (ZD_TYPE) compLen, (long) cdOffset); return false; } if (method == kCompressStored && - (off_t)(dataOffset + uncompLen) > cdOffset) + (off64_t)(dataOffset + uncompLen) > cdOffset) { LOGE("ERROR: bad uncompressed length in zip (%ld + " ZD " > %ld)\n", (long) dataOffset, (ZD_TYPE) uncompLen, (long) cdOffset); @@ -649,7 +649,7 @@ FileMap* ZipFileRO::createEntryFileMap(ZipEntryRO entry) const FileMap* newMap; size_t compLen; - off_t offset; + off64_t offset; if (!getEntryInfo(entry, NULL, NULL, &compLen, &offset, NULL, NULL)) return NULL; @@ -679,7 +679,7 @@ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, void* buffer) const int method; size_t uncompLen, compLen; - off_t offset; + off64_t offset; const unsigned char* ptr; getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL); @@ -739,7 +739,7 @@ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, int fd) const int method; size_t uncompLen, compLen; - off_t offset; + off64_t offset; const unsigned char* ptr; getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL); diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk index 00077ee..72d4876 100644 --- a/libs/utils/tests/Android.mk +++ b/libs/utils/tests/Android.mk @@ -8,7 +8,8 @@ ifneq ($(TARGET_SIMULATOR),true) test_src_files := \ ObbFile_test.cpp \ Looper_test.cpp \ - String8_test.cpp + String8_test.cpp \ + Unicode_test.cpp shared_libraries := \ libz \ diff --git a/libs/utils/tests/Looper_test.cpp b/libs/utils/tests/Looper_test.cpp index cea1313..8bf2ba2 100644 --- a/libs/utils/tests/Looper_test.cpp +++ b/libs/utils/tests/Looper_test.cpp @@ -16,6 +16,13 @@ namespace android { +enum { + MSG_TEST1 = 1, + MSG_TEST2 = 2, + MSG_TEST3 = 3, + MSG_TEST4 = 4, +}; + class DelayedWake : public DelayedTask { sp<Looper> mLooper; @@ -82,6 +89,15 @@ protected: } }; +class StubMessageHandler : public MessageHandler { +public: + Vector<Message> messages; + + virtual void handleMessage(const Message& message) { + messages.push(message); + } +}; + class LooperTest : public testing::Test { protected: sp<Looper> mLooper; @@ -421,5 +437,257 @@ TEST_F(LooperTest, PollOnce_WhenCallbackAddedTwice_OnlySecondCallbackShouldBeInv << "replacement handler callback should be invoked"; } +TEST_F(LooperTest, SendMessage_WhenOneMessageIsEnqueue_ShouldInvokeHandlerDuringNextPoll) { + sp<StubMessageHandler> handler = new StubMessageHandler(); + mLooper->sendMessage(handler, Message(MSG_TEST1)); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. zero because message was already sent"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; + EXPECT_EQ(size_t(1), handler->messages.size()) + << "handled message"; + EXPECT_EQ(MSG_TEST1, handler->messages[0].what) + << "handled message"; +} + +TEST_F(LooperTest, SendMessage_WhenMultipleMessagesAreEnqueued_ShouldInvokeHandlersInOrderDuringNextPoll) { + sp<StubMessageHandler> handler1 = new StubMessageHandler(); + sp<StubMessageHandler> handler2 = new StubMessageHandler(); + mLooper->sendMessage(handler1, Message(MSG_TEST1)); + mLooper->sendMessage(handler2, Message(MSG_TEST2)); + mLooper->sendMessage(handler1, Message(MSG_TEST3)); + mLooper->sendMessage(handler1, Message(MSG_TEST4)); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(1000); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. zero because message was already sent"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; + EXPECT_EQ(size_t(3), handler1->messages.size()) + << "handled message"; + EXPECT_EQ(MSG_TEST1, handler1->messages[0].what) + << "handled message"; + EXPECT_EQ(MSG_TEST3, handler1->messages[1].what) + << "handled message"; + EXPECT_EQ(MSG_TEST4, handler1->messages[2].what) + << "handled message"; + EXPECT_EQ(size_t(1), handler2->messages.size()) + << "handled message"; + EXPECT_EQ(MSG_TEST2, handler2->messages[0].what) + << "handled message"; +} + +TEST_F(LooperTest, SendMessageDelayed_WhenSentToTheFuture_ShouldInvokeHandlerAfterDelayTime) { + sp<StubMessageHandler> handler = new StubMessageHandler(); + mLooper->sendMessageDelayed(ms2ns(100), handler, Message(MSG_TEST1)); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(1000); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "first poll should end quickly because next message timeout was computed"; + EXPECT_EQ(ALOOPER_POLL_WAKE, result) + << "pollOnce result should be ALOOPER_POLL_WAKE due to wakeup"; + EXPECT_EQ(size_t(0), handler->messages.size()) + << "no message handled yet"; + + result = mLooper->pollOnce(1000); + elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_EQ(size_t(1), handler->messages.size()) + << "handled message"; + EXPECT_EQ(MSG_TEST1, handler->messages[0].what) + << "handled message"; + EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) + << "second poll should end around the time of the delayed message dispatch"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; + + result = mLooper->pollOnce(100); + elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(100 + 100, elapsedMillis, TIMING_TOLERANCE_MS) + << "third poll should timeout"; + EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) + << "pollOnce result should be ALOOPER_POLL_TIMEOUT because there were no messages left"; +} + +TEST_F(LooperTest, SendMessageDelayed_WhenSentToThePast_ShouldInvokeHandlerDuringNextPoll) { + sp<StubMessageHandler> handler = new StubMessageHandler(); + mLooper->sendMessageDelayed(ms2ns(-1000), handler, Message(MSG_TEST1)); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. zero because message was already sent"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; + EXPECT_EQ(size_t(1), handler->messages.size()) + << "handled message"; + EXPECT_EQ(MSG_TEST1, handler->messages[0].what) + << "handled message"; +} + +TEST_F(LooperTest, SendMessageDelayed_WhenSentToThePresent_ShouldInvokeHandlerDuringNextPoll) { + sp<StubMessageHandler> handler = new StubMessageHandler(); + mLooper->sendMessageDelayed(0, handler, Message(MSG_TEST1)); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. zero because message was already sent"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; + EXPECT_EQ(size_t(1), handler->messages.size()) + << "handled message"; + EXPECT_EQ(MSG_TEST1, handler->messages[0].what) + << "handled message"; +} + +TEST_F(LooperTest, SendMessageAtTime_WhenSentToTheFuture_ShouldInvokeHandlerAfterDelayTime) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + sp<StubMessageHandler> handler = new StubMessageHandler(); + mLooper->sendMessageAtTime(now + ms2ns(100), handler, Message(MSG_TEST1)); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(1000); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "first poll should end quickly because next message timeout was computed"; + EXPECT_EQ(ALOOPER_POLL_WAKE, result) + << "pollOnce result should be ALOOPER_POLL_WAKE due to wakeup"; + EXPECT_EQ(size_t(0), handler->messages.size()) + << "no message handled yet"; + + result = mLooper->pollOnce(1000); + elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_EQ(size_t(1), handler->messages.size()) + << "handled message"; + EXPECT_EQ(MSG_TEST1, handler->messages[0].what) + << "handled message"; + EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) + << "second poll should end around the time of the delayed message dispatch"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; + + result = mLooper->pollOnce(100); + elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(100 + 100, elapsedMillis, TIMING_TOLERANCE_MS) + << "third poll should timeout"; + EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) + << "pollOnce result should be ALOOPER_POLL_TIMEOUT because there were no messages left"; +} + +TEST_F(LooperTest, SendMessageAtTime_WhenSentToThePast_ShouldInvokeHandlerDuringNextPoll) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + sp<StubMessageHandler> handler = new StubMessageHandler(); + mLooper->sendMessageAtTime(now - ms2ns(1000), handler, Message(MSG_TEST1)); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. zero because message was already sent"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; + EXPECT_EQ(size_t(1), handler->messages.size()) + << "handled message"; + EXPECT_EQ(MSG_TEST1, handler->messages[0].what) + << "handled message"; +} + +TEST_F(LooperTest, SendMessageAtTime_WhenSentToThePresent_ShouldInvokeHandlerDuringNextPoll) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + sp<StubMessageHandler> handler = new StubMessageHandler(); + mLooper->sendMessageAtTime(now, handler, Message(MSG_TEST1)); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. zero because message was already sent"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; + EXPECT_EQ(size_t(1), handler->messages.size()) + << "handled message"; + EXPECT_EQ(MSG_TEST1, handler->messages[0].what) + << "handled message"; +} + +TEST_F(LooperTest, RemoveMessage_WhenRemovingAllMessagesForHandler_ShouldRemoveThoseMessage) { + sp<StubMessageHandler> handler = new StubMessageHandler(); + mLooper->sendMessage(handler, Message(MSG_TEST1)); + mLooper->sendMessage(handler, Message(MSG_TEST2)); + mLooper->sendMessage(handler, Message(MSG_TEST3)); + mLooper->removeMessages(handler); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(0); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. zero because message was sent so looper was awoken"; + EXPECT_EQ(ALOOPER_POLL_WAKE, result) + << "pollOnce result should be ALOOPER_POLL_WAKE because looper was awoken"; + EXPECT_EQ(size_t(0), handler->messages.size()) + << "no messages to handle"; + + result = mLooper->pollOnce(0); + + EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) + << "pollOnce result should be ALOOPER_POLL_TIMEOUT because there was nothing to do"; + EXPECT_EQ(size_t(0), handler->messages.size()) + << "no messages to handle"; +} + +TEST_F(LooperTest, RemoveMessage_WhenRemovingSomeMessagesForHandler_ShouldRemoveThoseMessage) { + sp<StubMessageHandler> handler = new StubMessageHandler(); + mLooper->sendMessage(handler, Message(MSG_TEST1)); + mLooper->sendMessage(handler, Message(MSG_TEST2)); + mLooper->sendMessage(handler, Message(MSG_TEST3)); + mLooper->sendMessage(handler, Message(MSG_TEST4)); + mLooper->removeMessages(handler, MSG_TEST3); + mLooper->removeMessages(handler, MSG_TEST1); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(0); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. zero because message was sent so looper was awoken"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because two messages were sent"; + EXPECT_EQ(size_t(2), handler->messages.size()) + << "no messages to handle"; + EXPECT_EQ(MSG_TEST2, handler->messages[0].what) + << "handled message"; + EXPECT_EQ(MSG_TEST4, handler->messages[1].what) + << "handled message"; + + result = mLooper->pollOnce(0); + + EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) + << "pollOnce result should be ALOOPER_POLL_TIMEOUT because there was nothing to do"; + EXPECT_EQ(size_t(2), handler->messages.size()) + << "no more messages to handle"; +} } // namespace android diff --git a/libs/utils/tests/Unicode_test.cpp b/libs/utils/tests/Unicode_test.cpp new file mode 100644 index 0000000..18c130c --- /dev/null +++ b/libs/utils/tests/Unicode_test.cpp @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "Unicode_test" +#include <utils/Log.h> +#include <utils/Unicode.h> + +#include <gtest/gtest.h> + +namespace android { + +class UnicodeTest : public testing::Test { +protected: + virtual void SetUp() { + } + + virtual void TearDown() { + } +}; + +TEST_F(UnicodeTest, UTF8toUTF16ZeroLength) { + ssize_t measured; + + const uint8_t str[] = { }; + + measured = utf8_to_utf16_length(str, 0); + EXPECT_EQ(0, measured) + << "Zero length input should return zero length output."; +} + +TEST_F(UnicodeTest, UTF8toUTF16ASCIILength) { + ssize_t measured; + + // U+0030 or ASCII '0' + const uint8_t str[] = { 0x30 }; + + measured = utf8_to_utf16_length(str, sizeof(str)); + EXPECT_EQ(1, measured) + << "ASCII glyphs should have a length of 1 char16_t"; +} + +TEST_F(UnicodeTest, UTF8toUTF16Plane1Length) { + ssize_t measured; + + // U+2323 SMILE + const uint8_t str[] = { 0xE2, 0x8C, 0xA3 }; + + measured = utf8_to_utf16_length(str, sizeof(str)); + EXPECT_EQ(1, measured) + << "Plane 1 glyphs should have a length of 1 char16_t"; +} + +TEST_F(UnicodeTest, UTF8toUTF16SurrogateLength) { + ssize_t measured; + + // U+10000 + const uint8_t str[] = { 0xF0, 0x90, 0x80, 0x80 }; + + measured = utf8_to_utf16_length(str, sizeof(str)); + EXPECT_EQ(2, measured) + << "Surrogate pairs should have a length of 2 char16_t"; +} + +TEST_F(UnicodeTest, UTF8toUTF16TruncatedUTF8) { + ssize_t measured; + + // Truncated U+2323 SMILE + // U+2323 SMILE + const uint8_t str[] = { 0xE2, 0x8C }; + + measured = utf8_to_utf16_length(str, sizeof(str)); + EXPECT_EQ(-1, measured) + << "Truncated UTF-8 should return -1 to indicate invalid"; +} + +TEST_F(UnicodeTest, UTF8toUTF16Normal) { + const uint8_t str[] = { + 0x30, // U+0030, 1 UTF-16 character + 0xC4, 0x80, // U+0100, 1 UTF-16 character + 0xE2, 0x8C, 0xA3, // U+2323, 1 UTF-16 character + 0xF0, 0x90, 0x80, 0x80, // U+10000, 2 UTF-16 character + }; + + char16_t output[1 + 1 + 1 + 2 + 1]; // Room for NULL + + utf8_to_utf16(str, sizeof(str), output); + + EXPECT_EQ(0x0030, output[0]) + << "should be U+0030"; + EXPECT_EQ(0x0100, output[1]) + << "should be U+0100"; + EXPECT_EQ(0x2323, output[2]) + << "should be U+2323"; + EXPECT_EQ(0xD800, output[3]) + << "should be first half of surrogate U+10000"; + EXPECT_EQ(0xDC00, output[4]) + << "should be second half of surrogate U+10000"; + EXPECT_EQ(NULL, output[5]) + << "should be NULL terminated"; +} + +} |