Merge "Add uwb to ApiDocs.bp"
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobPackageTracker.java b/apex/jobscheduler/service/java/com/android/server/job/JobPackageTracker.java
index 1f0900d..8f7cda9 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobPackageTracker.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobPackageTracker.java
@@ -499,7 +499,7 @@
             mCurDataSet.decActive(job.getSourceUid(), job.getSourcePackageName(), now, stopReason);
         }
         rebatchIfNeeded(now);
-        addEvent(job.getJob().isPeriodic() ? EVENT_STOP_JOB :  EVENT_STOP_PERIODIC_JOB,
+        addEvent(job.getJob().isPeriodic() ? EVENT_STOP_PERIODIC_JOB : EVENT_STOP_JOB,
                 job.getSourceUid(), job.getBatteryName(), job.getJobId(), stopReason, debugReason);
     }
 
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 5be85b0..30160c3 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -243,7 +243,8 @@
     private static native void nativeRemoveJankDataListener(long nativeListener);
     private static native long nativeCreateJankDataListenerWrapper(OnJankDataListener listener);
     private static native int nativeGetGPUContextPriority();
-    private static native void nativeSetTransformHint(long nativeObject, int transformHint);
+    private static native void nativeSetTransformHint(long nativeObject,
+            @SurfaceControl.BufferTransform int transformHint);
     private static native int nativeGetTransformHint(long nativeObject);
     private static native int nativeGetLayerId(long nativeObject);
     private static native void nativeAddTransactionCommittedListener(long nativeObject,
@@ -3753,7 +3754,7 @@
     /**
      * @hide
      */
-    public int getTransformHint() {
+    public @SurfaceControl.BufferTransform int getTransformHint() {
         checkNotReleased();
         return nativeGetTransformHint(mNativeObject);
     }
@@ -3767,7 +3768,7 @@
      * with the same size.
      * @hide
      */
-    public void setTransformHint(@Surface.Rotation int transformHint) {
+    public void setTransformHint(@SurfaceControl.BufferTransform int transformHint) {
         nativeSetTransformHint(mNativeObject, transformHint);
     }
 
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index b5f108c..1c44f443 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -214,7 +214,7 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     final Rect mSurfaceFrame = new Rect();
     int mLastSurfaceWidth = -1, mLastSurfaceHeight = -1;
-    int mTransformHint = 0;
+    @SurfaceControl.BufferTransform int mTransformHint = 0;
 
     private boolean mGlobalListenersAdded;
     private boolean mAttachedToWindow;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index b9c216d..cddf9ee 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -314,7 +314,8 @@
 
     private ArrayList<OnBufferTransformHintChangedListener> mTransformHintListeners =
             new ArrayList<>();
-    private @Surface.Rotation int mPreviousTransformHint = Surface.ROTATION_0;
+    private @SurfaceControl.BufferTransform
+            int mPreviousTransformHint = SurfaceControl.BUFFER_TRANSFORM_IDENTITY;
     /**
      * Callback for notifying about global configuration changes.
      */
@@ -7855,8 +7856,7 @@
             int transformHint = mSurfaceControl.getTransformHint();
             if (mPreviousTransformHint != transformHint) {
                 mPreviousTransformHint = transformHint;
-                dispatchTransformHintChanged(
-                        SurfaceControl.rotationToBufferTransform(transformHint));
+                dispatchTransformHintChanged(transformHint);
             }
         } else {
             destroySurface();
@@ -10464,7 +10464,7 @@
 
     @Override
     public @SurfaceControl.BufferTransform int getBufferTransformHint() {
-        return SurfaceControl.rotationToBufferTransform(mSurfaceControl.getTransformHint());
+        return mSurfaceControl.getTransformHint();
     }
 
     @Override
diff --git a/core/java/android/window/StartingWindowInfo.java b/core/java/android/window/StartingWindowInfo.java
index 5950e9f..aec910b 100644
--- a/core/java/android/window/StartingWindowInfo.java
+++ b/core/java/android/window/StartingWindowInfo.java
@@ -136,6 +136,11 @@
     /** @hide */
     public static final int TYPE_PARAMETER_USE_EMPTY_SPLASH_SCREEN = 0x00000020;
     /**
+     * The parameter which indicates if the activity has finished drawing.
+     * @hide
+     */
+    public static final int TYPE_PARAMETER_ACTIVITY_DRAWN = 0x00000040;
+    /**
      * Application is allowed to use the legacy splash screen
      * @hide
      */
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index dae844f..3bc3336 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -1870,16 +1870,12 @@
     if (surface == nullptr) {
         return;
     }
-    surface->setTransformHint(
-            ui::Transform::toRotationFlags(static_cast<ui::Rotation>(transformHint)));
+    surface->setTransformHint(transformHint);
 }
 
 static jint nativeGetTransformHint(JNIEnv* env, jclass clazz, jlong nativeSurfaceControl) {
     sp<SurfaceControl> surface(reinterpret_cast<SurfaceControl*>(nativeSurfaceControl));
-    ui::Transform::RotationFlags transformHintRotationFlags =
-            static_cast<ui::Transform::RotationFlags>(surface->getTransformHint());
-
-    return toRotationInt(ui::Transform::toRotation((transformHintRotationFlags)));
+    return surface->getTransformHint();
 }
 
 static jint nativeGetLayerId(JNIEnv* env, jclass clazz, jlong nativeSurfaceControl) {
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 2680c31..2eb32b0 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4065,7 +4065,7 @@
     <string translatable="false" name="config_inCallNotificationSound">/product/media/audio/ui/InCallNotification.ogg</string>
 
     <!-- URI for default ringtone sound file to be used for silent ringer vibration -->
-    <string translatable="false" name="config_defaultRingtoneVibrationSound">/product/media/audio/ui/AttentionalHaptics.ogg</string>
+    <string translatable="false" name="config_defaultRingtoneVibrationSound"></string>
 
     <!-- Default number of notifications from the same app before they are automatically grouped by the OS -->
     <integer translatable="false" name="config_autoGroupAtCount">4</integer>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java
index bde2b5f..05ba74a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java
@@ -23,6 +23,7 @@
 import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SNAPSHOT;
 import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN;
 import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_CREATED;
+import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_DRAWN;
 import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT;
 import static android.window.StartingWindowInfo.TYPE_PARAMETER_LEGACY_SPLASH_SCREEN;
 import static android.window.StartingWindowInfo.TYPE_PARAMETER_NEW_TASK;
@@ -41,7 +42,7 @@
 
 /**
  * Algorithm for determining the type of a new starting window on handheld devices.
- * At the moment also used on Android Auto.
+ * At the moment also used on Android Auto and Wear OS.
  */
 public class PhoneStartingWindowTypeAlgorithm implements StartingWindowTypeAlgorithm {
     private static final String TAG = PhoneStartingWindowTypeAlgorithm.class.getSimpleName();
@@ -58,6 +59,7 @@
                 (parameter & TYPE_PARAMETER_USE_EMPTY_SPLASH_SCREEN) != 0;
         final boolean legacySplashScreen =
                 ((parameter & TYPE_PARAMETER_LEGACY_SPLASH_SCREEN) != 0);
+        final boolean activityDrawn = (parameter & TYPE_PARAMETER_ACTIVITY_DRAWN) != 0;
         final boolean topIsHome = windowInfo.taskInfo.topActivityType == ACTIVITY_TYPE_HOME;
 
         if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) {
@@ -68,11 +70,14 @@
                     + " activityCreated:" + activityCreated
                     + " useEmptySplashScreen:" + useEmptySplashScreen
                     + " legacySplashScreen:" + legacySplashScreen
+                    + " activityDrawn:" + activityDrawn
                     + " topIsHome:" + topIsHome);
         }
 
         if (!topIsHome) {
-            if (!processRunning || newTask || (taskSwitch && !activityCreated)) {
+            if (!processRunning
+                    || newTask
+                    || (taskSwitch && (!activityCreated || !activityDrawn))) {
                 return useEmptySplashScreen
                         ? STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN
                         : legacySplashScreen
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index 28c6166..87a9825 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
@@ -51,7 +51,7 @@
         android:id="@+id/lockscreen_clock_view_large"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_below="@id/keyguard_status_area"
+        android:layout_below="@id/keyguard_slice_view"
         android:visibility="gone">
         <com.android.keyguard.AnimatableClockView
             android:id="@+id/animatable_clock_view_large"
@@ -68,19 +68,28 @@
             lockScreenWeight="400"
         />
     </FrameLayout>
-    <include layout="@layout/keyguard_status_area"
+
+    <!-- Not quite optimal but needed to translate these items as a group. The
+         NotificationIconContainer has its own logic for translation. -->
+    <LinearLayout
         android:id="@+id/keyguard_status_area"
+        android:orientation="vertical"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_alignParentStart="true"
-        android:layout_below="@id/lockscreen_clock_view" />
+        android:layout_below="@id/lockscreen_clock_view">
 
-    <com.android.systemui.statusbar.phone.NotificationIconContainer
-        android:id="@+id/left_aligned_notification_icon_container"
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/notification_shelf_height"
-        android:layout_below="@id/keyguard_status_area"
-        android:paddingStart="@dimen/below_clock_padding_start_icons"
-        android:visibility="invisible"
-    />
+      <include layout="@layout/keyguard_slice_view"
+               android:id="@+id/keyguard_slice_view"
+               android:layout_width="match_parent"
+               android:layout_height="wrap_content" />
+
+      <com.android.systemui.statusbar.phone.NotificationIconContainer
+          android:id="@+id/left_aligned_notification_icon_container"
+          android:layout_width="match_parent"
+          android:layout_height="@dimen/notification_shelf_height"
+          android:paddingStart="@dimen/below_clock_padding_start_icons"
+          android:visibility="invisible"
+          />
+    </LinearLayout>
 </com.android.keyguard.KeyguardClockSwitch>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_slice_view.xml
similarity index 89%
rename from packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
rename to packages/SystemUI/res-keyguard/layout/keyguard_slice_view.xml
index 95eb5c1..1863d11 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_slice_view.xml
@@ -22,11 +22,10 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:layout_gravity="center_horizontal"
+    android:layout_gravity="start"
     android:clipToPadding="false"
     android:orientation="vertical"
-    android:paddingStart="@dimen/below_clock_padding_start"
-    android:layout_centerHorizontal="true">
+    android:paddingStart="@dimen/below_clock_padding_start">
     <TextView
               android:id="@+id/title"
               android:layout_width="match_parent"
@@ -42,6 +41,6 @@
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
               android:orientation="horizontal"
-              android:gravity="center"
+              android:gravity="start"
     />
 </com.android.keyguard.KeyguardSliceView>
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index 14bf436..db7a0ec 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -24,9 +24,6 @@
 
     <bool name="flag_monet">true</bool>
 
-    <!-- AOD/Lockscreen alternate layout -->
-    <bool name="flag_keyguard_layout">true</bool>
-
     <!-- People Tile flag -->
     <bool name="flag_conversations">false</bool>
 
@@ -51,7 +48,7 @@
 
     <bool name="flag_ongoing_call_status_bar_chip">true</bool>
 
-    <bool name="flag_ongoing_call_in_immersive">false</bool>
+    <bool name="flag_ongoing_call_in_immersive">true</bool>
 
     <bool name="flag_ongoing_call_in_immersive_chip_tap">true</bool>
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index f81f0b9..6fd0c82 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -41,7 +41,7 @@
 
     private static final long CLOCK_OUT_MILLIS = 150;
     private static final long CLOCK_IN_MILLIS = 200;
-    private static final long SMARTSPACE_MOVE_MILLIS = 350;
+    private static final long STATUS_AREA_MOVE_MILLIS = 350;
 
     @IntDef({LARGE, SMALL})
     @Retention(RetentionPolicy.SOURCE)
@@ -63,13 +63,7 @@
     private AnimatableClockView mClockView;
     private AnimatableClockView mLargeClockView;
 
-    /**
-     * Status area (date and other stuff) shown below the clock. Plugin can decide whether or not to
-     * show it below the alternate clock.
-     */
-    private View mKeyguardStatusArea;
-    /** Mutually exclusive with mKeyguardStatusArea */
-    private View mSmartspaceView;
+    private View mStatusArea;
     private int mSmartspaceTopOffset;
 
     /**
@@ -85,7 +79,7 @@
 
     @VisibleForTesting AnimatorSet mClockInAnim = null;
     @VisibleForTesting AnimatorSet mClockOutAnim = null;
-    private ObjectAnimator mSmartspaceAnim = null;
+    private ObjectAnimator mStatusAreaAnim = null;
 
     /**
      * If the Keyguard Slice has a header (big center-aligned text.)
@@ -131,7 +125,7 @@
         mClockView = findViewById(R.id.animatable_clock_view);
         mLargeClockFrame = findViewById(R.id.lockscreen_clock_view_large);
         mLargeClockView = findViewById(R.id.animatable_clock_view_large);
-        mKeyguardStatusArea = findViewById(R.id.keyguard_status_area);
+        mStatusArea = findViewById(R.id.keyguard_status_area);
 
         onDensityOrFontScaleChanged();
     }
@@ -200,22 +194,22 @@
     private void animateClockChange(boolean useLargeClock) {
         if (mClockInAnim != null) mClockInAnim.cancel();
         if (mClockOutAnim != null) mClockOutAnim.cancel();
-        if (mSmartspaceAnim != null) mSmartspaceAnim.cancel();
+        if (mStatusAreaAnim != null) mStatusAreaAnim.cancel();
 
         View in, out;
         int direction = 1;
-        float smartspaceYTranslation;
+        float statusAreaYTranslation;
         if (useLargeClock) {
             out = mClockFrame;
             in = mLargeClockFrame;
             if (indexOfChild(in) == -1) addView(in);
             direction = -1;
-            smartspaceYTranslation = mSmartspaceView == null ? 0
-                    : mClockFrame.getTop() - mSmartspaceView.getTop() + mSmartspaceTopOffset;
+            statusAreaYTranslation = mClockFrame.getTop() - mStatusArea.getTop()
+                    + mSmartspaceTopOffset;
         } else {
             in = mClockFrame;
             out = mLargeClockFrame;
-            smartspaceYTranslation = 0f;
+            statusAreaYTranslation = 0f;
 
             // Must remove in order for notifications to appear in the proper place
             removeView(out);
@@ -251,18 +245,16 @@
         mClockInAnim.start();
         mClockOutAnim.start();
 
-        if (mSmartspaceView != null) {
-            mSmartspaceAnim = ObjectAnimator.ofFloat(mSmartspaceView, View.TRANSLATION_Y,
-                    smartspaceYTranslation);
-            mSmartspaceAnim.setDuration(SMARTSPACE_MOVE_MILLIS);
-            mSmartspaceAnim.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
-            mSmartspaceAnim.addListener(new AnimatorListenerAdapter() {
-                public void onAnimationEnd(Animator animation) {
-                    mSmartspaceAnim = null;
-                }
-            });
-            mSmartspaceAnim.start();
-        }
+        mStatusAreaAnim = ObjectAnimator.ofFloat(mStatusArea, View.TRANSLATION_Y,
+                statusAreaYTranslation);
+        mStatusAreaAnim.setDuration(STATUS_AREA_MOVE_MILLIS);
+        mStatusAreaAnim.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+        mStatusAreaAnim.addListener(new AnimatorListenerAdapter() {
+            public void onAnimationEnd(Animator animation) {
+                mStatusAreaAnim = null;
+            }
+        });
+        mStatusAreaAnim.start();
     }
 
     /**
@@ -352,10 +344,6 @@
         }
     }
 
-    void setSmartspaceView(View smartspaceView) {
-        mSmartspaceView = smartspaceView;
-    }
-
     void updateColors(ColorExtractor.GradientColors colors) {
         mSupportsDarkText = colors.supportsDarkText();
         mColorPalette = colors.getColorPalette();
@@ -369,8 +357,7 @@
         pw.println("  mClockPlugin: " + mClockPlugin);
         pw.println("  mClockFrame: " + mClockFrame);
         pw.println("  mLargeClockFrame: " + mLargeClockFrame);
-        pw.println("  mKeyguardStatusArea: " + mKeyguardStatusArea);
-        pw.println("  mSmartspaceView: " + mSmartspaceView);
+        pw.println("  mStatusArea: " + mStatusArea);
         pw.println("  mDarkAmount: " + mDarkAmount);
         pw.println("  mSupportsDarkText: " + mSupportsDarkText);
         pw.println("  mColorPalette: " + Arrays.toString(mColorPalette));
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 4111020..01976b4 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -25,7 +25,9 @@
 import android.content.res.Resources;
 import android.text.TextUtils;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.FrameLayout;
+import android.widget.LinearLayout;
 import android.widget.RelativeLayout;
 
 import com.android.internal.colorextraction.ColorExtractor;
@@ -99,7 +101,8 @@
 
     private final ClockManager.ClockChangedListener mClockChangedListener = this::setClockPlugin;
 
-    // If set, will replace keyguard_status_area
+    private ViewGroup mStatusArea;
+    // If set will replace keyguard_slice_view
     private View mSmartspaceView;
 
     private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
@@ -191,8 +194,8 @@
                 mView.getResources().getDimensionPixelSize(R.dimen.keyguard_clock_top_margin);
 
         if (mOnlyClock) {
-            View ksa = mView.findViewById(R.id.keyguard_status_area);
-            ksa.setVisibility(View.GONE);
+            View ksv = mView.findViewById(R.id.keyguard_slice_view);
+            ksv.setVisibility(View.GONE);
 
             View nic = mView.findViewById(
                     R.id.left_aligned_notification_icon_container);
@@ -201,19 +204,18 @@
         }
         updateAodIcons();
 
+        mStatusArea = mView.findViewById(R.id.keyguard_status_area);
+
         if (mSmartspaceController.isEnabled()) {
             mSmartspaceView = mSmartspaceController.buildAndConnectView(mView);
+            View ksv = mView.findViewById(R.id.keyguard_slice_view);
+            int ksvIndex = mStatusArea.indexOfChild(ksv);
+            ksv.setVisibility(View.GONE);
 
-            View ksa = mView.findViewById(R.id.keyguard_status_area);
-            int ksaIndex = mView.indexOfChild(ksa);
-            ksa.setVisibility(View.GONE);
-
-            // Place smartspace view below normal clock...
-            RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
+            LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
                     MATCH_PARENT, WRAP_CONTENT);
-            lp.addRule(RelativeLayout.BELOW, R.id.lockscreen_clock_view);
 
-            mView.addView(mSmartspaceView, ksaIndex, lp);
+            mStatusArea.addView(mSmartspaceView, ksvIndex, lp);
             int startPadding = getContext().getResources()
                     .getDimensionPixelSize(R.dimen.below_clock_padding_start);
             int endPadding = getContext().getResources()
@@ -221,14 +223,6 @@
             mSmartspaceView.setPaddingRelative(startPadding, 0, endPadding, 0);
 
             updateClockLayout();
-
-            View nic = mView.findViewById(
-                    R.id.left_aligned_notification_icon_container);
-            lp = (RelativeLayout.LayoutParams) nic.getLayoutParams();
-            lp.addRule(RelativeLayout.BELOW, mSmartspaceView.getId());
-            nic.setLayoutParams(lp);
-
-            mView.setSmartspaceView(mSmartspaceView);
             mSmartspaceTransitionController.setLockscreenSmartspace(mSmartspaceView);
         }
     }
@@ -244,8 +238,6 @@
         }
         mColorExtractor.removeOnColorsChangedListener(mColorsListener);
         mView.setClockPlugin(null, mStatusBarStateController.getState());
-
-        mSmartspaceController.disconnect();
     }
 
     /**
@@ -328,8 +320,8 @@
         PropertyAnimator.setProperty(mLargeClockFrame, AnimatableProperty.SCALE_Y,
                 scale, props, animate);
 
-        if (mSmartspaceView != null) {
-            PropertyAnimator.setProperty(mSmartspaceView, AnimatableProperty.TRANSLATION_X,
+        if (mStatusArea != null) {
+            PropertyAnimator.setProperty(mStatusArea, AnimatableProperty.TRANSLATION_X,
                     x, props, animate);
 
             // If we're unlocking with the SmartSpace shared element transition, let the controller
@@ -340,7 +332,6 @@
         }
 
         mKeyguardSliceViewController.updatePosition(x, props, animate);
-        mNotificationIconAreaController.updatePosition(x, props, animate);
     }
 
     /** Sets an alpha value on every child view except for the smartspace. */
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index 428006e..9b76bab 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -33,12 +33,10 @@
 import android.text.TextUtils;
 import android.text.TextUtils.TruncateAt;
 import android.util.AttributeSet;
-import android.util.TypedValue;
 import android.view.Gravity;
 import android.view.View;
 import android.view.animation.Animation;
 import android.widget.LinearLayout;
-import android.widget.RelativeLayout;
 import android.widget.TextView;
 
 import androidx.slice.SliceItem;
@@ -85,8 +83,6 @@
     private boolean mHasHeader;
     private View.OnClickListener mOnClickListener;
 
-    private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;
-
     public KeyguardSliceView(Context context, AttributeSet attrs) {
         super(context, attrs);
 
@@ -136,35 +132,6 @@
         }
     }
 
-    /**
-     * Updates the lockscreen mode which may change the layout of the keyguard slice view.
-     */
-    public void updateLockScreenMode(int mode) {
-        mLockScreenMode = mode;
-        if (mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) {
-            mTitle.setPaddingRelative(0, 0, 0, 0);
-            mTitle.setGravity(Gravity.START);
-            setGravity(Gravity.START);
-            RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) getLayoutParams();
-            lp.removeRule(RelativeLayout.CENTER_HORIZONTAL);
-            setLayoutParams(lp);
-        } else {
-            final int horizontalPaddingDpValue = (int) TypedValue.applyDimension(
-                    TypedValue.COMPLEX_UNIT_DIP,
-                    44,
-                    getResources().getDisplayMetrics()
-            );
-            mTitle.setPaddingRelative(horizontalPaddingDpValue, 0, horizontalPaddingDpValue, 0);
-            mTitle.setGravity(Gravity.CENTER_HORIZONTAL);
-            setGravity(Gravity.CENTER_HORIZONTAL);
-            RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) getLayoutParams();
-            lp.addRule(RelativeLayout.CENTER_HORIZONTAL);
-            setLayoutParams(lp);
-        }
-        mRow.setLockscreenMode(mode);
-        requestLayout();
-    }
-
     Map<View, PendingIntent> showSlice(RowContent header, List<SliceContent> subItems) {
         Trace.beginSection("KeyguardSliceView#showSlice");
         mHasHeader = header != null;
@@ -189,8 +156,7 @@
         final int startIndex = mHasHeader ? 1 : 0; // First item is header; skip it
         mRow.setVisibility(subItemsCount > 0 ? VISIBLE : GONE);
         LinearLayout.LayoutParams layoutParams = (LayoutParams) mRow.getLayoutParams();
-        layoutParams.gravity = mLockScreenMode !=  KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL
-                ? Gravity.START :  Gravity.CENTER;
+        layoutParams.gravity = Gravity.START;
         mRow.setLayoutParams(layoutParams);
 
         for (int i = startIndex; i < subItemsCount; i++) {
@@ -224,8 +190,7 @@
                 final int iconSize = mHasHeader ? mIconSizeWithHeader : mIconSize;
                 iconDrawable = icon.getIcon().loadDrawable(mContext);
                 if (iconDrawable != null) {
-                    if ((iconDrawable instanceof InsetDrawable)
-                            && mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) {
+                    if (iconDrawable instanceof InsetDrawable) {
                         // System icons (DnD) use insets which are fine for centered slice content
                         // but will cause a slight indent for left/right-aligned slice views
                         iconDrawable = ((InsetDrawable) iconDrawable).getDrawable();
@@ -321,7 +286,6 @@
         pw.println("  mTextColor: " + Integer.toHexString(mTextColor));
         pw.println("  mDarkAmount: " + mDarkAmount);
         pw.println("  mHasHeader: " + mHasHeader);
-        pw.println("  mLockScreenMode: " + mLockScreenMode);
     }
 
     @Override
@@ -332,7 +296,6 @@
 
     public static class Row extends LinearLayout {
         private Set<KeyguardSliceTextView> mKeyguardSliceTextViewSet = new HashSet();
-        private int mLockScreenModeRow = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;
 
         /**
          * This view is visible in AOD, which means that the device will sleep if we
@@ -407,11 +370,7 @@
             for (int i = 0; i < childCount; i++) {
                 View child = getChildAt(i);
                 if (child instanceof KeyguardSliceTextView) {
-                    if (mLockScreenModeRow == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) {
-                        ((KeyguardSliceTextView) child).setMaxWidth(Integer.MAX_VALUE);
-                    } else {
-                        ((KeyguardSliceTextView) child).setMaxWidth(width / 3);
-                    }
+                    ((KeyguardSliceTextView) child).setMaxWidth(Integer.MAX_VALUE);
                 }
             }
 
@@ -443,7 +402,6 @@
             super.addView(view, index);
 
             if (view instanceof KeyguardSliceTextView) {
-                ((KeyguardSliceTextView) view).setLockScreenMode(mLockScreenModeRow);
                 mKeyguardSliceTextViewSet.add((KeyguardSliceTextView) view);
             }
         }
@@ -455,24 +413,6 @@
                 mKeyguardSliceTextViewSet.remove((KeyguardSliceTextView) view);
             }
         }
-
-        /**
-         * Updates the lockscreen mode which may change the layout of this view.
-         */
-        public void setLockscreenMode(int mode) {
-            mLockScreenModeRow = mode;
-            if (mLockScreenModeRow == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) {
-                setOrientation(LinearLayout.VERTICAL);
-                setGravity(Gravity.START);
-            } else {
-                setOrientation(LinearLayout.HORIZONTAL);
-                setGravity(Gravity.CENTER);
-            }
-
-            for (KeyguardSliceTextView textView : mKeyguardSliceTextViewSet) {
-                textView.setLockScreenMode(mLockScreenModeRow);
-            }
-        }
     }
 
     /**
@@ -480,7 +420,6 @@
      */
     @VisibleForTesting
     static class KeyguardSliceTextView extends TextView {
-        private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;
 
         @StyleRes
         private static int sStyleId = R.style.TextAppearance_Keyguard_Secondary;
@@ -509,13 +448,8 @@
             boolean hasText = !TextUtils.isEmpty(getText());
             int padding = (int) getContext().getResources()
                     .getDimension(R.dimen.widget_horizontal_padding) / 2;
-            if (mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) {
-                // orientation is vertical, so add padding to top & bottom
-                setPadding(0, padding, 0, hasText ? padding : 0);
-            } else {
-                // orientation is horizontal, so add padding to left & right
-                setPadding(padding, 0, padding * (hasText ? 1 : -1), 0);
-            }
+            // orientation is vertical, so add padding to top & bottom
+            setPadding(0, padding, 0, hasText ? padding : 0);
 
             setCompoundDrawablePadding((int) mContext.getResources()
                     .getDimension(R.dimen.widget_icon_padding));
@@ -543,18 +477,5 @@
                 }
             }
         }
-
-        /**
-         * Updates the lockscreen mode which may change the layout of this view.
-         */
-        public void setLockScreenMode(int mode) {
-            mLockScreenMode = mode;
-            if (mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) {
-                setTextAlignment(View.TEXT_ALIGNMENT_VIEW_START);
-            } else {
-                setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
-            }
-            updatePadding();
-        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
index 4a56773..d05cc4e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
@@ -73,7 +73,6 @@
     private Uri mKeyguardSliceUri;
     private Slice mSlice;
     private Map<View, PendingIntent> mClickActions;
-    private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;
 
     TunerService.Tunable mTunable = (key, newValue) -> setupUri(newValue);
 
@@ -137,7 +136,6 @@
                 TAG + "@" + Integer.toHexString(
                         KeyguardSliceViewController.this.hashCode()),
                 KeyguardSliceViewController.this);
-        mView.updateLockScreenMode(mLockScreenMode);
     }
 
     @Override
@@ -160,14 +158,6 @@
     }
 
     /**
-     * Updates the lockscreen mode which may change the layout of the keyguard slice view.
-     */
-    public void updateLockScreenMode(int mode) {
-        mLockScreenMode = mode;
-        mView.updateLockScreenMode(mLockScreenMode);
-    }
-
-    /**
      * Sets the slice provider Uri.
      */
     public void setupUri(String uriString) {
@@ -249,6 +239,5 @@
     public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
         pw.println("  mSlice: " + mSlice);
         pw.println("  mClickActions: " + mClickActions);
-        pw.println("  mLockScreenMode: " + mLockScreenMode);
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index 2362a1a..a72a050e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -88,7 +88,7 @@
             mClockView.setAccessibilityDelegate(new KeyguardClockAccessibilityDelegate(mContext));
         }
 
-        mKeyguardSlice = findViewById(R.id.keyguard_status_area);
+        mKeyguardSlice = findViewById(R.id.keyguard_slice_view);
         mTextColor = mClockView.getCurrentTextColor();
 
         mKeyguardSlice.setContentChangeListener(this::onSliceContentChanged);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 682b217..60af66ba 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -252,11 +252,6 @@
 
     private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
         @Override
-        public void onLockScreenModeChanged(int mode) {
-            mKeyguardSliceViewController.updateLockScreenMode(mode);
-        }
-
-        @Override
         public void onTimeChanged() {
             refreshTime();
         }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index e9d0575..5707fa7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -188,9 +188,6 @@
     private static final int MSG_TIME_FORMAT_UPDATE = 344;
     private static final int MSG_REQUIRE_NFC_UNLOCK = 345;
 
-    public static final int LOCK_SCREEN_MODE_NORMAL = 0;
-    public static final int LOCK_SCREEN_MODE_LAYOUT_1 = 1;
-
     /** Biometric authentication state: Not listening. */
     private static final int BIOMETRIC_STATE_STOPPED = 0;
 
@@ -1867,9 +1864,6 @@
                     case MSG_KEYGUARD_GOING_AWAY:
                         handleKeyguardGoingAway((boolean) msg.obj);
                         break;
-                    case MSG_LOCK_SCREEN_MODE:
-                        handleLockScreenMode();
-                        break;
                     case MSG_TIME_FORMAT_UPDATE:
                         handleTimeFormatUpdate((String) msg.obj);
                         break;
@@ -2011,8 +2005,6 @@
             }
         }
 
