b/12068020 Make kb layouts only unique to vendor/product

Instead of storing a kb layout per device descriptor (which is expected
to be unique), store it for each vendor/product. This way we can keep
a consistent layout between identical but physically different keyboards.

There are some corner cases this is expected to fail on, namely devices
that incorrectly have the same vendor/product id. Devices that don't
define a vendor/product id will continue to use the descriptor to store
layout files.

Change-Id: Id0890d13e1c859eaf993d4831b7b1acbaf5df80f
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 9b6f82a..f1e7e98 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -16,6 +16,7 @@
 
 package android.hardware.input;
 
+import android.hardware.input.InputDeviceIdentifier;
 import android.hardware.input.KeyboardLayout;
 import android.hardware.input.IInputDevicesChangedListener;
 import android.os.IBinder;
@@ -41,13 +42,13 @@
     // Keyboard layouts configuration.
     KeyboardLayout[] getKeyboardLayouts();
     KeyboardLayout getKeyboardLayout(String keyboardLayoutDescriptor);
-    String getCurrentKeyboardLayoutForInputDevice(String inputDeviceDescriptor);
-    void setCurrentKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
+    String getCurrentKeyboardLayoutForInputDevice(in InputDeviceIdentifier identifier);
+    void setCurrentKeyboardLayoutForInputDevice(in InputDeviceIdentifier identifier,
             String keyboardLayoutDescriptor);
-    String[] getKeyboardLayoutsForInputDevice(String inputDeviceDescriptor);
-    void addKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
+    String[] getKeyboardLayoutsForInputDevice(in InputDeviceIdentifier identifier);
+    void addKeyboardLayoutForInputDevice(in InputDeviceIdentifier identifier,
             String keyboardLayoutDescriptor);
-    void removeKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
+    void removeKeyboardLayoutForInputDevice(in InputDeviceIdentifier identifier,
             String keyboardLayoutDescriptor);
 
     // Registers an input devices changed listener.
diff --git a/core/java/android/hardware/input/InputDeviceIdentifier.aidl b/core/java/android/hardware/input/InputDeviceIdentifier.aidl
new file mode 100644
index 0000000..7234a91
--- /dev/null
+++ b/core/java/android/hardware/input/InputDeviceIdentifier.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+package android.hardware.input;
+
+parcelable InputDeviceIdentifier;
diff --git a/core/java/android/hardware/input/InputDeviceIdentifier.java b/core/java/android/hardware/input/InputDeviceIdentifier.java
new file mode 100644
index 0000000..5e832e3
--- /dev/null
+++ b/core/java/android/hardware/input/InputDeviceIdentifier.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+package android.hardware.input;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Wrapper for passing identifying information for input devices.
+ *
+ * @hide
+ */
+public final class InputDeviceIdentifier implements Parcelable {
+    private final String mDescriptor;
+    private final int mVendorId;
+    private final int mProductId;
+
+    public InputDeviceIdentifier(String descriptor, int vendorId, int productId) {
+        this.mDescriptor = descriptor;
+        this.mVendorId = vendorId;
+        this.mProductId = productId;
+    }
+
+    private InputDeviceIdentifier(Parcel src) {
+        mDescriptor = src.readString();
+        mVendorId = src.readInt();
+        mProductId = src.readInt();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mDescriptor);
+        dest.writeInt(mVendorId);
+        dest.writeInt(mProductId);
+    }
+
+    public String getDescriptor() {
+        return mDescriptor;
+    }
+
+    public int getVendorId() {
+        return mVendorId;
+    }
+
+    public int getProductId() {
+        return mProductId;
+    }
+
+    public static final Parcelable.Creator<InputDeviceIdentifier> CREATOR =
+            new Parcelable.Creator<InputDeviceIdentifier>() {
+
+        @Override
+        public InputDeviceIdentifier createFromParcel(Parcel source) {
+            return new InputDeviceIdentifier(source);
+        }
+
+        @Override
+        public InputDeviceIdentifier[] newArray(int size) {
+            return new InputDeviceIdentifier[size];
+        }
+
+    };
+}
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 30e69a6..a2aeafb 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -26,6 +26,8 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.Vibrator;
@@ -373,20 +375,17 @@
     }
 
     /**
-     * Gets the current keyboard layout descriptor for the specified input device.
+     * Gets the current keyboard layout descriptor for the specified input
+     * device.
      *
-     * @param inputDeviceDescriptor The input device descriptor.
-     * @return The keyboard layout descriptor, or null if no keyboard layout has been set.
-     *
+     * @param identifier Identifier for the input device
+     * @return The keyboard layout descriptor, or null if no keyboard layout has
+     *         been set.
      * @hide
      */
