Expose input region monitoring to system components.

This is to allow SysUI to detect system-wide gestures.

Test: manual
Bug: 123748692
Change-Id: Ic1e2cd6afea219d0e20b29e7150c9a42b75c7a67
This commit is contained in:
Michael Wright
2019-02-14 12:33:46 +00:00
parent 0a0a49bec4
commit c7995239ef
10 changed files with 323 additions and 22 deletions

View File

@@ -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",

View File

@@ -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);
}

View File

@@ -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();

View File

@@ -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();
}

View File

@@ -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<InputChannel> CREATOR
= new Parcelable.Creator<InputChannel>() {
@@ -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) {

View File

@@ -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;

View File

@@ -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<InputMonitor> CREATOR =
new Parcelable.Creator<InputMonitor>() {
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 + "}";
}
}

View File

@@ -4532,6 +4532,10 @@
<permission android:name="android.permission.ACCESS_SHARED_LIBRARIES"
android:protectionLevel="signature|installer" />
<!-- Allows input events to be monitored. Very dangerous! @hide -->
<permission android:name="android.permission.MONITOR_INPUT"
android:protectionLevel="signature" />
<application android:process="system"
android:persistent="true"
android:hasCode="false"

View File

@@ -69,11 +69,13 @@ import android.util.SparseArray;
import android.view.Display;
import android.view.IInputFilter;
import android.view.IInputFilterHost;
import android.view.IInputMonitorHost;
import android.view.IWindow;
import android.view.InputApplicationHandle;
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputEvent;
import android.view.InputMonitor;
import android.view.InputWindowHandle;
import android.view.KeyEvent;
import android.view.PointerIcon;
@@ -202,7 +204,10 @@ public class InputManagerService extends IInputManager.Stub
int deviceId, int sourceMask, int[] keyCodes, boolean[] keyExists);
private static native void nativeRegisterInputChannel(long ptr, InputChannel inputChannel,
int displayId);
private static native void nativeRegisterInputMonitor(long ptr, InputChannel inputChannel,
int displayId, boolean isGestureMonitor);
private static native void nativeUnregisterInputChannel(long ptr, InputChannel inputChannel);
private static native void nativePilferPointers(long ptr, IBinder token);
private static native void nativeSetInputFilterEnabled(long ptr, boolean enable);
private static native int nativeInjectInputEvent(long ptr, InputEvent event,
int injectorPid, int injectorUid, int syncMode, int timeoutMillis,
@@ -489,11 +494,42 @@ public class InputManagerService extends IInputManager.Stub
}
InputChannel[] inputChannels = InputChannel.openInputChannelPair(inputChannelName);
nativeRegisterInputChannel(mPtr, inputChannels[0], displayId);
// Give the output channel a token just for identity purposes.
inputChannels[0].setToken(new Binder());
nativeRegisterInputMonitor(mPtr, inputChannels[0], displayId, false /*isGestureMonitor*/);
inputChannels[0].dispose(); // don't need to retain the Java object reference
return inputChannels[1];
}
/**
* Creates an input monitor that will receive pointer events for the purposes of system-wide
* gesture interpretation.
*
* @param inputChannelName The input channel name.
* @param displayId Target display id.
* @return The input channel.
*/
@Override // Binder call
public InputMonitor monitorGestureInput(String inputChannelName, int displayId) {
if (!checkCallingPermission(android.Manifest.permission.MONITOR_INPUT,
"monitorInputRegion()")) {
throw new SecurityException("Requires MONITOR_INPUT permission");
}
Objects.requireNonNull(inputChannelName, "inputChannelName must not be null.");
if (displayId < Display.DEFAULT_DISPLAY) {
throw new IllegalArgumentException("displayId must >= 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;

View File

@@ -47,6 +47,7 @@
#include <input/PointerController.h>
#include <input/SpriteController.h>
#include <ui/Region.h>
#include <inputflinger/InputManager.h>
@@ -136,8 +137,6 @@ static struct {
jmethodID getAffineTransform;
} gTouchCalibrationClassInfo;
// --- Global functions ---
template<typename T>
@@ -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>& inputChannel, int32_t displayId);
status_t registerInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel,
int32_t displayId);
status_t registerInputMonitor(JNIEnv* env, const sp<InputChannel>& inputChannel,
int32_t displayId, bool isGestureMonitor);
status_t unregisterInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel);
status_t pilferPointers(const sp<IBinder>& 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>& inputChannel, int32_t displayId, bool isGestureMonitor) {
ATRACE_CALL();
return mInputManager->getDispatcher()->registerInputMonitor(
inputChannel, displayId, isGestureMonitor);
}
status_t NativeInputManager::unregisterInputChannel(JNIEnv* /* env */,
const sp<InputChannel>& inputChannel) {
ATRACE_CALL();
return mInputManager->getDispatcher()->unregisterInputChannel(inputChannel);
}
status_t NativeInputManager::pilferPointers(const sp<IBinder>& 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<NativeInputManager*>(ptr);
sp<InputChannel> 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<NativeInputManager*>(ptr);
sp<IBinder> token = ibinderForJavaObject(env, tokenObj);
im->pilferPointers(token);
}
static void nativeSetInputFilterEnabled(JNIEnv* /* env */, jclass /* clazz */,
jlong ptr, jboolean enabled) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(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");