-        updateLockScreenMode(featureFlags.isKeyguardLayoutEnabled());
-
         mTimeFormatChangeObserver = new ContentObserver(mHandler) {
             @Override
             public void onChange(boolean selfChange) {
@@ -2028,14 +2020,6 @@
                 false, mTimeFormatChangeObserver, UserHandle.USER_ALL);
     }
 
-    private void updateLockScreenMode(boolean isEnabled) {
-        final int newMode = isEnabled ? LOCK_SCREEN_MODE_LAYOUT_1 : LOCK_SCREEN_MODE_NORMAL;
-        if (newMode != mLockScreenMode) {
-            mLockScreenMode = newMode;
-            mHandler.sendEmptyMessage(MSG_LOCK_SCREEN_MODE);
-        }
-    }
-
     private void updateUdfpsEnrolled(int userId) {
         mIsUdfpsEnrolled = mAuthController.isUdfpsEnrolled(userId);
     }
@@ -2666,20 +2650,6 @@
     }
 
     /**
-     * Handle {@link #MSG_LOCK_SCREEN_MODE}
-     */
-    private void handleLockScreenMode() {
-        Assert.isMainThread();
-        if (DEBUG) Log.d(TAG, "handleLockScreenMode(" + mLockScreenMode + ")");
-        for (int i = 0; i < mCallbacks.size(); i++) {
-            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
-            if (cb != null) {
-                cb.onLockScreenModeChanged(mLockScreenMode);
-            }
-        }
-    }
-
-    /**
      * Handle (@line #MSG_TIMEZONE_UPDATE}
      */
     private void handleTimeZoneUpdate(String timeZone) {
@@ -3057,7 +3027,6 @@
         callback.onKeyguardOccludedChanged(mKeyguardOccluded);
         callback.onKeyguardVisibilityChangedRaw(mKeyguardIsVisible);
         callback.onTelephonyCapable(mTelephonyCapable);
-        callback.onLockScreenModeChanged(mLockScreenMode);
 
         for (Entry<Integer, SimData> data : mSimDatas.entrySet()) {
             final SimData state = data.getValue();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index e970a86..12431984 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -325,11 +325,6 @@
     public void onSecondaryLockscreenRequirementChanged(int userId) { }
 
     /**
-     * Called to switch lock screen layout/clock layouts
-     */
-    public void onLockScreenModeChanged(int mode) { }
-
-    /**
      * Called when notifying user to unlock in order to use NFC.
      */
     public void onRequireUnlockForNfc() { }
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewModule.java
index 1d51e59..b8841ed 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewModule.java
@@ -34,6 +34,6 @@
 
     @Provides
     static KeyguardSliceView getKeyguardSliceView(KeyguardClockSwitch keyguardClockSwitch) {
-        return keyguardClockSwitch.findViewById(R.id.keyguard_status_area);
+        return keyguardClockSwitch.findViewById(R.id.keyguard_slice_view);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
index 5b9ccd6..2e30e7f 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
@@ -94,10 +94,6 @@
         return mFlagReader.isEnabled(R.bool.flag_notification_pipeline2_rendering);
     }
 
-    public boolean isKeyguardLayoutEnabled() {
-        return mFlagReader.isEnabled(R.bool.flag_keyguard_layout);
-    }
-
     /** */
     public boolean useNewLockscreenAnimations() {
         return mFlagReader.isEnabled(R.bool.flag_lockscreen_animations);
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index 84c5a57..72601e9 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -111,6 +111,17 @@
         return factory.create("CollapsedSbFragmentLog", 20);
     }
 
+    /**
+     * Provides a logging buffer for logs related to swiping away the status bar while in immersive
+     * mode. See {@link com.android.systemui.statusbar.gesture.SwipeStatusBarAwayGestureLogger}.
+     */
+    @Provides
+    @SysUISingleton
+    @SwipeStatusBarAwayLog
+    public static LogBuffer provideSwipeAwayGestureLogBuffer(LogBufferFactory factory) {
+        return factory.create("SwipeStatusBarAwayLog", 30);
+    }
+
     /** Allows logging buffers to be tweaked via adb on debug builds but not on prod builds. */
     @Provides
     @SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/SwipeStatusBarAwayLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/SwipeStatusBarAwayLog.java
new file mode 100644
index 0000000..dd68375
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/SwipeStatusBarAwayLog.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 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.log.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.android.systemui.log.LogBuffer;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+/**
+ * A {@link LogBuffer} for
+ * {@link com.android.systemui.statusbar.gesture.SwipeStatusBarAwayGestureLogger}.
+ */
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface SwipeStatusBarAwayLog {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 4f932a3..9ba846d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -147,7 +147,6 @@
     private boolean mBatteryPresent = true;
     private long mChargingTimeRemaining;
     private String mMessageToShowOnScreenOn;
-    protected int mLockScreenMode;
     private boolean mInited;
 
     private KeyguardUpdateMonitorCallback mUpdateMonitorCallback;
@@ -863,11 +862,6 @@
         public static final int HIDE_DELAY_MS = 5000;
 
         @Override
-        public void onLockScreenModeChanged(int mode) {
-            mLockScreenMode = mode;
-        }
-
-        @Override
         public void onRefreshBatteryInfo(BatteryStatus status) {
             boolean isChargingOrFull = status.status == BatteryManager.BATTERY_STATUS_CHARGING
                     || status.status == BatteryManager.BATTERY_STATUS_FULL;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
index 5758ba4..d297d95 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -47,6 +47,7 @@
 import com.android.systemui.statusbar.StatusBarStateControllerImpl;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.commandline.CommandRegistry;
+import com.android.systemui.statusbar.gesture.SwipeStatusBarAwayGestureHandler;
 import com.android.systemui.statusbar.notification.AssistantFeedbackController;
 import com.android.systemui.statusbar.notification.DynamicChildBindController;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
@@ -69,7 +70,6 @@
 import com.android.systemui.statusbar.phone.SystemUIHostDialogProvider;
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLogger;
-import com.android.systemui.statusbar.phone.ongoingcall.SwipeStatusBarAwayGestureHandler;
 import com.android.systemui.statusbar.policy.RemoteInputUriController;
 import com.android.systemui.tracing.ProtoTracer;
 import com.android.systemui.util.concurrency.DelayableExecutor;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/SwipeStatusBarAwayGestureHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt
similarity index 88%
rename from packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/SwipeStatusBarAwayGestureHandler.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt
index c97cf14..80577ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/SwipeStatusBarAwayGestureHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt
@@ -14,11 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.phone.ongoingcall
+package com.android.systemui.statusbar.gesture
 
 import android.content.Context
 import android.os.Looper
-import android.util.Log
 import android.view.Choreographer
 import android.view.Display
 import android.view.InputEvent
@@ -38,6 +37,7 @@
 open class SwipeStatusBarAwayGestureHandler @Inject constructor(
     context: Context,
     private val statusBarWindowController: StatusBarWindowController,
+    private val logger: SwipeStatusBarAwayGestureLogger
 ) {
 
     /**
@@ -53,7 +53,6 @@
     private var inputMonitor: InputMonitorCompat? = null
     private var inputReceiver: InputChannelCompat.InputEventReceiver? = null
 
-    // TODO(b/195839150): Update this threshold when the config changes?
     private var swipeDistanceThreshold: Int = context.resources.getDimensionPixelSize(
         com.android.internal.R.dimen.system_gestures_start_threshold
     )
@@ -84,12 +83,10 @@
             ACTION_DOWN -> {
                 if (
                     // Gesture starts just below the status bar
-                    // TODO(b/195839150): Is [statusBarHeight] the correct dimension to use for
-                    //   determining which down touches are valid?
                     ev.y >= statusBarWindowController.statusBarHeight
                     && ev.y <= 3 * statusBarWindowController.statusBarHeight
                 ) {
-                    Log.d(TAG, "Beginning gesture detection, y=${ev.y}")
+                    logger.logGestureDetectionStarted(ev.y.toInt())
                     startY = ev.y
                     startTime = ev.eventTime
                     monitoringCurrentTouch = true
@@ -109,12 +106,15 @@
                     // Gesture completed quickly enough
                     && (ev.eventTime - startTime) < SWIPE_TIMEOUT_MS
                 ) {
-                    Log.i(TAG, "Gesture detected; notifying callbacks")
-                    callbacks.values.forEach { it.invoke() }
                     monitoringCurrentTouch = false
+                    logger.logGestureDetected(ev.y.toInt())
+                    callbacks.values.forEach { it.invoke() }
                 }
             }
             ACTION_CANCEL, ACTION_UP -> {
+                if (monitoringCurrentTouch) {
+                    logger.logGestureDetectionEndedWithoutTriggering(ev.y.toInt())
+                }
                 monitoringCurrentTouch = false
             }
         }
@@ -124,7 +124,7 @@
     private fun startGestureListening() {
         stopGestureListening()
 
-        if (DEBUG) { Log.d(TAG, "Input listening started") }
+        logger.logInputListeningStarted()
         inputMonitor = InputMonitorCompat(TAG, Display.DEFAULT_DISPLAY).also {
             inputReceiver = it.getInputReceiver(
                 Looper.getMainLooper(),
@@ -137,7 +137,7 @@
     /** Stop listening for the swipe gesture. */
     private fun stopGestureListening() {
         inputMonitor?.let {
-            if (DEBUG) { Log.d(TAG, "Input listening stopped") }
+            logger.logInputListeningStopped()
             inputMonitor = null
             it.dispose()
         }
@@ -150,4 +150,3 @@
 
 private const val SWIPE_TIMEOUT_MS: Long = 500
 private val TAG = SwipeStatusBarAwayGestureHandler::class.simpleName
-private val DEBUG = Log.isLoggable(TAG, Log.DEBUG)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureLogger.kt
new file mode 100644
index 0000000..17feaa8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureLogger.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2021 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.gesture
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel
+import com.android.systemui.log.dagger.SwipeStatusBarAwayLog
+import javax.inject.Inject
+
+/** Log messages for [SwipeStatusBarAwayGestureHandler]. */
+class SwipeStatusBarAwayGestureLogger @Inject constructor(
+    @SwipeStatusBarAwayLog private val buffer: LogBuffer
+) {
+    fun logGestureDetectionStarted(y: Int) {
+        buffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            { int1 = y },
+            { "Beginning gesture detection. y=$int1" }
+        )
+    }
+
+    fun logGestureDetectionEndedWithoutTriggering(y: Int) {
+        buffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            { int1 = y },
+            { "Gesture finished; no swipe up gesture detected. Final y=$int1" }
+        )
+    }
+
+    fun logGestureDetected(y: Int) {
+        buffer.log(
+            TAG,
+            LogLevel.INFO,
+            { int1 = y },
+            { "Gesture detected; notifying callbacks. y=$int1" }
+        )
+    }
+
+    fun logInputListeningStarted() {
+        buffer.log(TAG, LogLevel.VERBOSE, {}, { "Input listening started "})
+    }
+
+    fun logInputListeningStopped() {
+        buffer.log(TAG, LogLevel.VERBOSE, {}, { "Input listening stopped "})
+    }
+}
+
+private const val TAG = "SwipeStatusBarAwayGestureHandler"
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index 83c98d5..26f6460 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -30,6 +30,7 @@
 import android.os.Handler
 import android.os.UserHandle
 import android.provider.Settings
+import android.util.Log
 import android.view.View
 import android.view.ViewGroup
 import com.android.settingslib.Utils
@@ -74,6 +75,10 @@
     @Main private val handler: Handler,
     optionalPlugin: Optional<BcSmartspaceDataPlugin>
 ) {
+    companion object {
+        private const val TAG = "LockscreenSmartspaceController"
+    }
+
     private var session: SmartspaceSession? = null
     private val plugin: BcSmartspaceDataPlugin? = optionalPlugin.orElse(null)
 
@@ -211,6 +216,7 @@
 
         val newSession = smartspaceManager.createSmartspaceSession(
                 SmartspaceConfig.Builder(context, "lockscreen").build())
+        Log.d(TAG, "Starting smartspace session for lockscreen")
         newSession.addOnTargetsAvailableListener(uiExecutor, sessionListener)
         this.session = newSession
 
@@ -232,6 +238,8 @@
      * Disconnects the smartspace view from the smartspace service and cleans up any resources.
      */
     fun disconnect() {
+        if (!smartspaceViews.isEmpty()) return
+
         execution.assertIsMainThread()
 
         if (session == null) {
@@ -249,6 +257,7 @@
         session = null
 
         plugin?.onTargetsAvailable(emptyList())
+        Log.d(TAG, "Ending smartspace session for lockscreen")
     }
 
     fun addListener(listener: SmartspaceTargetListener) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index 7d476bf..3806d9a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -35,6 +35,7 @@
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.statusbar.gesture.SwipeStatusBarAwayGestureHandler
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
@@ -200,11 +201,8 @@
                 statusBarWindowController.ifPresent {
                     it.setOngoingProcessRequiresStatusBarVisible(true)
                 }
-                // TODO(b/195839150): Only listen for the gesture when in immersive mode.
-                swipeStatusBarAwayGestureHandler.ifPresent {
-                    it.addOnGestureDetectedCallback(TAG, this::onSwipeAwayGestureDetected)
-                }
             }
+            updateGestureListening()
             mListeners.forEach { l -> l.onOngoingCallStateChanged(animate = true) }
         } else {
             // If we failed to update the chip, don't store the call info. Then [hasOngoingCall]
@@ -292,6 +290,18 @@
         return procState <= ActivityManager.PROCESS_STATE_TOP
     }
 
+    private fun updateGestureListening() {
+        if (callNotificationInfo == null
+            || callNotificationInfo?.statusBarSwipedAway == true
+            || !isFullscreen) {
+            swipeStatusBarAwayGestureHandler.ifPresent { it.removeOnGestureDetectedCallback(TAG) }
+        } else {
+            swipeStatusBarAwayGestureHandler.ifPresent {
+                it.addOnGestureDetectedCallback(TAG, this::onSwipeAwayGestureDetected)
+            }
+        }
+    }
+
     private fun removeChip() {
         callNotificationInfo = null
         tearDownChipView()
@@ -334,6 +344,7 @@
         override fun onFullscreenStateChanged(isFullscreen: Boolean) {
             this@OngoingCallController.isFullscreen = isFullscreen
             updateChipClickListener()
+            updateGestureListening()
         }
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index 5c7885f..fe0a2a4 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -19,7 +19,6 @@
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
@@ -30,6 +29,7 @@
 import android.testing.AndroidTestingRunner;
 import android.view.View;
 import android.widget.FrameLayout;
+import android.widget.LinearLayout;
 import android.widget.RelativeLayout;
 
 import androidx.test.filters.SmallTest;
@@ -108,7 +108,7 @@
     private final View mFakeSmartspaceView = new View(mContext);
 
     private KeyguardClockSwitchController mController;
-    private View mStatusArea;
+    private View mSliceView;
 
     @Before
     public void setup() {
@@ -149,8 +149,10 @@
         when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
         when(mColorExtractor.getColors(anyInt())).thenReturn(mGradientColors);
 
-        mStatusArea = new View(getContext());
-        when(mView.findViewById(R.id.keyguard_status_area)).thenReturn(mStatusArea);
+        mSliceView = new View(getContext());
+        when(mView.findViewById(R.id.keyguard_slice_view)).thenReturn(mSliceView);
+        when(mView.findViewById(R.id.keyguard_status_area)).thenReturn(
+                new LinearLayout(getContext()));
     }
 
     @Test
@@ -215,7 +217,7 @@
         when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mFakeSmartspaceView);
         mController.init();
 
-        assertEquals(View.GONE, mStatusArea.getVisibility());
+        assertEquals(View.GONE, mSliceView.getVisibility());
     }
 
     @Test
@@ -223,22 +225,7 @@
         when(mSmartspaceController.isEnabled()).thenReturn(false);
         mController.init();
 
-        assertEquals(View.VISIBLE, mStatusArea.getVisibility());
-    }
-
-    @Test
-    public void testDetachDisconnectsSmartspace() {
-        when(mSmartspaceController.isEnabled()).thenReturn(true);
-        when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mFakeSmartspaceView);
-        mController.init();
-        verify(mView).addView(eq(mFakeSmartspaceView), anyInt(), any());
-
-        ArgumentCaptor<View.OnAttachStateChangeListener> listenerArgumentCaptor =
-                ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);
-        verify(mView).addOnAttachStateChangeListener(listenerArgumentCaptor.capture());
-
-        listenerArgumentCaptor.getValue().onViewDetachedFromWindow(mView);
-        verify(mSmartspaceController).disconnect();
+        assertEquals(View.VISIBLE, mSliceView.getVisibility());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
index 1ab08c2..77302ce 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
@@ -54,7 +54,7 @@
         MockitoAnnotations.initMocks(this);
         LayoutInflater layoutInflater = LayoutInflater.from(getContext());
         mKeyguardSliceView = (KeyguardSliceView) layoutInflater
