Merge "change SHUTDOWN permission to signatureOrSystem so setup wizard can start ACTION_REQUEST_SHUTDOWN bug: 3201638"
diff --git a/include/ui/EventHub.h b/include/ui/EventHub.h
index f4dc536..6c798a6 100644
--- a/include/ui/EventHub.h
+++ b/include/ui/EventHub.h
@@ -26,6 +26,7 @@
 #include <utils/threads.h>
 #include <utils/List.h>
 #include <utils/Errors.h>
+#include <utils/PropertyMap.h>
 
 #include <linux/input.h>
 
@@ -156,6 +157,8 @@
 
     virtual String8 getDeviceName(int32_t deviceId) const = 0;
 
+    virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const = 0;
+
     virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
             RawAbsoluteAxisInfo* outAxisInfo) const = 0;
 
@@ -205,6 +208,8 @@
 
     virtual String8 getDeviceName(int32_t deviceId) const;
 
+    virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const;
+
     virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
             RawAbsoluteAxisInfo* outAxisInfo) const;
 
@@ -247,6 +252,8 @@
         uint32_t        classes;
         uint8_t*        keyBitmask;
         KeyLayoutMap*   layoutMap;
+        String8         configurationFile;
+        PropertyMap*    configuration;
         KeyMapInfo      keyMapInfo;
         int             fd;
         device_t*       next;
@@ -264,6 +271,7 @@
     bool markSupportedKeyCodesLocked(device_t* device, size_t numCodes,
             const int32_t* keyCodes, uint8_t* outFlags) const;
 
+    void loadConfiguration(device_t* device);
     void configureKeyMap(device_t* device);
     void setKeyboardProperties(device_t* device, bool firstKeyboard);
     void clearKeyboardProperties(device_t* device, bool firstKeyboard);
diff --git a/include/ui/Input.h b/include/ui/Input.h
index 1355bab..4dc8f2a 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -497,6 +497,22 @@
     KeyedVector<int32_t, MotionRange> mMotionRanges;
 };
 
+/* Types of input device configuration files. */
+enum InputDeviceConfigurationFileType {
+    INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION = 0,     /* .idc file */
+    INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT = 1,        /* .kl file */
+    INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP = 2, /* .kcm file */
+};
+
+/*
+ * Get the path of an input device configuration file, if one is available.
+ * Spaces in the name are replaced with underscores.
+ * Considers both system provided and user installed configuration files.
+ *
+ * Returns an empty string if not found.
+ */
+extern String8 getInputDeviceConfigurationFilePath(
+        const String8& name, InputDeviceConfigurationFileType type);
 
 } // namespace android
 
diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h
index d0812de..b621680 100644
--- a/include/ui/InputDispatcher.h
+++ b/include/ui/InputDispatcher.h
@@ -291,9 +291,7 @@
      * This method is expected to set the POLICY_FLAG_PASS_TO_USER policy flag if the event
      * should be dispatched to applications.
      */
-    virtual void interceptKeyBeforeQueueing(nsecs_t when, int32_t deviceId,
-            int32_t action, int32_t& flags, int32_t keyCode, int32_t scanCode,
-            uint32_t& policyFlags) = 0;
+    virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags) = 0;
 
     /* Intercepts a generic touch, trackball or other event before queueing it.
      * The policy can use this method as an opportunity to perform power management functions
@@ -894,9 +892,6 @@
     // Input channels that will receive a copy of all input events.
     Vector<sp<InputChannel> > mMonitoringChannels;
 
-    // Preallocated key event object used for policy inquiries.
-    KeyEvent mReusableKeyEvent;
-
     // Event injection and synchronization.
     Condition mInjectionResultAvailableCondition;
     bool hasInjectionPermission(int32_t injectorPid, int32_t injectorUid);
diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h
index f3a2dd2..cfceaab 100644
--- a/include/ui/InputReader.h
+++ b/include/ui/InputReader.h
@@ -47,23 +47,6 @@
 };
 
 
-/* Specifies input device calibration settings. */
-class InputDeviceCalibration {
-public:
-    InputDeviceCalibration();
-
-    void clear();
-    void addProperty(const String8& key, const String8& value);
-
-    bool tryGetProperty(const String8& key, String8& outValue) const;
-    bool tryGetProperty(const String8& key, int32_t& outValue) const;
-    bool tryGetProperty(const String8& key, float& outValue) const;
-
-private:
-    KeyedVector<String8, String8> mProperties;
-};
-
-
 /*
  * Input reader policy interface.
  *
@@ -107,10 +90,6 @@
     virtual void getVirtualKeyDefinitions(const String8& deviceName,
             Vector<VirtualKeyDefinition>& outVirtualKeyDefinitions) = 0;
 
-    /* Gets the calibration for an input device. */
-    virtual void getInputDeviceCalibration(const String8& deviceName,
-            InputDeviceCalibration& outCalibration) = 0;
-
     /* Gets the excluded device names for the platform. */
     virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames) = 0;
 };
@@ -314,8 +293,8 @@
 
     int32_t getMetaState();
 
-    inline const InputDeviceCalibration& getCalibration() {
-        return mCalibration;
+    inline const PropertyMap& getConfiguration() {
+        return mConfiguration;
     }
 
 private:
@@ -330,7 +309,7 @@
     typedef int32_t (InputMapper::*GetStateFunc)(uint32_t sourceMask, int32_t code);
     int32_t getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc);
 
-    InputDeviceCalibration mCalibration;
+    PropertyMap mConfiguration;
 };
 
 
@@ -389,13 +368,13 @@
 
 class KeyboardInputMapper : public InputMapper {
 public:
-    KeyboardInputMapper(InputDevice* device, int32_t associatedDisplayId, uint32_t sources,
-            int32_t keyboardType);
+    KeyboardInputMapper(InputDevice* device, uint32_t sources, int32_t keyboardType);
     virtual ~KeyboardInputMapper();
 
     virtual uint32_t getSources();
     virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
     virtual void dump(String8& dump);
+    virtual void configure();
     virtual void reset();
     virtual void process(const RawEvent* rawEvent);
 
@@ -414,10 +393,15 @@
         int32_t scanCode;
     };
 
-    int32_t mAssociatedDisplayId;
     uint32_t mSources;
     int32_t mKeyboardType;
 
