Merge "set RuntimePermissionPresentationInfo, getAppPermissions, and OnGetAppPermissions as Test Api, added Get Runtime Permissions to shell" into qt-dev am: 1ce9983162
am: 40bcb1d3cd

Change-Id: I16a5be1849af3a01b9c3f64c6734e6b6d1e91fc3
diff --git a/api/test-current.txt b/api/test-current.txt
index 5626905..cce6275 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -3187,10 +3187,13 @@
   }
 
   @UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback {
+    method @android.view.ViewDebug.ExportedProperty(mapping={@android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_CONTENT_CAPTURE_AUTO, to="auto"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_CONTENT_CAPTURE_YES, to="yes"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_CONTENT_CAPTURE_NO, to="no"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS, to="yesExcludeDescendants"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS, to="noExcludeDescendants")}) public int getImportantForContentCapture();
     method public android.view.View getTooltipView();
     method public boolean isAutofilled();
     method public static boolean isDefaultFocusHighlightEnabled();
     method public boolean isDefaultFocusHighlightNeeded(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable);
+    method public final boolean isImportantForContentCapture();
+    method public void onProvideContentCaptureStructure(@NonNull android.view.ViewStructure, int);
     method protected void resetResolvedDrawables();
     method public void resetResolvedLayoutDirection();
     method public void resetResolvedPadding();
@@ -3201,7 +3204,13 @@
     method public boolean restoreFocusNotInCluster();
     method public void setAutofilled(boolean);
     method public final void setFocusedInCluster();
+    method public void setImportantForContentCapture(int);
     method public void setIsRootNamespace(boolean);
+    field public static final int IMPORTANT_FOR_CONTENT_CAPTURE_AUTO = 0; // 0x0
+    field public static final int IMPORTANT_FOR_CONTENT_CAPTURE_NO = 2; // 0x2
+    field public static final int IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS = 8; // 0x8
+    field public static final int IMPORTANT_FOR_CONTENT_CAPTURE_YES = 1; // 0x1
+    field public static final int IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS = 4; // 0x4
   }
 
   public class ViewConfiguration {
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index 4771f9f..3bf659b 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -120,6 +120,7 @@
 
         mActivityTaskManager = ActivityTaskManager.getService();
         mSurfaceView = new SurfaceView(context);
+        mSurfaceView.setAlpha(0f);
         mSurfaceCallback = new SurfaceCallback();
         mSurfaceView.getHolder().addCallback(mSurfaceCallback);
         addView(mSurfaceView);
@@ -347,6 +348,16 @@
     }
 
     @Override
+    public void setAlpha(float alpha) {
+        mSurfaceView.setAlpha(alpha);
+    }
+
+    @Override
+    public float getAlpha() {
+        return mSurfaceView.getAlpha();
+    }
+
+    @Override
     public boolean gatherTransparentRegion(Region region) {
         // The tap exclude region may be affected by any view on top of it, so we detect the
         // possible change by monitoring this function.
diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index 1fdc8ca5..3f6880f 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -169,4 +169,12 @@
      * @param taskInfo info about the task which received the back press
      */
     void onBackPressedOnTaskRoot(in ActivityManager.RunningTaskInfo taskInfo);
+
+    /*
+     * Called when contents are drawn for the first time on a display which can only contain one
+     * task.
+     *
+     * @param displayId the id of the display on which contents are drawn.
+     */
+    void onSingleTaskDisplayDrawn(int displayId);
 }
diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java
index 00f3ad5..36daf32 100644
--- a/core/java/android/app/TaskStackListener.java
+++ b/core/java/android/app/TaskStackListener.java
@@ -173,4 +173,8 @@
     public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo)
             throws RemoteException {
     }
+
+    @Override
+    public void onSingleTaskDisplayDrawn(int displayId) throws RemoteException {
+    }
 }
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index bbd44c8..4b92968 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -830,6 +830,32 @@
         return false;
     }
 
