Get alias for Bluetooth devices.

Bluetooth devices can be renamed by the user.  Make the
input system aware of the user-specified name and transparently
pass it down to applications.  This enables the keyboard
layout picker Settings UI to use device names that are
consistent with what the user set in the Bluetooth UI.

Bug: 6363157
Change-Id: I8eea26ce2c69c2a3f09c8de02e9e847610e0419c
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 04af5f7..56e1735 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -160,6 +160,18 @@
             "android.bluetooth.device.action.NAME_CHANGED";
 
     /**
+     * Broadcast Action: Indicates the alias of a remote device has been
+     * changed.
+     * <p>Always contains the extra field {@link #EXTRA_DEVICE}.
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_ALIAS_CHANGED =
+            "android.bluetooth.device.action.ALIAS_CHANGED";
+
+    /**
      * Broadcast Action: Indicates a change in the bond state of a remote
      * device. For example, if a device is bonded (paired).
      * <p>Always contains the extra fields {@link #EXTRA_DEVICE}, {@link
diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java
index a2038c9..9c887a1 100644
--- a/core/java/android/server/BluetoothEventLoop.java
+++ b/core/java/android/server/BluetoothEventLoop.java
@@ -405,6 +405,10 @@
             mContext.sendBroadcast(intent, BLUETOOTH_PERM);
         } else if (name.equals("Alias")) {
             mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
+            Intent intent = new Intent(BluetoothDevice.ACTION_ALIAS_CHANGED);
+            intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+            mContext.sendBroadcast(intent, BLUETOOTH_PERM);
         } else if (name.equals("Class")) {
             mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
             Intent intent = new Intent(BluetoothDevice.ACTION_CLASS_CHANGED);
diff --git a/core/jni/android_view_InputDevice.cpp b/core/jni/android_view_InputDevice.cpp
index 5cb172b..d1f0a6a 100644
--- a/core/jni/android_view_InputDevice.cpp
+++ b/core/jni/android_view_InputDevice.cpp
@@ -35,13 +35,13 @@
 } gInputDeviceClassInfo;
 
 jobject android_view_InputDevice_create(JNIEnv* env, const InputDeviceInfo& deviceInfo) {
-    ScopedLocalRef<jstring> nameObj(env, env->NewStringUTF(deviceInfo.getName().string()));
+    ScopedLocalRef<jstring> nameObj(env, env->NewStringUTF(deviceInfo.getDisplayName().string()));
     if (!nameObj.get()) {
         return NULL;
     }
 
     ScopedLocalRef<jstring> descriptorObj(env,
-            env->NewStringUTF(deviceInfo.getDescriptor().string()));
+            env->NewStringUTF(deviceInfo.getIdentifier().descriptor.string()));
     if (!descriptorObj.get()) {
         return NULL;
     }
diff --git a/include/androidfw/InputDevice.h b/include/androidfw/InputDevice.h
index 38203af..d6ecbf0 100644
--- a/include/androidfw/InputDevice.h
+++ b/include/androidfw/InputDevice.h
@@ -66,13 +66,16 @@
         float fuzz;
     };
 
-    void initialize(int32_t id, int32_t generation,
-            const String8& name, const String8& descriptor);
+    void initialize(int32_t id, int32_t generation, const InputDeviceIdentifier& identifier,
+            const String8& alias);
 
     inline int32_t getId() const { return mId; }
     inline int32_t getGeneration() const { return mGeneration; }
-    inline const String8 getName() const { return mName; }
-    inline const String8 getDescriptor() const { return mDescriptor; }
+    inline const InputDeviceIdentifier& getIdentifier() const { return mIdentifier; }
+    inline const String8& getAlias() const { return mAlias; }
+    inline const String8& getDisplayName() const {
+        return mAlias.isEmpty() ? mIdentifier.name : mAlias;
+    }
     inline uint32_t getSources() const { return mSources; }
 
     const MotionRange* getMotionRange(int32_t axis, uint32_t source) const;
@@ -103,8 +106,8 @@
 private:
     int32_t mId;
     int32_t mGeneration;
-    String8 mName;
-    String8 mDescriptor;
+    InputDeviceIdentifier mIdentifier;
+    String8 mAlias;
     uint32_t mSources;
     int32_t mKeyboardType;
     sp<KeyCharacterMap> mKeyCharacterMap;
diff --git a/libs/androidfw/InputDevice.cpp b/libs/androidfw/InputDevice.cpp
index d6c49f7..5237063 100644
--- a/libs/androidfw/InputDevice.cpp
+++ b/libs/androidfw/InputDevice.cpp
@@ -127,12 +127,11 @@
 // --- InputDeviceInfo ---
 
 InputDeviceInfo::InputDeviceInfo() {
-    initialize(-1, -1, String8("uninitialized device info"), String8("unknown"));
+    initialize(-1, -1, InputDeviceIdentifier(), String8());
 }
 
 InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) :
-        mId(other.mId), mGeneration(other.mGeneration),
-        mName(other.mName), mDescriptor(other.mDescriptor),
+        mId(other.mId), mGeneration(other.mGeneration), mIdentifier(other.mIdentifier),
         mSources(other.mSources),
         mKeyboardType(other.mKeyboardType),
         mKeyCharacterMap(other.mKeyCharacterMap),
@@ -144,11 +143,11 @@
 }
 
 void InputDeviceInfo::initialize(int32_t id, int32_t generation,
-        const String8& name, const String8& descriptor) {
+        const InputDeviceIdentifier& identifier, const String8& alias) {
     mId = id;
     mGeneration = generation;
-    mName = name;
-    mDescriptor = descriptor;
+    mIdentifier = identifier;
+    mAlias = alias;
     mSources = 0;
     mKeyboardType = AINPUT_KEYBOARD_TYPE_NONE;
     mHasVibrator = false;
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index 3a48b16..6022f10 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -918,7 +918,7 @@
     getDeviceInfo(& deviceInfo);
 
     dump.appendFormat(INDENT "Device %d: %s\n", deviceInfo.getId(),
-            deviceInfo.getName().string());
+            deviceInfo.getDisplayName().string());
     dump.appendFormat(INDENT2 "Generation: %d\n", mGeneration);
     dump.appendFormat(INDENT2 "IsExternal: %s\n", toString(mIsExternal));
     dump.appendFormat(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources());
@@ -972,6 +972,16 @@
             }
         }
 
+        if (!changes || (changes & InputReaderConfiguration::CHANGE_DEVICE_ALIAS)) {
+            if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) {
+                String8 alias = mContext->getPolicy()->getDeviceAlias(mIdentifier);
+                if (mAlias != alias) {
+                    mAlias = alias;
+                    bumpGeneration();
+                }
+            }
+        }
+
         size_t numMappers = mMappers.size();
         for (size_t i = 0; i < numMappers; i++) {
             InputMapper* mapper = mMappers[i];
@@ -1039,7 +1049,7 @@
 }
 
 void InputDevice::getDeviceInfo(InputDeviceInfo* outDeviceInfo) {
-    outDeviceInfo->initialize(mId, mGeneration, mIdentifier.name, mIdentifier.descriptor);
+    outDeviceInfo->initialize(mId, mGeneration, mIdentifier, mAlias);
 
     size_t numMappers = mMappers.size();
     for (size_t i = 0; i < numMappers; i++) {
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index acdec85..8257dbc 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -70,6 +70,9 @@
         // The keyboard layouts must be reloaded.
         CHANGE_KEYBOARD_LAYOUTS = 1 << 4,
 
+        // The device name alias supplied by the may have changed for some devices.
+        CHANGE_DEVICE_ALIAS = 1 << 5,
+
         // All devices must be reopened.
         CHANGE_MUST_REOPEN = 1 << 31,
     };
@@ -228,6 +231,9 @@
 
     /* Gets the keyboard layout for a particular input device. */
     virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const String8& inputDeviceDescriptor) = 0;
