diff --git a/Android.bp b/Android.bp index b9b1bd87e80d3..5b8e6e1b8d322 100644 --- a/Android.bp +++ b/Android.bp @@ -375,6 +375,7 @@ java_defaults { "core/java/android/view/IDisplayFoldListener.aidl", "core/java/android/view/IGraphicsStats.aidl", "core/java/android/view/IGraphicsStatsCallback.aidl", + "core/java/android/view/IInputMonitorHost.aidl", "core/java/android/view/IInputFilter.aidl", "core/java/android/view/IInputFilterHost.aidl", "core/java/android/view/IOnKeyguardExitResult.aidl", diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl index 2923bbf14c309..0daf30f25d599 100644 --- a/core/java/android/hardware/input/IInputManager.aidl +++ b/core/java/android/hardware/input/IInputManager.aidl @@ -16,6 +16,7 @@ package android.hardware.input; +import android.graphics.Rect; import android.hardware.input.InputDeviceIdentifier; import android.hardware.input.KeyboardLayout; import android.hardware.input.IInputDevicesChangedListener; @@ -24,6 +25,7 @@ import android.hardware.input.TouchCalibration; import android.os.IBinder; import android.view.InputDevice; import android.view.InputEvent; +import android.view.InputMonitor; import android.view.PointerIcon; /** @hide */ @@ -82,4 +84,7 @@ interface IInputManager { void setCustomPointerIcon(in PointerIcon icon); void requestPointerCapture(IBinder windowToken, boolean enabled); + + /** Create an input monitor for gestures. */ + InputMonitor monitorGestureInput(String name, int displayId); } diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java index fec5c34fab28b..2a59be28a9372 100644 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -41,6 +41,7 @@ import android.util.Log; import android.util.SparseArray; import android.view.InputDevice; import android.view.InputEvent; +import android.view.InputMonitor; import android.view.MotionEvent; import android.view.PointerIcon; @@ -933,6 +934,19 @@ public final class InputManager { } } + /** + * Monitor input on the specified display for gestures. + * + * @hide + */ + public InputMonitor monitorGestureInput(String name, int displayId) { + try { + return mIm.monitorGestureInput(name, displayId); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + private void populateInputDevicesLocked() { if (mInputDevicesChangedListener == null) { final InputDevicesChangedListener listener = new InputDevicesChangedListener(); diff --git a/core/java/android/view/IInputMonitorHost.aidl b/core/java/android/view/IInputMonitorHost.aidl new file mode 100644 index 0000000000000..bde737d9a65b7 --- /dev/null +++ b/core/java/android/view/IInputMonitorHost.aidl @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2019, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +/** + * @hide + */ +oneway interface IInputMonitorHost { + void pilferPointers(); + void dispose(); +} diff --git a/core/java/android/view/InputChannel.java b/core/java/android/view/InputChannel.java index af2b992ccba2d..ecb727c790160 100644 --- a/core/java/android/view/InputChannel.java +++ b/core/java/android/view/InputChannel.java @@ -17,8 +17,8 @@ package android.view; import android.annotation.UnsupportedAppUsage; -import android.os.Parcel; import android.os.IBinder; +import android.os.Parcel; import android.os.Parcelable; import android.util.Slog; @@ -31,9 +31,9 @@ import android.util.Slog; */ public final class InputChannel implements Parcelable { private static final String TAG = "InputChannel"; - + private static final boolean DEBUG = false; - + @UnsupportedAppUsage public static final @android.annotation.NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { @@ -42,12 +42,12 @@ public final class InputChannel implements Parcelable { result.readFromParcel(source); return result; } - + public InputChannel[] newArray(int size) { return new InputChannel[size]; } }; - + @SuppressWarnings("unused") @UnsupportedAppUsage private long mPtr; // used by native code @@ -81,7 +81,7 @@ public final class InputChannel implements Parcelable { super.finalize(); } } - + /** * Creates a new input channel pair. One channel should be provided to the input * dispatcher and the other to the application's input queue. @@ -100,7 +100,7 @@ public final class InputChannel implements Parcelable { } return nativeOpenInputChannelPair(name); } - + /** * Gets the name of the input channel. * @return The input channel name. @@ -118,7 +118,7 @@ public final class InputChannel implements Parcelable { public void dispose() { nativeDispose(false); } - + /** * Transfers ownership of the internal state of the input channel to another * instance and invalidates this instance. This is used to pass an input channel @@ -129,7 +129,7 @@ public final class InputChannel implements Parcelable { if (outParameter == null) { throw new IllegalArgumentException("outParameter must not be null"); } - + nativeTransferTo(outParameter); } @@ -151,7 +151,7 @@ public final class InputChannel implements Parcelable { if (in == null) { throw new IllegalArgumentException("in must not be null"); } - + nativeReadFromParcel(in); } @@ -160,7 +160,7 @@ public final class InputChannel implements Parcelable { if (out == null) { throw new IllegalArgumentException("out must not be null"); } - + nativeWriteToParcel(out); if ((flags & PARCELABLE_WRITE_RETURN_VALUE) != 0) { diff --git a/core/java/android/view/InputMonitor.aidl b/core/java/android/view/InputMonitor.aidl new file mode 100644 index 0000000000000..bdd14fef392a4 --- /dev/null +++ b/core/java/android/view/InputMonitor.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +parcelable InputMonitor; diff --git a/core/java/android/view/InputMonitor.java b/core/java/android/view/InputMonitor.java new file mode 100644 index 0000000000000..693f2873522c1 --- /dev/null +++ b/core/java/android/view/InputMonitor.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.RemoteException; + +/** + * @hide + */ +public final class InputMonitor implements Parcelable { + private static final String TAG = "InputMonitor"; + + private static final boolean DEBUG = false; + + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + + public InputMonitor createFromParcel(Parcel source) { + return new InputMonitor(source); + } + + public InputMonitor[] newArray(int size) { + return new InputMonitor[size]; + } + }; + + @NonNull + private final String mName; + @NonNull + private final InputChannel mChannel; + @NonNull + private final IInputMonitorHost mHost; + + public InputMonitor(@NonNull String name, @NonNull InputChannel channel, + @NonNull IInputMonitorHost host) { + mName = name; + mChannel = channel; + mHost = host; + } + + public InputMonitor(Parcel in) { + mName = in.readString(); + mChannel = in.readParcelable(null); + mHost = IInputMonitorHost.Stub.asInterface(in.readStrongBinder()); + } + + /** + * Get the {@link InputChannel} corresponding to this InputMonitor + */ + public InputChannel getInputChannel() { + return mChannel; + } + + /** + * Get the name of this channel. + */ + public String getName() { + return mName; + } + + + /** + * Takes all of the current pointer events streams that are currently being sent to this + * monitor and generates appropriate cancellations for the windows that would normally get + * them. + * + * This method should be used with caution as unexpected pilfering can break fundamental user + * interactions. + */ + public void pilferPointers() { + try { + mHost.pilferPointers(); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + /** + * Disposes the input monitor. + * + * Explicitly release all of the resources this monitor is holding on to (e.g. the + * InputChannel). Once this method is called, this monitor and any resources it's provided may + * no longer be used. + */ + public void dispose() { + mChannel.dispose(); + try { + mHost.dispose(); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeString(mName); + out.writeParcelable(mChannel, flags); + out.writeStrongBinder(mHost.asBinder()); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + return "InputMonitor{mName=" + mName + ", mChannel=" + mChannel + ", mHost=" + mHost + "}"; + } +} diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index ba7a93f529390..2558bff55d8ee 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -4532,6 +4532,10 @@ + + + = 0."); + } + + + InputChannel[] inputChannels = InputChannel.openInputChannelPair(inputChannelName); + InputMonitorHost host = new InputMonitorHost(inputChannels[0]); + inputChannels[0].setToken(host.asBinder()); + nativeRegisterInputMonitor(mPtr, inputChannels[0], displayId, true /*isGestureMonitor*/); + return new InputMonitor(inputChannelName, inputChannels[1], host); + } + /** * Registers an input channel so that it can be used as an input event target. * @param inputChannel The input channel to register. @@ -2092,6 +2128,28 @@ public class InputManagerService extends IInputManager.Stub } } + /** + * Interface for the system to handle request from InputMonitors. + */ + private final class InputMonitorHost extends IInputMonitorHost.Stub { + private final InputChannel mInputChannel; + + InputMonitorHost(InputChannel channel) { + mInputChannel = channel; + } + + @Override + public void pilferPointers() { + nativePilferPointers(mPtr, asBinder()); + } + + @Override + public void dispose() { + nativeUnregisterInputChannel(mPtr, mInputChannel); + mInputChannel.dispose(); + } + } + private static final class KeyboardLayoutDescriptor { public String packageName; public String receiverName; diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 3d84bd40fb5a8..4e5b7c9a3f013 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -47,6 +47,7 @@ #include #include +#include #include @@ -136,8 +137,6 @@ static struct { jmethodID getAffineTransform; } gTouchCalibrationClassInfo; - - // --- Global functions --- template @@ -187,7 +186,6 @@ static std::string getStringElementFromJavaArray(JNIEnv* env, jobjectArray array return result; } - // --- NativeInputManager --- class NativeInputManager : public virtual RefBase, @@ -206,8 +204,12 @@ public: void setDisplayViewports(JNIEnv* env, jobjectArray viewportObjArray); - status_t registerInputChannel(JNIEnv* env, const sp& inputChannel, int32_t displayId); + status_t registerInputChannel(JNIEnv* env, const sp& inputChannel, + int32_t displayId); + status_t registerInputMonitor(JNIEnv* env, const sp& inputChannel, + int32_t displayId, bool isGestureMonitor); status_t unregisterInputChannel(JNIEnv* env, const sp& inputChannel); + status_t pilferPointers(const sp& token); void setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray, int32_t displayId); void setFocusedApplication(JNIEnv* env, int32_t displayId, jobject applicationHandleObj); @@ -441,12 +443,24 @@ status_t NativeInputManager::registerInputChannel(JNIEnv* /* env */, inputChannel, displayId); } +status_t NativeInputManager::registerInputMonitor(JNIEnv* /* env */, + const sp& inputChannel, int32_t displayId, bool isGestureMonitor) { + ATRACE_CALL(); + return mInputManager->getDispatcher()->registerInputMonitor( + inputChannel, displayId, isGestureMonitor); +} + status_t NativeInputManager::unregisterInputChannel(JNIEnv* /* env */, const sp& inputChannel) { ATRACE_CALL(); return mInputManager->getDispatcher()->unregisterInputChannel(inputChannel); } +status_t NativeInputManager::pilferPointers(const sp& token) { + ATRACE_CALL(); + return mInputManager->getDispatcher()->pilferPointers(token); +} + void NativeInputManager::getReaderConfiguration(InputReaderConfiguration* outConfig) { ATRACE_CALL(); JNIEnv* env = jniEnv(); @@ -1385,7 +1399,6 @@ static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */, throwInputChannelNotInitialized(env); return; } - bool monitor = inputChannel->getToken() == nullptr && displayId != ADISPLAY_ID_NONE; status_t status = im->registerInputChannel(env, inputChannel, displayId); @@ -1396,10 +1409,33 @@ static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */, return; } - // If inputWindowHandle is null and displayId >= 0, treat inputChannel as monitor. - if (!monitor) { - android_view_InputChannel_setDisposeCallback(env, inputChannelObj, - handleInputChannelDisposed, im); + android_view_InputChannel_setDisposeCallback(env, inputChannelObj, + handleInputChannelDisposed, im); +} + +static void nativeRegisterInputMonitor(JNIEnv* env, jclass /* clazz */, + jlong ptr, jobject inputChannelObj, jint displayId, jboolean isGestureMonitor) { + NativeInputManager* im = reinterpret_cast(ptr); + + sp inputChannel = android_view_InputChannel_getInputChannel(env, + inputChannelObj); + if (inputChannel == nullptr) { + throwInputChannelNotInitialized(env); + return; + } + + if (displayId == ADISPLAY_ID_NONE) { + std::string message = "InputChannel used as a monitor must be associated with a display"; + jniThrowRuntimeException(env, message.c_str()); + return; + } + + status_t status = im->registerInputMonitor(env, inputChannel, displayId, isGestureMonitor); + + if (status) { + std::string message = StringPrintf("Failed to register input channel. status=%d", status); + jniThrowRuntimeException(env, message.c_str()); + return; } } @@ -1424,6 +1460,13 @@ static void nativeUnregisterInputChannel(JNIEnv* env, jclass /* clazz */, } } +static void nativePilferPointers(JNIEnv* env, jclass /* clazz */, jlong ptr, jobject tokenObj) { + NativeInputManager* im = reinterpret_cast(ptr); + sp token = ibinderForJavaObject(env, tokenObj); + im->pilferPointers(token); +} + + static void nativeSetInputFilterEnabled(JNIEnv* /* env */, jclass /* clazz */, jlong ptr, jboolean enabled) { NativeInputManager* im = reinterpret_cast(ptr); @@ -1686,8 +1729,13 @@ static const JNINativeMethod gInputManagerMethods[] = { { "nativeRegisterInputChannel", "(JLandroid/view/InputChannel;I)V", (void*) nativeRegisterInputChannel }, + { "nativeRegisterInputMonitor", + "(JLandroid/view/InputChannel;IZ)V", + (void*) nativeRegisterInputMonitor}, { "nativeUnregisterInputChannel", "(JLandroid/view/InputChannel;)V", (void*) nativeUnregisterInputChannel }, + { "nativePilferPointers", "(JLandroid/os/IBinder;)V", + (void*) nativePilferPointers }, { "nativeSetInputFilterEnabled", "(JZ)V", (void*) nativeSetInputFilterEnabled }, { "nativeInjectInputEvent", "(JLandroid/view/InputEvent;IIIII)I", @@ -1781,7 +1829,7 @@ int register_android_server_InputManager(JNIEnv* env) { GET_METHOD_ID(gServiceClassInfo.notifyInputChannelBroken, clazz, "notifyInputChannelBroken", "(Landroid/os/IBinder;)V"); - + GET_METHOD_ID(gServiceClassInfo.notifyFocusChanged, clazz, "notifyFocusChanged", "(Landroid/os/IBinder;Landroid/os/IBinder;)V");