Merge "Secondary split screen app will be next to the navigation bar (1/2)" into pi-dev
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index e21392c..511aec3 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -122,6 +122,12 @@
     VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId);
 }
 
+
+void CountMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) {
+    flushIfNeededLocked(dumpTimeNs);
+    mPastBuckets.clear();
+}
+
 void CountMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
                                              const bool include_current_partial_bucket,
                                              ProtoOutputStream* protoOutput) {
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index cafc882..8e94a75 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -59,6 +59,8 @@
                             const bool include_current_partial_bucket,
                             android::util::ProtoOutputStream* protoOutput) override;
 
+    void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
+
     // Internal interface to handle condition change.
     void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override;
 
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 3661b31..5610492 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -438,6 +438,11 @@
     mPastBuckets.clear();
 }
 
+void DurationMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) {
+    flushIfNeededLocked(dumpTimeNs);
+    mPastBuckets.clear();
+}
+
 void DurationMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
                                                 const bool include_current_partial_bucket,
                                                 ProtoOutputStream* protoOutput) {
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index 80fbdde..f755294 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -65,6 +65,8 @@
                             const bool include_current_partial_bucket,
                             android::util::ProtoOutputStream* protoOutput) override;
 
+    void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
+
     // Internal interface to handle condition change.
     void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override;
 
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index 2f2679e..23f780a 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -100,6 +100,10 @@
     return buffer;
 }
 
+void EventMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) {
+    mProto->clear();
+}
+
 void EventMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
                                              const bool include_current_partial_bucket,
                                              ProtoOutputStream* protoOutput) {
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index 5c29174..23302c4 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -49,6 +49,7 @@
     void onDumpReportLocked(const int64_t dumpTimeNs,
                             const bool include_current_partial_bucket,
                             android::util::ProtoOutputStream* protoOutput) override;
+    void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
 
     // Internal interface to handle condition change.
     void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override;
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 1270856..005cb71 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -160,6 +160,12 @@
     }
 }
 
+void GaugeMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) {
+    flushIfNeededLocked(dumpTimeNs);
+    mPastBuckets.clear();
+    mSkippedBuckets.clear();
+}
+
 void GaugeMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
                                              const bool include_current_partial_bucket,
                                              ProtoOutputStream* protoOutput) {
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index 71d5912..8af2f36 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -91,6 +91,7 @@
     void onDumpReportLocked(const int64_t dumpTimeNs,
                             const bool include_current_partial_bucket,
                             android::util::ProtoOutputStream* protoOutput) override;
+    void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
 
     // for testing
     GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& gaugeMetric,
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 532ecbf..4239649 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -119,6 +119,11 @@
         return onDumpReportLocked(dumpTimeNs, include_current_partial_bucket, protoOutput);
     }
 
+    void clearPastBuckets(const int64_t dumpTimeNs) {
+        std::lock_guard<std::mutex> lock(mMutex);
+        return clearPastBucketsLocked(dumpTimeNs);
+    }
+
     void dumpStates(FILE* out, bool verbose) const {
         std::lock_guard<std::mutex> lock(mMutex);
         dumpStatesLocked(out, verbose);
@@ -177,6 +182,7 @@
     virtual void onDumpReportLocked(const int64_t dumpTimeNs,
                                     const bool include_current_partial_bucket,
                                     android::util::ProtoOutputStream* protoOutput) = 0;
+    virtual void clearPastBucketsLocked(const int64_t dumpTimeNs) = 0;
     virtual size_t byteSizeLocked() const = 0;
     virtual void dumpStatesLocked(FILE* out, bool verbose) const = 0;
 
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 47a1a86..0d3aebf 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -201,6 +201,8 @@
                     protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_METRICS);
             producer->onDumpReport(dumpTimeStampNs, include_current_partial_bucket, protoOutput);
             protoOutput->end(token);
