Merge "Public EuiccManager APIs."
diff --git a/Android.bp b/Android.bp
index 3d56254..c5d64fb 100644
--- a/Android.bp
+++ b/Android.bp
@@ -578,7 +578,6 @@
         "wifi/java/android/net/wifi/rtt/IWifiRttManager.aidl",
         "wifi/java/android/net/wifi/hotspot2/IProvisioningCallback.aidl",
         "wifi/java/android/net/wifi/IWifiScanner.aidl",
-        "wifi/java/android/net/wifi/IRttManager.aidl",
         "packages/services/PacProcessor/com/android/net/IProxyService.aidl",
         "packages/services/Proxy/com/android/net/IProxyCallback.aidl",
         "packages/services/Proxy/com/android/net/IProxyPortListener.aidl",
diff --git a/api/system-current.txt b/api/system-current.txt
index cd9c957..9922df6 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5173,22 +5173,19 @@
   }
 
   public class UiccSlotInfo implements android.os.Parcelable {
-    ctor public UiccSlotInfo(boolean, boolean, java.lang.String, int);
+    ctor public UiccSlotInfo(boolean, boolean, java.lang.String, int, int);
     method public int describeContents();
     method public java.lang.String getCardId();
     method public int getCardStateInfo();
     method public boolean getIsActive();
     method public boolean getIsEuicc();
+    method public int getLogicalSlotIdx();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int CARD_STATE_INFO_ABSENT = 1; // 0x1
     field public static final int CARD_STATE_INFO_ERROR = 3; // 0x3
     field public static final int CARD_STATE_INFO_PRESENT = 2; // 0x2
     field public static final int CARD_STATE_INFO_RESTRICTED = 4; // 0x4
     field public static final android.os.Parcelable.Creator<android.telephony.UiccSlotInfo> CREATOR;
-    field public final java.lang.String cardId;
-    field public final int cardStateInfo;
-    field public final boolean isActive;
-    field public final boolean isEuicc;
   }
 
   public abstract class VisualVoicemailService extends android.app.Service {
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index b46c5c1..90158a0 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -44,6 +44,7 @@
     src/external/KernelUidCpuActiveTimeReader.cpp \
     src/external/KernelUidCpuClusterTimeReader.cpp \
     src/external/StatsPullerManagerImpl.cpp \
+    src/external/puller_util.cpp \
     src/logd/LogEvent.cpp \
     src/logd/LogListener.cpp \
     src/logd/LogReader.cpp \
@@ -175,6 +176,7 @@
     tests/AnomalyMonitor_test.cpp \
     tests/anomaly/AnomalyTracker_test.cpp \
     tests/ConfigManager_test.cpp \
+    tests/external/puller_util_test.cpp \
     tests/indexed_priority_queue_test.cpp \
     tests/LogEntryMatcher_test.cpp \
     tests/LogReader_test.cpp \
diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp
index f0eaeff..8483b02 100644
--- a/cmds/statsd/src/HashableDimensionKey.cpp
+++ b/cmds/statsd/src/HashableDimensionKey.cpp
@@ -181,7 +181,9 @@
     return toString().compare(that.toString()) < 0;
 };
 
-
+bool compareDimensionsValue(const DimensionsValue& s1, const DimensionsValue& s2) {
+    return EqualsTo(s1, s2);
+}
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 3efe9b1..f24bb04 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -77,6 +77,7 @@
     : mAnomalyMonitor(new AnomalyMonitor(MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS))
 {
     mUidMap = new UidMap();
+    StatsPuller::SetUidMap(mUidMap);
     mConfigManager = new ConfigManager();
     mProcessor = new StatsLogProcessor(mUidMap, mAnomalyMonitor, time(nullptr), [this](const ConfigKey& key) {
         sp<IStatsCompanionService> sc = getStatsCompanionService();
diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp
index 41e4705..fc0ad7c 100644
--- a/cmds/statsd/src/external/StatsPuller.cpp
+++ b/cmds/statsd/src/external/StatsPuller.cpp
@@ -19,6 +19,7 @@
 
 #include "StatsPuller.h"
 #include "guardrail/StatsdStats.h"
+#include "puller_util.h"
 
 namespace android {
 namespace os {
@@ -26,6 +27,9 @@
 
 using std::lock_guard;
 
+sp<UidMap> StatsPuller::mUidMap = nullptr;
+void StatsPuller::SetUidMap(const sp<UidMap>& uidMap) { mUidMap = uidMap; }
+
 // ValueMetric has a minimum bucket size of 10min so that we don't pull too frequently
 StatsPuller::StatsPuller(const int tagId)
     : mTagId(tagId) {
@@ -54,7 +58,8 @@
     mLastPullTimeSec = curTime;
     bool ret = PullInternal(&mCachedData);
     if (ret) {
-        (*data) = mCachedData;
+      mergeIsolatedUidsToHostUid(mCachedData, mUidMap, mTagId);
+      (*data) = mCachedData;
     }
     return ret;
 }
diff --git a/cmds/statsd/src/external/StatsPuller.h b/cmds/statsd/src/external/StatsPuller.h
index 3446f9b..82a8611 100644
--- a/cmds/statsd/src/external/StatsPuller.h
+++ b/cmds/statsd/src/external/StatsPuller.h
@@ -18,11 +18,14 @@
 
 #include <android/os/StatsLogEventWrapper.h>
 #include <utils/String16.h>
+#include <utils/RefBase.h>
 #include <mutex>
 #include <vector>
+#include "packages/UidMap.h"
 
-#include "logd/LogEvent.h"
 #include "guardrail/StatsdStats.h"
+#include "logd/LogEvent.h"
+#include "puller_util.h"
 
 using android::os::StatsLogEventWrapper;
 
@@ -30,7 +33,7 @@
 namespace os {
 namespace statsd {
 
-class StatsPuller {
+class StatsPuller : public virtual RefBase {
 public:
     StatsPuller(const int tagId);
 
@@ -44,7 +47,9 @@
     // Clear cache if elapsed time is more than cooldown time
     int ClearCacheIfNecessary(long timestampSec);
 
-protected:
+    static void SetUidMap(const sp<UidMap>& uidMap);
+
+   protected:
     // The atom tag id this puller pulls
     const int mTagId;
 
@@ -67,6 +72,8 @@
     long mLastPullTimeSec;
 
     int clearCache();
+
+    static sp<UidMap> mUidMap;
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
index 19b3a86..0b772b3 100644
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define DEBUG true
+#define DEBUG false
 #include "Log.h"
 
 #include <android/os/IStatsCompanionService.h>
@@ -24,6 +24,9 @@
 #include "CpuTimePerUidFreqPuller.h"
 #include "CpuTimePerUidPuller.h"
 #include "ResourceHealthManagerPuller.h"
+#include "KernelUidCpuActiveTimeReader.h"
+#include "KernelUidCpuClusterTimeReader.h"
+#include "SubsystemSleepStatePuller.h"
 #include "StatsCompanionServicePuller.h"
 #include "StatsPullerManagerImpl.h"
 #include "StatsService.h"
@@ -44,60 +47,130 @@
 namespace os {
 namespace statsd {
 
+const std::map<int, PullAtomInfo> StatsPullerManagerImpl::kAllPullAtomInfo = {
+    // wifi_bytes_transfer
+    {android::util::WIFI_BYTES_TRANSFER,
+     {{2, 3, 4, 5},
+      {},
+      1,
+      new StatsCompanionServicePuller(android::util::WIFI_BYTES_TRANSFER)}},
+    // wifi_bytes_transfer_by_fg_bg
+    {android::util::WIFI_BYTES_TRANSFER_BY_FG_BG,
+     {{3, 4, 5, 6},
+      {2},
+      1,
+      new StatsCompanionServicePuller(
+          android::util::WIFI_BYTES_TRANSFER_BY_FG_BG)}},
+    // mobile_bytes_transfer
+    {android::util::MOBILE_BYTES_TRANSFER,
+     {{2, 3, 4, 5},
+      {},
+      1,
+      new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER)}},
+    // mobile_bytes_transfer_by_fg_bg
+    {android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG,
+     {{3, 4, 5, 6},
+      {2},
+      1,
+      new StatsCompanionServicePuller(
+          android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG)}},
+    // bluetooth_bytes_transfer
+    {android::util::BLUETOOTH_BYTES_TRANSFER,
+     {{2, 3},
+      {},
+      1,
+      new StatsCompanionServicePuller(
+          android::util::BLUETOOTH_BYTES_TRANSFER)}},
+    // kernel_wakelock
+    {android::util::KERNEL_WAKELOCK,
+     {{},
+      {},
+      1,
+      new StatsCompanionServicePuller(android::util::KERNEL_WAKELOCK)}},
+    // subsystem_sleep_state
+    {android::util::SUBSYSTEM_SLEEP_STATE,
+     {{},
+      {},
+      1,
+      new StatsCompanionServicePuller(android::util::SUBSYSTEM_SLEEP_STATE)}},
+    // cpu_time_per_freq
+    {android::util::CPU_TIME_PER_FREQ,
+     {{3},
+      {2},
+      1,
+      new StatsCompanionServicePuller(android::util::CPU_TIME_PER_FREQ)}},
+    // cpu_time_per_uid
+    {android::util::CPU_TIME_PER_UID,
+     {{2, 3}, {}, 1, new CpuTimePerUidPuller()}},
+    // cpu_time_per_uid_freq
+    {android::util::CPU_TIME_PER_UID_FREQ,
+     {{3}, {2}, 1, new CpuTimePerUidFreqPuller()}},
+    // wifi_activity_energy_info
+    {android::util::WIFI_ACTIVITY_ENERGY_INFO,
+     {{},
+      {},
+      1,
+      new StatsCompanionServicePuller(
+          android::util::WIFI_ACTIVITY_ENERGY_INFO)}},
+    // modem_activity_info
+    {android::util::MODEM_ACTIVITY_INFO,
+     {{},
+      {},
+      1,
+      new StatsCompanionServicePuller(android::util::MODEM_ACTIVITY_INFO)}},
+    // bluetooth_activity_info
+    {android::util::BLUETOOTH_ACTIVITY_INFO,
+     {{},
+      {},
+      1,
+      new StatsCompanionServicePuller(android::util::BLUETOOTH_ACTIVITY_INFO)}},
+    // system_elapsed_realtime
+    {android::util::SYSTEM_ELAPSED_REALTIME,
+     {{},
+      {},
+      1,
+      new StatsCompanionServicePuller(android::util::SYSTEM_ELAPSED_REALTIME)}},
+    // system_uptime
+    {android::util::SYSTEM_UPTIME,
+     {{},
+      {},
+      1,
+      new StatsCompanionServicePuller(android::util::SYSTEM_UPTIME)}},
+    // cpu_active_time
+    {android::util::CPU_ACTIVE_TIME,
+     {{3}, {2}, 1, new KernelUidCpuActiveTimeReader()}},
+    // cpu_cluster_time
+    {android::util::CPU_CLUSTER_TIME,
+     {{3}, {2}, 1, new KernelUidCpuClusterTimeReader()}},
+    // disk_space
+    {android::util::DISK_SPACE,
+     {{}, {}, 1, new StatsCompanionServicePuller(android::util::DISK_SPACE)}},
+    // remaining_battery_capacity
+    {android::util::REMAINING_BATTERY_CAPACITY,
+     {{},
+      {},
+      1,
+      new ResourceHealthManagerPuller(
+          android::util::REMAINING_BATTERY_CAPACITY)}},
+    // full_battery_capacity
+    {android::util::FULL_BATTERY_CAPACITY,
+     {{},
+      {},
+      1,
+      new ResourceHealthManagerPuller(android::util::FULL_BATTERY_CAPACITY)}}};
+
 StatsPullerManagerImpl::StatsPullerManagerImpl()
     : mCurrentPullingInterval(LONG_MAX) {
-    mPullers.insert({android::util::KERNEL_WAKELOCK,
-                     make_shared<StatsCompanionServicePuller>(android::util::KERNEL_WAKELOCK)});
-    mPullers.insert({android::util::WIFI_BYTES_TRANSFER,
-                     make_shared<StatsCompanionServicePuller>(android::util::WIFI_BYTES_TRANSFER)});
-    mPullers.insert(
-            {android::util::MOBILE_BYTES_TRANSFER,
-             make_shared<StatsCompanionServicePuller>(android::util::MOBILE_BYTES_TRANSFER)});
-    mPullers.insert({android::util::WIFI_BYTES_TRANSFER_BY_FG_BG,
-                     make_shared<StatsCompanionServicePuller>(
-                             android::util::WIFI_BYTES_TRANSFER_BY_FG_BG)});
-    mPullers.insert({android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG,
-                     make_shared<StatsCompanionServicePuller>(
-                             android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG)});
-    mPullers.insert(
-            {android::util::SUBSYSTEM_SLEEP_STATE,
-             make_shared<SubsystemSleepStatePuller>()});
-    mPullers.insert({android::util::CPU_TIME_PER_FREQ, make_shared<StatsCompanionServicePuller>(android::util::CPU_TIME_PER_FREQ)});
-    mPullers.insert({android::util::CPU_TIME_PER_UID, make_shared<CpuTimePerUidPuller>()});
-    mPullers.insert({android::util::CPU_TIME_PER_UID_FREQ, make_shared<CpuTimePerUidFreqPuller>()});
-    mPullers.insert(
-            {android::util::SYSTEM_ELAPSED_REALTIME,
-             make_shared<StatsCompanionServicePuller>(android::util::SYSTEM_ELAPSED_REALTIME)});
-    mPullers.insert({android::util::SYSTEM_UPTIME,
-                     make_shared<StatsCompanionServicePuller>(android::util::SYSTEM_UPTIME)});
-    mPullers.insert({android::util::DISK_SPACE,
-                     make_shared<StatsCompanionServicePuller>(android::util::DISK_SPACE)});
-    mPullers.insert(
-            {android::util::BLUETOOTH_ACTIVITY_INFO,
-             make_shared<StatsCompanionServicePuller>(android::util::BLUETOOTH_ACTIVITY_INFO)});
-
-    mPullers.insert(
-            {android::util::BLUETOOTH_BYTES_TRANSFER,
-             make_shared<StatsCompanionServicePuller>(android::util::BLUETOOTH_BYTES_TRANSFER)});
-    mPullers.insert(
-            {android::util::WIFI_ACTIVITY_ENERGY_INFO,
-             make_shared<StatsCompanionServicePuller>(android::util::WIFI_ACTIVITY_ENERGY_INFO)});
-    mPullers.insert({android::util::MODEM_ACTIVITY_INFO,
-                     make_shared<StatsCompanionServicePuller>(android::util::MODEM_ACTIVITY_INFO)});
-    mPullers.insert({android::util::REMAINING_BATTERY_CAPACITY,
-                     make_shared<ResourceHealthManagerPuller>(android::util::REMAINING_BATTERY_CAPACITY)});
-    mPullers.insert({android::util::FULL_BATTERY_CAPACITY,
-                     make_shared<ResourceHealthManagerPuller>(android::util::FULL_BATTERY_CAPACITY)});
     mStatsCompanionService = StatsService::getStatsCompanionService();
 }
 
 bool StatsPullerManagerImpl::Pull(int tagId, vector<shared_ptr<LogEvent>>* data) {
     if (DEBUG) ALOGD("Initiating pulling %d", tagId);
 
-    if (mPullers.find(tagId) != mPullers.end()) {
-        bool ret = mPullers.find(tagId)->second->Pull(data);
-        ALOGD("pulled %d items", (int)data->size());
-        return ret;
+    if (kAllPullAtomInfo.find(tagId) != kAllPullAtomInfo.end()) {
+      bool ret = kAllPullAtomInfo.find(tagId)->second.puller->Pull(data);
+      ALOGD("pulled %d items", (int)data->size());
+      return ret;
     } else {
         ALOGD("Unknown tagId %d", tagId);
         return false;  // Return early since we don't know what to pull.
@@ -110,7 +183,7 @@
 }
 
 bool StatsPullerManagerImpl::PullerForMatcherExists(int tagId) const {
-    return mPullers.find(tagId) != mPullers.end();
+  return kAllPullAtomInfo.find(tagId) != kAllPullAtomInfo.end();
 }
 
 void StatsPullerManagerImpl::RegisterReceiver(int tagId, wp<PullDataReceiver> receiver,
@@ -201,16 +274,16 @@
 
 int StatsPullerManagerImpl::ForceClearPullerCache() {
     int totalCleared = 0;
-    for (auto puller : mPullers) {
-        totalCleared += puller.second->ForceClearCache();
+    for (const auto& pulledAtom : kAllPullAtomInfo) {
+        totalCleared += pulledAtom.second.puller->ForceClearCache();
     }
     return totalCleared;
 }
 
 int StatsPullerManagerImpl::ClearPullerCacheIfNecessary(long timestampSec) {
     int totalCleared = 0;
-    for (auto puller : mPullers) {
-        totalCleared += puller.second->ClearCacheIfNecessary(timestampSec);
+    for (const auto& pulledAtom : kAllPullAtomInfo) {
+        totalCleared += pulledAtom.second.puller->ClearCacheIfNecessary(timestampSec);
     }
     return totalCleared;
 }
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.h b/cmds/statsd/src/external/StatsPullerManagerImpl.h
index 3535fa3..76a4c14 100644
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.h
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.h
@@ -32,6 +32,20 @@
 namespace os {
 namespace statsd {
 
+typedef struct {
+  // The field numbers of the fields that need to be summed when merging
+  // isolated uid with host uid.
+  std::vector<int> additiveFields;
+  // The field numbers of the fields that can't be merged when merging
+  // data belong to isolated uid and host uid.
+  std::vector<int> nonAdditiveFields;
+  // How long should the puller wait before doing an actual pull again. Default
+  // 1 sec. Set this to 0 if this is handled elsewhere.
+  long coolDownSec = 1;
+  // The actual puller
+  sp<StatsPuller> puller;
+} PullAtomInfo;
+
 class StatsPullerManagerImpl : public virtual RefBase {
 public:
     static StatsPullerManagerImpl& GetInstance();
@@ -53,7 +67,9 @@
 
     int ClearPullerCacheIfNecessary(long timestampSec);
 
-private:
+    const static std::map<int, PullAtomInfo> kAllPullAtomInfo;
+
+   private:
     StatsPullerManagerImpl();
 
     // use this to update alarm
@@ -61,9 +77,6 @@
 
     sp<IStatsCompanionService> get_stats_companion_service();
 
-    // mapping from simple matcher tagId to puller
-    std::map<int, std::shared_ptr<StatsPuller>> mPullers;
-
     typedef struct {
         // pull_interval_sec : last_pull_time_sec
         std::pair<uint64_t, uint64_t> timeInfo;
@@ -81,8 +94,6 @@
     // bucket size. All pulled metrics start pulling based on this time, so that they can be
     // correctly attributed to the correct buckets.
     long mTimeBaseSec;
-
-    LogEvent parse_pulled_data(String16 data);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/external/puller_util.cpp b/cmds/statsd/src/external/puller_util.cpp
new file mode 100644
index 0000000..7cfc1d48
--- /dev/null
+++ b/cmds/statsd/src/external/puller_util.cpp
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#define DEBUG false  // STOPSHIP if true
+#include "Log.h"
+
+#include "StatsPullerManagerImpl.h"
+#include "field_util.h"
+#include "puller_util.h"
+#include "statslog.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using std::map;
+using std::shared_ptr;
+using std::vector;
+
+DimensionsValue* getFieldValue(shared_ptr<LogEvent> event, int tagId, int fieldNum) {
+  Field field;
+  buildSimpleAtomField(tagId, fieldNum, &field);
+  return event->findFieldValueOrNull(field);
+}
+
+bool shouldMerge(shared_ptr<LogEvent>& lhs, shared_ptr<LogEvent>& rhs,
+                 const vector<int>& nonAdditiveFields, int tagId) {
+  for (int f : nonAdditiveFields) {
+    DimensionsValue* lValue = getFieldValue(lhs, tagId, f);
+    DimensionsValue* rValue = getFieldValue(rhs, tagId, f);
+    if (!compareDimensionsValue(*lValue, *rValue)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+// merge rhs to lhs
+void mergeEvent(shared_ptr<LogEvent>& lhs, shared_ptr<LogEvent>& rhs,
+                const vector<int>& additiveFields, int tagId) {
+  for (int f : additiveFields) {
+    DimensionsValue* lValue = getFieldValue(lhs, tagId, f);
+    DimensionsValue* rValue = getFieldValue(rhs, tagId, f);
+    if (lValue->has_value_int()) {
+      lValue->set_value_int(lValue->value_int() + rValue->value_int());
+    } else if (lValue->has_value_long()) {
+      lValue->set_value_long(lValue->value_long() + rValue->value_long());
+    }
+  }
+}
+
+// process all data and merge isolated with host if necessary
+void mergeIsolatedUidsToHostUid(vector<shared_ptr<LogEvent>>& data,
+                                const sp<UidMap>& uidMap, int tagId) {
+  if (StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId) ==
+      StatsPullerManagerImpl::kAllPullAtomInfo.end()) {
+    VLOG("Unknown pull atom id %d", tagId);
+    return;
+  }
+  if (android::util::kAtomsWithUidField.find(tagId) ==
+      android::util::kAtomsWithUidField.end()) {
+    VLOG("No uid to merge for atom %d", tagId);
+    return;
+  }
+  const vector<int>& additiveFields =
+      StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId)
+          ->second.additiveFields;
+  const vector<int>& nonAdditiveFields =
+      StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId)
+          ->second.nonAdditiveFields;
+
+  // map of host uid to isolated uid data index in the original vector.
+  // because of non additive fields, there could be multiple of them that can't
+  // be merged into one
+  map<int, vector<int>> hostToIsolated;
+  // map of host uid to their position in the original vector
+  map<int, vector<int>> hostPosition;
+  vector<int> isolatedUidPos;
+  // all uids in the original vector
+  vector<int> allUids;
+  for (size_t i = 0; i < data.size(); i++) {
+    // uid field is always first primitive filed, if present
+    DimensionsValue* uidField = getFieldValue(data[i], tagId, 1);
+    if (!uidField) {
+      VLOG("Bad data for %d, %s", tagId, data[i]->ToString().c_str());
+      return;
+    }
+    int uid = uidField->value_int();
+    allUids.push_back(uid);
+    const int hostUid = uidMap->getHostUidOrSelf(uid);
+    if (hostUid != uid) {
+      uidField->set_value_int(hostUid);
+      hostToIsolated[hostUid].push_back(i);
+      isolatedUidPos.push_back(i);
+    }
+  }
+  vector<shared_ptr<LogEvent>> mergedData;
+  for (size_t i = 0; i < allUids.size(); i++) {
+    if (hostToIsolated.find(allUids[i]) != hostToIsolated.end()) {
+      hostPosition[allUids[i]].push_back(i);
+    } else if (std::find(isolatedUidPos.begin(), isolatedUidPos.end(), i) != isolatedUidPos.end()) {
+      continue;
+    } else {
+      mergedData.push_back(data[i]);
+    }
+  }
+  for (auto iter = hostToIsolated.begin(); iter != hostToIsolated.end();
+       iter++) {
+    int uid = iter->first;
+    vector<int>& isolated = hostToIsolated[uid];
+    vector<int> toBeMerged;
+    toBeMerged.insert(toBeMerged.begin(), isolated.begin(), isolated.end());
+    if (hostPosition.find(uid) != hostPosition.end()) {
+      vector<int>& host = hostPosition[uid];
+      toBeMerged.insert(toBeMerged.end(), host.begin(), host.end());
+    }
+    vector<bool> used(toBeMerged.size());
+    for (size_t i = 0; i < toBeMerged.size(); i++) {
+      if (used[i] == true) {
+        continue;
+      }
+      for (size_t j = i + 1; j < toBeMerged.size(); j++) {
+        shared_ptr<LogEvent>& lhs = data[toBeMerged[i]];
+        shared_ptr<LogEvent>& rhs = data[toBeMerged[j]];
+        if (shouldMerge(lhs, rhs, nonAdditiveFields, tagId)) {
+          mergeEvent(lhs, rhs, additiveFields, tagId);
+          used[j] = true;
+        }
+      }
+    }
+    for (size_t i = 0; i < toBeMerged.size(); i++) {
+      if (used[i] == false) {
+      mergedData.push_back(data[i]);
+    }
+    }
+  }
+  data.clear();
+  data = mergedData;
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/external/puller_util.h b/cmds/statsd/src/external/puller_util.h
new file mode 100644
index 0000000..70d5321
--- /dev/null
+++ b/cmds/statsd/src/external/puller_util.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 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 <vector>
+#include "HashableDimensionKey.h"
+#include "StatsPuller.h"
+#include "logd/LogEvent.h"
+#include "packages/UidMap.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+void mergeIsolatedUidsToHostUid(std::vector<std::shared_ptr<LogEvent>>& data,
+                                const sp<UidMap>& uidMap, int tagId);
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 1dcd853..c8857aa 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -294,6 +294,28 @@
     }
 }
 
+int LogEvent::GetInt(size_t key, status_t* err) const {
+  DimensionsValue value;
+  if (!GetSimpleAtomDimensionsValueProto(key, &value)) {
+    *err = BAD_INDEX;
+    return 0;
+  }
+  const DimensionsValue* leafValue = getSingleLeafValue(&value);
+  switch (leafValue->value_case()) {
+    case DimensionsValue::ValueCase::kValueInt:
+      return leafValue->value_int();
+    case DimensionsValue::ValueCase::kValueLong:
+    case DimensionsValue::ValueCase::kValueBool:
+    case DimensionsValue::ValueCase::kValueFloat:
+    case DimensionsValue::ValueCase::kValueTuple:
+    case DimensionsValue::ValueCase::kValueStr:
+    case DimensionsValue::ValueCase::VALUE_NOT_SET: {
+      *err = BAD_TYPE;
+      return 0;
+    }
+  }
+}
+
 const char* LogEvent::GetString(size_t key, status_t* err) const {
     DimensionsValue value;
     if (!GetSimpleAtomDimensionsValueProto(key, &value)) {
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index eb2c008..d521e09 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -76,6 +76,7 @@
      * Returns BAD_TYPE if the index is available but the data is the wrong type.
      */
     int64_t GetLong(size_t key, status_t* err) const;
+    int GetInt(size_t key, status_t* err) const;
     const char* GetString(size_t key, status_t* err) const;
     bool GetBool(size_t key, status_t* err) const;
     float GetFloat(size_t key, status_t* err) const;
diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h
index 3304f6c..f1da452 100644
--- a/cmds/statsd/src/packages/UidMap.h
+++ b/cmds/statsd/src/packages/UidMap.h
@@ -91,7 +91,7 @@
     void removeIsolatedUid(int isolatedUid, int parentUid);
 
     // Returns the host uid if it exists. Otherwise, returns the same uid that was passed-in.
-    int getHostUidOrSelf(int uid) const;
+    virtual int getHostUidOrSelf(int uid) const;
 
     // Gets the output. If every config key has received the output, then the output is cleared.
     UidMapping getOutput(const ConfigKey& key);
diff --git a/cmds/statsd/tests/external/puller_util_test.cpp b/cmds/statsd/tests/external/puller_util_test.cpp
new file mode 100644
index 0000000..7d9c8a8
--- /dev/null
+++ b/cmds/statsd/tests/external/puller_util_test.cpp
@@ -0,0 +1,269 @@
+// Copyright (C) 2018 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 "external/puller_util.h"
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <stdio.h>
+#include <vector>
+#include "../metrics/metrics_test_helper.h"
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using namespace testing;
+using std::make_shared;
+using std::shared_ptr;
+using std::vector;
+using testing::Contains;
+/*
+ * Test merge isolated and host uid
+ */
+
+int uidAtomTagId = android::util::CPU_TIME_PER_UID_FREQ;
+int nonUidAtomTagId = android::util::SYSTEM_UPTIME;
+int timestamp = 1234;
+int isolatedUid = 30;
+int isolatedAdditiveData = 31;
+int isolatedNonAdditiveData = 32;
+int hostUid = 20;
+int hostAdditiveData = 21;
+int hostNonAdditiveData = 22;
+
+void extractIntoVector(vector<shared_ptr<LogEvent>> events,
+                      vector<vector<int>>& ret) {
+  ret.clear();
+  status_t err;
+  for (const auto& event : events) {
+    vector<int> vec;
+    vec.push_back(event->GetInt(1, &err));
+    vec.push_back(event->GetInt(2, &err));
+    vec.push_back(event->GetInt(3, &err));
+    ret.push_back(vec);
+  }
+}
+
+TEST(puller_util, MergeNoDimension) {
+  vector<shared_ptr<LogEvent>> inputData;
+  shared_ptr<LogEvent> event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+  // 30->22->31
+  event->write(isolatedUid);
+  event->write(hostNonAdditiveData);
+  event->write(isolatedAdditiveData);
+  event->init();
+  inputData.push_back(event);
+
+  // 20->22->21
+  event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+  event->write(hostUid);
+  event->write(hostNonAdditiveData);
+  event->write(hostAdditiveData);
+  event->init();
+  inputData.push_back(event);
+
+  sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
+  EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid))
+      .WillRepeatedly(Return(hostUid));
+  EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid)))
+      .WillRepeatedly(ReturnArg<0>());
+  mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+
+  vector<vector<int>> actual;
+  extractIntoVector(inputData, actual);
+  vector<int> expectedV1 = {20, 22, 52};
+  EXPECT_EQ(1, (int)actual.size());
+  EXPECT_THAT(actual, Contains(expectedV1));
+}
+
+TEST(puller_util, MergeWithDimension) {
+  vector<shared_ptr<LogEvent>> inputData;
+  shared_ptr<LogEvent> event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+  // 30->32->31
+  event->write(isolatedUid);
+  event->write(isolatedNonAdditiveData);
+  event->write(isolatedAdditiveData);
+  event->init();
+  inputData.push_back(event);
+
+  // 20->32->21
+  event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+  event->write(hostUid);
+  event->write(isolatedNonAdditiveData);
+  event->write(hostAdditiveData);
+  event->init();
+  inputData.push_back(event);
+
+  // 20->22->21
+  event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+  event->write(hostUid);
+  event->write(hostNonAdditiveData);
+  event->write(hostAdditiveData);
+  event->init();
+  inputData.push_back(event);
+
+  sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
+  EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid))
+      .WillRepeatedly(Return(hostUid));
+  EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid)))
+      .WillRepeatedly(ReturnArg<0>());
+  mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+
+  vector<vector<int>> actual;
+  extractIntoVector(inputData, actual);
+  vector<int> expectedV1 = {20, 22, 21};
+  vector<int> expectedV2 = {20, 32, 52};
+  EXPECT_EQ(2, (int)actual.size());
+  EXPECT_THAT(actual, Contains(expectedV1));
+  EXPECT_THAT(actual, Contains(expectedV2));
+}
+
+TEST(puller_util, NoMergeHostUidOnly) {
+  vector<shared_ptr<LogEvent>> inputData;
+  shared_ptr<LogEvent> event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+  // 20->32->31
+  event->write(hostUid);
+  event->write(isolatedNonAdditiveData);
+  event->write(isolatedAdditiveData);
+  event->init();
+  inputData.push_back(event);
+
+  // 20->22->21
+  event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+  event->write(hostUid);
+  event->write(hostNonAdditiveData);
+  event->write(hostAdditiveData);
+  event->init();
+  inputData.push_back(event);
+
+  sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
+  EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid))
+      .WillRepeatedly(Return(hostUid));
+  EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid)))
+      .WillRepeatedly(ReturnArg<0>());
+  mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+
+  // 20->32->31
+  // 20->22->21
+  vector<vector<int>> actual;
+  extractIntoVector(inputData, actual);
+  vector<int> expectedV1 = {20, 32, 31};
+  vector<int> expectedV2 = {20, 22, 21};
+  EXPECT_EQ(2, (int)actual.size());
+  EXPECT_THAT(actual, Contains(expectedV1));
+  EXPECT_THAT(actual, Contains(expectedV2));
+}
+
+TEST(puller_util, IsolatedUidOnly) {
+  vector<shared_ptr<LogEvent>> inputData;
+  shared_ptr<LogEvent> event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+  // 30->32->31
+  event->write(hostUid);
+  event->write(isolatedNonAdditiveData);
+  event->write(isolatedAdditiveData);
+  event->init();
+  inputData.push_back(event);
+
+  // 30->22->21
+  event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+  event->write(hostUid);
+  event->write(hostNonAdditiveData);
+  event->write(hostAdditiveData);
+  event->init();
+  inputData.push_back(event);
+
+  sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
+  EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid))
+      .WillRepeatedly(Return(hostUid));
+  EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid)))
+      .WillRepeatedly(ReturnArg<0>());
+  mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+
+  // 20->32->31
+  // 20->22->21
+  vector<vector<int>> actual;
+  extractIntoVector(inputData, actual);
+  vector<int> expectedV1 = {20, 32, 31};
+  vector<int> expectedV2 = {20, 22, 21};
+  EXPECT_EQ(2, (int)actual.size());
+  EXPECT_THAT(actual, Contains(expectedV1));
+  EXPECT_THAT(actual, Contains(expectedV2));
+}
+
+TEST(puller_util, MultipleIsolatedUidToOneHostUid) {
+  vector<shared_ptr<LogEvent>> inputData;
+  shared_ptr<LogEvent> event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+  // 30->32->31
+  event->write(isolatedUid);
+  event->write(isolatedNonAdditiveData);
+  event->write(isolatedAdditiveData);
+  event->init();
+  inputData.push_back(event);
+
+  // 31->32->21
+  event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+  event->write(isolatedUid + 1);
+  event->write(isolatedNonAdditiveData);
+  event->write(hostAdditiveData);
+  event->init();
+  inputData.push_back(event);
+
+  // 20->32->21
+  event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+  event->write(hostUid);
+  event->write(isolatedNonAdditiveData);
+  event->write(hostAdditiveData);
+  event->init();
+  inputData.push_back(event);
+
+  sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
+  EXPECT_CALL(*uidMap, getHostUidOrSelf(_)).WillRepeatedly(Return(hostUid));
+  mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+
+  vector<vector<int>> actual;
+  extractIntoVector(inputData, actual);
+  vector<int> expectedV1 = {20, 32, 73};
+  EXPECT_EQ(1, (int)actual.size());
+  EXPECT_THAT(actual, Contains(expectedV1));
+}
+
+TEST(puller_util, NoNeedToMerge) {
+  vector<shared_ptr<LogEvent>> inputData;
+  shared_ptr<LogEvent> event =
+      make_shared<LogEvent>(nonUidAtomTagId, timestamp);
+  // 32
+  event->write(isolatedNonAdditiveData);
+  event->init();
+  inputData.push_back(event);
+
+  event = make_shared<LogEvent>(nonUidAtomTagId, timestamp);
+  // 22
+  event->write(hostNonAdditiveData);
+  event->init();
+  inputData.push_back(event);
+
+  sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
+  mergeIsolatedUidsToHostUid(inputData, uidMap, nonUidAtomTagId);
+
+  EXPECT_EQ(2, (int)inputData.size());
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.h b/cmds/statsd/tests/metrics/metrics_test_helper.h
index 0a97456..b48de540 100644
--- a/cmds/statsd/tests/metrics/metrics_test_helper.h
+++ b/cmds/statsd/tests/metrics/metrics_test_helper.h
@@ -15,6 +15,7 @@
 
 #include "src/condition/ConditionWizard.h"
 #include "src/external/StatsPullerManager.h"
+#include "src/packages/UidMap.h"
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
@@ -40,6 +41,11 @@
     MOCK_METHOD2(Pull, bool(const int pullCode, vector<std::shared_ptr<LogEvent>>* data));
 };
 