+
+    /* Gets a user-supplied alias for a particular input device, or an empty string if none. */
+    virtual String8 getDeviceAlias(const InputDeviceIdentifier& identifier) = 0;
 };
 
 
@@ -528,6 +534,7 @@
     int32_t mId;
     int32_t mGeneration;
     InputDeviceIdentifier mIdentifier;
+    String8 mAlias;
     uint32_t mClasses;
 
     Vector<InputMapper*> mMappers;
diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp
index a4b7585..0f755ae 100644
--- a/services/input/tests/InputReader_test.cpp
+++ b/services/input/tests/InputReader_test.cpp
@@ -174,6 +174,10 @@
     virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const String8& inputDeviceDescriptor) {
         return NULL;
     }
+
+    virtual String8 getDeviceAlias(const InputDeviceIdentifier& identifier) {
+        return String8::empty();
+    }
 };
 
 
@@ -1081,7 +1085,7 @@
 
     ASSERT_EQ(1U, inputDevices.size());
     ASSERT_EQ(1, inputDevices[0].getId());
-    ASSERT_STREQ("keyboard", inputDevices[0].getName().string());
+    ASSERT_STREQ("keyboard", inputDevices[0].getIdentifier().name.string());
     ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, inputDevices[0].getKeyboardType());
     ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, inputDevices[0].getSources());
     ASSERT_EQ(size_t(0), inputDevices[0].getMotionRanges().size());
@@ -1090,7 +1094,7 @@
     inputDevices = mFakePolicy->getInputDevices();
     ASSERT_EQ(1U, inputDevices.size());
     ASSERT_EQ(1, inputDevices[0].getId());