+    // Immutable configuration parameters.
+    struct Parameters {
+        int32_t associatedDisplayId;
+        bool orientationAware;
+    } mParameters;
+
     struct LockedState {
         Vector<KeyDown> keyDowns; // keys that are down
         int32_t metaState;
@@ -435,6 +419,9 @@
     void initializeLocked();
     void initializeLedStateLocked(LockedState::LedState& ledState, int32_t led);
 
+    void configureParameters();
+    void dumpParameters(String8& dump);
+
     bool isKeyboardOrGamepadKey(int32_t scanCode);
 
     void processKey(nsecs_t when, bool down, int32_t keyCode, int32_t scanCode,
@@ -450,12 +437,13 @@
 
 class TrackballInputMapper : public InputMapper {
 public:
-    TrackballInputMapper(InputDevice* device, int32_t associatedDisplayId);
+    TrackballInputMapper(InputDevice* device);
     virtual ~TrackballInputMapper();
 
     virtual uint32_t getSources();
     virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
     virtual void dump(String8& dump);
+    virtual void configure();
     virtual void reset();
     virtual void process(const RawEvent* rawEvent);
 
@@ -467,7 +455,11 @@
 
     Mutex mLock;
 
-    int32_t mAssociatedDisplayId;
+    // Immutable configuration parameters.
+    struct Parameters {
+        int32_t associatedDisplayId;
+        bool orientationAware;
+    } mParameters;
 
     struct Accumulator {
         enum {
@@ -499,13 +491,16 @@
 
     void initializeLocked();
 
+    void configureParameters();
+    void dumpParameters(String8& dump);
+
     void sync(nsecs_t when);
 };
 
 
 class TouchInputMapper : public InputMapper {
 public:
-    TouchInputMapper(InputDevice* device, int32_t associatedDisplayId);
+    TouchInputMapper(InputDevice* device);
     virtual ~TouchInputMapper();
 
     virtual uint32_t getSources();
@@ -591,10 +586,17 @@
         }
     };
 
-    int32_t mAssociatedDisplayId;
-
     // Immutable configuration parameters.
     struct Parameters {
+        enum DeviceType {
+            DEVICE_TYPE_TOUCH_SCREEN,
+            DEVICE_TYPE_TOUCH_PAD,
+        };
+
+        DeviceType deviceType;
+        int32_t associatedDisplayId;
+        bool orientationAware;
+
         bool useBadTouchFilter;
         bool useJumpyTouchFilter;
         bool useAveragingTouchFilter;
@@ -641,7 +643,7 @@
         bool haveToolSizeAreaBias;
         float toolSizeAreaBias;
         bool haveToolSizeIsSummed;
-        int32_t toolSizeIsSummed;
+        bool toolSizeIsSummed;
 
         // Pressure
         enum PressureCalibration {
@@ -846,7 +848,7 @@
 
 class SingleTouchInputMapper : public TouchInputMapper {
 public:
-    SingleTouchInputMapper(InputDevice* device, int32_t associatedDisplayId);
+    SingleTouchInputMapper(InputDevice* device);
     virtual ~SingleTouchInputMapper();
 
     virtual void reset();
@@ -892,7 +894,7 @@
 
 class MultiTouchInputMapper : public TouchInputMapper {
 public:
-    MultiTouchInputMapper(InputDevice* device, int32_t associatedDisplayId);
+    MultiTouchInputMapper(InputDevice* device);
     virtual ~MultiTouchInputMapper();
 
     virtual void reset();
diff --git a/include/ui/Keyboard.h b/include/ui/Keyboard.h
index 3b477c7..689607d 100644
--- a/include/ui/Keyboard.h
+++ b/include/ui/Keyboard.h
@@ -20,6 +20,7 @@
 #include <ui/Input.h>
 #include <utils/Errors.h>
 #include <utils/String8.h>
+#include <utils/PropertyMap.h>
 
 namespace android {
 
@@ -33,19 +34,23 @@
 };
 
 struct KeyMapInfo {
-    String8 keyMapName;
     String8 keyLayoutFile;
     String8 keyCharacterMapFile;
     bool isDefaultKeyMap;
 
     KeyMapInfo() : isDefaultKeyMap(false) {
     }
+
+    bool isComplete() {
+        return !keyLayoutFile.isEmpty() && !keyCharacterMapFile.isEmpty();
+    }
 };
 
 /**
  * Resolves the key map to use for a particular keyboard device.
  */
-extern status_t resolveKeyMap(const String8& deviceName, KeyMapInfo& outKeyMapInfo);
+extern status_t resolveKeyMap(const String8& deviceName,
+        const PropertyMap* deviceConfiguration, KeyMapInfo& outKeyMapInfo);
 
 /**
  * Sets keyboard system properties.
diff --git a/include/utils/PropertyMap.h b/include/utils/PropertyMap.h
new file mode 100644
index 0000000..a54f819
--- /dev/null
+++ b/include/utils/PropertyMap.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UTILS_PROPERTY_MAP_H
+#define _UTILS_PROPERTY_MAP_H
+
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/Errors.h>
+#include <utils/Tokenizer.h>
+
+namespace android {
+
+/*
+ * Provides a mechanism for passing around string-based property key / value pairs
+ * and loading them from property files.
+ *
+ * The property files have the following simple structure:
+ *
+ * # Comment
+ * key = value
+ *
+ * Keys and values are any sequence of printable ASCII characters.
+ * The '=' separates the key from the value.
+ * The key and value may not contain whitespace.
+ *
+ * The '\' character is reserved for escape sequences and is not currently supported.
+ * The '"" character is reserved for quoting and is not currently supported.
+ * Files that contain the '\' or '"' character will fail to parse.
+ *
+ * The file must not contain duplicate keys.
+ *
+ * TODO Support escape sequences and quoted values when needed.
+ */
+class PropertyMap {
+public:
+    /* Creates an empty property map. */
+    PropertyMap();
+    ~PropertyMap();
+
+    /* Clears the property map. */
+    void clear();
+
+    /* Adds a property.
+     * Replaces the property with the same key if it is already present.
+     */
+    void addProperty(const String8& key, const String8& value);
+
+    /* Returns true if the property map contains the specified key. */
+    bool hasProperty(const String8& key) const;
+
+    /* Gets the value of a property and parses it.
+     * Returns true and sets outValue if the key was found and its value was parsed successfully.
+     * Otherwise returns false and does not modify outValue.  (Also logs a warning.)
+     */
+    bool tryGetProperty(const String8& key, String8& outValue) const;
+    bool tryGetProperty(const String8& key, bool& outValue) const;
+    bool tryGetProperty(const String8& key, int32_t& outValue) const;
+    bool tryGetProperty(const String8& key, float& outValue) const;
+
+    /* Loads a property map from a file. */
+    static status_t load(const String8& filename, PropertyMap** outMap);
+
+private:
+    class Parser {
+        PropertyMap* mMap;
+        Tokenizer* mTokenizer;
+
+    public:
+        Parser(PropertyMap* map, Tokenizer* tokenizer);
+        ~Parser();
+        status_t parse();
+
+    private:
+        status_t parseType();
+        status_t parseKey();
+        status_t parseKeyProperty();
+        status_t parseModifier(const String8& token, int32_t* outMetaState);
+        status_t parseCharacterLiteral(char16_t* outCharacter);
+    };
+
+    KeyedVector<String8, String8> mProperties;
+};
+
+} // namespace android
+
+#endif // _UTILS_PROPERTY_MAP_H
diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp
index f468217..b312cda 100644
--- a/libs/ui/EventHub.cpp
+++ b/libs/ui/EventHub.cpp
@@ -93,12 +93,13 @@
 
 EventHub::device_t::device_t(int32_t _id, const char* _path, const char* name)
     : id(_id), path(_path), name(name), classes(0)
-    , keyBitmask(NULL), layoutMap(NULL), fd(-1), next(NULL) {
+    , keyBitmask(NULL), layoutMap(NULL), configuration(NULL), fd(-1), next(NULL) {
 }
 
 EventHub::device_t::~device_t() {
     delete [] keyBitmask;
     delete layoutMap;
+    delete configuration;
 }
 
 EventHub::EventHub(void)
@@ -144,6 +145,16 @@
     return device->classes;
 }
 
+void EventHub::getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const {
+    AutoMutex _l(mLock);
+    device_t* device = getDeviceLocked(deviceId);
+    if (device && device->configuration) {
+        *outConfiguration = *device->configuration;
+    } else {
+        outConfiguration->clear();
+    }
+}
+
 status_t EventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis,
         RawAbsoluteAxisInfo* outAxisInfo) const {
     outAxisInfo->clear();
@@ -716,6 +727,9 @@
     mFDs[mFDCount].events = POLLIN;
     mFDs[mFDCount].revents = 0;
 
+    // Load the configuration file for the device.
+    loadConfiguration(device);
+
     // Figure out the kinds of events the device reports.
     
     uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)];
@@ -803,7 +817,6 @@
         device->name = name;
 
         // Configure the keymap for the device.
-
         configureKeyMap(device);
 
         // Tell the world about the devname (the descriptive name)
@@ -868,9 +881,11 @@
         return -1;
     }
 
-    LOGI("New device: path=%s name=%s id=0x%x (of 0x%x) index=%d fd=%d classes=0x%x\n",
-         deviceName, name, device->id, mNumDevicesById, mFDCount, fd, device->classes);
-         
+    LOGI("New device: path=%s name=%s id=0x%x (of 0x%x) index=%d fd=%d classes=0x%x "
+            "configuration='%s'\n",
+         deviceName, name, device->id, mNumDevicesById, mFDCount, fd, device->classes,
+         device->configurationFile.string());
+
     LOGV("Adding device %s %p at %d, id = %d, classes = 0x%x\n",
          deviceName, device, mFDCount, devid, device->classes);
 
@@ -883,8 +898,24 @@
     return 0;
 }
 
+void EventHub::loadConfiguration(device_t* device) {
+    device->configurationFile = getInputDeviceConfigurationFilePath(device->name,
+            INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION);
+    if (device->configurationFile.isEmpty()) {
+        LOGI("No input device configuration file found for device '%s'.",
+                device->name.string());
+    } else {
+        status_t status = PropertyMap::load(device->configurationFile,
+                &device->configuration);
+        if (status) {
+            LOGE("Error loading input device configuration file for device '%s'.",
+                    device->name.string());
+        }
+    }
+}
+
 void EventHub::configureKeyMap(device_t* device) {
-    android::resolveKeyMap(device->name, device->keyMapInfo);
+    android::resolveKeyMap(device->name, device->configuration, device->keyMapInfo);
 }
 
 void EventHub::setKeyboardProperties(device_t* device, bool firstKeyboard) {
@@ -1058,12 +1089,12 @@
                 dump.appendFormat(INDENT3 "Path: %s\n", device->path.string());
                 dump.appendFormat(INDENT3 "IsDefaultKeyMap: %s\n",
                         toString(device->keyMapInfo.isDefaultKeyMap));
-                dump.appendFormat(INDENT3 "KeyMapName: %s\n",
-                        device->keyMapInfo.keyMapName.string());
                 dump.appendFormat(INDENT3 "KeyLayoutFile: %s\n",
                         device->keyMapInfo.keyLayoutFile.string());
                 dump.appendFormat(INDENT3 "KeyCharacterMapFile: %s\n",
                         device->keyMapInfo.keyCharacterMapFile.string());
+                dump.appendFormat(INDENT3 "ConfigurationFile: %s\n",
+                        device->configurationFile.string());
             }
         }
     } // release lock
diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp
index 944a79b..9e697db 100644
--- a/libs/ui/Input.cpp
+++ b/libs/ui/Input.cpp
@@ -7,11 +7,82 @@
 
 //#define LOG_NDEBUG 0
 