+    private void adjustBoundsInScreenIfNeeded(List<AccessibilityNodeInfo> infos) {
+        if (infos == null || shouldBypassAdjustBoundsInScreen()) {
+            return;
+        }
+        final int infoCount = infos.size();
+        for (int i = 0; i < infoCount; i++) {
+            final AccessibilityNodeInfo info = infos.get(i);
+            adjustBoundsInScreenIfNeeded(info);
+        }
+    }
+
+    private void adjustBoundsInScreenIfNeeded(AccessibilityNodeInfo info) {
+        if (info == null || shouldBypassAdjustBoundsInScreen()) {
+            return;
+        }
+        final Rect boundsInScreen = mTempRect;
+        info.getBoundsInScreen(boundsInScreen);
+        boundsInScreen.offset(mViewRootImpl.mAttachInfo.mLocationInParentDisplay.x,
+                mViewRootImpl.mAttachInfo.mLocationInParentDisplay.y);
+        info.setBoundsInScreen(boundsInScreen);
+    }
+
+    private boolean shouldBypassAdjustBoundsInScreen() {
+        return mViewRootImpl.mAttachInfo.mLocationInParentDisplay.equals(0, 0);
+    }
+
     private void applyAppScaleAndMagnificationSpecIfNeeded(AccessibilityNodeInfo info,
             MagnificationSpec spec) {
         if (info == null) {
@@ -921,6 +947,7 @@
             MagnificationSpec spec, Region interactiveRegion) {
         try {
             mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
+            adjustBoundsInScreenIfNeeded(infos);
             applyAppScaleAndMagnificationSpecIfNeeded(infos, spec);
             adjustIsVisibleToUserIfNeeded(infos, interactiveRegion);
             callback.setFindAccessibilityNodeInfosResult(infos, interactionId);
@@ -939,6 +966,7 @@
             MagnificationSpec spec, Region interactiveRegion) {
         try {
             mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
+            adjustBoundsInScreenIfNeeded(info);
             applyAppScaleAndMagnificationSpecIfNeeded(info, spec);
             adjustIsVisibleToUserIfNeeded(info, interactiveRegion);
             callback.setFindAccessibilityNodeInfoResult(info, interactionId);
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index 699e795..f34f9e6 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -17,6 +17,7 @@
 
 package android.view;
 
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
@@ -57,6 +58,12 @@
             in DisplayCutout.ParcelableWrapper displayCutout);
 
     /**
+     * Called when the window location in parent display has changed. The offset will only be a
+     * nonzero value if the window is on an embedded display that is re-parented to another window.
+     */
+    void locationInParentDisplayChanged(in Point offset);
+
+    /**
      * Called when the window insets configuration has changed.
      */
     void insetsChanged(in InsetsState insetsState);
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 254d04e..add7376b 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -27,6 +27,7 @@
 import android.graphics.BlendMode;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.HardwareRenderer;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
 import android.graphics.PorterDuff;
@@ -201,6 +202,29 @@
 
     private SurfaceControl.Transaction mRtTransaction = new SurfaceControl.Transaction();
 
+    /**
+     * A callback which reflects an alpha value of this view onto the underlying surfaces.
+     *
+     * <p class="note"><strong>Note:</strong> This doesn't have to be defined as a member variable,
+     * but can be defined as an inline lambda when calling ViewRootImpl#registerRtFrameCallback().
+     * However when we do so, the callback is triggered only for a few times and stops working for
+     * some reason. It's suspected that there is a problem around garbage collection, and until
+     * the cause is fixed, we will keep this callback in a member variable.</p>
+    */
+    private HardwareRenderer.FrameDrawingCallback mSetSurfaceAlphaCallback = frame -> {
+        final ViewRootImpl viewRoot = getViewRootImpl();
+        if (viewRoot == null || viewRoot.mSurface == null || !viewRoot.mSurface.isValid()) {
+            // In this case, the alpha value is reflected on the screen in #updateSurface() later.
+            return;
+        }
+
+        final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+        t.setAlpha(mSurfaceControl, getAlpha());
+        t.deferTransactionUntilSurface(mSurfaceControl, viewRoot.mSurface, frame);
+        t.setEarlyWakeup();
+        t.apply();
+    };
+
     public SurfaceView(Context context) {
         this(context, null);
     }
@@ -288,6 +312,17 @@
         updateSurface();
     }
 
+    @Override
+    public void setAlpha(float alpha) {
+        super.setAlpha(alpha);
+        final ViewRootImpl viewRoot = getViewRootImpl();
+        if (viewRoot == null) {
+            return;
+        }
+        viewRoot.registerRtFrameCallback(mSetSurfaceAlphaCallback);
+        invalidate();
+    }
+
     private void performDrawFinished() {
         if (mPendingReportDraws > 0) {
             mDrawFinished = true;
@@ -647,6 +682,13 @@
                         }
                         updateBackgroundVisibilityInTransaction(viewRoot.getSurfaceControl());
 
+                        // Alpha value change is handled in setAlpha() directly using a local
+                        // transaction. However it can happen that setAlpha() is called while
+                        // local transactions cannot be applied, so the value is stored in a View
+                        // but not yet reflected on the Surface.
+                        mSurfaceControl.setAlpha(getAlpha());
+                        mBackgroundControl.setAlpha(getAlpha());
+
                         // While creating the surface, we will set it's initial
                         // geometry. Outside of that though, we should generally
                         // leave it to the RenderThread.
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index bf6191e..063f024 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1377,6 +1377,74 @@
      */
     public static final int AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 0x1;
 
+    /** @hide */
+    @IntDef(prefix = { "IMPORTANT_FOR_CONTENT_CAPTURE_" }, value = {
+            IMPORTANT_FOR_CONTENT_CAPTURE_AUTO,
+            IMPORTANT_FOR_CONTENT_CAPTURE_YES,
+            IMPORTANT_FOR_CONTENT_CAPTURE_NO,
+            IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS,
+            IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ContentCaptureImportance {}
+
+    /**
+     * Automatically determine whether a view is important for content capture.
+     *
+     * @see #isImportantForContentCapture()
+     * @see #setImportantForContentCapture(int)
+     *
+     * @hide
+     */
+    @TestApi
+    public static final int IMPORTANT_FOR_CONTENT_CAPTURE_AUTO = 0x0;
+
+    /**
+     * The view is important for content capture, and its children (if any) will be traversed.
+     *
+     * @see #isImportantForContentCapture()
+     * @see #setImportantForContentCapture(int)
+     *
+     * @hide
+     */
+    @TestApi
+    public static final int IMPORTANT_FOR_CONTENT_CAPTURE_YES = 0x1;
+
+    /**
+     * The view is not important for content capture, but its children (if any) will be traversed.
+     *
+     * @see #isImportantForContentCapture()
+     * @see #setImportantForContentCapture(int)
+     *
+     * @hide
+     */
+    @TestApi
+    public static final int IMPORTANT_FOR_CONTENT_CAPTURE_NO = 0x2;
+
+    /**
+     * The view is important for content capture, but its children (if any) will not be traversed.
+     *
+     * @see #isImportantForContentCapture()
+     * @see #setImportantForContentCapture(int)
+     *
+     * @hide
+     */
+    @TestApi
+    public static final int IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS = 0x4;
+
+    /**
+     * The view is not important for content capture, and its children (if any) will not be
+     * traversed.
+     *
+     * @see #isImportantForContentCapture()
+     * @see #setImportantForContentCapture(int)
+     *
+     * @hide
+     */
+    @TestApi
+    public static final int IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS = 0x8;
+
+
     /**
      * This view is enabled. Interpretation varies by subclass.
      * Use with ENABLED_MASK when calling setFlags.
@@ -3349,6 +3417,55 @@
 
     /* End of masks for mPrivateFlags3 */
 
+    /*
+     * Masks for mPrivateFlags4, as generated by dumpFlags():
+     *
+     * |-------|-------|-------|-------|
+     *                             1111 PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK
+     *                            1     PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED
+     *                           1      PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED
+     *                          1       PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED
+     *                         1        PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE
+     *                         11       PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK
+     * |-------|-------|-------|-------|
+     */
+
+    /**
+     * Mask for obtaining the bits which specify how to determine
+     * whether a view is important for autofill.
+     *
+     * <p>NOTE: the important for content capture values were the first flags added and are set in
+     * the rightmost position, so we don't need to shift them
+     */
+    private static final int PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK =
+            IMPORTANT_FOR_CONTENT_CAPTURE_AUTO | IMPORTANT_FOR_CONTENT_CAPTURE_YES
+            | IMPORTANT_FOR_CONTENT_CAPTURE_NO
+            | IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS
+            | IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS;
+
+    /*
+     * Variables used to control when the IntelligenceManager.notifyNodeAdded()/removed() methods
+     * should be called.
+     *
+     * The idea is to call notifyAppeared() after the view is layout and visible, then call
+     * notifyDisappeared() when it's gone (without known when it was removed from the parent).
+     */
+    private static final int PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED = 0x10;
+    private static final int PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED = 0x20;
+
+    /*
+     * Flags used to cache the value returned by isImportantForContentCapture while the view
+     * hierarchy is being traversed.
+     */
+    private static final int PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED = 0x40;
+    private static final int PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE = 0x80;
+
+    private static final int PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK =
+            PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED
+            | PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE;
+
+    /* End of masks for mPrivateFlags4 */
+
     /** @hide */
     protected static final int VIEW_STRUCTURE_FOR_ASSIST = 0;
     /** @hide */
@@ -3972,6 +4089,8 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 129147060)
     int mPrivateFlags3;
 
+    private int mPrivateFlags4;
+
     /**
      * This view's request for the visibility of the status bar.
      * @hide
@@ -8427,6 +8546,65 @@
         onProvideStructure(structure, VIEW_STRUCTURE_FOR_AUTOFILL, flags);
     }
 
+    /**
+     * Populates a {@link ViewStructure} for content capture.
+     *
+     * <p>This method is called after a view is that is eligible for content capture
+     * (for example, if it {@link #isImportantForAutofill()}, an intelligence service is enabled for
+     * the user, and the activity rendering the view is enabled for content capture) is laid out and
+     * is visible.
+     *
+     * <p>The populated structure is then passed to the service through
+     * {@link ContentCaptureSession#notifyViewAppeared(ViewStructure)}.
+     *
+     * <p><b>Note: </b>views that manage a virtual structure under this view must populate just
+     * the node representing this view and return right away, then asynchronously report (not
+     * necessarily in the UI thread) when the children nodes appear, disappear or have their text
+     * changed by calling
+     * {@link ContentCaptureSession#notifyViewAppeared(ViewStructure)},
+     * {@link ContentCaptureSession#notifyViewDisappeared(AutofillId)}, and
+     * {@link ContentCaptureSession#notifyViewTextChanged(AutofillId, CharSequence)}
+     * respectively. The structure for the a child must be created using
+     * {@link ContentCaptureSession#newVirtualViewStructure(AutofillId, long)}, and the
+     * {@code autofillId} for a child can be obtained either through
+     * {@code childStructure.getAutofillId()} or
+     * {@link ContentCaptureSession#newAutofillId(AutofillId, long)}.
+     *
+     * <p>When the virtual view hierarchy represents a web page, you should also:
+     *
+     * <ul>
+     *   <li>Call {@link ContentCaptureManager#getContentCaptureConditions()} to infer content
+     *   capture events should be generate for that URL.
+     *   <li>Create a new {@link ContentCaptureSession} child for every HTML element that
+     *   renders a new URL (like an {@code IFRAME}) and use that session to notify events from
+     *   that subtree.
+     * </ul>
+     *
+     * <p><b>Note: </b>the following methods of the {@code structure} will be ignored:
+     * <ul>
+     *   <li>{@link ViewStructure#setChildCount(int)}
+     *   <li>{@link ViewStructure#addChildCount(int)}
+     *   <li>{@link ViewStructure#getChildCount()}
+     *   <li>{@link ViewStructure#newChild(int)}
+     *   <li>{@link ViewStructure#asyncNewChild(int)}
+     *   <li>{@link ViewStructure#asyncCommit()}
+     *   <li>{@link ViewStructure#setWebDomain(String)}
+     *   <li>{@link ViewStructure#newHtmlInfoBuilder(String)}
+     *   <li>{@link ViewStructure#setHtmlInfo(android.view.ViewStructure.HtmlInfo)}
+     *   <li>{@link ViewStructure#setDataIsSensitive(boolean)}
+     *   <li>{@link ViewStructure#setAlpha(float)}
+     *   <li>{@link ViewStructure#setElevation(float)}
+     *   <li>{@link ViewStructure#setTransformation(Matrix)}
+     *
+     * </ul>
+     *
+     * @hide
+     */
+    @TestApi
+    public void onProvideContentCaptureStructure(@NonNull ViewStructure structure, int flags) {
+        onProvideStructure(structure, VIEW_STRUCTURE_FOR_CONTENT_CAPTURE, flags);
+    }
+
     /** @hide */
     protected void onProvideStructure(@NonNull ViewStructure structure,
             @ViewStructureType int viewFor, int flags) {
@@ -9065,6 +9243,274 @@
     }
 
     /**
+     * Gets the mode for determining whether this view is important for content capture.
+     *
+     * <p>See {@link #setImportantForContentCapture(int)} and
+     * {@link #isImportantForContentCapture()} for more info about this mode.
+     *
+     * @return {@link #IMPORTANT_FOR_CONTENT_CAPTURE_AUTO} by default, or value passed to
+     * {@link #setImportantForContentCapture(int)}.
+     *
+     * @attr ref android.R.styleable#View_importantForContentCapture
+     *
+     * @hide
+     */
+    @ViewDebug.ExportedProperty(mapping = {
+            @ViewDebug.IntToString(from = IMPORTANT_FOR_CONTENT_CAPTURE_AUTO, to = "auto"),
+            @ViewDebug.IntToString(from = IMPORTANT_FOR_CONTENT_CAPTURE_YES, to = "yes"),
+            @ViewDebug.IntToString(from = IMPORTANT_FOR_CONTENT_CAPTURE_NO, to = "no"),
+            @ViewDebug.IntToString(from = IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS,
+                to = "yesExcludeDescendants"),
+            @ViewDebug.IntToString(from = IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS,
+                to = "noExcludeDescendants")})
+//    @InspectableProperty(enumMapping = {
+//            @EnumEntry(value = IMPORTANT_FOR_CONTENT_CAPTURE_AUTO, name = "auto"),
+//            @EnumEntry(value = IMPORTANT_FOR_CONTENT_CAPTURE_YES, name = "yes"),
+//            @EnumEntry(value = IMPORTANT_FOR_CONTENT_CAPTURE_NO, name = "no"),
+//            @EnumEntry(value = IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS,
+//                    name = "yesExcludeDescendants"),
+//            @EnumEntry(value = IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS,
+//                    name = "noExcludeDescendants"),
+//    })
+    @TestApi
+    public @ContentCaptureImportance int getImportantForContentCapture() {
+        // NOTE: the important for content capture values were the first flags added and are set in
+        // the rightmost position, so we don't need to shift them
+        return mPrivateFlags4 & PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK;
+    }
+
+    /**
+     * Sets the mode for determining whether this view is considered important for content capture.
+     *
+     * <p>The platform determines the importance for autofill automatically but you
+     * can use this method to customize the behavior. Typically, a view that provides text should
+     * be marked as {@link #IMPORTANT_FOR_CONTENT_CAPTURE_YES}.
+     *
+     * @param mode {@link #IMPORTANT_FOR_CONTENT_CAPTURE_AUTO},
+     * {@link #IMPORTANT_FOR_CONTENT_CAPTURE_YES}, {@link #IMPORTANT_FOR_CONTENT_CAPTURE_NO},
+     * {@link #IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS},
+     * or {@link #IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS}.
+     *
+     * @attr ref android.R.styleable#View_importantForContentCapture
+     *
+     * @hide
+     */
+    @TestApi
+    public void setImportantForContentCapture(@ContentCaptureImportance int mode) {
+        // Reset first
+        mPrivateFlags4 &= ~PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK;
+        // Then set again
+        // NOTE: the important for content capture values were the first flags added and are set in
+        // the rightmost position, so we don't need to shift them
+        mPrivateFlags4 |= (mode & PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK);
+    }
+
+    /**
+     * Hints the Android System whether this view is considered important for content capture, based
+     * on the value explicitly set by {@link #setImportantForContentCapture(int)} and heuristics
+     * when it's {@link #IMPORTANT_FOR_CONTENT_CAPTURE_AUTO}.
+     *
+     * <p>See {@link ContentCaptureManager} for more info about content capture.
+     *
+     * @return whether the view is considered important for content capture.
+     *
+     * @see #setImportantForContentCapture(int)
+     * @see #IMPORTANT_FOR_CONTENT_CAPTURE_AUTO
+     * @see #IMPORTANT_FOR_CONTENT_CAPTURE_YES
+     * @see #IMPORTANT_FOR_CONTENT_CAPTURE_NO
+     * @see #IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS
+     * @see #IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS
+     *
+     * @hide
+     */
+    @TestApi
+    public final boolean isImportantForContentCapture() {
+        boolean isImportant;
+        if ((mPrivateFlags4 & PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED) != 0) {
+            isImportant = (mPrivateFlags4 & PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE) != 0;
+            return isImportant;
+        }
+
+        isImportant = calculateIsImportantForContentCapture();
+
+        mPrivateFlags4 &= ~PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE;
+        if (isImportant) {
+            mPrivateFlags4 |= PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE;
+        }
+        mPrivateFlags4 |= PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED;
+        return isImportant;
+    }
+
+    /**
+     * Calculates whether the flag is important for content capture so it can be used by
+     * {@link #isImportantForContentCapture()} while the tree is traversed.
+     */
+    private boolean calculateIsImportantForContentCapture() {
+        // Check parent mode to ensure we're important
+        ViewParent parent = mParent;
+        while (parent instanceof View) {
+            final int parentImportance = ((View) parent).getImportantForContentCapture();
+            if (parentImportance == IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS
+                    || parentImportance == IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS) {
+                if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.VERBOSE)) {
+                    Log.v(CONTENT_CAPTURE_LOG_TAG, "View (" +  this + ") is not important for "
+                            + "content capture because parent " + parent + "'s importance is "
+                            + parentImportance);
+                }
+                return false;
+            }
+            parent = parent.getParent();
+        }
+
+        final int importance = getImportantForContentCapture();
+
+        // First, check the explicit states.
+        if (importance == IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS
+                || importance == IMPORTANT_FOR_CONTENT_CAPTURE_YES) {
+            return true;
+        }
+        if (importance == IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS
+                || importance == IMPORTANT_FOR_CONTENT_CAPTURE_NO) {
+            if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.VERBOSE)) {
+                Log.v(CONTENT_CAPTURE_LOG_TAG, "View (" +  this + ") is not important for content "
+                        + "capture because its importance is " + importance);
+            }
+            return false;
+        }
+
+        // Then use some heuristics to handle AUTO.
+        if (importance != IMPORTANT_FOR_CONTENT_CAPTURE_AUTO) {
+            Log.w(CONTENT_CAPTURE_LOG_TAG, "invalid content capture importance (" + importance
+                    + " on view " + this);
+            return false;
+        }
+
+        // View group is important if at least one children also is
+        if (this instanceof ViewGroup) {
+            final ViewGroup group = (ViewGroup) this;
+            for (int i = 0; i < group.getChildCount(); i++) {
+                final View child = group.getChildAt(i);
+                if (child.isImportantForContentCapture()) {
+                    return true;
+                }
+            }
+        }
+
+        // If the app developer explicitly set hints or autofill hintsfor it, it's important.
+        if (getAutofillHints() != null) {
+            return true;
+        }
+
+        // Otherwise, assume it's not important...
+        return false;
+    }
+
+    /**
+     * Helper used to notify the {@link ContentCaptureManager} when the view is removed or
+     * added, based on whether it's laid out and visible, and without knowing if the parent removed
+     * it from the view hierarchy.
+     *
+     * <p>This method is called from many places (visibility changed, view laid out, view attached
+     * or detached to/from window, etc...) and hence must contain the logic to call the manager, as
+     * described below:
+     *
+     * <ol>
+     *   <li>It should only be called when content capture is enabled for the view.
+     *   <li>It must call viewAppeared() before viewDisappeared()
+     *   <li>viewAppearead() can only be called when the view is visible and laidout
+     *   <li>It should not call the same event twice.
+     * </ol>
+     */
+    private void notifyAppearedOrDisappearedForContentCaptureIfNeeded(boolean appeared) {
+        AttachInfo ai = mAttachInfo;
+        // Skip it while the view is being laided out for the first time
+        if (ai != null && !ai.mReadyForContentCaptureUpdates) return;
+
+        if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+            Trace.traceBegin(Trace.TRACE_TAG_VIEW,
+                    "notifyContentCapture(" + appeared + ") for " + getClass().getSimpleName());
+        }
+        try {
+            notifyAppearedOrDisappearedForContentCaptureIfNeededNoTrace(appeared);
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+        }
+    }
+
+    private void notifyAppearedOrDisappearedForContentCaptureIfNeededNoTrace(boolean appeared) {
+        AttachInfo ai = mAttachInfo;
+
+        // First check if context has client, so it saves a service lookup when it doesn't
+        if (mContext.getContentCaptureOptions() == null) return;
+
+        // Then check if it's enabled in the context...
+        final ContentCaptureManager ccm = ai != null ? ai.getContentCaptureManager(mContext)
+                : mContext.getSystemService(ContentCaptureManager.class);
+        if (ccm == null || !ccm.isContentCaptureEnabled()) return;
+
+        // ... and finally at the view level
+        // NOTE: isImportantForContentCapture() is more expensive than cm.isContentCaptureEnabled()
+        if (!isImportantForContentCapture()) return;
+
+        ContentCaptureSession session = getContentCaptureSession();
+        if (session == null) return;
+
+        if (appeared) {
+            if (!isLaidOut() || getVisibility() != VISIBLE
+                    || (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0) {
+                if (DEBUG_CONTENT_CAPTURE) {
+                    Log.v(CONTENT_CAPTURE_LOG_TAG, "Ignoring 'appeared' on " + this + ": laid="
+                            + isLaidOut() + ", visibleToUser=" + isVisibleToUser()
+                            + ", visible=" + (getVisibility() == VISIBLE)
+                            + ": alreadyNotifiedAppeared=" + ((mPrivateFlags4
+                                    & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0)
+                            + ", alreadyNotifiedDisappeared=" + ((mPrivateFlags4
+                                    & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0));
+                }
+                return;
+            }
+            setNotifiedContentCaptureAppeared();
+
+            if (ai != null) {
+                ai.delayNotifyContentCaptureEvent(session, this, appeared);
+            } else {
+                if (DEBUG_CONTENT_CAPTURE) {
+                    Log.w(CONTENT_CAPTURE_LOG_TAG, "no AttachInfo on appeared for " + this);
+                }
+            }
+        } else {
+            if ((mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) == 0
+                    || (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0) {
+                if (DEBUG_CONTENT_CAPTURE) {
+                    Log.v(CONTENT_CAPTURE_LOG_TAG, "Ignoring 'disappeared' on " + this + ": laid="
+                            + isLaidOut() + ", visibleToUser=" + isVisibleToUser()
+                            + ", visible=" + (getVisibility() == VISIBLE)
+                            + ": alreadyNotifiedAppeared=" + ((mPrivateFlags4
+                                    & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0)
+                            + ", alreadyNotifiedDisappeared=" + ((mPrivateFlags4
+                                    & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0));
+                }
+                return;
+            }
+            mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED;
+            mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED;
+
+            if (ai != null) {
+                ai.delayNotifyContentCaptureEvent(session, this, appeared);
+            } else {
+                if (DEBUG_CONTENT_CAPTURE) {
+                    Log.v(CONTENT_CAPTURE_LOG_TAG, "no AttachInfo on disappeared for " + this);
+                }
+            }
+        }
+    }
+
+    private void setNotifiedContentCaptureAppeared() {
+        mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED;
+        mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED;
+    }
+
+    /**
      * Sets the (optional) {@link ContentCaptureSession} associated with this view.
      *
      * <p>This method should be called when you need to associate a {@link ContentCaptureContext} to
@@ -9317,6 +9763,68 @@
     }
 
     /**
+     * Dispatches the initial content capture events for a view structure.
+     *
+     * @hide
+     */
+    public void dispatchInitialProvideContentCaptureStructure() {
+        AttachInfo ai = mAttachInfo;
+        if (ai == null) {
+            Log.w(CONTENT_CAPTURE_LOG_TAG,
+                    "dispatchProvideContentCaptureStructure(): no AttachInfo for " + this);
+            return;
+        }
+        ContentCaptureManager ccm = ai.mContentCaptureManager;
+        if (ccm == null) {
+            Log.w(CONTENT_CAPTURE_LOG_TAG, "dispatchProvideContentCaptureStructure(): "
+                    + "no ContentCaptureManager for " + this);
+            return;
+        }
+
+        // We must set it before checkign if the view itself is important, because it might
+        // initially not be (for example, if it's empty), although that might change later (for
+        // example, if important views are added)
+        ai.mReadyForContentCaptureUpdates = true;
+
+        if (!isImportantForContentCapture()) {
+            if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.DEBUG)) {
+                Log.d(CONTENT_CAPTURE_LOG_TAG,
+                        "dispatchProvideContentCaptureStructure(): decorView is not important");
+            }
+            return;
+        }
+
+        ai.mContentCaptureManager = ccm;
+
+        ContentCaptureSession session = getContentCaptureSession();
+        if (session == null) {
+            if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.DEBUG)) {
+                Log.d(CONTENT_CAPTURE_LOG_TAG,
+                        "dispatchProvideContentCaptureStructure(): no session for " + this);
+            }
+            return;
+        }
+
+        session.internalNotifyViewTreeEvent(/* started= */ true);
+        try {
+            dispatchProvideContentCaptureStructure();
+        } finally {
+            session.internalNotifyViewTreeEvent(/* started= */ false);
+        }
+    }
+
+    /** @hide */
+    void dispatchProvideContentCaptureStructure() {
+        ContentCaptureSession session = getContentCaptureSession();
+        if (session != null) {
+            ViewStructure structure = session.newViewStructure(this);
+            onProvideContentCaptureStructure(structure, /* flags= */ 0);
+            setNotifiedContentCaptureAppeared();
+            session.notifyViewAppeared(structure);
+        }
+    }
+
+    /**
      * @see #onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)
      *
      * Note: Called from the default {@link AccessibilityDelegate}.
@@ -13266,6 +13774,7 @@
     public void dispatchStartTemporaryDetach() {
         mPrivateFlags3 |= PFLAG3_TEMPORARY_DETACH;
         notifyEnterOrExitForAutoFillIfNeeded(false);
+        notifyAppearedOrDisappearedForContentCaptureIfNeeded(false);
         onStartTemporaryDetach();
     }
 
@@ -13292,6 +13801,7 @@
             notifyFocusChangeToInputMethodManager(true /* hasFocus */);
         }
         notifyEnterOrExitForAutoFillIfNeeded(true);