-    ASSERT_STREQ("keyboard", inputDevices[0].getName().string());
+    ASSERT_STREQ("keyboard", inputDevices[0].getIdentifier().name.string());
     ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, inputDevices[0].getKeyboardType());
     ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, inputDevices[0].getSources());
     ASSERT_EQ(size_t(0), inputDevices[0].getMotionRanges().size());
@@ -1311,7 +1315,7 @@
     InputDeviceInfo info;
     mDevice->getDeviceInfo(&info);
     ASSERT_EQ(DEVICE_ID, info.getId());
-    ASSERT_STREQ(DEVICE_NAME, info.getName().string());
+    ASSERT_STREQ(DEVICE_NAME, info.getIdentifier().name.string());
     ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NONE, info.getKeyboardType());
     ASSERT_EQ(AINPUT_SOURCE_UNKNOWN, info.getSources());
 
@@ -1381,7 +1385,7 @@
     InputDeviceInfo info;
     mDevice->getDeviceInfo(&info);
     ASSERT_EQ(DEVICE_ID, info.getId());
-    ASSERT_STREQ(DEVICE_NAME, info.getName().string());
+    ASSERT_STREQ(DEVICE_NAME, info.getIdentifier().name.string());
     ASSERT_EQ(AINPUT_KEYBOARD_TYPE_ALPHABETIC, info.getKeyboardType());
     ASSERT_EQ(uint32_t(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TOUCHSCREEN), info.getSources());
 
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 02c4d5a..729c3f3 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -726,6 +726,7 @@
         final StatusBarManagerService statusBarF = statusBar;
         final DreamManagerService dreamyF = dreamy;
         final InputManagerService inputManagerF = inputManager;
+        final BluetoothService bluetoothF = bluetooth;
 
         // We now tell the activity manager it is okay to run third party
         // code.  It will call back into us once it has gotten to the state
@@ -838,7 +839,7 @@
                     reportWtf("making DreamManagerService ready", e);
                 }
                 try {
-                    if (inputManagerF != null) inputManagerF.systemReady();
+                    if (inputManagerF != null) inputManagerF.systemReady(bluetoothF);
                 } catch (Throwable e) {
                     reportWtf("making InputManagerService ready", e);
                 }
diff --git a/services/java/com/android/server/input/InputManagerService.java b/services/java/com/android/server/input/InputManagerService.java
index a4ed31c..189a9c7 100644
--- a/services/java/com/android/server/input/InputManagerService.java
+++ b/services/java/com/android/server/input/InputManagerService.java
@@ -26,6 +26,8 @@
 import org.xmlpull.v1.XmlSerializer;
 
 import android.Manifest;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -56,6 +58,7 @@
 import android.os.RemoteException;
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
+import android.server.BluetoothService;
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -106,6 +109,7 @@
     private final Callbacks mCallbacks;
     private final InputManagerHandler mHandler;
     private boolean mSystemReady;
+    private BluetoothService mBluetoothService;
 
     // Persistent data store.  Must be locked each time during use.
     private final PersistentDataStore mDataStore = new PersistentDataStore();
@@ -167,6 +171,7 @@
             int repeat, int token);
     private static native void nativeCancelVibrate(int ptr, int deviceId, int token);
     private static native void nativeReloadKeyboardLayouts(int ptr);
+    private static native void nativeReloadDeviceAliases(int ptr);
     private static native String nativeDump(int ptr);
     private static native void nativeMonitor(int ptr);
 
@@ -217,12 +222,12 @@
         updateShowTouchesFromSettings();
     }
 
-    public void systemReady() {
+    public void systemReady(BluetoothService bluetoothService) {
         if (DEBUG) {
             Slog.d(TAG, "System ready.");
         }
+        mBluetoothService = bluetoothService;
         mSystemReady = true;
-        reloadKeyboardLayouts();
 
         IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
         filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
@@ -237,12 +242,30 @@
                 reloadKeyboardLayouts();
             }
         }, filter, null, mHandler);
+
+        filter = new IntentFilter(BluetoothDevice.ACTION_ALIAS_CHANGED);
+        mContext.registerReceiver(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                if (DEBUG) {
+                    Slog.d(TAG, "Bluetooth alias changed, reloading device names.");
+                }
+                reloadDeviceAliases();
+            }
+        }, filter, null, mHandler);
+
+        reloadKeyboardLayouts();
+        reloadDeviceAliases();
     }
 
     private void reloadKeyboardLayouts() {
         nativeReloadKeyboardLayouts(mPtr);
     }
 