+#define DEBUG_PROBE 0
+
+#include <stdlib.h>
+#include <unistd.h>
+
 #include <ui/Input.h>
 
 namespace android {
 
-// class InputEvent
+static const char* CONFIGURATION_FILE_DIR[] = {
+        "idc/",
+        "keylayout/",
+        "keychars/",
+};
+
+static const char* CONFIGURATION_FILE_EXTENSION[] = {
+        ".idc",
+        ".kl",
+        ".kcm",
+};
+
+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 (ch == ' ') {
+            ch = '_';
+        }
+        path.append(&ch, 1);
+    }
+    path.append(CONFIGURATION_FILE_EXTENSION[type]);
+}
+
+extern String8 getInputDeviceConfigurationFilePath(
+        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
+    LOGD("Probing for system provided input device configuration file: path='%s'", path.string());
+#endif
+    if (!access(path.string(), R_OK)) {
+#if DEBUG_PROBE
+        LOGD("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
+    LOGD("Probing for system user input device configuration file: path='%s'", path.string());
+#endif
+    if (!access(path.string(), R_OK)) {
+#if DEBUG_PROBE
+        LOGD("Found");
+#endif
+        return path;
+    }
+
+    // Not found.
+#if DEBUG_PROBE
+    LOGD("Probe failed to find input device configuration file: name='%s', type=%d",
+            name.string(), type);
+#endif
+    return String8();
+}
+
+
+// --- InputEvent ---
 
 void InputEvent::initialize(int32_t deviceId, int32_t source) {
     mDeviceId = deviceId;
@@ -23,7 +94,7 @@
     mSource = from.mSource;
 }
 
-// class KeyEvent
+// --- KeyEvent ---
 
 bool KeyEvent::hasDefaultAction(int32_t keyCode) {
     switch (keyCode) {
@@ -131,7 +202,7 @@
     mEventTime = from.mEventTime;
 }
 
-// class MotionEvent
+// --- MotionEvent ---
 
 void MotionEvent::initialize(
         int32_t deviceId,
@@ -178,7 +249,7 @@
     mYOffset += yOffset;
 }
 
-// class InputDeviceInfo
+// --- InputDeviceInfo ---
 
 InputDeviceInfo::InputDeviceInfo() {
     initialize(-1, String8("uninitialized device info"));
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
index db7d448..0708223 100644
--- a/libs/ui/InputDispatcher.cpp
+++ b/libs/ui/InputDispatcher.cpp
@@ -2081,9 +2081,22 @@
         return;
     }
 
+    if ((policyFlags & POLICY_FLAG_VIRTUAL) || (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY)) {
+        policyFlags |= POLICY_FLAG_VIRTUAL;
+        flags |= AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY;
+    }
+
     policyFlags |= POLICY_FLAG_TRUSTED;
-    mPolicy->interceptKeyBeforeQueueing(eventTime, deviceId, action, /*byref*/ flags,
-            keyCode, scanCode, /*byref*/ policyFlags);
+
+    KeyEvent event;
+    event.initialize(deviceId, source, action, flags, keyCode, scanCode,
+            metaState, 0, downTime, eventTime);
+
+    mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
+
+    if (policyFlags & POLICY_FLAG_WOKE_HERE) {
+        flags |= AKEY_EVENT_FLAG_WOKE_HERE;
+    }
 
     bool needWake;
     { // acquire lock
@@ -2289,17 +2302,22 @@
             return INPUT_EVENT_INJECTION_FAILED;
         }
 
-        nsecs_t eventTime = keyEvent->getEventTime();
-        int32_t deviceId = keyEvent->getDeviceId();
         int32_t flags = keyEvent->getFlags();
-        int32_t keyCode = keyEvent->getKeyCode();
-        int32_t scanCode = keyEvent->getScanCode();
-        mPolicy->interceptKeyBeforeQueueing(eventTime, deviceId, action, /*byref*/ flags,
-                keyCode, scanCode, /*byref*/ policyFlags);
+        if (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY) {
+            policyFlags |= POLICY_FLAG_VIRTUAL;
+        }
+
+        mPolicy->interceptKeyBeforeQueueing(keyEvent, /*byref*/ policyFlags);
+
+        if (policyFlags & POLICY_FLAG_WOKE_HERE) {
+            flags |= AKEY_EVENT_FLAG_WOKE_HERE;
+        }
 
         mLock.lock();
-        injectedEntry = mAllocator.obtainKeyEntry(eventTime, deviceId, keyEvent->getSource(),
-                policyFlags, action, flags, keyCode, scanCode, keyEvent->getMetaState(),
+        injectedEntry = mAllocator.obtainKeyEntry(keyEvent->getEventTime(),
+                keyEvent->getDeviceId(), keyEvent->getSource(),
+                policyFlags, action, flags,
+                keyEvent->getKeyCode(), keyEvent->getScanCode(), keyEvent->getMetaState(),
                 keyEvent->getRepeatCount(), keyEvent->getDownTime());
         break;
     }
@@ -2999,12 +3017,14 @@
 void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
         CommandEntry* commandEntry) {
     KeyEntry* entry = commandEntry->keyEntry;
-    initializeKeyEvent(&mReusableKeyEvent, entry);
+
+    KeyEvent event;
+    initializeKeyEvent(&event, entry);
 
     mLock.unlock();
 
     bool consumed = mPolicy->interceptKeyBeforeDispatching(commandEntry->inputChannel,
-            & mReusableKeyEvent, entry->policyFlags);
+            &event, entry->policyFlags);
 
     mLock.lock();
 
@@ -3025,12 +3045,13 @@
                 && dispatchEntry->hasForegroundTarget()
                 && dispatchEntry->eventEntry->type == EventEntry::TYPE_KEY) {
             KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry);
-            initializeKeyEvent(&mReusableKeyEvent, keyEntry);
+            KeyEvent event;
+            initializeKeyEvent(&event, keyEntry);
 
             mLock.unlock();
 
             mPolicy->dispatchUnhandledKey(connection->inputChannel,
-                    & mReusableKeyEvent, keyEntry->policyFlags);
+                    &event, keyEntry->policyFlags);
 
             mLock.lock();
         }
diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp
index daff2d0..aa690e5 100644
--- a/libs/ui/InputReader.cpp
+++ b/libs/ui/InputReader.cpp
@@ -98,64 +98,6 @@
 }
 
 