-                .inflate(R.layout.keyguard_status_area, null);
+                .inflate(R.layout.keyguard_slice_view, null);
         mSliceUri = Uri.parse(KeyguardSliceProvider.KEYGUARD_SLICE_URI);
         SliceProvider.setSpecs(new HashSet<>(Collections.singletonList(SliceSpecs.LIST)));
     }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index b95a33b..3edfd03 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -239,8 +239,6 @@
 
         when(mRingerModeTracker.getRingerMode()).thenReturn(mRingerModeLiveData);
 
-        when(mFeatureFlags.isKeyguardLayoutEnabled()).thenReturn(false);
-
         mMockitoSession = ExtendedMockito.mockitoSession()
                 .spyStatic(SubscriptionManager.class).startMocking();
         ExtendedMockito.doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
index 5d50485..b13c4d9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
@@ -246,6 +246,7 @@
         clearInvocations(plugin)
 
         // WHEN the session is closed
+        controller.stateChangeListener.onViewDetachedFromWindow(smartspaceView as View)
         controller.disconnect()
 
         // THEN the listener receives an empty list of targets
@@ -417,6 +418,7 @@
         connectSession()
 
         // WHEN we are told to cleanup
+        controller.stateChangeListener.onViewDetachedFromWindow(smartspaceView as View)
         controller.disconnect()
 
         // THEN we disconnect from the session and unregister any listeners
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
index ca6e1ee..3d2ff47 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
@@ -37,6 +37,7 @@
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.statusbar.gesture.SwipeStatusBarAwayGestureHandler
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
@@ -149,14 +150,6 @@
     }
 
     @Test
