Add setPointerIcon for PointerChoreographer (base)
To set pointer icon for mouse or stylus, we are going to use
setPointerIcon with more parameters which will be useful for
multi-device experience and security.
Test: Manual
Bug: 293587049
Change-Id: I17bb047b0bfbff6cf3a129e4fa742ec489420faf
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 88d7231..6626baf 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -169,6 +169,8 @@
void setPointerIconType(int typeId);
void setCustomPointerIcon(in PointerIcon icon);
+ boolean setPointerIcon(in PointerIcon icon, int displayId, int deviceId, int pointerId,
+ in IBinder inputToken);
oneway void requestPointerCapture(IBinder inputChannelToken, boolean enabled);
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index abbf954..f941ad8 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -1057,6 +1057,12 @@
mGlobal.setCustomPointerIcon(icon);
}
+ /** @hide */
+ public boolean setPointerIcon(PointerIcon icon, int displayId, int deviceId, int pointerId,
+ IBinder inputToken) {
+ return mGlobal.setPointerIcon(icon, displayId, deviceId, pointerId, inputToken);
+ }
+
/**
* Check if showing a {@link android.view.PointerIcon} for styluses is enabled.
*
diff --git a/core/java/android/hardware/input/InputManagerGlobal.java b/core/java/android/hardware/input/InputManagerGlobal.java
index cf1dfe3..24a6911 100644
--- a/core/java/android/hardware/input/InputManagerGlobal.java
+++ b/core/java/android/hardware/input/InputManagerGlobal.java
@@ -1286,6 +1286,18 @@
}
/**
+ * @see InputManager#setPointerIcon(PointerIcon, int, int, int, IBinder)
+ */
+ public boolean setPointerIcon(PointerIcon icon, int displayId, int deviceId, int pointerId,
+ IBinder inputToken) {
+ try {
+ return mIm.setPointerIcon(icon, displayId, deviceId, pointerId, inputToken);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* @see InputManager#requestPointerCapture(IBinder, boolean)
*/
public void requestPointerCapture(IBinder windowToken, boolean enable) {
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index d131dc9..f2c3abc 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -1308,24 +1308,6 @@
}
/**
- * Sets the current pointer type.
- * @param pointerType the type of the pointer icon.
- * @hide
- */
- public void setPointerType(int pointerType) {
- InputManagerGlobal.getInstance().setPointerIconType(pointerType);
- }
-
- /**
- * Specifies the current custom pointer.
- * @param icon the icon data.
- * @hide
- */
- public void setCustomPointerIcon(PointerIcon icon) {
- InputManagerGlobal.getInstance().setCustomPointerIcon(icon);
- }
-
- /**
* Reports whether the device has a battery.
* @return true if the device has a battery, false otherwise.
* @hide
diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java
index fee88d91..7800c28 100644
--- a/core/java/android/view/PointerIcon.java
+++ b/core/java/android/view/PointerIcon.java
@@ -223,6 +223,9 @@
* @throws IllegalArgumentException if context is null.
*/
public static @NonNull PointerIcon getSystemIcon(@NonNull Context context, int type) {
+ // TODO(b/293587049): Pointer Icon Refactor: There is no need to load the system
+ // icon resource into memory outside of system server. Remove the need to load
+ // resources when getting a system icon.
if (context == null) {
throw new IllegalArgumentException("context must not be null");
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index d27f787..8d469ee 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -97,6 +97,8 @@
import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.INSETS_CONTROLLER;
import static android.view.flags.Flags.toolkitSetFrameRateReadOnly;
+import static com.android.input.flags.Flags.enablePointerChoreographer;
+
import android.Manifest;
import android.accessibilityservice.AccessibilityService;
import android.animation.AnimationHandler;
@@ -7485,18 +7487,34 @@
mPointerIconType = pointerType;
mCustomPointerIcon = null;
if (mPointerIconType != PointerIcon.TYPE_CUSTOM) {
- InputManagerGlobal
- .getInstance()
- .setPointerIconType(pointerType);
+ if (enablePointerChoreographer()) {
+ InputManagerGlobal
+ .getInstance()
+ .setPointerIcon(PointerIcon.getSystemIcon(mContext, pointerType),
+ event.getDisplayId(), event.getDeviceId(),
+ event.getPointerId(pointerIndex), getInputToken());
+ } else {
+ InputManagerGlobal
+ .getInstance()
+ .setPointerIconType(pointerType);
+ }
return true;
}
}
if (mPointerIconType == PointerIcon.TYPE_CUSTOM &&
!pointerIcon.equals(mCustomPointerIcon)) {
mCustomPointerIcon = pointerIcon;
- InputManagerGlobal
- .getInstance()
- .setCustomPointerIcon(mCustomPointerIcon);
+ if (enablePointerChoreographer()) {
+ InputManagerGlobal
+ .getInstance()
+ .setPointerIcon(mCustomPointerIcon,
+ event.getDisplayId(), event.getDeviceId(),
+ event.getPointerId(pointerIndex), getInputToken());
+ } else {
+ InputManagerGlobal
+ .getInstance()
+ .setCustomPointerIcon(mCustomPointerIcon);
+ }
}
return true;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
index 53ec201..8511a21 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
@@ -24,6 +24,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
+import static com.android.input.flags.Flags.enablePointerChoreographer;
import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_BOTTOM;
import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_LEFT;
import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT;
@@ -63,6 +64,7 @@
class DragResizeInputListener implements AutoCloseable {
private static final String TAG = "DragResizeInputListener";
private final IWindowSession mWindowSession = WindowManagerGlobal.getWindowSession();
+ private final Context mContext;
private final Handler mHandler;
private final Choreographer mChoreographer;
private final InputManager mInputManager;
@@ -110,6 +112,7 @@
Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier,
DisplayController displayController) {
mInputManager = context.getSystemService(InputManager.class);
+ mContext = context;
mHandler = handler;
mChoreographer = choreographer;
mSurfaceControlTransactionSupplier = surfaceControlTransactionSupplier;
@@ -451,7 +454,9 @@
}
case MotionEvent.ACTION_HOVER_ENTER:
case MotionEvent.ACTION_HOVER_MOVE: {
- updateCursorType(e.getXCursorPosition(), e.getYCursorPosition());
+ updateCursorType(e.getDisplayId(), e.getDeviceId(),
+ e.getPointerId(/*pointerIndex=*/0), e.getXCursorPosition(),
+ e.getYCursorPosition());
result = true;
break;
}
@@ -579,7 +584,8 @@
return 0;
}
- private void updateCursorType(float x, float y) {
+ private void updateCursorType(int displayId, int deviceId, int pointerId, float x,
+ float y) {
@DragPositioningCallback.CtrlType int ctrlType = calculateResizeHandlesCtrlType(x, y);
int cursorType = PointerIcon.TYPE_DEFAULT;
@@ -611,9 +617,14 @@
// where views in the task can receive input events because we can't set touch regions
// of input sinks to have rounded corners.
if (mLastCursorType != cursorType || cursorType != PointerIcon.TYPE_DEFAULT) {
- mInputManager.setPointerIconType(cursorType);
+ if (enablePointerChoreographer()) {
+ mInputManager.setPointerIcon(PointerIcon.getSystemIcon(mContext, cursorType),
+ displayId, deviceId, pointerId, mInputChannel.getToken());
+ } else {
+ mInputManager.setPointerIconType(cursorType);
+ }
mLastCursorType = cursorType;
}
}
}
-}
\ No newline at end of file
+}
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index fa07c39..a8b9633 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -65,9 +65,9 @@
void setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
BitSet32 spotIdBits, int32_t displayId) override;
void clearSpots() override;
+ void updatePointerIcon(PointerIconStyle iconId) override;
+ void setCustomPointerIcon(const SpriteIcon& icon) override;
- void updatePointerIcon(PointerIconStyle iconId);
- void setCustomPointerIcon(const SpriteIcon& icon);
virtual void setInactivityTimeout(InactivityTimeout inactivityTimeout);
void doInactivityTimeout();
void reloadPointerResources();
@@ -192,10 +192,10 @@
void setPresentation(Presentation) override {
LOG_ALWAYS_FATAL("Should not be called");
}
- void updatePointerIcon(PointerIconStyle) {
+ void updatePointerIcon(PointerIconStyle) override {
LOG_ALWAYS_FATAL("Should not be called");
}
- void setCustomPointerIcon(const SpriteIcon&) {
+ void setCustomPointerIcon(const SpriteIcon&) override {
LOG_ALWAYS_FATAL("Should not be called");
}
// fade() should not be called by inactivity timeout. Do nothing.
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 2533e02..3fc9594 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -295,6 +295,8 @@
@GuardedBy("mAdditionalDisplayInputPropertiesLock")
private final AdditionalDisplayInputProperties mCurrentDisplayProperties =
new AdditionalDisplayInputProperties();
+ // TODO(b/293587049): Pointer Icon Refactor: There can be more than one pointer icon
+ // visible at once. Update this to support multi-pointer use cases.
@GuardedBy("mAdditionalDisplayInputPropertiesLock")
private int mPointerIconType = PointerIcon.TYPE_NOT_SPECIFIED;
@GuardedBy("mAdditionalDisplayInputPropertiesLock")
@@ -1756,6 +1758,21 @@
}
}
+ // Binder call
+ @Override
+ public boolean setPointerIcon(PointerIcon icon, int displayId, int deviceId, int pointerId,
+ IBinder inputToken) {
+ Objects.requireNonNull(icon);
+ synchronized (mAdditionalDisplayInputPropertiesLock) {
+ mPointerIconType = icon.getType();
+ mPointerIcon = mPointerIconType == PointerIcon.TYPE_CUSTOM ? icon : null;
+
+ if (!mCurrentDisplayProperties.pointerIconVisible) return false;
+
+ return mNative.setPointerIcon(icon, displayId, deviceId, pointerId, inputToken);
+ }
+ }
+
/**
* Add a runtime association between the input port and the display port. This overrides any
* static associations.
diff --git a/services/core/java/com/android/server/input/NativeInputManagerService.java b/services/core/java/com/android/server/input/NativeInputManagerService.java
index f126a89..620cde5 100644
--- a/services/core/java/com/android/server/input/NativeInputManagerService.java
+++ b/services/core/java/com/android/server/input/NativeInputManagerService.java
@@ -186,6 +186,9 @@
void setCustomPointerIcon(PointerIcon icon);
+ boolean setPointerIcon(PointerIcon icon, int displayId, int deviceId, int pointerId,
+ IBinder inputToken);
+
void requestPointerCapture(IBinder windowToken, boolean enabled);
boolean canDispatchToDisplay(int deviceId, int displayId);
@@ -434,6 +437,10 @@
public native void setCustomPointerIcon(PointerIcon icon);
@Override
+ public native boolean setPointerIcon(PointerIcon icon, int displayId, int deviceId,
+ int pointerId, IBinder inputToken);
+
+ @Override
public native void requestPointerCapture(IBinder windowToken, boolean enabled);
@Override
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 7af4aad..a888f84 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -692,6 +692,7 @@
void overridePointerIconLocked(int touchSource) {
mTouchSource = touchSource;
if (isFromSource(InputDevice.SOURCE_MOUSE)) {
+ // TODO(b/293587049): Pointer Icon Refactor: Set the pointer icon from the drag window.
InputManagerGlobal.getInstance().setPointerIconType(PointerIcon.TYPE_GRABBING);
}
}
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index f1cddc6..7de50e8 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -308,6 +308,8 @@
void reloadPointerIcons();
void requestPointerCapture(const sp<IBinder>& windowToken, bool enabled);
void setCustomPointerIcon(const SpriteIcon& icon);
+ bool setPointerIcon(std::variant<std::unique_ptr<SpriteIcon>, PointerIconStyle> icon,
+ int32_t displayId, DeviceId deviceId);
void setMotionClassifierEnabled(bool enabled);
std::optional<std::string> getBluetoothAddress(int32_t deviceId);
void setStylusButtonMotionEventsEnabled(bool enabled);
@@ -1347,6 +1349,12 @@
}
}
+bool NativeInputManager::setPointerIcon(
+ std::variant<std::unique_ptr<SpriteIcon>, PointerIconStyle> icon, int32_t displayId,
+ DeviceId deviceId) {
+ return mInputManager->getChoreographer().setPointerIcon(std::move(icon), displayId, deviceId);
+}
+
TouchAffineTransformation NativeInputManager::getTouchAffineTransformation(
JNIEnv *env, jfloatArray matrixArr) {
ATRACE_CALL();
@@ -2511,6 +2519,33 @@
im->setCustomPointerIcon(spriteIcon);
}
+static bool nativeSetPointerIcon(JNIEnv* env, jobject nativeImplObj, jobject iconObj,
+ jint displayId, jint deviceId, jint pointerId,
+ jobject inputTokenObj) {
+ NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
+
+ PointerIcon pointerIcon;
+ status_t result = android_view_PointerIcon_getLoadedIcon(env, iconObj, &pointerIcon);
+ if (result) {
+ jniThrowRuntimeException(env, "Failed to load pointer icon.");
+ return false;
+ }
+
+ std::variant<std::unique_ptr<SpriteIcon>, PointerIconStyle> icon;
+ if (pointerIcon.style == PointerIconStyle::TYPE_CUSTOM) {
+ icon = std::make_unique<SpriteIcon>(pointerIcon.bitmap.copy(
+ ANDROID_BITMAP_FORMAT_RGBA_8888),
+ pointerIcon.style, pointerIcon.hotSpotX,
+ pointerIcon.hotSpotY);
+ } else {
+ icon = pointerIcon.style;
+ }
+ // TODO(b/293587049): Perform hit test to ensure the pointer is dispatched to the channel.
+ sp<IBinder> inputToken = ibinderForJavaObject(env, inputTokenObj);
+
+ return im->setPointerIcon(std::move(icon), displayId, deviceId);
+}
+
static jboolean nativeCanDispatchToDisplay(JNIEnv* env, jobject nativeImplObj, jint deviceId,
jint displayId) {
NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
@@ -2769,6 +2804,8 @@
{"reloadPointerIcons", "()V", (void*)nativeReloadPointerIcons},
{"setCustomPointerIcon", "(Landroid/view/PointerIcon;)V",
(void*)nativeSetCustomPointerIcon},
+ {"setPointerIcon", "(Landroid/view/PointerIcon;IIILandroid/os/IBinder;)Z",
+ (void*)nativeSetPointerIcon},
{"canDispatchToDisplay", "(II)Z", (void*)nativeCanDispatchToDisplay},
{"notifyPortAssociationsChanged", "()V", (void*)nativeNotifyPortAssociationsChanged},
{"changeUniqueIdAssociation", "()V", (void*)nativeChangeUniqueIdAssociation},