-// --- InputDeviceCalibration ---
-
-InputDeviceCalibration::InputDeviceCalibration() {
-}
-
-void InputDeviceCalibration::clear() {
-    mProperties.clear();
-}
-
-void InputDeviceCalibration::addProperty(const String8& key, const String8& value) {
-    mProperties.add(key, value);
-}
-
-bool InputDeviceCalibration::tryGetProperty(const String8& key, String8& outValue) const {
-    ssize_t index = mProperties.indexOfKey(key);
-    if (index < 0) {
-        return false;
-    }
-
-    outValue = mProperties.valueAt(index);
-    return true;
-}
-
-bool InputDeviceCalibration::tryGetProperty(const String8& key, int32_t& outValue) const {
-    String8 stringValue;
-    if (! tryGetProperty(key, stringValue) || stringValue.length() == 0) {
-        return false;
-    }
-
-    char* end;
-    int value = strtol(stringValue.string(), & end, 10);
-    if (*end != '\0') {
-        LOGW("Input device calibration key '%s' has invalid value '%s'.  Expected an integer.",
-                key.string(), stringValue.string());
-        return false;
-    }
-    outValue = value;
-    return true;
-}
-
-bool InputDeviceCalibration::tryGetProperty(const String8& key, float& outValue) const {
-    String8 stringValue;
-    if (! tryGetProperty(key, stringValue) || stringValue.length() == 0) {
-        return false;
-    }
-
-    char* end;
-    float value = strtof(stringValue.string(), & end);
-    if (*end != '\0') {
-        LOGW("Input device calibration key '%s' has invalid value '%s'.  Expected a float.",
-                key.string(), stringValue.string());
-        return false;
-    }
-    outValue = value;
-    return true;
-}
-
-
 // --- InputReader ---
 
 InputReader::InputReader(const sp<EventHubInterface>& eventHub,
@@ -274,8 +216,6 @@
 InputDevice* InputReader::createDevice(int32_t deviceId, const String8& name, uint32_t classes) {
     InputDevice* device = new InputDevice(this, deviceId, name);
 
-    const int32_t associatedDisplayId = 0; // FIXME: hardcoded for current single-display devices
-
     // Switch-like devices.
     if (classes & INPUT_DEVICE_CLASS_SWITCH) {
         device->addMapper(new SwitchInputMapper(device));
@@ -295,20 +235,19 @@
     }
 
     if (keyboardSources != 0) {
-        device->addMapper(new KeyboardInputMapper(device,
-                associatedDisplayId, keyboardSources, keyboardType));
+        device->addMapper(new KeyboardInputMapper(device, keyboardSources, keyboardType));
     }
 
     // Trackball-like devices.
     if (classes & INPUT_DEVICE_CLASS_TRACKBALL) {
-        device->addMapper(new TrackballInputMapper(device, associatedDisplayId));
+        device->addMapper(new TrackballInputMapper(device));
     }
 
     // Touchscreen-like devices.
     if (classes & INPUT_DEVICE_CLASS_TOUCHSCREEN_MT) {
-        device->addMapper(new MultiTouchInputMapper(device, associatedDisplayId));
+        device->addMapper(new MultiTouchInputMapper(device));
     } else if (classes & INPUT_DEVICE_CLASS_TOUCHSCREEN) {
-        device->addMapper(new SingleTouchInputMapper(device, associatedDisplayId));
+        device->addMapper(new SingleTouchInputMapper(device));
     }
 
     return device;
@@ -626,7 +565,7 @@
 
 void InputDevice::configure() {
     if (! isIgnored()) {
-        mContext->getPolicy()->getInputDeviceCalibration(mName, mCalibration);
+        mContext->getEventHub()->getConfiguration(mId, &mConfiguration);
     }
 
     mSources = 0;
@@ -792,9 +731,9 @@
 
 // --- KeyboardInputMapper ---
 
-KeyboardInputMapper::KeyboardInputMapper(InputDevice* device, int32_t associatedDisplayId,
+KeyboardInputMapper::KeyboardInputMapper(InputDevice* device,
         uint32_t sources, int32_t keyboardType) :
-        InputMapper(device), mAssociatedDisplayId(associatedDisplayId), mSources(sources),
+        InputMapper(device), mSources(sources),
         mKeyboardType(keyboardType) {
     initializeLocked();
 }
@@ -832,7 +771,7 @@
     { // acquire lock
         AutoMutex _l(mLock);
         dump.append(INDENT2 "Keyboard Input Mapper:\n");
-        dump.appendFormat(INDENT3 "AssociatedDisplayId: %d\n", mAssociatedDisplayId);
+        dumpParameters(dump);
         dump.appendFormat(INDENT3 "KeyboardType: %d\n", mKeyboardType);
         dump.appendFormat(INDENT3 "KeyDowns: %d keys currently down\n", mLocked.keyDowns.size());
         dump.appendFormat(INDENT3 "MetaState: 0x%0x\n", mLocked.metaState);
@@ -840,6 +779,30 @@
     } // release lock
 }
 
+
+void KeyboardInputMapper::configure() {
+    InputMapper::configure();
+
+    // Configure basic parameters.
+    configureParameters();
+}
+
+void KeyboardInputMapper::configureParameters() {
+    mParameters.orientationAware = false;
+    getDevice()->getConfiguration().tryGetProperty(String8("keyboard.orientationAware"),
+            mParameters.orientationAware);
+
+    mParameters.associatedDisplayId = mParameters.orientationAware ? 0 : -1;
+}
+
+void KeyboardInputMapper::dumpParameters(String8& dump) {
+    dump.append(INDENT3 "Parameters:\n");
+    dump.appendFormat(INDENT4 "AssociatedDisplayId: %d\n",
+            mParameters.associatedDisplayId);
+    dump.appendFormat(INDENT4 "OrientationAware: %s\n",
+            toString(mParameters.orientationAware));
+}
+
 void KeyboardInputMapper::reset() {
     for (;;) {
         int32_t keyCode, scanCode;
@@ -896,9 +859,10 @@
         if (down) {
             // Rotate key codes according to orientation if needed.
             // Note: getDisplayInfo is non-reentrant so we can continue holding the lock.
-            if (mAssociatedDisplayId >= 0) {
+            if (mParameters.orientationAware && mParameters.associatedDisplayId >= 0) {
                 int32_t orientation;
-                if (!getPolicy()->getDisplayInfo(mAssociatedDisplayId, NULL, NULL, & orientation)) {
+                if (!getPolicy()->getDisplayInfo(mParameters.associatedDisplayId,
+                        NULL, NULL, & orientation)) {
                     orientation = InputReaderPolicyInterface::ROTATION_0;
                 }
 
@@ -1011,8 +975,8 @@
 
 // --- TrackballInputMapper ---
 
-TrackballInputMapper::TrackballInputMapper(InputDevice* device, int32_t associatedDisplayId) :
-        InputMapper(device), mAssociatedDisplayId(associatedDisplayId) {
+TrackballInputMapper::TrackballInputMapper(InputDevice* device) :
+        InputMapper(device) {
     mXPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
     mYPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
     mXScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
@@ -1039,7 +1003,7 @@
     { // acquire lock
         AutoMutex _l(mLock);
         dump.append(INDENT2 "Trackball Input Mapper:\n");
-        dump.appendFormat(INDENT3 "AssociatedDisplayId: %d\n", mAssociatedDisplayId);
+        dumpParameters(dump);
         dump.appendFormat(INDENT3 "XPrecision: %0.3f\n", mXPrecision);
         dump.appendFormat(INDENT3 "YPrecision: %0.3f\n", mYPrecision);
         dump.appendFormat(INDENT3 "Down: %s\n", toString(mLocked.down));
@@ -1047,6 +1011,29 @@
     } // release lock
 }
 
+void TrackballInputMapper::configure() {
+    InputMapper::configure();
+
+    // Configure basic parameters.
+    configureParameters();
+}
+
+void TrackballInputMapper::configureParameters() {
+    mParameters.orientationAware = false;
+    getDevice()->getConfiguration().tryGetProperty(String8("trackball.orientationAware"),
+            mParameters.orientationAware);
+
+    mParameters.associatedDisplayId = mParameters.orientationAware ? 0 : -1;
+}
+
+void TrackballInputMapper::dumpParameters(String8& dump) {
+    dump.append(INDENT3 "Parameters:\n");
+    dump.appendFormat(INDENT4 "AssociatedDisplayId: %d\n",
+            mParameters.associatedDisplayId);
+    dump.appendFormat(INDENT4 "OrientationAware: %s\n",
+            toString(mParameters.orientationAware));
+}
+
 void TrackballInputMapper::initializeLocked() {
     mAccumulator.clear();
 
@@ -1155,11 +1142,13 @@
         pointerCoords.toolMinor = 0;
         pointerCoords.orientation = 0;
 
-        if (mAssociatedDisplayId >= 0 && (x != 0.0f || y != 0.0f)) {
+        if (mParameters.orientationAware && mParameters.associatedDisplayId >= 0
+                && (x != 0.0f || y != 0.0f)) {
             // Rotate motion based on display orientation if needed.
             // Note: getDisplayInfo is non-reentrant so we can continue holding the lock.
             int32_t orientation;
-            if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, NULL, NULL, & orientation)) {
+            if (! getPolicy()->getDisplayInfo(mParameters.associatedDisplayId,
+                    NULL, NULL, & orientation)) {
                 orientation = InputReaderPolicyInterface::ROTATION_0;
             }
 
@@ -1205,8 +1194,8 @@
 
 // --- TouchInputMapper ---
 
-TouchInputMapper::TouchInputMapper(InputDevice* device, int32_t associatedDisplayId) :
-        InputMapper(device), mAssociatedDisplayId(associatedDisplayId) {
+TouchInputMapper::TouchInputMapper(InputDevice* device) :
+        InputMapper(device) {
     mLocked.surfaceOrientation = -1;
     mLocked.surfaceWidth = -1;
     mLocked.surfaceHeight = -1;
@@ -1218,7 +1207,15 @@
 }
 
 uint32_t TouchInputMapper::getSources() {
-    return mAssociatedDisplayId >= 0 ? AINPUT_SOURCE_TOUCHSCREEN : AINPUT_SOURCE_TOUCHPAD;
+    switch (mParameters.deviceType) {
+    case Parameters::DEVICE_TYPE_TOUCH_SCREEN:
+        return AINPUT_SOURCE_TOUCHSCREEN;
+    case Parameters::DEVICE_TYPE_TOUCH_PAD:
+        return AINPUT_SOURCE_TOUCHPAD;
+    default:
+        assert(false);
+        return AINPUT_SOURCE_UNKNOWN;
+    }
 }
 
 void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
@@ -1269,7 +1266,6 @@
     { // acquire lock
         AutoMutex _l(mLock);
         dump.append(INDENT2 "Touch Input Mapper:\n");
-        dump.appendFormat(INDENT3 "AssociatedDisplayId: %d\n", mAssociatedDisplayId);
         dumpParameters(dump);
         dumpVirtualKeysLocked(dump);
         dumpRawAxes(dump);
@@ -1339,14 +1335,50 @@
     mParameters.useBadTouchFilter = getPolicy()->filterTouchEvents();
     mParameters.useAveragingTouchFilter = getPolicy()->filterTouchEvents();
     mParameters.useJumpyTouchFilter = getPolicy()->filterJumpyTouchEvents();
+
+    String8 deviceTypeString;
+    mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN;
+    if (getDevice()->getConfiguration().tryGetProperty(String8("touch.deviceType"),
+            deviceTypeString)) {
+        if (deviceTypeString == "touchPad") {
+            mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD;
+        } else if (deviceTypeString != "touchScreen") {
+            LOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.string());
+        }
+    }
+    bool isTouchScreen = mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN;
+
+    mParameters.orientationAware = isTouchScreen;
+    getDevice()->getConfiguration().tryGetProperty(String8("touch.orientationAware"),
+            mParameters.orientationAware);
+
+    mParameters.associatedDisplayId = mParameters.orientationAware || isTouchScreen ? 0 : -1;
 }
 
 void TouchInputMapper::dumpParameters(String8& dump) {
-    dump.appendFormat(INDENT3 "UseBadTouchFilter: %s\n",
+    dump.append(INDENT3 "Parameters:\n");
+
+    switch (mParameters.deviceType) {
+    case Parameters::DEVICE_TYPE_TOUCH_SCREEN:
+        dump.append(INDENT4 "DeviceType: touchScreen\n");
+        break;
+    case Parameters::DEVICE_TYPE_TOUCH_PAD:
+        dump.append(INDENT4 "DeviceType: touchPad\n");
+        break;
+    default:
+        assert(false);
+    }
+
+    dump.appendFormat(INDENT4 "AssociatedDisplayId: %d\n",
+            mParameters.associatedDisplayId);
+    dump.appendFormat(INDENT4 "OrientationAware: %s\n",
+            toString(mParameters.orientationAware));
+
+    dump.appendFormat(INDENT4 "UseBadTouchFilter: %s\n",
             toString(mParameters.useBadTouchFilter));
-    dump.appendFormat(INDENT3 "UseAveragingTouchFilter: %s\n",
+    dump.appendFormat(INDENT4 "UseAveragingTouchFilter: %s\n",
             toString(mParameters.useAveragingTouchFilter));
-    dump.appendFormat(INDENT3 "UseJumpyTouchFilter: %s\n",
+    dump.appendFormat(INDENT4 "UseJumpyTouchFilter: %s\n",
             toString(mParameters.useJumpyTouchFilter));
 }
 
@@ -1384,17 +1416,20 @@
 
 bool TouchInputMapper::configureSurfaceLocked() {
     // Update orientation and dimensions if needed.
-    int32_t orientation;
-    int32_t width, height;
-    if (mAssociatedDisplayId >= 0) {
+    int32_t orientation = InputReaderPolicyInterface::ROTATION_0;
+    int32_t width = mRawAxes.x.getRange();
+    int32_t height = mRawAxes.y.getRange();
+
+    if (mParameters.associatedDisplayId >= 0) {
+        bool wantSize = mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN;
+        bool wantOrientation = mParameters.orientationAware;
+
         // Note: getDisplayInfo is non-reentrant so we can continue holding the lock.
-        if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, & width, & height, & orientation)) {
+        if (! getPolicy()->getDisplayInfo(mParameters.associatedDisplayId,
+                wantSize ? &width : NULL, wantSize ? &height : NULL,
+                wantOrientation ? &orientation : NULL)) {
             return false;
         }
-    } else {
-        orientation = InputReaderPolicyInterface::ROTATION_0;
-        width = mRawAxes.x.getRange();
-        height = mRawAxes.y.getRange();
     }
 
     bool orientationChanged = mLocked.surfaceOrientation != orientation;
@@ -1686,7 +1721,7 @@
 }
 
 void TouchInputMapper::parseCalibration() {
-    const InputDeviceCalibration& in = getDevice()->getCalibration();
+    const PropertyMap& in = getDevice()->getConfiguration();
     Calibration& out = mCalibration;
 
     // Position
@@ -1972,8 +2007,8 @@
     }
 
     if (mCalibration.haveToolSizeIsSummed) {
-        dump.appendFormat(INDENT4 "touch.toolSize.isSummed: %d\n",
-                mCalibration.toolSizeIsSummed);
+        dump.appendFormat(INDENT4 "touch.toolSize.isSummed: %s\n",
+                toString(mCalibration.toolSizeIsSummed));
     }
 
     // Pressure
@@ -3157,8 +3192,8 @@
 
 // --- SingleTouchInputMapper ---
 
-SingleTouchInputMapper::SingleTouchInputMapper(InputDevice* device, int32_t associatedDisplayId) :
-        TouchInputMapper(device, associatedDisplayId) {
+SingleTouchInputMapper::SingleTouchInputMapper(InputDevice* device) :
+        TouchInputMapper(device) {
     initialize();
 }
 
@@ -3286,8 +3321,8 @@
 
 // --- MultiTouchInputMapper ---
 
-MultiTouchInputMapper::MultiTouchInputMapper(InputDevice* device, int32_t associatedDisplayId) :
-        TouchInputMapper(device, associatedDisplayId) {
+MultiTouchInputMapper::MultiTouchInputMapper(InputDevice* device) :
+        TouchInputMapper(device) {
     initialize();
 }
 
diff --git a/libs/ui/Keyboard.cpp b/libs/ui/Keyboard.cpp
index de76e25..a4cc22d 100644
--- a/libs/ui/Keyboard.cpp
+++ b/libs/ui/Keyboard.cpp
@@ -28,74 +28,79 @@
 
 namespace android {
 
-static void selectKeyMap(KeyMapInfo& keyMapInfo, const String8& keyMapName, bool defaultKeyMap) {
-    if (keyMapInfo.keyMapName.isEmpty()) {
-        keyMapInfo.keyMapName.setTo(keyMapName);
-        keyMapInfo.isDefaultKeyMap = defaultKeyMap;
-    }
-}
-
 static bool probeKeyMap(KeyMapInfo& keyMapInfo, const String8& keyMapName, bool defaultKeyMap) {
-    const char* root = getenv("ANDROID_ROOT");
-
-    // TODO Consider also looking somewhere in a writeable partition like /data for a
-    //      custom keymap supplied by the user for this device.
-    bool haveKeyLayout = !keyMapInfo.keyLayoutFile.isEmpty();
-    if (!haveKeyLayout) {
-        keyMapInfo.keyLayoutFile.setTo(root);
-        keyMapInfo.keyLayoutFile.append("/usr/keylayout/");
-        keyMapInfo.keyLayoutFile.append(keyMapName);
-        keyMapInfo.keyLayoutFile.append(".kl");
-        if (access(keyMapInfo.keyLayoutFile.string(), R_OK)) {
-            keyMapInfo.keyLayoutFile.clear();
-        } else {
-            haveKeyLayout = true;
+    bool foundOne = false;
+    if (keyMapInfo.keyLayoutFile.isEmpty()) {
+        keyMapInfo.keyLayoutFile.setTo(getInputDeviceConfigurationFilePath(keyMapName,
+                INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT));
+        if (!keyMapInfo.keyLayoutFile.isEmpty()) {
+            foundOne = true;
         }
     }
 
-    bool haveKeyCharacterMap = !keyMapInfo.keyCharacterMapFile.isEmpty();
-    if (!haveKeyCharacterMap) {
-        keyMapInfo.keyCharacterMapFile.setTo(root);
-        keyMapInfo.keyCharacterMapFile.append("/usr/keychars/");
-        keyMapInfo.keyCharacterMapFile.append(keyMapName);
-        keyMapInfo.keyCharacterMapFile.append(".kcm");
-        if (access(keyMapInfo.keyCharacterMapFile.string(), R_OK)) {
-            keyMapInfo.keyCharacterMapFile.clear();
-        } else {
-            haveKeyCharacterMap = true;
+    if (keyMapInfo.keyCharacterMapFile.isEmpty()) {
+        keyMapInfo.keyCharacterMapFile.setTo(getInputDeviceConfigurationFilePath(keyMapName,
+                INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP));
+        if (!keyMapInfo.keyCharacterMapFile.isEmpty()) {
+            foundOne = true;
         }
     }
 
-    if (haveKeyLayout || haveKeyCharacterMap) {
-        selectKeyMap(keyMapInfo, keyMapName, defaultKeyMap);
+    if (foundOne && defaultKeyMap) {
+        keyMapInfo.isDefaultKeyMap = true;
     }
-    return haveKeyLayout && haveKeyCharacterMap;
+    return keyMapInfo.isComplete();
 }
 
-status_t resolveKeyMap(const String8& deviceName, KeyMapInfo& outKeyMapInfo) {
-    // As an initial key map name, try using the device name.
-    String8 keyMapName(deviceName);
-    char* p = keyMapName.lockBuffer(keyMapName.size());
-    while (*p) {
-        if (*p == ' ') *p = '_';
-        p++;
+status_t resolveKeyMap(const String8& deviceName,
+        const PropertyMap* deviceConfiguration, KeyMapInfo& outKeyMapInfo) {
+    // Use the configured key layout if available.
+    if (deviceConfiguration) {
+        String8 keyLayoutName;
+        if (deviceConfiguration->tryGetProperty(String8("keyboard.layout"),
+                keyLayoutName)) {
+            outKeyMapInfo.keyLayoutFile.setTo(getInputDeviceConfigurationFilePath(
+                    keyLayoutName, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT));
+            if (outKeyMapInfo.keyLayoutFile.isEmpty()) {
+                LOGW("Configuration for keyboard device '%s' requested keyboard layout '%s' but "
+                        "it was not found.",
+                        deviceName.string(), keyLayoutName.string());
+            }
+        }
+
+        String8 keyCharacterMapName;
+        if (deviceConfiguration->tryGetProperty(String8("keyboard.characterMap"),
+                keyCharacterMapName)) {
+            outKeyMapInfo.keyCharacterMapFile.setTo(getInputDeviceConfigurationFilePath(
+                    keyCharacterMapName, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP));
+            if (outKeyMapInfo.keyCharacterMapFile.isEmpty()) {
+                LOGW("Configuration for keyboard device '%s' requested keyboard character "
+                        "map '%s' but it was not found.",
+                        deviceName.string(), keyCharacterMapName.string());
+            }
+        }
+
+        if (outKeyMapInfo.isComplete()) {
+            return OK;
+        }
     }
-    keyMapName.unlockBuffer();
 
-    if (probeKeyMap(outKeyMapInfo, keyMapName, false)) return OK;
+    // Try searching by device name.
+    if (probeKeyMap(outKeyMapInfo, deviceName, false)) {
+        return OK;
+    }
 
-    // TODO Consider allowing the user to configure a specific key map somehow.
-
-    // Try the Generic key map.
+    // 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.).
-    keyMapName.setTo("Generic");
-    if (probeKeyMap(outKeyMapInfo, keyMapName, true)) return OK;
+    if (probeKeyMap(outKeyMapInfo, String8("Generic"), true)) {
+        return OK;
+    }
 
     // Give up!
-    keyMapName.setTo("unknown");
-    selectKeyMap(outKeyMapInfo, keyMapName, true);
-    LOGE("Could not determine key map for device '%s'.", deviceName.string());
+    LOGE("Could not determine key map for device '%s' and the Generic key map was not found!",
+            deviceName.string());
+    outKeyMapInfo.isDefaultKeyMap = true;
     return NAME_NOT_FOUND;
 }
 
@@ -104,8 +109,6 @@
     char propName[PROPERTY_KEY_MAX];
     snprintf(propName, sizeof(propName), "hw.keyboards.%u.devname", deviceId);
     property_set(propName, deviceName.string());
-    snprintf(propName, sizeof(propName), "hw.keyboards.%u.keymap", deviceId);
-    property_set(propName, keyMapInfo.keyMapName.string());
     snprintf(propName, sizeof(propName), "hw.keyboards.%u.klfile", deviceId);
     property_set(propName, keyMapInfo.keyLayoutFile.string());
     snprintf(propName, sizeof(propName), "hw.keyboards.%u.kcmfile", deviceId);
@@ -116,8 +119,6 @@
     char propName[PROPERTY_KEY_MAX];
     snprintf(propName, sizeof(propName), "hw.keyboards.%u.devname", deviceId);
     property_set(propName, "");
-    snprintf(propName, sizeof(propName), "hw.keyboards.%u.keymap", deviceId);
-    property_set(propName, "");
     snprintf(propName, sizeof(propName), "hw.keyboards.%u.klfile", deviceId);
     property_set(propName, "");
     snprintf(propName, sizeof(propName), "hw.keyboards.%u.kcmfile", deviceId);
@@ -125,6 +126,14 @@
 }
 
 status_t getKeyCharacterMapFile(int32_t deviceId, String8& outKeyCharacterMapFile) {
+    if (deviceId == DEVICE_ID_VIRTUAL_KEYBOARD) {
+        outKeyCharacterMapFile.setTo(getInputDeviceConfigurationFilePath(String8("Virtual"),
+                INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP));
+        if (!outKeyCharacterMapFile.isEmpty()) {
+            return OK;
+        }
+    }
+
     char propName[PROPERTY_KEY_MAX];
     char fn[PROPERTY_VALUE_MAX];
     snprintf(propName, sizeof(propName), "hw.keyboards.%u.kcmfile", deviceId);
@@ -133,23 +142,13 @@
         return OK;
     }
 
-    const char* root = getenv("ANDROID_ROOT");
-    char path[PATH_MAX];
-    if (deviceId == DEVICE_ID_VIRTUAL_KEYBOARD) {
-        snprintf(path, sizeof(path), "%s/usr/keychars/Virtual.kcm", root);
-        if (!access(path, R_OK)) {
-            outKeyCharacterMapFile.setTo(path);
-            return OK;
-        }
-    }
-
-    snprintf(path, sizeof(path), "%s/usr/keychars/Generic.kcm", root);
-    if (!access(path, R_OK)) {
-        outKeyCharacterMapFile.setTo(path);
+    outKeyCharacterMapFile.setTo(getInputDeviceConfigurationFilePath(String8("Generic"),
+            INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP));
+    if (!outKeyCharacterMapFile.isEmpty()) {
         return OK;
     }
 
-    LOGE("Can't find any key character map files (also tried %s)", path);
+    LOGE("Can't find any key character map files (also tried Virtual and Generic key maps)");
     return NAME_NOT_FOUND;
 }
 
diff --git a/libs/ui/tests/InputDispatcher_test.cpp b/libs/ui/tests/InputDispatcher_test.cpp
index f352dbf..68f9037 100644
--- a/libs/ui/tests/InputDispatcher_test.cpp
+++ b/libs/ui/tests/InputDispatcher_test.cpp
@@ -54,9 +54,7 @@
         return 60;
     }
 
-    virtual void interceptKeyBeforeQueueing(nsecs_t when, int32_t deviceId,
-            int32_t action, int32_t& flags, int32_t keyCode, int32_t scanCode,
-            uint32_t& policyFlags) {
+    virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags) {
     }
 
     virtual void interceptGenericBeforeQueueing(nsecs_t when, uint32_t& policyFlags) {
diff --git a/libs/ui/tests/InputReader_test.cpp b/libs/ui/tests/InputReader_test.cpp
index ded0225..05bebc5 100644
--- a/libs/ui/tests/InputReader_test.cpp
+++ b/libs/ui/tests/InputReader_test.cpp
@@ -43,7 +43,6 @@
     bool mFilterTouchEvents;
     bool mFilterJumpyTouchEvents;
     KeyedVector<String8, Vector<VirtualKeyDefinition> > mVirtualKeyDefinitions;
-    KeyedVector<String8, InputDeviceCalibration> mInputDeviceCalibrations;
     Vector<String8> mExcludedDeviceNames;
 
 protected:
@@ -76,20 +75,6 @@
         mFilterJumpyTouchEvents = enabled;
     }
 
-    void addInputDeviceCalibration(const String8& deviceName,
-            const InputDeviceCalibration& calibration) {
-        mInputDeviceCalibrations.add(deviceName, calibration);
-    }
-
-    void addInputDeviceCalibrationProperty(const String8& deviceName,
-            const String8& key, const String8& value) {
-        ssize_t index = mInputDeviceCalibrations.indexOfKey(deviceName);
-        if (index < 0) {
-            index = mInputDeviceCalibrations.add(deviceName, InputDeviceCalibration());
-        }
-        mInputDeviceCalibrations.editValueAt(index).addProperty(key, value);
-    }
-
     void addVirtualKeyDefinition(const String8& deviceName,
             const VirtualKeyDefinition& definition) {
         if (mVirtualKeyDefinitions.indexOfKey(deviceName) < 0) {
@@ -139,14 +124,6 @@
         }
     }
 
-    virtual void getInputDeviceCalibration(const String8& deviceName,
-            InputDeviceCalibration& outCalibration) {
-        ssize_t index = mInputDeviceCalibrations.indexOfKey(deviceName);
-        if (index >= 0) {
-            outCalibration = mInputDeviceCalibrations.valueAt(index);
-        }
-    }
-
     virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames) {
         outExcludedDeviceNames.appendVector(mExcludedDeviceNames);
     }
@@ -371,6 +348,7 @@
     struct Device {
         String8 name;
         uint32_t classes;
+        PropertyMap configuration;
         KeyedVector<int, RawAbsoluteAxisInfo> axes;
         KeyedVector<int32_t, int32_t> keyCodeStates;
         KeyedVector<int32_t, int32_t> scanCodeStates;
@@ -415,6 +393,11 @@
         enqueueEvent(ARBITRARY_TIME, 0, EventHubInterface::FINISHED_DEVICE_SCAN, 0, 0, 0, 0);
     }
 
+    void addConfigurationProperty(int32_t deviceId, const String8& key, const String8& value) {
+        Device* device = getDevice(deviceId);
+        device->configuration.addProperty(key, value);
+    }
+
     void addAxis(int32_t deviceId, int axis,
             int32_t minValue, int32_t maxValue, int flat, int fuzz) {
         Device* device = getDevice(deviceId);
@@ -499,6 +482,13 @@
         return device ? device->name : String8("unknown");
     }
 
+    virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const {
+        Device* device = getDevice(deviceId);
+        if (device) {
+            *outConfiguration = device->configuration;
+        }
+    }
+
     virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
             RawAbsoluteAxisInfo* outAxisInfo) const {
         Device* device = getDevice(deviceId);
@@ -1208,9 +1198,7 @@
 
 TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRequestsToMappers) {
     // Configuration.
-    InputDeviceCalibration calibration;
-    calibration.addProperty(String8("key"), String8("value"));
-    mFakePolicy->addInputDeviceCalibration(String8(DEVICE_NAME), calibration);
+    mFakeEventHub->addConfigurationProperty(DEVICE_ID, String8("key"), String8("value"));
 
     FakeInputMapper* mapper1 = new FakeInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD);
     mapper1->setKeyboardType(AINPUT_KEYBOARD_TYPE_ALPHABETIC);
@@ -1231,8 +1219,8 @@
     mDevice->configure();
 
     String8 propertyValue;
-    ASSERT_TRUE(mDevice->getCalibration().tryGetProperty(String8("key"), propertyValue))
-            << "Device should have read calibration during configuration phase.";
+    ASSERT_TRUE(mDevice->getConfiguration().tryGetProperty(String8("key"), propertyValue))
+            << "Device should have read configuration during configuration phase.";
     ASSERT_STREQ("value", propertyValue.string());
 
     ASSERT_NO_FATAL_FAILURE(mapper1->assertConfigureWasCalled());
@@ -1329,9 +1317,8 @@
         mFakeEventHub.clear();
     }
 
-    void prepareCalibration(const char* key, const char* value) {
-        mFakePolicy->addInputDeviceCalibrationProperty(String8(DEVICE_NAME),
-                String8(key), String8(value));
+    void addConfigurationProperty(const char* key, const char* value) {
+        mFakeEventHub->addConfigurationProperty(DEVICE_ID, String8(key), String8(value));
     }
 
     void addMapperAndConfigure(InputMapper* mapper) {
@@ -1448,7 +1435,7 @@
 
 
 TEST_F(KeyboardInputMapperTest, GetSources) {
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1,
+    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
             AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
     addMapperAndConfigure(mapper);
 
@@ -1456,7 +1443,7 @@
 }
 
 TEST_F(KeyboardInputMapperTest, Process_SimpleKeyPress) {
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1,
+    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
             AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
     addMapperAndConfigure(mapper);
 
@@ -1493,7 +1480,7 @@
 }
 
 TEST_F(KeyboardInputMapperTest, Reset_WhenKeysAreNotDown_DoesNotSynthesizeKeyUp) {
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1,
+    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
             AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
     addMapperAndConfigure(mapper);
 
@@ -1513,7 +1500,7 @@
 }
 
 TEST_F(KeyboardInputMapperTest, Reset_WhenKeysAreDown_SynthesizesKeyUps) {
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1,
+    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
             AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
     addMapperAndConfigure(mapper);
 
@@ -1558,7 +1545,7 @@
 }
 
 TEST_F(KeyboardInputMapperTest, Process_ShouldUpdateMetaState) {
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1,
+    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
             AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
     addMapperAndConfigure(mapper);
 
@@ -1597,11 +1584,14 @@
     ASSERT_NO_FATAL_FAILURE(mFakeContext->assertUpdateGlobalMetaStateWasCalled());
 }
 
-TEST_F(KeyboardInputMapperTest, Process_WhenNotAttachedToDisplay_ShouldNotRotateDPad) {
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1,
+TEST_F(KeyboardInputMapperTest, Process_WhenNotOrientationAware_ShouldNotRotateDPad) {
+    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
             AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
     addMapperAndConfigure(mapper);
 
+    mFakePolicy->setDisplayInfo(DISPLAY_ID,
+            DISPLAY_WIDTH, DISPLAY_HEIGHT,
+            InputReaderPolicyInterface::ROTATION_90);
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
             KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP));
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
@@ -1612,9 +1602,10 @@
             KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_LEFT));
 }
 
-TEST_F(KeyboardInputMapperTest, Process_WhenAttachedToDisplay_ShouldRotateDPad) {
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, DISPLAY_ID,
+TEST_F(KeyboardInputMapperTest, Process_WhenOrientationAware_ShouldRotateDPad) {
+    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
             AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+    addConfigurationProperty("keyboard.orientationAware", "1");
     addMapperAndConfigure(mapper);
 
     mFakePolicy->setDisplayInfo(DISPLAY_ID,
@@ -1689,7 +1680,7 @@
 }
 
 TEST_F(KeyboardInputMapperTest, GetKeyCodeState) {
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1,
+    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
             AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
     addMapperAndConfigure(mapper);
 
@@ -1701,7 +1692,7 @@
 }
 
 TEST_F(KeyboardInputMapperTest, GetScanCodeState) {
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1,
+    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
             AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
     addMapperAndConfigure(mapper);
 
@@ -1713,7 +1704,7 @@
 }
 
 TEST_F(KeyboardInputMapperTest, MarkSupportedKeyCodes) {
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1,
+    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
             AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
     addMapperAndConfigure(mapper);
 
@@ -1731,7 +1722,7 @@
     mFakeEventHub->addLed(DEVICE_ID, LED_NUML, false /*initially off*/);
     mFakeEventHub->addLed(DEVICE_ID, LED_SCROLLL, false /*initially off*/);
 
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1,
+    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
             AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
     addMapperAndConfigure(mapper);
 
@@ -1830,14 +1821,14 @@
 }
 
 TEST_F(TrackballInputMapperTest, GetSources) {
-    TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1);
+    TrackballInputMapper* mapper = new TrackballInputMapper(mDevice);
     addMapperAndConfigure(mapper);
 
     ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, mapper->getSources());
 }
 
 TEST_F(TrackballInputMapperTest, PopulateDeviceInfo) {
-    TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1);
+    TrackballInputMapper* mapper = new TrackballInputMapper(mDevice);
     addMapperAndConfigure(mapper);
 
     InputDeviceInfo info;
@@ -1850,7 +1841,7 @@
 }
 
 TEST_F(TrackballInputMapperTest, Process_ShouldSetAllFieldsAndIncludeGlobalMetaState) {
-    TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1);
+    TrackballInputMapper* mapper = new TrackballInputMapper(mDevice);
     addMapperAndConfigure(mapper);
 
     mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
@@ -1898,7 +1889,7 @@
 }
 
 TEST_F(TrackballInputMapperTest, Process_ShouldHandleIndependentXYUpdates) {
-    TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1);
+    TrackballInputMapper* mapper = new TrackballInputMapper(mDevice);
     addMapperAndConfigure(mapper);
 
     FakeInputDispatcher::NotifyMotionArgs args;
@@ -1922,7 +1913,7 @@
 }
 
 TEST_F(TrackballInputMapperTest, Process_ShouldHandleIndependentButtonUpdates) {
-    TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1);
+    TrackballInputMapper* mapper = new TrackballInputMapper(mDevice);
     addMapperAndConfigure(mapper);
 
     FakeInputDispatcher::NotifyMotionArgs args;
@@ -1943,7 +1934,7 @@
 }
 
 TEST_F(TrackballInputMapperTest, Process_ShouldHandleCombinedXYAndButtonUpdates) {
-    TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1);
+    TrackballInputMapper* mapper = new TrackballInputMapper(mDevice);
     addMapperAndConfigure(mapper);
 
     FakeInputDispatcher::NotifyMotionArgs args;
@@ -1978,7 +1969,7 @@
 }
 
 TEST_F(TrackballInputMapperTest, Reset_WhenButtonIsNotDown_ShouldNotSynthesizeButtonUp) {
-    TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1);
+    TrackballInputMapper* mapper = new TrackballInputMapper(mDevice);
     addMapperAndConfigure(mapper);
 
     FakeInputDispatcher::NotifyMotionArgs args;
@@ -1998,7 +1989,7 @@
 }
 
 TEST_F(TrackballInputMapperTest, Reset_WhenButtonIsDown_ShouldSynthesizeButtonUp) {
-    TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1);
+    TrackballInputMapper* mapper = new TrackballInputMapper(mDevice);
     addMapperAndConfigure(mapper);
 
     FakeInputDispatcher::NotifyMotionArgs args;
@@ -2016,10 +2007,13 @@
             0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 }
 
-TEST_F(TrackballInputMapperTest, Process_WhenNotAttachedToDisplay_ShouldNotRotateMotions) {
-    TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1);
+TEST_F(TrackballInputMapperTest, Process_WhenNotOrientationAware_ShouldNotRotateMotions) {
+    TrackballInputMapper* mapper = new TrackballInputMapper(mDevice);
     addMapperAndConfigure(mapper);
 
+    mFakePolicy->setDisplayInfo(DISPLAY_ID,
+            DISPLAY_WIDTH, DISPLAY_HEIGHT,
+            InputReaderPolicyInterface::ROTATION_90);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  0,  1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1,  1,  1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  0,  1,  0));
@@ -2030,8 +2024,9 @@
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1, -1,  1));
 }
 
-TEST_F(TrackballInputMapperTest, Process_WhenAttachedToDisplay_ShouldRotateMotions) {
-    TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, DISPLAY_ID);
+TEST_F(TrackballInputMapperTest, Process_WhenOrientationAware_ShouldRotateMotions) {
+    TrackballInputMapper* mapper = new TrackballInputMapper(mDevice);
+    addConfigurationProperty("trackball.orientationAware", "1");
     addMapperAndConfigure(mapper);
 
     mFakePolicy->setDisplayInfo(DISPLAY_ID,
@@ -2232,24 +2227,26 @@
 }
 
 
-TEST_F(SingleTouchInputMapperTest, GetSources_WhenNotAttachedToADisplay_ReturnsTouchPad) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, -1);
+TEST_F(SingleTouchInputMapperTest, GetSources_WhenDisplayTypeIsTouchPad_ReturnsTouchPad) {
+    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     prepareAxes(POSITION);
+    addConfigurationProperty("touch.displayType", "touchPad");
     addMapperAndConfigure(mapper);
 
     ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper->getSources());
 }
 
-TEST_F(SingleTouchInputMapperTest, GetSources_WhenAttachedToADisplay_ReturnsTouchScreen) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID);
+TEST_F(SingleTouchInputMapperTest, GetSources_WhenDisplayTypeIsTouchScreen_ReturnsTouchScreen) {
+    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     prepareAxes(POSITION);
+    addConfigurationProperty("touch.displayType", "touchScreen");
     addMapperAndConfigure(mapper);
 
     ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper->getSources());
 }
 
 TEST_F(SingleTouchInputMapperTest, GetKeyCodeState) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID);
+    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
     prepareAxes(POSITION);
     prepareVirtualKeys();
@@ -2276,7 +2273,7 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, GetScanCodeState) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID);
+    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
     prepareAxes(POSITION);
     prepareVirtualKeys();
@@ -2303,7 +2300,7 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, MarkSupportedKeyCodes) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID);
+    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
     prepareAxes(POSITION);
     prepareVirtualKeys();