+        } else {
+            producer->clearPastBuckets(dumpTimeStampNs);
         }
     }
     for (const auto& annotation : mAnnotations) {
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 27fd78f..506cf38 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -151,6 +151,12 @@
     mPastBuckets.clear();
 }
 
+void ValueMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) {
+    flushIfNeededLocked(dumpTimeNs);
+    mPastBuckets.clear();
+    mSkippedBuckets.clear();
+}
+
 void ValueMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
                                              const bool include_current_partial_bucket,
                                              ProtoOutputStream* protoOutput) {
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 8df30d3..64604c2 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -89,6 +89,7 @@
     void onDumpReportLocked(const int64_t dumpTimeNs,
                             const bool include_current_partial_bucket,
                             android::util::ProtoOutputStream* protoOutput) override;
+    void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
 
     // Internal interface to handle condition change.
     void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override;
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index c5b8019..725f240 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5102,6 +5102,10 @@
                     savedBundle.getBoolean(EXTRA_SHOW_CHRONOMETER));
             publicExtras.putBoolean(EXTRA_CHRONOMETER_COUNT_DOWN,
                     savedBundle.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN));
+            String appName = savedBundle.getString(EXTRA_SUBSTITUTE_APP_NAME);
+            if (appName != null) {
+                publicExtras.putString(EXTRA_SUBSTITUTE_APP_NAME, appName);
+            }
             mN.extras = publicExtras;
             RemoteViews view;
             if (ambient) {
diff --git a/core/java/android/hardware/radio/RadioMetadata.java b/core/java/android/hardware/radio/RadioMetadata.java
index 6e51060..baa7a50 100644
--- a/core/java/android/hardware/radio/RadioMetadata.java
+++ b/core/java/android/hardware/radio/RadioMetadata.java
@@ -269,6 +269,29 @@
         mBundle = in.readBundle();
     }
 
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder("RadioMetadata[");
+
+        final String removePrefix = "android.hardware.radio.metadata";
+
+        boolean first = true;
+        for (String key : mBundle.keySet()) {
+            if (first) first = false;
+            else sb.append(", ");
+
+            String keyDisp = key;
+            if (key.startsWith(removePrefix)) keyDisp = key.substring(removePrefix.length());
+
+            sb.append(keyDisp);
+            sb.append('=');
+            sb.append(mBundle.get(key));
+        }
+
+        sb.append("]");
+        return sb.toString();
+    }
+
     /**
      * Returns {@code true} if the given key is contained in the meta data
      *
diff --git a/core/java/com/android/internal/policy/DecorContext.java b/core/java/com/android/internal/policy/DecorContext.java
index eac9f64..cd80d53 100644
--- a/core/java/com/android/internal/policy/DecorContext.java
+++ b/core/java/com/android/internal/policy/DecorContext.java
@@ -23,6 +23,8 @@
 import android.view.WindowManager;
 import android.view.WindowManagerImpl;
 
+import java.lang.ref.WeakReference;
+
 /**
  * Context for decor views which can be seeded with pure application context and not depend on the
  * activity, but still provide some of the facilities that Activity has,
@@ -35,9 +37,12 @@
     private WindowManager mWindowManager;
     private Resources mActivityResources;
 
-    public DecorContext(Context context, Resources activityResources) {
+    private WeakReference<Context> mActivityContext;
+
+    public DecorContext(Context context, Context activityContext) {
         super(context, null);
-        mActivityResources = activityResources;
+        mActivityContext = new WeakReference<>(activityContext);
+        mActivityResources = activityContext.getResources();
     }
 
     void setPhoneWindow(PhoneWindow phoneWindow) {
@@ -60,6 +65,13 @@
 
     @Override
     public Resources getResources() {
+        Context activityContext = mActivityContext.get();
+        // Attempt to update the local cached Resources from the activity context. If the activity
+        // is no longer around, return the old cached values.
+        if (activityContext != null) {
+            mActivityResources = activityContext.getResources();
+        }
+
         return mActivityResources;
     }
 
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index eadefc9..cc95df7 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -41,7 +41,6 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
-import android.app.ActivityManager;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -55,7 +54,6 @@
 import android.graphics.Shader;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
-import android.os.RemoteException;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.TypedValue;
@@ -1844,6 +1842,13 @@
     }
 
     @Override
+    public Resources getResources() {
+        // Make sure the Resources object is propogated from the Context since it can be updated in
+        // the Context object.
+        return getContext().getResources();
+    }
+
+    @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
 
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 528888f..7ea023e 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -2299,7 +2299,7 @@
             if (applicationContext == null) {
                 context = getContext();
             } else {
-                context = new DecorContext(applicationContext, getContext().getResources());
+                context = new DecorContext(applicationContext, getContext());
                 if (mTheme != -1) {
                     context.setTheme(mTheme);
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 77c9f1e..ff0adb6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1904,6 +1904,13 @@
             }
         }
 
+        if (!panelsEnabled()) {
+            if (DEBUG) {
+                Log.d(TAG, "No peeking: disabled panel : " + sbn.getKey());
+            }
+            return false;
+        }
+
         if (sbn.getNotification().fullScreenIntent != null) {
             if (mAccessibilityManager.isTouchExplorationEnabled()) {
                 if (DEBUG) Log.d(TAG, "No peeking: accessible fullscreen: " + sbn.getKey());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index d3cb5a6..37e0005 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -443,6 +443,30 @@
     }
 
     @Test
+    public void testPeek_disabledStatusBar() {
+        Notification n = new Notification.Builder(getContext(), "a").build();
+        StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
+                UserHandle.of(0), null, 0);
+        NotificationData.Entry entry = new NotificationData.Entry(sbn);
+        mStatusBar.disable(StatusBarManager.DISABLE_EXPAND, 0, false /* animate */);
+
+        assertFalse("The panel shouldn't allow peek while disabled",
+                mStatusBar.shouldPeek(entry, sbn));
+    }
+
+    @Test
+    public void testPeek_disabledNotificationShade() {
+        Notification n = new Notification.Builder(getContext(), "a").build();
+        StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
+                UserHandle.of(0), null, 0);
+        NotificationData.Entry entry = new NotificationData.Entry(sbn);
+        mStatusBar.disable(0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false /* animate */);
+
+        assertFalse("The panel shouldn't allow peek while notitifcation shade disabled",
+                mStatusBar.shouldPeek(entry, sbn));
+    }
+
+    @Test
     public void testLogHidden() {
         try {
             mStatusBar.handleVisibleToUserChanged(false);
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index 2465ba2..d869734 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -33,10 +33,11 @@
 # It logs the time remaining before the device would've normally gone to sleep without the request.
 2731 power_soft_sleep_requested (savedwaketimems|2)
 # Power save state has changed. See BatterySaverController.java for the details.
-2739 battery_saver_mode (prevOffOrOn|1|5),(nowOffOrOn|1|5),(interactive|1|5),(features|3|5)
+2739 battery_saver_mode (prevOffOrOn|1|5),(nowOffOrOn|1|5),(interactive|1|5),(features|3|5),(reason|1|5)
 27390 battery_saving_stats (batterySaver|1|5),(interactive|1|5),(doze|1|5),(delta_duration|2|3),(delta_battery_drain|1|1),(delta_battery_drain_percent|1|6),(total_duration|2|3),(total_battery_drain|1|1),(total_battery_drain_percent|1|6)
 # Note when the user activity timeout has been overriden by ActivityManagerService
 27391 user_activity_timeout_override (override|2|3)
+27392 battery_saver_setting (threshold|1)
 
 #
 # Leave IDs through 2740 for more power logs (2730 used by battery_discharge above)
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index e840a29..03d8f39 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -1279,7 +1279,9 @@
                 case Process.SYSTEM_UID:
                     break; // Okay
                 default:
-                    throw new SecurityException("Invalid extras specified.");
+                    final String msg = "Invalid extras specified.";
+                    Log.w(TAG, msg + " requestsync -f/-F needs to run on 'adb shell'");
+                    throw new SecurityException(msg);
             }
         }
     }
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index f403953..0425844 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -316,7 +316,7 @@
         return true;
     }
 