+    private void reloadDeviceAliases() {
+        nativeReloadDeviceAliases(mPtr);
+    }
+
     public void setDisplaySize(int displayId, int width, int height,
             int externalWidth, int externalHeight) {
         if (width <= 0 || height <= 0 || externalWidth <= 0 || externalHeight <= 0) {
@@ -1121,6 +1144,15 @@
         return result;
     }
 
+    // Native callback.
+    private String getDeviceAlias(String uniqueId) {
+        if (mBluetoothService != null &&
+                BluetoothAdapter.checkBluetoothAddress(uniqueId)) {
+            return mBluetoothService.getRemoteAlias(uniqueId);
+        }
+        return null;
+    }
+
 
     /**
      * Callback interface implemented by the Window Manager.
diff --git a/services/jni/com_android_server_input_InputManagerService.cpp b/services/jni/com_android_server_input_InputManagerService.cpp
index b361a26..b2a2429 100644
--- a/services/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/jni/com_android_server_input_InputManagerService.cpp
@@ -83,6 +83,7 @@
     jmethodID getPointerLayer;
     jmethodID getPointerIcon;
     jmethodID getKeyboardLayoutOverlay;
+    jmethodID getDeviceAlias;
 } gServiceClassInfo;
 
 static struct {
@@ -183,7 +184,6 @@
     void setSystemUiVisibility(int32_t visibility);
     void setPointerSpeed(int32_t speed);
     void setShowTouches(bool enabled);
-    void reloadKeyboardLayouts();
 
     /* --- InputReaderPolicyInterface implementation --- */
 
@@ -191,6 +191,7 @@
     virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId);
     virtual void notifyInputDevicesChanged(const Vector<InputDeviceInfo>& inputDevices);
     virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const String8& inputDeviceDescriptor);
+    virtual String8 getDeviceAlias(const InputDeviceIdentifier& identifier);
 
     /* --- InputDispatcherPolicyInterface implementation --- */
 
@@ -551,6 +552,21 @@
     return result;
 }
 
+String8 NativeInputManager::getDeviceAlias(const InputDeviceIdentifier& identifier) {
+    JNIEnv* env = jniEnv();
+
+    ScopedLocalRef<jstring> uniqueIdObj(env, env->NewStringUTF(identifier.uniqueId.string()));
+    ScopedLocalRef<jstring> aliasObj(env, jstring(env->CallObjectMethod(mServiceObj,
+            gServiceClassInfo.getDeviceAlias, uniqueIdObj.get())));
+    String8 result;
+    if (aliasObj.get()) {
+        ScopedUtfChars aliasChars(env, aliasObj.get());
+        result.setTo(aliasChars.c_str());
+    }
+    checkAndClearExceptionFromCallback(env, "getDeviceAlias");
+    return result;
+}
+
 void NativeInputManager::notifySwitch(nsecs_t when, int32_t switchCode,
         int32_t switchValue, uint32_t policyFlags) {
 #if DEBUG_INPUT_DISPATCHER_POLICY
@@ -757,11 +773,6 @@
             InputReaderConfiguration::CHANGE_SHOW_TOUCHES);
 }
 
-void NativeInputManager::reloadKeyboardLayouts() {
-    mInputManager->getReader()->requestRefreshConfiguration(
-            InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS);
-}
-
 bool NativeInputManager::isScreenOn() {
     return android_server_PowerManagerService_isScreenOn();
 }
@@ -1296,7 +1307,16 @@
         jclass clazz, jint ptr) {
     NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
 
-    im->reloadKeyboardLayouts();
+    im->getInputManager()->getReader()->requestRefreshConfiguration(
+            InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS);
+}
+
+static void nativeReloadDeviceAliases(JNIEnv* env,
+        jclass clazz, jint ptr) {
+    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+    im->getInputManager()->getReader()->requestRefreshConfiguration(
+            InputReaderConfiguration::CHANGE_DEVICE_ALIAS);
 }
 
 static jstring nativeDump(JNIEnv* env, jclass clazz, jint ptr) {
@@ -1366,6 +1386,8 @@
             (void*) nativeCancelVibrate },
     { "nativeReloadKeyboardLayouts", "(I)V",
             (void*) nativeReloadKeyboardLayouts },
+    { "nativeReloadDeviceAliases", "(I)V",
+            (void*) nativeReloadDeviceAliases },
     { "nativeDump", "(I)Ljava/lang/String;",
             (void*) nativeDump },
     { "nativeMonitor", "(I)V",
@@ -1464,6 +1486,9 @@
     GET_METHOD_ID(gServiceClassInfo.getKeyboardLayoutOverlay, clazz,
             "getKeyboardLayoutOverlay", "(Ljava/lang/String;)[Ljava/lang/String;");
 
+    GET_METHOD_ID(gServiceClassInfo.getDeviceAlias, clazz,
+            "getDeviceAlias", "(Ljava/lang/String;)Ljava/lang/String;");
+
     // InputDevice
 
     FIND_CLASS(gInputDeviceClassInfo.clazz, "android/view/InputDevice");