+        notifyAppearedOrDisappearedForContentCaptureIfNeeded(true);
     }
 
     /**
@@ -13883,6 +14393,8 @@
                         : AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED);
             }
         }
+
+        notifyAppearedOrDisappearedForContentCaptureIfNeeded(isVisible);
     }
 
     /**
@@ -17578,6 +18090,7 @@
         }
 
         // Reset content capture caches
+        mPrivateFlags4 &= ~PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK;
         mCachedContentCaptureSession = null;
 
         if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)
@@ -19587,6 +20100,7 @@
         needGlobalAttributesUpdate(false);
 
         notifyEnterOrExitForAutoFillIfNeeded(true);
+        notifyAppearedOrDisappearedForContentCaptureIfNeeded(true);
     }
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
@@ -19636,6 +20150,7 @@
         }
 
         notifyEnterOrExitForAutoFillIfNeeded(false);
+        notifyAppearedOrDisappearedForContentCaptureIfNeeded(false);
     }
 
     /**
@@ -21970,6 +22485,8 @@
             mPrivateFlags3 &= ~PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT;
             notifyEnterOrExitForAutoFillIfNeeded(true);
         }
+
+        notifyAppearedOrDisappearedForContentCaptureIfNeeded(true);
     }
 
     private boolean hasParentWantsFocus() {
@@ -28035,6 +28552,12 @@
         boolean mHandlingPointerEvent;
 
         /**
+         * The offset of this view's window when it's on an embedded display that is re-parented
+         * to another window.
+         */
+        final Point mLocationInParentDisplay = new Point();
+
+        /**
          * Global to the view hierarchy used as a temporary for dealing with
          * x/y points in the transparent region computations.
          */
@@ -28181,6 +28704,23 @@
         View mTooltipHost;
 
         /**
+         * The initial structure has been reported so the view is ready to report updates.
+         */
+        boolean mReadyForContentCaptureUpdates;
+
+        /**
+         * Map(keyed by session) of content capture events that need to be notified after the view
+         * hierarchy is traversed: value is either the view itself for appearead events, or its
+         * autofill id for disappeared.
+         */
+        SparseArray<ArrayList<Object>> mContentCaptureEvents;
+
+        /**
+         * Cached reference to the {@link ContentCaptureManager}.
+         */
+        ContentCaptureManager mContentCaptureManager;
+
+        /**
          * Creates a new set of attachment information with the specified
          * events handler and thread.
          *
@@ -28198,6 +28738,31 @@
             mRootCallbacks = effectPlayer;
             mTreeObserver = new ViewTreeObserver(context);
         }
+
+        private void delayNotifyContentCaptureEvent(@NonNull ContentCaptureSession session,
+                @NonNull View view, boolean appeared) {
+            if (mContentCaptureEvents == null) {
+                // Most of the time there will be just one session, so intial capacity is 1
+                mContentCaptureEvents = new SparseArray<>(1);
+            }
+            int sessionId = session.getId();
+            // TODO: life would be much easier if we provided a MultiMap implementation somwhere...
+            ArrayList<Object> events = mContentCaptureEvents.get(sessionId);
+            if (events == null) {
+                events = new ArrayList<>();
+                mContentCaptureEvents.put(sessionId, events);
+            }
+            events.add(appeared ? view : view.getAutofillId());
+        }
+
+        @Nullable
+        ContentCaptureManager getContentCaptureManager(@NonNull Context context) {
+            if (mContentCaptureManager != null) {
+                return mContentCaptureManager;
+            }
+            mContentCaptureManager = context.getSystemService(ContentCaptureManager.class);
+            return mContentCaptureManager;
+        }
     }
 
     /**
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index d362024..937bd1b 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3606,7 +3606,7 @@
             return;
         }
 
-        final ChildListForAutofill children = getChildrenForAutofill(flags);
+        final ChildListForAutoFillOrContentCapture children = getChildrenForAutofill(flags);
         final int childrenCount = children.size();
         structure.setChildCount(childrenCount);
         for (int i = 0; i < childrenCount; i++) {
@@ -3617,14 +3617,30 @@
         children.recycle();
     }
 
+    /** @hide */
+    @Override
+    public void dispatchProvideContentCaptureStructure() {
+        super.dispatchProvideContentCaptureStructure();
+
+        if (!isLaidOut()) return;
+
+        final ChildListForAutoFillOrContentCapture children = getChildrenForContentCapture();
+        final int childrenCount = children.size();
+        for (int i = 0; i < childrenCount; i++) {
+            final View child = children.get(i);
+            child.dispatchProvideContentCaptureStructure();
+        }
+        children.recycle();
+    }
+
     /**
      * Gets the children for autofill. Children for autofill are the first
      * level descendants that are important for autofill. The returned
      * child list object is pooled and the caller must recycle it once done.
      * @hide */
-    private @NonNull ChildListForAutofill getChildrenForAutofill(
+    private @NonNull ChildListForAutoFillOrContentCapture getChildrenForAutofill(
             @AutofillFlags int flags) {
-        final ChildListForAutofill children = ChildListForAutofill
+        final ChildListForAutoFillOrContentCapture children = ChildListForAutoFillOrContentCapture
                 .obtain();
         populateChildrenForAutofill(children, flags);
         return children;
@@ -3652,6 +3668,34 @@
         }
     }
 
+    private @NonNull ChildListForAutoFillOrContentCapture getChildrenForContentCapture() {
+        final ChildListForAutoFillOrContentCapture children = ChildListForAutoFillOrContentCapture
+                .obtain();
+        populateChildrenForContentCapture(children);
+        return children;
+    }
+
+    /** @hide */
+    private void populateChildrenForContentCapture(ArrayList<View> list) {
+        final int childrenCount = mChildrenCount;
+        if (childrenCount <= 0) {
+            return;
+        }
+        final ArrayList<View> preorderedList = buildOrderedChildList();
+        final boolean customOrder = preorderedList == null
+                && isChildrenDrawingOrderEnabled();
+        for (int i = 0; i < childrenCount; i++) {
+            final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
+            final View child = (preorderedList == null)
+                    ? mChildren[childIndex] : preorderedList.get(childIndex);
+            if (child.isImportantForContentCapture()) {
+                list.add(child);
+            } else if (child instanceof ViewGroup) {
+                ((ViewGroup) child).populateChildrenForContentCapture(list);
+            }
+        }
+    }
+
     private static View getAndVerifyPreorderedView(ArrayList<View> preorderedList, View[] children,
             int childIndex) {
         final View child;
@@ -8634,16 +8678,16 @@
     /**
      * Pooled class that to hold the children for autifill.
      */
-    private static class ChildListForAutofill extends ArrayList<View> {
+    private static class ChildListForAutoFillOrContentCapture extends ArrayList<View> {
         private static final int MAX_POOL_SIZE = 32;
 
-        private static final Pools.SimplePool<ChildListForAutofill> sPool =
+        private static final Pools.SimplePool<ChildListForAutoFillOrContentCapture> sPool =
                 new Pools.SimplePool<>(MAX_POOL_SIZE);
 
-        public static ChildListForAutofill obtain() {
-            ChildListForAutofill list = sPool.acquire();
+        public static ChildListForAutoFillOrContentCapture obtain() {
+            ChildListForAutoFillOrContentCapture list = sPool.acquire();
             if (list == null) {
-                list = new ChildListForAutofill();
+                list = new ChildListForAutoFillOrContentCapture();
             }
             return list;
         }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index e835675..e071120 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -105,7 +105,11 @@
 import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
 import android.view.animation.AccelerateDecelerateInterpolator;
 import android.view.animation.Interpolator;
+import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillManager;
+import android.view.contentcapture.ContentCaptureManager;
+import android.view.contentcapture.ContentCaptureSession;
+import android.view.contentcapture.MainContentCaptureSession;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.Scroller;
 
@@ -220,6 +224,21 @@
      */
     static final int MAX_TRACKBALL_DELAY = 250;
 
+    /**
+     * Initial value for {@link #mContentCaptureEnabled}.
+     */
+    private static final int CONTENT_CAPTURE_ENABLED_NOT_CHECKED = 0;
+
+    /**
+     * Value for {@link #mContentCaptureEnabled} when it was checked and set to {@code true}.
+     */
+    private static final int CONTENT_CAPTURE_ENABLED_TRUE = 1;
+
+    /**
+     * Value for {@link #mContentCaptureEnabled} when it was checked and set to {@code false}.
+     */
+    private static final int CONTENT_CAPTURE_ENABLED_FALSE = 2;
+
     @UnsupportedAppUsage
     static final ThreadLocal<HandlerActionQueue> sRunQueues = new ThreadLocal<HandlerActionQueue>();
 
@@ -410,6 +429,10 @@
     boolean mLayoutRequested;
     boolean mFirst;
 
+    @Nullable
+    int mContentCaptureEnabled = CONTENT_CAPTURE_ENABLED_NOT_CHECKED;
+    boolean mPerformContentCapture;
+
     boolean mReportNextDraw;
     boolean mFullRedrawNeeded;
     boolean mNewSurfaceNeeded;
@@ -607,6 +630,7 @@
         mTransparentRegion = new Region();
         mPreviousTransparentRegion = new Region();
         mFirst = true; // true for the first time the view is added
+        mPerformContentCapture = true; // also true for the first time the view is added
         mAdded = false;
         mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this,
                 context);
@@ -2756,9 +2780,55 @@
             }
         }
 
+        if (mAttachInfo.mContentCaptureEvents != null) {
+            notifyContentCatpureEvents();
+        }
+
         mIsInTraversal = false;
     }
 
+    private void notifyContentCatpureEvents() {
+        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "notifyContentCaptureEvents");
+        try {
+            MainContentCaptureSession mainSession = mAttachInfo.mContentCaptureManager
+                    .getMainContentCaptureSession();
+            for (int i = 0; i < mAttachInfo.mContentCaptureEvents.size(); i++) {
+                int sessionId = mAttachInfo.mContentCaptureEvents.keyAt(i);
+                mainSession.notifyViewTreeEvent(sessionId, /* started= */ true);
+                ArrayList<Object> events = mAttachInfo.mContentCaptureEvents
+                        .valueAt(i);
+                for_each_event: for (int j = 0; j < events.size(); j++) {
+                    Object event = events.get(j);
+                    if (event instanceof AutofillId) {
+                        mainSession.notifyViewDisappeared(sessionId, (AutofillId) event);
+                    } else if (event instanceof View) {
+                        View view = (View) event;
+                        ContentCaptureSession session = view.getContentCaptureSession();
+                        if (session == null) {
+                            Log.w(mTag, "no content capture session on view: " + view);
+                            continue for_each_event;
+                        }
+                        int actualId = session.getId();
+                        if (actualId != sessionId) {
+                            Log.w(mTag, "content capture session mismatch for view (" + view
+                                    + "): was " + sessionId + " before, it's " + actualId + " now");
+                            continue for_each_event;
+                        }
+                        ViewStructure structure = session.newViewStructure(view);
+                        view.onProvideContentCaptureStructure(structure, /* flags= */ 0);
+                        session.notifyViewAppeared(structure);
+                    } else {
+                        Log.w(mTag, "invalid content capture event: " + event);
+                    }
+                }
+                mainSession.notifyViewTreeEvent(sessionId, /* started= */ false);
+            }
+            mAttachInfo.mContentCaptureEvents = null;
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+        }
+    }
+
     private void notifySurfaceDestroyed() {
         mSurfaceHolder.ungetCallbacks();
         SurfaceHolder.Callback[] callbacks = mSurfaceHolder.getCallbacks();
@@ -2893,6 +2963,13 @@
             }
         }
         mFirstInputStage.onWindowFocusChanged(hasWindowFocus);