-    private void resetShortTermModel() {
+    public void resetShortTermModel() {
         mBrightnessMapper.clearUserDataPoints();
         mShortTermModelValid = true;
         mShortTermModelAnchor = -1;
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 46e883c..3b35d02 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -774,14 +774,8 @@
             brightness = mScreenBrightnessForVr;
         }
 
-        boolean setBrightnessToOverride = false;
         if (brightness < 0 && mPowerRequest.screenBrightnessOverride > 0) {
             brightness = mPowerRequest.screenBrightnessOverride;
-            // If there's a screen brightness override, we want to reset the brightness to it
-            // whenever the user changes it, to communicate that these changes aren't taking
-            // effect. However, for a nicer user experience, we don't do it here, but rather after
-            // the temporary brightness has been taken into account.
-            setBrightnessToOverride = true;
         }
 
         final boolean autoBrightnessEnabledInDoze =
@@ -804,12 +798,6 @@
             brightnessIsTemporary = true;
         }
 
-        // Reset the brightness to the screen brightness override to communicate to the user that
-        // her changes aren't taking effect.
-        if (setBrightnessToOverride && !brightnessIsTemporary) {
-            putScreenBrightnessSetting(brightness);
-        }
-
         final boolean autoBrightnessAdjustmentChanged = updateAutoBrightnessAdjustment();
         if (autoBrightnessAdjustmentChanged) {
             mTemporaryAutoBrightnessAdjustment = Float.NaN;
@@ -1452,6 +1440,9 @@
         if (userSwitch) {
             // Don't treat user switches as user initiated change.
             mCurrentScreenBrightnessSetting = mPendingScreenBrightnessSetting;
+            if (mAutomaticBrightnessController != null) {
+                mAutomaticBrightnessController.resetShortTermModel();
+            }
         }
         mPendingAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting();
         // We don't bother with a pending variable for VR screen brightness since we just
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 20283a7..a492672 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -719,13 +719,14 @@
      * Plays the wireless charging sound for both wireless and non-wireless charging
      */
     private void playChargingStartedSound() {
-        // TODO (b/77912907): add back charging sound enabled check & default to charging sounds ON
+        final boolean enabled = Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.CHARGING_SOUNDS_ENABLED, 1) != 0;
         final boolean dndOff = Settings.Global.getInt(mContext.getContentResolver(),
                 Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
                 == Settings.Global.ZEN_MODE_OFF;
         final String soundPath = Settings.Global.getString(mContext.getContentResolver(),
                 Settings.Global.CHARGING_STARTED_SOUND);
-        if (dndOff && soundPath != null) {
+        if (enabled && dndOff && soundPath != null) {
             final Uri soundUri = Uri.parse("file://" + soundPath);
             if (soundUri != null) {
                 final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
index c527533..cb84cf3 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
@@ -90,6 +90,16 @@
      */
     private final Plugin[] mPlugins;
 
+    public static final int REASON_AUTOMATIC_ON = 0;
+    public static final int REASON_AUTOMATIC_OFF = 1;
+    public static final int REASON_MANUAL_ON = 2;
+    public static final int REASON_MANUAL_OFF = 3;
+    public static final int REASON_STICKY_RESTORE = 4;
+    public static final int REASON_INTERACTIVE_CHANGED = 5;
+    public static final int REASON_POLICY_CHANGED = 6;
+    public static final int REASON_PLUGGED_IN = 7;
+    public static final int REASON_SETTING_CHANGED = 8;
+
     /**
      * Plugin interface. All methods are guaranteed to be called on the same (handler) thread.
      */
@@ -113,7 +123,8 @@
                         return; // No need to send it if not enabled.
                     }
                     // Don't send the broadcast, because we never did so in this case.
-                    mHandler.postStateChanged(/*sendBroadcast=*/ false);
+                    mHandler.postStateChanged(/*sendBroadcast=*/ false,
+                            REASON_INTERACTIVE_CHANGED);
                     break;
                 case Intent.ACTION_BATTERY_CHANGED:
                     synchronized (mLock) {
@@ -184,7 +195,7 @@
         if (!isEnabled()) {
             return; // No need to send it if not enabled.
         }
-        mHandler.postStateChanged(/*sendBroadcast=*/ true);
+        mHandler.postStateChanged(/*sendBroadcast=*/ true, REASON_POLICY_CHANGED);
     }
 
     private class MyHandler extends Handler {
@@ -199,9 +210,9 @@
             super(looper);
         }
 
-        public void postStateChanged(boolean sendBroadcast) {
+        public void postStateChanged(boolean sendBroadcast, int reason) {
             obtainMessage(MSG_STATE_CHANGED, sendBroadcast ?
-                    ARG_SEND_BROADCAST : ARG_DONT_SEND_BROADCAST, 0).sendToTarget();
+                    ARG_SEND_BROADCAST : ARG_DONT_SEND_BROADCAST, reason).sendToTarget();
         }
 
         public void postSystemReady() {
@@ -212,7 +223,9 @@
         public void dispatchMessage(Message msg) {
             switch (msg.what) {
                 case MSG_STATE_CHANGED:
-                    handleBatterySaverStateChanged(msg.arg1 == ARG_SEND_BROADCAST);
+                    handleBatterySaverStateChanged(
+                            msg.arg1 == ARG_SEND_BROADCAST,
+                            msg.arg2);
                     break;
 
                 case MSG_SYSTEM_READY:
@@ -227,14 +240,14 @@
     /**
      * Called by {@link PowerManagerService} to update the battery saver stete.
      */
-    public void enableBatterySaver(boolean enable) {
+    public void enableBatterySaver(boolean enable, int reason) {
         synchronized (mLock) {
             if (mEnabled == enable) {
                 return;
             }
             mEnabled = enable;
 
-            mHandler.postStateChanged(/*sendBroadcast=*/ true);
+            mHandler.postStateChanged(/*sendBroadcast=*/ true, reason);
         }
     }
 
@@ -275,7 +288,7 @@
      * - When battery saver is on the interactive state changes.
      * - When battery saver is on the battery saver policy changes.
      */
-    void handleBatterySaverStateChanged(boolean sendBroadcast) {
+    void handleBatterySaverStateChanged(boolean sendBroadcast, int reason) {
         final LowPowerModeListener[] listeners;
 
         final boolean enabled;
@@ -287,7 +300,8 @@
                     mPreviouslyEnabled ? 1 : 0, // Previously off or on.
                     mEnabled ? 1 : 0, // Now off or on.
                     isInteractive ?  1 : 0, // Device interactive state.
-                    mEnabled ? mBatterySaverPolicy.toEventLogString() : "");
+                    mEnabled ? mBatterySaverPolicy.toEventLogString() : "",
+                    reason);
             mPreviouslyEnabled = mEnabled;
 
             listeners = mListeners.toArray(new LowPowerModeListener[mListeners.size()]);
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
index 2860521..b9f31b1 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
@@ -18,6 +18,7 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.ContentObserver;
+import android.os.Handler;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.provider.Settings.Global;
@@ -27,6 +28,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.BackgroundThread;
+import com.android.server.EventLogTags;
 import com.android.server.power.BatterySaverPolicy;
 import com.android.server.power.BatterySaverStateMachineProto;
 
@@ -95,6 +97,18 @@
     @GuardedBy("mLock")
     private boolean mBatterySaverSnoozing;
 
+    /**
+     * Last reason passed to {@link #enableBatterySaverLocked}.
+     */
+    @GuardedBy("mLock")
+    private int mLastChangedIntReason;
+
+    /**
+     * Last reason passed to {@link #enableBatterySaverLocked}.
+     */
+    @GuardedBy("mLock")
+    private String mLastChangedStrReason;
+
     private final ContentObserver mSettingsObserver = new ContentObserver(null) {
         @Override
         public void onChange(boolean selfChange) {
@@ -149,11 +163,25 @@
         });
     }
 
+    /**
+     * Run a {@link Runnable} on a background handler.
+     */
     @VisibleForTesting
     void runOnBgThread(Runnable r) {
         BackgroundThread.getHandler().post(r);
     }
 
+    /**
+     * Run a {@link Runnable} on a background handler, but lazily. If the same {@link Runnable},
+     * it'll be first removed before a new one is posted.
+     */
+    @VisibleForTesting
+    void runOnBgThreadLazy(Runnable r, int delayMillis) {
+        final Handler h = BackgroundThread.getHandler();
+        h.removeCallbacks(r);
+        h.postDelayed(r, delayMillis);
+    }
+
     void refreshSettingsLocked() {
         final ContentResolver cr = mContext.getContentResolver();
 
@@ -199,14 +227,23 @@
         mSettingBatterySaverEnabledSticky = batterySaverEnabledSticky;
         mSettingBatterySaverTriggerThreshold = batterySaverTriggerThreshold;
 
+        if (thresholdChanged) {
+            // To avoid spamming the event log, we throttle logging here.
+            runOnBgThreadLazy(mThresholdChangeLogger, 2000);
+        }
+
         if (enabledChanged) {
             final String reason = batterySaverEnabled
                     ? "Global.low_power changed to 1" : "Global.low_power changed to 0";
             enableBatterySaverLocked(/*enable=*/ batterySaverEnabled, /*manual=*/ true,
-                    reason);
+                    BatterySaverController.REASON_SETTING_CHANGED, reason);
         }
     }
 
+    private final Runnable mThresholdChangeLogger = () -> {
+        EventLogTags.writeBatterySaverSetting(mSettingBatterySaverTriggerThreshold);
+    };
+
     /**
      * {@link com.android.server.power.PowerManagerService} calls it when battery state changes.
      *
@@ -257,18 +294,26 @@
         }
         if (mIsPowered) {
             updateSnoozingLocked(false, "Plugged in");
-            enableBatterySaverLocked(/*enable=*/ false, /*manual=*/ false, "Plugged in");
+            enableBatterySaverLocked(/*enable=*/ false, /*manual=*/ false,
+                    BatterySaverController.REASON_PLUGGED_IN,
+                    "Plugged in");
 
         } else if (mSettingBatterySaverEnabledSticky) {
             // Re-enable BS.
-            enableBatterySaverLocked(/*enable=*/ true, /*manual=*/ true, "Sticky restore");
+            enableBatterySaverLocked(/*enable=*/ true, /*manual=*/ true,
+                    BatterySaverController.REASON_STICKY_RESTORE,
+                    "Sticky restore");
 
         } else if (mIsBatteryLevelLow) {
             if (!mBatterySaverSnoozing && isAutoBatterySaverConfigured()) {
-                enableBatterySaverLocked(/*enable=*/ true, /*manual=*/ false, "Auto ON");
+                enableBatterySaverLocked(/*enable=*/ true, /*manual=*/ false,
+                        BatterySaverController.REASON_AUTOMATIC_ON,
+                        "Auto ON");
             }
         } else { // Battery not low
-            enableBatterySaverLocked(/*enable=*/ false, /*manual=*/ false, "Auto OFF");
+            enableBatterySaverLocked(/*enable=*/ false, /*manual=*/ false,
+                    BatterySaverController.REASON_AUTOMATIC_OFF,
+                    "Auto OFF");
         }
     }
 
@@ -284,6 +329,8 @@
         }
         synchronized (mLock) {
             enableBatterySaverLocked(/*enable=*/ enabled, /*manual=*/ true,
+                    (enabled ? BatterySaverController.REASON_MANUAL_ON
+                            : BatterySaverController.REASON_MANUAL_OFF),
                     (enabled ? "Manual ON" : "Manual OFF"));
         }
     }
@@ -292,10 +339,11 @@
      * Actually enable / disable battery saver. Write the new state to the global settings
      * and propagate it to {@link #mBatterySaverController}.
      */
-    private void enableBatterySaverLocked(boolean enable, boolean manual, String reason) {
+    private void enableBatterySaverLocked(boolean enable, boolean manual, int intReason,
+            String strReason) {
         if (DEBUG) {
             Slog.d(TAG, "enableBatterySaver: enable=" + enable + " manual=" + manual
-                    + " reason=" + reason);
+                    + " reason=" + strReason + "(" + intReason + ")");
         }
         final boolean wasEnabled = mBatterySaverController.isEnabled();
 
@@ -309,6 +357,8 @@
             if (DEBUG) Slog.d(TAG, "Can't enable: isPowered");
             return;
         }
+        mLastChangedIntReason = intReason;
+        mLastChangedStrReason = strReason;
 
         if (manual) {
             if (enable) {
@@ -330,12 +380,12 @@
             mSettingBatterySaverEnabledSticky = enable;
             putGlobalSetting(Global.LOW_POWER_MODE_STICKY, enable ? 1 : 0);
         }
-        mBatterySaverController.enableBatterySaver(enable);
+        mBatterySaverController.enableBatterySaver(enable, intReason);
 
         if (DEBUG) {
             Slog.d(TAG, "Battery saver: Enabled=" + enable
                     + " manual=" + manual
-                    + " reason=" + reason);
+                    + " reason=" + strReason + "(" + intReason + ")");
         }
     }
 
@@ -365,6 +415,11 @@
             pw.print("  Enabled=");
             pw.println(mBatterySaverController.isEnabled());
 
+            pw.print("  mLastChangedIntReason=");
+            pw.println(mLastChangedIntReason);
+            pw.print("  mLastChangedStrReason=");
+            pw.println(mLastChangedStrReason);
+
             pw.print("  mBootCompleted=");
             pw.println(mBootCompleted);
             pw.print("  mSettingsLoaded=");
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
index 1367f58..62fe6b2 100644
--- a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
@@ -17,6 +17,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -144,6 +145,11 @@
         void runOnBgThread(Runnable r) {
             r.run();
         }
+
+        @Override
+        void runOnBgThreadLazy(Runnable r, int delayMillis) {
+            r.run();
+        }
     }
 
     @Before
