blob: a867a5d41e2431854c47c9b299ae130ef361c3b1 [file] [log] [blame]
Chong Zhang8e89b312015-09-09 15:09:30 -07001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.wm;
18
Wale Ogunwale65ebd952018-04-25 15:41:44 -070019import static android.app.ActivityTaskManager.RESIZE_MODE_USER;
20import static android.app.ActivityTaskManager.RESIZE_MODE_USER_FORCED;
Wale Ogunwalecad05a02015-09-25 10:41:44 -070021import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
Garfield Tan07544cd2018-09-12 16:16:54 -070022
23import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
Adrian Roosb125e0b2019-10-02 14:55:14 +020024import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -080025import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -080026import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
27import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
Filip Gruszczynski57b6cce2015-10-06 09:50:51 -070028import static com.android.server.wm.WindowManagerService.dipToPixel;
Wale Ogunwale231b06e2015-09-16 12:03:09 -070029import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP;
30import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP;
Chong Zhang8e89b312015-09-09 15:09:30 -070031
32import android.annotation.IntDef;
Yongjin Kwon9c5f0aa2019-11-06 17:36:24 +090033import android.annotation.NonNull;
Wale Ogunwale04d9cb52018-04-30 13:55:07 -070034import android.app.IActivityTaskManager;
Chong Zhang8e89b312015-09-09 15:09:30 -070035import android.graphics.Point;
36import android.graphics.Rect;
Robert Carr0bcbe642018-10-11 19:07:43 -070037import android.os.Binder;
yj81.kwon79c97672019-04-16 19:44:48 -070038import android.os.IBinder;
Chong Zhang8e89b312015-09-09 15:09:30 -070039import android.os.Looper;
40import android.os.Process;
41import android.os.RemoteException;
Wale Ogunwalecad05a02015-09-25 10:41:44 -070042import android.os.Trace;
Chong Zhang8e89b312015-09-09 15:09:30 -070043import android.util.DisplayMetrics;
44import android.util.Slog;
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -080045import android.view.BatchedInputEventReceiver;
Wale Ogunwale4f52bc62015-09-24 13:47:31 -070046import android.view.Choreographer;
Chong Zhang8e89b312015-09-09 15:09:30 -070047import android.view.Display;
Evan Rosky0d654cb2019-02-26 10:59:10 -080048import android.view.InputApplicationHandle;
Chong Zhang8e89b312015-09-09 15:09:30 -070049import android.view.InputChannel;
50import android.view.InputDevice;
51import android.view.InputEvent;
Evan Rosky0d654cb2019-02-26 10:59:10 -080052import android.view.InputWindowHandle;
Chong Zhang8e89b312015-09-09 15:09:30 -070053import android.view.MotionEvent;
Arthur Hungb62e5002019-08-29 12:28:07 +080054import android.view.SurfaceControl;
Chong Zhang8e89b312015-09-09 15:09:30 -070055import android.view.WindowManager;
56
skuhne@google.com322347b2016-12-02 12:54:03 -080057import com.android.internal.annotations.VisibleForTesting;
Adrian Roosb125e0b2019-10-02 14:55:14 +020058import com.android.server.protolog.common.ProtoLog;
Wale Ogunwale228d4042015-09-13 10:17:34 -070059
60import java.lang.annotation.Retention;
61import java.lang.annotation.RetentionPolicy;
62
yj81.kwon79c97672019-04-16 19:44:48 -070063class TaskPositioner implements IBinder.DeathRecipient {
skuhne@google.com322347b2016-12-02 12:54:03 -080064 private static final boolean DEBUG_ORIENTATION_VIOLATIONS = false;
Jorim Jaggibc5425c2016-03-01 13:51:16 +010065 private static final String TAG_LOCAL = "TaskPositioner";
66 private static final String TAG = TAG_WITH_CLASS_NAME ? TAG_LOCAL : TAG_WM;
Chong Zhang8e89b312015-09-09 15:09:30 -070067
Garfield Tan6caf1d8c2018-01-18 12:37:50 -080068 private static Factory sFactory;
69
Chong Zhang8e89b312015-09-09 15:09:30 -070070 @IntDef(flag = true,
71 value = {
72 CTRL_NONE,
73 CTRL_LEFT,
74 CTRL_RIGHT,
75 CTRL_TOP,
76 CTRL_BOTTOM
77 })
78 @Retention(RetentionPolicy.SOURCE)
79 @interface CtrlType {}
80
81 private static final int CTRL_NONE = 0x0;
82 private static final int CTRL_LEFT = 0x1;
83 private static final int CTRL_RIGHT = 0x2;
84 private static final int CTRL_TOP = 0x4;
85 private static final int CTRL_BOTTOM = 0x8;
86
Filip Gruszczynski64b6b442016-01-18 13:20:58 -080087 public static final float RESIZING_HINT_ALPHA = 0.5f;
88
89 public static final int RESIZING_HINT_DURATION_MS = 0;
90
skuhne@google.com322347b2016-12-02 12:54:03 -080091 // The minimal aspect ratio which needs to be met to count as landscape (or 1/.. for portrait).
92 // Note: We do not use the 1.33 from the CDD here since the user is allowed to use what ever
93 // aspect he desires.
94 @VisibleForTesting
95 static final float MIN_ASPECT = 1.2f;
96
Chong Zhang8e89b312015-09-09 15:09:30 -070097 private final WindowManagerService mService;
Wale Ogunwale04d9cb52018-04-30 13:55:07 -070098 private final IActivityTaskManager mActivityManager;
Chong Zhang8e89b312015-09-09 15:09:30 -070099 private WindowPositionerEventReceiver mInputEventReceiver;
Riddle Hsu654a6f92018-07-13 22:59:36 +0800100 private DisplayContent mDisplayContent;
Chong Zhang8e89b312015-09-09 15:09:30 -0700101 private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
Wale Ogunwale228d4042015-09-13 10:17:34 -0700102 private Rect mTmpRect = new Rect();
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700103 private int mMinVisibleWidth;
104 private int mMinVisibleHeight;
Chong Zhang8e89b312015-09-09 15:09:30 -0700105
Charles Chen2cb5a0f2019-03-22 20:58:00 +0800106 @VisibleForTesting
107 Task mTask;
Yongjin Kwon9c5f0aa2019-11-06 17:36:24 +0900108 WindowState mWindow;
Chong Zhang09b21ef2015-09-14 10:20:21 -0700109 private boolean mResizing;
skuhne@google.com322347b2016-12-02 12:54:03 -0800110 private boolean mPreserveOrientation;
111 private boolean mStartOrientationWasLandscape;
Chong Zhang8e89b312015-09-09 15:09:30 -0700112 private final Rect mWindowOriginalBounds = new Rect();
113 private final Rect mWindowDragBounds = new Rect();
skuhne@google.com322347b2016-12-02 12:54:03 -0800114 private final Point mMaxVisibleSize = new Point();
Chong Zhang8e89b312015-09-09 15:09:30 -0700115 private float mStartDragX;
116 private float mStartDragY;
117 @CtrlType
118 private int mCtrlType = CTRL_NONE;
yj81.kwon79c97672019-04-16 19:44:48 -0700119 @VisibleForTesting
120 boolean mDragEnded;
yj81.kwon19585ff2019-04-23 18:53:57 -0700121 IBinder mClientCallback;
Chong Zhang8e89b312015-09-09 15:09:30 -0700122
123 InputChannel mServerChannel;
124 InputChannel mClientChannel;
125 InputApplicationHandle mDragApplicationHandle;
126 InputWindowHandle mDragWindowHandle;
127
Wale Ogunwale4f52bc62015-09-24 13:47:31 -0700128 private final class WindowPositionerEventReceiver extends BatchedInputEventReceiver {
129 public WindowPositionerEventReceiver(
130 InputChannel inputChannel, Looper looper, Choreographer choreographer) {
131 super(inputChannel, looper, choreographer);
Chong Zhang8e89b312015-09-09 15:09:30 -0700132 }
133
134 @Override
Siarhei Vishniakou85ddfff2018-01-31 16:49:36 -0800135 public void onInputEvent(InputEvent event) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700136 boolean handled = false;
Chong Zhang8e89b312015-09-09 15:09:30 -0700137 try {
Yunfan Chen54872c32019-11-14 19:41:47 -0800138 // All returns need to be in the try block to make sure the finishInputEvent is
139 // called correctly.
140 if (!(event instanceof MotionEvent)
141 || (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) == 0) {
142 return;
143 }
144 final MotionEvent motionEvent = (MotionEvent) event;
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700145 if (mDragEnded) {
146 // The drag has ended but the clean-up message has not been processed by
147 // window manager. Drop events that occur after this until window manager
148 // has a chance to clean-up the input handle.
149 handled = true;
150 return;
151 }
152
Chong Zhang8e89b312015-09-09 15:09:30 -0700153 final float newX = motionEvent.getRawX();
154 final float newY = motionEvent.getRawY();
155
156 switch (motionEvent.getAction()) {
157 case MotionEvent.ACTION_DOWN: {
158 if (DEBUG_TASK_POSITIONING) {
159 Slog.w(TAG, "ACTION_DOWN @ {" + newX + ", " + newY + "}");
160 }
161 } break;
162
163 case MotionEvent.ACTION_MOVE: {
164 if (DEBUG_TASK_POSITIONING){
165 Slog.w(TAG, "ACTION_MOVE @ {" + newX + ", " + newY + "}");
166 }
Wale Ogunwaledb485de2018-10-29 09:47:07 -0700167 synchronized (mService.mGlobalLock) {
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700168 mDragEnded = notifyMoveLocked(newX, newY);
Chong Zhang4c9ba52a2015-11-10 18:36:33 -0800169 mTask.getDimBounds(mTmpRect);
Chong Zhang8e89b312015-09-09 15:09:30 -0700170 }
Wale Ogunwale04ad7b12015-10-02 12:43:27 -0700171 if (!mTmpRect.equals(mWindowDragBounds)) {
172 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
173 "wm.TaskPositioner.resizeTask");
174 try {
Daichi Hirono67f2f4b2018-05-25 16:07:30 +0900175 mActivityManager.resizeTask(
Wale Ogunwale04ad7b12015-10-02 12:43:27 -0700176 mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER);
177 } catch (RemoteException e) {
178 }
179 Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
180 }
Chong Zhang8e89b312015-09-09 15:09:30 -0700181 } break;
182
183 case MotionEvent.ACTION_UP: {
184 if (DEBUG_TASK_POSITIONING) {
185 Slog.w(TAG, "ACTION_UP @ {" + newX + ", " + newY + "}");
186 }
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700187 mDragEnded = true;
Chong Zhang8e89b312015-09-09 15:09:30 -0700188 } break;
189
190 case MotionEvent.ACTION_CANCEL: {
191 if (DEBUG_TASK_POSITIONING) {
192 Slog.w(TAG, "ACTION_CANCEL @ {" + newX + ", " + newY + "}");
193 }
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700194 mDragEnded = true;
Chong Zhang8e89b312015-09-09 15:09:30 -0700195 } break;
196 }
197
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700198 if (mDragEnded) {
Wale Ogunwale04ad7b12015-10-02 12:43:27 -0700199 final boolean wasResizing = mResizing;
Wale Ogunwaledb485de2018-10-29 09:47:07 -0700200 synchronized (mService.mGlobalLock) {
Chong Zhang3005e752015-09-18 18:46:28 -0700201 endDragLocked();
Vladislav Kaznacheev824bc5d2016-04-22 17:48:35 -0700202 mTask.getDimBounds(mTmpRect);
Chong Zhang3005e752015-09-18 18:46:28 -0700203 }
Chong Zhang09b21ef2015-09-14 10:20:21 -0700204 try {
Vladislav Kaznacheev824bc5d2016-04-22 17:48:35 -0700205 if (wasResizing && !mTmpRect.equals(mWindowDragBounds)) {
Chong Zhang3005e752015-09-18 18:46:28 -0700206 // We were using fullscreen surface during resizing. Request
207 // resizeTask() one last time to restore surface to window size.
Daichi Hirono67f2f4b2018-05-25 16:07:30 +0900208 mActivityManager.resizeTask(
Chong Zhang6de2ae82015-09-30 18:25:21 -0700209 mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER_FORCED);
Chong Zhang3005e752015-09-18 18:46:28 -0700210 }
Chong Zhang09b21ef2015-09-14 10:20:21 -0700211 } catch(RemoteException e) {}
Chong Zhang3005e752015-09-18 18:46:28 -0700212
Chong Zhang8e89b312015-09-09 15:09:30 -0700213 // Post back to WM to handle clean-ups. We still need the input
214 // event handler for the last finishInputEvent()!
Daichi Hironoce2f97a2017-11-30 16:44:15 +0900215 mService.mTaskPositioningController.finishTaskPositioning();
Chong Zhang8e89b312015-09-09 15:09:30 -0700216 }
217 handled = true;
218 } catch (Exception e) {
219 Slog.e(TAG, "Exception caught by drag handleMotion", e);
220 } finally {
221 finishInputEvent(event, handled);
222 }
223 }
224 }
225
Daichi Hirono67f2f4b2018-05-25 16:07:30 +0900226 @VisibleForTesting
Wale Ogunwale04d9cb52018-04-30 13:55:07 -0700227 TaskPositioner(WindowManagerService service, IActivityTaskManager activityManager) {
Daichi Hirono67f2f4b2018-05-25 16:07:30 +0900228 mService = service;
229 mActivityManager = activityManager;
230 }
231
Garfield Tan6caf1d8c2018-01-18 12:37:50 -0800232 /** Use {@link #create(WindowManagerService)} instead **/
Chong Zhang8e89b312015-09-09 15:09:30 -0700233 TaskPositioner(WindowManagerService service) {
Wale Ogunwale04d9cb52018-04-30 13:55:07 -0700234 this(service, service.mActivityTaskManager);
Chong Zhang8e89b312015-09-09 15:09:30 -0700235 }
236
skuhne@google.com322347b2016-12-02 12:54:03 -0800237 @VisibleForTesting
238 Rect getWindowDragBounds() {
239 return mWindowDragBounds;
240 }
241
Chong Zhang8e89b312015-09-09 15:09:30 -0700242 /**
Garfield Tan07544cd2018-09-12 16:16:54 -0700243 * @param displayContent The Display that the window being dragged is on.
Yongjin Kwon9c5f0aa2019-11-06 17:36:24 +0900244 * @param win The window which will be dragged.
Chong Zhang8e89b312015-09-09 15:09:30 -0700245 */
Yongjin Kwon9c5f0aa2019-11-06 17:36:24 +0900246 void register(DisplayContent displayContent, @NonNull WindowState win) {
Robert Carrb1579c82017-09-05 14:54:47 -0700247 final Display display = displayContent.getDisplay();
248
Chong Zhang8e89b312015-09-09 15:09:30 -0700249 if (DEBUG_TASK_POSITIONING) {
250 Slog.d(TAG, "Registering task positioner");
251 }
252
253 if (mClientChannel != null) {
254 Slog.e(TAG, "Task positioner already registered");
255 return;
256 }
257
Riddle Hsu654a6f92018-07-13 22:59:36 +0800258 mDisplayContent = displayContent;
259 display.getMetrics(mDisplayMetrics);
Chong Zhang8e89b312015-09-09 15:09:30 -0700260 final InputChannel[] channels = InputChannel.openInputChannelPair(TAG);
261 mServerChannel = channels[0];
262 mClientChannel = channels[1];
Vishnu Nair18782162019-10-08 14:57:16 -0700263 mService.mInputManager.registerInputChannel(mServerChannel);
Chong Zhang8e89b312015-09-09 15:09:30 -0700264
Wale Ogunwale4f52bc62015-09-24 13:47:31 -0700265 mInputEventReceiver = new WindowPositionerEventReceiver(
Jorim Jaggied7993b2017-03-28 18:50:01 +0100266 mClientChannel, mService.mAnimationHandler.getLooper(),
267 mService.mAnimator.getChoreographer());
Chong Zhang8e89b312015-09-09 15:09:30 -0700268
Robert Carr0bcbe642018-10-11 19:07:43 -0700269 mDragApplicationHandle = new InputApplicationHandle(new Binder());
Chong Zhang8e89b312015-09-09 15:09:30 -0700270 mDragApplicationHandle.name = TAG;
271 mDragApplicationHandle.dispatchingTimeoutNanos =
272 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
273
Vishnu Nair18782162019-10-08 14:57:16 -0700274 mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, display.getDisplayId());
Chong Zhang8e89b312015-09-09 15:09:30 -0700275 mDragWindowHandle.name = TAG;
Robert Carreadae822018-10-11 19:07:03 -0700276 mDragWindowHandle.token = mServerChannel.getToken();
Filip Gruszczynski57b6cce2015-10-06 09:50:51 -0700277 mDragWindowHandle.layer = mService.getDragLayerLocked();
Chong Zhang8e89b312015-09-09 15:09:30 -0700278 mDragWindowHandle.layoutParamsFlags = 0;
279 mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
280 mDragWindowHandle.dispatchingTimeoutNanos =
281 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
282 mDragWindowHandle.visible = true;
283 mDragWindowHandle.canReceiveKeys = false;
284 mDragWindowHandle.hasFocus = true;
285 mDragWindowHandle.hasWallpaper = false;
286 mDragWindowHandle.paused = false;
287 mDragWindowHandle.ownerPid = Process.myPid();
288 mDragWindowHandle.ownerUid = Process.myUid();
289 mDragWindowHandle.inputFeatures = 0;
290 mDragWindowHandle.scaleFactor = 1.0f;
291
292 // The drag window cannot receive new touches.
293 mDragWindowHandle.touchableRegion.setEmpty();
294
295 // The drag window covers the entire display
296 mDragWindowHandle.frameLeft = 0;
297 mDragWindowHandle.frameTop = 0;
298 final Point p = new Point();
Riddle Hsu654a6f92018-07-13 22:59:36 +0800299 display.getRealSize(p);
Chong Zhang8e89b312015-09-09 15:09:30 -0700300 mDragWindowHandle.frameRight = p.x;
301 mDragWindowHandle.frameBottom = p.y;
302
303 // Pause rotations before a drag.
Adrian Roosb125e0b2019-10-02 14:55:14 +0200304 ProtoLog.d(WM_DEBUG_ORIENTATION, "Pausing rotation during re-position");
Riddle Hsuccf09402019-08-13 00:33:06 +0800305 mDisplayContent.getDisplayRotation().pause();
Wale Ogunwale228d4042015-09-13 10:17:34 -0700306
Garfield Tan07544cd2018-09-12 16:16:54 -0700307 // Notify InputMonitor to take mDragWindowHandle.
Arthur Hungb62e5002019-08-29 12:28:07 +0800308 mDisplayContent.getInputMonitor().updateInputWindowsImmediately();
309 new SurfaceControl.Transaction().syncInputWindows().apply(true);
Garfield Tan07544cd2018-09-12 16:16:54 -0700310
Filip Gruszczynski57b6cce2015-10-06 09:50:51 -0700311 mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics);
312 mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics);
Riddle Hsu654a6f92018-07-13 22:59:36 +0800313 display.getRealSize(mMaxVisibleSize);
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700314
315 mDragEnded = false;
Yongjin Kwon9c5f0aa2019-11-06 17:36:24 +0900316
317 try {
318 mClientCallback = win.mClient.asBinder();
319 mClientCallback.linkToDeath(this, 0 /* flags */);
320 } catch (RemoteException e) {
321 // The caller has died, so clean up TaskPositioningController.
322 mService.mTaskPositioningController.finishTaskPositioning();
323 return;
324 }
325 mWindow = win;
326 mTask = win.getTask();
Chong Zhang8e89b312015-09-09 15:09:30 -0700327 }
328
329 void unregister() {
330 if (DEBUG_TASK_POSITIONING) {
331 Slog.d(TAG, "Unregistering task positioner");
332 }
333
334 if (mClientChannel == null) {
335 Slog.e(TAG, "Task positioner not registered");
336 return;
337 }
338
339 mService.mInputManager.unregisterInputChannel(mServerChannel);
340
341 mInputEventReceiver.dispose();
342 mInputEventReceiver = null;
343 mClientChannel.dispose();
344 mServerChannel.dispose();
345 mClientChannel = null;
346 mServerChannel = null;
347
348 mDragWindowHandle = null;
349 mDragApplicationHandle = null;
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700350 mDragEnded = true;
Wale Ogunwale228d4042015-09-13 10:17:34 -0700351
Garfield Tan07544cd2018-09-12 16:16:54 -0700352 // Notify InputMonitor to remove mDragWindowHandle.
353 mDisplayContent.getInputMonitor().updateInputWindowsLw(true /*force*/);
354
Chong Zhang8e89b312015-09-09 15:09:30 -0700355 // Resume rotations after a drag.
Adrian Roosb125e0b2019-10-02 14:55:14 +0200356 ProtoLog.d(WM_DEBUG_ORIENTATION, "Resuming rotation after re-position");
Riddle Hsuccf09402019-08-13 00:33:06 +0800357 mDisplayContent.getDisplayRotation().resume();
Riddle Hsu654a6f92018-07-13 22:59:36 +0800358 mDisplayContent = null;
Yongjin Kwon9c5f0aa2019-11-06 17:36:24 +0900359 if (mClientCallback != null) {
360 mClientCallback.unlinkToDeath(this, 0 /* flags */);
361 }
362 mWindow = null;
Chong Zhang8e89b312015-09-09 15:09:30 -0700363 }
364
Yongjin Kwon9c5f0aa2019-11-06 17:36:24 +0900365 void startDrag(boolean resize, boolean preserveOrientation, float startX,
366 float startY) {
Wale Ogunwale228d4042015-09-13 10:17:34 -0700367 if (DEBUG_TASK_POSITIONING) {
Yongjin Kwon9c5f0aa2019-11-06 17:36:24 +0900368 Slog.d(TAG, "startDrag: win=" + mWindow + ", resize=" + resize
skuhne@google.com322347b2016-12-02 12:54:03 -0800369 + ", preserveOrientation=" + preserveOrientation + ", {" + startX + ", "
370 + startY + "}");
Chong Zhang8e89b312015-09-09 15:09:30 -0700371 }
Evan Rosky0d654cb2019-02-26 10:59:10 -0800372 // Use the bounds of the task which accounts for
Chong Zhang2e2c81a2016-07-15 11:28:17 -0700373 // multiple app windows. Don't use any bounds from win itself as it
374 // may not be the same size as the task.
Evan Rosky0d654cb2019-02-26 10:59:10 -0800375 mTask.getBounds(mTmpRect);
skuhne@google.com322347b2016-12-02 12:54:03 -0800376 startDrag(resize, preserveOrientation, startX, startY, mTmpRect);
377 }
378
Daichi Hirono43094a12018-05-31 12:32:23 +0900379 protected void startDrag(boolean resize, boolean preserveOrientation,
skuhne@google.com322347b2016-12-02 12:54:03 -0800380 float startX, float startY, Rect startBounds) {
381 mCtrlType = CTRL_NONE;
382 mStartDragX = startX;
383 mStartDragY = startY;
384 mPreserveOrientation = preserveOrientation;
Chong Zhangd8ceb852015-11-11 14:53:41 -0800385
Chong Zhang8e89b312015-09-09 15:09:30 -0700386 if (resize) {
skuhne@google.com322347b2016-12-02 12:54:03 -0800387 if (startX < startBounds.left) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700388 mCtrlType |= CTRL_LEFT;
389 }
skuhne@google.com322347b2016-12-02 12:54:03 -0800390 if (startX > startBounds.right) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700391 mCtrlType |= CTRL_RIGHT;
392 }
skuhne@google.com322347b2016-12-02 12:54:03 -0800393 if (startY < startBounds.top) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700394 mCtrlType |= CTRL_TOP;
395 }
skuhne@google.com322347b2016-12-02 12:54:03 -0800396 if (startY > startBounds.bottom) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700397 mCtrlType |= CTRL_BOTTOM;
398 }
skuhne@google.com322347b2016-12-02 12:54:03 -0800399 mResizing = mCtrlType != CTRL_NONE;
Chong Zhang8e89b312015-09-09 15:09:30 -0700400 }
401
skuhne@google.com322347b2016-12-02 12:54:03 -0800402 // In case of !isDockedInEffect we are using the union of all task bounds. These might be
403 // made up out of multiple windows which are only partially overlapping. When that happens,
404 // the orientation from the window of interest to the entire stack might diverge. However
405 // for now we treat them as the same.
406 mStartOrientationWasLandscape = startBounds.width() >= startBounds.height();
407 mWindowOriginalBounds.set(startBounds);
Vladislav Kaznacheev824bc5d2016-04-22 17:48:35 -0700408
Tomasz Mikolajewski79e8d172017-11-21 11:21:42 +0900409 // Notify the app that resizing has started, even though we haven't received any new
410 // bounds yet. This will guarantee that the app starts the backdrop renderer before
411 // configuration changes which could cause an activity restart.
412 if (mResizing) {
Wale Ogunwaledb485de2018-10-29 09:47:07 -0700413 synchronized (mService.mGlobalLock) {
Tomasz Mikolajewski79e8d172017-11-21 11:21:42 +0900414 notifyMoveLocked(startX, startY);
415 }
416
417 // Perform the resize on the WMS handler thread when we don't have the WMS lock held
418 // to ensure that we don't deadlock WMS and AMS. Note that WindowPositionerEventReceiver
419 // callbacks are delivered on the same handler so this initial resize is always
420 // guaranteed to happen before subsequent drag resizes.
421 mService.mH.post(() -> {
422 try {
Daichi Hirono67f2f4b2018-05-25 16:07:30 +0900423 mActivityManager.resizeTask(
Tomasz Mikolajewski79e8d172017-11-21 11:21:42 +0900424 mTask.mTaskId, startBounds, RESIZE_MODE_USER_FORCED);
425 } catch (RemoteException e) {
426 }
427 });
428 }
429
Vladislav Kaznacheev824bc5d2016-04-22 17:48:35 -0700430 // Make sure we always have valid drag bounds even if the drag ends before any move events
431 // have been handled.
skuhne@google.com322347b2016-12-02 12:54:03 -0800432 mWindowDragBounds.set(startBounds);
Chong Zhang3005e752015-09-18 18:46:28 -0700433 }
434
435 private void endDragLocked() {
436 mResizing = false;
Jorim Jaggi0b46f3c2016-03-14 12:21:37 +0100437 mTask.setDragResizing(false, DRAG_RESIZE_MODE_FREEFORM);
Chong Zhang8e89b312015-09-09 15:09:30 -0700438 }
439
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700440 /** Returns true if the move operation should be ended. */
441 private boolean notifyMoveLocked(float x, float y) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700442 if (DEBUG_TASK_POSITIONING) {
Chong Zhangb15758a2015-11-17 12:12:03 -0800443 Slog.d(TAG, "notifyMoveLocked: {" + x + "," + y + "}");
Chong Zhang8e89b312015-09-09 15:09:30 -0700444 }
445
446 if (mCtrlType != CTRL_NONE) {
skuhne@google.com322347b2016-12-02 12:54:03 -0800447 resizeDrag(x, y);
Jorim Jaggi0b46f3c2016-03-14 12:21:37 +0100448 mTask.setDragResizing(true, DRAG_RESIZE_MODE_FREEFORM);
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700449 return false;
Chong Zhang8e89b312015-09-09 15:09:30 -0700450 }
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700451
Vladislav Kaznacheeva90a3b82016-04-27 14:54:53 -0700452 // This is a moving or scrolling operation.
Wale Ogunwale8577a052019-10-26 23:22:34 -0700453 mTask.getTaskStack().getDimBounds(mTmpRect);
GyeHun Jeon84b30d22019-04-24 14:20:15 -0700454 // If a target window is covered by system bar, there is no way to move it again by touch.
455 // So we exclude them from stack bounds. and then it will be shown inside stable area.
456 Rect stableBounds = new Rect();
457 mDisplayContent.getStableRect(stableBounds);
458 mTmpRect.intersect(stableBounds);
Chong Zhangb15758a2015-11-17 12:12:03 -0800459
Vladislav Kaznacheeva90a3b82016-04-27 14:54:53 -0700460 int nX = (int) x;
461 int nY = (int) y;
Chong Zhangb15758a2015-11-17 12:12:03 -0800462 if (!mTmpRect.contains(nX, nY)) {
Chong Zhang2e2c81a2016-07-15 11:28:17 -0700463 // For a moving operation we allow the pointer to go out of the stack bounds, but
464 // use the clamped pointer position for the drag bounds computation.
465 nX = Math.min(Math.max(nX, mTmpRect.left), mTmpRect.right);
466 nY = Math.min(Math.max(nY, mTmpRect.top), mTmpRect.bottom);
Chong Zhangb15758a2015-11-17 12:12:03 -0800467 }
468
Vladislav Kaznacheeva90a3b82016-04-27 14:54:53 -0700469 updateWindowDragBounds(nX, nY, mTmpRect);
Chong Zhang2e2c81a2016-07-15 11:28:17 -0700470 return false;
Chong Zhangb15758a2015-11-17 12:12:03 -0800471 }
472
skuhne@google.com322347b2016-12-02 12:54:03 -0800473 /**
474 * The user is drag - resizing the window.
475 *
476 * @param x The x coordinate of the current drag coordinate.
477 * @param y the y coordinate of the current drag coordinate.
478 */
479 @VisibleForTesting
480 void resizeDrag(float x, float y) {
481 // This is a resizing operation.
482 // We need to keep various constraints:
483 // 1. mMinVisible[Width/Height] <= [width/height] <= mMaxVisibleSize.[x/y]
484 // 2. The orientation is kept - if required.
485 final int deltaX = Math.round(x - mStartDragX);
486 final int deltaY = Math.round(y - mStartDragY);
487 int left = mWindowOriginalBounds.left;
488 int top = mWindowOriginalBounds.top;
489 int right = mWindowOriginalBounds.right;
490 int bottom = mWindowOriginalBounds.bottom;
491
skuhne@google.com322347b2016-12-02 12:54:03 -0800492 // Calculate the resulting width and height of the drag operation.
493 int width = right - left;
494 int height = bottom - top;
495 if ((mCtrlType & CTRL_LEFT) != 0) {
496 width = Math.max(mMinVisibleWidth, width - deltaX);
497 } else if ((mCtrlType & CTRL_RIGHT) != 0) {
498 width = Math.max(mMinVisibleWidth, width + deltaX);
499 }
500 if ((mCtrlType & CTRL_TOP) != 0) {
501 height = Math.max(mMinVisibleHeight, height - deltaY);
502 } else if ((mCtrlType & CTRL_BOTTOM) != 0) {
503 height = Math.max(mMinVisibleHeight, height + deltaY);
504 }
505
506 // If we have to preserve the orientation - check that we are doing so.
507 final float aspect = (float) width / (float) height;
508 if (mPreserveOrientation && ((mStartOrientationWasLandscape && aspect < MIN_ASPECT)
509 || (!mStartOrientationWasLandscape && aspect > (1.0 / MIN_ASPECT)))) {
510 // Calculate 2 rectangles fulfilling all requirements for either X or Y being the major
511 // drag axis. What ever is producing the bigger rectangle will be chosen.
512 int width1;
513 int width2;
514 int height1;
515 int height2;
516 if (mStartOrientationWasLandscape) {
517 // Assuming that the width is our target we calculate the height.
518 width1 = Math.max(mMinVisibleWidth, Math.min(mMaxVisibleSize.x, width));
519 height1 = Math.min(height, Math.round((float)width1 / MIN_ASPECT));
520 if (height1 < mMinVisibleHeight) {
521 // If the resulting height is too small we adjust to the minimal size.
522 height1 = mMinVisibleHeight;
523 width1 = Math.max(mMinVisibleWidth,
524 Math.min(mMaxVisibleSize.x, Math.round((float)height1 * MIN_ASPECT)));
525 }
526 // Assuming that the height is our target we calculate the width.
527 height2 = Math.max(mMinVisibleHeight, Math.min(mMaxVisibleSize.y, height));
528 width2 = Math.max(width, Math.round((float)height2 * MIN_ASPECT));
529 if (width2 < mMinVisibleWidth) {
530 // If the resulting width is too small we adjust to the minimal size.
531 width2 = mMinVisibleWidth;
532 height2 = Math.max(mMinVisibleHeight,
533 Math.min(mMaxVisibleSize.y, Math.round((float)width2 / MIN_ASPECT)));
534 }
535 } else {
536 // Assuming that the width is our target we calculate the height.
537 width1 = Math.max(mMinVisibleWidth, Math.min(mMaxVisibleSize.x, width));
538 height1 = Math.max(height, Math.round((float)width1 * MIN_ASPECT));
539 if (height1 < mMinVisibleHeight) {
540 // If the resulting height is too small we adjust to the minimal size.
541 height1 = mMinVisibleHeight;
542 width1 = Math.max(mMinVisibleWidth,
543 Math.min(mMaxVisibleSize.x, Math.round((float)height1 / MIN_ASPECT)));
544 }
545 // Assuming that the height is our target we calculate the width.
546 height2 = Math.max(mMinVisibleHeight, Math.min(mMaxVisibleSize.y, height));
547 width2 = Math.min(width, Math.round((float)height2 / MIN_ASPECT));
548 if (width2 < mMinVisibleWidth) {
549 // If the resulting width is too small we adjust to the minimal size.
550 width2 = mMinVisibleWidth;
551 height2 = Math.max(mMinVisibleHeight,
552 Math.min(mMaxVisibleSize.y, Math.round((float)width2 * MIN_ASPECT)));
553 }
554 }
555
556 // Use the bigger of the two rectangles if the major change was positive, otherwise
557 // do the opposite.
558 final boolean grows = width > (right - left) || height > (bottom - top);
559 if (grows == (width1 * height1 > width2 * height2)) {
560 width = width1;
561 height = height1;
562 } else {
563 width = width2;
564 height = height2;
565 }
566 }
567
568 // Update mWindowDragBounds to the new drag size.
569 updateDraggedBounds(left, top, right, bottom, width, height);
570 }
571
572 /**
573 * Given the old coordinates and the new width and height, update the mWindowDragBounds.
574 *
575 * @param left The original left bound before the user started dragging.
576 * @param top The original top bound before the user started dragging.
577 * @param right The original right bound before the user started dragging.
578 * @param bottom The original bottom bound before the user started dragging.
579 * @param newWidth The new dragged width.
580 * @param newHeight The new dragged height.
581 */
582 void updateDraggedBounds(int left, int top, int right, int bottom, int newWidth,
583 int newHeight) {
584 // Generate the final bounds by keeping the opposite drag edge constant.
585 if ((mCtrlType & CTRL_LEFT) != 0) {
586 left = right - newWidth;
587 } else { // Note: The right might have changed - if we pulled at the right or not.
588 right = left + newWidth;
589 }
590 if ((mCtrlType & CTRL_TOP) != 0) {
591 top = bottom - newHeight;
592 } else { // Note: The height might have changed - if we pulled at the bottom or not.
593 bottom = top + newHeight;
594 }
595
596 mWindowDragBounds.set(left, top, right, bottom);
597
598 checkBoundsForOrientationViolations(mWindowDragBounds);
599 }
600
601 /**
602 * Validate bounds against orientation violations (if DEBUG_ORIENTATION_VIOLATIONS is set).
603 *
604 * @param bounds The bounds to be checked.
605 */
606 private void checkBoundsForOrientationViolations(Rect bounds) {
607 // When using debug check that we are not violating the given constraints.
608 if (DEBUG_ORIENTATION_VIOLATIONS) {
609 if (mStartOrientationWasLandscape != (bounds.width() >= bounds.height())) {
610 Slog.e(TAG, "Orientation violation detected! should be "
611 + (mStartOrientationWasLandscape ? "landscape" : "portrait")
612 + " but is the other");
613 } else {
614 Slog.v(TAG, "new bounds size: " + bounds.width() + " x " + bounds.height());
615 }
616 if (mMinVisibleWidth > bounds.width() || mMinVisibleHeight > bounds.height()) {
617 Slog.v(TAG, "Minimum requirement violated: Width(min, is)=(" + mMinVisibleWidth
618 + ", " + bounds.width() + ") Height(min,is)=("
619 + mMinVisibleHeight + ", " + bounds.height() + ")");
620 }
621 if (mMaxVisibleSize.x < bounds.width() || mMaxVisibleSize.y < bounds.height()) {
622 Slog.v(TAG, "Maximum requirement violated: Width(min, is)=(" + mMaxVisibleSize.x
623 + ", " + bounds.width() + ") Height(min,is)=("
624 + mMaxVisibleSize.y + ", " + bounds.height() + ")");
625 }
626 }
627 }
628
Vladislav Kaznacheeva90a3b82016-04-27 14:54:53 -0700629 private void updateWindowDragBounds(int x, int y, Rect stackBounds) {
630 final int offsetX = Math.round(x - mStartDragX);
631 final int offsetY = Math.round(y - mStartDragY);
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700632 mWindowDragBounds.set(mWindowOriginalBounds);
Chong Zhang2e2c81a2016-07-15 11:28:17 -0700633 // Horizontally, at least mMinVisibleWidth pixels of the window should remain visible.
634 final int maxLeft = stackBounds.right - mMinVisibleWidth;
635 final int minLeft = stackBounds.left + mMinVisibleWidth - mWindowOriginalBounds.width();
Vladislav Kaznacheeva90a3b82016-04-27 14:54:53 -0700636
Chong Zhang2e2c81a2016-07-15 11:28:17 -0700637 // Vertically, the top mMinVisibleHeight of the window should remain visible.
638 // (This assumes that the window caption bar is at the top of the window).
639 final int minTop = stackBounds.top;
640 final int maxTop = stackBounds.bottom - mMinVisibleHeight;
Vladislav Kaznacheeva90a3b82016-04-27 14:54:53 -0700641
Chong Zhang2e2c81a2016-07-15 11:28:17 -0700642 mWindowDragBounds.offsetTo(
643 Math.min(Math.max(mWindowOriginalBounds.left + offsetX, minLeft), maxLeft),
644 Math.min(Math.max(mWindowOriginalBounds.top + offsetY, minTop), maxTop));
645
Chong Zhangb15758a2015-11-17 12:12:03 -0800646 if (DEBUG_TASK_POSITIONING) Slog.d(TAG,
647 "updateWindowDragBounds: " + mWindowDragBounds);
Chong Zhang8e89b312015-09-09 15:09:30 -0700648 }
649
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700650 public String toShortString() {
651 return TAG;
652 }
Garfield Tan6caf1d8c2018-01-18 12:37:50 -0800653
654 static void setFactory(Factory factory) {
655 sFactory = factory;
656 }
657
658 static TaskPositioner create(WindowManagerService service) {
659 if (sFactory == null) {
660 sFactory = new Factory() {};
661 }
662
663 return sFactory.create(service);
664 }
665
yj81.kwon79c97672019-04-16 19:44:48 -0700666 @Override
667 public void binderDied() {
668 mService.mTaskPositioningController.finishTaskPositioning();
669 }
670
Garfield Tan6caf1d8c2018-01-18 12:37:50 -0800671 interface Factory {
672 default TaskPositioner create(WindowManagerService service) {
673 return new TaskPositioner(service);
674 }
675 }
Wale Ogunwale228d4042015-09-13 10:17:34 -0700676}