+
+        // NOTE: there's no view visibility (appeared / disapparead) events when the windows focus
+        // is lost, so we don't need to to force a flush - there might be other events such as
+        // text changes, but these should be flushed independently.
+        if (hasWindowFocus) {
+            handleContentCaptureFlush();
+        }
     }
 
     private void fireAccessibilityFocusEventIfHasFocusedNode() {
@@ -3459,6 +3536,86 @@
                 pendingDrawFinished();
             }
         }
+        if (mPerformContentCapture) {
+            performContentCaptureInitialReport();
+        }
+    }
+
+    /**
+     * Checks (and caches) if content capture is enabled for this context.
+     */
+    private boolean isContentCaptureEnabled() {
+        switch (mContentCaptureEnabled) {
+            case CONTENT_CAPTURE_ENABLED_TRUE:
+                return true;
+            case CONTENT_CAPTURE_ENABLED_FALSE:
+                return false;
+            case CONTENT_CAPTURE_ENABLED_NOT_CHECKED:
+                final boolean reallyEnabled = isContentCaptureReallyEnabled();
+                mContentCaptureEnabled = reallyEnabled ? CONTENT_CAPTURE_ENABLED_TRUE
+                        : CONTENT_CAPTURE_ENABLED_FALSE;
+                return reallyEnabled;
+            default:
+                Log.w(TAG, "isContentCaptureEnabled(): invalid state " + mContentCaptureEnabled);
+                return false;
+        }
+
+    }
+
+    /**
+     * Checks (without caching) if content capture is enabled for this context.
+     */
+    private boolean isContentCaptureReallyEnabled() {
+        // First check if context supports it, so it saves a service lookup when it doesn't
+        if (mContext.getContentCaptureOptions() == null) return false;
+
+        final ContentCaptureManager ccm = mAttachInfo.getContentCaptureManager(mContext);
+        // Then check if it's enabled in the contex itself.
+        if (ccm == null || !ccm.isContentCaptureEnabled()) return false;
+
+        return true;
+    }
+
+    private void performContentCaptureInitialReport() {
+        mPerformContentCapture = false; // One-time offer!
+        final View rootView = mView;
+        if (DEBUG_CONTENT_CAPTURE) {
+            Log.v(mTag, "performContentCaptureInitialReport() on " + rootView);
+        }
+        if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "dispatchContentCapture() for "
+                    + getClass().getSimpleName());
+        }
+        try {
+            if (!isContentCaptureEnabled()) return;
+
+            // Content capture is a go!
+            rootView.dispatchInitialProvideContentCaptureStructure();
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+        }
+    }
+
+    private void handleContentCaptureFlush() {
+        if (DEBUG_CONTENT_CAPTURE) {
+            Log.v(mTag, "handleContentCaptureFlush()");
+        }
+        if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "flushContentCapture for "
+                    + getClass().getSimpleName());
+        }
+        try {
+            if (!isContentCaptureEnabled()) return;
+
+            final ContentCaptureManager ccm = mAttachInfo.mContentCaptureManager;
+            if (ccm == null) {
+                Log.w(TAG, "No ContentCapture on AttachInfo");
+                return;
+            }
+            ccm.flush(ContentCaptureSession.FLUSH_REASON_VIEW_ROOT_ENTERED);
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+        }
     }
 
     private boolean draw(boolean fullRedrawNeeded) {
@@ -3825,6 +3982,13 @@
         }
     }
 
+    void updateLocationInParentDisplay(int x, int y) {
+        if (mAttachInfo != null
+                && !mAttachInfo.mLocationInParentDisplay.equals(x, y)) {
+            mAttachInfo.mLocationInParentDisplay.set(x, y);
+        }
+    }
+
     /**
      * Set the root-level system gesture exclusion rects. These are added to those provided by
      * the root's view hierarchy.
@@ -4329,6 +4493,7 @@
     private static final int MSG_INSETS_CHANGED = 30;
     private static final int MSG_INSETS_CONTROL_CHANGED = 31;
     private static final int MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED = 32;
+    private static final int MSG_LOCATION_IN_PARENT_DISPLAY_CHANGED = 33;
 
     final class ViewRootHandler extends Handler {
         @Override
@@ -4390,6 +4555,8 @@
                     return "MSG_INSETS_CONTROL_CHANGED";
                 case MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED:
                     return "MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED";
+                case MSG_LOCATION_IN_PARENT_DISPLAY_CHANGED:
+                    return "MSG_LOCATION_IN_PARENT_DISPLAY_CHANGED";
             }
             return super.getMessageName(message);
         }
@@ -4623,6 +4790,9 @@
                 case MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED: {
                     systemGestureExclusionChanged();
                 } break;
+                case MSG_LOCATION_IN_PARENT_DISPLAY_CHANGED: {
+                    updateLocationInParentDisplay(msg.arg1, msg.arg2);
+                } break;
             }
         }
     }
@@ -7829,6 +7999,17 @@
         mHandler.sendMessage(msg);
     }
 
+    /**
+     * Dispatch the offset changed.
+     *
+     * @param offset the offset of this view in the parent window.
+     */
+    public void dispatchLocationInParentDisplayChanged(Point offset) {
+        Message msg =
+                mHandler.obtainMessage(MSG_LOCATION_IN_PARENT_DISPLAY_CHANGED, offset.x, offset.y);
+        mHandler.sendMessage(msg);
+    }
+
     public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
         synchronized (this) {
             mWindowFocusChanged = true;
@@ -8356,6 +8537,14 @@
         }
 
         @Override
+        public void locationInParentDisplayChanged(Point offset) {
+            final ViewRootImpl viewAncestor = mViewAncestor.get();
+            if (viewAncestor != null) {
+                viewAncestor.dispatchLocationInParentDisplayChanged(offset);
+            }
+        }
+
+        @Override
         public void insetsChanged(InsetsState insetsState) {
             final ViewRootImpl viewAncestor = mViewAncestor.get();
             if (viewAncestor != null) {
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index a25f2ee..1f89de8 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -261,6 +261,13 @@
     int TRANSIT_TASK_CHANGE_WINDOWING_MODE = 27;
 
     /**
+     * A display which can only contain one task is being shown because the first activity is
+     * started or it's being turned on.
+     * @hide
+     */
+    int TRANSIT_SHOW_SINGLE_TASK_DISPLAY = 28;
+
+    /**
      * @hide
      */
     @IntDef(prefix = { "TRANSIT_" }, value = {
@@ -287,7 +294,8 @@
             TRANSIT_TRANSLUCENT_ACTIVITY_OPEN,
             TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE,
             TRANSIT_CRASHING_ACTIVITY_CLOSE,
-            TRANSIT_TASK_CHANGE_WINDOWING_MODE
+            TRANSIT_TASK_CHANGE_WINDOWING_MODE,
+            TRANSIT_SHOW_SINGLE_TASK_DISPLAY
     })
     @Retention(RetentionPolicy.SOURCE)
     @interface TransitionType {}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 137b67c..9dc66d7 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -413,6 +413,9 @@
         if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) {
             setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
         }
+        if (getImportantForContentCapture() == IMPORTANT_FOR_CONTENT_CAPTURE_AUTO) {
+            setImportantForContentCapture(IMPORTANT_FOR_CONTENT_CAPTURE_YES);
+        }
 
         if (context == null) {
             throw new IllegalArgumentException("Invalid context argument");
@@ -2795,6 +2798,12 @@
         mProvider.getViewDelegate().onProvideAutofillVirtualStructure(structure, flags);
     }
 
+    /** @hide */
+    @Override
+    public void onProvideContentCaptureStructure(ViewStructure structure, int flags) {
+        mProvider.getViewDelegate().onProvideContentCaptureStructure(structure, flags);
+    }
+
     @Override
     public void autofill(SparseArray<AutofillValue>values) {
         mProvider.getViewDelegate().autofill(values);
diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java
index c3bb9a0..c55f7d6 100644
--- a/core/java/android/widget/AdapterView.java
+++ b/core/java/android/widget/AdapterView.java
@@ -1318,7 +1318,8 @@
             @ViewStructureType int viewFor, int flags) {
         super.onProvideStructure(structure, viewFor, flags);
 
-        if (viewFor == VIEW_STRUCTURE_FOR_AUTOFILL) {
+        if (viewFor == VIEW_STRUCTURE_FOR_AUTOFILL
+                || viewFor == VIEW_STRUCTURE_FOR_CONTENT_CAPTURE) {
             final Adapter adapter = getAdapter();
             if (adapter == null) return;
 
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index a9e183a..cdbec29 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -162,6 +162,8 @@
 import android.view.animation.AnimationUtils;
 import android.view.autofill.AutofillManager;
 import android.view.autofill.AutofillValue;
+import android.view.contentcapture.ContentCaptureManager;
+import android.view.contentcapture.ContentCaptureSession;
 import android.view.inputmethod.BaseInputConnection;
 import android.view.inputmethod.CompletionInfo;
 import android.view.inputmethod.CorrectionInfo;
@@ -977,6 +979,9 @@
         if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) {
             setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
         }
+        if (getImportantForContentCapture() == IMPORTANT_FOR_CONTENT_CAPTURE_AUTO) {
+            setImportantForContentCapture(IMPORTANT_FOR_CONTENT_CAPTURE_YES);
+        }
 
         setTextInternal("");
 
@@ -10550,7 +10555,8 @@
     }
 
     /**
-     * Notify managers (such as {@link AutofillManager}) that are interested in text changes.
+     * Notify managers (such as {@link AutofillManager} and {@link ContentCaptureManager}) that are
+     * interested on text changes.
      */
     private void notifyListeningManagersAfterTextChanged() {
 
@@ -10566,6 +10572,22 @@
                 afm.notifyValueChanged(TextView.this);
             }
         }
+
+        // TODO(b/121045053): should use a flag / boolean to keep status of SHOWN / HIDDEN instead
+        // of using isLaidout(), so it's not called in cases where it's laid out but a
+        // notifyAppeared was not sent.
+
+        // ContentCapture
+        if (isLaidOut() && isImportantForContentCapture() && isTextEditable()) {
+            final ContentCaptureManager cm = mContext.getSystemService(ContentCaptureManager.class);
+            if (cm != null && cm.isContentCaptureEnabled()) {
+                final ContentCaptureSession session = getContentCaptureSession();
+                if (session != null) {
+                    // TODO(b/111276913): pass flags when edited by user / add CTS test
+                    session.notifyViewTextChanged(getAutofillId(), getText());
+                }
+            }
+        }
     }
 
     private boolean isAutofillable() {
@@ -11409,7 +11431,8 @@
 
         final boolean isPassword = hasPasswordTransformationMethod()
                 || isPasswordInputType(getInputType());
-        if (viewFor == VIEW_STRUCTURE_FOR_AUTOFILL) {
+        if (viewFor == VIEW_STRUCTURE_FOR_AUTOFILL
+                || viewFor == VIEW_STRUCTURE_FOR_CONTENT_CAPTURE) {
             if (viewFor == VIEW_STRUCTURE_FOR_AUTOFILL) {
                 structure.setDataIsSensitive(!mTextSetFromXmlOrResourceId);
             }
@@ -11425,8 +11448,12 @@
             }
         }
 
-        if (!isPassword || viewFor == VIEW_STRUCTURE_FOR_AUTOFILL) {
+        if (!isPassword || viewFor == VIEW_STRUCTURE_FOR_AUTOFILL
+                || viewFor == VIEW_STRUCTURE_FOR_CONTENT_CAPTURE) {
             if (mLayout == null) {
+                if (viewFor == VIEW_STRUCTURE_FOR_CONTENT_CAPTURE) {
+                    Log.w(LOG_TAG, "onProvideContentCaptureStructure(): calling assumeLayout()");
+                }
                 assumeLayout();
             }
             Layout layout = mLayout;
@@ -11514,7 +11541,8 @@
                 }
             }
 
-            if (viewFor == VIEW_STRUCTURE_FOR_ASSIST) {
+            if (viewFor == VIEW_STRUCTURE_FOR_ASSIST
+                    || viewFor == VIEW_STRUCTURE_FOR_CONTENT_CAPTURE) {
                 // Extract style information that applies to the TextView as a whole.
                 int style = 0;
                 int typefaceStyle = getTypefaceStyle();
@@ -11542,7 +11570,8 @@
                 structure.setTextStyle(getTextSize(), getCurrentTextColor(),
                         AssistStructure.ViewNode.TEXT_COLOR_UNDEFINED /* bgColor */, style);
             }
-            if (viewFor == VIEW_STRUCTURE_FOR_AUTOFILL) {
+            if (viewFor == VIEW_STRUCTURE_FOR_AUTOFILL
+                    || viewFor == VIEW_STRUCTURE_FOR_CONTENT_CAPTURE) {
                 structure.setMinTextEms(getMinEms());
                 structure.setMaxTextEms(getMaxEms());
                 int maxLength = -1;
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index fb9ff15..f9cdf3d 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.view;
 
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.hardware.input.InputManager;
 import android.os.Bundle;
@@ -55,6 +56,10 @@
     }
 
     @Override
+    public void locationInParentDisplayChanged(Point offset) {
+    }
+
+    @Override
     public void insetsChanged(InsetsState insetsState) {
     }
 
diff --git a/core/res/res/drawable/ic_battery_80_24dp.xml b/core/res/res/drawable/ic_battery_80_24dp.xml
new file mode 100644
index 0000000..2513d0d
--- /dev/null
+++ b/core/res/res/drawable/ic_battery_80_24dp.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2019 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M9.5,2v2H7.33C6.6,4 6,4.6 6,5.33V15v5.67C6,21.4 6.6,22 7.33,22h9.33C17.4,22 18,21.4 18,20.67V15V5.33C18,4.6 17.4,4 16.67,4H14.5V2H9.5zM8,20v-5V6h8v9v5H8L8,20z"/>
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M16.67,22H7.33C6.6,22 6,21.4 6,20.67V8h12v12.67C18,21.4 17.4,22 16.67,22z"/>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 5b917cc..fe49a31 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1499,16 +1499,16 @@
     <string name="fingerprint_icon_content_description">Fingerprint icon</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=70] -->
-    <string name="permlab_manageFace">manage face authentication hardware</string>
+    <string name="permlab_manageFace">manage face unlock hardware</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=90] -->
     <string name="permdesc_manageFace">Allows the app to invoke methods to add and delete facial templates for use.</string>
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=70] -->
-    <string name="permlab_useFaceAuthentication">use face authentication hardware</string>
+    <string name="permlab_useFaceAuthentication">use face unlock hardware</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=90] -->
-    <string name="permdesc_useFaceAuthentication">Allows the app to use face authentication hardware for authentication</string>
+    <string name="permdesc_useFaceAuthentication">Allows the app to use face unlock hardware for authentication</string>
 
     <!-- Notification name shown when the system requires the user to re-enroll their face. [CHAR LIMIT=NONE] -->