-    fun onEntryUpdated_isOngoingCallNotif_swipeGestureCallbackAdded() {
-        notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
-
-        verify(mockSwipeStatusBarAwayGestureHandler)
-            .addOnGestureDetectedCallback(anyString(), any())
-    }
-
-    @Test
     fun onEntryUpdated_notOngoingCallNotif_listenerNotNotified() {
         notifCollectionListener.onEntryUpdated(createNotCallNotifEntry())
 
@@ -257,17 +250,6 @@
         verify(mockStatusBarWindowController).setOngoingProcessRequiresStatusBarVisible(false)
     }
 
-    @Test
-    fun onEntryUpdated_callNotifAddedThenRemoved_swipeGestureCallbackRemoved() {
-        val ongoingCallNotifEntry = createOngoingCallNotifEntry()
-        notifCollectionListener.onEntryAdded(ongoingCallNotifEntry)
-
-        notifCollectionListener.onEntryRemoved(ongoingCallNotifEntry, REASON_USER_STOPPED)
-
-        verify(mockSwipeStatusBarAwayGestureHandler)
-            .removeOnGestureDetectedCallback(anyString())
-    }
-
     /** Regression test for b/188491504. */
     @Test
     fun onEntryRemoved_removedNotifHasSameKeyAsAddedNotif_listenerNotified() {
@@ -508,6 +490,70 @@
         assertThat(chipView.hasOnClickListeners()).isTrue()
     }
 
