blob: 864af8c60b327ed091cd5f912f2d4080e47233aa [file] [log] [blame]
Andrii Kuliand3134692017-06-26 14:57:02 -07001/**
2 * Copyright (c) 2017 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 android.app;
18
chaviwda4c6942018-11-07 15:52:56 -080019import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL;
20import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
21import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
Mady Mellor390bff42019-04-05 15:09:01 -070022import static android.view.Display.INVALID_DISPLAY;
chaviwda4c6942018-11-07 15:52:56 -080023
Andrii Kuliand3134692017-06-26 14:57:02 -070024import android.annotation.NonNull;
Wale Ogunwale691af682019-02-11 03:09:10 -080025import android.annotation.TestApi;
Andrii Kuliancf8f6832018-01-23 19:43:30 -080026import android.app.ActivityManager.StackInfo;
Mark Renouf7b71a5d2019-01-23 17:12:34 -050027import android.content.ComponentName;
Andrii Kuliand3134692017-06-26 14:57:02 -070028import android.content.Context;
29import android.content.Intent;
Issei Suzukia5dbf522019-02-01 17:58:15 +010030import android.graphics.Insets;
Yohei Yukawab4f328a2019-05-02 08:41:27 -070031import android.graphics.Matrix;
Tiger Huang2b210c22019-03-18 21:21:26 +080032import android.graphics.Region;
Andrii Kuliand3134692017-06-26 14:57:02 -070033import android.hardware.display.DisplayManager;
34import android.hardware.display.VirtualDisplay;
Mark Renouf041d7262019-02-06 12:09:41 -050035import android.hardware.input.InputManager;
Andrii Kuliand3134692017-06-26 14:57:02 -070036import android.os.RemoteException;
Mark Renouf041d7262019-02-06 12:09:41 -050037import android.os.SystemClock;
Brad Stenninga1dbe9c2018-05-02 08:29:28 -070038import android.os.UserHandle;
Andrii Kuliand3134692017-06-26 14:57:02 -070039import android.util.AttributeSet;
40import android.util.DisplayMetrics;
41import android.util.Log;
Andrii Kulianf0379de2018-03-14 16:24:07 -070042import android.view.IWindowManager;
Mark Renouf041d7262019-02-06 12:09:41 -050043import android.view.InputDevice;
44import android.view.KeyCharacterMap;
45import android.view.KeyEvent;
chaviwff2e7d82018-11-02 11:11:27 -070046import android.view.SurfaceControl;
Andrii Kuliand3134692017-06-26 14:57:02 -070047import android.view.SurfaceHolder;
chaviwff2e7d82018-11-02 11:11:27 -070048import android.view.SurfaceSession;
Andrii Kuliand3134692017-06-26 14:57:02 -070049import android.view.SurfaceView;
chaviwd44449d2019-01-02 16:33:12 -080050import android.view.View;
Andrii Kuliand3134692017-06-26 14:57:02 -070051import android.view.ViewGroup;
Tiger Huang2b210c22019-03-18 21:21:26 +080052import android.view.ViewParent;
Andrii Kuliand3134692017-06-26 14:57:02 -070053import android.view.WindowManager;
Andrii Kulian4b6599e2018-01-15 17:24:08 -080054import android.view.WindowManagerGlobal;
Yohei Yukawab4f328a2019-05-02 08:41:27 -070055import android.view.inputmethod.InputMethodManager;
Andrii Kuliand3134692017-06-26 14:57:02 -070056
57import dalvik.system.CloseGuard;
58
Andrii Kuliancf8f6832018-01-23 19:43:30 -080059import java.util.List;
60
Andrii Kuliand3134692017-06-26 14:57:02 -070061/**
Tiger Huang04dc4cc2019-01-17 18:41:41 +080062 * Activity container that allows launching activities into itself.
Andrii Kuliand3134692017-06-26 14:57:02 -070063 * <p>Activity launching into this container is restricted by the same rules that apply to launching
64 * on VirtualDisplays.
65 * @hide
66 */
Wale Ogunwale691af682019-02-11 03:09:10 -080067@TestApi
Andrii Kuliand3134692017-06-26 14:57:02 -070068public class ActivityView extends ViewGroup {
69
70 private static final String DISPLAY_NAME = "ActivityViewVirtualDisplay";
71 private static final String TAG = "ActivityView";
72
73 private VirtualDisplay mVirtualDisplay;
74 private final SurfaceView mSurfaceView;
chaviwff2e7d82018-11-02 11:11:27 -070075
76 /**
77 * This is the root surface for the VirtualDisplay. The VirtualDisplay child surfaces will be
78 * re-parented to this surface. This will also be a child of the SurfaceView's SurfaceControl.
79 */
80 private SurfaceControl mRootSurfaceControl;
Andrii Kuliand3134692017-06-26 14:57:02 -070081
82 private final SurfaceCallback mSurfaceCallback;
83 private StateCallback mActivityViewCallback;
84
Wale Ogunwale04d9cb52018-04-30 13:55:07 -070085 private IActivityTaskManager mActivityTaskManager;
Tiger Huang04dc4cc2019-01-17 18:41:41 +080086 // Temp container to store view coordinates in window.
87 private final int[] mLocationInWindow = new int[2];
Andrii Kuliand3134692017-06-26 14:57:02 -070088
Tiger Huang2b210c22019-03-18 21:21:26 +080089 // The latest tap exclude region that we've sent to WM.
90 private final Region mTapExcludeRegion = new Region();
91
Andrii Kuliancf8f6832018-01-23 19:43:30 -080092 private TaskStackListener mTaskStackListener;
93
Andrii Kuliand3134692017-06-26 14:57:02 -070094 private final CloseGuard mGuard = CloseGuard.get();
95 private boolean mOpened; // Protected by mGuard.
96
chaviwff2e7d82018-11-02 11:11:27 -070097 private final SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction();
chaviwff2e7d82018-11-02 11:11:27 -070098
Wale Ogunwale9e737db2018-12-17 15:42:37 -080099 /** The ActivityView is only allowed to contain one task. */
100 private final boolean mSingleTaskInstance;
101
Issei Suzukia5dbf522019-02-01 17:58:15 +0100102 private Insets mForwardedInsets;
103
Andrii Kuliand3134692017-06-26 14:57:02 -0700104 public ActivityView(Context context) {
105 this(context, null /* attrs */);
106 }
107
108 public ActivityView(Context context, AttributeSet attrs) {
109 this(context, attrs, 0 /* defStyle */);
110 }
111
112 public ActivityView(Context context, AttributeSet attrs, int defStyle) {
Wale Ogunwale9e737db2018-12-17 15:42:37 -0800113 this(context, attrs, defStyle, false /*singleTaskInstance*/);
114 }
115
116 public ActivityView(
117 Context context, AttributeSet attrs, int defStyle, boolean singleTaskInstance) {
Andrii Kuliand3134692017-06-26 14:57:02 -0700118 super(context, attrs, defStyle);
Wale Ogunwale9e737db2018-12-17 15:42:37 -0800119 mSingleTaskInstance = singleTaskInstance;
Andrii Kuliand3134692017-06-26 14:57:02 -0700120
Wale Ogunwale04d9cb52018-04-30 13:55:07 -0700121 mActivityTaskManager = ActivityTaskManager.getService();
Andrii Kuliand3134692017-06-26 14:57:02 -0700122 mSurfaceView = new SurfaceView(context);
123 mSurfaceCallback = new SurfaceCallback();
124 mSurfaceView.getHolder().addCallback(mSurfaceCallback);
125 addView(mSurfaceView);
126
127 mOpened = true;
128 mGuard.open("release");
129 }
130
131 /** Callback that notifies when the container is ready or destroyed. */
132 public abstract static class StateCallback {
Mark Renouf7b71a5d2019-01-23 17:12:34 -0500133
Andrii Kuliand3134692017-06-26 14:57:02 -0700134 /**
135 * Called when the container is ready for launching activities. Calling
136 * {@link #startActivity(Intent)} prior to this callback will result in an
137 * {@link IllegalStateException}.
138 *
139 * @see #startActivity(Intent)
140 */
141 public abstract void onActivityViewReady(ActivityView view);
Mark Renouf7b71a5d2019-01-23 17:12:34 -0500142
Andrii Kuliand3134692017-06-26 14:57:02 -0700143 /**
144 * Called when the container can no longer launch activities. Calling
145 * {@link #startActivity(Intent)} after this callback will result in an
146 * {@link IllegalStateException}.
147 *
148 * @see #startActivity(Intent)
149 */
150 public abstract void onActivityViewDestroyed(ActivityView view);
Mark Renouf7b71a5d2019-01-23 17:12:34 -0500151
152 /**
153 * Called when a task is created inside the container.
154 * This is a filtered version of {@link TaskStackListener}
155 */
156 public void onTaskCreated(int taskId, ComponentName componentName) { }
157
Brad Stenninga1dbe9c2018-05-02 08:29:28 -0700158 /**
159 * Called when a task is moved to the front of the stack inside the container.
160 * This is a filtered version of {@link TaskStackListener}
161 */
Wale Ogunwale691af682019-02-11 03:09:10 -0800162 public void onTaskMovedToFront(int taskId) { }
Mark Renouf7b71a5d2019-01-23 17:12:34 -0500163
164 /**
165 * Called when a task is about to be removed from the stack inside the container.
166 * This is a filtered version of {@link TaskStackListener}
167 */
168 public void onTaskRemovalStarted(int taskId) { }
Andrii Kuliand3134692017-06-26 14:57:02 -0700169 }
170
171 /**
172 * Set the callback to be notified about state changes.
173 * <p>This class must finish initializing before {@link #startActivity(Intent)} can be called.
174 * <p>Note: If the instance was ready prior to this call being made, then
175 * {@link StateCallback#onActivityViewReady(ActivityView)} will be called from within
176 * this method call.
177 *
178 * @param callback The callback to report events to.
179 *
180 * @see StateCallback
181 * @see #startActivity(Intent)
182 */
183 public void setCallback(StateCallback callback) {
184 mActivityViewCallback = callback;
185
186 if (mVirtualDisplay != null && mActivityViewCallback != null) {
187 mActivityViewCallback.onActivityViewReady(this);
188 }
189 }
190
191 /**
192 * Launch a new activity into this container.
193 * <p>Activity resolved by the provided {@link Intent} must have
194 * {@link android.R.attr#resizeableActivity} attribute set to {@code true} in order to be
195 * launched here. Also, if activity is not owned by the owner of this container, it must allow
196 * embedding and the caller must have permission to embed.
197 * <p>Note: This class must finish initializing and
198 * {@link StateCallback#onActivityViewReady(ActivityView)} callback must be triggered before
199 * this method can be called.
200 *
201 * @param intent Intent used to launch an activity.
202 *
203 * @see StateCallback
204 * @see #startActivity(PendingIntent)
205 */
206 public void startActivity(@NonNull Intent intent) {
207 final ActivityOptions options = prepareActivityOptions();
208 getContext().startActivity(intent, options.toBundle());
209 }
210
211 /**
212 * Launch a new activity into this container.
Brad Stenninga1dbe9c2018-05-02 08:29:28 -0700213 * <p>Activity resolved by the provided {@link Intent} must have
214 * {@link android.R.attr#resizeableActivity} attribute set to {@code true} in order to be
215 * launched here. Also, if activity is not owned by the owner of this container, it must allow
216 * embedding and the caller must have permission to embed.
217 * <p>Note: This class must finish initializing and
218 * {@link StateCallback#onActivityViewReady(ActivityView)} callback must be triggered before
219 * this method can be called.
220 *
221 * @param intent Intent used to launch an activity.
222 * @param user The UserHandle of the user to start this activity for.
223 *
224 *
225 * @see StateCallback
226 * @see #startActivity(PendingIntent)
227 */
228 public void startActivity(@NonNull Intent intent, UserHandle user) {
229 final ActivityOptions options = prepareActivityOptions();
230 getContext().startActivityAsUser(intent, options.toBundle(), user);
231 }
232
233 /**
234 * Launch a new activity into this container.
Andrii Kuliand3134692017-06-26 14:57:02 -0700235 * <p>Activity resolved by the provided {@link PendingIntent} must have
236 * {@link android.R.attr#resizeableActivity} attribute set to {@code true} in order to be
237 * launched here. Also, if activity is not owned by the owner of this container, it must allow
238 * embedding and the caller must have permission to embed.
239 * <p>Note: This class must finish initializing and
240 * {@link StateCallback#onActivityViewReady(ActivityView)} callback must be triggered before
241 * this method can be called.
242 *
243 * @param pendingIntent Intent used to launch an activity.
244 *
245 * @see StateCallback
246 * @see #startActivity(Intent)
247 */
248 public void startActivity(@NonNull PendingIntent pendingIntent) {
249 final ActivityOptions options = prepareActivityOptions();
250 try {
251 pendingIntent.send(null /* context */, 0 /* code */, null /* intent */,
252 null /* onFinished */, null /* handler */, null /* requiredPermission */,
253 options.toBundle());
254 } catch (PendingIntent.CanceledException e) {
255 throw new RuntimeException(e);
256 }
257 }
258
259 /**
Mady Mellor60101c92019-04-11 19:04:00 -0700260 * Launch a new activity into this container.
261 * <p>Activity resolved by the provided {@link PendingIntent} must have
262 * {@link android.R.attr#resizeableActivity} attribute set to {@code true} in order to be
263 * launched here. Also, if activity is not owned by the owner of this container, it must allow
264 * embedding and the caller must have permission to embed.
265 * <p>Note: This class must finish initializing and
266 * {@link StateCallback#onActivityViewReady(ActivityView)} callback must be triggered before
267 * this method can be called.
268 *
269 * @param pendingIntent Intent used to launch an activity.
270 * @param options options for the activity
271 *
272 * @see StateCallback
273 * @see #startActivity(Intent)
274 */
275 public void startActivity(@NonNull PendingIntent pendingIntent,
276 @NonNull ActivityOptions options) {
277 options.setLaunchDisplayId(mVirtualDisplay.getDisplay().getDisplayId());
278 try {
279 pendingIntent.send(null /* context */, 0 /* code */, null /* intent */,
280 null /* onFinished */, null /* handler */, null /* requiredPermission */,
281 options.toBundle());
282 } catch (PendingIntent.CanceledException e) {
283 throw new RuntimeException(e);
284 }
285 }
286
287 /**
Andrii Kuliand3134692017-06-26 14:57:02 -0700288 * Check if container is ready to launch and create {@link ActivityOptions} to target the
289 * virtual display.
290 */
291 private ActivityOptions prepareActivityOptions() {
292 if (mVirtualDisplay == null) {
293 throw new IllegalStateException(
294 "Trying to start activity before ActivityView is ready.");
295 }
296 final ActivityOptions options = ActivityOptions.makeBasic();
297 options.setLaunchDisplayId(mVirtualDisplay.getDisplay().getDisplayId());
298 return options;
299 }
300
301 /**
302 * Release this container. Activity launching will no longer be permitted.
303 * <p>Note: Calling this method is allowed after
304 * {@link StateCallback#onActivityViewReady(ActivityView)} callback was triggered and before
305 * {@link StateCallback#onActivityViewDestroyed(ActivityView)}.
306 *
307 * @see StateCallback
308 */
309 public void release() {
310 if (mVirtualDisplay == null) {
311 throw new IllegalStateException(
312 "Trying to release container that is not initialized.");
313 }
314 performRelease();
315 }
316
Andrii Kulian4b6599e2018-01-15 17:24:08 -0800317 /**
Tiger Huang2b210c22019-03-18 21:21:26 +0800318 * Triggers an update of {@link ActivityView}'s location in window to properly set tap exclude
Andrii Kulian4b6599e2018-01-15 17:24:08 -0800319 * regions and avoid focus switches by touches on this view.
320 */
321 public void onLocationChanged() {
Tiger Huangd8ec9382019-04-18 14:35:09 -0700322 updateLocationAndTapExcludeRegion();
Andrii Kulian4b6599e2018-01-15 17:24:08 -0800323 }
324
Yohei Yukawab4f328a2019-05-02 08:41:27 -0700325 private void clearActivityViewGeometryForIme() {
326 if (mVirtualDisplay == null) {
327 return;
328 }
329 final int displayId = mVirtualDisplay.getDisplay().getDisplayId();
330 mContext.getSystemService(InputMethodManager.class).reportActivityView(displayId, null);
331 }
332
Andrii Kuliand3134692017-06-26 14:57:02 -0700333 @Override
334 public void onLayout(boolean changed, int l, int t, int r, int b) {
335 mSurfaceView.layout(0 /* left */, 0 /* top */, r - l /* right */, b - t /* bottom */);
336 }
337
Tiger Huang2b210c22019-03-18 21:21:26 +0800338 @Override
339 public boolean gatherTransparentRegion(Region region) {
340 // The tap exclude region may be affected by any view on top of it, so we detect the
341 // possible change by monitoring this function.
Tiger Huangd8ec9382019-04-18 14:35:09 -0700342 updateLocationAndTapExcludeRegion();
Tiger Huang2b210c22019-03-18 21:21:26 +0800343 return super.gatherTransparentRegion(region);
344 }
345
Tiger Huangd8ec9382019-04-18 14:35:09 -0700346 /**
347 * Sends current location in window and tap exclude region to WM for this view.
348 */
349 private void updateLocationAndTapExcludeRegion() {
350 if (mVirtualDisplay == null || !isAttachedToWindow()) {
Mark Renouf3856b4f2019-02-13 16:43:18 -0500351 return;
352 }
Tiger Huangd8ec9382019-04-18 14:35:09 -0700353 try {
354 int x = mLocationInWindow[0];
355 int y = mLocationInWindow[1];
356 getLocationInWindow(mLocationInWindow);
357 if (x != mLocationInWindow[0] || y != mLocationInWindow[1]) {
358 x = mLocationInWindow[0];
359 y = mLocationInWindow[1];
Yohei Yukawab4f328a2019-05-02 08:41:27 -0700360 final int displayId = mVirtualDisplay.getDisplay().getDisplayId();
Tiger Huangd8ec9382019-04-18 14:35:09 -0700361 WindowManagerGlobal.getWindowSession().updateDisplayContentLocation(
Yohei Yukawab4f328a2019-05-02 08:41:27 -0700362 getWindow(), x, y, displayId);
363
364 // Also report this geometry information to InputMethodManagerService.
365 // TODO(b/115693908): Unify this logic into the above WMS-based one.
366 final Matrix matrix = new Matrix();
367 matrix.set(getMatrix());
368 matrix.postTranslate(x, y);
369 mContext.getSystemService(InputMethodManager.class)
370 .reportActivityView(displayId, matrix);
Tiger Huangd8ec9382019-04-18 14:35:09 -0700371 }
372 updateTapExcludeRegion(x, y);
373 } catch (RemoteException e) {
374 e.rethrowAsRuntimeException();
375 }
376 }
377
378 /** Computes and sends current tap exclude region to WM for this view. */
379 private void updateTapExcludeRegion(int x, int y) throws RemoteException {
Tiger Huang2b210c22019-03-18 21:21:26 +0800380 if (!canReceivePointerEvents()) {
381 cleanTapExcludeRegion();
382 return;
383 }
Tiger Huangd8ec9382019-04-18 14:35:09 -0700384 mTapExcludeRegion.set(x, y, x + getWidth(), y + getHeight());
Tiger Huang2b210c22019-03-18 21:21:26 +0800385
Tiger Huangd8ec9382019-04-18 14:35:09 -0700386 // There might be views on top of us. We need to subtract those areas from the tap
387 // exclude region.
388 final ViewParent parent = getParent();
389 if (parent != null) {
390 parent.subtractObscuredTouchableRegion(mTapExcludeRegion, this);
Andrii Kulian4b6599e2018-01-15 17:24:08 -0800391 }
Tiger Huangd8ec9382019-04-18 14:35:09 -0700392
393 WindowManagerGlobal.getWindowSession().updateTapExcludeRegion(getWindow(), hashCode(),
394 mTapExcludeRegion);
Andrii Kulian4b6599e2018-01-15 17:24:08 -0800395 }
396
Andrii Kuliand3134692017-06-26 14:57:02 -0700397 private class SurfaceCallback implements SurfaceHolder.Callback {
398 @Override
399 public void surfaceCreated(SurfaceHolder surfaceHolder) {
400 if (mVirtualDisplay == null) {
Robert Carr5fea55b2018-12-10 13:05:52 -0800401 initVirtualDisplay(new SurfaceSession());
Andrii Kuliand3134692017-06-26 14:57:02 -0700402 if (mVirtualDisplay != null && mActivityViewCallback != null) {
403 mActivityViewCallback.onActivityViewReady(ActivityView.this);
404 }
405 } else {
chaviwff2e7d82018-11-02 11:11:27 -0700406 mTmpTransaction.reparent(mRootSurfaceControl,
Robert Carr10584fa2019-01-14 15:55:19 -0800407 mSurfaceView.getSurfaceControl()).apply();
Andrii Kuliand3134692017-06-26 14:57:02 -0700408 }
chaviwda4c6942018-11-07 15:52:56 -0800409
410 if (mVirtualDisplay != null) {
411 mVirtualDisplay.setDisplayState(true);
412 }
413
Tiger Huangd8ec9382019-04-18 14:35:09 -0700414 updateLocationAndTapExcludeRegion();
Andrii Kuliand3134692017-06-26 14:57:02 -0700415 }
416
417 @Override
418 public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {
419 if (mVirtualDisplay != null) {
420 mVirtualDisplay.resize(width, height, getBaseDisplayDensity());
421 }
Tiger Huangd8ec9382019-04-18 14:35:09 -0700422 updateLocationAndTapExcludeRegion();
Andrii Kuliand3134692017-06-26 14:57:02 -0700423 }
424
425 @Override
426 public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
Andrii Kuliand3134692017-06-26 14:57:02 -0700427 if (mVirtualDisplay != null) {
chaviwda4c6942018-11-07 15:52:56 -0800428 mVirtualDisplay.setDisplayState(false);
Andrii Kuliand3134692017-06-26 14:57:02 -0700429 }
Yohei Yukawab4f328a2019-05-02 08:41:27 -0700430 clearActivityViewGeometryForIme();
Andrii Kulian4b6599e2018-01-15 17:24:08 -0800431 cleanTapExcludeRegion();
Andrii Kuliand3134692017-06-26 14:57:02 -0700432 }
433 }
434
chaviwd44449d2019-01-02 16:33:12 -0800435 @Override
436 protected void onVisibilityChanged(View changedView, int visibility) {
437 super.onVisibilityChanged(changedView, visibility);
438 mSurfaceView.setVisibility(visibility);
439 }
440
Mark Renouf041d7262019-02-06 12:09:41 -0500441 /**
Mady Mellor390bff42019-04-05 15:09:01 -0700442 * @return the display id of the virtual display.
443 */
444 public int getVirtualDisplayId() {
445 if (mVirtualDisplay != null) {
446 return mVirtualDisplay.getDisplay().getDisplayId();
447 }
448 return INVALID_DISPLAY;
449 }
450
451 /**
Mark Renouf041d7262019-02-06 12:09:41 -0500452 * Injects a pair of down/up key events with keycode {@link KeyEvent#KEYCODE_BACK} to the
453 * virtual display.
454 */
455 public void performBackPress() {
456 if (mVirtualDisplay == null) {
457 return;
458 }
459 final int displayId = mVirtualDisplay.getDisplay().getDisplayId();
460 final InputManager im = InputManager.getInstance();
461 im.injectInputEvent(createKeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK, displayId),
462 InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
463 im.injectInputEvent(createKeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK, displayId),
464 InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
465 }
466
467 private static KeyEvent createKeyEvent(int action, int code, int displayId) {
468 long when = SystemClock.uptimeMillis();
469 final KeyEvent ev = new KeyEvent(when, when, action, code, 0 /* repeat */,
470 0 /* metaState */, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */,
471 KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
472 InputDevice.SOURCE_KEYBOARD);
473 ev.setDisplayId(displayId);
474 return ev;
475 }
476
chaviwff2e7d82018-11-02 11:11:27 -0700477 private void initVirtualDisplay(SurfaceSession surfaceSession) {
Andrii Kuliand3134692017-06-26 14:57:02 -0700478 if (mVirtualDisplay != null) {
479 throw new IllegalStateException("Trying to initialize for the second time.");
480 }
481
482 final int width = mSurfaceView.getWidth();
483 final int height = mSurfaceView.getHeight();
484 final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
chaviwff2e7d82018-11-02 11:11:27 -0700485
Andrii Kuliand3134692017-06-26 14:57:02 -0700486 mVirtualDisplay = displayManager.createVirtualDisplay(
chaviwda4c6942018-11-07 15:52:56 -0800487 DISPLAY_NAME + "@" + System.identityHashCode(this), width, height,
488 getBaseDisplayDensity(), null,
489 VIRTUAL_DISPLAY_FLAG_PUBLIC | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
490 | VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL);
Andrii Kuliand3134692017-06-26 14:57:02 -0700491 if (mVirtualDisplay == null) {
492 Log.e(TAG, "Failed to initialize ActivityView");
493 return;
494 }
495
Andrii Kulianf0379de2018-03-14 16:24:07 -0700496 final int displayId = mVirtualDisplay.getDisplay().getDisplayId();
497 final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
chaviwff2e7d82018-11-02 11:11:27 -0700498
499 mRootSurfaceControl = new SurfaceControl.Builder(surfaceSession)
Chavi Weingarten6ef9cc62019-02-07 16:28:45 +0000500 .setContainerLayer()
Robert Carr5fea55b2018-12-10 13:05:52 -0800501 .setParent(mSurfaceView.getSurfaceControl())
chaviwff2e7d82018-11-02 11:11:27 -0700502 .setName(DISPLAY_NAME)
503 .build();
504
Andrii Kulianf0379de2018-03-14 16:24:07 -0700505 try {
Issei Suzukia5dbf522019-02-01 17:58:15 +0100506 // TODO: Find a way to consolidate these calls to the server.
Tiger Huangd8ec9382019-04-18 14:35:09 -0700507 WindowManagerGlobal.getWindowSession().reparentDisplayContent(
508 getWindow(), mRootSurfaceControl, displayId);
Andrii Kulianf0379de2018-03-14 16:24:07 -0700509 wm.dontOverrideDisplayInfo(displayId);
Wale Ogunwale9e737db2018-12-17 15:42:37 -0800510 if (mSingleTaskInstance) {
511 mActivityTaskManager.setDisplayToSingleTaskInstance(displayId);
512 }
Issei Suzukia5dbf522019-02-01 17:58:15 +0100513 wm.setForwardedInsets(displayId, mForwardedInsets);
Andrii Kulianf0379de2018-03-14 16:24:07 -0700514 } catch (RemoteException e) {
515 e.rethrowAsRuntimeException();
516 }
chaviwff2e7d82018-11-02 11:11:27 -0700517
518 mTmpTransaction.show(mRootSurfaceControl).apply();
Brad Stenninga1dbe9c2018-05-02 08:29:28 -0700519 mTaskStackListener = new TaskStackListenerImpl();
Andrii Kuliancf8f6832018-01-23 19:43:30 -0800520 try {
Wale Ogunwale04d9cb52018-04-30 13:55:07 -0700521 mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
Andrii Kuliancf8f6832018-01-23 19:43:30 -0800522 } catch (RemoteException e) {
523 Log.e(TAG, "Failed to register task stack listener", e);
524 }
Andrii Kuliand3134692017-06-26 14:57:02 -0700525 }
526
527 private void performRelease() {
528 if (!mOpened) {
529 return;
530 }
531
532 mSurfaceView.getHolder().removeCallback(mSurfaceCallback);
533
Andrii Kulian4b6599e2018-01-15 17:24:08 -0800534 cleanTapExcludeRegion();
Andrii Kuliand3134692017-06-26 14:57:02 -0700535
Andrii Kuliancf8f6832018-01-23 19:43:30 -0800536 if (mTaskStackListener != null) {
537 try {
Wale Ogunwale04d9cb52018-04-30 13:55:07 -0700538 mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
Andrii Kuliancf8f6832018-01-23 19:43:30 -0800539 } catch (RemoteException e) {
540 Log.e(TAG, "Failed to unregister task stack listener", e);
541 }
542 mTaskStackListener = null;
543 }
544
Andrii Kuliand3134692017-06-26 14:57:02 -0700545 final boolean displayReleased;
546 if (mVirtualDisplay != null) {
547 mVirtualDisplay.release();
548 mVirtualDisplay = null;
549 displayReleased = true;
550 } else {
551 displayReleased = false;
552 }
553
Andrii Kuliand3134692017-06-26 14:57:02 -0700554 if (displayReleased && mActivityViewCallback != null) {
555 mActivityViewCallback.onActivityViewDestroyed(this);
556 }
557
558 mGuard.close();
559 mOpened = false;
560 }
561
Andrii Kulian4b6599e2018-01-15 17:24:08 -0800562 /** Report to server that tap exclude region on hosting display should be cleared. */
563 private void cleanTapExcludeRegion() {
Tiger Huang2b210c22019-03-18 21:21:26 +0800564 if (!isAttachedToWindow() || mTapExcludeRegion.isEmpty()) {
Mark Renouf3856b4f2019-02-13 16:43:18 -0500565 return;
566 }
Tiger Huang2b210c22019-03-18 21:21:26 +0800567 // Update tap exclude region with a null region to clean the state on server.
Andrii Kulian4b6599e2018-01-15 17:24:08 -0800568 try {
569 WindowManagerGlobal.getWindowSession().updateTapExcludeRegion(getWindow(), hashCode(),
Tiger Huang2b210c22019-03-18 21:21:26 +0800570 null /* region */);
571 mTapExcludeRegion.setEmpty();
Andrii Kulian4b6599e2018-01-15 17:24:08 -0800572 } catch (RemoteException e) {
573 e.rethrowAsRuntimeException();
574 }
575 }
576
Andrii Kuliand3134692017-06-26 14:57:02 -0700577 /** Get density of the hosting display. */
578 private int getBaseDisplayDensity() {
579 final WindowManager wm = mContext.getSystemService(WindowManager.class);
580 final DisplayMetrics metrics = new DisplayMetrics();
581 wm.getDefaultDisplay().getMetrics(metrics);
582 return metrics.densityDpi;
583 }
584
585 @Override
586 protected void finalize() throws Throwable {
587 try {
588 if (mGuard != null) {
589 mGuard.warnIfOpen();
590 performRelease();
591 }
592 } finally {
593 super.finalize();
594 }
595 }
Andrii Kuliancf8f6832018-01-23 19:43:30 -0800596
597 /**
Issei Suzukia5dbf522019-02-01 17:58:15 +0100598 * Set forwarded insets on the virtual display.
599 *
600 * @see IWindowManager#setForwardedInsets
601 */
602 public void setForwardedInsets(Insets insets) {
603 mForwardedInsets = insets;
604 if (mVirtualDisplay == null) {
605 return;
606 }
607 try {
608 final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
609 wm.setForwardedInsets(mVirtualDisplay.getDisplay().getDisplayId(), mForwardedInsets);
610 } catch (RemoteException e) {
611 e.rethrowAsRuntimeException();
612 }
613 }
614
615 /**
Andrii Kuliancf8f6832018-01-23 19:43:30 -0800616 * A task change listener that detects background color change of the topmost stack on our
617 * virtual display and updates the background of the surface view. This background will be shown
618 * when surface view is resized, but the app hasn't drawn its content in new size yet.
Brad Stenninga1dbe9c2018-05-02 08:29:28 -0700619 * It also calls StateCallback.onTaskMovedToFront to notify interested parties that the stack
620 * associated with the {@link ActivityView} has had a Task moved to the front. This is useful
621 * when needing to also bring the host Activity to the foreground at the same time.
Andrii Kuliancf8f6832018-01-23 19:43:30 -0800622 */
Brad Stenninga1dbe9c2018-05-02 08:29:28 -0700623 private class TaskStackListenerImpl extends TaskStackListener {
Andrii Kuliancf8f6832018-01-23 19:43:30 -0800624
625 @Override
Mark Renoufc808f062019-02-07 15:20:37 -0500626 public void onTaskDescriptionChanged(ActivityManager.RunningTaskInfo taskInfo)
Andrii Kuliancf8f6832018-01-23 19:43:30 -0800627 throws RemoteException {
Mark Renoufc808f062019-02-07 15:20:37 -0500628 if (mVirtualDisplay == null
629 || taskInfo.displayId != mVirtualDisplay.getDisplay().getDisplayId()) {
Andrii Kuliancf8f6832018-01-23 19:43:30 -0800630 return;
631 }
632
Brad Stenninga1dbe9c2018-05-02 08:29:28 -0700633 StackInfo stackInfo = getTopMostStackInfo();
634 if (stackInfo == null) {
635 return;
636 }
637 // Found the topmost stack on target display. Now check if the topmost task's
638 // description changed.
Mark Renoufc808f062019-02-07 15:20:37 -0500639 if (taskInfo.taskId == stackInfo.taskIds[stackInfo.taskIds.length - 1]) {
640 mSurfaceView.setResizeBackgroundColor(
641 taskInfo.taskDescription.getBackgroundColor());
Brad Stenninga1dbe9c2018-05-02 08:29:28 -0700642 }
643 }
644
645 @Override
Mark Renoufc808f062019-02-07 15:20:37 -0500646 public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo)
647 throws RemoteException {
648 if (mActivityViewCallback == null || mVirtualDisplay == null
649 || taskInfo.displayId != mVirtualDisplay.getDisplay().getDisplayId()) {
Mark Renouf7b71a5d2019-01-23 17:12:34 -0500650 return;
651 }
652
653 StackInfo stackInfo = getTopMostStackInfo();
654 // if StackInfo was null or unrelated to the "move to front" then there's no use
655 // notifying the callback
656 if (stackInfo != null
Mark Renoufc808f062019-02-07 15:20:37 -0500657 && taskInfo.taskId == stackInfo.taskIds[stackInfo.taskIds.length - 1]) {
Wale Ogunwale691af682019-02-11 03:09:10 -0800658 mActivityViewCallback.onTaskMovedToFront(taskInfo.taskId);
Mark Renouf7b71a5d2019-01-23 17:12:34 -0500659 }
660 }
661
662 @Override
663 public void onTaskCreated(int taskId, ComponentName componentName) throws RemoteException {
Mark Renoufc808f062019-02-07 15:20:37 -0500664 if (mActivityViewCallback == null || mVirtualDisplay == null) {
Mark Renouf7b71a5d2019-01-23 17:12:34 -0500665 return;
666 }
667
668 StackInfo stackInfo = getTopMostStackInfo();
669 // if StackInfo was null or unrelated to the task creation then there's no use
670 // notifying the callback
671 if (stackInfo != null
672 && taskId == stackInfo.taskIds[stackInfo.taskIds.length - 1]) {
673 mActivityViewCallback.onTaskCreated(taskId, componentName);
674 }
675 }
676
677 @Override
Mark Renoufc808f062019-02-07 15:20:37 -0500678 public void onTaskRemovalStarted(ActivityManager.RunningTaskInfo taskInfo)
679 throws RemoteException {
680 if (mActivityViewCallback == null || mVirtualDisplay == null
681 || taskInfo.displayId != mVirtualDisplay.getDisplay().getDisplayId()) {
Mark Renouf7b71a5d2019-01-23 17:12:34 -0500682 return;
683 }
Mark Renoufc808f062019-02-07 15:20:37 -0500684 mActivityViewCallback.onTaskRemovalStarted(taskInfo.taskId);
Brad Stenninga1dbe9c2018-05-02 08:29:28 -0700685 }
686
687 private StackInfo getTopMostStackInfo() throws RemoteException {
Andrii Kuliancf8f6832018-01-23 19:43:30 -0800688 // Find the topmost task on our virtual display - it will define the background
689 // color of the surface view during resizing.
690 final int displayId = mVirtualDisplay.getDisplay().getDisplayId();
Wale Ogunwale04d9cb52018-04-30 13:55:07 -0700691 final List<StackInfo> stackInfoList = mActivityTaskManager.getAllStackInfos();
Andrii Kuliancf8f6832018-01-23 19:43:30 -0800692
693 // Iterate through stacks from top to bottom.
694 final int stackCount = stackInfoList.size();
695 for (int i = 0; i < stackCount; i++) {
696 final StackInfo stackInfo = stackInfoList.get(i);
697 // Only look for stacks on our virtual display.
698 if (stackInfo.displayId != displayId) {
699 continue;
700 }
Brad Stenninga1dbe9c2018-05-02 08:29:28 -0700701 // Found the topmost stack on target display.
702 return stackInfo;
Andrii Kuliancf8f6832018-01-23 19:43:30 -0800703 }
Brad Stenninga1dbe9c2018-05-02 08:29:28 -0700704 return null;
Andrii Kuliancf8f6832018-01-23 19:43:30 -0800705 }
706 }
Andrii Kuliand3134692017-06-26 14:57:02 -0700707}