-    <string name="face_recalibrate_notification_name">Face Authentication</string>
+    <string name="face_recalibrate_notification_name">Face unlock</string>
     <!-- Notification title shown when the system requires the user to re-enroll their face. [CHAR LIMIT=NONE] -->
     <string name="face_recalibrate_notification_title">Re-enroll your face</string>
     <!-- Notification content shown when the system requires the user to re-enroll their face. [CHAR LIMIT=NONE] -->
@@ -1561,23 +1561,23 @@
     <!-- Error message shown when the face hardware can't be accessed. [CHAR LIMIT=69] -->
     <string name="face_error_hw_not_available">Can\u2019t verify face. Hardware not available.</string>
     <!-- Error message shown when the face hardware timer has expired and the user needs to restart the operation. [CHAR LIMIT=50] -->
-    <string name="face_error_timeout">Try face authentication again.</string>
+    <string name="face_error_timeout">Try face unlock again.</string>
     <!-- Error message shown when the face hardware has run out of room for storing faces. [CHAR LIMIT=69] -->
     <string name="face_error_no_space">Can\u2019t store new face data. Delete an old one first.</string>
     <!-- Generic error message shown when the face operation (e.g. enrollment or authentication) is canceled. Generally not shown to the user. [CHAR LIMIT=50] -->
-    <string name="face_error_canceled">Face operation canceled</string>
-    <!-- Generic error message shown when the face authentication operation is canceled due to user input. Generally not shown to the user [CHAR LIMIT=54] -->
-    <string name="face_error_user_canceled">Face authentication canceled by user</string>
+    <string name="face_error_canceled">Face operation canceled.</string>
+    <!-- Generic error message shown when the face unlock operation is canceled due to user input. Generally not shown to the user [CHAR LIMIT=54] -->
+    <string name="face_error_user_canceled">Face unlock canceled by user.</string>
     <!-- Generic error message shown when the face operation fails because too many attempts have been made. [CHAR LIMIT=50] -->
     <string name="face_error_lockout">Too many attempts. Try again later.</string>
     <!-- Generic error message shown when the face operation fails because strong authentication is required. [CHAR LIMIT=71] -->
-    <string name="face_error_lockout_permanent">Too many attempts. Face authentication disabled.</string>
+    <string name="face_error_lockout_permanent">Too many attempts. Face unlock disabled.</string>
     <!-- Generic error message shown when the face hardware can't recognize the face. [CHAR LIMIT=50] -->
     <string name="face_error_unable_to_process">Can\u2019t verify face. Try again.</string>
     <!-- Generic error message shown when the user has no enrolled face. [CHAR LIMIT=52] -->
-    <string name="face_error_not_enrolled">You haven\u2019t set up face authentication</string>
-    <!-- Generic error message shown when the app requests face authentication on a device without a sensor. [CHAR LIMIT=61] -->
-    <string name="face_error_hw_not_present">Face authentication is not supported on this device</string>
+    <string name="face_error_not_enrolled">You haven\u2019t set up face unlock.</string>
+    <!-- Generic error message shown when the app requests face unlock on a device without a sensor. [CHAR LIMIT=61] -->
+    <string name="face_error_hw_not_present">Face unlock is not supported on this device.</string>
 
     <!-- Template to be used to name enrolled faces by default. [CHAR LIMIT=10] -->
     <string name="face_name_template">Face <xliff:g id="faceId" example="1">%d</xliff:g></string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/graph/SignalDrawable.java b/packages/SettingsLib/src/com/android/settingslib/graph/SignalDrawable.java
index 98eb573..7ce713b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/graph/SignalDrawable.java
+++ b/packages/SettingsLib/src/com/android/settingslib/graph/SignalDrawable.java
@@ -60,6 +60,7 @@
     private static final int NUM_LEVEL_MASK = 0xff << NUM_LEVEL_SHIFT;
     private static final int STATE_SHIFT = 16;
     private static final int STATE_MASK = 0xff << STATE_SHIFT;
+    private static final int STATE_EMPTY = 1;
     private static final int STATE_CUT = 2;
     private static final int STATE_CARRIER_CHANGE = 3;
 
@@ -203,7 +204,7 @@
             drawDotAndPadding(x - dotSpacing * 2, y, dotPadding, dotSize, 0);
             canvas.drawPath(mCutoutPath, mTransparentPaint);
             canvas.drawPath(mForegroundPath, mForegroundPaint);
-        } else if (isInState(STATE_CUT)) {
+        } else if (isInState(STATE_CUT) || isInState(STATE_EMPTY)) {
             float cut = (CUT_OUT * width);
             mCutoutPath.moveTo(width - padding, height - padding);
             mCutoutPath.rLineTo(-cut, 0);
@@ -268,13 +269,14 @@
     /**
      * Returns whether this drawable is in the specified state.
      *
-     * @param state must be one of {@link #STATE_CARRIER_CHANGE} or {@link #STATE_CUT}
+     * @param state must be one of {@link #STATE_CARRIER_CHANGE}, {@link #STATE_CUT},
+     *              or {@link #STATE_EMPTY}.
      */
     private boolean isInState(int state) {
         return getState(getLevel()) == state;
     }
 
-    public static int getState(int fullState) {
+    private static int getState(int fullState) {
         return (fullState & STATE_MASK) >> STATE_SHIFT;
     }
 
@@ -286,7 +288,12 @@
 
     /** Returns the state representing empty mobile signal with the given number of levels. */
     public static int getEmptyState(int numLevels) {
-        return getState(0, numLevels, true);
+        return (STATE_EMPTY << STATE_SHIFT) | (numLevels << NUM_LEVEL_SHIFT);
+    }
+
+    /** Returns whether fullState corresponds to the empty state. */
+    public static boolean isEmptyState(int fullState) {
+        return getState(fullState) == STATE_EMPTY;
     }
 
     /** Returns the state representing carrier change with the given number of levels. */
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
index 21b3a00..bd2b19c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
@@ -64,6 +64,14 @@
         onActivityLaunchOnSecondaryDisplayRerouted();
     }
 
+    /**
+     * Called when contents are drawn for the first time on a display which can only contain one
+     * task.
+     *
+     * @param displayId the id of the display on which contents are drawn.
+     */
+    public void onSingleTaskDisplayDrawn(int displayId) { }
+
     public void onTaskProfileLocked(int taskId, int userId) { }
     public void onTaskCreated(int taskId, ComponentName componentName) { }
     public void onTaskRemoved(int taskId) { }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
index 06ae399..c89f2ab 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
@@ -196,11 +196,18 @@
     }
 
     @Override
-    public void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken) {
+    public void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken)
+            throws RemoteException {
         mHandler.obtainMessage(H.ON_SIZE_COMPAT_MODE_ACTIVITY_CHANGED, displayId, 0 /* unused */,
                 activityToken).sendToTarget();
     }
 
+    @Override
+    public void onSingleTaskDisplayDrawn(int displayId) throws RemoteException {
+        mHandler.obtainMessage(H.ON_SINGLE_TASK_DISPLAY_DRAWN, displayId,
+                0 /* unused */).sendToTarget();
+    }
+
     private final class H extends Handler {
         private static final int ON_TASK_STACK_CHANGED = 1;
         private static final int ON_TASK_SNAPSHOT_CHANGED = 2;
@@ -220,6 +227,7 @@
         private static final int ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED = 16;
         private static final int ON_SIZE_COMPAT_MODE_ACTIVITY_CHANGED = 17;
         private static final int ON_BACK_PRESSED_ON_TASK_ROOT = 18;
+        private static final int ON_SINGLE_TASK_DISPLAY_DRAWN = 19;
 
 
         public H(Looper looper) {
@@ -356,6 +364,12 @@
                         }
                         break;
                     }
+                    case ON_SINGLE_TASK_DISPLAY_DRAWN: {
+                        for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+                            mTaskStackListeners.get(i).onSingleTaskDisplayDrawn(msg.arg1);
+                        }
+                        break;
+                    }
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
index 2ff7266..a4b6958 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
@@ -310,7 +310,9 @@
 
     @Override
     public void showMessage(CharSequence message, ColorStateList colorState) {
-        mSecurityMessageDisplay.setNextMessageColor(colorState);
+        if (colorState != null) {
+            mSecurityMessageDisplay.setNextMessageColor(colorState);
+        }
         mSecurityMessageDisplay.setMessage(message);
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
index d8086da..362ead3 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
@@ -442,7 +442,9 @@
 
     @Override
     public void showMessage(CharSequence message, ColorStateList colorState) {
-        mSecurityMessageDisplay.setNextMessageColor(colorState);
+        if (colorState != null) {
+            mSecurityMessageDisplay.setNextMessageColor(colorState);
+        }
         mSecurityMessageDisplay.setMessage(message);
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 6cd971d..eef61db 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -231,8 +231,10 @@
         }
         if (action == MotionEvent.ACTION_UP) {
             if (-getTranslationY() > TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
-                    MIN_DRAG_SIZE, getResources().getDisplayMetrics())) {
+                    MIN_DRAG_SIZE, getResources().getDisplayMetrics())
+                    && !mUpdateMonitor.isFaceDetectionRunning()) {
                 mUpdateMonitor.requestFaceAuth();
+                showMessage(null, null);
             }
         }
         return true;
@@ -267,7 +269,8 @@
         mSwipeUpToRetry = mUpdateMonitor.isUnlockWithFacePossible(userId)
                 && securityMode != SecurityMode.SimPin
                 && securityMode != SecurityMode.SimPuk
-                && securityMode != SecurityMode.None;
+                && securityMode != SecurityMode.None
+                && securityMode != SecurityMode.Pattern;
     }
 
     public CharSequence getTitle() {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 6a4dbc8d..873874f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -168,6 +168,9 @@
      */
     private static final int BIOMETRIC_STATE_CANCELLING_RESTARTING = 3;
 
+    private static final int BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED = -1;
+    public static final int BIOMETRIC_HELP_FACE_NOT_RECOGNIZED = -2;
+
     private static final int DEFAULT_CHARGING_VOLTAGE_MICRO_VOLT = 5000000;
 
     private static final ComponentName FALLBACK_HOME_COMPONENT = new ComponentName(
@@ -570,7 +573,8 @@
                 cb.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
             }
         }
-        handleFingerprintHelp(-1, mContext.getString(R.string.kg_fingerprint_not_recognized));
+        handleFingerprintHelp(BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED,
+                mContext.getString(R.string.kg_fingerprint_not_recognized));
     }
 
     private void handleFingerprintAcquired(int acquireInfo) {
@@ -722,7 +726,8 @@
                 cb.onBiometricAuthFailed(BiometricSourceType.FACE);
             }
         }
-        handleFaceHelp(-1, mContext.getString(R.string.kg_face_not_recognized));
+        handleFaceHelp(BIOMETRIC_HELP_FACE_NOT_RECOGNIZED,
+                mContext.getString(R.string.kg_face_not_recognized));
     }
 
     private void handleFaceAcquired(int acquireInfo) {
@@ -803,6 +808,11 @@
                     getCurrentUser());
         }
 
+        // The face timeout message is not very actionable, let's ask the user to
+        // manually retry.
+        if (msgId == FaceManager.FACE_ERROR_TIMEOUT) {
+            errString = mContext.getString(R.string.keyguard_unlock);
+        }
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index f60e95e..5c6c397 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -16,6 +16,8 @@
 package com.android.systemui.bubbles;
 
 
+import static android.view.Display.INVALID_DISPLAY;
+
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
 
 import android.content.Context;
@@ -129,6 +131,20 @@
         mInflated = true;
     }
 
+    /**
+     * Set visibility of bubble in the expanded state.
+     *
+     * @param visibility {@code true} if the expanded bubble should be visible on the screen.
+     *
+     * Note that this contents visibility doesn't affect visibility at {@link android.view.View},
+     * and setting {@code false} actually means rendering the expanded view in transparent.
+     */
+    void setContentVisibility(boolean visibility) {
+        if (expandedView != null) {
+            expandedView.setContentVisibility(visibility);
+        }
+    }
+
     void setDismissed() {
         entry.setBubbleDismissed(true);
         // TODO: move this somewhere where it can be guaranteed not to run until safe from flicker
@@ -168,6 +184,13 @@
     }
 
     /**
+     * @return the display id of the virtual display on which bubble contents is drawn.
+     */
+    int getDisplayId() {
+        return expandedView != null ? expandedView.getVirtualDisplayId() : INVALID_DISPLAY;
+    }
+
+    /**
      * Should be invoked whenever a Bubble is accessed (selected while expanded).
      */
     void markAsAccessedAt(long lastAccessedMillis) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 4ec79a6..6cfbb22 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -665,17 +665,23 @@
      * status bar, otherwise returns {@link Display#INVALID_DISPLAY}.
      */
     public int getExpandedDisplayId(Context context) {
+        final Bubble bubble = getExpandedBubble(context);
+        return bubble != null ? bubble.getDisplayId() : INVALID_DISPLAY;
+    }
+
+    @Nullable
+    private Bubble getExpandedBubble(Context context) {
         if (mStackView == null) {
-            return INVALID_DISPLAY;
+            return null;
         }
-        boolean defaultDisplay = context.getDisplay() != null
+        final boolean defaultDisplay = context.getDisplay() != null
                 && context.getDisplay().getDisplayId() == DEFAULT_DISPLAY;
-        Bubble b = mStackView.getExpandedBubble();
-        if (defaultDisplay && b != null && isStackExpanded()
+        final Bubble expandedBubble = mStackView.getExpandedBubble();
+        if (defaultDisplay && expandedBubble != null && isStackExpanded()
                 && !mStatusBarWindowController.getPanelExpanded()) {
-            return b.expandedView.getVirtualDisplayId();
+            return expandedBubble;
         }
-        return INVALID_DISPLAY;
+        return null;
     }
 
     @VisibleForTesting
@@ -786,6 +792,14 @@
                 mBubbleData.setExpanded(false);
             }
         }