+    // Swipe gesture tests
+
+    @Test
+    fun callStartedInImmersiveMode_swipeGestureCallbackAdded() {
+        getStateListener().onFullscreenStateChanged(true)
+
+        notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
+
+        verify(mockSwipeStatusBarAwayGestureHandler)
+            .addOnGestureDetectedCallback(anyString(), any())
+    }
+
+    @Test
+    fun callStartedNotInImmersiveMode_swipeGestureCallbackNotAdded() {
+        getStateListener().onFullscreenStateChanged(false)
+
+        notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
+
+        verify(mockSwipeStatusBarAwayGestureHandler, never())
+            .addOnGestureDetectedCallback(anyString(), any())
+    }
+
+    @Test
+    fun transitionToImmersiveMode_swipeGestureCallbackAdded() {
+        notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
+
+        getStateListener().onFullscreenStateChanged(true)
+
+        verify(mockSwipeStatusBarAwayGestureHandler)
+            .addOnGestureDetectedCallback(anyString(), any())
+    }
+
+    @Test
+    fun transitionOutOfImmersiveMode_swipeGestureCallbackRemoved() {
+        getStateListener().onFullscreenStateChanged(true)
+        notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
+        reset(mockSwipeStatusBarAwayGestureHandler)
+
+        getStateListener().onFullscreenStateChanged(false)
+
+        verify(mockSwipeStatusBarAwayGestureHandler)
+            .removeOnGestureDetectedCallback(anyString())
+    }
+
+    @Test
+    fun callEndedWhileInImmersiveMode_swipeGestureCallbackRemoved() {
+        getStateListener().onFullscreenStateChanged(true)
+        val ongoingCallNotifEntry = createOngoingCallNotifEntry()
+        notifCollectionListener.onEntryAdded(ongoingCallNotifEntry)
+        reset(mockSwipeStatusBarAwayGestureHandler)
+
+        notifCollectionListener.onEntryRemoved(ongoingCallNotifEntry, REASON_USER_STOPPED)
+
+        verify(mockSwipeStatusBarAwayGestureHandler)
+            .removeOnGestureDetectedCallback(anyString())
+    }
+
+    // TODO(b/195839150): Add test
+    //  swipeGesturedTriggeredPreviously_entersImmersiveModeAgain_callbackNotAdded(). That's
+    //  difficult to add now because we have no way to trigger [SwipeStatusBarAwayGestureHandler]'s
+    //  callbacks in test.
+
+    // END swipe gesture tests
+
     private fun createOngoingCallNotifEntry() = createCallNotifEntry(ongoingCallStyle)
 
     private fun createScreeningCallNotifEntry() = createCallNotifEntry(screeningCallStyle)
