Merge "Change secure setting that controls skipping of user-facing packages" into rvc-dev
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 3dbe413..0617eb6 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -116,7 +116,6 @@
         "libcutils",
         "libgtest_prod",
         "libprotoutil",
-        "libstatsmetadata",
         "libstatslog_statsd",
         "libsysutils",
         "libutils",
@@ -129,51 +128,6 @@
     ],
 }
 
-// ================
-// libstatsmetadata
-// ================
-
-genrule {
-    name: "atoms_info.h",
-    tools: ["stats-log-api-gen"],
-    cmd: "$(location stats-log-api-gen) --atomsInfoHeader $(genDir)/atoms_info.h",
-    out: [
-        "atoms_info.h",
-    ],
-}
-
-genrule {
-    name: "atoms_info.cpp",
-    tools: ["stats-log-api-gen"],
-    cmd: "$(location stats-log-api-gen) --atomsInfoCpp $(genDir)/atoms_info.cpp",
-    out: [
-        "atoms_info.cpp",
-    ],
-}
-
-cc_library_static {
-    name: "libstatsmetadata",
-    host_supported: true,
-    generated_sources: [
-        "atoms_info.cpp",
-    ],
-    generated_headers: [
-        "atoms_info.h",
-    ],
-    cflags: [
-        "-Wall",
-        "-Werror",
-    ],
-    export_generated_headers: [
-        "atoms_info.h",
-    ],
-    apex_available: [
-        //TODO(b/149782403): Remove this once statsd no longer links against libstatsmetadata
-        "com.android.os.statsd",
-        "test_com.android.os.statsd",
-    ],
-}
-
 genrule {
     name: "statslog_statsd.h",
     tools: ["stats-log-api-gen"],
diff --git a/cmds/statsd/src/atom_field_options.proto b/cmds/statsd/src/atom_field_options.proto
index 8527185..ff5717e 100644
--- a/cmds/statsd/src/atom_field_options.proto
+++ b/cmds/statsd/src/atom_field_options.proto
@@ -110,8 +110,6 @@
 
     optional LogMode log_mode = 50002 [default = MODE_AUTOMATIC];
 
-    optional bool allow_from_any_uid = 50003 [default = false];
-
     repeated string module = 50004;
 
     optional bool truncate_timestamp = 50005 [default = false];
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 663f910..a5f0ac9 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -145,8 +145,7 @@
         PacketWakeupOccurred packet_wakeup_occurred = 44 [(module) = "framework"];
         WallClockTimeShifted wall_clock_time_shifted = 45 [(module) = "framework"];
         AnomalyDetected anomaly_detected = 46 [(module) = "statsd"];
-        AppBreadcrumbReported app_breadcrumb_reported =
-                47 [(allow_from_any_uid) = true, (module) = "statsd"];
+        AppBreadcrumbReported app_breadcrumb_reported = 47 [(module) = "statsd"];
         AppStartOccurred app_start_occurred = 48 [(module) = "framework", (module) = "statsdtest"];
         AppStartCanceled app_start_canceled = 49 [(module) = "framework"];
         AppStartFullyDrawn app_start_fully_drawn = 50 [(module) = "framework"];
@@ -157,7 +156,7 @@
         AppStartMemoryStateCaptured app_start_memory_state_captured = 55 [(module) = "framework"];
         ShutdownSequenceReported shutdown_sequence_reported = 56 [(module) = "framework"];
         BootSequenceReported boot_sequence_reported = 57;
-        DaveyOccurred davey_occurred = 58 [(allow_from_any_uid) = true, (module) = "statsd"];
+        DaveyOccurred davey_occurred = 58 [(module) = "statsd"];
         OverlayStateChanged overlay_state_changed =
                 59 [(module) = "framework", (module) = "statsdtest"];
         ForegroundServiceStateChanged foreground_service_state_changed
@@ -186,8 +185,7 @@
         WTFOccurred wtf_occurred = 80 [(module) = "framework"];
         LowMemReported low_mem_reported = 81 [(module) = "framework"];
         GenericAtom generic_atom = 82;
-        KeyValuePairsAtom key_value_pairs_atom =
-                83 [(allow_from_any_uid) = true, (module) = "framework", (module) = "statsd"];
+        KeyValuePairsAtom key_value_pairs_atom = 83 [(module) = "framework", (module) = "statsd"];
         VibratorStateChanged vibrator_state_changed = 84 [(module) = "framework"];
         DeferredJobStatsReported deferred_job_stats_reported = 85 [(module) = "framework"];
         ThermalThrottlingStateChanged thermal_throttling = 86 [deprecated=true];
@@ -317,7 +315,7 @@
         AssistGestureFeedbackReported assist_gesture_feedback_reported = 175 [(module) = "sysui"];
         AssistGestureProgressReported assist_gesture_progress_reported = 176 [(module) = "sysui"];
         TouchGestureClassified touch_gesture_classified = 177 [(module) = "framework"];
-        HiddenApiUsed hidden_api_used = 178 [(allow_from_any_uid) = true, (module) = "framework"];
+        HiddenApiUsed hidden_api_used = 178 [(module) = "framework"];
         StyleUIChanged style_ui_changed = 179 [(module) = "sysui"];
         PrivacyIndicatorsInteracted privacy_indicators_interacted =
                 180 [(module) = "permissioncontroller"];
@@ -383,7 +381,7 @@
         UpdateEngineSuccessfulUpdateReported update_engine_successful_update_reported = 226;
         CameraActionEvent camera_action_event = 227 [(module) = "framework"];
         AppCompatibilityChangeReported app_compatibility_change_reported =
-            228 [(allow_from_any_uid) = true, (module) = "framework"];
+                228 [(module) = "framework"];
         PerfettoUploaded perfetto_uploaded = 229 [(module) = "perfetto"];
         VmsClientConnectionStateChanged vms_client_connection_state_changed =
                 230 [(module) = "car"];
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 7e825ef..60de1a2 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -21,7 +21,6 @@
 #include <private/android_filesystem_config.h>
 
 #include "CountMetricProducer.h"
-#include "atoms_info.h"
 #include "condition/CombinationConditionTracker.h"
 #include "condition/SimpleConditionTracker.h"
 #include "guardrail/StatsdStats.h"
@@ -372,13 +371,6 @@
 
 
 bool MetricsManager::checkLogCredentials(const LogEvent& event) {
-    // TODO(b/154856835): Remove this check once we get whitelist from the config.
-    if (android::util::AtomsInfo::kWhitelistedAtoms.find(event.GetTagId()) !=
-      android::util::AtomsInfo::kWhitelistedAtoms.end())
-    {
-        return true;
-    }
-
     if (mWhitelistedAtomIds.find(event.GetTagId()) != mWhitelistedAtomIds.end()) {
         return true;
     }
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 2ce6a86..8a6cc95 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -154,6 +154,7 @@
      * been misbehaving in some manner.
      * Apps in this bucket will have the most restrictions, including network restrictions and
      * additional restrictions on jobs.
+     * <p> Note: this bucket is not enabled in {@link Build.VERSION_CODES#R}.
      * @see #getAppStandbyBucket()
      */
     public static final int STANDBY_BUCKET_RESTRICTED = 45;
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 772845d..8d65c92 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -84,6 +84,8 @@
 import java.util.Arrays;
 import java.util.Deque;
 import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
 import java.util.concurrent.Executor;
 import java.util.concurrent.RejectedExecutionException;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -189,6 +191,9 @@
     // Only show an annoying dialog at most every 30 seconds
     private static final long MIN_DIALOG_INTERVAL_MS = 30000;
 
+    // Only log a dropbox entry at most every 30 seconds
+    private static final long MIN_DROPBOX_INTERVAL_MS = 3000;
+
     // How many Span tags (e.g. animations) to report.
     private static final int MAX_SPAN_TAGS = 20;
 
@@ -1752,16 +1757,20 @@
             // Not perfect, but fast and good enough for dup suppression.
             Integer crashFingerprint = info.hashCode();
             long lastViolationTime = 0;
-            if (mLastViolationTime != null) {
-                Long vtime = mLastViolationTime.get(crashFingerprint);
-                if (vtime != null) {
-                    lastViolationTime = vtime;
-                }
-            } else {
-                mLastViolationTime = new ArrayMap<>(1);
-            }
             long now = SystemClock.uptimeMillis();
-            mLastViolationTime.put(crashFingerprint, now);
+            if (sLogger == LOGCAT_LOGGER) { // Don't throttle it if there is a non-default logger
+                if (mLastViolationTime != null) {
+                    Long vtime = mLastViolationTime.get(crashFingerprint);
+                    if (vtime != null) {
+                        lastViolationTime = vtime;
+                    }
+                    clampViolationTimeMap(mLastViolationTime, Math.max(MIN_LOG_INTERVAL_MS,
+                                Math.max(MIN_DIALOG_INTERVAL_MS, MIN_DROPBOX_INTERVAL_MS)));
+                } else {
+                    mLastViolationTime = new ArrayMap<>(1);
+                }
+                mLastViolationTime.put(crashFingerprint, now);
+            }
             long timeSinceLastViolationMillis =
                     lastViolationTime == 0 ? Long.MAX_VALUE : (now - lastViolationTime);
 
@@ -1780,7 +1789,8 @@
                 penaltyMask |= PENALTY_DIALOG;
             }
 
-            if (info.penaltyEnabled(PENALTY_DROPBOX) && lastViolationTime == 0) {
+            if (info.penaltyEnabled(PENALTY_DROPBOX)
+                    && timeSinceLastViolationMillis > MIN_DROPBOX_INTERVAL_MS) {
                 penaltyMask |= PENALTY_DROPBOX;
             }
 
@@ -2215,6 +2225,23 @@
     @UnsupportedAppUsage
     private static final HashMap<Integer, Long> sLastVmViolationTime = new HashMap<>();
 
+    /**
+     * Clamp the given map by removing elements with timestamp older than the given retainSince.
+     */
+    private static void clampViolationTimeMap(final @NonNull Map<Integer, Long> violationTime,
+            final long retainSince) {
+        final Iterator<Map.Entry<Integer, Long>> iterator = violationTime.entrySet().iterator();
+        while (iterator.hasNext()) {
+            Map.Entry<Integer, Long> e = iterator.next();
+            if (e.getValue() < retainSince) {
+                // Remove stale entries
+                iterator.remove();
+            }
+        }
+        // Ideally we'd cap the total size of the map, though it'll involve quickselect of topK,
+        // seems not worth it (saving some space immediately but they will be obsoleted soon anyway)
+    }
+
     /** @hide */
     public static void onVmPolicyViolation(Violation originStack) {
         onVmPolicyViolation(originStack, false);
@@ -2238,13 +2265,17 @@
         final long now = SystemClock.uptimeMillis();
         long lastViolationTime;
         long timeSinceLastViolationMillis = Long.MAX_VALUE;
-        synchronized (sLastVmViolationTime) {
-            if (sLastVmViolationTime.containsKey(fingerprint)) {
-                lastViolationTime = sLastVmViolationTime.get(fingerprint);
-                timeSinceLastViolationMillis = now - lastViolationTime;
-            }
-            if (timeSinceLastViolationMillis > MIN_VM_INTERVAL_MS) {
-                sLastVmViolationTime.put(fingerprint, now);
+        if (sLogger == LOGCAT_LOGGER) { // Don't throttle it if there is a non-default logger
+            synchronized (sLastVmViolationTime) {
+                if (sLastVmViolationTime.containsKey(fingerprint)) {
+                    lastViolationTime = sLastVmViolationTime.get(fingerprint);
+                    timeSinceLastViolationMillis = now - lastViolationTime;
+                }
+                if (timeSinceLastViolationMillis > MIN_VM_INTERVAL_MS) {
+                    sLastVmViolationTime.put(fingerprint, now);
+                }
+                clampViolationTimeMap(sLastVmViolationTime,
+                        now - Math.max(MIN_VM_INTERVAL_MS, MIN_LOG_INTERVAL_MS));
             }
         }
         if (timeSinceLastViolationMillis <= MIN_VM_INTERVAL_MS) {
diff --git a/core/java/android/os/strictmode/Violation.java b/core/java/android/os/strictmode/Violation.java
index 31c7d58..0edb78a 100644
--- a/core/java/android/os/strictmode/Violation.java
+++ b/core/java/android/os/strictmode/Violation.java
@@ -18,7 +18,58 @@
 
 /** Root class for all StrictMode violations. */
 public abstract class Violation extends Throwable {
+    private int mHashCode;
+    private boolean mHashCodeValid;
+
     Violation(String message) {
         super(message);
     }
+
+    @Override
+    public int hashCode() {
+        synchronized (this) {
+            if (mHashCodeValid) {
+                return mHashCode;
+            }
+            final String message = getMessage();
+            final Throwable cause = getCause();
+            int hashCode = message != null ? message.hashCode() : getClass().hashCode();
+            hashCode = hashCode * 37 + calcStackTraceHashCode(getStackTrace());
+            hashCode = hashCode * 37 + (cause != null ? cause.toString().hashCode() : 0);
+            mHashCodeValid = true;
+            return mHashCode = hashCode;
+        }
+    }
+
+    @Override
+    public synchronized Throwable initCause(Throwable cause) {
+        mHashCodeValid = false;
+        return super.initCause(cause);
+    }
+
+    @Override
+    public void setStackTrace(StackTraceElement[] stackTrace) {
+        super.setStackTrace(stackTrace);
+        synchronized (this) {
+            mHashCodeValid = false;
+        }
+    }
+
+    @Override
+    public synchronized Throwable fillInStackTrace() {
+        mHashCodeValid = false;
+        return super.fillInStackTrace();
+    }
+
+    private static int calcStackTraceHashCode(final StackTraceElement[] stackTrace) {
+        int hashCode = 17;
+        if (stackTrace != null) {
+            for (int i = 0; i < stackTrace.length; i++) {
+                if (stackTrace[i] != null) {
+                    hashCode = hashCode * 37 + stackTrace[i].hashCode();
+                }
+            }
+        }
+        return hashCode;
+    }
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 4abb2c5..64d9c9d 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -12009,7 +12009,7 @@
          * @see #ENABLE_RESTRICTED_BUCKET
          * @hide
          */
-        public static final int DEFAULT_ENABLE_RESTRICTED_BUCKET = 1;
+        public static final int DEFAULT_ENABLE_RESTRICTED_BUCKET = 0;
 
         /**
          * Whether or not app auto restriction is enabled. When it is enabled, settings app will
diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp
index eb6ee95..5f231ff 100644
--- a/libs/androidfw/Idmap.cpp
+++ b/libs/androidfw/Idmap.cpp
@@ -157,7 +157,7 @@
   table_value->dataType = entry->type;
   table_value->data = entry->value;
 
-  return Result(ResTable_entry_handle::managed(table_entry));
+  return Result(ResTable_entry_handle::managed(table_entry, [](auto p) { free(p); }));
 }
 
 static bool is_word_aligned(const void* data) {
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 21be81c..e351a46 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -1601,8 +1601,8 @@
       entry_ = handle.entry_;
     }
 
-    inline static ResTable_entry_handle managed(ResTable_entry* entry)  {
-      return ResTable_entry_handle(std::shared_ptr<const ResTable_entry>(entry));
+    inline static ResTable_entry_handle managed(ResTable_entry* entry, void (*deleter)(void *)) {
+      return ResTable_entry_handle(std::shared_ptr<const ResTable_entry>(entry, deleter));
     }
 
     inline static ResTable_entry_handle unmanaged(const ResTable_entry* entry)  {
diff --git a/packages/SystemUI/res/layout/media_view.xml b/packages/SystemUI/res/layout/media_view.xml
index 594bbcf..5b86492 100644
--- a/packages/SystemUI/res/layout/media_view.xml
+++ b/packages/SystemUI/res/layout/media_view.xml
@@ -115,7 +115,7 @@
             android:id="@+id/media_seamless_text"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:fontFamily="@*android:string/config_bodyFontFamily"
+            android:fontFamily="@*android:string/config_headlineFontFamily"
             android:singleLine="true"
             android:text="@*android:string/ext_media_seamless_action"
             android:textColor="@color/media_primary_text"
@@ -161,6 +161,7 @@
         android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:singleLine="true"
+        android:fontFamily="@*android:string/config_headlineFontFamily"
         android:textSize="14sp" />
 
     <!-- Song name -->
@@ -178,7 +179,7 @@
         android:id="@+id/header_artist"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:fontFamily="@*android:string/config_bodyFontFamily"
+        android:fontFamily="@*android:string/config_headlineFontFamily"
         android:singleLine="true"
         android:textColor="@color/media_secondary_text"
         android:textSize="14sp" />
@@ -186,8 +187,8 @@
     <com.android.internal.widget.CachingIconView
         android:id="@+id/icon"
         android:tint="@color/media_primary_text"
-        android:layout_width="16dp"
-        android:layout_height="16dp" />
+        android:layout_width="20dp"
+        android:layout_height="20dp" />
 
     <!-- Buttons to remove this view when no longer needed -->
     <include
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index c2e5f31..50828e8 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -1504,6 +1504,7 @@
                     && ((BadgedImageView) v).getKey().equals(bubble.getKey())) {
                 mBubbleContainer.removeViewAt(i);
                 bubble.cleanupViews();
+                updatePointerPosition();
                 logBubbleEvent(bubble, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED);
                 return;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index 9e498a8..3fc162e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -57,6 +57,7 @@
  */
 public class MediaControlPanel {
     private static final String TAG = "MediaControlPanel";
+    private static final float DISABLED_ALPHA = 0.38f;
 
     // Button IDs for QS controls
     static final int[] ACTION_IDS = {
@@ -267,6 +268,11 @@
         mViewHolder.getSeamless().setVisibility(seamlessVisibility);
         expandedSet.setVisibility(seamlessId, seamlessVisibility);
         collapsedSet.setVisibility(seamlessId, seamlessVisibility);
+        final float seamlessAlpha = data.getResumption() ? DISABLED_ALPHA : 1.0f;
+        expandedSet.setAlpha(seamlessId, seamlessAlpha);
+        collapsedSet.setAlpha(seamlessId, seamlessAlpha);
+        // Disable clicking on output switcher for resumption controls.
+        mViewHolder.getSeamless().setEnabled(!data.getResumption());
         if (showFallback) {
             iconView.setImageDrawable(null);
             deviceName.setText(null);
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
index 0b0ffce..8c9cb1b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
@@ -81,6 +81,11 @@
      */
     var resumeAction: Runnable?,
     /**
+     * Indicates that this player is a resumption player (ie. It only shows a play actions which
+     * will start the app and start playing).
+     */
+    var resumption: Boolean = false,
+    /**
      * Notification key for cancelling a media player after a timeout (when not using resumption.)
      */
     val notificationKey: String? = null,
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index 3c863a3..416c81a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -323,7 +323,7 @@
             onMediaDataLoaded(packageName, null, MediaData(true, bgColor, appName,
                     null, desc.subtitle, desc.title, artworkIcon, listOf(mediaAction), listOf(0),
                     packageName, token, appIntent, device = null, active = false,
-                    resumeAction = resumeAction, notificationKey = packageName,
+                    resumeAction = resumeAction, resumption = true, notificationKey = packageName,
                     hasCheckedForResume = true))
         }
     }
@@ -542,7 +542,7 @@
             val data = mediaEntries.remove(key)!!
             val resumeAction = getResumeMediaAction(data.resumeAction!!)
             val updated = data.copy(token = null, actions = listOf(resumeAction),
-                actionsToShowInCompact = listOf(0), active = false)
+                    actionsToShowInCompact = listOf(0), active = false, resumption = true)
             mediaEntries.put(data.packageName, updated)
             // Notify listeners of "new" controls
             val listenersCopy = listeners.toSet()
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index 03a0d93..ad31220 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -387,6 +387,7 @@
         }
         // Always set this because we could be entering split when mMinimized is already true
         wct.setFocusable(mSplits.mPrimary.token, !mMinimized);
+        boolean onlyFocusable = true;
 
         // Update home-stack resizability
         final boolean homeResizableChanged = mHomeStackResizable != homeStackResizable;
@@ -395,6 +396,7 @@
             if (isDividerVisible()) {
                 WindowManagerProxy.applyHomeTasksMinimized(
                         mSplitLayout, mSplits.mSecondary.token, wct);
+                onlyFocusable = false;
             }
         }
 
@@ -416,7 +418,15 @@
             }
         }
         updateTouchable();
-        mWindowManagerProxy.applySyncTransaction(wct);
+        if (onlyFocusable) {
+            // If we are only setting focusability, a sync transaction isn't necessary (in fact it
+            // can interrupt other animations), so see if it can be submitted on pending instead.
+            if (!mSplits.mDivider.getWmProxy().queueSyncTransactionIfWaiting(wct)) {
+                WindowOrganizer.applyTransaction(wct);
+            }
+        } else {
+            mWindowManagerProxy.applySyncTransaction(wct);
+        }
     }
 
     void setAdjustedForIme(boolean adjustedForIme) {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java
index d782a3ca..47c8c0a 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java
@@ -29,6 +29,7 @@
 import android.window.TaskOrganizer;
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
+import android.window.WindowOrganizer;
 
 import androidx.annotation.Nullable;
 
@@ -173,47 +174,51 @@
     }
 
     private void updateImeAdjustState() {
-        // Reposition the server's secondary split position so that it evaluates
-        // insets properly.
-        WindowContainerTransaction wct = new WindowContainerTransaction();
-        final SplitDisplayLayout splitLayout = getLayout();
-        if (mTargetAdjusted) {
-            splitLayout.updateAdjustedBounds(mShownTop, mHiddenTop, mShownTop);
-            wct.setBounds(mSplits.mSecondary.token, splitLayout.mAdjustedSecondary);
-            // "Freeze" the configuration size so that the app doesn't get a config
-            // or relaunch. This is required because normally nav-bar contributes
-            // to configuration bounds (via nondecorframe).
-            Rect adjustAppBounds = new Rect(mSplits.mSecondary.configuration
-                    .windowConfiguration.getAppBounds());
-            adjustAppBounds.offset(0, splitLayout.mAdjustedSecondary.top
-                    - splitLayout.mSecondary.top);
-            wct.setAppBounds(mSplits.mSecondary.token, adjustAppBounds);
-            wct.setScreenSizeDp(mSplits.mSecondary.token,
-                    mSplits.mSecondary.configuration.screenWidthDp,
-                    mSplits.mSecondary.configuration.screenHeightDp);
+        if (mAdjusted != mTargetAdjusted) {
+            // Reposition the server's secondary split position so that it evaluates
+            // insets properly.
+            WindowContainerTransaction wct = new WindowContainerTransaction();
+            final SplitDisplayLayout splitLayout = getLayout();
+            if (mTargetAdjusted) {
+                splitLayout.updateAdjustedBounds(mShownTop, mHiddenTop, mShownTop);
+                wct.setBounds(mSplits.mSecondary.token, splitLayout.mAdjustedSecondary);
+                // "Freeze" the configuration size so that the app doesn't get a config
+                // or relaunch. This is required because normally nav-bar contributes
+                // to configuration bounds (via nondecorframe).
+                Rect adjustAppBounds = new Rect(mSplits.mSecondary.configuration
+                        .windowConfiguration.getAppBounds());
+                adjustAppBounds.offset(0, splitLayout.mAdjustedSecondary.top
+                        - splitLayout.mSecondary.top);
+                wct.setAppBounds(mSplits.mSecondary.token, adjustAppBounds);
+                wct.setScreenSizeDp(mSplits.mSecondary.token,
+                        mSplits.mSecondary.configuration.screenWidthDp,
+                        mSplits.mSecondary.configuration.screenHeightDp);
 
-            wct.setBounds(mSplits.mPrimary.token, splitLayout.mAdjustedPrimary);
-            adjustAppBounds = new Rect(mSplits.mPrimary.configuration
-                    .windowConfiguration.getAppBounds());
-            adjustAppBounds.offset(0, splitLayout.mAdjustedPrimary.top
-                    - splitLayout.mPrimary.top);
-            wct.setAppBounds(mSplits.mPrimary.token, adjustAppBounds);
-            wct.setScreenSizeDp(mSplits.mPrimary.token,
-                    mSplits.mPrimary.configuration.screenWidthDp,
-                    mSplits.mPrimary.configuration.screenHeightDp);
-        } else {
-            wct.setBounds(mSplits.mSecondary.token, splitLayout.mSecondary);
-            wct.setAppBounds(mSplits.mSecondary.token, null);
-            wct.setScreenSizeDp(mSplits.mSecondary.token,
-                    SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED);
-            wct.setBounds(mSplits.mPrimary.token, splitLayout.mPrimary);
-            wct.setAppBounds(mSplits.mPrimary.token, null);
-            wct.setScreenSizeDp(mSplits.mPrimary.token,
-                    SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED);
+                wct.setBounds(mSplits.mPrimary.token, splitLayout.mAdjustedPrimary);
+                adjustAppBounds = new Rect(mSplits.mPrimary.configuration
+                        .windowConfiguration.getAppBounds());
+                adjustAppBounds.offset(0, splitLayout.mAdjustedPrimary.top
+                        - splitLayout.mPrimary.top);
+                wct.setAppBounds(mSplits.mPrimary.token, adjustAppBounds);
+                wct.setScreenSizeDp(mSplits.mPrimary.token,
+                        mSplits.mPrimary.configuration.screenWidthDp,
+                        mSplits.mPrimary.configuration.screenHeightDp);
+            } else {
+                wct.setBounds(mSplits.mSecondary.token, splitLayout.mSecondary);
+                wct.setAppBounds(mSplits.mSecondary.token, null);
+                wct.setScreenSizeDp(mSplits.mSecondary.token,
+                        SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED);
+                wct.setBounds(mSplits.mPrimary.token, splitLayout.mPrimary);
+                wct.setAppBounds(mSplits.mPrimary.token, null);
+                wct.setScreenSizeDp(mSplits.mPrimary.token,
+                        SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED);
+            }
+
+            if (!mSplits.mDivider.getWmProxy().queueSyncTransactionIfWaiting(wct)) {
+                WindowOrganizer.applyTransaction(wct);
+            }
         }
 
-        mSplits.mDivider.getWmProxy().applySyncTransaction(wct);
-
         // Update all the adjusted-for-ime states
         if (!mPaused) {
             final DividerView view = getView();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index b4220f1..11e698b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -83,6 +83,8 @@
     private float mDozeAmount = 0.0f;
     private HeadsUpManager mHeadUpManager;
     private Runnable mOnPulseHeightChangedListener;
+    private ExpandableNotificationRow mTrackedHeadsUpRow;
+    private float mAppearFraction;
 
     public AmbientState(
             Context context,
@@ -543,4 +545,27 @@
     public Runnable getOnPulseHeightChangedListener() {
         return mOnPulseHeightChangedListener;
     }
+
+    public void setTrackedHeadsUpRow(ExpandableNotificationRow row) {
+        mTrackedHeadsUpRow = row;
+    }
+
+    /**
+     * Returns the currently tracked heads up row, if there is one and it is currently above the
+     * shelf (still appearing).
+     */
+    public ExpandableNotificationRow getTrackedHeadsUpRow() {
+        if (mTrackedHeadsUpRow == null || !mTrackedHeadsUpRow.isAboveShelf()) {
+            return null;
+        }
+        return mTrackedHeadsUpRow;
+    }
+
+    public void setAppearFraction(float appearFraction) {
+        mAppearFraction = appearFraction;
+    }
+
+    public float getAppearFraction() {
+        return mAppearFraction;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 8ebadfb..b9d31a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -535,6 +535,7 @@
     private float mLastSentAppear;
     private float mLastSentExpandedHeight;
     private boolean mWillExpand;
+    private int mGapHeight;
 
     private int mWaterfallTopInset;
 
@@ -1060,6 +1061,7 @@
 
         Resources res = context.getResources();
         mCollapsedSize = res.getDimensionPixelSize(R.dimen.notification_min_height);
+        mGapHeight = res.getDimensionPixelSize(R.dimen.notification_section_divider_height);
         mStackScrollAlgorithm.initView(context);
         mAmbientState.reload(context);
         mPaddingBetweenElements = Math.max(1,
@@ -1420,14 +1422,12 @@
                 // start
                 translationY = height - appearStartPosition + getExpandTranslationStart();
             }
+            stackHeight = (int) (height - translationY);
             if (isHeadsUpTransition()) {
-                stackHeight =
-                        getFirstVisibleSection().getFirstVisibleChild().getPinnedHeadsUpHeight();
                 translationY = MathUtils.lerp(mHeadsUpInset - mTopPadding, 0, appearFraction);
-            } else {
-                stackHeight = (int) (height - translationY);
             }
         }
+        mAmbientState.setAppearFraction(appearFraction);
         if (stackHeight != mCurrentStackHeight) {
             mCurrentStackHeight = stackHeight;
             updateAlgorithmHeightAndPadding();
@@ -1545,17 +1545,18 @@
      */
     @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     private float getAppearEndPosition() {
-        int appearPosition;
-        int notGoneChildCount = getNotGoneChildCount();
-        if (mEmptyShadeView.getVisibility() == GONE && notGoneChildCount != 0) {
+        int appearPosition = 0;
+        int visibleNotifCount = getVisibleNotificationCount();
+        if (mEmptyShadeView.getVisibility() == GONE && visibleNotifCount > 0) {
             if (isHeadsUpTransition()
                     || (mHeadsUpManager.hasPinnedHeadsUp() && !mAmbientState.isDozing())) {
-                appearPosition = getTopHeadsUpPinnedHeight();
-            } else {
-                appearPosition = 0;
-                if (notGoneChildCount >= 1 && mShelf.getVisibility() != GONE) {
-                    appearPosition += mShelf.getIntrinsicHeight();
+                if (mShelf.getVisibility() != GONE && visibleNotifCount > 1) {
+                    appearPosition += mShelf.getIntrinsicHeight() + mPaddingBetweenElements;
                 }
+                appearPosition += getTopHeadsUpPinnedHeight()
+                        + getPositionInLinearLayout(mAmbientState.getTrackedHeadsUpRow());
+            } else if (mShelf.getVisibility() != GONE) {
+                appearPosition += mShelf.getIntrinsicHeight();
             }
         } else {
             appearPosition = mEmptyShadeView.getHeight();
@@ -1565,9 +1566,7 @@
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     private boolean isHeadsUpTransition() {
-        NotificationSection firstVisibleSection = getFirstVisibleSection();
-        return mTrackingHeadsUp && firstVisibleSection != null
-                && firstVisibleSection.getFirstVisibleChild().isAboveShelf();
+        return mAmbientState.getTrackedHeadsUpRow() != null;
     }
 
     /**
@@ -2975,7 +2974,16 @@
     @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     public int getLayoutMinHeight() {
         if (isHeadsUpTransition()) {
-            return getTopHeadsUpPinnedHeight();
+            ExpandableNotificationRow trackedHeadsUpRow = mAmbientState.getTrackedHeadsUpRow();
+            if (trackedHeadsUpRow.isAboveShelf()) {
+                int hunDistance = (int) MathUtils.lerp(
+                        0,
+                        getPositionInLinearLayout(trackedHeadsUpRow),
+                        mAmbientState.getAppearFraction());
+                return getTopHeadsUpPinnedHeight() + hunDistance;
+            } else {
+                return getTopHeadsUpPinnedHeight();
+            }
         }
         return mShelf.getVisibility() == GONE ? 0 : mShelf.getIntrinsicHeight();
     }
@@ -5093,8 +5101,10 @@
     }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public int getFooterViewHeight() {
-        return mFooterView == null ? 0 : mFooterView.getHeight() + mPaddingBetweenElements;
+    public int getFooterViewHeightWithPadding() {
+        return mFooterView == null ? 0 : mFooterView.getHeight()
+                + mPaddingBetweenElements
+                + mGapHeight;
     }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
@@ -5295,6 +5305,7 @@
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setTrackingHeadsUp(ExpandableNotificationRow row) {
+        mAmbientState.setTrackedHeadsUpRow(row);
         mTrackingHeadsUp = row != null;
         mRoundnessManager.setTrackingHeadsUp(row);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index a4598e9..541c784 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.util.Log;
+import android.util.MathUtils;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -441,7 +442,7 @@
         } else if (isEmptyShadeView) {
             childViewState.yTranslation = ambientState.getInnerHeight() - childHeight
                     + ambientState.getStackTranslation() * 0.25f;
-        } else {
+        } else if (child != ambientState.getTrackedHeadsUpRow()) {
             clampPositionToShelf(child, childViewState, ambientState);
         }
 
@@ -539,6 +540,19 @@
     private void updateHeadsUpStates(StackScrollAlgorithmState algorithmState,
             AmbientState ambientState) {
         int childCount = algorithmState.visibleChildren.size();
+
+        // Move the tracked heads up into position during the appear animation, by interpolating
+        // between the HUN inset (where it will appear as a HUN) and the end position in the shade
+        ExpandableNotificationRow trackedHeadsUpRow = ambientState.getTrackedHeadsUpRow();
+        if (trackedHeadsUpRow != null) {
+            ExpandableViewState childState = trackedHeadsUpRow.getViewState();
+            if (childState != null) {
+                float endPosition = childState.yTranslation - ambientState.getStackTranslation();
+                childState.yTranslation = MathUtils.lerp(
+                        mHeadsUpInset, endPosition, ambientState.getAppearFraction());
+            }
+        }
+
         ExpandableNotificationRow topHeadsUpEntry = null;
         for (int i = 0; i < childCount; i++) {
             View child = algorithmState.visibleChildren.get(i);
@@ -561,7 +575,7 @@
                         && !row.showingPulsing()) {
                     // Ensure that the heads up is always visible even when scrolled off
                     clampHunToTop(ambientState, row, childState);
-                    if (i == 0 && row.isAboveShelf()) {
+                    if (isTopEntry && row.isAboveShelf()) {
                         // the first hun can't get off screen.
                         clampHunToMaxTranslation(ambientState, row, childState);
                         childState.hidden = false;
@@ -636,9 +650,13 @@
             return;
         }
 
+        ExpandableNotificationRow trackedHeadsUpRow = ambientState.getTrackedHeadsUpRow();
+        boolean isBeforeTrackedHeadsUp = trackedHeadsUpRow != null
+                && mHostView.indexOfChild(child) < mHostView.indexOfChild(trackedHeadsUpRow);
+
         int shelfStart = ambientState.getInnerHeight()
                 - ambientState.getShelf().getIntrinsicHeight();
-        if (ambientState.isAppearing() && !child.isAboveShelf()) {
+        if (ambientState.isAppearing() && !child.isAboveShelf() && !isBeforeTrackedHeadsUp) {
             // Don't show none heads-up notifications while in appearing phase.
             childViewState.yTranslation = Math.max(childViewState.yTranslation, shelfStart);
         }
@@ -695,7 +713,8 @@
             }
             childViewState.zTranslation = baseZ
                     + childrenOnTop * zDistanceBetweenElements;
-        } else if (i == 0 && (child.isAboveShelf() || child.showingPulsing())) {
+        } else if (child == ambientState.getTrackedHeadsUpRow()
+                || (i == 0 && (child.isAboveShelf() || child.showingPulsing()))) {
             // In case this is a new view that has never been measured before, we don't want to
             // elevate if we are currently expanded more then the notification
             int shelfHeight = ambientState.getShelf() == null ? 0 :
@@ -703,7 +722,7 @@
             float shelfStart = ambientState.getInnerHeight()
                     - shelfHeight + ambientState.getTopPadding()
                     + ambientState.getStackTranslation();
-            float notificationEnd = childViewState.yTranslation + child.getPinnedHeadsUpHeight()
+            float notificationEnd = childViewState.yTranslation + child.getIntrinsicHeight()
                     + mPaddingBetweenElements;
             if (shelfStart > notificationEnd) {
                 childViewState.zTranslation = baseZ;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index e720d82..0b664e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -2397,8 +2397,8 @@
     }
 
     @Override
-    protected int getClearAllHeight() {
-        return mNotificationStackScroller.getFooterViewHeight();
+    protected int getClearAllHeightWithPadding() {
+        return mNotificationStackScroller.getFooterViewHeightWithPadding();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index a902e1b..caddc4a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -537,9 +537,9 @@
         // the animation only to the last notification, and then jump to the maximum panel height so
         // clear all just fades in and the decelerating motion is towards the last notification.
         final boolean clearAllExpandHack = expand &&
-                shouldExpandToTopOfClearAll(getMaxPanelHeight() - getClearAllHeight());
+                shouldExpandToTopOfClearAll(getMaxPanelHeight() - getClearAllHeightWithPadding());
         if (clearAllExpandHack) {
-            target = getMaxPanelHeight() - getClearAllHeight();
+            target = getMaxPanelHeight() - getClearAllHeightWithPadding();
         }
         if (target == mExpandedHeight || getOverExpansionAmount() > 0f && expand) {
             notifyExpandingFinished();
@@ -1030,9 +1030,9 @@
     protected abstract boolean isClearAllVisible();
 
     /**
-     * @return the height of the clear all button, in pixels
+     * @return the height of the clear all button, in pixels including padding
      */
-    protected abstract int getClearAllHeight();
+    protected abstract int getClearAllHeightWithPadding();
 
     public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
         mHeadsUpManager = headsUpManager;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 3df1c11..6bc0565 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -72,9 +72,6 @@
         } catch (RemoteException ex) {
             // If the system process isn't there we're doomed anyway.
         }
-
-        // Creating AudioRecordingDisclosureBar and just letting it run
-        new AudioRecordingDisclosureBar(mContext);
     }
 
     @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index a297f32..b7f317b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -242,4 +242,15 @@
         assertThat(seamlessText.getText()).isEqualTo(context.getResources().getString(
                 com.android.internal.R.string.ext_media_seamless_action))
     }
+
+    @Test
+    fun bindDeviceResumptionPlayer() {
+        player.attach(holder)
+        val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
+                emptyList(), PACKAGE, session.getSessionToken(), null, device, true, null,
+                resumption = true)
+        player.bind(state)
+        assertThat(seamlessText.getText()).isEqualTo(DEVICE_NAME)
+        assertThat(seamless.isEnabled()).isFalse()
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
index 618ee89..9fdd9ad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
@@ -79,8 +79,8 @@
         mManager.addListener(mListener);
 
         mMediaData = new MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null,
-                new ArrayList<>(), new ArrayList<>(), PACKAGE, null, null, null, true, null, KEY,
-                false);
+                new ArrayList<>(), new ArrayList<>(), PACKAGE, null, null, null, true, null, false,
+                KEY, false);
         mDeviceData = new MediaDeviceData(true, null, DEVICE_NAME);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
index 54520be..20a6da5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -1,6 +1,11 @@
 package com.android.systemui.media
 
-import android.app.Notification
+import android.app.Notification.MediaStyle
+import android.app.PendingIntent
+import android.media.MediaDescription
+import android.media.MediaMetadata
+import android.media.session.MediaController
+import android.media.session.MediaSession
 import android.service.notification.StatusBarNotification
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper.RunWithLooper
@@ -8,6 +13,9 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.statusbar.SbnBuilder
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import org.junit.After
 import org.junit.Before
@@ -18,12 +26,14 @@
 import org.mockito.Mockito
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.verify
-import org.mockito.junit.MockitoJUnit
-import java.util.concurrent.Executor
 import org.mockito.Mockito.`when` as whenever
+import org.mockito.junit.MockitoJUnit
 
 private const val KEY = "KEY"
 private const val PACKAGE_NAME = "com.android.systemui"
+private const val APP_NAME = "SystemUI"
+private const val SESSION_ARTIST = "artist"
+private const val SESSION_TITLE = "title"
 
 private fun <T> eq(value: T): T = Mockito.eq(value) ?: value
 private fun <T> anyObject(): T {
@@ -36,33 +46,47 @@
 class MediaDataManagerTest : SysuiTestCase() {
 
     @Mock lateinit var mediaControllerFactory: MediaControllerFactory
-    @Mock lateinit var backgroundExecutor: Executor
-    @Mock lateinit var foregroundExecutor: Executor
+    @Mock lateinit var controller: MediaController
+    lateinit var session: MediaSession
+    lateinit var metadataBuilder: MediaMetadata.Builder
+    lateinit var backgroundExecutor: FakeExecutor
+    lateinit var foregroundExecutor: FakeExecutor
     @Mock lateinit var dumpManager: DumpManager
     @Mock lateinit var broadcastDispatcher: BroadcastDispatcher
     @Mock lateinit var mediaTimeoutListener: MediaTimeoutListener
     @Mock lateinit var mediaResumeListener: MediaResumeListener
+    @Mock lateinit var pendingIntent: PendingIntent
     @JvmField @Rule val mockito = MockitoJUnit.rule()
     lateinit var mediaDataManager: MediaDataManager
     lateinit var mediaNotification: StatusBarNotification
 
     @Before
     fun setup() {
+        foregroundExecutor = FakeExecutor(FakeSystemClock())
+        backgroundExecutor = FakeExecutor(FakeSystemClock())
         mediaDataManager = MediaDataManager(context, backgroundExecutor, foregroundExecutor,
                 mediaControllerFactory, broadcastDispatcher, dumpManager,
                 mediaTimeoutListener, mediaResumeListener, useMediaResumption = true,
                 useQsMediaPlayer = true)
-        val sbn = mock(StatusBarNotification::class.java)
-        val notification = mock(Notification::class.java)
-        whenever(notification.hasMediaSession()).thenReturn(true)
-        whenever(notification.notificationStyle).thenReturn(Notification.MediaStyle::class.java)
-        whenever(sbn.notification).thenReturn(notification)
-        whenever(sbn.packageName).thenReturn(PACKAGE_NAME)
-        mediaNotification = sbn
+        session = MediaSession(context, "MediaDataManagerTestSession")
+        mediaNotification = SbnBuilder().run {
+            setPkg(PACKAGE_NAME)
+            modifyNotification(context).also {
+                it.setSmallIcon(android.R.drawable.ic_media_pause)
+                it.setStyle(MediaStyle().apply { setMediaSession(session.sessionToken) })
+            }
+            build()
+        }
+        metadataBuilder = MediaMetadata.Builder().apply {
+            putString(MediaMetadata.METADATA_KEY_ARTIST, SESSION_ARTIST)
+            putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_TITLE)
+        }
+        whenever(mediaControllerFactory.create(eq(session.sessionToken))).thenReturn(controller)
     }
 
     @After
     fun tearDown() {
+        session.release()
         mediaDataManager.destroy()
     }
 
@@ -82,7 +106,7 @@
     @Test
     fun testLoadsMetadataOnBackground() {
         mediaDataManager.onNotificationAdded(KEY, mediaNotification)
-        verify(backgroundExecutor).execute(anyObject())
+        assertThat(backgroundExecutor.numPending()).isEqualTo(1)
     }
 
     @Test
@@ -123,4 +147,66 @@
 
         verify(listener).onMediaDataRemoved(eq(KEY))
     }
-}
\ No newline at end of file
+
+    @Test
+    fun testOnNotificationRemoved_withResumption() {
+        // GIVEN that the manager has a notification with a resume action
+        val listener = TestListener()
+        mediaDataManager.addListener(listener)
+        whenever(controller.metadata).thenReturn(metadataBuilder.build())
+        mediaDataManager.onNotificationAdded(KEY, mediaNotification)
+        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
+        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+        val data = listener.data!!
+        assertThat(data.resumption).isFalse()
+        mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {}))
+        // WHEN the notification is removed
+        mediaDataManager.onNotificationRemoved(KEY)
+        // THEN the media data indicates that it is
+        assertThat(listener.data!!.resumption).isTrue()
+    }
+
+    @Test
+    fun testAddResumptionControls() {
+        val listener = TestListener()
+        mediaDataManager.addListener(listener)
+        // WHEN resumption controls are added`
+        val desc = MediaDescription.Builder().run {
+            setTitle(SESSION_TITLE)
+            build()
+        }
+        mediaDataManager.addResumptionControls(desc, Runnable {}, session.sessionToken, APP_NAME,
+                pendingIntent, PACKAGE_NAME)
+        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
+        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+        // THEN the media data indicates that it is for resumption
+        val data = listener.data!!
+        assertThat(data.resumption).isTrue()
+        assertThat(data.song).isEqualTo(SESSION_TITLE)
+        assertThat(data.app).isEqualTo(APP_NAME)
+        assertThat(data.actions).hasSize(1)
+    }
+
+    /**
+     * Simple implementation of [MediaDataManager.Listener] for the test.
+     *
+     * Giving up on trying to get a mock Listener and ArgumentCaptor to work.
+     */
+    private class TestListener : MediaDataManager.Listener {
+        var data: MediaData? = null
+        var key: String? = null
+        var oldKey: String? = null
+
+        override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) {
+            this.key = key
+            this.oldKey = oldKey
+            this.data = data
+        }
+
+        override fun onMediaDataRemoved(key: String) {
+            this.key = key
+            oldKey = null
+            data = null
+        }
+    }
+}
diff --git a/tools/stats_log_api_gen/Android.bp b/tools/stats_log_api_gen/Android.bp
index b1e2487..e3b6db0 100644
--- a/tools/stats_log_api_gen/Android.bp
+++ b/tools/stats_log_api_gen/Android.bp
@@ -21,7 +21,6 @@
     name: "stats-log-api-gen",
     srcs: [
         "Collation.cpp",
-        "atoms_info_writer.cpp",
         "java_writer.cpp",
         "java_writer_q.cpp",
         "main.cpp",
diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp
index 958e94e..a230de4 100644
--- a/tools/stats_log_api_gen/Collation.cpp
+++ b/tools/stats_log_api_gen/Collation.cpp
@@ -52,9 +52,7 @@
       defaultState(that.defaultState),
       triggerStateReset(that.triggerStateReset),
       nested(that.nested),
-      uidField(that.uidField),
-      whitelisted(that.whitelisted),
-      truncateTimestamp(that.truncateTimestamp) {
+      uidField(that.uidField) {
 }
 
 AtomDecl::AtomDecl(int c, const string& n, const string& m) : code(c), name(n), message(m) {
@@ -520,13 +518,6 @@
         shared_ptr<AtomDecl> atomDecl =
                 make_shared<AtomDecl>(atomField->number(), atomField->name(), atom->name());
 
-        if (atomField->options().GetExtension(os::statsd::allow_from_any_uid) == true) {
-            atomDecl->whitelisted = true;
-            if (dbg) {
-                printf("%s is whitelisted\n", atomField->name().c_str());
-            }
-        }
-
         if (atomDecl->code < PULL_ATOM_START_ID &&
             atomField->options().GetExtension(os::statsd::truncate_timestamp)) {
             addAnnotationToAtomDecl(atomDecl.get(), ATOM_ID_FIELD_NUMBER,
diff --git a/tools/stats_log_api_gen/Collation.h b/tools/stats_log_api_gen/Collation.h
index 043f8b1..10b34ec 100644
--- a/tools/stats_log_api_gen/Collation.h
+++ b/tools/stats_log_api_gen/Collation.h
@@ -164,10 +164,6 @@
 
     int uidField = 0;
 
-    bool whitelisted = false;
-
-    bool truncateTimestamp = false;
-
     AtomDecl();
     AtomDecl(const AtomDecl& that);
     AtomDecl(int code, const string& name, const string& message);
diff --git a/tools/stats_log_api_gen/atoms_info_writer.cpp b/tools/stats_log_api_gen/atoms_info_writer.cpp
deleted file mode 100644
index 292cb21..0000000
--- a/tools/stats_log_api_gen/atoms_info_writer.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "atoms_info_writer.h"
-
-#include <map>
-#include <set>
-#include <vector>
-
-#include "utils.h"
-
-namespace android {
-namespace stats_log_api_gen {
-
-static void write_atoms_info_header_body(FILE* out) {
-    fprintf(out, "struct AtomsInfo {\n");
-    fprintf(out, "  const static std::set<int> kWhitelistedAtoms;\n");
-    fprintf(out, "};\n");
-}
-
-static void write_atoms_info_cpp_body(FILE* out, const Atoms& atoms) {
-
-    fprintf(out, "const std::set<int> AtomsInfo::kWhitelistedAtoms = {\n");
-    for (AtomDeclSet::const_iterator atomIt = atoms.decls.begin(); atomIt != atoms.decls.end();
-         atomIt++) {
-        if ((*atomIt)->whitelisted) {
-            const string constant = make_constant_name((*atomIt)->name);
-            fprintf(out, "    %d, // %s\n", (*atomIt)->code, constant.c_str());
-        }
-    }
-
-    fprintf(out, "};\n");
-    fprintf(out, "\n");
-
-}
-
-int write_atoms_info_header(FILE* out, const string& namespaceStr) {
-    // Print prelude
-    fprintf(out, "// This file is autogenerated\n");
-    fprintf(out, "\n");
-    fprintf(out, "#pragma once\n");
-    fprintf(out, "\n");
-    fprintf(out, "#include <vector>\n");
-    fprintf(out, "#include <map>\n");
-    fprintf(out, "#include <set>\n");
-    fprintf(out, "\n");
-
-    write_namespace(out, namespaceStr);
-
-    write_atoms_info_header_body(out);
-
-    fprintf(out, "\n");
-    write_closing_namespace(out, namespaceStr);
-
-    return 0;
-}
-
-int write_atoms_info_cpp(FILE* out, const Atoms& atoms, const string& namespaceStr,
-                         const string& importHeader) {
-    // Print prelude
-    fprintf(out, "// This file is autogenerated\n");
-    fprintf(out, "\n");
-    fprintf(out, "#include <%s>\n", importHeader.c_str());
-    fprintf(out, "\n");
-
-    write_namespace(out, namespaceStr);
-
-    write_atoms_info_cpp_body(out, atoms);
-
-    // Print footer
-    fprintf(out, "\n");
-    write_closing_namespace(out, namespaceStr);
-
-    return 0;
-}
-
-}  // namespace stats_log_api_gen
-}  // namespace android
diff --git a/tools/stats_log_api_gen/atoms_info_writer.h b/tools/stats_log_api_gen/atoms_info_writer.h
deleted file mode 100644
index 09a4303..0000000
--- a/tools/stats_log_api_gen/atoms_info_writer.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <stdio.h>
-#include <string.h>
-
-#include "Collation.h"
-
-namespace android {
-namespace stats_log_api_gen {
-
-using namespace std;
-
-int write_atoms_info_cpp(FILE* out, const Atoms& atoms, const string& namespaceStr,
-                         const string& importHeader);
-
-int write_atoms_info_header(FILE* out, const string& namespaceStr);
-
-}  // namespace stats_log_api_gen
-}  // namespace android
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index 136933b..b888ce9 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -9,7 +9,6 @@
 #include <vector>
 
 #include "Collation.h"
-#include "atoms_info_writer.h"
 #include "frameworks/base/cmds/statsd/src/atoms.pb.h"
 #include "java_writer.h"
 #include "java_writer_q.h"
@@ -30,12 +29,6 @@
     fprintf(stderr, "OPTIONS\n");
     fprintf(stderr, "  --cpp FILENAME       the header file to output for write helpers\n");
     fprintf(stderr, "  --header FILENAME    the cpp file to output for write helpers\n");
-    fprintf(stderr,
-            "  --atomsInfoCpp FILENAME       the header file to output for "
-            "statsd metadata\n");
-    fprintf(stderr,
-            "  --atomsInfoHeader FILENAME    the cpp file to output for statsd "
-            "metadata\n");
     fprintf(stderr, "  --help               this message\n");
     fprintf(stderr, "  --java FILENAME      the java file to output\n");
     fprintf(stderr, "  --module NAME        optional, module name to generate outputs for\n");
@@ -49,10 +42,6 @@
             "  --importHeader NAME  required for cpp/jni to say which header to "
             "import "
             "for write helpers\n");
-    fprintf(stderr,
-            "  --atomsInfoImportHeader NAME  required for cpp to say which "
-            "header to import "
-            "for statsd metadata\n");
     fprintf(stderr, "  --javaPackage PACKAGE             the package for the java file.\n");
     fprintf(stderr, "                                    required for java with module\n");
     fprintf(stderr, "  --javaClass CLASS    the class name of the java class.\n");
@@ -74,15 +63,12 @@
     string cppFilename;
     string headerFilename;
     string javaFilename;
-    string atomsInfoCppFilename;
-    string atomsInfoHeaderFilename;
     string javaPackage;
     string javaClass;
 
     string moduleName = DEFAULT_MODULE_NAME;
     string cppNamespace = DEFAULT_CPP_NAMESPACE;
     string cppHeaderImport = DEFAULT_CPP_HEADER_IMPORT;
-    string atomsInfoCppHeaderImport = DEFAULT_ATOMS_INFO_CPP_HEADER_IMPORT;
     bool supportQ = false;
     bool supportWorkSource = false;
     bool compileQ = false;
@@ -148,27 +134,6 @@
                 return 1;
             }
             javaClass = argv[index];
-        } else if (0 == strcmp("--atomsInfoHeader", argv[index])) {
-            index++;
-            if (index >= argc) {
-                print_usage();
-                return 1;
-            }
-            atomsInfoHeaderFilename = argv[index];
-        } else if (0 == strcmp("--atomsInfoCpp", argv[index])) {
-            index++;
-            if (index >= argc) {
-                print_usage();
-                return 1;
-            }
-            atomsInfoCppFilename = argv[index];
-        } else if (0 == strcmp("--atomsInfoImportHeader", argv[index])) {
-            index++;
-            if (index >= argc) {
-                print_usage();
-                return 1;
-            }
-            atomsInfoCppHeaderImport = argv[index];
         } else if (0 == strcmp("--supportQ", argv[index])) {
             supportQ = true;
         } else if (0 == strcmp("--worksource", argv[index])) {
@@ -180,8 +145,7 @@
         index++;
     }
 
-    if (cppFilename.size() == 0 && headerFilename.size() == 0 && javaFilename.size() == 0 &&
-        atomsInfoHeaderFilename.size() == 0 && atomsInfoCppFilename.size() == 0) {
+    if (cppFilename.size() == 0 && headerFilename.size() == 0 && javaFilename.size() == 0) {
         print_usage();
         return 1;
     }
@@ -210,29 +174,6 @@
     collate_atom(android::os::statsd::AttributionNode::descriptor(), &attributionDecl,
                  &attributionSignature);
 
-    // Write the atoms info .cpp file
-    if (atomsInfoCppFilename.size() != 0) {
-        FILE* out = fopen(atomsInfoCppFilename.c_str(), "w");
-        if (out == NULL) {
-            fprintf(stderr, "Unable to open file for write: %s\n", atomsInfoCppFilename.c_str());
-            return 1;
-        }
-        errorCount = android::stats_log_api_gen::write_atoms_info_cpp(out, atoms, cppNamespace,
-                                                                      atomsInfoCppHeaderImport);
-        fclose(out);
-    }
-
-    // Write the atoms info .h file
-    if (atomsInfoHeaderFilename.size() != 0) {
-        FILE* out = fopen(atomsInfoHeaderFilename.c_str(), "w");
-        if (out == NULL) {
-            fprintf(stderr, "Unable to open file for write: %s\n", atomsInfoHeaderFilename.c_str());
-            return 1;
-        }
-        errorCount = android::stats_log_api_gen::write_atoms_info_header(out, cppNamespace);
-        fclose(out);
-    }
-
     // Write the .cpp file
     if (cppFilename.size() != 0) {
         FILE* out = fopen(cppFilename.c_str(), "w");
diff --git a/tools/stats_log_api_gen/test.proto b/tools/stats_log_api_gen/test.proto
index d22acc6..aaa488e 100644
--- a/tools/stats_log_api_gen/test.proto
+++ b/tools/stats_log_api_gen/test.proto
@@ -187,24 +187,6 @@
     optional int32 state = 3 [(android.os.statsd.state_field_option).exclusive_state = true];
 }
 
-message WhitelistedAtom {
-  optional int32 field = 1;
-}
-
-message NonWhitelistedAtom {
-  optional int32 field = 1;
-}
-
-message ListedAtoms {
-  oneof event {
-    // Atoms can be whitelisted i.e. they can be triggered by any source
-    WhitelistedAtom whitelisted_atom = 1 [(android.os.statsd.allow_from_any_uid) = true];
-    // Atoms are not whitelisted by default, so they can only be triggered
-    // by whitelisted sources
-    NonWhitelistedAtom non_whitelisted_atom = 2;
-  }
-}
-
 message ModuleOneAtom {
     optional int32 field = 1 [(android.os.statsd.is_uid) = true];
 }
diff --git a/tools/stats_log_api_gen/test_collation.cpp b/tools/stats_log_api_gen/test_collation.cpp
index 1504752..dbae588 100644
--- a/tools/stats_log_api_gen/test_collation.cpp
+++ b/tools/stats_log_api_gen/test_collation.cpp
@@ -225,25 +225,6 @@
     EXPECT_TRUE(errorCount > 0);
 }
 
-TEST(CollationTest, PassOnWhitelistedAtom) {
-    Atoms atoms;
-    int errorCount = collate_atoms(ListedAtoms::descriptor(), DEFAULT_MODULE_NAME, &atoms);
-    EXPECT_EQ(errorCount, 0);
-    EXPECT_EQ(atoms.decls.size(), 2ul);
-}
-
-TEST(CollationTest, RecogniseWhitelistedAtom) {
-    Atoms atoms;
-    collate_atoms(ListedAtoms::descriptor(), DEFAULT_MODULE_NAME, &atoms);
-    for (const auto& atomDecl : atoms.decls) {
-        if (atomDecl->code == 1) {
-            EXPECT_TRUE(atomDecl->whitelisted);
-        } else {
-            EXPECT_FALSE(atomDecl->whitelisted);
-        }
-    }
-}
-
 TEST(CollationTest, PassOnLogFromModuleAtom) {
     Atoms atoms;
     int errorCount = collate_atoms(ModuleAtoms::descriptor(), DEFAULT_MODULE_NAME, &atoms);
diff --git a/tools/stats_log_api_gen/utils.h b/tools/stats_log_api_gen/utils.h
index 7d6d08e..73e0cb8 100644
--- a/tools/stats_log_api_gen/utils.h
+++ b/tools/stats_log_api_gen/utils.h
@@ -32,7 +32,6 @@
 
 const string DEFAULT_CPP_NAMESPACE = "android,util";
 const string DEFAULT_CPP_HEADER_IMPORT = "statslog.h";
-const string DEFAULT_ATOMS_INFO_CPP_HEADER_IMPORT = "atoms_info.h";
 
 const int JAVA_MODULE_REQUIRES_FLOAT = 0x01;
 const int JAVA_MODULE_REQUIRES_ATTRIBUTION = 0x02;