+
+        @Override
+        public void onSingleTaskDisplayDrawn(int displayId) {
+            final Bubble expandedBubble = getExpandedBubble(mContext);
+            if (expandedBubble != null && expandedBubble.getDisplayId() == displayId) {
+                expandedBubble.setContentVisibility(true);
+            }
+        }
     }
 
     private static boolean shouldAutoBubbleMessages(Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index cbe6c99..09d4b05 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -182,6 +182,8 @@
 
         mActivityView = new ActivityView(mContext, null /* attrs */, 0 /* defStyle */,
                 true /* singleTaskInstance */);
+
+        setContentVisibility(false);
         addView(mActivityView);
 
         // Expanded stack layout, top to bottom:
@@ -236,6 +238,22 @@
     }
 
     /**
+     * Set visibility of contents in the expanded state.
+     *
+     * @param visibility {@code true} if the contents should be visible on the screen.
+     *
+     * Note that this contents visibility doesn't affect visibility at {@link android.view.View},
+     * and setting {@code false} actually means rendering the contents in transparent.
+     */
+    void setContentVisibility(boolean visibility) {
+        final float alpha = visibility ? 1f : 0f;
+        mPointerView.setAlpha(alpha);
+        if (mActivityView != null) {
+            mActivityView.setAlpha(alpha);
+        }
+    }
+
+    /**
      * Called by {@link BubbleStackView} when the insets for the expanded state should be updated.
      * This should be done post-move and post-animation.
      */
@@ -307,6 +325,7 @@
                 parent.removeView(mNotifRow);
             }
             addView(mNotifRow, 1 /* index */);
+            mPointerView.setAlpha(1f);
         }
     }
 
@@ -333,6 +352,7 @@
                 removeView(mNotifRow);
                 mNotifRow = null;
             }
+            setContentVisibility(false);
             mActivityView.setVisibility(VISIBLE);
         } else if (DEBUG_ENABLE_AUTO_BUBBLE) {
             // Hide activity view if we had it previously
@@ -428,6 +448,7 @@
             mActivityView.onLocationChanged();
         } else if (mNotifRow != null) {
             applyRowState(mNotifRow);
+            mPointerView.setAlpha(1f);
         }
         updateHeight();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 0abd25d..97425b3 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -746,12 +746,16 @@
         }
         final Bubble previouslySelected = mExpandedBubble;
         mExpandedBubble = bubbleToSelect;
+
         if (mIsExpanded) {
             // Make the container of the expanded view transparent before removing the expanded view
             // from it. Otherwise a punch hole created by {@link android.view.SurfaceView} in the
             // expanded view becomes visible on the screen. See b/126856255
             mExpandedViewContainer.setAlpha(0.0f);
             mSurfaceSynchronizer.syncSurfaceAndRun(() -> {
+                if (previouslySelected != null) {
+                    previouslySelected.setContentVisibility(false);
+                }
                 updateExpandedBubble();
                 updatePointerPosition();
                 requestUpdate();
@@ -780,6 +784,14 @@
         }
         if (wasExpanded) {
             // Collapse the stack
+            mExpandedViewContainer.setAlpha(0.0f);
+            // TODO: In order to prevent flicker, code below should be executed after the alpha
+            // value set on the mExpandedViewContainer is reflected on the screen. However, we
+            // cannot just postpone the execution like #setSelectedBubble(), since some of member
+            // variables referred by the code are overridden before the execution.
+            if (mExpandedBubble != null) {
+                mExpandedBubble.setContentVisibility(false);
+            }
             animateExpansion(false /* expand */);
             logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
         } else {
@@ -937,14 +949,10 @@
             if (shouldExpand) {
                 mExpandedViewContainer.setTranslationX(xStart);
                 mExpandedViewContainer.setTranslationY(yStart);
-                mExpandedViewContainer.setAlpha(0f);
             }
 
             mExpandedViewXAnim.animateToFinalPosition(shouldExpand ? 0f : xStart);
             mExpandedViewYAnim.animateToFinalPosition(shouldExpand ? yDest : yStart);
-            mExpandedViewContainer.animate()
-                    .setDuration(100)
-                    .alpha(shouldExpand ? 1f : 0f);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index fd76a79..25bde3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -78,6 +78,7 @@
 
     private static final int MSG_HIDE_TRANSIENT = 1;
     private static final int MSG_CLEAR_BIOMETRIC_MSG = 2;
+    private static final int MSG_SWIPE_UP_TO_UNLOCK = 3;
     private static final long TRANSIENT_BIOMETRIC_ERROR_TIMEOUT = 1300;
 
     private final Context mContext;
@@ -318,6 +319,7 @@
         mTransientIndication = transientIndication;
         mTransientTextColorState = textColorState;
         mHandler.removeMessages(MSG_HIDE_TRANSIENT);
+        mHandler.removeMessages(MSG_SWIPE_UP_TO_UNLOCK);
         if (mDozing && !TextUtils.isEmpty(mTransientIndication)) {
             // Make sure this doesn't get stuck and burns in. Acquire wakelock until its cleared.
             mWakeLock.setAcquired(true);
@@ -536,10 +538,26 @@
                 hideTransientIndication();
             } else if (msg.what == MSG_CLEAR_BIOMETRIC_MSG) {
                 mLockIcon.setTransientBiometricsError(false);
+            } else if (msg.what == MSG_SWIPE_UP_TO_UNLOCK) {
+                showSwipeUpToUnlock();
             }
         }
     };
 
+    private void showSwipeUpToUnlock() {
+        if (mDozing) {
+            return;
+        }
+
+        String message = mContext.getString(R.string.keyguard_unlock);
+        if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
+            mStatusBarKeyguardViewManager.showBouncerMessage(message, mInitialTextColorState);
+        } else if (mKeyguardUpdateMonitor.isScreenOn()) {
+            showTransientIndication(message);
+            hideTransientIndicationDelayed(BaseKeyguardCallback.HIDE_DELAY_MS);
+        }
+    }
+
     public void setDozing(boolean dozing) {
         if (mDozing == dozing) {
             return;
@@ -620,12 +638,20 @@
                 return;
             }
             animatePadlockError();
+            boolean showSwipeToUnlock =
+                    msgId == KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED;
             if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
                 mStatusBarKeyguardViewManager.showBouncerMessage(helpString,
                         mInitialTextColorState);
             } else if (updateMonitor.isScreenOn()) {
                 showTransientIndication(helpString);
-                hideTransientIndicationDelayed(TRANSIENT_BIOMETRIC_ERROR_TIMEOUT);
+                if (!showSwipeToUnlock) {
+                    hideTransientIndicationDelayed(TRANSIENT_BIOMETRIC_ERROR_TIMEOUT);
+                }
+            }
+            if (showSwipeToUnlock) {
+                mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SWIPE_UP_TO_UNLOCK),
+                        TRANSIENT_BIOMETRIC_ERROR_TIMEOUT);
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 4d4818d..6159f6c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -39,7 +39,6 @@
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.statusbar.NotificationMediaManager;
-import com.android.systemui.tuner.TunerService;
 
 import java.io.PrintWriter;
 
@@ -102,20 +101,10 @@
      */
     private static final float BIOMETRIC_COLLAPSE_SPEEDUP_FACTOR = 1.1f;
 
-    /**
-     * If face unlock dismisses the lock screen or keeps user on keyguard by default on this device.
-     */
-    private final boolean mFaceDismissesKeyguardByDefault;
-
-    /**
-     * If face unlock dismisses the lock screen or keeps user on keyguard for the current user.
-     */
-    @VisibleForTesting
-    protected boolean mFaceDismissesKeyguard;
-
     private final NotificationMediaManager mMediaManager;
     private final PowerManager mPowerManager;
     private final Handler mHandler;
+    private final KeyguardBypassController mKeyguardBypassController;
     private PowerManager.WakeLock mWakeLock;
     private final KeyguardUpdateMonitor mUpdateMonitor;
     private final UnlockMethodCache mUnlockMethodCache;
@@ -133,16 +122,6 @@
     private boolean mPendingShowBouncer;
     private boolean mHasScreenTurnedOnSinceAuthenticating;
 
-    private final TunerService.Tunable mFaceDismissedKeyguardTunable = new TunerService.Tunable() {
-        @Override
-        public void onTuningChanged(String key, String newValue) {
-            int defaultValue = mFaceDismissesKeyguardByDefault ? 1 : 0;
-            mFaceDismissesKeyguard = Settings.Secure.getIntForUser(mContext.getContentResolver(),
-                    Settings.Secure.FACE_UNLOCK_DISMISSES_KEYGUARD,
-                    defaultValue, KeyguardUpdateMonitor.getCurrentUser()) != 0;
-        }
-    };
-
     private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
 
     public BiometricUnlockController(Context context,
@@ -152,12 +131,12 @@
             StatusBar statusBar,
             UnlockMethodCache unlockMethodCache, Handler handler,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
-            TunerService tunerService) {
+            KeyguardBypassController keyguardBypassController) {
         this(context, dozeScrimController, keyguardViewMediator, scrimController, statusBar,
-                unlockMethodCache, handler, keyguardUpdateMonitor, tunerService,
+                unlockMethodCache, handler, keyguardUpdateMonitor,
                 context.getResources()
                         .getInteger(com.android.internal.R.integer.config_wakeUpDelayDoze),
-                context.getResources().getBoolean(R.bool.config_faceAuthDismissesKeyguard));
+                keyguardBypassController);
     }
 
     @VisibleForTesting
@@ -168,9 +147,8 @@
                                      StatusBar statusBar,
                                      UnlockMethodCache unlockMethodCache, Handler handler,
                                      KeyguardUpdateMonitor keyguardUpdateMonitor,
-                                     TunerService tunerService,
                                      int wakeUpDelay,
