From 5bbd4b4f5fc19302fa017ad6afee6eb2d489d91a Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Fri, 20 Apr 2012 19:28:00 -0700 Subject: [PATCH] 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 --- .../android/bluetooth/BluetoothDevice.java | 12 ++++++ .../android/server/BluetoothEventLoop.java | 4 ++ core/jni/android_view_InputDevice.cpp | 4 +- include/androidfw/InputDevice.h | 15 ++++--- libs/androidfw/InputDevice.cpp | 11 +++--- services/input/InputReader.cpp | 14 ++++++- services/input/InputReader.h | 7 ++++ services/input/tests/InputReader_test.cpp | 12 ++++-- .../java/com/android/server/SystemServer.java | 3 +- .../server/input/InputManagerService.java | 36 ++++++++++++++++- ...droid_server_input_InputManagerService.cpp | 39 +++++++++++++++---- 11 files changed, 127 insertions(+), 30 deletions(-) diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index 04af5f75ba7e0..56e17354cca2e 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -159,6 +159,18 @@ public final class BluetoothDevice implements Parcelable { public static final String ACTION_NAME_CHANGED = "android.bluetooth.device.action.NAME_CHANGED"; + /** + * Broadcast Action: Indicates the alias of a remote device has been + * changed. + *

Always contains the extra field {@link #EXTRA_DEVICE}. + *

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). diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java index a2038c9321568..9c887a1a54928 100644 --- a/core/java/android/server/BluetoothEventLoop.java +++ b/core/java/android/server/BluetoothEventLoop.java @@ -405,6 +405,10 @@ class BluetoothEventLoop { 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 5cb172bb9c530..d1f0a6a8673a7 100644 --- a/core/jni/android_view_InputDevice.cpp +++ b/core/jni/android_view_InputDevice.cpp @@ -35,13 +35,13 @@ static struct { } gInputDeviceClassInfo; jobject android_view_InputDevice_create(JNIEnv* env, const InputDeviceInfo& deviceInfo) { - ScopedLocalRef nameObj(env, env->NewStringUTF(deviceInfo.getName().string())); + ScopedLocalRef nameObj(env, env->NewStringUTF(deviceInfo.getDisplayName().string())); if (!nameObj.get()) { return NULL; } ScopedLocalRef 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 38203afac1495..d6ecbf0712a71 100644 --- a/include/androidfw/InputDevice.h +++ b/include/androidfw/InputDevice.h @@ -66,13 +66,16 @@ public: 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 @@ public: private: int32_t mId; int32_t mGeneration; - String8 mName; - String8 mDescriptor; + InputDeviceIdentifier mIdentifier; + String8 mAlias; uint32_t mSources; int32_t mKeyboardType; sp mKeyCharacterMap; diff --git a/libs/androidfw/InputDevice.cpp b/libs/androidfw/InputDevice.cpp index d6c49f7113291..5237063a4e94a 100644 --- a/libs/androidfw/InputDevice.cpp +++ b/libs/androidfw/InputDevice.cpp @@ -127,12 +127,11 @@ String8 getInputDeviceConfigurationFilePathByName( // --- 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 @@ InputDeviceInfo::~InputDeviceInfo() { } 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 3a48b1607130a..6022f107ec899 100644 --- a/services/input/InputReader.cpp +++ b/services/input/InputReader.cpp @@ -918,7 +918,7 @@ void InputDevice::dump(String8& dump) { 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 @@ void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config } } + 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::timeoutExpired(nsecs_t when) { } 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 acdec85a6ae5c..8257dbcef0977 100644 --- a/services/input/InputReader.h +++ b/services/input/InputReader.h @@ -70,6 +70,9 @@ struct InputReaderConfiguration { // 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 @@ public: /* Gets the keyboard layout for a particular input device. */ virtual sp 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 @@ private: int32_t mId; int32_t mGeneration; InputDeviceIdentifier mIdentifier; + String8 mAlias; uint32_t mClasses; Vector mMappers; diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp index a4b75854b4631..0f755aecd5447 100644 --- a/services/input/tests/InputReader_test.cpp +++ b/services/input/tests/InputReader_test.cpp @@ -174,6 +174,10 @@ private: virtual sp getKeyboardLayoutOverlay(const String8& inputDeviceDescriptor) { return NULL; } + + virtual String8 getDeviceAlias(const InputDeviceIdentifier& identifier) { + return String8::empty(); + } }; @@ -1081,7 +1085,7 @@ TEST_F(InputReaderTest, 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()); @@ -1090,7 +1094,7 @@ TEST_F(InputReaderTest, GetInputDevices) { 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 @@ TEST_F(InputDeviceTest, WhenNoMappersAreRegistered_DeviceIsIgnored) { 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 @@ TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRe 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 02c4d5a9cdb26..729c3f3cacf29 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -726,6 +726,7 @@ class ServerThread extends Thread { 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 @@ class ServerThread extends Thread { 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 a4ed31c040aff..189a9c7267463 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.XmlPullParserException; 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.Process; 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 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog. 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 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog. 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 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog. 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 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog. 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 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog. 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 b361a267f4725..b2a24292c02b9 100644 --- a/services/jni/com_android_server_input_InputManagerService.cpp +++ b/services/jni/com_android_server_input_InputManagerService.cpp @@ -83,6 +83,7 @@ static struct { jmethodID getPointerLayer; jmethodID getPointerIcon; jmethodID getKeyboardLayoutOverlay; + jmethodID getDeviceAlias; } gServiceClassInfo; static struct { @@ -183,7 +184,6 @@ public: void setSystemUiVisibility(int32_t visibility); void setPointerSpeed(int32_t speed); void setShowTouches(bool enabled); - void reloadKeyboardLayouts(); /* --- InputReaderPolicyInterface implementation --- */ @@ -191,6 +191,7 @@ public: virtual sp obtainPointerController(int32_t deviceId); virtual void notifyInputDevicesChanged(const Vector& inputDevices); virtual sp getKeyboardLayoutOverlay(const String8& inputDeviceDescriptor); + virtual String8 getDeviceAlias(const InputDeviceIdentifier& identifier); /* --- InputDispatcherPolicyInterface implementation --- */ @@ -551,6 +552,21 @@ sp NativeInputManager::getKeyboardLayoutOverlay( return result; } +String8 NativeInputManager::getDeviceAlias(const InputDeviceIdentifier& identifier) { + JNIEnv* env = jniEnv(); + + ScopedLocalRef uniqueIdObj(env, env->NewStringUTF(identifier.uniqueId.string())); + ScopedLocalRef 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 @@ void NativeInputManager::setShowTouches(bool enabled) { 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 @@ static void nativeReloadKeyboardLayouts(JNIEnv* env, jclass clazz, jint ptr) { NativeInputManager* im = reinterpret_cast(ptr); - im->reloadKeyboardLayouts(); + im->getInputManager()->getReader()->requestRefreshConfiguration( + InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS); +} + +static void nativeReloadDeviceAliases(JNIEnv* env, + jclass clazz, jint ptr) { + NativeInputManager* im = reinterpret_cast(ptr); + + im->getInputManager()->getReader()->requestRefreshConfiguration( + InputReaderConfiguration::CHANGE_DEVICE_ALIAS); } static jstring nativeDump(JNIEnv* env, jclass clazz, jint ptr) { @@ -1366,6 +1386,8 @@ static JNINativeMethod gInputManagerMethods[] = { (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 @@ int register_android_server_InputManager(JNIEnv* env) { 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");