@@ -2319,7 +2316,7 @@
 TEST_F(SingleTouchInputMapperTest, Reset_WhenVirtualKeysAreDown_SendsUp) {
     // Note: Ideally we should send cancels but the implementation is more straightforward
     // with up and this will only happen if a device is forcibly removed.
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID);
+    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
     prepareAxes(POSITION);
     prepareVirtualKeys();
@@ -2352,7 +2349,7 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, Reset_WhenNothingIsPressed_NothingMuchHappens) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID);
+    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
     prepareAxes(POSITION);
     prepareVirtualKeys();
@@ -2378,7 +2375,7 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndReleasedNormally_SendsKeyDownAndKeyUp) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID);
+    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
     prepareAxes(POSITION);
     prepareVirtualKeys();
@@ -2427,7 +2424,7 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndMovedOutOfBounds_SendsKeyDownAndKeyCancel) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID);
+    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
     prepareAxes(POSITION);
     prepareVirtualKeys();
@@ -2541,7 +2538,7 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_WhenTouchStartsOutsideDisplayAndMovesIn_SendsDownAsTouchEntersDisplay) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID);
+    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
     prepareAxes(POSITION);
     prepareVirtualKeys();
@@ -2609,7 +2606,7 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_NormalSingleTouchGesture) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID);
+    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
     prepareAxes(POSITION);
     prepareVirtualKeys();