-                                     boolean faceDismissesKeyguard) {
+                                     KeyguardBypassController keyguardBypassController) {
         mContext = context;
         mPowerManager = context.getSystemService(PowerManager.class);
         mUpdateMonitor = keyguardUpdateMonitor;
@@ -186,9 +164,7 @@
         mUnlockMethodCache = unlockMethodCache;
         mHandler = handler;
         mWakeUpDelay = wakeUpDelay;
-        mFaceDismissesKeyguardByDefault = faceDismissesKeyguard;
-        tunerService.addTunable(mFaceDismissedKeyguardTunable,
-                Settings.Secure.FACE_UNLOCK_DISMISSES_KEYGUARD);
+        mKeyguardBypassController = keyguardBypassController;
     }
 
     public void setStatusBarKeyguardViewManager(
@@ -392,7 +368,7 @@
         boolean unlockingAllowed = mUpdateMonitor.isUnlockingWithBiometricAllowed();
         boolean deviceDreaming = mUpdateMonitor.isDreaming();
         boolean faceStayingOnKeyguard = biometricSourceType == BiometricSourceType.FACE
-                && !mFaceDismissesKeyguard;
+                && !mKeyguardBypassController.getBypassEnabled();
 
         if (!mUpdateMonitor.isDeviceInteractive()) {
             if (!mStatusBarKeyguardViewManager.isShowing()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
new file mode 100644
index 0000000..5b5eb76
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.phone
+
+import android.content.Context
+import android.provider.Settings
+import com.android.internal.annotations.VisibleForTesting
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.R
+import com.android.systemui.tuner.TunerService
+
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class KeyguardBypassController {
+
+    @Inject
+    constructor(context: Context,
+                tunerService: TunerService) {
+        val dismissByDefault = if (context.getResources().getBoolean(
+                R.bool.config_faceAuthDismissesKeyguard)) 1 else 0
+        tunerService.addTunable(
+                object : TunerService.Tunable {
+                        override fun onTuningChanged(key: String?, newValue: String?) {
+                                bypassEnabled = Settings.Secure.getIntForUser(
+                                        context.contentResolver,
+                                        Settings.Secure.FACE_UNLOCK_DISMISSES_KEYGUARD,
+                                        dismissByDefault,
+                                        KeyguardUpdateMonitor.getCurrentUser()) != 0
+            }
+        }, Settings.Secure.FACE_UNLOCK_DISMISSES_KEYGUARD)
+    }
+
+    @VisibleForTesting
+    constructor(bypassEnabled: Boolean) {
+       this.bypassEnabled = bypassEnabled;
+    }
+
+    /**
+     * If face unlock dismisses the lock screen or keeps user on keyguard for the current user.
+     */
+    var bypassEnabled: Boolean = false
+    private set
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 7623dee..d303ade 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -3079,6 +3079,11 @@
 
     @Override
     public void onDynamicPrivacyChanged() {
+        // Do not request animation when pulsing or waking up, otherwise the clock wiill be out
+        // of sync with the notification panel.
+        if (mLinearDarkAmount != 0) {
+            return;
+        }
         mAnimateNextPositionUpdate = true;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 1da819f..5fca4f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -386,6 +386,8 @@
     PulseExpansionHandler mPulseExpansionHandler;
     @Inject
     NotificationWakeUpCoordinator mWakeUpCoordinator;
+    @Inject
+    KeyguardBypassController mKeyguardBypassController;
 
     // expanded notifications
     protected NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window
@@ -1229,7 +1231,7 @@
         mBiometricUnlockController = new BiometricUnlockController(mContext,
                 mDozeScrimController, keyguardViewMediator,
                 mScrimController, this, UnlockMethodCache.getInstance(mContext),
-                new Handler(), mKeyguardUpdateMonitor, Dependency.get(TunerService.class));
+                new Handler(), mKeyguardUpdateMonitor, mKeyguardBypassController);
         mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this,
                 getBouncerContainer(), mNotificationPanel, mBiometricUnlockController,
                 mStatusBarWindow.findViewById(R.id.lock_icon_container));
@@ -3890,6 +3892,8 @@
         private boolean mAnimateWakeup;
         private boolean mAnimateScreenOff;
         private boolean mIgnoreTouchWhilePulsing;
+        private boolean mWakeLockScreenPerformsAuth = SystemProperties.getBoolean(
+                "persist.sysui.wake_performs_auth", false);
 
         @Override
         public String toString() {
@@ -3945,7 +3949,9 @@
                 mStatusBarWindow.suppressWakeUpGesture(true);
             }
 
-            boolean passiveAuthInterrupt = reason == DozeLog.PULSE_REASON_NOTIFICATION;
+            boolean passiveAuthInterrupt = reason == DozeLog.PULSE_REASON_NOTIFICATION || (
+                    reason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN
+                            && mWakeLockScreenPerformsAuth);
             // Set the state to pulsing, so ScrimController will know what to do once we ask it to
             // execute the transition. The pulse callback will then be invoked when the scrims
             // are black, indicating that StatusBar is ready to present the rest of the UI.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index 8286d26..2558d77 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -22,6 +22,7 @@
 import android.util.ArraySet;
 import android.util.Log;
 
+import com.android.settingslib.graph.SignalDrawable;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.policy.NetworkController;
@@ -186,8 +187,8 @@
 
         // Visibility of the data type indicator changed
         boolean typeChanged = statusType != state.typeId && (statusType == 0 || state.typeId == 0);
-
-        state.visible = statusIcon.visible && !mBlockMobile;
+        state.visible = statusIcon.visible && !mBlockMobile
+                && !isInEmptyStateOnSingleSimDevice(subId, statusIcon.icon);
         state.strengthId = statusIcon.icon;
         state.typeId = statusType;
         state.contentDescription = statusIcon.contentDescription;
@@ -209,6 +210,12 @@
         }
     }
 
+    private boolean isInEmptyStateOnSingleSimDevice(int subId, int icon) {
+        return mMobileStates.size() == 1
+                && mMobileStates.get(0).subId == subId
+                && SignalDrawable.isEmptyState(icon);
+    }
+
     private MobileIconState getState(int subId) {
         for (MobileIconState state : mMobileStates) {
             if (state.subId == subId) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index d2d294b..fdc2cd3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -71,8 +71,6 @@
     @Mock
     private UnlockMethodCache mUnlockMethodCache;
     @Mock
-    private TunerService mTunerService;
-    @Mock
     private Handler mHandler;
     private BiometricUnlockController mBiometricUnlockController;
 
@@ -192,9 +190,8 @@
         TestableBiometricUnlockController(boolean faceDismissesKeyguard) {
             super(mContext, mDozeScrimController,
                     mKeyguardViewMediator, mScrimController, mStatusBar, mUnlockMethodCache,
-                    mHandler, mUpdateMonitor, mTunerService, 0 /* wakeUpDelay */,
-                    faceDismissesKeyguard);
-            mFaceDismissesKeyguard = faceDismissesKeyguard;
+                    mHandler, mUpdateMonitor, 0 /* wakeUpDelay */,
+                    new KeyguardBypassController(faceDismissesKeyguard));
         }
     }
 }
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index b6a5be8..4cfc1d1 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -1065,8 +1065,6 @@
 
         private final long mRecurringAccessibilityEventsIntervalMillis;
 
-        private int mTempLayer = 0;
-
         public WindowsForAccessibilityObserver(WindowManagerService windowManagerService,
                 WindowsForAccessibilityCallback callback) {
             mContext = windowManagerService.mContext;
@@ -1091,7 +1089,7 @@
         }
 
         /**
-         * Check if windows have changed, and send them to the accessibilty subsystem if they have.
+         * Check if windows have changed, and send them to the accessibility subsystem if they have.
          *
          * @param forceSend Send the windows the accessibility even if they haven't changed.
          */
@@ -1108,8 +1106,7 @@
                 // the window manager is still looking for where to put it.
                 // We will do the work when we get a focus change callback.
                 // TODO(b/112273690): Support multiple displays
-                // TODO(b/129098348): Support embedded displays
-                if (mService.getDefaultDisplayContentLocked().mCurrentFocus == null) {
+                if (!isCurrentFocusWindowOnDefaultDisplay()) {
                     return;
                 }
 
@@ -1395,21 +1392,35 @@
         }
 
         private void populateVisibleWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
+            final List<WindowState> tempWindowStatesList = new ArrayList<>();
             final DisplayContent dc = mService.getDefaultDisplayContentLocked();
-            mTempLayer = 0;
             dc.forAllWindows((w) -> {
                 if (w.isVisibleLw()) {
-                    outWindows.put(mTempLayer++, w);
+                    tempWindowStatesList.add(w);
                 }
             }, false /* traverseTopToBottom */);
+            // Insert the re-parented windows in another display on top of their parents in
+            // default display.
             mService.mRoot.forAllWindows(w -> {
-                final WindowState win = findRootDisplayParentWindow(w);
-                if (win != null && win.getDisplayContent().isDefaultDisplay && w.isVisibleLw()) {
-                    // TODO(b/129098348): insert windows on child displays into outWindows based on
-                    // root-display-parent window.
-                    outWindows.put(mTempLayer++, w);
+                final WindowState parentWindow = findRootDisplayParentWindow(w);
+                if (parentWindow == null) {
+                    return;
                 }
-            }, false /* traverseTopToBottom */);
+
+                // TODO: Use Region instead to get rid of this complicated logic.
+                // Check the tap exclude region of the parent window. If the tap exclude region
+                // is empty, it means there is another can-receive-pointer-event view on top of
+                // the region. Hence, we don't count the window as visible.
+                if (w.isVisibleLw() && parentWindow.getDisplayContent().isDefaultDisplay
+                        && parentWindow.hasTapExcludeRegion()
+                        && tempWindowStatesList.contains(parentWindow)) {
+                    tempWindowStatesList.add(
+                            tempWindowStatesList.lastIndexOf(parentWindow) + 1, w);
+                }
+            }, true /* traverseTopToBottom */);
+            for (int i = 0; i < tempWindowStatesList.size(); i++) {
+                outWindows.put(i, tempWindowStatesList.get(i));
+            }
         }
 
         private WindowState findRootDisplayParentWindow(WindowState win) {
@@ -1425,6 +1436,23 @@
             return displayParentWindow;
         }
 