-    public String getCurrentKeyboardLayoutForInputDevice(String inputDeviceDescriptor) {
-        if (inputDeviceDescriptor == null) {
-            throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
-        }
-
+    public String getCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier) {
         try {
-            return mIm.getCurrentKeyboardLayoutForInputDevice(inputDeviceDescriptor);
+            return mIm.getCurrentKeyboardLayoutForInputDevice(identifier);
         } catch (RemoteException ex) {
             Log.w(TAG, "Could not get current keyboard layout for input device.", ex);
             return null;
@@ -394,28 +393,29 @@
     }
 
     /**
-     * Sets the current keyboard layout descriptor for the specified input device.
+     * Sets the current keyboard layout descriptor for the specified input
+     * device.
      * <p>
-     * This method may have the side-effect of causing the input device in question
-     * to be reconfigured.
+     * This method may have the side-effect of causing the input device in
+     * question to be reconfigured.
      * </p>
      *
-     * @param inputDeviceDescriptor The input device descriptor.
-     * @param keyboardLayoutDescriptor The keyboard layout descriptor to use, must not be null.
-     *
+     * @param identifier The identifier for the input device.
+     * @param keyboardLayoutDescriptor The keyboard layout descriptor to use,
+     *            must not be null.
      * @hide
      */
-    public void setCurrentKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
+    public void setCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
             String keyboardLayoutDescriptor) {
-        if (inputDeviceDescriptor == null) {
-            throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
+        if (identifier == null) {
+            throw new IllegalArgumentException("identifier must not be null");
         }
         if (keyboardLayoutDescriptor == null) {
             throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
         }
 
         try {
-            mIm.setCurrentKeyboardLayoutForInputDevice(inputDeviceDescriptor,
+            mIm.setCurrentKeyboardLayoutForInputDevice(identifier,
                     keyboardLayoutDescriptor);
         } catch (RemoteException ex) {
             Log.w(TAG, "Could not set current keyboard layout for input device.", ex);
@@ -423,20 +423,20 @@
     }
 
     /**
-     * Gets all keyboard layout descriptors that are enabled for the specified input device.
+     * Gets all keyboard layout descriptors that are enabled for the specified
+     * input device.
      *
-     * @param inputDeviceDescriptor The input device descriptor.
+     * @param identifier The identifier for the input device.
      * @return The keyboard layout descriptors.
-     *
      * @hide
      */
-    public String[] getKeyboardLayoutsForInputDevice(String inputDeviceDescriptor) {
-        if (inputDeviceDescriptor == null) {
+    public String[] getKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier) {
+        if (identifier == null) {
             throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
         }
 
         try {
-            return mIm.getKeyboardLayoutsForInputDevice(inputDeviceDescriptor);
+            return mIm.getKeyboardLayoutsForInputDevice(identifier);
         } catch (RemoteException ex) {
             Log.w(TAG, "Could not get keyboard layouts for input device.", ex);
             return ArrayUtils.emptyArray(String.class);
@@ -446,18 +446,18 @@
     /**
      * Adds the keyboard layout descriptor for the specified input device.
      * <p>
-     * This method may have the side-effect of causing the input device in question
-     * to be reconfigured.
+     * This method may have the side-effect of causing the input device in
+     * question to be reconfigured.
      * </p>
      *
-     * @param inputDeviceDescriptor The input device descriptor.
-     * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to add.
-     *
+     * @param identifier The identifier for the input device.
+     * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to
+     *            add.
      * @hide
      */
-    public void addKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
+    public void addKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
             String keyboardLayoutDescriptor) {
-        if (inputDeviceDescriptor == null) {
+        if (identifier == null) {
             throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
         }
         if (keyboardLayoutDescriptor == null) {
@@ -465,7 +465,7 @@
         }
 
         try {
-            mIm.addKeyboardLayoutForInputDevice(inputDeviceDescriptor, keyboardLayoutDescriptor);
+            mIm.addKeyboardLayoutForInputDevice(identifier, keyboardLayoutDescriptor);
         } catch (RemoteException ex) {
             Log.w(TAG, "Could not add keyboard layout for input device.", ex);
         }
@@ -474,18 +474,18 @@
     /**
      * Removes the keyboard layout descriptor for the specified input device.
      * <p>
-     * This method may have the side-effect of causing the input device in question
-     * to be reconfigured.
+     * This method may have the side-effect of causing the input device in
+     * question to be reconfigured.
      * </p>
      *
-     * @param inputDeviceDescriptor The input device descriptor.
-     * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to remove.
-     *
+     * @param identifier The identifier for the input device.
+     * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to
+     *            remove.
      * @hide
      */
-    public void removeKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
+    public void removeKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
             String keyboardLayoutDescriptor) {
-        if (inputDeviceDescriptor == null) {
+        if (identifier == null) {
             throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
         }
         if (keyboardLayoutDescriptor == null) {
@@ -493,7 +493,7 @@
         }
 
         try {
-            mIm.removeKeyboardLayoutForInputDevice(inputDeviceDescriptor, keyboardLayoutDescriptor);
+            mIm.removeKeyboardLayoutForInputDevice(identifier, keyboardLayoutDescriptor);
         } catch (RemoteException ex) {
             Log.w(TAG, "Could not remove keyboard layout for input device.", ex);
         }
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index e829116..0b12cbe 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import android.content.Context;
+import android.hardware.input.InputDeviceIdentifier;
 import android.hardware.input.InputManager;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -49,6 +50,7 @@
     private final int mVendorId;
     private final int mProductId;
     private final String mDescriptor;
+    private final InputDeviceIdentifier mIdentifier;
     private final boolean mIsExternal;
     private final int mSources;
     private final int mKeyboardType;
@@ -61,7 +63,7 @@
 
     /**
      * A mask for input source classes.
-     * 
+     *
      * Each distinct input source constant has one or more input source class bits set to
      * specify the desired interpretation for its input events.
      */
@@ -77,46 +79,46 @@
     /**
      * The input source has buttons or keys.
      * Examples: {@link #SOURCE_KEYBOARD}, {@link #SOURCE_DPAD}.
-     * 
+     *
      * A {@link KeyEvent} should be interpreted as a button or key press.
-     * 
+     *
      * Use {@link #getKeyCharacterMap} to query the device's button and key mappings.
      */
     public static final int SOURCE_CLASS_BUTTON = 0x00000001;
-    
+
     /**
      * The input source is a pointing device associated with a display.
      * Examples: {@link #SOURCE_TOUCHSCREEN}, {@link #SOURCE_MOUSE}.
-     * 
+     *
      * A {@link MotionEvent} should be interpreted as absolute coordinates in
      * display units according to the {@link View} hierarchy.  Pointer down/up indicated when
      * the finger touches the display or when the selection button is pressed/released.
-     * 
+     *
      * Use {@link #getMotionRange} to query the range of the pointing device.  Some devices permit
      * touches outside the display area so the effective range may be somewhat smaller or larger
      * than the actual display size.
      */
     public static final int SOURCE_CLASS_POINTER = 0x00000002;
-    
+
     /**
      * The input source is a trackball navigation device.
      * Examples: {@link #SOURCE_TRACKBALL}.
-     * 
+     *
      * A {@link MotionEvent} should be interpreted as relative movements in device-specific
      * units used for navigation purposes.  Pointer down/up indicates when the selection button
      * is pressed/released.
-     * 
+     *
      * Use {@link #getMotionRange} to query the range of motion.
      */
     public static final int SOURCE_CLASS_TRACKBALL = 0x00000004;
-    
+
     /**
      * The input source is an absolute positioning device not associated with a display
      * (unlike {@link #SOURCE_CLASS_POINTER}).
-     * 
+     *
      * A {@link MotionEvent} should be interpreted as absolute coordinates in
      * device-specific surface units.
-     * 
+     *
      * Use {@link #getMotionRange} to query the range of positions.
      */
     public static final int SOURCE_CLASS_POSITION = 0x00000008;
@@ -134,7 +136,7 @@
      * The input source is unknown.
      */
     public static final int SOURCE_UNKNOWN = 0x00000000;
-    
+
     /**
      * The input source is a keyboard.
      *
@@ -145,10 +147,10 @@
      * @see #SOURCE_CLASS_BUTTON
      */
     public static final int SOURCE_KEYBOARD = 0x00000100 | SOURCE_CLASS_BUTTON;
-    
+
     /**
      * The input source is a DPad.
-     * 
+     *
      * @see #SOURCE_CLASS_BUTTON
      */
     public static final int SOURCE_DPAD = 0x00000200 | SOURCE_CLASS_BUTTON;
@@ -163,16 +165,16 @@
 
     /**
      * The input source is a touch screen pointing device.
-     * 
+     *
      * @see #SOURCE_CLASS_POINTER
      */
     public static final int SOURCE_TOUCHSCREEN = 0x00001000 | SOURCE_CLASS_POINTER;
-    
+
     /**
      * The input source is a mouse pointing device.
      * This code is also used for other mouse-like pointing devices such as trackpads
      * and trackpoints.
-     * 
+     *
      * @see #SOURCE_CLASS_POINTER
      */
     public static final int SOURCE_MOUSE = 0x00002000 | SOURCE_CLASS_POINTER;
@@ -199,15 +201,15 @@
 
     /**
      * The input source is a trackball.
-     * 
+     *
      * @see #SOURCE_CLASS_TRACKBALL
      */
     public static final int SOURCE_TRACKBALL = 0x00010000 | SOURCE_CLASS_TRACKBALL;
-    
+
     /**
      * The input source is a touch pad or digitizer tablet that is not
      * associated with a display (unlike {@link #SOURCE_TOUCHSCREEN}).
-     * 
+     *
      * @see #SOURCE_CLASS_POSITION
      */
     public static final int SOURCE_TOUCHPAD = 0x00100000 | SOURCE_CLASS_POSITION;
@@ -239,7 +241,7 @@
 
     /**
      * Constant for retrieving the range of values for {@link MotionEvent#AXIS_X}.
-     * 
+     *
      * @see #getMotionRange
      * @deprecated Use {@link MotionEvent#AXIS_X} instead.
      */
@@ -248,7 +250,7 @@
 
     /**
      * Constant for retrieving the range of values for {@link MotionEvent#AXIS_Y}.
-     * 
+     *
      * @see #getMotionRange
      * @deprecated Use {@link MotionEvent#AXIS_Y} instead.
      */
@@ -257,7 +259,7 @@
 
     /**
      * Constant for retrieving the range of values for {@link MotionEvent#AXIS_PRESSURE}.
-     * 
+     *
      * @see #getMotionRange
      * @deprecated Use {@link MotionEvent#AXIS_PRESSURE} instead.
      */
@@ -266,7 +268,7 @@
 
     /**
      * Constant for retrieving the range of values for {@link MotionEvent#AXIS_SIZE}.
-     * 
+     *
      * @see #getMotionRange
      * @deprecated Use {@link MotionEvent#AXIS_SIZE} instead.
      */
@@ -275,7 +277,7 @@
 
     /**
      * Constant for retrieving the range of values for {@link MotionEvent#AXIS_TOUCH_MAJOR}.
-     * 
+     *
      * @see #getMotionRange
      * @deprecated Use {@link MotionEvent#AXIS_TOUCH_MAJOR} instead.
      */
@@ -284,7 +286,7 @@
 
     /**
      * Constant for retrieving the range of values for {@link MotionEvent#AXIS_TOUCH_MINOR}.
-     * 
+     *
      * @see #getMotionRange
      * @deprecated Use {@link MotionEvent#AXIS_TOUCH_MINOR} instead.
      */
@@ -293,7 +295,7 @@
 
     /**
      * Constant for retrieving the range of values for {@link MotionEvent#AXIS_TOOL_MAJOR}.
-     * 
+     *
      * @see #getMotionRange
      * @deprecated Use {@link MotionEvent#AXIS_TOOL_MAJOR} instead.
      */
@@ -302,7 +304,7 @@
 
     /**
      * Constant for retrieving the range of values for {@link MotionEvent#AXIS_TOOL_MINOR}.
-     * 
+     *
      * @see #getMotionRange
      * @deprecated Use {@link MotionEvent#AXIS_TOOL_MINOR} instead.
      */
@@ -311,24 +313,24 @@
 
     /**
      * Constant for retrieving the range of values for {@link MotionEvent#AXIS_ORIENTATION}.
-     * 
+     *
      * @see #getMotionRange
      * @deprecated Use {@link MotionEvent#AXIS_ORIENTATION} instead.
      */
     @Deprecated
     public static final int MOTION_RANGE_ORIENTATION = MotionEvent.AXIS_ORIENTATION;
-    
+
     /**
      * There is no keyboard.
      */
     public static final int KEYBOARD_TYPE_NONE = 0;
-    
+
     /**
      * The keyboard is not fully alphabetic.  It may be a numeric keypad or an assortment
      * of buttons that are not mapped as alphabetic keys suitable for text input.
      */
     public static final int KEYBOARD_TYPE_NON_ALPHABETIC = 1;
-    
+
     /**
      * The keyboard supports a complement of alphabetic keys.
      */
@@ -361,6 +363,7 @@
         mKeyCharacterMap = keyCharacterMap;
         mHasVibrator = hasVibrator;
         mHasButtonUnderPad = hasButtonUnderPad;
+        mIdentifier = new InputDeviceIdentifier(descriptor, vendorId, productId);
     }
 
     private InputDevice(Parcel in) {
@@ -377,6 +380,7 @@
         mKeyCharacterMap = KeyCharacterMap.CREATOR.createFromParcel(in);
         mHasVibrator = in.readInt() != 0;
         mHasButtonUnderPad = in.readInt() != 0;
+        mIdentifier = new InputDeviceIdentifier(mDescriptor, mVendorId, mProductId);
 
         for (;;) {
             int axis = in.readInt();
@@ -396,7 +400,7 @@
     public static InputDevice getDevice(int id) {
         return InputManager.getInstance().getInputDevice(id);
     }
-    
+
     /**
      * Gets the ids of all input devices in the system.
      * @return The input device ids.
@@ -441,6 +445,18 @@
     }
 
     /**
+     * The set of identifying information for type of input device. This
+     * information can be used by the system to configure appropriate settings
+     * for the device.
+     *
+     * @return The identifier object for this device
+     * @hide
+     */
+    public InputDeviceIdentifier getIdentifier() {
+        return mIdentifier;
+    }
+
+    /**
      * Gets a generation number for this input device.
      * The generation number is incremented whenever the device is reconfigured and its
      * properties may have changed.
@@ -553,7 +569,7 @@
     public String getName() {
         return mName;
     }
-    
+
     /**
      * Gets the input sources supported by this input device as a combined bitfield.
      * @return The supported input sources.
@@ -561,7 +577,7 @@
     public int getSources() {
         return mSources;
     }
-    
+
     /**
      * Gets the keyboard type.
      * @return The keyboard type.
@@ -569,7 +585,7 @@
     public int getKeyboardType() {
         return mKeyboardType;
     }
-    
+
     /**
      * Gets the key character map associated with this input device.
      * @return The key character map.
diff --git a/libs/input/InputReader.cpp b/libs/input/InputReader.cpp
index a683c4b..94e2a80 100644
--- a/libs/input/InputReader.cpp
+++ b/libs/input/InputReader.cpp
@@ -911,7 +911,7 @@
         if (!changes || (changes & InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS)) {
             if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) {
                 sp<KeyCharacterMap> keyboardLayout =
-                        mContext->getPolicy()->getKeyboardLayoutOverlay(mIdentifier.descriptor);
+                        mContext->getPolicy()->getKeyboardLayoutOverlay(mIdentifier);
                 if (mContext->getEventHub()->setKeyboardLayoutOverlay(mId, keyboardLayout)) {
                     bumpGeneration();
                 }
diff --git a/libs/input/InputReader.h b/libs/input/InputReader.h
index e6f45b6..674f67d 100644
--- a/libs/input/InputReader.h
+++ b/libs/input/InputReader.h
@@ -281,7 +281,8 @@
     virtual void notifyInputDevicesChanged(const Vector<InputDeviceInfo>& inputDevices) = 0;
 
     /* Gets the keyboard layout for a particular input device. */
-    virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const String8& inputDeviceDescriptor) = 0;
+    virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(
+            const InputDeviceIdentifier& identifier) = 0;
 
     /* Gets a user-supplied alias for a particular input device, or an empty string if none. */
     virtual String8 getDeviceAlias(const InputDeviceIdentifier& identifier) = 0;
diff --git a/libs/input/tests/InputReader_test.cpp b/libs/input/tests/InputReader_test.cpp
index f068732..aaa973d 100644
--- a/libs/input/tests/InputReader_test.cpp
+++ b/libs/input/tests/InputReader_test.cpp
@@ -186,7 +186,7 @@
         mInputDevices = inputDevices;
     }
 
-    virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const String8& inputDeviceDescriptor) {
+    virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const InputDeviceIdentifier& identifier) {
         return NULL;
     }
 
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 3145805..c3c22f6 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -46,6 +46,7 @@
 import android.database.ContentObserver;
 import android.hardware.input.IInputDevicesChangedListener;
 import android.hardware.input.IInputManager;
+import android.hardware.input.InputDeviceIdentifier;
 import android.hardware.input.InputManager;
 import android.hardware.input.KeyboardLayout;
 import android.os.Binder;
@@ -378,7 +379,7 @@
     public int getScanCodeState(int deviceId, int sourceMask, int scanCode) {
         return nativeGetScanCodeState(mPtr, deviceId, sourceMask, scanCode);
     }
-    
+
     /**
      * Gets the current state of a switch by switch code.
      * @param deviceId The input device id, or -1 to consult all devices.
@@ -413,10 +414,10 @@
             throw new IllegalArgumentException("keyExists must not be null and must be at "
                     + "least as large as keyCodes.");
         }
-        
+
         return nativeHasKeys(mPtr, deviceId, sourceMask, keyCodes, keyExists);
     }
-    
+
     /**
      * Creates an input channel that will receive all input from the input dispatcher.
      * @param inputChannelName The input channel name.
@@ -426,7 +427,7 @@
         if (inputChannelName == null) {
             throw new IllegalArgumentException("inputChannelName must not be null.");
         }
-        
+
         InputChannel[] inputChannels = InputChannel.openInputChannelPair(inputChannelName);
         nativeRegisterInputChannel(mPtr, inputChannels[0], null, true);
         inputChannels[0].dispose(); // don't need to retain the Java object reference
@@ -444,10 +445,10 @@
         if (inputChannel == null) {
             throw new IllegalArgumentException("inputChannel must not be null.");
         }
-        
+
         nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);
     }
-    
+
     /**
      * Unregisters an input channel.
      * @param inputChannel The input channel to unregister.
@@ -456,7 +457,7 @@
         if (inputChannel == null) {
             throw new IllegalArgumentException("inputChannel must not be null.");
         }
-        
+
         nativeUnregisterInputChannel(mPtr, inputChannel);
     }
 
@@ -894,35 +895,62 @@
         }
     }
 
-    @Override // Binder call
-    public String getCurrentKeyboardLayoutForInputDevice(String inputDeviceDescriptor) {
-        if (inputDeviceDescriptor == null) {
-            throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
+    /**
+     * Builds a layout descriptor for the vendor/product. This returns the
+     * descriptor for ids that aren't useful (such as the default 0, 0).
+     */
+    private String getLayoutDescriptor(InputDeviceIdentifier identifier) {
+        if (identifier == null || identifier.getDescriptor() == null) {
+            throw new IllegalArgumentException("identifier and descriptor must not be null");
         }
 
+        if (identifier.getVendorId() == 0 && identifier.getProductId() == 0) {
+            return identifier.getDescriptor();
+        }
+        StringBuilder bob = new StringBuilder();
+        bob.append("vendor:").append(identifier.getVendorId());
+        bob.append(",product:").append(identifier.getProductId());
+        return bob.toString();
+    }
+
+    @Override // Binder call
+    public String getCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier) {
+
+        String key = getLayoutDescriptor(identifier);
         synchronized (mDataStore) {
-            return mDataStore.getCurrentKeyboardLayout(inputDeviceDescriptor);
+            String layout = null;
+            // try loading it using the layout descriptor if we have it
+            layout = mDataStore.getCurrentKeyboardLayout(key);
+            if (layout == null && !key.equals(identifier.getDescriptor())) {
+                // if it doesn't exist fall back to the device descriptor
+                layout = mDataStore.getCurrentKeyboardLayout(identifier.getDescriptor());
+            }
+            if (DEBUG) {
+                Slog.d(TAG, "Loaded keyboard layout id for " + key + " and got "
+                        + layout);
+            }
+            return layout;
         }
     }
 
     @Override // Binder call
-    public void setCurrentKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
+    public void setCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
             String keyboardLayoutDescriptor) {
         if (!checkCallingPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT,
                 "setCurrentKeyboardLayoutForInputDevice()")) {
             throw new SecurityException("Requires SET_KEYBOARD_LAYOUT permission");
         }
-        if (inputDeviceDescriptor == null) {
-            throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
-        }
         if (keyboardLayoutDescriptor == null) {
             throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
         }
 
+        String key = getLayoutDescriptor(identifier);
         synchronized (mDataStore) {
             try {
-                if (mDataStore.setCurrentKeyboardLayout(
-                        inputDeviceDescriptor, keyboardLayoutDescriptor)) {
+                if (mDataStore.setCurrentKeyboardLayout(key, keyboardLayoutDescriptor)) {
+                    if (DEBUG) {
+                        Slog.d(TAG, "Saved keyboard layout using " + key);
+                    }
                     mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS);
                 }
             } finally {
@@ -932,36 +960,39 @@
     }
 
     @Override // Binder call
-    public String[] getKeyboardLayoutsForInputDevice(String inputDeviceDescriptor) {
-        if (inputDeviceDescriptor == null) {
-            throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
-        }
-
+    public String[] getKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier) {
+        String key = getLayoutDescriptor(identifier);
         synchronized (mDataStore) {
-            return mDataStore.getKeyboardLayouts(inputDeviceDescriptor);
+            String[] layouts = mDataStore.getKeyboardLayouts(key);
+            if ((layouts == null || layouts.length == 0)
+                    && !key.equals(identifier.getDescriptor())) {
+                layouts = mDataStore.getKeyboardLayouts(identifier.getDescriptor());
+            }
+            return layouts;
         }
     }
 
     @Override // Binder call
-    public void addKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
+    public void addKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
             String keyboardLayoutDescriptor) {
         if (!checkCallingPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT,
                 "addKeyboardLayoutForInputDevice()")) {
             throw new SecurityException("Requires SET_KEYBOARD_LAYOUT permission");
         }
-        if (inputDeviceDescriptor == null) {
-            throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
-        }
         if (keyboardLayoutDescriptor == null) {
             throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
         }
 
+        String key = getLayoutDescriptor(identifier);
         synchronized (mDataStore) {
             try {
-                String oldLayout = mDataStore.getCurrentKeyboardLayout(inputDeviceDescriptor);
-                if (mDataStore.addKeyboardLayout(inputDeviceDescriptor, keyboardLayoutDescriptor)
+                String oldLayout = mDataStore.getCurrentKeyboardLayout(key);
+                if (oldLayout == null && !key.equals(identifier.getDescriptor())) {
+                    oldLayout = mDataStore.getCurrentKeyboardLayout(identifier.getDescriptor());
+                }
+                if (mDataStore.addKeyboardLayout(key, keyboardLayoutDescriptor)
                         && !Objects.equal(oldLayout,
-                                mDataStore.getCurrentKeyboardLayout(inputDeviceDescriptor))) {
+                                mDataStore.getCurrentKeyboardLayout(key))) {
                     mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS);
                 }
             } finally {
@@ -971,26 +1002,31 @@
     }
 
     @Override // Binder call
-    public void removeKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
+    public void removeKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
             String keyboardLayoutDescriptor) {
         if (!checkCallingPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT,
                 "removeKeyboardLayoutForInputDevice()")) {
             throw new SecurityException("Requires SET_KEYBOARD_LAYOUT permission");
         }
-        if (inputDeviceDescriptor == null) {
-            throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
-        }
         if (keyboardLayoutDescriptor == null) {
             throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
         }
 
+        String key = getLayoutDescriptor(identifier);
         synchronized (mDataStore) {
             try {
-                String oldLayout = mDataStore.getCurrentKeyboardLayout(inputDeviceDescriptor);
-                if (mDataStore.removeKeyboardLayout(inputDeviceDescriptor,
-                        keyboardLayoutDescriptor)
-                        && !Objects.equal(oldLayout,
-                                mDataStore.getCurrentKeyboardLayout(inputDeviceDescriptor))) {
+                String oldLayout = mDataStore.getCurrentKeyboardLayout(key);
+                if (oldLayout == null && !key.equals(identifier.getDescriptor())) {
+                    oldLayout = mDataStore.getCurrentKeyboardLayout(identifier.getDescriptor());
+                }
+                boolean removed = mDataStore.removeKeyboardLayout(key, keyboardLayoutDescriptor);
+                if (!key.equals(identifier.getDescriptor())) {
+                    // We need to remove from both places to ensure it is gone
+                    removed |= mDataStore.removeKeyboardLayout(identifier.getDescriptor(),
+                            keyboardLayoutDescriptor);
+                }
+                if (removed && !Objects.equal(oldLayout,
+                                mDataStore.getCurrentKeyboardLayout(key))) {
                     mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS);
                 }
             } finally {
@@ -1007,14 +1043,15 @@
     private void handleSwitchKeyboardLayout(int deviceId, int direction) {
         final InputDevice device = getInputDevice(deviceId);
         if (device != null) {
-            final String inputDeviceDescriptor = device.getDescriptor();
             final boolean changed;
             final String keyboardLayoutDescriptor;
+
+            String key = getLayoutDescriptor(device.getIdentifier());
             synchronized (mDataStore) {
                 try {
-                    changed = mDataStore.switchKeyboardLayout(inputDeviceDescriptor, direction);
+                    changed = mDataStore.switchKeyboardLayout(key, direction);
                     keyboardLayoutDescriptor = mDataStore.getCurrentKeyboardLayout(
-                            inputDeviceDescriptor);
+                            key);
                 } finally {
                     mDataStore.saveIfNeeded();
                 }
@@ -1042,11 +1079,11 @@
     public void setInputWindows(InputWindowHandle[] windowHandles) {
         nativeSetInputWindows(mPtr, windowHandles);
     }
-    
+
     public void setFocusedApplication(InputApplicationHandle application) {
         nativeSetFocusedApplication(mPtr, application);
     }
-    
+
     public void setInputDispatchMode(boolean enabled, boolean frozen) {
         nativeSetInputDispatchMode(mPtr, enabled, frozen);
     }
@@ -1426,13 +1463,12 @@
     }
 
     // Native callback.
-    private String[] getKeyboardLayoutOverlay(String inputDeviceDescriptor) {
+    private String[] getKeyboardLayoutOverlay(InputDeviceIdentifier identifier) {
         if (!mSystemReady) {
             return null;
         }
 
-        String keyboardLayoutDescriptor = getCurrentKeyboardLayoutForInputDevice(
-                inputDeviceDescriptor);
+        String keyboardLayoutDescriptor = getCurrentKeyboardLayoutForInputDevice(identifier);
         if (keyboardLayoutDescriptor == null) {
             return null;
         }
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 4ab2086..12a1c7a 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -99,6 +99,12 @@
     jclass clazz;
 } gMotionEventClassInfo;
 
+static struct {
+    jclass clazz;
+    jmethodID constructor;
+} gInputDeviceIdentifierInfo;
+
+
 
 // --- Global functions ---
 
@@ -183,7 +189,7 @@
     virtual void getReaderConfiguration(InputReaderConfiguration* outConfig);
     virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId);
     virtual void notifyInputDevicesChanged(const Vector<InputDeviceInfo>& inputDevices);
-    virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const String8& inputDeviceDescriptor);
+    virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const InputDeviceIdentifier& identifier);
     virtual String8 getDeviceAlias(const InputDeviceIdentifier& identifier);
 
     /* --- InputDispatcherPolicyInterface implementation --- */
@@ -492,13 +498,16 @@
 }
 
 sp<KeyCharacterMap> NativeInputManager::getKeyboardLayoutOverlay(
-        const String8& inputDeviceDescriptor) {
+        const InputDeviceIdentifier& identifier) {
     JNIEnv* env = jniEnv();
 
     sp<KeyCharacterMap> result;
-    ScopedLocalRef<jstring> descriptorObj(env, env->NewStringUTF(inputDeviceDescriptor.string()));
+    ScopedLocalRef<jstring> descriptor(env, env->NewStringUTF(identifier.descriptor.string()));
+    ScopedLocalRef<jobject> identifierObj(env, env->NewObject(gInputDeviceIdentifierInfo.clazz,
+            gInputDeviceIdentifierInfo.constructor, descriptor.get(),
+            identifier.vendor, identifier.product));
     ScopedLocalRef<jobjectArray> arrayObj(env, jobjectArray(env->CallObjectMethod(mServiceObj,
-                gServiceClassInfo.getKeyboardLayoutOverlay, descriptorObj.get())));
+                gServiceClassInfo.getKeyboardLayoutOverlay, identifierObj.get())));
     if (arrayObj.get()) {
         ScopedLocalRef<jstring> filenameObj(env,
                 jstring(env->GetObjectArrayElement(arrayObj.get(), 0)));
@@ -1438,7 +1447,8 @@
             "getPointerIcon", "()Landroid/view/PointerIcon;");
 
     GET_METHOD_ID(gServiceClassInfo.getKeyboardLayoutOverlay, clazz,
-            "getKeyboardLayoutOverlay", "(Ljava/lang/String;)[Ljava/lang/String;");
+            "getKeyboardLayoutOverlay",
+            "(Landroid/hardware/input/InputDeviceIdentifier;)[Ljava/lang/String;");
 
     GET_METHOD_ID(gServiceClassInfo.getDeviceAlias, clazz,
             "getDeviceAlias", "(Ljava/lang/String;)Ljava/lang/String;");
@@ -1458,6 +1468,13 @@
     FIND_CLASS(gMotionEventClassInfo.clazz, "android/view/MotionEvent");
     gMotionEventClassInfo.clazz = jclass(env->NewGlobalRef(gMotionEventClassInfo.clazz));
 
+    // InputDeviceIdentifier
+
+    FIND_CLASS(gInputDeviceIdentifierInfo.clazz, "android/hardware/input/InputDeviceIdentifier");
+    gInputDeviceIdentifierInfo.clazz = jclass(env->NewGlobalRef(gInputDeviceIdentifierInfo.clazz));
+    GET_METHOD_ID(gInputDeviceIdentifierInfo.constructor, gInputDeviceIdentifierInfo.clazz,
+            "<init>", "(Ljava/lang/String;II)V");
+
     return 0;
 }