• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
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 
17 package android.app;
18 
19 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL;
20 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
21 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
22 import static android.view.Display.INVALID_DISPLAY;
23 
24 import android.annotation.NonNull;
25 import android.annotation.TestApi;
26 import android.app.ActivityManager.StackInfo;
27 import android.content.ComponentName;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.graphics.Insets;
31 import android.graphics.Matrix;
32 import android.graphics.Region;
33 import android.hardware.display.DisplayManager;
34 import android.hardware.display.VirtualDisplay;
35 import android.hardware.input.InputManager;
36 import android.os.RemoteException;
37 import android.os.SystemClock;
38 import android.os.UserHandle;
39 import android.util.AttributeSet;
40 import android.util.DisplayMetrics;
41 import android.util.Log;
42 import android.view.IWindowManager;
43 import android.view.InputDevice;
44 import android.view.KeyCharacterMap;
45 import android.view.KeyEvent;
46 import android.view.SurfaceControl;
47 import android.view.SurfaceHolder;
48 import android.view.SurfaceSession;
49 import android.view.SurfaceView;
50 import android.view.View;
51 import android.view.ViewGroup;
52 import android.view.ViewParent;
53 import android.view.WindowManager;
54 import android.view.WindowManagerGlobal;
55 import android.view.inputmethod.InputMethodManager;
56 
57 import dalvik.system.CloseGuard;
58 
59 import java.util.List;
60 
61 /**
62  * Activity container that allows launching activities into itself.
63  * <p>Activity launching into this container is restricted by the same rules that apply to launching
64  * on VirtualDisplays.
65  * @hide
66  */
67 @TestApi
68 public 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;
75 
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;
81 
82     private final SurfaceCallback mSurfaceCallback;
83     private StateCallback mActivityViewCallback;
84 
85     private IActivityTaskManager mActivityTaskManager;
86     // Temp container to store view coordinates in window.
87     private final int[] mLocationInWindow = new int[2];
88 
89     // The latest tap exclude region that we've sent to WM.
90     private final Region mTapExcludeRegion = new Region();
91 
92     private TaskStackListener mTaskStackListener;
93 
94     private final CloseGuard mGuard = CloseGuard.get();
95     private boolean mOpened; // Protected by mGuard.
96 
97     private final SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction();
98 
99     /** The ActivityView is only allowed to contain one task. */
100     private final boolean mSingleTaskInstance;
101 
102     private Insets mForwardedInsets;
103 
ActivityView(Context context)104     public ActivityView(Context context) {
105         this(context, null /* attrs */);
106     }
107 
ActivityView(Context context, AttributeSet attrs)108     public ActivityView(Context context, AttributeSet attrs) {
109         this(context, attrs, 0 /* defStyle */);
110     }
111 
ActivityView(Context context, AttributeSet attrs, int defStyle)112     public ActivityView(Context context, AttributeSet attrs, int defStyle) {
113         this(context, attrs, defStyle, false /*singleTaskInstance*/);
114     }
115 
ActivityView( Context context, AttributeSet attrs, int defStyle, boolean singleTaskInstance)116     public ActivityView(
117             Context context, AttributeSet attrs, int defStyle, boolean singleTaskInstance) {
118         super(context, attrs, defStyle);
119         mSingleTaskInstance = singleTaskInstance;
120 
121         mActivityTaskManager = ActivityTaskManager.getService();
122         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 {
133 
134         /**
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          */
onActivityViewReady(ActivityView view)141         public abstract void onActivityViewReady(ActivityView view);
142 
143         /**
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          */
onActivityViewDestroyed(ActivityView view)150         public abstract void onActivityViewDestroyed(ActivityView view);
151 
152         /**
153          * Called when a task is created inside the container.
154          * This is a filtered version of {@link TaskStackListener}
155          */
onTaskCreated(int taskId, ComponentName componentName)156         public void onTaskCreated(int taskId, ComponentName componentName) { }
157 
158         /**
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          */
onTaskMovedToFront(int taskId)162         public void onTaskMovedToFront(int taskId) { }
163 
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          */
onTaskRemovalStarted(int taskId)168         public void onTaskRemovalStarted(int taskId) { }
169     }
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      */
setCallback(StateCallback callback)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      * Sets the corner radius for the Activity displayed here. The corners will be
193      * cropped from the window painted by the contained Activity.
194      *
195      * @param cornerRadius the radius for the corners, in pixels
196      * @hide
197      */
setCornerRadius(float cornerRadius)198     public void setCornerRadius(float cornerRadius) {
199         mSurfaceView.setCornerRadius(cornerRadius);
200     }
201 
202     /**
203      * Launch a new activity into this container.
204      * <p>Activity resolved by the provided {@link Intent} must have
205      * {@link android.R.attr#resizeableActivity} attribute set to {@code true} in order to be
206      * launched here. Also, if activity is not owned by the owner of this container, it must allow
207      * embedding and the caller must have permission to embed.
208      * <p>Note: This class must finish initializing and
209      * {@link StateCallback#onActivityViewReady(ActivityView)} callback must be triggered before
210      * this method can be called.
211      *
212      * @param intent Intent used to launch an activity.
213      *
214      * @see StateCallback
215      * @see #startActivity(PendingIntent)
216      */
startActivity(@onNull Intent intent)217     public void startActivity(@NonNull Intent intent) {
218         final ActivityOptions options = prepareActivityOptions();
219         getContext().startActivity(intent, options.toBundle());
220     }
221 
222     /**
223      * Launch a new activity into this container.
224      * <p>Activity resolved by the provided {@link Intent} must have
225      * {@link android.R.attr#resizeableActivity} attribute set to {@code true} in order to be
226      * launched here. Also, if activity is not owned by the owner of this container, it must allow
227      * embedding and the caller must have permission to embed.
228      * <p>Note: This class must finish initializing and
229      * {@link StateCallback#onActivityViewReady(ActivityView)} callback must be triggered before
230      * this method can be called.
231      *
232      * @param intent Intent used to launch an activity.
233      * @param user The UserHandle of the user to start this activity for.
234      *
235      *
236      * @see StateCallback
237      * @see #startActivity(PendingIntent)
238      */
startActivity(@onNull Intent intent, UserHandle user)239     public void startActivity(@NonNull Intent intent, UserHandle user) {
240         final ActivityOptions options = prepareActivityOptions();
241         getContext().startActivityAsUser(intent, options.toBundle(), user);
242     }
243 
244     /**
245      * Launch a new activity into this container.
246      * <p>Activity resolved by the provided {@link PendingIntent} must have
247      * {@link android.R.attr#resizeableActivity} attribute set to {@code true} in order to be
248      * launched here. Also, if activity is not owned by the owner of this container, it must allow
249      * embedding and the caller must have permission to embed.
250      * <p>Note: This class must finish initializing and
251      * {@link StateCallback#onActivityViewReady(ActivityView)} callback must be triggered before
252      * this method can be called.
253      *
254      * @param pendingIntent Intent used to launch an activity.
255      *
256      * @see StateCallback
257      * @see #startActivity(Intent)
258      */
startActivity(@onNull PendingIntent pendingIntent)259     public void startActivity(@NonNull PendingIntent pendingIntent) {
260         final ActivityOptions options = prepareActivityOptions();
261         try {
262             pendingIntent.send(null /* context */, 0 /* code */, null /* intent */,
263                     null /* onFinished */, null /* handler */, null /* requiredPermission */,
264                     options.toBundle());
265         } catch (PendingIntent.CanceledException e) {
266             throw new RuntimeException(e);
267         }
268     }
269 
270     /**
271      * Launch a new activity into this container.
272      * <p>Activity resolved by the provided {@link PendingIntent} must have
273      * {@link android.R.attr#resizeableActivity} attribute set to {@code true} in order to be
274      * launched here. Also, if activity is not owned by the owner of this container, it must allow
275      * embedding and the caller must have permission to embed.
276      * <p>Note: This class must finish initializing and
277      * {@link StateCallback#onActivityViewReady(ActivityView)} callback must be triggered before
278      * this method can be called.
279      *
280      * @param pendingIntent Intent used to launch an activity.
281      * @param options options for the activity
282      *
283      * @see StateCallback
284      * @see #startActivity(Intent)
285      */
startActivity(@onNull PendingIntent pendingIntent, @NonNull ActivityOptions options)286     public void startActivity(@NonNull PendingIntent pendingIntent,
287             @NonNull ActivityOptions options) {
288         options.setLaunchDisplayId(mVirtualDisplay.getDisplay().getDisplayId());
289         try {
290             pendingIntent.send(null /* context */, 0 /* code */, null /* intent */,
291                     null /* onFinished */, null /* handler */, null /* requiredPermission */,
292                     options.toBundle());
293         } catch (PendingIntent.CanceledException e) {
294             throw new RuntimeException(e);
295         }
296     }
297 
298     /**
299      * Check if container is ready to launch and create {@link ActivityOptions} to target the
300      * virtual display.
301      */
prepareActivityOptions()302     private ActivityOptions prepareActivityOptions() {
303         if (mVirtualDisplay == null) {
304             throw new IllegalStateException(
305                     "Trying to start activity before ActivityView is ready.");
306         }
307         final ActivityOptions options = ActivityOptions.makeBasic();
308         options.setLaunchDisplayId(mVirtualDisplay.getDisplay().getDisplayId());
309         return options;
310     }
311 
312     /**
313      * Release this container. Activity launching will no longer be permitted.
314      * <p>Note: Calling this method is allowed after
315      * {@link StateCallback#onActivityViewReady(ActivityView)} callback was triggered and before
316      * {@link StateCallback#onActivityViewDestroyed(ActivityView)}.
317      *
318      * @see StateCallback
319      */
release()320     public void release() {
321         if (mVirtualDisplay == null) {
322             throw new IllegalStateException(
323                     "Trying to release container that is not initialized.");
324         }
325         performRelease();
326     }
327 
328     /**
329      * Triggers an update of {@link ActivityView}'s location in window to properly set tap exclude
330      * regions and avoid focus switches by touches on this view.
331      */
onLocationChanged()332     public void onLocationChanged() {
333         updateLocationAndTapExcludeRegion();
334     }
335 
clearActivityViewGeometryForIme()336     private void clearActivityViewGeometryForIme() {
337         if (mVirtualDisplay == null) {
338             return;
339         }
340         final int displayId = mVirtualDisplay.getDisplay().getDisplayId();
341         mContext.getSystemService(InputMethodManager.class).reportActivityView(displayId, null);
342     }
343 
344     @Override
onLayout(boolean changed, int l, int t, int r, int b)345     public void onLayout(boolean changed, int l, int t, int r, int b) {
346         mSurfaceView.layout(0 /* left */, 0 /* top */, r - l /* right */, b - t /* bottom */);
347     }
348 
349     @Override
gatherTransparentRegion(Region region)350     public boolean gatherTransparentRegion(Region region) {
351         // The tap exclude region may be affected by any view on top of it, so we detect the
352         // possible change by monitoring this function.
353         updateLocationAndTapExcludeRegion();
354         return super.gatherTransparentRegion(region);
355     }
356 
357     /**
358      * Sends current location in window and tap exclude region to WM for this view.
359      */
updateLocationAndTapExcludeRegion()360     private void updateLocationAndTapExcludeRegion() {
361         if (mVirtualDisplay == null || !isAttachedToWindow()) {
362             return;
363         }
364         try {
365             int x = mLocationInWindow[0];
366             int y = mLocationInWindow[1];
367             getLocationInWindow(mLocationInWindow);
368             if (x != mLocationInWindow[0] || y != mLocationInWindow[1]) {
369                 x = mLocationInWindow[0];
370                 y = mLocationInWindow[1];
371                 final int displayId = mVirtualDisplay.getDisplay().getDisplayId();
372                 WindowManagerGlobal.getWindowSession().updateDisplayContentLocation(
373                         getWindow(), x, y, displayId);
374 
375                 // Also report this geometry information to InputMethodManagerService.
376                 // TODO(b/115693908): Unify this logic into the above WMS-based one.
377                 final Matrix matrix = new Matrix();
378                 matrix.set(getMatrix());
379                 matrix.postTranslate(x, y);
380                 mContext.getSystemService(InputMethodManager.class)
381                         .reportActivityView(displayId, matrix);
382             }
383             updateTapExcludeRegion(x, y);
384         } catch (RemoteException e) {
385             e.rethrowAsRuntimeException();
386         }
387     }
388 
389     /** Computes and sends current tap exclude region to WM for this view. */
updateTapExcludeRegion(int x, int y)390     private void updateTapExcludeRegion(int x, int y) throws RemoteException {
391         if (!canReceivePointerEvents()) {
392             cleanTapExcludeRegion();
393             return;
394         }
395         mTapExcludeRegion.set(x, y, x + getWidth(), y + getHeight());
396 
397         // There might be views on top of us. We need to subtract those areas from the tap
398         // exclude region.
399         final ViewParent parent = getParent();
400         if (parent != null) {
401             parent.subtractObscuredTouchableRegion(mTapExcludeRegion, this);
402         }
403 
404         WindowManagerGlobal.getWindowSession().updateTapExcludeRegion(getWindow(), hashCode(),
405                 mTapExcludeRegion);
406     }
407 
408     private class SurfaceCallback implements SurfaceHolder.Callback {
409         @Override
surfaceCreated(SurfaceHolder surfaceHolder)410         public void surfaceCreated(SurfaceHolder surfaceHolder) {
411             if (mVirtualDisplay == null) {
412                 initVirtualDisplay(new SurfaceSession());
413                 if (mVirtualDisplay != null && mActivityViewCallback != null) {
414                     mActivityViewCallback.onActivityViewReady(ActivityView.this);
415                 }
416             } else {
417                 mTmpTransaction.reparent(mRootSurfaceControl,
418                         mSurfaceView.getSurfaceControl()).apply();
419             }
420 
421             if (mVirtualDisplay != null) {
422                 mVirtualDisplay.setDisplayState(true);
423             }
424 
425             updateLocationAndTapExcludeRegion();
426         }
427 
428         @Override
surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height)429         public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {
430             if (mVirtualDisplay != null) {
431                 mVirtualDisplay.resize(width, height, getBaseDisplayDensity());
432             }
433             updateLocationAndTapExcludeRegion();
434         }
435 
436         @Override
surfaceDestroyed(SurfaceHolder surfaceHolder)437         public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
438             if (mVirtualDisplay != null) {
439                 mVirtualDisplay.setDisplayState(false);
440             }
441             clearActivityViewGeometryForIme();
442             cleanTapExcludeRegion();
443         }
444     }
445 
446     @Override
onVisibilityChanged(View changedView, int visibility)447     protected void onVisibilityChanged(View changedView, int visibility) {
448         super.onVisibilityChanged(changedView, visibility);
449         mSurfaceView.setVisibility(visibility);
450     }
451 
452     /**
453      * @return the display id of the virtual display.
454      */
getVirtualDisplayId()455     public int getVirtualDisplayId() {
456         if (mVirtualDisplay != null) {
457             return mVirtualDisplay.getDisplay().getDisplayId();
458         }
459         return INVALID_DISPLAY;
460     }
461 
462     /**
463      * Injects a pair of down/up key events with keycode {@link KeyEvent#KEYCODE_BACK} to the
464      * virtual display.
465      */
performBackPress()466     public void performBackPress() {
467         if (mVirtualDisplay == null) {
468             return;
469         }
470         final int displayId = mVirtualDisplay.getDisplay().getDisplayId();
471         final InputManager im = InputManager.getInstance();
472         im.injectInputEvent(createKeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK, displayId),
473                 InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
474         im.injectInputEvent(createKeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK, displayId),
475                 InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
476     }
477 
createKeyEvent(int action, int code, int displayId)478     private static KeyEvent createKeyEvent(int action, int code, int displayId) {
479         long when = SystemClock.uptimeMillis();
480         final KeyEvent ev = new KeyEvent(when, when, action, code, 0 /* repeat */,
481                 0 /* metaState */, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */,
482                 KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
483                 InputDevice.SOURCE_KEYBOARD);
484         ev.setDisplayId(displayId);
485         return ev;
486     }
487 
initVirtualDisplay(SurfaceSession surfaceSession)488     private void initVirtualDisplay(SurfaceSession surfaceSession) {
489         if (mVirtualDisplay != null) {
490             throw new IllegalStateException("Trying to initialize for the second time.");
491         }
492 
493         final int width = mSurfaceView.getWidth();
494         final int height = mSurfaceView.getHeight();
495         final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
496 
497         mVirtualDisplay = displayManager.createVirtualDisplay(
498                 DISPLAY_NAME + "@" + System.identityHashCode(this), width, height,
499                 getBaseDisplayDensity(), null,
500                 VIRTUAL_DISPLAY_FLAG_PUBLIC | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
501                         | VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL);
502         if (mVirtualDisplay == null) {
503             Log.e(TAG, "Failed to initialize ActivityView");
504             return;
505         }
506 
507         final int displayId = mVirtualDisplay.getDisplay().getDisplayId();
508         final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
509 
510         mRootSurfaceControl = new SurfaceControl.Builder(surfaceSession)
511                 .setContainerLayer()
512                 .setParent(mSurfaceView.getSurfaceControl())
513                 .setName(DISPLAY_NAME)
514                 .build();
515 
516         try {
517             // TODO: Find a way to consolidate these calls to the server.
518             WindowManagerGlobal.getWindowSession().reparentDisplayContent(
519                     getWindow(), mRootSurfaceControl, displayId);
520             wm.dontOverrideDisplayInfo(displayId);
521             if (mSingleTaskInstance) {
522                 mActivityTaskManager.setDisplayToSingleTaskInstance(displayId);
523             }
524             wm.setForwardedInsets(displayId, mForwardedInsets);
525         } catch (RemoteException e) {
526             e.rethrowAsRuntimeException();
527         }
528 
529         mTmpTransaction.show(mRootSurfaceControl).apply();
530         mTaskStackListener = new TaskStackListenerImpl();
531         try {
532             mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
533         } catch (RemoteException e) {
534             Log.e(TAG, "Failed to register task stack listener", e);
535         }
536     }
537 
performRelease()538     private void performRelease() {
539         if (!mOpened) {
540             return;
541         }
542 
543         mSurfaceView.getHolder().removeCallback(mSurfaceCallback);
544 
545         cleanTapExcludeRegion();
546 
547         if (mTaskStackListener != null) {
548             try {
549                 mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
550             } catch (RemoteException e) {
551                 Log.e(TAG, "Failed to unregister task stack listener", e);
552             }
553             mTaskStackListener = null;
554         }
555 
556         final boolean displayReleased;
557         if (mVirtualDisplay != null) {
558             mVirtualDisplay.release();
559             mVirtualDisplay = null;
560             displayReleased = true;
561         } else {
562             displayReleased = false;
563         }
564 
565         if (displayReleased && mActivityViewCallback != null) {
566             mActivityViewCallback.onActivityViewDestroyed(this);
567         }
568 
569         mGuard.close();
570         mOpened = false;
571     }
572 
573     /** Report to server that tap exclude region on hosting display should be cleared. */
cleanTapExcludeRegion()574     private void cleanTapExcludeRegion() {
575         if (!isAttachedToWindow() || mTapExcludeRegion.isEmpty()) {
576             return;
577         }
578         // Update tap exclude region with a null region to clean the state on server.
579         try {
580             WindowManagerGlobal.getWindowSession().updateTapExcludeRegion(getWindow(), hashCode(),
581                     null /* region */);
582             mTapExcludeRegion.setEmpty();
583         } catch (RemoteException e) {
584             e.rethrowAsRuntimeException();
585         }
586     }
587 
588     /** Get density of the hosting display. */
getBaseDisplayDensity()589     private int getBaseDisplayDensity() {
590         final WindowManager wm = mContext.getSystemService(WindowManager.class);
591         final DisplayMetrics metrics = new DisplayMetrics();
592         wm.getDefaultDisplay().getMetrics(metrics);
593         return metrics.densityDpi;
594     }
595 
596     @Override
finalize()597     protected void finalize() throws Throwable {
598         try {
599             if (mGuard != null) {
600                 mGuard.warnIfOpen();
601                 performRelease();
602             }
603         } finally {
604             super.finalize();
605         }
606     }
607 
608     /**
609      * Set forwarded insets on the virtual display.
610      *
611      * @see IWindowManager#setForwardedInsets
612      */
setForwardedInsets(Insets insets)613     public void setForwardedInsets(Insets insets) {
614         mForwardedInsets = insets;
615         if (mVirtualDisplay == null) {
616             return;
617         }
618         try {
619             final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
620             wm.setForwardedInsets(mVirtualDisplay.getDisplay().getDisplayId(), mForwardedInsets);
621         } catch (RemoteException e) {
622             e.rethrowAsRuntimeException();
623         }
624     }
625 
626     /**
627      * A task change listener that detects background color change of the topmost stack on our
628      * virtual display and updates the background of the surface view. This background will be shown
629      * when surface view is resized, but the app hasn't drawn its content in new size yet.
630      * It also calls StateCallback.onTaskMovedToFront to notify interested parties that the stack
631      * associated with the {@link ActivityView} has had a Task moved to the front. This is useful
632      * when needing to also bring the host Activity to the foreground at the same time.
633      */
634     private class TaskStackListenerImpl extends TaskStackListener {
635 
636         @Override
onTaskDescriptionChanged(ActivityManager.RunningTaskInfo taskInfo)637         public void onTaskDescriptionChanged(ActivityManager.RunningTaskInfo taskInfo)
638                 throws RemoteException {
639             if (mVirtualDisplay == null
640                     || taskInfo.displayId != mVirtualDisplay.getDisplay().getDisplayId()) {
641                 return;
642             }
643 
644             StackInfo stackInfo = getTopMostStackInfo();
645             if (stackInfo == null) {
646                 return;
647             }
648             // Found the topmost stack on target display. Now check if the topmost task's
649             // description changed.
650             if (taskInfo.taskId == stackInfo.taskIds[stackInfo.taskIds.length - 1]) {
651                 mSurfaceView.setResizeBackgroundColor(
652                         taskInfo.taskDescription.getBackgroundColor());
653             }
654         }
655 
656         @Override
onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo)657         public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo)
658                 throws RemoteException {
659             if (mActivityViewCallback  == null || mVirtualDisplay == null
660                     || taskInfo.displayId != mVirtualDisplay.getDisplay().getDisplayId()) {
661                 return;
662             }
663 
664             StackInfo stackInfo = getTopMostStackInfo();
665             // if StackInfo was null or unrelated to the "move to front" then there's no use
666             // notifying the callback
667             if (stackInfo != null
668                     && taskInfo.taskId == stackInfo.taskIds[stackInfo.taskIds.length - 1]) {
669                 mActivityViewCallback.onTaskMovedToFront(taskInfo.taskId);
670             }
671         }
672 
673         @Override
onTaskCreated(int taskId, ComponentName componentName)674         public void onTaskCreated(int taskId, ComponentName componentName) throws RemoteException {
675             if (mActivityViewCallback == null || mVirtualDisplay == null) {
676                 return;
677             }
678 
679             StackInfo stackInfo = getTopMostStackInfo();
680             // if StackInfo was null or unrelated to the task creation then there's no use
681             // notifying the callback
682             if (stackInfo != null
683                     && taskId == stackInfo.taskIds[stackInfo.taskIds.length - 1]) {
684                 mActivityViewCallback.onTaskCreated(taskId, componentName);
685             }
686         }
687 
688         @Override
onTaskRemovalStarted(ActivityManager.RunningTaskInfo taskInfo)689         public void onTaskRemovalStarted(ActivityManager.RunningTaskInfo taskInfo)
690                 throws RemoteException {
691             if (mActivityViewCallback == null || mVirtualDisplay == null
692                     || taskInfo.displayId != mVirtualDisplay.getDisplay().getDisplayId()) {
693                 return;
694             }
695             mActivityViewCallback.onTaskRemovalStarted(taskInfo.taskId);
696         }
697 
getTopMostStackInfo()698         private StackInfo getTopMostStackInfo() throws RemoteException {
699             // Find the topmost task on our virtual display - it will define the background
700             // color of the surface view during resizing.
701             final int displayId = mVirtualDisplay.getDisplay().getDisplayId();
702             final List<StackInfo> stackInfoList = mActivityTaskManager.getAllStackInfos();
703 
704             // Iterate through stacks from top to bottom.
705             final int stackCount = stackInfoList.size();
706             for (int i = 0; i < stackCount; i++) {
707                 final StackInfo stackInfo = stackInfoList.get(i);
708                 // Only look for stacks on our virtual display.
709                 if (stackInfo.displayId != displayId) {
710                     continue;
711                 }
712                 // Found the topmost stack on target display.
713                 return stackInfo;
714             }
715             return null;
716         }
717     }
718 }
719