+        private boolean isCurrentFocusWindowOnDefaultDisplay() {
+            final WindowState focusedWindow =
+                    mService.mRoot.getTopFocusedDisplayContent().mCurrentFocus;
+            if (focusedWindow == null) {
+                return false;
+            }
+
+            final WindowState rootDisplayParentWindow = findRootDisplayParentWindow(focusedWindow);
+            if (!focusedWindow.isDefaultDisplay()
+                    && (rootDisplayParentWindow == null
+                    || !rootDisplayParentWindow.isDefaultDisplay())) {
+                return false;
+            }
+
+            return true;
+        }
+
         private class MyHandler extends Handler {
             public static final int MESSAGE_COMPUTE_CHANGED_WINDOWS = 1;
 
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 3d59e66..b3b6efe 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -40,6 +40,7 @@
 import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
 import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
 import static android.view.WindowManager.TRANSIT_NONE;
+import static android.view.WindowManager.TRANSIT_SHOW_SINGLE_TASK_DISPLAY;
 import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
 import static android.view.WindowManager.TRANSIT_TASK_OPEN;
 import static android.view.WindowManager.TRANSIT_TASK_OPEN_BEHIND;
@@ -3194,6 +3195,8 @@
                 if (newTask) {
                     if (r.mLaunchTaskBehind) {
                         transit = TRANSIT_TASK_OPEN_BEHIND;
+                    } else if (getDisplay().isSingleTaskInstance()) {
+                        transit = TRANSIT_SHOW_SINGLE_TASK_DISPLAY;
                     } else {
                         // If a new task is being launched, then mark the existing top activity as
                         // supporting picture-in-picture while pausing only if the starting activity
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index ed56501..dbc530d 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -34,7 +34,6 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.service.voice.IVoiceInteractionSession;
-import android.util.Pair;
 import android.util.SparseIntArray;
 import android.util.proto.ProtoOutputStream;
 
@@ -189,6 +188,13 @@
     public abstract void notifyDockedStackMinimizedChanged(boolean minimized);
 
     /**
+     * Notify listeners that contents are drawn for the first time on a single task display.
+     *
+     * @param displayId An ID of the display on which contents are drawn.
+     */
+    public abstract void notifySingleTaskDisplayDrawn(int displayId);
+
+    /**
      * Start activity {@code intents} as if {@code packageName} on user {@code userId} did it.
      *
      * - DO NOT call it with the calling UID cleared.
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 765c9d0..2e5b9fd 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -6097,7 +6097,8 @@
         }
 
         @Override
-        public void notifyAppTransitionStarting(SparseIntArray reasons, long timestamp) {
+        public void notifyAppTransitionStarting(SparseIntArray reasons,
+                long timestamp) {
             synchronized (mGlobalLock) {
                 mStackSupervisor.getActivityMetricsLogger().notifyTransitionStarting(
                         reasons, timestamp);
@@ -6105,6 +6106,11 @@
         }
 
         @Override
+        public void notifySingleTaskDisplayDrawn(int displayId) {
+            mTaskChangeNotificationController.notifySingleTaskDisplayDrawn(displayId);
+        }
+
+        @Override
         public void notifyAppTransitionFinished() {
             synchronized (mGlobalLock) {
                 mStackSupervisor.notifyAppTransitionDone();
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index ddd5c0a..19ccc62 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -29,6 +29,7 @@
 import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
 import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
 import static android.view.WindowManager.TRANSIT_NONE;
+import static android.view.WindowManager.TRANSIT_SHOW_SINGLE_TASK_DISPLAY;
 import static android.view.WindowManager.TRANSIT_TASK_CHANGE_WINDOWING_MODE;
 import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
 import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE;
@@ -2052,6 +2053,9 @@
             case TRANSIT_CRASHING_ACTIVITY_CLOSE: {
                 return "TRANSIT_CRASHING_ACTIVITY_CLOSE";
             }
+            case TRANSIT_SHOW_SINGLE_TASK_DISPLAY: {
+                return "TRANSIT_SHOW_SINGLE_TASK_DISPLAY";
+            }
             default: {
                 return "<UNKNOWN: " + transition + ">";
             }
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index d4c4e6a..6c5ef52 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -28,6 +28,7 @@
 import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
 import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
 import static android.view.WindowManager.TRANSIT_NONE;
+import static android.view.WindowManager.TRANSIT_SHOW_SINGLE_TASK_DISPLAY;
 import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
 import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE;
 import static android.view.WindowManager.TRANSIT_TASK_OPEN;
@@ -211,6 +212,12 @@
         mService.mAtmInternal.notifyAppTransitionStarting(mTempTransitionReasons.clone(),
                 SystemClock.uptimeMillis());
 
+        if (transit == TRANSIT_SHOW_SINGLE_TASK_DISPLAY) {
+            mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
+                mService.mAtmInternal.notifySingleTaskDisplayDrawn(mDisplayContent.getDisplayId());
+            });
+        }
+
         Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
 
         mDisplayContent.pendingLayoutChanges |=
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index c3a769b..a91fd25 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -2541,6 +2541,9 @@
     void removeImmediately() {
         mRemovingDisplay = true;
         try {
+            if (mParentWindow != null) {
+                mParentWindow.removeEmbeddedDisplayContent(this);
+            }
             // Clear all transitions & screen frozen states when removing display.
             mOpeningApps.clear();
             mClosingApps.clear();
@@ -5028,6 +5031,7 @@
      */
     void reparentDisplayContent(WindowState win, SurfaceControl sc) {
         mParentWindow = win;
+        mParentWindow.addEmbeddedDisplayContent(this);
         mParentSurfaceControl = sc;
         if (mPortalWindowHandle == null) {
             mPortalWindowHandle = createPortalWindowHandle(sc.toString());
@@ -5058,12 +5062,12 @@
             throw new IllegalArgumentException(
                     "The given window is not the parent window of this display.");
         }
-        if (mLocationInParentWindow.x != x || mLocationInParentWindow.y != y) {
-            mLocationInParentWindow.x = x;
-            mLocationInParentWindow.y = y;
+        if (!mLocationInParentWindow.equals(x, y)) {
+            mLocationInParentWindow.set(x, y);
             if (mWmService.mAccessibilityController != null) {
                 mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
             }
+            notifyLocationInParentDisplayChanged();
         }
     }
 
@@ -5071,6 +5075,30 @@
         return mLocationInParentWindow;
     }
 
+    Point getLocationInParentDisplay() {
+        final Point location = new Point();
+        if (mParentWindow != null) {
+            // LocationInParentWindow indicates the offset to (0,0) of window, but what we need is
+            // the offset to (0,0) of display.
+            DisplayContent dc = this;
+            do {
+                final WindowState displayParent = dc.getParentWindow();
+                location.x += displayParent.getFrameLw().left
+                        + (dc.getLocationInParentWindow().x * displayParent.mGlobalScale + 0.5f);
+                location.y += displayParent.getFrameLw().top
+                        + (dc.getLocationInParentWindow().y * displayParent.mGlobalScale + 0.5f);
+                dc = displayParent.getDisplayContent();
+            } while (dc != null && dc.getParentWindow() != null);
+        }
+        return location;
+    }
+
+    void notifyLocationInParentDisplayChanged() {
+        forAllWindows(w -> {
+            w.updateLocationInParentDisplayIfNeeded();
+        }, false /* traverseTopToBottom */);
+    }
+
     @VisibleForTesting
     SurfaceControl getWindowingLayer() {
         return mWindowingLayer;
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index 3ec461d..d58c613 100644
--- a/services/core/java/com/android/server/wm/RootActivityContainer.java
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -35,6 +35,7 @@
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
+import static android.view.WindowManager.TRANSIT_SHOW_SINGLE_TASK_DISPLAY;
 
 import static com.android.server.am.ActivityStackSupervisorProto.CONFIGURATION_CONTAINER;
 import static com.android.server.am.ActivityStackSupervisorProto.DISPLAYS;
@@ -1217,6 +1218,15 @@
                 if (displayShouldSleep) {
                     stack.goToSleepIfPossible(false /* shuttingDown */);
                 } else {
+                    // When the display which can only contain one task turns on, start a special
+                    // transition. {@link AppTransitionController#handleAppTransitionReady} later
+                    // picks up the transition, and schedules
+                    // {@link ITaskStackListener#onSingleTaskDisplayDrawn} callback which is
+                    // triggered after contents are drawn on the display.
+                    if (display.isSingleTaskInstance()) {
+                        display.mDisplayContent.prepareAppTransition(
+                                TRANSIT_SHOW_SINGLE_TASK_DISPLAY, false);
+                    }
                     stack.awakeFromSleepingLocked();
                     if (stack.isFocusedStackOnDisplay()
                             && !mStackSupervisor.getKeyguardController()
diff --git a/services/core/java/com/android/server/wm/TapExcludeRegionHolder.java b/services/core/java/com/android/server/wm/TapExcludeRegionHolder.java
index 22f529b..8f72cda 100644
--- a/services/core/java/com/android/server/wm/TapExcludeRegionHolder.java
+++ b/services/core/java/com/android/server/wm/TapExcludeRegionHolder.java
@@ -52,4 +52,11 @@
             region.op(r, Region.Op.UNION);
         }
     }
+
+    /**
+     * Return true if tap exclude region is empty.
+     */
+    boolean isEmpty() {
+        return mTapExcludeRegions.size() == 0;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
index 66200e3..27175c7 100644
--- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
@@ -54,6 +54,7 @@
     private static final int NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED_MSG = 19;
     private static final int NOTIFY_SIZE_COMPAT_MODE_ACTIVITY_CHANGED_MSG = 20;
     private static final int NOTIFY_BACK_PRESSED_ON_TASK_ROOT = 21;
+    private static final int NOTIFY_SINGLE_TASK_DISPLAY_DRAWN = 22;
 
     // Delay in notifying task stack change listeners (in millis)
     private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100;
@@ -154,6 +155,10 @@
         l.onSizeCompatModeActivityChanged(m.arg1, (IBinder) m.obj);
     };
 
+    private final TaskStackConsumer mNotifySingleTaskDisplayDrawn = (l, m) -> {
+        l.onSingleTaskDisplayDrawn(m.arg1);
+    };
+
     @FunctionalInterface
     public interface TaskStackConsumer {
         void accept(ITaskStackListener t, Message m) throws RemoteException;
@@ -233,6 +238,9 @@
                 case NOTIFY_BACK_PRESSED_ON_TASK_ROOT:
                     forAllRemoteListeners(mNotifyBackPressedOnTaskRoot, msg);
                     break;
+                case NOTIFY_SINGLE_TASK_DISPLAY_DRAWN:
+                    forAllRemoteListeners(mNotifySingleTaskDisplayDrawn, msg);
+                    break;
             }
         }
     }
@@ -477,4 +485,14 @@
         forAllLocalListeners(mNotifyBackPressedOnTaskRoot, msg);
         msg.sendToTarget();
     }
+
+    /**
+     * Notify listeners that contents are drawn for the first time on a single task display.
+     */
+    void notifySingleTaskDisplayDrawn(int displayId) {
+        final Message msg = mHandler.obtainMessage(NOTIFY_SINGLE_TASK_DISPLAY_DRAWN,
+                displayId, 0 /* unused */);
+        forAllLocalListeners(mNotifySingleTaskDisplayDrawn, msg);
+        msg.sendToTarget();
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index d39ee40..0a2fc9b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -4543,8 +4543,11 @@
                     AccessibilityController accessibilityController = null;
 
                     synchronized (mGlobalLock) {
-                        // TODO(multidisplay): Accessibility supported only of default desiplay.
-                        if (mAccessibilityController != null && displayContent.isDefaultDisplay) {
+                        // TODO(multidisplay): Accessibility supported only of default display and
+                        // embedded displays.
+                        if (mAccessibilityController != null
+                                && (displayContent.isDefaultDisplay
+                                || displayContent.getParentWindow() != null)) {
                             accessibilityController = mAccessibilityController;
                         }
 
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 5ef184a..89fd33b 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -167,6 +167,7 @@
 import android.os.WorkSource;
 import android.provider.Settings;
 import android.text.TextUtils;
+import android.util.ArraySet;
 import android.util.DisplayMetrics;
 import android.util.MergedConfiguration;
 import android.util.Slog;
@@ -316,6 +317,9 @@
 
     int mLayoutSeq = -1;
 
+    /** @see #addEmbeddedDisplayContent(DisplayContent dc) */
+    private final ArraySet<DisplayContent> mEmbeddedDisplayContents = new ArraySet<>();
+
     /**
      * Used to store last reported to client configuration and check if we have newer available.
      * We'll send configuration to client only if it is different from the last applied one and
@@ -536,6 +540,12 @@
     private final Point mTmpPoint = new Point();
 
     /**
+     * If a window is on a display which has been re-parented to a view in another window,
+     * use this offset to indicate the correct location.
+     */
+    private final Point mLastReportedDisplayOffset = new Point();
+
+    /**
      * Whether the window was resized by us while it was gone for layout.
      */
     boolean mResizedWhileGone = false;
@@ -1777,11 +1787,13 @@
             startMoveAnimation(left, top);
         }
 
-        //TODO (multidisplay): Accessibility supported only for the default display.
-        if (mWmService.mAccessibilityController != null
-                && getDisplayContent().getDisplayId() == DEFAULT_DISPLAY) {
+        // TODO (multidisplay): Accessibility supported only for the default display and
+        // embedded displays
+        if (mWmService.mAccessibilityController != null && (getDisplayId() == DEFAULT_DISPLAY
+                || getDisplayContent().getParentWindow() != null)) {
             mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
         }
+        updateLocationInParentDisplayIfNeeded();
 
         try {
             mClient.moved(left, top);
@@ -3143,11 +3155,13 @@
                         displayCutout);
             }
 
-            //TODO (multidisplay): Accessibility supported only for the default display.
+            // TODO (multidisplay): Accessibility supported only for the default display and
+            // embedded displays
             if (mWmService.mAccessibilityController != null && (getDisplayId() == DEFAULT_DISPLAY
                     || getDisplayContent().getParentWindow() != null)) {
                 mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
             }
+            updateLocationInParentDisplayIfNeeded();
 
             mWindowFrames.resetInsetsChanged();
             mWinAnimator.mSurfaceResized = false;
@@ -3165,6 +3179,36 @@
         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
     }
 
+    void updateLocationInParentDisplayIfNeeded() {
+        final int embeddedDisplayContentsSize = mEmbeddedDisplayContents.size();
+        // If there is any embedded display which is re-parented to this window, we need to
+        // notify all windows in the embedded display about the location change.
+        if (embeddedDisplayContentsSize != 0) {
+            for (int i = embeddedDisplayContentsSize - 1; i >= 0; i--) {
+                final DisplayContent edc = mEmbeddedDisplayContents.valueAt(i);
+                edc.notifyLocationInParentDisplayChanged();
+            }
+        }
+        // If this window is in a embedded display which is re-parented to another window,
+        // we may need to update its correct on-screen location.
+        final DisplayContent dc = getDisplayContent();
+        if (dc.getParentWindow() == null) {
+            return;
+        }
+
+        final Point offset = dc.getLocationInParentDisplay();
+        if (mLastReportedDisplayOffset.equals(offset)) {
+            return;
+        }
+
+        mLastReportedDisplayOffset.set(offset.x, offset.y);
+        try {
+            mClient.locationInParentDisplayChanged(mLastReportedDisplayOffset);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to update offset from DisplayContent", e);
+        }
+    }
+
     /**
      * Called when the insets state changed.
      */
@@ -3584,6 +3628,7 @@
         }
         pw.println(prefix + "isOnScreen=" + isOnScreen());
         pw.println(prefix + "isVisible=" + isVisible());
+        pw.println(prefix + "mEmbeddedDisplayContents=" + mEmbeddedDisplayContents);
     }
 
     @Override
@@ -4209,7 +4254,8 @@
             return;
         }
 
-        //TODO (multidisplay): Accessibility is supported only for the default display.
+        // TODO (multidisplay): Accessibility supported only for the default display and
+        // embedded displays
         if (mWmService.mAccessibilityController != null && (getDisplayId() == DEFAULT_DISPLAY
                 || getDisplayContent().getParentWindow() != null)) {
             mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
@@ -4581,6 +4627,28 @@
     }
 
     /**
+     * Add the DisplayContent of the embedded display which is re-parented to this window to
+     * the list of embedded displays.
+     *
+     * @param dc DisplayContent of the re-parented embedded display.
+     * @return {@code true} if the giving DisplayContent is added, {@code false} otherwise.
+     */
+    boolean addEmbeddedDisplayContent(DisplayContent dc) {
+        return mEmbeddedDisplayContents.add(dc);
+    }
+
+    /**
+     * Remove the DisplayContent of the embedded display which is re-parented to this window from
+     * the list of embedded displays.
+     *
+     * @param dc DisplayContent of the re-parented embedded display.
+     * @return {@code true} if the giving DisplayContent is removed, {@code false} otherwise.
+     */
+    boolean removeEmbeddedDisplayContent(DisplayContent dc) {
+        return mEmbeddedDisplayContents.remove(dc);
+    }
+
+    /**
      * Updates the last inset values to the current ones.
      */
     void updateLastInsetValues() {
@@ -5002,6 +5070,10 @@
         tempRegion.recycle();
     }
 
+    boolean hasTapExcludeRegion() {
+        return mTapExcludeRegionHolder != null && !mTapExcludeRegionHolder.isEmpty();
+    }
+
     @Override
     public boolean isInputMethodTarget() {
         return getDisplayContent().mInputMethodTarget == this;
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index 5136705..4c27a3c 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -49,6 +49,9 @@
         <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityRequestedOrientationChange" />
         <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityTaskChangeCallbacks" />
         <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityTaskDescriptionChange" />
+        <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityViewTestActivity" />
+        <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityInActivityView"
+                  android:resizeableActivity="true" />
         <activity android:name="com.android.server.wm.ScreenDecorWindowTests$TestActivity"
                   android:showWhenLocked="true" />
     </application>
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
index 62247d8..19fd93fe 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.wm;
 
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
 import static org.junit.Assert.assertEquals;
@@ -26,18 +28,22 @@
 import android.app.ActivityManager;
 import android.app.ActivityManager.TaskDescription;
 import android.app.ActivityTaskManager;
+import android.app.ActivityView;
 import android.app.IActivityManager;
 import android.app.ITaskStackListener;
+import android.app.Instrumentation;
 import android.app.Instrumentation.ActivityMonitor;
 import android.app.TaskStackListener;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
+import android.os.Bundle;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
 import android.support.test.uiautomator.UiDevice;
 import android.text.TextUtils;
+import android.view.ViewGroup;
 
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.MediumTest;
@@ -231,6 +237,40 @@
         assertTrue(activity.mOnDetachedFromWindowCalled);
     }
 
+    @Test
+    public void testTaskOnSingleTaskDisplayDrawn() throws Exception {
+        final Instrumentation instrumentation = getInstrumentation();
+
+        final CountDownLatch activityViewReadyLatch = new CountDownLatch(1);
+        final CountDownLatch singleTaskDisplayDrawnLatch = new CountDownLatch(1);
+        registerTaskStackChangedListener(new TaskStackListener() {
+            @Override
+            public void onSingleTaskDisplayDrawn(int displayId) throws RemoteException {
+                singleTaskDisplayDrawnLatch.countDown();
+            }
+        });
+        final ActivityViewTestActivity activity =
+                (ActivityViewTestActivity) startTestActivity(ActivityViewTestActivity.class);
+        final ActivityView activityView = activity.getActivityView();
+        activityView.setCallback(new ActivityView.StateCallback() {
+            @Override
+            public void onActivityViewReady(ActivityView view) {
+                activityViewReadyLatch.countDown();
+            }
+
+            @Override
+            public void onActivityViewDestroyed(ActivityView view) {
+            }
+        });
+        waitForCallback(activityViewReadyLatch);
+
+        final Context context = instrumentation.getContext();
+        Intent intent = new Intent(context, ActivityInActivityView.class);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+        activityView.startActivity(intent);
+        waitForCallback(singleTaskDisplayDrawnLatch);
+    }
+
     /**
      * Starts the provided activity and returns the started instance.
      */
@@ -369,4 +409,29 @@
             mOnDetachedFromWindowCountDownLatch = countDownLatch;
         }
     }
+
+    public static class ActivityViewTestActivity extends TestActivity {
+        private ActivityView mActivityView;
+
+        @Override
+        public void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+
+            mActivityView = new ActivityView(this, null /* attrs */, 0 /* defStyle */,
+                    true /* singleTaskInstance */);
+            setContentView(mActivityView);
+
+            ViewGroup.LayoutParams layoutParams = mActivityView.getLayoutParams();
+            layoutParams.width = MATCH_PARENT;
+            layoutParams.height = MATCH_PARENT;
+            mActivityView.requestLayout();
+        }
+
+        ActivityView getActivityView() {
+            return mActivityView;
+        }
+    }
+
+    // Activity that has {@link android.R.attr#resizeableActivity} attribute set to {@code true}
+    public static class ActivityInActivityView extends TestActivity {}
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
index 83aa620..a758681 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
@@ -43,6 +44,10 @@
     }
 
     @Override
+    public void locationInParentDisplayChanged(Point offset) throws RemoteException {
+    }
+
+    @Override
     public void insetsChanged(InsetsState insetsState) throws RemoteException {
     }