diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2013-11-22 11:18:57 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2013-11-22 11:18:57 -0800 |
commit | dbccd44a638ae8705a5b14bff8b2dd74abc26045 (patch) | |
tree | 14bfabaf3f3c7be86dfc064e919e00433a0cf2bb /libs/androidfw | |
parent | ecfae4f899873f224e1aeed076dc8a41f8884487 (diff) | |
parent | b873a17ce7be0a9771c24999adca6964431728f6 (diff) | |
download | frameworks_base-dbccd44a638ae8705a5b14bff8b2dd74abc26045.zip frameworks_base-dbccd44a638ae8705a5b14bff8b2dd74abc26045.tar.gz frameworks_base-dbccd44a638ae8705a5b14bff8b2dd74abc26045.tar.bz2 |
Merge commit 'b873a17ce7be0a9771c24999adca6964431728f6' into HEAD
Change-Id: I938755073e70602cc8f51ce9bd420fdcf870cecd
Diffstat (limited to 'libs/androidfw')
23 files changed, 1776 insertions, 6048 deletions
diff --git a/libs/androidfw/Android.mk b/libs/androidfw/Android.mk index 3ed75a2..d80612b 100644 --- a/libs/androidfw/Android.mk +++ b/libs/androidfw/Android.mk @@ -14,47 +14,47 @@ LOCAL_PATH:= $(call my-dir) -# libandroidfw is partially built for the host (used by build time keymap validation tool) +# libandroidfw is partially built for the host (used by obbtool and others) # These files are common to host and target builds. -# formerly in libutils -commonUtilsSources:= \ +commonSources := \ Asset.cpp \ AssetDir.cpp \ AssetManager.cpp \ + misc.cpp \ ObbFile.cpp \ ResourceTypes.cpp \ - StreamingZipInflater.cpp - -# formerly in libui -commonUiSources:= \ - Input.cpp \ - InputDevice.cpp \ - Keyboard.cpp \ - KeyCharacterMap.cpp \ - KeyLayoutMap.cpp \ - VelocityControl.cpp \ - VelocityTracker.cpp \ - VirtualKeyMap.cpp - -commonSources:= \ - $(commonUtilsSources) \ - $(commonUiSources) + StreamingZipInflater.cpp \ + ZipFileRO.cpp \ + ZipUtils.cpp + +deviceSources := \ + $(commonSources) \ + BackupData.cpp \ + BackupHelpers.cpp \ + CursorWindow.cpp + +hostSources := \ + $(commonSources) # For the host # ===================================================== include $(CLEAR_VARS) -LOCAL_SRC_FILES:= $(commonSources) +LOCAL_SRC_FILES:= $(hostSources) LOCAL_MODULE:= libandroidfw LOCAL_MODULE_TAGS := optional +LOCAL_CFLAGS += -DSTATIC_ANDROIDFW_FOR_TOOLS + LOCAL_C_INCLUDES := \ external/zlib +LOCAL_STATIC_LIBRARIES := liblog + include $(BUILD_HOST_STATIC_LIBRARY) @@ -63,23 +63,16 @@ include $(BUILD_HOST_STATIC_LIBRARY) include $(CLEAR_VARS) -LOCAL_SRC_FILES:= \ - $(commonSources) \ - BackupData.cpp \ - BackupHelpers.cpp \ - CursorWindow.cpp \ - InputTransport.cpp +LOCAL_SRC_FILES:= $(deviceSources) LOCAL_SHARED_LIBRARIES := \ + libbinder \ liblog \ libcutils \ libutils \ - libbinder \ - libskia \ libz LOCAL_C_INCLUDES := \ - external/skia/include/core \ external/icu4c/common \ external/zlib @@ -90,20 +83,6 @@ LOCAL_MODULE_TAGS := optional include $(BUILD_SHARED_LIBRARY) -ifeq ($(TARGET_OS),linux) -include $(CLEAR_VARS) -LOCAL_C_INCLUDES += \ - external/skia/include/core \ - external/zlib \ - external/icu4c/common \ - bionic/libc/private -LOCAL_LDLIBS := -lrt -ldl -lpthread -LOCAL_MODULE := libandroidfw -LOCAL_SRC_FILES := $(commonUtilsSources) BackupData.cpp BackupHelpers.cpp -include $(BUILD_STATIC_LIBRARY) -endif - - # Include subdirectory makefiles # ============================================================ diff --git a/libs/androidfw/Asset.cpp b/libs/androidfw/Asset.cpp index fd5b3e4..cb7628d 100644 --- a/libs/androidfw/Asset.cpp +++ b/libs/androidfw/Asset.cpp @@ -23,11 +23,11 @@ #include <androidfw/Asset.h> #include <androidfw/StreamingZipInflater.h> +#include <androidfw/ZipFileRO.h> +#include <androidfw/ZipUtils.h> #include <utils/Atomic.h> #include <utils/FileMap.h> #include <utils/Log.h> -#include <utils/ZipFileRO.h> -#include <utils/ZipUtils.h> #include <utils/threads.h> #include <assert.h> diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp index 9156725..1066715 100644 --- a/libs/androidfw/AssetManager.cpp +++ b/libs/androidfw/AssetManager.cpp @@ -25,14 +25,15 @@ #include <androidfw/Asset.h> #include <androidfw/AssetDir.h> #include <androidfw/AssetManager.h> +#include <androidfw/misc.h> #include <androidfw/ResourceTypes.h> +#include <androidfw/ZipFileRO.h> #include <utils/Atomic.h> #include <utils/Log.h> #include <utils/String8.h> #include <utils/String8.h> #include <utils/threads.h> #include <utils/Timers.h> -#include <utils/ZipFileRO.h> #ifdef HAVE_ANDROID_OS #include <cutils/trace.h> #endif diff --git a/libs/androidfw/CursorWindow.cpp b/libs/androidfw/CursorWindow.cpp index 047a4c8..0f54edb 100644 --- a/libs/androidfw/CursorWindow.cpp +++ b/libs/androidfw/CursorWindow.cpp @@ -17,8 +17,9 @@ #undef LOG_TAG #define LOG_TAG "CursorWindow" -#include <utils/Log.h> #include <androidfw/CursorWindow.h> +#include <binder/Parcel.h> +#include <utils/Log.h> #include <cutils/ashmem.h> #include <sys/mman.h> diff --git a/libs/androidfw/Input.cpp b/libs/androidfw/Input.cpp deleted file mode 100644 index eca692a..0000000 --- a/libs/androidfw/Input.cpp +++ /dev/null @@ -1,634 +0,0 @@ -/* - * 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 "Input" -//#define LOG_NDEBUG 0 - -#include <math.h> -#include <limits.h> - -#include <androidfw/Input.h> - -#ifdef HAVE_ANDROID_OS -#include <binder/Parcel.h> - -#include "SkPoint.h" -#include "SkMatrix.h" -#include "SkScalar.h" -#endif - -namespace android { - -// --- InputEvent --- - -void InputEvent::initialize(int32_t deviceId, int32_t source) { - mDeviceId = deviceId; - mSource = source; -} - -void InputEvent::initialize(const InputEvent& from) { - mDeviceId = from.mDeviceId; - mSource = from.mSource; -} - -// --- KeyEvent --- - -bool KeyEvent::hasDefaultAction(int32_t keyCode) { - switch (keyCode) { - case AKEYCODE_HOME: - case AKEYCODE_BACK: - case AKEYCODE_CALL: - case AKEYCODE_ENDCALL: - case AKEYCODE_VOLUME_UP: - case AKEYCODE_VOLUME_DOWN: - case AKEYCODE_VOLUME_MUTE: - case AKEYCODE_POWER: - case AKEYCODE_CAMERA: - case AKEYCODE_HEADSETHOOK: - case AKEYCODE_MENU: - case AKEYCODE_NOTIFICATION: - case AKEYCODE_FOCUS: - case AKEYCODE_SEARCH: - case AKEYCODE_MEDIA_PLAY: - case AKEYCODE_MEDIA_PAUSE: - case AKEYCODE_MEDIA_PLAY_PAUSE: - case AKEYCODE_MEDIA_STOP: - case AKEYCODE_MEDIA_NEXT: - case AKEYCODE_MEDIA_PREVIOUS: - case AKEYCODE_MEDIA_REWIND: - case AKEYCODE_MEDIA_RECORD: - case AKEYCODE_MEDIA_FAST_FORWARD: - case AKEYCODE_MUTE: - case AKEYCODE_BRIGHTNESS_DOWN: - case AKEYCODE_BRIGHTNESS_UP: - return true; - } - - return false; -} - -bool KeyEvent::hasDefaultAction() const { - return hasDefaultAction(getKeyCode()); -} - -bool KeyEvent::isSystemKey(int32_t keyCode) { - switch (keyCode) { - case AKEYCODE_MENU: - case AKEYCODE_SOFT_RIGHT: - case AKEYCODE_HOME: - case AKEYCODE_BACK: - case AKEYCODE_CALL: - case AKEYCODE_ENDCALL: - case AKEYCODE_VOLUME_UP: - case AKEYCODE_VOLUME_DOWN: - case AKEYCODE_VOLUME_MUTE: - case AKEYCODE_MUTE: - case AKEYCODE_POWER: - case AKEYCODE_HEADSETHOOK: - case AKEYCODE_MEDIA_PLAY: - case AKEYCODE_MEDIA_PAUSE: - case AKEYCODE_MEDIA_PLAY_PAUSE: - case AKEYCODE_MEDIA_STOP: - case AKEYCODE_MEDIA_NEXT: - case AKEYCODE_MEDIA_PREVIOUS: - case AKEYCODE_MEDIA_REWIND: - case AKEYCODE_MEDIA_RECORD: - case AKEYCODE_MEDIA_FAST_FORWARD: - case AKEYCODE_CAMERA: - case AKEYCODE_FOCUS: - case AKEYCODE_SEARCH: - case AKEYCODE_BRIGHTNESS_DOWN: - case AKEYCODE_BRIGHTNESS_UP: - return true; - } - - return false; -} - -bool KeyEvent::isSystemKey() const { - return isSystemKey(getKeyCode()); -} - -void KeyEvent::initialize( - int32_t deviceId, - int32_t source, - int32_t action, - int32_t flags, - int32_t keyCode, - int32_t scanCode, - int32_t metaState, - int32_t repeatCount, - nsecs_t downTime, - nsecs_t eventTime) { - InputEvent::initialize(deviceId, source); - mAction = action; - mFlags = flags; - mKeyCode = keyCode; - mScanCode = scanCode; - mMetaState = metaState; - mRepeatCount = repeatCount; - mDownTime = downTime; - mEventTime = eventTime; -} - -void KeyEvent::initialize(const KeyEvent& from) { - InputEvent::initialize(from); - mAction = from.mAction; - mFlags = from.mFlags; - mKeyCode = from.mKeyCode; - mScanCode = from.mScanCode; - mMetaState = from.mMetaState; - mRepeatCount = from.mRepeatCount; - mDownTime = from.mDownTime; - mEventTime = from.mEventTime; -} - - -// --- PointerCoords --- - -float PointerCoords::getAxisValue(int32_t axis) const { - if (axis < 0 || axis > 63) { - return 0; - } - - uint64_t axisBit = 1LL << axis; - if (!(bits & axisBit)) { - return 0; - } - uint32_t index = __builtin_popcountll(bits & (axisBit - 1LL)); - return values[index]; -} - -status_t PointerCoords::setAxisValue(int32_t axis, float value) { - if (axis < 0 || axis > 63) { - return NAME_NOT_FOUND; - } - - uint64_t axisBit = 1LL << axis; - uint32_t index = __builtin_popcountll(bits & (axisBit - 1LL)); - if (!(bits & axisBit)) { - if (value == 0) { - return OK; // axes with value 0 do not need to be stored - } - uint32_t count = __builtin_popcountll(bits); - if (count >= MAX_AXES) { - tooManyAxes(axis); - return NO_MEMORY; - } - bits |= axisBit; - for (uint32_t i = count; i > index; i--) { - values[i] = values[i - 1]; - } - } - values[index] = value; - return OK; -} - -static inline void scaleAxisValue(PointerCoords& c, int axis, float scaleFactor) { - float value = c.getAxisValue(axis); - if (value != 0) { - c.setAxisValue(axis, value * scaleFactor); - } -} - -void PointerCoords::scale(float scaleFactor) { - // No need to scale pressure or size since they are normalized. - // No need to scale orientation since it is meaningless to do so. - scaleAxisValue(*this, AMOTION_EVENT_AXIS_X, scaleFactor); - scaleAxisValue(*this, AMOTION_EVENT_AXIS_Y, scaleFactor); - scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MAJOR, scaleFactor); - scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MINOR, scaleFactor); - scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MAJOR, scaleFactor); - scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MINOR, scaleFactor); -} - -#ifdef HAVE_ANDROID_OS -status_t PointerCoords::readFromParcel(Parcel* parcel) { - bits = parcel->readInt64(); - - uint32_t count = __builtin_popcountll(bits); - if (count > MAX_AXES) { - return BAD_VALUE; - } - - for (uint32_t i = 0; i < count; i++) { - values[i] = parcel->readFloat(); - } - return OK; -} - -status_t PointerCoords::writeToParcel(Parcel* parcel) const { - parcel->writeInt64(bits); - - uint32_t count = __builtin_popcountll(bits); - for (uint32_t i = 0; i < count; i++) { - parcel->writeFloat(values[i]); - } - return OK; -} -#endif - -void PointerCoords::tooManyAxes(int axis) { - ALOGW("Could not set value for axis %d because the PointerCoords structure is full and " - "cannot contain more than %d axis values.", axis, int(MAX_AXES)); -} - -bool PointerCoords::operator==(const PointerCoords& other) const { - if (bits != other.bits) { - return false; - } - uint32_t count = __builtin_popcountll(bits); - for (uint32_t i = 0; i < count; i++) { - if (values[i] != other.values[i]) { - return false; - } - } - return true; -} - -void PointerCoords::copyFrom(const PointerCoords& other) { - bits = other.bits; - uint32_t count = __builtin_popcountll(bits); - for (uint32_t i = 0; i < count; i++) { - values[i] = other.values[i]; - } -} - - -// --- PointerProperties --- - -bool PointerProperties::operator==(const PointerProperties& other) const { - return id == other.id - && toolType == other.toolType; -} - -void PointerProperties::copyFrom(const PointerProperties& other) { - id = other.id; - toolType = other.toolType; -} - - -// --- MotionEvent --- - -void MotionEvent::initialize( - int32_t deviceId, - int32_t source, - int32_t action, - int32_t flags, - int32_t edgeFlags, - int32_t metaState, - int32_t buttonState, - float xOffset, - float yOffset, - float xPrecision, - float yPrecision, - nsecs_t downTime, - nsecs_t eventTime, - size_t pointerCount, - const PointerProperties* pointerProperties, - const PointerCoords* pointerCoords) { - InputEvent::initialize(deviceId, source); - mAction = action; - mFlags = flags; - mEdgeFlags = edgeFlags; - mMetaState = metaState; - mButtonState = buttonState; - mXOffset = xOffset; - mYOffset = yOffset; - mXPrecision = xPrecision; - mYPrecision = yPrecision; - mDownTime = downTime; - mPointerProperties.clear(); - mPointerProperties.appendArray(pointerProperties, pointerCount); - mSampleEventTimes.clear(); - mSamplePointerCoords.clear(); - addSample(eventTime, pointerCoords); -} - -void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) { - InputEvent::initialize(other->mDeviceId, other->mSource); - mAction = other->mAction; - mFlags = other->mFlags; - mEdgeFlags = other->mEdgeFlags; - mMetaState = other->mMetaState; - mButtonState = other->mButtonState; - mXOffset = other->mXOffset; - mYOffset = other->mYOffset; - mXPrecision = other->mXPrecision; - mYPrecision = other->mYPrecision; - mDownTime = other->mDownTime; - mPointerProperties = other->mPointerProperties; - - if (keepHistory) { - mSampleEventTimes = other->mSampleEventTimes; - mSamplePointerCoords = other->mSamplePointerCoords; - } else { - mSampleEventTimes.clear(); - mSampleEventTimes.push(other->getEventTime()); - mSamplePointerCoords.clear(); - size_t pointerCount = other->getPointerCount(); - size_t historySize = other->getHistorySize(); - mSamplePointerCoords.appendArray(other->mSamplePointerCoords.array() - + (historySize * pointerCount), pointerCount); - } -} - -void MotionEvent::addSample( - int64_t eventTime, - const PointerCoords* pointerCoords) { - mSampleEventTimes.push(eventTime); - mSamplePointerCoords.appendArray(pointerCoords, getPointerCount()); -} - -const PointerCoords* MotionEvent::getRawPointerCoords(size_t pointerIndex) const { - return &mSamplePointerCoords[getHistorySize() * getPointerCount() + pointerIndex]; -} - -float MotionEvent::getRawAxisValue(int32_t axis, size_t pointerIndex) const { - return getRawPointerCoords(pointerIndex)->getAxisValue(axis); -} - -float MotionEvent::getAxisValue(int32_t axis, size_t pointerIndex) const { - float value = getRawPointerCoords(pointerIndex)->getAxisValue(axis); - switch (axis) { - case AMOTION_EVENT_AXIS_X: - return value + mXOffset; - case AMOTION_EVENT_AXIS_Y: - return value + mYOffset; - } - return value; -} - -const PointerCoords* MotionEvent::getHistoricalRawPointerCoords( - size_t pointerIndex, size_t historicalIndex) const { - return &mSamplePointerCoords[historicalIndex * getPointerCount() + pointerIndex]; -} - -float MotionEvent::getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex, - size_t historicalIndex) const { - return getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis); -} - -float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex, - size_t historicalIndex) const { - float value = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis); - switch (axis) { - case AMOTION_EVENT_AXIS_X: - return value + mXOffset; - case AMOTION_EVENT_AXIS_Y: - return value + mYOffset; - } - return value; -} - -ssize_t MotionEvent::findPointerIndex(int32_t pointerId) const { - size_t pointerCount = mPointerProperties.size(); - for (size_t i = 0; i < pointerCount; i++) { - if (mPointerProperties.itemAt(i).id == pointerId) { - return i; - } - } - return -1; -} - -void MotionEvent::offsetLocation(float xOffset, float yOffset) { - mXOffset += xOffset; - mYOffset += yOffset; -} - -void MotionEvent::scale(float scaleFactor) { - mXOffset *= scaleFactor; - mYOffset *= scaleFactor; - mXPrecision *= scaleFactor; - mYPrecision *= scaleFactor; - - size_t numSamples = mSamplePointerCoords.size(); - for (size_t i = 0; i < numSamples; i++) { - mSamplePointerCoords.editItemAt(i).scale(scaleFactor); - } -} - -#ifdef HAVE_ANDROID_OS -static inline float transformAngle(const SkMatrix* matrix, float angleRadians) { - // Construct and transform a vector oriented at the specified clockwise angle from vertical. - // Coordinate system: down is increasing Y, right is increasing X. - SkPoint vector; - vector.fX = SkFloatToScalar(sinf(angleRadians)); - vector.fY = SkFloatToScalar(-cosf(angleRadians)); - matrix->mapVectors(& vector, 1); - - // Derive the transformed vector's clockwise angle from vertical. - float result = atan2f(SkScalarToFloat(vector.fX), SkScalarToFloat(-vector.fY)); - if (result < - M_PI_2) { - result += M_PI; - } else if (result > M_PI_2) { - result -= M_PI; - } - return result; -} - -void MotionEvent::transform(const SkMatrix* matrix) { - float oldXOffset = mXOffset; - float oldYOffset = mYOffset; - - // The tricky part of this implementation is to preserve the value of - // rawX and rawY. So we apply the transformation to the first point - // then derive an appropriate new X/Y offset that will preserve rawX and rawY. - SkPoint point; - float rawX = getRawX(0); - float rawY = getRawY(0); - matrix->mapXY(SkFloatToScalar(rawX + oldXOffset), SkFloatToScalar(rawY + oldYOffset), - & point); - float newX = SkScalarToFloat(point.fX); - float newY = SkScalarToFloat(point.fY); - float newXOffset = newX - rawX; - float newYOffset = newY - rawY; - - mXOffset = newXOffset; - mYOffset = newYOffset; - - // Apply the transformation to all samples. - size_t numSamples = mSamplePointerCoords.size(); - for (size_t i = 0; i < numSamples; i++) { - PointerCoords& c = mSamplePointerCoords.editItemAt(i); - float x = c.getAxisValue(AMOTION_EVENT_AXIS_X) + oldXOffset; - float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y) + oldYOffset; - matrix->mapXY(SkFloatToScalar(x), SkFloatToScalar(y), &point); - c.setAxisValue(AMOTION_EVENT_AXIS_X, SkScalarToFloat(point.fX) - newXOffset); - c.setAxisValue(AMOTION_EVENT_AXIS_Y, SkScalarToFloat(point.fY) - newYOffset); - - float orientation = c.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION); - c.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, transformAngle(matrix, orientation)); - } -} - -status_t MotionEvent::readFromParcel(Parcel* parcel) { - size_t pointerCount = parcel->readInt32(); - size_t sampleCount = parcel->readInt32(); - if (pointerCount == 0 || pointerCount > MAX_POINTERS || sampleCount == 0) { - return BAD_VALUE; - } - - mDeviceId = parcel->readInt32(); - mSource = parcel->readInt32(); - mAction = parcel->readInt32(); - mFlags = parcel->readInt32(); - mEdgeFlags = parcel->readInt32(); - mMetaState = parcel->readInt32(); - mButtonState = parcel->readInt32(); - mXOffset = parcel->readFloat(); - mYOffset = parcel->readFloat(); - mXPrecision = parcel->readFloat(); - mYPrecision = parcel->readFloat(); - mDownTime = parcel->readInt64(); - - mPointerProperties.clear(); - mPointerProperties.setCapacity(pointerCount); - mSampleEventTimes.clear(); - mSampleEventTimes.setCapacity(sampleCount); - mSamplePointerCoords.clear(); - mSamplePointerCoords.setCapacity(sampleCount * pointerCount); - - for (size_t i = 0; i < pointerCount; i++) { - mPointerProperties.push(); - PointerProperties& properties = mPointerProperties.editTop(); - properties.id = parcel->readInt32(); - properties.toolType = parcel->readInt32(); - } - - while (sampleCount-- > 0) { - mSampleEventTimes.push(parcel->readInt64()); - for (size_t i = 0; i < pointerCount; i++) { - mSamplePointerCoords.push(); - status_t status = mSamplePointerCoords.editTop().readFromParcel(parcel); - if (status) { - return status; - } - } - } - return OK; -} - -status_t MotionEvent::writeToParcel(Parcel* parcel) const { - size_t pointerCount = mPointerProperties.size(); - size_t sampleCount = mSampleEventTimes.size(); - - parcel->writeInt32(pointerCount); - parcel->writeInt32(sampleCount); - - parcel->writeInt32(mDeviceId); - parcel->writeInt32(mSource); - parcel->writeInt32(mAction); - parcel->writeInt32(mFlags); - parcel->writeInt32(mEdgeFlags); - parcel->writeInt32(mMetaState); - parcel->writeInt32(mButtonState); - parcel->writeFloat(mXOffset); - parcel->writeFloat(mYOffset); - parcel->writeFloat(mXPrecision); - parcel->writeFloat(mYPrecision); - parcel->writeInt64(mDownTime); - - for (size_t i = 0; i < pointerCount; i++) { - const PointerProperties& properties = mPointerProperties.itemAt(i); - parcel->writeInt32(properties.id); - parcel->writeInt32(properties.toolType); - } - - const PointerCoords* pc = mSamplePointerCoords.array(); - for (size_t h = 0; h < sampleCount; h++) { - parcel->writeInt64(mSampleEventTimes.itemAt(h)); - for (size_t i = 0; i < pointerCount; i++) { - status_t status = (pc++)->writeToParcel(parcel); - if (status) { - return status; - } - } - } - return OK; -} -#endif - -bool MotionEvent::isTouchEvent(int32_t source, int32_t action) { - if (source & AINPUT_SOURCE_CLASS_POINTER) { - // Specifically excludes HOVER_MOVE and SCROLL. - switch (action & AMOTION_EVENT_ACTION_MASK) { - case AMOTION_EVENT_ACTION_DOWN: - case AMOTION_EVENT_ACTION_MOVE: - case AMOTION_EVENT_ACTION_UP: - case AMOTION_EVENT_ACTION_POINTER_DOWN: - case AMOTION_EVENT_ACTION_POINTER_UP: - case AMOTION_EVENT_ACTION_CANCEL: - case AMOTION_EVENT_ACTION_OUTSIDE: - return true; - } - } - return false; -} - - -// --- PooledInputEventFactory --- - -PooledInputEventFactory::PooledInputEventFactory(size_t maxPoolSize) : - mMaxPoolSize(maxPoolSize) { -} - -PooledInputEventFactory::~PooledInputEventFactory() { - for (size_t i = 0; i < mKeyEventPool.size(); i++) { - delete mKeyEventPool.itemAt(i); - } - for (size_t i = 0; i < mMotionEventPool.size(); i++) { - delete mMotionEventPool.itemAt(i); - } -} - -KeyEvent* PooledInputEventFactory::createKeyEvent() { - if (!mKeyEventPool.isEmpty()) { - KeyEvent* event = mKeyEventPool.top(); - mKeyEventPool.pop(); - return event; - } - return new KeyEvent(); -} - -MotionEvent* PooledInputEventFactory::createMotionEvent() { - if (!mMotionEventPool.isEmpty()) { - MotionEvent* event = mMotionEventPool.top(); - mMotionEventPool.pop(); - return event; - } - return new MotionEvent(); -} - -void PooledInputEventFactory::recycle(InputEvent* event) { - switch (event->getType()) { - case AINPUT_EVENT_TYPE_KEY: - if (mKeyEventPool.size() < mMaxPoolSize) { - mKeyEventPool.push(static_cast<KeyEvent*>(event)); - return; - } - break; - case AINPUT_EVENT_TYPE_MOTION: - if (mMotionEventPool.size() < mMaxPoolSize) { - mMotionEventPool.push(static_cast<MotionEvent*>(event)); - return; - } - break; - } - delete event; -} - -} // namespace android diff --git a/libs/androidfw/InputDevice.cpp b/libs/androidfw/InputDevice.cpp deleted file mode 100644 index f742052..0000000 --- a/libs/androidfw/InputDevice.cpp +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "InputDevice" - -#include <stdlib.h> -#include <unistd.h> -#include <ctype.h> - -#include <androidfw/InputDevice.h> - -namespace android { - -static const char* CONFIGURATION_FILE_DIR[] = { - "idc/", - "keylayout/", - "keychars/", -}; - -static const char* CONFIGURATION_FILE_EXTENSION[] = { - ".idc", - ".kl", - ".kcm", -}; - -static bool isValidNameChar(char ch) { - return isascii(ch) && (isdigit(ch) || isalpha(ch) || ch == '-' || ch == '_'); -} - -static void appendInputDeviceConfigurationFileRelativePath(String8& path, - const String8& name, InputDeviceConfigurationFileType type) { - path.append(CONFIGURATION_FILE_DIR[type]); - for (size_t i = 0; i < name.length(); i++) { - char ch = name[i]; - if (!isValidNameChar(ch)) { - ch = '_'; - } - path.append(&ch, 1); - } - path.append(CONFIGURATION_FILE_EXTENSION[type]); -} - -String8 getInputDeviceConfigurationFilePathByDeviceIdentifier( - const InputDeviceIdentifier& deviceIdentifier, - InputDeviceConfigurationFileType type) { - if (deviceIdentifier.vendor !=0 && deviceIdentifier.product != 0) { - if (deviceIdentifier.version != 0) { - // Try vendor product version. - String8 versionPath(getInputDeviceConfigurationFilePathByName( - String8::format("Vendor_%04x_Product_%04x_Version_%04x", - deviceIdentifier.vendor, deviceIdentifier.product, - deviceIdentifier.version), - type)); - if (!versionPath.isEmpty()) { - return versionPath; - } - } - - // Try vendor product. - String8 productPath(getInputDeviceConfigurationFilePathByName( - String8::format("Vendor_%04x_Product_%04x", - deviceIdentifier.vendor, deviceIdentifier.product), - type)); - if (!productPath.isEmpty()) { - return productPath; - } - } - - // Try device name. - return getInputDeviceConfigurationFilePathByName(deviceIdentifier.name, type); -} - -String8 getInputDeviceConfigurationFilePathByName( - const String8& name, InputDeviceConfigurationFileType type) { - // Search system repository. - String8 path; - path.setTo(getenv("ANDROID_ROOT")); - path.append("/usr/"); - appendInputDeviceConfigurationFileRelativePath(path, name, type); -#if DEBUG_PROBE - ALOGD("Probing for system provided input device configuration file: path='%s'", path.string()); -#endif - if (!access(path.string(), R_OK)) { -#if DEBUG_PROBE - ALOGD("Found"); -#endif - return path; - } - - // Search user repository. - // TODO Should only look here if not in safe mode. - path.setTo(getenv("ANDROID_DATA")); - path.append("/system/devices/"); - appendInputDeviceConfigurationFileRelativePath(path, name, type); -#if DEBUG_PROBE - ALOGD("Probing for system user input device configuration file: path='%s'", path.string()); -#endif - if (!access(path.string(), R_OK)) { -#if DEBUG_PROBE - ALOGD("Found"); -#endif - return path; - } - - // Not found. -#if DEBUG_PROBE - ALOGD("Probe failed to find input device configuration file: name='%s', type=%d", - name.string(), type); -#endif - return String8(); -} - - -// --- InputDeviceInfo --- - -InputDeviceInfo::InputDeviceInfo() { - initialize(-1, -1, InputDeviceIdentifier(), String8(), false); -} - -InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) : - mId(other.mId), mGeneration(other.mGeneration), mIdentifier(other.mIdentifier), - mAlias(other.mAlias), mIsExternal(other.mIsExternal), mSources(other.mSources), - mKeyboardType(other.mKeyboardType), - mKeyCharacterMap(other.mKeyCharacterMap), - mHasVibrator(other.mHasVibrator), - mMotionRanges(other.mMotionRanges) { -} - -InputDeviceInfo::~InputDeviceInfo() { -} - -void InputDeviceInfo::initialize(int32_t id, int32_t generation, - const InputDeviceIdentifier& identifier, const String8& alias, bool isExternal) { - mId = id; - mGeneration = generation; - mIdentifier = identifier; - mAlias = alias; - mIsExternal = isExternal; - mSources = 0; - mKeyboardType = AINPUT_KEYBOARD_TYPE_NONE; - mHasVibrator = false; - mMotionRanges.clear(); -} - -const InputDeviceInfo::MotionRange* InputDeviceInfo::getMotionRange( - int32_t axis, uint32_t source) const { - size_t numRanges = mMotionRanges.size(); - for (size_t i = 0; i < numRanges; i++) { - const MotionRange& range = mMotionRanges.itemAt(i); - if (range.axis == axis && range.source == source) { - return ⦥ - } - } - return NULL; -} - -void InputDeviceInfo::addSource(uint32_t source) { - mSources |= source; -} - -void InputDeviceInfo::addMotionRange(int32_t axis, uint32_t source, float min, float max, - float flat, float fuzz, float resolution) { - MotionRange range = { axis, source, min, max, flat, fuzz, resolution }; - mMotionRanges.add(range); -} - -void InputDeviceInfo::addMotionRange(const MotionRange& range) { - mMotionRanges.add(range); -} - -} // namespace android diff --git a/libs/androidfw/InputTransport.cpp b/libs/androidfw/InputTransport.cpp deleted file mode 100644 index 498389e..0000000 --- a/libs/androidfw/InputTransport.cpp +++ /dev/null @@ -1,957 +0,0 @@ -// -// Copyright 2010 The Android Open Source Project -// -// Provides a shared memory transport for input events. -// -#define LOG_TAG "InputTransport" - -//#define LOG_NDEBUG 0 - -// Log debug messages about channel messages (send message, receive message) -#define DEBUG_CHANNEL_MESSAGES 0 - -// Log debug messages whenever InputChannel objects are created/destroyed -#define DEBUG_CHANNEL_LIFECYCLE 0 - -// Log debug messages about transport actions -#define DEBUG_TRANSPORT_ACTIONS 0 - -// Log debug messages about touch event resampling -#define DEBUG_RESAMPLING 0 - - -#include <cutils/log.h> -#include <cutils/properties.h> -#include <errno.h> -#include <fcntl.h> -#include <androidfw/InputTransport.h> -#include <unistd.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <math.h> - - -namespace android { - -// Socket buffer size. The default is typically about 128KB, which is much larger than -// we really need. So we make it smaller. It just needs to be big enough to hold -// a few dozen large multi-finger motion events in the case where an application gets -// behind processing touches. -static const size_t SOCKET_BUFFER_SIZE = 32 * 1024; - -// Nanoseconds per milliseconds. -static const nsecs_t NANOS_PER_MS = 1000000; - -// Latency added during resampling. A few milliseconds doesn't hurt much but -// reduces the impact of mispredicted touch positions. -static const nsecs_t RESAMPLE_LATENCY = 5 * NANOS_PER_MS; - -// Minimum time difference between consecutive samples before attempting to resample. -static const nsecs_t RESAMPLE_MIN_DELTA = 2 * NANOS_PER_MS; - -// Maximum time to predict forward from the last known state, to avoid predicting too -// far into the future. This time is further bounded by 50% of the last time delta. -static const nsecs_t RESAMPLE_MAX_PREDICTION = 8 * NANOS_PER_MS; - -template<typename T> -inline static T min(const T& a, const T& b) { - return a < b ? a : b; -} - -inline static float lerp(float a, float b, float alpha) { - return a + alpha * (b - a); -} - -// --- InputMessage --- - -bool InputMessage::isValid(size_t actualSize) const { - if (size() == actualSize) { - switch (header.type) { - case TYPE_KEY: - return true; - case TYPE_MOTION: - return body.motion.pointerCount > 0 - && body.motion.pointerCount <= MAX_POINTERS; - case TYPE_FINISHED: - return true; - } - } - return false; -} - -size_t InputMessage::size() const { - switch (header.type) { - case TYPE_KEY: - return sizeof(Header) + body.key.size(); - case TYPE_MOTION: - return sizeof(Header) + body.motion.size(); - case TYPE_FINISHED: - return sizeof(Header) + body.finished.size(); - } - return sizeof(Header); -} - - -// --- InputChannel --- - -InputChannel::InputChannel(const String8& name, int fd) : - mName(name), mFd(fd) { -#if DEBUG_CHANNEL_LIFECYCLE - ALOGD("Input channel constructed: name='%s', fd=%d", - mName.string(), fd); -#endif - - int result = fcntl(mFd, F_SETFL, O_NONBLOCK); - LOG_ALWAYS_FATAL_IF(result != 0, "channel '%s' ~ Could not make socket " - "non-blocking. errno=%d", mName.string(), errno); -} - -InputChannel::~InputChannel() { -#if DEBUG_CHANNEL_LIFECYCLE - ALOGD("Input channel destroyed: name='%s', fd=%d", - mName.string(), mFd); -#endif - - ::close(mFd); -} - -status_t InputChannel::openInputChannelPair(const String8& name, - sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) { - int sockets[2]; - if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) { - status_t result = -errno; - ALOGE("channel '%s' ~ Could not create socket pair. errno=%d", - name.string(), errno); - outServerChannel.clear(); - outClientChannel.clear(); - return result; - } - - int bufferSize = SOCKET_BUFFER_SIZE; - setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)); - setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)); - setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)); - setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)); - - String8 serverChannelName = name; - serverChannelName.append(" (server)"); - outServerChannel = new InputChannel(serverChannelName, sockets[0]); - - String8 clientChannelName = name; - clientChannelName.append(" (client)"); - outClientChannel = new InputChannel(clientChannelName, sockets[1]); - return OK; -} - -status_t InputChannel::sendMessage(const InputMessage* msg) { - size_t msgLength = msg->size(); - ssize_t nWrite; - do { - nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL); - } while (nWrite == -1 && errno == EINTR); - - if (nWrite < 0) { - int error = errno; -#if DEBUG_CHANNEL_MESSAGES - ALOGD("channel '%s' ~ error sending message of type %d, errno=%d", mName.string(), - msg->header.type, error); -#endif - if (error == EAGAIN || error == EWOULDBLOCK) { - return WOULD_BLOCK; - } - if (error == EPIPE || error == ENOTCONN) { - return DEAD_OBJECT; - } - return -error; - } - - if (size_t(nWrite) != msgLength) { -#if DEBUG_CHANNEL_MESSAGES - ALOGD("channel '%s' ~ error sending message type %d, send was incomplete", - mName.string(), msg->header.type); -#endif - return DEAD_OBJECT; - } - -#if DEBUG_CHANNEL_MESSAGES - ALOGD("channel '%s' ~ sent message of type %d", mName.string(), msg->header.type); -#endif - return OK; -} - -status_t InputChannel::receiveMessage(InputMessage* msg) { - ssize_t nRead; - do { - nRead = ::recv(mFd, msg, sizeof(InputMessage), MSG_DONTWAIT); - } while (nRead == -1 && errno == EINTR); - - if (nRead < 0) { - int error = errno; -#if DEBUG_CHANNEL_MESSAGES - ALOGD("channel '%s' ~ receive message failed, errno=%d", mName.string(), errno); -#endif - if (error == EAGAIN || error == EWOULDBLOCK) { - return WOULD_BLOCK; - } - if (error == EPIPE || error == ENOTCONN) { - return DEAD_OBJECT; - } - return -error; - } - - if (nRead == 0) { // check for EOF -#if DEBUG_CHANNEL_MESSAGES - ALOGD("channel '%s' ~ receive message failed because peer was closed", mName.string()); -#endif - return DEAD_OBJECT; - } - - if (!msg->isValid(nRead)) { -#if DEBUG_CHANNEL_MESSAGES - ALOGD("channel '%s' ~ received invalid message", mName.string()); -#endif - return BAD_VALUE; - } - -#if DEBUG_CHANNEL_MESSAGES - ALOGD("channel '%s' ~ received message of type %d", mName.string(), msg->header.type); -#endif - return OK; -} - -sp<InputChannel> InputChannel::dup() const { - int fd = ::dup(getFd()); - return fd >= 0 ? new InputChannel(getName(), fd) : NULL; -} - - -// --- InputPublisher --- - -InputPublisher::InputPublisher(const sp<InputChannel>& channel) : - mChannel(channel) { -} - -InputPublisher::~InputPublisher() { -} - -status_t InputPublisher::publishKeyEvent( - uint32_t seq, - int32_t deviceId, - int32_t source, - int32_t action, - int32_t flags, - int32_t keyCode, - int32_t scanCode, - int32_t metaState, - int32_t repeatCount, - nsecs_t downTime, - nsecs_t eventTime) { -#if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' publisher ~ publishKeyEvent: seq=%u, deviceId=%d, source=0x%x, " - "action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d," - "downTime=%lld, eventTime=%lld", - mChannel->getName().string(), seq, - deviceId, source, action, flags, keyCode, scanCode, metaState, repeatCount, - downTime, eventTime); -#endif - - if (!seq) { - ALOGE("Attempted to publish a key event with sequence number 0."); - return BAD_VALUE; - } - - InputMessage msg; - msg.header.type = InputMessage::TYPE_KEY; - msg.body.key.seq = seq; - msg.body.key.deviceId = deviceId; - msg.body.key.source = source; - msg.body.key.action = action; - msg.body.key.flags = flags; - msg.body.key.keyCode = keyCode; - msg.body.key.scanCode = scanCode; - msg.body.key.metaState = metaState; - msg.body.key.repeatCount = repeatCount; - msg.body.key.downTime = downTime; - msg.body.key.eventTime = eventTime; - return mChannel->sendMessage(&msg); -} - -status_t InputPublisher::publishMotionEvent( - uint32_t seq, - int32_t deviceId, - int32_t source, - int32_t action, - int32_t flags, - int32_t edgeFlags, - int32_t metaState, - int32_t buttonState, - float xOffset, - float yOffset, - float xPrecision, - float yPrecision, - nsecs_t downTime, - nsecs_t eventTime, - size_t pointerCount, - const PointerProperties* pointerProperties, - const PointerCoords* pointerCoords) { -#if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, " - "action=0x%x, flags=0x%x, edgeFlags=0x%x, metaState=0x%x, buttonState=0x%x, " - "xOffset=%f, yOffset=%f, " - "xPrecision=%f, yPrecision=%f, downTime=%lld, eventTime=%lld, " - "pointerCount=%d", - mChannel->getName().string(), seq, - deviceId, source, action, flags, edgeFlags, metaState, buttonState, - xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime, pointerCount); -#endif - - if (!seq) { - ALOGE("Attempted to publish a motion event with sequence number 0."); - return BAD_VALUE; - } - - if (pointerCount > MAX_POINTERS || pointerCount < 1) { - ALOGE("channel '%s' publisher ~ Invalid number of pointers provided: %d.", - mChannel->getName().string(), pointerCount); - return BAD_VALUE; - } - - InputMessage msg; - msg.header.type = InputMessage::TYPE_MOTION; - msg.body.motion.seq = seq; - msg.body.motion.deviceId = deviceId; - msg.body.motion.source = source; - msg.body.motion.action = action; - msg.body.motion.flags = flags; - msg.body.motion.edgeFlags = edgeFlags; - msg.body.motion.metaState = metaState; - msg.body.motion.buttonState = buttonState; - msg.body.motion.xOffset = xOffset; - msg.body.motion.yOffset = yOffset; - msg.body.motion.xPrecision = xPrecision; - msg.body.motion.yPrecision = yPrecision; - msg.body.motion.downTime = downTime; - msg.body.motion.eventTime = eventTime; - msg.body.motion.pointerCount = pointerCount; - for (size_t i = 0; i < pointerCount; i++) { - msg.body.motion.pointers[i].properties.copyFrom(pointerProperties[i]); - msg.body.motion.pointers[i].coords.copyFrom(pointerCoords[i]); - } - return mChannel->sendMessage(&msg); -} - -status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandled) { -#if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' publisher ~ receiveFinishedSignal", - mChannel->getName().string()); -#endif - - InputMessage msg; - status_t result = mChannel->receiveMessage(&msg); - if (result) { - *outSeq = 0; - *outHandled = false; - return result; - } - if (msg.header.type != InputMessage::TYPE_FINISHED) { - ALOGE("channel '%s' publisher ~ Received unexpected message of type %d from consumer", - mChannel->getName().string(), msg.header.type); - return UNKNOWN_ERROR; - } - *outSeq = msg.body.finished.seq; - *outHandled = msg.body.finished.handled; - return OK; -} - -// --- InputConsumer --- - -InputConsumer::InputConsumer(const sp<InputChannel>& channel) : - mResampleTouch(isTouchResamplingEnabled()), - mChannel(channel), mMsgDeferred(false) { -} - -InputConsumer::~InputConsumer() { -} - -bool InputConsumer::isTouchResamplingEnabled() { - char value[PROPERTY_VALUE_MAX]; - int length = property_get("debug.inputconsumer.resample", value, NULL); - if (length > 0) { - if (!strcmp("0", value)) { - return false; - } - if (strcmp("1", value)) { - ALOGD("Unrecognized property value for 'debug.inputconsumer.resample'. " - "Use '1' or '0'."); - } - } - return true; -} - -status_t InputConsumer::consume(InputEventFactoryInterface* factory, - bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) { -#if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%lld", - mChannel->getName().string(), consumeBatches ? "true" : "false", frameTime); -#endif - - *outSeq = 0; - *outEvent = NULL; - - // Fetch the next input message. - // Loop until an event can be returned or no additional events are received. - while (!*outEvent) { - if (mMsgDeferred) { - // mMsg contains a valid input message from the previous call to consume - // that has not yet been processed. - mMsgDeferred = false; - } else { - // Receive a fresh message. - status_t result = mChannel->receiveMessage(&mMsg); - if (result) { - // Consume the next batched event unless batches are being held for later. - if (consumeBatches || result != WOULD_BLOCK) { - result = consumeBatch(factory, frameTime, outSeq, outEvent); - if (*outEvent) { -#if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' consumer ~ consumed batch event, seq=%u", - mChannel->getName().string(), *outSeq); -#endif - break; - } - } - return result; - } - } - - switch (mMsg.header.type) { - case InputMessage::TYPE_KEY: { - KeyEvent* keyEvent = factory->createKeyEvent(); - if (!keyEvent) return NO_MEMORY; - - initializeKeyEvent(keyEvent, &mMsg); - *outSeq = mMsg.body.key.seq; - *outEvent = keyEvent; -#if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' consumer ~ consumed key event, seq=%u", - mChannel->getName().string(), *outSeq); -#endif - break; - } - - case AINPUT_EVENT_TYPE_MOTION: { - ssize_t batchIndex = findBatch(mMsg.body.motion.deviceId, mMsg.body.motion.source); - if (batchIndex >= 0) { - Batch& batch = mBatches.editItemAt(batchIndex); - if (canAddSample(batch, &mMsg)) { - batch.samples.push(mMsg); -#if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' consumer ~ appended to batch event", - mChannel->getName().string()); -#endif - break; - } else { - // We cannot append to the batch in progress, so we need to consume - // the previous batch right now and defer the new message until later. - mMsgDeferred = true; - status_t result = consumeSamples(factory, - batch, batch.samples.size(), outSeq, outEvent); - mBatches.removeAt(batchIndex); - if (result) { - return result; - } -#if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' consumer ~ consumed batch event and " - "deferred current event, seq=%u", - mChannel->getName().string(), *outSeq); -#endif - break; - } - } - - // Start a new batch if needed. - if (mMsg.body.motion.action == AMOTION_EVENT_ACTION_MOVE - || mMsg.body.motion.action == AMOTION_EVENT_ACTION_HOVER_MOVE) { - mBatches.push(); - Batch& batch = mBatches.editTop(); - batch.samples.push(mMsg); -#if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' consumer ~ started batch event", - mChannel->getName().string()); -#endif - break; - } - - MotionEvent* motionEvent = factory->createMotionEvent(); - if (! motionEvent) return NO_MEMORY; - - updateTouchState(&mMsg); - initializeMotionEvent(motionEvent, &mMsg); - *outSeq = mMsg.body.motion.seq; - *outEvent = motionEvent; -#if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' consumer ~ consumed motion event, seq=%u", - mChannel->getName().string(), *outSeq); -#endif - break; - } - - default: - ALOGE("channel '%s' consumer ~ Received unexpected message of type %d", - mChannel->getName().string(), mMsg.header.type); - return UNKNOWN_ERROR; - } - } - return OK; -} - -status_t InputConsumer::consumeBatch(InputEventFactoryInterface* factory, - nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) { - status_t result; - for (size_t i = mBatches.size(); i-- > 0; ) { - Batch& batch = mBatches.editItemAt(i); - if (frameTime < 0) { - result = consumeSamples(factory, batch, batch.samples.size(), - outSeq, outEvent); - mBatches.removeAt(i); - return result; - } - - nsecs_t sampleTime = frameTime - RESAMPLE_LATENCY; - ssize_t split = findSampleNoLaterThan(batch, sampleTime); - if (split < 0) { - continue; - } - - result = consumeSamples(factory, batch, split + 1, outSeq, outEvent); - const InputMessage* next; - if (batch.samples.isEmpty()) { - mBatches.removeAt(i); - next = NULL; - } else { - next = &batch.samples.itemAt(0); - } - if (!result) { - resampleTouchState(sampleTime, static_cast<MotionEvent*>(*outEvent), next); - } - return result; - } - - return WOULD_BLOCK; -} - -status_t InputConsumer::consumeSamples(InputEventFactoryInterface* factory, - Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent) { - MotionEvent* motionEvent = factory->createMotionEvent(); - if (! motionEvent) return NO_MEMORY; - - uint32_t chain = 0; - for (size_t i = 0; i < count; i++) { - InputMessage& msg = batch.samples.editItemAt(i); - updateTouchState(&msg); - if (i) { - SeqChain seqChain; - seqChain.seq = msg.body.motion.seq; - seqChain.chain = chain; - mSeqChains.push(seqChain); - addSample(motionEvent, &msg); - } else { - initializeMotionEvent(motionEvent, &msg); - } - chain = msg.body.motion.seq; - } - batch.samples.removeItemsAt(0, count); - - *outSeq = chain; - *outEvent = motionEvent; - return OK; -} - -void InputConsumer::updateTouchState(InputMessage* msg) { - if (!mResampleTouch || - !(msg->body.motion.source & AINPUT_SOURCE_CLASS_POINTER)) { - return; - } - - int32_t deviceId = msg->body.motion.deviceId; - int32_t source = msg->body.motion.source; - nsecs_t eventTime = msg->body.motion.eventTime; - - // Update the touch state history to incorporate the new input message. - // If the message is in the past relative to the most recently produced resampled - // touch, then use the resampled time and coordinates instead. - switch (msg->body.motion.action & AMOTION_EVENT_ACTION_MASK) { - case AMOTION_EVENT_ACTION_DOWN: { - ssize_t index = findTouchState(deviceId, source); - if (index < 0) { - mTouchStates.push(); - index = mTouchStates.size() - 1; - } - TouchState& touchState = mTouchStates.editItemAt(index); - touchState.initialize(deviceId, source); - touchState.addHistory(msg); - break; - } - - case AMOTION_EVENT_ACTION_MOVE: { - ssize_t index = findTouchState(deviceId, source); - if (index >= 0) { - TouchState& touchState = mTouchStates.editItemAt(index); - touchState.addHistory(msg); - if (eventTime < touchState.lastResample.eventTime) { - rewriteMessage(touchState, msg); - } else { - touchState.lastResample.idBits.clear(); - } - } - break; - } - - case AMOTION_EVENT_ACTION_POINTER_DOWN: { - ssize_t index = findTouchState(deviceId, source); - if (index >= 0) { - TouchState& touchState = mTouchStates.editItemAt(index); - touchState.lastResample.idBits.clearBit(msg->body.motion.getActionId()); - rewriteMessage(touchState, msg); - } - break; - } - - case AMOTION_EVENT_ACTION_POINTER_UP: { - ssize_t index = findTouchState(deviceId, source); - if (index >= 0) { - TouchState& touchState = mTouchStates.editItemAt(index); - rewriteMessage(touchState, msg); - touchState.lastResample.idBits.clearBit(msg->body.motion.getActionId()); - } - break; - } - - case AMOTION_EVENT_ACTION_SCROLL: { - ssize_t index = findTouchState(deviceId, source); - if (index >= 0) { - const TouchState& touchState = mTouchStates.itemAt(index); - rewriteMessage(touchState, msg); - } - break; - } - - case AMOTION_EVENT_ACTION_UP: - case AMOTION_EVENT_ACTION_CANCEL: { - ssize_t index = findTouchState(deviceId, source); - if (index >= 0) { - const TouchState& touchState = mTouchStates.itemAt(index); - rewriteMessage(touchState, msg); - mTouchStates.removeAt(index); - } - break; - } - } -} - -void InputConsumer::rewriteMessage(const TouchState& state, InputMessage* msg) { - for (size_t i = 0; i < msg->body.motion.pointerCount; i++) { - uint32_t id = msg->body.motion.pointers[i].properties.id; - if (state.lastResample.idBits.hasBit(id)) { - PointerCoords& msgCoords = msg->body.motion.pointers[i].coords; - const PointerCoords& resampleCoords = state.lastResample.getPointerById(id); -#if DEBUG_RESAMPLING - ALOGD("[%d] - rewrite (%0.3f, %0.3f), old (%0.3f, %0.3f)", id, - resampleCoords.getAxisValue(AMOTION_EVENT_AXIS_X), - resampleCoords.getAxisValue(AMOTION_EVENT_AXIS_Y), - msgCoords.getAxisValue(AMOTION_EVENT_AXIS_X), - msgCoords.getAxisValue(AMOTION_EVENT_AXIS_Y)); -#endif - msgCoords.setAxisValue(AMOTION_EVENT_AXIS_X, resampleCoords.getX()); - msgCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, resampleCoords.getY()); - } - } -} - -void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event, - const InputMessage* next) { - if (!mResampleTouch - || !(event->getSource() & AINPUT_SOURCE_CLASS_POINTER) - || event->getAction() != AMOTION_EVENT_ACTION_MOVE) { - return; - } - - ssize_t index = findTouchState(event->getDeviceId(), event->getSource()); - if (index < 0) { -#if DEBUG_RESAMPLING - ALOGD("Not resampled, no touch state for device."); -#endif - return; - } - - TouchState& touchState = mTouchStates.editItemAt(index); - if (touchState.historySize < 1) { -#if DEBUG_RESAMPLING - ALOGD("Not resampled, no history for device."); -#endif - return; - } - - // Ensure that the current sample has all of the pointers that need to be reported. - const History* current = touchState.getHistory(0); - size_t pointerCount = event->getPointerCount(); - for (size_t i = 0; i < pointerCount; i++) { - uint32_t id = event->getPointerId(i); - if (!current->idBits.hasBit(id)) { -#if DEBUG_RESAMPLING - ALOGD("Not resampled, missing id %d", id); -#endif - return; - } - } - - // Find the data to use for resampling. - const History* other; - History future; - float alpha; - if (next) { - // Interpolate between current sample and future sample. - // So current->eventTime <= sampleTime <= future.eventTime. - future.initializeFrom(next); - other = &future; - nsecs_t delta = future.eventTime - current->eventTime; - if (delta < RESAMPLE_MIN_DELTA) { -#if DEBUG_RESAMPLING - ALOGD("Not resampled, delta time is %lld ns.", delta); -#endif - return; - } - alpha = float(sampleTime - current->eventTime) / delta; - } else if (touchState.historySize >= 2) { - // Extrapolate future sample using current sample and past sample. - // So other->eventTime <= current->eventTime <= sampleTime. - other = touchState.getHistory(1); - nsecs_t delta = current->eventTime - other->eventTime; - if (delta < RESAMPLE_MIN_DELTA) { -#if DEBUG_RESAMPLING - ALOGD("Not resampled, delta time is %lld ns.", delta); -#endif - return; - } - nsecs_t maxPredict = current->eventTime + min(delta / 2, RESAMPLE_MAX_PREDICTION); - if (sampleTime > maxPredict) { -#if DEBUG_RESAMPLING - ALOGD("Sample time is too far in the future, adjusting prediction " - "from %lld to %lld ns.", - sampleTime - current->eventTime, maxPredict - current->eventTime); -#endif - sampleTime = maxPredict; - } - alpha = float(current->eventTime - sampleTime) / delta; - } else { -#if DEBUG_RESAMPLING - ALOGD("Not resampled, insufficient data."); -#endif - return; - } - - // Resample touch coordinates. - touchState.lastResample.eventTime = sampleTime; - touchState.lastResample.idBits.clear(); - for (size_t i = 0; i < pointerCount; i++) { - uint32_t id = event->getPointerId(i); - touchState.lastResample.idToIndex[id] = i; - touchState.lastResample.idBits.markBit(id); - PointerCoords& resampledCoords = touchState.lastResample.pointers[i]; - const PointerCoords& currentCoords = current->getPointerById(id); - if (other->idBits.hasBit(id) - && shouldResampleTool(event->getToolType(i))) { - const PointerCoords& otherCoords = other->getPointerById(id); - resampledCoords.copyFrom(currentCoords); - resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_X, - lerp(currentCoords.getX(), otherCoords.getX(), alpha)); - resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, - lerp(currentCoords.getY(), otherCoords.getY(), alpha)); -#if DEBUG_RESAMPLING - ALOGD("[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f), " - "other (%0.3f, %0.3f), alpha %0.3f", - id, resampledCoords.getX(), resampledCoords.getY(), - currentCoords.getX(), currentCoords.getY(), - otherCoords.getX(), otherCoords.getY(), - alpha); -#endif - } else { - resampledCoords.copyFrom(currentCoords); -#if DEBUG_RESAMPLING - ALOGD("[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f)", - id, resampledCoords.getX(), resampledCoords.getY(), - currentCoords.getX(), currentCoords.getY()); -#endif - } - } - - event->addSample(sampleTime, touchState.lastResample.pointers); -} - -bool InputConsumer::shouldResampleTool(int32_t toolType) { - return toolType == AMOTION_EVENT_TOOL_TYPE_FINGER - || toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN; -} - -status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) { -#if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' consumer ~ sendFinishedSignal: seq=%u, handled=%s", - mChannel->getName().string(), seq, handled ? "true" : "false"); -#endif - - if (!seq) { - ALOGE("Attempted to send a finished signal with sequence number 0."); - return BAD_VALUE; - } - - // Send finished signals for the batch sequence chain first. - size_t seqChainCount = mSeqChains.size(); - if (seqChainCount) { - uint32_t currentSeq = seq; - uint32_t chainSeqs[seqChainCount]; - size_t chainIndex = 0; - for (size_t i = seqChainCount; i-- > 0; ) { - const SeqChain& seqChain = mSeqChains.itemAt(i); - if (seqChain.seq == currentSeq) { - currentSeq = seqChain.chain; - chainSeqs[chainIndex++] = currentSeq; - mSeqChains.removeAt(i); - } - } - status_t status = OK; - while (!status && chainIndex-- > 0) { - status = sendUnchainedFinishedSignal(chainSeqs[chainIndex], handled); - } - if (status) { - // An error occurred so at least one signal was not sent, reconstruct the chain. - do { - SeqChain seqChain; - seqChain.seq = chainIndex != 0 ? chainSeqs[chainIndex - 1] : seq; - seqChain.chain = chainSeqs[chainIndex]; - mSeqChains.push(seqChain); - } while (chainIndex-- > 0); - return status; - } - } - - // Send finished signal for the last message in the batch. - return sendUnchainedFinishedSignal(seq, handled); -} - -status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) { - InputMessage msg; - msg.header.type = InputMessage::TYPE_FINISHED; - msg.body.finished.seq = seq; - msg.body.finished.handled = handled; - return mChannel->sendMessage(&msg); -} - -bool InputConsumer::hasDeferredEvent() const { - return mMsgDeferred; -} - -bool InputConsumer::hasPendingBatch() const { - return !mBatches.isEmpty(); -} - -ssize_t InputConsumer::findBatch(int32_t deviceId, int32_t source) const { - for (size_t i = 0; i < mBatches.size(); i++) { - const Batch& batch = mBatches.itemAt(i); - const InputMessage& head = batch.samples.itemAt(0); - if (head.body.motion.deviceId == deviceId && head.body.motion.source == source) { - return i; - } - } - return -1; -} - -ssize_t InputConsumer::findTouchState(int32_t deviceId, int32_t source) const { - for (size_t i = 0; i < mTouchStates.size(); i++) { - const TouchState& touchState = mTouchStates.itemAt(i); - if (touchState.deviceId == deviceId && touchState.source == source) { - return i; - } - } - return -1; -} - -void InputConsumer::initializeKeyEvent(KeyEvent* event, const InputMessage* msg) { - event->initialize( - msg->body.key.deviceId, - msg->body.key.source, - msg->body.key.action, - msg->body.key.flags, - msg->body.key.keyCode, - msg->body.key.scanCode, - msg->body.key.metaState, - msg->body.key.repeatCount, - msg->body.key.downTime, - msg->body.key.eventTime); -} - -void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage* msg) { - size_t pointerCount = msg->body.motion.pointerCount; - PointerProperties pointerProperties[pointerCount]; - PointerCoords pointerCoords[pointerCount]; - for (size_t i = 0; i < pointerCount; i++) { - pointerProperties[i].copyFrom(msg->body.motion.pointers[i].properties); - pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords); - } - - event->initialize( - msg->body.motion.deviceId, - msg->body.motion.source, - msg->body.motion.action, - msg->body.motion.flags, - msg->body.motion.edgeFlags, - msg->body.motion.metaState, - msg->body.motion.buttonState, - msg->body.motion.xOffset, - msg->body.motion.yOffset, - msg->body.motion.xPrecision, - msg->body.motion.yPrecision, - msg->body.motion.downTime, - msg->body.motion.eventTime, - pointerCount, - pointerProperties, - pointerCoords); -} - -void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) { - size_t pointerCount = msg->body.motion.pointerCount; - PointerCoords pointerCoords[pointerCount]; - for (size_t i = 0; i < pointerCount; i++) { - pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords); - } - - event->setMetaState(event->getMetaState() | msg->body.motion.metaState); - event->addSample(msg->body.motion.eventTime, pointerCoords); -} - -bool InputConsumer::canAddSample(const Batch& batch, const InputMessage *msg) { - const InputMessage& head = batch.samples.itemAt(0); - size_t pointerCount = msg->body.motion.pointerCount; - if (head.body.motion.pointerCount != pointerCount - || head.body.motion.action != msg->body.motion.action) { - return false; - } - for (size_t i = 0; i < pointerCount; i++) { - if (head.body.motion.pointers[i].properties - != msg->body.motion.pointers[i].properties) { - return false; - } - } - return true; -} - -ssize_t InputConsumer::findSampleNoLaterThan(const Batch& batch, nsecs_t time) { - size_t numSamples = batch.samples.size(); - size_t index = 0; - while (index < numSamples - && batch.samples.itemAt(index).body.motion.eventTime <= time) { - index += 1; - } - return ssize_t(index) - 1; -} - -} // namespace android diff --git a/libs/androidfw/KeyCharacterMap.cpp b/libs/androidfw/KeyCharacterMap.cpp deleted file mode 100644 index 36cb6e1..0000000 --- a/libs/androidfw/KeyCharacterMap.cpp +++ /dev/null @@ -1,1153 +0,0 @@ -/* - * 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 "KeyCharacterMap" - -#include <stdlib.h> -#include <string.h> -#include <android/keycodes.h> -#include <androidfw/Keyboard.h> -#include <androidfw/KeyCharacterMap.h> - -#if HAVE_ANDROID_OS -#include <binder/Parcel.h> -#endif - -#include <utils/Log.h> -#include <utils/Errors.h> -#include <utils/Tokenizer.h> -#include <utils/Timers.h> - -// Enables debug output for the parser. -#define DEBUG_PARSER 0 - -// Enables debug output for parser performance. -#define DEBUG_PARSER_PERFORMANCE 0 - -// Enables debug output for mapping. -#define DEBUG_MAPPING 0 - - -namespace android { - -static const char* WHITESPACE = " \t\r"; -static const char* WHITESPACE_OR_PROPERTY_DELIMITER = " \t\r,:"; - -struct Modifier { - const char* label; - int32_t metaState; -}; -static const Modifier modifiers[] = { - { "shift", AMETA_SHIFT_ON }, - { "lshift", AMETA_SHIFT_LEFT_ON }, - { "rshift", AMETA_SHIFT_RIGHT_ON }, - { "alt", AMETA_ALT_ON }, - { "lalt", AMETA_ALT_LEFT_ON }, - { "ralt", AMETA_ALT_RIGHT_ON }, - { "ctrl", AMETA_CTRL_ON }, - { "lctrl", AMETA_CTRL_LEFT_ON }, - { "rctrl", AMETA_CTRL_RIGHT_ON }, - { "meta", AMETA_META_ON }, - { "lmeta", AMETA_META_LEFT_ON }, - { "rmeta", AMETA_META_RIGHT_ON }, - { "sym", AMETA_SYM_ON }, - { "fn", AMETA_FUNCTION_ON }, - { "capslock", AMETA_CAPS_LOCK_ON }, - { "numlock", AMETA_NUM_LOCK_ON }, - { "scrolllock", AMETA_SCROLL_LOCK_ON }, -}; - -#if DEBUG_MAPPING -static String8 toString(const char16_t* chars, size_t numChars) { - String8 result; - for (size_t i = 0; i < numChars; i++) { - result.appendFormat(i == 0 ? "%d" : ", %d", chars[i]); - } - return result; -} -#endif - - -// --- KeyCharacterMap --- - -sp<KeyCharacterMap> KeyCharacterMap::sEmpty = new KeyCharacterMap(); - -KeyCharacterMap::KeyCharacterMap() : - mType(KEYBOARD_TYPE_UNKNOWN) { -} - -KeyCharacterMap::KeyCharacterMap(const KeyCharacterMap& other) : - RefBase(), mType(other.mType), mKeysByScanCode(other.mKeysByScanCode), - mKeysByUsageCode(other.mKeysByUsageCode) { - for (size_t i = 0; i < other.mKeys.size(); i++) { - mKeys.add(other.mKeys.keyAt(i), new Key(*other.mKeys.valueAt(i))); - } -} - -KeyCharacterMap::~KeyCharacterMap() { - for (size_t i = 0; i < mKeys.size(); i++) { - Key* key = mKeys.editValueAt(i); - delete key; - } -} - -status_t KeyCharacterMap::load(const String8& filename, - Format format, sp<KeyCharacterMap>* outMap) { - outMap->clear(); - - Tokenizer* tokenizer; - status_t status = Tokenizer::open(filename, &tokenizer); - if (status) { - ALOGE("Error %d opening key character map file %s.", status, filename.string()); - } else { - status = load(tokenizer, format, outMap); - delete tokenizer; - } - return status; -} - -status_t KeyCharacterMap::loadContents(const String8& filename, const char* contents, - Format format, sp<KeyCharacterMap>* outMap) { - outMap->clear(); - - Tokenizer* tokenizer; - status_t status = Tokenizer::fromContents(filename, contents, &tokenizer); - if (status) { - ALOGE("Error %d opening key character map.", status); - } else { - status = load(tokenizer, format, outMap); - delete tokenizer; - } - return status; -} - -status_t KeyCharacterMap::load(Tokenizer* tokenizer, - Format format, sp<KeyCharacterMap>* outMap) { - status_t status = OK; - sp<KeyCharacterMap> map = new KeyCharacterMap(); - if (!map.get()) { - ALOGE("Error allocating key character map."); - status = NO_MEMORY; - } else { -#if DEBUG_PARSER_PERFORMANCE - nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); -#endif - Parser parser(map.get(), tokenizer, format); - status = parser.parse(); -#if DEBUG_PARSER_PERFORMANCE - nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; - ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.", - tokenizer->getFilename().string(), tokenizer->getLineNumber(), - elapsedTime / 1000000.0); -#endif - if (!status) { - *outMap = map; - } - } - return status; -} - -sp<KeyCharacterMap> KeyCharacterMap::combine(const sp<KeyCharacterMap>& base, - const sp<KeyCharacterMap>& overlay) { - if (overlay == NULL) { - return base; - } - if (base == NULL) { - return overlay; - } - - sp<KeyCharacterMap> map = new KeyCharacterMap(*base.get()); - for (size_t i = 0; i < overlay->mKeys.size(); i++) { - int32_t keyCode = overlay->mKeys.keyAt(i); - Key* key = overlay->mKeys.valueAt(i); - ssize_t oldIndex = map->mKeys.indexOfKey(keyCode); - if (oldIndex >= 0) { - delete map->mKeys.valueAt(oldIndex); - map->mKeys.editValueAt(oldIndex) = new Key(*key); - } else { - map->mKeys.add(keyCode, new Key(*key)); - } - } - - for (size_t i = 0; i < overlay->mKeysByScanCode.size(); i++) { - map->mKeysByScanCode.replaceValueFor(overlay->mKeysByScanCode.keyAt(i), - overlay->mKeysByScanCode.valueAt(i)); - } - - for (size_t i = 0; i < overlay->mKeysByUsageCode.size(); i++) { - map->mKeysByUsageCode.replaceValueFor(overlay->mKeysByUsageCode.keyAt(i), - overlay->mKeysByUsageCode.valueAt(i)); - } - return map; -} - -sp<KeyCharacterMap> KeyCharacterMap::empty() { - return sEmpty; -} - -int32_t KeyCharacterMap::getKeyboardType() const { - return mType; -} - -char16_t KeyCharacterMap::getDisplayLabel(int32_t keyCode) const { - char16_t result = 0; - const Key* key; - if (getKey(keyCode, &key)) { - result = key->label; - } -#if DEBUG_MAPPING - ALOGD("getDisplayLabel: keyCode=%d ~ Result %d.", keyCode, result); -#endif - return result; -} - -char16_t KeyCharacterMap::getNumber(int32_t keyCode) const { - char16_t result = 0; - const Key* key; - if (getKey(keyCode, &key)) { - result = key->number; - } -#if DEBUG_MAPPING - ALOGD("getNumber: keyCode=%d ~ Result %d.", keyCode, result); -#endif - return result; -} - -char16_t KeyCharacterMap::getCharacter(int32_t keyCode, int32_t metaState) const { - char16_t result = 0; - const Key* key; - const Behavior* behavior; - if (getKeyBehavior(keyCode, metaState, &key, &behavior)) { - result = behavior->character; - } -#if DEBUG_MAPPING - ALOGD("getCharacter: keyCode=%d, metaState=0x%08x ~ Result %d.", keyCode, metaState, result); -#endif - return result; -} - -bool KeyCharacterMap::getFallbackAction(int32_t keyCode, int32_t metaState, - FallbackAction* outFallbackAction) const { - outFallbackAction->keyCode = 0; - outFallbackAction->metaState = 0; - - bool result = false; - const Key* key; - const Behavior* behavior; - if (getKeyBehavior(keyCode, metaState, &key, &behavior)) { - if (behavior->fallbackKeyCode) { - outFallbackAction->keyCode = behavior->fallbackKeyCode; - outFallbackAction->metaState = metaState & ~behavior->metaState; - result = true; - } - } -#if DEBUG_MAPPING - ALOGD("getFallbackKeyCode: keyCode=%d, metaState=0x%08x ~ Result %s, " - "fallback keyCode=%d, fallback metaState=0x%08x.", - keyCode, metaState, result ? "true" : "false", - outFallbackAction->keyCode, outFallbackAction->metaState); -#endif - return result; -} - -char16_t KeyCharacterMap::getMatch(int32_t keyCode, const char16_t* chars, size_t numChars, - int32_t metaState) const { - char16_t result = 0; - const Key* key; - if (getKey(keyCode, &key)) { - // Try to find the most general behavior that maps to this character. - // For example, the base key behavior will usually be last in the list. - // However, if we find a perfect meta state match for one behavior then use that one. - for (const Behavior* behavior = key->firstBehavior; behavior; behavior = behavior->next) { - if (behavior->character) { - for (size_t i = 0; i < numChars; i++) { - if (behavior->character == chars[i]) { - result = behavior->character; - if ((behavior->metaState & metaState) == behavior->metaState) { - goto ExactMatch; - } - break; - } - } - } - } - ExactMatch: ; - } -#if DEBUG_MAPPING - ALOGD("getMatch: keyCode=%d, chars=[%s], metaState=0x%08x ~ Result %d.", - keyCode, toString(chars, numChars).string(), metaState, result); -#endif - return result; -} - -bool KeyCharacterMap::getEvents(int32_t deviceId, const char16_t* chars, size_t numChars, - Vector<KeyEvent>& outEvents) const { - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - - for (size_t i = 0; i < numChars; i++) { - int32_t keyCode, metaState; - char16_t ch = chars[i]; - if (!findKey(ch, &keyCode, &metaState)) { -#if DEBUG_MAPPING - ALOGD("getEvents: deviceId=%d, chars=[%s] ~ Failed to find mapping for character %d.", - deviceId, toString(chars, numChars).string(), ch); -#endif - return false; - } - - int32_t currentMetaState = 0; - addMetaKeys(outEvents, deviceId, metaState, true, now, ¤tMetaState); - addKey(outEvents, deviceId, keyCode, currentMetaState, true, now); - addKey(outEvents, deviceId, keyCode, currentMetaState, false, now); - addMetaKeys(outEvents, deviceId, metaState, false, now, ¤tMetaState); - } -#if DEBUG_MAPPING - ALOGD("getEvents: deviceId=%d, chars=[%s] ~ Generated %d events.", - deviceId, toString(chars, numChars).string(), int32_t(outEvents.size())); - for (size_t i = 0; i < outEvents.size(); i++) { - ALOGD(" Key: keyCode=%d, metaState=0x%08x, %s.", - outEvents[i].getKeyCode(), outEvents[i].getMetaState(), - outEvents[i].getAction() == AKEY_EVENT_ACTION_DOWN ? "down" : "up"); - } -#endif - return true; -} - -status_t KeyCharacterMap::mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode) const { - if (usageCode) { - ssize_t index = mKeysByUsageCode.indexOfKey(usageCode); - if (index >= 0) { -#if DEBUG_MAPPING - ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d.", - scanCode, usageCode, *outKeyCode); -#endif - *outKeyCode = mKeysByUsageCode.valueAt(index); - return OK; - } - } - if (scanCode) { - ssize_t index = mKeysByScanCode.indexOfKey(scanCode); - if (index >= 0) { -#if DEBUG_MAPPING - ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d.", - scanCode, usageCode, *outKeyCode); -#endif - *outKeyCode = mKeysByScanCode.valueAt(index); - return OK; - } - } - -#if DEBUG_MAPPING - ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Failed.", scanCode, usageCode); -#endif - *outKeyCode = AKEYCODE_UNKNOWN; - return NAME_NOT_FOUND; -} - -bool KeyCharacterMap::getKey(int32_t keyCode, const Key** outKey) const { - ssize_t index = mKeys.indexOfKey(keyCode); - if (index >= 0) { - *outKey = mKeys.valueAt(index); - return true; - } - return false; -} - -bool KeyCharacterMap::getKeyBehavior(int32_t keyCode, int32_t metaState, - const Key** outKey, const Behavior** outBehavior) const { - const Key* key; - if (getKey(keyCode, &key)) { - const Behavior* behavior = key->firstBehavior; - while (behavior) { - if (matchesMetaState(metaState, behavior->metaState)) { - *outKey = key; - *outBehavior = behavior; - return true; - } - behavior = behavior->next; - } - } - return false; -} - -bool KeyCharacterMap::matchesMetaState(int32_t eventMetaState, int32_t behaviorMetaState) { - // Behavior must have at least the set of meta states specified. - // And if the key event has CTRL, ALT or META then the behavior must exactly - // match those, taking into account that a behavior can specify that it handles - // one, both or either of a left/right modifier pair. - if ((eventMetaState & behaviorMetaState) == behaviorMetaState) { - const int32_t EXACT_META_STATES = - AMETA_CTRL_ON | AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON - | AMETA_ALT_ON | AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON - | AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON; - int32_t unmatchedMetaState = eventMetaState & ~behaviorMetaState & EXACT_META_STATES; - if (behaviorMetaState & AMETA_CTRL_ON) { - unmatchedMetaState &= ~(AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON); - } else if (behaviorMetaState & (AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) { - unmatchedMetaState &= ~AMETA_CTRL_ON; - } - if (behaviorMetaState & AMETA_ALT_ON) { - unmatchedMetaState &= ~(AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON); - } else if (behaviorMetaState & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) { - unmatchedMetaState &= ~AMETA_ALT_ON; - } - if (behaviorMetaState & AMETA_META_ON) { - unmatchedMetaState &= ~(AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON); - } else if (behaviorMetaState & (AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON)) { - unmatchedMetaState &= ~AMETA_META_ON; - } - return !unmatchedMetaState; - } - return false; -} - -bool KeyCharacterMap::findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMetaState) const { - if (!ch) { - return false; - } - - for (size_t i = 0; i < mKeys.size(); i++) { - const Key* key = mKeys.valueAt(i); - - // Try to find the most general behavior that maps to this character. - // For example, the base key behavior will usually be last in the list. - const Behavior* found = NULL; - for (const Behavior* behavior = key->firstBehavior; behavior; behavior = behavior->next) { - if (behavior->character == ch) { - found = behavior; - } - } - if (found) { - *outKeyCode = mKeys.keyAt(i); - *outMetaState = found->metaState; - return true; - } - } - return false; -} - -void KeyCharacterMap::addKey(Vector<KeyEvent>& outEvents, - int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time) { - outEvents.push(); - KeyEvent& event = outEvents.editTop(); - event.initialize(deviceId, AINPUT_SOURCE_KEYBOARD, - down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, - 0, keyCode, 0, metaState, 0, time, time); -} - -void KeyCharacterMap::addMetaKeys(Vector<KeyEvent>& outEvents, - int32_t deviceId, int32_t metaState, bool down, nsecs_t time, - int32_t* currentMetaState) { - // Add and remove meta keys symmetrically. - if (down) { - addLockedMetaKey(outEvents, deviceId, metaState, time, - AKEYCODE_CAPS_LOCK, AMETA_CAPS_LOCK_ON, currentMetaState); - addLockedMetaKey(outEvents, deviceId, metaState, time, - AKEYCODE_NUM_LOCK, AMETA_NUM_LOCK_ON, currentMetaState); - addLockedMetaKey(outEvents, deviceId, metaState, time, - AKEYCODE_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, currentMetaState); - - addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time, - AKEYCODE_SHIFT_LEFT, AMETA_SHIFT_LEFT_ON, - AKEYCODE_SHIFT_RIGHT, AMETA_SHIFT_RIGHT_ON, - AMETA_SHIFT_ON, currentMetaState); - addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time, - AKEYCODE_ALT_LEFT, AMETA_ALT_LEFT_ON, - AKEYCODE_ALT_RIGHT, AMETA_ALT_RIGHT_ON, - AMETA_ALT_ON, currentMetaState); - addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time, - AKEYCODE_CTRL_LEFT, AMETA_CTRL_LEFT_ON, - AKEYCODE_CTRL_RIGHT, AMETA_CTRL_RIGHT_ON, - AMETA_CTRL_ON, currentMetaState); - addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time, - AKEYCODE_META_LEFT, AMETA_META_LEFT_ON, - AKEYCODE_META_RIGHT, AMETA_META_RIGHT_ON, - AMETA_META_ON, currentMetaState); - - addSingleEphemeralMetaKey(outEvents, deviceId, metaState, true, time, - AKEYCODE_SYM, AMETA_SYM_ON, currentMetaState); - addSingleEphemeralMetaKey(outEvents, deviceId, metaState, true, time, - AKEYCODE_FUNCTION, AMETA_FUNCTION_ON, currentMetaState); - } else { - addSingleEphemeralMetaKey(outEvents, deviceId, metaState, false, time, - AKEYCODE_FUNCTION, AMETA_FUNCTION_ON, currentMetaState); - addSingleEphemeralMetaKey(outEvents, deviceId, metaState, false, time, - AKEYCODE_SYM, AMETA_SYM_ON, currentMetaState); - - addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time, - AKEYCODE_META_LEFT, AMETA_META_LEFT_ON, - AKEYCODE_META_RIGHT, AMETA_META_RIGHT_ON, - AMETA_META_ON, currentMetaState); - addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time, - AKEYCODE_CTRL_LEFT, AMETA_CTRL_LEFT_ON, - AKEYCODE_CTRL_RIGHT, AMETA_CTRL_RIGHT_ON, - AMETA_CTRL_ON, currentMetaState); - addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time, - AKEYCODE_ALT_LEFT, AMETA_ALT_LEFT_ON, - AKEYCODE_ALT_RIGHT, AMETA_ALT_RIGHT_ON, - AMETA_ALT_ON, currentMetaState); - addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time, - AKEYCODE_SHIFT_LEFT, AMETA_SHIFT_LEFT_ON, - AKEYCODE_SHIFT_RIGHT, AMETA_SHIFT_RIGHT_ON, - AMETA_SHIFT_ON, currentMetaState); - - addLockedMetaKey(outEvents, deviceId, metaState, time, - AKEYCODE_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, currentMetaState); - addLockedMetaKey(outEvents, deviceId, metaState, time, - AKEYCODE_NUM_LOCK, AMETA_NUM_LOCK_ON, currentMetaState); - addLockedMetaKey(outEvents, deviceId, metaState, time, - AKEYCODE_CAPS_LOCK, AMETA_CAPS_LOCK_ON, currentMetaState); - } -} - -bool KeyCharacterMap::addSingleEphemeralMetaKey(Vector<KeyEvent>& outEvents, - int32_t deviceId, int32_t metaState, bool down, nsecs_t time, - int32_t keyCode, int32_t keyMetaState, - int32_t* currentMetaState) { - if ((metaState & keyMetaState) == keyMetaState) { - *currentMetaState = updateMetaState(keyCode, down, *currentMetaState); - addKey(outEvents, deviceId, keyCode, *currentMetaState, down, time); - return true; - } - return false; -} - -void KeyCharacterMap::addDoubleEphemeralMetaKey(Vector<KeyEvent>& outEvents, - int32_t deviceId, int32_t metaState, bool down, nsecs_t time, - int32_t leftKeyCode, int32_t leftKeyMetaState, - int32_t rightKeyCode, int32_t rightKeyMetaState, - int32_t eitherKeyMetaState, - int32_t* currentMetaState) { - bool specific = false; - specific |= addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time, - leftKeyCode, leftKeyMetaState, currentMetaState); - specific |= addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time, - rightKeyCode, rightKeyMetaState, currentMetaState); - - if (!specific) { - addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time, - leftKeyCode, eitherKeyMetaState, currentMetaState); - } -} - -void KeyCharacterMap::addLockedMetaKey(Vector<KeyEvent>& outEvents, - int32_t deviceId, int32_t metaState, nsecs_t time, - int32_t keyCode, int32_t keyMetaState, - int32_t* currentMetaState) { - if ((metaState & keyMetaState) == keyMetaState) { - *currentMetaState = updateMetaState(keyCode, true, *currentMetaState); - addKey(outEvents, deviceId, keyCode, *currentMetaState, true, time); - *currentMetaState = updateMetaState(keyCode, false, *currentMetaState); - addKey(outEvents, deviceId, keyCode, *currentMetaState, false, time); - } -} - -#if HAVE_ANDROID_OS -sp<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) { - sp<KeyCharacterMap> map = new KeyCharacterMap(); - map->mType = parcel->readInt32(); - size_t numKeys = parcel->readInt32(); - if (parcel->errorCheck()) { - return NULL; - } - - for (size_t i = 0; i < numKeys; i++) { - int32_t keyCode = parcel->readInt32(); - char16_t label = parcel->readInt32(); - char16_t number = parcel->readInt32(); - if (parcel->errorCheck()) { - return NULL; - } - - Key* key = new Key(); - key->label = label; - key->number = number; - map->mKeys.add(keyCode, key); - - Behavior* lastBehavior = NULL; - while (parcel->readInt32()) { - int32_t metaState = parcel->readInt32(); - char16_t character = parcel->readInt32(); - int32_t fallbackKeyCode = parcel->readInt32(); - if (parcel->errorCheck()) { - return NULL; - } - - Behavior* behavior = new Behavior(); - behavior->metaState = metaState; - behavior->character = character; - behavior->fallbackKeyCode = fallbackKeyCode; - if (lastBehavior) { - lastBehavior->next = behavior; - } else { - key->firstBehavior = behavior; - } - lastBehavior = behavior; - } - - if (parcel->errorCheck()) { - return NULL; - } - } - return map; -} - -void KeyCharacterMap::writeToParcel(Parcel* parcel) const { - parcel->writeInt32(mType); - - size_t numKeys = mKeys.size(); - parcel->writeInt32(numKeys); - for (size_t i = 0; i < numKeys; i++) { - int32_t keyCode = mKeys.keyAt(i); - const Key* key = mKeys.valueAt(i); - parcel->writeInt32(keyCode); - parcel->writeInt32(key->label); - parcel->writeInt32(key->number); - for (const Behavior* behavior = key->firstBehavior; behavior != NULL; - behavior = behavior->next) { - parcel->writeInt32(1); - parcel->writeInt32(behavior->metaState); - parcel->writeInt32(behavior->character); - parcel->writeInt32(behavior->fallbackKeyCode); - } - parcel->writeInt32(0); - } -} -#endif - - -// --- KeyCharacterMap::Key --- - -KeyCharacterMap::Key::Key() : - label(0), number(0), firstBehavior(NULL) { -} - -KeyCharacterMap::Key::Key(const Key& other) : - label(other.label), number(other.number), - firstBehavior(other.firstBehavior ? new Behavior(*other.firstBehavior) : NULL) { -} - -KeyCharacterMap::Key::~Key() { - Behavior* behavior = firstBehavior; - while (behavior) { - Behavior* next = behavior->next; - delete behavior; - behavior = next; - } -} - - -// --- KeyCharacterMap::Behavior --- - -KeyCharacterMap::Behavior::Behavior() : - next(NULL), metaState(0), character(0), fallbackKeyCode(0) { -} - -KeyCharacterMap::Behavior::Behavior(const Behavior& other) : - next(other.next ? new Behavior(*other.next) : NULL), - metaState(other.metaState), character(other.character), - fallbackKeyCode(other.fallbackKeyCode) { -} - - -// --- KeyCharacterMap::Parser --- - -KeyCharacterMap::Parser::Parser(KeyCharacterMap* map, Tokenizer* tokenizer, Format format) : - mMap(map), mTokenizer(tokenizer), mFormat(format), mState(STATE_TOP) { -} - -KeyCharacterMap::Parser::~Parser() { -} - -status_t KeyCharacterMap::Parser::parse() { - while (!mTokenizer->isEof()) { -#if DEBUG_PARSER - ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(), - mTokenizer->peekRemainderOfLine().string()); -#endif - - mTokenizer->skipDelimiters(WHITESPACE); - - if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { - switch (mState) { - case STATE_TOP: { - String8 keywordToken = mTokenizer->nextToken(WHITESPACE); - if (keywordToken == "type") { - mTokenizer->skipDelimiters(WHITESPACE); - status_t status = parseType(); - if (status) return status; - } else if (keywordToken == "map") { - mTokenizer->skipDelimiters(WHITESPACE); - status_t status = parseMap(); - if (status) return status; - } else if (keywordToken == "key") { - mTokenizer->skipDelimiters(WHITESPACE); - status_t status = parseKey(); - if (status) return status; - } else { - ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(), - keywordToken.string()); - return BAD_VALUE; - } - break; - } - - case STATE_KEY: { - status_t status = parseKeyProperty(); - if (status) return status; - break; - } - } - - mTokenizer->skipDelimiters(WHITESPACE); - if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { - ALOGE("%s: Expected end of line or trailing comment, got '%s'.", - mTokenizer->getLocation().string(), - mTokenizer->peekRemainderOfLine().string()); - return BAD_VALUE; - } - } - - mTokenizer->nextLine(); - } - - if (mState != STATE_TOP) { - ALOGE("%s: Unterminated key description at end of file.", - mTokenizer->getLocation().string()); - return BAD_VALUE; - } - - if (mMap->mType == KEYBOARD_TYPE_UNKNOWN) { - ALOGE("%s: Keyboard layout missing required keyboard 'type' declaration.", - mTokenizer->getLocation().string()); - return BAD_VALUE; - } - - if (mFormat == FORMAT_BASE) { - if (mMap->mType == KEYBOARD_TYPE_OVERLAY) { - ALOGE("%s: Base keyboard layout must specify a keyboard 'type' other than 'OVERLAY'.", - mTokenizer->getLocation().string()); - return BAD_VALUE; - } - } else if (mFormat == FORMAT_OVERLAY) { - if (mMap->mType != KEYBOARD_TYPE_OVERLAY) { - ALOGE("%s: Overlay keyboard layout missing required keyboard " - "'type OVERLAY' declaration.", - mTokenizer->getLocation().string()); - return BAD_VALUE; - } - } - - return NO_ERROR; -} - -status_t KeyCharacterMap::Parser::parseType() { - if (mMap->mType != KEYBOARD_TYPE_UNKNOWN) { - ALOGE("%s: Duplicate keyboard 'type' declaration.", - mTokenizer->getLocation().string()); - return BAD_VALUE; - } - - KeyboardType type; - String8 typeToken = mTokenizer->nextToken(WHITESPACE); - if (typeToken == "NUMERIC") { - type = KEYBOARD_TYPE_NUMERIC; - } else if (typeToken == "PREDICTIVE") { - type = KEYBOARD_TYPE_PREDICTIVE; - } else if (typeToken == "ALPHA") { - type = KEYBOARD_TYPE_ALPHA; - } else if (typeToken == "FULL") { - type = KEYBOARD_TYPE_FULL; - } else if (typeToken == "SPECIAL_FUNCTION") { - type = KEYBOARD_TYPE_SPECIAL_FUNCTION; - } else if (typeToken == "OVERLAY") { - type = KEYBOARD_TYPE_OVERLAY; - } else { - ALOGE("%s: Expected keyboard type label, got '%s'.", mTokenizer->getLocation().string(), - typeToken.string()); - return BAD_VALUE; - } - -#if DEBUG_PARSER - ALOGD("Parsed type: type=%d.", type); -#endif - mMap->mType = type; - return NO_ERROR; -} - -status_t KeyCharacterMap::Parser::parseMap() { - String8 keywordToken = mTokenizer->nextToken(WHITESPACE); - if (keywordToken == "key") { - mTokenizer->skipDelimiters(WHITESPACE); - return parseMapKey(); - } - ALOGE("%s: Expected keyword after 'map', got '%s'.", mTokenizer->getLocation().string(), - keywordToken.string()); - return BAD_VALUE; -} - -status_t KeyCharacterMap::Parser::parseMapKey() { - String8 codeToken = mTokenizer->nextToken(WHITESPACE); - bool mapUsage = false; - if (codeToken == "usage") { - mapUsage = true; - mTokenizer->skipDelimiters(WHITESPACE); - codeToken = mTokenizer->nextToken(WHITESPACE); - } - - char* end; - int32_t code = int32_t(strtol(codeToken.string(), &end, 0)); - if (*end) { - ALOGE("%s: Expected key %s number, got '%s'.", mTokenizer->getLocation().string(), - mapUsage ? "usage" : "scan code", codeToken.string()); - return BAD_VALUE; - } - KeyedVector<int32_t, int32_t>& map = - mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode; - if (map.indexOfKey(code) >= 0) { - ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().string(), - mapUsage ? "usage" : "scan code", codeToken.string()); - return BAD_VALUE; - } - - mTokenizer->skipDelimiters(WHITESPACE); - String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE); - int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string()); - if (!keyCode) { - ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(), - keyCodeToken.string()); - return BAD_VALUE; - } - -#if DEBUG_PARSER - ALOGD("Parsed map key %s: code=%d, keyCode=%d.", - mapUsage ? "usage" : "scan code", code, keyCode); -#endif - map.add(code, keyCode); - return NO_ERROR; -} - -status_t KeyCharacterMap::Parser::parseKey() { - String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE); - int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string()); - if (!keyCode) { - ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(), - keyCodeToken.string()); - return BAD_VALUE; - } - if (mMap->mKeys.indexOfKey(keyCode) >= 0) { - ALOGE("%s: Duplicate entry for key code '%s'.", mTokenizer->getLocation().string(), - keyCodeToken.string()); - return BAD_VALUE; - } - - mTokenizer->skipDelimiters(WHITESPACE); - String8 openBraceToken = mTokenizer->nextToken(WHITESPACE); - if (openBraceToken != "{") { - ALOGE("%s: Expected '{' after key code label, got '%s'.", - mTokenizer->getLocation().string(), openBraceToken.string()); - return BAD_VALUE; - } - -#if DEBUG_PARSER - ALOGD("Parsed beginning of key: keyCode=%d.", keyCode); -#endif - mKeyCode = keyCode; - mMap->mKeys.add(keyCode, new Key()); - mState = STATE_KEY; - return NO_ERROR; -} - -status_t KeyCharacterMap::Parser::parseKeyProperty() { - Key* key = mMap->mKeys.valueFor(mKeyCode); - String8 token = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER); - if (token == "}") { - mState = STATE_TOP; - return finishKey(key); - } - - Vector<Property> properties; - - // Parse all comma-delimited property names up to the first colon. - for (;;) { - if (token == "label") { - properties.add(Property(PROPERTY_LABEL)); - } else if (token == "number") { - properties.add(Property(PROPERTY_NUMBER)); - } else { - int32_t metaState; - status_t status = parseModifier(token, &metaState); - if (status) { - ALOGE("%s: Expected a property name or modifier, got '%s'.", - mTokenizer->getLocation().string(), token.string()); - return status; - } - properties.add(Property(PROPERTY_META, metaState)); - } - - mTokenizer->skipDelimiters(WHITESPACE); - if (!mTokenizer->isEol()) { - char ch = mTokenizer->nextChar(); - if (ch == ':') { - break; - } else if (ch == ',') { - mTokenizer->skipDelimiters(WHITESPACE); - token = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER); - continue; - } - } - - ALOGE("%s: Expected ',' or ':' after property name.", - mTokenizer->getLocation().string()); - return BAD_VALUE; - } - - // Parse behavior after the colon. - mTokenizer->skipDelimiters(WHITESPACE); - - Behavior behavior; - bool haveCharacter = false; - bool haveFallback = false; - - do { - char ch = mTokenizer->peekChar(); - if (ch == '\'') { - char16_t character; - status_t status = parseCharacterLiteral(&character); - if (status || !character) { - ALOGE("%s: Invalid character literal for key.", - mTokenizer->getLocation().string()); - return BAD_VALUE; - } - if (haveCharacter) { - ALOGE("%s: Cannot combine multiple character literals or 'none'.", - mTokenizer->getLocation().string()); - return BAD_VALUE; - } - behavior.character = character; - haveCharacter = true; - } else { - token = mTokenizer->nextToken(WHITESPACE); - if (token == "none") { - if (haveCharacter) { - ALOGE("%s: Cannot combine multiple character literals or 'none'.", - mTokenizer->getLocation().string()); - return BAD_VALUE; - } - haveCharacter = true; - } else if (token == "fallback") { - mTokenizer->skipDelimiters(WHITESPACE); - token = mTokenizer->nextToken(WHITESPACE); - int32_t keyCode = getKeyCodeByLabel(token.string()); - if (!keyCode) { - ALOGE("%s: Invalid key code label for fallback behavior, got '%s'.", - mTokenizer->getLocation().string(), - token.string()); - return BAD_VALUE; - } - if (haveFallback) { - ALOGE("%s: Cannot combine multiple fallback key codes.", - mTokenizer->getLocation().string()); - return BAD_VALUE; - } - behavior.fallbackKeyCode = keyCode; - haveFallback = true; - } else { - ALOGE("%s: Expected a key behavior after ':'.", - mTokenizer->getLocation().string()); - return BAD_VALUE; - } - } - - mTokenizer->skipDelimiters(WHITESPACE); - } while (!mTokenizer->isEol() && mTokenizer->peekChar() != '#'); - - // Add the behavior. - for (size_t i = 0; i < properties.size(); i++) { - const Property& property = properties.itemAt(i); - switch (property.property) { - case PROPERTY_LABEL: - if (key->label) { - ALOGE("%s: Duplicate label for key.", - mTokenizer->getLocation().string()); - return BAD_VALUE; - } - key->label = behavior.character; -#if DEBUG_PARSER - ALOGD("Parsed key label: keyCode=%d, label=%d.", mKeyCode, key->label); -#endif - break; - case PROPERTY_NUMBER: - if (key->number) { - ALOGE("%s: Duplicate number for key.", - mTokenizer->getLocation().string()); - return BAD_VALUE; - } - key->number = behavior.character; -#if DEBUG_PARSER - ALOGD("Parsed key number: keyCode=%d, number=%d.", mKeyCode, key->number); -#endif - break; - case PROPERTY_META: { - for (Behavior* b = key->firstBehavior; b; b = b->next) { - if (b->metaState == property.metaState) { - ALOGE("%s: Duplicate key behavior for modifier.", - mTokenizer->getLocation().string()); - return BAD_VALUE; - } - } - Behavior* newBehavior = new Behavior(behavior); - newBehavior->metaState = property.metaState; - newBehavior->next = key->firstBehavior; - key->firstBehavior = newBehavior; -#if DEBUG_PARSER - ALOGD("Parsed key meta: keyCode=%d, meta=0x%x, char=%d, fallback=%d.", mKeyCode, - newBehavior->metaState, newBehavior->character, newBehavior->fallbackKeyCode); -#endif - break; - } - } - } - return NO_ERROR; -} - -status_t KeyCharacterMap::Parser::finishKey(Key* key) { - // Fill in default number property. - if (!key->number) { - char16_t digit = 0; - char16_t symbol = 0; - for (Behavior* b = key->firstBehavior; b; b = b->next) { - char16_t ch = b->character; - if (ch) { - if (ch >= '0' && ch <= '9') { - digit = ch; - } else if (ch == '(' || ch == ')' || ch == '#' || ch == '*' - || ch == '-' || ch == '+' || ch == ',' || ch == '.' - || ch == '\'' || ch == ':' || ch == ';' || ch == '/') { - symbol = ch; - } - } - } - key->number = digit ? digit : symbol; - } - return NO_ERROR; -} - -status_t KeyCharacterMap::Parser::parseModifier(const String8& token, int32_t* outMetaState) { - if (token == "base") { - *outMetaState = 0; - return NO_ERROR; - } - - int32_t combinedMeta = 0; - - const char* str = token.string(); - const char* start = str; - for (const char* cur = str; ; cur++) { - char ch = *cur; - if (ch == '+' || ch == '\0') { - size_t len = cur - start; - int32_t metaState = 0; - for (size_t i = 0; i < sizeof(modifiers) / sizeof(Modifier); i++) { - if (strlen(modifiers[i].label) == len - && strncmp(modifiers[i].label, start, len) == 0) { - metaState = modifiers[i].metaState; - break; - } - } - if (!metaState) { - return BAD_VALUE; - } - if (combinedMeta & metaState) { - ALOGE("%s: Duplicate modifier combination '%s'.", - mTokenizer->getLocation().string(), token.string()); - return BAD_VALUE; - } - - combinedMeta |= metaState; - start = cur + 1; - - if (ch == '\0') { - break; - } - } - } - *outMetaState = combinedMeta; - return NO_ERROR; -} - -status_t KeyCharacterMap::Parser::parseCharacterLiteral(char16_t* outCharacter) { - char ch = mTokenizer->nextChar(); - if (ch != '\'') { - goto Error; - } - - ch = mTokenizer->nextChar(); - if (ch == '\\') { - // Escape sequence. - ch = mTokenizer->nextChar(); - if (ch == 'n') { - *outCharacter = '\n'; - } else if (ch == 't') { - *outCharacter = '\t'; - } else if (ch == '\\') { - *outCharacter = '\\'; - } else if (ch == '\'') { - *outCharacter = '\''; - } else if (ch == '"') { - *outCharacter = '"'; - } else if (ch == 'u') { - *outCharacter = 0; - for (int i = 0; i < 4; i++) { - ch = mTokenizer->nextChar(); - int digit; - if (ch >= '0' && ch <= '9') { - digit = ch - '0'; - } else if (ch >= 'A' && ch <= 'F') { - digit = ch - 'A' + 10; - } else if (ch >= 'a' && ch <= 'f') { - digit = ch - 'a' + 10; - } else { - goto Error; - } - *outCharacter = (*outCharacter << 4) | digit; - } - } else { - goto Error; - } - } else if (ch >= 32 && ch <= 126 && ch != '\'') { - // ASCII literal character. - *outCharacter = ch; - } else { - goto Error; - } - - ch = mTokenizer->nextChar(); - if (ch != '\'') { - goto Error; - } - - // Ensure that we consumed the entire token. - if (mTokenizer->nextToken(WHITESPACE).isEmpty()) { - return NO_ERROR; - } - -Error: - ALOGE("%s: Malformed character literal.", mTokenizer->getLocation().string()); - return BAD_VALUE; -} - -} // namespace android diff --git a/libs/androidfw/KeyLayoutMap.cpp b/libs/androidfw/KeyLayoutMap.cpp deleted file mode 100644 index ae14f23..0000000 --- a/libs/androidfw/KeyLayoutMap.cpp +++ /dev/null @@ -1,366 +0,0 @@ -/* - * 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 "KeyLayoutMap" - -#include <stdlib.h> -#include <android/keycodes.h> -#include <androidfw/Keyboard.h> -#include <androidfw/KeyLayoutMap.h> -#include <utils/Log.h> -#include <utils/Errors.h> -#include <utils/Tokenizer.h> -#include <utils/Timers.h> - -// Enables debug output for the parser. -#define DEBUG_PARSER 0 - -// Enables debug output for parser performance. -#define DEBUG_PARSER_PERFORMANCE 0 - -// Enables debug output for mapping. -#define DEBUG_MAPPING 0 - - -namespace android { - -static const char* WHITESPACE = " \t\r"; - -// --- KeyLayoutMap --- - -KeyLayoutMap::KeyLayoutMap() { -} - -KeyLayoutMap::~KeyLayoutMap() { -} - -status_t KeyLayoutMap::load(const String8& filename, sp<KeyLayoutMap>* outMap) { - outMap->clear(); - - Tokenizer* tokenizer; - status_t status = Tokenizer::open(filename, &tokenizer); - if (status) { - ALOGE("Error %d opening key layout map file %s.", status, filename.string()); - } else { - sp<KeyLayoutMap> map = new KeyLayoutMap(); - if (!map.get()) { - ALOGE("Error allocating key layout map."); - status = NO_MEMORY; - } else { -#if DEBUG_PARSER_PERFORMANCE - nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); -#endif - Parser parser(map.get(), tokenizer); - status = parser.parse(); -#if DEBUG_PARSER_PERFORMANCE - nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; - ALOGD("Parsed key layout map file '%s' %d lines in %0.3fms.", - tokenizer->getFilename().string(), tokenizer->getLineNumber(), - elapsedTime / 1000000.0); -#endif - if (!status) { - *outMap = map; - } - } - delete tokenizer; - } - return status; -} - -status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t usageCode, - int32_t* outKeyCode, uint32_t* outFlags) const { - const Key* key = getKey(scanCode, usageCode); - if (!key) { -#if DEBUG_MAPPING - ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Failed.", scanCode, usageCode); -#endif - *outKeyCode = AKEYCODE_UNKNOWN; - *outFlags = 0; - return NAME_NOT_FOUND; - } - - *outKeyCode = key->keyCode; - *outFlags = key->flags; - -#if DEBUG_MAPPING - ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d, outFlags=0x%08x.", - scanCode, usageCode, *outKeyCode, *outFlags); -#endif - return NO_ERROR; -} - -const KeyLayoutMap::Key* KeyLayoutMap::getKey(int32_t scanCode, int32_t usageCode) const { - if (usageCode) { - ssize_t index = mKeysByUsageCode.indexOfKey(usageCode); - if (index >= 0) { - return &mKeysByUsageCode.valueAt(index); - } - } - if (scanCode) { - ssize_t index = mKeysByScanCode.indexOfKey(scanCode); - if (index >= 0) { - return &mKeysByScanCode.valueAt(index); - } - } - return NULL; -} - -status_t KeyLayoutMap::findScanCodesForKey(int32_t keyCode, Vector<int32_t>* outScanCodes) const { - const size_t N = mKeysByScanCode.size(); - for (size_t i=0; i<N; i++) { - if (mKeysByScanCode.valueAt(i).keyCode == keyCode) { - outScanCodes->add(mKeysByScanCode.keyAt(i)); - } - } - return NO_ERROR; -} - -status_t KeyLayoutMap::mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const { - ssize_t index = mAxes.indexOfKey(scanCode); - if (index < 0) { -#if DEBUG_MAPPING - ALOGD("mapAxis: scanCode=%d ~ Failed.", scanCode); -#endif - return NAME_NOT_FOUND; - } - - *outAxisInfo = mAxes.valueAt(index); - -#if DEBUG_MAPPING - ALOGD("mapAxis: scanCode=%d ~ Result mode=%d, axis=%d, highAxis=%d, " - "splitValue=%d, flatOverride=%d.", - scanCode, - outAxisInfo->mode, outAxisInfo->axis, outAxisInfo->highAxis, - outAxisInfo->splitValue, outAxisInfo->flatOverride); -#endif - return NO_ERROR; -} - - -// --- KeyLayoutMap::Parser --- - -KeyLayoutMap::Parser::Parser(KeyLayoutMap* map, Tokenizer* tokenizer) : - mMap(map), mTokenizer(tokenizer) { -} - -KeyLayoutMap::Parser::~Parser() { -} - -status_t KeyLayoutMap::Parser::parse() { - while (!mTokenizer->isEof()) { -#if DEBUG_PARSER - ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(), - mTokenizer->peekRemainderOfLine().string()); -#endif - - mTokenizer->skipDelimiters(WHITESPACE); - - if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { - String8 keywordToken = mTokenizer->nextToken(WHITESPACE); - if (keywordToken == "key") { - mTokenizer->skipDelimiters(WHITESPACE); - status_t status = parseKey(); - if (status) return status; - } else if (keywordToken == "axis") { - mTokenizer->skipDelimiters(WHITESPACE); - status_t status = parseAxis(); - if (status) return status; - } else { - ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(), - keywordToken.string()); - return BAD_VALUE; - } - - mTokenizer->skipDelimiters(WHITESPACE); - if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { - ALOGE("%s: Expected end of line or trailing comment, got '%s'.", - mTokenizer->getLocation().string(), - mTokenizer->peekRemainderOfLine().string()); - return BAD_VALUE; - } - } - - mTokenizer->nextLine(); - } - return NO_ERROR; -} - -status_t KeyLayoutMap::Parser::parseKey() { - String8 codeToken = mTokenizer->nextToken(WHITESPACE); - bool mapUsage = false; - if (codeToken == "usage") { - mapUsage = true; - mTokenizer->skipDelimiters(WHITESPACE); - codeToken = mTokenizer->nextToken(WHITESPACE); - } - - char* end; - int32_t code = int32_t(strtol(codeToken.string(), &end, 0)); - if (*end) { - ALOGE("%s: Expected key %s number, got '%s'.", mTokenizer->getLocation().string(), - mapUsage ? "usage" : "scan code", codeToken.string()); - return BAD_VALUE; - } - KeyedVector<int32_t, Key>& map = - mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode; - if (map.indexOfKey(code) >= 0) { - ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().string(), - mapUsage ? "usage" : "scan code", codeToken.string()); - return BAD_VALUE; - } - - mTokenizer->skipDelimiters(WHITESPACE); - String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE); - int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string()); - if (!keyCode) { - ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(), - keyCodeToken.string()); - return BAD_VALUE; - } - - uint32_t flags = 0; - for (;;) { - mTokenizer->skipDelimiters(WHITESPACE); - if (mTokenizer->isEol() || mTokenizer->peekChar() == '#') break; - - String8 flagToken = mTokenizer->nextToken(WHITESPACE); - uint32_t flag = getKeyFlagByLabel(flagToken.string()); - if (!flag) { - ALOGE("%s: Expected key flag label, got '%s'.", mTokenizer->getLocation().string(), - flagToken.string()); - return BAD_VALUE; - } - if (flags & flag) { - ALOGE("%s: Duplicate key flag '%s'.", mTokenizer->getLocation().string(), - flagToken.string()); - return BAD_VALUE; - } - flags |= flag; - } - -#if DEBUG_PARSER - ALOGD("Parsed key %s: code=%d, keyCode=%d, flags=0x%08x.", - mapUsage ? "usage" : "scan code", code, keyCode, flags); -#endif - Key key; - key.keyCode = keyCode; - key.flags = flags; - map.add(code, key); - return NO_ERROR; -} - -status_t KeyLayoutMap::Parser::parseAxis() { - String8 scanCodeToken = mTokenizer->nextToken(WHITESPACE); - char* end; - int32_t scanCode = int32_t(strtol(scanCodeToken.string(), &end, 0)); - if (*end) { - ALOGE("%s: Expected axis scan code number, got '%s'.", mTokenizer->getLocation().string(), - scanCodeToken.string()); - return BAD_VALUE; - } - if (mMap->mAxes.indexOfKey(scanCode) >= 0) { - ALOGE("%s: Duplicate entry for axis scan code '%s'.", mTokenizer->getLocation().string(), - scanCodeToken.string()); - return BAD_VALUE; - } - - AxisInfo axisInfo; - - mTokenizer->skipDelimiters(WHITESPACE); - String8 token = mTokenizer->nextToken(WHITESPACE); - if (token == "invert") { - axisInfo.mode = AxisInfo::MODE_INVERT; - - mTokenizer->skipDelimiters(WHITESPACE); - String8 axisToken = mTokenizer->nextToken(WHITESPACE); - axisInfo.axis = getAxisByLabel(axisToken.string()); - if (axisInfo.axis < 0) { - ALOGE("%s: Expected inverted axis label, got '%s'.", - mTokenizer->getLocation().string(), axisToken.string()); - return BAD_VALUE; - } - } else if (token == "split") { - axisInfo.mode = AxisInfo::MODE_SPLIT; - - mTokenizer->skipDelimiters(WHITESPACE); - String8 splitToken = mTokenizer->nextToken(WHITESPACE); - axisInfo.splitValue = int32_t(strtol(splitToken.string(), &end, 0)); - if (*end) { - ALOGE("%s: Expected split value, got '%s'.", - mTokenizer->getLocation().string(), splitToken.string()); - return BAD_VALUE; - } - - mTokenizer->skipDelimiters(WHITESPACE); - String8 lowAxisToken = mTokenizer->nextToken(WHITESPACE); - axisInfo.axis = getAxisByLabel(lowAxisToken.string()); - if (axisInfo.axis < 0) { - ALOGE("%s: Expected low axis label, got '%s'.", - mTokenizer->getLocation().string(), lowAxisToken.string()); - return BAD_VALUE; - } - - mTokenizer->skipDelimiters(WHITESPACE); - String8 highAxisToken = mTokenizer->nextToken(WHITESPACE); - axisInfo.highAxis = getAxisByLabel(highAxisToken.string()); - if (axisInfo.highAxis < 0) { - ALOGE("%s: Expected high axis label, got '%s'.", - mTokenizer->getLocation().string(), highAxisToken.string()); - return BAD_VALUE; - } - } else { - axisInfo.axis = getAxisByLabel(token.string()); - if (axisInfo.axis < 0) { - ALOGE("%s: Expected axis label, 'split' or 'invert', got '%s'.", - mTokenizer->getLocation().string(), token.string()); - return BAD_VALUE; - } - } - - for (;;) { - mTokenizer->skipDelimiters(WHITESPACE); - if (mTokenizer->isEol() || mTokenizer->peekChar() == '#') { - break; - } - String8 keywordToken = mTokenizer->nextToken(WHITESPACE); - if (keywordToken == "flat") { - mTokenizer->skipDelimiters(WHITESPACE); - String8 flatToken = mTokenizer->nextToken(WHITESPACE); - axisInfo.flatOverride = int32_t(strtol(flatToken.string(), &end, 0)); - if (*end) { - ALOGE("%s: Expected flat value, got '%s'.", - mTokenizer->getLocation().string(), flatToken.string()); - return BAD_VALUE; - } - } else { - ALOGE("%s: Expected keyword 'flat', got '%s'.", - mTokenizer->getLocation().string(), keywordToken.string()); - return BAD_VALUE; - } - } - -#if DEBUG_PARSER - ALOGD("Parsed axis: scanCode=%d, mode=%d, axis=%d, highAxis=%d, " - "splitValue=%d, flatOverride=%d.", - scanCode, - axisInfo.mode, axisInfo.axis, axisInfo.highAxis, - axisInfo.splitValue, axisInfo.flatOverride); -#endif - mMap->mAxes.add(scanCode, axisInfo); - return NO_ERROR; -} - -}; diff --git a/libs/androidfw/Keyboard.cpp b/libs/androidfw/Keyboard.cpp deleted file mode 100644 index 4ddbeab..0000000 --- a/libs/androidfw/Keyboard.cpp +++ /dev/null @@ -1,298 +0,0 @@ -/* - * 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 "Keyboard" - -#include <stdlib.h> -#include <unistd.h> -#include <limits.h> - -#include <androidfw/Keyboard.h> -#include <androidfw/KeycodeLabels.h> -#include <androidfw/KeyLayoutMap.h> -#include <androidfw/KeyCharacterMap.h> -#include <androidfw/InputDevice.h> -#include <utils/Errors.h> -#include <utils/Log.h> -#include <cutils/properties.h> - -namespace android { - -// --- KeyMap --- - -KeyMap::KeyMap() { -} - -KeyMap::~KeyMap() { -} - -status_t KeyMap::load(const InputDeviceIdentifier& deviceIdenfifier, - const PropertyMap* deviceConfiguration) { - // Use the configured key layout if available. - if (deviceConfiguration) { - String8 keyLayoutName; - if (deviceConfiguration->tryGetProperty(String8("keyboard.layout"), - keyLayoutName)) { - status_t status = loadKeyLayout(deviceIdenfifier, keyLayoutName); - if (status == NAME_NOT_FOUND) { - ALOGE("Configuration for keyboard device '%s' requested keyboard layout '%s' but " - "it was not found.", - deviceIdenfifier.name.string(), keyLayoutName.string()); - } - } - - String8 keyCharacterMapName; - if (deviceConfiguration->tryGetProperty(String8("keyboard.characterMap"), - keyCharacterMapName)) { - status_t status = loadKeyCharacterMap(deviceIdenfifier, keyCharacterMapName); - if (status == NAME_NOT_FOUND) { - ALOGE("Configuration for keyboard device '%s' requested keyboard character " - "map '%s' but it was not found.", - deviceIdenfifier.name.string(), keyLayoutName.string()); - } - } - - if (isComplete()) { - return OK; - } - } - - // Try searching by device identifier. - if (probeKeyMap(deviceIdenfifier, String8::empty())) { - return OK; - } - - // Fall back on the Generic key map. - // TODO Apply some additional heuristics here to figure out what kind of - // generic key map to use (US English, etc.) for typical external keyboards. - if (probeKeyMap(deviceIdenfifier, String8("Generic"))) { - return OK; - } - - // Try the Virtual key map as a last resort. - if (probeKeyMap(deviceIdenfifier, String8("Virtual"))) { - return OK; - } - - // Give up! - ALOGE("Could not determine key map for device '%s' and no default key maps were found!", - deviceIdenfifier.name.string()); - return NAME_NOT_FOUND; -} - -bool KeyMap::probeKeyMap(const InputDeviceIdentifier& deviceIdentifier, - const String8& keyMapName) { - if (!haveKeyLayout()) { - loadKeyLayout(deviceIdentifier, keyMapName); - } - if (!haveKeyCharacterMap()) { - loadKeyCharacterMap(deviceIdentifier, keyMapName); - } - return isComplete(); -} - -status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, - const String8& name) { - String8 path(getPath(deviceIdentifier, name, - INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT)); - if (path.isEmpty()) { - return NAME_NOT_FOUND; - } - - status_t status = KeyLayoutMap::load(path, &keyLayoutMap); - if (status) { - return status; - } - - keyLayoutFile.setTo(path); - return OK; -} - -status_t KeyMap::loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier, - const String8& name) { - String8 path(getPath(deviceIdentifier, name, - INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP)); - if (path.isEmpty()) { - return NAME_NOT_FOUND; - } - - status_t status = KeyCharacterMap::load(path, - KeyCharacterMap::FORMAT_BASE, &keyCharacterMap); - if (status) { - return status; - } - - keyCharacterMapFile.setTo(path); - return OK; -} - -String8 KeyMap::getPath(const InputDeviceIdentifier& deviceIdentifier, - const String8& name, InputDeviceConfigurationFileType type) { - return name.isEmpty() - ? getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier, type) - : getInputDeviceConfigurationFilePathByName(name, type); -} - - -// --- Global functions --- - -bool isEligibleBuiltInKeyboard(const InputDeviceIdentifier& deviceIdentifier, - const PropertyMap* deviceConfiguration, const KeyMap* keyMap) { - if (!keyMap->haveKeyCharacterMap() - || keyMap->keyCharacterMap->getKeyboardType() - == KeyCharacterMap::KEYBOARD_TYPE_SPECIAL_FUNCTION) { - return false; - } - - if (deviceConfiguration) { - bool builtIn = false; - if (deviceConfiguration->tryGetProperty(String8("keyboard.builtIn"), builtIn) - && builtIn) { - return true; - } - } - - return strstr(deviceIdentifier.name.string(), "-keypad"); -} - -static int lookupValueByLabel(const char* literal, const KeycodeLabel *list) { - while (list->literal) { - if (strcmp(literal, list->literal) == 0) { - return list->value; - } - list++; - } - return list->value; -} - -static const char* lookupLabelByValue(int value, const KeycodeLabel *list) { - while (list->literal) { - if (list->value == value) { - return list->literal; - } - list++; - } - return NULL; -} - -int32_t getKeyCodeByLabel(const char* label) { - return int32_t(lookupValueByLabel(label, KEYCODES)); -} - -uint32_t getKeyFlagByLabel(const char* label) { - return uint32_t(lookupValueByLabel(label, FLAGS)); -} - -int32_t getAxisByLabel(const char* label) { - return int32_t(lookupValueByLabel(label, AXES)); -} - -const char* getAxisLabel(int32_t axisId) { - return lookupLabelByValue(axisId, AXES); -} - -static int32_t setEphemeralMetaState(int32_t mask, bool down, int32_t oldMetaState) { - int32_t newMetaState; - if (down) { - newMetaState = oldMetaState | mask; - } else { - newMetaState = oldMetaState & - ~(mask | AMETA_ALT_ON | AMETA_SHIFT_ON | AMETA_CTRL_ON | AMETA_META_ON); - } - - if (newMetaState & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) { - newMetaState |= AMETA_ALT_ON; - } - - if (newMetaState & (AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON)) { - newMetaState |= AMETA_SHIFT_ON; - } - - if (newMetaState & (AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) { - newMetaState |= AMETA_CTRL_ON; - } - - if (newMetaState & (AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON)) { - newMetaState |= AMETA_META_ON; - } - return newMetaState; -} - -static int32_t toggleLockedMetaState(int32_t mask, bool down, int32_t oldMetaState) { - if (down) { - return oldMetaState; - } else { - return oldMetaState ^ mask; - } -} - -int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) { - int32_t mask; - switch (keyCode) { - case AKEYCODE_ALT_LEFT: - return setEphemeralMetaState(AMETA_ALT_LEFT_ON, down, oldMetaState); - case AKEYCODE_ALT_RIGHT: - return setEphemeralMetaState(AMETA_ALT_RIGHT_ON, down, oldMetaState); - case AKEYCODE_SHIFT_LEFT: - return setEphemeralMetaState(AMETA_SHIFT_LEFT_ON, down, oldMetaState); - case AKEYCODE_SHIFT_RIGHT: - return setEphemeralMetaState(AMETA_SHIFT_RIGHT_ON, down, oldMetaState); - case AKEYCODE_SYM: - return setEphemeralMetaState(AMETA_SYM_ON, down, oldMetaState); - case AKEYCODE_FUNCTION: - return setEphemeralMetaState(AMETA_FUNCTION_ON, down, oldMetaState); - case AKEYCODE_CTRL_LEFT: - return setEphemeralMetaState(AMETA_CTRL_LEFT_ON, down, oldMetaState); - case AKEYCODE_CTRL_RIGHT: - return setEphemeralMetaState(AMETA_CTRL_RIGHT_ON, down, oldMetaState); - case AKEYCODE_META_LEFT: - return setEphemeralMetaState(AMETA_META_LEFT_ON, down, oldMetaState); - case AKEYCODE_META_RIGHT: - return setEphemeralMetaState(AMETA_META_RIGHT_ON, down, oldMetaState); - case AKEYCODE_CAPS_LOCK: - return toggleLockedMetaState(AMETA_CAPS_LOCK_ON, down, oldMetaState); - case AKEYCODE_NUM_LOCK: - return toggleLockedMetaState(AMETA_NUM_LOCK_ON, down, oldMetaState); - case AKEYCODE_SCROLL_LOCK: - return toggleLockedMetaState(AMETA_SCROLL_LOCK_ON, down, oldMetaState); - default: - return oldMetaState; - } -} - -bool isMetaKey(int32_t keyCode) { - switch (keyCode) { - case AKEYCODE_ALT_LEFT: - case AKEYCODE_ALT_RIGHT: - case AKEYCODE_SHIFT_LEFT: - case AKEYCODE_SHIFT_RIGHT: - case AKEYCODE_SYM: - case AKEYCODE_FUNCTION: - case AKEYCODE_CTRL_LEFT: - case AKEYCODE_CTRL_RIGHT: - case AKEYCODE_META_LEFT: - case AKEYCODE_META_RIGHT: - case AKEYCODE_CAPS_LOCK: - case AKEYCODE_NUM_LOCK: - case AKEYCODE_SCROLL_LOCK: - return true; - default: - return false; - } -} - - -} // namespace android diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index a730065..1cc3563 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -24,7 +24,6 @@ #include <utils/Log.h> #include <utils/String16.h> #include <utils/String8.h> -#include <utils/TextOutput.h> #include <stdlib.h> #include <string.h> @@ -36,7 +35,7 @@ #define INT32_MAX ((int32_t)(2147483647)) #endif -#define POOL_NOISY(x) //x +#define STRING_POOL_NOISY(x) //x #define XML_NOISY(x) //x #define TABLE_NOISY(x) //x #define TABLE_GETENTRY(x) //x @@ -379,7 +378,6 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) size_t charSize; if (mHeader->flags&ResStringPool_header::UTF8_FLAG) { charSize = sizeof(uint8_t); - mCache = (char16_t**)calloc(mHeader->stringCount, sizeof(char16_t**)); } else { charSize = sizeof(char16_t); } @@ -594,6 +592,23 @@ const uint16_t* ResStringPool::stringAt(size_t idx, size_t* u16len) const if ((uint32_t)(u8str+u8len-strings) < mStringPoolSize) { AutoMutex lock(mDecodeLock); + if (mCache == NULL) { +#ifndef HAVE_ANDROID_OS + STRING_POOL_NOISY(ALOGI("CREATING STRING CACHE OF %d bytes", + mHeader->stringCount*sizeof(char16_t**))); +#else + // We do not want to be in this case when actually running Android. + ALOGW("CREATING STRING CACHE OF %d bytes", + mHeader->stringCount*sizeof(char16_t**)); +#endif + mCache = (char16_t**)calloc(mHeader->stringCount, sizeof(char16_t**)); + if (mCache == NULL) { + ALOGW("No memory trying to allocate decode cache table of %d bytes\n", + (int)(mHeader->stringCount*sizeof(char16_t**))); + return NULL; + } + } + if (mCache[idx] != NULL) { return mCache[idx]; } @@ -613,6 +628,7 @@ const uint16_t* ResStringPool::stringAt(size_t idx, size_t* u16len) const return NULL; } + STRING_POOL_NOISY(ALOGI("Caching UTF8 string: %s", u8str)); utf8_to_utf16(u8str, u8len, u16str); mCache[idx] = u16str; return u16str; @@ -634,20 +650,20 @@ const uint16_t* ResStringPool::stringAt(size_t idx, size_t* u16len) const const char* ResStringPool::string8At(size_t idx, size_t* outLen) const { if (mError == NO_ERROR && idx < mHeader->stringCount) { - const bool isUTF8 = (mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0; - const uint32_t off = mEntries[idx]/(isUTF8?sizeof(char):sizeof(char16_t)); + if ((mHeader->flags&ResStringPool_header::UTF8_FLAG) == 0) { + return NULL; + } + const uint32_t off = mEntries[idx]/sizeof(char); if (off < (mStringPoolSize-1)) { - if (isUTF8) { - const uint8_t* strings = (uint8_t*)mStrings; - const uint8_t* str = strings+off; - *outLen = decodeLength(&str); - size_t encLen = decodeLength(&str); - if ((uint32_t)(str+encLen-strings) < mStringPoolSize) { - return (const char*)str; - } else { - ALOGW("Bad string block: string #%d extends to %d, past end at %d\n", - (int)idx, (int)(str+encLen-strings), (int)mStringPoolSize); - } + const uint8_t* strings = (uint8_t*)mStrings; + const uint8_t* str = strings+off; + *outLen = decodeLength(&str); + size_t encLen = decodeLength(&str); + if ((uint32_t)(str+encLen-strings) < mStringPoolSize) { + return (const char*)str; + } else { + ALOGW("Bad string block: string #%d extends to %d, past end at %d\n", + (int)idx, (int)(str+encLen-strings), (int)mStringPoolSize); } } else { ALOGW("Bad string block: string #%d entry is at %d, past end at %d\n", @@ -696,45 +712,104 @@ ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const size_t len; - // TODO optimize searching for UTF-8 strings taking into account - // the cache fill to determine when to convert the searched-for - // string key to UTF-8. - - if (mHeader->flags&ResStringPool_header::SORTED_FLAG) { - // Do a binary search for the string... - ssize_t l = 0; - ssize_t h = mHeader->stringCount-1; - - ssize_t mid; - while (l <= h) { - mid = l + (h - l)/2; - const char16_t* s = stringAt(mid, &len); - int c = s ? strzcmp16(s, len, str, strLen) : -1; - POOL_NOISY(printf("Looking for %s, at %s, cmp=%d, l/mid/h=%d/%d/%d\n", - String8(str).string(), - String8(s).string(), - c, (int)l, (int)mid, (int)h)); - if (c == 0) { - return mid; - } else if (c < 0) { - l = mid + 1; - } else { - h = mid - 1; + if ((mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0) { + STRING_POOL_NOISY(ALOGI("indexOfString UTF-8: %s", String8(str, strLen).string())); + + // The string pool contains UTF 8 strings; we don't want to cause + // temporary UTF-16 strings to be created as we search. + if (mHeader->flags&ResStringPool_header::SORTED_FLAG) { + // Do a binary search for the string... this is a little tricky, + // because the strings are sorted with strzcmp16(). So to match + // the ordering, we need to convert strings in the pool to UTF-16. + // But we don't want to hit the cache, so instead we will have a + // local temporary allocation for the conversions. + char16_t* convBuffer = (char16_t*)malloc(strLen+4); + ssize_t l = 0; + ssize_t h = mHeader->stringCount-1; + + ssize_t mid; + while (l <= h) { + mid = l + (h - l)/2; + const uint8_t* s = (const uint8_t*)string8At(mid, &len); + int c; + if (s != NULL) { + char16_t* end = utf8_to_utf16_n(s, len, convBuffer, strLen+3); + *end = 0; + c = strzcmp16(convBuffer, end-convBuffer, str, strLen); + } else { + c = -1; + } + STRING_POOL_NOISY(ALOGI("Looking at %s, cmp=%d, l/mid/h=%d/%d/%d\n", + (const char*)s, c, (int)l, (int)mid, (int)h)); + if (c == 0) { + STRING_POOL_NOISY(ALOGI("MATCH!")); + free(convBuffer); + return mid; + } else if (c < 0) { + l = mid + 1; + } else { + h = mid - 1; + } + } + free(convBuffer); + } else { + // It is unusual to get the ID from an unsorted string block... + // most often this happens because we want to get IDs for style + // span tags; since those always appear at the end of the string + // block, start searching at the back. + String8 str8(str, strLen); + const size_t str8Len = str8.size(); + for (int i=mHeader->stringCount-1; i>=0; i--) { + const char* s = string8At(i, &len); + STRING_POOL_NOISY(ALOGI("Looking at %s, i=%d\n", + String8(s).string(), + i)); + if (s && str8Len == len && memcmp(s, str8.string(), str8Len) == 0) { + STRING_POOL_NOISY(ALOGI("MATCH!")); + return i; + } } } + } else { - // It is unusual to get the ID from an unsorted string block... - // most often this happens because we want to get IDs for style - // span tags; since those always appear at the end of the string - // block, start searching at the back. - for (int i=mHeader->stringCount-1; i>=0; i--) { - const char16_t* s = stringAt(i, &len); - POOL_NOISY(printf("Looking for %s, at %s, i=%d\n", - String8(str, strLen).string(), - String8(s).string(), - i)); - if (s && strzcmp16(s, len, str, strLen) == 0) { - return i; + STRING_POOL_NOISY(ALOGI("indexOfString UTF-16: %s", String8(str, strLen).string())); + + if (mHeader->flags&ResStringPool_header::SORTED_FLAG) { + // Do a binary search for the string... + ssize_t l = 0; + ssize_t h = mHeader->stringCount-1; + + ssize_t mid; + while (l <= h) { + mid = l + (h - l)/2; + const char16_t* s = stringAt(mid, &len); + int c = s ? strzcmp16(s, len, str, strLen) : -1; + STRING_POOL_NOISY(ALOGI("Looking at %s, cmp=%d, l/mid/h=%d/%d/%d\n", + String8(s).string(), + c, (int)l, (int)mid, (int)h)); + if (c == 0) { + STRING_POOL_NOISY(ALOGI("MATCH!")); + return mid; + } else if (c < 0) { + l = mid + 1; + } else { + h = mid - 1; + } + } + } else { + // It is unusual to get the ID from an unsorted string block... + // most often this happens because we want to get IDs for style + // span tags; since those always appear at the end of the string + // block, start searching at the back. + for (int i=mHeader->stringCount-1; i>=0; i--) { + const char16_t* s = stringAt(i, &len); + STRING_POOL_NOISY(ALOGI("Looking at %s, i=%d\n", + String8(s).string(), + i)); + if (s && strLen == len && strzcmp16(s, len, str, strLen) == 0) { + STRING_POOL_NOISY(ALOGI("MATCH!")); + return i; + } } } } @@ -937,6 +1012,14 @@ const uint16_t* ResXMLParser::getAttributeNamespace(size_t idx, size_t* outLen) return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; } +const char* ResXMLParser::getAttributeNamespace8(size_t idx, size_t* outLen) const +{ + int32_t id = getAttributeNamespaceID(idx); + //printf("attribute namespace=%d idx=%d event=%p\n", id, idx, mEventCode); + //XML_NOISY(printf("getAttributeNamespace 0x%x=0x%x\n", idx, id)); + return id >= 0 ? mTree.mStrings.string8At(id, outLen) : NULL; +} + int32_t ResXMLParser::getAttributeNameID(size_t idx) const { if (mEventCode == START_TAG) { @@ -960,6 +1043,14 @@ const uint16_t* ResXMLParser::getAttributeName(size_t idx, size_t* outLen) const return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; } +const char* ResXMLParser::getAttributeName8(size_t idx, size_t* outLen) const +{ + int32_t id = getAttributeNameID(idx); + //printf("attribute name=%d idx=%d event=%p\n", id, idx, mEventCode); + //XML_NOISY(printf("getAttributeName 0x%x=0x%x\n", idx, id)); + return id >= 0 ? mTree.mStrings.string8At(id, outLen) : NULL; +} + uint32_t ResXMLParser::getAttributeNameResID(size_t idx) const { int32_t id = getAttributeNameID(idx); @@ -1049,22 +1140,67 @@ ssize_t ResXMLParser::indexOfAttribute(const char16_t* ns, size_t nsLen, const char16_t* attr, size_t attrLen) const { if (mEventCode == START_TAG) { + if (attr == NULL) { + return NAME_NOT_FOUND; + } const size_t N = getAttributeCount(); - for (size_t i=0; i<N; i++) { - size_t curNsLen, curAttrLen; - const char16_t* curNs = getAttributeNamespace(i, &curNsLen); - const char16_t* curAttr = getAttributeName(i, &curAttrLen); - //printf("%d: ns=%p attr=%p curNs=%p curAttr=%p\n", - // i, ns, attr, curNs, curAttr); - //printf(" --> attr=%s, curAttr=%s\n", - // String8(attr).string(), String8(curAttr).string()); - if (attr && curAttr && (strzcmp16(attr, attrLen, curAttr, curAttrLen) == 0)) { - if (ns == NULL) { - if (curNs == NULL) return i; - } else if (curNs != NULL) { - //printf(" --> ns=%s, curNs=%s\n", - // String8(ns).string(), String8(curNs).string()); - if (strzcmp16(ns, nsLen, curNs, curNsLen) == 0) return i; + if (mTree.mStrings.isUTF8()) { + String8 ns8, attr8; + if (ns != NULL) { + ns8 = String8(ns, nsLen); + } + attr8 = String8(attr, attrLen); + STRING_POOL_NOISY(ALOGI("indexOfAttribute UTF8 %s (%d) / %s (%d)", ns8.string(), nsLen, + attr8.string(), attrLen)); + for (size_t i=0; i<N; i++) { + size_t curNsLen = 0, curAttrLen = 0; + const char* curNs = getAttributeNamespace8(i, &curNsLen); + const char* curAttr = getAttributeName8(i, &curAttrLen); + STRING_POOL_NOISY(ALOGI(" curNs=%s (%d), curAttr=%s (%d)", curNs, curNsLen, + curAttr, curAttrLen)); + if (curAttr != NULL && curNsLen == nsLen && curAttrLen == attrLen + && memcmp(attr8.string(), curAttr, attrLen) == 0) { + if (ns == NULL) { + if (curNs == NULL) { + STRING_POOL_NOISY(ALOGI(" FOUND!")); + return i; + } + } else if (curNs != NULL) { + //printf(" --> ns=%s, curNs=%s\n", + // String8(ns).string(), String8(curNs).string()); + if (memcmp(ns8.string(), curNs, nsLen) == 0) { + STRING_POOL_NOISY(ALOGI(" FOUND!")); + return i; + } + } + } + } + } else { + STRING_POOL_NOISY(ALOGI("indexOfAttribute UTF16 %s (%d) / %s (%d)", + String8(ns, nsLen).string(), nsLen, + String8(attr, attrLen).string(), attrLen)); + for (size_t i=0; i<N; i++) { + size_t curNsLen = 0, curAttrLen = 0; + const char16_t* curNs = getAttributeNamespace(i, &curNsLen); + const char16_t* curAttr = getAttributeName(i, &curAttrLen); + STRING_POOL_NOISY(ALOGI(" curNs=%s (%d), curAttr=%s (%d)", + String8(curNs, curNsLen).string(), curNsLen, + String8(curAttr, curAttrLen).string(), curAttrLen)); + if (curAttr != NULL && curNsLen == nsLen && curAttrLen == attrLen + && (memcmp(attr, curAttr, attrLen*sizeof(char16_t)) == 0)) { + if (ns == NULL) { + if (curNs == NULL) { + STRING_POOL_NOISY(ALOGI(" FOUND!")); + return i; + } + } else if (curNs != NULL) { + //printf(" --> ns=%s, curNs=%s\n", + // String8(ns).string(), String8(curNs).string()); + if (memcmp(ns, curNs, nsLen*sizeof(char16_t)) == 0) { + STRING_POOL_NOISY(ALOGI(" FOUND!")); + return i; + } + } } } } @@ -2941,7 +3077,7 @@ void ResTable::uninit() mHeaders.clear(); } -bool ResTable::getResourceName(uint32_t resID, resource_name* outName) const +bool ResTable::getResourceName(uint32_t resID, bool allowUtf8, resource_name* outName) const { if (mError != NO_ERROR) { return false; @@ -2981,13 +3117,28 @@ bool ResTable::getResourceName(uint32_t resID, resource_name* outName) const outName->package = grp->name.string(); outName->packageLen = grp->name.size(); - 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; + if (allowUtf8) { + outName->type8 = grp->basePackage->typeStrings.string8At(t, &outName->typeLen); + outName->name8 = grp->basePackage->keyStrings.string8At( + dtohl(entry->key.index), &outName->nameLen); + } else { + outName->type8 = NULL; + outName->name8 = NULL; + } + if (outName->type8 == NULL) { + outName->type = grp->basePackage->typeStrings.stringAt(t, &outName->typeLen); + // If we have a bad index for some reason, we should abort. + if (outName->type == NULL) { + return false; + } + } + if (outName->name8 == NULL) { + 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->name == NULL) { + return false; + } } return true; @@ -4486,7 +4637,7 @@ bool ResTable::stringToValue(Res_value* outValue, String16* outString, while (cnt > 0) { if (!Res_INTERNALID(bag->map.name.ident)) { //printf("Trying attr #%08x\n", bag->map.name.ident); - if (getResourceName(bag->map.name.ident, &rname)) { + if (getResourceName(bag->map.name.ident, false, &rname)) { #if 0 printf("Matching %s against %s (0x%08x)\n", String8(s, len).string(), @@ -4539,7 +4690,7 @@ bool ResTable::stringToValue(Res_value* outValue, String16* outString, for (i=0; i<cnt; i++, bagi++) { if (!Res_INTERNALID(bagi->map.name.ident)) { //printf("Trying attr #%08x\n", bagi->map.name.ident); - if (getResourceName(bagi->map.name.ident, &rname)) { + if (getResourceName(bagi->map.name.ident, false, &rname)) { #if 0 printf("Matching %s against %s (0x%08x)\n", String8(start,pos-start).string(), @@ -5217,7 +5368,7 @@ status_t ResTable::createIdmap(const ResTable& overlay, uint32_t originalCrc, ui | (0x00ff0000 & ((typeIndex+1)<<16)) | (0x0000ffff & (entryIndex)); resource_name resName; - if (!this->getResourceName(resID, &resName)) { + if (!this->getResourceName(resID, true, &resName)) { ALOGW("idmap: resource 0x%08x has spec but lacks values, skipping\n", resID); // add dummy value, or trimming leading/trailing zeroes later will fail vector.push(0); @@ -5324,13 +5475,12 @@ bool ResTable::getIdmapInfo(const void* idmap, size_t sizeBytes, } -#ifndef HAVE_ANDROID_OS #define CHAR16_TO_CSTR(c16, len) (String8(String16(c16,len)).string()) #define CHAR16_ARRAY_EQ(constant, var, len) \ ((len == (sizeof(constant)/sizeof(constant[0]))) && (0 == memcmp((var), (constant), (len)))) -void print_complex(uint32_t complex, bool isFraction) +static void print_complex(uint32_t complex, bool isFraction) { const float MANTISSA_MULT = 1.0f / (1<<Res_value::COMPLEX_MANTISSA_SHIFT); @@ -5485,12 +5635,23 @@ void ResTable::print(bool inclValues) const | (0x00ff0000 & ((typeIndex+1)<<16)) | (0x0000ffff & (entryIndex)); resource_name resName; - if (this->getResourceName(resID, &resName)) { + if (this->getResourceName(resID, true, &resName)) { + String8 type8; + String8 name8; + if (resName.type8 != NULL) { + type8 = String8(resName.type8, resName.typeLen); + } else { + type8 = String8(resName.type, resName.typeLen); + } + if (resName.name8 != NULL) { + name8 = String8(resName.name8, resName.nameLen); + } else { + name8 = String8(resName.name, resName.nameLen); + } 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), + type8.string(), name8.string(), dtohl(typeConfigs->typeSpecFlags[entryIndex])); } else { printf(" INVALID TYPE CONFIG FOR RESOURCE 0x%08x\n", resID); @@ -5533,11 +5694,22 @@ void ResTable::print(bool inclValues) const | (0x00ff0000 & ((typeIndex+1)<<16)) | (0x0000ffff & (entryIndex)); resource_name resName; - if (this->getResourceName(resID, &resName)) { + if (this->getResourceName(resID, true, &resName)) { + String8 type8; + String8 name8; + if (resName.type8 != NULL) { + type8 = String8(resName.type8, resName.typeLen); + } else { + type8 = String8(resName.type, resName.typeLen); + } + if (resName.name8 != NULL) { + name8 = String8(resName.name8, resName.nameLen); + } else { + name8 = String8(resName.name, resName.nameLen); + } 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)); + type8.string(), name8.string()); } else { printf(" INVALID RESOURCE 0x%08x: ", resID); } @@ -5621,6 +5793,4 @@ void ResTable::print(bool inclValues) const } } -#endif // HAVE_ANDROID_OS - } // namespace android diff --git a/libs/androidfw/VelocityControl.cpp b/libs/androidfw/VelocityControl.cpp deleted file mode 100644 index cde2b76..0000000 --- a/libs/androidfw/VelocityControl.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "VelocityControl" -//#define LOG_NDEBUG 0 - -// Log debug messages about acceleration. -#define DEBUG_ACCELERATION 0 - -#include <math.h> -#include <limits.h> - -#include <androidfw/VelocityControl.h> -#include <utils/BitSet.h> -#include <utils/Timers.h> - -namespace android { - -// --- VelocityControl --- - -const nsecs_t VelocityControl::STOP_TIME; - -VelocityControl::VelocityControl() { - reset(); -} - -void VelocityControl::setParameters(const VelocityControlParameters& parameters) { - mParameters = parameters; - reset(); -} - -void VelocityControl::reset() { - mLastMovementTime = LLONG_MIN; - mRawPosition.x = 0; - mRawPosition.y = 0; - mVelocityTracker.clear(); -} - -void VelocityControl::move(nsecs_t eventTime, float* deltaX, float* deltaY) { - if ((deltaX && *deltaX) || (deltaY && *deltaY)) { - if (eventTime >= mLastMovementTime + STOP_TIME) { -#if DEBUG_ACCELERATION - ALOGD("VelocityControl: stopped, last movement was %0.3fms ago", - (eventTime - mLastMovementTime) * 0.000001f); -#endif - reset(); - } - - mLastMovementTime = eventTime; - if (deltaX) { - mRawPosition.x += *deltaX; - } - if (deltaY) { - mRawPosition.y += *deltaY; - } - mVelocityTracker.addMovement(eventTime, BitSet32(BitSet32::valueForBit(0)), &mRawPosition); - - float vx, vy; - float scale = mParameters.scale; - if (mVelocityTracker.getVelocity(0, &vx, &vy)) { - float speed = hypotf(vx, vy) * scale; - if (speed >= mParameters.highThreshold) { - // Apply full acceleration above the high speed threshold. - scale *= mParameters.acceleration; - } else if (speed > mParameters.lowThreshold) { - // Linearly interpolate the acceleration to apply between the low and high - // speed thresholds. - scale *= 1 + (speed - mParameters.lowThreshold) - / (mParameters.highThreshold - mParameters.lowThreshold) - * (mParameters.acceleration - 1); - } - -#if DEBUG_ACCELERATION - ALOGD("VelocityControl(%0.3f, %0.3f, %0.3f, %0.3f): " - "vx=%0.3f, vy=%0.3f, speed=%0.3f, accel=%0.3f", - mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold, - mParameters.acceleration, - vx, vy, speed, scale / mParameters.scale); -#endif - } else { -#if DEBUG_ACCELERATION - ALOGD("VelocityControl(%0.3f, %0.3f, %0.3f, %0.3f): unknown velocity", - mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold, - mParameters.acceleration); -#endif - } - - if (deltaX) { - *deltaX *= scale; - } - if (deltaY) { - *deltaY *= scale; - } - } -} - -} // namespace android diff --git a/libs/androidfw/VelocityTracker.cpp b/libs/androidfw/VelocityTracker.cpp deleted file mode 100644 index f48ec62..0000000 --- a/libs/androidfw/VelocityTracker.cpp +++ /dev/null @@ -1,928 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "VelocityTracker" -//#define LOG_NDEBUG 0 - -// Log debug messages about velocity tracking. -#define DEBUG_VELOCITY 0 - -// Log debug messages about the progress of the algorithm itself. -#define DEBUG_STRATEGY 0 - -#include <math.h> -#include <limits.h> - -#include <androidfw/VelocityTracker.h> -#include <utils/BitSet.h> -#include <utils/String8.h> -#include <utils/Timers.h> - -#include <cutils/properties.h> - -namespace android { - -// Nanoseconds per milliseconds. -static const nsecs_t NANOS_PER_MS = 1000000; - -// Threshold for determining that a pointer has stopped moving. -// Some input devices do not send ACTION_MOVE events in the case where a pointer has -// stopped. We need to detect this case so that we can accurately predict the -// velocity after the pointer starts moving again. -static const nsecs_t ASSUME_POINTER_STOPPED_TIME = 40 * NANOS_PER_MS; - - -static float vectorDot(const float* a, const float* b, uint32_t m) { - float r = 0; - while (m--) { - r += *(a++) * *(b++); - } - return r; -} - -static float vectorNorm(const float* a, uint32_t m) { - float r = 0; - while (m--) { - float t = *(a++); - r += t * t; - } - return sqrtf(r); -} - -#if DEBUG_STRATEGY || DEBUG_VELOCITY -static String8 vectorToString(const float* a, uint32_t m) { - String8 str; - str.append("["); - while (m--) { - str.appendFormat(" %f", *(a++)); - if (m) { - str.append(","); - } - } - str.append(" ]"); - return str; -} - -static String8 matrixToString(const float* a, uint32_t m, uint32_t n, bool rowMajor) { - String8 str; - str.append("["); - for (size_t i = 0; i < m; i++) { - if (i) { - str.append(","); - } - str.append(" ["); - for (size_t j = 0; j < n; j++) { - if (j) { - str.append(","); - } - str.appendFormat(" %f", a[rowMajor ? i * n + j : j * m + i]); - } - str.append(" ]"); - } - str.append(" ]"); - return str; -} -#endif - - -// --- VelocityTracker --- - -// The default velocity tracker strategy. -// Although other strategies are available for testing and comparison purposes, -// this is the strategy that applications will actually use. Be very careful -// when adjusting the default strategy because it can dramatically affect -// (often in a bad way) the user experience. -const char* VelocityTracker::DEFAULT_STRATEGY = "lsq2"; - -VelocityTracker::VelocityTracker(const char* strategy) : - mLastEventTime(0), mCurrentPointerIdBits(0), mActivePointerId(-1) { - char value[PROPERTY_VALUE_MAX]; - - // Allow the default strategy to be overridden using a system property for debugging. - if (!strategy) { - int length = property_get("debug.velocitytracker.strategy", value, NULL); - if (length > 0) { - strategy = value; - } else { - strategy = DEFAULT_STRATEGY; - } - } - - // Configure the strategy. - if (!configureStrategy(strategy)) { - ALOGD("Unrecognized velocity tracker strategy name '%s'.", strategy); - if (!configureStrategy(DEFAULT_STRATEGY)) { - LOG_ALWAYS_FATAL("Could not create the default velocity tracker strategy '%s'!", - strategy); - } - } -} - -VelocityTracker::~VelocityTracker() { - delete mStrategy; -} - -bool VelocityTracker::configureStrategy(const char* strategy) { - mStrategy = createStrategy(strategy); - return mStrategy != NULL; -} - -VelocityTrackerStrategy* VelocityTracker::createStrategy(const char* strategy) { - if (!strcmp("lsq1", strategy)) { - // 1st order least squares. Quality: POOR. - // Frequently underfits the touch data especially when the finger accelerates - // or changes direction. Often underestimates velocity. The direction - // is overly influenced by historical touch points. - return new LeastSquaresVelocityTrackerStrategy(1); - } - if (!strcmp("lsq2", strategy)) { - // 2nd order least squares. Quality: VERY GOOD. - // Pretty much ideal, but can be confused by certain kinds of touch data, - // particularly if the panel has a tendency to generate delayed, - // duplicate or jittery touch coordinates when the finger is released. - return new LeastSquaresVelocityTrackerStrategy(2); - } - if (!strcmp("lsq3", strategy)) { - // 3rd order least squares. Quality: UNUSABLE. - // Frequently overfits the touch data yielding wildly divergent estimates - // of the velocity when the finger is released. - return new LeastSquaresVelocityTrackerStrategy(3); - } - if (!strcmp("wlsq2-delta", strategy)) { - // 2nd order weighted least squares, delta weighting. Quality: EXPERIMENTAL - return new LeastSquaresVelocityTrackerStrategy(2, - LeastSquaresVelocityTrackerStrategy::WEIGHTING_DELTA); - } - if (!strcmp("wlsq2-central", strategy)) { - // 2nd order weighted least squares, central weighting. Quality: EXPERIMENTAL - return new LeastSquaresVelocityTrackerStrategy(2, - LeastSquaresVelocityTrackerStrategy::WEIGHTING_CENTRAL); - } - if (!strcmp("wlsq2-recent", strategy)) { - // 2nd order weighted least squares, recent weighting. Quality: EXPERIMENTAL - return new LeastSquaresVelocityTrackerStrategy(2, - LeastSquaresVelocityTrackerStrategy::WEIGHTING_RECENT); - } - if (!strcmp("int1", strategy)) { - // 1st order integrating filter. Quality: GOOD. - // Not as good as 'lsq2' because it cannot estimate acceleration but it is - // more tolerant of errors. Like 'lsq1', this strategy tends to underestimate - // the velocity of a fling but this strategy tends to respond to changes in - // direction more quickly and accurately. - return new IntegratingVelocityTrackerStrategy(1); - } - if (!strcmp("int2", strategy)) { - // 2nd order integrating filter. Quality: EXPERIMENTAL. - // For comparison purposes only. Unlike 'int1' this strategy can compensate - // for acceleration but it typically overestimates the effect. - return new IntegratingVelocityTrackerStrategy(2); - } - if (!strcmp("legacy", strategy)) { - // Legacy velocity tracker algorithm. Quality: POOR. - // For comparison purposes only. This algorithm is strongly influenced by - // old data points, consistently underestimates velocity and takes a very long - // time to adjust to changes in direction. - return new LegacyVelocityTrackerStrategy(); - } - return NULL; -} - -void VelocityTracker::clear() { - mCurrentPointerIdBits.clear(); - mActivePointerId = -1; - - mStrategy->clear(); -} - -void VelocityTracker::clearPointers(BitSet32 idBits) { - BitSet32 remainingIdBits(mCurrentPointerIdBits.value & ~idBits.value); - mCurrentPointerIdBits = remainingIdBits; - - if (mActivePointerId >= 0 && idBits.hasBit(mActivePointerId)) { - mActivePointerId = !remainingIdBits.isEmpty() ? remainingIdBits.firstMarkedBit() : -1; - } - - mStrategy->clearPointers(idBits); -} - -void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits, const Position* positions) { - while (idBits.count() > MAX_POINTERS) { - idBits.clearLastMarkedBit(); - } - - if ((mCurrentPointerIdBits.value & idBits.value) - && eventTime >= mLastEventTime + ASSUME_POINTER_STOPPED_TIME) { -#if DEBUG_VELOCITY - ALOGD("VelocityTracker: stopped for %0.3f ms, clearing state.", - (eventTime - mLastEventTime) * 0.000001f); -#endif - // We have not received any movements for too long. Assume that all pointers - // have stopped. - mStrategy->clear(); - } - mLastEventTime = eventTime; - - mCurrentPointerIdBits = idBits; - if (mActivePointerId < 0 || !idBits.hasBit(mActivePointerId)) { - mActivePointerId = idBits.isEmpty() ? -1 : idBits.firstMarkedBit(); - } - - mStrategy->addMovement(eventTime, idBits, positions); - -#if DEBUG_VELOCITY - ALOGD("VelocityTracker: addMovement eventTime=%lld, idBits=0x%08x, activePointerId=%d", - eventTime, idBits.value, mActivePointerId); - for (BitSet32 iterBits(idBits); !iterBits.isEmpty(); ) { - uint32_t id = iterBits.firstMarkedBit(); - uint32_t index = idBits.getIndexOfBit(id); - iterBits.clearBit(id); - Estimator estimator; - getEstimator(id, &estimator); - ALOGD(" %d: position (%0.3f, %0.3f), " - "estimator (degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f)", - id, positions[index].x, positions[index].y, - int(estimator.degree), - vectorToString(estimator.xCoeff, estimator.degree + 1).string(), - vectorToString(estimator.yCoeff, estimator.degree + 1).string(), - estimator.confidence); - } -#endif -} - -void VelocityTracker::addMovement(const MotionEvent* event) { - int32_t actionMasked = event->getActionMasked(); - - switch (actionMasked) { - case AMOTION_EVENT_ACTION_DOWN: - case AMOTION_EVENT_ACTION_HOVER_ENTER: - // Clear all pointers on down before adding the new movement. - clear(); - break; - case AMOTION_EVENT_ACTION_POINTER_DOWN: { - // Start a new movement trace for a pointer that just went down. - // We do this on down instead of on up because the client may want to query the - // final velocity for a pointer that just went up. - BitSet32 downIdBits; - downIdBits.markBit(event->getPointerId(event->getActionIndex())); - clearPointers(downIdBits); - break; - } - case AMOTION_EVENT_ACTION_MOVE: - case AMOTION_EVENT_ACTION_HOVER_MOVE: - break; - default: - // Ignore all other actions because they do not convey any new information about - // pointer movement. We also want to preserve the last known velocity of the pointers. - // Note that ACTION_UP and ACTION_POINTER_UP always report the last known position - // of the pointers that went up. ACTION_POINTER_UP does include the new position of - // pointers that remained down but we will also receive an ACTION_MOVE with this - // information if any of them actually moved. Since we don't know how many pointers - // will be going up at once it makes sense to just wait for the following ACTION_MOVE - // before adding the movement. - return; - } - - size_t pointerCount = event->getPointerCount(); - if (pointerCount > MAX_POINTERS) { - pointerCount = MAX_POINTERS; - } - - BitSet32 idBits; - for (size_t i = 0; i < pointerCount; i++) { - idBits.markBit(event->getPointerId(i)); - } - - uint32_t pointerIndex[MAX_POINTERS]; - for (size_t i = 0; i < pointerCount; i++) { - pointerIndex[i] = idBits.getIndexOfBit(event->getPointerId(i)); - } - - nsecs_t eventTime; - Position positions[pointerCount]; - - size_t historySize = event->getHistorySize(); - for (size_t h = 0; h < historySize; h++) { - eventTime = event->getHistoricalEventTime(h); - for (size_t i = 0; i < pointerCount; i++) { - uint32_t index = pointerIndex[i]; - positions[index].x = event->getHistoricalX(i, h); - positions[index].y = event->getHistoricalY(i, h); - } - addMovement(eventTime, idBits, positions); - } - - eventTime = event->getEventTime(); - for (size_t i = 0; i < pointerCount; i++) { - uint32_t index = pointerIndex[i]; - positions[index].x = event->getX(i); - positions[index].y = event->getY(i); - } - addMovement(eventTime, idBits, positions); -} - -bool VelocityTracker::getVelocity(uint32_t id, float* outVx, float* outVy) const { - Estimator estimator; - if (getEstimator(id, &estimator) && estimator.degree >= 1) { - *outVx = estimator.xCoeff[1]; - *outVy = estimator.yCoeff[1]; - return true; - } - *outVx = 0; - *outVy = 0; - return false; -} - -bool VelocityTracker::getEstimator(uint32_t id, Estimator* outEstimator) const { - return mStrategy->getEstimator(id, outEstimator); -} - - -// --- LeastSquaresVelocityTrackerStrategy --- - -const nsecs_t LeastSquaresVelocityTrackerStrategy::HORIZON; -const uint32_t LeastSquaresVelocityTrackerStrategy::HISTORY_SIZE; - -LeastSquaresVelocityTrackerStrategy::LeastSquaresVelocityTrackerStrategy( - uint32_t degree, Weighting weighting) : - mDegree(degree), mWeighting(weighting) { - clear(); -} - -LeastSquaresVelocityTrackerStrategy::~LeastSquaresVelocityTrackerStrategy() { -} - -void LeastSquaresVelocityTrackerStrategy::clear() { - mIndex = 0; - mMovements[0].idBits.clear(); -} - -void LeastSquaresVelocityTrackerStrategy::clearPointers(BitSet32 idBits) { - BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value); - mMovements[mIndex].idBits = remainingIdBits; -} - -void LeastSquaresVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, - const VelocityTracker::Position* positions) { - if (++mIndex == HISTORY_SIZE) { - mIndex = 0; - } - - Movement& movement = mMovements[mIndex]; - movement.eventTime = eventTime; - movement.idBits = idBits; - uint32_t count = idBits.count(); - for (uint32_t i = 0; i < count; i++) { - movement.positions[i] = positions[i]; - } -} - -/** - * Solves a linear least squares problem to obtain a N degree polynomial that fits - * the specified input data as nearly as possible. - * - * Returns true if a solution is found, false otherwise. - * - * The input consists of two vectors of data points X and Y with indices 0..m-1 - * along with a weight vector W of the same size. - * - * The output is a vector B with indices 0..n that describes a polynomial - * that fits the data, such the sum of W[i] * W[i] * abs(Y[i] - (B[0] + B[1] X[i] - * + B[2] X[i]^2 ... B[n] X[i]^n)) for all i between 0 and m-1 is minimized. - * - * Accordingly, the weight vector W should be initialized by the caller with the - * reciprocal square root of the variance of the error in each input data point. - * In other words, an ideal choice for W would be W[i] = 1 / var(Y[i]) = 1 / stddev(Y[i]). - * The weights express the relative importance of each data point. If the weights are - * all 1, then the data points are considered to be of equal importance when fitting - * the polynomial. It is a good idea to choose weights that diminish the importance - * of data points that may have higher than usual error margins. - * - * Errors among data points are assumed to be independent. W is represented here - * as a vector although in the literature it is typically taken to be a diagonal matrix. - * - * That is to say, the function that generated the input data can be approximated - * by y(x) ~= B[0] + B[1] x + B[2] x^2 + ... + B[n] x^n. - * - * The coefficient of determination (R^2) is also returned to describe the goodness - * of fit of the model for the given data. It is a value between 0 and 1, where 1 - * indicates perfect correspondence. - * - * This function first expands the X vector to a m by n matrix A such that - * A[i][0] = 1, A[i][1] = X[i], A[i][2] = X[i]^2, ..., A[i][n] = X[i]^n, then - * multiplies it by w[i]./ - * - * Then it calculates the QR decomposition of A yielding an m by m orthonormal matrix Q - * and an m by n upper triangular matrix R. Because R is upper triangular (lower - * part is all zeroes), we can simplify the decomposition into an m by n matrix - * Q1 and a n by n matrix R1 such that A = Q1 R1. - * - * Finally we solve the system of linear equations given by R1 B = (Qtranspose W Y) - * to find B. - * - * For efficiency, we lay out A and Q column-wise in memory because we frequently - * operate on the column vectors. Conversely, we lay out R row-wise. - * - * http://en.wikipedia.org/wiki/Numerical_methods_for_linear_least_squares - * http://en.wikipedia.org/wiki/Gram-Schmidt - */ -static bool solveLeastSquares(const float* x, const float* y, - const float* w, uint32_t m, uint32_t n, float* outB, float* outDet) { -#if DEBUG_STRATEGY - ALOGD("solveLeastSquares: m=%d, n=%d, x=%s, y=%s, w=%s", int(m), int(n), - vectorToString(x, m).string(), vectorToString(y, m).string(), - vectorToString(w, m).string()); -#endif - - // Expand the X vector to a matrix A, pre-multiplied by the weights. - float a[n][m]; // column-major order - for (uint32_t h = 0; h < m; h++) { - a[0][h] = w[h]; - for (uint32_t i = 1; i < n; i++) { - a[i][h] = a[i - 1][h] * x[h]; - } - } -#if DEBUG_STRATEGY - ALOGD(" - a=%s", matrixToString(&a[0][0], m, n, false /*rowMajor*/).string()); -#endif - - // Apply the Gram-Schmidt process to A to obtain its QR decomposition. - float q[n][m]; // orthonormal basis, column-major order - float r[n][n]; // upper triangular matrix, row-major order - for (uint32_t j = 0; j < n; j++) { - for (uint32_t h = 0; h < m; h++) { - q[j][h] = a[j][h]; - } - for (uint32_t i = 0; i < j; i++) { - float dot = vectorDot(&q[j][0], &q[i][0], m); - for (uint32_t h = 0; h < m; h++) { - q[j][h] -= dot * q[i][h]; - } - } - - float norm = vectorNorm(&q[j][0], m); - if (norm < 0.000001f) { - // vectors are linearly dependent or zero so no solution -#if DEBUG_STRATEGY - ALOGD(" - no solution, norm=%f", norm); -#endif - return false; - } - - float invNorm = 1.0f / norm; - for (uint32_t h = 0; h < m; h++) { - q[j][h] *= invNorm; - } - for (uint32_t i = 0; i < n; i++) { - r[j][i] = i < j ? 0 : vectorDot(&q[j][0], &a[i][0], m); - } - } -#if DEBUG_STRATEGY - ALOGD(" - q=%s", matrixToString(&q[0][0], m, n, false /*rowMajor*/).string()); - ALOGD(" - r=%s", matrixToString(&r[0][0], n, n, true /*rowMajor*/).string()); - - // calculate QR, if we factored A correctly then QR should equal A - float qr[n][m]; - for (uint32_t h = 0; h < m; h++) { - for (uint32_t i = 0; i < n; i++) { - qr[i][h] = 0; - for (uint32_t j = 0; j < n; j++) { - qr[i][h] += q[j][h] * r[j][i]; - } - } - } - ALOGD(" - qr=%s", matrixToString(&qr[0][0], m, n, false /*rowMajor*/).string()); -#endif - - // Solve R B = Qt W Y to find B. This is easy because R is upper triangular. - // We just work from bottom-right to top-left calculating B's coefficients. - float wy[m]; - for (uint32_t h = 0; h < m; h++) { - wy[h] = y[h] * w[h]; - } - for (uint32_t i = n; i-- != 0; ) { - outB[i] = vectorDot(&q[i][0], wy, m); - for (uint32_t j = n - 1; j > i; j--) { - outB[i] -= r[i][j] * outB[j]; - } - outB[i] /= r[i][i]; - } -#if DEBUG_STRATEGY - ALOGD(" - b=%s", vectorToString(outB, n).string()); -#endif - - // Calculate the coefficient of determination as 1 - (SSerr / SStot) where - // SSerr is the residual sum of squares (variance of the error), - // and SStot is the total sum of squares (variance of the data) where each - // has been weighted. - float ymean = 0; - for (uint32_t h = 0; h < m; h++) { - ymean += y[h]; - } - ymean /= m; - - float sserr = 0; - float sstot = 0; - for (uint32_t h = 0; h < m; h++) { - float err = y[h] - outB[0]; - float term = 1; - for (uint32_t i = 1; i < n; i++) { - term *= x[h]; - err -= term * outB[i]; - } - sserr += w[h] * w[h] * err * err; - float var = y[h] - ymean; - sstot += w[h] * w[h] * var * var; - } - *outDet = sstot > 0.000001f ? 1.0f - (sserr / sstot) : 1; -#if DEBUG_STRATEGY - ALOGD(" - sserr=%f", sserr); - ALOGD(" - sstot=%f", sstot); - ALOGD(" - det=%f", *outDet); -#endif - return true; -} - -bool LeastSquaresVelocityTrackerStrategy::getEstimator(uint32_t id, - VelocityTracker::Estimator* outEstimator) const { - outEstimator->clear(); - - // Iterate over movement samples in reverse time order and collect samples. - float x[HISTORY_SIZE]; - float y[HISTORY_SIZE]; - float w[HISTORY_SIZE]; - float time[HISTORY_SIZE]; - uint32_t m = 0; - uint32_t index = mIndex; - const Movement& newestMovement = mMovements[mIndex]; - do { - const Movement& movement = mMovements[index]; - if (!movement.idBits.hasBit(id)) { - break; - } - - nsecs_t age = newestMovement.eventTime - movement.eventTime; - if (age > HORIZON) { - break; - } - - const VelocityTracker::Position& position = movement.getPosition(id); - x[m] = position.x; - y[m] = position.y; - w[m] = chooseWeight(index); - time[m] = -age * 0.000000001f; - index = (index == 0 ? HISTORY_SIZE : index) - 1; - } while (++m < HISTORY_SIZE); - - if (m == 0) { - return false; // no data - } - - // Calculate a least squares polynomial fit. - uint32_t degree = mDegree; - if (degree > m - 1) { - degree = m - 1; - } - if (degree >= 1) { - float xdet, ydet; - uint32_t n = degree + 1; - if (solveLeastSquares(time, x, w, m, n, outEstimator->xCoeff, &xdet) - && solveLeastSquares(time, y, w, m, n, outEstimator->yCoeff, &ydet)) { - outEstimator->time = newestMovement.eventTime; - outEstimator->degree = degree; - outEstimator->confidence = xdet * ydet; -#if DEBUG_STRATEGY - ALOGD("estimate: degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f", - int(outEstimator->degree), - vectorToString(outEstimator->xCoeff, n).string(), - vectorToString(outEstimator->yCoeff, n).string(), - outEstimator->confidence); -#endif - return true; - } - } - - // No velocity data available for this pointer, but we do have its current position. - outEstimator->xCoeff[0] = x[0]; - outEstimator->yCoeff[0] = y[0]; - outEstimator->time = newestMovement.eventTime; - outEstimator->degree = 0; - outEstimator->confidence = 1; - return true; -} - -float LeastSquaresVelocityTrackerStrategy::chooseWeight(uint32_t index) const { - switch (mWeighting) { - case WEIGHTING_DELTA: { - // Weight points based on how much time elapsed between them and the next - // point so that points that "cover" a shorter time span are weighed less. - // delta 0ms: 0.5 - // delta 10ms: 1.0 - if (index == mIndex) { - return 1.0f; - } - uint32_t nextIndex = (index + 1) % HISTORY_SIZE; - float deltaMillis = (mMovements[nextIndex].eventTime- mMovements[index].eventTime) - * 0.000001f; - if (deltaMillis < 0) { - return 0.5f; - } - if (deltaMillis < 10) { - return 0.5f + deltaMillis * 0.05; - } - return 1.0f; - } - - case WEIGHTING_CENTRAL: { - // Weight points based on their age, weighing very recent and very old points less. - // age 0ms: 0.5 - // age 10ms: 1.0 - // age 50ms: 1.0 - // age 60ms: 0.5 - float ageMillis = (mMovements[mIndex].eventTime - mMovements[index].eventTime) - * 0.000001f; - if (ageMillis < 0) { - return 0.5f; - } - if (ageMillis < 10) { - return 0.5f + ageMillis * 0.05; - } - if (ageMillis < 50) { - return 1.0f; - } - if (ageMillis < 60) { - return 0.5f + (60 - ageMillis) * 0.05; - } - return 0.5f; - } - - case WEIGHTING_RECENT: { - // Weight points based on their age, weighing older points less. - // age 0ms: 1.0 - // age 50ms: 1.0 - // age 100ms: 0.5 - float ageMillis = (mMovements[mIndex].eventTime - mMovements[index].eventTime) - * 0.000001f; - if (ageMillis < 50) { - return 1.0f; - } - if (ageMillis < 100) { - return 0.5f + (100 - ageMillis) * 0.01f; - } - return 0.5f; - } - - case WEIGHTING_NONE: - default: - return 1.0f; - } -} - - -// --- IntegratingVelocityTrackerStrategy --- - -IntegratingVelocityTrackerStrategy::IntegratingVelocityTrackerStrategy(uint32_t degree) : - mDegree(degree) { -} - -IntegratingVelocityTrackerStrategy::~IntegratingVelocityTrackerStrategy() { -} - -void IntegratingVelocityTrackerStrategy::clear() { - mPointerIdBits.clear(); -} - -void IntegratingVelocityTrackerStrategy::clearPointers(BitSet32 idBits) { - mPointerIdBits.value &= ~idBits.value; -} - -void IntegratingVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, - const VelocityTracker::Position* positions) { - uint32_t index = 0; - for (BitSet32 iterIdBits(idBits); !iterIdBits.isEmpty();) { - uint32_t id = iterIdBits.clearFirstMarkedBit(); - State& state = mPointerState[id]; - const VelocityTracker::Position& position = positions[index++]; - if (mPointerIdBits.hasBit(id)) { - updateState(state, eventTime, position.x, position.y); - } else { - initState(state, eventTime, position.x, position.y); - } - } - - mPointerIdBits = idBits; -} - -bool IntegratingVelocityTrackerStrategy::getEstimator(uint32_t id, - VelocityTracker::Estimator* outEstimator) const { - outEstimator->clear(); - - if (mPointerIdBits.hasBit(id)) { - const State& state = mPointerState[id]; - populateEstimator(state, outEstimator); - return true; - } - - return false; -} - -void IntegratingVelocityTrackerStrategy::initState(State& state, - nsecs_t eventTime, float xpos, float ypos) const { - state.updateTime = eventTime; - state.degree = 0; - - state.xpos = xpos; - state.xvel = 0; - state.xaccel = 0; - state.ypos = ypos; - state.yvel = 0; - state.yaccel = 0; -} - -void IntegratingVelocityTrackerStrategy::updateState(State& state, - nsecs_t eventTime, float xpos, float ypos) const { - const nsecs_t MIN_TIME_DELTA = 2 * NANOS_PER_MS; - const float FILTER_TIME_CONSTANT = 0.010f; // 10 milliseconds - - if (eventTime <= state.updateTime + MIN_TIME_DELTA) { - return; - } - - float dt = (eventTime - state.updateTime) * 0.000000001f; - state.updateTime = eventTime; - - float xvel = (xpos - state.xpos) / dt; - float yvel = (ypos - state.ypos) / dt; - if (state.degree == 0) { - state.xvel = xvel; - state.yvel = yvel; - state.degree = 1; - } else { - float alpha = dt / (FILTER_TIME_CONSTANT + dt); - if (mDegree == 1) { - state.xvel += (xvel - state.xvel) * alpha; - state.yvel += (yvel - state.yvel) * alpha; - } else { - float xaccel = (xvel - state.xvel) / dt; - float yaccel = (yvel - state.yvel) / dt; - if (state.degree == 1) { - state.xaccel = xaccel; - state.yaccel = yaccel; - state.degree = 2; - } else { - state.xaccel += (xaccel - state.xaccel) * alpha; - state.yaccel += (yaccel - state.yaccel) * alpha; - } - state.xvel += (state.xaccel * dt) * alpha; - state.yvel += (state.yaccel * dt) * alpha; - } - } - state.xpos = xpos; - state.ypos = ypos; -} - -void IntegratingVelocityTrackerStrategy::populateEstimator(const State& state, - VelocityTracker::Estimator* outEstimator) const { - outEstimator->time = state.updateTime; - outEstimator->confidence = 1.0f; - outEstimator->degree = state.degree; - outEstimator->xCoeff[0] = state.xpos; - outEstimator->xCoeff[1] = state.xvel; - outEstimator->xCoeff[2] = state.xaccel / 2; - outEstimator->yCoeff[0] = state.ypos; - outEstimator->yCoeff[1] = state.yvel; - outEstimator->yCoeff[2] = state.yaccel / 2; -} - - -// --- LegacyVelocityTrackerStrategy --- - -const nsecs_t LegacyVelocityTrackerStrategy::HORIZON; -const uint32_t LegacyVelocityTrackerStrategy::HISTORY_SIZE; -const nsecs_t LegacyVelocityTrackerStrategy::MIN_DURATION; - -LegacyVelocityTrackerStrategy::LegacyVelocityTrackerStrategy() { - clear(); -} - -LegacyVelocityTrackerStrategy::~LegacyVelocityTrackerStrategy() { -} - -void LegacyVelocityTrackerStrategy::clear() { - mIndex = 0; - mMovements[0].idBits.clear(); -} - -void LegacyVelocityTrackerStrategy::clearPointers(BitSet32 idBits) { - BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value); - mMovements[mIndex].idBits = remainingIdBits; -} - -void LegacyVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, - const VelocityTracker::Position* positions) { - if (++mIndex == HISTORY_SIZE) { - mIndex = 0; - } - - Movement& movement = mMovements[mIndex]; - movement.eventTime = eventTime; - movement.idBits = idBits; - uint32_t count = idBits.count(); - for (uint32_t i = 0; i < count; i++) { - movement.positions[i] = positions[i]; - } -} - -bool LegacyVelocityTrackerStrategy::getEstimator(uint32_t id, - VelocityTracker::Estimator* outEstimator) const { - outEstimator->clear(); - - const Movement& newestMovement = mMovements[mIndex]; - if (!newestMovement.idBits.hasBit(id)) { - return false; // no data - } - - // Find the oldest sample that contains the pointer and that is not older than HORIZON. - nsecs_t minTime = newestMovement.eventTime - HORIZON; - uint32_t oldestIndex = mIndex; - uint32_t numTouches = 1; - do { - uint32_t nextOldestIndex = (oldestIndex == 0 ? HISTORY_SIZE : oldestIndex) - 1; - const Movement& nextOldestMovement = mMovements[nextOldestIndex]; - if (!nextOldestMovement.idBits.hasBit(id) - || nextOldestMovement.eventTime < minTime) { - break; - } - oldestIndex = nextOldestIndex; - } while (++numTouches < HISTORY_SIZE); - - // Calculate an exponentially weighted moving average of the velocity estimate - // at different points in time measured relative to the oldest sample. - // This is essentially an IIR filter. Newer samples are weighted more heavily - // than older samples. Samples at equal time points are weighted more or less - // equally. - // - // One tricky problem is that the sample data may be poorly conditioned. - // Sometimes samples arrive very close together in time which can cause us to - // overestimate the velocity at that time point. Most samples might be measured - // 16ms apart but some consecutive samples could be only 0.5sm apart because - // the hardware or driver reports them irregularly or in bursts. - float accumVx = 0; - float accumVy = 0; - uint32_t index = oldestIndex; - uint32_t samplesUsed = 0; - const Movement& oldestMovement = mMovements[oldestIndex]; - const VelocityTracker::Position& oldestPosition = oldestMovement.getPosition(id); - nsecs_t lastDuration = 0; - - while (numTouches-- > 1) { - if (++index == HISTORY_SIZE) { - index = 0; - } - const Movement& movement = mMovements[index]; - nsecs_t duration = movement.eventTime - oldestMovement.eventTime; - - // If the duration between samples is small, we may significantly overestimate - // the velocity. Consequently, we impose a minimum duration constraint on the - // samples that we include in the calculation. - if (duration >= MIN_DURATION) { - const VelocityTracker::Position& position = movement.getPosition(id); - float scale = 1000000000.0f / duration; // one over time delta in seconds - float vx = (position.x - oldestPosition.x) * scale; - float vy = (position.y - oldestPosition.y) * scale; - accumVx = (accumVx * lastDuration + vx * duration) / (duration + lastDuration); - accumVy = (accumVy * lastDuration + vy * duration) / (duration + lastDuration); - lastDuration = duration; - samplesUsed += 1; - } - } - - // Report velocity. - const VelocityTracker::Position& newestPosition = newestMovement.getPosition(id); - outEstimator->time = newestMovement.eventTime; - outEstimator->confidence = 1; - outEstimator->xCoeff[0] = newestPosition.x; - outEstimator->yCoeff[0] = newestPosition.y; - if (samplesUsed) { - outEstimator->xCoeff[1] = accumVx; - outEstimator->yCoeff[1] = accumVy; - outEstimator->degree = 1; - } else { - outEstimator->degree = 0; - } - return true; -} - -} // namespace android diff --git a/libs/androidfw/VirtualKeyMap.cpp b/libs/androidfw/VirtualKeyMap.cpp deleted file mode 100644 index 2ba1673..0000000 --- a/libs/androidfw/VirtualKeyMap.cpp +++ /dev/null @@ -1,171 +0,0 @@ -/* - * 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 "VirtualKeyMap" - -#include <stdlib.h> -#include <string.h> -#include <androidfw/VirtualKeyMap.h> -#include <utils/Log.h> -#include <utils/Errors.h> -#include <utils/Tokenizer.h> -#include <utils/Timers.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_FIELD_DELIMITER = " \t\r:"; - - -// --- VirtualKeyMap --- - -VirtualKeyMap::VirtualKeyMap() { -} - -VirtualKeyMap::~VirtualKeyMap() { -} - -status_t VirtualKeyMap::load(const String8& filename, VirtualKeyMap** outMap) { - *outMap = NULL; - - Tokenizer* tokenizer; - status_t status = Tokenizer::open(filename, &tokenizer); - if (status) { - ALOGE("Error %d opening virtual key map file %s.", status, filename.string()); - } else { - VirtualKeyMap* map = new VirtualKeyMap(); - if (!map) { - ALOGE("Error allocating virtual key map."); - status = NO_MEMORY; - } else { -#if DEBUG_PARSER_PERFORMANCE - nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); -#endif - Parser parser(map, tokenizer); - status = parser.parse(); -#if DEBUG_PARSER_PERFORMANCE - nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; - ALOGD("Parsed key character map 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; -} - - -// --- VirtualKeyMap::Parser --- - -VirtualKeyMap::Parser::Parser(VirtualKeyMap* map, Tokenizer* tokenizer) : - mMap(map), mTokenizer(tokenizer) { -} - -VirtualKeyMap::Parser::~Parser() { -} - -status_t VirtualKeyMap::Parser::parse() { - while (!mTokenizer->isEof()) { -#if DEBUG_PARSER - ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(), - mTokenizer->peekRemainderOfLine().string()); -#endif - - mTokenizer->skipDelimiters(WHITESPACE); - - if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { - // Multiple keys can appear on one line or they can be broken up across multiple lines. - do { - String8 token = mTokenizer->nextToken(WHITESPACE_OR_FIELD_DELIMITER); - if (token != "0x01") { - ALOGE("%s: Unknown virtual key type, expected 0x01.", - mTokenizer->getLocation().string()); - return BAD_VALUE; - } - - VirtualKeyDefinition defn; - bool success = parseNextIntField(&defn.scanCode) - && parseNextIntField(&defn.centerX) - && parseNextIntField(&defn.centerY) - && parseNextIntField(&defn.width) - && parseNextIntField(&defn.height); - if (!success) { - ALOGE("%s: Expected 5 colon-delimited integers in virtual key definition.", - mTokenizer->getLocation().string()); - return BAD_VALUE; - } - -#if DEBUG_PARSER - ALOGD("Parsed virtual key: scanCode=%d, centerX=%d, centerY=%d, " - "width=%d, height=%d", - defn.scanCode, defn.centerX, defn.centerY, defn.width, defn.height); -#endif - mMap->mVirtualKeys.push(defn); - } while (consumeFieldDelimiterAndSkipWhitespace()); - - if (!mTokenizer->isEol()) { - ALOGE("%s: Expected end of line, got '%s'.", - mTokenizer->getLocation().string(), - mTokenizer->peekRemainderOfLine().string()); - return BAD_VALUE; - } - } - - mTokenizer->nextLine(); - } - - return NO_ERROR; -} - -bool VirtualKeyMap::Parser::consumeFieldDelimiterAndSkipWhitespace() { - mTokenizer->skipDelimiters(WHITESPACE); - if (mTokenizer->peekChar() == ':') { - mTokenizer->nextChar(); - mTokenizer->skipDelimiters(WHITESPACE); - return true; - } - return false; -} - -bool VirtualKeyMap::Parser::parseNextIntField(int32_t* outValue) { - if (!consumeFieldDelimiterAndSkipWhitespace()) { - return false; - } - - String8 token = mTokenizer->nextToken(WHITESPACE_OR_FIELD_DELIMITER); - char* end; - *outValue = strtol(token.string(), &end, 0); - if (token.isEmpty() || *end != '\0') { - ALOGE("Expected an integer, got '%s'.", token.string()); - return false; - } - return true; -} - -} // namespace android diff --git a/libs/androidfw/ZipFileRO.cpp b/libs/androidfw/ZipFileRO.cpp new file mode 100644 index 0000000..ec5f95c --- /dev/null +++ b/libs/androidfw/ZipFileRO.cpp @@ -0,0 +1,995 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// Read-only access to Zip archives, with minimal heap allocation. +// +#define LOG_TAG "zipro" +//#define LOG_NDEBUG 0 +#include <androidfw/ZipFileRO.h> +#include <utils/Log.h> +#include <utils/Compat.h> +#include <utils/misc.h> +#include <utils/threads.h> + +#include <zlib.h> + +#include <string.h> +#include <fcntl.h> +#include <errno.h> +#include <assert.h> +#include <unistd.h> + +/* + * We must open binary files using open(path, ... | O_BINARY) under Windows. + * Otherwise strange read errors will happen. + */ +#ifndef O_BINARY +# define O_BINARY 0 +#endif + +using namespace android; + +/* + * Zip file constants. + */ +#define kEOCDSignature 0x06054b50 +#define kEOCDLen 22 +#define kEOCDDiskNumber 4 // number of the current disk +#define kEOCDDiskNumberForCD 6 // disk number with the Central Directory +#define kEOCDNumEntries 8 // offset to #of entries in file +#define kEOCDTotalNumEntries 10 // offset to total #of entries in spanned archives +#define kEOCDSize 12 // size of the central directory +#define kEOCDFileOffset 16 // offset to central directory +#define kEOCDCommentSize 20 // offset to the length of the file comment + +#define kMaxCommentLen 65535 // longest possible in ushort +#define kMaxEOCDSearch (kMaxCommentLen + kEOCDLen) + +#define kLFHSignature 0x04034b50 +#define kLFHLen 30 // excluding variable-len fields +#define kLFHGPBFlags 6 // offset to GPB flags +#define kLFHNameLen 26 // offset to filename length +#define kLFHExtraLen 28 // offset to extra length + +#define kCDESignature 0x02014b50 +#define kCDELen 46 // excluding variable-len fields +#define kCDEGPBFlags 8 // offset to GPB flags +#define kCDEMethod 10 // offset to compression method +#define kCDEModWhen 12 // offset to modification timestamp +#define kCDECRC 16 // offset to entry CRC +#define kCDECompLen 20 // offset to compressed length +#define kCDEUncompLen 24 // offset to uncompressed length +#define kCDENameLen 28 // offset to filename length +#define kCDEExtraLen 30 // offset to extra length +#define kCDECommentLen 32 // offset to comment length +#define kCDELocalOffset 42 // offset to local hdr + +/* General Purpose Bit Flag */ +#define kGPFEncryptedFlag (1 << 0) +#define kGPFUnsupportedMask (kGPFEncryptedFlag) + +/* + * The values we return for ZipEntryRO use 0 as an invalid value, so we + * want to adjust the hash table index by a fixed amount. Using a large + * value helps insure that people don't mix & match arguments, e.g. to + * findEntryByIndex(). + */ +#define kZipEntryAdj 10000 + +ZipFileRO::~ZipFileRO() { + free(mHashTable); + if (mDirectoryMap) + mDirectoryMap->release(); + if (mFd >= 0) + TEMP_FAILURE_RETRY(close(mFd)); + if (mFileName) + free(mFileName); +} + +/* + * Convert a ZipEntryRO to a hash table index, verifying that it's in a + * valid range. + */ +int ZipFileRO::entryToIndex(const ZipEntryRO entry) const +{ + long ent = ((intptr_t) entry) - kZipEntryAdj; + if (ent < 0 || ent >= mHashTableSize || mHashTable[ent].name == NULL) { + ALOGW("Invalid ZipEntryRO %p (%ld)\n", entry, ent); + return -1; + } + return ent; +} + + +/* + * Open the specified file read-only. We memory-map the entire thing and + * close the file before returning. + */ +status_t ZipFileRO::open(const char* zipFileName) +{ + int fd = -1; + + assert(mDirectoryMap == NULL); + + /* + * Open and map the specified file. + */ + fd = TEMP_FAILURE_RETRY(::open(zipFileName, O_RDONLY | O_BINARY)); + if (fd < 0) { + ALOGW("Unable to open zip '%s': %s\n", zipFileName, strerror(errno)); + return NAME_NOT_FOUND; + } + + mFileLength = lseek64(fd, 0, SEEK_END); + if (mFileLength < kEOCDLen) { + TEMP_FAILURE_RETRY(close(fd)); + return UNKNOWN_ERROR; + } + + if (mFileName != NULL) { + free(mFileName); + } + mFileName = strdup(zipFileName); + + mFd = fd; + + /* + * Find the Central Directory and store its size and number of entries. + */ + if (!mapCentralDirectory()) { + goto bail; + } + + /* + * Verify Central Directory and create data structures for fast access. + */ + if (!parseZipArchive()) { + goto bail; + } + + return OK; + +bail: + free(mFileName); + mFileName = NULL; + TEMP_FAILURE_RETRY(close(fd)); + return UNKNOWN_ERROR; +} + +/* + * Parse the Zip archive, verifying its contents and initializing internal + * data structures. + */ +bool ZipFileRO::mapCentralDirectory(void) +{ + ssize_t readAmount = kMaxEOCDSearch; + if (readAmount > (ssize_t) mFileLength) + readAmount = mFileLength; + + if (readAmount < kEOCDSize) { + ALOGW("File too short to be a zip file"); + return false; + } + + unsigned char* scanBuf = (unsigned char*) malloc(readAmount); + if (scanBuf == NULL) { + ALOGW("couldn't allocate scanBuf: %s", strerror(errno)); + free(scanBuf); + return false; + } + + /* + * Make sure this is a Zip archive. + */ + if (lseek64(mFd, 0, SEEK_SET) != 0) { + ALOGW("seek to start failed: %s", strerror(errno)); + free(scanBuf); + return false; + } + + ssize_t actual = TEMP_FAILURE_RETRY(read(mFd, scanBuf, sizeof(int32_t))); + if (actual != (ssize_t) sizeof(int32_t)) { + ALOGI("couldn't read first signature from zip archive: %s", strerror(errno)); + free(scanBuf); + return false; + } + + unsigned int header = get4LE(scanBuf); + if (header != kLFHSignature) { + ALOGV("Not a Zip archive (found 0x%08x)\n", header); + free(scanBuf); + return false; + } + + /* + * Perform the traditional EOCD snipe hunt. + * + * We're searching for the End of Central Directory magic number, + * which appears at the start of the EOCD block. It's followed by + * 18 bytes of EOCD stuff and up to 64KB of archive comment. We + * need to read the last part of the file into a buffer, dig through + * it to find the magic number, parse some values out, and use those + * to determine the extent of the CD. + * + * We start by pulling in the last part of the file. + */ + off64_t searchStart = mFileLength - readAmount; + + if (lseek64(mFd, searchStart, SEEK_SET) != searchStart) { + ALOGW("seek %ld failed: %s\n", (long) searchStart, strerror(errno)); + free(scanBuf); + return false; + } + actual = TEMP_FAILURE_RETRY(read(mFd, scanBuf, readAmount)); + if (actual != (ssize_t) readAmount) { + ALOGW("Zip: read " ZD ", expected " ZD ". Failed: %s\n", + (ZD_TYPE) actual, (ZD_TYPE) readAmount, strerror(errno)); + free(scanBuf); + return false; + } + + /* + * Scan backward for the EOCD magic. In an archive without a trailing + * comment, we'll find it on the first try. (We may want to consider + * doing an initial minimal read; if we don't find it, retry with a + * second read as above.) + */ + int i; + for (i = readAmount - kEOCDLen; i >= 0; i--) { + if (scanBuf[i] == 0x50 && get4LE(&scanBuf[i]) == kEOCDSignature) { + ALOGV("+++ Found EOCD at buf+%d\n", i); + break; + } + } + if (i < 0) { + ALOGD("Zip: EOCD not found, %s is not zip\n", mFileName); + free(scanBuf); + return false; + } + + off64_t eocdOffset = searchStart + i; + const unsigned char* eocdPtr = scanBuf + i; + + assert(eocdOffset < mFileLength); + + /* + * Grab the CD offset and size, and the number of entries in the + * archive. After that, we can release our EOCD hunt buffer. + */ + unsigned int diskNumber = get2LE(eocdPtr + kEOCDDiskNumber); + unsigned int diskWithCentralDir = get2LE(eocdPtr + kEOCDDiskNumberForCD); + unsigned int numEntries = get2LE(eocdPtr + kEOCDNumEntries); + unsigned int totalNumEntries = get2LE(eocdPtr + kEOCDTotalNumEntries); + unsigned int centralDirSize = get4LE(eocdPtr + kEOCDSize); + unsigned int centralDirOffset = get4LE(eocdPtr + kEOCDFileOffset); + unsigned int commentSize = get2LE(eocdPtr + kEOCDCommentSize); + free(scanBuf); + + // Verify that they look reasonable. + if ((long long) centralDirOffset + (long long) centralDirSize > (long long) eocdOffset) { + ALOGW("bad offsets (dir %ld, size %u, eocd %ld)\n", + (long) centralDirOffset, centralDirSize, (long) eocdOffset); + return false; + } + if (numEntries == 0) { + ALOGW("empty archive?\n"); + return false; + } else if (numEntries != totalNumEntries || diskNumber != 0 || diskWithCentralDir != 0) { + ALOGW("spanned archives not supported"); + return false; + } + + // Check to see if comment is a sane size + if ((commentSize > (mFileLength - kEOCDLen)) + || (eocdOffset > (mFileLength - kEOCDLen) - commentSize)) { + ALOGW("comment size runs off end of file"); + return false; + } + + ALOGV("+++ numEntries=%d dirSize=%d dirOffset=%d\n", + numEntries, centralDirSize, centralDirOffset); + + mDirectoryMap = new FileMap(); + if (mDirectoryMap == NULL) { + ALOGW("Unable to create directory map: %s", strerror(errno)); + return false; + } + + if (!mDirectoryMap->create(mFileName, mFd, centralDirOffset, centralDirSize, true)) { + ALOGW("Unable to map '%s' (" ZD " to " ZD "): %s\n", mFileName, + (ZD_TYPE) centralDirOffset, (ZD_TYPE) (centralDirOffset + centralDirSize), strerror(errno)); + return false; + } + + mNumEntries = numEntries; + mDirectoryOffset = centralDirOffset; + + return true; +} + + +/* + * Round up to the next highest power of 2. + * + * Found on http://graphics.stanford.edu/~seander/bithacks.html. + */ +static unsigned int roundUpPower2(unsigned int val) +{ + val--; + val |= val >> 1; + val |= val >> 2; + val |= val >> 4; + val |= val >> 8; + val |= val >> 16; + val++; + + return val; +} + +bool ZipFileRO::parseZipArchive(void) +{ + bool result = false; + const unsigned char* cdPtr = (const unsigned char*) mDirectoryMap->getDataPtr(); + size_t cdLength = mDirectoryMap->getDataLength(); + int numEntries = mNumEntries; + + /* + * Create hash table. We have a minimum 75% load factor, possibly as + * low as 50% after we round off to a power of 2. + */ + mHashTableSize = roundUpPower2(1 + (numEntries * 4) / 3); + mHashTable = (HashEntry*) calloc(mHashTableSize, sizeof(HashEntry)); + + /* + * Walk through the central directory, adding entries to the hash + * table. + */ + const unsigned char* ptr = cdPtr; + for (int i = 0; i < numEntries; i++) { + if (get4LE(ptr) != kCDESignature) { + ALOGW("Missed a central dir sig (at %d)\n", i); + goto bail; + } + if (ptr + kCDELen > cdPtr + cdLength) { + ALOGW("Ran off the end (at %d)\n", i); + goto bail; + } + + long localHdrOffset = (long) get4LE(ptr + kCDELocalOffset); + if (localHdrOffset >= mDirectoryOffset) { + ALOGW("bad LFH offset %ld at entry %d\n", localHdrOffset, i); + goto bail; + } + + unsigned int gpbf = get2LE(ptr + kCDEGPBFlags); + if ((gpbf & kGPFUnsupportedMask) != 0) { + ALOGW("Invalid General Purpose Bit Flag: %d", gpbf); + goto bail; + } + + unsigned int nameLen = get2LE(ptr + kCDENameLen); + unsigned int extraLen = get2LE(ptr + kCDEExtraLen); + unsigned int commentLen = get2LE(ptr + kCDECommentLen); + + const char *name = (const char *) ptr + kCDELen; + + /* Check name for NULL characters */ + if (memchr(name, 0, nameLen) != NULL) { + ALOGW("Filename contains NUL byte"); + goto bail; + } + + /* add the CDE filename to the hash table */ + unsigned int hash = computeHash(name, nameLen); + addToHash(name, nameLen, hash); + + /* We don't care about the comment or extra data. */ + ptr += kCDELen + nameLen + extraLen + commentLen; + if ((size_t)(ptr - cdPtr) > cdLength) { + ALOGW("bad CD advance (%d vs " ZD ") at entry %d\n", + (int) (ptr - cdPtr), (ZD_TYPE) cdLength, i); + goto bail; + } + } + ALOGV("+++ zip good scan %d entries\n", numEntries); + result = true; + +bail: + return result; +} + +/* + * Simple string hash function for non-null-terminated strings. + */ +/*static*/ unsigned int ZipFileRO::computeHash(const char* str, int len) +{ + unsigned int hash = 0; + + while (len--) + hash = hash * 31 + *str++; + + return hash; +} + +/* + * Add a new entry to the hash table. + */ +void ZipFileRO::addToHash(const char* str, int strLen, unsigned int hash) +{ + int ent = hash & (mHashTableSize-1); + + /* + * We over-allocate the table, so we're guaranteed to find an empty slot. + */ + while (mHashTable[ent].name != NULL) + ent = (ent + 1) & (mHashTableSize-1); + + mHashTable[ent].name = str; + mHashTable[ent].nameLen = strLen; +} + +/* + * Find a matching entry. + * + * Returns NULL if not found. + */ +ZipEntryRO ZipFileRO::findEntryByName(const char* fileName) const +{ + /* + * If the ZipFileRO instance is not initialized, the entry number will + * end up being garbage since mHashTableSize is -1. + */ + if (mHashTableSize <= 0) { + return NULL; + } + + int nameLen = strlen(fileName); + unsigned int hash = computeHash(fileName, nameLen); + int ent = hash & (mHashTableSize-1); + + while (mHashTable[ent].name != NULL) { + if (mHashTable[ent].nameLen == nameLen && + memcmp(mHashTable[ent].name, fileName, nameLen) == 0) + { + /* match */ + return (ZipEntryRO)(long)(ent + kZipEntryAdj); + } + + ent = (ent + 1) & (mHashTableSize-1); + } + + return NULL; +} + +/* + * Find the Nth entry. + * + * This currently involves walking through the sparse hash table, counting + * non-empty entries. If we need to speed this up we can either allocate + * a parallel lookup table or (perhaps better) provide an iterator interface. + */ +ZipEntryRO ZipFileRO::findEntryByIndex(int idx) const +{ + if (idx < 0 || idx >= mNumEntries) { + ALOGW("Invalid index %d\n", idx); + return NULL; + } + + for (int ent = 0; ent < mHashTableSize; ent++) { + if (mHashTable[ent].name != NULL) { + if (idx-- == 0) + return (ZipEntryRO) (intptr_t)(ent + kZipEntryAdj); + } + } + + return NULL; +} + +/* + * Get the useful fields from the zip entry. + * + * Returns "false" if the offsets to the fields or the contents of the fields + * appear to be bogus. + */ +bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, + size_t* pCompLen, off64_t* pOffset, long* pModWhen, long* pCrc32) const +{ + bool ret = false; + + const int ent = entryToIndex(entry); + if (ent < 0) { + ALOGW("cannot find entry"); + return false; + } + + HashEntry hashEntry = mHashTable[ent]; + + /* + * Recover the start of the central directory entry from the filename + * pointer. The filename is the first entry past the fixed-size data, + * so we can just subtract back from that. + */ + const unsigned char* ptr = (const unsigned char*) hashEntry.name; + off64_t cdOffset = mDirectoryOffset; + + ptr -= kCDELen; + + int method = get2LE(ptr + kCDEMethod); + if (pMethod != NULL) + *pMethod = method; + + if (pModWhen != NULL) + *pModWhen = get4LE(ptr + kCDEModWhen); + if (pCrc32 != NULL) + *pCrc32 = get4LE(ptr + kCDECRC); + + size_t compLen = get4LE(ptr + kCDECompLen); + if (pCompLen != NULL) + *pCompLen = compLen; + size_t uncompLen = get4LE(ptr + kCDEUncompLen); + if (pUncompLen != NULL) + *pUncompLen = uncompLen; + + /* + * If requested, determine the offset of the start of the data. All we + * have is the offset to the Local File Header, which is variable size, + * so we have to read the contents of the struct to figure out where + * the actual data starts. + * + * We also need to make sure that the lengths are not so large that + * somebody trying to map the compressed or uncompressed data runs + * off the end of the mapped region. + * + * Note we don't verify compLen/uncompLen if they don't request the + * dataOffset, because dataOffset is expensive to determine. However, + * if they don't have the file offset, they're not likely to be doing + * anything with the contents. + */ + if (pOffset != NULL) { + long localHdrOffset = get4LE(ptr + kCDELocalOffset); + if (localHdrOffset + kLFHLen >= cdOffset) { + ALOGE("ERROR: bad local hdr offset in zip\n"); + return false; + } + + unsigned char lfhBuf[kLFHLen]; + +#ifdef HAVE_PREAD + /* + * This file descriptor might be from zygote's preloaded assets, + * 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(pread64(mFd, lfhBuf, sizeof(lfhBuf), localHdrOffset)); + + if (actual != sizeof(lfhBuf)) { + ALOGW("failed reading lfh from offset %ld\n", localHdrOffset); + return false; + } + + if (get4LE(lfhBuf) != kLFHSignature) { + ALOGW("didn't find signature at start of lfh; wanted: offset=%ld data=0x%08x; " + "got: data=0x%08lx\n", + localHdrOffset, kLFHSignature, get4LE(lfhBuf)); + return false; + } +#else /* HAVE_PREAD */ + /* + * 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 lseek64() concurrently. + */ + + { + AutoMutex _l(mFdLock); + + if (lseek64(mFd, localHdrOffset, SEEK_SET) != localHdrOffset) { + ALOGW("failed seeking to lfh at offset %ld\n", localHdrOffset); + return false; + } + + ssize_t actual = + TEMP_FAILURE_RETRY(read(mFd, lfhBuf, sizeof(lfhBuf))); + if (actual != sizeof(lfhBuf)) { + ALOGW("failed reading lfh from offset %ld\n", localHdrOffset); + return false; + } + + if (get4LE(lfhBuf) != kLFHSignature) { + off64_t actualOffset = lseek64(mFd, 0, SEEK_CUR); + ALOGW("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)); + return false; + } + } +#endif /* HAVE_PREAD */ + + unsigned int gpbf = get2LE(lfhBuf + kLFHGPBFlags); + if ((gpbf & kGPFUnsupportedMask) != 0) { + ALOGW("Invalid General Purpose Bit Flag: %d", gpbf); + return false; + } + + off64_t dataOffset = localHdrOffset + kLFHLen + + get2LE(lfhBuf + kLFHNameLen) + get2LE(lfhBuf + kLFHExtraLen); + if (dataOffset >= cdOffset) { + ALOGW("bad data offset %ld in zip\n", (long) dataOffset); + return false; + } + + /* check lengths */ + if ((dataOffset >= cdOffset) || (compLen > (cdOffset - dataOffset))) { + ALOGW("bad compressed length in zip (%ld + " ZD " > %ld)\n", + (long) dataOffset, (ZD_TYPE) compLen, (long) cdOffset); + return false; + } + + if (method == kCompressStored && + ((dataOffset >= cdOffset) || + (uncompLen > (cdOffset - dataOffset)))) + { + ALOGE("ERROR: bad uncompressed length in zip (%ld + " ZD " > %ld)\n", + (long) dataOffset, (ZD_TYPE) uncompLen, (long) cdOffset); + return false; + } + + *pOffset = dataOffset; + } + + return true; +} + +/* + * Copy the entry's filename to the buffer. + */ +int ZipFileRO::getEntryFileName(ZipEntryRO entry, char* buffer, int bufLen) + const +{ + int ent = entryToIndex(entry); + if (ent < 0) + return -1; + + int nameLen = mHashTable[ent].nameLen; + if (bufLen < nameLen+1) + return nameLen+1; + + memcpy(buffer, mHashTable[ent].name, nameLen); + buffer[nameLen] = '\0'; + return 0; +} + +/* + * Create a new FileMap object that spans the data in "entry". + */ +FileMap* ZipFileRO::createEntryFileMap(ZipEntryRO entry) const +{ + /* + * TODO: the efficient way to do this is to modify FileMap to allow + * sub-regions of a file to be mapped. A reference-counting scheme + * can manage the base memory mapping. For now, we just create a brand + * new mapping off of the Zip archive file descriptor. + */ + + FileMap* newMap; + int method; + size_t uncompLen; + size_t compLen; + off64_t offset; + + if (!getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL)) { + return NULL; + } + + size_t actualLen; + if (method == kCompressStored) { + actualLen = uncompLen; + } else { + actualLen = compLen; + } + + newMap = new FileMap(); + if (!newMap->create(mFileName, mFd, offset, actualLen, true)) { + newMap->release(); + return NULL; + } + + return newMap; +} + +/* + * Uncompress an entry, in its entirety, into the provided output buffer. + * + * This doesn't verify the data's CRC, which might be useful for + * uncompressed data. The caller should be able to manage it. + */ +bool ZipFileRO::uncompressEntry(ZipEntryRO entry, void* buffer) const +{ + const size_t kSequentialMin = 32768; + bool result = false; + int ent = entryToIndex(entry); + if (ent < 0) { + return false; + } + + int method; + size_t uncompLen, compLen; + off64_t offset; + const unsigned char* ptr; + FileMap *file; + + if (!getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL)) { + goto bail; + } + + file = createEntryFileMap(entry); + if (file == NULL) { + goto bail; + } + + ptr = (const unsigned char*) file->getDataPtr(); + + /* + * Experiment with madvise hint. When we want to uncompress a file, + * we pull some stuff out of the central dir entry and then hit a + * bunch of compressed or uncompressed data sequentially. The CDE + * visit will cause a limited amount of read-ahead because it's at + * the end of the file. We could end up doing lots of extra disk + * access if the file we're prying open is small. Bottom line is we + * probably don't want to turn MADV_SEQUENTIAL on and leave it on. + * + * So, if the compressed size of the file is above a certain minimum + * size, temporarily boost the read-ahead in the hope that the extra + * pair of system calls are negated by a reduction in page faults. + */ + if (compLen > kSequentialMin) + file->advise(FileMap::SEQUENTIAL); + + if (method == kCompressStored) { + memcpy(buffer, ptr, uncompLen); + } else { + if (!inflateBuffer(buffer, ptr, uncompLen, compLen)) + goto unmap; + } + + if (compLen > kSequentialMin) + file->advise(FileMap::NORMAL); + + result = true; + +unmap: + file->release(); +bail: + return result; +} + +/* + * Uncompress an entry, in its entirety, to an open file descriptor. + * + * This doesn't verify the data's CRC, but probably should. + */ +bool ZipFileRO::uncompressEntry(ZipEntryRO entry, int fd) const +{ + bool result = false; + int ent = entryToIndex(entry); + if (ent < 0) { + return false; + } + + int method; + size_t uncompLen, compLen; + off64_t offset; + const unsigned char* ptr; + FileMap *file; + + if (!getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL)) { + goto bail; + } + + file = createEntryFileMap(entry); + if (file == NULL) { + goto bail; + } + + ptr = (const unsigned char*) file->getDataPtr(); + + if (method == kCompressStored) { + ssize_t actual = TEMP_FAILURE_RETRY(write(fd, ptr, uncompLen)); + if (actual < 0) { + ALOGE("Write failed: %s\n", strerror(errno)); + goto unmap; + } else if ((size_t) actual != uncompLen) { + ALOGE("Partial write during uncompress (" ZD " of " ZD ")\n", + (ZD_TYPE) actual, (ZD_TYPE) uncompLen); + goto unmap; + } else { + ALOGI("+++ successful write\n"); + } + } else { + if (!inflateBuffer(fd, ptr, uncompLen, compLen)) { + goto unmap; + } + } + + result = true; + +unmap: + file->release(); +bail: + return result; +} + +/* + * Uncompress "deflate" data from one buffer to another. + */ +/*static*/ bool ZipFileRO::inflateBuffer(void* outBuf, const void* inBuf, + size_t uncompLen, size_t compLen) +{ + bool result = false; + z_stream zstream; + int zerr; + + /* + * Initialize the zlib stream struct. + */ + memset(&zstream, 0, sizeof(zstream)); + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + zstream.next_in = (Bytef*)inBuf; + zstream.avail_in = compLen; + zstream.next_out = (Bytef*) outBuf; + zstream.avail_out = uncompLen; + zstream.data_type = Z_UNKNOWN; + + /* + * Use the undocumented "negative window bits" feature to tell zlib + * that there's no zlib header waiting for it. + */ + zerr = inflateInit2(&zstream, -MAX_WBITS); + if (zerr != Z_OK) { + if (zerr == Z_VERSION_ERROR) { + ALOGE("Installed zlib is not compatible with linked version (%s)\n", + ZLIB_VERSION); + } else { + ALOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); + } + goto bail; + } + + /* + * Expand data. + */ + zerr = inflate(&zstream, Z_FINISH); + if (zerr != Z_STREAM_END) { + ALOGW("Zip inflate failed, zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)\n", + zerr, zstream.next_in, zstream.avail_in, + zstream.next_out, zstream.avail_out); + goto z_bail; + } + + /* paranoia */ + if (zstream.total_out != uncompLen) { + ALOGW("Size mismatch on inflated file (%ld vs " ZD ")\n", + zstream.total_out, (ZD_TYPE) uncompLen); + goto z_bail; + } + + result = true; + +z_bail: + inflateEnd(&zstream); /* free up any allocated structures */ + +bail: + return result; +} + +/* + * Uncompress "deflate" data from one buffer to an open file descriptor. + */ +/*static*/ bool ZipFileRO::inflateBuffer(int fd, const void* inBuf, + size_t uncompLen, size_t compLen) +{ + bool result = false; + const size_t kWriteBufSize = 32768; + unsigned char writeBuf[kWriteBufSize]; + z_stream zstream; + int zerr; + + /* + * Initialize the zlib stream struct. + */ + memset(&zstream, 0, sizeof(zstream)); + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + zstream.next_in = (Bytef*)inBuf; + zstream.avail_in = compLen; + zstream.next_out = (Bytef*) writeBuf; + zstream.avail_out = sizeof(writeBuf); + zstream.data_type = Z_UNKNOWN; + + /* + * Use the undocumented "negative window bits" feature to tell zlib + * that there's no zlib header waiting for it. + */ + zerr = inflateInit2(&zstream, -MAX_WBITS); + if (zerr != Z_OK) { + if (zerr == Z_VERSION_ERROR) { + ALOGE("Installed zlib is not compatible with linked version (%s)\n", + ZLIB_VERSION); + } else { + ALOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); + } + goto bail; + } + + /* + * Loop while we have more to do. + */ + do { + /* + * Expand data. + */ + zerr = inflate(&zstream, Z_NO_FLUSH); + if (zerr != Z_OK && zerr != Z_STREAM_END) { + ALOGW("zlib inflate: zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)\n", + zerr, zstream.next_in, zstream.avail_in, + zstream.next_out, zstream.avail_out); + goto z_bail; + } + + /* write when we're full or when we're done */ + if (zstream.avail_out == 0 || + (zerr == Z_STREAM_END && zstream.avail_out != sizeof(writeBuf))) + { + long writeSize = zstream.next_out - writeBuf; + int cc = TEMP_FAILURE_RETRY(write(fd, writeBuf, writeSize)); + if (cc < 0) { + ALOGW("write failed in inflate: %s", strerror(errno)); + goto z_bail; + } else if (cc != (int) writeSize) { + ALOGW("write failed in inflate (%d vs %ld)", cc, writeSize); + goto z_bail; + } + + zstream.next_out = writeBuf; + zstream.avail_out = sizeof(writeBuf); + } + } while (zerr == Z_OK); + + assert(zerr == Z_STREAM_END); /* other errors should've been caught */ + + /* paranoia */ + if (zstream.total_out != uncompLen) { + ALOGW("Size mismatch on inflated file (%ld vs " ZD ")\n", + zstream.total_out, (ZD_TYPE) uncompLen); + goto z_bail; + } + + result = true; + +z_bail: + inflateEnd(&zstream); /* free up any allocated structures */ + +bail: + return result; +} diff --git a/libs/androidfw/ZipUtils.cpp b/libs/androidfw/ZipUtils.cpp new file mode 100644 index 0000000..997eb7d --- /dev/null +++ b/libs/androidfw/ZipUtils.cpp @@ -0,0 +1,345 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// Misc zip/gzip utility functions. +// + +#define LOG_TAG "ziputil" + +#include <androidfw/ZipUtils.h> +#include <androidfw/ZipFileRO.h> +#include <utils/Log.h> +#include <utils/Compat.h> + +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#include <zlib.h> + +using namespace android; + +/* + * Utility function that expands zip/gzip "deflate" compressed data + * into a buffer. + * + * "fd" is an open file positioned at the start of the "deflate" data + * "buf" must hold at least "uncompressedLen" bytes. + */ +/*static*/ bool ZipUtils::inflateToBuffer(int fd, void* buf, + long uncompressedLen, long compressedLen) +{ + bool result = false; + const unsigned long kReadBufSize = 32768; + unsigned char* readBuf = NULL; + z_stream zstream; + int zerr; + unsigned long compRemaining; + + assert(uncompressedLen >= 0); + assert(compressedLen >= 0); + + readBuf = new unsigned char[kReadBufSize]; + if (readBuf == NULL) + goto bail; + compRemaining = compressedLen; + + /* + * Initialize the zlib stream. + */ + memset(&zstream, 0, sizeof(zstream)); + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + zstream.next_in = NULL; + zstream.avail_in = 0; + zstream.next_out = (Bytef*) buf; + zstream.avail_out = uncompressedLen; + zstream.data_type = Z_UNKNOWN; + + /* + * Use the undocumented "negative window bits" feature to tell zlib + * that there's no zlib header waiting for it. + */ + zerr = inflateInit2(&zstream, -MAX_WBITS); + if (zerr != Z_OK) { + if (zerr == Z_VERSION_ERROR) { + ALOGE("Installed zlib is not compatible with linked version (%s)\n", + ZLIB_VERSION); + } else { + ALOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); + } + goto bail; + } + + /* + * Loop while we have data. + */ + do { + unsigned long getSize; + + /* read as much as we can */ + if (zstream.avail_in == 0) { + getSize = (compRemaining > kReadBufSize) ? + kReadBufSize : compRemaining; + ALOGV("+++ reading %ld bytes (%ld left)\n", + getSize, compRemaining); + + int cc = TEMP_FAILURE_RETRY(read(fd, readBuf, getSize)); + if (cc < 0) { + ALOGW("inflate read failed: %s", strerror(errno)); + } else if (cc != (int) getSize) { + ALOGW("inflate read failed (%d vs %ld)", cc, getSize); + goto z_bail; + } + + compRemaining -= getSize; + + zstream.next_in = readBuf; + zstream.avail_in = getSize; + } + + /* uncompress the data */ + zerr = inflate(&zstream, Z_NO_FLUSH); + if (zerr != Z_OK && zerr != Z_STREAM_END) { + ALOGD("zlib inflate call failed (zerr=%d)\n", zerr); + goto z_bail; + } + + /* output buffer holds all, so no need to write the output */ + } while (zerr == Z_OK); + + assert(zerr == Z_STREAM_END); /* other errors should've been caught */ + + if ((long) zstream.total_out != uncompressedLen) { + ALOGW("Size mismatch on inflated file (%ld vs %ld)\n", + zstream.total_out, uncompressedLen); + goto z_bail; + } + + // success! + result = true; + +z_bail: + inflateEnd(&zstream); /* free up any allocated structures */ + +bail: + delete[] readBuf; + return result; +} + +/* + * Utility function that expands zip/gzip "deflate" compressed data + * into a buffer. + * + * (This is a clone of the previous function, but it takes a FILE* instead + * of an fd. We could pass fileno(fd) to the above, but we can run into + * trouble when "fp" has a different notion of what fd's file position is.) + * + * "fp" is an open file positioned at the start of the "deflate" data + * "buf" must hold at least "uncompressedLen" bytes. + */ +/*static*/ bool ZipUtils::inflateToBuffer(FILE* fp, void* buf, + long uncompressedLen, long compressedLen) +{ + bool result = false; + const unsigned long kReadBufSize = 32768; + unsigned char* readBuf = NULL; + z_stream zstream; + int zerr; + unsigned long compRemaining; + + assert(uncompressedLen >= 0); + assert(compressedLen >= 0); + + readBuf = new unsigned char[kReadBufSize]; + if (readBuf == NULL) + goto bail; + compRemaining = compressedLen; + + /* + * Initialize the zlib stream. + */ + memset(&zstream, 0, sizeof(zstream)); + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + zstream.next_in = NULL; + zstream.avail_in = 0; + zstream.next_out = (Bytef*) buf; + zstream.avail_out = uncompressedLen; + zstream.data_type = Z_UNKNOWN; + + /* + * Use the undocumented "negative window bits" feature to tell zlib + * that there's no zlib header waiting for it. + */ + zerr = inflateInit2(&zstream, -MAX_WBITS); + if (zerr != Z_OK) { + if (zerr == Z_VERSION_ERROR) { + ALOGE("Installed zlib is not compatible with linked version (%s)\n", + ZLIB_VERSION); + } else { + ALOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); + } + goto bail; + } + + /* + * Loop while we have data. + */ + do { + unsigned long getSize; + + /* read as much as we can */ + if (zstream.avail_in == 0) { + getSize = (compRemaining > kReadBufSize) ? + kReadBufSize : compRemaining; + ALOGV("+++ reading %ld bytes (%ld left)\n", + getSize, compRemaining); + + int cc = fread(readBuf, 1, getSize, fp); + if (cc != (int) getSize) { + ALOGD("inflate read failed (%d vs %ld)\n", + cc, getSize); + goto z_bail; + } + + compRemaining -= getSize; + + zstream.next_in = readBuf; + zstream.avail_in = getSize; + } + + /* uncompress the data */ + zerr = inflate(&zstream, Z_NO_FLUSH); + if (zerr != Z_OK && zerr != Z_STREAM_END) { + ALOGD("zlib inflate call failed (zerr=%d)\n", zerr); + goto z_bail; + } + + /* output buffer holds all, so no need to write the output */ + } while (zerr == Z_OK); + + assert(zerr == Z_STREAM_END); /* other errors should've been caught */ + + if ((long) zstream.total_out != uncompressedLen) { + ALOGW("Size mismatch on inflated file (%ld vs %ld)\n", + zstream.total_out, uncompressedLen); + goto z_bail; + } + + // success! + result = true; + +z_bail: + inflateEnd(&zstream); /* free up any allocated structures */ + +bail: + delete[] readBuf; + return result; +} + +/* + * Look at the contents of a gzip archive. We want to know where the + * data starts, and how long it will be after it is uncompressed. + * + * We expect to find the CRC and length as the last 8 bytes on the file. + * This is a pretty reasonable thing to expect for locally-compressed + * files, but there's a small chance that some extra padding got thrown + * on (the man page talks about compressed data written to tape). We + * don't currently deal with that here. If "gzip -l" whines, we're going + * to fail too. + * + * On exit, "fp" is pointing at the start of the compressed data. + */ +/*static*/ bool ZipUtils::examineGzip(FILE* fp, int* pCompressionMethod, + long* pUncompressedLen, long* pCompressedLen, unsigned long* pCRC32) +{ + enum { // flags + FTEXT = 0x01, + FHCRC = 0x02, + FEXTRA = 0x04, + FNAME = 0x08, + FCOMMENT = 0x10, + }; + int ic; + int method, flags; + int i; + + ic = getc(fp); + if (ic != 0x1f || getc(fp) != 0x8b) + return false; // not gzip + method = getc(fp); + flags = getc(fp); + + /* quick sanity checks */ + if (method == EOF || flags == EOF) + return false; + if (method != ZipFileRO::kCompressDeflated) + return false; + + /* skip over 4 bytes of mod time, 1 byte XFL, 1 byte OS */ + for (i = 0; i < 6; i++) + (void) getc(fp); + /* consume "extra" field, if present */ + if ((flags & FEXTRA) != 0) { + int len; + + len = getc(fp); + len |= getc(fp) << 8; + while (len-- && getc(fp) != EOF) + ; + } + /* consume filename, if present */ + if ((flags & FNAME) != 0) { + do { + ic = getc(fp); + } while (ic != 0 && ic != EOF); + } + /* consume comment, if present */ + if ((flags & FCOMMENT) != 0) { + do { + ic = getc(fp); + } while (ic != 0 && ic != EOF); + } + /* consume 16-bit header CRC, if present */ + if ((flags & FHCRC) != 0) { + (void) getc(fp); + (void) getc(fp); + } + + if (feof(fp) || ferror(fp)) + return false; + + /* seek to the end; CRC and length are in the last 8 bytes */ + long curPosn = ftell(fp); + unsigned char buf[8]; + fseek(fp, -8, SEEK_END); + *pCompressedLen = ftell(fp) - curPosn; + + if (fread(buf, 1, 8, fp) != 8) + return false; + /* seek back to start of compressed data */ + fseek(fp, curPosn, SEEK_SET); + + *pCompressionMethod = method; + *pCRC32 = ZipFileRO::get4LE(&buf[0]); + *pUncompressedLen = ZipFileRO::get4LE(&buf[4]); + + return true; +} diff --git a/libs/androidfw/misc.cpp b/libs/androidfw/misc.cpp new file mode 100644 index 0000000..29686ef --- /dev/null +++ b/libs/androidfw/misc.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2005 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "misc" + +// +// Miscellaneous utility functions. +// +#include <androidfw/misc.h> + +#include <sys/stat.h> +#include <string.h> +#include <errno.h> +#include <stdio.h> + +using namespace android; + +namespace android { + +/* + * Get a file's type. + */ +FileType getFileType(const char* fileName) +{ + struct stat sb; + + if (stat(fileName, &sb) < 0) { + if (errno == ENOENT || errno == ENOTDIR) + return kFileTypeNonexistent; + else { + fprintf(stderr, "getFileType got errno=%d on '%s'\n", + errno, fileName); + return kFileTypeUnknown; + } + } else { + if (S_ISREG(sb.st_mode)) + return kFileTypeRegular; + else if (S_ISDIR(sb.st_mode)) + return kFileTypeDirectory; + else if (S_ISCHR(sb.st_mode)) + return kFileTypeCharDev; + else if (S_ISBLK(sb.st_mode)) + return kFileTypeBlockDev; + else if (S_ISFIFO(sb.st_mode)) + return kFileTypeFifo; +#ifdef HAVE_SYMLINKS + else if (S_ISLNK(sb.st_mode)) + return kFileTypeSymlink; + else if (S_ISSOCK(sb.st_mode)) + return kFileTypeSocket; +#endif + else + return kFileTypeUnknown; + } +} + +/* + * Get a file's modification date. + */ +time_t getFileModDate(const char* fileName) +{ + struct stat sb; + + if (stat(fileName, &sb) < 0) + return (time_t) -1; + + return sb.st_mtime; +} + +}; // namespace android diff --git a/libs/androidfw/tests/Android.mk b/libs/androidfw/tests/Android.mk index 4ae23ec..0522212 100644 --- a/libs/androidfw/tests/Android.mk +++ b/libs/androidfw/tests/Android.mk @@ -4,19 +4,15 @@ include $(CLEAR_VARS) # Build the unit tests. test_src_files := \ - InputChannel_test.cpp \ - InputEvent_test.cpp \ - InputPublisherAndConsumer_test.cpp \ - ObbFile_test.cpp + ObbFile_test.cpp \ + ZipFileRO_test.cpp shared_libraries := \ libandroidfw \ libcutils \ libutils \ - libbinder \ libui \ - libstlport \ - libskia + libstlport static_libraries := \ libgtest \ diff --git a/libs/androidfw/tests/InputChannel_test.cpp b/libs/androidfw/tests/InputChannel_test.cpp deleted file mode 100644 index 7fff8af..0000000 --- a/libs/androidfw/tests/InputChannel_test.cpp +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <androidfw/InputTransport.h> -#include <utils/Timers.h> -#include <utils/StopWatch.h> -#include <utils/StrongPointer.h> -#include <gtest/gtest.h> -#include <unistd.h> -#include <time.h> -#include <errno.h> - -#include "TestHelpers.h" - -namespace android { - -class InputChannelTest : public testing::Test { -protected: - virtual void SetUp() { } - virtual void TearDown() { } -}; - - -TEST_F(InputChannelTest, ConstructorAndDestructor_TakesOwnershipOfFileDescriptors) { - // Our purpose here is to verify that the input channel destructor closes the - // file descriptor provided to it. One easy way is to provide it with one end - // of a pipe and to check for EPIPE on the other end after the channel is destroyed. - Pipe pipe; - - sp<InputChannel> inputChannel = new InputChannel(String8("channel name"), pipe.sendFd); - - EXPECT_STREQ("channel name", inputChannel->getName().string()) - << "channel should have provided name"; - EXPECT_EQ(pipe.sendFd, inputChannel->getFd()) - << "channel should have provided fd"; - - inputChannel.clear(); // destroys input channel - - EXPECT_EQ(-EPIPE, pipe.readSignal()) - << "channel should have closed fd when destroyed"; - - // clean up fds of Pipe endpoints that were closed so we don't try to close them again - pipe.sendFd = -1; -} - -TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) { - sp<InputChannel> serverChannel, clientChannel; - - status_t result = InputChannel::openInputChannelPair(String8("channel name"), - serverChannel, clientChannel); - - ASSERT_EQ(OK, result) - << "should have successfully opened a channel pair"; - - // Name - EXPECT_STREQ("channel name (server)", serverChannel->getName().string()) - << "server channel should have suffixed name"; - EXPECT_STREQ("channel name (client)", clientChannel->getName().string()) - << "client channel should have suffixed name"; - - // Server->Client communication - InputMessage serverMsg; - memset(&serverMsg, 0, sizeof(InputMessage)); - serverMsg.header.type = InputMessage::TYPE_KEY; - serverMsg.body.key.action = AKEY_EVENT_ACTION_DOWN; - EXPECT_EQ(OK, serverChannel->sendMessage(&serverMsg)) - << "server channel should be able to send message to client channel"; - - InputMessage clientMsg; - EXPECT_EQ(OK, clientChannel->receiveMessage(&clientMsg)) - << "client channel should be able to receive message from server channel"; - EXPECT_EQ(serverMsg.header.type, clientMsg.header.type) - << "client channel should receive the correct message from server channel"; - EXPECT_EQ(serverMsg.body.key.action, clientMsg.body.key.action) - << "client channel should receive the correct message from server channel"; - - // Client->Server communication - InputMessage clientReply; - memset(&clientReply, 0, sizeof(InputMessage)); - clientReply.header.type = InputMessage::TYPE_FINISHED; - clientReply.body.finished.seq = 0x11223344; - clientReply.body.finished.handled = true; - EXPECT_EQ(OK, clientChannel->sendMessage(&clientReply)) - << "client channel should be able to send message to server channel"; - - InputMessage serverReply; - EXPECT_EQ(OK, serverChannel->receiveMessage(&serverReply)) - << "server channel should be able to receive message from client channel"; - EXPECT_EQ(clientReply.header.type, serverReply.header.type) - << "server channel should receive the correct message from client channel"; - EXPECT_EQ(clientReply.body.finished.seq, serverReply.body.finished.seq) - << "server channel should receive the correct message from client channel"; - EXPECT_EQ(clientReply.body.finished.handled, serverReply.body.finished.handled) - << "server channel should receive the correct message from client channel"; -} - -TEST_F(InputChannelTest, ReceiveSignal_WhenNoSignalPresent_ReturnsAnError) { - sp<InputChannel> serverChannel, clientChannel; - - status_t result = InputChannel::openInputChannelPair(String8("channel name"), - serverChannel, clientChannel); - - ASSERT_EQ(OK, result) - << "should have successfully opened a channel pair"; - - InputMessage msg; - EXPECT_EQ(WOULD_BLOCK, clientChannel->receiveMessage(&msg)) - << "receiveMessage should have returned WOULD_BLOCK"; -} - -TEST_F(InputChannelTest, ReceiveSignal_WhenPeerClosed_ReturnsAnError) { - sp<InputChannel> serverChannel, clientChannel; - - status_t result = InputChannel::openInputChannelPair(String8("channel name"), - serverChannel, clientChannel); - - ASSERT_EQ(OK, result) - << "should have successfully opened a channel pair"; - - serverChannel.clear(); // close server channel - - InputMessage msg; - EXPECT_EQ(DEAD_OBJECT, clientChannel->receiveMessage(&msg)) - << "receiveMessage should have returned DEAD_OBJECT"; -} - -TEST_F(InputChannelTest, SendSignal_WhenPeerClosed_ReturnsAnError) { - sp<InputChannel> serverChannel, clientChannel; - - status_t result = InputChannel::openInputChannelPair(String8("channel name"), - serverChannel, clientChannel); - - ASSERT_EQ(OK, result) - << "should have successfully opened a channel pair"; - - serverChannel.clear(); // close server channel - - InputMessage msg; - msg.header.type = InputMessage::TYPE_KEY; - EXPECT_EQ(DEAD_OBJECT, clientChannel->sendMessage(&msg)) - << "sendMessage should have returned DEAD_OBJECT"; -} - - -} // namespace android diff --git a/libs/androidfw/tests/InputEvent_test.cpp b/libs/androidfw/tests/InputEvent_test.cpp deleted file mode 100644 index e9164d1..0000000 --- a/libs/androidfw/tests/InputEvent_test.cpp +++ /dev/null @@ -1,581 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <androidfw/Input.h> -#include <gtest/gtest.h> -#include <binder/Parcel.h> - -#include <math.h> -#include <core/SkMatrix.h> - -namespace android { - -class BaseTest : public testing::Test { -protected: - virtual void SetUp() { } - virtual void TearDown() { } -}; - -// --- PointerCoordsTest --- - -class PointerCoordsTest : public BaseTest { -}; - -TEST_F(PointerCoordsTest, ClearSetsBitsToZero) { - PointerCoords coords; - coords.clear(); - - ASSERT_EQ(0ULL, coords.bits); -} - -TEST_F(PointerCoordsTest, AxisValues) { - float* valuePtr; - PointerCoords coords; - coords.clear(); - - // Check invariants when no axes are present. - ASSERT_EQ(0, coords.getAxisValue(0)) - << "getAxisValue should return zero because axis is not present"; - ASSERT_EQ(0, coords.getAxisValue(1)) - << "getAxisValue should return zero because axis is not present"; - - // Set first axis. - ASSERT_EQ(OK, coords.setAxisValue(1, 5)); - ASSERT_EQ(0x00000002ULL, coords.bits); - ASSERT_EQ(5, coords.values[0]); - - ASSERT_EQ(0, coords.getAxisValue(0)) - << "getAxisValue should return zero because axis is not present"; - ASSERT_EQ(5, coords.getAxisValue(1)) - << "getAxisValue should return value of axis"; - - // Set an axis with a higher id than all others. (appending value at the end) - ASSERT_EQ(OK, coords.setAxisValue(3, 2)); - ASSERT_EQ(0x0000000aULL, coords.bits); - ASSERT_EQ(5, coords.values[0]); - ASSERT_EQ(2, coords.values[1]); - - ASSERT_EQ(0, coords.getAxisValue(0)) - << "getAxisValue should return zero because axis is not present"; - ASSERT_EQ(5, coords.getAxisValue(1)) - << "getAxisValue should return value of axis"; - ASSERT_EQ(0, coords.getAxisValue(2)) - << "getAxisValue should return zero because axis is not present"; - ASSERT_EQ(2, coords.getAxisValue(3)) - << "getAxisValue should return value of axis"; - - // Set an axis with an id lower than all others. (prepending value at beginning) - ASSERT_EQ(OK, coords.setAxisValue(0, 4)); - ASSERT_EQ(0x0000000bULL, coords.bits); - ASSERT_EQ(4, coords.values[0]); - ASSERT_EQ(5, coords.values[1]); - ASSERT_EQ(2, coords.values[2]); - - ASSERT_EQ(4, coords.getAxisValue(0)) - << "getAxisValue should return value of axis"; - ASSERT_EQ(5, coords.getAxisValue(1)) - << "getAxisValue should return value of axis"; - ASSERT_EQ(0, coords.getAxisValue(2)) - << "getAxisValue should return zero because axis is not present"; - ASSERT_EQ(2, coords.getAxisValue(3)) - << "getAxisValue should return value of axis"; - - // Set an axis with an id between the others. (inserting value in the middle) - ASSERT_EQ(OK, coords.setAxisValue(2, 1)); - ASSERT_EQ(0x0000000fULL, coords.bits); - ASSERT_EQ(4, coords.values[0]); - ASSERT_EQ(5, coords.values[1]); - ASSERT_EQ(1, coords.values[2]); - ASSERT_EQ(2, coords.values[3]); - - ASSERT_EQ(4, coords.getAxisValue(0)) - << "getAxisValue should return value of axis"; - ASSERT_EQ(5, coords.getAxisValue(1)) - << "getAxisValue should return value of axis"; - ASSERT_EQ(1, coords.getAxisValue(2)) - << "getAxisValue should return value of axis"; - ASSERT_EQ(2, coords.getAxisValue(3)) - << "getAxisValue should return value of axis"; - - // Set an existing axis value in place. - ASSERT_EQ(OK, coords.setAxisValue(1, 6)); - ASSERT_EQ(0x0000000fULL, coords.bits); - ASSERT_EQ(4, coords.values[0]); - ASSERT_EQ(6, coords.values[1]); - ASSERT_EQ(1, coords.values[2]); - ASSERT_EQ(2, coords.values[3]); - - ASSERT_EQ(4, coords.getAxisValue(0)) - << "getAxisValue should return value of axis"; - ASSERT_EQ(6, coords.getAxisValue(1)) - << "getAxisValue should return value of axis"; - ASSERT_EQ(1, coords.getAxisValue(2)) - << "getAxisValue should return value of axis"; - ASSERT_EQ(2, coords.getAxisValue(3)) - << "getAxisValue should return value of axis"; - - // Set maximum number of axes. - for (size_t axis = 4; axis < PointerCoords::MAX_AXES; axis++) { - ASSERT_EQ(OK, coords.setAxisValue(axis, axis)); - } - ASSERT_EQ(PointerCoords::MAX_AXES, __builtin_popcountll(coords.bits)); - - // Try to set one more axis beyond maximum number. - // Ensure bits are unchanged. - ASSERT_EQ(NO_MEMORY, coords.setAxisValue(PointerCoords::MAX_AXES, 100)); - ASSERT_EQ(PointerCoords::MAX_AXES, __builtin_popcountll(coords.bits)); -} - -TEST_F(PointerCoordsTest, Parcel) { - Parcel parcel; - - PointerCoords inCoords; - inCoords.clear(); - PointerCoords outCoords; - - // Round trip with empty coords. - inCoords.writeToParcel(&parcel); - parcel.setDataPosition(0); - outCoords.readFromParcel(&parcel); - - ASSERT_EQ(0ULL, outCoords.bits); - - // Round trip with some values. - parcel.freeData(); - inCoords.setAxisValue(2, 5); - inCoords.setAxisValue(5, 8); - - inCoords.writeToParcel(&parcel); - parcel.setDataPosition(0); - outCoords.readFromParcel(&parcel); - - ASSERT_EQ(outCoords.bits, inCoords.bits); - ASSERT_EQ(outCoords.values[0], inCoords.values[0]); - ASSERT_EQ(outCoords.values[1], inCoords.values[1]); -} - - -// --- KeyEventTest --- - -class KeyEventTest : public BaseTest { -}; - -TEST_F(KeyEventTest, Properties) { - KeyEvent event; - - // Initialize and get properties. - const nsecs_t ARBITRARY_DOWN_TIME = 1; - const nsecs_t ARBITRARY_EVENT_TIME = 2; - event.initialize(2, AINPUT_SOURCE_GAMEPAD, AKEY_EVENT_ACTION_DOWN, - AKEY_EVENT_FLAG_FROM_SYSTEM, AKEYCODE_BUTTON_X, 121, - AMETA_ALT_ON, 1, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME); - - ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, event.getType()); - ASSERT_EQ(2, event.getDeviceId()); - ASSERT_EQ(AINPUT_SOURCE_GAMEPAD, event.getSource()); - ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, event.getAction()); - ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, event.getFlags()); - ASSERT_EQ(AKEYCODE_BUTTON_X, event.getKeyCode()); - ASSERT_EQ(121, event.getScanCode()); - ASSERT_EQ(AMETA_ALT_ON, event.getMetaState()); - ASSERT_EQ(1, event.getRepeatCount()); - ASSERT_EQ(ARBITRARY_DOWN_TIME, event.getDownTime()); - ASSERT_EQ(ARBITRARY_EVENT_TIME, event.getEventTime()); - - // Set source. - event.setSource(AINPUT_SOURCE_JOYSTICK); - ASSERT_EQ(AINPUT_SOURCE_JOYSTICK, event.getSource()); -} - - -// --- MotionEventTest --- - -class MotionEventTest : public BaseTest { -protected: - static const nsecs_t ARBITRARY_DOWN_TIME; - static const nsecs_t ARBITRARY_EVENT_TIME; - static const float X_OFFSET; - static const float Y_OFFSET; - - void initializeEventWithHistory(MotionEvent* event); - void assertEqualsEventWithHistory(const MotionEvent* event); -}; - -const nsecs_t MotionEventTest::ARBITRARY_DOWN_TIME = 1; -const nsecs_t MotionEventTest::ARBITRARY_EVENT_TIME = 2; -const float MotionEventTest::X_OFFSET = 1.0f; -const float MotionEventTest::Y_OFFSET = 1.1f; - -void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { - PointerProperties pointerProperties[2]; - pointerProperties[0].clear(); - pointerProperties[0].id = 1; - pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; - pointerProperties[1].clear(); - pointerProperties[1].id = 2; - pointerProperties[1].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; - - PointerCoords pointerCoords[2]; - pointerCoords[0].clear(); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 10); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 11); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 12); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 13); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 14); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 15); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 16); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 17); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 18); - pointerCoords[1].clear(); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 20); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 21); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 22); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 23); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 24); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 25); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 26); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 27); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 28); - event->initialize(2, AINPUT_SOURCE_TOUCHSCREEN, AMOTION_EVENT_ACTION_MOVE, - AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, - AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY, - X_OFFSET, Y_OFFSET, 2.0f, 2.1f, - ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, - 2, pointerProperties, pointerCoords); - - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 110); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 111); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 112); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 113); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 114); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 115); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 116); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 117); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 118); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 120); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 121); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 122); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 123); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 124); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 125); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 126); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 127); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 128); - event->addSample(ARBITRARY_EVENT_TIME + 1, pointerCoords); - - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 210); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 211); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 212); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 213); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 214); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 215); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 216); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 217); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 218); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 220); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 221); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 222); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 223); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 224); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 225); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 226); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 227); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 228); - event->addSample(ARBITRARY_EVENT_TIME + 2, pointerCoords); -} - -void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) { - // Check properties. - ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType()); - ASSERT_EQ(2, event->getDeviceId()); - ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, event->getSource()); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, event->getAction()); - ASSERT_EQ(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, event->getFlags()); - ASSERT_EQ(AMOTION_EVENT_EDGE_FLAG_TOP, event->getEdgeFlags()); - ASSERT_EQ(AMETA_ALT_ON, event->getMetaState()); - ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, event->getButtonState()); - ASSERT_EQ(X_OFFSET, event->getXOffset()); - ASSERT_EQ(Y_OFFSET, event->getYOffset()); - ASSERT_EQ(2.0f, event->getXPrecision()); - ASSERT_EQ(2.1f, event->getYPrecision()); - ASSERT_EQ(ARBITRARY_DOWN_TIME, event->getDownTime()); - - ASSERT_EQ(2U, event->getPointerCount()); - ASSERT_EQ(1, event->getPointerId(0)); - ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, event->getToolType(0)); - ASSERT_EQ(2, event->getPointerId(1)); - ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_STYLUS, event->getToolType(1)); - - ASSERT_EQ(2U, event->getHistorySize()); - - // Check data. - ASSERT_EQ(ARBITRARY_EVENT_TIME, event->getHistoricalEventTime(0)); - ASSERT_EQ(ARBITRARY_EVENT_TIME + 1, event->getHistoricalEventTime(1)); - ASSERT_EQ(ARBITRARY_EVENT_TIME + 2, event->getEventTime()); - - ASSERT_EQ(11, event->getHistoricalRawPointerCoords(0, 0)-> - getAxisValue(AMOTION_EVENT_AXIS_Y)); - ASSERT_EQ(21, event->getHistoricalRawPointerCoords(1, 0)-> - getAxisValue(AMOTION_EVENT_AXIS_Y)); - ASSERT_EQ(111, event->getHistoricalRawPointerCoords(0, 1)-> - getAxisValue(AMOTION_EVENT_AXIS_Y)); - ASSERT_EQ(121, event->getHistoricalRawPointerCoords(1, 1)-> - getAxisValue(AMOTION_EVENT_AXIS_Y)); - ASSERT_EQ(211, event->getRawPointerCoords(0)-> - getAxisValue(AMOTION_EVENT_AXIS_Y)); - ASSERT_EQ(221, event->getRawPointerCoords(1)-> - getAxisValue(AMOTION_EVENT_AXIS_Y)); - - ASSERT_EQ(11, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 0)); - ASSERT_EQ(21, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 0)); - ASSERT_EQ(111, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 1)); - ASSERT_EQ(121, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 1)); - ASSERT_EQ(211, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 0)); - ASSERT_EQ(221, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 1)); - - ASSERT_EQ(10, event->getHistoricalRawX(0, 0)); - ASSERT_EQ(20, event->getHistoricalRawX(1, 0)); - ASSERT_EQ(110, event->getHistoricalRawX(0, 1)); - ASSERT_EQ(120, event->getHistoricalRawX(1, 1)); - ASSERT_EQ(210, event->getRawX(0)); - ASSERT_EQ(220, event->getRawX(1)); - - ASSERT_EQ(11, event->getHistoricalRawY(0, 0)); - ASSERT_EQ(21, event->getHistoricalRawY(1, 0)); - ASSERT_EQ(111, event->getHistoricalRawY(0, 1)); - ASSERT_EQ(121, event->getHistoricalRawY(1, 1)); - ASSERT_EQ(211, event->getRawY(0)); - ASSERT_EQ(221, event->getRawY(1)); - - ASSERT_EQ(X_OFFSET + 10, event->getHistoricalX(0, 0)); - ASSERT_EQ(X_OFFSET + 20, event->getHistoricalX(1, 0)); - ASSERT_EQ(X_OFFSET + 110, event->getHistoricalX(0, 1)); - ASSERT_EQ(X_OFFSET + 120, event->getHistoricalX(1, 1)); - ASSERT_EQ(X_OFFSET + 210, event->getX(0)); - ASSERT_EQ(X_OFFSET + 220, event->getX(1)); - - ASSERT_EQ(Y_OFFSET + 11, event->getHistoricalY(0, 0)); - ASSERT_EQ(Y_OFFSET + 21, event->getHistoricalY(1, 0)); - ASSERT_EQ(Y_OFFSET + 111, event->getHistoricalY(0, 1)); - ASSERT_EQ(Y_OFFSET + 121, event->getHistoricalY(1, 1)); - ASSERT_EQ(Y_OFFSET + 211, event->getY(0)); - ASSERT_EQ(Y_OFFSET + 221, event->getY(1)); - - ASSERT_EQ(12, event->getHistoricalPressure(0, 0)); - ASSERT_EQ(22, event->getHistoricalPressure(1, 0)); - ASSERT_EQ(112, event->getHistoricalPressure(0, 1)); - ASSERT_EQ(122, event->getHistoricalPressure(1, 1)); - ASSERT_EQ(212, event->getPressure(0)); - ASSERT_EQ(222, event->getPressure(1)); - - ASSERT_EQ(13, event->getHistoricalSize(0, 0)); - ASSERT_EQ(23, event->getHistoricalSize(1, 0)); - ASSERT_EQ(113, event->getHistoricalSize(0, 1)); - ASSERT_EQ(123, event->getHistoricalSize(1, 1)); - ASSERT_EQ(213, event->getSize(0)); - ASSERT_EQ(223, event->getSize(1)); - - ASSERT_EQ(14, event->getHistoricalTouchMajor(0, 0)); - ASSERT_EQ(24, event->getHistoricalTouchMajor(1, 0)); - ASSERT_EQ(114, event->getHistoricalTouchMajor(0, 1)); - ASSERT_EQ(124, event->getHistoricalTouchMajor(1, 1)); - ASSERT_EQ(214, event->getTouchMajor(0)); - ASSERT_EQ(224, event->getTouchMajor(1)); - - ASSERT_EQ(15, event->getHistoricalTouchMinor(0, 0)); - ASSERT_EQ(25, event->getHistoricalTouchMinor(1, 0)); - ASSERT_EQ(115, event->getHistoricalTouchMinor(0, 1)); - ASSERT_EQ(125, event->getHistoricalTouchMinor(1, 1)); - ASSERT_EQ(215, event->getTouchMinor(0)); - ASSERT_EQ(225, event->getTouchMinor(1)); - - ASSERT_EQ(16, event->getHistoricalToolMajor(0, 0)); - ASSERT_EQ(26, event->getHistoricalToolMajor(1, 0)); - ASSERT_EQ(116, event->getHistoricalToolMajor(0, 1)); - ASSERT_EQ(126, event->getHistoricalToolMajor(1, 1)); - ASSERT_EQ(216, event->getToolMajor(0)); - ASSERT_EQ(226, event->getToolMajor(1)); - - ASSERT_EQ(17, event->getHistoricalToolMinor(0, 0)); - ASSERT_EQ(27, event->getHistoricalToolMinor(1, 0)); - ASSERT_EQ(117, event->getHistoricalToolMinor(0, 1)); - ASSERT_EQ(127, event->getHistoricalToolMinor(1, 1)); - ASSERT_EQ(217, event->getToolMinor(0)); - ASSERT_EQ(227, event->getToolMinor(1)); - - ASSERT_EQ(18, event->getHistoricalOrientation(0, 0)); - ASSERT_EQ(28, event->getHistoricalOrientation(1, 0)); - ASSERT_EQ(118, event->getHistoricalOrientation(0, 1)); - ASSERT_EQ(128, event->getHistoricalOrientation(1, 1)); - ASSERT_EQ(218, event->getOrientation(0)); - ASSERT_EQ(228, event->getOrientation(1)); -} - -TEST_F(MotionEventTest, Properties) { - MotionEvent event; - - // Initialize, add samples and check properties. - initializeEventWithHistory(&event); - ASSERT_NO_FATAL_FAILURE(assertEqualsEventWithHistory(&event)); - - // Set source. - event.setSource(AINPUT_SOURCE_JOYSTICK); - ASSERT_EQ(AINPUT_SOURCE_JOYSTICK, event.getSource()); - - // Set action. - event.setAction(AMOTION_EVENT_ACTION_CANCEL); - ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, event.getAction()); - - // Set meta state. - event.setMetaState(AMETA_CTRL_ON); - ASSERT_EQ(AMETA_CTRL_ON, event.getMetaState()); -} - -TEST_F(MotionEventTest, CopyFrom_KeepHistory) { - MotionEvent event; - initializeEventWithHistory(&event); - - MotionEvent copy; - copy.copyFrom(&event, true /*keepHistory*/); - - ASSERT_NO_FATAL_FAILURE(assertEqualsEventWithHistory(&event)); -} - -TEST_F(MotionEventTest, CopyFrom_DoNotKeepHistory) { - MotionEvent event; - initializeEventWithHistory(&event); - - MotionEvent copy; - copy.copyFrom(&event, false /*keepHistory*/); - - ASSERT_EQ(event.getPointerCount(), copy.getPointerCount()); - ASSERT_EQ(0U, copy.getHistorySize()); - - ASSERT_EQ(event.getPointerId(0), copy.getPointerId(0)); - ASSERT_EQ(event.getPointerId(1), copy.getPointerId(1)); - - ASSERT_EQ(event.getEventTime(), copy.getEventTime()); - - ASSERT_EQ(event.getX(0), copy.getX(0)); -} - -TEST_F(MotionEventTest, OffsetLocation) { - MotionEvent event; - initializeEventWithHistory(&event); - - event.offsetLocation(5.0f, -2.0f); - - ASSERT_EQ(X_OFFSET + 5.0f, event.getXOffset()); - ASSERT_EQ(Y_OFFSET - 2.0f, event.getYOffset()); -} - -TEST_F(MotionEventTest, Scale) { - MotionEvent event; - initializeEventWithHistory(&event); - - event.scale(2.0f); - - ASSERT_EQ(X_OFFSET * 2, event.getXOffset()); - ASSERT_EQ(Y_OFFSET * 2, event.getYOffset()); - - ASSERT_EQ(210 * 2, event.getRawX(0)); - ASSERT_EQ(211 * 2, event.getRawY(0)); - ASSERT_EQ((X_OFFSET + 210) * 2, event.getX(0)); - ASSERT_EQ((Y_OFFSET + 211) * 2, event.getY(0)); - ASSERT_EQ(212, event.getPressure(0)); - ASSERT_EQ(213, event.getSize(0)); - ASSERT_EQ(214 * 2, event.getTouchMajor(0)); - ASSERT_EQ(215 * 2, event.getTouchMinor(0)); - ASSERT_EQ(216 * 2, event.getToolMajor(0)); - ASSERT_EQ(217 * 2, event.getToolMinor(0)); - ASSERT_EQ(218, event.getOrientation(0)); -} - -TEST_F(MotionEventTest, Parcel) { - Parcel parcel; - - MotionEvent inEvent; - initializeEventWithHistory(&inEvent); - MotionEvent outEvent; - - // Round trip. - inEvent.writeToParcel(&parcel); - parcel.setDataPosition(0); - outEvent.readFromParcel(&parcel); - - ASSERT_NO_FATAL_FAILURE(assertEqualsEventWithHistory(&outEvent)); -} - -TEST_F(MotionEventTest, Transform) { - // Generate some points on a circle. - // Each point 'i' is a point on a circle of radius ROTATION centered at (3,2) at an angle - // of ARC * i degrees clockwise relative to the Y axis. - // The geometrical representation is irrelevant to the test, it's just easy to generate - // and check rotation. We set the orientation to the same angle. - // Coordinate system: down is increasing Y, right is increasing X. - const float PI_180 = float(M_PI / 180); - const float RADIUS = 10; - const float ARC = 36; - const float ROTATION = ARC * 2; - - const size_t pointerCount = 11; - PointerProperties pointerProperties[pointerCount]; - PointerCoords pointerCoords[pointerCount]; - for (size_t i = 0; i < pointerCount; i++) { - float angle = float(i * ARC * PI_180); - pointerProperties[i].clear(); - pointerProperties[i].id = i; - pointerCoords[i].clear(); - pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, sinf(angle) * RADIUS + 3); - pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, -cosf(angle) * RADIUS + 2); - pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, angle); - } - MotionEvent event; - event.initialize(0, 0, AMOTION_EVENT_ACTION_MOVE, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, pointerCount, pointerProperties, pointerCoords); - float originalRawX = 0 + 3; - float originalRawY = -RADIUS + 2; - - // Check original raw X and Y assumption. - ASSERT_NEAR(originalRawX, event.getRawX(0), 0.001); - ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001); - - // Now translate the motion event so the circle's origin is at (0,0). - event.offsetLocation(-3, -2); - - // Offsetting the location should preserve the raw X and Y of the first point. - ASSERT_NEAR(originalRawX, event.getRawX(0), 0.001); - ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001); - - // Apply a rotation about the origin by ROTATION degrees clockwise. - SkMatrix matrix; - matrix.setRotate(ROTATION); - event.transform(&matrix); - - // Check the points. - for (size_t i = 0; i < pointerCount; i++) { - float angle = float((i * ARC + ROTATION) * PI_180); - ASSERT_NEAR(sinf(angle) * RADIUS, event.getX(i), 0.001); - ASSERT_NEAR(-cosf(angle) * RADIUS, event.getY(i), 0.001); - ASSERT_NEAR(tanf(angle), tanf(event.getOrientation(i)), 0.1); - } - - // Applying the transformation should preserve the raw X and Y of the first point. - ASSERT_NEAR(originalRawX, event.getRawX(0), 0.001); - ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001); -} - -} // namespace android diff --git a/libs/androidfw/tests/InputPublisherAndConsumer_test.cpp b/libs/androidfw/tests/InputPublisherAndConsumer_test.cpp deleted file mode 100644 index f45774b..0000000 --- a/libs/androidfw/tests/InputPublisherAndConsumer_test.cpp +++ /dev/null @@ -1,287 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <androidfw/InputTransport.h> -#include <utils/Timers.h> -#include <utils/StopWatch.h> -#include <gtest/gtest.h> -#include <unistd.h> -#include <time.h> -#include <sys/mman.h> -#include <cutils/ashmem.h> - -#include "TestHelpers.h" - -namespace android { - -class InputPublisherAndConsumerTest : public testing::Test { -protected: - sp<InputChannel> serverChannel, clientChannel; - InputPublisher* mPublisher; - InputConsumer* mConsumer; - PreallocatedInputEventFactory mEventFactory; - - virtual void SetUp() { - status_t result = InputChannel::openInputChannelPair(String8("channel name"), - serverChannel, clientChannel); - - mPublisher = new InputPublisher(serverChannel); - mConsumer = new InputConsumer(clientChannel); - } - - virtual void TearDown() { - if (mPublisher) { - delete mPublisher; - mPublisher = NULL; - } - - if (mConsumer) { - delete mConsumer; - mConsumer = NULL; - } - - serverChannel.clear(); - clientChannel.clear(); - } - - void PublishAndConsumeKeyEvent(); - void PublishAndConsumeMotionEvent(); -}; - -TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) { - EXPECT_EQ(serverChannel.get(), mPublisher->getChannel().get()); - EXPECT_EQ(clientChannel.get(), mConsumer->getChannel().get()); -} - -void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { - status_t status; - - const uint32_t seq = 15; - const int32_t deviceId = 1; - const int32_t source = AINPUT_SOURCE_KEYBOARD; - const int32_t action = AKEY_EVENT_ACTION_DOWN; - const int32_t flags = AKEY_EVENT_FLAG_FROM_SYSTEM; - const int32_t keyCode = AKEYCODE_ENTER; - const int32_t scanCode = 13; - const int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON; - const int32_t repeatCount = 1; - const nsecs_t downTime = 3; - const nsecs_t eventTime = 4; - - status = mPublisher->publishKeyEvent(seq, deviceId, source, action, flags, - keyCode, scanCode, metaState, repeatCount, downTime, eventTime); - ASSERT_EQ(OK, status) - << "publisher publishKeyEvent should return OK"; - - uint32_t consumeSeq; - InputEvent* event; - status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event); - ASSERT_EQ(OK, status) - << "consumer consume should return OK"; - - ASSERT_TRUE(event != NULL) - << "consumer should have returned non-NULL event"; - ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, event->getType()) - << "consumer should have returned a key event"; - - KeyEvent* keyEvent = static_cast<KeyEvent*>(event); - EXPECT_EQ(seq, consumeSeq); - EXPECT_EQ(deviceId, keyEvent->getDeviceId()); - EXPECT_EQ(source, keyEvent->getSource()); - EXPECT_EQ(action, keyEvent->getAction()); - EXPECT_EQ(flags, keyEvent->getFlags()); - EXPECT_EQ(keyCode, keyEvent->getKeyCode()); - EXPECT_EQ(scanCode, keyEvent->getScanCode()); - EXPECT_EQ(metaState, keyEvent->getMetaState()); - EXPECT_EQ(repeatCount, keyEvent->getRepeatCount()); - EXPECT_EQ(downTime, keyEvent->getDownTime()); - EXPECT_EQ(eventTime, keyEvent->getEventTime()); - - status = mConsumer->sendFinishedSignal(seq, true); - ASSERT_EQ(OK, status) - << "consumer sendFinishedSignal should return OK"; - - uint32_t finishedSeq = 0; - bool handled = false; - status = mPublisher->receiveFinishedSignal(&finishedSeq, &handled); - ASSERT_EQ(OK, status) - << "publisher receiveFinishedSignal should return OK"; - ASSERT_EQ(seq, finishedSeq) - << "publisher receiveFinishedSignal should have returned the original sequence number"; - ASSERT_TRUE(handled) - << "publisher receiveFinishedSignal should have set handled to consumer's reply"; -} - -void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { - status_t status; - - const uint32_t seq = 15; - const int32_t deviceId = 1; - const int32_t source = AINPUT_SOURCE_TOUCHSCREEN; - const int32_t action = AMOTION_EVENT_ACTION_MOVE; - const int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED; - const int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_TOP; - const int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON; - const int32_t buttonState = AMOTION_EVENT_BUTTON_PRIMARY; - const float xOffset = -10; - const float yOffset = -20; - const float xPrecision = 0.25; - const float yPrecision = 0.5; - const nsecs_t downTime = 3; - const size_t pointerCount = 3; - const nsecs_t eventTime = 4; - PointerProperties pointerProperties[pointerCount]; - PointerCoords pointerCoords[pointerCount]; - for (size_t i = 0; i < pointerCount; i++) { - pointerProperties[i].clear(); - pointerProperties[i].id = (i + 2) % pointerCount; - pointerProperties[i].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; - - pointerCoords[i].clear(); - pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, 100 * i); - pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, 200 * i); - pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.5 * i); - pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 0.7 * i); - pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 1.5 * i); - pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 1.7 * i); - pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.5 * i); - pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.7 * i); - pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i); - } - - status = mPublisher->publishMotionEvent(seq, deviceId, source, action, flags, edgeFlags, - metaState, buttonState, xOffset, yOffset, xPrecision, yPrecision, - downTime, eventTime, pointerCount, - pointerProperties, pointerCoords); - ASSERT_EQ(OK, status) - << "publisher publishMotionEvent should return OK"; - - uint32_t consumeSeq; - InputEvent* event; - status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event); - ASSERT_EQ(OK, status) - << "consumer consume should return OK"; - - ASSERT_TRUE(event != NULL) - << "consumer should have returned non-NULL event"; - ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType()) - << "consumer should have returned a motion event"; - - MotionEvent* motionEvent = static_cast<MotionEvent*>(event); - EXPECT_EQ(seq, consumeSeq); - EXPECT_EQ(deviceId, motionEvent->getDeviceId()); - EXPECT_EQ(source, motionEvent->getSource()); - EXPECT_EQ(action, motionEvent->getAction()); - EXPECT_EQ(flags, motionEvent->getFlags()); - EXPECT_EQ(edgeFlags, motionEvent->getEdgeFlags()); - EXPECT_EQ(metaState, motionEvent->getMetaState()); - EXPECT_EQ(buttonState, motionEvent->getButtonState()); - EXPECT_EQ(xPrecision, motionEvent->getXPrecision()); - EXPECT_EQ(yPrecision, motionEvent->getYPrecision()); - EXPECT_EQ(downTime, motionEvent->getDownTime()); - EXPECT_EQ(eventTime, motionEvent->getEventTime()); - EXPECT_EQ(pointerCount, motionEvent->getPointerCount()); - EXPECT_EQ(0U, motionEvent->getHistorySize()); - - for (size_t i = 0; i < pointerCount; i++) { - SCOPED_TRACE(i); - EXPECT_EQ(pointerProperties[i].id, motionEvent->getPointerId(i)); - EXPECT_EQ(pointerProperties[i].toolType, motionEvent->getToolType(i)); - - EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X), - motionEvent->getRawX(i)); - EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y), - motionEvent->getRawY(i)); - EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X) + xOffset, - motionEvent->getX(i)); - EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y) + yOffset, - motionEvent->getY(i)); - EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), - motionEvent->getPressure(i)); - EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE), - motionEvent->getSize(i)); - EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), - motionEvent->getTouchMajor(i)); - EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), - motionEvent->getTouchMinor(i)); - EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), - motionEvent->getToolMajor(i)); - EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), - motionEvent->getToolMinor(i)); - EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), - motionEvent->getOrientation(i)); - } - - status = mConsumer->sendFinishedSignal(seq, false); - ASSERT_EQ(OK, status) - << "consumer sendFinishedSignal should return OK"; - - uint32_t finishedSeq = 0; - bool handled = true; - status = mPublisher->receiveFinishedSignal(&finishedSeq, &handled); - ASSERT_EQ(OK, status) - << "publisher receiveFinishedSignal should return OK"; - ASSERT_EQ(seq, finishedSeq) - << "publisher receiveFinishedSignal should have returned the original sequence number"; - ASSERT_FALSE(handled) - << "publisher receiveFinishedSignal should have set handled to consumer's reply"; -} - -TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_EndToEnd) { - ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent()); -} - -TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_EndToEnd) { - ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent()); -} - -TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountLessThan1_ReturnsError) { - status_t status; - const size_t pointerCount = 0; - PointerProperties pointerProperties[pointerCount]; - PointerCoords pointerCoords[pointerCount]; - - status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - pointerCount, pointerProperties, pointerCoords); - ASSERT_EQ(BAD_VALUE, status) - << "publisher publishMotionEvent should return BAD_VALUE"; -} - -TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountGreaterThanMax_ReturnsError) { - status_t status; - const size_t pointerCount = MAX_POINTERS + 1; - PointerProperties pointerProperties[pointerCount]; - PointerCoords pointerCoords[pointerCount]; - for (size_t i = 0; i < pointerCount; i++) { - pointerProperties[i].clear(); - pointerCoords[i].clear(); - } - - status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - pointerCount, pointerProperties, pointerCoords); - ASSERT_EQ(BAD_VALUE, status) - << "publisher publishMotionEvent should return BAD_VALUE"; -} - -TEST_F(InputPublisherAndConsumerTest, PublishMultipleEvents_EndToEnd) { - ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent()); - ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent()); - ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent()); - ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent()); - ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent()); -} - -} // namespace android diff --git a/libs/androidfw/tests/TestHelpers.h b/libs/androidfw/tests/TestHelpers.h deleted file mode 100644 index d8e985e..0000000 --- a/libs/androidfw/tests/TestHelpers.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef TESTHELPERS_H -#define TESTHELPERS_H - -#include <utils/threads.h> - -namespace android { - -class Pipe { -public: - int sendFd; - int receiveFd; - - Pipe() { - int fds[2]; - ::pipe(fds); - - receiveFd = fds[0]; - sendFd = fds[1]; - } - - ~Pipe() { - if (sendFd != -1) { - ::close(sendFd); - } - - if (receiveFd != -1) { - ::close(receiveFd); - } - } - - status_t writeSignal() { - ssize_t nWritten = ::write(sendFd, "*", 1); - return nWritten == 1 ? 0 : -errno; - } - - status_t readSignal() { - char buf[1]; - ssize_t nRead = ::read(receiveFd, buf, 1); - return nRead == 1 ? 0 : nRead == 0 ? -EPIPE : -errno; - } -}; - -class DelayedTask : public Thread { - int mDelayMillis; - -public: - DelayedTask(int delayMillis) : mDelayMillis(delayMillis) { } - -protected: - virtual ~DelayedTask() { } - - virtual void doTask() = 0; - - virtual bool threadLoop() { - usleep(mDelayMillis * 1000); - doTask(); - return false; - } -}; - -} // namespace android - -#endif // TESTHELPERS_H diff --git a/libs/androidfw/tests/ZipFileRO_test.cpp b/libs/androidfw/tests/ZipFileRO_test.cpp new file mode 100644 index 0000000..cb9c721 --- /dev/null +++ b/libs/androidfw/tests/ZipFileRO_test.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "ZipFileRO_test" +#include <utils/Log.h> +#include <androidfw/ZipFileRO.h> + +#include <gtest/gtest.h> + +#include <fcntl.h> +#include <string.h> + +namespace android { + +class ZipFileROTest : public testing::Test { +protected: + virtual void SetUp() { + } + + virtual void TearDown() { + } +}; + +TEST_F(ZipFileROTest, ZipTimeConvertSuccess) { + struct tm t; + + // 2011-06-29 14:40:40 + long when = 0x3EDD7514; + + ZipFileRO::zipTimeToTimespec(when, &t); + + EXPECT_EQ(2011, t.tm_year + 1900) + << "Year was improperly converted."; + + EXPECT_EQ(6, t.tm_mon) + << "Month was improperly converted."; + + EXPECT_EQ(29, t.tm_mday) + << "Day was improperly converted."; + + EXPECT_EQ(14, t.tm_hour) + << "Hour was improperly converted."; + + EXPECT_EQ(40, t.tm_min) + << "Minute was improperly converted."; + + EXPECT_EQ(40, t.tm_sec) + << "Second was improperly converted."; +} + +} |