diff options
| author | 2022-10-03 22:18:58 +0000 | |
|---|---|---|
| committer | 2022-10-27 20:45:22 +0000 | |
| commit | e513b7ffe35460072038548a9c12ea98a618e67f (patch) | |
| tree | 4c09dc58ecc8d999ebb0b21c53be85180a381f06 | |
| parent | 7a762763d8560af77cfd95c6df4207681547936d (diff) | |
Add hidden API to query the bluetooth address of an InputDevice
Since revealing the bluetooth address of an input device can reveal
whether bluetooth is enabled, we require the BLUETOOTH permission for
this API.
This also means we cannot send the bluetooth address to apps using the
InputDevice class. We add an IInputManager method to query the address
instead that performs the permission check.
Since we are not using the Java InputDevice class to store the address,
we must cache the vector of last-reported InputDeviceInfos in
NativeInputManager.
DD: go/inputdevice-battery-notifications
Bug: 243005009
Test: manual
Change-Id: I560cb170ab7008314821e5150caf4868efb5d8d5
7 files changed, 70 insertions, 0 deletions
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl index f213224b981e..49c0f9261c53 100644 --- a/core/java/android/hardware/input/IInputManager.aidl +++ b/core/java/android/hardware/input/IInputManager.aidl @@ -161,4 +161,11 @@ interface IInputManager { void registerBatteryListener(int deviceId, IInputDeviceBatteryListener listener); void unregisterBatteryListener(int deviceId, IInputDeviceBatteryListener listener); + + // Get the bluetooth address of an input device if known, returning null if it either is not + // connected via bluetooth or if the address cannot be determined. + @EnforcePermission("BLUETOOTH") + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = " + + "android.Manifest.permission.BLUETOOTH)") + String getInputDeviceBluetoothAddress(int deviceId); } diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java index 8d4aac4bba88..0cf15f76103e 100644 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -1481,6 +1481,24 @@ public final class InputManager { } /** + * Returns the Bluetooth address of this input device, if known. + * + * The returned string is always null if this input device is not connected + * via Bluetooth, or if the Bluetooth address of the device cannot be + * determined. The returned address will look like: "11:22:33:44:55:66". + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + @Nullable + public String getInputDeviceBluetoothAddress(int deviceId) { + try { + return mIm.getInputDeviceBluetoothAddress(deviceId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Gets a vibrator service associated with an input device, always creates a new instance. * @return The vibrator, never null. * @hide diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java index 9b1d8673390b..799955b1107a 100644 --- a/core/java/android/view/InputDevice.java +++ b/core/java/android/view/InputDevice.java @@ -16,6 +16,7 @@ package android.view; +import android.Manifest; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -1010,6 +1011,22 @@ public final class InputDevice implements Parcelable { } /** + * Returns the Bluetooth address of this input device, if known. + * + * The returned string is always null if this input device is not connected + * via Bluetooth, or if the Bluetooth address of the device cannot be + * determined. The returned address will look like: "11:22:33:44:55:66". + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + @Nullable + public String getBluetoothAddress() { + // We query the address via a separate InputManager API instead of pre-populating it in + // this class to avoid leaking it to apps that do not have sufficient permissions. + return InputManager.getInstance().getInputDeviceBluetoothAddress(mId); + } + + /** * Gets the vibrator service associated with the device, if there is one. * Even if the device does not have a vibrator, the result is never null. * Use {@link Vibrator#hasVibrator} to determine whether a vibrator is diff --git a/core/jni/android_view_InputDevice.cpp b/core/jni/android_view_InputDevice.cpp index 39ec0374dc5e..b2994f41af4b 100644 --- a/core/jni/android_view_InputDevice.cpp +++ b/core/jni/android_view_InputDevice.cpp @@ -70,6 +70,8 @@ jobject android_view_InputDevice_create(JNIEnv* env, const InputDeviceInfo& devi deviceInfo.hasMic(), deviceInfo.hasButtonUnderPad(), deviceInfo.hasSensor(), deviceInfo.hasBattery(), deviceInfo.supportsUsi())); + // Note: We do not populate the Bluetooth address into the InputDevice object to avoid leaking + // it to apps that do not have the Bluetooth permission. const std::vector<InputDeviceInfo::MotionRange>& ranges = deviceInfo.getMotionRanges(); for (const InputDeviceInfo::MotionRange& range: ranges) { diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 08e16763c6aa..31f63d864f6c 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -19,6 +19,8 @@ package com.android.server.input; import static android.provider.DeviceConfig.NAMESPACE_INPUT_NATIVE_BOOT; import static android.view.KeyEvent.KEYCODE_UNKNOWN; +import android.Manifest; +import android.annotation.EnforcePermission; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManagerInternal; @@ -2671,6 +2673,12 @@ public class InputManagerService extends IInputManager.Stub mBatteryController.unregisterBatteryListener(deviceId, listener, Binder.getCallingPid()); } + @EnforcePermission(Manifest.permission.BLUETOOTH) + @Override + public String getInputDeviceBluetoothAddress(int deviceId) { + return mNative.getBluetoothAddress(deviceId); + } + @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; diff --git a/services/core/java/com/android/server/input/NativeInputManagerService.java b/services/core/java/com/android/server/input/NativeInputManagerService.java index 63c0a88bf467..cfa7fb141be1 100644 --- a/services/core/java/com/android/server/input/NativeInputManagerService.java +++ b/services/core/java/com/android/server/input/NativeInputManagerService.java @@ -204,6 +204,9 @@ interface NativeInputManagerService { /** Set the displayId on which the mouse cursor should be shown. */ void setPointerDisplayId(int displayId); + /** Get the bluetooth address of an input device if known, otherwise return null. */ + String getBluetoothAddress(int deviceId); + /** The native implementation of InputManagerService methods. */ class NativeImpl implements NativeInputManagerService { /** Pointer to native input manager service object, used by native code. */ @@ -418,5 +421,8 @@ interface NativeInputManagerService { @Override public native void setPointerDisplayId(int displayId); + + @Override + public native String getBluetoothAddress(int deviceId); } } diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 705284d376fd..0d872370dcdc 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -300,6 +300,7 @@ public: void requestPointerCapture(const sp<IBinder>& windowToken, bool enabled); void setCustomPointerIcon(const SpriteIcon& icon); void setMotionClassifierEnabled(bool enabled); + std::optional<std::string> getBluetoothAddress(int32_t deviceId); /* --- InputReaderPolicyInterface implementation --- */ @@ -1486,6 +1487,10 @@ void NativeInputManager::setMotionClassifierEnabled(bool enabled) { mInputManager->getProcessor().setMotionClassifierEnabled(enabled); } +std::optional<std::string> NativeInputManager::getBluetoothAddress(int32_t deviceId) { + return mInputManager->getReader().getBluetoothAddress(deviceId); +} + bool NativeInputManager::isPerDisplayTouchModeEnabled() { JNIEnv* env = jniEnv(); jboolean enabled = @@ -2325,6 +2330,12 @@ static void nativeSetPointerDisplayId(JNIEnv* env, jobject nativeImplObj, jint d im->setPointerDisplayId(displayId); } +static jstring nativeGetBluetoothAddress(JNIEnv* env, jobject nativeImplObj, jint deviceId) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); + const auto address = im->getBluetoothAddress(deviceId); + return address ? env->NewStringUTF(address->c_str()) : nullptr; +} + // ---------------------------------------------------------------------------- static const JNINativeMethod gInputManagerMethods[] = { @@ -2407,6 +2418,7 @@ static const JNINativeMethod gInputManagerMethods[] = { {"flushSensor", "(II)Z", (void*)nativeFlushSensor}, {"cancelCurrentTouch", "()V", (void*)nativeCancelCurrentTouch}, {"setPointerDisplayId", "(I)V", (void*)nativeSetPointerDisplayId}, + {"getBluetoothAddress", "(I)Ljava/lang/String;", (void*)nativeGetBluetoothAddress}, }; #define FIND_CLASS(var, className) \ |