Replace syncInputWindows with addWindowInfosReportedListener

Bug: 222421815
Change-Id: Ia76481daa1ec2684890eb3ae58fafa148424843b
Test: DragDropControllerTests
This commit is contained in:
Patrick Williams
2022-06-23 20:23:13 +00:00
parent 1475e5f3dd
commit ea66a1e954
10 changed files with 276 additions and 137 deletions

View File

@@ -233,7 +233,8 @@ public final class SurfaceControl implements Parcelable {
private static native boolean nativeGetProtectedContentSupport();
private static native void nativeSetMetadata(long transactionObj, long nativeObject, int key,
Parcel data);
private static native void nativeSyncInputWindows(long transactionObj);
private static native void nativeAddWindowInfosReportedListener(long transactionObj,
Runnable listener);
private static native boolean nativeGetDisplayBrightnessSupport(IBinder displayToken);
private static native boolean nativeSetDisplayBrightness(IBinder displayToken,
float sdrBrightness, float sdrBrightnessNits, float displayBrightness,
@@ -3061,13 +3062,14 @@ public final class SurfaceControl implements Parcelable {
}
/**
* Waits until any changes to input windows have been sent from SurfaceFlinger to
* InputFlinger before returning.
* Adds a callback that is called after WindowInfosListeners from the systems server are
* complete. This is primarily used to ensure that InputDispatcher::setInputWindowsLocked
* has been called before running the added callback.
*
* @hide
*/
public Transaction syncInputWindows() {
nativeSyncInputWindows(mNativeObject);
public Transaction addWindowInfosReportedListener(@NonNull Runnable listener) {
nativeAddWindowInfosReportedListener(mNativeObject, listener);
return this;
}

View File

@@ -17,18 +17,12 @@
#define LOG_TAG "SurfaceControl"
#define LOG_NDEBUG 0
#include "android_os_Parcel.h"
#include "android_util_Binder.h"
#include "android_hardware_input_InputWindowHandle.h"
#include "core_jni_helpers.h"
#include <memory>
#include <aidl/android/hardware/graphics/common/PixelFormat.h>
#include <android-base/chrono_utils.h>
#include <android/graphics/properties.h>
#include <android/graphics/region.h>
#include <android/gui/BnScreenCaptureListener.h>
#include <android/gui/BnWindowInfosReportedListener.h>
#include <android/hardware/display/IDeviceProductInfoConstants.h>
#include <android/os/IInputConstants.h>
#include <android_runtime/AndroidRuntime.h>
@@ -60,6 +54,13 @@
#include <utils/LightRefBase.h>
#include <utils/Log.h>
#include <memory>
#include "android_hardware_input_InputWindowHandle.h"
#include "android_os_Parcel.h"
#include "android_util_Binder.h"
#include "core_jni_helpers.h"
// ----------------------------------------------------------------------------
namespace android {
@@ -87,6 +88,11 @@ static jobject toInteger(JNIEnv* env, int32_t i) {
return env->NewObject(gIntegerClassInfo.clazz, gIntegerClassInfo.ctor, i);
}
static struct {
jclass clazz;
jmethodID run;
} gRunnableClassInfo;
static struct {
jclass clazz;
jmethodID ctor;
@@ -381,6 +387,38 @@ private:
}
};
class WindowInfosReportedListenerWrapper : public gui::BnWindowInfosReportedListener {
public:
explicit WindowInfosReportedListenerWrapper(JNIEnv* env, jobject listener) {
env->GetJavaVM(&mVm);
mListener = env->NewGlobalRef(listener);
LOG_ALWAYS_FATAL_IF(!mListener, "Failed to make global ref");
}
~WindowInfosReportedListenerWrapper() {
if (mListener) {
getenv()->DeleteGlobalRef(mListener);
mListener = nullptr;
}
}
binder::Status onWindowInfosReported() override {
JNIEnv* env = getenv();
env->CallVoidMethod(mListener, gRunnableClassInfo.run);
return binder::Status::ok();
}
private:
jobject mListener;
JavaVM* mVm;
JNIEnv* getenv() {
JNIEnv* env;
mVm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
return env;
}
};
// ----------------------------------------------------------------------------
static jlong nativeCreateTransaction(JNIEnv* env, jclass clazz) {
@@ -890,9 +928,11 @@ static void nativeSetInputWindowInfo(JNIEnv* env, jclass clazz, jlong transactio
transaction->setInputWindowInfo(ctrl, *handle->getInfo());
}
static void nativeSyncInputWindows(JNIEnv* env, jclass clazz, jlong transactionObj) {
static void nativeAddWindowInfosReportedListener(JNIEnv* env, jclass clazz, jlong transactionObj,
jobject runnable) {
auto listener = sp<WindowInfosReportedListenerWrapper>::make(env, runnable);
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
transaction->syncInputWindows();
transaction->addWindowInfosReportedListener(listener);
}
static void nativeSetMetadata(JNIEnv* env, jclass clazz, jlong transactionObj,
@@ -2258,8 +2298,8 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
{"nativeSetBufferTransform", "(JJI)V", (void*) nativeSetBufferTransform},
{"nativeSetDataSpace", "(JJI)V",
(void*)nativeSetDataSpace },
{"nativeSyncInputWindows", "(J)V",
(void*)nativeSyncInputWindows },
{"nativeAddWindowInfosReportedListener", "(JLjava/lang/Runnable;)V",
(void*)nativeAddWindowInfosReportedListener },
{"nativeGetDisplayBrightnessSupport", "(Landroid/os/IBinder;)Z",
(void*)nativeGetDisplayBrightnessSupport },
{"nativeSetDisplayBrightness", "(Landroid/os/IBinder;FFFF)Z",
@@ -2323,6 +2363,10 @@ int register_android_view_SurfaceControl(JNIEnv* env)
gIntegerClassInfo.clazz = MakeGlobalRefOrDie(env, integerClass);
gIntegerClassInfo.ctor = GetMethodIDOrDie(env, gIntegerClassInfo.clazz, "<init>", "(I)V");
jclass runnableClazz = FindClassOrDie(env, "java/lang/Runnable");
gRunnableClassInfo.clazz = MakeGlobalRefOrDie(env, runnableClazz);
gRunnableClassInfo.run = GetMethodIDOrDie(env, runnableClazz, "run", "()V");
jclass infoClazz = FindClassOrDie(env, "android/view/SurfaceControl$StaticDisplayInfo");
gStaticDisplayInfoClassInfo.clazz = MakeGlobalRefOrDie(env, infoClazz);
gStaticDisplayInfoClassInfo.ctor = GetMethodIDOrDie(env, infoClazz, "<init>", "()V");

View File

@@ -38,6 +38,8 @@ import android.view.accessibility.AccessibilityManager;
import com.android.server.wm.WindowManagerInternal.IDragDropCallback;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
/**
@@ -106,6 +108,8 @@ class DragDropController {
final boolean callbackResult = mCallback.get().prePerformDrag(window, dragToken,
touchSource, touchX, touchY, thumbCenterX, thumbCenterY, data);
try {
DisplayContent displayContent = null;
CompletableFuture<Boolean> touchFocusTransferredFuture = null;
synchronized (mService.mGlobalLock) {
try {
if (!callbackResult) {
@@ -137,7 +141,7 @@ class DragDropController {
// !!! TODO(multi-display): support other displays
final DisplayContent displayContent = callingWin.getDisplayContent();
displayContent = callingWin.getDisplayContent();
if (displayContent == null) {
Slog.w(TAG_WM, "display content is null");
return null;
@@ -158,33 +162,9 @@ class DragDropController {
if ((flags & View.DRAG_FLAG_ACCESSIBILITY_ACTION) == 0) {
final Display display = displayContent.getDisplay();
if (!mCallback.get().registerInputChannel(
touchFocusTransferredFuture = mCallback.get().registerInputChannel(
mDragState, display, mService.mInputManager,
callingWin.mInputChannel)) {
Slog.e(TAG_WM, "Unable to transfer touch focus");
return null;
}
final SurfaceControl surfaceControl = mDragState.mSurfaceControl;
mDragState.broadcastDragStartedLocked(touchX, touchY);
mDragState.overridePointerIconLocked(touchSource);
// remember the thumb offsets for later
mDragState.mThumbOffsetX = thumbCenterX;
mDragState.mThumbOffsetY = thumbCenterY;
// Make the surface visible at the proper location
if (SHOW_LIGHT_TRANSACTIONS) {
Slog.i(TAG_WM, ">>> OPEN TRANSACTION performDrag");
}
final SurfaceControl.Transaction transaction = mDragState.mTransaction;
transaction.setAlpha(surfaceControl, mDragState.mOriginalAlpha);
transaction.show(surfaceControl);
displayContent.reparentToOverlay(transaction, surfaceControl);
mDragState.updateDragSurfaceLocked(true, touchX, touchY);
if (SHOW_LIGHT_TRANSACTIONS) {
Slog.i(TAG_WM, "<<< CLOSE TRANSACTION performDrag");
}
callingWin.mInputChannel);
} else {
// Skip surface logic for a drag triggered by an AccessibilityAction
mDragState.broadcastDragStartedLocked(touchX, touchY);
@@ -194,14 +174,51 @@ class DragDropController {
getAccessibilityManager().getRecommendedTimeoutMillis(
A11Y_DRAG_TIMEOUT_DEFAULT_MS,
AccessibilityManager.FLAG_CONTENT_CONTROLS));
return dragToken;
}
} finally {
if (surface != null) {
surface.release();
}
if (mDragState != null && !mDragState.isInProgress()) {
mDragState.closeLocked();
}
}
}
boolean touchFocusTransferred = false;
try {
touchFocusTransferred = touchFocusTransferredFuture.get(DRAG_TIMEOUT_MS,
TimeUnit.MILLISECONDS);
} catch (Exception exception) {
Slog.e(TAG_WM, "Exception thrown while waiting for touch focus transfer",
exception);
}
synchronized (mService.mGlobalLock) {
if (!touchFocusTransferred) {
Slog.e(TAG_WM, "Unable to transfer touch focus");
mDragState.closeLocked();
return null;
}
final SurfaceControl surfaceControl = mDragState.mSurfaceControl;
mDragState.broadcastDragStartedLocked(touchX, touchY);
mDragState.overridePointerIconLocked(touchSource);
// remember the thumb offsets for later
mDragState.mThumbOffsetX = thumbCenterX;
mDragState.mThumbOffsetY = thumbCenterY;
// Make the surface visible at the proper location
if (SHOW_LIGHT_TRANSACTIONS) {
Slog.i(TAG_WM, ">>> OPEN TRANSACTION performDrag");
}
final SurfaceControl.Transaction transaction = mDragState.mTransaction;
transaction.setAlpha(surfaceControl, mDragState.mOriginalAlpha);
transaction.show(surfaceControl);
displayContent.reparentToOverlay(transaction, surfaceControl);
mDragState.updateDragSurfaceLocked(true, touchX, touchY);
if (SHOW_LIGHT_TRANSACTIONS) {
Slog.i(TAG_WM, "<<< CLOSE TRANSACTION performDrag");
}
}
return dragToken; // success!

View File

@@ -34,6 +34,8 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.MY_PID;
import static com.android.server.wm.WindowManagerService.MY_UID;
import static java.util.concurrent.CompletableFuture.completedFuture;
import android.animation.Animator;
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
@@ -70,6 +72,7 @@ import com.android.server.LocalServices;
import com.android.server.pm.UserManagerInternal;
import java.util.ArrayList;
import java.util.concurrent.CompletableFuture;
/**
* Drag/drop state
@@ -160,7 +163,10 @@ class DragState {
return mIsClosing;
}
private void showInputSurface() {
/**
* @return a future that completes after window info is sent.
*/
private CompletableFuture<Void> showInputSurface() {
if (mInputSurface == null) {
mInputSurface = mService.makeSurfaceBuilder(mDisplayContent.getSession())
.setContainerLayer()
@@ -173,7 +179,7 @@ class DragState {
if (h == null) {
Slog.w(TAG_WM, "Drag is in progress but there is no "
+ "drag window handle.");
return;
return completedFuture(null);
}
// Crop the input surface to the display size.
@@ -184,10 +190,13 @@ class DragState {
.setLayer(mInputSurface, Integer.MAX_VALUE)
.setCrop(mInputSurface, mTmpClipRect);
// syncInputWindows here to ensure the input window info is sent before the
// A completableFuture is returned to ensure that input window info is sent before the
// transferTouchFocus is called.
mTransaction.syncInputWindows()
.apply(true /*sync*/);
CompletableFuture<Void> result = new CompletableFuture<>();
mTransaction
.addWindowInfosReportedListener(() -> result.complete(null))
.apply();
return result;
}
/**
@@ -416,14 +425,15 @@ class DragState {
/**
* @param display The Display that the window being dragged is on.
*/
void register(Display display) {
CompletableFuture<Void> register(Display display) {
display.getRealSize(mDisplaySize);
if (DEBUG_DRAG) Slog.d(TAG_WM, "registering drag input channel");
if (mInputInterceptor != null) {
Slog.e(TAG_WM, "Duplicate register of drag input channel");
return completedFuture(null);
} else {
mInputInterceptor = new InputInterceptor(display);
showInputSurface();
return showInputSurface();
}
}

View File

@@ -147,13 +147,13 @@ final class InputMonitor {
void onDisplayRemoved() {
mHandler.removeCallbacks(mUpdateInputWindows);
mHandler.post(() -> {
// Make sure any pending setInputWindowInfo transactions are completed. That prevents
// the timing of updating input info of removed display after cleanup.
mService.mTransactionFactory.get().syncInputWindows().apply();
// It calls InputDispatcher::setInputWindows directly.
mService.mInputManager.onDisplayRemoved(mDisplayId);
});
mService.mTransactionFactory.get()
// Make sure any pending setInputWindowInfo transactions are completed. That
// prevents the timing of updating input info of removed display after cleanup.
.addWindowInfosReportedListener(() ->
// It calls InputDispatcher::setInputWindows directly.
mService.mInputManager.onDisplayRemoved(mDisplayId))
.apply();
mDisplayRemoved = true;
}

View File

@@ -35,6 +35,8 @@ import static com.android.server.wm.WindowManagerService.dipToPixel;
import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP;
import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP;
import static java.util.concurrent.CompletableFuture.completedFuture;
import android.annotation.NonNull;
import android.graphics.Point;
import android.graphics.Rect;
@@ -60,6 +62,8 @@ import com.android.internal.policy.TaskResizingAlgorithm;
import com.android.internal.policy.TaskResizingAlgorithm.CtrlType;
import com.android.internal.protolog.common.ProtoLog;
import java.util.concurrent.CompletableFuture;
class TaskPositioner implements IBinder.DeathRecipient {
private static final boolean DEBUG_ORIENTATION_VIOLATIONS = false;
private static final String TAG_LOCAL = "TaskPositioner";
@@ -195,14 +199,14 @@ class TaskPositioner implements IBinder.DeathRecipient {
* @param displayContent The Display that the window being dragged is on.
* @param win The window which will be dragged.
*/
void register(DisplayContent displayContent, @NonNull WindowState win) {
CompletableFuture<Void> register(DisplayContent displayContent, @NonNull WindowState win) {
if (DEBUG_TASK_POSITIONING) {
Slog.d(TAG, "Registering task positioner");
}
if (mClientChannel != null) {
Slog.e(TAG, "Task positioner already registered");
return;
return completedFuture(null);
}
mDisplayContent = displayContent;
@@ -235,27 +239,33 @@ class TaskPositioner implements IBinder.DeathRecipient {
mDisplayContent.getDisplayRotation().pause();
// Notify InputMonitor to take mDragWindowHandle.
mService.mTaskPositioningController.showInputSurface(win.getDisplayId());
return mService.mTaskPositioningController.showInputSurface(win.getDisplayId())
.thenRun(() -> {
// The global lock is held by the callers of register but released before the async
// results are waited on. We must acquire the lock in this callback to ensure thread
// safety.
synchronized (mService.mGlobalLock) {
final Rect displayBounds = mTmpRect;
displayContent.getBounds(displayBounds);
final DisplayMetrics displayMetrics = displayContent.getDisplayMetrics();
mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, displayMetrics);
mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, displayMetrics);
mMaxVisibleSize.set(displayBounds.width(), displayBounds.height());
final Rect displayBounds = mTmpRect;
displayContent.getBounds(displayBounds);
final DisplayMetrics displayMetrics = displayContent.getDisplayMetrics();
mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, displayMetrics);
mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, displayMetrics);
mMaxVisibleSize.set(displayBounds.width(), displayBounds.height());
mDragEnded = false;
mDragEnded = false;
try {
mClientCallback = win.mClient.asBinder();
mClientCallback.linkToDeath(this, 0 /* flags */);
} catch (RemoteException e) {
// The caller has died, so clean up TaskPositioningController.
mService.mTaskPositioningController.finishTaskPositioning();
return;
}
mWindow = win;
mTask = win.getTask();
try {
mClientCallback = win.mClient.asBinder();
mClientCallback.linkToDeath(this, 0 /* flags */);
} catch (RemoteException e) {
// The caller has died, so clean up TaskPositioningController.
mService.mTaskPositioningController.finishTaskPositioning();
return;
}
mWindow = win;
mTask = win.getTask();
}
});
}
void unregister() {

View File

@@ -19,6 +19,8 @@ package com.android.server.wm;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static java.util.concurrent.CompletableFuture.completedFuture;
import android.annotation.Nullable;
import android.graphics.Point;
import android.graphics.Rect;
@@ -28,6 +30,8 @@ import android.view.IWindow;
import android.view.InputWindowHandle;
import android.view.SurfaceControl;
import java.util.concurrent.CompletableFuture;
/**
* Controller for task positioning by drag.
*/
@@ -58,14 +62,16 @@ class TaskPositioningController {
void hideInputSurface(int displayId) {
if (mPositioningDisplay != null && mPositioningDisplay.getDisplayId() == displayId
&& mInputSurface != null) {
mTransaction.hide(mInputSurface);
mTransaction.syncInputWindows().apply();
mTransaction.hide(mInputSurface).apply();
}
}
void showInputSurface(int displayId) {
/**
* @return a future that completes after window info is sent.
*/
CompletableFuture<Void> showInputSurface(int displayId) {
if (mPositioningDisplay == null || mPositioningDisplay.getDisplayId() != displayId) {
return;
return completedFuture(null);
}
final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
if (mInputSurface == null) {
@@ -81,7 +87,7 @@ class TaskPositioningController {
if (h == null) {
Slog.w(TAG_WM, "Drag is in progress but there is no "
+ "drag window handle.");
return;
return completedFuture(null);
}
final Display display = dc.getDisplay();
@@ -89,25 +95,38 @@ class TaskPositioningController {
display.getRealSize(p);
mTmpClipRect.set(0, 0, p.x, p.y);
CompletableFuture<Void> result = new CompletableFuture<>();
mTransaction.show(mInputSurface)
.setInputWindowInfo(mInputSurface, h)
.setLayer(mInputSurface, Integer.MAX_VALUE)
.setPosition(mInputSurface, 0, 0)
.setCrop(mInputSurface, mTmpClipRect)
.syncInputWindows()
.addWindowInfosReportedListener(() -> result.complete(null))
.apply();
return result;
}
boolean startMovingTask(IWindow window, float startX, float startY) {
WindowState win = null;
CompletableFuture<Boolean> startPositioningLockedFuture;
synchronized (mService.mGlobalLock) {
win = mService.windowForClientLocked(null, window, false);
// win shouldn't be null here, pass it down to startPositioningLocked
// to get warning if it's null.
if (!startPositioningLocked(
win, false /*resize*/, false /*preserveOrientation*/, startX, startY)) {
startPositioningLockedFuture =
startPositioningLocked(
win, false /*resize*/, false /*preserveOrientation*/, startX, startY);
}
try {
if (!startPositioningLockedFuture.get()) {
return false;
}
} catch (Exception exception) {
Slog.e(TAG_WM, "Exception thrown while waiting for startPositionLocked future",
exception);
return false;
}
synchronized (mService.mGlobalLock) {
mService.mAtmService.setFocusedTask(win.getTask().mTaskId);
}
return true;
@@ -115,25 +134,37 @@ class TaskPositioningController {
void handleTapOutsideTask(DisplayContent displayContent, int x, int y) {
mService.mH.post(() -> {
Task task;
CompletableFuture<Boolean> startPositioningLockedFuture;
synchronized (mService.mGlobalLock) {
final Task task = displayContent.findTaskForResizePoint(x, y);
if (task != null) {
if (!task.isResizeable()) {
// The task is not resizable, so don't do anything when the user drags the
// the resize handles.
return;
}
if (!startPositioningLocked(task.getTopVisibleAppMainWindow(), true /*resize*/,
task.preserveOrientationOnResize(), x, y)) {
return;
}
mService.mAtmService.setFocusedTask(task.mTaskId);
task = displayContent.findTaskForResizePoint(x, y);
if (task == null || !task.isResizeable()) {
// The task is not resizable, so don't do anything when the user drags the
// the resize handles.
return;
}
startPositioningLockedFuture =
startPositioningLocked(task.getTopVisibleAppMainWindow(), true /*resize*/,
task.preserveOrientationOnResize(), x, y);
}
try {
if (!startPositioningLockedFuture.get()) {
return;
}
} catch (Exception exception) {
Slog.e(TAG_WM, "Exception thrown while waiting for startPositionLocked future",
exception);
return;
}
synchronized (mService.mGlobalLock) {
mService.mAtmService.setFocusedTask(task.mTaskId);
}
});
}
private boolean startPositioningLocked(WindowState win, boolean resize,
private CompletableFuture<Boolean> startPositioningLocked(WindowState win, boolean resize,
boolean preserveOrientation, float startX, float startY) {
if (DEBUG_TASK_POSITIONING)
Slog.d(TAG_WM, "startPositioningLocked: "
@@ -142,43 +173,48 @@ class TaskPositioningController {
if (win == null || win.mActivityRecord == null) {
Slog.w(TAG_WM, "startPositioningLocked: Bad window " + win);
return false;
return completedFuture(false);
}
if (win.mInputChannel == null) {
Slog.wtf(TAG_WM, "startPositioningLocked: " + win + " has no input channel, "
+ " probably being removed");
return false;
return completedFuture(false);
}
final DisplayContent displayContent = win.getDisplayContent();
if (displayContent == null) {
Slog.w(TAG_WM, "startPositioningLocked: Invalid display content " + win);
return false;
return completedFuture(false);
}
mPositioningDisplay = displayContent;
mTaskPositioner = TaskPositioner.create(mService);
mTaskPositioner.register(displayContent, win);
return mTaskPositioner.register(displayContent, win).thenApply(unused -> {
// The global lock is held by the callers of startPositioningLocked but released before
// the async results are waited on. We must acquire the lock in this callback to ensure
// thread safety.
synchronized (mService.mGlobalLock) {
// We need to grab the touch focus so that the touch events during the
// resizing/scrolling are not sent to the app. 'win' is the main window
// of the app, it may not have focus since there might be other windows
// on top (eg. a dialog window).
WindowState transferFocusFromWin = win;
if (displayContent.mCurrentFocus != null && displayContent.mCurrentFocus != win
&& displayContent.mCurrentFocus.mActivityRecord == win.mActivityRecord) {
transferFocusFromWin = displayContent.mCurrentFocus;
}
if (!mService.mInputManager.transferTouchFocus(
transferFocusFromWin.mInputChannel, mTaskPositioner.mClientChannel,
false /* isDragDrop */)) {
Slog.e(TAG_WM, "startPositioningLocked: Unable to transfer touch focus");
cleanUpTaskPositioner();
return false;
}
// We need to grab the touch focus so that the touch events during the
// resizing/scrolling are not sent to the app. 'win' is the main window
// of the app, it may not have focus since there might be other windows
// on top (eg. a dialog window).
WindowState transferFocusFromWin = win;
if (displayContent.mCurrentFocus != null && displayContent.mCurrentFocus != win
&& displayContent.mCurrentFocus.mActivityRecord == win.mActivityRecord) {
transferFocusFromWin = displayContent.mCurrentFocus;
}
if (!mService.mInputManager.transferTouchFocus(
transferFocusFromWin.mInputChannel, mTaskPositioner.mClientChannel,
false /* isDragDrop */)) {
Slog.e(TAG_WM, "startPositioningLocked: Unable to transfer touch focus");
cleanUpTaskPositioner();
return false;
}
mTaskPositioner.startDrag(resize, preserveOrientation, startX, startY);
return true;
mTaskPositioner.startDrag(resize, preserveOrientation, startX, startY);
return true;
}
});
}
public void finishTaskPositioning(IWindow window) {

View File

@@ -50,6 +50,7 @@ import com.android.server.policy.WindowManagerPolicy;
import java.lang.annotation.Retention;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
/**
* Window manager local system service interface.
@@ -303,12 +304,13 @@ public abstract class WindowManagerInternal {
* An interface to customize drag and drop behaviors.
*/
public interface IDragDropCallback {
default boolean registerInputChannel(
default CompletableFuture<Boolean> registerInputChannel(
DragState state, Display display, InputManagerService service,
InputChannel source) {
state.register(display);
return service.transferTouchFocus(source, state.getInputChannel(),
true /* isDragDrop */);
return state.register(display)
.thenApply(unused ->
service.transferTouchFocus(source, state.getInputChannel(),
true /* isDragDrop */));
}
/**

View File

@@ -344,6 +344,8 @@ import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Supplier;
@@ -374,6 +376,8 @@ public class WindowManagerService extends IWindowManager.Stub
// proceding with safe mode detection.
private static final int INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS = 1000;
private static final int SYNC_INPUT_TRANSACTIONS_TIMEOUT_MS = 5000;
// Poll interval in milliseconds for watching boot animation finished.
// TODO(b/159045990) Migrate to SystemService.waitForState with dedicated thread.
private static final int BOOT_ANIMATION_POLL_INTERVAL = 50;
@@ -8440,7 +8444,12 @@ public class WindowManagerService extends IWindowManager.Stub
displayContent.getInputMonitor().updateInputWindowsImmediately(t));
}
t.syncInputWindows().apply();
CountDownLatch countDownLatch = new CountDownLatch(1);
t.addWindowInfosReportedListener(countDownLatch::countDown).apply();
countDownLatch.await(SYNC_INPUT_TRANSACTIONS_TIMEOUT_MS, TimeUnit.MILLISECONDS);
} catch (InterruptedException exception) {
Slog.e(TAG_WM, "Exception thrown while waiting for window infos to be reported",
exception);
} finally {
Binder.restoreCallingIdentity(token);
}

View File

@@ -30,6 +30,7 @@ import android.view.InputWindowHandle;
import android.view.Surface;
import android.view.SurfaceControl;
import java.util.HashSet;
import java.util.concurrent.Executor;
/**
@@ -37,8 +38,14 @@ import java.util.concurrent.Executor;
* testing to avoid calls to native code.
*/
public class StubTransaction extends SurfaceControl.Transaction {
private HashSet<Runnable> mWindowInfosReportedListeners = new HashSet<>();
@Override
public void apply() {
for (Runnable listener : mWindowInfosReportedListeners) {
listener.run();
}
}
@Override
@@ -47,6 +54,7 @@ public class StubTransaction extends SurfaceControl.Transaction {
@Override
public void apply(boolean sync) {
apply();
}
@Override
@@ -234,11 +242,6 @@ public class StubTransaction extends SurfaceControl.Transaction {
return this;
}
@Override
public SurfaceControl.Transaction syncInputWindows() {
return this;
}
@Override
public SurfaceControl.Transaction setColorSpaceAgnostic(SurfaceControl sc, boolean agnostic) {
return this;
@@ -299,4 +302,10 @@ public class StubTransaction extends SurfaceControl.Transaction {
boolean isTrustedOverlay) {
return this;
}
@Override
public SurfaceControl.Transaction addWindowInfosReportedListener(@NonNull Runnable listener) {
mWindowInfosReportedListeners.add(listener);
return this;
}
}