@@ -2691,8 +2688,30 @@
     ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasNotCalled());
 }
 
-TEST_F(SingleTouchInputMapperTest, Process_Rotation) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID);
+TEST_F(SingleTouchInputMapperTest, Process_WhenNotOrientationAware_DoesNotRotateMotions) {
+    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
+    prepareAxes(POSITION);
+    addConfigurationProperty("touch.orientationAware", "0");
+    addMapperAndConfigure(mapper);
+
+    FakeInputDispatcher::NotifyMotionArgs args;
+
+    // Rotation 90.
+    prepareDisplay(InputReaderPolicyInterface::ROTATION_90);
+    processDown(mapper, toRawX(50), toRawY(75));
+    processSync(mapper);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+    ASSERT_NEAR(50, args.pointerCoords[0].x, 1);
+    ASSERT_NEAR(75, args.pointerCoords[0].y, 1);
+
+    processUp(mapper);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled());
+}
+
+TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationAware_RotatesMotions) {
+    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     prepareAxes(POSITION);
     addMapperAndConfigure(mapper);
 
@@ -2752,7 +2771,7 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_AllAxes_DefaultCalibration) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID);
+    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
     prepareAxes(POSITION | PRESSURE | TOOL);
     addMapperAndConfigure(mapper);
@@ -2884,7 +2903,7 @@
 
 
 TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithoutTrackingIds) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice, DISPLAY_ID);
