Loading core/java/android/hardware/input/IInputManager.aidl +7 −0 Original line number Diff line number Diff line Loading @@ -90,4 +90,11 @@ interface IInputManager { /** Create an input monitor for gestures. */ InputMonitor monitorGestureInput(String name, int displayId); // Add a runtime association between the input port and the display port. This overrides any // static associations. void addPortAssociation(in String inputPort, int displayPort); // Remove the runtime association between the input port and the display port. Any existing // static association for the cleared input port will be restored. void removePortAssociation(in String inputPort); } core/java/android/hardware/input/InputManager.java +36 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package android.hardware.input; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemService; Loading Loading @@ -963,6 +964,41 @@ public final class InputManager { } } /** * Add a runtime association between the input port and the display port. This overrides any * static associations. * @param inputPort The port of the input device. * @param displayPort The physical port of the associated display. * <p> * Requires {@link android.Manifest.permissions.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY_BY_PORT}. * </p> * @hide */ public void addPortAssociation(@NonNull String inputPort, int displayPort) { try { mIm.addPortAssociation(inputPort, displayPort); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } /** * Remove the runtime association between the input port and the display port. Any existing * static association for the cleared input port will be restored. * @param inputPort The port of the input device to be cleared. * <p> * Requires {@link android.Manifest.permissions.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY_BY_PORT}. * </p> * @hide */ public void removePortAssociation(@NonNull String inputPort) { try { mIm.removePortAssociation(inputPort); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } private void populateInputDevicesLocked() { if (mInputDevicesChangedListener == null) { final InputDevicesChangedListener listener = new InputDevicesChangedListener(); Loading core/res/AndroidManifest.xml +4 −0 Original line number Diff line number Diff line Loading @@ -4756,6 +4756,10 @@ <!-- Allows input events to be monitored. Very dangerous! @hide --> <permission android:name="android.permission.MONITOR_INPUT" android:protectionLevel="signature" /> <!-- Allows the caller to change the associations between input devices and displays. Very dangerous! @hide --> <permission android:name="android.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY_BY_PORT" android:protectionLevel="signature" /> <!-- Allows query of any normal app on the device, regardless of manifest declarations. --> <permission android:name="android.permission.QUERY_ALL_PACKAGES" Loading services/core/java/com/android/server/input/InputManagerService.java +71 −6 Original line number Diff line number Diff line Loading @@ -83,11 +83,11 @@ import android.view.ViewConfiguration; import android.widget.Toast; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.os.SomeArgs; import com.android.internal.util.DumpUtils; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.server.DisplayThread; import com.android.server.LocalServices; Loading Loading @@ -186,6 +186,9 @@ public class InputManagerService extends IInputManager.Stub // The associations of input devices to displays by port. Maps from input device port (String) // to display id (int). Currently only accessed by InputReader. private final Map<String, Integer> mStaticAssociations; private final Object mAssociationsLock = new Object(); @GuardedBy("mAssociationLock") private final Map<String, Integer> mRuntimeAssociations = new HashMap<String, Integer>(); private static native long nativeInit(InputManagerService service, Context context, MessageQueue messageQueue); Loading Loading @@ -240,6 +243,7 @@ public class InputManagerService extends IInputManager.Stub private static native void nativeSetCustomPointerIcon(long ptr, PointerIcon icon); private static native void nativeSetPointerCapture(long ptr, boolean detached); private static native boolean nativeCanDispatchToDisplay(long ptr, int deviceId, int displayId); private static native void nativeNotifyPortAssociationsChanged(long ptr); // Input event injection constants defined in InputDispatcher.h. private static final int INPUT_EVENT_INJECTION_SUCCEEDED = 0; Loading Loading @@ -1723,6 +1727,49 @@ public class InputManagerService extends IInputManager.Stub nativeSetCustomPointerIcon(mPtr, icon); } /** * Add a runtime association between the input port and the display port. This overrides any * static associations. * @param inputPort The port of the input device. * @param displayPort The physical port of the associated display. */ @Override // Binder call public void addPortAssociation(@NonNull String inputPort, int displayPort) { if (!checkCallingPermission( android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY_BY_PORT, "addPortAssociation()")) { throw new SecurityException( "Requires ASSOCIATE_INPUT_DEVICE_TO_DISPLAY_BY_PORT permission"); } Objects.requireNonNull(inputPort); synchronized (mAssociationsLock) { mRuntimeAssociations.put(inputPort, displayPort); } nativeNotifyPortAssociationsChanged(mPtr); } /** * Remove the runtime association between the input port and the display port. Any existing * static association for the cleared input port will be restored. * @param inputPort The port of the input device to be cleared. */ @Override // Binder call public void removePortAssociation(@NonNull String inputPort) { if (!checkCallingPermission( android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY_BY_PORT, "clearPortAssociations()")) { throw new SecurityException( "Requires ASSOCIATE_INPUT_DEVICE_TO_DISPLAY_BY_PORT permission"); } Objects.requireNonNull(inputPort); synchronized (mAssociationsLock) { mRuntimeAssociations.remove(inputPort); } nativeNotifyPortAssociationsChanged(mPtr); } @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; Loading @@ -1743,6 +1790,16 @@ public class InputManagerService extends IInputManager.Stub pw.println(" display: " + v); }); } synchronized (mAssociationsLock) { if (!mRuntimeAssociations.isEmpty()) { pw.println("Runtime Associations:"); mRuntimeAssociations.forEach((k, v) -> { pw.print(" port: " + k); pw.println(" display: " + v); }); } } } private boolean checkCallingPermission(String permission, String func) { Loading @@ -1766,6 +1823,7 @@ public class InputManagerService extends IInputManager.Stub @Override public void monitor() { synchronized (mInputFilterLock) { } synchronized (mAssociationsLock) { /* Test if blocked by associations lock. */} nativeMonitor(mPtr); } Loading Loading @@ -1930,7 +1988,7 @@ public class InputManagerService extends IInputManager.Stub * @return Flattened list */ private static List<String> flatten(@NonNull Map<String, Integer> map) { List<String> list = new ArrayList<>(map.size() * 2); final List<String> list = new ArrayList<>(map.size() * 2); map.forEach((k, v)-> { list.add(k); list.add(v.toString()); Loading @@ -1943,11 +2001,11 @@ public class InputManagerService extends IInputManager.Stub * directory. */ private static Map<String, Integer> loadStaticInputPortAssociations() { File baseDir = Environment.getVendorDirectory(); File confFile = new File(baseDir, PORT_ASSOCIATIONS_PATH); final File baseDir = Environment.getVendorDirectory(); final File confFile = new File(baseDir, PORT_ASSOCIATIONS_PATH); try { InputStream stream = new FileInputStream(confFile); final InputStream stream = new FileInputStream(confFile); return ConfigurationProcessor.processInputPortAssociations(stream); } catch (FileNotFoundException e) { // Most of the time, file will not exist, which is expected. Loading @@ -1960,7 +2018,14 @@ public class InputManagerService extends IInputManager.Stub // Native callback private String[] getInputPortAssociations() { List<String> associationList = flatten(mStaticAssociations); final Map<String, Integer> associations = new HashMap<>(mStaticAssociations); // merge the runtime associations. synchronized (mAssociationsLock) { associations.putAll(mRuntimeAssociations); } final List<String> associationList = flatten(associations); return associationList.toArray(new String[0]); } Loading services/core/jni/com_android_server_input_InputManagerService.cpp +8 −0 Original line number Diff line number Diff line Loading @@ -1757,6 +1757,12 @@ static jboolean nativeCanDispatchToDisplay(JNIEnv* env, jclass /* clazz */, jlon return im->getInputManager()->getReader()->canDispatchToDisplay(deviceId, displayId); } static void nativeNotifyPortAssociationsChanged(JNIEnv* env, jclass /* clazz */, jlong ptr) { NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); im->getInputManager()->getReader()->requestRefreshConfiguration( InputReaderConfiguration::CHANGE_DISPLAY_INFO); } // ---------------------------------------------------------------------------- static const JNINativeMethod gInputManagerMethods[] = { Loading Loading @@ -1842,6 +1848,8 @@ static const JNINativeMethod gInputManagerMethods[] = { (void*) nativeSetCustomPointerIcon }, { "nativeCanDispatchToDisplay", "(JII)Z", (void*) nativeCanDispatchToDisplay }, { "nativeNotifyPortAssociationsChanged", "(J)V", (void*) nativeNotifyPortAssociationsChanged }, }; #define FIND_CLASS(var, className) \ Loading Loading
core/java/android/hardware/input/IInputManager.aidl +7 −0 Original line number Diff line number Diff line Loading @@ -90,4 +90,11 @@ interface IInputManager { /** Create an input monitor for gestures. */ InputMonitor monitorGestureInput(String name, int displayId); // Add a runtime association between the input port and the display port. This overrides any // static associations. void addPortAssociation(in String inputPort, int displayPort); // Remove the runtime association between the input port and the display port. Any existing // static association for the cleared input port will be restored. void removePortAssociation(in String inputPort); }
core/java/android/hardware/input/InputManager.java +36 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package android.hardware.input; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemService; Loading Loading @@ -963,6 +964,41 @@ public final class InputManager { } } /** * Add a runtime association between the input port and the display port. This overrides any * static associations. * @param inputPort The port of the input device. * @param displayPort The physical port of the associated display. * <p> * Requires {@link android.Manifest.permissions.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY_BY_PORT}. * </p> * @hide */ public void addPortAssociation(@NonNull String inputPort, int displayPort) { try { mIm.addPortAssociation(inputPort, displayPort); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } /** * Remove the runtime association between the input port and the display port. Any existing * static association for the cleared input port will be restored. * @param inputPort The port of the input device to be cleared. * <p> * Requires {@link android.Manifest.permissions.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY_BY_PORT}. * </p> * @hide */ public void removePortAssociation(@NonNull String inputPort) { try { mIm.removePortAssociation(inputPort); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } private void populateInputDevicesLocked() { if (mInputDevicesChangedListener == null) { final InputDevicesChangedListener listener = new InputDevicesChangedListener(); Loading
core/res/AndroidManifest.xml +4 −0 Original line number Diff line number Diff line Loading @@ -4756,6 +4756,10 @@ <!-- Allows input events to be monitored. Very dangerous! @hide --> <permission android:name="android.permission.MONITOR_INPUT" android:protectionLevel="signature" /> <!-- Allows the caller to change the associations between input devices and displays. Very dangerous! @hide --> <permission android:name="android.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY_BY_PORT" android:protectionLevel="signature" /> <!-- Allows query of any normal app on the device, regardless of manifest declarations. --> <permission android:name="android.permission.QUERY_ALL_PACKAGES" Loading
services/core/java/com/android/server/input/InputManagerService.java +71 −6 Original line number Diff line number Diff line Loading @@ -83,11 +83,11 @@ import android.view.ViewConfiguration; import android.widget.Toast; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.os.SomeArgs; import com.android.internal.util.DumpUtils; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.server.DisplayThread; import com.android.server.LocalServices; Loading Loading @@ -186,6 +186,9 @@ public class InputManagerService extends IInputManager.Stub // The associations of input devices to displays by port. Maps from input device port (String) // to display id (int). Currently only accessed by InputReader. private final Map<String, Integer> mStaticAssociations; private final Object mAssociationsLock = new Object(); @GuardedBy("mAssociationLock") private final Map<String, Integer> mRuntimeAssociations = new HashMap<String, Integer>(); private static native long nativeInit(InputManagerService service, Context context, MessageQueue messageQueue); Loading Loading @@ -240,6 +243,7 @@ public class InputManagerService extends IInputManager.Stub private static native void nativeSetCustomPointerIcon(long ptr, PointerIcon icon); private static native void nativeSetPointerCapture(long ptr, boolean detached); private static native boolean nativeCanDispatchToDisplay(long ptr, int deviceId, int displayId); private static native void nativeNotifyPortAssociationsChanged(long ptr); // Input event injection constants defined in InputDispatcher.h. private static final int INPUT_EVENT_INJECTION_SUCCEEDED = 0; Loading Loading @@ -1723,6 +1727,49 @@ public class InputManagerService extends IInputManager.Stub nativeSetCustomPointerIcon(mPtr, icon); } /** * Add a runtime association between the input port and the display port. This overrides any * static associations. * @param inputPort The port of the input device. * @param displayPort The physical port of the associated display. */ @Override // Binder call public void addPortAssociation(@NonNull String inputPort, int displayPort) { if (!checkCallingPermission( android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY_BY_PORT, "addPortAssociation()")) { throw new SecurityException( "Requires ASSOCIATE_INPUT_DEVICE_TO_DISPLAY_BY_PORT permission"); } Objects.requireNonNull(inputPort); synchronized (mAssociationsLock) { mRuntimeAssociations.put(inputPort, displayPort); } nativeNotifyPortAssociationsChanged(mPtr); } /** * Remove the runtime association between the input port and the display port. Any existing * static association for the cleared input port will be restored. * @param inputPort The port of the input device to be cleared. */ @Override // Binder call public void removePortAssociation(@NonNull String inputPort) { if (!checkCallingPermission( android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY_BY_PORT, "clearPortAssociations()")) { throw new SecurityException( "Requires ASSOCIATE_INPUT_DEVICE_TO_DISPLAY_BY_PORT permission"); } Objects.requireNonNull(inputPort); synchronized (mAssociationsLock) { mRuntimeAssociations.remove(inputPort); } nativeNotifyPortAssociationsChanged(mPtr); } @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; Loading @@ -1743,6 +1790,16 @@ public class InputManagerService extends IInputManager.Stub pw.println(" display: " + v); }); } synchronized (mAssociationsLock) { if (!mRuntimeAssociations.isEmpty()) { pw.println("Runtime Associations:"); mRuntimeAssociations.forEach((k, v) -> { pw.print(" port: " + k); pw.println(" display: " + v); }); } } } private boolean checkCallingPermission(String permission, String func) { Loading @@ -1766,6 +1823,7 @@ public class InputManagerService extends IInputManager.Stub @Override public void monitor() { synchronized (mInputFilterLock) { } synchronized (mAssociationsLock) { /* Test if blocked by associations lock. */} nativeMonitor(mPtr); } Loading Loading @@ -1930,7 +1988,7 @@ public class InputManagerService extends IInputManager.Stub * @return Flattened list */ private static List<String> flatten(@NonNull Map<String, Integer> map) { List<String> list = new ArrayList<>(map.size() * 2); final List<String> list = new ArrayList<>(map.size() * 2); map.forEach((k, v)-> { list.add(k); list.add(v.toString()); Loading @@ -1943,11 +2001,11 @@ public class InputManagerService extends IInputManager.Stub * directory. */ private static Map<String, Integer> loadStaticInputPortAssociations() { File baseDir = Environment.getVendorDirectory(); File confFile = new File(baseDir, PORT_ASSOCIATIONS_PATH); final File baseDir = Environment.getVendorDirectory(); final File confFile = new File(baseDir, PORT_ASSOCIATIONS_PATH); try { InputStream stream = new FileInputStream(confFile); final InputStream stream = new FileInputStream(confFile); return ConfigurationProcessor.processInputPortAssociations(stream); } catch (FileNotFoundException e) { // Most of the time, file will not exist, which is expected. Loading @@ -1960,7 +2018,14 @@ public class InputManagerService extends IInputManager.Stub // Native callback private String[] getInputPortAssociations() { List<String> associationList = flatten(mStaticAssociations); final Map<String, Integer> associations = new HashMap<>(mStaticAssociations); // merge the runtime associations. synchronized (mAssociationsLock) { associations.putAll(mRuntimeAssociations); } final List<String> associationList = flatten(associations); return associationList.toArray(new String[0]); } Loading
services/core/jni/com_android_server_input_InputManagerService.cpp +8 −0 Original line number Diff line number Diff line Loading @@ -1757,6 +1757,12 @@ static jboolean nativeCanDispatchToDisplay(JNIEnv* env, jclass /* clazz */, jlon return im->getInputManager()->getReader()->canDispatchToDisplay(deviceId, displayId); } static void nativeNotifyPortAssociationsChanged(JNIEnv* env, jclass /* clazz */, jlong ptr) { NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); im->getInputManager()->getReader()->requestRefreshConfiguration( InputReaderConfiguration::CHANGE_DISPLAY_INFO); } // ---------------------------------------------------------------------------- static const JNINativeMethod gInputManagerMethods[] = { Loading Loading @@ -1842,6 +1848,8 @@ static const JNINativeMethod gInputManagerMethods[] = { (void*) nativeSetCustomPointerIcon }, { "nativeCanDispatchToDisplay", "(JII)Z", (void*) nativeCanDispatchToDisplay }, { "nativeNotifyPortAssociationsChanged", "(J)V", (void*) nativeNotifyPortAssociationsChanged }, }; #define FIND_CLASS(var, className) \ Loading