diff --git a/services/core/java/com/android/server/notification/ZenLog.java b/services/core/java/com/android/server/notification/ZenLog.java
index 1050835..7d7f3a9 100644
--- a/services/core/java/com/android/server/notification/ZenLog.java
+++ b/services/core/java/com/android/server/notification/ZenLog.java
@@ -65,6 +65,7 @@
     private static final int TYPE_LISTENER_HINTS_CHANGED = 15;
     private static final int TYPE_SET_NOTIFICATION_POLICY = 16;
     private static final int TYPE_SET_CONSOLIDATED_ZEN_POLICY = 17;
+    private static final int TYPE_MATCHES_CALL_FILTER = 18;
 
     private static int sNext;
     private static int sSize;
@@ -166,6 +167,13 @@
             + hintsToString(newHints) + ",listeners=" + listenerCount);
     }
 
+    /*
+     * Trace calls to matchesCallFilter with the result of the call and the reason for the result.
+     */
+    public static void traceMatchesCallFilter(boolean result, String reason) {
+        append(TYPE_MATCHES_CALL_FILTER, "result=" + result + ", reason=" + reason);
+    }
+
     private static String subscribeResult(IConditionProvider provider, RemoteException e) {
         return provider == null ? "no provider" : e != null ? e.getMessage() : "ok";
     }
@@ -189,6 +197,7 @@
             case TYPE_LISTENER_HINTS_CHANGED: return "listener_hints_changed";
             case TYPE_SET_NOTIFICATION_POLICY: return "set_notification_policy";
             case TYPE_SET_CONSOLIDATED_ZEN_POLICY: return "set_consolidated_policy";
+            case TYPE_MATCHES_CALL_FILTER: return "matches_call_filter";
             default: return "unknown";
         }
     }
diff --git a/services/core/java/com/android/server/notification/ZenModeFiltering.java b/services/core/java/com/android/server/notification/ZenModeFiltering.java
index 0f526d4..b186f61 100644
--- a/services/core/java/com/android/server/notification/ZenModeFiltering.java
+++ b/services/core/java/com/android/server/notification/ZenModeFiltering.java
@@ -89,20 +89,34 @@
     public static boolean matchesCallFilter(Context context, int zen, NotificationManager.Policy
             consolidatedPolicy, UserHandle userHandle, Bundle extras,
             ValidateNotificationPeople validator, int contactsTimeoutMs, float timeoutAffinity) {
-        if (zen == Global.ZEN_MODE_NO_INTERRUPTIONS) return false; // nothing gets through
-        if (zen == Global.ZEN_MODE_ALARMS) return false; // not an alarm
+        if (zen == Global.ZEN_MODE_NO_INTERRUPTIONS) {
+            ZenLog.traceMatchesCallFilter(false, "no interruptions");
+            return false; // nothing gets through
+        }
+        if (zen == Global.ZEN_MODE_ALARMS) {
+            ZenLog.traceMatchesCallFilter(false, "alarms only");
+            return false; // not an alarm
+        }
         if (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
             if (consolidatedPolicy.allowRepeatCallers()
                     && REPEAT_CALLERS.isRepeat(context, extras)) {
+                ZenLog.traceMatchesCallFilter(true, "repeat caller");
                 return true;
             }
-            if (!consolidatedPolicy.allowCalls()) return false; // no other calls get through
+            if (!consolidatedPolicy.allowCalls()) {
+                ZenLog.traceMatchesCallFilter(false, "calls not allowed");
+                return false; // no other calls get through
+            }
             if (validator != null) {
                 final float contactAffinity = validator.getContactAffinity(userHandle, extras,
                         contactsTimeoutMs, timeoutAffinity);
-                return audienceMatches(consolidatedPolicy.allowCallsFrom(), contactAffinity);
+                boolean match =
+                        audienceMatches(consolidatedPolicy.allowCallsFrom(), contactAffinity);
+                ZenLog.traceMatchesCallFilter(match, "contact affinity " + contactAffinity);
+                return match;
             }
         }
+        ZenLog.traceMatchesCallFilter(true, "no restrictions");
         return true;
     }
 