+    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
     prepareAxes(POSITION);
     prepareVirtualKeys();
@@ -3135,7 +3154,7 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithTrackingIds) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice, DISPLAY_ID);
+    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
     prepareAxes(POSITION | ID);
     prepareVirtualKeys();
@@ -3295,7 +3314,7 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_AllAxes_WithDefaultCalibration) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice, DISPLAY_ID);
+    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
     prepareAxes(POSITION | TOUCH | TOOL | PRESSURE | ORIENTATION | ID | MINOR);
     addMapperAndConfigure(mapper);
@@ -3340,11 +3359,11 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_GeometricCalibration) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice, DISPLAY_ID);
+    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
     prepareAxes(POSITION | TOUCH | TOOL | MINOR);
-    prepareCalibration("touch.touchSize.calibration", "geometric");
-    prepareCalibration("touch.toolSize.calibration", "geometric");
+    addConfigurationProperty("touch.touchSize.calibration", "geometric");
+    addConfigurationProperty("touch.toolSize.calibration", "geometric");
     addMapperAndConfigure(mapper);
 
     // These calculations are based on the input device calibration documentation.
@@ -3381,17 +3400,17 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_TouchToolPressureSizeAxes_SummedLinearCalibration) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice, DISPLAY_ID);
+    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
     prepareAxes(POSITION | TOUCH | TOOL);
