Merge "RTP: Prepare to unhide the APIs."
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/binder/CursorWindow.cpp b/libs/binder/CursorWindow.cpp
index fbba281..47bbd04 100644
--- a/libs/binder/CursorWindow.cpp
+++ b/libs/binder/CursorWindow.cpp
@@ -219,7 +219,8 @@
 field_slot_t * CursorWindow::getFieldSlotWithCheck(int row, int column)
 {
   if (row < 0 || row >= mHeader->numRows || column < 0 || column >= mHeader->numColumns) {
-      LOGE("Bad request for field slot %d,%d. numRows = %d, numColumns = %d", row, column, mHeader->numRows, mHeader->numColumns);
+      LOGE("Failed to read row# %d, column# from a CursorWindow which has %d rows, %d columns.",
+              row, column, mHeader->numRows, mHeader->numColumns);
       return NULL;
   }        
   row_slot_t * rowSlot = getRowSlot(row);
@@ -238,7 +239,8 @@
 uint32_t CursorWindow::read_field_slot(int row, int column, field_slot_t * slotOut)
 {
     if (row < 0 || row >= mHeader->numRows || column < 0 || column >= mHeader->numColumns) {
-        LOGE("Bad request for field slot %d,%d. numRows = %d, numColumns = %d", row, column, mHeader->numRows, mHeader->numColumns);
+        LOGE("Can't read row# %d, col# %d from CursorWindow. Make sure your Cursor is initialized correctly.",
+                row, column);
         return -1;
     }        
     row_slot_t * rowSlot = getRowSlot(row);
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
diff --git a/opengl/tests/hwc/Android.mk b/opengl/tests/hwc/Android.mk
new file mode 100644
index 0000000..743dbf1
--- /dev/null
+++ b/opengl/tests/hwc/Android.mk
@@ -0,0 +1,27 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES:= hwc_stress.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+    libcutils \
+    libEGL \
+    libGLESv2 \
+    libui \
+    libhardware \
+
+LOCAL_STATIC_LIBRARIES := \
+    libtestUtil \
+
+LOCAL_C_INCLUDES += \
+    system/extras/tests/include \
+    hardware/libhardware/include \
+
+LOCAL_MODULE:= hwc_stress
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/nativestresstest
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_CFLAGS := -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
+
+include $(BUILD_NATIVE_TEST)
diff --git a/opengl/tests/hwc/hwc_stress.cpp b/opengl/tests/hwc/hwc_stress.cpp
new file mode 100644
index 0000000..d119734
--- /dev/null
+++ b/opengl/tests/hwc/hwc_stress.cpp
@@ -0,0 +1,1193 @@
+/*
+ * 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.
+ *
+ */
+
+/*
+ * Hardware Composer stress test
+ *
+ * Performs a pseudo-random (prandom) sequence of operations to the
+ * Hardware Composer (HWC), for a specified number of passes or for
+ * a specified period of time.  By default the period of time is FLT_MAX,
+ * so that the number of passes will take precedence.
+ *
+ * The passes are grouped together, where (pass / passesPerGroup) specifies
+ * which group a particular pass is in.  This causes every passesPerGroup
+ * worth of sequential passes to be within the same group.  Computationally
+ * intensive operations are performed just once at the beginning of a group
+ * of passes and then used by all the passes in that group.  This is done
+ * so as to increase both the average and peak rate of graphic operations,
+ * by moving computationally intensive operations to the beginning of a group.
+ * In particular, at the start of each group of passes a set of
+ * graphic buffers are created, then used by the first and remaining
+ * passes of that group of passes.
+ *
+ * The per-group initialization of the graphic buffers is performed
+ * by a function called initFrames.  This function creates an array
+ * of smart pointers to the graphic buffers, in the form of a vector
+ * of vectors.  The array is accessed in row major order, so each
+ * row is a vector of smart pointers.  All the pointers of a single
+ * row point to graphic buffers which use the same pixel format and
+ * have the same dimension, although it is likely that each one is
+ * filled with a different color.  This is done so that after doing
+ * the first HWC prepare then set call, subsequent set calls can
+ * be made with each of the layer handles changed to a different
+ * graphic buffer within the same row.  Since the graphic buffers
+ * in a particular row have the same pixel format and dimension,
+ * additional HWC set calls can be made, without having to perform
+ * an HWC prepare call.
+ *
+ * This test supports the following command-line options:
+ *
+ *   -v        Verbose
+ *   -s num    Starting pass
+ *   -e num    Ending pass
+ *   -p num    Execute the single pass specified by num
+ *   -n num    Number of set operations to perform after each prepare operation
+ *   -t float  Maximum time in seconds to execute the test
+ *   -d float  Delay in seconds performed after each set operation
+ *   -D float  Delay in seconds performed after the last pass is executed
+ *
+ * Typically the test is executed for a large range of passes.  By default
+ * passes 0 through 99999 (100,000 passes) are executed.  Although this test
+ * does not validate the generated image, at times it is useful to reexecute
+ * a particular pass and leave the displayed image on the screen for an
+ * extended period of time.  This can be done either by setting the -s
+ * and -e options to the desired pass, along with a large value for -D.
+ * This can also be done via the -p option, again with a large value for
+ * the -D options.
+ *
+ * So far this test only contains code to create graphic buffers with
+ * a continuous solid color.  Although this test is unable to validate the
+ * image produced, any image that contains other than rectangles of a solid
+ * color are incorrect.  Note that the rectangles may use a transparent
+ * color and have a blending operation that causes the color in overlapping
+ * rectangles to be mixed.  In such cases the overlapping portions may have
+ * a different color from the rest of the rectangle.
+ */
+
+#include <algorithm>
+#include <assert.h>
+#include <cerrno>
+#include <cmath>
+#include <cstdlib>
+#include <ctime>
+#include <libgen.h>
+#include <sched.h>
+#include <sstream>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <vector>
+
+#include <arpa/inet.h> // For ntohl() and htonl()
+
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include <ui/FramebufferNativeWindow.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/EGLUtils.h>
+
+#define LOG_TAG "hwcStressTest"
+#include <utils/Log.h>
+#include <testUtil.h>
+
+#include <hardware/hwcomposer.h>
+
+using namespace std;
+using namespace android;
+
+const float maxSizeRatio = 1.3;  // Graphic buffers can be upto this munch
+                                 // larger than the default screen size
+const unsigned int passesPerGroup = 10; // A group of passes all use the same
+                                        // graphic buffers
+const float rareRatio = 0.1; // Ratio at which rare conditions are produced.
+
+// Defaults for command-line options
+const bool defaultVerbose = false;
+const unsigned int defaultStartPass = 0;
+const unsigned int defaultEndPass = 99999;
+const unsigned int defaultPerPassNumSet = 10;
+const float defaultPerPassDelay = 0.1;
+const float defaultEndDelay = 2.0; // Default delay between completion of
+                                   // final pass and restart of framework
+const float defaultDuration = FLT_MAX; // A fairly long time, so that
+                                       // range of passes will have
+                                       // precedence
+
+// Command-line option settings
+static bool verbose = defaultVerbose;
+static unsigned int startPass = defaultStartPass;
+static unsigned int endPass = defaultEndPass;
+static unsigned int numSet = defaultPerPassNumSet;
+static float perSetDelay = defaultPerPassDelay;
+static float endDelay = defaultEndDelay;
+static float duration = defaultDuration;
+
+// Command-line mutual exclusion detection flags.
+// Corresponding flag set true once an option is used.
+bool eFlag, sFlag, pFlag;
+
+#define MAXSTR               100
+#define MAXCMD               200
+#define BITSPERBYTE            8 // TODO: Obtain from <values.h>, once
+                                 // it has been added
+
+#define CMD_STOP_FRAMEWORK   "stop 2>&1"
+#define CMD_START_FRAMEWORK  "start 2>&1"
+
+#define NUMA(a) (sizeof(a) / sizeof(a [0]))
+#define MEMCLR(addr, size) do { \
+        memset((addr), 0, (size)); \
+    } while (0)
+
+// Represent RGB color as fraction of color components.
+// Each of the color components are expected in the range [0.0, 1.0]
+class RGBColor {
+  public:
+    RGBColor(): _r(0.0), _g(0.0), _b(0.0) {};
+    RGBColor(float f): _r(f), _g(f), _b(f) {}; // Gray
+    RGBColor(float r, float g, float b): _r(r), _g(g), _b(b) {};
+    float r(void) const { return _r; }
+    float g(void) const { return _g; }
+    float b(void) const { return _b; }
+
+  private:
+    float _r;
+    float _g;
+    float _b;
+};
+
+// Represent YUV color as fraction of color components.
+// Each of the color components are expected in the range [0.0, 1.0]
+class YUVColor {
+  public:
+    YUVColor(): _y(0.0), _u(0.0), _v(0.0) {};
+    YUVColor(float f): _y(f), _u(0.0), _v(0.0) {}; // Gray
+    YUVColor(float y, float u, float v): _y(y), _u(u), _v(v) {};
+    float y(void) const { return _y; }
+    float u(void) const { return _u; }
+    float v(void) const { return _v; }
+
+  private:
+    float _y;
+    float _u;
+    float _v;
+};
+
+// File scope constants
+static const struct {
+    unsigned int format;
+    const char *desc;
+} graphicFormat[] = {
+    {HAL_PIXEL_FORMAT_RGBA_8888, "RGBA8888"},
+    {HAL_PIXEL_FORMAT_RGBX_8888, "RGBX8888"},
+//    {HAL_PIXEL_FORMAT_RGB_888, "RGB888"},  // Known issue: 3198458
+    {HAL_PIXEL_FORMAT_RGB_565, "RGB565"},
+    {HAL_PIXEL_FORMAT_BGRA_8888, "BGRA8888"},
+    {HAL_PIXEL_FORMAT_RGBA_5551, "RGBA5551"},
+    {HAL_PIXEL_FORMAT_RGBA_4444, "RGBA4444"},
+//    {HAL_PIXEL_FORMAT_YV12, "YV12"}, // Currently not supported by HWC
+};
+const unsigned int blendingOps[] = {
+    HWC_BLENDING_NONE,
+    HWC_BLENDING_PREMULT,
+    HWC_BLENDING_COVERAGE,
+};
+const unsigned int layerFlags[] = {
+    HWC_SKIP_LAYER,
+};
+const vector<unsigned int> vecLayerFlags(layerFlags,
+    layerFlags + NUMA(layerFlags));
+
+const unsigned int transformFlags[] = {
+    HWC_TRANSFORM_FLIP_H,
+    HWC_TRANSFORM_FLIP_V,
+    HWC_TRANSFORM_ROT_90,
+    // ROT_180 & ROT_270 intentionally not listed, because they
+    // they are formed from combinations of the flags already listed.
+};
+const vector<unsigned int> vecTransformFlags(transformFlags,
+    transformFlags + NUMA(transformFlags));
+
+// File scope globals
+static const int texUsage = GraphicBuffer::USAGE_HW_TEXTURE |
+        GraphicBuffer::USAGE_SW_WRITE_RARELY;
+static hw_module_t const *hwcModule;
+static hwc_composer_device_t *hwcDevice;
+static vector <vector <sp<GraphicBuffer> > > frames;
+static EGLDisplay dpy;
+static EGLContext context;
+static EGLSurface surface;
+static EGLint width, height;
+
+// File scope prototypes
+static void execCmd(const char *cmd);
+static void checkEglError(const char* op, EGLBoolean returnVal = EGL_TRUE);
+static void checkGlError(const char* op);
+static void printEGLConfiguration(EGLDisplay dpy, EGLConfig config);
+static void printGLString(const char *name, GLenum s);
+static hwc_layer_list_t *createLayerList(size_t numLayers);
+static void freeLayerList(hwc_layer_list_t *list);
+static void fillColor(GraphicBuffer *gBuf, RGBColor color, float trans);
+static void fillColor(GraphicBuffer *gBuf, YUVColor color, float trans);
+void init(void);
+void initFrames(unsigned int seed);
+void displayList(hwc_layer_list_t *list);
+void displayListPrepareModifiable(hwc_layer_list_t *list);
+void displayListHandles(hwc_layer_list_t *list);
+const char *graphicFormat2str(unsigned int format);
+template <class T> vector<T> vectorRandSelect(const vector<T>& vec, size_t num);
+template <class T> T vectorOr(const vector<T>& vec);
+
+/*
+ * Main
+ *
+ * Performs the following high-level sequence of operations:
+ *
+ *   1. Command-line parsing
+ *
+ *   2. Initialization
+ *
+ *   3. For each pass:
+ *
+ *        a. If pass is first pass or in a different group from the
+ *           previous pass, initialize the array of graphic buffers.
+ *
+ *        b. Create a HWC list with room to specify a prandomly
+ *           selected number of layers.
+ *
+ *        c. Select a subset of the rows from the graphic buffer array,
+ *           such that there is a unique row to be used for each
+ *           of the layers in the HWC list.
+ *
+ *        d. Prandomly fill in the HWC list with handles
+ *           selected from any of the columns of the selected row.
+ *
+ *        e. Pass the populated list to the HWC prepare call.
+ *
+ *        f. Pass the populated list to the HWC set call.
+ *
+ *        g. If additional set calls are to be made, then for each
+ *           additional set call, select a new set of handles and
+ *           perform the set call.
+ */
+int
+main(int argc, char *argv[])
+{
+    int rv, opt;
+    char *chptr;
+    unsigned int pass;
+    char cmd[MAXCMD];
+    struct timeval startTime, currentTime, delta;
+
+    testSetLogCatTag(LOG_TAG);
+
+    // Parse command line arguments
+    while ((opt = getopt(argc, argv, "vp:d:D:n:s:e:t:?h")) != -1) {
+        switch (opt) {
+          case 'd': // Delay after each set operation
+            perSetDelay = strtod(optarg, &chptr);
+            if ((*chptr != '\0') || (perSetDelay < 0.0)) {
+                testPrintE("Invalid command-line specified per pass delay of: "
+                           "%s", optarg);
+                exit(1);
+            }
+            break;
+
+          case 'D': // End of test delay
+                    // Delay between completion of final pass and restart
+                    // of framework
+            endDelay = strtod(optarg, &chptr);
+            if ((*chptr != '\0') || (endDelay < 0.0)) {
+                testPrintE("Invalid command-line specified end of test delay "
+                           "of: %s", optarg);
+                exit(2);
+            }
+            break;
+
+          case 't': // Duration
+            duration = strtod(optarg, &chptr);
+            if ((*chptr != '\0') || (duration < 0.0)) {
+                testPrintE("Invalid command-line specified duration of: %s",
+                           optarg);
+                exit(3);
+            }
+            break;
+
+          case 'n': // Num set operations per pass
+            numSet = strtoul(optarg, &chptr, 10);
+            if (*chptr != '\0') {
+                testPrintE("Invalid command-line specified num set per pass "
+                           "of: %s", optarg);
+                exit(4);
+            }
+            break;
+
+          case 's': // Starting Pass
+            sFlag = true;
+            if (pFlag) {
+                testPrintE("Invalid combination of command-line options.");
+                testPrintE("  The -p option is mutually exclusive from the");
+                testPrintE("  -s and -e options.");
+                exit(5);
+            }
+            startPass = strtoul(optarg, &chptr, 10);
+            if (*chptr != '\0') {
+                testPrintE("Invalid command-line specified starting pass "
+                           "of: %s", optarg);
+                exit(6);
+            }
+            break;
+
+          case 'e': // Ending Pass
+            eFlag = true;
+            if (pFlag) {
+                testPrintE("Invalid combination of command-line options.");
+                testPrintE("  The -p option is mutually exclusive from the");
+                testPrintE("  -s and -e options.");
+                exit(7);
+            }
+            endPass = strtoul(optarg, &chptr, 10);
+            if (*chptr != '\0') {
+                testPrintE("Invalid command-line specified ending pass "
+                           "of: %s", optarg);
+                exit(8);
+            }
+            break;
+
+          case 'p': // Run a single specified pass
+            pFlag = true;
+            if (sFlag || eFlag) {
+                testPrintE("Invalid combination of command-line options.");
+                testPrintE("  The -p option is mutually exclusive from the");
+                testPrintE("  -s and -e options.");
+                exit(9);
+            }
+            startPass = endPass = strtoul(optarg, &chptr, 10);
+            if (*chptr != '\0') {
+                testPrintE("Invalid command-line specified pass of: %s",
+                           optarg);
+                exit(10);
+            }
+            break;
+
+          case 'v': // Verbose
+            verbose = true;
+            break;
+
+          case 'h': // Help
+          case '?':
+          default:
+            testPrintE("  %s [options]", basename(argv[0]));
+            testPrintE("    options:");
+            testPrintE("      -p Execute specified pass");
+            testPrintE("      -s Starting pass");
+            testPrintE("      -e Ending pass");
+            testPrintE("      -t Duration");
+            testPrintE("      -d Delay after each set operation");
+            testPrintE("      -D End of test delay");
+            testPrintE("      -n Num set operations per pass");
+            testPrintE("      -v Verbose");
+            exit(((optopt == 0) || (optopt == '?')) ? 0 : 11);
+        }
+    }
+    if (endPass < startPass) {
+        testPrintE("Unexpected ending pass before starting pass");
+        testPrintE("  startPass: %u endPass: %u", startPass, endPass);
+        exit(12);
+    }
+    if (argc != optind) {
+        testPrintE("Unexpected command-line postional argument");
+        testPrintE("  %s [-s start_pass] [-e end_pass] [-t duration]",
+            basename(argv[0]));
+        exit(13);
+    }
+    testPrintI("duration: %g", duration);
+    testPrintI("startPass: %u", startPass);
+    testPrintI("endPass: %u", endPass);
+    testPrintI("numSet: %u", numSet);
+
+    // Stop framework
+    rv = snprintf(cmd, sizeof(cmd), "%s", CMD_STOP_FRAMEWORK);
+    if (rv >= (signed) sizeof(cmd) - 1) {
+        testPrintE("Command too long for: %s", CMD_STOP_FRAMEWORK);
+        exit(14);
+    }
+    execCmd(cmd);
+    testDelay(1.0); // TODO - needs means to query whether asyncronous stop
+                    // framework operation has completed.  For now, just wait
+                    // a long time.
+
+    init();
+
+    // For each pass
+    gettimeofday(&startTime, NULL);
+    for (pass = startPass; pass <= endPass; pass++) {
+        // Stop if duration of work has already been performed
+        gettimeofday(&currentTime, NULL);
+        delta = tvDelta(&startTime, &currentTime);
+        if (tv2double(&delta) > duration) { break; }
+
+        // Regenerate a new set of test frames when this pass is
+        // either the first pass or is in a different group then
+        // the previous pass.  A group of passes are passes that
+        // all have the same quotient when their pass number is
+        // divided by passesPerGroup.
+        if ((pass == startPass)
+            || ((pass / passesPerGroup) != ((pass - 1) / passesPerGroup))) {
+            initFrames(pass / passesPerGroup);
+        }
+
+        testPrintI("==== Starting pass: %u", pass);
+
+        // Cause deterministic sequence of prandom numbers to be
+        // generated for this pass.
+        srand48(pass);
+
+        hwc_layer_list_t *list;
+        list = createLayerList(testRandMod(frames.size()) + 1);
+        if (list == NULL) {
+            testPrintE("createLayerList failed");
+            exit(20);
+        }
+
+	// Prandomly select a subset of frames to be used by this pass.
+        vector <vector <sp<GraphicBuffer> > > selectedFrames;
+        selectedFrames = vectorRandSelect(frames, list->numHwLayers);
+
+        // Any transform tends to create a layer that the hardware
+        // composer is unable to support and thus has to leave for
+        // SurfaceFlinger.  Place heavy bias on specifying no transforms.
+        bool noTransform = testRandFract() > rareRatio;
+
+        for (unsigned int n1 = 0; n1 < list->numHwLayers; n1++) {
+            unsigned int idx = testRandMod(selectedFrames[n1].size());
+            sp<GraphicBuffer> gBuf = selectedFrames[n1][idx];
+            hwc_layer_t *layer = &list->hwLayers[n1];
+            layer->handle = gBuf->handle;
+
+            layer->blending = blendingOps[testRandMod(NUMA(blendingOps))];
+            layer->flags = (testRandFract() > rareRatio) ? 0
+                : vectorOr(vectorRandSelect(vecLayerFlags,
+                           testRandMod(vecLayerFlags.size() + 1)));
+            layer->transform = (noTransform || testRandFract() > rareRatio) ? 0
+                : vectorOr(vectorRandSelect(vecTransformFlags,
+                           testRandMod(vecTransformFlags.size() + 1)));
+            layer->sourceCrop.left = testRandMod(gBuf->getWidth());
+            layer->sourceCrop.top = testRandMod(gBuf->getHeight());
+            layer->sourceCrop.right = layer->sourceCrop.left
+                + testRandMod(gBuf->getWidth() - layer->sourceCrop.left) + 1;
+            layer->sourceCrop.bottom = layer->sourceCrop.top
+                + testRandMod(gBuf->getHeight() - layer->sourceCrop.top) + 1;
+            layer->displayFrame.left = testRandMod(width);
+            layer->displayFrame.top = testRandMod(height);
+            layer->displayFrame.right = layer->displayFrame.left
+                + testRandMod(width - layer->displayFrame.left) + 1;
+            layer->displayFrame.bottom = layer->displayFrame.top
+                + testRandMod(height - layer->displayFrame.top) + 1;
+            layer->visibleRegionScreen.numRects = 1;
+            layer->visibleRegionScreen.rects = &layer->displayFrame;
+        }
+
+        // Perform prepare operation
+        if (verbose) { testPrintI("Prepare:"); displayList(list); }
+        hwcDevice->prepare(hwcDevice, list);
+        if (verbose) {
+            testPrintI("Post Prepare:");
+            displayListPrepareModifiable(list);
+        }
+
+        // Turn off the geometry changed flag
+        list->flags &= ~HWC_GEOMETRY_CHANGED;
+
+        // Perform the set operation(s)
+        if (verbose) {testPrintI("Set:"); }
+        for (unsigned int n1 = 0; n1 < numSet; n1++) {
+            if (verbose) {displayListHandles(list); }
+            hwcDevice->set(hwcDevice, dpy, surface, list);
+
+            // Prandomly select a new set of handles
+            for (unsigned int n1 = 0; n1 < list->numHwLayers; n1++) {
+                unsigned int idx = testRandMod(selectedFrames[n1].size());
+                sp<GraphicBuffer> gBuf = selectedFrames[n1][idx];
+                hwc_layer_t *layer = &list->hwLayers[n1];
+                layer->handle = (native_handle_t *) gBuf->handle;
+            }
+
+            testDelay(perSetDelay);
+        }
+
+
+        freeLayerList(list);
+        testPrintI("==== Completed pass: %u", pass);
+    }
+
+    testDelay(endDelay);
+
+    // Start framework
+    rv = snprintf(cmd, sizeof(cmd), "%s", CMD_START_FRAMEWORK);
+    if (rv >= (signed) sizeof(cmd) - 1) {
+        testPrintE("Command too long for: %s", CMD_START_FRAMEWORK);
+        exit(21);
+    }
+    execCmd(cmd);
+
+    testPrintI("Successfully completed %u passes", pass - startPass);
+
+    return 0;
+}
+
+/*
+ * Execute Command
+ *
+ * Executes the command pointed to by cmd.  Output from the
+ * executed command is captured and sent to LogCat Info.  Once
+ * the command has finished execution, it's exit status is captured
+ * and checked for an exit status of zero.  Any other exit status
+ * causes diagnostic information to be printed and an immediate
+ * testcase failure.
+ */
+static void execCmd(const char *cmd)
+{
+    FILE *fp;
+    int rv;
+    int status;
+    char str[MAXSTR];
+
+    // Display command to be executed
+    testPrintI("cmd: %s", cmd);
+
+    // Execute the command
+    fflush(stdout);
+    if ((fp = popen(cmd, "r")) == NULL) {
+        testPrintE("execCmd popen failed, errno: %i", errno);
+        exit(30);
+    }
+
+    // Obtain and display each line of output from the executed command
+    while (fgets(str, sizeof(str), fp) != NULL) {
+        if ((strlen(str) > 1) && (str[strlen(str) - 1] == '\n')) {
+            str[strlen(str) - 1] = '\0';
+        }
+        testPrintI(" out: %s", str);
+    }
+
+    // Obtain and check return status of executed command.
+    // Fail on non-zero exit status
+    status = pclose(fp);
+    if (!(WIFEXITED(status) && (WEXITSTATUS(status) == 0))) {
+        testPrintE("Unexpected command failure");
+        testPrintE("  status: %#x", status);
+        if (WIFEXITED(status)) {
+            testPrintE("WEXITSTATUS: %i", WEXITSTATUS(status));
+        }
+        if (WIFSIGNALED(status)) {
+            testPrintE("WTERMSIG: %i", WTERMSIG(status));
+        }
+        exit(31);
+    }
+}
+
+static void checkEglError(const char* op, EGLBoolean returnVal) {
+    if (returnVal != EGL_TRUE) {
+        testPrintE("%s() returned %d", op, returnVal);
+    }
+
+    for (EGLint error = eglGetError(); error != EGL_SUCCESS; error
+            = eglGetError()) {
+        testPrintE("after %s() eglError %s (0x%x)",
+                   op, EGLUtils::strerror(error), error);
+    }
+}
+
+static void checkGlError(const char* op) {
+    for (GLint error = glGetError(); error; error
+            = glGetError()) {
+        testPrintE("after %s() glError (0x%x)", op, error);
+    }
+}
+
+static void printEGLConfiguration(EGLDisplay dpy, EGLConfig config) {
+
+#define X(VAL) {VAL, #VAL}
+    struct {EGLint attribute; const char* name;} names[] = {
+    X(EGL_BUFFER_SIZE),
+    X(EGL_ALPHA_SIZE),
+    X(EGL_BLUE_SIZE),
+    X(EGL_GREEN_SIZE),
+    X(EGL_RED_SIZE),
+    X(EGL_DEPTH_SIZE),
+    X(EGL_STENCIL_SIZE),
+    X(EGL_CONFIG_CAVEAT),
+    X(EGL_CONFIG_ID),
+    X(EGL_LEVEL),
+    X(EGL_MAX_PBUFFER_HEIGHT),
+    X(EGL_MAX_PBUFFER_PIXELS),
+    X(EGL_MAX_PBUFFER_WIDTH),
+    X(EGL_NATIVE_RENDERABLE),
+    X(EGL_NATIVE_VISUAL_ID),
+    X(EGL_NATIVE_VISUAL_TYPE),
+    X(EGL_SAMPLES),
+    X(EGL_SAMPLE_BUFFERS),
+    X(EGL_SURFACE_TYPE),
+    X(EGL_TRANSPARENT_TYPE),
+    X(EGL_TRANSPARENT_RED_VALUE),
+    X(EGL_TRANSPARENT_GREEN_VALUE),
+    X(EGL_TRANSPARENT_BLUE_VALUE),
+    X(EGL_BIND_TO_TEXTURE_RGB),
+    X(EGL_BIND_TO_TEXTURE_RGBA),
+    X(EGL_MIN_SWAP_INTERVAL),
+    X(EGL_MAX_SWAP_INTERVAL),
+    X(EGL_LUMINANCE_SIZE),
+    X(EGL_ALPHA_MASK_SIZE),
+    X(EGL_COLOR_BUFFER_TYPE),
+    X(EGL_RENDERABLE_TYPE),
+    X(EGL_CONFORMANT),
+   };
+#undef X
+
+    for (size_t j = 0; j < sizeof(names) / sizeof(names[0]); j++) {
+        EGLint value = -1;
+        EGLint returnVal = eglGetConfigAttrib(dpy, config, names[j].attribute, &value);
+        EGLint error = eglGetError();
+        if (returnVal && error == EGL_SUCCESS) {
+            testPrintI(" %s: %d (%#x)", names[j].name, value, value);
+        }
+    }
+    testPrintI("");
+}
+
+static void printGLString(const char *name, GLenum s)
+{
+    const char *v = (const char *) glGetString(s);
+
+    if (v == NULL) {
+        testPrintI("GL %s unknown", name);
+    } else {
+        testPrintI("GL %s = %s", name, v);
+    }
+}
+
+/*
+ * createLayerList
+ * dynamically creates layer list with numLayers worth
+ * of hwLayers entries.
+ */
+static hwc_layer_list_t *createLayerList(size_t numLayers)
+{
+    hwc_layer_list_t *list;
+
+    size_t size = sizeof(hwc_layer_list) + numLayers * sizeof(hwc_layer_t);
+    if ((list = (hwc_layer_list_t *) calloc(1, size)) == NULL) {
+        return NULL;
+    }
+    list->flags = HWC_GEOMETRY_CHANGED;
+    list->numHwLayers = numLayers;
+
+    return list;
+}
+
+/*
+ * freeLayerList
+ * Frees memory previous allocated via createLayerList().
+ */
+static void freeLayerList(hwc_layer_list_t *list)
+{
+    free(list);
+}
+
+static void fillColor(GraphicBuffer *gBuf, RGBColor color, float trans)
+{
+    unsigned char* buf = NULL;
+    status_t err;
+    unsigned int numPixels = gBuf->getWidth() * gBuf->getHeight();
+    uint32_t pixel;
+
+    // RGB 2 YUV conversion ratios
+    const struct rgb2yuvRatios {
+        int format;
+        float weightRed;
+        float weightBlu;
+        float weightGrn;
+    } rgb2yuvRatios[] = {
+        { HAL_PIXEL_FORMAT_YV12, 0.299, 0.114, 0.587 },
+    };
+
+    const struct rgbAttrib {
+        int format;
+        bool   hostByteOrder;
+        size_t bytes;
+        size_t rOffset;
+        size_t rSize;
+        size_t gOffset;
+        size_t gSize;
+        size_t bOffset;
+        size_t bSize;
+        size_t aOffset;
+        size_t aSize;
+    } rgbAttributes[] = {
+        {HAL_PIXEL_FORMAT_RGBA_8888, false, 4,  0, 8,  8, 8, 16, 8, 24, 8},
+        {HAL_PIXEL_FORMAT_RGBX_8888, false, 4,  0, 8,  8, 8, 16, 8,  0, 0},
+        {HAL_PIXEL_FORMAT_RGB_888,   false, 3,  0, 8,  8, 8, 16, 8,  0, 0},
+        {HAL_PIXEL_FORMAT_RGB_565,   true,  2,  0, 5,  5, 6, 11, 5,  0, 0},
+        {HAL_PIXEL_FORMAT_BGRA_8888, false, 4, 16, 8,  8, 8,  0, 8, 24, 8},
+        {HAL_PIXEL_FORMAT_RGBA_5551, true , 2,  0, 5,  5, 5, 10, 5, 15, 1},
+        {HAL_PIXEL_FORMAT_RGBA_4444, false, 2, 12, 4,  0, 4,  4, 4,  8, 4},
+    };
+
+    // If YUV format, convert color and pass work to YUV color fill
+    for (unsigned int n1 = 0; n1 < NUMA(rgb2yuvRatios); n1++) {
+        if (gBuf->getPixelFormat() == rgb2yuvRatios[n1].format) {
+            float wr = rgb2yuvRatios[n1].weightRed;
+            float wb = rgb2yuvRatios[n1].weightBlu;
+            float wg = rgb2yuvRatios[n1].weightGrn;
+            float y = wr * color.r() + wb * color.b() + wg * color.g();
+            float u = 0.5 * ((color.b() - y) / (1 - wb)) + 0.5;
+            float v = 0.5 * ((color.r() - y) / (1 - wr)) + 0.5;
+            YUVColor yuvColor(y, u, v);
+            fillColor(gBuf, yuvColor, trans);
+            return;
+        }
+    }
+
+    const struct rgbAttrib *attrib;
+    for (attrib = rgbAttributes; attrib < rgbAttributes + NUMA(rgbAttributes);
+         attrib++) {
+        if (attrib->format == gBuf->getPixelFormat()) { break; }
+    }
+    if (attrib >= rgbAttributes + NUMA(rgbAttributes)) {
+        testPrintE("fillColor rgb unsupported format of: %u",
+        gBuf->getPixelFormat());
+        exit(50);
+    }
+
+    pixel = htonl((uint32_t) (((1 << attrib->rSize) - 1) * color.r())
+         << ((sizeof(pixel) * BITSPERBYTE)
+             - (attrib->rOffset + attrib->rSize)));
+    pixel |= htonl((uint32_t) (((1 << attrib->gSize) - 1) * color.g())
+         << ((sizeof(pixel) * BITSPERBYTE)
+             - (attrib->gOffset + attrib->gSize)));
+    pixel |= htonl((uint32_t) (((1 << attrib->bSize) - 1) * color.b())
+         << ((sizeof(pixel) * BITSPERBYTE)
+             - (attrib->bOffset + attrib->bSize)));
+    if (attrib->aSize) {
+        pixel |= htonl((uint32_t) (((1 << attrib->aSize) - 1) * trans)
+             << ((sizeof(pixel) * BITSPERBYTE)
+                 - (attrib->aOffset + attrib->aSize)));
+    }
+    if (attrib->hostByteOrder) {
+        pixel = ntohl(pixel);
+        pixel >>= sizeof(pixel) * BITSPERBYTE - attrib->bytes * BITSPERBYTE;
+    }
+
+    err = gBuf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&buf));
+    if (err != 0) {
+        testPrintE("fillColor rgb lock failed: %d", err);
+        exit(51);
+    }
+
+    for (unsigned int n1 = 0; n1 < numPixels; n1++) {
+        memmove(buf, &pixel, attrib->bytes);
+        buf += attrib->bytes;
+    }
+
+    err = gBuf->unlock();
+    if (err != 0) {
+        testPrintE("fillColor rgb unlock failed: %d", err);
+        exit(52);
+    }
+}
+
+static void fillColor(GraphicBuffer *gBuf, YUVColor color, float trans)
+{
+    unsigned char* buf = NULL;
+    status_t err;
+    unsigned int width = gBuf->getWidth();
+    unsigned int height = gBuf->getHeight();
+
+    const struct yuvAttrib {
+        int format;
+        size_t padWidth;
+        bool   planar;
+        unsigned int uSubSampX;
+        unsigned int uSubSampY;
+        unsigned int vSubSampX;
+        unsigned int vSubSampY;
+    } yuvAttributes[] = {
+        { HAL_PIXEL_FORMAT_YV12, 16, true, 2, 2, 2, 2},
+    };
+
+    const struct yuvAttrib *attrib;
+    for (attrib = yuvAttributes; attrib < yuvAttributes + NUMA(yuvAttributes);
+         attrib++) {
+        if (attrib->format == gBuf->getPixelFormat()) { break; }
+    }
+    if (attrib >= yuvAttributes + NUMA(yuvAttributes)) {
+        testPrintE("fillColor yuv unsupported format of: %u",
+        gBuf->getPixelFormat());
+        exit(60);
+    }
+
+    assert(attrib->planar == true); // So far, only know how to handle planar
+
+    // If needed round width up to pad size
+    if (width % attrib->padWidth) {
+        width += attrib->padWidth - (width % attrib->padWidth);
+    }
+    assert((width % attrib->padWidth) == 0);
+
+    err = gBuf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&buf));
+    if (err != 0) {
+        testPrintE("fillColor lock failed: %d", err);
+        exit(61);
+    }
+
+    // Fill in Y component
+    for (unsigned int x = 0; x < width; x++) {
+        for (unsigned int y = 0; y < height; y++) {
+            *buf++ = (x < gBuf->getWidth()) ? (255 * color.y()) : 0;
+        }
+    }
+
+    // Fill in U component
+    for (unsigned int x = 0; x < width; x += attrib->uSubSampX) {
+        for (unsigned int y = 0; y < height; y += attrib->uSubSampY) {
+            *buf++ = (x < gBuf->getWidth()) ? (255 * color.u()) : 0;
+        }
+    }
+
+    // Fill in V component
+    for (unsigned int x = 0; x < width; x += attrib->vSubSampX) {
+        for (unsigned int y = 0; y < height; y += attrib->vSubSampY) {
+            *buf++ = (x < gBuf->getWidth()) ? (255 * color.v()) : 0;
+        }
+    }
+
+    err = gBuf->unlock();
+    if (err != 0) {
+        testPrintE("fillColor unlock failed: %d", err);
+        exit(62);
+    }
+}
+
+void init(void)
+{
+    int rv;
+
+    EGLBoolean returnValue;
+    EGLConfig myConfig = {0};
+    EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
+    EGLint sConfigAttribs[] = {
+        EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+        EGL_NONE };
+    EGLint majorVersion, minorVersion;
+
+    checkEglError("<init>");
+    dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+    checkEglError("eglGetDisplay");
+    if (dpy == EGL_NO_DISPLAY) {
+        testPrintE("eglGetDisplay returned EGL_NO_DISPLAY");
+        exit(70);
+    }
+
+    returnValue = eglInitialize(dpy, &majorVersion, &minorVersion);
+    checkEglError("eglInitialize", returnValue);
+    testPrintI("EGL version %d.%d", majorVersion, minorVersion);
+    if (returnValue != EGL_TRUE) {
+        testPrintE("eglInitialize failed");
+        exit(71);
+    }
+
+    EGLNativeWindowType window = android_createDisplaySurface();
+    if (window == NULL) {
+        testPrintE("android_createDisplaySurface failed");
+        exit(72);
+    }
+    returnValue = EGLUtils::selectConfigForNativeWindow(dpy,
+        sConfigAttribs, window, &myConfig);
+    if (returnValue) {
+        testPrintE("EGLUtils::selectConfigForNativeWindow() returned %d",
+            returnValue);
+        exit(73);
+    }
+    checkEglError("EGLUtils::selectConfigForNativeWindow");
+
+    testPrintI("Chose this configuration:");
+    printEGLConfiguration(dpy, myConfig);
+
+    surface = eglCreateWindowSurface(dpy, myConfig, window, NULL);
+    checkEglError("eglCreateWindowSurface");
+    if (surface == EGL_NO_SURFACE) {
+        testPrintE("gelCreateWindowSurface failed.");
+        exit(74);
+    }
+
+    context = eglCreateContext(dpy, myConfig, EGL_NO_CONTEXT, contextAttribs);
+    checkEglError("eglCreateContext");
+    if (context == EGL_NO_CONTEXT) {
+        testPrintE("eglCreateContext failed");
+        exit(75);
+    }
+    returnValue = eglMakeCurrent(dpy, surface, surface, context);
+    checkEglError("eglMakeCurrent", returnValue);
+    if (returnValue != EGL_TRUE) {
+        testPrintE("eglMakeCurrent failed");
+        exit(76);
+    }
+    eglQuerySurface(dpy, surface, EGL_WIDTH, &width);
+    checkEglError("eglQuerySurface");
+    eglQuerySurface(dpy, surface, EGL_HEIGHT, &height);
+    checkEglError("eglQuerySurface");
+
+    fprintf(stderr, "Window dimensions: %d x %d", width, height);
+
+    printGLString("Version", GL_VERSION);
+    printGLString("Vendor", GL_VENDOR);
+    printGLString("Renderer", GL_RENDERER);
+    printGLString("Extensions", GL_EXTENSIONS);
+
+    if ((rv = hw_get_module(HWC_HARDWARE_MODULE_ID, &hwcModule)) != 0) {
+        testPrintE("hw_get_module failed, rv: %i", rv);
+        errno = -rv;
+        perror(NULL);
+        exit(77);
+    }
+    if ((rv = hwc_open(hwcModule, &hwcDevice)) != 0) {
+        testPrintE("hwc_open failed, rv: %i", rv);
+        errno = -rv;
+        perror(NULL);
+        exit(78);
+    }
+
+    testPrintI("");
+}
+
+/*
+ * Initialize Frames
+ *
+ * Creates an array of graphic buffers, within the global variable
+ * named frames.  The graphic buffers are contained within a vector of
+ * verctors.  All the graphic buffers in a particular row are of the same
+ * format and dimension.  Each graphic buffer is uniformly filled with a
+ * prandomly selected color.  It is likely that each buffer, even
+ * in the same row, will be filled with a unique color.
+ */
+void initFrames(unsigned int seed)
+{
+    const size_t maxRows = 5;
+    const size_t minCols = 2;  // Need at least double buffering
+    const size_t maxCols = 4;  // One more than triple buffering
+
+    if (verbose) { testPrintI("initFrames seed: %u", seed); }
+    srand48(seed);
+    size_t rows = testRandMod(maxRows) + 1;
+
+    frames.clear();
+    frames.resize(rows);
+
+    for (unsigned int row = 0; row < rows; row++) {
+        // All frames within a row have to have the same format and
+        // dimensions.  Width and height need to be >= 1.
+        int format = graphicFormat[testRandMod(NUMA(graphicFormat))].format;
+        size_t w = (width * maxSizeRatio) * testRandFract();
+        size_t h = (height * maxSizeRatio) * testRandFract();
+        w = max(1u, w);
+        h = max(1u, h);
+        if (verbose) {
+            testPrintI("  frame %u width: %u height: %u format: %u %s",
+                       row, w, h, format, graphicFormat2str(format));
+        }
+
+        size_t cols = testRandMod((maxCols + 1) - minCols) + minCols;
+        frames[row].resize(cols);
+        for (unsigned int col = 0; col < cols; col++) {
+            RGBColor color(testRandFract(), testRandFract(), testRandFract());
+            float transp = testRandFract();
+
+            frames[row][col] = new GraphicBuffer(w, h, format, texUsage);
+            fillColor(frames[row][col].get(), color, transp);
+            if (verbose) {
+                testPrintI("    buf: %p handle: %p color: <%f, %f, %f> "
+                           "transp: %f",
+                           frames[row][col].get(), frames[row][col]->handle,
+                           color.r(), color.g(), color.b(), transp);
+            }
+        }
+    }
+}
+
+void displayList(hwc_layer_list_t *list)
+{
+    testPrintI("  flags: %#x%s", list->flags,
+               (list->flags & HWC_GEOMETRY_CHANGED) ? " GEOMETRY_CHANGED" : "");
+    testPrintI("  numHwLayers: %u", list->numHwLayers);
+
+    for (unsigned int layer = 0; layer < list->numHwLayers; layer++) {
+        testPrintI("    layer %u compositionType: %#x%s%s", layer,
+                   list->hwLayers[layer].compositionType,
+                   (list->hwLayers[layer].compositionType == HWC_FRAMEBUFFER)
+                       ? " FRAMEBUFFER" : "",
+                   (list->hwLayers[layer].compositionType == HWC_OVERLAY)
+                       ? " OVERLAY" : "");
+
+        testPrintI("      hints: %#x",
+                   list->hwLayers[layer].hints,
+                   (list->hwLayers[layer].hints & HWC_HINT_TRIPLE_BUFFER)
+                       ? " TRIPLE_BUFFER" : "",
+                   (list->hwLayers[layer].hints & HWC_HINT_CLEAR_FB)
+                       ? " CLEAR_FB" : "");
+
+        testPrintI("      flags: %#x%s",
+                   list->hwLayers[layer].flags,
+                   (list->hwLayers[layer].flags & HWC_SKIP_LAYER)
+                       ? " SKIP_LAYER" : "");
+
+        testPrintI("      handle: %p",
+                   list->hwLayers[layer].handle);
+
+        // Intentionally skipped display of ROT_180 & ROT_270,
+        // which are formed from combinations of the other flags.
+        testPrintI("      transform: %#x%s%s%s",
+                   list->hwLayers[layer].transform,
+                   (list->hwLayers[layer].transform & HWC_TRANSFORM_FLIP_H)
+                       ? " FLIP_H" : "",
+                   (list->hwLayers[layer].transform & HWC_TRANSFORM_FLIP_V)
+                       ? " FLIP_V" : "",
+                   (list->hwLayers[layer].transform & HWC_TRANSFORM_ROT_90)
+                       ? " ROT_90" : "");
+
+        testPrintI("      blending: %#x",
+                   list->hwLayers[layer].blending,
+                   (list->hwLayers[layer].blending == HWC_BLENDING_NONE)
+                       ? " NONE" : "",
+                   (list->hwLayers[layer].blending == HWC_BLENDING_PREMULT)
+                       ? " PREMULT" : "",
+                   (list->hwLayers[layer].blending == HWC_BLENDING_COVERAGE)
+                       ? " COVERAGE" : "");
+
+        testPrintI("      sourceCrop: [%i, %i, %i, %i]",
+                   list->hwLayers[layer].sourceCrop.left,
+                   list->hwLayers[layer].sourceCrop.top,
+                   list->hwLayers[layer].sourceCrop.right,
+                   list->hwLayers[layer].sourceCrop.bottom);
+
+        testPrintI("      displayFrame: [%i, %i, %i, %i]",
+                   list->hwLayers[layer].displayFrame.left,
+                   list->hwLayers[layer].displayFrame.top,
+                   list->hwLayers[layer].displayFrame.right,
+                   list->hwLayers[layer].displayFrame.bottom);
+    }
+}
+
+/*
+ * Display List Prepare Modifiable
+ *
+ * Displays the portions of a list that are meant to be modified by
+ * a prepare call.
+ */
+void displayListPrepareModifiable(hwc_layer_list_t *list)
+{
+    for (unsigned int layer = 0; layer < list->numHwLayers; layer++) {
+        testPrintI("    layer %u compositionType: %#x%s%s", layer,
+                   list->hwLayers[layer].compositionType,
+                   (list->hwLayers[layer].compositionType == HWC_FRAMEBUFFER)
+                       ? " FRAMEBUFFER" : "",
+                   (list->hwLayers[layer].compositionType == HWC_OVERLAY)
+                       ? " OVERLAY" : "");
+        testPrintI("      hints: %#x%s%s",
+                   list->hwLayers[layer].hints,
+                   (list->hwLayers[layer].hints & HWC_HINT_TRIPLE_BUFFER)
+                       ? " TRIPLE_BUFFER" : "",
+                   (list->hwLayers[layer].hints & HWC_HINT_CLEAR_FB)
+                       ? " CLEAR_FB" : "");
+    }
+}
+
+/*
+ * Display List Handles
+ *
+ * Displays the handles of all the graphic buffers in the list.
+ */
+void displayListHandles(hwc_layer_list_t *list)
+{
+    const unsigned int maxLayersPerLine = 6;
+
+    ostringstream str("  layers:");
+    for (unsigned int layer = 0; layer < list->numHwLayers; layer++) {
+        str << ' ' << list->hwLayers[layer].handle;
+        if (((layer % maxLayersPerLine) == (maxLayersPerLine - 1))
+            && (layer != list->numHwLayers - 1)) {
+            testPrintI("%s", str.str().c_str());
+            str.str("    ");
+        }
+    }
+    testPrintI("%s", str.str().c_str());
+}
+
+const char *graphicFormat2str(unsigned int format)
+{
+    const static char *unknown = "unknown";
+
+    for (unsigned int n1 = 0; n1 < NUMA(graphicFormat); n1++) {
+        if (format == graphicFormat[n1].format) {
+            return graphicFormat[n1].desc;
+        }
+    }
+
+    return unknown;
+}
+
+/*
+ * Vector Random Select
+ *
+ * Prandomly selects and returns num elements from vec.
+ */
+template <class T>
+vector<T> vectorRandSelect(const vector<T>& vec, size_t num)
+{
+    vector<T> rv = vec;
+
+    while (rv.size() > num) {
+        rv.erase(rv.begin() + testRandMod(rv.size()));
+    }
+
+    return rv;
+}
+
+/*
+ * Vector Or
+ *
+ * Or's togethen the values of each element of vec and returns the result.
+ */
+template <class T>
+T vectorOr(const vector<T>& vec)
+{
+    T rv = 0;
+
+    for (size_t n1 = 0; n1 < vec.size(); n1++) {
+        rv |= vec[n1];
+    }
+
+    return rv;
+}