diff --git a/services/core/java/com/android/server/vibrator/RampDownAdapter.java b/services/core/java/com/android/server/vibrator/RampDownAdapter.java
index d5cd344..e97ed4c 100644
--- a/services/core/java/com/android/server/vibrator/RampDownAdapter.java
+++ b/services/core/java/com/android/server/vibrator/RampDownAdapter.java
@@ -77,9 +77,8 @@
      */
     private int addRampDownToZeroAmplitudeSegments(List<VibrationEffectSegment> segments,
             int repeatIndex) {
-        int newRepeatIndex = repeatIndex;
-        int newSegmentCount = segments.size();
-        for (int i = 1; i < newSegmentCount; i++) {
+        int segmentCount = segments.size();
+        for (int i = 1; i < segmentCount; i++) {
             VibrationEffectSegment previousSegment = segments.get(i - 1);
             if (!isOffSegment(segments.get(i))
                     || !endsWithNonZeroAmplitude(previousSegment)) {
@@ -116,16 +115,23 @@
             if (replacementSegments != null) {
                 int segmentsAdded = replacementSegments.size() - 1;
 
-                segments.remove(i);
+                VibrationEffectSegment originalOffSegment = segments.remove(i);
                 segments.addAll(i, replacementSegments);
-                if (repeatIndex > i) {
-                    newRepeatIndex += segmentsAdded;
+                if (repeatIndex >= i) {
+                    if (repeatIndex == i) {
+                        // This effect is repeating to the removed off segment: add it back at the
+                        // end of the vibration so the loop timings are preserved, and skip it.
+                        segments.add(originalOffSegment);
+                        repeatIndex++;
+                        segmentCount++;
+                    }
+                    repeatIndex += segmentsAdded;
                 }
                 i += segmentsAdded;
-                newSegmentCount += segmentsAdded;
+                segmentCount += segmentsAdded;
             }
         }
-        return newRepeatIndex;
+        return repeatIndex;
     }
 
     /**
diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java
index 25321c1..a327382 100644
--- a/services/core/java/com/android/server/vibrator/VibrationThread.java
+++ b/services/core/java/com/android/server/vibrator/VibrationThread.java
@@ -1077,7 +1077,7 @@
             newSegments.remove(segmentIndex);
             newSegments.addAll(segmentIndex, fallback.getSegments());
             if (segmentIndex < effect.getRepeatIndex()) {
-                newRepeatIndex += fallback.getSegments().size();
+                newRepeatIndex += fallback.getSegments().size() - 1;
             }
             return new VibrationEffect.Composed(newSegments, newRepeatIndex);
         }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 8ef973d..98397b8 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2039,7 +2039,8 @@
     boolean addStartingWindow(String pkg, int resolvedTheme, CompatibilityInfo compatInfo,
             CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags,
             ActivityRecord from, boolean newTask, boolean taskSwitch, boolean processRunning,
-            boolean allowTaskSnapshot, boolean activityCreated, boolean useEmpty) {
+            boolean allowTaskSnapshot, boolean activityCreated, boolean useEmpty,
+            boolean activityAllDrawn) {
         // If the display is frozen, we won't do anything until the actual window is
         // displayed so there is no reason to put in the starting window.
         if (!okToDisplay()) {
@@ -2060,7 +2061,7 @@
                 mWmService.mTaskSnapshotController.getSnapshot(task.mTaskId, task.mUserId,
                         false /* restoreFromDisk */, false /* isLowResolution */);
         final int type = getStartingWindowType(newTask, taskSwitch, processRunning,
-                allowTaskSnapshot, activityCreated, snapshot);
+                allowTaskSnapshot, activityCreated, activityAllDrawn, snapshot);
 
         //TODO(191787740) Remove for T
         final boolean useLegacy = type == STARTING_WINDOW_TYPE_SPLASH_SCREEN
@@ -2074,7 +2075,7 @@
 
         final int typeParameter = mWmService.mStartingSurfaceController
                 .makeStartingWindowTypeParameter(newTask, taskSwitch, processRunning,
-                        allowTaskSnapshot, activityCreated, useEmpty, useLegacy);
+                        allowTaskSnapshot, activityCreated, useEmpty, useLegacy, activityAllDrawn);
 
         if (type == STARTING_WINDOW_TYPE_SNAPSHOT) {
             if (isActivityTypeHome()) {
@@ -2209,10 +2210,10 @@
     private final AddStartingWindow mAddStartingWindow = new AddStartingWindow();
 
     private int getStartingWindowType(boolean newTask, boolean taskSwitch, boolean processRunning,
-            boolean allowTaskSnapshot, boolean activityCreated,
+            boolean allowTaskSnapshot, boolean activityCreated, boolean activityAllDrawn,
             TaskSnapshot snapshot) {
-        if ((newTask || !processRunning || (taskSwitch && !activityCreated))
-                && !isActivityTypeHome()) {
+        if ((newTask || !processRunning || (taskSwitch && !activityCreated)
+                || (taskSwitch && !activityAllDrawn)) && !isActivityTypeHome()) {
             return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
         } else if (taskSwitch && allowTaskSnapshot) {
             if (isSnapshotCompatible(snapshot)) {
@@ -6629,7 +6630,7 @@
         final boolean scheduled = addStartingWindow(packageName, resolvedTheme,
                 compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
                 prev, newTask || newSingleActivity, taskSwitch, isProcessRunning(),
-                allowTaskSnapshot(), activityCreated, mSplashScreenStyleEmpty);
+                allowTaskSnapshot(), activityCreated, mSplashScreenStyleEmpty, allDrawn);
         if (DEBUG_STARTING_WINDOW_VERBOSE && scheduled) {
             Slog.d(TAG, "Scheduled starting window for " + this);
         }
diff --git a/services/core/java/com/android/server/wm/StartingSurfaceController.java b/services/core/java/com/android/server/wm/StartingSurfaceController.java
index 26f0384..8b2a425 100644
--- a/services/core/java/com/android/server/wm/StartingSurfaceController.java
+++ b/services/core/java/com/android/server/wm/StartingSurfaceController.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_CREATED;
+import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_DRAWN;
 import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT;
 import static android.window.StartingWindowInfo.TYPE_PARAMETER_LEGACY_SPLASH_SCREEN;
 import static android.window.StartingWindowInfo.TYPE_PARAMETER_NEW_TASK;
@@ -86,7 +87,7 @@
 
     int makeStartingWindowTypeParameter(boolean newTask, boolean taskSwitch,
             boolean processRunning, boolean allowTaskSnapshot, boolean activityCreated,
-            boolean useEmpty, boolean useLegacy) {
+            boolean useEmpty, boolean useLegacy, boolean activityDrawn) {
         int parameter = 0;
         if (newTask) {
             parameter |= TYPE_PARAMETER_NEW_TASK;
@@ -109,6 +110,9 @@
         if (useLegacy) {
             parameter |= TYPE_PARAMETER_LEGACY_SPLASH_SCREEN;
         }
+        if (activityDrawn) {
+            parameter |= TYPE_PARAMETER_ACTIVITY_DRAWN;
+        }
         return parameter;
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index f11fa16..80f74f0 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2473,7 +2473,8 @@
             if (isPrimaryDisplay) {
                 transformHint = (transformHint + mPrimaryDisplayOrientation) % 4;
             }
-            outSurfaceControl.setTransformHint(transformHint);
+            outSurfaceControl.setTransformHint(
+                    SurfaceControl.rotationToBufferTransform(transformHint));
             ProtoLog.v(WM_DEBUG_ORIENTATION,
                     "Passing transform hint %d for window %s%s",
                     transformHint, win,
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/RampDownAdapterTest.java b/services/tests/servicestests/src/com/android/server/vibrator/RampDownAdapterTest.java
index b90df21..4c3312c 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/RampDownAdapterTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/RampDownAdapterTest.java
@@ -103,21 +103,18 @@
     public void testStepSegments_withShortZeroSegment_replaceWithStepsDown() {
         List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
                 new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 10),
-                new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 10),
-                new StepSegment(/* amplitude= */ 0.8f, /* frequency= */ 0, /* duration= */ 100)));
+                new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 10)));
         List<VibrationEffectSegment> expectedSegments = Arrays.asList(
                 new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 10),
                 new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 5),
-                new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 5),
-                new StepSegment(/* amplitude= */ 0.8f, /* frequency= */ 0, /* duration= */ 100));
+                new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 5));
 
-        assertEquals(1, mAdapter.apply(segments, 1, TEST_VIBRATOR_INFO));
-
+        assertEquals(-1, mAdapter.apply(segments, -1, TEST_VIBRATOR_INFO));
         assertEquals(expectedSegments, segments);
     }
 
     @Test
-    public void testStepSegments_withLongZeroSegment_replaceWithStepsDown() {
+    public void testStepSegments_withLongZeroSegment_replaceWithStepsDownWithRemainingOffSegment() {
         List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
                 new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 10),
                 new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 0,
@@ -131,9 +128,67 @@
                 new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 35),
                 new StepSegment(/* amplitude= */ 0.8f, /* frequency= */ 0, /* duration= */ 100));
 
+        assertEquals(-1, mAdapter.apply(segments, -1, TEST_VIBRATOR_INFO));
+        assertEquals(expectedSegments, segments);
+    }
+
+    @Test
+    public void testStepSegments_withZeroSegmentBeforeRepeat_fixesRepeat() {
+        List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
+                new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 10),
+                new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 50),
+                new StepSegment(/* amplitude= */ 0.8f, /* frequency= */ 0, /* duration= */ 100)));
+        List<VibrationEffectSegment> expectedSegments = Arrays.asList(
+                new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 10),
+                new StepSegment(/* amplitude= */ 0.75f, /* frequency= */ 0, /* duration= */ 5),
+                new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 5),
+                new StepSegment(/* amplitude= */ 0.25f, /* frequency= */ 0, /* duration= */ 5),
+                new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 35),
+                new StepSegment(/* amplitude= */ 0.8f, /* frequency= */ 0, /* duration= */ 100));
+
         // Repeat index fixed after intermediate steps added
         assertEquals(5, mAdapter.apply(segments, 2, TEST_VIBRATOR_INFO));
+        assertEquals(expectedSegments, segments);
+    }
 
+    @Test
+    public void testStepSegments_withZeroSegmentAfterRepeat_preservesRepeat() {
+        List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
+                new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 10),
+                new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 10),
+                new StepSegment(/* amplitude= */ 0.8f, /* frequency= */ 0, /* duration= */ 100)));
+        List<VibrationEffectSegment> expectedSegments = Arrays.asList(
+                new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 10),
+                new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 5),
+                new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 5),
+                new StepSegment(/* amplitude= */ 0.8f, /* frequency= */ 0, /* duration= */ 100));
+
+        assertEquals(3, mAdapter.apply(segments, 2, TEST_VIBRATOR_INFO));
+        assertEquals(expectedSegments, segments);
+    }
+
+    @Test
+    public void testStepSegments_withZeroSegmentAtRepeat_fixesRepeatAndAppendOriginalToListEnd() {
+        List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
+                new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 10),
+                new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 50),
+                new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 100)));
+        List<VibrationEffectSegment> expectedSegments = Arrays.asList(
+                new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 10),
+                new StepSegment(/* amplitude= */ 0.75f, /* frequency= */ 0, /* duration= */ 5),
+                new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 5),
+                new StepSegment(/* amplitude= */ 0.25f, /* frequency= */ 0, /* duration= */ 5),
+                new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 35),
+                new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 100),
+                // Original zero segment appended to the end of new looping vibration,
+                // then converted to ramp down as well.
+                new StepSegment(/* amplitude= */ 0.75f, /* frequency= */ 0, /* duration= */ 5),
+                new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 5),
+                new StepSegment(/* amplitude= */ 0.25f, /* frequency= */ 0, /* duration= */ 5),
+                new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 35));
+
+        // Repeat index fixed after intermediate steps added
+        assertEquals(5, mAdapter.apply(segments, 1, TEST_VIBRATOR_INFO));
         assertEquals(expectedSegments, segments);
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 65733d7..5504f06 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2463,7 +2463,7 @@
         final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
         activity.addStartingWindow(mPackageName,
                 android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
-                false, false);
+                false, false, false);
         waitUntilHandlersIdle();
         assertHasStartingWindow(activity);
         activity.removeStartingWindow();