@@ -153,7 +159,7 @@
         mMockBatterySaverController = mock(BatterySaverController.class);
 
         doAnswer((inv) -> mDevice.batterySaverEnabled = inv.getArgument(0))
-                .when(mMockBatterySaverController).enableBatterySaver(anyBoolean());
+                .when(mMockBatterySaverController).enableBatterySaver(anyBoolean(), anyInt());
         when(mMockBatterySaverController.isEnabled())
                 .thenAnswer((inv) -> mDevice.batterySaverEnabled);
 
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index f1653ce..f2438b8 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -2900,12 +2900,30 @@
          * @hide
          */
         public static final int OWNED_BY_DPC = 0;
+
         /**
          * Possible value for the OWNED_BY field.
          * APN is owned by other sources.
          * @hide
          */
         public static final int OWNED_BY_OTHERS = 1;
+
+        /**
+         * The APN set id. When the user manually selects an APN or the framework sets an APN as
+         * preferred, all APNs with the same set id as the selected APN should be prioritized over
+         * APNs in other sets.
+         * @hide
+         */
+        public static final String APN_SET_ID = "apn_set_id";
+
+        /**
+         * Possible value for the APN_SET_ID field. By default APNs will not belong to a set. If the
+         * user manually selects an APN with no set set, there is no need to prioritize any specific
+         * APN set ids.
+         * @hide
+         */
+        public static final int NO_SET_SET = 0;
+
     }
 
     /**