From ea66a1e9543380a7756ec648af97953fee253485 Mon Sep 17 00:00:00 2001 From: Patrick Williams Date: Thu, 23 Jun 2022 20:23:13 +0000 Subject: [PATCH] Replace syncInputWindows with addWindowInfosReportedListener Bug: 222421815 Change-Id: Ia76481daa1ec2684890eb3ae58fafa148424843b Test: DragDropControllerTests --- core/java/android/view/SurfaceControl.java | 12 +- core/jni/android_view_SurfaceControl.cpp | 66 +++++++-- .../android/server/wm/DragDropController.java | 77 +++++++---- .../java/com/android/server/wm/DragState.java | 24 +++- .../com/android/server/wm/InputMonitor.java | 14 +- .../com/android/server/wm/TaskPositioner.java | 52 ++++--- .../server/wm/TaskPositioningController.java | 128 +++++++++++------- .../server/wm/WindowManagerInternal.java | 10 +- .../server/wm/WindowManagerService.java | 11 +- .../android/server/wm/StubTransaction.java | 19 ++- 10 files changed, 276 insertions(+), 137 deletions(-) diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 07a04a8385570..5f6c24bff3f7d 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -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; } diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index ab751cb02cd16..22f84b6e961af 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -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 - #include #include #include #include #include +#include #include #include #include @@ -60,6 +54,13 @@ #include #include +#include + +#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(&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::make(env, runnable); auto transaction = reinterpret_cast(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, "", "(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, "", "()V"); diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java index aa8ebf763beca..3108d4e0fcc06 100644 --- a/services/core/java/com/android/server/wm/DragDropController.java +++ b/services/core/java/com/android/server/wm/DragDropController.java @@ -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 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! diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java index 475dd17ef727b..2242bfde7ced3 100644 --- a/services/core/java/com/android/server/wm/DragState.java +++ b/services/core/java/com/android/server/wm/DragState.java @@ -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 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 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 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(); } } diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 4ff4a9a491bf0..9703dfb8dd6fb 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -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; } diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java index 03ca4fd49a968..5b32149b818dd 100644 --- a/services/core/java/com/android/server/wm/TaskPositioner.java +++ b/services/core/java/com/android/server/wm/TaskPositioner.java @@ -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 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() { diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java index 6dcad2bb263e1..55a3decdca08b 100644 --- a/services/core/java/com/android/server/wm/TaskPositioningController.java +++ b/services/core/java/com/android/server/wm/TaskPositioningController.java @@ -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 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 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 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 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 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) { diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index 9eee7ba871a21..6121ad4da05f5 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -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 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 */)); } /** diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 00e8d8bdaaec7..287b41a311179 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -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); } diff --git a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java index d27581865e266..31546e85f8a40 100644 --- a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java +++ b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java @@ -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 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; + } }