Home | History | Annotate | Download | only in wm
      1 /*
      2  * Copyright (C) 2016 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 com.android.server.wm;
     18 
     19 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
     20 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
     21 import static android.util.TypedValue.COMPLEX_UNIT_DIP;
     22 
     23 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
     24 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
     25 import static com.android.server.wm.PinnedStackControllerProto.DEFAULT_BOUNDS;
     26 import static com.android.server.wm.PinnedStackControllerProto.MOVEMENT_BOUNDS;
     27 
     28 import android.app.RemoteAction;
     29 import android.content.pm.ParceledListSlice;
     30 import android.content.res.Resources;
     31 import android.graphics.Point;
     32 import android.graphics.Rect;
     33 import android.os.Handler;
     34 import android.os.IBinder;
     35 import android.os.RemoteException;
     36 import android.util.DisplayMetrics;
     37 import android.util.Log;
     38 import android.util.Size;
     39 import android.util.Slog;
     40 import android.util.TypedValue;
     41 import android.util.proto.ProtoOutputStream;
     42 import android.view.DisplayInfo;
     43 import android.view.Gravity;
     44 import android.view.IPinnedStackController;
     45 import android.view.IPinnedStackListener;
     46 
     47 import com.android.internal.policy.PipSnapAlgorithm;
     48 import com.android.server.UiThread;
     49 
     50 import java.io.PrintWriter;
     51 import java.lang.ref.WeakReference;
     52 import java.util.ArrayList;
     53 import java.util.List;
     54 
     55 /**
     56  * Holds the common state of the pinned stack between the system and SystemUI. If SystemUI ever
     57  * needs to be restarted, it will be notified with the last known state.
     58  *
     59  * Changes to the pinned stack also flow through this controller, and generally, the system only
     60  * changes the pinned stack bounds through this controller in two ways:
     61  *
     62  * 1) When first entering PiP: the controller returns the valid bounds given, taking aspect ratio
     63  *    and IME state into account.
     64  * 2) When rotating the device: the controller calculates the new bounds in the new orientation,
     65  *    taking the minimized and IME state into account. In this case, we currently ignore the
     66  *    SystemUI adjustments (ie. expanded for menu, interaction, etc).
     67  *
     68  * Other changes in the system, including adjustment of IME, configuration change, and more are
     69  * handled by SystemUI (similar to the docked stack divider).
     70  */
     71 class PinnedStackController {
     72 
     73     private static final String TAG = TAG_WITH_CLASS_NAME ? "PinnedStackController" : TAG_WM;
     74 
     75     public static final float INVALID_SNAP_FRACTION = -1f;
     76     private final WindowManagerService mService;
     77     private final DisplayContent mDisplayContent;
     78     private final Handler mHandler = UiThread.getHandler();
     79 
     80     private IPinnedStackListener mPinnedStackListener;
     81     private final PinnedStackListenerDeathHandler mPinnedStackListenerDeathHandler =
     82             new PinnedStackListenerDeathHandler();
     83 
     84     private final PinnedStackControllerCallback mCallbacks = new PinnedStackControllerCallback();
     85     private final PipSnapAlgorithm mSnapAlgorithm;
     86 
     87     // States that affect how the PIP can be manipulated
     88     private boolean mIsMinimized;
     89     private boolean mIsImeShowing;
     90     private int mImeHeight;
     91     private boolean mIsShelfShowing;
     92     private int mShelfHeight;
     93 
     94     // The set of actions and aspect-ratio for the that are currently allowed on the PiP activity
     95     private ArrayList<RemoteAction> mActions = new ArrayList<>();
     96     private float mAspectRatio = -1f;
     97 
     98     // Used to calculate stack bounds across rotations
     99     private final DisplayInfo mDisplayInfo = new DisplayInfo();
    100     private final Rect mStableInsets = new Rect();
    101 
    102     // The size and position information that describes where the pinned stack will go by default.
    103     private int mDefaultMinSize;
    104     private int mDefaultStackGravity;
    105     private float mDefaultAspectRatio;
    106     private Point mScreenEdgeInsets;
    107     private int mCurrentMinSize;
    108     private float mReentrySnapFraction = INVALID_SNAP_FRACTION;
    109     private WeakReference<AppWindowToken> mLastPipActivity = null;
    110 
    111     // The aspect ratio bounds of the PIP.
    112     private float mMinAspectRatio;
    113     private float mMaxAspectRatio;
    114 
    115     // Temp vars for calculation
    116     private final DisplayMetrics mTmpMetrics = new DisplayMetrics();
    117     private final Rect mTmpInsets = new Rect();
    118     private final Rect mTmpRect = new Rect();
    119     private final Rect mTmpAnimatingBoundsRect = new Rect();
    120     private final Point mTmpDisplaySize = new Point();
    121 
    122 
    123     /**
    124      * The callback object passed to listeners for them to notify the controller of state changes.
    125      */
    126     private class PinnedStackControllerCallback extends IPinnedStackController.Stub {
    127 
    128         @Override
    129         public void setIsMinimized(final boolean isMinimized) {
    130             mHandler.post(() -> {
    131                 mIsMinimized = isMinimized;
    132                 mSnapAlgorithm.setMinimized(isMinimized);
    133             });
    134         }
    135 
    136         @Override
    137         public void setMinEdgeSize(int minEdgeSize) {
    138             mHandler.post(() -> {
    139                 mCurrentMinSize = Math.max(mDefaultMinSize, minEdgeSize);
    140             });
    141         }
    142 
    143         @Override
    144         public int getDisplayRotation() {
    145             synchronized (mService.mWindowMap) {
    146                 return mDisplayInfo.rotation;
    147             }
    148         }
    149     }
    150 
    151     /**
    152      * Handler for the case where the listener dies.
    153      */
    154     private class PinnedStackListenerDeathHandler implements IBinder.DeathRecipient {
    155 
    156         @Override
    157         public void binderDied() {
    158             // Clean up the state if the listener dies
    159             if (mPinnedStackListener != null) {
    160                 mPinnedStackListener.asBinder().unlinkToDeath(mPinnedStackListenerDeathHandler, 0);
    161             }
    162             mPinnedStackListener = null;
    163         }
    164     }
    165 
    166     PinnedStackController(WindowManagerService service, DisplayContent displayContent) {
    167         mService = service;
    168         mDisplayContent = displayContent;
    169         mSnapAlgorithm = new PipSnapAlgorithm(service.mContext);
    170         mDisplayInfo.copyFrom(mDisplayContent.getDisplayInfo());
    171         reloadResources();
    172         // Initialize the aspect ratio to the default aspect ratio.  Don't do this in reload
    173         // resources as it would clobber mAspectRatio when entering PiP from fullscreen which
    174         // triggers a configuration change and the resources to be reloaded.
    175         mAspectRatio = mDefaultAspectRatio;
    176     }
    177 
    178     void onConfigurationChanged() {
    179         reloadResources();
    180     }
    181 
    182     /**
    183      * Reloads all the resources for the current configuration.
    184      */
    185     private void reloadResources() {
    186         final Resources res = mService.mContext.getResources();
    187         mDefaultMinSize = res.getDimensionPixelSize(
    188                 com.android.internal.R.dimen.default_minimal_size_pip_resizable_task);
    189         mCurrentMinSize = mDefaultMinSize;
    190         mDefaultAspectRatio = res.getFloat(
    191                 com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio);
    192         final String screenEdgeInsetsDpString = res.getString(
    193                 com.android.internal.R.string.config_defaultPictureInPictureScreenEdgeInsets);
    194         final Size screenEdgeInsetsDp = !screenEdgeInsetsDpString.isEmpty()
    195                 ? Size.parseSize(screenEdgeInsetsDpString)
    196                 : null;
    197         mDefaultStackGravity = res.getInteger(
    198                 com.android.internal.R.integer.config_defaultPictureInPictureGravity);
    199         mDisplayContent.getDisplay().getRealMetrics(mTmpMetrics);
    200         mScreenEdgeInsets = screenEdgeInsetsDp == null ? new Point()
    201                 : new Point(dpToPx(screenEdgeInsetsDp.getWidth(), mTmpMetrics),
    202                         dpToPx(screenEdgeInsetsDp.getHeight(), mTmpMetrics));
    203         mMinAspectRatio = res.getFloat(
    204                 com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio);
    205         mMaxAspectRatio = res.getFloat(
    206                 com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio);
    207     }
    208 
    209     /**
    210      * Registers a pinned stack listener.
    211      */
    212     void registerPinnedStackListener(IPinnedStackListener listener) {
    213         try {
    214             listener.asBinder().linkToDeath(mPinnedStackListenerDeathHandler, 0);
    215             listener.onListenerRegistered(mCallbacks);
    216             mPinnedStackListener = listener;
    217             notifyImeVisibilityChanged(mIsImeShowing, mImeHeight);
    218             notifyShelfVisibilityChanged(mIsShelfShowing, mShelfHeight);
    219             // The movement bounds notification needs to be sent before the minimized state, since
    220             // SystemUI may use the bounds to retore the minimized position
    221             notifyMovementBoundsChanged(false /* fromImeAdjustment */,
    222                     false /* fromShelfAdjustment */);
    223             notifyActionsChanged(mActions);
    224             notifyMinimizeChanged(mIsMinimized);
    225         } catch (RemoteException e) {
    226             Log.e(TAG, "Failed to register pinned stack listener", e);
    227         }
    228     }
    229 
    230     /**
    231      * @return whether the given {@param aspectRatio} is valid.
    232      */
    233     public boolean isValidPictureInPictureAspectRatio(float aspectRatio) {
    234         return Float.compare(mMinAspectRatio, aspectRatio) <= 0 &&
    235                 Float.compare(aspectRatio, mMaxAspectRatio) <= 0;
    236     }
    237 
    238     /**
    239      * Returns the current bounds (or the default bounds if there are no current bounds) with the
    240      * specified aspect ratio.
    241      */
    242     Rect transformBoundsToAspectRatio(Rect stackBounds, float aspectRatio,
    243             boolean useCurrentMinEdgeSize) {
    244         // Save the snap fraction, calculate the aspect ratio based on screen size
    245         final float snapFraction = mSnapAlgorithm.getSnapFraction(stackBounds,
    246                 getMovementBounds(stackBounds));
    247 
    248         final int minEdgeSize = useCurrentMinEdgeSize ? mCurrentMinSize : mDefaultMinSize;
    249         final Size size = mSnapAlgorithm.getSizeForAspectRatio(aspectRatio, minEdgeSize,
    250                 mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
    251         final int left = (int) (stackBounds.centerX() - size.getWidth() / 2f);
    252         final int top = (int) (stackBounds.centerY() - size.getHeight() / 2f);
    253         stackBounds.set(left, top, left + size.getWidth(), top + size.getHeight());
    254         mSnapAlgorithm.applySnapFraction(stackBounds, getMovementBounds(stackBounds), snapFraction);
    255         if (mIsMinimized) {
    256             applyMinimizedOffset(stackBounds, getMovementBounds(stackBounds));
    257         }
    258         return stackBounds;
    259     }
    260 
    261     /**
    262      * Saves the current snap fraction for re-entry of the current activity into PiP.
    263      */
    264     void saveReentrySnapFraction(final AppWindowToken token, final Rect stackBounds) {
    265         mReentrySnapFraction = getSnapFraction(stackBounds);
    266         mLastPipActivity = new WeakReference<>(token);
    267     }
    268 
    269     /**
    270      * Resets the last saved snap fraction so that the default bounds will be returned.
    271      */
    272     void resetReentrySnapFraction(AppWindowToken token) {
    273         if (mLastPipActivity != null && mLastPipActivity.get() == token) {
    274             mReentrySnapFraction = INVALID_SNAP_FRACTION;
    275             mLastPipActivity = null;
    276         }
    277     }
    278 
    279     /**
    280      * @return the default bounds to show the PIP when there is no active PIP.
    281      */
    282     Rect getDefaultOrLastSavedBounds() {
    283         return getDefaultBounds(mReentrySnapFraction);
    284     }
    285 
    286     /**
    287      * @return the default bounds to show the PIP, if a {@param snapFraction} is provided, then it
    288      * will apply the default bounds to the provided snap fraction.
    289      */
    290     Rect getDefaultBounds(float snapFraction) {
    291         synchronized (mService.mWindowMap) {
    292             final Rect insetBounds = new Rect();
    293             getInsetBounds(insetBounds);
    294 
    295             final Rect defaultBounds = new Rect();
    296             final Size size = mSnapAlgorithm.getSizeForAspectRatio(mDefaultAspectRatio,
    297                     mDefaultMinSize, mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
    298             if (snapFraction != INVALID_SNAP_FRACTION) {
    299                 defaultBounds.set(0, 0, size.getWidth(), size.getHeight());
    300                 final Rect movementBounds = getMovementBounds(defaultBounds);
    301                 mSnapAlgorithm.applySnapFraction(defaultBounds, movementBounds, snapFraction);
    302             } else {
    303                 Gravity.apply(mDefaultStackGravity, size.getWidth(), size.getHeight(), insetBounds,
    304                         0, Math.max(mIsImeShowing ? mImeHeight : 0,
    305                                 mIsShelfShowing ? mShelfHeight : 0),
    306                         defaultBounds);
    307             }
    308             return defaultBounds;
    309         }
    310     }
    311 
    312     /**
    313      * In the case where the display rotation is changed but there is no stack, we can't depend on
    314      * onTaskStackBoundsChanged() to be called.  But we still should update our known display info
    315      * with the new state so that we can update SystemUI.
    316      */
    317     synchronized void onDisplayInfoChanged() {
    318         mDisplayInfo.copyFrom(mDisplayContent.getDisplayInfo());
    319         notifyMovementBoundsChanged(false /* fromImeAdjustment */, false /* fromShelfAdjustment */);
    320     }
    321 
    322     /**
    323      * Updates the display info, calculating and returning the new stack and movement bounds in the
    324      * new orientation of the device if necessary.
    325      */
    326     boolean onTaskStackBoundsChanged(Rect targetBounds, Rect outBounds) {
    327         synchronized (mService.mWindowMap) {
    328             final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();
    329             if (mDisplayInfo.equals(displayInfo)) {
    330                 // We are already in the right orientation, ignore
    331                 outBounds.setEmpty();
    332                 return false;
    333             } else if (targetBounds.isEmpty()) {
    334                 // The stack is null, we are just initializing the stack, so just store the display
    335                 // info and ignore
    336                 mDisplayInfo.copyFrom(displayInfo);
    337                 outBounds.setEmpty();
    338                 return false;
    339             }
    340 
    341             mTmpRect.set(targetBounds);
    342             final Rect postChangeStackBounds = mTmpRect;
    343 
    344             // Calculate the snap fraction of the current stack along the old movement bounds
    345             final float snapFraction = getSnapFraction(postChangeStackBounds);
    346             mDisplayInfo.copyFrom(displayInfo);
    347 
    348             // Calculate the stack bounds in the new orientation to the same same fraction along the
    349             // rotated movement bounds.
    350             final Rect postChangeMovementBounds = getMovementBounds(postChangeStackBounds,
    351                     false /* adjustForIme */, false /* adjustForShelf */);
    352             mSnapAlgorithm.applySnapFraction(postChangeStackBounds, postChangeMovementBounds,
    353                     snapFraction);
    354             if (mIsMinimized) {
    355                 applyMinimizedOffset(postChangeStackBounds, postChangeMovementBounds);
    356             }
    357 
    358             notifyMovementBoundsChanged(false /* fromImeAdjustment */,
    359                     false /* fromShelfAdjustment */);
    360 
    361             outBounds.set(postChangeStackBounds);
    362             return true;
    363         }
    364     }
    365 
    366     /**
    367      * Sets the Ime state and height.
    368      */
    369     void setAdjustedForIme(boolean adjustedForIme, int imeHeight) {
    370         // Due to the order of callbacks from the system, we may receive an ime height even when
    371         // {@param adjustedForIme} is false, and also a zero height when {@param adjustedForIme}
    372         // is true.  Instead, ensure that the ime state changes with the height and if the ime is
    373         // showing, then the height is non-zero.
    374         final boolean imeShowing = adjustedForIme && imeHeight > 0;
    375         imeHeight = imeShowing ? imeHeight : 0;
    376         if (imeShowing == mIsImeShowing && imeHeight == mImeHeight) {
    377             return;
    378         }
    379 
    380         mIsImeShowing = imeShowing;
    381         mImeHeight = imeHeight;
    382         notifyImeVisibilityChanged(imeShowing, imeHeight);
    383         notifyMovementBoundsChanged(true /* fromImeAdjustment */, false /* fromShelfAdjustment */);
    384     }
    385 
    386     /**
    387      * Sets the shelf state and height.
    388      */
    389     void setAdjustedForShelf(boolean adjustedForShelf, int shelfHeight) {
    390         final boolean shelfShowing = adjustedForShelf && shelfHeight > 0;
    391         if (shelfShowing == mIsShelfShowing && shelfHeight == mShelfHeight) {
    392             return;
    393         }
    394 
    395         mIsShelfShowing = shelfShowing;
    396         mShelfHeight = shelfHeight;
    397         notifyShelfVisibilityChanged(shelfShowing, shelfHeight);
    398         notifyMovementBoundsChanged(false /* fromImeAdjustment */, true /* fromShelfAdjustment */);
    399     }
    400 
    401     /**
    402      * Sets the current aspect ratio.
    403      */
    404     void setAspectRatio(float aspectRatio) {
    405         if (Float.compare(mAspectRatio, aspectRatio) != 0) {
    406             mAspectRatio = aspectRatio;
    407             notifyMovementBoundsChanged(false /* fromImeAdjustment */,
    408                     false /* fromShelfAdjustment */);
    409         }
    410     }
    411 
    412     /**
    413      * @return the current aspect ratio.
    414      */
    415     float getAspectRatio() {
    416         return mAspectRatio;
    417     }
    418 
    419     /**
    420      * Sets the current set of actions.
    421      */
    422     void setActions(List<RemoteAction> actions) {
    423         mActions.clear();
    424         if (actions != null) {
    425             mActions.addAll(actions);
    426         }
    427         notifyActionsChanged(mActions);
    428     }
    429 
    430     /**
    431      * Notifies listeners that the PIP needs to be adjusted for the IME.
    432      */
    433     private void notifyImeVisibilityChanged(boolean imeVisible, int imeHeight) {
    434         if (mPinnedStackListener != null) {
    435             try {
    436                 mPinnedStackListener.onImeVisibilityChanged(imeVisible, imeHeight);
    437             } catch (RemoteException e) {
    438                 Slog.e(TAG_WM, "Error delivering bounds changed event.", e);
    439             }
    440         }
    441     }
    442 
    443     private void notifyShelfVisibilityChanged(boolean shelfVisible, int shelfHeight) {
    444         if (mPinnedStackListener != null) {
    445             try {
    446                 mPinnedStackListener.onShelfVisibilityChanged(shelfVisible, shelfHeight);
    447             } catch (RemoteException e) {
    448                 Slog.e(TAG_WM, "Error delivering bounds changed event.", e);
    449             }
    450         }
    451     }
    452 
    453     /**
    454      * Notifies listeners that the PIP minimized state has changed.
    455      */
    456     private void notifyMinimizeChanged(boolean isMinimized) {
    457         if (mPinnedStackListener != null) {
    458             try {
    459                 mPinnedStackListener.onMinimizedStateChanged(isMinimized);
    460             } catch (RemoteException e) {
    461                 Slog.e(TAG_WM, "Error delivering minimize changed event.", e);
    462             }
    463         }
    464     }
    465 
    466     /**
    467      * Notifies listeners that the PIP actions have changed.
    468      */
    469     private void notifyActionsChanged(List<RemoteAction> actions) {
    470         if (mPinnedStackListener != null) {
    471             try {
    472                 mPinnedStackListener.onActionsChanged(new ParceledListSlice(actions));
    473             } catch (RemoteException e) {
    474                 Slog.e(TAG_WM, "Error delivering actions changed event.", e);
    475             }
    476         }
    477     }
    478 
    479     /**
    480      * Notifies listeners that the PIP movement bounds have changed.
    481      */
    482     private void notifyMovementBoundsChanged(boolean fromImeAdjustment,
    483             boolean fromShelfAdjustment) {
    484         synchronized (mService.mWindowMap) {
    485             if (mPinnedStackListener == null) {
    486                 return;
    487             }
    488             try {
    489                 final Rect insetBounds = new Rect();
    490                 getInsetBounds(insetBounds);
    491                 final Rect normalBounds = getDefaultBounds(INVALID_SNAP_FRACTION);
    492                 if (isValidPictureInPictureAspectRatio(mAspectRatio)) {
    493                     transformBoundsToAspectRatio(normalBounds, mAspectRatio,
    494                             false /* useCurrentMinEdgeSize */);
    495                 }
    496                 final Rect animatingBounds = mTmpAnimatingBoundsRect;
    497                 final TaskStack pinnedStack = mDisplayContent.getPinnedStack();
    498                 if (pinnedStack != null) {
    499                     pinnedStack.getAnimationOrCurrentBounds(animatingBounds);
    500                 } else {
    501                     animatingBounds.set(normalBounds);
    502                 }
    503                 mPinnedStackListener.onMovementBoundsChanged(insetBounds, normalBounds,
    504                         animatingBounds, fromImeAdjustment, fromShelfAdjustment,
    505                         mDisplayInfo.rotation);
    506             } catch (RemoteException e) {
    507                 Slog.e(TAG_WM, "Error delivering actions changed event.", e);
    508             }
    509         }
    510     }
    511 
    512     /**
    513      * @return the bounds on the screen that the PIP can be visible in.
    514      */
    515     private void getInsetBounds(Rect outRect) {
    516         synchronized (mService.mWindowMap) {
    517             mService.mPolicy.getStableInsetsLw(mDisplayInfo.rotation, mDisplayInfo.logicalWidth,
    518                     mDisplayInfo.logicalHeight, mDisplayInfo.displayCutout, mTmpInsets);
    519             outRect.set(mTmpInsets.left + mScreenEdgeInsets.x, mTmpInsets.top + mScreenEdgeInsets.y,
    520                     mDisplayInfo.logicalWidth - mTmpInsets.right - mScreenEdgeInsets.x,
    521                     mDisplayInfo.logicalHeight - mTmpInsets.bottom - mScreenEdgeInsets.y);
    522         }
    523     }
    524 
    525     /**
    526      * @return the movement bounds for the given {@param stackBounds} and the current state of the
    527      *         controller.
    528      */
    529     private Rect getMovementBounds(Rect stackBounds) {
    530         synchronized (mService.mWindowMap) {
    531             return getMovementBounds(stackBounds, true /* adjustForIme */,
    532                     true /* adjustForShelf */);
    533         }
    534     }
    535 
    536     /**
    537      * @return the movement bounds for the given {@param stackBounds} and the current state of the
    538      *         controller.
    539      */
    540     private Rect getMovementBounds(Rect stackBounds, boolean adjustForIme, boolean adjustForShelf) {
    541         synchronized (mService.mWindowMap) {
    542             final Rect movementBounds = new Rect();
    543             getInsetBounds(movementBounds);
    544 
    545             // Apply the movement bounds adjustments based on the current state
    546             mSnapAlgorithm.getMovementBounds(stackBounds, movementBounds, movementBounds,
    547                     Math.max((adjustForIme && mIsImeShowing) ? mImeHeight : 0,
    548                             (adjustForShelf && mIsShelfShowing) ? mShelfHeight : 0));
    549             return movementBounds;
    550         }
    551     }
    552 
    553     /**
    554      * Applies the minimized offsets to the given stack bounds.
    555      */
    556     private void applyMinimizedOffset(Rect stackBounds, Rect movementBounds) {
    557         synchronized (mService.mWindowMap) {
    558             mTmpDisplaySize.set(mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
    559             mService.getStableInsetsLocked(mDisplayContent.getDisplayId(), mStableInsets);
    560             mSnapAlgorithm.applyMinimizedOffset(stackBounds, movementBounds, mTmpDisplaySize,
    561                     mStableInsets);
    562         }
    563     }
    564 
    565     /**
    566      * @return the default snap fraction to apply instead of the default gravity when calculating
    567      *         the default stack bounds when first entering PiP.
    568      */
    569     private float getSnapFraction(Rect stackBounds) {
    570         return mSnapAlgorithm.getSnapFraction(stackBounds, getMovementBounds(stackBounds));
    571     }
    572 
    573     /**
    574      * @return the pixels for a given dp value.
    575      */
    576     private int dpToPx(float dpValue, DisplayMetrics dm) {
    577         return (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, dpValue, dm);
    578     }
    579 
    580     void dump(String prefix, PrintWriter pw) {
    581         pw.println(prefix + "PinnedStackController");
    582         pw.print(prefix + "  defaultBounds=");
    583         getDefaultBounds(INVALID_SNAP_FRACTION).printShortString(pw);
    584         pw.println();
    585         mService.getStackBounds(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, mTmpRect);
    586         pw.print(prefix + "  movementBounds="); getMovementBounds(mTmpRect).printShortString(pw);
    587         pw.println();
    588         pw.println(prefix + "  mIsImeShowing=" + mIsImeShowing);
    589         pw.println(prefix + "  mImeHeight=" + mImeHeight);
    590         pw.println(prefix + "  mIsShelfShowing=" + mIsShelfShowing);
    591         pw.println(prefix + "  mShelfHeight=" + mShelfHeight);
    592         pw.println(prefix + "  mReentrySnapFraction=" + mReentrySnapFraction);
    593         pw.println(prefix + "  mIsMinimized=" + mIsMinimized);
    594         if (mActions.isEmpty()) {
    595             pw.println(prefix + "  mActions=[]");
    596         } else {
    597             pw.println(prefix + "  mActions=[");
    598             for (int i = 0; i < mActions.size(); i++) {
    599                 RemoteAction action = mActions.get(i);
    600                 pw.print(prefix + "    Action[" + i + "]: ");
    601                 action.dump("", pw);
    602             }
    603             pw.println(prefix + "  ]");
    604         }
    605         pw.println(prefix + " mDisplayInfo=" + mDisplayInfo);
    606     }
    607 
    608     void writeToProto(ProtoOutputStream proto, long fieldId) {
    609         final long token = proto.start(fieldId);
    610         getDefaultBounds(INVALID_SNAP_FRACTION).writeToProto(proto, DEFAULT_BOUNDS);
    611         mService.getStackBounds(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, mTmpRect);
    612         getMovementBounds(mTmpRect).writeToProto(proto, MOVEMENT_BOUNDS);
    613         proto.end(token);
    614     }
    615 }
    616