Add a unique input device descriptor.

The purpose of the input device descriptor is to make it possible
to associate persistent settings for each input device, such as the
keyboard layout.

The descriptor is a hash of the information we have about the
device, such as its vendor id, product id, unique id, name,
or location.

Bug: 6110399
Change-Id: Idb80f946819b3f0dbf4e661bb0a753dbc2b60981
diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp
index 1b74aa6..665d711 100644
--- a/services/input/EventHub.cpp
+++ b/services/input/EventHub.cpp
@@ -40,6 +40,7 @@
 #include <androidfw/KeyCharacterMap.h>
 #include <androidfw/VirtualKeyMap.h>
 
+#include <sha1.h>
 #include <string.h>
 #include <stdint.h>
 #include <dirent.h>
@@ -78,6 +79,20 @@
     return value ? "true" : "false";
 }
 
+static String8 sha1(const String8& in) {
+    SHA1_CTX ctx;
+    SHA1Init(&ctx);
+    SHA1Update(&ctx, reinterpret_cast<const u_char*>(in.string()), in.size());
+    u_char digest[SHA1_DIGEST_LENGTH];
+    SHA1Final(digest, &ctx);
+
+    String8 out;
+    for (size_t i = 0; i < SHA1_DIGEST_LENGTH; i++) {
+        out.appendFormat("%02x", digest[i]);
+    }
+    return out;
+}
+
 // --- Global Functions ---
 
 uint32_t getAbsAxisUsage(int32_t axis, uint32_t deviceClasses) {
@@ -209,11 +224,11 @@
     release_wake_lock(WAKE_LOCK_ID);
 }
 
-String8 EventHub::getDeviceName(int32_t deviceId) const {
+InputDeviceIdentifier EventHub::getDeviceIdentifier(int32_t deviceId) const {
     AutoMutex _l(mLock);
     Device* device = getDeviceLocked(deviceId);
-    if (device == NULL) return String8();
-    return device->identifier.name;
+    if (device == NULL) return InputDeviceIdentifier();
+    return device->identifier;
 }
 
 uint32_t EventHub::getDeviceClasses(int32_t deviceId) const {
@@ -893,6 +908,30 @@
         identifier.uniqueId.setTo(buffer);
     }
 
+    // Compute a device descriptor that uniquely identifies the device.
+    // The descriptor is assumed to be a stable identifier.  Its value should not
+    // change between reboots, reconnections, firmware updates or new releases of Android.
+    // Ideally, we also want the descriptor to be short and relatively opaque.
+    String8 rawDescriptor;
+    rawDescriptor.appendFormat(":%04x:%04x:", identifier.vendor, identifier.product);
+    if (!identifier.uniqueId.isEmpty()) {
+        rawDescriptor.append("uniqueId:");
+        rawDescriptor.append(identifier.uniqueId);
+    } if (identifier.vendor == 0 && identifier.product == 0) {
+        // If we don't know the vendor and product id, then the device is probably
+        // built-in so we need to rely on other information to uniquely identify
+        // the input device.  Usually we try to avoid relying on the device name or
+        // location but for built-in input device, they are unlikely to ever change.
+        if (!identifier.name.isEmpty()) {
+            rawDescriptor.append("name:");
+            rawDescriptor.append(identifier.name);
+        } else if (!identifier.location.isEmpty()) {
+            rawDescriptor.append("location:");
+            rawDescriptor.append(identifier.location);
+        }
+    }
+    identifier.descriptor = sha1(rawDescriptor);
+
     // Make file descriptor non-blocking for use with poll().
     if (fcntl(fd, F_SETFL, O_NONBLOCK)) {
         ALOGE("Error %d making device file descriptor non-blocking.", errno);
@@ -904,19 +943,18 @@
     int32_t deviceId = mNextDeviceId++;
     Device* device = new Device(fd, deviceId, String8(devicePath), identifier);
 
-#if 0
-    ALOGI("add device %d: %s\n", deviceId, devicePath);
-    ALOGI("  bus:       %04x\n"
-         "  vendor     %04x\n"
-         "  product    %04x\n"
-         "  version    %04x\n",
+    ALOGV("add device %d: %s\n", deviceId, devicePath);
+    ALOGV("  bus:        %04x\n"
+         "  vendor      %04x\n"
+         "  product     %04x\n"
+         "  version     %04x\n",
         identifier.bus, identifier.vendor, identifier.product, identifier.version);
-    ALOGI("  name:      \"%s\"\n", identifier.name.string());
-    ALOGI("  location:  \"%s\"\n", identifier.location.string());
-    ALOGI("  unique id: \"%s\"\n", identifier.uniqueId.string());
-    ALOGI("  driver:    v%d.%d.%d\n",
+    ALOGV("  name:       \"%s\"\n", identifier.name.string());
+    ALOGV("  location:   \"%s\"\n", identifier.location.string());
+    ALOGV("  unique id:  \"%s\"\n", identifier.uniqueId.string());
+    ALOGV("  descriptor: \"%s\" (%s)\n", identifier.descriptor.string(), rawDescriptor.string());
+    ALOGV("  driver:     v%d.%d.%d\n",
         driverVersion >> 16, (driverVersion >> 8) & 0xff, driverVersion & 0xff);
-#endif
 
     // Load the configuration file for the device.
     loadConfigurationLocked(device);
@@ -1303,6 +1341,7 @@
             }
             dump.appendFormat(INDENT3 "Classes: 0x%08x\n", device->classes);
             dump.appendFormat(INDENT3 "Path: %s\n", device->path.string());
+            dump.appendFormat(INDENT3 "Descriptor: %s\n", device->identifier.descriptor.string());
             dump.appendFormat(INDENT3 "Location: %s\n", device->identifier.location.string());
             dump.appendFormat(INDENT3 "UniqueId: %s\n", device->identifier.uniqueId.string());
             dump.appendFormat(INDENT3 "Identifier: bus=0x%04x, vendor=0x%04x, "