+class MockUidMap : public UidMap {
+ public:
+  MOCK_CONST_METHOD1(getHostUidOrSelf, int(int uid));
+};
+
 HashableDimensionKey getMockedDimensionKey(int tagId, int key, std::string value);
 MetricDimensionKey getMockedMetricDimensionKey(int tagId, int key, std::string value);
 
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 5ee7ede..4626cb2 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -348,4 +348,9 @@
      * Returns is the caller has the same uid as the Recents component
      */
     public abstract boolean isCallerRecents(int callingUid);
+
+    /**
+     * Whether an UID is active or idle.
+     */
+    public abstract boolean isUidActive(int uid);
 }
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 2b648ea..eb26026 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -609,18 +609,19 @@
 
     /**
      * A key was pressed down.
+     * <p>
+     * If the focused view didn't want this event, this method is called.
+     * <p>
+     * Default implementation consumes {@link KeyEvent#KEYCODE_BACK KEYCODE_BACK}
+     * and, as of {@link android.os.Build.VERSION_CODES#P P}, {@link KeyEvent#KEYCODE_ESCAPE
+     * KEYCODE_ESCAPE} to later handle them in {@link #onKeyUp}.
      * 
-     * <p>If the focused view didn't want this event, this method is called.
-     *
-     * <p>The default implementation consumed the KEYCODE_BACK to later
-     * handle it in {@link #onKeyUp}.
-     *
      * @see #onKeyUp
      * @see android.view.KeyEvent
      */
     @Override
     public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) {
-        if (keyCode == KeyEvent.KEYCODE_BACK) {
+        if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE) {
             event.startTracking();
             return true;
         }
@@ -640,16 +641,18 @@
 
     /**
      * A key was released.
-     * 
-     * <p>The default implementation handles KEYCODE_BACK to close the
-     * dialog.
+     * <p>
+     * Default implementation consumes {@link KeyEvent#KEYCODE_BACK KEYCODE_BACK}
+     * and, as of {@link android.os.Build.VERSION_CODES#P P}, {@link KeyEvent#KEYCODE_ESCAPE
+     * KEYCODE_ESCAPE} to close the dialog.
      *
      * @see #onKeyDown
-     * @see KeyEvent
+     * @see android.view.KeyEvent
      */
     @Override
     public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) {
-        if (keyCode == KeyEvent.KEYCODE_BACK && event.isTracking()
+        if ((keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE)
+                && event.isTracking()
                 && !event.isCanceled()) {
             onBackPressed();
             return true;
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 87f32b2..aa52cde 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -38,7 +38,6 @@
 import android.content.Context;
 import android.content.IRestrictionsManager;
 import android.content.RestrictionsManager;
-import android.content.pm.ApplicationInfo;
 import android.content.pm.CrossProfileApps;
 import android.content.pm.ICrossProfileApps;
 import android.content.pm.IShortcutService;
@@ -90,7 +89,6 @@
 import android.net.lowpan.LowpanManager;
 import android.net.nsd.INsdManager;
 import android.net.nsd.NsdManager;
-import android.net.wifi.IRttManager;
 import android.net.wifi.IWifiManager;
 import android.net.wifi.IWifiScanner;
 import android.net.wifi.RttManager;
@@ -117,7 +115,6 @@
 import android.os.IUserManager;
 import android.os.IncidentManager;
 import android.os.PowerManager;
-import android.os.Process;
 import android.os.RecoverySystem;
 import android.os.ServiceManager;
 import android.os.ServiceManager.ServiceNotFoundException;
@@ -639,13 +636,13 @@
 
         registerService(Context.WIFI_RTT_SERVICE, RttManager.class,
                 new CachedServiceFetcher<RttManager>() {
-            @Override
-            public RttManager createService(ContextImpl ctx) throws ServiceNotFoundException {
-                IBinder b = ServiceManager.getServiceOrThrow(Context.WIFI_RTT_SERVICE);
-                IRttManager service = IRttManager.Stub.asInterface(b);
-                return new RttManager(ctx.getOuterContext(), service,
-                        ConnectivityThread.getInstanceLooper());
-            }});
+                @Override
+                public RttManager createService(ContextImpl ctx) throws ServiceNotFoundException {
+                    IBinder b = ServiceManager.getServiceOrThrow(Context.WIFI_RTT_RANGING_SERVICE);
+                    IWifiRttManager service = IWifiRttManager.Stub.asInterface(b);
+                    return new RttManager(ctx.getOuterContext(),
+                            new WifiRttManager(ctx.getOuterContext(), service));
+                }});
 
         registerService(Context.WIFI_RTT_RANGING_SERVICE, WifiRttManager.class,
                 new CachedServiceFetcher<WifiRttManager>() {
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index 09a46b8..0342c93 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -468,6 +468,18 @@
         dest.writeBoolean(mOverlayIsStatic);
         dest.writeInt(compileSdkVersion);
         dest.writeString(compileSdkVersionCodename);
+        writeSigningCertificateHistoryToParcel(dest, parcelableFlags);
+    }
+
+    private void writeSigningCertificateHistoryToParcel(Parcel dest, int parcelableFlags) {
+        if (signingCertificateHistory != null) {
+            dest.writeInt(signingCertificateHistory.length);
+            for (int i = 0; i < signingCertificateHistory.length; i++) {
+                dest.writeTypedArray(signingCertificateHistory[i], parcelableFlags);
+            }
+        } else {
+            dest.writeInt(-1);
+        }
     }
 
     public static final Parcelable.Creator<PackageInfo> CREATOR
@@ -523,6 +535,7 @@
         mOverlayIsStatic = source.readBoolean();
         compileSdkVersion = source.readInt();
         compileSdkVersionCodename = source.readString();
+        readSigningCertificateHistoryFromParcel(source);
 
         // The component lists were flattened with the redundant ApplicationInfo
         // instances omitted.  Distribute the canonical one here as appropriate.
@@ -534,6 +547,16 @@
         }
     }
 