@@ -2476,7 +2476,7 @@
         activity.mTargetSdk = targetSdk;
         activity.addStartingWindow(mPackageName,
                 android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
-                false, false);
+                false, false, false);
         waitUntilHandlersIdle();
         assertHasStartingWindow(activity);
         assertEquals(activity.mStartingData.mTypeParams & TYPE_PARAMETER_LEGACY_SPLASH_SCREEN,
@@ -2514,11 +2514,11 @@
                 .setVisible(false).build();
         activity1.addStartingWindow(mPackageName,
                 android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
-                false, false);
+                false, false, false);
         waitUntilHandlersIdle();
         activity2.addStartingWindow(mPackageName,
                 android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity1,
-                true, true, false, true, false, false);
+                true, true, false, true, false, false, false);
         waitUntilHandlersIdle();
         assertFalse(mDisplayContent.mSkipAppTransitionAnimation);
         assertNoStartingWindow(activity1);
@@ -2536,11 +2536,11 @@
                     activity2.addStartingWindow(mPackageName,
                             android.R.style.Theme, null, "Test", 0, 0, 0, 0,
                             activity1, true, true, false,
-                            true, false, false);
+                            true, false, false, false);
                 });
         activity1.addStartingWindow(mPackageName,
                 android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
-                false, false);
+                false, false, false);
         waitUntilHandlersIdle();
         assertNoStartingWindow(activity1);
         assertHasStartingWindow(activity2);
@@ -2553,11 +2553,11 @@
         final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build();
         activity1.addStartingWindow(mPackageName,
                 android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
-                false, false);
+                false, false, false);
         waitUntilHandlersIdle();
         activity2.addStartingWindow(mPackageName,
                 android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity1,
-                true, true, false, true, false, false);
+                true, true, false, true, false, false, false);
         waitUntilHandlersIdle();
         assertNoStartingWindow(activity1);
         assertHasStartingWindow(activity2);
@@ -2599,7 +2599,7 @@
                 "Test", 0 /* labelRes */, 0 /* icon */, 0 /* logo */, 0 /* windowFlags */,
                 null /* transferFrom */, true /* newTask */, true /* taskSwitch */,
                 false /* processRunning */, false /* allowTaskSnapshot */,
-                false /* activityCreate */, false /* suggestEmpty */);
+                false /* activityCreate */, false /* suggestEmpty */, false /* activityAllDrawn */);
         waitUntilHandlersIdle();
         assertHasStartingWindow(activity);
 
@@ -2647,7 +2647,7 @@
         task.positionChildAt(topActivity, POSITION_TOP);
         activity.addStartingWindow(mPackageName,
                 android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
-                false, false);
+                false, false, false);
         waitUntilHandlersIdle();
 
         // Make activities to have different rotation from it display and set fixed rotation
@@ -2664,7 +2664,7 @@
         // on activity2.
         topActivity.addStartingWindow(mPackageName,
                 android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity,
-                false, false, false, true, false, false);
+                false, false, false, true, false, false, false);
         waitUntilHandlersIdle();
         assertTrue(topActivity.hasFixedRotationTransform());
     }
@@ -2680,7 +2680,7 @@
         // Add a starting window.
         activityTop.addStartingWindow(mPackageName,
                 android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
-                false, false);
+                false, false, false);
         waitUntilHandlersIdle();
 
         // Make the top one invisible, and try transferring the starting window from the top to the
diff --git a/tests/componentalias/src/com/android/compatibility/common/util/BroadcastMessenger.java b/tests/componentalias/src/com/android/compatibility/common/util/BroadcastMessenger.java
deleted file mode 100644
index a3a2abe..0000000
--- a/tests/componentalias/src/com/android/compatibility/common/util/BroadcastMessenger.java
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- * Copyright (C) 2021 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.compatibility.common.util;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.HandlerThread;
-import android.os.Parcelable;
-import android.os.SystemClock;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.Objects;
-
-/**
- * Provides a one-way communication mechanism using a Parcelable as a payload, via broadcasts.
- *
- * Use {@link #send(Context, String, Parcelable)} to send a message.
- * USe {@link Receiver} to receive a message.
- *
- * Pick a unique "suffix" for your test, and use it with both the sender and receiver, in order
- * to avoid "cross-talks" between different tests. (if they ever run at the same time.)
- *
- * TODO: Move it to compatibility-device-util-axt.
- */
-public final class BroadcastMessenger {
-    private static final String TAG = "BroadcastMessenger";
-
-    private static final String ACTION_MESSAGE =
-            "com.android.compatibility.common.util.BroadcastMessenger.ACTION_MESSAGE_";
-    private static final String ACTION_PING =
-            "com.android.compatibility.common.util.BroadcastMessenger.ACTION_PING_";
-    private static final String EXTRA_MESSAGE =
-            "com.android.compatibility.common.util.BroadcastMessenger.EXTRA_MESSAGE";
-
-    /**
-     * We need to drop messages that were sent before the receiver was created. We keep
-     * track of the message send time in this extra.
-     */
-    private static final String EXTRA_SENT_TIME =
-            "com.android.compatibility.common.util.BroadcastMessenger.EXTRA_SENT_TIME";
-
-    public static final int DEFAULT_TIMEOUT_MS = 10_000;
-
-    private static long getCurrentTime() {
-        return SystemClock.uptimeMillis();
-    }
-
-    private static void sendBroadcast(@NonNull Intent i, @NonNull Context context,
-            @NonNull String broadcastSuffix, @Nullable String receiverPackage) {
-        i.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-        i.setPackage(receiverPackage);
-        i.putExtra(EXTRA_SENT_TIME, getCurrentTime());
-
-        context.sendBroadcast(i);
-    }
-
-    /** Send a message to the {@link Receiver} expecting a given "suffix". */
-    public static <T extends Parcelable> void send(@NonNull Context context,
-            @NonNull String broadcastSuffix, @NonNull T message) {
-        final Intent i = new Intent(ACTION_MESSAGE + Objects.requireNonNull(broadcastSuffix));
-        i.putExtra(EXTRA_MESSAGE, Objects.requireNonNull(message));
-
-        Log.i(TAG, "Sending: " + message);
-        sendBroadcast(i, context, broadcastSuffix, /*receiverPackage=*/ null);
-    }
-
-    private static void sendPing(@NonNull Context context,@NonNull String broadcastSuffix,
-            @NonNull String receiverPackage) {
-        final Intent i = new Intent(ACTION_PING + Objects.requireNonNull(broadcastSuffix));
-
-        Log.i(TAG, "Sending a ping");
-        sendBroadcast(i, context, broadcastSuffix, receiverPackage);
-    }
-
-    /**
-     * Receive messages sent with {@link #send}. Note it'll ignore all the messages that were
-     * sent before instantiated.
-     */
-    public static final class Receiver<T extends Parcelable> implements AutoCloseable {
-        private final Context mContext;
-        private final String mBroadcastSuffix;
-        private final HandlerThread mReceiverThread = new HandlerThread(TAG);
-
-        // @GuardedBy("mMessages")
-        private final ArrayList<T> mMessages = new ArrayList<>();
-        private final long mCreatedTime = getCurrentTime();
-        private boolean mRegistered;
-
-        private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                // Log.d(TAG, "Received intent: " + intent);
-                if (intent.getAction().equals(ACTION_MESSAGE + mBroadcastSuffix)
-                        || intent.getAction().equals(ACTION_PING + mBroadcastSuffix)) {
-                    // OK
-                } else {
-                    throw new RuntimeException("Unknown broadcast received: " + intent);
-                }
-                if (intent.getLongExtra(EXTRA_SENT_TIME, 0) < mCreatedTime) {
-                    Log.i(TAG, "Dropping stale broadcast: " + intent);
-                    return;
-                }
-
-                // Note for a PING, the message will be null.
-                final T message = intent.getParcelableExtra(EXTRA_MESSAGE);
-                if (message != null) {
-                    Log.i(TAG, "Received: " + message);
-                }
-
-                synchronized (mMessages) {
-                    mMessages.add(message);
-                    mMessages.notifyAll();
-                }
-            }
-        };
-
-        /**
-         * Constructor.
-         */
-        public Receiver(@NonNull Context context, @NonNull String broadcastSuffix) {
-            mContext = context;
-            mBroadcastSuffix = Objects.requireNonNull(broadcastSuffix);
-
-            mReceiverThread.start();
-
-            final IntentFilter fi = new IntentFilter(ACTION_MESSAGE + mBroadcastSuffix);
-            fi.addAction(ACTION_PING + mBroadcastSuffix);
-
-            context.registerReceiver(mReceiver, fi, /* permission=*/ null,
-                    mReceiverThread.getThreadHandler(), Context.RECEIVER_EXPORTED);
-            mRegistered = true;
-        }
-
-        @Override
-        public void close() {
-            if (mRegistered) {
-                mContext.unregisterReceiver(mReceiver);
-                mReceiverThread.quit();
-                mRegistered = false;
-            }
-        }
-
-        /**
-         * Receive the next message with a 60 second timeout.
-         */
-        @NonNull
-        public T waitForNextMessage() {
-            return waitForNextMessage(DEFAULT_TIMEOUT_MS);
-        }
-
-        /**
-         * Receive the next message.
-         */
-        @NonNull
-        public T waitForNextMessage(long timeoutMillis) {
-            synchronized (mMessages) {
-                final long timeout = System.currentTimeMillis() + timeoutMillis;
-                while (mMessages.size() == 0) {
-                    final long wait = timeout - System.currentTimeMillis();
-                    if (wait <= 0) {
-                        throw new RuntimeException("Timeout waiting for the next message");
-                    }
-                    try {
-                        mMessages.wait(wait);
-                    } catch (InterruptedException e) {
-                        throw new RuntimeException(e);
-                    }
-                }
-                return mMessages.remove(0);
-            }
-        }
-
-        /**
-         * Ensure that no further messages have been received.
-         *
-         * Call it before {@link #close()}.
-         */
-        public void ensureNoMoreMessages() {
-            // Send a ping to myself.
-            sendPing(mContext, mBroadcastSuffix, mContext.getPackageName());
-
-            final T m = waitForNextMessage();
-            if (m == null) {
-                return; // Okay. Ping will deliver a null message.
-            }
-            throw new RuntimeException("No more messages expected, but received: " + m);
-        }
-    }
-}