Home | History | Annotate | Download | only in app
      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 
    104     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) {
    113         this(context, attrs, defStyle, false /*singleTaskInstance*/);
    114     }
    115 
    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          */
    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          */
    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          */
    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          */
    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          */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    332     public void onLocationChanged() {
    333         updateLocationAndTapExcludeRegion();
    334     }
    335 
    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
    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
    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      */
    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. */
    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
    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
    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
    437         public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
    438             if (mVirtualDisplay != null) {
    439                 mVirtualDisplay.setDisplayState(false);
    440             }
    441             clearActivityViewGeometryForIme();
    442             cleanTapExcludeRegion();
    443         }
    444     }
    445 
    446     @Override
    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      */
    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      */
    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 
    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 
    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 
    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. */
    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. */
    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
    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      */
    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
    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
    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
    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
    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 
    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