+    private void readSigningCertificateHistoryFromParcel(Parcel source) {
+        int len = source.readInt();
+        if (len != -1) {
+            signingCertificateHistory = new Signature[len][];
+            for (int i = 0; i < len; i++) {
+                signingCertificateHistory[i] = source.createTypedArray(Signature.CREATOR);
+            }
+        }
+    }
+
     private void propagateApplicationInfo(ApplicationInfo appInfo, ComponentInfo[] components) {
         if (components != null) {
             for (ComponentInfo ci : components) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index f11e516..d14ac20 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7396,7 +7396,8 @@
          */
         public static final String NIGHT_DISPLAY_AUTO_MODE = "night_display_auto_mode";
 
-        private static final Validator NIGHT_DISPLAY_AUTO_MODE_VALIDATOR = BOOLEAN_VALIDATOR;
+        private static final Validator NIGHT_DISPLAY_AUTO_MODE_VALIDATOR =
+                new SettingsValidators.InclusiveIntegerRangeValidator(0, 2);
 
         /**
          * Control the color temperature of Night Display, represented in Kelvin.
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 79fc134..c460a33 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
+
 import static java.lang.Math.max;
 
 import android.animation.AnimatorInflater;
@@ -8378,7 +8379,7 @@
             AccessibilityNodeProvider provider, AccessibilityNodeInfo info,
             boolean forAutofill) {
         structure.setId(AccessibilityNodeInfo.getVirtualDescendantId(info.getSourceNodeId()),
-                null, null, null);
+                null, null, info.getViewIdResourceName());
         Rect rect = structure.getTempRect();
         info.getBoundsInParent(rect);
         structure.setDimens(rect.left, rect.top, 0, 0, rect.width(), rect.height());
@@ -8418,6 +8419,13 @@
         CharSequence cname = info.getClassName();
         structure.setClassName(cname != null ? cname.toString() : null);
         structure.setContentDescription(info.getContentDescription());
+        if (forAutofill) {
+            final int maxTextLength = info.getMaxTextLength();
+            if (maxTextLength != -1) {
+                structure.setMaxTextLength(maxTextLength);
+            }
+            structure.setHint(info.getHintText());
+        }
         if ((info.getText() != null || info.getError() != null)) {
             structure.setText(info.getText(), info.getTextSelectionStart(),
                     info.getTextSelectionEnd());
@@ -8428,7 +8436,8 @@
                     final AutofillValue autofillValue = AutofillValue.forText(structure.getText());
                     structure.setAutofillValue(autofillValue);
                     if (info.isPassword()) {
-                        structure.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD);
+                        structure.setInputType(InputType.TYPE_CLASS_TEXT
+                                | InputType.TYPE_TEXT_VARIATION_PASSWORD);
                     }
                 } else {
                     structure.setDataIsSensitive(false);
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 63a9990..5131a8a 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -16,6 +16,10 @@
 
 package android.view.autofill;
 
+import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
+import static android.view.autofill.Helper.sDebug;
+import static android.view.autofill.Helper.sVerbose;
+
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -47,13 +51,14 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeProvider;
 import android.view.accessibility.AccessibilityWindowInfo;
+
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.Preconditions;
+
 import org.xmlpull.v1.XmlPullParserException;
-import sun.misc.Cleaner;
 
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -66,9 +71,7 @@
 import java.util.List;
 import java.util.Objects;
 
-import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
-import static android.view.autofill.Helper.sDebug;
-import static android.view.autofill.Helper.sVerbose;
+import sun.misc.Cleaner;
 
 // TODO: use java.lang.ref.Cleaner once Android supports Java 9
 
@@ -616,10 +619,8 @@
     /**
      * @hide
      */
-    public boolean isCompatibilityModeEnabled() {
-        synchronized (mLock) {
-            return mCompatibilityBridge != null;
-        }
+    public boolean isCompatibilityModeEnabledLocked() {
+        return mCompatibilityBridge != null;
     }
 
     /**
@@ -1381,7 +1382,8 @@
             @NonNull AutofillValue value, int flags) {
         if (sVerbose) {
             Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value
-                    + ", flags=" + flags + ", state=" + getStateAsStringLocked());
+                    + ", flags=" + flags + ", state=" + getStateAsStringLocked()
+                    + ", compatMode=" + isCompatibilityModeEnabledLocked());
         }
         if (mState != STATE_UNKNOWN && !isFinishedLocked() && (flags & FLAG_MANUAL_REQUEST) == 0) {
             if (sVerbose) {
@@ -1392,7 +1394,7 @@
         }
         try {
             final AutofillClient client = getClient();
-            if (client == null) return; // NOTE: getClient() already logd it..
+            if (client == null) return; // NOTE: getClient() already logged it..
 
             mSessionId = mService.startSession(client.autofillClientGetActivityToken(),
                     mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
@@ -1939,7 +1941,8 @@
         pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds);
         pw.print(pfx); pw.print("save trigger id: "); pw.println(mSaveTriggerId);
         pw.print(pfx); pw.print("save on finish(): "); pw.println(mSaveOnFinish);
-        pw.print(pfx); pw.print("compat mode enabled: "); pw.println(isCompatibilityModeEnabled());
+        pw.print(pfx); pw.print("compat mode enabled: "); pw.println(
+                isCompatibilityModeEnabledLocked());
         pw.print(pfx); pw.print("debug: "); pw.print(sDebug);
         pw.print(" verbose: "); pw.println(sVerbose);
     }
diff --git a/core/java/com/android/internal/widget/VerifyCredentialResponse.java b/core/java/com/android/internal/widget/VerifyCredentialResponse.java
index ad6020c..7d1c706 100644
--- a/core/java/com/android/internal/widget/VerifyCredentialResponse.java
+++ b/core/java/com/android/internal/widget/VerifyCredentialResponse.java
@@ -98,6 +98,8 @@
             if (mPayload != null) {
                 dest.writeInt(mPayload.length);
                 dest.writeByteArray(mPayload);
+            } else {
+                dest.writeInt(0);
             }
         }
     }
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index ed032c7..f6223fa 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -90,7 +90,7 @@
 }
 
 static bool addSkTypeface(NativeFamilyBuilder* builder, sk_sp<SkData>&& data, int ttcIndex,
-        jint weight, jint italic) {
+        jint givenWeight, jint givenItalic) {
     uirenderer::FatVector<SkFontArguments::Axis, 2> skiaAxes;
     for (const auto& axis : builder->axes) {
         skiaAxes.emplace_back(SkFontArguments::Axis{axis.axisTag, axis.value});
@@ -114,15 +114,27 @@
     std::shared_ptr<minikin::MinikinFont> minikinFont =
             std::make_shared<MinikinFontSkia>(std::move(face), fontPtr, fontSize, ttcIndex,
                     builder->axes);
-    minikin::Font::Builder fontBuilder(minikinFont);
 
-    if (weight != RESOLVE_BY_FONT_TABLE) {
-        fontBuilder.setWeight(weight);
+    int weight = givenWeight;
+    bool italic = givenItalic == 1;
+    if (givenWeight == RESOLVE_BY_FONT_TABLE || givenItalic == RESOLVE_BY_FONT_TABLE) {
+        int os2Weight;
+        bool os2Italic;
+        if (!minikin::FontFamily::analyzeStyle(minikinFont, &os2Weight, &os2Italic)) {
+            ALOGE("analyzeStyle failed. Using default style");
+            os2Weight = 400;
+            os2Italic = false;
+        }
+        if (givenWeight == RESOLVE_BY_FONT_TABLE) {
+            weight = os2Weight;
+        }
+        if (givenItalic == RESOLVE_BY_FONT_TABLE) {
+            italic = os2Italic;
+        }
     }
-    if (italic != RESOLVE_BY_FONT_TABLE) {
-        fontBuilder.setSlant(static_cast<minikin::FontStyle::Slant>(italic != 0));
-    }
-    builder->fonts.push_back(fontBuilder.build());
+
+    builder->fonts.push_back(minikin::Font(minikinFont,
+            minikin::FontStyle(weight, static_cast<minikin::FontStyle::Slant>(italic))));
     builder->axes.clear();
     return true;
 }
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 482d028..115d0d5 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -576,7 +576,7 @@
         minikin::FakedFont baseFont = typeface->fFontCollection->baseFontFaked(typeface->fStyle);
         float saveSkewX = paint->getTextSkewX();
         bool savefakeBold = paint->isFakeBoldText();
-        MinikinFontSkia::populateSkPaint(paint, baseFont.font->typeface().get(), baseFont.fakery);
+        MinikinFontSkia::populateSkPaint(paint, baseFont.font, baseFont.fakery);
         SkScalar spacing = paint->getFontMetrics(metrics);
         // The populateSkPaint call may have changed fake bold / text skew
         // because we want to measure with those effects applied, so now
diff --git a/core/proto/android/server/alarmmanagerservice.proto b/core/proto/android/server/alarmmanagerservice.proto
index 0342c9c..d1c5db6 100644
--- a/core/proto/android/server/alarmmanagerservice.proto
+++ b/core/proto/android/server/alarmmanagerservice.proto
@@ -104,11 +104,6 @@
 
   repeated InFlightProto outstanding_deliveries = 34;
 
-  // Minimum time between ALLOW_WHILE_IDLE alarms when system is idling. It
-  // should be either CosntantsProto.allow_while_idle_short_duration_ms or
-  // ConstantsProto.allow_while_idle_long_duration_ms.
-  optional int64 allow_while_idle_min_duration_ms = 35;
-
   message LastAllowWhileIdleDispatch {
     option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
@@ -121,7 +116,7 @@
   }
 
   // Whether the short or long while-idle timeout should be used for each UID.
-  repeated int32 use_allow_while_idle_short_time = 42;
+  repeated int32 use_allow_while_idle_short_time = 35;
 
   // For each uid, this is the last time we dispatched an "allow while idle"
   // alarm, used to determine the earliest we can dispatch the next such alarm.
diff --git a/core/proto/android/server/powermanagerservice.proto b/core/proto/android/server/powermanagerservice.proto
index b5c3ac0..5cb5319 100644
--- a/core/proto/android/server/powermanagerservice.proto
+++ b/core/proto/android/server/powermanagerservice.proto
@@ -227,7 +227,6 @@
         optional int32 setting_minimum = 1;
         optional int32 setting_maximum = 2;
         optional int32 setting_default = 3;
-        optional int32 setting_for_vr_default = 4;
     }
 
     // True to decouple auto-suspend mode from the display state.
@@ -293,44 +292,27 @@
     // The stay on while plugged in setting.
     // A set of battery conditions under which to make the screen stay on.
     optional StayOnWhilePluggedInProto stay_on_while_plugged_in = 29;
-    // The screen brightness setting, from 0 to 255.
-    // Use -1 if no value has been set.
-    optional sint32 screen_brightness_setting = 30;
-    // The screen auto-brightness adjustment setting, from -1 to 1.
-    // Use 0 if there is no adjustment.
-    optional float screen_auto_brightness_adjustment_setting = 31;
     // The screen brightness mode.
-    optional .android.providers.settings.SettingsProto.ScreenBrightnessMode screen_brightness_mode_setting = 32;
+    optional .android.providers.settings.SettingsProto.ScreenBrightnessMode screen_brightness_mode_setting = 30;
     // The screen brightness setting override from the window manager
     // to allow the current foreground activity to override the brightness.
     // Use -1 to disable.
-    optional sint32 screen_brightness_override_from_window_manager = 33;
+    optional sint32 screen_brightness_override_from_window_manager = 31;
     // The user activity timeout override from the window manager
     // to allow the current foreground activity to override the user activity
     // timeout. Use -1 to disable.
-    optional sint64 user_activity_timeout_override_from_window_manager_ms = 34;
+    optional sint64 user_activity_timeout_override_from_window_manager_ms = 32;
     // The window manager has determined the user to be inactive via other means.
     // Set this to false to disable.
-    optional bool is_user_inactive_override_from_window_manager = 35;
-    // The screen brightness setting override from the settings application
-    // to temporarily adjust the brightness until next updated,
-    // Use -1 to disable.
-    optional sint32 temporary_screen_brightness_setting_override = 36;
-    // The screen brightness adjustment setting override from the settings
-    // application to temporarily adjust the auto-brightness adjustment factor
-    // until next updated, in the range -1..1.
-    // Use NaN to disable.
-    optional float temporary_screen_auto_brightness_adjustment_setting_override = 37;
+    optional bool is_user_inactive_override_from_window_manager = 33;
     // The screen state to use while dozing.
-    optional .android.view.DisplayStateEnum doze_screen_state_override_from_dream_manager = 38;
+    optional .android.view.DisplayStateEnum doze_screen_state_override_from_dream_manager = 34;
     // The screen brightness to use while dozing.
-    optional float dozed_screen_brightness_override_from_dream_manager = 39;
+    optional float dozed_screen_brightness_override_from_dream_manager = 35;
     // Screen brightness settings limits.
-    optional ScreenBrightnessSettingLimitsProto screen_brightness_setting_limits = 40;
-    // The screen brightness setting, from 0 to 255, to be used while in VR Mode.
-    optional int32 screen_brightness_for_vr_setting = 41;
+    optional ScreenBrightnessSettingLimitsProto screen_brightness_setting_limits = 36;
     // True if double tap to wake is enabled
-    optional bool is_double_tap_wake_enabled = 42;
+    optional bool is_double_tap_wake_enabled = 37;
     // True if we are currently in VR Mode.
-    optional bool is_vr_mode_enabled = 43;
+    optional bool is_vr_mode_enabled = 38;
 }
diff --git a/data/keyboards/Generic.kcm b/data/keyboards/Generic.kcm
index 1ef74ba..d0565ca 100644
--- a/data/keyboards/Generic.kcm
+++ b/data/keyboards/Generic.kcm
@@ -472,11 +472,15 @@
 ### Non-printing keys ###
 
 key ESCAPE {
-    base:                               fallback BACK
+    base:                               none
     alt, meta:                          fallback HOME
     ctrl:                               fallback MENU
 }
 
+key DEL {
+    ctrl+alt:                           fallback BACK
+}
+
 ### Gamepad buttons ###
 
 key BUTTON_A {
diff --git a/data/keyboards/Virtual.kcm b/data/keyboards/Virtual.kcm
index c4647e0..c763cc09 100644
--- a/data/keyboards/Virtual.kcm
+++ b/data/keyboards/Virtual.kcm
@@ -469,11 +469,15 @@
 ### Non-printing keys ###
 
 key ESCAPE {
-    base:                               fallback BACK
+    base:                               none
     alt, meta:                          fallback HOME
     ctrl:                               fallback MENU
 }
 
+key DEL {
+    ctrl+alt:                           fallback BACK
+}
+
 ### Gamepad buttons ###
 
 key BUTTON_A {
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index f5a6f49..8b5114c 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -825,6 +825,14 @@
         mFillPaint.setXfermode(mode);
     }
 
+    /**
+     * @param aa to draw this drawable with
+     * @hide
+     */
+    public void setAntiAlias(boolean aa) {
+        mFillPaint.setAntiAlias(aa);
+    }
+
     private void buildPathIfDirty() {
         final GradientState st = mGradientState;
         if (mPathIsDirty) {
diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp
index dca9ef5..091b526 100644
--- a/libs/hwui/hwui/Typeface.cpp
+++ b/libs/hwui/hwui/Typeface.cpp
@@ -132,8 +132,8 @@
         bool italicFromFont;
 
         const minikin::FontStyle defaultStyle;
-        const minikin::MinikinFont* mf = families.empty() ? nullptr
-                : families[0]->getClosestMatch(defaultStyle).font->typeface().get();
+        const minikin::MinikinFont* mf =
+                families.empty() ? nullptr : families[0]->getClosestMatch(defaultStyle).font;
         if (mf != nullptr) {
             SkTypeface* skTypeface = reinterpret_cast<const MinikinFontSkia*>(mf)->GetSkTypeface();
             const SkFontStyle& style = skTypeface->fontStyle();
@@ -183,7 +183,7 @@
     std::shared_ptr<minikin::MinikinFont> font = std::make_shared<MinikinFontSkia>(
             std::move(typeface), data, st.st_size, 0, std::vector<minikin::FontVariation>());
     std::vector<minikin::Font> fonts;
-    fonts.push_back(minikin::Font::Builder(font).build());
+    fonts.push_back(minikin::Font(std::move(font), minikin::FontStyle()));
 
     std::shared_ptr<minikin::FontCollection> collection = std::make_shared<minikin::FontCollection>(
             std::make_shared<minikin::FontFamily>(std::move(fonts)));
diff --git a/libs/hwui/tests/unit/TypefaceTests.cpp b/libs/hwui/tests/unit/TypefaceTests.cpp
index e424a26..2232c25 100644
--- a/libs/hwui/tests/unit/TypefaceTests.cpp
+++ b/libs/hwui/tests/unit/TypefaceTests.cpp
@@ -57,7 +57,7 @@
     std::shared_ptr<minikin::MinikinFont> font = std::make_shared<MinikinFontSkia>(
             std::move(typeface), data, st.st_size, 0, std::vector<minikin::FontVariation>());
     std::vector<minikin::Font> fonts;
-    fonts.push_back(minikin::Font::Builder(font).build());
+    fonts.push_back(minikin::Font(std::move(font), minikin::FontStyle()));
     return std::make_shared<minikin::FontFamily>(std::move(fonts));
 }
 
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 6ef3fac..1cff59c 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -865,30 +865,49 @@
     <!-- Summary shown for color space correction preference when its value is overridden by another preference [CHAR LIMIT=35] -->
     <string name="daltonizer_type_overridden">Overridden by <xliff:g id="title" example="Simulate color space">%1$s</xliff:g></string>
 
-    <!-- [CHAR_LIMIT=40] Label for estimated remaining duration of battery discharging -->
-    <string name="power_remaining_duration_only">About <xliff:g id="time">^1</xliff:g> left</string>
-    <!-- [CHAR_LIMIT=60] Label for estimated remaining duration of battery discharging -->
-    <string name="power_remaining_duration_only_enhanced">About <xliff:g id="time">^1</xliff:g> left based on your usage</string>
-    <!-- [CHAR_LIMIT=40] Label for estimated remaining duration of battery charging -->
-    <string name="power_remaining_charging_duration_only"><xliff:g id="time">^1</xliff:g> left until fully charged</string>
+  <!-- [CHAR_LIMIT=40] Label for estimated remaining duration of battery discharging -->
+  <string name="power_remaining_duration_only">About <xliff:g id="time">%1$s</xliff:g> left</string>
+  <!-- [CHAR_LIMIT=60] Label for estimated remaining duration of battery discharging -->
+  <string name="power_remaining_duration_only_enhanced">About <xliff:g id="time">%1$s</xliff:g> left based on your usage</string>
+  <!-- [CHAR_LIMIT=40] Label for estimated remaining duration of battery charging -->
+  <string name="power_remaining_charging_duration_only"><xliff:g id="time">%1$s</xliff:g> left until fully charged</string>
 
-    <!-- [CHAR_LIMIT=40] Short label for estimated remaining duration of battery charging/discharging -->
-    <string name="power_remaining_duration_only_short"><xliff:g id="time">^1</xliff:g> left</string>
-    <!-- [CHAR_LIMIT=60] Short label for estimated remaining duration of battery charging/discharging -->
-    <string name="power_remaining_duration_only_short_enhanced"><xliff:g id="time">^1</xliff:g> left based on your usage</string>
+  <!-- [CHAR_LIMIT=40] Short label for estimated remaining duration of battery charging/discharging -->
+  <string name="power_remaining_duration_only_short"><xliff:g id="time">%1$s</xliff:g> left</string>
 
-    <!-- [CHAR_LIMIT=40] Label for battery level chart when discharging with duration -->
-    <string name="power_discharging_duration"><xliff:g id="level">^1</xliff:g> - about <xliff:g id="time">^2</xliff:g> left</string>
-    <!-- [CHAR_LIMIT=60] Label for battery level chart when discharging with duration and using enhanced estimate -->
-    <string name="power_discharging_duration_enhanced"><xliff:g id="level">^1</xliff:g> - about <xliff:g id="time">^2</xliff:g> left based on your usage</string>
+  <!-- [CHAR_LIMIT=60] label for estimated remaining duration of battery when under a certain amount -->
+  <string name="power_remaining_less_than_duration_only">Less than <xliff:g id="threshold">%1$s</xliff:g> remaining</string>
+  <!-- [CHAR_LIMIT=60] label for estimated remaining duration of battery when under a certain amount with the percentage -->
+  <string name="power_remaining_less_than_duration"><xliff:g id="level">%1$s</xliff:g> - Less than <xliff:g id="threshold">%2$s</xliff:g> remaining</string>
 
-    <!-- [CHAR_LIMIT=40] Label for battery level chart when discharging with duration -->
-    <string name="power_discharging_duration_short"><xliff:g id="level">^1</xliff:g> - <xliff:g id="time">^2</xliff:g> left</string>
+  <!-- Used to let users know that they have more than some amount of battery life remaining with percentage. ex: 75% - more than 1 day remaining [CHAR LIMIT = 80] -->
+  <string name="power_remaining_more_than_subtext"><xliff:g id="level">%1$s</xliff:g>more than <xliff:g id="time_remaining">%2$s</xliff:g> remaining</string>
+  <!-- Used to let users know that they have more than some amount of battery life remaining. ex: more than 1 day remaining [CHAR LIMIT = 40] -->
+  <string name="power_remaining_only_more_than_subtext">more than <xliff:g id="time_remaining">%1$s</xliff:g> remaining</string>
+
+  <!-- [CHAR_LIMIT=50] Short label for imminent shutdown warning of device -->
+  <string name="power_remaining_duration_only_shutdown_imminent" product="default">phone may shutdown soon</string>
+  <!-- [CHAR_LIMIT=50] Short label for imminent shutdown warning of device -->
+  <string name="power_remaining_duration_only_shutdown_imminent" product="tablet">tablet may shutdown soon</string>
+  <!-- [CHAR_LIMIT=50] Short label for imminent shutdown warning of device -->
+  <string name="power_remaining_duration_only_shutdown_imminent" product="device">device may shutdown soon</string>
+
+  <!-- [CHAR_LIMIT=40] Label for battery level chart when discharging with duration -->
+  <string name="power_discharging_duration"><xliff:g id="level">%1$s</xliff:g> - about <xliff:g id="time">%2$s</xliff:g> left</string>
+  <!-- [CHAR_LIMIT=60] Label for battery level chart when discharging with duration and using enhanced estimate -->
+  <string name="power_discharging_duration_enhanced"><xliff:g id="level">%1$s</xliff:g> - about <xliff:g id="time">%2$s</xliff:g> left based on your usage</string>
+
+  <!-- [CHAR_LIMIT=60] Label for battery level chart when shutdown is imminent-->
+  <string name="power_remaining_duration_shutdown_imminent" product="default"><xliff:g id="level">%1$s</xliff:g> - phone may shutdown soon</string>
+  <!-- [CHAR_LIMIT=60] Label for battery level chart when shutdown is imminent-->
+  <string name="power_remaining_duration_shutdown_imminent" product="tablet"><xliff:g id="level">%1$s</xliff:g> - tablet may shutdown soon</string>
+  <!-- [CHAR_LIMIT=60] Label for battery level chart when shutdown is imminent-->
+  <string name="power_remaining_duration_shutdown_imminent" product="device"><xliff:g id="level">%1$s</xliff:g> - device may shutdown soon</string>
 
     <!-- [CHAR_LIMIT=40] Label for battery level chart when charging -->
     <string name="power_charging"><xliff:g id="level">%1$s</xliff:g> - <xliff:g id="state">%2$s</xliff:g></string>
     <!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration -->
-    <string name="power_charging_duration"><xliff:g id="level">^1</xliff:g> - <xliff:g id="time">^2</xliff:g> until fully charged</string>
+    <string name="power_charging_duration"><xliff:g id="level">%1$s</xliff:g> - <xliff:g id="time">%2$s</xliff:g> until fully charged</string>
 
     <!-- Battery Info screen. Value for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
     <string name="battery_info_status_unknown">Unknown</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
index 190f5e6..68ead09 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
@@ -17,7 +17,6 @@
 
 import android.annotation.LayoutRes;
 import android.annotation.Nullable;
-import android.app.ActionBar;
 import android.app.Activity;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -72,9 +71,9 @@
             requestWindowFeature(Window.FEATURE_NO_TITLE);
         }
         super.setContentView(R.layout.settings_with_drawer);
-        mContentHeaderContainer = (FrameLayout) findViewById(R.id.content_header_container);
+        mContentHeaderContainer = findViewById(R.id.content_header_container);
 
-        Toolbar toolbar = (Toolbar) findViewById(R.id.action_bar);
+        Toolbar toolbar = findViewById(R.id.action_bar);
         if (theme.getBoolean(android.R.styleable.Theme_windowNoTitle, false)) {
             toolbar.setVisibility(View.GONE);
             return;
@@ -89,7 +88,9 @@
 
     @Override
     public boolean onNavigateUp() {
-        finish();
+        if (!super.onNavigateUp()) {
+            finish();
+        }
         return true;
     }
 
@@ -104,11 +105,6 @@
         registerReceiver(mPackageReceiver, filter);
 
         new CategoriesUpdateTask().execute();
-        final Intent intent = getIntent();
-        if (intent != null && intent.getBooleanExtra(EXTRA_SHOW_MENU, false)) {
-            // Intent explicitly set to show menu.
-            showMenuIcon();
-        }
     }
 
     @Override
@@ -125,13 +121,6 @@
         mCategoryListeners.remove(listener);
     }
 
-    public void setContentHeaderView(View headerView) {
-        mContentHeaderContainer.removeAllViews();
-        if (headerView != null) {
-            mContentHeaderContainer.addView(headerView);
-        }
-    }
-
     @Override
     public void setContentView(@LayoutRes int layoutResID) {
         final ViewGroup parent = findViewById(R.id.content_frame);
@@ -151,13 +140,6 @@
         ((ViewGroup) findViewById(R.id.content_frame)).addView(view, params);
     }
 
-    private void showMenuIcon() {
-        final ActionBar actionBar = getActionBar();
-        if (actionBar != null) {
-            actionBar.setDisplayHomeAsUpEnabled(true);
-        }
-    }
-
     private void onCategoriesChanged() {
         final int N = mCategoryListeners.size();
         for (int i = 0; i < N; i++) {
@@ -165,10 +147,6 @@
         }
     }
 
-    public void onProfileTileOpen() {
-        finish();
-    }
-
     /**
      * @return whether or not the enabled state actually changed.
      */
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java b/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java
new file mode 100644
index 0000000..346ca66
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.utils;
+
+import android.content.Context;
+import android.icu.text.MeasureFormat;
+import android.icu.text.MeasureFormat.FormatWidth;
+import android.icu.util.Measure;
+import android.icu.util.MeasureUnit;
+import android.support.annotation.Nullable;
+import android.text.TextUtils;
+import com.android.settingslib.R;
+import com.android.settingslib.utils.StringUtil;
+import java.util.Locale;
+import java.util.concurrent.TimeUnit;
+
+/** Utility class for keeping power related strings consistent**/
+public class PowerUtil {
+    private static final long SEVEN_MINUTES_MILLIS = TimeUnit.MINUTES.toMillis(7);
+    private static final long FIFTEEN_MINUTES_MILLIS = TimeUnit.MINUTES.toMillis(15);
+    private static final long ONE_DAY_MILLIS = TimeUnit.DAYS.toMillis(1);
+
+    /**
+     * This method produces the text used in various places throughout the system to describe the
+     * remaining battery life of the phone in a consistent manner.
+     *
+     * @param context
+     * @param drainTimeMs The estimated time remaining before the phone dies in milliseconds.
+     * @param percentageString An optional percentage of battery remaining string.
+     * @param basedOnUsage Whether this estimate is based on usage or simple extrapolation.
+     * @return a properly formatted and localized string describing how much time remains
+     * before the battery runs out.
+     */
+    public static String getBatteryRemainingStringFormatted(Context context, long drainTimeMs,
+            @Nullable String percentageString, boolean basedOnUsage) {
+        if (drainTimeMs > 0) {
+            if (drainTimeMs <= SEVEN_MINUTES_MILLIS) {
+                // show a imminent shutdown warning if less than 7 minutes remain
+                return getShutdownImminentString(context, percentageString);
+            } else if (drainTimeMs <= FIFTEEN_MINUTES_MILLIS) {
+                // show a less than 15 min remaining warning if appropriate
+                CharSequence timeString = StringUtil.formatElapsedTime(context,
+                        FIFTEEN_MINUTES_MILLIS,
+                        false /* withSeconds */);
+                return getUnderFifteenString(context, timeString, percentageString);
+            } else if (drainTimeMs >= ONE_DAY_MILLIS) {
+                // just say more than one day if over 24 hours
+                return getMoreThanOneDayString(context, percentageString);
+            } else {
+                // show a regular time remaining string
+                return getRegularTimeRemainingString(context, drainTimeMs,
+                        percentageString, basedOnUsage);
+            }
+        }
+        return null;
+    }
+
+    private static String getShutdownImminentString(Context context, String percentageString) {
+        return TextUtils.isEmpty(percentageString)
+                ? context.getString(R.string.power_remaining_duration_only_shutdown_imminent)
+                : context.getString(
+                        R.string.power_remaining_duration_shutdown_imminent,
+                        percentageString);
+    }
+
+    private static String getUnderFifteenString(Context context, CharSequence timeString,
+            String percentageString) {
+        return TextUtils.isEmpty(percentageString)
+                ? context.getString(R.string.power_remaining_less_than_duration_only, timeString)
+                : context.getString(
+                        R.string.power_remaining_less_than_duration,
+                        percentageString,
+                        timeString);
+
+    }
+
+    private static String getMoreThanOneDayString(Context context, String percentageString) {
+        final Locale currentLocale = context.getResources().getConfiguration().getLocales().get(0);
+        final MeasureFormat frmt = MeasureFormat.getInstance(currentLocale, FormatWidth.SHORT);
+
+        final Measure daysMeasure = new Measure(1, MeasureUnit.DAY);
+
+        return TextUtils.isEmpty(percentageString)
+                ? context.getString(R.string.power_remaining_only_more_than_subtext,
+                        frmt.formatMeasures(daysMeasure))
+                : context.getString(
+                        R.string.power_remaining_more_than_subtext,
+                        percentageString,
+                        frmt.formatMeasures(daysMeasure));
+    }
+
+    private static String getRegularTimeRemainingString(Context context, long drainTimeMs,
+            String percentageString, boolean basedOnUsage) {
+        // round to the nearest 15 min to not appear oversly precise
+        final long roundedTimeMs = roundToNearestThreshold(drainTimeMs,
+                FIFTEEN_MINUTES_MILLIS);
+        CharSequence timeString = StringUtil.formatElapsedTime(context,
+                roundedTimeMs,
+                false /* withSeconds */);
+        if (TextUtils.isEmpty(percentageString)) {
+            int id = basedOnUsage
+                    ? R.string.power_remaining_duration_only_enhanced
+                    : R.string.power_remaining_duration_only;
+            return context.getString(id, timeString);
+        } else {
+            int id = basedOnUsage
+                    ? R.string.power_discharging_duration_enhanced
+                    : R.string.power_discharging_duration;
+            return context.getString(id, percentageString, timeString);
+        }
+    }
+
+    public static long convertUsToMs(long timeUs) {
+        return timeUs / 1000;
+    }
+
+    public static long convertMsToUs(long timeMs) {
+        return timeMs * 1000;
+    }
+
+    private static long roundToNearestThreshold(long drainTime, long threshold) {
+        final long remainder = drainTime % threshold;
+        if (remainder < threshold / 2) {
+            return drainTime - remainder;
+        } else {
+            return drainTime - remainder + threshold;
+        }
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java b/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java
new file mode 100644
index 0000000..45fdd78
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.utils;
+
+import android.content.Context;
+import android.icu.text.MeasureFormat;
+import android.icu.text.MeasureFormat.FormatWidth;
+import android.icu.text.RelativeDateTimeFormatter;
+import android.icu.text.RelativeDateTimeFormatter.RelativeUnit;
+import android.icu.util.Measure;
+import android.icu.util.MeasureUnit;
+import android.icu.util.ULocale;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.style.TtsSpan;
+import java.util.ArrayList;
+import java.util.Locale;
+
+/** Utility class for generally useful string methods **/
+public class StringUtil {
+
+  public static final int SECONDS_PER_MINUTE = 60;
+  public static final int SECONDS_PER_HOUR = 60 * 60;
+  public static final int SECONDS_PER_DAY = 24 * 60 * 60;
+
+  /**
+   * Returns elapsed time for the given millis, in the following format:
+   * 2d 5h 40m 29s
+   * @param context the application context
+   * @param millis the elapsed time in milli seconds
+   * @param withSeconds include seconds?
+   * @return the formatted elapsed time
+   */
+  public static CharSequence formatElapsedTime(Context context, double millis,
+          boolean withSeconds) {
+      SpannableStringBuilder sb = new SpannableStringBuilder();
+      int seconds = (int) Math.floor(millis / 1000);
+      if (!withSeconds) {
+          // Round up.
+          seconds += 30;
+      }
+
+      int days = 0, hours = 0, minutes = 0;
+      if (seconds >= SECONDS_PER_DAY) {
+          days = seconds / SECONDS_PER_DAY;
+          seconds -= days * SECONDS_PER_DAY;
+      }
+      if (seconds >= SECONDS_PER_HOUR) {
+          hours = seconds / SECONDS_PER_HOUR;
+          seconds -= hours * SECONDS_PER_HOUR;
+      }
+      if (seconds >= SECONDS_PER_MINUTE) {
+          minutes = seconds / SECONDS_PER_MINUTE;
+          seconds -= minutes * SECONDS_PER_MINUTE;
+      }
+
+      final ArrayList<Measure> measureList = new ArrayList(4);
+      if (days > 0) {
+          measureList.add(new Measure(days, MeasureUnit.DAY));
+      }
+      if (hours > 0) {
+          measureList.add(new Measure(hours, MeasureUnit.HOUR));
+      }
+      if (minutes > 0) {
+          measureList.add(new Measure(minutes, MeasureUnit.MINUTE));
+      }
+      if (withSeconds && seconds > 0) {
+          measureList.add(new Measure(seconds, MeasureUnit.SECOND));
+      }
+      if (measureList.size() == 0) {
+          // Everything addable was zero, so nothing was added. We add a zero.
+          measureList.add(new Measure(0, withSeconds ? MeasureUnit.SECOND : MeasureUnit.MINUTE));
+      }
+      final Measure[] measureArray = measureList.toArray(new Measure[measureList.size()]);
+
+      final Locale locale = context.getResources().getConfiguration().locale;
+      final MeasureFormat measureFormat = MeasureFormat.getInstance(
+              locale, FormatWidth.NARROW);
+      sb.append(measureFormat.formatMeasures(measureArray));
+
+      if (measureArray.length == 1 && MeasureUnit.MINUTE.equals(measureArray[0].getUnit())) {
+          // Add ttsSpan if it only have minute value, because it will be read as "meters"
+          final TtsSpan ttsSpan = new TtsSpan.MeasureBuilder().setNumber(minutes)
+                  .setUnit("minute").build();
+          sb.setSpan(ttsSpan, 0, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+      }
+
+      return sb;
+  }
+
+    /**
+     * Returns relative time for the given millis in the past, in a short format such as "2 days
+     * ago", "5 hr. ago", "40 min. ago", or "29 sec. ago".
+     *
+     * <p>The unit is chosen to have good information value while only using one unit. So 27 hours
+     * and 50 minutes would be formatted as "28 hr. ago", while 50 hours would be formatted as
+     * "2 days ago".
+     *
+     * @param context the application context
+     * @param millis the elapsed time in milli seconds
+     * @param withSeconds include seconds?
+     * @return the formatted elapsed time
+     */
+    public static CharSequence formatRelativeTime(Context context, double millis,
+            boolean withSeconds) {
+        final int seconds = (int) Math.floor(millis / 1000);
+        final RelativeUnit unit;
+        final int value;
+        if (withSeconds && seconds < 2 * SECONDS_PER_MINUTE) {
+            unit = RelativeUnit.SECONDS;
+            value = seconds;
+        } else if (seconds < 2 * SECONDS_PER_HOUR) {
+            unit = RelativeUnit.MINUTES;
+            value = (seconds + SECONDS_PER_MINUTE / 2)
+                    / SECONDS_PER_MINUTE;
+        } else if (seconds < 2 * SECONDS_PER_DAY) {
+            unit = RelativeUnit.HOURS;
+            value = (seconds + SECONDS_PER_HOUR / 2)
+                    / SECONDS_PER_HOUR;
+        } else {
+            unit = RelativeUnit.DAYS;
+            value = (seconds + SECONDS_PER_DAY / 2)
+                    / SECONDS_PER_DAY;
+        }
+
+        final Locale locale = context.getResources().getConfiguration().locale;
+        final RelativeDateTimeFormatter formatter = RelativeDateTimeFormatter.getInstance(
+                ULocale.forLocale(locale),
+                null /* default NumberFormat */,
+                RelativeDateTimeFormatter.Style.SHORT,
+                android.icu.text.DisplayContext.CAPITALIZATION_FOR_MIDDLE_OF_SENTENCE);
+
+        return formatter.format(value, RelativeDateTimeFormatter.Direction.LAST, unit);
+    }
+}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawer/SettingsDrawerActivityTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawer/SettingsDrawerActivityTest.java
index 003f905..2f417ad 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawer/SettingsDrawerActivityTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawer/SettingsDrawerActivityTest.java
@@ -18,8 +18,6 @@
 
 import static android.support.test.espresso.Espresso.onView;
 import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist;
-import static android.support.test.espresso.assertion.ViewAssertions.matches;
-import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
 import static android.support.test.espresso.matcher.ViewMatchers.withContentDescription;
 
 import android.app.Instrumentation;
@@ -49,42 +47,22 @@
     }
 
     @Test
-    public void startActivityWithNoExtra_showNoNavUp() {
-        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
-        instrumentation.startActivitySync(new Intent(instrumentation.getTargetContext(),
-                TestActivity.class));
-
-        onView(withContentDescription(com.android.internal.R.string.action_bar_up_description))
-                .check(doesNotExist());
-    }
-
-    @Test
-    public void startActivityWithExtraToHideMenu_showNavUp() {
-        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
-        Intent intent = new Intent(instrumentation.getTargetContext(), TestActivity.class)
-                .putExtra(TestActivity.EXTRA_SHOW_MENU, false);
+    public void startActivity_doNotShowNavUp() {
+        final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        final Intent intent = new Intent(instrumentation.getTargetContext(), TestActivity.class)
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         instrumentation.startActivitySync(intent);
 
         onView(withContentDescription(com.android.internal.R.string.action_bar_up_description))
                 .check(doesNotExist());
     }
 
-    @Test
-    public void startActivityWithExtraToShowMenu_showNavUp() {
-        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
-        Intent intent = new Intent(instrumentation.getTargetContext(), TestActivity.class)
-                .putExtra(TestActivity.EXTRA_SHOW_MENU, true);
-        instrumentation.startActivitySync(intent);
-
-        onView(withContentDescription(com.android.internal.R.string.action_bar_up_description))
-                .check(matches(isDisplayed()));
-    }
-
     /**
      * Test Activity in this test.
      *
      * Use this activity because SettingsDrawerActivity hasn't been registered in its
      * AndroidManifest.xml
      */
-    public static class TestActivity extends SettingsDrawerActivity {}
+    public static class TestActivity extends SettingsDrawerActivity {
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
new file mode 100644
index 0000000..f93210f
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.utils;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import com.android.settingslib.R;
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.TestConfig;
+import com.android.settingslib.utils.PowerUtil;
+import java.time.Duration;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class PowerUtilTest {
+    public static final String TEST_BATTERY_LEVEL_10 = "10%";
+    public static final String FIFTEEN_MIN_FORMATTED = "15m";
+    public static final long SEVENTEEN_MIN_MILLIS = Duration.ofMinutes(17).toMillis();
+    public static final long FIVE_MINUTES_MILLIS = Duration.ofMinutes(5).toMillis();
+    public static final long TEN_MINUTES_MILLIS = Duration.ofMinutes(10).toMillis();
+    public static final long TWO_DAYS_MILLIS = Duration.ofDays(2).toMillis();
+    public static final String ONE_DAY_FORMATTED = "1 day";
+
+    private Context mContext;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mContext = spy(RuntimeEnvironment.application);
+    }
+
+    @Test
+    public void testGetBatteryRemainingStringFormatted_moreThanFifteenMinutes_withPercentage() {
+        String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+                SEVENTEEN_MIN_MILLIS,
+                TEST_BATTERY_LEVEL_10,
+                true /* basedOnUsage */);
+        String info2 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+                SEVENTEEN_MIN_MILLIS,
+                TEST_BATTERY_LEVEL_10,
+                false /* basedOnUsage */);
+
+        // We only add special mention for the long string
+        assertThat(info).isEqualTo(mContext.getString(
+                R.string.power_discharging_duration_enhanced,
+                TEST_BATTERY_LEVEL_10,
+                FIFTEEN_MIN_FORMATTED));
+        // shortened string should not have extra text
+        assertThat(info2).isEqualTo(mContext.getString(
+                R.string.power_discharging_duration,
+                TEST_BATTERY_LEVEL_10,
+                FIFTEEN_MIN_FORMATTED));
+    }
+
+    @Test
+    public void testGetBatteryRemainingStringFormatted_moreThanFifteenMinutes_noPercentage() {
+        String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+                SEVENTEEN_MIN_MILLIS,
+                null /* percentageString */,
+                true /* basedOnUsage */);
+        String info2 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+                SEVENTEEN_MIN_MILLIS,
+                null /* percentageString */,
+                false /* basedOnUsage */);
+
+        // We only add special mention for the long string
+        assertThat(info).isEqualTo(mContext.getString(
+                R.string.power_remaining_duration_only_enhanced,
+                FIFTEEN_MIN_FORMATTED));
+        // shortened string should not have extra text
+        assertThat(info2).isEqualTo(mContext.getString(
+                R.string.power_remaining_duration_only,
+                FIFTEEN_MIN_FORMATTED));
+    }
+
+
+    @Test
+    public void testGetBatteryRemainingStringFormatted_lessThanSevenMinutes_usesCorrectString() {
+        String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+                FIVE_MINUTES_MILLIS,
+                TEST_BATTERY_LEVEL_10 /* percentageString */,
+                true /* basedOnUsage */);
+        String info2 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+                FIVE_MINUTES_MILLIS,
+                null /* percentageString */,
+                true /* basedOnUsage */);
+
+        // additional battery percentage in this string
+        assertThat(info).isEqualTo(mContext.getString(
+                R.string.power_remaining_duration_shutdown_imminent,
+                TEST_BATTERY_LEVEL_10));
+        // shortened string should not have percentage
+        assertThat(info2).isEqualTo(mContext.getString(
+                R.string.power_remaining_duration_only_shutdown_imminent));
+    }
+
+    @Test
+    public void testGetBatteryRemainingStringFormatted_betweenSevenAndFifteenMinutes_usesCorrectString() {
+        String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+                TEN_MINUTES_MILLIS,
+                null /* percentageString */,
+                true /* basedOnUsage */);
+        String info2 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+                TEN_MINUTES_MILLIS,
+                TEST_BATTERY_LEVEL_10 /* percentageString */,
+                true /* basedOnUsage */);
+
+        // shortened string should not have percentage
+        assertThat(info).isEqualTo(mContext.getString(
+                R.string.power_remaining_less_than_duration_only,
+                FIFTEEN_MIN_FORMATTED));
+        // Add percentage to string when provided
+        assertThat(info2).isEqualTo(mContext.getString(
+                R.string.power_remaining_less_than_duration,
+                TEST_BATTERY_LEVEL_10,
+                FIFTEEN_MIN_FORMATTED));
+    }
+
+    @Test
+    public void testGetBatteryRemainingStringFormatted_moreThanOneDay_usesCorrectString() {
+        String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+                TWO_DAYS_MILLIS,
+                null /* percentageString */,
+                true /* basedOnUsage */);
+        String info2 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+                TWO_DAYS_MILLIS,
+                TEST_BATTERY_LEVEL_10 /* percentageString */,
+                true /* basedOnUsage */);
+
+        // shortened string should not have percentage
+        assertThat(info).isEqualTo(mContext.getString(
+                R.string.power_remaining_only_more_than_subtext,
+                ONE_DAY_FORMATTED));
+        // Add percentage to string when provided
+        assertThat(info2).isEqualTo(mContext.getString(
+                R.string.power_remaining_more_than_subtext,
+                TEST_BATTERY_LEVEL_10,
+                ONE_DAY_FORMATTED));
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java
new file mode 100644
index 0000000..d5e3cdb
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.utils;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.text.SpannableStringBuilder;
+import android.text.format.DateUtils;
+import android.text.style.TtsSpan;
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.TestConfig;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class StringUtilTest {
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        mContext = spy(RuntimeEnvironment.application);
+    }
+
+    @Test
+    public void testFormatElapsedTime_WithSeconds_ShowSeconds() {
+        final double testMillis = 5 * DateUtils.MINUTE_IN_MILLIS + 30 * DateUtils.SECOND_IN_MILLIS;
+        final String expectedTime = "5m 30s";
+
+        assertThat(StringUtil.formatElapsedTime(mContext, testMillis, true).toString())
+                .isEqualTo(expectedTime);
+    }
+
+    @Test
+    public void testFormatElapsedTime_NoSeconds_DoNotShowSeconds() {
+        final double testMillis = 5 * DateUtils.MINUTE_IN_MILLIS + 30 * DateUtils.SECOND_IN_MILLIS;
+        final String expectedTime = "6m";
+
+        assertThat(StringUtil.formatElapsedTime(mContext, testMillis, false).toString())
+                .isEqualTo(expectedTime);
+    }
+
+    @Test
+    public void testFormatElapsedTime_TimeMoreThanOneDay_ShowCorrectly() {
+        final double testMillis = 2 * DateUtils.DAY_IN_MILLIS
+                + 4 * DateUtils.HOUR_IN_MILLIS + 15 * DateUtils.MINUTE_IN_MILLIS;
+        final String expectedTime = "2d 4h 15m";
+
+        assertThat(StringUtil.formatElapsedTime(mContext, testMillis, false).toString())
+                .isEqualTo(expectedTime);
+    }
+
+    @Test
+    public void testFormatElapsedTime_ZeroFieldsInTheMiddleDontShow() {
+        final double testMillis = 2 * DateUtils.DAY_IN_MILLIS + 15 * DateUtils.MINUTE_IN_MILLIS;
+        final String expectedTime = "2d 15m";
+
+        assertThat(StringUtil.formatElapsedTime(mContext, testMillis, false).toString())
+                .isEqualTo(expectedTime);
+    }
+
+    @Test
+    public void testFormatElapsedTime_FormatZero_WithSeconds() {
+        final double testMillis = 0;
+        final String expectedTime = "0s";
+
+        assertThat(StringUtil.formatElapsedTime(mContext, testMillis, true).toString())
+                .isEqualTo(expectedTime);
+    }
+
+    @Test
+    public void testFormatElapsedTime_FormatZero_NoSeconds() {
+        final double testMillis = 0;
+        final String expectedTime = "0m";
+
+        assertThat(StringUtil.formatElapsedTime(mContext, testMillis, false).toString())
+                .isEqualTo(expectedTime);
+    }
+
+    @Test
+    public void testFormatElapsedTime_onlyContainsMinute_hasTtsSpan() {
+        final double testMillis = 15 * DateUtils.MINUTE_IN_MILLIS;
+
+        final CharSequence charSequence =
+                StringUtil.formatElapsedTime(mContext, testMillis, false);
+        assertThat(charSequence).isInstanceOf(SpannableStringBuilder.class);
+
+        final SpannableStringBuilder expectedString = (SpannableStringBuilder) charSequence;
+        final TtsSpan[] ttsSpans = expectedString.getSpans(0, expectedString.length(),
+                TtsSpan.class);
+
+        assertThat(ttsSpans).asList().hasSize(1);
+        assertThat(ttsSpans[0].getType()).isEqualTo(TtsSpan.TYPE_MEASURE);
+    }
+
+    @Test
+    public void testFormatRelativeTime_WithSeconds_ShowSeconds() {
+        final double testMillis = 40 * DateUtils.SECOND_IN_MILLIS;
+        final String expectedTime = "40 sec. ago";
+
+        assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
+                expectedTime);
+    }
+
+    @Test
+    public void testFormatRelativeTime_NoSeconds_DoNotShowSeconds() {
+        final double testMillis = 40 * DateUtils.SECOND_IN_MILLIS;
+        final String expectedTime = "1 min. ago";
+
+        assertThat(StringUtil.formatRelativeTime(mContext, testMillis, false).toString()).isEqualTo(
+                expectedTime);
+    }
+
+    @Test
+    public void testFormatRelativeTime_LessThanTwoMinutes_withSeconds() {
+        final double testMillis = 119 * DateUtils.SECOND_IN_MILLIS;
+        final String expectedTime = "119 sec. ago";
+
+        assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
+                expectedTime);
+    }
+
+    @Test
+    public void testFormatRelativeTime_LessThanTwoMinutes_NoSeconds() {
+        final double testMillis = 119 * DateUtils.SECOND_IN_MILLIS;
+        final String expectedTime = "2 min. ago";
+
+        assertThat(StringUtil.formatRelativeTime(mContext, testMillis, false).toString()).isEqualTo(
+                expectedTime);
+    }
+
+    @Test
+    public void testFormatRelativeTime_TwoMinutes_withSeconds() {
+        final double testMillis = 2 * DateUtils.MINUTE_IN_MILLIS;
+        final String expectedTime = "2 min. ago";
+
+        assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
+                expectedTime);
+    }
+
+    @Test
+    public void testFormatRelativeTime_LessThanTwoHours_withSeconds() {
+        final double testMillis = 119 * DateUtils.MINUTE_IN_MILLIS;
+        final String expectedTime = "119 min. ago";
+
+        assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
+                expectedTime);
+    }
+
+    @Test
+    public void testFormatRelativeTime_TwoHours_withSeconds() {
+        final double testMillis = 2 * DateUtils.HOUR_IN_MILLIS;
+        final String expectedTime = "2 hr. ago";
+
+        assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
+                expectedTime);
+    }
+
+    @Test
+    public void testFormatRelativeTime_LessThanTwoDays_withSeconds() {
+        final double testMillis = 47 * DateUtils.HOUR_IN_MILLIS;
+        final String expectedTime = "47 hr. ago";
+
+        assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
+                expectedTime);
+    }
+
+    @Test
+    public void testFormatRelativeTime_TwoDays_withSeconds() {
+        final double testMillis = 2 * DateUtils.DAY_IN_MILLIS;
+        final String expectedTime = "2 days ago";
+
+        assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
+                expectedTime);
+    }
+
+    @Test
+    public void testFormatRelativeTime_FormatZero_WithSeconds() {
+        final double testMillis = 0;
+        final String expectedTime = "0 sec. ago";
+
+        assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
+                expectedTime);
+    }
+
+    @Test
+    public void testFormatRelativeTime_FormatZero_NoSeconds() {
+        final double testMillis = 0;
+        final String expectedTime = "0 min. ago";
+
+        assertThat(StringUtil.formatRelativeTime(mContext, testMillis, false).toString()).isEqualTo(
+                expectedTime);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index aa56694..3a2b12f 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -26,10 +26,6 @@
 import android.content.DialogInterface.OnDismissListener;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.icu.text.MeasureFormat;
-import android.icu.text.MeasureFormat.FormatWidth;
-import android.icu.util.Measure;
-import android.icu.util.MeasureUnit;
 import android.media.AudioAttributes;
 import android.os.AsyncTask;
 import android.os.Handler;
@@ -37,11 +33,11 @@
 import android.os.PowerManager;
 import android.os.UserHandle;
 import android.support.annotation.VisibleForTesting;
-import android.text.format.DateUtils;
 import android.util.Slog;
 
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.settingslib.Utils;
+import com.android.settingslib.utils.PowerUtil;
 import com.android.systemui.R;
 import com.android.systemui.SystemUI;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
@@ -49,8 +45,6 @@
 
 import java.io.PrintWriter;
 import java.text.NumberFormat;
-import java.util.Locale;
-import java.util.concurrent.TimeUnit;
 
 public class PowerNotificationWarnings implements PowerUI.WarningsUI {
     private static final String TAG = PowerUI.TAG + ".Notification";
@@ -200,12 +194,7 @@
         // override notification copy if hybrid notification enabled
         if (mEstimate != null) {
             title = mContext.getString(R.string.battery_low_title_hybrid);
-            contentText = mContext.getString(
-                    mEstimate.isBasedOnUsage
-                            ? R.string.battery_low_percent_format_hybrid
-                            : R.string.battery_low_percent_format_hybrid_short,
-                    percentage,
-                    getTimeRemainingFormatted());
+            contentText = getHybridContentString(percentage);
         }
 
         final Notification.Builder nb =
@@ -239,21 +228,12 @@
         mNoMan.notifyAsUser(TAG_BATTERY, SystemMessage.NOTE_POWER_LOW, n, UserHandle.ALL);
     }
 
-    @VisibleForTesting
-    String getTimeRemainingFormatted() {
-        final Locale currentLocale = mContext.getResources().getConfiguration().getLocales().get(0);
-        MeasureFormat frmt = MeasureFormat.getInstance(currentLocale, FormatWidth.NARROW);
-
-        final long remainder = mEstimate.estimateMillis % DateUtils.HOUR_IN_MILLIS;
-        final long hours = TimeUnit.MILLISECONDS.toHours(
-                mEstimate.estimateMillis - remainder);
-        // round down to the nearest 15 min for now to not appear overly precise
-        final long minutes = TimeUnit.MILLISECONDS.toMinutes(
-                remainder - (remainder % TimeUnit.MINUTES.toMillis(15)));
-        final Measure hoursMeasure = new Measure(hours, MeasureUnit.HOUR);
-        final Measure minutesMeasure = new Measure(minutes, MeasureUnit.MINUTE);
-
-        return frmt.formatMeasures(hoursMeasure, minutesMeasure);
+    private String getHybridContentString(String percentage) {
+        return PowerUtil.getBatteryRemainingStringFormatted(
+            mContext,
+            mEstimate.estimateMillis,
+            percentage,
+            mEstimate.isBasedOnUsage);
     }
 
     private PendingIntent pendingBroadcast(String action) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index 3b9e7bc..65135ab 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -104,6 +104,11 @@
         setMeasuredDimension(width, height);
     }
 
+    @Override
+    public boolean hasOverlappingRendering() {
+        return false;
+    }
+
     private static int exactly(int size) {
         return MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
index d6beb7f..ab89a52 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
@@ -249,6 +249,9 @@
                     (GradientDrawable) ((LayerDrawable) mBackground).getDrawable(0);
             gradientDrawable.setXfermode(
                     running ? new PorterDuffXfermode(PorterDuff.Mode.SRC) : null);
+            // Speed optimization: disable AA if transfer mode is not SRC_OVER. AA is not easy to
+            // spot during animation anyways.
+            gradientDrawable.setAntiAlias(!running);
         }
         if (!mExpandAnimationRunning) {
             setDrawableAlpha(mDrawableAlpha);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
index 3e37cfe..bf6cc53 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
@@ -151,22 +151,4 @@
         verify(mMockNotificationManager, times(1)).cancelAsUser(anyString(),
                 eq(SystemMessage.NOTE_THERMAL_SHUTDOWN), any());
     }
-
-    @Test
-    public void testGetTimeRemainingFormatted_roundsDownTo15() {
-        mPowerNotificationWarnings.updateEstimate(
-                new Estimate(TimeUnit.MINUTES.toMillis(57), true));
-        String time = mPowerNotificationWarnings.getTimeRemainingFormatted();
-
-        assertTrue("time:" + time + ", expected: " + FORMATTED_45M, time.equals(FORMATTED_45M));
-    }
-
-    @Test
-    public void testGetTimeRemainingFormatted_keepsMinutesWhenZero() {
-        mPowerNotificationWarnings.updateEstimate(
-                new Estimate(TimeUnit.MINUTES.toMillis(65), true));
-        String time = mPowerNotificationWarnings.getTimeRemainingFormatted();
-
-        assertTrue("time:" + time + ", expected: " + FORMATTED_HOUR, time.equals(FORMATTED_HOUR));
-    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java
index a10bebf..e1b97bda 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java
@@ -29,12 +29,14 @@
 import com.android.systemui.statusbar.policy.LocationController.LocationChangeCallback;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper
 @SmallTest
+@Ignore
 public class LocationControllerImplTest extends SysuiTestCase {
 
     private LocationControllerImpl mLocationController;
@@ -79,4 +81,4 @@
 
         TestableLooper.get(this).processAllMessages();
     }
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 17c617c..355da2d 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -98,7 +98,8 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.LocalLog;
-import com.android.server.ForceAppStandbyTracker.Listener;
+import com.android.internal.util.Preconditions;
+import com.android.server.AppStateTracker.Listener;
 
 /**
  * Alarm manager implementaion.
@@ -249,7 +250,7 @@
     private final SparseArray<AlarmManager.AlarmClockInfo> mHandlerSparseAlarmClockArray =
             new SparseArray<>();
 
-    private final ForceAppStandbyTracker mForceAppStandbyTracker;
+    private AppStateTracker mAppStateTracker;
     private boolean mAppStandbyParole;
     private ArrayMap<Pair<String, Integer>, Long> mLastAlarmDeliveredForPackage = new ArrayMap<>();
 
@@ -707,9 +708,6 @@
         super(context);
         mConstants = new Constants(mHandler);
 
-        mForceAppStandbyTracker = ForceAppStandbyTracker.getInstance(context);
-        mForceAppStandbyTracker.addListener(mForceAppStandbyListener);
-
         publishLocalService(AlarmManagerInternal.class, new LocalService());
     }
 
@@ -1329,13 +1327,15 @@
     @Override
     public void onBootPhase(int phase) {
         if (phase == PHASE_SYSTEM_SERVICES_READY) {
-            mForceAppStandbyTracker.start();
             mConstants.start(getContext().getContentResolver());
             mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
             mLocalDeviceIdleController
                     = LocalServices.getService(DeviceIdleController.LocalService.class);
             mUsageStatsManagerInternal = LocalServices.getService(UsageStatsManagerInternal.class);
             mUsageStatsManagerInternal.addAppIdleStateChangeListener(new AppStandbyTracker());
+
+            mAppStateTracker = LocalServices.getService(AppStateTracker.class);
+            mAppStateTracker.addListener(mForceAppStandbyListener);
         }
     }
 
@@ -1729,7 +1729,8 @@
             // timing restrictions.
             } else if (workSource == null && (callingUid < Process.FIRST_APPLICATION_UID
                     || callingUid == mSystemUiUid
-                    || mForceAppStandbyTracker.isUidPowerSaveWhitelisted(callingUid))) {
+                    || (mAppStateTracker != null
+                        && mAppStateTracker.isUidPowerSaveWhitelisted(callingUid)))) {
                 flags |= AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
                 flags &= ~AlarmManager.FLAG_ALLOW_WHILE_IDLE;
             }
@@ -1812,8 +1813,10 @@
             mConstants.dump(pw);
             pw.println();
 
-            mForceAppStandbyTracker.dump(pw, "  ");
-            pw.println();
+            if (mAppStateTracker != null) {
+                mAppStateTracker.dump(pw, "  ");
+                pw.println();
+            }
 
             pw.println("  App Standby Parole: " + mAppStandbyParole);
             pw.println();
@@ -2161,8 +2164,10 @@
 
             mConstants.dumpProto(proto, AlarmManagerServiceProto.SETTINGS);
 
-            mForceAppStandbyTracker.dumpProto(proto,
-                    AlarmManagerServiceProto.FORCE_APP_STANDBY_TRACKER);
+            if (mAppStateTracker != null) {
+                mAppStateTracker.dumpProto(proto,
+                        AlarmManagerServiceProto.FORCE_APP_STANDBY_TRACKER);
+            }
 
             proto.write(AlarmManagerServiceProto.IS_INTERACTIVE, mInteractive);
             if (!mInteractive) {
@@ -2942,7 +2947,7 @@
         }
         final String sourcePackage = alarm.sourcePackage;
         final int sourceUid = alarm.creatorUid;
-        return mForceAppStandbyTracker.areAlarmsRestricted(sourceUid, sourcePackage,
+        return mAppStateTracker.areAlarmsRestricted(sourceUid, sourcePackage,
                 allowWhileIdle);
     }
 
@@ -2955,7 +2960,7 @@
 
     private long getWhileIdleMinIntervalLocked(int uid) {
         final boolean dozing = mPendingIdleUntil != null;
-        final boolean ebs = mForceAppStandbyTracker.isForceAllAppsStandbyEnabled();
+        final boolean ebs = mAppStateTracker.isForceAllAppsStandbyEnabled();
         if (!dozing && !ebs) {
             return mConstants.ALLOW_WHILE_IDLE_SHORT_TIME;
         }
@@ -4133,7 +4138,7 @@
             if (allowWhileIdle) {
                 // Record the last time this uid handled an ALLOW_WHILE_IDLE alarm.
                 mLastAllowWhileIdleDispatch.put(alarm.creatorUid, nowELAPSED);
-                if (mForceAppStandbyTracker.isUidInForeground(alarm.creatorUid)) {
+                if (mAppStateTracker.isUidInForeground(alarm.creatorUid)) {
                     mUseAllowWhileIdleShortTime.put(alarm.creatorUid, true);
                 } else {
                     mUseAllowWhileIdleShortTime.put(alarm.creatorUid, false);
diff --git a/services/core/java/com/android/server/ForceAppStandbyTracker.java b/services/core/java/com/android/server/AppStateTracker.java
similarity index 95%
rename from services/core/java/com/android/server/ForceAppStandbyTracker.java
rename to services/core/java/com/android/server/AppStateTracker.java
index 100680d..51dff56 100644
--- a/services/core/java/com/android/server/ForceAppStandbyTracker.java
+++ b/services/core/java/com/android/server/AppStateTracker.java
@@ -54,7 +54,6 @@
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.Preconditions;
-import com.android.server.DeviceIdleController.LocalService;
 import com.android.server.ForceAppStandbyTrackerProto.ExemptedPackage;
 import com.android.server.ForceAppStandbyTrackerProto.RunAnyInBackgroundRestrictedPackages;
 
@@ -73,14 +72,14 @@
  * TODO: Make it a LocalService.
  *
  * Test:
-  atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
+  atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java
  */
-public class ForceAppStandbyTracker {
+public class AppStateTracker {
     private static final String TAG = "ForceAppStandbyTracker";
     private static final boolean DEBUG = true;
 
-    @GuardedBy("ForceAppStandbyTracker.class")
-    private static ForceAppStandbyTracker sInstance;
+    @GuardedBy("AppStateTracker.class")
+    private static AppStateTracker sInstance;
 
     private final Object mLock = new Object();
     private final Context mContext;
@@ -89,6 +88,7 @@
     static final int TARGET_OP = AppOpsManager.OP_RUN_ANY_IN_BACKGROUND;
 
     IActivityManager mIActivityManager;
+    ActivityManagerInternal mActivityManagerInternal;
     AppOpsManager mAppOpsManager;
     IAppOpsService mAppOpsService;
     PowerManagerInternal mPowerManagerInternal;
@@ -172,6 +172,9 @@
         int EXEMPT_CHANGED = 6;
         int FORCE_ALL_CHANGED = 7;
         int FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED = 8;
+
+        int IS_UID_ACTIVE_CACHED = 9;
+        int IS_UID_ACTIVE_RAW = 10;
     }
 
     private final StatLogger mStatLogger = new StatLogger(new String[] {
@@ -184,6 +187,9 @@
             "EXEMPT_CHANGED",
             "FORCE_ALL_CHANGED",
             "FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED",
+
+            "IS_UID_ACTIVE_CACHED",
+            "IS_UID_ACTIVE_RAW",
     });
 
     @VisibleForTesting
@@ -249,7 +255,7 @@
         /**
          * This is called when the OP_RUN_ANY_IN_BACKGROUND appops changed for a package.
          */
-        private void onRunAnyAppOpsChanged(ForceAppStandbyTracker sender,
+        private void onRunAnyAppOpsChanged(AppStateTracker sender,
                 int uid, @NonNull String packageName) {
             updateJobsForUidPackage(uid, packageName);
 
@@ -264,14 +270,14 @@
         /**
          * This is called when the foreground state changed for a UID.
          */
-        private void onUidForegroundStateChanged(ForceAppStandbyTracker sender, int uid) {
+        private void onUidForegroundStateChanged(AppStateTracker sender, int uid) {
             onUidForeground(uid, sender.isUidInForeground(uid));
         }
 
         /**
          * This is called when the active/idle state changed for a UID.
          */
-        private void onUidActiveStateChanged(ForceAppStandbyTracker sender, int uid) {
+        private void onUidActiveStateChanged(AppStateTracker sender, int uid) {
             updateJobsForUid(uid);
 
             if (sender.isUidActive(uid)) {
@@ -282,7 +288,7 @@
         /**
          * This is called when an app-id(s) is removed from the power save whitelist.
          */
-        private void onPowerSaveUnwhitelisted(ForceAppStandbyTracker sender) {
+        private void onPowerSaveUnwhitelisted(AppStateTracker sender) {
             updateAllJobs();
             unblockAllUnrestrictedAlarms();
         }
@@ -291,14 +297,14 @@
          * This is called when the power save whitelist changes, excluding the
          * {@link #onPowerSaveUnwhitelisted} case.
          */
-        private void onPowerSaveWhitelistedChanged(ForceAppStandbyTracker sender) {
+        private void onPowerSaveWhitelistedChanged(AppStateTracker sender) {
             updateAllJobs();
         }
 
         /**
          * This is called when the temp whitelist changes.
          */
-        private void onTempPowerSaveWhitelistChanged(ForceAppStandbyTracker sender) {
+        private void onTempPowerSaveWhitelistChanged(AppStateTracker sender) {
 
             // TODO This case happens rather frequently; consider optimizing and update jobs
             // only for affected app-ids.
@@ -311,7 +317,7 @@
         /**
          * This is called when the EXEMPT bucket is updated.
          */
-        private void onExemptChanged(ForceAppStandbyTracker sender) {
+        private void onExemptChanged(AppStateTracker sender) {
             // This doesn't happen very often, so just re-evaluate all jobs / alarms.
             updateAllJobs();
             unblockAllUnrestrictedAlarms();
@@ -320,7 +326,7 @@
         /**
          * This is called when the global "force all apps standby" flag changes.
          */
-        private void onForceAllAppsStandbyChanged(ForceAppStandbyTracker sender) {
+        private void onForceAllAppsStandbyChanged(AppStateTracker sender) {
             updateAllJobs();
 
             if (!sender.isForceAllAppsStandbyEnabled()) {
@@ -377,30 +383,15 @@
         }
     }
 
-    @VisibleForTesting
-    ForceAppStandbyTracker(Context context, Looper looper) {
+    public AppStateTracker(Context context, Looper looper) {
         mContext = context;
         mHandler = new MyHandler(looper);
     }
 
-    private ForceAppStandbyTracker(Context context) {
-        this(context, FgThread.get().getLooper());
-    }
-
-    /**
-     * Get the singleton instance.
-     */
-    public static synchronized ForceAppStandbyTracker getInstance(Context context) {
-        if (sInstance == null) {
-            sInstance = new ForceAppStandbyTracker(context);
-        }
-        return sInstance;
-    }
-
     /**
      * Call it when the system is ready.
      */
-    public void start() {
+    public void onSystemServicesReady() {
         synchronized (mLock) {
             if (mStarted) {
                 return;
@@ -408,6 +399,7 @@
             mStarted = true;
 
             mIActivityManager = Preconditions.checkNotNull(injectIActivityManager());
+            mActivityManagerInternal = Preconditions.checkNotNull(injectActivityManagerInternal());
             mAppOpsManager = Preconditions.checkNotNull(injectAppOpsManager());
             mAppOpsService = Preconditions.checkNotNull(injectIAppOpsService());
             mPowerManagerInternal = Preconditions.checkNotNull(injectPowerManagerInternal());
@@ -475,6 +467,11 @@
     }
 
     @VisibleForTesting
+    ActivityManagerInternal injectActivityManagerInternal() {
+        return LocalServices.getService(ActivityManagerInternal.class);
+    }
+
+    @VisibleForTesting
     PowerManagerInternal injectPowerManagerInternal() {
         return LocalServices.getService(PowerManagerInternal.class);
     }
@@ -799,7 +796,7 @@
                     return;
                 }
             }
-            final ForceAppStandbyTracker sender = ForceAppStandbyTracker.this;
+            final AppStateTracker sender = AppStateTracker.this;
 
             long start = mStatLogger.getTime();
             switch (msg.what) {
@@ -1089,11 +1086,11 @@
     }
 
     /**
-     * @return whether a UID is in active or not.
+     * @return whether a UID is in active or not *based on cached information.*
      *
      * Note this information is based on the UID proc state callback, meaning it's updated
      * asynchronously and may subtly be stale. If the fresh data is needed, use
-     * {@link ActivityManagerInternal#getUidProcessState} instead.
+     * {@link #isUidActiveSynced} instead.
      */
     public boolean isUidActive(int uid) {
         if (UserHandle.isCore(uid)) {
@@ -1105,6 +1102,23 @@
     }
 
     /**
+     * @return whether a UID is in active or not *right now.*
+     *
+     * This gives the fresh information, but may access the activity manager so is slower.
+     */
+    public boolean isUidActiveSynced(int uid) {
+        if (isUidActive(uid)) { // Use the cached one first.
+            return true;
+        }
+        final long start = mStatLogger.getTime();
+
+        final boolean ret = mActivityManagerInternal.isUidActive(uid);
+        mStatLogger.logDurationStat(Stats.IS_UID_ACTIVE_RAW, start);
+
+        return ret;
+    }
+
+    /**
      * @return whether a UID is in the foreground or not.
      *
      * Note this information is based on the UID proc state callback, meaning it's updated
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 44974ff..2b3c585 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -82,6 +82,7 @@
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.Preconditions;
 import com.android.internal.util.XmlUtils;
 import com.android.server.am.BatteryStatsService;
 import com.android.server.net.NetworkPolicyManagerInternal;
@@ -128,6 +129,7 @@
     private Intent mIdleIntent;
     private Intent mLightIdleIntent;
     private AnyMotionDetector mAnyMotionDetector;
+    private final AppStateTracker mAppStateTracker;
     private boolean mLightEnabled;
     private boolean mDeepEnabled;
     private boolean mForceIdle;
@@ -1371,6 +1373,8 @@
         super(context);
         mConfigFile = new AtomicFile(new File(getSystemDir(), "deviceidle.xml"));
         mHandler = new MyHandler(BackgroundThread.getHandler().getLooper());
+        mAppStateTracker = new AppStateTracker(context, FgThread.get().getLooper());
+        LocalServices.addService(AppStateTracker.class, mAppStateTracker);
     }
 
     boolean isAppOnWhitelistInternal(int appid) {
@@ -1501,6 +1505,8 @@
                         (PowerManager) getContext().getSystemService(Context.POWER_SERVICE),
                         mHandler, mSensorManager, this, angleThreshold);
 
+                mAppStateTracker.onSystemServicesReady();
+
                 mIdleIntent = new Intent(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
                 mIdleIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
                         | Intent.FLAG_RECEIVER_FOREGROUND);
@@ -2615,7 +2621,7 @@
     }
 
     private void passWhiteListToForceAppStandbyTrackerLocked() {
-        ForceAppStandbyTracker.getInstance(getContext()).setPowerSaveWhitelistAppIds(
+        mAppStateTracker.setPowerSaveWhitelistAppIds(
                 mPowerSaveWhitelistExceptIdleAppIdArray,
                 mTempWhitelistAppIdArray);
     }
diff --git a/services/core/java/com/android/server/StatLogger.java b/services/core/java/com/android/server/StatLogger.java
index f211731..0e6f5e2 100644
--- a/services/core/java/com/android/server/StatLogger.java
+++ b/services/core/java/com/android/server/StatLogger.java
@@ -17,6 +17,7 @@
 package com.android.server;
 
 import android.os.SystemClock;
+import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.GuardedBy;
@@ -33,6 +34,8 @@
  * @hide
  */
 public class StatLogger {
+    private static final String TAG = "StatLogger";
+
     private final Object mLock = new Object();
 
     private final int SIZE;
@@ -66,8 +69,12 @@
      */
     public void logDurationStat(int eventId, long start) {
         synchronized (mLock) {
-            mCountStats[eventId]++;
-            mDurationStats[eventId] += (getTime() - start);
+            if (eventId >= 0 && eventId < SIZE) {
+                mCountStats[eventId]++;
+                mDurationStats[eventId] += (getTime() - start);
+            } else {
+                Slog.wtf(TAG, "Invalid event ID: " + eventId);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index a6c4fc9..e278e38 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1854,7 +1854,6 @@
     static final int REPORT_MEM_USAGE_MSG = 33;
     static final int IMMERSIVE_MODE_LOCK_MSG = 37;
     static final int PERSIST_URI_GRANTS_MSG = 38;
-    static final int REQUEST_ALL_PSS_MSG = 39;
     static final int UPDATE_TIME_PREFERENCE_MSG = 41;
     static final int ENTER_ANIMATION_COMPLETE_MSG = 44;
     static final int FINISH_BOOTING_MSG = 45;
@@ -2320,12 +2319,6 @@
                 writeGrantedUriPermissions();
                 break;
             }
-            case REQUEST_ALL_PSS_MSG: {
-                synchronized (ActivityManagerService.this) {
-                    requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false);
-                }
-                break;
-            }
             case UPDATE_TIME_PREFERENCE_MSG: {
                 // The user's time format preference might have changed.
                 // For convenience we re-use the Intent extra values.
@@ -2615,11 +2608,17 @@
                         procState = proc.pssProcState;
                         statType = proc.pssStatType;
                         lastPssTime = proc.lastPssTime;
+                        long now = SystemClock.uptimeMillis();
                         if (proc.thread != null && procState == proc.setProcState
                                 && (lastPssTime+ProcessList.PSS_SAFE_TIME_FROM_STATE_CHANGE)
-                                        < SystemClock.uptimeMillis()) {
+                                        < now) {
                             pid = proc.pid;
                         } else {
+                            ProcessList.abortNextPssTime(proc.procStateMemTracker);
+                            if (DEBUG_PSS) Slog.d(TAG_PSS, "Skipped pss collection of " + pid +
+                                    ": still need " +
+                                    (lastPssTime+ProcessList.PSS_SAFE_TIME_FROM_STATE_CHANGE-now) +
+                                    "ms until safe");
                             proc = null;
                             pid = 0;
                         }
@@ -3005,6 +3004,16 @@
 
         Watchdog.getInstance().addMonitor(this);
         Watchdog.getInstance().addThread(mHandler);
+
+        // bind background thread to little cores
+        // this is expected to fail inside of framework tests because apps can't touch cpusets directly
+        try {
+            Process.setThreadGroupAndCpuset(BackgroundThread.get().getThreadId(),
+                    Process.THREAD_GROUP_BG_NONINTERACTIVE);
+        } catch (Exception e) {
+            Slog.w(TAG, "Setting background thread cpuset failed");
+        }
+
     }
 
     protected ActivityStackSupervisor createStackSupervisor() {
@@ -12651,6 +12660,9 @@
         if (!mBooted && !mBooting
                 && userId == UserHandle.USER_SYSTEM
                 && (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
+            // The system process is initialized to SCHED_GROUP_DEFAULT in init.rc.
+            r.curSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+            r.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
             r.persistent = true;
             r.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
         }
@@ -14480,9 +14492,6 @@
                             mTestPssMode, isSleepingLocked(), now);
                 }
             }
-
-            mHandler.removeMessages(REQUEST_ALL_PSS_MSG);
-            mHandler.sendEmptyMessageDelayed(REQUEST_ALL_PSS_MSG, 2*60*1000);
         }
     }
 
@@ -19529,6 +19538,7 @@
 
         mProcessesToGc.remove(app);
         mPendingPssProcesses.remove(app);
+        ProcessList.abortNextPssTime(app.procStateMemTracker);
 
         // Dismiss any open dialogs.
         if (app.crashDialog != null && !app.forceCrashReport) {
@@ -23293,9 +23303,9 @@
     /**
      * Schedule PSS collection of a process.
      */
-    void requestPssLocked(ProcessRecord proc, int procState) {
+    boolean requestPssLocked(ProcessRecord proc, int procState) {
         if (mPendingPssProcesses.contains(proc)) {
-            return;
+            return false;
         }
         if (mPendingPssProcesses.size() == 0) {
             mBgHandler.sendEmptyMessage(COLLECT_PSS_BG_MSG);
@@ -23304,6 +23314,7 @@
         proc.pssProcState = procState;
         proc.pssStatType = ProcessStats.ADD_PSS_INTERNAL_SINGLE;
         mPendingPssProcesses.add(proc);
+        return true;
     }
 
     /**
@@ -23320,6 +23331,9 @@
         if (DEBUG_PSS) Slog.d(TAG_PSS, "Requesting pss of all procs!  memLowered=" + memLowered);
         mLastFullPssTime = now;
         mFullPssPending = true;
+        for (int i = mPendingPssProcesses.size() - 1; i >= 0; i--) {
+            ProcessList.abortNextPssTime(mPendingPssProcesses.get(i).procStateMemTracker);;
+        }
         mPendingPssProcesses.ensureCapacity(mLruProcesses.size());
         mPendingPssProcesses.clear();
         for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
@@ -23328,7 +23342,9 @@
                     || app.curProcState == ActivityManager.PROCESS_STATE_NONEXISTENT) {
                 continue;
             }
-            if (memLowered || now > (app.lastStateTime+ProcessList.PSS_ALL_INTERVAL)) {
+            if (memLowered || (always && now >
+                            app.lastStateTime+ProcessList.PSS_SAFE_TIME_FROM_STATE_CHANGE)
+                    || now > (app.lastStateTime+ProcessList.PSS_ALL_INTERVAL)) {
                 app.pssProcState = app.setProcState;
                 app.pssStatType = always ? ProcessStats.ADD_PSS_INTERNAL_ALL_POLL
                         : ProcessStats.ADD_PSS_INTERNAL_ALL_MEM;
@@ -23337,7 +23353,9 @@
                 mPendingPssProcesses.add(app);
             }
         }
-        mBgHandler.sendEmptyMessage(COLLECT_PSS_BG_MSG);
+        if (!mBgHandler.hasMessages(COLLECT_PSS_BG_MSG)) {
+            mBgHandler.sendEmptyMessage(COLLECT_PSS_BG_MSG);
+        }
     }
 
     public void setTestPssMode(boolean enabled) {
@@ -23695,7 +23713,7 @@
                 // Experimental code to more aggressively collect pss while
                 // running test...  the problem is that this tends to collect
                 // the data right when a process is transitioning between process
-                // states, which well tend to give noisy data.
+                // states, which will tend to give noisy data.
                 long start = SystemClock.uptimeMillis();
                 long startTime = SystemClock.currentThreadTimeMillis();
                 long pss = Debug.getPss(app.pid, mTmpLong, null);
@@ -23718,9 +23736,10 @@
             if (now > app.nextPssTime || (now > (app.lastPssTime+ProcessList.PSS_MAX_INTERVAL)
                     && now > (app.lastStateTime+ProcessList.minTimeFromStateChange(
                     mTestPssMode)))) {
-                requestPssLocked(app, app.setProcState);
-                app.nextPssTime = ProcessList.computeNextPssTime(app.curProcState,
-                        app.procStateMemTracker, mTestPssMode, isSleepingLocked(), now);
+                if (requestPssLocked(app, app.setProcState)) {
+                    app.nextPssTime = ProcessList.computeNextPssTime(app.curProcState,
+                            app.procStateMemTracker, mTestPssMode, isSleepingLocked(), now);
+                }
             } else if (false && DEBUG_PSS) Slog.d(TAG_PSS,
                     "Not requesting pss of " + app + ": next=" + (app.nextPssTime-now));
         }
@@ -25863,6 +25882,14 @@
         public boolean isCallerRecents(int callingUid) {
             return getRecentTasks().isCallerRecents(callingUid);
         }
+
+        @Override
+        public boolean isUidActive(int uid) {
+            synchronized (ActivityManagerService.this) {
+                final UidRecord uidRec = mActiveUids.get(uid);
+                return (uidRec != null) && !uidRec.idle;
+            }
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 08ee237..bf7aef9 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -501,19 +501,19 @@
     private static final int PSS_FIRST_CACHED_INTERVAL = 20*1000;
 
     // The amount of time until PSS when an important process stays in the same state.
-    private static final int PSS_SAME_PERSISTENT_INTERVAL = 20*60*1000;
+    private static final int PSS_SAME_PERSISTENT_INTERVAL = 10*60*1000;
 
     // The amount of time until PSS when the top process stays in the same state.
-    private static final int PSS_SAME_TOP_INTERVAL = 5*60*1000;
+    private static final int PSS_SAME_TOP_INTERVAL = 1*60*1000;
 
     // The amount of time until PSS when an important process stays in the same state.
-    private static final int PSS_SAME_IMPORTANT_INTERVAL = 15*60*1000;
+    private static final int PSS_SAME_IMPORTANT_INTERVAL = 10*60*1000;
 
     // The amount of time until PSS when a service process stays in the same state.
-    private static final int PSS_SAME_SERVICE_INTERVAL = 20*60*1000;
+    private static final int PSS_SAME_SERVICE_INTERVAL = 5*60*1000;
 
     // The amount of time until PSS when a cached process stays in the same state.
-    private static final int PSS_SAME_CACHED_INTERVAL = 20*60*1000;
+    private static final int PSS_SAME_CACHED_INTERVAL = 10*60*1000;
 
     // The amount of time until PSS when a persistent process first appears.
     private static final int PSS_FIRST_ASLEEP_PERSISTENT_INTERVAL = 1*60*1000;
@@ -622,16 +622,17 @@
 
     public static final class ProcStateMemTracker {
         final int[] mHighestMem = new int[PROC_MEM_NUM];
+        final float[] mScalingFactor = new float[PROC_MEM_NUM];
         int mTotalHighestMem = PROC_MEM_CACHED;
-        float mCurFactor = 1.0f;
 
         int mPendingMemState;
         int mPendingHighestMemState;
-        boolean mPendingSame;
+        float mPendingScalingFactor;
 
         public ProcStateMemTracker() {
             for (int i = PROC_MEM_PERSISTENT; i < PROC_MEM_NUM; i++) {
                 mHighestMem[i] = PROC_MEM_NUM;
+                mScalingFactor[i] = 1.0f;
             }
             mPendingMemState = -1;
         }
@@ -639,16 +640,22 @@
         public void dumpLine(PrintWriter pw) {
             pw.print("best=");
             pw.print(mTotalHighestMem);
-            pw.print(" ");
-            pw.print(mCurFactor);
-            pw.print("x (");
+            pw.print(" (");
+            boolean needSep = false;
             for (int i = 0; i < PROC_MEM_NUM; i++) {
-                if (i != 0) {
-                    pw.print(", ");
+                if (mHighestMem[i] < PROC_MEM_NUM) {
+                    if (needSep) {
+                        pw.print(", ");
+                        needSep = false;
+                    }
+                    pw.print(i);
+                    pw.print("=");
+                    pw.print(mHighestMem[i]);
+                    pw.print(" ");
+                    pw.print(mScalingFactor[i]);
+                    pw.print("x");
+                    needSep = true;
                 }
-                pw.print(i);
-                pw.print("=");
-                pw.print(mHighestMem[i]);
             }
             pw.print(")");
             if (mPendingMemState >= 0) {
@@ -656,8 +663,9 @@
                 pw.print(mPendingMemState);
                 pw.print(" highest=");
                 pw.print(mPendingHighestMemState);
-                pw.print(" same=");
-                pw.print(mPendingSame);
+                pw.print(" ");
+                pw.print(mPendingScalingFactor);
+                pw.print("x");
             }
             pw.println();
         }
@@ -674,12 +682,8 @@
     public static void commitNextPssTime(ProcStateMemTracker tracker) {
         if (tracker.mPendingMemState >= 0) {
             tracker.mHighestMem[tracker.mPendingMemState] = tracker.mPendingHighestMemState;
+            tracker.mScalingFactor[tracker.mPendingMemState] = tracker.mPendingScalingFactor;
             tracker.mTotalHighestMem = tracker.mPendingHighestMemState;
-            if (tracker.mPendingSame) {
-                tracker.mCurFactor *= 1.5f;
-            } else {
-                tracker.mCurFactor = 1;
-            }
             tracker.mPendingMemState = -1;
         }
     }
@@ -691,6 +695,7 @@
     public static long computeNextPssTime(int procState, ProcStateMemTracker tracker, boolean test,
             boolean sleeping, long now) {
         boolean first;
+        float scalingFactor;
         final int memState = sProcStateToProcMem[procState];
         if (tracker != null) {
             final int highestMemState = memState < tracker.mTotalHighestMem
@@ -698,9 +703,15 @@
             first = highestMemState < tracker.mHighestMem[memState];
             tracker.mPendingMemState = memState;
             tracker.mPendingHighestMemState = highestMemState;
-            tracker.mPendingSame = !first;
+            if (first) {
+                tracker.mPendingScalingFactor = scalingFactor = 1.0f;
+            } else {
+                scalingFactor = tracker.mScalingFactor[memState];
+                tracker.mPendingScalingFactor = scalingFactor * 1.5f;
+            }
         } else {
             first = true;
+            scalingFactor = 1.0f;
         }
         final long[] table = test
                 ? (first
@@ -709,8 +720,7 @@
                 : (first
                 ? (sleeping ? sFirstAsleepPssTimes : sFirstAwakePssTimes)
                 : (sleeping ? sSameAsleepPssTimes : sSameAwakePssTimes));
-        long delay = (long)(table[memState] * (tracker != null && !first
-                ? tracker.mCurFactor : 1.0f));
+        long delay = (long)(table[memState] * scalingFactor);
         if (delay > PSS_MAX_INTERVAL) {
             delay = PSS_MAX_INTERVAL;
         }
diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java
index 8bf320e..edf565a 100644
--- a/services/core/java/com/android/server/am/ProcessStatsService.java
+++ b/services/core/java/com/android/server/am/ProcessStatsService.java
@@ -237,6 +237,7 @@
             if (commit) {
                 mProcessStats.resetSafely();
                 updateFile();
+                mAm.requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false);
             }
             mLastWriteTime = SystemClock.uptimeMillis();
             totalTime = SystemClock.uptimeMillis() - now;
@@ -784,12 +785,14 @@
                 } else if ("--reset".equals(arg)) {
                     synchronized (mAm) {
                         mProcessStats.resetSafely();
+                        mAm.requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false);
                         pw.println("Process stats reset.");
                         quit = true;
                     }
                 } else if ("--clear".equals(arg)) {
                     synchronized (mAm) {
                         mProcessStats.resetSafely();
+                        mAm.requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false);
                         ArrayList<String> files = getCommittedFiles(0, true, true);
                         if (files != null) {
                             for (int fi=0; fi<files.size(); fi++) {
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 401c05e..47a4fb2 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -40,7 +40,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.Intent.UriFlags;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
@@ -79,7 +78,7 @@
 import com.android.internal.util.Preconditions;
 import com.android.server.DeviceIdleController;
 import com.android.server.FgThread;
-import com.android.server.ForceAppStandbyTracker;
+import com.android.server.AppStateTracker;
 import com.android.server.LocalServices;
 import com.android.server.job.JobSchedulerServiceDumpProto.ActiveJob;
 import com.android.server.job.JobSchedulerServiceDumpProto.PendingJob;
@@ -184,7 +183,7 @@
     ActivityManagerInternal mActivityManagerInternal;
     IBatteryStats mBatteryStats;
     DeviceIdleController.LocalService mLocalDeviceIdleController;
-    final ForceAppStandbyTracker mForceAppStandbyTracker;
+    AppStateTracker mAppStateTracker;
 
     /**
      * Set to true once we are allowed to run third party apps.
@@ -787,20 +786,13 @@
     }
 
     /**
-     * Return whether an UID is in the foreground or not.
+     * Return whether an UID is active or idle.
      */
-    private boolean isUidInForeground(int uid) {
-        synchronized (mLock) {
-            if (mUidPriorityOverride.get(uid, 0) > 0) {
-                return true;
-            }
-        }
-        // Note UID observer may not be called in time, so we always check with the AM.
-        return mActivityManagerInternal.getUidProcessState(uid)
-                <= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+    private boolean isUidActive(int uid) {
+        return mAppStateTracker.isUidActiveSynced(uid);
     }
 
-    private final Predicate<Integer> mIsUidInForegroundPredicate = this::isUidInForeground;
+    private final Predicate<Integer> mIsUidActivePredicate = this::isUidActive;
 
     public int scheduleAsPackage(JobInfo job, JobWorkItem work, int uId, String packageName,
             int userId, String tag) {
@@ -826,7 +818,7 @@
 
                     // If any of work item is enqueued when the source is in the foreground,
                     // exempt the entire job.
-                    toCancel.maybeAddForegroundExemption(mIsUidInForegroundPredicate);
+                    toCancel.maybeAddForegroundExemption(mIsUidActivePredicate);
 
                     return JobScheduler.RESULT_SUCCESS;
                 }
@@ -838,7 +830,7 @@
             // Note if it's a sync job, this method is called on the handler so it's not exactly
             // the state when requestSync() was called, but that should be fine because of the
             // 1 minute foreground grace period.
-            jobStatus.maybeAddForegroundExemption(mIsUidInForegroundPredicate);
+            jobStatus.maybeAddForegroundExemption(mIsUidActivePredicate);
 
             if (DEBUG) Slog.d(TAG, "SCHEDULE: " + jobStatus.toShortString());
             // Jobs on behalf of others don't apply to the per-app job cap
@@ -1123,8 +1115,6 @@
         mDeviceIdleJobsController = DeviceIdleJobsController.get(this);
         mControllers.add(mDeviceIdleJobsController);
 
-        mForceAppStandbyTracker = ForceAppStandbyTracker.getInstance(context);
-
         // If the job store determined that it can't yet reschedule persisted jobs,
         // we need to start watching the clock.
         if (!mJobs.jobTimesInflatedValid()) {
@@ -1185,7 +1175,8 @@
         if (PHASE_SYSTEM_SERVICES_READY == phase) {
             mConstants.start(getContext().getContentResolver());
 
-            mForceAppStandbyTracker.start();
+            mAppStateTracker = Preconditions.checkNotNull(
+                    LocalServices.getService(AppStateTracker.class));
 
             // Register br for package removals and user removals.
             final IntentFilter filter = new IntentFilter();
diff --git a/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java b/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java
index 5eb7700..e8057fb 100644
--- a/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java
+++ b/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java
@@ -22,8 +22,10 @@
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 
-import com.android.server.ForceAppStandbyTracker;
-import com.android.server.ForceAppStandbyTracker.Listener;
+import com.android.internal.util.Preconditions;
+import com.android.server.AppStateTracker;
+import com.android.server.AppStateTracker.Listener;
+import com.android.server.LocalServices;
 import com.android.server.job.JobSchedulerService;
 import com.android.server.job.JobStore;
 import com.android.server.job.StateControllerProto;
@@ -42,8 +44,7 @@
 
     private final JobSchedulerService mJobSchedulerService;
 
-    private final ForceAppStandbyTracker mForceAppStandbyTracker;
-
+    private final AppStateTracker mAppStateTracker;
 
     public static BackgroundJobsController get(JobSchedulerService service) {
         synchronized (sCreationLock) {
@@ -59,10 +60,9 @@
         super(service, context, lock);
         mJobSchedulerService = service;
 
-        mForceAppStandbyTracker = ForceAppStandbyTracker.getInstance(context);
-
-        mForceAppStandbyTracker.addListener(mForceAppStandbyListener);
-        mForceAppStandbyTracker.start();
+        mAppStateTracker = Preconditions.checkNotNull(
+                LocalServices.getService(AppStateTracker.class));
+        mAppStateTracker.addListener(mForceAppStandbyListener);
     }
 
     @Override
@@ -79,7 +79,7 @@
     public void dumpControllerStateLocked(final PrintWriter pw, final int filterUid) {
         pw.println("BackgroundJobsController");
 
-        mForceAppStandbyTracker.dump(pw, "");
+        mAppStateTracker.dump(pw, "");
 
         pw.println("Job state:");
         mJobSchedulerService.getJobStore().forEachJob((jobStatus) -> {
@@ -92,16 +92,16 @@
             jobStatus.printUniqueId(pw);
             pw.print(" from ");
             UserHandle.formatUid(pw, uid);
-            pw.print(mForceAppStandbyTracker.isUidActive(uid) ? " active" : " idle");
-            if (mForceAppStandbyTracker.isUidPowerSaveWhitelisted(uid) ||
-                    mForceAppStandbyTracker.isUidTempPowerSaveWhitelisted(uid)) {
+            pw.print(mAppStateTracker.isUidActive(uid) ? " active" : " idle");
+            if (mAppStateTracker.isUidPowerSaveWhitelisted(uid) ||
+                    mAppStateTracker.isUidTempPowerSaveWhitelisted(uid)) {
                 pw.print(", whitelisted");
             }
             pw.print(": ");
             pw.print(sourcePkg);
 
             pw.print(" [RUN_ANY_IN_BACKGROUND ");
-            pw.print(mForceAppStandbyTracker.isRunAnyInBackgroundAppOpsAllowed(uid, sourcePkg)
+            pw.print(mAppStateTracker.isRunAnyInBackgroundAppOpsAllowed(uid, sourcePkg)
                     ? "allowed]" : "disallowed]");
 
             if ((jobStatus.satisfiedConstraints
@@ -118,7 +118,7 @@
         final long token = proto.start(fieldId);
         final long mToken = proto.start(StateControllerProto.BACKGROUND);
 
-        mForceAppStandbyTracker.dumpProto(proto,
+        mAppStateTracker.dumpProto(proto,
                 StateControllerProto.BackgroundJobsController.FORCE_APP_STANDBY_TRACKER);
 
         mJobSchedulerService.getJobStore().forEachJob((jobStatus) -> {
@@ -136,14 +136,14 @@
             proto.write(TrackedJob.SOURCE_PACKAGE_NAME, sourcePkg);
 
             proto.write(TrackedJob.IS_IN_FOREGROUND,
-                    mForceAppStandbyTracker.isUidActive(sourceUid));
+                    mAppStateTracker.isUidActive(sourceUid));
             proto.write(TrackedJob.IS_WHITELISTED,
-                    mForceAppStandbyTracker.isUidPowerSaveWhitelisted(sourceUid) ||
-                    mForceAppStandbyTracker.isUidTempPowerSaveWhitelisted(sourceUid));
+                    mAppStateTracker.isUidPowerSaveWhitelisted(sourceUid) ||
+                    mAppStateTracker.isUidTempPowerSaveWhitelisted(sourceUid));
 
             proto.write(
                     TrackedJob.CAN_RUN_ANY_IN_BACKGROUND,
-                    mForceAppStandbyTracker.isRunAnyInBackgroundAppOpsAllowed(
+                    mAppStateTracker.isRunAnyInBackgroundAppOpsAllowed(
                             sourceUid, sourcePkg));
 
             proto.write(
@@ -197,7 +197,7 @@
         final int uid = jobStatus.getSourceUid();
         final String packageName = jobStatus.getSourcePackageName();
 
-        final boolean canRun = !mForceAppStandbyTracker.areJobsRestricted(uid, packageName,
+        final boolean canRun = !mAppStateTracker.areJobsRestricted(uid, packageName,
                 (jobStatus.getInternalFlags() & JobStatus.INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION)
                         != 0);
 
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index a6f049e..a17dd12 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1722,7 +1722,7 @@
                 final long totalBytes = getTotalBytes(
                         NetworkTemplate.buildTemplateMobileAll(state.subscriberId), start, end);
                 final long remainingBytes = limitBytes - totalBytes;
-                final long remainingDays = Math.min(1, (end - currentTimeMillis())
+                final long remainingDays = Math.max(1, (end - currentTimeMillis())
                         / TimeUnit.DAYS.toMillis(1));
                 if (remainingBytes > 0) {
                     quotaBytes = (remainingBytes / remainingDays) / 10;
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index c361434..5ea113b 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -724,7 +724,6 @@
         MmsServiceBroker mmsService = null;
         HardwarePropertiesManagerService hardwarePropertiesService = null;
 
-        boolean disableRtt = SystemProperties.getBoolean("config.disable_rtt", false);
         boolean disableSystemTextClassifier = SystemProperties.getBoolean(
                 "config.disable_systemtextclassifier", false);
         boolean disableCameraService = SystemProperties.getBoolean("config.disable_cameraservice",
@@ -1105,18 +1104,12 @@
                 traceEnd();
             }
 
-            if (!disableRtt) {
-                traceBeginAndSlog("StartWifiRtt");
-                mSystemServiceManager.startService("com.android.server.wifi.RttService");
+            if (context.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_WIFI_RTT)) {
+                traceBeginAndSlog("StartRttService");
+                mSystemServiceManager.startService(
+                    "com.android.server.wifi.rtt.RttService");
                 traceEnd();
-
-                if (context.getPackageManager().hasSystemFeature(
-                    PackageManager.FEATURE_WIFI_RTT)) {
-                    traceBeginAndSlog("StartRttService");
-                    mSystemServiceManager.startService(
-                        "com.android.server.wifi.rtt.RttService");
-                    traceEnd();
-                }
             }
 
             if (context.getPackageManager().hasSystemFeature(
diff --git a/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java b/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java
similarity index 93%
rename from services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
rename to services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java
index a499472..90db2a3 100644
--- a/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java
@@ -15,7 +15,7 @@
  */
 package com.android.server;
 
-import static com.android.server.ForceAppStandbyTracker.TARGET_OP;
+import static com.android.server.AppStateTracker.TARGET_OP;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -33,6 +33,7 @@
 import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
 import android.app.AppOpsManager;
 import android.app.AppOpsManager.OpEntry;
 import android.app.AppOpsManager.PackageOps;
@@ -63,7 +64,7 @@
 
 import com.android.internal.app.IAppOpsCallback;
 import com.android.internal.app.IAppOpsService;
-import com.android.server.ForceAppStandbyTracker.Listener;
+import com.android.server.AppStateTracker.Listener;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -82,17 +83,17 @@
 import java.util.function.Consumer;
 
 /**
- * Tests for {@link ForceAppStandbyTracker}
+ * Tests for {@link AppStateTracker}
  *
  * Run with:
- atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
+ atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java
  */
 @SmallTest
 @RunWith(AndroidJUnit4.class)
-public class ForceAppStandbyTrackerTest {
+public class AppStateTrackerTest {
 
-    private class ForceAppStandbyTrackerTestable extends ForceAppStandbyTracker {
-        ForceAppStandbyTrackerTestable() {
+    private class AppStateTrackerTestable extends AppStateTracker {
+        AppStateTrackerTestable() {
             super(mMockContext, Looper.getMainLooper());
         }
 
@@ -112,6 +113,11 @@
         }
 
         @Override
+        ActivityManagerInternal injectActivityManagerInternal() {
+            return mMockIActivityManagerInternal;
+        }
+
+        @Override
         PowerManagerInternal injectPowerManagerInternal() {
             return mMockPowerManagerInternal;
         }
@@ -152,6 +158,9 @@
     private IActivityManager mMockIActivityManager;
 
     @Mock
+    private ActivityManagerInternal mMockIActivityManagerInternal;
+
+    @Mock
     private AppOpsManager mMockAppOpsManager;
 
     @Mock
@@ -195,7 +204,7 @@
         return new PowerSaveState.Builder().setBatterySaverEnabled(mPowerSaveMode).build();
     }
 
-    private ForceAppStandbyTrackerTestable newInstance() throws Exception {
+    private AppStateTrackerTestable newInstance() throws Exception {
         MockitoAnnotations.initMocks(this);
 
         when(mMockIAppOpsService.checkOperation(eq(TARGET_OP), anyInt(), anyString()))
@@ -205,12 +214,12 @@
                             AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED;
                 });
 
-        final ForceAppStandbyTrackerTestable instance = new ForceAppStandbyTrackerTestable();
+        final AppStateTrackerTestable instance = new AppStateTrackerTestable();
 
         return instance;
     }
 
-    private void callStart(ForceAppStandbyTrackerTestable instance) throws RemoteException {
+    private void callStart(AppStateTrackerTestable instance) throws RemoteException {
 
         // Set up functions that start() calls.
         when(mMockPowerManagerInternal.getLowPowerState(eq(ServiceType.FORCE_ALL_APPS_STANDBY)))
@@ -223,7 +232,7 @@
         when(mMockContext.getContentResolver()).thenReturn(mMockContentResolver);
 
         // Call start.
-        instance.start();
+        instance.onSystemServicesReady();
 
         // Capture the listeners.
         ArgumentCaptor<IUidObserver> uidObserverArgumentCaptor =
@@ -287,7 +296,7 @@
     private static final int JOBS_ONLY = 1 << 1;
     private static final int JOBS_AND_ALARMS = ALARMS_ONLY | JOBS_ONLY;
 
-    private void areRestricted(ForceAppStandbyTrackerTestable instance, int uid, String packageName,
+    private void areRestricted(AppStateTrackerTestable instance, int uid, String packageName,
             int restrictionTypes, boolean exemptFromBatterySaver) {
         assertEquals(((restrictionTypes & JOBS_ONLY) != 0),
                 instance.areJobsRestricted(uid, packageName, exemptFromBatterySaver));
@@ -295,13 +304,13 @@
                 instance.areAlarmsRestricted(uid, packageName, exemptFromBatterySaver));
     }
 
-    private void areRestricted(ForceAppStandbyTrackerTestable instance, int uid, String packageName,
+    private void areRestricted(AppStateTrackerTestable instance, int uid, String packageName,
             int restrictionTypes) {
         areRestricted(instance, uid, packageName, restrictionTypes,
                 /*exemptFromBatterySaver=*/ false);
     }
 
-    private void areRestrictedWithExemption(ForceAppStandbyTrackerTestable instance,
+    private void areRestrictedWithExemption(AppStateTrackerTestable instance,
             int uid, String packageName, int restrictionTypes) {
         areRestricted(instance, uid, packageName, restrictionTypes,
                 /*exemptFromBatterySaver=*/ true);
@@ -309,7 +318,7 @@
 
     @Test
     public void testAll() throws Exception {
-        final ForceAppStandbyTrackerTestable instance = newInstance();
+        final AppStateTrackerTestable instance = newInstance();
         callStart(instance);
 
         assertFalse(instance.isForceAllAppsStandbyEnabled());
@@ -466,7 +475,7 @@
 
     @Test
     public void testUidStateForeground() throws Exception {
-        final ForceAppStandbyTrackerTestable instance = newInstance();
+        final AppStateTrackerTestable instance = newInstance();
         callStart(instance);
 
         mIUidObserver.onUidActive(UID_1);
@@ -476,6 +485,10 @@
         assertFalse(instance.isUidActive(UID_2));
         assertTrue(instance.isUidActive(Process.SYSTEM_UID));
 
+        assertTrue(instance.isUidActiveSynced(UID_1));
+        assertFalse(instance.isUidActiveSynced(UID_2));
+        assertTrue(instance.isUidActiveSynced(Process.SYSTEM_UID));
+
         assertFalse(instance.isUidInForeground(UID_1));
         assertFalse(instance.isUidInForeground(UID_2));
         assertTrue(instance.isUidInForeground(Process.SYSTEM_UID));
@@ -489,6 +502,10 @@
         assertFalse(instance.isUidActive(UID_2));
         assertTrue(instance.isUidActive(Process.SYSTEM_UID));
 
+        assertTrue(instance.isUidActiveSynced(UID_1));
+        assertFalse(instance.isUidActiveSynced(UID_2));
+        assertTrue(instance.isUidActiveSynced(Process.SYSTEM_UID));
+
         assertFalse(instance.isUidInForeground(UID_1));
         assertTrue(instance.isUidInForeground(UID_2));
         assertTrue(instance.isUidInForeground(Process.SYSTEM_UID));
@@ -548,14 +565,34 @@
         assertFalse(instance.isUidActive(UID_2));
         assertTrue(instance.isUidActive(Process.SYSTEM_UID));
 
+        assertFalse(instance.isUidActiveSynced(UID_1));
+        assertFalse(instance.isUidActiveSynced(UID_2));
+        assertTrue(instance.isUidActiveSynced(Process.SYSTEM_UID));
+
         assertFalse(instance.isUidInForeground(UID_1));
         assertFalse(instance.isUidInForeground(UID_2));
         assertTrue(instance.isUidInForeground(Process.SYSTEM_UID));
+
+        // The result from AMI.isUidActive() only affects isUidActiveSynced().
+        when(mMockIActivityManagerInternal.isUidActive(anyInt())).thenReturn(true);
+
+        assertFalse(instance.isUidActive(UID_1));
+        assertFalse(instance.isUidActive(UID_2));
+        assertTrue(instance.isUidActive(Process.SYSTEM_UID));
+
+        assertTrue(instance.isUidActiveSynced(UID_1));
+        assertTrue(instance.isUidActiveSynced(UID_2));
+        assertTrue(instance.isUidActiveSynced(Process.SYSTEM_UID));
+
+        assertFalse(instance.isUidInForeground(UID_1));
+        assertFalse(instance.isUidInForeground(UID_2));
+        assertTrue(instance.isUidInForeground(Process.SYSTEM_UID));
+
     }
 
     @Test
     public void testExempt() throws Exception {
-        final ForceAppStandbyTrackerTestable instance = newInstance();
+        final AppStateTrackerTestable instance = newInstance();
         callStart(instance);
 
         assertFalse(instance.isForceAllAppsStandbyEnabled());
@@ -621,7 +658,7 @@
     }
 
     public void loadPersistedAppOps() throws Exception {
-        final ForceAppStandbyTrackerTestable instance = newInstance();
+        final AppStateTrackerTestable instance = newInstance();
 
         final List<PackageOps> ops = new ArrayList<>();
 
@@ -631,7 +668,7 @@
                 AppOpsManager.OP_ACCESS_NOTIFICATIONS,
                 AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, null));
         entries.add(new AppOpsManager.OpEntry(
-                ForceAppStandbyTracker.TARGET_OP,
+                AppStateTracker.TARGET_OP,
                 AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, null));
 
         ops.add(new PackageOps(PACKAGE_1, UID_1, entries));
@@ -639,7 +676,7 @@
         //--------------------------------------------------
         entries = new ArrayList<>();
         entries.add(new AppOpsManager.OpEntry(
-                ForceAppStandbyTracker.TARGET_OP,
+                AppStateTracker.TARGET_OP,
                 AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, null));
 
         ops.add(new PackageOps(PACKAGE_2, UID_2, entries));
@@ -647,7 +684,7 @@
         //--------------------------------------------------
         entries = new ArrayList<>();
         entries.add(new AppOpsManager.OpEntry(
-                ForceAppStandbyTracker.TARGET_OP,
+                AppStateTracker.TARGET_OP,
                 AppOpsManager.MODE_ALLOWED, 0, 0, 0, 0, null));
 
         ops.add(new PackageOps(PACKAGE_1, UID_10_1, entries));
@@ -655,7 +692,7 @@
         //--------------------------------------------------
         entries = new ArrayList<>();
         entries.add(new AppOpsManager.OpEntry(
-                ForceAppStandbyTracker.TARGET_OP,
+                AppStateTracker.TARGET_OP,
                 AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, null));
         entries.add(new AppOpsManager.OpEntry(
                 AppOpsManager.OP_ACCESS_NOTIFICATIONS,
@@ -688,10 +725,10 @@
 
     @Test
     public void testPowerSaveListener() throws Exception {
-        final ForceAppStandbyTrackerTestable instance = newInstance();
+        final AppStateTrackerTestable instance = newInstance();
         callStart(instance);
 
-        ForceAppStandbyTracker.Listener l = mock(ForceAppStandbyTracker.Listener.class);
+        AppStateTracker.Listener l = mock(AppStateTracker.Listener.class);
         instance.addListener(l);
 
         // Power save on.
@@ -731,10 +768,10 @@
 
     @Test
     public void testAllListeners() throws Exception {
-        final ForceAppStandbyTrackerTestable instance = newInstance();
+        final AppStateTrackerTestable instance = newInstance();
         callStart(instance);
 
-        ForceAppStandbyTracker.Listener l = mock(ForceAppStandbyTracker.Listener.class);
+        AppStateTracker.Listener l = mock(AppStateTracker.Listener.class);
         instance.addListener(l);
 
         // -------------------------------------------------------------------------
@@ -1042,7 +1079,7 @@
 
     @Test
     public void testUserRemoved() throws Exception {
-        final ForceAppStandbyTrackerTestable instance = newInstance();
+        final AppStateTrackerTestable instance = newInstance();
         callStart(instance);
 
         mIUidObserver.onUidActive(UID_1);
@@ -1077,7 +1114,7 @@
         // This is a small battery device
         mIsSmallBatteryDevice = true;
 
-        final ForceAppStandbyTrackerTestable instance = newInstance();
+        final AppStateTrackerTestable instance = newInstance();
         callStart(instance);
         assertFalse(instance.isForceAllAppsStandbyEnabled());
 
@@ -1103,7 +1140,7 @@
         // Not a small battery device, so plugged in status should not affect forced app standby
         mIsSmallBatteryDevice = false;
 
-        final ForceAppStandbyTrackerTestable instance = newInstance();
+        final AppStateTrackerTestable instance = newInstance();
         callStart(instance);
         assertFalse(instance.isForceAllAppsStandbyEnabled());
 
@@ -1152,7 +1189,7 @@
 
     private void checkAnyAppIdUnwhitelisted(int[] prevArray, int[] newArray, boolean expected) {
         assertEquals("Input: " + Arrays.toString(prevArray) + " " + Arrays.toString(newArray),
-                expected, ForceAppStandbyTracker.isAnyAppIdUnwhitelisted(prevArray, newArray));
+                expected, AppStateTracker.isAnyAppIdUnwhitelisted(prevArray, newArray));
 
         // Also test isAnyAppIdUnwhitelistedSlow.
         assertEquals("Input: " + Arrays.toString(prevArray) + " " + Arrays.toString(newArray),
@@ -1184,7 +1221,7 @@
             final int[] array2 = makeRandomArray();
 
             final boolean expected = isAnyAppIdUnwhitelistedSlow(array1, array2);
-            final boolean actual = ForceAppStandbyTracker.isAnyAppIdUnwhitelisted(array1, array2);
+            final boolean actual = AppStateTracker.isAnyAppIdUnwhitelisted(array1, array2);
 
             assertEquals("Input: " + Arrays.toString(array1) + " " + Arrays.toString(array2),
                     expected, actual);
diff --git a/telephony/java/android/telephony/UiccSlotInfo.java b/telephony/java/android/telephony/UiccSlotInfo.java
index 0b3cbad..0c17147 100644
--- a/telephony/java/android/telephony/UiccSlotInfo.java
+++ b/telephony/java/android/telephony/UiccSlotInfo.java
@@ -55,10 +55,11 @@
     /** Card state restricted. */
     public static final int CARD_STATE_INFO_RESTRICTED = 4;
 
-    public final boolean isActive;
-    public final boolean isEuicc;
-    public final String cardId;
-    public final @CardStateInfo int cardStateInfo;
+    private final boolean mIsActive;
+    private final boolean mIsEuicc;
+    private final String mCardId;
+    private final @CardStateInfo int mCardStateInfo;
+    private final int mLogicalSlotIdx;
 
     public static final Creator<UiccSlotInfo> CREATOR = new Creator<UiccSlotInfo>() {
         @Override
@@ -73,18 +74,20 @@
     };
 
     private UiccSlotInfo(Parcel in) {
-        isActive = in.readByte() != 0;
-        isEuicc = in.readByte() != 0;
-        cardId = in.readString();
-        cardStateInfo = in.readInt();
+        mIsActive = in.readByte() != 0;
+        mIsEuicc = in.readByte() != 0;
+        mCardId = in.readString();
+        mCardStateInfo = in.readInt();
+        mLogicalSlotIdx = in.readInt();
     }
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeByte((byte) (isActive ? 1 : 0));
-        dest.writeByte((byte) (isEuicc ? 1 : 0));
-        dest.writeString(cardId);
-        dest.writeInt(cardStateInfo);
+        dest.writeByte((byte) (mIsActive ? 1 : 0));
+        dest.writeByte((byte) (mIsEuicc ? 1 : 0));
+        dest.writeString(mCardId);
+        dest.writeInt(mCardStateInfo);
+        dest.writeInt(mLogicalSlotIdx);
     }
 
     @Override
@@ -93,28 +96,33 @@
     }
 
     public UiccSlotInfo(boolean isActive, boolean isEuicc, String cardId,
-            @CardStateInfo int cardStateInfo) {
-        this.isActive = isActive;
-        this.isEuicc = isEuicc;
-        this.cardId = cardId;
-        this.cardStateInfo = cardStateInfo;
+            @CardStateInfo int cardStateInfo, int logicalSlotIdx) {
+        this.mIsActive = isActive;
+        this.mIsEuicc = isEuicc;
+        this.mCardId = cardId;
+        this.mCardStateInfo = cardStateInfo;
+        this.mLogicalSlotIdx = logicalSlotIdx;
     }
 
     public boolean getIsActive() {
-        return isActive;
+        return mIsActive;
     }
 
     public boolean getIsEuicc() {
-        return isEuicc;
+        return mIsEuicc;
     }
 
     public String getCardId() {
-        return cardId;
+        return mCardId;
     }
 
     @CardStateInfo
     public int getCardStateInfo() {
-        return cardStateInfo;
+        return mCardStateInfo;
+    }
+
+    public int getLogicalSlotIdx() {
+        return mLogicalSlotIdx;
     }
 
     @Override
@@ -127,32 +135,36 @@
         }
 
         UiccSlotInfo that = (UiccSlotInfo) obj;
-        return (isActive == that.isActive)
-                && (isEuicc == that.isEuicc)
-                && (cardId == that.cardId)
-                && (cardStateInfo == that.cardStateInfo);
+        return (mIsActive == that.mIsActive)
+                && (mIsEuicc == that.mIsEuicc)
+                && (mCardId == that.mCardId)
+                && (mCardStateInfo == that.mCardStateInfo)
+                && (mLogicalSlotIdx == that.mLogicalSlotIdx);
     }
 
     @Override
     public int hashCode() {
         int result = 1;
-        result = 31 * result + (isActive ? 1 : 0);
-        result = 31 * result + (isEuicc ? 1 : 0);
-        result = 31 * result + Objects.hashCode(cardId);
-        result = 31 * result + cardStateInfo;
+        result = 31 * result + (mIsActive ? 1 : 0);
+        result = 31 * result + (mIsEuicc ? 1 : 0);
+        result = 31 * result + Objects.hashCode(mCardId);
+        result = 31 * result + mCardStateInfo;
+        result = 31 * result + mLogicalSlotIdx;
         return result;
     }
 
     @Override
     public String toString() {
-        return "UiccSlotInfo (isActive="
-                + isActive
-                + ", isEuicc="
-                + isEuicc
-                + ", cardId="
-                + cardId
+        return "UiccSlotInfo (mIsActive="
+                + mIsActive
+                + ", mIsEuicc="
+                + mIsEuicc
+                + ", mCardId="
+                + mCardId
                 + ", cardState="
-                + cardStateInfo
+                + mCardStateInfo
+                + ", phoneId="
+                + mLogicalSlotIdx
                 + ")";
     }
 }
diff --git a/wifi/java/android/net/wifi/IRttManager.aidl b/wifi/java/android/net/wifi/IRttManager.aidl
deleted file mode 100644
index 3831809..0000000
--- a/wifi/java/android/net/wifi/IRttManager.aidl
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi;
-import android.os.Messenger;
-import android.net.wifi.RttManager;
-
-/**
- * {@hide}
- */
-interface IRttManager
-{
-    Messenger getMessenger(in IBinder binder, out int[] key);
-    RttManager.RttCapabilities getRttCapabilities();
-}
diff --git a/wifi/java/android/net/wifi/RttManager.java b/wifi/java/android/net/wifi/RttManager.java
index fe63aa1..0b81dd2 100644
--- a/wifi/java/android/net/wifi/RttManager.java
+++ b/wifi/java/android/net/wifi/RttManager.java
@@ -12,7 +12,6 @@
 import android.net.wifi.rtt.RangingResult;
 import android.net.wifi.rtt.RangingResultCallback;
 import android.net.wifi.rtt.WifiRttManager;
-import android.os.Looper;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Log;
@@ -919,51 +918,6 @@
     }
 
     /**
-     * A parcelable that contains rtt client information.
-     *
-     * @hide
-     */
-    public static class RttClient implements Parcelable {
-        // Package name of RttClient.
-        private final String mPackageName;
-
-        public RttClient(String packageName) {
-            mPackageName = packageName;
-        }
-
-        protected RttClient(Parcel in) {
-            mPackageName = in.readString();
-        }
-
-        public static final Creator<RttManager.RttClient> CREATOR =
-                new Creator<RttManager.RttClient>() {
-            @Override
-            public RttManager.RttClient createFromParcel(Parcel in) {
-                return new RttManager.RttClient(in);
-            }
-
-            @Override
-            public RttManager.RttClient[] newArray(int size) {
-                return new RttManager.RttClient[size];
-            }
-        };
-
-        @Override
-        public int describeContents() {
-            return 0;
-        }
-
-        @Override
-        public void writeToParcel(Parcel parcel, int i) {
-            parcel.writeString(mPackageName);
-        }
-
-        public String getPackageName() {
-            return mPackageName;
-        }
-    }
-
-    /**
      * Request to start an RTT ranging
      *
      * @param params  -- RTT request Parameters
@@ -1202,7 +1156,6 @@
     /** @hide */
     public static final int CMD_OP_REG_BINDER           = BASE + 9;
 
-    private final Context mContext;
     private final WifiRttManager mNewService;
     private RttCapabilities mRttCapabilities;
 
@@ -1211,17 +1164,14 @@
      * Applications will almost always want to use
      * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
      * the standard {@link android.content.Context#WIFI_RTT_SERVICE Context.WIFI_RTT_SERVICE}.
-     * @param context the application context
-     * @param service the Binder interface
-     * @param looper Looper for running the callbacks.
+     * @param service the new WifiRttManager service
      *
      * @hide
      */
-    public RttManager(Context context, IRttManager service, Looper looper) {
-        mContext = context;
-        mNewService = (WifiRttManager) mContext.getSystemService(Context.WIFI_RTT_RANGING_SERVICE);
+    public RttManager(Context context, WifiRttManager service) {
+        mNewService = service;
 
-        boolean rttSupported = mContext.getPackageManager().hasSystemFeature(
+        boolean rttSupported = context.getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_WIFI_RTT);
 
         mRttCapabilities = new RttCapabilities();
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 7da2656..ddcf327 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -917,6 +917,9 @@
      * Does not guarantee that the returned address is valid for use.
      */
     public MacAddress getRandomizedMacAddress() {
+        if (mRandomizedMacAddress == null) {
+            mRandomizedMacAddress = MacAddress.ALL_ZEROS_ADDRESS;
+        }
         return mRandomizedMacAddress;
     }
 
@@ -1617,6 +1620,7 @@
         creatorUid = -1;
         shared = true;
         dtimInterval = 0;
+        mRandomizedMacAddress = MacAddress.ALL_ZEROS_ADDRESS;
     }
 
     /**
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index e7377c1..8a3a7f5 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -176,6 +176,8 @@
     @Test
     public void testGetOrCreateRandomizedMacAddress_SavesAndReturnsSameAddress() {
         WifiConfiguration config = new WifiConfiguration();
+        assertEquals(MacAddress.ALL_ZEROS_ADDRESS, config.getRandomizedMacAddress());
+
         MacAddress firstMacAddress = config.getOrCreateRandomizedMacAddress();
         MacAddress secondMacAddress = config.getOrCreateRandomizedMacAddress();
 
@@ -185,6 +187,8 @@
     @Test
     public void testSetRandomizedMacAddress_ChangesSavedAddress() {
         WifiConfiguration config = new WifiConfiguration();
+        assertEquals(MacAddress.ALL_ZEROS_ADDRESS, config.getRandomizedMacAddress());
+
         MacAddress macToChangeInto = MacAddress.createRandomUnicastAddress();
         config.setRandomizedMacAddress(macToChangeInto);
         MacAddress macAfterChange = config.getOrCreateRandomizedMacAddress();