-    prepareCalibration("touch.touchSize.calibration", "pressure");
-    prepareCalibration("touch.toolSize.calibration", "linear");
-    prepareCalibration("touch.toolSize.linearScale", "10");
-    prepareCalibration("touch.toolSize.linearBias", "160");
-    prepareCalibration("touch.toolSize.isSummed", "1");
-    prepareCalibration("touch.pressure.calibration", "amplitude");
-    prepareCalibration("touch.pressure.source", "touch");
-    prepareCalibration("touch.pressure.scale", "0.01");
+    addConfigurationProperty("touch.touchSize.calibration", "pressure");
+    addConfigurationProperty("touch.toolSize.calibration", "linear");
+    addConfigurationProperty("touch.toolSize.linearScale", "10");
+    addConfigurationProperty("touch.toolSize.linearBias", "160");
+    addConfigurationProperty("touch.toolSize.isSummed", "1");
+    addConfigurationProperty("touch.pressure.calibration", "amplitude");
+    addConfigurationProperty("touch.pressure.source", "touch");
+    addConfigurationProperty("touch.pressure.scale", "0.01");
     addMapperAndConfigure(mapper);
 
     // These calculations are based on the input device calibration documentation.
@@ -3437,18 +3456,18 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_TouchToolPressureSizeAxes_AreaCalibration) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice, DISPLAY_ID);
+    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
     prepareAxes(POSITION | TOUCH | TOOL);
-    prepareCalibration("touch.touchSize.calibration", "pressure");
-    prepareCalibration("touch.toolSize.calibration", "area");
-    prepareCalibration("touch.toolSize.areaScale", "22");
-    prepareCalibration("touch.toolSize.areaBias", "1");
-    prepareCalibration("touch.toolSize.linearScale", "9.2");
-    prepareCalibration("touch.toolSize.linearBias", "3");
-    prepareCalibration("touch.pressure.calibration", "amplitude");
-    prepareCalibration("touch.pressure.source", "touch");
-    prepareCalibration("touch.pressure.scale", "0.01");
+    addConfigurationProperty("touch.touchSize.calibration", "pressure");
+    addConfigurationProperty("touch.toolSize.calibration", "area");
+    addConfigurationProperty("touch.toolSize.areaScale", "22");
+    addConfigurationProperty("touch.toolSize.areaBias", "1");
+    addConfigurationProperty("touch.toolSize.linearScale", "9.2");
+    addConfigurationProperty("touch.toolSize.linearBias", "3");
+    addConfigurationProperty("touch.pressure.calibration", "amplitude");
+    addConfigurationProperty("touch.pressure.source", "touch");
+    addConfigurationProperty("touch.pressure.scale", "0.01");
     addMapperAndConfigure(mapper);
 
     // These calculations are based on the input device calibration documentation.
diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk
index 9c01aea..8bd833b 100644
--- a/libs/utils/Android.mk
+++ b/libs/utils/Android.mk
@@ -28,6 +28,7 @@
 	Flattenable.cpp \
 	ObbFile.cpp \
 	Pool.cpp \
+	PropertyMap.cpp \
 	RefBase.cpp \
 	ResourceTypes.cpp \
 	SharedBuffer.cpp \
diff --git a/libs/utils/PropertyMap.cpp b/libs/utils/PropertyMap.cpp
new file mode 100644
index 0000000..fd7edec
--- /dev/null
+++ b/libs/utils/PropertyMap.cpp
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "PropertyMap"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <utils/PropertyMap.h>
+#include <utils/Log.h>
+
+// Enables debug output for the parser.
+#define DEBUG_PARSER 0
+
+// Enables debug output for parser performance.
+#define DEBUG_PARSER_PERFORMANCE 0
+
+
+namespace android {
+
+static const char* WHITESPACE = " \t\r";
+static const char* WHITESPACE_OR_PROPERTY_DELIMITER = " \t\r=";
+
+
+// --- PropertyMap ---
+
+PropertyMap::PropertyMap() {
+}
+
+PropertyMap::~PropertyMap() {
+}
+
+void PropertyMap::clear() {
+    mProperties.clear();
+}
+
+void PropertyMap::addProperty(const String8& key, const String8& value) {
+    mProperties.add(key, value);
+}
+
+bool PropertyMap::hasProperty(const String8& key) const {
+    return mProperties.indexOfKey(key) >= 0;
+}
+
+bool PropertyMap::tryGetProperty(const String8& key, String8& outValue) const {
+    ssize_t index = mProperties.indexOfKey(key);
+    if (index < 0) {
+        return false;
+    }
+
+    outValue = mProperties.valueAt(index);
+    return true;
+}
+
+bool PropertyMap::tryGetProperty(const String8& key, bool& outValue) const {
+    int32_t intValue;
+    if (!tryGetProperty(key, intValue)) {
+        return false;
+    }
+
+    outValue = intValue;
+    return true;
+}
+
+bool PropertyMap::tryGetProperty(const String8& key, int32_t& outValue) const {
+    String8 stringValue;
+    if (! tryGetProperty(key, stringValue) || stringValue.length() == 0) {
+        return false;
+    }
+
+    char* end;
+    int value = strtol(stringValue.string(), & end, 10);
+    if (*end != '\0') {
+        LOGW("Property key '%s' has invalid value '%s'.  Expected an integer.",
+                key.string(), stringValue.string());
+        return false;
+    }
+    outValue = value;
+    return true;
+}
+
+bool PropertyMap::tryGetProperty(const String8& key, float& outValue) const {
+    String8 stringValue;
+    if (! tryGetProperty(key, stringValue) || stringValue.length() == 0) {
+        return false;
+    }
+
+    char* end;
+    float value = strtof(stringValue.string(), & end);
+    if (*end != '\0') {
+        LOGW("Property key '%s' has invalid value '%s'.  Expected a float.",
+                key.string(), stringValue.string());
+        return false;
+    }
+    outValue = value;
+    return true;
+}
+
+status_t PropertyMap::load(const String8& filename, PropertyMap** outMap) {
+    *outMap = NULL;
+
+    Tokenizer* tokenizer;
+    status_t status = Tokenizer::open(filename, &tokenizer);
+    if (status) {
+        LOGE("Error %d opening property file %s.", status, filename.string());
+    } else {
+        PropertyMap* map = new PropertyMap();
+        if (!map) {
+            LOGE("Error allocating property map.");
+            status = NO_MEMORY;
+        } else {
+#if DEBUG_PARSER_PERFORMANCE
+            nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
+#endif
+            Parser parser(map, tokenizer);
+            status = parser.parse();
+#if DEBUG_PARSER_PERFORMANCE
+            nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
+            LOGD("Parsed property file '%s' %d lines in %0.3fms.",
+                    tokenizer->getFilename().string(), tokenizer->getLineNumber(),
+                    elapsedTime / 1000000.0);
+#endif
+            if (status) {
+                delete map;
+            } else {
+                *outMap = map;
+            }
+        }
+        delete tokenizer;
+    }
+    return status;
+}
+
+
+// --- PropertyMap::Parser ---
+
+PropertyMap::Parser::Parser(PropertyMap* map, Tokenizer* tokenizer) :
+        mMap(map), mTokenizer(tokenizer) {
+}
+
+PropertyMap::Parser::~Parser() {
+}
+
+status_t PropertyMap::Parser::parse() {
+    while (!mTokenizer->isEof()) {
+#if DEBUG_PARSER
+        LOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(),
+                mTokenizer->peekRemainderOfLine().string());
+#endif
+
+        mTokenizer->skipDelimiters(WHITESPACE);
+
+        if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
+            String8 keyToken = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER);
+            if (keyToken.isEmpty()) {
+                LOGE("%s: Expected non-empty property key.", mTokenizer->getLocation().string());
+                return BAD_VALUE;
+            }
+
+            mTokenizer->skipDelimiters(WHITESPACE);
+
+            if (mTokenizer->nextChar() != '=') {
+                LOGE("%s: Expected '=' between property key and value.",
+                        mTokenizer->getLocation().string());
+                return BAD_VALUE;
+            }
+
+            mTokenizer->skipDelimiters(WHITESPACE);
+
+            String8 valueToken = mTokenizer->nextToken(WHITESPACE);
+            if (valueToken.find("\\", 0) >= 0 || valueToken.find("\"", 0) >= 0) {
+                LOGE("%s: Found reserved character '\\' or '\"' in property value.",
+                        mTokenizer->getLocation().string());
+                return BAD_VALUE;
+            }
+
+            mTokenizer->skipDelimiters(WHITESPACE);
+            if (!mTokenizer->isEol()) {
+                LOGE("%s: Expected end of line, got '%s'.",
+                        mTokenizer->getLocation().string(),
+                        mTokenizer->peekRemainderOfLine().string());
+                return BAD_VALUE;
+            }
+
+            if (mMap->hasProperty(keyToken)) {
+                LOGE("%s: Duplicate property value for key '%s'.",
+                        mTokenizer->getLocation().string(), keyToken.string());
+                return BAD_VALUE;
+            }
+
+            mMap->addProperty(keyToken, valueToken);
+        }
+
+        mTokenizer->nextLine();
+    }
+    return NO_ERROR;
+}
+
+} // namespace android