Adding feature: input device disable/enable.
This functionality will only be available for signed system applications.
A disable call will cause a file descriptor to the input device
driver to be closed, which in turn may cause the input device
to switch into a low-power mode.
An enable call will reopen the input device.
Bug: 30143923
Test: developed a custom apk with signature permission that
calls disable/enable on touchscreen device. Verified that
touchscreen stops working when disable is called and starts
working again when enable is called. Verified that the file
handle to the driver is closed and reopened. Verified that
the notification onInputDeviceChanged is received in the app.
CTS test - android.view.cts.InputDeviceEnabledTest
Change-Id: I1e42c6996e679c083cd3f0c9adfaef8ba22c4ee4
diff --git a/api/current.txt b/api/current.txt
index 405e91e..8211768 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -44101,6 +44101,7 @@
method public android.os.Vibrator getVibrator();
method public boolean[] hasKeys(int...);
method public boolean hasMicrophone();
+ method public boolean isEnabled();
method public boolean isVirtual();
method public boolean supportsSource(int);
method public void writeToParcel(android.os.Parcel, int);
diff --git a/api/system-current.txt b/api/system-current.txt
index 8376662..5aa7b22 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -47696,6 +47696,7 @@
method public android.os.Vibrator getVibrator();
method public boolean[] hasKeys(int...);
method public boolean hasMicrophone();
+ method public boolean isEnabled();
method public boolean isVirtual();
method public boolean supportsSource(int);
method public void writeToParcel(android.os.Parcel, int);
diff --git a/api/test-current.txt b/api/test-current.txt
index 33f4f7d..14a9976 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -44465,6 +44465,8 @@
public final class InputDevice implements android.os.Parcelable {
method public int describeContents();
+ method public void disable();
+ method public void enable();
method public int getControllerNumber();
method public java.lang.String getDescriptor();
method public static android.view.InputDevice getDevice(int);
@@ -44482,6 +44484,7 @@
method public android.os.Vibrator getVibrator();
method public boolean[] hasKeys(int...);
method public boolean hasMicrophone();
+ method public boolean isEnabled();
method public boolean isVirtual();
method public boolean supportsSource(int);
method public void writeToParcel(android.os.Parcel, int);
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index bdb278b..4586316 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -34,6 +34,11 @@
InputDevice getInputDevice(int deviceId);
int[] getInputDeviceIds();
+ // Enable/disable input device.
+ boolean isInputDeviceEnabled(int deviceId);
+ void enableInputDevice(int deviceId);
+ void disableInputDevice(int deviceId);
+
// Reports whether the hardware supports the given keys; returns true if successful
boolean hasKeys(int deviceId, int sourceMask, in int[] keyCodes, out boolean[] keyExists);
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 631b77d..22b3638 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -16,8 +16,6 @@
package android.hardware.input;
-import com.android.internal.os.SomeArgs;
-
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
@@ -31,10 +29,10 @@
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.os.Vibrator;
-import android.os.VibrationEffect;
import android.os.ServiceManager.ServiceNotFoundException;
+import android.os.SystemClock;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.util.Log;
@@ -46,6 +44,8 @@
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
+import com.android.internal.os.SomeArgs;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -323,6 +323,62 @@
}
/**
+ * Returns true if an input device is enabled. Should return true for most
+ * situations. Some system apps may disable an input device, for
+ * example to prevent unwanted touch events.
+ *
+ * @param id The input device Id.
+ *
+ * @hide
+ */
+ public boolean isInputDeviceEnabled(int id) {
+ try {
+ return mIm.isInputDeviceEnabled(id);
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Could not check enabled status of input device with id = " + id);
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Enables an InputDevice.
+ * <p>
+ * Requires {@link android.Manifest.permissions.DISABLE_INPUT_DEVICE}.
+ * </p>
+ *
+ * @param id The input device Id.
+ *
+ * @hide
+ */
+ public void enableInputDevice(int id) {
+ try {
+ mIm.enableInputDevice(id);
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Could not enable input device with id = " + id);
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Disables an InputDevice.
+ * <p>
+ * Requires {@link android.Manifest.permissions.DISABLE_INPUT_DEVICE}.
+ * </p>
+ *
+ * @param id The input device Id.
+ *
+ * @hide
+ */
+ public void disableInputDevice(int id) {
+ try {
+ mIm.disableInputDevice(id);
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Could not disable input device with id = " + id);
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Registers an input device listener to receive notifications about when
* input devices are added, removed or changed.
*
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index ea2434e..8405d9e 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -16,6 +16,8 @@
package android.view;
+import android.annotation.RequiresPermission;
+import android.annotation.TestApi;
import android.content.Context;
import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.InputManager;
@@ -768,6 +770,36 @@
}
/**
+ * Returns true if input device is enabled.
+ * @return Whether the input device is enabled.
+ */
+ public boolean isEnabled() {
+ return InputManager.getInstance().isInputDeviceEnabled(mId);
+ }
+
+ /**
+ * Enables the input device.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.DISABLE_INPUT_DEVICE)
+ @TestApi
+ public void enable() {
+ InputManager.getInstance().enableInputDevice(mId);
+ }
+
+ /**
+ * Disables the input device.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.DISABLE_INPUT_DEVICE)
+ @TestApi
+ public void disable() {
+ InputManager.getInstance().disableInputDevice(mId);
+ }
+
+ /**
* Reports whether the device has a built-in microphone.
* @return Whether the device has a built-in microphone.
*/
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4811f7a..0565ba1 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2743,6 +2743,13 @@
<permission android:name="android.permission.ACCESS_INPUT_FLINGER"
android:protectionLevel="signature" />
+ <!-- Allows an application to disable/enable input devices.
+ Could be used to prevent unwanted touch events
+ on a touchscreen, for example during swimming or rain.
+ @hide -->
+ <permission android:name="android.permission.DISABLE_INPUT_DEVICE"
+ android:protectionLevel="signature" />
+
<!-- Allows an application to configure and connect to Wifi displays
@hide -->
<permission android:name="android.permission.CONFIGURE_WIFI_DISPLAY"
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index aafc9a8..0e52871 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -230,6 +230,9 @@
private static native void nativeReloadDeviceAliases(long ptr);
private static native String nativeDump(long ptr);
private static native void nativeMonitor(long ptr);
+ private static native boolean nativeIsInputDeviceEnabled(long ptr, int deviceId);
+ private static native void nativeEnableInputDevice(long ptr, int deviceId);
+ private static native void nativeDisableInputDevice(long ptr, int deviceId);
private static native void nativeSetPointerIconType(long ptr, int iconId);
private static native void nativeReloadPointerIcons(long ptr);
private static native void nativeSetCustomPointerIcon(long ptr, PointerIcon icon);
@@ -639,6 +642,32 @@
return null;
}
+ // Binder call
+ @Override
+ public boolean isInputDeviceEnabled(int deviceId) {
+ return nativeIsInputDeviceEnabled(mPtr, deviceId);
+ }
+
+ // Binder call
+ @Override
+ public void enableInputDevice(int deviceId) {
+ if (!checkCallingPermission(android.Manifest.permission.DISABLE_INPUT_DEVICE,
+ "enableInputDevice()")) {
+ throw new SecurityException("Requires DISABLE_INPUT_DEVICE permission");
+ }
+ nativeEnableInputDevice(mPtr, deviceId);
+ }
+
+ // Binder call
+ @Override
+ public void disableInputDevice(int deviceId) {
+ if (!checkCallingPermission(android.Manifest.permission.DISABLE_INPUT_DEVICE,
+ "disableInputDevice()")) {
+ throw new SecurityException("Requires DISABLE_INPUT_DEVICE permission");
+ }
+ nativeDisableInputDevice(mPtr, deviceId);
+ }
+
/**
* Gets the ids of all input devices in the system.
* @return The input device ids.
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 0006110..14a2381 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -36,6 +36,7 @@
#include <utils/Log.h>
#include <utils/Looper.h>
#include <utils/threads.h>
+#include <utils/SortedVector.h>
#include <input/PointerController.h>
#include <input/SpriteController.h>
@@ -206,6 +207,7 @@
void setInputDispatchMode(bool enabled, bool frozen);
void setSystemUiVisibility(int32_t visibility);
void setPointerSpeed(int32_t speed);
+ void setInputDeviceEnabled(uint32_t deviceId, bool enabled);
void setShowTouches(bool enabled);
void setInteractive(bool interactive);
void reloadCalibration();
@@ -290,6 +292,9 @@
// Pointer controller singleton, created and destroyed as needed.
wp<PointerController> pointerController;
+
+ // Input devices to be disabled
+ SortedVector<int32_t> disabledInputDevices;
} mLocked;
std::atomic<bool> mInteractive;
@@ -475,6 +480,8 @@
outConfig->setDisplayInfo(false /*external*/, mLocked.internalViewport);
outConfig->setDisplayInfo(true /*external*/, mLocked.externalViewport);
+
+ outConfig->disabledDevices = mLocked.disabledInputDevices;
} // release lock
}
@@ -764,6 +771,24 @@
InputReaderConfiguration::CHANGE_POINTER_SPEED);
}
+void NativeInputManager::setInputDeviceEnabled(uint32_t deviceId, bool enabled) {
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ ssize_t index = mLocked.disabledInputDevices.indexOf(deviceId);
+ bool currentlyEnabled = index < 0;
+ if (!enabled && currentlyEnabled) {
+ mLocked.disabledInputDevices.add(deviceId);
+ }
+ if (enabled && !currentlyEnabled) {
+ mLocked.disabledInputDevices.remove(deviceId);
+ }
+ } // release lock
+
+ mInputManager->getReader()->requestRefreshConfiguration(
+ InputReaderConfiguration::CHANGE_ENABLED_STATE);
+}
+
void NativeInputManager::setShowTouches(bool enabled) {
{ // acquire lock
AutoMutex _l(mLock);
@@ -1335,6 +1360,7 @@
static void nativeToggleCapsLock(JNIEnv* env, jclass /* clazz */,
jlong ptr, jint deviceId) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
im->getInputManager()->getReader()->toggleCapsLockState(deviceId);
}
@@ -1355,6 +1381,7 @@
static void nativeSetPointerCapture(JNIEnv* env, jclass /* clazz */, jlong ptr,
jboolean enabled) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
im->setPointerCapture(enabled);
}
@@ -1416,6 +1443,7 @@
static void nativeReloadCalibration(JNIEnv* env, jclass clazz, jlong ptr) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
im->reloadCalibration();
}
@@ -1482,13 +1510,36 @@
im->getInputManager()->getDispatcher()->monitor();
}
+static jboolean nativeIsInputDeviceEnabled(JNIEnv* env /* env */,
+ jclass /* clazz */, jlong ptr, jint deviceId) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+ return im->getInputManager()->getReader()->isInputDeviceEnabled(deviceId);
+}
+
+static void nativeEnableInputDevice(JNIEnv* /* env */,
+ jclass /* clazz */, jlong ptr, jint deviceId) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+ im->setInputDeviceEnabled(deviceId, true);
+}
+
+static void nativeDisableInputDevice(JNIEnv* /* env */,
+ jclass /* clazz */, jlong ptr, jint deviceId) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+ im->setInputDeviceEnabled(deviceId, false);
+}
+
static void nativeSetPointerIconType(JNIEnv* /* env */, jclass /* clazz */, jlong ptr, jint iconId) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
im->setPointerIconType(iconId);
}
static void nativeReloadPointerIcons(JNIEnv* /* env */, jclass /* clazz */, jlong ptr) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
im->reloadPointerIcons();
}
@@ -1576,6 +1627,12 @@
(void*) nativeDump },
{ "nativeMonitor", "(J)V",
(void*) nativeMonitor },
+ { "nativeIsInputDeviceEnabled", "(JI)Z",
+ (void*) nativeIsInputDeviceEnabled },
+ { "nativeEnableInputDevice", "(JI)V",
+ (void*) nativeEnableInputDevice },
+ { "nativeDisableInputDevice", "(JI)V",
+ (void*) nativeDisableInputDevice },
{ "nativeSetPointerIconType", "(JI)V",
(void*) nativeSetPointerIconType },
{ "nativeReloadPointerIcons", "(J)V",