Merge "Introduce OEM customization XML parser"
diff --git a/api/current.txt b/api/current.txt
index 87aaaa6..7d6c939 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -7,6 +7,7 @@
   public static final class Manifest.permission {
     ctor public Manifest.permission();
     field public static final java.lang.String ACCEPT_HANDOVER = "android.permission.ACCEPT_HANDOVER";
+    field public static final java.lang.String ACCESS_BACKGROUND_LOCATION = "android.permission.ACCESS_BACKGROUND_LOCATION";
     field public static final java.lang.String ACCESS_CHECKIN_PROPERTIES = "android.permission.ACCESS_CHECKIN_PROPERTIES";
     field public static final java.lang.String ACCESS_COARSE_LOCATION = "android.permission.ACCESS_COARSE_LOCATION";
     field public static final java.lang.String ACCESS_FINE_LOCATION = "android.permission.ACCESS_FINE_LOCATION";
@@ -32521,6 +32522,7 @@
 
   public class Build {
     ctor public Build();
+    method public static java.util.List<android.os.Build.Partition> getPartitions();
     method public static java.lang.String getRadioVersion();
     method public static java.lang.String getSerial();
     field public static final java.lang.String BOARD;
@@ -32549,6 +32551,14 @@
     field public static final java.lang.String USER;
   }
 
+  public static class Build.Partition {
+    ctor public Build.Partition();
+    method public java.lang.String getFingerprint();
+    method public java.lang.String getName();
+    method public long getTimeMillis();
+    field public static final java.lang.String PARTITION_NAME_SYSTEM = "system";
+  }
+
   public static class Build.VERSION {
     ctor public Build.VERSION();
     field public static final java.lang.String BASE_OS;
@@ -42568,7 +42578,7 @@
     method public android.telephony.mbms.StreamingService startStreaming(android.telephony.mbms.StreamingServiceInfo, java.util.concurrent.Executor, android.telephony.mbms.StreamingServiceCallback);
   }
 
-  public class NeighboringCellInfo implements android.os.Parcelable {
+  public deprecated class NeighboringCellInfo implements android.os.Parcelable {
     ctor public deprecated NeighboringCellInfo();
     ctor public deprecated NeighboringCellInfo(int, int);
     ctor public NeighboringCellInfo(int, java.lang.String, int);
@@ -43029,7 +43039,6 @@
     method public java.lang.String getMmsUAProfUrl();
     method public java.lang.String getMmsUserAgent();
     method public java.lang.String getNai();
-    method public deprecated java.util.List<android.telephony.NeighboringCellInfo> getNeighboringCellInfo();
     method public java.lang.String getNetworkCountryIso();
     method public java.lang.String getNetworkOperator();
     method public java.lang.String getNetworkOperatorName();
@@ -45696,6 +45705,7 @@
     method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
     method public V get(java.lang.Object);
     method public int indexOfKey(java.lang.Object);
+    method public int indexOfValue(java.lang.Object);
     method public boolean isEmpty();
     method public K keyAt(int);
     method public java.util.Set<K> keySet();
@@ -45716,6 +45726,7 @@
     ctor public ArraySet();
     ctor public ArraySet(int);
     ctor public ArraySet(android.util.ArraySet<E>);
+    ctor public ArraySet(java.util.Collection<? extends E>);
     method public boolean add(E);
     method public void addAll(android.util.ArraySet<? extends E>);
     method public boolean addAll(java.util.Collection<? extends E>);
@@ -46287,6 +46298,7 @@
     method public int keyAt(int);
     method public void put(int, boolean);
     method public void removeAt(int);
+    method public void setValueAt(int, boolean);
     method public int size();
     method public boolean valueAt(int);
   }
@@ -46305,6 +46317,7 @@
     method public int keyAt(int);
     method public void put(int, int);
     method public void removeAt(int);
+    method public void setValueAt(int, int);
     method public int size();
     method public int valueAt(int);
   }
diff --git a/api/removed.txt b/api/removed.txt
index b6dabcd..f7106d2 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -545,6 +545,7 @@
   }
 
   public class TelephonyManager {
+    method public deprecated java.util.List<android.telephony.NeighboringCellInfo> getNeighboringCellInfo();
     method public deprecated android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, android.telephony.TelephonyScanManager.NetworkScanCallback);
   }
 
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index d3496ed..e090ed1 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -99,6 +99,7 @@
     libhidlbase \
     libhidltransport \
     libhwbinder \
+    android.frameworks.stats@1.0 \
     android.hardware.health@2.0 \
     android.hardware.power@1.0 \
     android.hardware.power@1.1 \
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 2ef1169..8da2d44 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -992,6 +992,60 @@
     return Status::ok();
 }
 
+hardware::Return<void> StatsService::reportSpeakerImpedance(
+        const SpeakerImpedance& speakerImpedance) {
+    LogEvent event(getWallClockSec() * NS_PER_SEC, getElapsedRealtimeNs(), speakerImpedance);
+    mProcessor->OnLogEvent(&event);
+
+    return hardware::Void();
+}
+
+hardware::Return<void> StatsService::reportHardwareFailed(const HardwareFailed& hardwareFailed) {
+    LogEvent event(getWallClockSec() * NS_PER_SEC, getElapsedRealtimeNs(), hardwareFailed);
+    mProcessor->OnLogEvent(&event);
+
+    return hardware::Void();
+}
+
+hardware::Return<void> StatsService::reportPhysicalDropDetected(
+        const PhysicalDropDetected& physicalDropDetected) {
+    LogEvent event(getWallClockSec() * NS_PER_SEC, getElapsedRealtimeNs(), physicalDropDetected);
+    mProcessor->OnLogEvent(&event);
+
+    return hardware::Void();
+}
+
+hardware::Return<void> StatsService::reportChargeCycles(const ChargeCycles& chargeCycles) {
+    LogEvent event(getWallClockSec() * NS_PER_SEC, getElapsedRealtimeNs(), chargeCycles);
+    mProcessor->OnLogEvent(&event);
+
+    return hardware::Void();
+}
+
+hardware::Return<void> StatsService::reportBatteryHealthSnapshot(
+        const BatteryHealthSnapshotArgs& batteryHealthSnapshotArgs) {
+    LogEvent event(getWallClockSec() * NS_PER_SEC, getElapsedRealtimeNs(),
+                   batteryHealthSnapshotArgs);
+    mProcessor->OnLogEvent(&event);
+
+    return hardware::Void();
+}
+
+hardware::Return<void> StatsService::reportSlowIo(const SlowIo& slowIo) {
+    LogEvent event(getWallClockSec() * NS_PER_SEC, getElapsedRealtimeNs(), slowIo);
+    mProcessor->OnLogEvent(&event);
+
+    return hardware::Void();
+}
+
+hardware::Return<void> StatsService::reportBatteryCausedShutdown(
+        const BatteryCausedShutdown& batteryCausedShutdown) {
+    LogEvent event(getWallClockSec() * NS_PER_SEC, getElapsedRealtimeNs(), batteryCausedShutdown);
+    mProcessor->OnLogEvent(&event);
+
+    return hardware::Void();
+}
+
 void StatsService::binderDied(const wp <IBinder>& who) {
     ALOGW("statscompanion service died");
     StatsdStats::getInstance().noteSystemServerRestart(getWallClockSec());
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 0618927..1f1d782 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -27,6 +27,8 @@
 #include "shell/ShellSubscriber.h"
 #include "statscompanion_util.h"
 
+#include <android/frameworks/stats/1.0/IStats.h>
+#include <android/frameworks/stats/1.0/types.h>
 #include <android/os/BnStatsManager.h>
 #include <android/os/IStatsCompanionService.h>
 #include <binder/IResultReceiver.h>
@@ -38,6 +40,7 @@
 using namespace android;
 using namespace android::base;
 using namespace android::binder;
+using namespace android::frameworks::stats::V1_0;
 using namespace android::os;
 using namespace std;
 
@@ -45,7 +48,12 @@
 namespace os {
 namespace statsd {
 
-class StatsService : public BnStatsManager, public LogListener, public IBinder::DeathRecipient {
+using android::hardware::Return;
+
+class StatsService : public BnStatsManager,
+                     public LogListener,
+                     public IStats,
+                     public IBinder::DeathRecipient {
 public:
     StatsService(const sp<Looper>& handlerLooper);
     virtual ~StatsService();
@@ -146,6 +154,44 @@
      */
     virtual Status sendAppBreadcrumbAtom(int32_t label, int32_t state) override;
 
+    /**
+     * Binder call to get SpeakerImpedance atom.
+     */
+    virtual Return<void> reportSpeakerImpedance(const SpeakerImpedance& speakerImpedance) override;
+
+    /**
+     * Binder call to get HardwareFailed atom.
+     */
+    virtual Return<void> reportHardwareFailed(const HardwareFailed& hardwareFailed) override;
+
+    /**
+     * Binder call to get PhysicalDropDetected atom.
+     */
+    virtual Return<void> reportPhysicalDropDetected(
+            const PhysicalDropDetected& physicalDropDetected) override;
+
+    /**
+     * Binder call to get ChargeCyclesReported atom.
+     */
+    virtual Return<void> reportChargeCycles(const ChargeCycles& chargeCycles) override;
+
+    /**
+     * Binder call to get BatteryHealthSnapshot atom.
+     */
+    virtual Return<void> reportBatteryHealthSnapshot(
+            const BatteryHealthSnapshotArgs& batteryHealthSnapshotArgs) override;
+
+    /**
+     * Binder call to get SlowIo atom.
+     */
+    virtual Return<void> reportSlowIo(const SlowIo& slowIo) override;
+
+    /**
+     * Binder call to get BatteryCausedShutdown atom.
+     */
+    virtual Return<void> reportBatteryCausedShutdown(
+            const BatteryCausedShutdown& batteryCausedShutdown) override;
+
     /** IBinder::DeathRecipient */
     virtual void binderDied(const wp<IBinder>& who) override;
 
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 10a00ee..30d8bfc 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -138,6 +138,9 @@
         FingerprintAuthenticated fingerprint_authenticated = 88;
         FingerprintErrorOccurred fingerprint_error_occurred = 89;
         Notification notification = 90;
+        BatteryHealthSnapshot battery_health_snapshot = 91;
+        SlowIo slow_io = 92;
+        BatteryCausedShutdown battery_caused_shutdown = 93;
     }
 
     // Pulled events will start at field 10000.
@@ -1313,6 +1316,68 @@
 }
 
 /**
+ * Log battery health snapshot.
+ *
+ * Resistance, Voltage, Open Circuit Voltage, Temperature, and Charge Level
+ * are snapshotted periodically over 24hrs.
+ */
+message BatteryHealthSnapshot {
+    enum BatterySnapshotType {
+        UNKNOWN = 0;
+        MIN_TEMP = 1;         // Snapshot at min batt temp over 24hrs.
+        MAX_TEMP = 2;         // Snapshot at max batt temp over 24hrs.
+        MIN_RESISTANCE = 3;   // Snapshot at min batt resistance over 24hrs.
+        MAX_RESISTANCE = 4;   // Snapshot at max batt resistance over 24hrs.
+        MIN_VOLTAGE = 5;      // Snapshot at min batt voltage over 24hrs.
+        MAX_VOLTAGE = 6;      // Snapshot at max batt voltage over 24hrs.
+        MIN_CURRENT = 7;      // Snapshot at min batt current over 24hrs.
+        MAX_CURRENT = 8;      // Snapshot at max batt current over 24hrs.
+        MIN_BATT_LEVEL = 9;   // Snapshot at min battery level (SoC) over 24hrs.
+        MAX_BATT_LEVEL = 10;  // Snapshot at max battery level (SoC) over 24hrs.
+        AVG_RESISTANCE = 11;  // Snapshot at average battery resistance over 24hrs.
+    }
+    optional BatterySnapshotType type = 1;
+    // Temperature, in 1/10ths of degree C.
+    optional int32 temperature_deci_celcius = 2;
+    // Voltage Battery Voltage, in microVolts.
+    optional int32 voltage_micro_volt = 3;
+    // Current Battery current, in microAmps.
+    optional int32 current_micro_amps = 4;
+    // OpenCircuitVoltage Battery Open Circuit Voltage, in microVolts.
+    optional int32 open_circuit_micro_volt = 5;
+    // Resistance Battery Resistance, in microOhms.
+    optional int32 resistance_micro_ohm = 6;
+    // Level Battery Level, as % of full.
+    optional int32 level_percent = 7;
+}
+
+/**
+ * Log slow I/O operations on the primary storage.
+ */
+message SlowIo {
+    // Classifications of IO Operations.
+    enum IoOperation {
+        UNKNOWN = 0;
+        READ = 1;
+        WRITE = 2;
+        UNMAP = 3;
+        SYNC = 4;
+    }
+    optional IoOperation operation = 1;
+
+    // The number of slow IO operations of this type over 24 hours.
+    optional int32 count = 2;
+}
+
+/**
+ * Log battery caused shutdown with the last recorded voltage.
+ */
+message BatteryCausedShutdown {
+    // The last recorded battery voltage prior to shutdown.
+    optional int32 last_recorded_micro_volt = 1;
+}
+
+/**
  * Logs the duration of a davey (jank of >=700ms) when it occurs
  *
  * Logged from:
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 66392f8..5fb196f 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -163,10 +163,7 @@
           new ResourceHealthManagerPuller(android::util::FULL_BATTERY_CAPACITY)}},
         // battery_voltage
         {android::util::BATTERY_VOLTAGE,
-         {{},
-          {},
-          1 * NS_PER_SEC,
-          new ResourceHealthManagerPuller(android::util::BATTERY_VOLTAGE)}},
+         {{}, {}, 1 * NS_PER_SEC, new ResourceHealthManagerPuller(android::util::BATTERY_VOLTAGE)}},
         // process_memory_state
         {android::util::PROCESS_MEMORY_STATE,
          {{4, 5, 6, 7, 8, 9},
@@ -204,17 +201,17 @@
          {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::APP_SIZE)}},
         // Size of specific categories of files. Eg. Music.
         {android::util::CATEGORY_SIZE,
-         {{},
-          {},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::CATEGORY_SIZE)}},
+         {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CATEGORY_SIZE)}},
         // Number of fingerprints registered to each user.
         {android::util::NUM_FINGERPRINTS,
          {{},
           {},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::NUM_FINGERPRINTS)}},
-        };
+        // ProcStats.
+        {android::util::PROC_STATS,
+         {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::PROC_STATS)}},
+};
 
 StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
 }
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 3afa08f..4bbcfd5 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -146,6 +146,127 @@
     }
 }
 
+LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+                   const SpeakerImpedance& speakerImpedance) {
+    mLogdTimestampNs = wallClockTimestampNs;
+    mElapsedTimestampNs = elapsedTimestampNs;
+    mTagId = android::util::SPEAKER_IMPEDANCE_REPORTED;
+
+    mValues.push_back(
+            FieldValue(Field(mTagId, getSimpleField(1)), Value(speakerImpedance.speakerLocation)));
+    mValues.push_back(
+            FieldValue(Field(mTagId, getSimpleField(2)), Value(speakerImpedance.milliOhms)));
+    if (!mValues.empty()) {
+        mValues.back().mField.decorateLastPos(1);
+    }
+}
+
+LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+                   const HardwareFailed& hardwareFailed) {
+    mLogdTimestampNs = wallClockTimestampNs;
+    mElapsedTimestampNs = elapsedTimestampNs;
+    mTagId = android::util::HARDWARE_FAILED;
+
+    mValues.push_back(FieldValue(Field(mTagId, getSimpleField(1)),
+                                 Value(int32_t(hardwareFailed.hardwareType))));
+    mValues.push_back(
+            FieldValue(Field(mTagId, getSimpleField(2)), Value(hardwareFailed.hardwareLocation)));
+    mValues.push_back(
+            FieldValue(Field(mTagId, getSimpleField(3)), Value(int32_t(hardwareFailed.errorCode))));
+    if (!mValues.empty()) {
+        mValues.back().mField.decorateLastPos(1);
+    }
+}
+
+LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+                   const PhysicalDropDetected& physicalDropDetected) {
+    mLogdTimestampNs = wallClockTimestampNs;
+    mElapsedTimestampNs = elapsedTimestampNs;
+    mTagId = android::util::PHYSICAL_DROP_DETECTED;
+
+    mValues.push_back(FieldValue(Field(mTagId, getSimpleField(1)),
+                                 Value(int32_t(physicalDropDetected.confidencePctg))));
+    mValues.push_back(
+            FieldValue(Field(mTagId, getSimpleField(2)), Value(physicalDropDetected.accelPeak)));
+    mValues.push_back(FieldValue(Field(mTagId, getSimpleField(3)),
+                                 Value(physicalDropDetected.freefallDuration)));
+    if (!mValues.empty()) {
+        mValues.back().mField.decorateLastPos(1);
+    }
+}
+
+LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+                   const ChargeCycles& chargeCycles) {
+    mLogdTimestampNs = wallClockTimestampNs;
+    mElapsedTimestampNs = elapsedTimestampNs;
+    mTagId = android::util::CHARGE_CYCLES_REPORTED;
+
+    for (size_t i = 0; i < chargeCycles.cycleBucket.size(); i++) {
+        mValues.push_back(FieldValue(Field(mTagId, getSimpleField(i + 1)),
+                                     Value(chargeCycles.cycleBucket[i])));
+    }
+
+    if (!mValues.empty()) {
+        mValues.back().mField.decorateLastPos(1);
+    }
+}
+
+LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+                   const BatteryHealthSnapshotArgs& batteryHealthSnapshotArgs) {
+    mLogdTimestampNs = wallClockTimestampNs;
+    mElapsedTimestampNs = elapsedTimestampNs;
+    mTagId = android::util::BATTERY_HEALTH_SNAPSHOT;
+
+    mValues.push_back(FieldValue(Field(mTagId, getSimpleField(1)),
+                                 Value(int32_t(batteryHealthSnapshotArgs.type))));
+    mValues.push_back(FieldValue(Field(mTagId, getSimpleField(2)),
+                                 Value(batteryHealthSnapshotArgs.temperatureDeciC)));
+    mValues.push_back(FieldValue(Field(mTagId, getSimpleField(3)),
+                                 Value(batteryHealthSnapshotArgs.voltageMicroV)));
+    mValues.push_back(FieldValue(Field(mTagId, getSimpleField(4)),
+                                 Value(batteryHealthSnapshotArgs.currentMicroA)));
+    mValues.push_back(FieldValue(Field(mTagId, getSimpleField(5)),
+                                 Value(batteryHealthSnapshotArgs.openCircuitVoltageMicroV)));
+    mValues.push_back(FieldValue(Field(mTagId, getSimpleField(6)),
+                                 Value(batteryHealthSnapshotArgs.resistanceMicroOhm)));
+    mValues.push_back(FieldValue(Field(mTagId, getSimpleField(7)),
+                                 Value(batteryHealthSnapshotArgs.levelPercent)));
+
+    if (!mValues.empty()) {
+        mValues.back().mField.decorateLastPos(1);
+    }
+}
+
+LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs, const SlowIo& slowIo) {
+    mLogdTimestampNs = wallClockTimestampNs;
+    mElapsedTimestampNs = elapsedTimestampNs;
+    mTagId = android::util::SLOW_IO;
+
+    int pos[] = {1};
+    mValues.push_back(
+            FieldValue(Field(mTagId, getSimpleField(1)), Value(int32_t(slowIo.operation))));
+    pos[0]++;
+    mValues.push_back(FieldValue(Field(mTagId, getSimpleField(2)), Value(slowIo.count)));
+
+    if (!mValues.empty()) {
+        mValues.back().mField.decorateLastPos(1);
+    }
+}
+
+LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+                   const BatteryCausedShutdown& batteryCausedShutdown) {
+    mLogdTimestampNs = wallClockTimestampNs;
+    mElapsedTimestampNs = elapsedTimestampNs;
+    mTagId = android::util::BATTERY_CAUSED_SHUTDOWN;
+
+    mValues.push_back(FieldValue(Field(mTagId, getSimpleField(1)),
+                                 Value(batteryCausedShutdown.voltageMicroV)));
+
+    if (!mValues.empty()) {
+        mValues.back().mField.decorateLastPos(1);
+    }
+}
+
 LogEvent::LogEvent(int32_t tagId, int64_t timestampNs) {
     mLogdTimestampNs = timestampNs;
     mTagId = tagId;
@@ -223,8 +344,6 @@
     return false;
 }
 
-
-
 bool LogEvent::writeKeyValuePairs(const std::map<int32_t, int32_t>& int_map,
                                   const std::map<int32_t, int64_t>& long_map,
                                   const std::map<int32_t, std::string>& string_map,
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 0a8ac2b..c7e2a8c 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -18,6 +18,7 @@
 
 #include "FieldValue.h"
 
+#include <android/frameworks/stats/1.0/types.h>
 #include <android/os/StatsLogEventWrapper.h>
 #include <android/util/ProtoOutputStream.h>
 #include <log/log_event_list.h>
@@ -28,6 +29,8 @@
 #include <string>
 #include <vector>
 
+using namespace android::frameworks::stats::V1_0;
+
 namespace android {
 namespace os {
 namespace statsd {
@@ -82,6 +85,27 @@
                       const std::map<int32_t, std::string>& string_map,
                       const std::map<int32_t, float>& float_map);
 
+    explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+                      const SpeakerImpedance& speakerImpedance);
+
+    explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+                      const HardwareFailed& hardwareFailed);
+
+    explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+                      const PhysicalDropDetected& physicalDropDetected);
+
+    explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+                      const ChargeCycles& chargeCycles);
+
+    explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+                      const BatteryHealthSnapshotArgs& batteryHealthSnapshotArgs);
+
+    explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+                      const SlowIo& slowIo);
+
+    explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+                      const BatteryCausedShutdown& batteryCausedShutdown);
+
     ~LogEvent();
 
     /**
diff --git a/cmds/statsd/src/main.cpp b/cmds/statsd/src/main.cpp
index 9002f07..a5dac08 100644
--- a/cmds/statsd/src/main.cpp
+++ b/cmds/statsd/src/main.cpp
@@ -25,6 +25,7 @@
 #include <binder/IServiceManager.h>
 #include <binder/ProcessState.h>
 #include <binder/Status.h>
+#include <hidl/HidlTransportSupport.h>
 #include <utils/Looper.h>
 #include <utils/StrongPointer.h>
 
@@ -56,12 +57,21 @@
     ps->giveThreadPoolName();
     IPCThreadState::self()->disableBackgroundScheduling(true);
 
+    ::android::hardware::configureRpcThreadpool(1 /*threads*/, false /*willJoin*/);
+
     // Create the service
     sp<StatsService> service = new StatsService(looper);
     if (defaultServiceManager()->addService(String16("stats"), service) != 0) {
-        ALOGE("Failed to add service");
+        ALOGE("Failed to add service as AIDL service");
         return -1;
     }
+
+    auto ret = service->registerAsService();
+    if (ret != ::android::OK) {
+        ALOGE("Failed to add service as HIDL service");
+        return 1; // or handle error
+    }
+
     service->sayHiToStatsCompanion();
 
     service->Startup();
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 9d9e5be..dd3402d 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -781,7 +781,7 @@
         if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
             for (const auto& condIt : whatIt->second) {
                 const bool cond = dimensionKeysInCondition.find(condIt.first) !=
-                        dimensionKeysInCondition.end();
+                        dimensionKeysInCondition.end() && condition;
                 handleStartEvent(MetricDimensionKey(dimensionInWhat, condIt.first),
                     conditionKey, cond, event);
                 dimensionKeysInCondition.erase(condIt.first);
diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp
index f038214..75bd40f 100644
--- a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp
+++ b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp
@@ -81,6 +81,34 @@
 
 }  // namespace
 
+/*
+ The following test has the following input.
+
+{ 10000000002 10000000002 (8)9999[I], [S], job0[S], 1[I],  }
+{ 10000000010 10000000010 (7)111[I], App1[S], 222[I], GMSCoreModule1[S], 222[I], GMSCoreModule2[S], ReadEmail[S], 1[I],  }
+{ 10000000011 10000000011 (29)1[I],  }
+{ 10000000040 10000000040 (29)2[I],  }
+{ 10000000050 10000000050 (7)111[I], App1[S], 222[I], GMSCoreModule1[S], 222[I], GMSCoreModule2[S], ReadEmail[S], 0[I],  }
+{ 10000000101 10000000101 (8)9999[I], [S], job0[S], 0[I],  }
+{ 10000000102 10000000102 (29)1[I],  }
+{ 10000000200 10000000200 (7)111[I], App1[S], 222[I], GMSCoreModule1[S], 222[I], GMSCoreModule2[S], ReadEmail[S], 1[I],  }
+{ 10000000201 10000000201 (8)9999[I], [S], job2[S], 1[I],  }
+{ 10000000400 10000000400 (7)111[I], App1[S], 222[I], GMSCoreModule1[S], 222[I], GMSCoreModule2[S], ReadDoc[S], 1[I],  }
+{ 10000000401 10000000401 (7)333[I], App2[S], 222[I], GMSCoreModule1[S], 555[I], GMSCoreModule2[S], ReadEmail[S], 1[I],  }
+{ 10000000450 10000000450 (29)2[I],  }
+{ 10000000500 10000000500 (8)9999[I], [S], job2[S], 0[I],  }
+{ 10000000600 10000000600 (8)8888[I], [S], job2[S], 1[I],  }
+{ 10000000650 10000000650 (29)1[I],  }
+{ 309999999999 309999999999 (7)111[I], App1[S], 222[I], GMSCoreModule1[S], 222[I], GMSCoreModule2[S], ReadDoc[S], 0[I],  }
+{ 310000000100 310000000100 (29)2[I],  }
+{ 310000000300 310000000300 (7)111[I], App1[S], 222[I], GMSCoreModule1[S], 222[I], GMSCoreModule2[S], ReadEmail[S], 0[I],  }
+{ 310000000600 310000000600 (8)8888[I], [S], job1[S], 1[I],  }
+{ 310000000640 310000000640 (29)1[I],  }
+{ 310000000650 310000000650 (29)2[I],  }
+{ 310000000700 310000000700 (7)333[I], App2[S], 222[I], GMSCoreModule1[S], 555[I], GMSCoreModule2[S], ReadEmail[S], 0[I],  }
+{ 310000000850 310000000850 (8)8888[I], [S], job2[S], 0[I],  }
+{ 310000000900 310000000900 (8)8888[I], [S], job1[S], 0[I],  }
+*/
 TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_AND_CombinationCondition) {
     for (const bool hashStringInReport : { true, false }) {
         for (bool isDimensionInConditionSubSetOfConditionTrackerDimension : { true, false }) {
@@ -250,7 +278,7 @@
                     EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
                     EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
                               bucketStartTimeNs + bucketSizeNs);
-                    EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 201 + bucketSizeNs - 600);
+                    EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 201 + bucketSizeNs - 650);
                     EXPECT_EQ(data.bucket_info(1).duration_nanos(), 100);
                     EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
                               bucketStartTimeNs + bucketSizeNs);
@@ -269,7 +297,7 @@
                             data.dimensions_in_condition(),
                             android::util::SYNC_STATE_CHANGED, 333, "App2");
                     EXPECT_EQ(data.bucket_info_size(), 2);
-                    EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 401 + bucketSizeNs - 600);
+                    EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 401 + bucketSizeNs - 650);
                     EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
                     EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
                               bucketStartTimeNs + bucketSizeNs);
@@ -331,7 +359,7 @@
                     EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
                               bucketStartTimeNs + bucketSizeNs);
                     EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 201);
-                    EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 600 + 100);
+                    EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 650 + 100);
                     EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
                               bucketStartTimeNs + bucketSizeNs);
                     EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
@@ -353,7 +381,7 @@
                     EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
                     EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
                               bucketStartTimeNs + bucketSizeNs);
-                    EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 600 + 110);
+                    EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 650 + 110);
                     EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
                               bucketStartTimeNs + bucketSizeNs);
                     EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 2acae1c..482ef2d 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -394,7 +394,7 @@
  *         <td>The final call you receive before your
  *             activity is destroyed.  This can happen either because the
  *             activity is finishing (someone called {@link Activity#finish} on
- *             it, or because the system is temporarily destroying this
+ *             it), or because the system is temporarily destroying this
  *             instance of the activity to save space.  You can distinguish
  *             between these two scenarios with the {@link
  *             Activity#isFinishing} method.</td>
@@ -1985,7 +1985,7 @@
     /**
      * Perform any final cleanup before an activity is destroyed.  This can
      * happen either because the activity is finishing (someone called
-     * {@link #finish} on it, or because the system is temporarily destroying
+     * {@link #finish} on it), or because the system is temporarily destroying
      * this instance of the activity to save space.  You can distinguish
      * between these two scenarios with the {@link #isFinishing} method.
      *
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 2718bfa..bf3d885 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -17,6 +17,7 @@
 package android.app;
 
 import android.annotation.IntDef;
+import android.annotation.Nullable;
 import android.annotation.SystemService;
 import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
@@ -208,10 +209,11 @@
     }
 
     /**
-     * Expand the settings panel and open a subPanel, pass null to just open the settings panel.
+     * Expand the settings panel and open a subPanel. If the subpanel is null or does not have a
+     * corresponding tile, the QS panel is simply expanded
      */
     @UnsupportedAppUsage
-    public void expandSettingsPanel(String subPanel) {
+    public void expandSettingsPanel(@Nullable String subPanel) {
         try {
             final IStatusBarService svc = getService();
             if (svc != null) {
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index e6fb5dc..096c7aa 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -28,9 +28,13 @@
 import android.graphics.Rect;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.proto.ProtoInputStream;
 import android.util.proto.ProtoOutputStream;
+import android.util.proto.WireTypeMismatchException;
 import android.view.DisplayInfo;
 
+import java.io.IOException;
+
 /**
  * Class that contains windowing configuration/state for other objects that contain windows directly
  * or indirectly. E.g. Activities, Task, Displays, ...
@@ -511,6 +515,38 @@
     }
 
     /**
+     * Read from a protocol buffer input stream.
+     * Protocol buffer message definition at {@link android.app.WindowConfigurationProto}
+     *
+     * @param proto   Stream to read the WindowConfiguration object from.
+     * @param fieldId Field Id of the WindowConfiguration as defined in the parent message
+     * @hide
+     */
+    public void readFromProto(ProtoInputStream proto, long fieldId)
+            throws IOException, WireTypeMismatchException {
+        final long token = proto.start(fieldId);
+        try {
+            while (proto.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+                switch (proto.getFieldNumber()) {
+                    case (int) APP_BOUNDS:
+                        mAppBounds = new Rect();
+                        mAppBounds.readFromProto(proto, APP_BOUNDS);
+                        break;
+                    case (int) WINDOWING_MODE:
+                        mWindowingMode = proto.readInt(WINDOWING_MODE);
+                        break;
+                    case (int) ACTIVITY_TYPE:
+                        mActivityType = proto.readInt(ACTIVITY_TYPE);
+                        break;
+                }
+            }
+        } finally {
+            // Let caller handle any exceptions
+            proto.end(token);
+        }
+    }
+
+    /**
      * Returns true if the activities associated with this window configuration display a shadow
      * around their border.
      * @hide
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 9f22ad1..308b39e 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -165,6 +165,12 @@
          */
         public static final int KEYGUARD_HIDDEN = 18;
 
+        /**
+         * Keep in sync with the greatest event type value.
+         * @hide
+         */
+        public static final int MAX_EVENT_TYPE = 18;
+
         /** @hide */
         public static final int FLAG_IS_PACKAGE_INSTANT_APP = 1 << 0;
 
@@ -176,6 +182,12 @@
         public @interface EventFlags {}
 
         /**
+         * Bitwise OR all valid flag constants to create this constant.
+         * @hide
+         */
+        public static final int VALID_FLAG_BITS = FLAG_IS_PACKAGE_INSTANT_APP;
+
+        /**
          * {@hide}
          */
         @UnsupportedAppUsage
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 121b432..799f8e5 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -46,6 +46,7 @@
 import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.app.WindowConfiguration;
+import android.content.LocaleProto;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ActivityInfo.Config;
 import android.os.Build;
@@ -54,7 +55,9 @@
 import android.os.Parcelable;
 import android.text.TextUtils;
 import android.util.DisplayMetrics;
+import android.util.proto.ProtoInputStream;
 import android.util.proto.ProtoOutputStream;
+import android.util.proto.WireTypeMismatchException;
 import android.view.View;
 
 import com.android.internal.util.XmlUtils;
@@ -67,6 +70,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.List;
 import java.util.Locale;
 
 /**
@@ -1086,12 +1090,14 @@
     /**
      * Write to a protocol buffer output stream.
      * Protocol buffer message definition at {@link android.content.ConfigurationProto}
+     * Has the option to ignore fields that don't need to be persisted to disk.
      *
      * @param protoOutputStream Stream to write the Configuration object to.
      * @param fieldId           Field Id of the Configuration as defined in the parent message
+     * @param persisted         Note if this proto will be persisted to disk
      * @hide
      */
-    public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) {
+    public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId, boolean persisted) {
         final long token = protoOutputStream.start(fieldId);
         protoOutputStream.write(FONT_SCALE, fontScale);
         protoOutputStream.write(MCC, mcc);
@@ -1113,13 +1119,137 @@
         protoOutputStream.write(SCREEN_HEIGHT_DP, screenHeightDp);
         protoOutputStream.write(SMALLEST_SCREEN_WIDTH_DP, smallestScreenWidthDp);
         protoOutputStream.write(DENSITY_DPI, densityDpi);
-        if (windowConfiguration != null) {
+        // For persistence, we do not care about window configuration
+        if (!persisted && windowConfiguration != null) {
             windowConfiguration.writeToProto(protoOutputStream, WINDOW_CONFIGURATION);
         }
         protoOutputStream.end(token);
     }
 
     /**
+     * Write to a protocol buffer output stream.
+     * Protocol buffer message definition at {@link android.content.ConfigurationProto}
+     *
+     * @param protoOutputStream Stream to write the Configuration object to.
+     * @param fieldId           Field Id of the Configuration as defined in the parent message
+     * @hide
+     */
+    public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) {
+        writeToProto(protoOutputStream, fieldId, false);
+    }
+
+    /**
+     * Read from a protocol buffer output stream.
+     * Protocol buffer message definition at {@link android.content.ConfigurationProto}
+     *
+     * @param protoInputStream Stream to read the Configuration object from.
+     * @param fieldId          Field Id of the Configuration as defined in the parent message
+     * @hide
+     */
+    public void readFromProto(ProtoInputStream protoInputStream, long fieldId) throws IOException {
+        final long token = protoInputStream.start(fieldId);
+        final List<Locale> list = new ArrayList();
+        try {
+            while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+                switch (protoInputStream.getFieldNumber()) {
+                    case (int) FONT_SCALE:
+                        fontScale = protoInputStream.readFloat(FONT_SCALE);
+                        break;
+                    case (int) MCC:
+                        mcc = protoInputStream.readInt(MCC);
+                        break;
+                    case (int) MNC:
+                        mnc = protoInputStream.readInt(MNC);
+                        break;
+                    case (int) LOCALES:
+                        // Parse the Locale here to handle all the repeated Locales
+                        // The LocaleList will be created when the message is completed
+                        final long localeToken = protoInputStream.start(LOCALES);
+                        String language = "";
+                        String country = "";
+                        String variant = "";
+                        try {
+                            while (protoInputStream.nextField()
+                                    != ProtoInputStream.NO_MORE_FIELDS) {
+                                switch (protoInputStream.getFieldNumber()) {
+                                    case (int) LocaleProto.LANGUAGE:
+                                        language = protoInputStream.readString(
+                                                LocaleProto.LANGUAGE);
+                                        break;
+                                    case (int) LocaleProto.COUNTRY:
+                                        country = protoInputStream.readString(LocaleProto.COUNTRY);
+                                        break;
+                                    case (int) LocaleProto.VARIANT:
+                                        variant = protoInputStream.readString(LocaleProto.VARIANT);
+                                        break;
+                                }
+                            }
+                        } catch (WireTypeMismatchException wtme) {
+                            // rethrow for caller deal with
+                            throw wtme;
+                        } finally {
+                            protoInputStream.end(localeToken);
+                            list.add(new Locale(language, country, variant));
+                        }
+                        break;
+                    case (int) SCREEN_LAYOUT:
+                        screenLayout = protoInputStream.readInt(SCREEN_LAYOUT);
+                        break;
+                    case (int) COLOR_MODE:
+                        colorMode = protoInputStream.readInt(COLOR_MODE);
+                        break;
+                    case (int) TOUCHSCREEN:
+                        touchscreen = protoInputStream.readInt(TOUCHSCREEN);
+                        break;
+                    case (int) KEYBOARD:
+                        keyboard = protoInputStream.readInt(KEYBOARD);
+                        break;
+                    case (int) KEYBOARD_HIDDEN:
+                        keyboardHidden = protoInputStream.readInt(KEYBOARD_HIDDEN);
+                        break;
+                    case (int) HARD_KEYBOARD_HIDDEN:
+                        hardKeyboardHidden = protoInputStream.readInt(HARD_KEYBOARD_HIDDEN);
+                        break;
+                    case (int) NAVIGATION:
+                        navigation = protoInputStream.readInt(NAVIGATION);
+                        break;
+                    case (int) NAVIGATION_HIDDEN:
+                        navigationHidden = protoInputStream.readInt(NAVIGATION_HIDDEN);
+                        break;
+                    case (int) ORIENTATION:
+                        orientation = protoInputStream.readInt(ORIENTATION);
+                        break;
+                    case (int) UI_MODE:
+                        uiMode = protoInputStream.readInt(UI_MODE);
+                        break;
+                    case (int) SCREEN_WIDTH_DP:
+                        screenWidthDp = protoInputStream.readInt(SCREEN_WIDTH_DP);
+                        break;
+                    case (int) SCREEN_HEIGHT_DP:
+                        screenHeightDp = protoInputStream.readInt(SCREEN_HEIGHT_DP);
+                        break;
+                    case (int) SMALLEST_SCREEN_WIDTH_DP:
+                        smallestScreenWidthDp = protoInputStream.readInt(SMALLEST_SCREEN_WIDTH_DP);
+                        break;
+                    case (int) DENSITY_DPI:
+                        densityDpi = protoInputStream.readInt(DENSITY_DPI);
+                        break;
+                    case (int) WINDOW_CONFIGURATION:
+                        windowConfiguration.readFromProto(protoInputStream, WINDOW_CONFIGURATION);
+                        break;
+                }
+            }
+        } finally {
+            // Let caller handle any exceptions
+            if (list.size() > 0) {
+                //Create the LocaleList from the collected Locales
+                setLocales(new LocaleList(list.toArray(new Locale[list.size()])));
+            }
+            protoInputStream.end(token);
+        }
+    }
+
+    /**
      * Write full {@link android.content.ResourcesConfigurationProto} to protocol buffer output
      * stream.
      *
diff --git a/core/java/android/hardware/GeomagneticField.java b/core/java/android/hardware/GeomagneticField.java
index 94f2ac0..0d7b695 100644
--- a/core/java/android/hardware/GeomagneticField.java
+++ b/core/java/android/hardware/GeomagneticField.java
@@ -31,7 +31,7 @@
  * Android may use a newer version of the model.
  */
 public class GeomagneticField {
-    // The magnetic field at a given point, in nonoteslas in geodetic
+    // The magnetic field at a given point, in nanoteslas in geodetic
     // coordinates.
     private float mX;
     private float mY;
@@ -278,7 +278,7 @@
     }
 
     /**
-     * @return  Horizontal component of the field strength in nonoteslas.
+     * @return  Horizontal component of the field strength in nanoteslas.
      */
     public float getHorizontalStrength() {
         return (float) Math.hypot(mX, mY);
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index f2e9078..8333b81 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -26,7 +26,6 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.PackageManager;
 import android.os.Binder;
 import android.os.Build.VERSION_CODES;
 import android.os.Bundle;
@@ -3801,8 +3800,9 @@
 
     private void unsupportedStartingFrom(int version) {
         if (Process.myUid() == Process.SYSTEM_UID) {
-            // The getApplicationInfo() call we make below is not supported in system context, and
-            // we want to allow the system to use these APIs anyway.
+            // The getApplicationInfo() call we make below is not supported in system context. Let
+            // the call through here, and rely on the fact that ConnectivityService will refuse to
+            // allow the system to use these APIs anyway.
             return;
         }
 
@@ -3819,11 +3819,6 @@
     // functions by accessing ConnectivityService directly. However, it should be clear that doing
     // so is unsupported and may break in the future. http://b/22728205
     private void checkLegacyRoutingApiAccess() {
-        if (mContext.checkCallingOrSelfPermission("com.android.permission.INJECT_OMADM_SETTINGS")
-                == PackageManager.PERMISSION_GRANTED) {
-            return;
-        }
-
         unsupportedStartingFrom(VERSION_CODES.M);
     }
 
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 6bd2e76..8681893 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -30,6 +30,8 @@
 
 import dalvik.system.VMRuntime;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Objects;
 
 /**
@@ -1083,7 +1085,67 @@
         return true;
     }
 
+    /** Build information for a particular device partition. */
+    public static class Partition {
+        /** The name identifying the system partition. */
+        public static final String PARTITION_NAME_SYSTEM = "system";
+
+        private String mName;
+        private String mFingerprint;
+        private long mTimeMs;
+
+        public Partition() {}
+
+        private Partition(String name, String fingerprint, long timeMs) {
+            mName = name;
+            mFingerprint = fingerprint;
+            mTimeMs = timeMs;
+        }
+
+        /** The name of this partition, e.g. "system", or "vendor" */
+        public String getName() {
+            return mName;
+        }
+
+        /** The build fingerprint of this partition, see {@link Build#FINGERPRINT}. */
+        public String getFingerprint() {
+            return mFingerprint;
+        }
+
+        /** The time (ms since epoch), at which this partition was built, see {@link Build#TIME}. */
+        public long getTimeMillis() {
+            return mTimeMs;
+        }
+    }
+
+    /**
+     * Get build information about partitions that have a separate fingerprint defined.
+     *
+     * The list includes partitions that are suitable candidates for over-the-air updates. This is
+     * not an exhaustive list of partitions on the device.
+     */
+    public static List<Partition> getPartitions() {
+        ArrayList<Partition> partitions = new ArrayList();
+
+        String[] names = new String[] {
+            "bootimage", "odm", "product", "product_services", Partition.PARTITION_NAME_SYSTEM,
+            "vendor"
+        };
+        for (String name : names) {
+            String fingerprint = SystemProperties.get("ro." + name + ".build.fingerprint");
+            if (TextUtils.isEmpty(fingerprint)) {
+                continue;
+            }
+            long time = getLong("ro." + name + ".build.date.utc") * 1000;
+            partitions.add(new Partition(name, fingerprint, time));
+        }
+
+        return partitions;
+    }
+
     // The following properties only make sense for internal engineering builds.
+
+    /** The time at which the build was produced, given in milliseconds since the UNIX epoch. */
     public static final long TIME = getLong("ro.build.date.utc") * 1000;
     public static final String USER = getString("ro.build.user");
     public static final String HOST = getString("ro.build.host");
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index f83acb6..54be639 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -45,6 +45,7 @@
     private static final String TAG = "GraphicsEnvironment";
     private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0";
     private static final String ANGLE_PACKAGE_NAME = "com.android.angle";
+    private static final String GLES_MODE_METADATA_KEY = "com.android.angle.GLES_MODE";
 
     private ClassLoader mClassLoader;
     private String mLayerPath;
@@ -123,7 +124,6 @@
                     }
                 }
             }
-
         }
 
         // Include the app's lib directory in all cases
@@ -133,7 +133,7 @@
     }
 
     /**
-     * Selectively enable ANGLE for applications
+     * Pass ANGLE details down to trigger enable logic
      */
     private static void setupAngle(Context context) {
 
@@ -143,39 +143,67 @@
 
         String packageName = context.getPackageName();
 
-        // Only provide an ANGLE namespace if the package name matches setting
+        boolean devOptIn = false;
         if ((angleEnabledApp != null && packageName != null)
                 && (!angleEnabledApp.isEmpty() && !packageName.isEmpty())
                 && angleEnabledApp.equals(packageName)) {
 
-            if (DEBUG) Log.v(TAG, "ANGLE enabled for " + packageName);
+            if (DEBUG) Log.v(TAG, packageName + " opted in for ANGLE via Developer Setting");
 
-            ApplicationInfo angleInfo;
-
-            try {
-                angleInfo = context.getPackageManager().getApplicationInfo(ANGLE_PACKAGE_NAME,
-                    PackageManager.MATCH_SYSTEM_ONLY);
-            } catch (PackageManager.NameNotFoundException e) {
-                Log.w(TAG, "ANGLE package '" + ANGLE_PACKAGE_NAME + "' not installed");
-                return;
-            }
-
-            String abi = chooseAbi(angleInfo);
-
-            // Build a path that includes installed native libs and APK
-            StringBuilder sb = new StringBuilder();
-            sb.append(angleInfo.nativeLibraryDir)
-                .append(File.pathSeparator)
-                .append(angleInfo.sourceDir)
-                .append("!/lib/")
-                .append(abi);
-            String paths = sb.toString();
-
-            if (DEBUG) Log.v(TAG, "ANGLE package libs: " + paths);
-
-            // Providing any path will trigger namespace creation
-            setAnglePath(paths);
+            devOptIn = true;
         }
+
+        ApplicationInfo appInfo;
+        try {
+            appInfo = context.getPackageManager().getApplicationInfo(packageName,
+                PackageManager.GET_META_DATA);
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.w(TAG, "Failed to get info about current application: " + packageName);
+            return;
+        }
+
+        String appPref = "dontcare";
+        final BaseBundle metadata = appInfo.metaData;
+        if (metadata != null) {
+            final String glesMode = metadata.getString(GLES_MODE_METADATA_KEY);
+            if (glesMode != null) {
+                if (glesMode.equals("angle")) {
+                    appPref = "angle";
+                    if (DEBUG) Log.v(TAG, packageName + " opted for ANGLE via AndroidManifest");
+                } else if (glesMode.equals("native")) {
+                    appPref = "native";
+                    if (DEBUG) Log.v(TAG, packageName + " opted for NATIVE via AndroidManifest");
+                } else {
+                    Log.w(TAG, "Unrecognized GLES_MODE (\"" + glesMode + "\") for " + packageName
+                               + ". Supported values are \"angle\" or \"native\"");
+                }
+            }
+        }
+
+        ApplicationInfo angleInfo;
+        try {
+            angleInfo = context.getPackageManager().getApplicationInfo(ANGLE_PACKAGE_NAME,
+                PackageManager.MATCH_SYSTEM_ONLY);
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.w(TAG, "ANGLE package '" + ANGLE_PACKAGE_NAME + "' not installed");
+            return;
+        }
+
+        String abi = chooseAbi(angleInfo);
+
+        // Build a path that includes installed native libs and APK
+        StringBuilder sb = new StringBuilder();
+        sb.append(angleInfo.nativeLibraryDir)
+            .append(File.pathSeparator)
+            .append(angleInfo.sourceDir)
+            .append("!/lib/")
+            .append(abi);
+        String paths = sb.toString();
+
+        if (DEBUG) Log.v(TAG, "ANGLE package libs: " + paths);
+
+        // Further opt-in logic is handled in native, so pass relevant info down
+        setAngleInfo(paths, packageName, appPref, devOptIn);
     }
 
     /**
@@ -266,5 +294,6 @@
     private static native void setLayerPaths(ClassLoader classLoader, String layerPaths);
     private static native void setDebugLayers(String layers);
     private static native void setDriverPath(String path);
-    private static native void setAnglePath(String path);
+    private static native void setAngleInfo(String path, String appPackage, String appPref,
+                                            boolean devOptIn);
 }
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 6fab3c4..0f64c45 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -483,6 +483,8 @@
      * @param appDataDir null-ok the data directory of the app.
      * @param invokeWith null-ok the command to invoke with.
      * @param packageName null-ok the name of the package this process belongs to.
+     * @param packagesForUid null-ok all the packages with the same uid as this process.
+     * @param visibleVols null-ok storage volumes that can be accessed by this process.
      * @param zygoteArgs Additional arguments to supply to the zygote process.
      * 
      * @return An object that describes the result of the attempt to start the process.
@@ -501,10 +503,13 @@
                                   @Nullable String appDataDir,
                                   @Nullable String invokeWith,
                                   @Nullable String packageName,
+                                  @Nullable String[] packagesForUid,
+                                  @Nullable String[] visibleVols,
                                   @Nullable String[] zygoteArgs) {
         return zygoteProcess.start(processClass, niceName, uid, gid, gids,
                     runtimeFlags, mountExternal, targetSdkVersion, seInfo,
-                    abi, instructionSet, appDataDir, invokeWith, packageName, zygoteArgs);
+                    abi, instructionSet, appDataDir, invokeWith, packageName,
+                    packagesForUid, visibleVols, zygoteArgs);
     }
 
     /** @hide */
@@ -519,10 +524,13 @@
                                   @Nullable String appDataDir,
                                   @Nullable String invokeWith,
                                   @Nullable String packageName,
+                                  @Nullable String[] packagesForUid,
+                                  @Nullable String[] visibleVols,
                                   @Nullable String[] zygoteArgs) {
         return WebViewZygote.getProcess().start(processClass, niceName, uid, gid, gids,
                     runtimeFlags, mountExternal, targetSdkVersion, seInfo,
-                    abi, instructionSet, appDataDir, invokeWith, packageName, zygoteArgs);
+                    abi, instructionSet, appDataDir, invokeWith, packageName,
+                    packagesForUid, visibleVols, zygoteArgs);
     }
 
     /**
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 99181ac..7fd0a4b 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -215,6 +215,8 @@
      * @param appDataDir null-ok the data directory of the app.
      * @param invokeWith null-ok the command to invoke with.
      * @param packageName null-ok the name of the package this process belongs to.
+     * @param packagesForUid null-ok all the packages with the same uid as this process.
+     * @param visibleVols null-ok storage volumes that can be accessed by this process.
      * @param zygoteArgs Additional arguments to supply to the zygote process.
      *
      * @return An object that describes the result of the attempt to start the process.
@@ -231,12 +233,14 @@
                                                   @Nullable String appDataDir,
                                                   @Nullable String invokeWith,
                                                   @Nullable String packageName,
+                                                  @Nullable String[] packagesForUid,
+                                                  @Nullable String[] visibleVols,
                                                   @Nullable String[] zygoteArgs) {
         try {
             return startViaZygote(processClass, niceName, uid, gid, gids,
                     runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                     abi, instructionSet, appDataDir, invokeWith, false /* startChildZygote */,
-                    packageName, zygoteArgs);
+                    packageName, packagesForUid, visibleVols, zygoteArgs);
         } catch (ZygoteStartFailedEx ex) {
             Log.e(LOG_TAG,
                     "Starting VM process through Zygote failed");
@@ -355,6 +359,8 @@
      * @param startChildZygote Start a sub-zygote. This creates a new zygote process
      * that has its state cloned from this zygote process.
      * @param packageName null-ok the name of the package this process belongs to.
+     * @param packagesForUid null-ok all the packages with the same uid as this process.
+     * @param visibleVols null-ok storage volumes that can be accessed by this process.
      * @param extraArgs Additional arguments to supply to the zygote process.
      * @return An object that describes the result of the attempt to start the process.
      * @throws ZygoteStartFailedEx if process start failed for any reason
@@ -372,6 +378,8 @@
                                                       @Nullable String invokeWith,
                                                       boolean startChildZygote,
                                                       @Nullable String packageName,
+                                                      @Nullable String[] packagesForUid,
+                                                      @Nullable String[] visibleVols,
                                                       @Nullable String[] extraArgs)
                                                       throws ZygoteStartFailedEx {
         ArrayList<String> argsForZygote = new ArrayList<String>();
@@ -439,6 +447,32 @@
             argsForZygote.add("--package-name=" + packageName);
         }
 
+        if (packagesForUid != null && packagesForUid.length > 0) {
+            final StringBuilder sb = new StringBuilder();
+            sb.append("--packages-for-uid=");
+
+            for (int i = 0; i < packagesForUid.length; ++i) {
+                if (i != 0) {
+                    sb.append(',');
+                }
+                sb.append(packagesForUid[i]);
+            }
+            argsForZygote.add(sb.toString());
+        }
+
+        if (visibleVols != null && visibleVols.length > 0) {
+            final StringBuilder sb = new StringBuilder();
+            sb.append("--visible-vols=");
+
+            for (int i = 0; i < visibleVols.length; ++i) {
+                if (i != 0) {
+                    sb.append(',');
+                }
+                sb.append(visibleVols[i]);
+            }
+            argsForZygote.add(sb.toString());
+        }
+
         argsForZygote.add(processClass);
 
         if (extraArgs != null) {
@@ -746,7 +780,8 @@
             result = startViaZygote(processClass, niceName, uid, gid,
                     gids, runtimeFlags, 0 /* mountExternal */, 0 /* targetSdkVersion */, seInfo,
                     abi, instructionSet, null /* appDataDir */, null /* invokeWith */,
-                    true /* startChildZygote */, null /* packageName */, extraArgs);
+                    true /* startChildZygote */, null /* packageName */,
+                    null /* packagesForUid */, null /* visibleVolumes */, extraArgs);
         } catch (ZygoteStartFailedEx ex) {
             throw new RuntimeException("Starting child-zygote through Zygote failed", ex);
         }
diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java
index d850e27..1f54ea5 100644
--- a/core/java/android/os/storage/StorageManagerInternal.java
+++ b/core/java/android/os/storage/StorageManagerInternal.java
@@ -89,8 +89,13 @@
      * @param appId The appId for the given package.
      * @param sharedUserId The sharedUserId for given package if it specified
      *      {@code android:sharedUserId} in the manifest, otherwise {@code null}
-     * @param userId
+     * @param userId The userId in which the storage needs to be mounted.
      */
     public abstract void mountExternalStorageForApp(String packageName, int appId,
             String sharedUserId, int userId);
+
+    /**
+     * @return Labels of storage volumes that are visible to the given userId.
+     */
+    public abstract String[] getVisibleVolumesForUser(int userId);
 }
diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java
index afd38369..e55afb6 100644
--- a/core/java/android/os/storage/VolumeInfo.java
+++ b/core/java/android/os/storage/VolumeInfo.java
@@ -157,7 +157,7 @@
     public final DiskInfo disk;
     public final String partGuid;
     public int mountFlags = 0;
-    public int mountUserId = -1;
+    public int mountUserId = UserHandle.USER_NULL;
     @UnsupportedAppUsage
     public int state = STATE_UNMOUNTED;
     public String fsType;
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 82459b1..828fd73 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -1318,18 +1318,6 @@
         }
 
         public static final class Media implements AudioColumns {
-
-            private static final String[] EXTERNAL_PATHS;
-
-            static {
-                String secondary_storage = System.getenv("SECONDARY_STORAGE");
-                if (secondary_storage != null) {
-                    EXTERNAL_PATHS = secondary_storage.split(":");
-                } else {
-                    EXTERNAL_PATHS = new String[0];
-                }
-            }
-
             /**
              * Get the content:// style URI for the audio media table on the
              * given volume.
@@ -1343,14 +1331,9 @@
             }
 
             public static Uri getContentUriForPath(String path) {
-                for (String ep : EXTERNAL_PATHS) {
-                    if (path.startsWith(ep)) {
-                        return EXTERNAL_CONTENT_URI;
-                    }
-                }
-
-                return (path.startsWith(Environment.getExternalStorageDirectory().getPath()) ?
-                        EXTERNAL_CONTENT_URI : INTERNAL_CONTENT_URI);
+                return (path.startsWith(
+                        Environment.getStorageDirectory().getAbsolutePath() + "/")
+                        ? EXTERNAL_CONTENT_URI : INTERNAL_CONTENT_URI);
             }
 
             /**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ad8626c..1d3cf19 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7267,12 +7267,12 @@
         private static final Validator DOZE_DOUBLE_TAP_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR;
 
         /**
-         * Whether the device should pulse on reach gesture.
+         * Gesture that wakes up the lock screen.
          * @hide
          */
-        public static final String DOZE_REACH_GESTURE = "doze_reach_gesture";
+        public static final String DOZE_WAKE_LOCK_SCREEN_GESTURE = "doze_wake_lock_screen_gesture";
 
-        private static final Validator DOZE_REACH_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR;
+        private static final Validator DOZE_WAKE_LOCK_SCREEN_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR;
 
         /**
          * Gesture that wakes up the display, showing the ambient version of the status bar.
@@ -8193,7 +8193,7 @@
             DOZE_ALWAYS_ON,
             DOZE_PICK_UP_GESTURE,
             DOZE_DOUBLE_TAP_GESTURE,
-            DOZE_REACH_GESTURE,
+            DOZE_WAKE_LOCK_SCREEN_GESTURE,
             DOZE_WAKE_SCREEN_GESTURE,
             NFC_PAYMENT_DEFAULT_COMPONENT,
             AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN,
@@ -8341,7 +8341,7 @@
             VALIDATORS.put(DOZE_ALWAYS_ON, DOZE_ALWAYS_ON_VALIDATOR);
             VALIDATORS.put(DOZE_PICK_UP_GESTURE, DOZE_PICK_UP_GESTURE_VALIDATOR);
             VALIDATORS.put(DOZE_DOUBLE_TAP_GESTURE, DOZE_DOUBLE_TAP_GESTURE_VALIDATOR);
-            VALIDATORS.put(DOZE_REACH_GESTURE, DOZE_REACH_GESTURE_VALIDATOR);
+            VALIDATORS.put(DOZE_WAKE_LOCK_SCREEN_GESTURE, DOZE_WAKE_LOCK_SCREEN_GESTURE_VALIDATOR);
             VALIDATORS.put(DOZE_WAKE_SCREEN_GESTURE, DOZE_WAKE_SCREEN_GESTURE_VALIDATOR);
             VALIDATORS.put(NFC_PAYMENT_DEFAULT_COMPONENT, NFC_PAYMENT_DEFAULT_COMPONENT_VALIDATOR);
             VALIDATORS.put(AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN,
@@ -9604,8 +9604,7 @@
          * Use the old dnsmasq DHCP server for tethering instead of the framework implementation.
          *
          * Integer values are interpreted as boolean, and the absence of an explicit setting
-         * is interpreted as |true|.
-         * TODO: make the default |false|
+         * is interpreted as |false|.
          * @hide
          */
         public static final String TETHER_ENABLE_LEGACY_DHCP_SERVER =
diff --git a/core/java/android/transition/ChangeImageTransform.java b/core/java/android/transition/ChangeImageTransform.java
index d7a9120..9fa9961 100644
--- a/core/java/android/transition/ChangeImageTransform.java
+++ b/core/java/android/transition/ChangeImageTransform.java
@@ -97,22 +97,13 @@
         values.put(PROPNAME_BOUNDS, bounds);
         Matrix matrix;
         ImageView.ScaleType scaleType = imageView.getScaleType();
-        if (scaleType == ImageView.ScaleType.FIT_XY) {
-            matrix = imageView.getImageMatrix();
-            if (!matrix.isIdentity()) {
-                matrix = new Matrix(matrix);
-            } else {
-                int drawableWidth = drawable.getIntrinsicWidth();
-                int drawableHeight = drawable.getIntrinsicHeight();
-                if (drawableWidth > 0 && drawableHeight > 0) {
-                    float scaleX = ((float) bounds.width()) / drawableWidth;
-                    float scaleY = ((float) bounds.height()) / drawableHeight;
-                    matrix = new Matrix();
-                    matrix.setScale(scaleX, scaleY);
-                } else {
-                    matrix = null;
-                }
-            }
+        int drawableWidth = drawable.getIntrinsicWidth();
+        int drawableHeight = drawable.getIntrinsicHeight();
+        if (scaleType == ImageView.ScaleType.FIT_XY && drawableWidth > 0 && drawableHeight > 0) {
+            float scaleX = ((float) bounds.width()) / drawableWidth;
+            float scaleY = ((float) bounds.height()) / drawableHeight;
+            matrix = new Matrix();
+            matrix.setScale(scaleX, scaleY);
         } else {
             matrix = new Matrix(imageView.getImageMatrix());
         }
@@ -152,17 +143,13 @@
         }
         Rect startBounds = (Rect) startValues.values.get(PROPNAME_BOUNDS);
         Rect endBounds = (Rect) endValues.values.get(PROPNAME_BOUNDS);
-        if (startBounds == null || endBounds == null) {
+        Matrix startMatrix = (Matrix) startValues.values.get(PROPNAME_MATRIX);
+        Matrix endMatrix = (Matrix) endValues.values.get(PROPNAME_MATRIX);
+        if (startBounds == null || endBounds == null || startMatrix == null || endMatrix == null) {
             return null;
         }
 
-        Matrix startMatrix = (Matrix) startValues.values.get(PROPNAME_MATRIX);
-        Matrix endMatrix = (Matrix) endValues.values.get(PROPNAME_MATRIX);
-
-        boolean matricesEqual = (startMatrix == null && endMatrix == null) ||
-                (startMatrix != null && startMatrix.equals(endMatrix));
-
-        if (startBounds.equals(endBounds) && matricesEqual) {
+        if (startBounds.equals(endBounds) && startMatrix.equals(endMatrix)) {
             return null;
         }
 
@@ -172,15 +159,9 @@
         int drawableHeight = drawable.getIntrinsicHeight();
 
         ObjectAnimator animator;
-        if (drawableWidth == 0 || drawableHeight == 0) {
+        if (drawableWidth <= 0 || drawableHeight <= 0) {
             animator = createNullAnimator(imageView);
         } else {
-            if (startMatrix == null) {
-                startMatrix = Matrix.IDENTITY_MATRIX;
-            }
-            if (endMatrix == null) {
-                endMatrix = Matrix.IDENTITY_MATRIX;
-            }
             ANIMATED_TRANSFORM_PROPERTY.set(imageView, startMatrix);
             animator = createMatrixAnimator(imageView, startMatrix, endMatrix);
         }
@@ -189,7 +170,7 @@
 
     private ObjectAnimator createNullAnimator(ImageView imageView) {
         return ObjectAnimator.ofObject(imageView, ANIMATED_TRANSFORM_PROPERTY,
-                NULL_MATRIX_EVALUATOR, null, null);
+                NULL_MATRIX_EVALUATOR, Matrix.IDENTITY_MATRIX, Matrix.IDENTITY_MATRIX);
     }
 
     private ObjectAnimator createMatrixAnimator(final ImageView imageView, Matrix startMatrix,
diff --git a/core/java/android/transition/TransitionManager.java b/core/java/android/transition/TransitionManager.java
index c4ef77a..4608e20 100644
--- a/core/java/android/transition/TransitionManager.java
+++ b/core/java/android/transition/TransitionManager.java
@@ -188,8 +188,12 @@
 
         final ViewGroup sceneRoot = scene.getSceneRoot();
         if (!sPendingTransitions.contains(sceneRoot)) {
+            Scene oldScene = Scene.getCurrentScene(sceneRoot);
             if (transition == null) {
-                exitPreviousScene(sceneRoot);
+                // Notify old scene that it is being exited
+                if (oldScene != null) {
+                    oldScene.exit();
+                }
 
                 scene.enter();
             } else {
@@ -198,7 +202,6 @@
                 Transition transitionClone = transition.clone();
                 transitionClone.setSceneRoot(sceneRoot);
 
-                Scene oldScene = Scene.getCurrentScene(sceneRoot);
                 if (oldScene != null && oldScene.isCreatedFromLayoutResource()) {
                     transitionClone.setCanRemoveViews(true);
                 }
@@ -212,14 +215,6 @@
         }
     }
 
-    private static void exitPreviousScene(final ViewGroup sceneRoot) {
-        // Notify previous scene that it is being exited
-        final Scene previousScene = Scene.getCurrentScene(sceneRoot);
-        if (previousScene != null) {
-            previousScene.exit();
-        }
-    }
-
     @UnsupportedAppUsage
     private static ArrayMap<ViewGroup, ArrayList<Transition>> getRunningTransitions() {
         WeakReference<ArrayMap<ViewGroup, ArrayList<Transition>>> runningTransitions =
@@ -349,7 +344,11 @@
             transition.captureValues(sceneRoot, true);
         }
 
-        exitPreviousScene(sceneRoot);
+        // Notify previous scene that it is being exited
+        Scene previousScene = Scene.getCurrentScene(sceneRoot);
+        if (previousScene != null) {
+            previousScene.exit();
+        }
     }
 
     /**
diff --git a/core/java/android/util/ArrayMap.java b/core/java/android/util/ArrayMap.java
index 5108a79..294a179 100644
--- a/core/java/android/util/ArrayMap.java
+++ b/core/java/android/util/ArrayMap.java
@@ -71,19 +71,19 @@
     /**
      * Maximum number of entries to have in array caches.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail.
     private static final int CACHE_SIZE = 10;
 
     /**
      * Special hash array value that indicates the container is immutable.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail.
     static final int[] EMPTY_IMMUTABLE_INTS = new int[0];
 
     /**
      * @hide Special immutable empty ArrayMap.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 28) // Use your own singleton empty map.
     public static final ArrayMap EMPTY = new ArrayMap<>(-1);
 
     /**
@@ -92,21 +92,21 @@
      * The first entry in the array is a pointer to the next array in the
      * list; the second entry is a pointer to the int[] hash code array for it.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail.
     static Object[] mBaseCache;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail.
     static int mBaseCacheSize;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail.
     static Object[] mTwiceBaseCache;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail.
     static int mTwiceBaseCacheSize;
 
     final boolean mIdentityHashCode;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 28) // Hashes are an implementation detail. Use public key/value API.
     int[] mHashes;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 28) // Storage is an implementation detail. Use public key/value API.
     Object[] mArray;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 28) // Use size()
     int mSize;
     MapCollections<K, V> mCollections;
 
@@ -122,7 +122,7 @@
         }
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 28) // Hashes are an implementation detail. Use indexOfKey(Object).
     int indexOf(Object key, int hash) {
         final int N = mSize;
 
@@ -161,7 +161,7 @@
         return ~end;
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 28) // Use indexOf(null)
     int indexOfNull() {
         final int N = mSize;
 
@@ -200,7 +200,7 @@
         return ~end;
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail.
     private void allocArrays(final int size) {
         if (mHashes == EMPTY_IMMUTABLE_INTS) {
             throw new UnsupportedOperationException("ArrayMap is immutable");
@@ -239,7 +239,7 @@
         mArray = new Object[size<<1];
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail.
     private static void freeArrays(final int[] hashes, final Object[] array, final int size) {
         if (hashes.length == (BASE_SIZE*2)) {
             synchronized (ArrayMap.class) {
@@ -393,8 +393,15 @@
                 : indexOf(key, mIdentityHashCode ? System.identityHashCode(key) : key.hashCode());
     }
 
-    @UnsupportedAppUsage
-    int indexOfValue(Object value) {
+    /**
+     * Returns an index for which {@link #valueAt} would return the
+     * specified value, or a negative number if no keys map to the
+     * specified value.
+     * Beware that this is a linear search, unlike lookups by key,
+     * and that multiple keys can map to the same value and this will
+     * find only one of them.
+     */
+    public int indexOfValue(Object value) {
         final int N = mSize*2;
         final Object[] array = mArray;
         if (value == null) {
@@ -551,7 +558,7 @@
      * The array must already be large enough to contain the item.
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 28) // Storage is an implementation detail. Use put(K, V).
     public void append(K key, V value) {
         int index = mSize;
         final int hash = key == null ? 0
diff --git a/core/java/android/util/ArraySet.java b/core/java/android/util/ArraySet.java
index 526a950..d74a0fe 100644
--- a/core/java/android/util/ArraySet.java
+++ b/core/java/android/util/ArraySet.java
@@ -71,15 +71,15 @@
     static int sTwiceBaseCacheSize;
 
     final boolean mIdentityHashCode;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 28) // Hashes are an implementation detail. Use public API.
     int[] mHashes;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 28) // Storage is an implementation detail. Use public API.
     Object[] mArray;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 28) // Use size()
     int mSize;
     MapCollections<E, E> mCollections;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 28) // Hashes are an implementation detail. Use indexOfKey(Object).
     private int indexOf(Object key, int hash) {
         final int N = mSize;
 
@@ -118,7 +118,7 @@
         return ~end;
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 28) // Use indexOf(null)
     private int indexOfNull() {
         final int N = mSize;
 
@@ -157,7 +157,7 @@
         return ~end;
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail.
     private void allocArrays(final int size) {
         if (size == (BASE_SIZE * 2)) {
             synchronized (ArraySet.class) {
@@ -215,7 +215,7 @@
         mArray = new Object[size];
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail.
     private static void freeArrays(final int[] hashes, final Object[] array, final int size) {
         if (hashes.length == (BASE_SIZE * 2)) {
             synchronized (ArraySet.class) {
@@ -289,9 +289,10 @@
         }
     }
 
-    /** {@hide} */
-    @UnsupportedAppUsage
-    public ArraySet(Collection<E> set) {
+    /**
+     * Create a new ArraySet with items from the given collection.
+     */
+    public ArraySet(Collection<? extends E> set) {
         this();
         if (set != null) {
             addAll(set);
diff --git a/core/java/android/util/LongSparseLongArray.java b/core/java/android/util/LongSparseLongArray.java
index d5af922..af163ac 100644
--- a/core/java/android/util/LongSparseLongArray.java
+++ b/core/java/android/util/LongSparseLongArray.java
@@ -46,11 +46,11 @@
  * @hide
  */
 public class LongSparseLongArray implements Cloneable {
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 28) // The type isn't even public.
     private long[] mKeys;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 28) // The type isn't even public.
     private long[] mValues;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 28) // The type isn't even public.
     private int mSize;
 
     /**
diff --git a/core/java/android/util/SparseArray.java b/core/java/android/util/SparseArray.java
index aa5ca35..89ea2d3 100644
--- a/core/java/android/util/SparseArray.java
+++ b/core/java/android/util/SparseArray.java
@@ -56,11 +56,11 @@
     private static final Object DELETED = new Object();
     private boolean mGarbage = false;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 28) // Use keyAt(int)
     private int[] mKeys;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 28) // Use valueAt(int), setValueAt(int, E)
     private Object[] mValues;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 28) // Use size()
     private int mSize;
 
     /**
diff --git a/core/java/android/util/SparseBooleanArray.java b/core/java/android/util/SparseBooleanArray.java
index 9c6b969..d4c4095 100644
--- a/core/java/android/util/SparseBooleanArray.java
+++ b/core/java/android/util/SparseBooleanArray.java
@@ -185,7 +185,9 @@
         return mValues[index];
     }
 
-    /** @hide */
+    /**
+     * Directly set the value at a particular index.
+     */
     public void setValueAt(int index, boolean value) {
         mValues[index] = value;
     }
@@ -304,10 +306,10 @@
         return buffer.toString();
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 28) // Use keyAt(int)
     private int[] mKeys;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 28) // Use valueAt(int), setValueAt(int, boolean)
     private boolean[] mValues;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 28) // Use size()
     private int mSize;
 }
diff --git a/core/java/android/util/SparseIntArray.java b/core/java/android/util/SparseIntArray.java
index 1954753..9e6bad1 100644
--- a/core/java/android/util/SparseIntArray.java
+++ b/core/java/android/util/SparseIntArray.java
@@ -46,11 +46,11 @@
  * order in the case of <code>valueAt(int)</code>.</p>
  */
 public class SparseIntArray implements Cloneable {
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 28) // Use keyAt(int)
     private int[] mKeys;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 28) // Use valueAt(int), setValueAt(int, int)
     private int[] mValues;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 28) // Use size()
     private int mSize;
 
     /**
@@ -191,7 +191,6 @@
 
     /**
      * Directly set the value at a particular index.
-     * @hide
      */
     public void setValueAt(int index, int value) {
         mValues[index] = value;
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
index 1203541..1bbef8e 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
@@ -410,7 +410,7 @@
                    NoSuchAlgorithmException {
         try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
             SignatureInfo signatureInfo = findSignature(apk);
-            return ApkVerityBuilder.generateApkVerity(apkPath, bufferFactory, signatureInfo);
+            return VerityBuilder.generateApkVerity(apkPath, bufferFactory, signatureInfo);
         }
     }
 
@@ -423,7 +423,7 @@
             if (vSigner.verityRootHash == null) {
                 return null;
             }
-            return ApkVerityBuilder.generateApkVerityRootHash(
+            return VerityBuilder.generateApkVerityRootHash(
                     apk, ByteBuffer.wrap(vSigner.verityRootHash), signatureInfo);
         }
     }
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
index 939522d..1471870 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
@@ -534,7 +534,7 @@
                    NoSuchAlgorithmException {
         try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
             SignatureInfo signatureInfo = findSignature(apk);
-            return ApkVerityBuilder.generateApkVerity(apkPath, bufferFactory, signatureInfo);
+            return VerityBuilder.generateApkVerity(apkPath, bufferFactory, signatureInfo);
         }
     }
 
@@ -547,7 +547,7 @@
             if (vSigner.verityRootHash == null) {
                 return null;
             }
-            return ApkVerityBuilder.generateApkVerityRootHash(
+            return VerityBuilder.generateApkVerityRootHash(
                     apk, ByteBuffer.wrap(vSigner.verityRootHash), signatureInfo);
         }
     }
diff --git a/core/java/android/util/apk/ApkSigningBlockUtils.java b/core/java/android/util/apk/ApkSigningBlockUtils.java
index 081033a..87af5364 100644
--- a/core/java/android/util/apk/ApkSigningBlockUtils.java
+++ b/core/java/android/util/apk/ApkSigningBlockUtils.java
@@ -332,7 +332,7 @@
         try {
             byte[] expectedRootHash = parseVerityDigestAndVerifySourceLength(expectedDigest,
                     apk.length(), signatureInfo);
-            ApkVerityBuilder.ApkVerityResult verity = ApkVerityBuilder.generateApkVerityTree(apk,
+            VerityBuilder.VerityResult verity = VerityBuilder.generateApkVerityTree(apk,
                     signatureInfo, new ByteBufferFactory() {
                         @Override
                         public ByteBuffer create(int capacity) {
diff --git a/core/java/android/util/apk/ApkVerityBuilder.java b/core/java/android/util/apk/VerityBuilder.java
similarity index 93%
rename from core/java/android/util/apk/ApkVerityBuilder.java
rename to core/java/android/util/apk/VerityBuilder.java
index edd09f8..443bbd8 100644
--- a/core/java/android/util/apk/ApkVerityBuilder.java
+++ b/core/java/android/util/apk/VerityBuilder.java
@@ -29,19 +29,18 @@
 import java.util.ArrayList;
 
 /**
- * ApkVerityBuilder builds the APK verity tree and the verity header.  The generated tree format can
- * be stored on disk for apk-verity setup and used by kernel.  Note that since the current
- * implementation is different from the upstream, we call this implementation apk-verity instead of
- * fs-verity.
+ * VerityBuilder builds the verity Merkle tree and other metadata.  The generated tree format can
+ * be stored on disk for fs-verity setup and used by kernel.  The builder support standard
+ * fs-verity, and Android specific apk-verity that requires additional kernel patches.
  *
- * <p>Unlike a regular Merkle tree, APK verity tree does not cover the content fully. Due to
- * the existing APK format, it has to skip APK Signing Block and also has some special treatment for
- * the "Central Directory offset" field of ZIP End of Central Directory.
+ * <p>Unlike a regular Merkle tree of fs-verity, the apk-verity tree does not cover the file content
+ * fully, and has to skip APK Signing Block with some special treatment for the "Central Directory
+ * offset" field of ZIP End of Central Directory.
  *
  * @hide
  */
-public abstract class ApkVerityBuilder {
-    private ApkVerityBuilder() {}
+public abstract class VerityBuilder {
+    private VerityBuilder() {}
 
     private static final int CHUNK_SIZE_BYTES = 4096;  // Typical Linux block size
     private static final int DIGEST_SIZE_BYTES = 32;  // SHA-256 size
@@ -52,7 +51,7 @@
     private static final byte[] DEFAULT_SALT = new byte[8];
 
     /** Result generated by the builder. */
-    public static class ApkVerityResult {
+    public static class VerityResult {
         /** Raw fs-verity metadata and Merkle tree ready to be deployed on disk. */
         public final ByteBuffer verityData;
 
@@ -62,7 +61,7 @@
         /** Root hash of the Merkle tree. */
         public final byte[] rootHash;
 
-        private ApkVerityResult(ByteBuffer verityData, int merkleTreeSize, byte[] rootHash) {
+        private VerityResult(ByteBuffer verityData, int merkleTreeSize, byte[] rootHash) {
             this.verityData = verityData;
             this.merkleTreeSize = merkleTreeSize;
             this.rootHash = rootHash;
@@ -74,14 +73,14 @@
      * ByteBuffer} created by the {@link ByteBufferFactory}.  The output is suitable to be used as
      * the on-disk format for fs-verity to use.
      *
-     * @return ApkVerityResult containing a buffer with the generated Merkle tree stored at the
+     * @return VerityResult containing a buffer with the generated Merkle tree stored at the
      *         front, the tree size, and the calculated root hash.
      */
     @NonNull
-    public static ApkVerityResult generateFsVerityTree(@NonNull RandomAccessFile apk,
+    public static VerityResult generateFsVerityTree(@NonNull RandomAccessFile apk,
             @NonNull ByteBufferFactory bufferFactory)
             throws IOException, SecurityException, NoSuchAlgorithmException, DigestException {
-        return generateVerityTree(apk, bufferFactory, null /* signatureInfo */,
+        return generateVerityTreeInternal(apk, bufferFactory, null /* signatureInfo */,
                 false /* skipSigningBlock */);
     }
 
@@ -91,18 +90,19 @@
      * Block specificed in {@code signatureInfo}.  The output is suitable to be used as the on-disk
      * format for fs-verity to use (with elide and patch extensions).
      *
-     * @return ApkVerityResult containing a buffer with the generated Merkle tree stored at the
+     * @return VerityResult containing a buffer with the generated Merkle tree stored at the
      *         front, the tree size, and the calculated root hash.
      */
     @NonNull
-    public static ApkVerityResult generateApkVerityTree(@NonNull RandomAccessFile apk,
+    public static VerityResult generateApkVerityTree(@NonNull RandomAccessFile apk,
             @Nullable SignatureInfo signatureInfo, @NonNull ByteBufferFactory bufferFactory)
             throws IOException, SecurityException, NoSuchAlgorithmException, DigestException {
-        return generateVerityTree(apk, bufferFactory, signatureInfo, true /* skipSigningBlock */);
+        return generateVerityTreeInternal(apk, bufferFactory, signatureInfo,
+                true /* skipSigningBlock */);
     }
 
     @NonNull
-    private static ApkVerityResult generateVerityTree(@NonNull RandomAccessFile apk,
+    private static VerityResult generateVerityTreeInternal(@NonNull RandomAccessFile apk,
             @NonNull ByteBufferFactory bufferFactory, @Nullable SignatureInfo signatureInfo,
             boolean skipSigningBlock)
             throws IOException, SecurityException, NoSuchAlgorithmException, DigestException {
@@ -124,7 +124,7 @@
         byte[] salt = skipSigningBlock ? DEFAULT_SALT : null;
         byte[] apkRootHash = generateVerityTreeInternal(apk, signatureInfo, salt, levelOffset,
                 tree, skipSigningBlock);
-        return new ApkVerityResult(output, merkleTreeSize, apkRootHash);
+        return new VerityResult(output, merkleTreeSize, apkRootHash);
     }
 
     static void generateApkVerityFooter(@NonNull RandomAccessFile apk,
@@ -173,7 +173,7 @@
             throws IOException, SignatureNotFoundException, SecurityException, DigestException,
                    NoSuchAlgorithmException {
         try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
-            ApkVerityResult result = generateVerityTree(apk, bufferFactory, signatureInfo,
+            VerityResult result = generateVerityTreeInternal(apk, bufferFactory, signatureInfo,
                     true /* skipSigningBlock */);
             ByteBuffer footer = slice(result.verityData, result.merkleTreeSize,
                     result.verityData.limit());
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 78e6dd8..b2944d6 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -15636,7 +15636,7 @@
     /**
      * Sets the visual z position of this view, in pixels. This is equivalent to setting the
      * {@link #setTranslationZ(float) translationZ} property to be the difference between
-     * the x value passed in and the current {@link #getElevation() elevation} property.
+     * the z value passed in and the current {@link #getElevation() elevation} property.
      *
      * @param z The visual z position of this view, in pixels.
      */
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 0fef9a5..12cc54d 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -1338,7 +1338,9 @@
             return;
         }
         if (matrix == null) {
-            mDrawable.setBounds(0, 0, getWidth(), getHeight());
+            final int vwidth = getWidth() - mPaddingLeft - mPaddingRight;
+            final int vheight = getHeight() - mPaddingTop - mPaddingBottom;
+            mDrawable.setBounds(0, 0, vwidth, vheight);
         } else {
             mDrawable.setBounds(0, 0, mDrawableWidth, mDrawableHeight);
             if (mDrawMatrix == null) {
diff --git a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
index cb282b6..0080ace 100644
--- a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
+++ b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
@@ -66,13 +66,13 @@
         return !TextUtils.isEmpty(doubleTapSensorType());
     }
 
-    public boolean reachGestureEnabled(int user) {
-        return boolSettingDefaultOn(Settings.Secure.DOZE_REACH_GESTURE, user)
-                && reachGestureAvailable();
+    public boolean wakeLockScreenGestureEnabled(int user) {
+        return boolSettingDefaultOn(Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE, user)
+                && wakeLockScreenGestureAvailable();
     }
 
-    public boolean reachGestureAvailable() {
-        return !TextUtils.isEmpty(reachSensorType());
+    public boolean wakeLockScreenGestureAvailable() {
+        return !TextUtils.isEmpty(wakeLockScreenSensorType());
     }
 
     public boolean wakeScreenGestureEnabled(int user) {
@@ -92,8 +92,8 @@
         return mContext.getResources().getString(R.string.config_dozeLongPressSensorType);
     }
 
-    public String reachSensorType() {
-        return mContext.getResources().getString(R.string.config_dozeReachSensorType);
+    public String wakeLockScreenSensorType() {
+        return mContext.getResources().getString(R.string.config_dozeWakeLockScreenSensorType);
     }
 
     public String wakeScreenSensorType() {
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 927322e..98b7b5d 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -133,15 +133,16 @@
      * if this is the parent, or -1 on error.
      */
     public static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
-          int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
-          int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
-          String packageName) {
+            int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
+            int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
+            String packageName, String[] packagesForUid, String[] visibleVolIds) {
         VM_HOOKS.preFork();
         // Resets nice priority for zygote process.
         resetNicePriority();
         int pid = nativeForkAndSpecialize(
                   uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
-                  fdsToIgnore, startChildZygote, instructionSet, appDataDir, packageName);
+                  fdsToIgnore, startChildZygote, instructionSet, appDataDir, packageName,
+                  packagesForUid, visibleVolIds);
         // Enable tracing as soon as possible for the child process.
         if (pid == 0) {
             Trace.setTracingEnabled(true, runtimeFlags);
@@ -154,9 +155,9 @@
     }
 
     native private static int nativeForkAndSpecialize(int uid, int gid, int[] gids,int runtimeFlags,
-          int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
-          int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
-          String packageName);
+            int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
+            int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
+            String packageName, String[] packagesForUid, String[] visibleVolIds);
 
     /**
      * Called to do any initialization before starting an application.
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 06c41d8..4a94ec4 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -241,7 +241,8 @@
         pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
                 parsedArgs.runtimeFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
                 parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.startChildZygote,
-                parsedArgs.instructionSet, parsedArgs.appDataDir, parsedArgs.packageName);
+                parsedArgs.instructionSet, parsedArgs.appDataDir, parsedArgs.packageName,
+                parsedArgs.packagesForUid, parsedArgs.visibleVolIds);
 
         try {
             if (pid == 0) {
@@ -432,6 +433,12 @@
         /** from --package-name */
         String packageName;
 
+        /** from --packages-for-uid */
+        String[] packagesForUid;
+
+        /** from --visible-vols */
+        String[] visibleVolIds;
+
         /**
          * Any args after and including the first non-option arg
          * (or after a '--')
@@ -687,6 +694,10 @@
                         throw new IllegalArgumentException("Duplicate arg specified");
                     }
                     packageName = arg.substring(arg.indexOf('=') + 1);
+                } else if (arg.startsWith("--packages-for-uid=")) {
+                    packagesForUid = arg.substring(arg.indexOf('=') + 1).split(",");
+                } else if (arg.startsWith("--visible-vols=")) {
+                    visibleVolIds = arg.substring(arg.indexOf('=') + 1).split(",");
                 } else {
                     break;
                 }
diff --git a/core/jni/android_net_LocalSocketImpl.cpp b/core/jni/android_net_LocalSocketImpl.cpp
index 6df23f7..a1f2377 100644
--- a/core/jni/android_net_LocalSocketImpl.cpp
+++ b/core/jni/android_net_LocalSocketImpl.cpp
@@ -58,6 +58,11 @@
     int ret;
     int fd;
 
+    if (name == NULL) {
+        jniThrowNullPointerException(env, NULL);
+        return;
+    }
+
     fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
 
     if (env->ExceptionCheck()) {
diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp
index e3bec3c..b70485d 100644
--- a/core/jni/android_os_GraphicsEnvironment.cpp
+++ b/core/jni/android_os_GraphicsEnvironment.cpp
@@ -28,9 +28,12 @@
     android::GraphicsEnv::getInstance().setDriverPath(pathChars.c_str());
 }
 
-void setAnglePath(JNIEnv* env, jobject clazz, jstring path) {
+void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jstring appName, jstring appPref, jboolean devOptIn) {
     ScopedUtfChars pathChars(env, path);
-    android::GraphicsEnv::getInstance().setAnglePath(pathChars.c_str());
+    ScopedUtfChars appNameChars(env, appName);
+    ScopedUtfChars appPrefChars(env, appPref);
+    android::GraphicsEnv::getInstance().setAngleInfo(pathChars.c_str(), appNameChars.c_str(),
+            appPrefChars.c_str(), devOptIn);
 }
 
 void setLayerPaths_native(JNIEnv* env, jobject clazz, jobject classLoader, jstring layerPaths) {
@@ -49,7 +52,7 @@
 
 const JNINativeMethod g_methods[] = {
     { "setDriverPath", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDriverPath) },
-    { "setAnglePath", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setAnglePath) },
+    { "setAngleInfo", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V", reinterpret_cast<void*>(setAngleInfo_native) },
     { "setLayerPaths", "(Ljava/lang/ClassLoader;Ljava/lang/String;)V", reinterpret_cast<void*>(setLayerPaths_native) },
     { "setDebugLayers", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDebugLayers_native) },
 };
diff --git a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
index c15b7ee..109e65c 100644
--- a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
+++ b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
@@ -285,10 +285,6 @@
 static int readNetworkStatsDetail(JNIEnv* env, jclass clazz, jobject stats, jstring path,
                                   jint limitUid, jobjectArray limitIfacesObj, jint limitTag,
                                   jboolean useBpfStats) {
-    ScopedUtfChars path8(env, path);
-    if (path8.c_str() == NULL) {
-        return -1;
-    }
 
     std::vector<std::string> limitIfaces;
     if (limitIfacesObj != NULL && env->GetArrayLength(limitIfacesObj) > 0) {
@@ -308,6 +304,11 @@
         if (parseBpfNetworkStatsDetail(&lines, limitIfaces, limitTag, limitUid) < 0)
             return -1;
     } else {
+        ScopedUtfChars path8(env, path);
+        if (path8.c_str() == NULL) {
+            ALOGE("the qtaguid legacy path is invalid: %s", path8.c_str());
+            return -1;
+        }
         if (legacyReadNetworkStatsDetail(&lines, limitIfaces, limitTag,
                                          limitUid, path8.c_str()) < 0)
             return -1;
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 364393e..1f95862 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -382,11 +382,10 @@
     return 0;
 }
 
-static bool createPkgSandbox(uid_t uid, const char* package_name, std::string& pkg_sandbox_dir,
-        std::string* error_msg) {
+static bool createPkgSandbox(uid_t uid, const std::string& package_name, std::string* error_msg) {
     // Create /mnt/user/0/package/<package-name>
     userid_t user_id = multiuser_get_user_id(uid);
-    StringAppendF(&pkg_sandbox_dir, "/%d", user_id);
+    std::string pkg_sandbox_dir = StringPrintf("/mnt/user/%d", user_id);
     if (fs_prepare_dir(pkg_sandbox_dir.c_str(), 0751, AID_ROOT, AID_ROOT) != 0) {
         *error_msg = CREATE_ERROR("fs_prepare_dir failed on %s", pkg_sandbox_dir.c_str());
         return false;
@@ -396,7 +395,7 @@
         *error_msg = CREATE_ERROR("fs_prepare_dir failed on %s", pkg_sandbox_dir.c_str());
         return false;
     }
-    StringAppendF(&pkg_sandbox_dir, "/%s", package_name);
+    StringAppendF(&pkg_sandbox_dir, "/%s", package_name.c_str());
     if (fs_prepare_dir(pkg_sandbox_dir.c_str(), 0755, uid, uid) != 0) {
         *error_msg = CREATE_ERROR("fs_prepare_dir failed on %s", pkg_sandbox_dir.c_str());
         return false;
@@ -404,10 +403,51 @@
     return true;
 }
 
+static bool mountPkgSpecificDir(const std::string& mntSourceRoot,
+        const std::string& mntTargetRoot, const std::string& packageName,
+        const char* dirName, std::string* error_msg) {
+    std::string mntSourceDir = StringPrintf("%s/Android/%s/%s",
+            mntSourceRoot.c_str(), dirName, packageName.c_str());
+    std::string mntTargetDir = StringPrintf("%s/Android/%s/%s",
+            mntTargetRoot.c_str(), dirName, packageName.c_str());
+    if (TEMP_FAILURE_RETRY(mount(mntSourceDir.c_str(), mntTargetDir.c_str(),
+            nullptr, MS_BIND | MS_REC, nullptr)) == -1) {
+        *error_msg = CREATE_ERROR("Failed to mount %s to %s: %s",
+                mntSourceDir.c_str(), mntTargetDir.c_str(), strerror(errno));
+        return false;
+    }
+    if (TEMP_FAILURE_RETRY(mount(nullptr, mntTargetDir.c_str(),
+            nullptr, MS_SLAVE | MS_REC, nullptr)) == -1) {
+        *error_msg = CREATE_ERROR("Failed to set MS_SLAVE for %s", mntTargetDir.c_str());
+        return false;
+    }
+    return true;
+}
+
+static bool preparePkgSpecificDirs(const std::vector<std::string>& packageNames,
+        const std::vector<std::string>& volumeLabels, userid_t userId, std::string* error_msg) {
+    for (auto& label : volumeLabels) {
+        std::string mntSource = StringPrintf("/mnt/runtime/write/%s", label.c_str());
+        std::string mntTarget = StringPrintf("/storage/%s", label.c_str());
+        if (label == "emulated") {
+            StringAppendF(&mntSource, "/%d", userId);
+            StringAppendF(&mntTarget, "/%d", userId);
+        }
+        for (auto& package : packageNames) {
+            mountPkgSpecificDir(mntSource, mntTarget, package, "data", error_msg);
+            mountPkgSpecificDir(mntSource, mntTarget, package, "media", error_msg);
+            mountPkgSpecificDir(mntSource, mntTarget, package, "obb", error_msg);
+        }
+    }
+    return true;
+}
+
 // Create a private mount namespace and bind mount appropriate emulated
 // storage for the given user.
 static bool MountEmulatedStorage(uid_t uid, jint mount_mode,
-        bool force_mount_namespace, std::string* error_msg, const char* package_name) {
+        bool force_mount_namespace, std::string* error_msg, const std::string& package_name,
+        const std::vector<std::string>& packages_for_uid,
+        const std::vector<std::string>& visible_vol_ids) {
     // See storage config details at http://source.android.com/tech/storage/
 
     String8 storageSource;
@@ -459,12 +499,25 @@
                 return false;
             }
         } else {
-            if (package_name == nullptr) {
+            if (package_name.empty()) {
                 return true;
             }
-            std::string pkgSandboxDir("/mnt/user");
-            if (!createPkgSandbox(uid, package_name, pkgSandboxDir, error_msg)) {
-                return false;
+            userid_t user_id = multiuser_get_user_id(uid);
+            std::string pkgSandboxDir = StringPrintf("/mnt/user/%d/package/%s",
+                    user_id, package_name.c_str());
+            struct stat sb;
+            bool sandboxAlreadyCreated = true;
+            if (TEMP_FAILURE_RETRY(lstat(pkgSandboxDir.c_str(), &sb)) == -1) {
+                if (errno == ENOENT) {
+                    ALOGD("Sandbox not yet created for %s", pkgSandboxDir.c_str());
+                    sandboxAlreadyCreated = false;
+                    if (!createPkgSandbox(uid, package_name, error_msg)) {
+                        return false;
+                    }
+                } else {
+                    ALOGE("Failed to lstat %s", pkgSandboxDir.c_str());
+                    return false;
+                }
             }
             if (TEMP_FAILURE_RETRY(mount(pkgSandboxDir.c_str(), "/storage",
                     nullptr, MS_BIND | MS_REC | MS_SLAVE, nullptr)) == -1) {
@@ -472,6 +525,15 @@
                         pkgSandboxDir.c_str(), strerror(errno));
                 return false;
             }
+            // If the sandbox was already created by vold, only then set up the bind mounts for
+            // pkg specific directories. Otherwise, leave as is and bind mounts will be taken
+            // care of by vold later.
+            if (sandboxAlreadyCreated) {
+                if (!preparePkgSpecificDirs(packages_for_uid, visible_vol_ids,
+                        user_id, error_msg)) {
+                    return false;
+                }
+            }
         }
     } else {
         if (TEMP_FAILURE_RETRY(mount(storageSource.string(), "/storage",
@@ -611,7 +673,8 @@
                              jlong permittedCapabilities, jlong effectiveCapabilities,
                              jint mount_external, jstring java_se_info, jstring java_se_name,
                              bool is_system_server, bool is_child_zygote, jstring instructionSet,
-                             jstring dataDir, jstring packageName) {
+                             jstring dataDir, jstring packageName, jobjectArray packagesForUid,
+                             jobjectArray visibleVolIds) {
   std::string error_msg;
 
   auto fail_fn = [env, java_se_name, is_system_server](const std::string& msg)
@@ -661,17 +724,33 @@
     ALOGW("Native bridge will not be used because dataDir == NULL.");
   }
 
-  ScopedUtfChars* package_name = nullptr;
-  const char* package_name_c_str = nullptr;
+  std::string package_name_str("");
   if (packageName != nullptr) {
-    package_name = new ScopedUtfChars(env, packageName);
-    package_name_c_str = package_name->c_str();
+    ScopedUtfChars package(env, packageName);
+    package_name_str = package.c_str();
   } else if (is_system_server) {
-    package_name_c_str = "android";
+    package_name_str = "android";
+  }
+  std::vector<std::string> packages_for_uid;
+  if (packagesForUid != nullptr) {
+    jsize count = env->GetArrayLength(packagesForUid);
+    for (jsize i = 0; i < count; ++i) {
+      jstring package_for_uid = (jstring) env->GetObjectArrayElement(packagesForUid, i);
+      ScopedUtfChars package(env, package_for_uid);
+      packages_for_uid.push_back(package.c_str());
+    }
+  }
+  std::vector<std::string> visible_vol_ids;
+  if (visibleVolIds != nullptr) {
+    jsize count = env->GetArrayLength(visibleVolIds);
+    for (jsize i = 0; i < count; ++i) {
+      jstring visible_vol_id = (jstring) env->GetObjectArrayElement(visibleVolIds, i);
+      ScopedUtfChars vol(env, visible_vol_id);
+      visible_vol_ids.push_back(vol.c_str());
+    }
   }
   bool success = MountEmulatedStorage(uid, mount_external, use_native_bridge, &error_msg,
-      package_name_c_str);
-  delete package_name;
+      package_name_str, packages_for_uid, visible_vol_ids);
   if (!success) {
     ALOGW("Failed to mount emulated storage: %s (%s)", error_msg.c_str(), strerror(errno));
     if (errno == ENOTCONN || errno == EROFS) {
@@ -936,7 +1015,8 @@
         jint runtime_flags, jobjectArray rlimits,
         jint mount_external, jstring se_info, jstring se_name,
         jintArray fdsToClose, jintArray fdsToIgnore, jboolean is_child_zygote,
-        jstring instructionSet, jstring appDataDir, jstring packageName) {
+        jstring instructionSet, jstring appDataDir, jstring packageName,
+        jobjectArray packagesForUid, jobjectArray visibleVolIds) {
     jlong capabilities = 0;
 
     // Grant CAP_WAKE_ALARM to the Bluetooth process.
@@ -989,7 +1069,8 @@
       SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
                        capabilities, capabilities,
                        mount_external, se_info, se_name, false,
-                       is_child_zygote == JNI_TRUE, instructionSet, appDataDir, packageName);
+                       is_child_zygote == JNI_TRUE, instructionSet, appDataDir, packageName,
+                       packagesForUid, visibleVolIds);
     }
     return pid;
 }
@@ -1003,7 +1084,7 @@
       SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
                        permittedCapabilities, effectiveCapabilities,
                        MOUNT_EXTERNAL_DEFAULT, NULL, NULL, true,
-                       false, NULL, NULL, nullptr);
+                       false, NULL, NULL, nullptr, nullptr, nullptr);
   } else if (pid > 0) {
       // The zygote process checks whether the child process has died or not.
       ALOGI("System server process %d has been created", pid);
@@ -1084,7 +1165,7 @@
     { "nativeSecurityInit", "()V",
       (void *) com_android_internal_os_Zygote_nativeSecurityInit },
     { "nativeForkAndSpecialize",
-      "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
+      "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)I",
       (void *) com_android_internal_os_Zygote_nativeForkAndSpecialize },
     { "nativeForkSystemServer", "(II[II[[IJJ)I",
       (void *) com_android_internal_os_Zygote_nativeForkSystemServer },
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index f9f725a..14ed9e6 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -397,6 +397,9 @@
         // Ordered GPU debug layer list
         // i.e. <layer1>:<layer2>:...:<layerN>
         optional SettingProto debug_layers = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+        // App will load ANGLE instead of native GLES drivers.
+        optional SettingProto angle_enabled_app = 3;
     }
     optional Gpu gpu = 59;
 
diff --git a/core/proto/android/server/usagestatsservice.proto b/core/proto/android/server/usagestatsservice.proto
new file mode 100644
index 0000000..941c81f
--- /dev/null
+++ b/core/proto/android/server/usagestatsservice.proto
@@ -0,0 +1,100 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+package com.android.server.usage;
+import "frameworks/base/core/proto/android/content/configuration.proto";
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
+
+option java_multiple_files = true;
+
+message IntervalStatsProto {
+  message StringPool {
+    optional int32 size = 1;
+    repeated string strings = 2;
+  }
+
+  message CountAndTime {
+    optional int32 count = 1;
+    optional int64 time_ms = 2;
+  }
+
+  // Stores the relevant information from a UsageStats
+  message UsageStats {
+    message ChooserAction {
+      message CategoryCount {
+        optional string name = 1;
+        optional int32 count = 3;
+      }
+      optional string name = 1;
+      repeated CategoryCount counts = 3;
+    }
+    optional string package = 1;
+    // package_index contains the index + 1 of the package name in the string pool
+    optional int32 package_index = 2;
+    optional int64 last_time_active_ms = 3;
+    optional int64 total_time_active_ms = 4;
+    optional int32 last_event = 5;
+    optional int32 app_launch_count = 6;
+    repeated ChooserAction chooser_actions = 7;
+  }
+
+  // Stores the relevant information an IntervalStats will have about a Configuration
+  message Configuration {
+    optional .android.content.ConfigurationProto config = 1;
+    optional int64 last_time_active_ms = 2;
+    optional int64 total_time_active_ms = 3;
+    optional int32 count = 4;
+    optional bool active = 5;
+  }
+
+  // Stores the relevant information from a UsageEvents.Event
+  message Event {
+    optional string package = 1;
+    // package_index contains the index + 1 of the package name in the string pool
+    optional int32 package_index = 2;
+    optional string class = 3;
+    // class_index contains the index + 1 of the class name in the string pool
+    optional int32 class_index = 4;
+    optional int64 time_ms = 5;
+    optional int32 flags = 6;
+    optional int32 type = 7;
+    optional .android.content.ConfigurationProto config = 8;
+    optional string shortcut_id = 9;
+    optional int32 standby_bucket = 11;
+    optional string notification_channel = 12;
+    // notification_channel_index contains the index + 1 of the channel name in the string pool
+    optional int32 notification_channel_index = 13;
+  }
+
+  // The following fields contain supplemental data used to build IntervalStats, such as a string
+  // pool.
+  optional int64 end_time_ms = 1;
+  // stringpool contains all the package and class names used by UsageStats and Event
+  // They will hold a number that is equal to the index + 1 of their string in the pool
+  optional StringPool stringpool = 2;
+
+  // The following fields contain aggregated usage stats data
+  optional CountAndTime interactive = 10;
+  optional CountAndTime non_interactive = 11;
+  optional CountAndTime keyguard_shown = 12;
+  optional CountAndTime keyguard_hidden = 13;
+
+  // The following fields contain listed usage stats data
+  repeated UsageStats packages = 20;
+  repeated Configuration configurations = 21;
+  repeated Event event_log = 22;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 85a52d5..2a0e482 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -941,7 +941,6 @@
          Requesting this by itself is not sufficient to give you
          location access.
          <p>Protection level: dangerous
-         @hide
     -->
     <permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"
         android:permissionGroup="android.permission-group.LOCATION"
@@ -1186,9 +1185,9 @@
         android:priority="700" />
 
     <!-- Required to be able to access the camera device.
-         <p>This will automatically enforce the <a
-         href="{@docRoot}guide/topics/manifest/uses-feature-element.html">
-         <uses-feature>}</a> manifest element for <em>all</em> camera features.
+         <p>This will automatically enforce the
+         <a href="{@docRoot}guide/topics/manifest/uses-feature-element.html">
+         uses-feature</a> manifest element for <em>all</em> camera features.
          If you do not require all camera features or can properly operate if a camera
          is not available, then you must modify your manifest as appropriate in order to
          install on devices that don't support all camera features.</p>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 6ae2541..bebd489 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -531,7 +531,7 @@
     <skip />
     <string name="fingerprint_authenticated" msgid="5309333983002526448">"फ़िंगरप्रिंट की पुष्टि हो गई"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"चेहरे की पहचान की गई"</string>
-    <string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"चेहरा की पहचान की गई, कृपया पुष्टि बटन दबाएं"</string>
+    <string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"चेहरे की पहचान की गई, कृपया पुष्टि बटन दबाएं"</string>
     <string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"फ़िंगरप्रिंट हार्डवेयर उपलब्ध नहीं है."</string>
     <string name="fingerprint_error_no_space" msgid="1055819001126053318">"फ़िंगरप्रिंट को संग्रहित नहीं किया जा सका. कृपया कोई मौजूदा फ़िंगरप्रिंट निकालें."</string>
     <string name="fingerprint_error_timeout" msgid="3927186043737732875">"फ़िंगरप्रिंट का समय समाप्त हो गया. पुनः प्रयास करें."</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 41616df..912db20 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -529,7 +529,7 @@
     <string name="biometric_not_recognized" msgid="5770511773560736082">"Tanınmadı"</string>
     <string name="fingerprint_authenticated" msgid="5309333983002526448">"Parmak izi kimlik doğrulaması yapıldı"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Yüz kimliği doğrulandı"</string>
-    <string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Yüz kimliği doğrulandı, lütfen doğrula\'ya basın"</string>
+    <string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Yüz kimliği doğrulandı, lütfen onayla\'ya basın"</string>
     <string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Parmak izi donanımı kullanılamıyor."</string>
     <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Parmak izi depolanamıyor. Lütfen mevcut parmak izlerinden birini kaldırın."</string>
     <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Parmak izi için zaman aşımı oluştu. Tekrar deneyin."</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 65b8807..e0db946 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1425,7 +1425,7 @@
              at {@link android.view.inputmethod.InputConnection#performEditorAction(int)
              InputConnection.performEditorAction(int)}.
              <p>Corresponds to
-             {@link android.view.inputmethod.EditorInfo#IME_FLAG_NO_FULLSCREEN}. -->
+             {@link android.view.inputmethod.EditorInfo#IME_FLAG_NAVIGATE_PREVIOUS}. -->
         <flag name="flagNavigatePrevious" value="0x4000000" />
         <!-- Used to specify that there is something
              interesting that a forward navigation can focus on. This is like using
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 9aebf6c..0daf5a7 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2113,8 +2113,8 @@
     <!-- Type of the long press sensor. Empty if long press is not supported. -->
     <string name="config_dozeLongPressSensorType" translatable="false"></string>
 
-    <!-- Type of the reach sensor. Empty if reach is not supported. -->
-    <string name="config_dozeReachSensorType" translatable="false"></string>
+    <!-- Type of sensor that wakes up the lock screen. Empty if not supported. -->
+    <string name="config_dozeWakeLockScreenSensorType" translatable="false"></string>
 
     <!-- Type of the wake up sensor. Empty if not supported. -->
     <string name="config_dozeWakeScreenSensorType" translatable="false"></string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 9f2256a..72ae0d6 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3279,7 +3279,7 @@
   <java-symbol type="array" name="config_hideWhenDisabled_packageNames" />
 
   <java-symbol type="string" name="config_dozeLongPressSensorType" />
-  <java-symbol type="string" name="config_dozeReachSensorType" />
+  <java-symbol type="string" name="config_dozeWakeLockScreenSensorType" />
 
   <java-symbol type="array" name="config_allowedGlobalInstantAppSettings" />
   <java-symbol type="array" name="config_allowedSystemInstantAppSettings" />
diff --git a/core/tests/coretests/src/android/net/LocalSocketTest.java b/core/tests/coretests/src/android/net/LocalSocketTest.java
index 1349844..1286b13 100644
--- a/core/tests/coretests/src/android/net/LocalSocketTest.java
+++ b/core/tests/coretests/src/android/net/LocalSocketTest.java
@@ -22,6 +22,7 @@
 import android.net.LocalSocketAddress;
 import android.test.MoreAsserts;
 import android.test.suitebuilder.annotation.SmallTest;
+
 import junit.framework.TestCase;
 
 import java.io.FileDescriptor;
@@ -39,6 +40,20 @@
 
         ls = new LocalSocket();
 
+        try {
+            ls.connect(new LocalSocketAddress(null));
+            fail("Expected NullPointerException");
+        } catch (NullPointerException e) {
+            // pass
+        }
+
+        try {
+            ls.bind(new LocalSocketAddress(null));
+            fail("Expected NullPointerException");
+        } catch (NullPointerException e) {
+            // pass
+        }
+
         ls.connect(new LocalSocketAddress("android.net.LocalSocketTest"));
 
         ls1 = ss.accept();
diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java
index 4fec33f..c4dc0ad 100644
--- a/graphics/java/android/graphics/Rect.java
+++ b/graphics/java/android/graphics/Rect.java
@@ -23,8 +23,11 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
+import android.util.proto.ProtoInputStream;
 import android.util.proto.ProtoOutputStream;
+import android.util.proto.WireTypeMismatchException;
 
+import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -232,6 +235,40 @@
     }
 
     /**
+     * Read from a protocol buffer input stream.
+     * Protocol buffer message definition at {@link android.graphics.RectProto}
+     *
+     * @param proto     Stream to read the Rect object from.
+     * @param fieldId   Field Id of the Rect as defined in the parent message
+     * @hide
+     */
+    public void readFromProto(@NonNull ProtoInputStream proto, long fieldId) throws IOException,
+            WireTypeMismatchException {
+        final long token = proto.start(fieldId);
+        try {
+            while (proto.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+                switch (proto.getFieldNumber()) {
+                    case (int) RectProto.LEFT:
+                        left = proto.readInt(RectProto.LEFT);
+                        break;
+                    case (int) RectProto.TOP:
+                        top = proto.readInt(RectProto.TOP);
+                        break;
+                    case (int) RectProto.RIGHT:
+                        right = proto.readInt(RectProto.RIGHT);
+                        break;
+                    case (int) RectProto.BOTTOM:
+                        bottom = proto.readInt(RectProto.BOTTOM);
+                        break;
+                }
+            }
+        } finally {
+            // Let caller handle any exceptions
+            proto.end(token);
+        }
+    }
+
+    /**
      * Returns true if the rectangle is empty (left >= right or top >= bottom)
      */
     public final boolean isEmpty() {
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index ed4da22..340f279 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -336,7 +336,7 @@
  *
  * <table border="0" cellspacing="0" cellpadding="0">
  * <tr><td>Method Name </p></td>
- *     <td>Valid Sates </p></td>
+ *     <td>Valid States </p></td>
  *     <td>Invalid States </p></td>
  *     <td>Comments </p></td></tr>
  * <tr><td>attachAuxEffect </p></td>
diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java
index 84d246f..dfcbabe 100644
--- a/media/java/android/media/MediaPlayer2Impl.java
+++ b/media/java/android/media/MediaPlayer2Impl.java
@@ -36,8 +36,6 @@
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.Message;
-import android.os.Parcel;
-import android.os.Parcelable;
 import android.os.PersistableBundle;
 import android.os.PowerManager;
 import android.os.Process;
@@ -60,7 +58,6 @@
 import dalvik.system.CloseGuard;
 
 import libcore.io.IoBridge;
-import libcore.io.Streams;
 
 import java.io.ByteArrayOutputStream;
 import java.io.File;
@@ -335,19 +332,14 @@
                     final String msg = "Cannot set AudioAttributes to null";
                     throw new IllegalArgumentException(msg);
                 }
-                Parcel pattributes = Parcel.obtain();
-                attributes.writeToParcel(pattributes, AudioAttributes.FLATTEN_TAGS);
-                setParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES, pattributes);
-                pattributes.recycle();
+                setParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES, attributes);
             }
         });
     }
 
     @Override
     public @NonNull AudioAttributes getAudioAttributes() {
-        Parcel pattributes = getParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES);
-        AudioAttributes attributes = AudioAttributes.CREATOR.createFromParcel(pattributes);
-        pattributes.recycle();
+        AudioAttributes attributes = (AudioAttributes) getParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES);
         return attributes;
     }
 
@@ -1588,9 +1580,9 @@
      * @param value value of the parameter to be set.
      * @return true if the parameter is set successfully, false otherwise
      */
-    private native boolean setParameter(int key, Parcel value);
+    private native boolean setParameter(int key, Object value);
 
-    private native Parcel getParameter(int key);
+    private native Object getParameter(int key);
 
 
     /**
@@ -3689,7 +3681,7 @@
                       supportedSchemes[i]);
             }
 
-            Log.v(TAG, "DrmInfoImpl() Parcel psshsize: " + pssh.length +
+            Log.v(TAG, "DrmInfoImpl() psshsize: " + pssh.length +
                   " supportedDRMsCount: " + supportedDRMsCount);
         }
 
@@ -3954,7 +3946,7 @@
                     connection.setReadTimeout(TIMEOUT_MS);
 
                     connection.connect();
-                    response = Streams.readFully(connection.getInputStream());
+                    response = readInputStreamFully(connection.getInputStream());
 
                     Log.v(TAG, "HandleProvisioninig: Thread run: response " +
                             response.length + " " + response);
@@ -4034,6 +4026,29 @@
             finished = true;
         }   // run()
 
+        /**
+         * Returns a byte[] containing the remainder of 'in', closing it when done.
+         */
+        private byte[] readInputStreamFully(InputStream in) throws IOException {
+            try {
+                return readInputStreamFullyNoClose(in);
+            } finally {
+                in.close();
+            }
+        }
+
+        /**
+         * Returns a byte[] containing the remainder of 'in'.
+         */
+        private byte[] readInputStreamFullyNoClose(InputStream in) throws IOException {
+            ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+            byte[] buffer = new byte[1024];
+            int count;
+            while ((count = in.read(buffer)) != -1) {
+                bytes.write(buffer, 0, count);
+            }
+            return bytes.toByteArray();
+        }
     }   // ProvisioningThread
 
     private int HandleProvisioninig(UUID uuid) {
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 0ff2d8f..c537945 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -160,8 +160,9 @@
 
     public static final String SCANNED_BUILD_PREFS_NAME = "MediaScanBuild";
     public static final String LAST_INTERNAL_SCAN_FINGERPRINT = "lastScanFingerprint";
-    private static final String SYSTEM_SOUNDS_DIR = "/system/media/audio";
-    private static final String PRODUCT_SOUNDS_DIR = "/product/media/audio";
+    private static final String SYSTEM_SOUNDS_DIR = Environment.getRootDirectory() + "/media/audio";
+    private static final String OEM_SOUNDS_DIR = Environment.getOemDirectory() + "/media/audio";
+    private static final String PRODUCT_SOUNDS_DIR = Environment.getProductDirectory() + "/media/audio";
     private static String sLastInternalScanFingerprint;
 
     private static final String[] ID3_GENRES = {
@@ -1193,6 +1194,9 @@
         if (path.startsWith(SYSTEM_SOUNDS_DIR + ALARMS_DIR)
                 || path.startsWith(SYSTEM_SOUNDS_DIR + RINGTONES_DIR)
                 || path.startsWith(SYSTEM_SOUNDS_DIR + NOTIFICATIONS_DIR)
+                || path.startsWith(OEM_SOUNDS_DIR + ALARMS_DIR)
+                || path.startsWith(OEM_SOUNDS_DIR + RINGTONES_DIR)
+                || path.startsWith(OEM_SOUNDS_DIR + NOTIFICATIONS_DIR)
                 || path.startsWith(PRODUCT_SOUNDS_DIR + ALARMS_DIR)
                 || path.startsWith(PRODUCT_SOUNDS_DIR + RINGTONES_DIR)
                 || path.startsWith(PRODUCT_SOUNDS_DIR + NOTIFICATIONS_DIR)) {
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index 1a844cc..693a3d0 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -1490,8 +1490,8 @@
     {"_release",            "()V",                              (void *)android_media_MediaPlayer2_release},
     {"_reset",              "()V",                              (void *)android_media_MediaPlayer2_reset},
     {"_getAudioStreamType", "()I",                              (void *)android_media_MediaPlayer2_getAudioStreamType},
-    {"setParameter",        "(ILandroid/os/Parcel;)Z",          (void *)android_media_MediaPlayer2_setParameter},
-    {"getParameter",        "(I)Landroid/os/Parcel;",           (void *)android_media_MediaPlayer2_getParameter},
+    {"setParameter",        "(ILjava/lang/Object;)Z",          (void *)android_media_MediaPlayer2_setParameter},
+    {"getParameter",        "(I)Ljava/lang/Object;",           (void *)android_media_MediaPlayer2_getParameter},
     {"setLooping",          "(Z)V",                             (void *)android_media_MediaPlayer2_setLooping},
     {"isLooping",           "()Z",                              (void *)android_media_MediaPlayer2_isLooping},
     {"_setVolume",          "(FF)V",                            (void *)android_media_MediaPlayer2_setVolume},
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index ee4c954..89438e5 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -15,6 +15,7 @@
         "SettingsLibHelpUtils",
         "SettingsLibRestrictedLockUtils",
         "SettingsLibAppPreference",
+        "SettingsLibSearchWidget",
     ],
 
     // ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
diff --git a/packages/SettingsLib/HelpUtils/res/values-nl/strings.xml b/packages/SettingsLib/HelpUtils/res/values-nl/strings.xml
index a034d29..2f576e6 100644
--- a/packages/SettingsLib/HelpUtils/res/values-nl/strings.xml
+++ b/packages/SettingsLib/HelpUtils/res/values-nl/strings.xml
@@ -17,5 +17,5 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="help_feedback_label" msgid="4550436169116444686">"Help en feedback"</string>
+    <string name="help_feedback_label" msgid="4550436169116444686">"Hulp en feedback"</string>
 </resources>
diff --git a/packages/SettingsLib/SearchWidget/Android.bp b/packages/SettingsLib/SearchWidget/Android.bp
new file mode 100644
index 0000000..7541ca45
--- /dev/null
+++ b/packages/SettingsLib/SearchWidget/Android.bp
@@ -0,0 +1,8 @@
+android_library {
+    name: "SettingsLibSearchWidget",
+
+    srcs: ["src/**/*.java"],
+    resource_dirs: ["res"],
+    sdk_version: "system_current",
+    min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/SearchWidget/AndroidManifest.xml b/packages/SettingsLib/SearchWidget/AndroidManifest.xml
new file mode 100644
index 0000000..b86544e
--- /dev/null
+++ b/packages/SettingsLib/SearchWidget/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.settingslib.search">
+
+    <uses-sdk android:minSdkVersion="21" />
+
+</manifest>
diff --git a/packages/SettingsLib/SearchWidget/res/drawable/ic_search_24dp.xml b/packages/SettingsLib/SearchWidget/res/drawable/ic_search_24dp.xml
new file mode 100644
index 0000000..7e65848
--- /dev/null
+++ b/packages/SettingsLib/SearchWidget/res/drawable/ic_search_24dp.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24"
+        android:tint="?android:attr/colorControlNormal">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20.49,19l-5.73,-5.73C15.53,12.2 16,10.91 16,9.5C16,5.91 13.09,3 9.5,3S3,5.91 3,9.5C3,13.09 5.91,16 9.5,16c1.41,0 2.7,-0.47 3.77,-1.24L19,20.49L20.49,19zM5,9.5C5,7.01 7.01,5 9.5,5S14,7.01 14,9.5S11.99,14 9.5,14S5,11.99 5,9.5z"/>
+</vector>
diff --git a/packages/SettingsLib/SearchWidget/res/values/strings.xml b/packages/SettingsLib/SearchWidget/res/values/strings.xml
new file mode 100644
index 0000000..0b12810
--- /dev/null
+++ b/packages/SettingsLib/SearchWidget/res/values/strings.xml
@@ -0,0 +1,20 @@
+<!--
+  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.
+  -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Text used as a search hint into the search box [CHAR_LIMIT=60]-->
+    <string name="search_menu">Search settings</string>
+</resources>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 332ced6..508adbd 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1116,4 +1116,6 @@
     <!-- time label for event have that happened very recently [CHAR LIMIT=60] -->
     <string name="time_unit_just_now">Just now</string>
 
-  </resources>
+    <!-- The notice header of Third-party licenses. not translatable -->
+    <string name="notice_header" translatable="false"></string>
+</resources>
diff --git a/packages/SettingsLib/search/Android.mk b/packages/SettingsLib/search/Android.mk
index cb19891..14f9626 100644
--- a/packages/SettingsLib/search/Android.mk
+++ b/packages/SettingsLib/search/Android.mk
@@ -5,6 +5,9 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
+LOCAL_RESOURCE_DIR := \
+    $(LOCAL_PATH)/main/res
+
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index 0c29f43..9653972 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -30,6 +30,7 @@
 import android.bluetooth.BluetoothPan;
 import android.bluetooth.BluetoothPbap;
 import android.bluetooth.BluetoothPbapClient;
+import android.bluetooth.BluetoothSap;
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothUuid;
 import android.content.Context;
@@ -98,6 +99,7 @@
     private PbapClientProfile mPbapClientProfile;
     private PbapServerProfile mPbapProfile;
     private HearingAidProfile mHearingAidProfile;
+    private SapProfile mSapProfile;
 
     /**
      * Mapping from profile name, e.g. "HEADSET" to profile object.
@@ -210,6 +212,13 @@
             addProfile(mPbapClientProfile, PbapClientProfile.NAME,
                     BluetoothPbapClient.ACTION_CONNECTION_STATE_CHANGED);
         }
+        if (mSapProfile == null && supportedList.contains(BluetoothProfile.SAP)) {
+            if (DEBUG) {
+                Log.d(TAG, "Adding local SAP profile");
+            }
+            mSapProfile = new SapProfile(mContext, mDeviceManager, this);
+            addProfile(mSapProfile, SapProfile.NAME, BluetoothSap.ACTION_CONNECTION_STATE_CHANGED);
+        }
         mEventManager.registerProfileIntentReceiver();
     }
 
@@ -550,6 +559,11 @@
             removedProfiles.remove(mHearingAidProfile);
         }
 
+        if (mSapProfile != null && BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.SAP)) {
+            profiles.add(mSapProfile);
+            removedProfiles.remove(mSapProfile);
+        }
+
         if (DEBUG) {
             Log.d(TAG,"New Profiles" + profiles.toString());
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
index 9a6f104..b4acc48 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
@@ -36,12 +36,10 @@
  */
 final class SapProfile implements LocalBluetoothProfile {
     private static final String TAG = "SapProfile";
-    private static boolean V = true;
 
     private BluetoothSap mService;
     private boolean mIsProfileReady;
 
-    private final LocalBluetoothAdapter mLocalAdapter;
     private final CachedBluetoothDeviceManager mDeviceManager;
     private final LocalBluetoothProfileManager mProfileManager;
 
@@ -59,7 +57,7 @@
             implements BluetoothProfile.ServiceListener {
 
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            if (V) Log.d(TAG,"Bluetooth service connected");
+            Log.d(TAG, "Bluetooth service connected, profile:" + profile);
             mService = (BluetoothSap) proxy;
             // We just bound to the service, so refresh the UI for any connected SAP devices.
             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -81,7 +79,7 @@
         }
 
         public void onServiceDisconnected(int profile) {
-            if (V) Log.d(TAG,"Bluetooth service disconnected");
+            Log.d(TAG, "Bluetooth service disconnected, profile:" + profile);
             mProfileManager.callServiceDisconnectedListeners();
             mIsProfileReady=false;
         }
@@ -96,13 +94,11 @@
         return BluetoothProfile.SAP;
     }
 
-    SapProfile(Context context, LocalBluetoothAdapter adapter,
-            CachedBluetoothDeviceManager deviceManager,
+    SapProfile(Context context, CachedBluetoothDeviceManager deviceManager,
             LocalBluetoothProfileManager profileManager) {
-        mLocalAdapter = adapter;
         mDeviceManager = deviceManager;
         mProfileManager = profileManager;
-        mLocalAdapter.getProfileProxy(context, new SapServiceListener(),
+        BluetoothAdapter.getDefaultAdapter().getProfileProxy(context, new SapServiceListener(),
                 BluetoothProfile.SAP);
     }
 
@@ -115,50 +111,47 @@
     }
 
     public boolean connect(BluetoothDevice device) {
-        if (mService == null) return false;
-        List<BluetoothDevice> sinks = mService.getConnectedDevices();
-        if (sinks != null) {
-            for (BluetoothDevice sink : sinks) {
-                mService.disconnect(sink);
-            }
+        if (mService == null) {
+            return false;
         }
         return mService.connect(device);
     }
 
     public boolean disconnect(BluetoothDevice device) {
-        if (mService == null) return false;
-        List<BluetoothDevice> deviceList = mService.getConnectedDevices();
-        if (!deviceList.isEmpty() && deviceList.get(0).equals(device)) {
-            if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) {
-                mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
-            }
-            return mService.disconnect(device);
-        } else {
+        if (mService == null) {
             return false;
         }
+        if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) {
+            mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+        }
+        return mService.disconnect(device);
     }
 
     public int getConnectionStatus(BluetoothDevice device) {
-        if (mService == null) return BluetoothProfile.STATE_DISCONNECTED;
-        List<BluetoothDevice> deviceList = mService.getConnectedDevices();
-
-        return !deviceList.isEmpty() && deviceList.get(0).equals(device)
-                ? mService.getConnectionState(device)
-                : BluetoothProfile.STATE_DISCONNECTED;
+        if (mService == null) {
+            return BluetoothProfile.STATE_DISCONNECTED;
+        }
+        return mService.getConnectionState(device);
     }
 
     public boolean isPreferred(BluetoothDevice device) {
-        if (mService == null) return false;
+        if (mService == null) {
+            return false;
+        }
         return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
     }
 
     public int getPreferred(BluetoothDevice device) {
-        if (mService == null) return BluetoothProfile.PRIORITY_OFF;
+        if (mService == null) {
+            return BluetoothProfile.PRIORITY_OFF;
+        }
         return mService.getPriority(device);
     }
 
     public void setPreferred(BluetoothDevice device, boolean preferred) {
-        if (mService == null) return;
+        if (mService == null) {
+            return;
+        }
         if (preferred) {
             if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
                 mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
@@ -169,7 +162,9 @@
     }
 
     public List<BluetoothDevice> getConnectedDevices() {
-        if (mService == null) return new ArrayList<BluetoothDevice>(0);
+        if (mService == null) {
+            return new ArrayList<BluetoothDevice>(0);
+        }
         return mService.getDevicesMatchingConnectionStates(
               new int[] {BluetoothProfile.STATE_CONNECTED,
                          BluetoothProfile.STATE_CONNECTING,
@@ -207,11 +202,11 @@
     }
 
     protected void finalize() {
-        if (V) Log.d(TAG, "finalize()");
+        Log.d(TAG, "finalize()");
         if (mService != null) {
             try {
                 BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.SAP,
-                                                                       mService);
+                        mService);
                 mService = null;
             }catch (Throwable t) {
                 Log.w(TAG, "Error cleaning up SAP proxy", t);
diff --git a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java
index 42306f6..9db4a35 100644
--- a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java
+++ b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java
@@ -46,7 +46,7 @@
  * TODO: Remove duplicate codes once backward support ends.
  */
 class LicenseHtmlGeneratorFromXml {
-    private static final String TAG = "LicenseHtmlGeneratorFromXml";
+    private static final String TAG = "LicenseGeneratorFromXml";
 
     private static final String TAG_ROOT = "licenses";
     private static final String TAG_FILE_NAME = "file-name";
@@ -107,12 +107,13 @@
         mXmlFiles = xmlFiles;
     }
 
-    public static boolean generateHtml(List<File> xmlFiles, File outputFile) {
+    public static boolean generateHtml(List<File> xmlFiles, File outputFile,
+            String noticeHeader) {
         LicenseHtmlGeneratorFromXml genertor = new LicenseHtmlGeneratorFromXml(xmlFiles);
-        return genertor.generateHtml(outputFile);
+        return genertor.generateHtml(outputFile, noticeHeader);
     }
 
-    private boolean generateHtml(File outputFile) {
+    private boolean generateHtml(File outputFile, String noticeHeader) {
         for (File xmlFile : mXmlFiles) {
             parse(xmlFile);
         }
@@ -125,7 +126,8 @@
         try {
             writer = new PrintWriter(outputFile);
 
-            generateHtml(mFileNameToContentIdMap, mContentIdToFileContentMap, writer);
+            generateHtml(mFileNameToContentIdMap, mContentIdToFileContentMap, writer,
+                noticeHeader);
 
             writer.flush();
             writer.close();
@@ -239,13 +241,18 @@
 
     @VisibleForTesting
     static void generateHtml(Map<String, String> fileNameToContentIdMap,
-            Map<String, String> contentIdToFileContentMap, PrintWriter writer) {
+            Map<String, String> contentIdToFileContentMap, PrintWriter writer,
+            String noticeHeader) {
         List<String> fileNameList = new ArrayList();
         fileNameList.addAll(fileNameToContentIdMap.keySet());
         Collections.sort(fileNameList);
 
         writer.println(HTML_HEAD_STRING);
 
+        if (!TextUtils.isEmpty(noticeHeader)) {
+            writer.println(noticeHeader);
+        }
+
         int count = 0;
         Map<String, Integer> contentIdToOrderMap = new HashMap();
         List<ContentIdAndFileNames> contentIdAndFileNamesList = new ArrayList();
diff --git a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java
index 3930069..78e807c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java
+++ b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java
@@ -60,7 +60,7 @@
 
         File cachedHtmlFile = getCachedHtmlFile(mContext);
         if (!isCachedHtmlFileOutdated(xmlFiles, cachedHtmlFile)
-                || generateHtmlFile(xmlFiles, cachedHtmlFile)) {
+                || generateHtmlFile(mContext, xmlFiles, cachedHtmlFile)) {
             return cachedHtmlFile;
         }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java
index 360c19c..ca62485 100644
--- a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java
+++ b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.util.Log;
 
+import com.android.settingslib.R;
 import com.android.settingslib.utils.AsyncLoaderCompat;
 
 import java.io.File;
@@ -65,7 +66,7 @@
 
         File cachedHtmlFile = getCachedHtmlFile(mContext);
         if (!isCachedHtmlFileOutdated(xmlFiles, cachedHtmlFile)
-                || generateHtmlFile(xmlFiles, cachedHtmlFile)) {
+                || generateHtmlFile(mContext, xmlFiles, cachedHtmlFile)) {
             return cachedHtmlFile;
         }
 
@@ -101,7 +102,8 @@
         return outdated;
     }
 
-    static boolean generateHtmlFile(List<File> xmlFiles, File htmlFile) {
-        return LicenseHtmlGeneratorFromXml.generateHtml(xmlFiles, htmlFile);
+    static boolean generateHtmlFile(Context context, List<File> xmlFiles, File htmlFile) {
+        return LicenseHtmlGeneratorFromXml.generateHtml(xmlFiles, htmlFile,
+                context.getString(R.string.notice_header));
     }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoaderCompat.java b/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoaderCompat.java
index 74bd97f..e9c5238 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoaderCompat.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoaderCompat.java
@@ -37,7 +37,8 @@
 /**
  * Loader for historical chart data for both network and UID details.
  *
- * Deprecated in favor of {@link NetworkCycleDataLoader}
+ * Deprecated in favor of {@link NetworkCycleChartDataLoader} and
+ * {@link NetworkCycleDataForUidLoader}
  *
  * @deprecated
  */
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleChartData.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleChartData.java
new file mode 100644
index 0000000..9b3ff8b
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleChartData.java
@@ -0,0 +1,56 @@
+/*
+ * 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.net;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Usage data in a billing cycle with bucketized data for plotting the usage chart.
+ */
+public class NetworkCycleChartData extends NetworkCycleData {
+    public static final long BUCKET_DURATION_MS = TimeUnit.DAYS.toMillis(1);
+
+    private List<NetworkCycleData> mUsageBuckets;
+
+    private NetworkCycleChartData() {
+    }
+
+    public List<NetworkCycleData> getUsageBuckets() {
+        return mUsageBuckets;
+    }
+
+    public static class Builder extends NetworkCycleData.Builder {
+        private NetworkCycleChartData mObject = new NetworkCycleChartData();
+
+        public Builder setUsageBuckets(List<NetworkCycleData> buckets) {
+            getObject().mUsageBuckets = buckets;
+            return this;
+        }
+
+        @Override
+        protected NetworkCycleChartData getObject() {
+            return mObject;
+        }
+
+        @Override
+        public NetworkCycleChartData build() {
+            return getObject();
+        }
+    }
+
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleChartDataLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleChartDataLoader.java
new file mode 100644
index 0000000..7ae3398
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleChartDataLoader.java
@@ -0,0 +1,106 @@
+/*
+ * 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.net;
+
+import android.app.usage.NetworkStats;
+import android.content.Context;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Loader for network data usage history. It returns a list of usage data per billing cycle with
+ * bucketized usages.
+ */
+public class NetworkCycleChartDataLoader
+        extends NetworkCycleDataLoader<List<NetworkCycleChartData>> {
+
+    private static final String TAG = "NetworkCycleChartLoader";
+
+    private final List<NetworkCycleChartData> mData;
+
+    private NetworkCycleChartDataLoader(Builder builder) {
+        super(builder);
+        mData = new ArrayList<NetworkCycleChartData>();
+    }
+
+    @Override
+    void recordUsage(long start, long end) {
+        try {
+            final NetworkStats stats = mNetworkStatsManager.querySummary(
+                mNetworkType, mSubId, start, end);
+            final long total = getTotalUsage(stats);
+            if (total > 0L) {
+                final NetworkCycleChartData.Builder builder = new NetworkCycleChartData.Builder();
+                builder.setUsageBuckets(getUsageBuckets(start, end))
+                    .setStartTime(start)
+                    .setEndTime(end)
+                    .setTotalUsage(total);
+                mData.add(builder.build());
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Exception querying network detail.", e);
+        }
+    }
+
+    @Override
+    List<NetworkCycleChartData> getCycleUsage() {
+        return mData;
+    }
+
+    public static Builder<?> builder(Context context) {
+        return new Builder<NetworkCycleChartDataLoader>(context) {
+            @Override
+            public NetworkCycleChartDataLoader build() {
+                return new NetworkCycleChartDataLoader(this);
+            }
+        };
+    }
+
+    private List<NetworkCycleData> getUsageBuckets(long start, long end) {
+        final List<NetworkCycleData> data = new ArrayList<>();
+        long bucketStart = start;
+        long bucketEnd = start + NetworkCycleChartData.BUCKET_DURATION_MS;
+        while (bucketEnd <= end) {
+            long usage = 0L;
+            try {
+                final NetworkStats stats = mNetworkStatsManager.querySummary(
+                    mNetworkType, mSubId, bucketStart, bucketEnd);
+                usage = getTotalUsage(stats);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Exception querying network detail.", e);
+            }
+            data.add(new NetworkCycleData.Builder()
+                .setStartTime(bucketStart).setEndTime(bucketEnd).setTotalUsage(usage).build());
+            bucketStart = bucketEnd;
+            bucketEnd += NetworkCycleChartData.BUCKET_DURATION_MS;
+        }
+        return data;
+    }
+
+    public static abstract class Builder<T extends NetworkCycleChartDataLoader>
+            extends NetworkCycleDataLoader.Builder<T> {
+
+        public Builder(Context context) {
+            super(context);
+        }
+
+    }
+
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleData.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleData.java
index 2d8c0de..26c65a2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleData.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleData.java
@@ -16,54 +16,55 @@
 
 package com.android.settingslib.net;
 
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
 /**
- * Data structure representing usage data in a billing cycle.
+ * Base data structure representing usage data in a billing cycle.
  */
 public class NetworkCycleData {
-    public static final long BUCKET_DURATION_MS = TimeUnit.DAYS.toMillis(1);
-    public long startTime;
-    public long endTime;
-    public long totalUsage;
-    public List<NetworkCycleData> usageBuckets;
 
-    private NetworkCycleData(Builder builder) {
-        startTime = builder.mStart;
-        endTime = builder.mEnd;
-        totalUsage = builder.mTotalUsage;
-        usageBuckets = builder.mUsageBuckets;
+    private long mStartTime;
+    private long mEndTime;
+    private long mTotalUsage;
+
+    protected NetworkCycleData() {
+    }
+
+    public long getStartTime() {
+        return mStartTime;
+    }
+
+    public long getEndTime() {
+        return mEndTime;
+    }
+
+    public long getTotalUsage() {
+        return mTotalUsage;
     }
 
     public static class Builder {
-        private long mStart;
-        private long mEnd;
-        private long mTotalUsage;
-        private List<NetworkCycleData> mUsageBuckets;
+
+        private NetworkCycleData mObject = new NetworkCycleData();
 
         public Builder setStartTime(long start) {
-            mStart = start;
+            getObject().mStartTime = start;
             return this;
         }
 
         public Builder setEndTime(long end) {
-            mEnd = end;
+            getObject().mEndTime = end;
             return this;
         }
 
         public Builder setTotalUsage(long total) {
-            mTotalUsage = total;
+            getObject().mTotalUsage = total;
             return this;
         }
 
-        public Builder setUsageBuckets(List<NetworkCycleData> buckets) {
-            mUsageBuckets = buckets;
-            return this;
+        protected NetworkCycleData getObject() {
+            return mObject;
         }
 
         public NetworkCycleData build() {
-            return new NetworkCycleData(this);
+            return getObject();
         }
     }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUid.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUid.java
new file mode 100644
index 0000000..9d13717
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUid.java
@@ -0,0 +1,65 @@
+/*
+ * 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.net;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Usage data in a billing cycle for a specific Uid.
+ */
+public class NetworkCycleDataForUid extends NetworkCycleData {
+
+    private long mBackgroudUsage;
+    private long mForegroudUsage;
+
+    private NetworkCycleDataForUid() {
+    }
+
+    public long getBackgroudUsage() {
+        return mBackgroudUsage;
+    }
+
+    public long getForegroudUsage() {
+        return mForegroudUsage;
+    }
+
+    public static class Builder extends NetworkCycleData.Builder {
+
+        private NetworkCycleDataForUid mObject = new NetworkCycleDataForUid();
+
+        public Builder setBackgroundUsage(long backgroundUsage) {
+            getObject().mBackgroudUsage = backgroundUsage;
+            return this;
+        }
+
+        public Builder setForegroundUsage(long foregroundUsage) {
+            getObject().mForegroudUsage = foregroundUsage;
+            return this;
+        }
+
+        @Override
+        public NetworkCycleDataForUid getObject() {
+            return mObject;
+        }
+
+        @Override
+        public NetworkCycleDataForUid build() {
+            return getObject();
+        }
+    }
+
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUidLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUidLoader.java
new file mode 100644
index 0000000..95efb4c
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUidLoader.java
@@ -0,0 +1,102 @@
+/*
+ * 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.net;
+
+import static android.app.usage.NetworkStats.Bucket.STATE_FOREGROUND;
+import static android.net.NetworkStats.TAG_NONE;
+
+import android.app.usage.NetworkStats;
+import android.content.Context;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Loader for network data usage history. It returns a list of usage data per billing cycle for a
+ * specific Uid.
+ */
+public class NetworkCycleDataForUidLoader extends
+        NetworkCycleDataLoader<List<NetworkCycleDataForUid>> {
+    private static final String TAG = "NetworkDataForUidLoader";
+
+    private final List<NetworkCycleDataForUid> mData;
+    private final int mUid;
+
+    private NetworkCycleDataForUidLoader(Builder builder) {
+        super(builder);
+        mUid = builder.mUid;
+        mData = new ArrayList<NetworkCycleDataForUid>();
+    }
+
+    @Override
+    void recordUsage(long start, long end) {
+        try {
+            final NetworkStats stats = mNetworkStatsManager.queryDetailsForUid(
+                mNetworkType, mSubId, start, end, mUid);
+            final long total = getTotalUsage(stats);
+            if (total > 0L) {
+                final long foreground = getForegroundUsage(start, end);
+                final NetworkCycleDataForUid.Builder builder = new NetworkCycleDataForUid.Builder();
+                builder.setBackgroundUsage(total - foreground)
+                    .setForegroundUsage(foreground)
+                    .setStartTime(start)
+                    .setEndTime(end)
+                    .setTotalUsage(total);
+                mData.add(builder.build());
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "Exception querying network detail.", e);
+        }
+    }
+
+    @Override
+    List<NetworkCycleDataForUid> getCycleUsage() {
+        return mData;
+    }
+
+    public static Builder<?> builder(Context context) {
+        return new Builder<NetworkCycleDataForUidLoader>(context) {
+            @Override
+            public NetworkCycleDataForUidLoader build() {
+                return new NetworkCycleDataForUidLoader(this);
+            }
+        };
+    }
+
+    private long getForegroundUsage(long start, long end) {
+        final NetworkStats stats = mNetworkStatsManager.queryDetailsForUidTagState(
+            mNetworkType, mSubId, start, end, mUid, TAG_NONE, STATE_FOREGROUND);
+        return getTotalUsage(stats);
+    }
+
+    public static abstract class Builder<T extends NetworkCycleDataForUidLoader>
+            extends NetworkCycleDataLoader.Builder<T> {
+
+        private int mUid;
+
+        public Builder(Context context) {
+            super(context);
+        }
+
+        public Builder<T> setUid(int uid) {
+            mUid = uid;
+            return this;
+        }
+    }
+
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java
index 80e1356..cc936d2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java
@@ -32,32 +32,28 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.text.format.DateUtils;
-import android.util.Log;
 import android.util.Pair;
 
 import java.time.ZonedDateTime;
-import java.util.ArrayList;
 import java.util.Iterator;
-import java.util.List;
 
-import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
 import androidx.loader.content.AsyncTaskLoader;
 
 /**
  * Loader for network data usage history. It returns a list of usage data per billing cycle.
  */
-public class NetworkCycleDataLoader extends AsyncTaskLoader<List<NetworkCycleData>> {
-    private static final String TAG = "CycleDataSummaryLoader";
-    private final NetworkStatsManager mNetworkStatsManager;
-    private final String mSubId;
-    private final int mNetworkType;
+public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> {
+    private static final String TAG = "NetworkCycleDataLoader";
+    protected final NetworkStatsManager mNetworkStatsManager;
+    protected final String mSubId;
+    protected final int mNetworkType;
     private final NetworkPolicy mPolicy;
     private final NetworkTemplate mNetworkTemplate;
     @VisibleForTesting
     final INetworkStatsService mNetworkStatsService;
 
-    private NetworkCycleDataLoader(Builder builder) {
+    protected NetworkCycleDataLoader(Builder<?> builder) {
         super(builder.mContext);
         mPolicy = builder.mPolicy;
         mSubId = builder.mSubId;
@@ -75,21 +71,25 @@
         forceLoad();
     }
 
-    @Override
-    public List<NetworkCycleData> loadInBackground() {
+    public D loadInBackground() {
         if (mPolicy == null) {
-            return loadFourWeeksData();
+            loadFourWeeksData();
+        } else {
+            loadPolicyData();
         }
-        final List<NetworkCycleData> data = new ArrayList<>();
-        final Iterator<Pair<ZonedDateTime, ZonedDateTime>> iterator = NetworkPolicyManager
-            .cycleIterator(mPolicy);
+        return getCycleUsage();
+    }
+
+    @VisibleForTesting
+    void loadPolicyData() {
+        final Iterator<Pair<ZonedDateTime, ZonedDateTime>> iterator =
+            NetworkPolicyManager.cycleIterator(mPolicy);
         while (iterator.hasNext()) {
             final Pair<ZonedDateTime, ZonedDateTime> cycle = iterator.next();
             final long cycleStart = cycle.first.toInstant().toEpochMilli();
             final long cycleEnd = cycle.second.toInstant().toEpochMilli();
-            getUsage(cycleStart, cycleEnd, data);
+            recordUsage(cycleStart, cycleEnd);
         }
-        return data;
     }
 
     @Override
@@ -105,8 +105,7 @@
     }
 
     @VisibleForTesting
-    List<NetworkCycleData> loadFourWeeksData() {
-        final List<NetworkCycleData> data = new ArrayList<>();
+    void loadFourWeeksData() {
         try {
             final INetworkStatsSession networkSession = mNetworkStatsService.openSession();
             final NetworkStatsHistory networkHistory = networkSession.getHistoryForNetwork(
@@ -117,7 +116,7 @@
             long cycleEnd = historyEnd;
             while (cycleEnd > historyStart) {
                 final long cycleStart = cycleEnd - (DateUtils.WEEK_IN_MILLIS * 4);
-                getUsage(cycleStart, cycleEnd, data);
+                recordUsage(cycleStart, cycleEnd);
                 cycleEnd = cycleStart;
             }
 
@@ -125,29 +124,23 @@
         } catch (RemoteException e) {
             throw new RuntimeException(e);
         }
-        return data;
     }
 
     @VisibleForTesting
-    void getUsage(long start, long end, @NonNull List<NetworkCycleData> data) {
-        try {
-            final NetworkStats stats = mNetworkStatsManager.querySummary(
-                mNetworkType, mSubId, start, end);
-            final long total = getTotalUsage(stats);
-            if (total > 0L) {
-                data.add(new NetworkCycleData.Builder()
-                    .setStartTime(start)
-                    .setEndTime(end)
-                    .setTotalUsage(total)
-                    .setUsageBuckets(getUsageBuckets(start, end))
-                    .build());
+    abstract void recordUsage(long start, long end);
+
+    abstract D getCycleUsage();
+
+    public static Builder<?> builder(Context context) {
+        return new Builder<NetworkCycleDataLoader>(context) {
+            @Override
+            public NetworkCycleDataLoader build() {
+                return null;
             }
-        } catch (RemoteException e) {
-            Log.e(TAG, "Exception querying network detail.", e);
-        }
+        };
     }
 
-    private long getTotalUsage(NetworkStats stats) {
+    protected long getTotalUsage(NetworkStats stats) {
         long bytes = 0L;
         if (stats != null) {
             final NetworkStats.Bucket bucket = new NetworkStats.Bucket();
@@ -159,61 +152,38 @@
         return bytes;
     }
 
-    private List<NetworkCycleData> getUsageBuckets(long start, long end) {
-        final List<NetworkCycleData> data = new ArrayList<>();
-        long bucketStart = start;
-        long bucketEnd = start + NetworkCycleData.BUCKET_DURATION_MS;
-        while (bucketEnd <= end) {
-            long usage = 0L;
-            try {
-                final NetworkStats stats = mNetworkStatsManager.querySummary(
-                    mNetworkType, mSubId, bucketStart, bucketEnd);
-                usage = getTotalUsage(stats);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Exception querying network detail.", e);
-            }
-            data.add(new NetworkCycleData.Builder()
-                .setStartTime(bucketStart).setEndTime(bucketEnd).setTotalUsage(usage).build());
-            bucketStart = bucketEnd;
-            bucketEnd += NetworkCycleData.BUCKET_DURATION_MS;
-        }
-        return data;
-    }
-
-    public static class Builder {
+    public static abstract class Builder<T extends NetworkCycleDataLoader> {
         private final Context mContext;
         private NetworkPolicy mPolicy;
         private String mSubId;
         private int mNetworkType;
         private NetworkTemplate mNetworkTemplate;
 
-        public Builder(Context context) {
+        public Builder (Context context) {
             mContext = context;
         }
 
-        public Builder setNetworkPolicy(NetworkPolicy policy) {
+        public Builder<T> setNetworkPolicy(NetworkPolicy policy) {
             mPolicy = policy;
             return this;
         }
 
-        public Builder setSubscriberId(String subId) {
+        public Builder<T> setSubscriberId(String subId) {
             mSubId = subId;
             return this;
         }
 
-        public Builder setNetworkType(int networkType) {
+        public Builder<T> setNetworkType(int networkType) {
             mNetworkType = networkType;
             return this;
         }
 
-        public Builder setNetworkTemplate(NetworkTemplate template) {
+        public Builder<T> setNetworkTemplate(NetworkTemplate template) {
             mNetworkTemplate = template;
             return this;
         }
 
-        public NetworkCycleDataLoader build() {
-            return new NetworkCycleDataLoader(this);
-        }
+        public abstract T build();
     }
 
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java
new file mode 100644
index 0000000..9bb53ee
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothSap;
+import android.bluetooth.BluetoothProfile;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadow.api.Shadow;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(shadows = {ShadowBluetoothAdapter.class})
+public class SapProfileTest {
+
+    @Mock
+    private CachedBluetoothDeviceManager mDeviceManager;
+    @Mock
+    private LocalBluetoothProfileManager mProfileManager;
+    @Mock
+    private BluetoothSap mService;
+    @Mock
+    private CachedBluetoothDevice mCachedBluetoothDevice;
+    @Mock
+    private BluetoothDevice mBluetoothDevice;
+    private BluetoothProfile.ServiceListener mServiceListener;
+    private SapProfile mProfile;
+    private ShadowBluetoothAdapter mShadowBluetoothAdapter;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+        mProfile = new SapProfile(RuntimeEnvironment.application, mDeviceManager, mProfileManager);
+        mServiceListener = mShadowBluetoothAdapter.getServiceListener();
+        mServiceListener.onServiceConnected(BluetoothProfile.SAP, mService);
+    }
+
+    @Test
+    public void connect_shouldConnectBluetoothSap() {
+        mProfile.connect(mBluetoothDevice);
+        verify(mService).connect(mBluetoothDevice);
+    }
+
+    @Test
+    public void disconnect_shouldDisconnectBluetoothSap() {
+        mProfile.disconnect(mBluetoothDevice);
+        verify(mService).disconnect(mBluetoothDevice);
+    }
+
+    @Test
+    public void getConnectionStatus_shouldReturnConnectionState() {
+        when(mService.getConnectionState(mBluetoothDevice)).
+                thenReturn(BluetoothProfile.STATE_CONNECTED);
+        assertThat(mProfile.getConnectionStatus(mBluetoothDevice)).
+                isEqualTo(BluetoothProfile.STATE_CONNECTED);
+    }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
index 96b2a14..b00476b2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
@@ -50,7 +50,7 @@
             + "<file-content contentId=\"0\"><![CDATA[license content #0]]></file-content>\n"
             + "</licenses2>";
 
-    private static final String EXPECTED_HTML_STRING =
+    private static final String HTML_HEAD_STRING =
             "<html><head>\n"
             + "<style type=\"text/css\">\n"
             + "body { padding: 0; font-family: sans-serif; }\n"
@@ -63,8 +63,12 @@
             + "</head>"
             + "<body topmargin=\"0\" leftmargin=\"0\" rightmargin=\"0\" bottommargin=\"0\">\n"
             + "<div class=\"toc\">\n"
-            + "<ul>\n"
-            + "<li><a href=\"#id0\">/file0</a></li>\n"
+            + "<ul>\n";
+
+    private static final String HTML_CUSTOM_HEADING = "Custom heading";
+
+    private static final String HTML_BODY_STRING =
+            "<li><a href=\"#id0\">/file0</a></li>\n"
             + "<li><a href=\"#id0\">/file1</a></li>\n"
             + "</ul>\n"
             + "</div><!-- table of contents -->\n"
@@ -81,6 +85,11 @@
             + "</td></tr><!-- same-license -->\n"
             + "</table></body></html>\n";
 
+    private static final String EXPECTED_HTML_STRING = HTML_HEAD_STRING + HTML_BODY_STRING;
+
+    private static final String EXPECTED_HTML_STRING_WITH_CUSTOM_HEADING =
+            HTML_HEAD_STRING + HTML_CUSTOM_HEADING + "\n" + HTML_BODY_STRING;
+
     @Test
     public void testParseValidXmlStream() throws XmlPullParserException, IOException {
         Map<String, String> fileNameToContentIdMap = new HashMap<String, String>();
@@ -117,7 +126,23 @@
 
         StringWriter output = new StringWriter();
         LicenseHtmlGeneratorFromXml.generateHtml(
-                fileNameToContentIdMap, contentIdToFileContentMap, new PrintWriter(output));
+                fileNameToContentIdMap, contentIdToFileContentMap, new PrintWriter(output), "");
         assertThat(output.toString()).isEqualTo(EXPECTED_HTML_STRING);
     }
+
+    @Test
+    public void testGenerateHtmlWithCustomHeading() {
+        Map<String, String> fileNameToContentIdMap = new HashMap<String, String>();
+        Map<String, String> contentIdToFileContentMap = new HashMap<String, String>();
+
+        fileNameToContentIdMap.put("/file0", "0");
+        fileNameToContentIdMap.put("/file1", "0");
+        contentIdToFileContentMap.put("0", "license content #0");
+
+        StringWriter output = new StringWriter();
+        LicenseHtmlGeneratorFromXml.generateHtml(
+                fileNameToContentIdMap, contentIdToFileContentMap, new PrintWriter(output),
+                HTML_CUSTOM_HEADING);
+        assertThat(output.toString()).isEqualTo(EXPECTED_HTML_STRING_WITH_CUSTOM_HEADING);
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderCompatTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderCompatTest.java
index 12a4e69..c32cc99 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderCompatTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderCompatTest.java
@@ -142,7 +142,7 @@
         }
 
         @Implementation
-        static boolean generateHtmlFile(List<File> xmlFiles, File htmlFile) {
+        static boolean generateHtmlFile(Context context, List<File> xmlFiles, File htmlFile) {
             return sGenerateHtmlFileSucceeded;
         }
     }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java
new file mode 100644
index 0000000..3dc110d
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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.net;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.usage.NetworkStatsManager;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.os.RemoteException;
+import android.text.format.DateUtils;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+public class NetworkCycleChartDataLoaderTest {
+
+    @Mock
+    private NetworkStatsManager mNetworkStatsManager;
+    @Mock
+    private Context mContext;
+
+    private NetworkCycleChartDataLoader mLoader;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mContext.getSystemService(Context.NETWORK_STATS_SERVICE))
+            .thenReturn(mNetworkStatsManager);
+    }
+
+    @Test
+    public void recordUsage_shouldQueryNetworkSummary() throws RemoteException {
+        final long end = System.currentTimeMillis();
+        final long start = end - (DateUtils.WEEK_IN_MILLIS * 4);
+        final int networkType = ConnectivityManager.TYPE_MOBILE;
+        final String subId = "TestSubscriber";
+        mLoader = NetworkCycleChartDataLoader.builder(mContext)
+            .setNetworkType(networkType).setSubscriberId(subId).build();
+
+        mLoader.recordUsage(start, end);
+
+        verify(mNetworkStatsManager).querySummary(networkType, subId, start, end);
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java
new file mode 100644
index 0000000..53fe451
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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.net;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.usage.NetworkStatsManager;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.text.format.DateUtils;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+public class NetworkCycleDataForUidLoaderTest {
+
+    @Mock
+    private NetworkStatsManager mNetworkStatsManager;
+    @Mock
+    private Context mContext;
+
+    private NetworkCycleDataForUidLoader mLoader;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mContext.getSystemService(Context.NETWORK_STATS_SERVICE))
+            .thenReturn(mNetworkStatsManager);
+    }
+
+    @Test
+    public void recordUsage_shouldQueryNetworkDetailsForUid() {
+        final long end = System.currentTimeMillis();
+        final long start = end - (DateUtils.WEEK_IN_MILLIS * 4);
+        final int networkType = ConnectivityManager.TYPE_MOBILE;
+        final String subId = "TestSubscriber";
+        final int uid = 1;
+        mLoader = NetworkCycleDataForUidLoader.builder(mContext)
+            .setUid(uid).setNetworkType(networkType).setSubscriberId(subId).build();
+
+        mLoader.recordUsage(start, end);
+
+        verify(mNetworkStatsManager).queryDetailsForUid(networkType, subId, start, end, uid);
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
index 4c4207b..be7f1bb 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
@@ -16,13 +16,10 @@
 
 package com.android.settingslib.net;
 
-import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
-import static org.mockito.Mockito.anyLong;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.mock;
 import static org.mockito.Matchers.nullable;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -50,6 +47,7 @@
 
 import java.time.ZonedDateTime;
 import java.util.Iterator;
+import java.util.List;
 
 @RunWith(SettingsLibRobolectricTestRunner.class)
 public class NetworkCycleDataLoaderTest {
@@ -64,8 +62,11 @@
     private Iterator<Range<ZonedDateTime>> mIterator;
     @Mock
     private INetworkStatsService mNetworkStatsService;
+    @Mock
+    private NetworkCycleDataLoader.Builder mBuilder;
 
-    private NetworkCycleDataLoader mLoader;
+
+    private NetworkCycleDataTestLoader mLoader;
 
     @Before
     public void setUp() {
@@ -77,8 +78,8 @@
 
     @Test
     public void loadInBackground_noNetworkPolicy_shouldLoad4WeeksData() {
-        mLoader = spy(new NetworkCycleDataLoader.Builder(mContext).build());
-        doReturn(null).when(mLoader).loadFourWeeksData();
+        mLoader = spy(new NetworkCycleDataTestLoader(mContext));
+        doNothing().when(mLoader).loadFourWeeksData();
 
         mLoader.loadInBackground();
 
@@ -86,31 +87,45 @@
     }
 
     @Test
-    public void loadInBackground_shouldQueryNetworkSummary() throws RemoteException {
+    public void loadInBackground_hasNetworkPolicy_shouldLoadPolicyData() {
+        mLoader = spy(new NetworkCycleDataTestLoader(mContext));
+        ReflectionHelpers.setField(mLoader, "mPolicy", mPolicy);
+
+        mLoader.loadInBackground();
+
+        verify(mLoader).loadPolicyData();
+    }
+
+    @Test
+    public void loadPolicyData_shouldRecordUsageFromPolicyCycle() {
         final int networkType = ConnectivityManager.TYPE_MOBILE;
         final String subId = "TestSubscriber";
         final ZonedDateTime now = ZonedDateTime.now();
         final Range<ZonedDateTime> cycle = new Range<>(now, now);
+        final long nowInMs = now.toInstant().toEpochMilli();
         // mock 1 cycle data.
         // hasNext() will be called internally in next(), hence setting it to return true twice.
         when(mIterator.hasNext()).thenReturn(true).thenReturn(true).thenReturn(false);
         when(mIterator.next()).thenReturn(cycle);
-        mLoader = new NetworkCycleDataLoader.Builder(mContext)
-            .setNetworkPolicy(mPolicy).setNetworkType(networkType).setSubscriberId(subId).build();
+        mLoader = spy(new NetworkCycleDataTestLoader(mContext));
+        ReflectionHelpers.setField(mLoader, "mPolicy", mPolicy);
+        ReflectionHelpers.setField(mLoader, "mNetworkType", networkType);
+        ReflectionHelpers.setField(mLoader, "mSubId", subId);
 
-        mLoader.loadInBackground();
+        mLoader.loadPolicyData();
 
-        verify(mNetworkStatsManager).querySummary(eq(networkType), eq(subId), anyLong(), anyLong());
+        verify(mLoader).recordUsage(nowInMs, nowInMs);
     }
 
     @Test
-    public void loadFourWeeksData_shouldGetUsageForLast4Weeks() throws RemoteException {
-        mLoader = spy(new NetworkCycleDataLoader.Builder(mContext).build());
+    public void loadFourWeeksData_shouldRecordUsageForLast4Weeks() throws RemoteException {
+        mLoader = spy(new NetworkCycleDataTestLoader(mContext));
         ReflectionHelpers.setField(mLoader, "mNetworkStatsService", mNetworkStatsService);
         final INetworkStatsSession networkSession = mock(INetworkStatsSession.class);
         when(mNetworkStatsService.openSession()).thenReturn(networkSession);
         final NetworkStatsHistory networkHistory = mock(NetworkStatsHistory.class);
-        when(networkSession.getHistoryForNetwork(nullable(NetworkTemplate.class), anyInt())).thenReturn(networkHistory);
+        when(networkSession.getHistoryForNetwork(nullable(NetworkTemplate.class), anyInt()))
+            .thenReturn(networkHistory);
         final long now = System.currentTimeMillis();
         final long fourWeeksAgo = now - (DateUtils.WEEK_IN_MILLIS * 4);
         when(networkHistory.getStart()).thenReturn(fourWeeksAgo);
@@ -118,6 +133,23 @@
 
         mLoader.loadFourWeeksData();
 
-        verify(mLoader).getUsage(eq(fourWeeksAgo), eq(now), any());
+        verify(mLoader).recordUsage(fourWeeksAgo, now);
+    }
+
+    public class NetworkCycleDataTestLoader extends NetworkCycleDataLoader<List<NetworkCycleData>> {
+
+        private NetworkCycleDataTestLoader(Context context) {
+            super(NetworkCycleDataLoader.builder(mContext));
+            mContext = context;
+        }
+
+        @Override
+        void recordUsage(long start, long end) {
+        }
+
+        @Override
+        List<NetworkCycleData> getCycleUsage() {
+            return null;
+        }
     }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/FragmentTestUtils.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/FragmentTestUtils.java
deleted file mode 100644
index d8e73b7..0000000
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/FragmentTestUtils.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * 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.testutils;
-
-import android.os.Bundle;
-import android.widget.LinearLayout;
-
-import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentActivity;
-import androidx.fragment.app.FragmentManager;
-
-import org.robolectric.Robolectric;
-
-/**
- * Utilities for creating Fragments for testing.
- * <p>
- * TODO(b/111195449) - Duplicated from org.robolectric.shadows.support.v4.SupportFragmentTestUtil
- */
-@Deprecated
-public class FragmentTestUtils {
-
-    public static void startFragment(Fragment fragment) {
-        buildFragmentManager(FragmentUtilActivity.class)
-                .beginTransaction().add(fragment, null).commit();
-    }
-
-    public static void startFragment(Fragment fragment,
-            Class<? extends FragmentActivity> activityClass) {
-        buildFragmentManager(activityClass)
-                .beginTransaction().add(fragment, null).commit();
-    }
-
-    public static void startVisibleFragment(Fragment fragment) {
-        buildFragmentManager(FragmentUtilActivity.class)
-                .beginTransaction().add(1, fragment, null).commit();
-    }
-
-    public static void startVisibleFragment(Fragment fragment,
-            Class<? extends FragmentActivity> activityClass, int containerViewId) {
-        buildFragmentManager(activityClass)
-                .beginTransaction().add(containerViewId, fragment, null).commit();
-    }
-
-    private static FragmentManager buildFragmentManager(
-            Class<? extends FragmentActivity> activityClass) {
-        FragmentActivity activity = Robolectric.setupActivity(activityClass);
-        return activity.getSupportFragmentManager();
-    }
-
-    private static class FragmentUtilActivity extends FragmentActivity {
-        @Override
-        protected void onCreate(Bundle savedInstanceState) {
-            super.onCreate(savedInstanceState);
-            LinearLayout view = new LinearLayout(this);
-            view.setId(1);
-
-            setContentView(view);
-        }
-    }
-}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index bd21b83..c09b763 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -659,6 +659,9 @@
         dumpSetting(s, p,
                 Settings.Global.GPU_DEBUG_LAYERS,
                 GlobalSettingsProto.Gpu.DEBUG_LAYERS);
+        dumpSetting(s, p,
+                Settings.Global.ANGLE_ENABLED_APP,
+                GlobalSettingsProto.Gpu.ANGLE_ENABLED_APP);
         p.end(gpuToken);
 
         final long hdmiToken = p.start(GlobalSettingsProto.HDMI);
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavGesture.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavGesture.java
index aa2fb32..814324e 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavGesture.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavGesture.java
@@ -20,6 +20,7 @@
 
 import com.android.systemui.plugins.Plugin;
 import com.android.systemui.plugins.annotations.ProvidesInterface;
+import java.io.PrintWriter;
 
 @ProvidesInterface(action = NavGesture.ACTION, version = NavGesture.VERSION)
 public interface NavGesture extends Plugin {
@@ -46,6 +47,8 @@
         public void onNavigationButtonLongPress(View v);
 
         public default void destroy() { }
+
+        public default void dump(PrintWriter pw) { }
     }
 
 }
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index 710b5f7..defc49b 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -20,6 +20,10 @@
         "src/**/I*.aidl",
     ],
 
+    static_libs: [
+        "SystemUIPluginLib"
+    ],
+
     // Enforce that the library is build agains java 7 so that there are
     // no compatibility issues with launcher
     java_version: "1.7",
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java
new file mode 100644
index 0000000..9857894
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java
@@ -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.
+ */
+
+package com.android.systemui.shared.plugins;
+
+import android.content.Context;
+import android.os.Looper;
+
+/**
+ * Provides necessary components for initializing {@link PluginManagerImpl}.
+ */
+public interface PluginInitializer {
+
+    Looper getBgLooper();
+
+    /**
+     * This Runnable is run on the bg looper during initialization of {@link PluginManagerImpl}.
+     * It can be null.
+     */
+    Runnable getBgInitCallback();
+
+    String[] getWhitelistedPlugins(Context context);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginInstanceManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
similarity index 95%
rename from packages/SystemUI/src/com/android/systemui/plugins/PluginInstanceManager.java
rename to packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
index 7bc7e5f..e80c079 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginInstanceManager.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
@@ -12,7 +12,7 @@
  * permissions and limitations under the License.
  */
 
-package com.android.systemui.plugins;
+package com.android.systemui.shared.plugins;
 
 import android.app.Notification;
 import android.app.Notification.Action;
@@ -39,12 +39,14 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
-import com.android.systemui.plugins.VersionInfo.InvalidVersionException;
+import com.android.systemui.plugins.Plugin;
+import com.android.systemui.plugins.PluginFragment;
+import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.shared.plugins.VersionInfo.InvalidVersionException;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
-import com.android.systemui.R;
 
 public class PluginInstanceManager<T extends Plugin> {
 
@@ -71,8 +73,7 @@
     PluginInstanceManager(Context context, String action, PluginListener<T> listener,
             boolean allowMultiple, Looper looper, VersionInfo version, PluginManagerImpl manager) {
         this(context, context.getPackageManager(), action, listener, allowMultiple, looper, version,
-                manager, Build.IS_DEBUGGABLE,
-                context.getResources().getStringArray(R.array.config_pluginWhitelist));
+                manager, Build.IS_DEBUGGABLE, manager.getWhitelistedPlugins());
     }
 
     @VisibleForTesting
@@ -114,7 +115,7 @@
 
     public void destroy() {
         if (DEBUG) Log.d(TAG, "stopListening");
-        ArrayList<PluginInfo> plugins = new ArrayList<>(mPluginHandler.mPlugins);
+        ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins);
         for (PluginInfo plugin : plugins) {
             mMainHandler.obtainMessage(MainHandler.PLUGIN_DISCONNECTED,
                     plugin.mPlugin).sendToTarget();
@@ -132,7 +133,7 @@
 
     public boolean checkAndDisable(String className) {
         boolean disableAny = false;
-        ArrayList<PluginInfo> plugins = new ArrayList<>(mPluginHandler.mPlugins);
+        ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins);
         for (PluginInfo info : plugins) {
             if (className.startsWith(info.mPackage)) {
                 disable(info);
@@ -143,7 +144,7 @@
     }
 
     public boolean disableAll() {
-        ArrayList<PluginInfo> plugins = new ArrayList<>(mPluginHandler.mPlugins);
+        ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins);
         for (int i = 0; i < plugins.size(); i++) {
             disable(plugins.get(i));
         }
@@ -165,7 +166,7 @@
     }
 
     public <T> boolean dependsOn(Plugin p, Class<T> cls) {
-        ArrayList<PluginInfo> plugins = new ArrayList<>(mPluginHandler.mPlugins);
+        ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins);
         for (PluginInfo info : plugins) {
             if (info.mPlugin.getClass().getName().equals(p.getClass().getName())) {
                 return info.mVersion != null && info.mVersion.hasClass(cls);
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManager.java
similarity index 72%
rename from packages/SystemUI/src/com/android/systemui/plugins/PluginManager.java
rename to packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManager.java
index 298eaf1..208f4fe 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginManager.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManager.java
@@ -12,10 +12,12 @@
  * permissions and limitations under the License.
  */
 
-package com.android.systemui.plugins;
+package com.android.systemui.shared.plugins;
 
 import android.text.TextUtils;
 
+import com.android.systemui.plugins.Plugin;
+import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.plugins.annotations.ProvidesInterface;
 
 public interface PluginManager {
@@ -40,14 +42,17 @@
 
     <T> boolean dependsOn(Plugin p, Class<T> cls);
 
-    static <P> String getAction(Class<P> cls) {
-        ProvidesInterface info = cls.getDeclaredAnnotation(ProvidesInterface.class);
-        if (info == null) {
-            throw new RuntimeException(cls + " doesn't provide an interface");
+    class Helper {
+        public static <P> String getAction(Class<P> cls) {
+            ProvidesInterface info = cls.getDeclaredAnnotation(ProvidesInterface.class);
+            if (info == null) {
+                throw new RuntimeException(cls + " doesn't provide an interface");
+            }
+            if (TextUtils.isEmpty(info.action())) {
+                throw new RuntimeException(cls + " doesn't provide an action");
+            }
+            return info.action();
         }
-        if (TextUtils.isEmpty(info.action())) {
-            throw new RuntimeException(cls + " doesn't provide an action");
-        }
-        return info.action();
     }
+
 }
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
similarity index 90%
rename from packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java
rename to packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
index 1cbf1fe..7f1d161 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
@@ -12,7 +12,7 @@
  * permissions and limitations under the License.
  */
 
-package com.android.systemui.plugins;
+package com.android.systemui.shared.plugins;
 
 import android.app.Notification;
 import android.app.Notification.Action;
@@ -41,10 +41,11 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
-import com.android.systemui.Dependency;
-import com.android.systemui.R;
-import com.android.systemui.plugins.PluginInstanceManager.PluginContextWrapper;
-import com.android.systemui.plugins.PluginInstanceManager.PluginInfo;
+
+import com.android.systemui.plugins.Plugin;
+import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.shared.plugins.PluginInstanceManager.PluginContextWrapper;
+import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo;
 import com.android.systemui.plugins.annotations.ProvidesInterface;
 
 import dalvik.system.PathClassLoader;
@@ -79,31 +80,33 @@
     private Looper mLooper;
     private boolean mWtfsSet;
 
-    public PluginManagerImpl(Context context) {
+    public PluginManagerImpl(Context context, PluginInitializer initializer) {
         this(context, new PluginInstanceManagerFactory(), Build.IS_DEBUGGABLE,
-                context.getResources().getStringArray(R.array.config_pluginWhitelist),
-                Thread.getUncaughtExceptionPreHandler());
+                Thread.getUncaughtExceptionPreHandler(), initializer);
     }
 
     @VisibleForTesting
     PluginManagerImpl(Context context, PluginInstanceManagerFactory factory, boolean debuggable,
-            String[] whitelistedPlugins, UncaughtExceptionHandler defaultHandler) {
+            UncaughtExceptionHandler defaultHandler, PluginInitializer initializer) {
         mContext = context;
         mFactory = factory;
-        mLooper = Dependency.get(Dependency.BG_LOOPER);
+        mLooper = initializer.getBgLooper();
         isDebuggable = debuggable;
-        mWhitelistedPlugins.addAll(Arrays.asList(whitelistedPlugins));
+        mWhitelistedPlugins.addAll(Arrays.asList(initializer.getWhitelistedPlugins(mContext)));
         mPluginPrefs = new PluginPrefs(mContext);
 
         PluginExceptionHandler uncaughtExceptionHandler = new PluginExceptionHandler(
                 defaultHandler);
         Thread.setUncaughtExceptionPreHandler(uncaughtExceptionHandler);
-        new Handler(mLooper).post(() -> {
-            // Plugin dependencies that don't have another good home can go here, but
-            // dependencies that have better places to init can happen elsewhere.
-            Dependency.get(PluginDependencyProvider.class)
-                    .allowPluginDependency(ActivityStarter.class);
-        });
+
+        Runnable bgRunnable = initializer.getBgInitCallback();
+        if (bgRunnable != null) {
+            new Handler(mLooper).post(bgRunnable);
+        }
+    }
+
+    public String[] getWhitelistedPlugins() {
+        return mWhitelistedPlugins.toArray(new String[0]);
     }
 
     public <T extends Plugin> T getOneShotPlugin(Class<T> cls) {
@@ -121,7 +124,9 @@
         if (Looper.myLooper() != Looper.getMainLooper()) {
             throw new RuntimeException("Must be called from UI thread");
         }
-        PluginInstanceManager<T> p = mFactory.createPluginInstanceManager(mContext, action, null,
+        // Passing null causes compiler to complain about incompatible (generic) types.
+        PluginListener<Plugin> dummy = null;
+        PluginInstanceManager<T> p = mFactory.createPluginInstanceManager(mContext, action, dummy,
                 false, mLooper, cls, this);
         mPluginPrefs.addAction(action);
         PluginInfo<T> info = p.getPlugin();
@@ -140,7 +145,7 @@
 
     public <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls,
             boolean allowMultiple) {
-        addPluginListener(PluginManager.getAction(cls), listener, cls, allowMultiple);
+        addPluginListener(PluginManager.Helper.getAction(cls), listener, cls, allowMultiple);
     }
 
     public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
@@ -293,8 +298,12 @@
     public void handleWtfs() {
         if (!mWtfsSet) {
             mWtfsSet = true;
-            Log.setWtfHandler((tag, what, system) -> {
-                throw new CrashWhilePluginActiveException(what);
+            Log.setWtfHandler(new Log.TerribleFailureHandler() {
+                @Override
+                public void onTerribleFailure(String tag, Log.TerribleFailure what,
+                        boolean system) {
+                    throw new CrashWhilePluginActiveException(what);
+                }
             });
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginPrefs.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginPrefs.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/plugins/PluginPrefs.java
rename to packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginPrefs.java
index 3671b3c..c0c5d70 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginPrefs.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginPrefs.java
@@ -12,7 +12,7 @@
  * permissions and limitations under the License.
  */
 
-package com.android.systemui.plugins;
+package com.android.systemui.shared.plugins;
 
 import android.content.Context;
 import android.content.SharedPreferences;
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/VersionInfo.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/VersionInfo.java
similarity index 75%
rename from packages/SystemUI/src/com/android/systemui/plugins/VersionInfo.java
rename to packages/SystemUI/shared/src/com/android/systemui/shared/plugins/VersionInfo.java
index facfd98..bb845cd 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/VersionInfo.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/VersionInfo.java
@@ -12,7 +12,9 @@
  * permissions and limitations under the License.
  */
 
-package com.android.systemui.plugins;
+package com.android.systemui.shared.plugins;
+
+import android.util.ArrayMap;
 
 import com.android.systemui.plugins.annotations.Dependencies;
 import com.android.systemui.plugins.annotations.DependsOn;
@@ -20,7 +22,7 @@
 import com.android.systemui.plugins.annotations.Requirements;
 import com.android.systemui.plugins.annotations.Requires;
 
-import android.util.ArrayMap;
+import java.util.function.BiConsumer;
 
 public class VersionInfo {
 
@@ -73,25 +75,32 @@
     }
 
     public void checkVersion(VersionInfo plugin) throws InvalidVersionException {
-        ArrayMap<Class<?>, Version> versions = new ArrayMap<>(mVersions);
-        plugin.mVersions.forEach((aClass, version) -> {
-            Version v = versions.remove(aClass);
-            if (v == null) {
-                v = createVersion(aClass);
-            }
-            if (v == null) {
-                throw new InvalidVersionException(aClass.getSimpleName()
-                        + " does not provide an interface", false);
-            }
-            if (v.mVersion != version.mVersion) {
-                throw new InvalidVersionException(aClass, v.mVersion < version.mVersion, v.mVersion,
-                        version.mVersion);
+        final ArrayMap<Class<?>, Version> versions = new ArrayMap<>(mVersions);
+        plugin.mVersions.forEach(new BiConsumer<Class<?>, Version>() {
+            @Override
+            public void accept(Class<?> aClass, Version version) {
+                Version v = versions.remove(aClass);
+                if (v == null) {
+                    v = VersionInfo.this.createVersion(aClass);
+                }
+                if (v == null) {
+                    throw new InvalidVersionException(aClass.getSimpleName()
+                            + " does not provide an interface", false);
+                }
+                if (v.mVersion != version.mVersion) {
+                    throw new InvalidVersionException(aClass, v.mVersion < version.mVersion,
+                            v.mVersion,
+                            version.mVersion);
+                }
             }
         });
-        versions.forEach((aClass, version) -> {
-            if (version.mRequired) {
-                throw new InvalidVersionException("Missing required dependency "
-                        + aClass.getSimpleName(), false);
+        versions.forEach(new BiConsumer<Class<?>, Version>() {
+            @Override
+            public void accept(Class<?> aClass, Version version) {
+                if (version.mRequired) {
+                    throw new InvalidVersionException("Missing required dependency "
+                            + aClass.getSimpleName(), false);
+                }
             }
         });
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 5bbbc52..28eff46 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -14,7 +14,7 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.plugins.ClockPlugin;
 import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManager;
 
 /**
  * Switch to show plugin clock when plugin is connected, otherwise it will show default clock.
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index b2cf305..fe1fe1a 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -39,9 +39,10 @@
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.PluginInitializerImpl;
 import com.android.systemui.plugins.PluginDependencyProvider;
-import com.android.systemui.plugins.PluginManager;
-import com.android.systemui.plugins.PluginManagerImpl;
+import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManagerImpl;
 import com.android.systemui.plugins.VolumeDialogController;
 import com.android.systemui.power.EnhancedEstimates;
 import com.android.systemui.power.EnhancedEstimatesImpl;
@@ -236,7 +237,7 @@
                 new DeviceProvisionedControllerImpl(mContext));
 
         mProviders.put(PluginManager.class, () ->
-                new PluginManagerImpl(mContext));
+                new PluginManagerImpl(mContext, new PluginInitializerImpl()));
 
         mProviders.put(AssistManager.class, () ->
                 new AssistManager(getDependency(DeviceProvisionedController.class), mContext));
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 408e599..77f4bf5 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -256,6 +256,12 @@
                 Log.d(TAG, "onSurfaceRedrawNeeded");
             }
             super.onSurfaceRedrawNeeded(holder);
+            // At the end of this method we should have drawn into the surface.
+            // This means that the bitmap should be loaded synchronously if
+            // it was already unloaded.
+            if (mBackground == null) {
+                updateBitmap(mWallpaperManager.getBitmap(true /* hardware */));
+            }
             mSurfaceRedrawNeeded = true;
             drawFrame();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/PluginInflateContainer.java b/packages/SystemUI/src/com/android/systemui/PluginInflateContainer.java
index ddd4833..f6ad626 100644
--- a/packages/SystemUI/src/com/android/systemui/PluginInflateContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/PluginInflateContainer.java
@@ -21,7 +21,7 @@
 import android.view.View;
 
 import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.plugins.ViewProvider;
 
 /**
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index b96a604..78053b2 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -34,7 +34,7 @@
 
 import com.android.systemui.plugins.OverlayPlugin;
 import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarWindowController;
 import com.android.systemui.util.NotificationChannels;
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIService.java b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
index bb82a54..8e29841 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIService.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
@@ -28,8 +28,8 @@
 import java.io.PrintWriter;
 
 import com.android.internal.os.BinderInternal;
-import com.android.systemui.plugins.PluginManager;
-import com.android.systemui.plugins.PluginManagerImpl;
+import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManagerImpl;
 
 public class SystemUIService extends Service {
 
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index e6026c1..21b21d9 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -44,7 +44,7 @@
     public static final int PULSE_REASON_SENSOR_PICKUP = 3;
     public static final int PULSE_REASON_SENSOR_DOUBLE_TAP = 4;
     public static final int PULSE_REASON_SENSOR_LONG_PRESS = 5;
-    public static final int PULSE_REASON_SENSOR_REACH = 6;
+    public static final int PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN = 6;
     public static final int REASON_SENSOR_WAKE_UP = 7;
 
     private static boolean sRegisterKeyguardCallback = true;
@@ -177,9 +177,9 @@
         log("state " + state);
     }
 
-    public static void traceReachWakeUp() {
+    public static void traceWakeLockScreenWakeUp() {
         if (!ENABLED) return;
-        log("reachWakeUp");
+        log("wakeLockScreenWakeUp");
     }
 
     public static void traceProximityResult(Context context, boolean near, long millis,
@@ -199,7 +199,7 @@
             case PULSE_REASON_SENSOR_PICKUP: return "pickup";
             case PULSE_REASON_SENSOR_DOUBLE_TAP: return "doubletap";
             case PULSE_REASON_SENSOR_LONG_PRESS: return "longpress";
-            case PULSE_REASON_SENSOR_REACH: return "reach";
+            case PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN: return "wakeLockScreen";
             case REASON_SENSOR_WAKE_UP: return "wakeup";
             default: throw new IllegalArgumentException("bad reason: " + pulseReason);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index f9dfb5d..7013947 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -113,10 +113,10 @@
                         true /* reports touch coordinates */,
                         true /* touchscreen */),
                 new TriggerSensor(
-                        findSensorWithType(config.reachSensorType()),
-                        Settings.Secure.DOZE_REACH_GESTURE,
+                        findSensorWithType(config.wakeLockScreenSensorType()),
+                        Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE,
                         true /* configured */,
-                        DozeLog.PULSE_REASON_SENSOR_REACH,
+                        DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN,
                         false /* reports touch coordinates */,
                         false /* touchscreen */),
                 new WakeScreenSensor(),
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
index 7339304..c61e10a 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
@@ -26,7 +26,7 @@
 import com.android.systemui.plugins.DozeServicePlugin;
 import com.android.systemui.plugins.DozeServicePlugin.RequestDoze;
 import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManager;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 31548b9..cb91d78 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -128,7 +128,7 @@
         boolean isDoubleTap = pulseReason == DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP;
         boolean isPickup = pulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP;
         boolean isLongPress = pulseReason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS;
-        boolean isReach = pulseReason == DozeLog.PULSE_REASON_SENSOR_REACH;
+        boolean isWakeLockScreen = pulseReason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN;
 
         if (isLongPress) {
             requestPulse(pulseReason, sensorPerformedProxCheck);
@@ -141,7 +141,7 @@
                 if (isDoubleTap) {
                     mDozeHost.onDoubleTap(screenX, screenY);
                     mMachine.wakeUp();
-                } else if (isPickup || isReach) {
+                } else if (isPickup || isWakeLockScreen) {
                     mMachine.wakeUp();
                 } else {
                     mDozeHost.extendPulse();
@@ -156,8 +156,8 @@
             final boolean withinVibrationThreshold =
                     timeSinceNotification < mDozeParameters.getPickupVibrationThreshold();
             DozeLog.tracePickupWakeUp(mContext, withinVibrationThreshold);
-        } else if (isReach) {
-            DozeLog.traceReachWakeUp();
+        } else if (isWakeLockScreen) {
+            DozeLog.traceWakeLockScreenWakeUp();
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java
index c58d889..03daa95 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java
@@ -18,6 +18,7 @@
 
 import com.android.systemui.Dependency;
 import com.android.systemui.plugins.PluginDependency.DependencyProvider;
+import com.android.systemui.shared.plugins.PluginManager;
 
 public class PluginDependencyProvider extends DependencyProvider {
 
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java
new file mode 100644
index 0000000..108c2d0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java
@@ -0,0 +1,47 @@
+/*
+ * 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.systemui.plugins;
+
+import android.content.Context;
+import android.os.Looper;
+
+import com.android.systemui.Dependency;
+import com.android.systemui.shared.plugins.PluginInitializer;
+import com.android.systemui.R;
+
+public class PluginInitializerImpl implements PluginInitializer {
+    @Override
+    public Looper getBgLooper() {
+        return Dependency.get(Dependency.BG_LOOPER);
+    }
+
+    @Override
+    public Runnable getBgInitCallback() {
+        return new Runnable() {
+            @Override
+            public void run() {
+                // Plugin dependencies that don't have another good home can go here, but
+                // dependencies that have better places to init can happen elsewhere.
+                Dependency.get(PluginDependencyProvider.class)
+                        .allowPluginDependency(ActivityStarter.class);
+            }
+        };
+    }
+
+    @Override
+    public String[] getWhitelistedPlugins(Context context) {
+        return context.getResources().getStringArray(R.array.config_pluginWhitelist);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 8b2e1d5..e98ef4c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -199,7 +199,11 @@
 
     public void openDetails(String subPanel) {
         QSTile tile = getTile(subPanel);
-        showDetailAdapter(true, tile.getDetailAdapter(), new int[]{getWidth() / 2, 0});
+        // If there's no tile with that name (as defined in QSFactoryImpl or other QSFactory),
+        // QSFactory will not be able to create a tile and getTile will return null
+        if (tile != null) {
+            showDetailAdapter(true, tile.getDetailAdapter(), new int[]{getWidth() / 2, 0});
+        }
     }
 
     private QSTile getTile(String subPanel) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 86e69e3..cefeeb5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -31,7 +31,7 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.plugins.qs.QSFactory;
 import com.android.systemui.plugins.qs.QSTileView;
 import com.android.systemui.plugins.qs.QSTile;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 9b1d334..bce613a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -72,7 +72,7 @@
 import com.android.systemui.R;
 import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
 import com.android.systemui.statusbar.notification.NotificationData;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
deleted file mode 100644
index 62d2099..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.view.MotionEvent;
-import android.view.View;
-import com.android.systemui.SysUiServiceProvider;
-import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper;
-
-/**
- * TODO: Remove and replace with QuickStepController
- */
-public class NavigationBarGestureHelper implements GestureHelper {
-
-    private static final String TAG = "NavigationBarGestureHelper";
-
-    private NavigationBarView mNavigationBarView;
-
-    private final QuickStepController mQuickStepController;
-    private final StatusBar mStatusBar;
-
-    public NavigationBarGestureHelper(Context context) {
-        mStatusBar = SysUiServiceProvider.getComponent(context, StatusBar.class);
-        mQuickStepController = new QuickStepController(context);
-    }
-
-    public void setComponents(NavigationBarView navigationBarView) {
-        mNavigationBarView = navigationBarView;
-        mQuickStepController.setComponents(mNavigationBarView);
-    }
-
-    public void setBarState(boolean isVertical, boolean isRTL) {
-        mQuickStepController.setBarState(isVertical, isRTL);
-    }
-
-    public boolean onInterceptTouchEvent(MotionEvent event) {
-        if (!canHandleGestures()) {
-            return false;
-        }
-        return mQuickStepController.onInterceptTouchEvent(event);
-    }
-
-    public boolean onTouchEvent(MotionEvent event) {
-        if (!canHandleGestures()) {
-            return false;
-        }
-        return mQuickStepController.onTouchEvent(event);
-    }
-
-    public void onDraw(Canvas canvas) {
-        mQuickStepController.onDraw(canvas);
-    }
-
-    public void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        mQuickStepController.onLayout(changed, left, top, right, bottom);
-    }
-
-    public void onDarkIntensityChange(float intensity) {
-        mQuickStepController.onDarkIntensityChange(intensity);
-    }
-
-    public void onNavigationButtonLongPress(View v) {
-        mQuickStepController.onNavigationButtonLongPress(v);
-    }
-
-    private boolean canHandleGestures() {
-        return !mStatusBar.isKeyguardShowing();
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
index e6f2c33..52134d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
@@ -37,7 +37,7 @@
 import com.android.systemui.OverviewProxyService;
 import com.android.systemui.R;
 import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.plugins.statusbar.phone.NavBarButtonProvider;
 import com.android.systemui.statusbar.phone.ReverseLinearLayout.ReverseRelativeLayout;
 import com.android.systemui.statusbar.policy.KeyButtonView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index e5c9100..16b2987 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -40,7 +40,6 @@
 import android.os.Handler;
 import android.os.Message;
 import android.os.SystemProperties;
-import androidx.annotation.ColorInt;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.SparseArray;
@@ -64,12 +63,11 @@
 import com.android.systemui.RecentsComponent;
 import com.android.systemui.SysUiServiceProvider;
 import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.plugins.statusbar.phone.NavGesture;
 import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsOnboarding;
-import com.android.systemui.shared.recents.IOverviewProxy;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.NavigationBarCompat;
 import com.android.systemui.shared.system.WindowManagerWrapper;
@@ -314,8 +312,8 @@
 
     public void setComponents(NotificationPanelView panel) {
         mPanelView = panel;
-        if (mGestureHelper instanceof NavigationBarGestureHelper) {
-            ((NavigationBarGestureHelper) mGestureHelper).setComponents(this);
+        if (mGestureHelper instanceof QuickStepController) {
+            ((QuickStepController) mGestureHelper).setComponents(this);
         }
     }
 
@@ -1071,7 +1069,7 @@
 
     @Override
     public void onPluginDisconnected(NavGesture plugin) {
-        NavigationBarGestureHelper defaultHelper = new NavigationBarGestureHelper(getContext());
+        QuickStepController defaultHelper = new QuickStepController(getContext());
         defaultHelper.setComponents(this);
         if (mGestureHelper != null) {
             mGestureHelper.destroy();
@@ -1117,6 +1115,7 @@
         pw.println("    }");
 
         mContextualButtonGroup.dump(pw);
+        mGestureHelper.dump(pw);
         mRecentsOnboarding.dump(pw);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationListenerWithPlugins.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationListenerWithPlugins.java
index 9ff907b..9e561d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationListenerWithPlugins.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationListenerWithPlugins.java
@@ -24,7 +24,7 @@
 import com.android.systemui.plugins.NotificationListenerController;
 import com.android.systemui.plugins.NotificationListenerController.NotificationProvider;
 import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManager;
 
 import java.util.ArrayList;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
index 30e6afa..bce52a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
@@ -58,10 +58,12 @@
 import com.android.systemui.Interpolators;
 import com.android.systemui.OverviewProxyService;
 import com.android.systemui.R;
+import com.android.systemui.SysUiServiceProvider;
 import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper;
 import com.android.systemui.shared.recents.IOverviewProxy;
 import com.android.systemui.shared.recents.utilities.Utilities;
 import com.android.systemui.shared.system.NavigationBarCompat;
+import java.io.PrintWriter;
 
 /**
  * Class to detect gestures on the navigation bar and implement quick scrub.
@@ -117,6 +119,7 @@
     private final int mTrackEndPadding;
     private final int mHomeBackGestureDragLimit;
     private final Context mContext;
+    private final StatusBar mStatusBar;
     private final Matrix mTransformGlobalMatrix = new Matrix();
     private final Matrix mTransformLocalMatrix = new Matrix();
     private final Paint mTrackPaint = new Paint();
@@ -195,6 +198,7 @@
     public QuickStepController(Context context) {
         final Resources res = context.getResources();
         mContext = context;
+        mStatusBar = SysUiServiceProvider.getComponent(context, StatusBar.class);
         mOverviewEventSender = Dependency.get(OverviewProxyService.class);
         mTrackThickness = res.getDimensionPixelSize(R.dimen.nav_quick_scrub_track_thickness);
         mTrackEndPadding = res.getDimensionPixelSize(R.dimen.nav_quick_scrub_track_edge_padding);
@@ -218,6 +222,10 @@
      */
     @Override
     public boolean onInterceptTouchEvent(MotionEvent event) {
+        if (mStatusBar.isKeyguardShowing()) {
+            // Disallow any handling when the keyguard is showing
+            return false;
+        }
         return handleTouchEvent(event);
     }
 
@@ -227,6 +235,11 @@
      */
     @Override
     public boolean onTouchEvent(MotionEvent event) {
+        if (mStatusBar.isKeyguardShowing()) {
+            // Disallow any handling when the keyguard is showing
+            return false;
+        }
+
         // The same down event was just sent on intercept and therefore can be ignored here
         final boolean ignoreProxyDownEvent = event.getAction() == MotionEvent.ACTION_DOWN
                 && mOverviewEventSender.getProxy() != null;
@@ -483,6 +496,21 @@
         mHandler.removeCallbacksAndMessages(null);
     }
 
+    @Override
+    public void dump(PrintWriter pw) {
+        pw.println("QuickStepController {");
+        pw.print("    "); pw.println("mQuickScrubActive=" + mQuickScrubActive);
+        pw.print("    "); pw.println("mQuickStepStarted=" + mQuickStepStarted);
+        pw.print("    "); pw.println("mAllowGestureDetection=" + mAllowGestureDetection);
+        pw.print("    "); pw.println("mBackGestureActive=" + mBackGestureActive);
+        pw.print("    "); pw.println("mCanPerformBack=" + mCanPerformBack);
+        pw.print("    "); pw.println("mNotificationsVisibleOnDown=" + mNotificationsVisibleOnDown);
+        pw.print("    "); pw.println("mIsVertical=" + mIsVertical);
+        pw.print("    "); pw.println("mIsRTL=" + mIsRTL);
+        pw.print("    "); pw.println("mIsInScreenPinning=" + mIsInScreenPinning);
+        pw.println("}");
+    }
+
     private void startQuickStep(MotionEvent event) {
         if (mIsInScreenPinning) {
             mNavigationBarView.showPinningEscapeToast();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 226b645..c3b87af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -2182,7 +2182,7 @@
     }
 
     @Override
-    public void animateExpandSettingsPanel(String subPanel) {
+    public void animateExpandSettingsPanel(@Nullable String subPanel) {
         if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible);
         if (!panelsEnabled()) {
             return;
@@ -2191,7 +2191,6 @@
         // Settings are not available in setup
         if (!mUserSetup) return;
 
-
         if (subPanel != null) {
             mQSPanel.openDetails(subPanel);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
index 6d75cfc..a6146a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
@@ -22,7 +22,7 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.plugins.Plugin;
 import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.tuner.TunerService.Tunable;
@@ -71,7 +71,7 @@
 
         @Override
         public <P extends T> ExtensionController.ExtensionBuilder<T> withPlugin(Class<P> cls) {
-            return withPlugin(cls, PluginManager.getAction(cls));
+            return withPlugin(cls, PluginManager.Helper.getAction(cls));
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
index c294806..71414a2 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
@@ -34,11 +34,10 @@
 import android.view.View;
 
 import com.android.systemui.R;
-import com.android.systemui.plugins.PluginInstanceManager;
-import com.android.systemui.plugins.PluginManager;
-import com.android.systemui.plugins.PluginPrefs;
+import com.android.systemui.shared.plugins.PluginInstanceManager;
+import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginPrefs;
 
-import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
 
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
index 088630f..5aa3035 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
@@ -32,7 +32,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.systemui.R;
-import com.android.systemui.plugins.PluginPrefs;
+import com.android.systemui.shared.plugins.PluginPrefs;
 
 public class TunerFragment extends PreferenceFragment {
 
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 13c43f7..4810b0b 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -18,6 +18,7 @@
 
 import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK;
 import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_GENERIC;
+import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
 import static android.media.AudioManager.RINGER_MODE_NORMAL;
 import static android.media.AudioManager.RINGER_MODE_SILENT;
 import static android.media.AudioManager.RINGER_MODE_VIBRATE;
@@ -34,6 +35,7 @@
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.animation.ObjectAnimator;
 import android.annotation.SuppressLint;
+import android.app.ActivityManager;
 import android.app.Dialog;
 import android.app.KeyguardManager;
 import android.content.ContentResolver;
@@ -129,6 +131,7 @@
     private ConfigurableTexts mConfigurableTexts;
     private final SparseBooleanArray mDynamic = new SparseBooleanArray();
     private final KeyguardManager mKeyguard;
+    private final ActivityManager mActivityManager;
     private final AccessibilityManagerWrapper mAccessibilityMgr;
     private final Object mSafetyWarningLock = new Object();
     private final Accessibility mAccessibility = new Accessibility();
@@ -154,6 +157,7 @@
         mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
         mController = Dependency.get(VolumeDialogController.class);
         mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
+        mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
         mAccessibilityMgr = Dependency.get(AccessibilityManagerWrapper.class);
         mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
         mShowActiveStreamOnly = showActiveStreamOnly();
@@ -431,7 +435,9 @@
     public void initSettingsH() {
         if (mSettingsView != null) {
             mSettingsView.setVisibility(
-                    mDeviceProvisionedController.isCurrentUserSetup() ? VISIBLE : GONE);
+                    mDeviceProvisionedController.isCurrentUserSetup() &&
+                            mActivityManager.getLockTaskModeState() == LOCK_TASK_MODE_NONE ?
+                            VISIBLE : GONE);
         }
         if (mSettingsIcon != null) {
             mSettingsIcon.setOnClickListener(v -> {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
index e6e4857..62ca3f3 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
@@ -40,7 +40,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.ClockPlugin;
 import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManager;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
index 85cdfcc..12a122a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
@@ -14,8 +14,12 @@
 
 package com.android.systemui.qs;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -23,15 +27,21 @@
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.qs.QSTile;
+import com.android.systemui.plugins.qs.QSTileView;
 import com.android.systemui.qs.customize.QSCustomizer;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 import java.util.Collections;
 
@@ -41,19 +51,37 @@
 public class QSPanelTest extends SysuiTestCase {
 
     private MetricsLogger mMetricsLogger;
+    private TestableLooper mTestableLooper;
     private QSPanel mQsPanel;
+    @Mock
     private QSTileHost mHost;
+    @Mock
     private QSCustomizer mCustomizer;
+    @Mock
+    private QSTile dndTile;
+    private ViewGroup mParentView;
+    @Mock
+    private QSDetail.Callback mCallback;
 
     @Before
     public void setup() throws Exception {
-        TestableLooper.get(this).runWithLooper(() -> {
+        MockitoAnnotations.initMocks(this);
+
+        mTestableLooper = TestableLooper.get(this);
+        mTestableLooper.runWithLooper(() -> {
             mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
             mQsPanel = new QSPanel(mContext, null);
-            mHost = mock(QSTileHost.class);
+            // Provides a parent with non-zero size for QSPanel
+            mParentView = new FrameLayout(mContext);
+            mParentView.addView(mQsPanel);
+
+            when(dndTile.getTileSpec()).thenReturn("dnd");
             when(mHost.getTiles()).thenReturn(Collections.emptyList());
-            mCustomizer = mock(QSCustomizer.class);
+            when(mHost.createTileView(any(), anyBoolean())).thenReturn(mock(QSTileView.class));
+
             mQsPanel.setHost(mHost, mCustomizer);
+            mQsPanel.addTile(dndTile, true);
+            mQsPanel.setCallback(mCallback);
         });
     }
 
@@ -64,4 +92,31 @@
         mQsPanel.setExpanded(false);
         verify(mMetricsLogger).visibility(eq(MetricsEvent.QS_PANEL), eq(false));
     }
+
+    @Test
+    public void testOpenDetailsWithExistingTile_NoException() {
+        mTestableLooper.processAllMessages();
+        mQsPanel.openDetails("dnd");
+        mTestableLooper.processAllMessages();
+
+        verify(mCallback).onShowingDetail(any(), anyInt(), anyInt());
+    }
+
+/*    @Test
+    public void testOpenDetailsWithNullParameter_NoException() {
+        mTestableLooper.processAllMessages();
+        mQsPanel.openDetails(null);
+        mTestableLooper.processAllMessages();
+
+        verify(mCallback, never()).onShowingDetail(any(), anyInt(), anyInt());
+    }*/
+
+    @Test
+    public void testOpenDetailsWithNonExistingTile_NoException() {
+        mTestableLooper.processAllMessages();
+        mQsPanel.openDetails("invalid-name");
+        mTestableLooper.processAllMessages();
+
+        verify(mCallback, never()).onShowingDetail(any(), anyInt(), anyInt());
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java
index 19974f8..6d1ff8c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java
@@ -12,7 +12,7 @@
  * permissions and limitations under the License.
  */
 
-package com.android.systemui.plugins;
+package com.android.systemui.shared.plugins;
 
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertNotNull;
@@ -27,8 +27,10 @@
 
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.PluginInstanceManager.PluginInfo;
-import com.android.systemui.plugins.VersionInfo.InvalidVersionException;
+import com.android.systemui.plugins.Plugin;
+import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo;
+import com.android.systemui.shared.plugins.VersionInfo.InvalidVersionException;
 import com.android.systemui.plugins.annotations.Requires;
 
 import org.junit.After;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java
similarity index 87%
rename from packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java
index 438f9e4..3c70205 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java
@@ -11,7 +11,7 @@
  * KIND, either express or implied. See the License for the specific language governing
  * permissions and limitations under the License.
  */
-package com.android.systemui.plugins;
+package com.android.systemui.shared.plugins;
 
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
@@ -27,6 +27,7 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.net.Uri;
+import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -35,9 +36,12 @@
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.systemui.Dependency;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.PluginInstanceManager.PluginInfo;
-import com.android.systemui.plugins.PluginManagerImpl.PluginInstanceManagerFactory;
+import com.android.systemui.plugins.Plugin;
+import com.android.systemui.plugins.PluginInitializerImpl;
+import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.plugins.annotations.ProvidesInterface;
+import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo;
+import com.android.systemui.shared.plugins.PluginManagerImpl.PluginInstanceManagerFactory;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -74,8 +78,14 @@
         when(mMockFactory.createPluginInstanceManager(Mockito.any(), Mockito.any(), Mockito.any(),
                 Mockito.anyBoolean(), Mockito.any(), Mockito.any(), Mockito.any()))
                 .thenReturn(mMockPluginInstance);
-        mPluginManager = new PluginManagerImpl(getContext(), mMockFactory, true, new String[0],
-                mMockExceptionHandler);
+
+        mPluginManager = new PluginManagerImpl(getContext(), mMockFactory, true,
+                mMockExceptionHandler, new PluginInitializerImpl() {
+            @Override
+            public String[] getWhitelistedPlugins(Context context) {
+                return new String[0];
+            }
+        });
         resetExceptionHandler();
         mMockListener = mock(PluginListener.class);
     }
@@ -109,7 +119,12 @@
     @RunWithLooper(setAsMainLooper = true)
     public void testNonDebuggable_noWhitelist() {
         mPluginManager = new PluginManagerImpl(getContext(), mMockFactory, false,
-                new String[0], mMockExceptionHandler);
+                mMockExceptionHandler, new PluginInitializerImpl() {
+            @Override
+            public String[] getWhitelistedPlugins(Context context) {
+                return new String[0];
+            }
+        });
         resetExceptionHandler();
 
         mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class);
@@ -121,7 +136,12 @@
     @RunWithLooper(setAsMainLooper = true)
     public void testNonDebuggable_whitelistedPkg() {
         mPluginManager = new PluginManagerImpl(getContext(), mMockFactory, false,
-                new String[] {WHITELISTED_PACKAGE}, mMockExceptionHandler);
+                mMockExceptionHandler, new PluginInitializerImpl() {
+            @Override
+            public String[] getWhitelistedPlugins(Context context) {
+                return new String[] {WHITELISTED_PACKAGE};
+            }
+        });
         resetExceptionHandler();
 
         mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/plugins/VersionInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/VersionInfoTest.java
similarity index 94%
rename from packages/SystemUI/tests/src/com/android/systemui/plugins/VersionInfoTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/shared/plugins/VersionInfoTest.java
index 0b4d9b5..9bad78d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/plugins/VersionInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/VersionInfoTest.java
@@ -12,7 +12,7 @@
  * permissions and limitations under the License.
  */
 
-package com.android.systemui.plugins;
+package com.android.systemui.shared.plugins;
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -20,7 +20,8 @@
 import android.support.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.VersionInfo.InvalidVersionException;
+import com.android.systemui.plugins.OverlayPlugin;
+import com.android.systemui.shared.plugins.VersionInfo.InvalidVersionException;
 import com.android.systemui.plugins.annotations.Requires;
 import com.android.systemui.plugins.qs.QS;
 import com.android.systemui.plugins.qs.DetailAdapter;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java
index b22a646..1cceefa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java
@@ -33,7 +33,7 @@
 import com.android.systemui.plugins.OverlayPlugin;
 import com.android.systemui.plugins.Plugin;
 import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
 import com.android.systemui.statusbar.policy.ExtensionController.Extension;
 import com.android.systemui.statusbar.policy.ExtensionController.TunerFactory;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java
index 0a83a89..5f54bce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java
@@ -14,12 +14,11 @@
 
 package com.android.systemui.utils.leaks;
 
-import android.content.Context;
 import android.testing.LeakCheck;
 
 import com.android.systemui.plugins.Plugin;
 import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManager;
 
 public class FakePluginManager implements PluginManager {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java
index ecda9620..f479126 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java
@@ -20,7 +20,7 @@
 import android.util.ArrayMap;
 
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.statusbar.phone.ManagedProfileController;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.policy.BatteryController;
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 90c10fd..3947295 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -6447,7 +6447,7 @@
 
     // OPEN: Settings > System > Input & Gesture > Reach up gesture
     // OS: Q
-    SETTINGS_GESTURE_REACH = 1557;
+    SETTINGS_GESTURE_WAKE_LOCK_SCREEN = 1557;
 
     // OPEN: Emergency dialer opened
     // CLOSE: Emergency dialer closed
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index e41a09e..a1989e5 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -57,6 +57,7 @@
 import android.net.ConnectivityManager.PacketKeepalive;
 import android.net.IConnectivityManager;
 import android.net.IIpConnectivityMetrics;
+import android.net.INetd;
 import android.net.INetdEventCallback;
 import android.net.INetworkManagementEventObserver;
 import android.net.INetworkPolicyListener;
@@ -88,6 +89,7 @@
 import android.net.metrics.NetworkEvent;
 import android.net.netlink.InetDiagMessage;
 import android.net.util.MultinetworkPolicyTracker;
+import android.net.util.NetdService;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
@@ -259,7 +261,8 @@
     // 0 is full bad, 100 is full good
     private int mDefaultInetConditionPublished = 0;
 
-    private INetworkManagementService mNetd;
+    private INetworkManagementService mNMS;
+    private INetd mNetd;
     private INetworkStatsService mStatsService;
     private INetworkPolicyManager mPolicyManager;
     private NetworkPolicyManagerInternal mPolicyManagerInternal;
@@ -759,7 +762,7 @@
         mLingerDelayMs = mSystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS);
 
         mContext = checkNotNull(context, "missing Context");
-        mNetd = checkNotNull(netManager, "missing INetworkManagementService");
+        mNMS = checkNotNull(netManager, "missing INetworkManagementService");
         mStatsService = checkNotNull(statsService, "missing INetworkStatsService");
         mPolicyManager = checkNotNull(policyManager, "missing INetworkPolicyManager");
         mPolicyManagerInternal = checkNotNull(
@@ -767,6 +770,7 @@
                 "missing NetworkPolicyManagerInternal");
         mProxyTracker = new ProxyTracker(context, mHandler, EVENT_PROXY_HAS_CHANGED);
 
+        mNetd = NetdService.getInstance();
         mKeyStore = KeyStore.getInstance();
         mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
 
@@ -849,7 +853,7 @@
 
         mTethering = makeTethering();
 
-        mPermissionMonitor = new PermissionMonitor(mContext, mNetd);
+        mPermissionMonitor = new PermissionMonitor(mContext, mNMS);
 
         //set up the listener for user state for creating user VPNs
         IntentFilter intentFilter = new IntentFilter();
@@ -864,8 +868,8 @@
                 new IntentFilter(Intent.ACTION_USER_PRESENT), null, null);
 
         try {
-            mNetd.registerObserver(mTethering);
-            mNetd.registerObserver(mDataActivityObserver);
+            mNMS.registerObserver(mTethering);
+            mNMS.registerObserver(mDataActivityObserver);
         } catch (RemoteException e) {
             loge("Error registering observer :" + e);
         }
@@ -896,7 +900,7 @@
 
         mMultipathPolicyTracker = new MultipathPolicyTracker(mContext, mHandler);
 
-        mDnsManager = new DnsManager(mContext, mNetd, mSystemProperties);
+        mDnsManager = new DnsManager(mContext, mNMS, mSystemProperties);
         registerPrivateDnsSettingsCallbacks();
     }
 
@@ -912,7 +916,7 @@
                 return mDefaultRequest;
             }
         };
-        return new Tethering(mContext, mNetd, mStatsService, mPolicyManager,
+        return new Tethering(mContext, mNMS, mStatsService, mPolicyManager,
                 IoThread.get().getLooper(), new MockableSystemProperties(),
                 deps);
     }
@@ -1476,6 +1480,20 @@
     };
 
     /**
+     * Ensures that the system cannot call a particular method.
+     */
+    private boolean disallowedBecauseSystemCaller() {
+        // TODO: start throwing a SecurityException when GnssLocationProvider stops calling
+        // requestRouteToHost.
+        if (isSystem(Binder.getCallingUid())) {
+            log("This method exists only for app backwards compatibility"
+                    + " and must not be called by system services.");
+            return true;
+        }
+        return false;
+    }
+
+    /**
      * Ensure that a network route exists to deliver traffic to the specified
      * host via the specified network interface.
      * @param networkType the type of the network over which traffic to the
@@ -1486,6 +1504,9 @@
      */
     @Override
     public boolean requestRouteToHostAddress(int networkType, byte[] hostAddress) {
+        if (disallowedBecauseSystemCaller()) {
+            return false;
+        }
         enforceChangePermission();
         if (mProtectedNetworks.contains(networkType)) {
             enforceConnectivityInternalPermission();
@@ -1563,7 +1584,7 @@
         if (DBG) log("Adding legacy route " + bestRoute +
                 " for UID/PID " + uid + "/" + Binder.getCallingPid());
         try {
-            mNetd.addLegacyRouteForNetId(netId, bestRoute, uid);
+            mNMS.addLegacyRouteForNetId(netId, bestRoute, uid);
         } catch (Exception e) {
             // never crash - catch them all
             if (DBG) loge("Exception trying to add a route: " + e);
@@ -1853,7 +1874,7 @@
 
         if (timeout > 0 && iface != null && type != ConnectivityManager.TYPE_NONE) {
             try {
-                mNetd.addIdleTimer(iface, timeout, type);
+                mNMS.addIdleTimer(iface, timeout, type);
             } catch (Exception e) {
                 // You shall not crash!
                 loge("Exception in setupDataActivityTracking " + e);
@@ -1872,7 +1893,7 @@
                               caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))) {
             try {
                 // the call fails silently if no idle timer setup for this interface
-                mNetd.removeIdleTimer(iface);
+                mNMS.removeIdleTimer(iface);
             } catch (Exception e) {
                 loge("Exception in removeDataActivityTracking " + e);
             }
@@ -1880,6 +1901,18 @@
     }
 
     /**
+     * Update data activity tracking when network state is updated.
+     */
+    private void updateDataActivityTracking(NetworkAgentInfo newNetwork,
+            NetworkAgentInfo oldNetwork) {
+        if (newNetwork != null) {
+            setupDataActivityTracking(newNetwork);
+        }
+        if (oldNetwork != null) {
+            removeDataActivityTracking(oldNetwork);
+        }
+    }
+    /**
      * Reads the network specific MTU size from resources.
      * and set it on it's iface.
      */
@@ -1907,7 +1940,7 @@
 
         try {
             if (VDBG) log("Setting MTU size: " + iface + ", " + mtu);
-            mNetd.setMtu(iface, mtu);
+            mNMS.setMtu(iface, mtu);
         } catch (Exception e) {
             Slog.e(TAG, "exception in setMtu()" + e);
         }
@@ -2561,7 +2594,7 @@
         }
         nai.clearLingerState();
         if (nai.isSatisfyingRequest(mDefaultRequest.requestId)) {
-            removeDataActivityTracking(nai);
+            updateDataActivityTracking(null /* newNetwork */, nai);
             notifyLockdownVpn(nai);
             ensureNetworkTransitionWakelock(nai.name());
         }
@@ -2581,7 +2614,7 @@
             // NetworkFactories, so network traffic isn't interrupted for an unnecessarily
             // long time.
             try {
-                mNetd.removeNetwork(nai.network.netId);
+                mNMS.removeNetwork(nai.network.netId);
             } catch (Exception e) {
                 loge("Exception removing network: " + e);
             }
@@ -2779,20 +2812,6 @@
                 }
             }
 
-            // TODO: remove this code once we know that the Slog.wtf is never hit.
-            //
-            // Find all networks that are satisfying this request and remove the request
-            // from their request lists.
-            // TODO - it's my understanding that for a request there is only a single
-            // network satisfying it, so this loop is wasteful
-            for (NetworkAgentInfo otherNai : mNetworkAgentInfos.values()) {
-                if (otherNai.isSatisfyingRequest(nri.request.requestId) && otherNai != nai) {
-                    Slog.wtf(TAG, "Request " + nri.request + " satisfied by " +
-                            otherNai.name() + ", but mNetworkAgentInfos says " +
-                            (nai != null ? nai.name() : "null"));
-                }
-            }
-
             // Maintain the illusion.  When this request arrived, we might have pretended
             // that a network connected to serve it, even though the network was already
             // connected.  Now that this request has gone away, we might have to pretend
@@ -3760,7 +3779,7 @@
                     Slog.w(TAG, "VPN for user " + user + " not ready yet. Skipping lockdown");
                     return false;
                 }
-                setLockdownTracker(new LockdownVpnTracker(mContext, mNetd, this, vpn, profile));
+                setLockdownTracker(new LockdownVpnTracker(mContext, mNMS, this, vpn, profile));
             } else {
                 setLockdownTracker(null);
             }
@@ -4015,7 +4034,7 @@
                 loge("Starting user already has a VPN");
                 return;
             }
-            userVpn = new Vpn(mHandler.getLooper(), mContext, mNetd, userId);
+            userVpn = new Vpn(mHandler.getLooper(), mContext, mNMS, userId);
             mVpns.put(userId, userVpn);
             if (mUserManager.getUserInfo(userId).isPrimary() && LockdownVpnTracker.isEnabled()) {
                 updateLockdownVpn();
@@ -4632,7 +4651,7 @@
         mDnsManager.updatePrivateDnsStatus(netId, newLp);
 
         // Start or stop clat accordingly to network state.
-        networkAgent.updateClat(mNetd);
+        networkAgent.updateClat(mNMS);
         if (isDefaultNetwork(networkAgent)) {
             handleApplyDefaultProxy(newLp.getHttpProxy());
         } else {
@@ -4671,9 +4690,9 @@
         final String prefix = "iface:" + iface;
         try {
             if (add) {
-                mNetd.getNetdService().wakeupAddInterface(iface, prefix, mark, mask);
+                mNetd.wakeupAddInterface(iface, prefix, mark, mask);
             } else {
-                mNetd.getNetdService().wakeupDelInterface(iface, prefix, mark, mask);
+                mNetd.wakeupDelInterface(iface, prefix, mark, mask);
             }
         } catch (Exception e) {
             loge("Exception modifying wakeup packet monitoring: " + e);
@@ -4689,7 +4708,7 @@
         for (String iface : interfaceDiff.added) {
             try {
                 if (DBG) log("Adding iface " + iface + " to network " + netId);
-                mNetd.addInterfaceToNetwork(iface, netId);
+                mNMS.addInterfaceToNetwork(iface, netId);
                 wakeupModifyInterface(iface, caps, true);
             } catch (Exception e) {
                 loge("Exception adding interface: " + e);
@@ -4699,7 +4718,7 @@
             try {
                 if (DBG) log("Removing iface " + iface + " from network " + netId);
                 wakeupModifyInterface(iface, caps, false);
-                mNetd.removeInterfaceFromNetwork(iface, netId);
+                mNMS.removeInterfaceFromNetwork(iface, netId);
             } catch (Exception e) {
                 loge("Exception removing interface: " + e);
             }
@@ -4723,7 +4742,7 @@
             if (route.hasGateway()) continue;
             if (VDBG) log("Adding Route [" + route + "] to network " + netId);
             try {
-                mNetd.addRoute(netId, route);
+                mNMS.addRoute(netId, route);
             } catch (Exception e) {
                 if ((route.getDestination().getAddress() instanceof Inet4Address) || VDBG) {
                     loge("Exception in addRoute for non-gateway: " + e);
@@ -4734,7 +4753,7 @@
             if (route.hasGateway() == false) continue;
             if (VDBG) log("Adding Route [" + route + "] to network " + netId);
             try {
-                mNetd.addRoute(netId, route);
+                mNMS.addRoute(netId, route);
             } catch (Exception e) {
                 if ((route.getGateway() instanceof Inet4Address) || VDBG) {
                     loge("Exception in addRoute for gateway: " + e);
@@ -4745,7 +4764,7 @@
         for (RouteInfo route : routeDiff.removed) {
             if (VDBG) log("Removing Route [" + route + "] from network " + netId);
             try {
-                mNetd.removeRoute(netId, route);
+                mNMS.removeRoute(netId, route);
             } catch (Exception e) {
                 loge("Exception in removeRoute: " + e);
             }
@@ -4857,7 +4876,7 @@
         final String newPermission = getNetworkPermission(newNc);
         if (!Objects.equals(oldPermission, newPermission) && nai.created && !nai.isVPN()) {
             try {
-                mNetd.setNetworkPermission(nai.network.netId, newPermission);
+                mNMS.setNetworkPermission(nai.network.netId, newPermission);
             } catch (RemoteException e) {
                 loge("Exception in setNetworkPermission: " + e);
             }
@@ -4917,12 +4936,12 @@
             if (!newRanges.isEmpty()) {
                 final UidRange[] addedRangesArray = new UidRange[newRanges.size()];
                 newRanges.toArray(addedRangesArray);
-                mNetd.addVpnUidRanges(nai.network.netId, addedRangesArray);
+                mNMS.addVpnUidRanges(nai.network.netId, addedRangesArray);
             }
             if (!prevRanges.isEmpty()) {
                 final UidRange[] removedRangesArray = new UidRange[prevRanges.size()];
                 prevRanges.toArray(removedRangesArray);
-                mNetd.removeVpnUidRanges(nai.network.netId, removedRangesArray);
+                mNMS.removeVpnUidRanges(nai.network.netId, removedRangesArray);
             }
         } catch (Exception e) {
             // Never crash!
@@ -5091,9 +5110,9 @@
 
     private void makeDefault(NetworkAgentInfo newNetwork) {
         if (DBG) log("Switching to new default network: " + newNetwork);
-        setupDataActivityTracking(newNetwork);
+
         try {
-            mNetd.setDefaultNetId(newNetwork.network.netId);
+            mNMS.setDefaultNetId(newNetwork.network.netId);
         } catch (Exception e) {
             loge("Exception setting default network :" + e);
         }
@@ -5266,6 +5285,7 @@
             }
         }
         if (isNewDefault) {
+            updateDataActivityTracking(newNetwork, oldDefaultNetwork);
             // Notify system services that this network is up.
             makeDefault(newNetwork);
             // Log 0 -> X and Y -> X default network transitions, where X is the new default.
@@ -5488,12 +5508,12 @@
             try {
                 // This should never fail.  Specifying an already in use NetID will cause failure.
                 if (networkAgent.isVPN()) {
-                    mNetd.createVirtualNetwork(networkAgent.network.netId,
+                    mNMS.createVirtualNetwork(networkAgent.network.netId,
                             !networkAgent.linkProperties.getDnsServers().isEmpty(),
                             (networkAgent.networkMisc == null ||
                                 !networkAgent.networkMisc.allowBypass));
                 } else {
-                    mNetd.createPhysicalNetwork(networkAgent.network.netId,
+                    mNMS.createPhysicalNetwork(networkAgent.network.netId,
                             getNetworkPermission(networkAgent.networkCapabilities));
                 }
             } catch (Exception e) {
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 1d163ee..de930f7 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -161,6 +161,8 @@
 
     private static final int MAX_UID_RANGES_PER_COMMAND = 10;
 
+    private static final  String[] EMPTY_STRING_ARRAY = new String[0];
+
     /**
      * Name representing {@link #setGlobalAlert(long)} limit when delivered to
      * {@link INetworkManagementEventObserver#limitReached(String, String)}.
@@ -1234,18 +1236,12 @@
     @Override
     public void startTethering(String[] dhcpRange) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
-        // cmd is "tether start first_start first_stop second_start second_stop ..."
         // an odd number of addrs will fail
 
-        final Command cmd = new Command("tether", "start");
-        for (String d : dhcpRange) {
-            cmd.appendArg(d);
-        }
-
         try {
-            mConnector.execute(cmd);
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
+            mNetdService.tetherStart(dhcpRange);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw new IllegalStateException(e);
         }
     }
 
@@ -1253,9 +1249,9 @@
     public void stopTethering() {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
         try {
-            mConnector.execute("tether", "stop");
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
+            mNetdService.tetherStop();
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw new IllegalStateException(e);
         }
     }
 
@@ -1263,25 +1259,21 @@
     public boolean isTetheringStarted() {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
 
-        final NativeDaemonEvent event;
         try {
-            event = mConnector.execute("tether", "status");
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
+            final boolean isEnabled = mNetdService.tetherIsEnabled();
+            return isEnabled;
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw new IllegalStateException(e);
         }
-
-        // 210 Tethering services started
-        event.checkCode(TetherStatusResult);
-        return event.getMessage().endsWith("started");
     }
 
     @Override
     public void tetherInterface(String iface) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
         try {
-            mConnector.execute("tether", "interface", "add", iface);
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
+            mNetdService.tetherInterfaceAdd(iface);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw new IllegalStateException(e);
         }
         List<RouteInfo> routes = new ArrayList<>();
         // The RouteInfo constructor truncates the LinkAddress to a network prefix, thus making it
@@ -1294,9 +1286,9 @@
     public void untetherInterface(String iface) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
         try {
-            mConnector.execute("tether", "interface", "remove", iface);
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
+            mNetdService.tetherInterfaceRemove(iface);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw new IllegalStateException(e);
         } finally {
             removeInterfaceFromLocalNetwork(iface);
         }
@@ -1306,11 +1298,10 @@
     public String[] listTetheredInterfaces() {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
         try {
-            return NativeDaemonEvent.filterMessageList(
-                    mConnector.executeForList("tether", "interface", "list"),
-                    TetherInterfaceListResult);
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
+            final List<String> result = mNetdService.tetherInterfaceList();
+            return result.toArray(EMPTY_STRING_ARRAY);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw new IllegalStateException(e);
         }
     }
 
@@ -1319,16 +1310,11 @@
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
 
         int netId = (network != null) ? network.netId : ConnectivityManager.NETID_UNSET;
-        final Command cmd = new Command("tether", "dns", "set", netId);
-
-        for (String s : dns) {
-            cmd.appendArg(NetworkUtils.numericToInetAddress(s).getHostAddress());
-        }
 
         try {
-            mConnector.execute(cmd);
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
+            mNetdService.tetherDnsSet(netId, dns);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw new IllegalStateException(e);
         }
     }
 
@@ -1336,10 +1322,10 @@
     public String[] getDnsForwarders() {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
         try {
-            return NativeDaemonEvent.filterMessageList(
-                    mConnector.executeForList("tether", "dns", "list"), TetherDnsFwdTgtListResult);
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
+            final List<String> result = mNetdService.tetherDnsList();
+            return result.toArray(EMPTY_STRING_ARRAY);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw new IllegalStateException(e);
         }
     }
 
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index d505a77..21f54dd 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -329,6 +329,12 @@
     @GuardedBy("mPackagesLock")
     private final SparseArray<String> mSandboxIds = new SparseArray<>();
 
+    /**
+     * List of volumes visible to any user.
+     * TODO: may be have a map of userId -> volumes?
+     */
+    private final CopyOnWriteArrayList<VolumeInfo> mVisibleVols = new CopyOnWriteArrayList<>();
+
     private volatile int mCurrentUserId = UserHandle.USER_SYSTEM;
 
     /** Holding lock for AppFuse business */
@@ -623,16 +629,12 @@
                         Slog.i(TAG, "Ignoring mount " + vol.getId() + " due to policy");
                         break;
                     }
-                    try {
-                        mVold.mount(vol.id, vol.mountFlags, vol.mountUserId);
-                    } catch (Exception e) {
-                        Slog.wtf(TAG, e);
-                    }
+                    mount(vol);
                     break;
                 }
                 case H_VOLUME_UNMOUNT: {
                     final VolumeInfo vol = (VolumeInfo) msg.obj;
-                    unmount(vol.getId());
+                    unmount(vol);
                     break;
                 }
                 case H_VOLUME_BROADCAST: {
@@ -869,6 +871,8 @@
                 addInternalVolumeLocked();
             }
 
+            mVisibleVols.clear();
+
             try {
                 mVold.reset();
 
@@ -1466,7 +1470,7 @@
                     = mContext.getPackageManager().getInstalledApplicationsAsUser(
                             PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
             synchronized (mPackagesLock) {
-                final ArraySet<String> userPackages = getPackagesForUserPL(userId);
+                final ArraySet<String> userPackages = getAvailablePackagesForUserPL(userId);
                 for (int i = appInfos.size() - 1; i >= 0; --i) {
                     if (appInfos.get(i).isInstantApp()) {
                         continue;
@@ -1523,7 +1527,7 @@
     }
 
     @GuardedBy("mPackagesLock")
-    private ArraySet<String> getPackagesForUserPL(int userId) {
+    private ArraySet<String> getAvailablePackagesForUserPL(int userId) {
         ArraySet<String> userPackages = mPackages.get(userId);
         if (userPackages == null) {
             userPackages = new ArraySet<>();
@@ -1535,8 +1539,24 @@
     private String[] getPackagesArrayForUser(int userId) {
         if (!ENABLE_ISOLATED_STORAGE) return EmptyArray.STRING;
 
+        final ArraySet<String> userPackages;
         synchronized (mPackagesLock) {
-            return getPackagesForUserPL(userId).toArray(new String[0]);
+            userPackages = getAvailablePackagesForUserPL(userId);
+            if (!userPackages.isEmpty()) {
+                return userPackages.toArray(new String[0]);
+            }
+        }
+        final List<ApplicationInfo> appInfos =
+                mContext.getPackageManager().getInstalledApplicationsAsUser(
+                        PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
+        synchronized (mPackagesLock) {
+            for (int i = appInfos.size() - 1; i >= 0; --i) {
+                if (appInfos.get(i).isInstantApp()) {
+                    continue;
+                }
+                userPackages.add(appInfos.get(i).packageName);
+            }
+            return userPackages.toArray(new String[0]);
         }
     }
 
@@ -1747,8 +1767,15 @@
         if (isMountDisallowed(vol)) {
             throw new SecurityException("Mounting " + volId + " restricted by policy");
         }
+        mount(vol);
+    }
+
+    private void mount(VolumeInfo vol) {
         try {
             mVold.mount(vol.id, vol.mountFlags, vol.mountUserId);
+            if ((vol.mountFlags & VolumeInfo.MOUNT_FLAG_VISIBLE) != 0) {
+                mVisibleVols.add(vol);
+            }
         } catch (Exception e) {
             Slog.wtf(TAG, e);
         }
@@ -1759,8 +1786,15 @@
         enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
 
         final VolumeInfo vol = findVolumeByIdOrThrow(volId);
+        unmount(vol);
+    }
+
+    private void unmount(VolumeInfo vol) {
         try {
             mVold.unmount(vol.id);
+            if ((vol.mountFlags & VolumeInfo.MOUNT_FLAG_VISIBLE) != 0) {
+                mVisibleVols.remove(vol);
+            }
         } catch (Exception e) {
             Slog.wtf(TAG, e);
         }
@@ -3596,6 +3630,14 @@
             pw.decreaseIndent();
 
             pw.println();
+            pw.println("mVisibleVols:");
+            pw.increaseIndent();
+            for (int i = 0; i < mVisibleVols.size(); i++) {
+                mVisibleVols.get(i).dump(pw);
+            }
+            pw.decreaseIndent();
+
+            pw.println();
             pw.println("Primary storage UUID: " + mPrimaryStorageUuid);
             final Pair<String, Long> pair = StorageManager.getPrimaryStoragePathAndSize();
             if (pair == null) {
@@ -3716,7 +3758,7 @@
                 int userId) {
             final String sandboxId;
             synchronized (mPackagesLock) {
-                final ArraySet<String> userPackages = getPackagesForUserPL(userId);
+                final ArraySet<String> userPackages = getAvailablePackagesForUserPL(userId);
                 // If userPackages is empty, it means the user is not started yet, so no need to
                 // do anything now.
                 if (userPackages.isEmpty() || userPackages.contains(packageName)) {
@@ -3734,5 +3776,29 @@
                 Slog.wtf(TAG, e);
             }
         }
+
+        @Override
+        public String[] getVisibleVolumesForUser(int userId) {
+            final ArrayList<String> visibleVolsForUser = new ArrayList<>();
+            for (int i = mVisibleVols.size() - 1; i >= 0; --i) {
+                final VolumeInfo vol = mVisibleVols.get(i);
+                if (vol.isVisibleForUser(userId)) {
+                    visibleVolsForUser.add(getVolumeLabel(vol));
+                }
+            }
+            return visibleVolsForUser.toArray(new String[visibleVolsForUser.size()]);
+        }
+
+        private String getVolumeLabel(VolumeInfo vol) {
+            // STOPSHIP: Label needs to part of VolumeInfo and need to be passed on from vold
+            switch (vol.getType()) {
+                case VolumeInfo.TYPE_EMULATED:
+                    return "emulated";
+                case VolumeInfo.TYPE_PUBLIC:
+                    return vol.fsUuid == null ? vol.id : vol.fsUuid;
+                default:
+                    return null;
+            }
+        }
     }
 }
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 6d69fcd..0b836f0 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -94,7 +94,7 @@
         "media.metrics", // system/bin/mediametrics
         "media.codec", // vendor/bin/hw/android.hardware.media.omx@1.0-service
         "com.android.bluetooth",  // Bluetooth service
-        "statsd",  // Stats daemon
+        "/system/bin/statsd",  // Stats daemon
     };
 
     public static final List<String> HAL_INTERFACES_OF_INTEREST = Arrays.asList(
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 2fd699e..a3a5ac8 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -368,7 +368,6 @@
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.io.UnsupportedEncodingException;
-import java.lang.ref.WeakReference;
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
@@ -2398,6 +2397,9 @@
 
         mUserController = new UserController(this);
 
+        mPendingIntentController = new PendingIntentController(
+                mHandlerThread.getLooper(), mUserController);
+
         GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version",
             ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
 
@@ -2413,9 +2415,6 @@
         mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
         mStackSupervisor = mActivityTaskManager.mStackSupervisor;
 
-        mPendingIntentController = new PendingIntentController(
-                mHandlerThread.getLooper(), mUserController);
-
         mProcessCpuThread = new Thread("CpuTracker") {
             @Override
             public void run() {
@@ -3538,6 +3537,9 @@
             String seInfo, String requiredAbi, String instructionSet, String invokeWith,
             long startTime) {
         try {
+            final String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
+            final String[] visibleVolIds = LocalServices.getService(StorageManagerInternal.class)
+                    .getVisibleVolumesForUser(UserHandle.getUserId(uid));
             Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
                     app.processName);
             checkTime(startTime, "startProcess: asking zygote to start proc");
@@ -3547,12 +3549,14 @@
                         app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                         app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                         app.info.dataDir, null, app.info.packageName,
+                        packageNames, visibleVolIds,
                         new String[] {PROC_START_SEQ_IDENT + app.startSeq});
             } else {
                 startResult = Process.start(entryPoint,
                         app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                         app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                         app.info.dataDir, invokeWith, app.info.packageName,
+                        packageNames, visibleVolIds,
                         new String[] {PROC_START_SEQ_IDENT + app.startSeq});
             }
             checkTime(startTime, "startProcess: returned from zygote!");
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 66c7c43..f0ff570 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -4685,7 +4685,9 @@
     @Override
     public void setHearingAidDeviceConnectionState(BluetoothDevice device, int state)
     {
-        Log.i(TAG, "setBluetoothHearingAidDeviceConnectionState");
+        mDeviceLogger.log((new AudioEventLogger.StringEvent(
+                "setHearingAidDeviceConnectionState state=" + state
+                        + " addr=" + device.getAddress())).printLog(TAG));
 
         setBluetoothHearingAidDeviceConnectionState(
                 device, state,  false /* suppressNoisyIntent */, AudioSystem.DEVICE_NONE);
@@ -4723,12 +4725,12 @@
     public int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(BluetoothDevice device,
                 int state, int profile, boolean suppressNoisyIntent, int a2dpVolume)
     {
-        mDeviceLogger.log(new AudioEventLogger.StringEvent(
+        mDeviceLogger.log((new AudioEventLogger.StringEvent(
                 "setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent state=" + state
                 // only querying address as this is the only readily available field on the device
                 + " addr=" + device.getAddress()
                 + " prof=" + profile + " supprNoisy=" + suppressNoisyIntent
-                + " vol=" + a2dpVolume));
+                + " vol=" + a2dpVolume)).printLog(TAG));
         if (mAudioHandler.hasMessages(MSG_SET_A2DP_SINK_CONNECTION_STATE, device)) {
             mDeviceLogger.log(new AudioEventLogger.StringEvent("A2DP connection state ignored"));
             return 0;
@@ -5888,6 +5890,8 @@
     }
 
     private void onSendBecomingNoisyIntent() {
+        mDeviceLogger.log((new AudioEventLogger.StringEvent(
+                "broadcast ACTION_AUDIO_BECOMING_NOISY")).printLog(TAG));
         sendBroadcastToAll(new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY));
     }
 
@@ -7253,7 +7257,7 @@
     // - wired: logged before onSetWiredDeviceConnectionState() is executed
     // - A2DP: logged at reception of method call
     final private AudioEventLogger mDeviceLogger = new AudioEventLogger(
-            LOG_NB_EVENTS_DEVICE_CONNECTION, "wired/A2DP device connection");
+            LOG_NB_EVENTS_DEVICE_CONNECTION, "wired/A2DP/hearing aid device connection");
 
     final private AudioEventLogger mForceUseLogger = new AudioEventLogger(
             LOG_NB_EVENTS_FORCE_USE,
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 2a80f0e..48082b6 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -163,8 +163,8 @@
     // TODO: create separate trackers for each unique VPN to support
     // automated reconnection
 
-    private Context mContext;
-    private NetworkInfo mNetworkInfo;
+    private final Context mContext;
+    private final NetworkInfo mNetworkInfo;
     private String mPackage;
     private int mOwnerUID;
     private String mInterface;
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
index 2b1d919..1e6bb04 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
@@ -320,9 +320,8 @@
     }
 
     private static boolean getEnableLegacyDhcpServer(Context ctx) {
-        // TODO: make the default false (0) and update javadoc in Settings.java
         final ContentResolver cr = ctx.getContentResolver();
-        final int intVal = Settings.Global.getInt(cr, TETHER_ENABLE_LEGACY_DHCP_SERVER, 1);
+        final int intVal = Settings.Global.getInt(cr, TETHER_ENABLE_LEGACY_DHCP_SERVER, 0);
         return intVal != 0;
     }
 
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index cade07c..9381fa2 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -4732,7 +4732,8 @@
                     if (notification.getSmallIcon() != null) {
                         StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
                         mListeners.notifyPostedLocked(r, old);
-                        if (oldSbn == null || !Objects.equals(oldSbn.getGroup(), n.getGroup())) {
+                        if ((oldSbn == null || !Objects.equals(oldSbn.getGroup(), n.getGroup()))
+                                && !isCritical(r)) {
                             mHandler.post(new Runnable() {
                                 @Override
                                 public void run() {
@@ -4902,6 +4903,19 @@
     }
 
     /**
+     * Check if the notification is classified as critical.
+     *
+     * @param record the record to test for criticality
+     * @return {@code true} if notification is considered critical
+     *
+     * @see CriticalNotificationExtractor for criteria
+     */
+    private boolean isCritical(NotificationRecord record) {
+        // 0 is the most critical
+        return record.getCriticality() < CriticalNotificationExtractor.NORMAL;
+    }
+
+    /**
      * Keeps the last 5 packages that have notified, by user.
      */
     @GuardedBy("mNotificationLock")
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 10980b7..296d7ae 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -8474,7 +8474,7 @@
     private boolean canSkipFullApkVerification(String apkPath) {
         final byte[] rootHashObserved;
         try {
-            rootHashObserved = VerityUtils.generateFsverityRootHash(apkPath);
+            rootHashObserved = VerityUtils.generateApkVerityRootHash(apkPath);
             if (rootHashObserved == null) {
                 return false;  // APK does not contain Merkle tree root hash.
             }
@@ -16010,7 +16010,8 @@
                     if (Build.IS_DEBUGGABLE) Slog.i(TAG, "Enabling apk verity to " + apkPath);
                     FileDescriptor fd = result.getUnownedFileDescriptor();
                     try {
-                        final byte[] signedRootHash = VerityUtils.generateFsverityRootHash(apkPath);
+                        final byte[] signedRootHash =
+                                VerityUtils.generateApkVerityRootHash(apkPath);
                         mInstaller.installApkVerity(apkPath, fd, result.getContentSize());
                         mInstaller.assertFsverityRootHashMatches(apkPath, signedRootHash);
                     } catch (InstallerException | IOException | DigestException |
diff --git a/services/core/java/com/android/server/pm/dex/TEST_MAPPING b/services/core/java/com/android/server/pm/dex/TEST_MAPPING
new file mode 100644
index 0000000..ad52559
--- /dev/null
+++ b/services/core/java/com/android/server/pm/dex/TEST_MAPPING
@@ -0,0 +1,22 @@
+{
+  "presubmit": [
+    {
+      "name": "DexLoggerTests"
+    },
+    {
+      "name": "DexManagerTests"
+    },
+    {
+      "name": "DexoptOptionsTests"
+    },
+    {
+      "name": "DexoptUtilsTest"
+    },
+    {
+      "name": "PackageDexUsageTests"
+    },
+    {
+      "name": "DexLoggerIntegrationTests"
+    }
+  ]
+}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionsState.java b/services/core/java/com/android/server/pm/permission/PermissionsState.java
index 5e66bfc3..82d6b22 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionsState.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionsState.java
@@ -135,7 +135,8 @@
             final int userCount = other.mPermissionReviewRequired.size();
             for (int i = 0; i < userCount; i++) {
                 final boolean reviewRequired = other.mPermissionReviewRequired.valueAt(i);
-                mPermissionReviewRequired.put(i, reviewRequired);
+                mPermissionReviewRequired.put(other.mPermissionReviewRequired.keyAt(i),
+                        reviewRequired);
             }
         }
     }
diff --git a/services/core/java/com/android/server/security/VerityUtils.java b/services/core/java/com/android/server/security/VerityUtils.java
index 3796610..8070f3a 100644
--- a/services/core/java/com/android/server/security/VerityUtils.java
+++ b/services/core/java/com/android/server/security/VerityUtils.java
@@ -26,9 +26,9 @@
 import android.util.Pair;
 import android.util.Slog;
 import android.util.apk.ApkSignatureVerifier;
-import android.util.apk.ApkVerityBuilder;
 import android.util.apk.ByteBufferFactory;
 import android.util.apk.SignatureNotFoundException;
+import android.util.apk.VerityBuilder;
 
 import libcore.util.HexEncoding;
 
@@ -115,9 +115,9 @@
     }
 
     /**
-     * {@see ApkSignatureVerifier#generateFsverityRootHash(String)}.
+     * {@see ApkSignatureVerifier#generateApkVerityRootHash(String)}.
      */
-    public static byte[] generateFsverityRootHash(@NonNull String apkPath)
+    public static byte[] generateApkVerityRootHash(@NonNull String apkPath)
             throws NoSuchAlgorithmException, DigestException, IOException {
         return ApkSignatureVerifier.generateApkVerityRootHash(apkPath);
     }
@@ -146,7 +146,7 @@
             throws IOException, SignatureNotFoundException, SecurityException, DigestException,
                    NoSuchAlgorithmException {
         try (RandomAccessFile file = new RandomAccessFile(filePath, "r")) {
-            ApkVerityBuilder.ApkVerityResult result = ApkVerityBuilder.generateFsVerityTree(
+            VerityBuilder.VerityResult result = VerityBuilder.generateFsVerityTree(
                     file, trackedBufferFactory);
 
             ByteBuffer buffer = result.verityData;
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index c6e6449..3c64dd2 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -47,6 +47,7 @@
 import android.os.IStoraged;
 import android.os.IThermalEventListener;
 import android.os.IThermalService;
+import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
 import android.os.Process;
 import android.os.RemoteException;
@@ -63,10 +64,13 @@
 import android.telephony.ModemActivityInfo;
 import android.telephony.TelephonyManager;
 import android.util.ArrayMap;
+import android.util.Log;
 import android.util.Slog;
 import android.util.StatsLog;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.app.procstats.IProcessStats;
+import com.android.internal.app.procstats.ProcessStats;
 import com.android.internal.net.NetworkStatsFactory;
 import com.android.internal.os.BinderCallsStats.ExportedCallStat;
 import com.android.internal.os.KernelCpuSpeedReader;
@@ -82,6 +86,7 @@
 import com.android.server.BinderCallsStatsService;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
+import com.android.server.SystemServiceManager;
 import com.android.server.storage.DiskStatsFileLogger;
 import com.android.server.storage.DiskStatsLoggingService;
 
@@ -95,6 +100,7 @@
 import java.io.FileDescriptor;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -123,7 +129,7 @@
     public static final String CONFIG_DIR = "/data/misc/stats-service";
 
     static final String TAG = "StatsCompanionService";
-    static final boolean DEBUG = true;
+    static final boolean DEBUG = false;
 
     public static final int CODE_DATA_BROADCAST = 1;
     public static final int CODE_SUBSCRIBER_BROADCAST = 1;
@@ -174,12 +180,14 @@
             new KernelUidCpuClusterTimeReader();
 
     private static IThermalService sThermalService;
+    private File mBaseDir =
+            new File(SystemServiceManager.ensureSystemDir(), "stats_companion");
 
     public StatsCompanionService(Context context) {
         super();
         mContext = context;
         mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
-
+        mBaseDir.mkdirs();
         mAppUpdateReceiver = new AppUpdateReceiver();
         mUserUpdateReceiver = new BroadcastReceiver() {
             @Override
@@ -1245,6 +1253,86 @@
         Binder.restoreCallingIdentity(token);
     }
 
+    long mLastProcStatsHighWaterMark = readProcStatsHighWaterMark();
+
+    private long readProcStatsHighWaterMark() {
+        try {
+            File[] files = mBaseDir.listFiles();
+            if (files == null || files.length == 0) {
+                return 0;
+            }
+            if (files.length > 1) {
+                Log.e(TAG, "Only 1 file expected for high water mark. Found " + files.length);
+            }
+            return Long.valueOf(files[0].getName());
+        } catch (SecurityException e) {
+            Log.e(TAG, "Failed to get procstats high watermark file.", e);
+        } catch (NumberFormatException e) {
+            Log.e(TAG, "Failed to parse file name.", e);
+        }
+        return 0;
+    }
+
+    private IProcessStats mProcessStats =
+            IProcessStats.Stub.asInterface(ServiceManager.getService(ProcessStats.SERVICE_NAME));
+
+    private void pullProcessStats(
+            int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
+        try {
+            List<ParcelFileDescriptor> statsFiles = new ArrayList<>();
+            long highWaterMark = mProcessStats.getCommittedStats(
+                    mLastProcStatsHighWaterMark, ProcessStats.REPORT_ALL, true, statsFiles);
+            if (statsFiles.size() != 1) {
+                return;
+            }
+            InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(statsFiles.get(0));
+            int[] len = new int[1];
+            byte[] stats = readFully(stream, len);
+            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+            e.writeStorage(Arrays.copyOf(stats, len[0]));
+            pulledData.add(e);
+            new File(mBaseDir.getAbsolutePath() + "/" + mLastProcStatsHighWaterMark).delete();
+            mLastProcStatsHighWaterMark = highWaterMark;
+            new File(
+                    mBaseDir.getAbsolutePath() + "/" + mLastProcStatsHighWaterMark).createNewFile();
+        } catch (IOException e) {
+            Log.e(TAG, "Getting procstats failed: ", e);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Getting procstats failed: ", e);
+        } catch (SecurityException e) {
+            Log.e(TAG, "Getting procstats failed: ", e);
+        }
+    }
+
+    static byte[] readFully(InputStream stream, int[] outLen) throws IOException {
+        int pos = 0;
+        final int initialAvail = stream.available();
+        byte[] data = new byte[initialAvail > 0 ? (initialAvail + 1) : 16384];
+        while (true) {
+            int amt = stream.read(data, pos, data.length - pos);
+            if (DEBUG) {
+                Slog.i(TAG, "Read " + amt + " bytes at " + pos + " of avail " + data.length);
+            }
+            if (amt < 0) {
+                if (DEBUG) {
+                    Slog.i(TAG, "**** FINISHED READING: pos=" + pos + " len=" + data.length);
+                }
+                outLen[0] = pos;
+                return data;
+            }
+            pos += amt;
+            if (pos >= data.length) {
+                byte[] newData = new byte[pos + 16384];
+                if (DEBUG) {
+                    Slog.i(TAG, "Copying " + pos + " bytes to new array len " + newData.length);
+                }
+                System.arraycopy(data, 0, newData, 0, pos);
+                data = newData;
+            }
+        }
+    }
+
     /**
      * Pulls various data.
      */
@@ -1358,6 +1446,10 @@
                 pullNumFingerprints(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
+            case StatsLog.PROC_STATS: {
+                pullProcessStats(tagId, elapsedNanos, wallClockNanos, ret);
+                break;
+            }
             default:
                 Slog.w(TAG, "No such tagId data as " + tagId);
                 return null;
@@ -1368,13 +1460,13 @@
     @Override // Binder call
     public void statsdReady() {
         enforceCallingPermission();
-        if (DEBUG) Slog.d(TAG, "learned that statsdReady");
+        if (DEBUG) {
+            Slog.d(TAG, "learned that statsdReady");
+        }
         sayHiToStatsd(); // tell statsd that we're ready too and link to it
-        mContext.sendBroadcastAsUser(
-                new Intent(StatsManager.ACTION_STATSD_STARTED)
+        mContext.sendBroadcastAsUser(new Intent(StatsManager.ACTION_STATSD_STARTED)
                         .addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND),
-                UserHandle.SYSTEM,
-                android.Manifest.permission.DUMP);
+                UserHandle.SYSTEM, android.Manifest.permission.DUMP);
     }
 
     @Override
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 061f8e2..157b634 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -90,7 +90,6 @@
         "libsensorservicehidl",
         "libgui",
         "libusbhost",
-        "libsuspend",
         "libtinyalsa",
         "libEGL",
         "libGLESv2",
@@ -122,6 +121,7 @@
         "android.hardware.vr@1.0",
         "android.frameworks.schedulerservice@1.0",
         "android.frameworks.sensorservice@1.0",
+        "android.system.suspend@1.0",
     ],
 
     static_libs: [
diff --git a/services/core/jni/com_android_server_am_BatteryStatsService.cpp b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
index 02ad6c7..0ff60e4 100644
--- a/services/core/jni/com_android_server_am_BatteryStatsService.cpp
+++ b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
@@ -30,6 +30,8 @@
 
 #include <android/hardware/power/1.0/IPower.h>
 #include <android/hardware/power/1.1/IPower.h>
+#include <android/system/suspend/1.0/ISystemSuspend.h>
+#include <android/system/suspend/1.0/ISystemSuspendCallback.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <jni.h>
 
@@ -39,7 +41,6 @@
 #include <log/log.h>
 #include <utils/misc.h>
 #include <utils/Log.h>
-#include <suspend/autosuspend.h>
 
 using android::hardware::Return;
 using android::hardware::Void;
@@ -49,6 +50,8 @@
 using android::hardware::power::V1_1::PowerStateSubsystem;
 using android::hardware::power::V1_1::PowerStateSubsystemSleepState;
 using android::hardware::hidl_vec;
+using android::system::suspend::V1_0::ISystemSuspend;
+using android::system::suspend::V1_0::ISystemSuspendCallback;
 using IPowerV1_1 = android::hardware::power::V1_1::IPower;
 using IPowerV1_0 = android::hardware::power::V1_0::IPower;
 
@@ -63,6 +66,7 @@
 extern sp<IPowerV1_0> getPowerHalV1_0();
 extern sp<IPowerV1_1> getPowerHalV1_1();
 extern bool processPowerHalReturn(const Return<void> &ret, const char* functionName);
+extern sp<ISystemSuspend> getSuspendHal();
 
 // Java methods used in getLowPowerStats
 static jmethodID jgetAndUpdatePlatformState = NULL;
@@ -70,16 +74,19 @@
 static jmethodID jputVoter = NULL;
 static jmethodID jputState = NULL;
 
-static void wakeup_callback(bool success)
-{
-    ALOGV("In wakeup_callback: %s", success ? "resumed from suspend" : "suspend aborted");
-    int ret = sem_post(&wakeup_sem);
-    if (ret < 0) {
-        char buf[80];
-        strerror_r(errno, buf, sizeof(buf));
-        ALOGE("Error posting wakeup sem: %s\n", buf);
+class WakeupCallback : public ISystemSuspendCallback {
+public:
+    Return<void> notifyWakeup(bool success) override {
+        ALOGV("In wakeup_callback: %s", success ? "resumed from suspend" : "suspend aborted");
+        int ret = sem_post(&wakeup_sem);
+        if (ret < 0) {
+            char buf[80];
+            strerror_r(errno, buf, sizeof(buf));
+            ALOGE("Error posting wakeup sem: %s\n", buf);
+        }
+        return Void();
     }
-}
+};
 
 static jint nativeWaitWakeup(JNIEnv *env, jobject clazz, jobject outBuf)
 {
@@ -101,11 +108,14 @@
             return -1;
         }
         ALOGV("Registering callback...");
-        autosuspend_set_wakeup_callback(&wakeup_callback);
+        sp<ISystemSuspend> suspendHal = getSuspendHal();
+        suspendHal->registerCallback(new WakeupCallback());
     }
 
     // Wait for wakeup.
     ALOGV("Waiting for wakeup...");
+    // TODO(b/116747600): device can suspend and wakeup after sem_wait() finishes and before wakeup
+    // reason is recorded, i.e. BatteryStats might occasionally miss wakeup events.
     int ret = sem_wait(&wakeup_sem);
     if (ret < 0) {
         char buf[80];
diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp
index b2d35d4..0c9b5f4 100644
--- a/services/core/jni/com_android_server_power_PowerManagerService.cpp
+++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp
@@ -19,6 +19,7 @@
 //#define LOG_NDEBUG 0
 
 #include <android/hardware/power/1.1/IPower.h>
+#include <android/system/suspend/1.0/ISystemSuspend.h>
 #include <nativehelper/JNIHelp.h>
 #include "jni.h"
 
@@ -35,7 +36,7 @@
 #include <utils/Log.h>
 #include <hardware/power.h>
 #include <hardware_legacy/power.h>
-#include <suspend/autosuspend.h>
+#include <hidl/ServiceManagement.h>
 
 #include "com_android_server_power_PowerManagerService.h"
 
@@ -44,6 +45,9 @@
 using android::hardware::power::V1_0::PowerHint;
 using android::hardware::power::V1_0::Feature;
 using android::String8;
+using android::system::suspend::V1_0::ISystemSuspend;
+using android::system::suspend::V1_0::IWakeLock;
+using android::system::suspend::V1_0::WakeLockType;
 using IPowerV1_1 = android::hardware::power::V1_1::IPower;
 using IPowerV1_0 = android::hardware::power::V1_0::IPower;
 
@@ -171,6 +175,46 @@
     }
 }
 
+static sp<ISystemSuspend> gSuspendHal = nullptr;
+static sp<IWakeLock> gSuspendBlocker = nullptr;
+static std::mutex gSuspendMutex;
+
+// Assume SystemSuspend HAL is always alive.
+// TODO: Force device to restart if SystemSuspend HAL dies.
+sp<ISystemSuspend> getSuspendHal() {
+    static std::once_flag suspendHalFlag;
+    std::call_once(suspendHalFlag, [](){
+        ::android::hardware::details::waitForHwService(ISystemSuspend::descriptor, "default");
+        gSuspendHal = ISystemSuspend::getService();
+        assert(gSuspendHal != nullptr);
+    });
+    return gSuspendHal;
+}
+
+void enableAutoSuspend() {
+    static bool enabled = false;
+
+    std::lock_guard<std::mutex> lock(gSuspendMutex);
+    if (!enabled) {
+        sp<ISystemSuspend> suspendHal = getSuspendHal();
+        suspendHal->enableAutosuspend();
+        enabled = true;
+    }
+    if (gSuspendBlocker) {
+        gSuspendBlocker->release();
+        gSuspendBlocker.clear();
+    }
+}
+
+void disableAutoSuspend() {
+    std::lock_guard<std::mutex> lock(gSuspendMutex);
+    if (!gSuspendBlocker) {
+        sp<ISystemSuspend> suspendHal = getSuspendHal();
+        gSuspendBlocker = suspendHal->acquireWakeLock(WakeLockType::PARTIAL,
+                "PowerManager.SuspendLockout");
+    }
+}
+
 // ----------------------------------------------------------------------------
 
 static void nativeInit(JNIEnv* env, jobject obj) {
@@ -207,13 +251,13 @@
 static void nativeSetAutoSuspend(JNIEnv* /* env */, jclass /* clazz */, jboolean enable) {
     if (enable) {
         android::base::Timer t;
-        autosuspend_enable();
+        enableAutoSuspend();
         if (t.duration() > 100ms) {
             ALOGD("Excessive delay in autosuspend_enable() while turning screen off");
         }
     } else {
         android::base::Timer t;
-        autosuspend_disable();
+        disableAutoSuspend();
         if (t.duration() > 100ms) {
             ALOGD("Excessive delay in autosuspend_disable() while turning screen on");
         }
diff --git a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
new file mode 100644
index 0000000..5a787ec
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
@@ -0,0 +1,352 @@
+/*
+ * 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.server.usage;
+
+import static junit.framework.TestCase.assertNull;
+import static junit.framework.TestCase.fail;
+
+import static org.testng.Assert.assertEquals;
+
+import android.app.usage.EventList;
+import android.app.usage.UsageEvents;
+import android.app.usage.UsageStats;
+import android.app.usage.UsageStatsManager;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Locale;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class UsageStatsDatabaseTest {
+    protected Context mContext;
+    private UsageStatsDatabase mUsageStatsDatabase;
+    private File mTestDir;
+
+    private IntervalStats mIntervalStats = new IntervalStats();
+    private long mEndTime = 0;
+
+    private static final UsageStatsDatabase.StatCombiner<IntervalStats> mIntervalStatsVerifier =
+            new UsageStatsDatabase.StatCombiner<IntervalStats>() {
+                @Override
+                public void combine(IntervalStats stats, boolean mutable,
+                        List<IntervalStats> accResult) {
+                    accResult.add(stats);
+                }
+            };
+
+    @Before
+    public void setUp() {
+        mContext = InstrumentationRegistry.getTargetContext();
+        mTestDir = new File(mContext.getFilesDir(), "UsageStatsDatabaseTest");
+        mUsageStatsDatabase = new UsageStatsDatabase(mTestDir);
+        mUsageStatsDatabase.init(1);
+        populateIntervalStats();
+        clearUsageStatsFiles();
+    }
+
+    /**
+     * A debugging utility for viewing the files currently in the test directory
+     */
+    private void clearUsageStatsFiles() {
+        File[] intervalDirs = mTestDir.listFiles();
+        for (File intervalDir : intervalDirs) {
+            if (intervalDir.isDirectory()) {
+                File[] usageFiles = intervalDir.listFiles();
+                for (File f : usageFiles) {
+                    f.delete();
+                }
+            }
+        }
+    }
+
+    /**
+     * A debugging utility for viewing the files currently in the test directory
+     */
+    private String dumpUsageStatsFiles() {
+        StringBuilder sb = new StringBuilder();
+        File[] intervalDirs = mTestDir.listFiles();
+        for (File intervalDir : intervalDirs) {
+            if (intervalDir.isDirectory()) {
+                File[] usageFiles = intervalDir.listFiles();
+                for (File f : usageFiles) {
+                    sb.append(f.toString());
+                }
+            }
+        }
+        return sb.toString();
+    }
+
+    private void populateIntervalStats() {
+        final int numberOfEvents = 3000;
+        long time = 1;
+        mIntervalStats = new IntervalStats();
+
+        mIntervalStats.beginTime = 1;
+        mIntervalStats.interactiveTracker.count = 2;
+        mIntervalStats.interactiveTracker.duration = 111111;
+        mIntervalStats.nonInteractiveTracker.count = 3;
+        mIntervalStats.nonInteractiveTracker.duration = 222222;
+        mIntervalStats.keyguardShownTracker.count = 4;
+        mIntervalStats.keyguardShownTracker.duration = 333333;
+        mIntervalStats.keyguardHiddenTracker.count = 5;
+        mIntervalStats.keyguardHiddenTracker.duration = 4444444;
+
+        if (mIntervalStats.events == null) {
+            mIntervalStats.events = new EventList();
+        }
+
+        for (int i = 0; i < numberOfEvents; i++) {
+            UsageEvents.Event event = new UsageEvents.Event();
+            final int packageInt = ((i / 3) % 7);
+            event.mPackage = "fake.package.name" + packageInt; //clusters of 3 events from 7 "apps"
+            if (packageInt == 3) {
+                // Third app is an instant app
+                event.mFlags |= UsageEvents.Event.FLAG_IS_PACKAGE_INSTANT_APP;
+            } else if (packageInt == 2 || packageInt == 4) {
+                event.mClass = ".fake.class.name" + i % 11;
+            }
+
+
+            event.mTimeStamp = time;
+            event.mEventType = i % 19; //"random" event type
+
+            switch (event.mEventType) {
+                case UsageEvents.Event.CONFIGURATION_CHANGE:
+                    //empty config,
+                    event.mConfiguration = new Configuration();
+                    break;
+                case UsageEvents.Event.SHORTCUT_INVOCATION:
+                    //"random" shortcut
+                    event.mShortcutId = "shortcut" + (i % 8);
+                    break;
+                case UsageEvents.Event.STANDBY_BUCKET_CHANGED:
+                    //"random" bucket and reason
+                    event.mBucketAndReason = (((i % 5 + 1) * 10) << 16) & (i % 5 + 1) << 8;
+                    break;
+                case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
+                    //"random" channel
+                    event.mNotificationChannelId = "channel" + (i % 5);
+                    break;
+            }
+
+            mIntervalStats.events.insert(event);
+            mIntervalStats.update(event.mPackage, event.mTimeStamp, event.mEventType);
+
+            time += 23; // Arbitrary progression of time
+        }
+        mEndTime = time;
+
+        Configuration config1 = new Configuration();
+        config1.fontScale = 3.3f;
+        config1.mcc = 4;
+        mIntervalStats.getOrCreateConfigurationStats(config1);
+
+        Configuration config2 = new Configuration();
+        config2.mnc = 5;
+        config2.setLocale(new Locale("en", "US"));
+        mIntervalStats.getOrCreateConfigurationStats(config2);
+
+        Configuration config3 = new Configuration();
+        config3.touchscreen = 6;
+        config3.keyboard = 7;
+        mIntervalStats.getOrCreateConfigurationStats(config3);
+
+        Configuration config4 = new Configuration();
+        config4.keyboardHidden = 8;
+        config4.hardKeyboardHidden = 9;
+        mIntervalStats.getOrCreateConfigurationStats(config4);
+
+        Configuration config5 = new Configuration();
+        config5.navigation = 10;
+        config5.navigationHidden = 11;
+        mIntervalStats.getOrCreateConfigurationStats(config5);
+
+        Configuration config6 = new Configuration();
+        config6.orientation = 12;
+        //Ignore screen layout, it's determined by locale
+        mIntervalStats.getOrCreateConfigurationStats(config6);
+
+        Configuration config7 = new Configuration();
+        config7.colorMode = 14;
+        config7.uiMode = 15;
+        mIntervalStats.getOrCreateConfigurationStats(config7);
+
+        Configuration config8 = new Configuration();
+        config8.screenWidthDp = 16;
+        config8.screenHeightDp = 17;
+        mIntervalStats.getOrCreateConfigurationStats(config8);
+
+        Configuration config9 = new Configuration();
+        config9.smallestScreenWidthDp = 18;
+        config9.densityDpi = 19;
+        mIntervalStats.getOrCreateConfigurationStats(config9);
+
+        mIntervalStats.activeConfiguration = config9;
+    }
+
+    void compareUsageStats(UsageStats us1, UsageStats us2) {
+        assertEquals(us1.mPackageName, us2.mPackageName);
+        // mBeginTimeStamp is based on the enclosing IntervalStats, don't bother checking
+        // mEndTimeStamp is based on the enclosing IntervalStats, don't bother checking
+        assertEquals(us1.mLastTimeUsed, us2.mLastTimeUsed);
+        assertEquals(us1.mTotalTimeInForeground, us2.mTotalTimeInForeground);
+        // mLaunchCount not persisted, so skipped
+        assertEquals(us1.mAppLaunchCount, us2.mAppLaunchCount);
+        assertEquals(us1.mLastEvent, us2.mLastEvent);
+        assertEquals(us1.mChooserCounts, us2.mChooserCounts);
+    }
+
+    void compareUsageEvent(UsageEvents.Event e1, UsageEvents.Event e2, int debugId) {
+        assertEquals(e1.mPackage, e2.mPackage, "Usage event " + debugId);
+        assertEquals(e1.mClass, e2.mClass, "Usage event " + debugId);
+        assertEquals(e1.mTimeStamp, e2.mTimeStamp, "Usage event " + debugId);
+        assertEquals(e1.mEventType, e2.mEventType, "Usage event " + debugId);
+        switch (e1.mEventType) {
+            case UsageEvents.Event.CONFIGURATION_CHANGE:
+                assertEquals(e1.mConfiguration, e2.mConfiguration,
+                        "Usage event " + debugId + e2.mConfiguration.toString());
+                break;
+            case UsageEvents.Event.SHORTCUT_INVOCATION:
+                assertEquals(e1.mShortcutId, e2.mShortcutId, "Usage event " + debugId);
+                break;
+            case UsageEvents.Event.STANDBY_BUCKET_CHANGED:
+                assertEquals(e1.mBucketAndReason, e2.mBucketAndReason, "Usage event " + debugId);
+                break;
+            case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
+                assertEquals(e1.mNotificationChannelId, e2.mNotificationChannelId,
+                        "Usage event " + debugId);
+                break;
+        }
+        assertEquals(e1.mFlags, e2.mFlags);
+    }
+
+    void compareIntervalStats(IntervalStats stats1, IntervalStats stats2) {
+        assertEquals(stats1.beginTime, stats2.beginTime);
+        assertEquals(stats1.endTime, stats2.endTime);
+        assertEquals(stats1.interactiveTracker.count, stats2.interactiveTracker.count);
+        assertEquals(stats1.interactiveTracker.duration, stats2.interactiveTracker.duration);
+        assertEquals(stats1.nonInteractiveTracker.count, stats2.nonInteractiveTracker.count);
+        assertEquals(stats1.nonInteractiveTracker.duration, stats2.nonInteractiveTracker.duration);
+        assertEquals(stats1.keyguardShownTracker.count, stats2.keyguardShownTracker.count);
+        assertEquals(stats1.keyguardShownTracker.duration, stats2.keyguardShownTracker.duration);
+        assertEquals(stats1.keyguardHiddenTracker.count, stats2.keyguardHiddenTracker.count);
+        assertEquals(stats1.keyguardHiddenTracker.duration, stats2.keyguardHiddenTracker.duration);
+
+        String[] usageKey1 = stats1.packageStats.keySet().toArray(new String[0]);
+        String[] usageKey2 = stats2.packageStats.keySet().toArray(new String[0]);
+        for (int i = 0; i < usageKey1.length; i++) {
+            UsageStats usageStats1 = stats1.packageStats.get(usageKey1[i]);
+            UsageStats usageStats2 = stats2.packageStats.get(usageKey2[i]);
+            compareUsageStats(usageStats1, usageStats2);
+        }
+
+        assertEquals(stats1.configurations.size(), stats2.configurations.size());
+        Configuration[] configSet1 = stats1.configurations.keySet().toArray(new Configuration[0]);
+        for (int i = 0; i < configSet1.length; i++) {
+            if (!stats2.configurations.containsKey(configSet1[i])) {
+                Configuration[] configSet2 = stats2.configurations.keySet().toArray(
+                        new Configuration[0]);
+                String debugInfo = "";
+                for (Configuration c : configSet1) {
+                    debugInfo += c.toString() + "\n";
+                }
+                debugInfo += "\n";
+                for (Configuration c : configSet2) {
+                    debugInfo += c.toString() + "\n";
+                }
+                fail("Config " + configSet1[i].toString()
+                        + " not found in deserialized IntervalStat\n" + debugInfo);
+            }
+        }
+        assertEquals(stats1.activeConfiguration, stats2.activeConfiguration);
+
+        assertEquals(stats1.events.size(), stats2.events.size());
+        for (int i = 0; i < stats1.events.size(); i++) {
+            compareUsageEvent(stats1.events.get(i), stats2.events.get(i), i);
+        }
+    }
+
+    /**
+     * Runs the Write Read test.
+     * Will write the generated IntervalStat to disk, read it from disk and compare the two
+     */
+    void runWriteReadTest(int interval) throws IOException {
+        mUsageStatsDatabase.putUsageStats(interval, mIntervalStats);
+        List<IntervalStats> stats = mUsageStatsDatabase.queryUsageStats(interval, 0, mEndTime,
+                mIntervalStatsVerifier);
+
+        assertEquals(1, stats.size());
+        compareIntervalStats(mIntervalStats, stats.get(0));
+    }
+
+    /**
+     * Demonstrate that IntervalStats can be serialized and deserialized from disk without loss of
+     * relevant data.
+     */
+    @Test
+    public void testWriteRead() throws IOException {
+        runWriteReadTest(UsageStatsManager.INTERVAL_DAILY);
+        runWriteReadTest(UsageStatsManager.INTERVAL_WEEKLY);
+        runWriteReadTest(UsageStatsManager.INTERVAL_MONTHLY);
+        runWriteReadTest(UsageStatsManager.INTERVAL_YEARLY);
+    }
+
+    /**
+     * Runs the Version Change tests.
+     * Will write the generated IntervalStat to disk in one version format, "upgrade" to another
+     * version and read the automatically upgraded files on disk in the new file format.
+     */
+    void runVersionChangeTest(int oldVersion, int newVersion, int interval) throws IOException {
+        // Write IntervalStats to disk in old version format
+        UsageStatsDatabase prevDB = new UsageStatsDatabase(mTestDir, oldVersion);
+        prevDB.init(1);
+        prevDB.putUsageStats(interval, mIntervalStats);
+
+        // Simulate an upgrade to a new version and read from the disk
+        UsageStatsDatabase newDB = new UsageStatsDatabase(mTestDir, newVersion);
+        newDB.init(mEndTime);
+        List<IntervalStats> stats = newDB.queryUsageStats(interval, 0, mEndTime,
+                mIntervalStatsVerifier);
+
+        assertEquals(1, stats.size());
+        // The written and read IntervalStats should match
+        compareIntervalStats(mIntervalStats, stats.get(0));
+    }
+
+    /**
+     * Test the version upgrade from 3 to 4
+     */
+    @Test
+    public void testVersionUpgradeFrom3to4() throws IOException {
+        runVersionChangeTest(3, 4, UsageStatsManager.INTERVAL_DAILY);
+        runVersionChangeTest(3, 4, UsageStatsManager.INTERVAL_WEEKLY);
+        runVersionChangeTest(3, 4, UsageStatsManager.INTERVAL_MONTHLY);
+        runVersionChangeTest(3, 4, UsageStatsManager.INTERVAL_YEARLY);
+    }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 45d2fa2..b0d942c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -2192,6 +2192,26 @@
     }
 
     @Test
+    public void testDontAutogroupIfCritical() throws Exception {
+        NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, 0, null, false);
+        r.setCriticality(CriticalNotificationExtractor.CRITICAL_LOW);
+        mService.addEnqueuedNotification(r);
+        NotificationManagerService.PostNotificationRunnable runnable =
+                mService.new PostNotificationRunnable(r.getKey());
+        runnable.run();
+
+        r = generateNotificationRecord(mTestNotificationChannel, 1, null, false);
+        r.setCriticality(CriticalNotificationExtractor.CRITICAL);
+        runnable = mService.new PostNotificationRunnable(r.getKey());
+        mService.addEnqueuedNotification(r);
+
+        runnable.run();
+        waitForIdle();
+
+        verify(mGroupHelper, never()).onNotificationPosted(any(), anyBoolean());
+    }
+
+    @Test
     public void testNoFakeColorizedPermission() throws Exception {
         when(mPackageManagerClient.checkPermission(any(), any())).thenReturn(PERMISSION_DENIED);
         Notification.Builder nb = new Notification.Builder(mContext,
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 702161e..13f3e5e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -1030,6 +1030,14 @@
         assertEquals(1, mZenModeHelperSpy.mConditions.mSubscriptions.size());
     }
 
+    @Test
+    public void testEmptyDefaultRulesMap() {
+        ZenModeConfig config = new ZenModeConfig();
+        config.automaticRules = new ArrayMap<>();
+        mZenModeHelperSpy.mConfig = config;
+        mZenModeHelperSpy.updateDefaultZenRules(); // shouldn't throw null pointer
+    }
+
     private void setupZenConfig() {
         mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         mZenModeHelperSpy.mConfig.allowAlarms = false;
diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index 4b7e21f..db9972f 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -24,7 +24,9 @@
 import android.content.res.Configuration;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.proto.ProtoInputStream;
 
+import java.io.IOException;
 import java.util.List;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -46,7 +48,7 @@
     // keep hundreds of strings that have the same contents. We will read the string
     // and only keep it if it's not in the cache. The GC will take care of the
     // strings that had identical copies in the cache.
-    private final ArraySet<String> mStringCache = new ArraySet<>();
+    public final ArraySet<String> mStringCache = new ArraySet<>();
 
     public static final class EventTracker {
         public long curStartTime;
@@ -129,6 +131,90 @@
         return event;
     }
 
+    /**
+     * Builds a UsageEvents.Event from a proto, but does not add it internally.
+     * Built here to take advantage of the cached String Refs
+     */
+    UsageEvents.Event buildEvent(ProtoInputStream parser, List<String> stringPool)
+            throws IOException {
+        final UsageEvents.Event event = new UsageEvents.Event();
+        while (true) {
+            switch (parser.nextField()) {
+                case (int) IntervalStatsProto.Event.PACKAGE:
+                    event.mPackage = getCachedStringRef(
+                            parser.readString(IntervalStatsProto.Event.PACKAGE));
+                    break;
+                case (int) IntervalStatsProto.Event.PACKAGE_INDEX:
+                    event.mPackage = getCachedStringRef(stringPool.get(
+                            parser.readInt(IntervalStatsProto.Event.PACKAGE_INDEX) - 1));
+                    break;
+                case (int) IntervalStatsProto.Event.CLASS:
+                    event.mClass = getCachedStringRef(
+                            parser.readString(IntervalStatsProto.Event.CLASS));
+                    break;
+                case (int) IntervalStatsProto.Event.CLASS_INDEX:
+                    event.mClass = getCachedStringRef(stringPool.get(
+                            parser.readInt(IntervalStatsProto.Event.CLASS_INDEX) - 1));
+                    break;
+                case (int) IntervalStatsProto.Event.TIME_MS:
+                    event.mTimeStamp = beginTime + parser.readLong(
+                            IntervalStatsProto.Event.TIME_MS);
+                    break;
+                case (int) IntervalStatsProto.Event.FLAGS:
+                    event.mFlags = parser.readInt(IntervalStatsProto.Event.FLAGS);
+                    break;
+                case (int) IntervalStatsProto.Event.TYPE:
+                    event.mEventType = parser.readInt(IntervalStatsProto.Event.TYPE);
+                    break;
+                case (int) IntervalStatsProto.Event.CONFIG:
+                    event.mConfiguration = new Configuration();
+                    event.mConfiguration.readFromProto(parser, IntervalStatsProto.Event.CONFIG);
+                    break;
+                case (int) IntervalStatsProto.Event.SHORTCUT_ID:
+                    event.mShortcutId = parser.readString(
+                            IntervalStatsProto.Event.SHORTCUT_ID).intern();
+                    break;
+                case (int) IntervalStatsProto.Event.STANDBY_BUCKET:
+                    event.mBucketAndReason = parser.readInt(
+                            IntervalStatsProto.Event.STANDBY_BUCKET);
+                    break;
+                case (int) IntervalStatsProto.Event.NOTIFICATION_CHANNEL:
+                    event.mNotificationChannelId = parser.readString(
+                            IntervalStatsProto.Event.NOTIFICATION_CHANNEL);
+                    break;
+                case (int) IntervalStatsProto.Event.NOTIFICATION_CHANNEL_INDEX:
+                    event.mNotificationChannelId = getCachedStringRef(stringPool.get(
+                            parser.readInt(IntervalStatsProto.Event.NOTIFICATION_CHANNEL_INDEX)
+                                    - 1));
+                    break;
+                case ProtoInputStream.NO_MORE_FIELDS:
+                    // Handle default values for certain events types
+                    switch (event.mEventType) {
+                        case UsageEvents.Event.CONFIGURATION_CHANGE:
+                            if (event.mConfiguration == null) {
+                                event.mConfiguration = new Configuration();
+                            }
+                            break;
+                        case UsageEvents.Event.SHORTCUT_INVOCATION:
+                            if (event.mShortcutId == null) {
+                                event.mShortcutId = "";
+                            }
+                            break;
+                        case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
+                            if (event.mNotificationChannelId == null) {
+                                event.mNotificationChannelId = "";
+                            }
+                            break;
+                    }
+                    if (event.mTimeStamp == 0) {
+                        //mTimestamp not set, assume default value 0 plus beginTime
+                        event.mTimeStamp = beginTime;
+                    }
+                    return event;
+            }
+        }
+    }
+
     private boolean isStatefulEvent(int eventType) {
         switch (eventType) {
             case UsageEvents.Event.MOVE_TO_FOREGROUND:
@@ -143,8 +229,6 @@
     /**
      * Returns whether the event type is one caused by user visible
      * interaction. Excludes those that are internally generated.
-     * @param eventType
-     * @return
      */
     private boolean isUserVisibleEvent(int eventType) {
         return eventType != UsageEvents.Event.SYSTEM_INTERACTION
@@ -184,6 +268,25 @@
         endTime = timeStamp;
     }
 
+    /**
+     * @hide
+     */
+    @VisibleForTesting
+    public void addEvent(UsageEvents.Event event) {
+        if (events == null) {
+            events = new EventList();
+        }
+        // Cache common use strings
+        event.mPackage = getCachedStringRef(event.mPackage);
+        if (event.mClass != null) {
+            event.mClass = getCachedStringRef(event.mClass);
+        }
+        if (event.mEventType == UsageEvents.Event.NOTIFICATION_INTERRUPTION) {
+            event.mNotificationChannelId = getCachedStringRef(event.mNotificationChannelId);
+        }
+        events.insert(event);
+    }
+
     void updateChooserCounts(String packageName, String category, String action) {
         UsageStats usageStats = getOrCreateUsageStats(packageName);
         if (usageStats.mChooserCounts == null) {
diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
index 5ab5dc2..c616685 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
@@ -17,6 +17,7 @@
 package com.android.server.usage;
 
 import android.app.usage.TimeSparseArray;
+import android.app.usage.UsageEvents;
 import android.app.usage.UsageStats;
 import android.app.usage.UsageStatsManager;
 import android.os.Build;
@@ -25,6 +26,10 @@
 import android.util.Slog;
 import android.util.TimeUtils;
 
+import com.android.internal.annotations.VisibleForTesting;
+
+import libcore.io.IoUtils;
+
 import java.io.BufferedReader;
 import java.io.BufferedWriter;
 import java.io.ByteArrayInputStream;
@@ -32,18 +37,49 @@
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
 import java.io.FileReader;
 import java.io.FileWriter;
 import java.io.FilenameFilter;
+import java.io.InputStream;
 import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.StandardCopyOption;
 import java.util.ArrayList;
 import java.util.List;
 
 /**
- * Provides an interface to query for UsageStat data from an XML database.
+ * Provides an interface to query for UsageStat data from a Protocol Buffer database.
+ *
+ * Prior to version 4, UsageStatsDatabase used XML to store Usage Stats data to disk.
+ * When the UsageStatsDatabase version is upgraded, the files on disk are migrated to the new
+ * version on init. The steps of migration are as follows:
+ * 1) Check if version upgrade breadcrumb exists on disk, if so skip to step 4.
+ * 2) Copy current files to versioned backup files.
+ * 3) Write a temporary breadcrumb file with some info about the backed up files.
+ * 4) Deserialize a versioned backup file using the info written to the breadcrumb for the
+ * correct deserialization methodology.
+ * 5) Reserialize the data read from the file with the new version format and replace the old files
+ * 6) Repeat Step 3 and 4 for each versioned backup file matching the breadcrumb file.
+ * 7) Update the version file with the new version and build fingerprint.
+ * 8) Delete the versioned backup files (unless flagged to be kept).
+ * 9) Delete the breadcrumb file.
+ *
+ * Performing the upgrade steps in this order, protects against unexpected shutdowns mid upgrade
+ *
+ * A versioned backup file is simply a copy of a Usage Stats file with some extra info embedded in
+ * the file name. The structure of the versioned backup filename is as followed:
+ * (original file name).(backup timestamp).(original file version).vak
+ *
+ * During the version upgrade process, the new upgraded file will have it's name set to the original
+ * file name. The backup timestamp helps distinguish between versioned backups if multiple upgrades
+ * and downgrades have taken place. The original file version denotes how to parse the file.
  */
 public class UsageStatsDatabase {
-    private static final int CURRENT_VERSION = 3;
+    private static final int DEFAULT_CURRENT_VERSION = 4;
 
     // Current version of the backup schema
     static final int BACKUP_VERSION = 1;
@@ -52,10 +88,16 @@
     // same as UsageStatsBackupHelper.KEY_USAGE_STATS
     static final String KEY_USAGE_STATS = "usage_stats";
 
+    // Persist versioned backup files.
+    // Should be false, except when testing new versions
+    // STOPSHIP: b/111422946 this should be false on launch
+    static final boolean KEEP_VAK_FILES = true;
 
     private static final String TAG = "UsageStatsDatabase";
-    private static final boolean DEBUG = UsageStatsService.DEBUG;
+    // STOPSHIP: b/111422946 this should be boolean DEBUG = UsageStatsService.DEBUG; on launch
+    private static final boolean DEBUG = true;
     private static final String BAK_SUFFIX = ".bak";
+    private static final String VERSIONED_BAK_SUFFIX = ".vak";
     private static final String CHECKED_IN_SUFFIX = UsageStatsXml.CHECKED_IN_SUFFIX;
     private static final String RETENTION_LEN_KEY = "ro.usagestats.chooser.retention";
     private static final int SELECTION_LOG_RETENTION_LEN =
@@ -66,21 +108,40 @@
     private final TimeSparseArray<AtomicFile>[] mSortedStatFiles;
     private final UnixCalendar mCal;
     private final File mVersionFile;
+    // If this file exists on disk, UsageStatsDatabase is in the middle of migrating files to a new
+    // version. If this file exists on boot, the upgrade was interrupted and needs to be picked up
+    // where it left off.
+    private final File mUpdateBreadcrumb;
+    // Current version of the database files schema
+    private final int mCurrentVersion;
     private boolean mFirstUpdate;
     private boolean mNewUpdate;
 
-    public UsageStatsDatabase(File dir) {
-        mIntervalDirs = new File[] {
+    /**
+     * UsageStatsDatabase constructor that allows setting the version number.
+     * This should only be used for testing.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public UsageStatsDatabase(File dir, int version) {
+        mIntervalDirs = new File[]{
                 new File(dir, "daily"),
                 new File(dir, "weekly"),
                 new File(dir, "monthly"),
                 new File(dir, "yearly"),
         };
+        mCurrentVersion = version;
         mVersionFile = new File(dir, "version");
+        mUpdateBreadcrumb = new File(dir, "breadcrumb");
         mSortedStatFiles = new TimeSparseArray[mIntervalDirs.length];
         mCal = new UnixCalendar(0);
     }
 
+    public UsageStatsDatabase(File dir) {
+        this(dir, DEFAULT_CURRENT_VERSION);
+    }
+
     /**
      * Initialize any directories required and index what stats are available.
      */
@@ -154,7 +215,7 @@
             try {
                 IntervalStats stats = new IntervalStats();
                 for (int i = start; i < fileCount - 1; i++) {
-                    UsageStatsXml.read(files.valueAt(i), stats);
+                    readLocked(files.valueAt(i), stats);
                     if (!checkinAction.checkin(stats)) {
                         return false;
                     }
@@ -190,7 +251,7 @@
         final FilenameFilter backupFileFilter = new FilenameFilter() {
             @Override
             public boolean accept(File dir, String name) {
-                return !name.endsWith(BAK_SUFFIX);
+                return !name.endsWith(BAK_SUFFIX) && !name.endsWith(VERSIONED_BAK_SUFFIX);
             }
         };
 
@@ -210,7 +271,7 @@
                 for (File f : files) {
                     final AtomicFile af = new AtomicFile(f);
                     try {
-                        mSortedStatFiles[i].put(UsageStatsXml.parseBeginTime(af), af);
+                        mSortedStatFiles[i].put(parseBeginTime(af), af);
                     } catch (IOException e) {
                         Slog.e(TAG, "failed to index file: " + f, e);
                     }
@@ -252,14 +313,32 @@
             version = 0;
         }
 
-        if (version != CURRENT_VERSION) {
-            Slog.i(TAG, "Upgrading from version " + version + " to " + CURRENT_VERSION);
-            doUpgradeLocked(version);
+        if (version != mCurrentVersion) {
+            Slog.i(TAG, "Upgrading from version " + version + " to " + mCurrentVersion);
+            if (!mUpdateBreadcrumb.exists()) {
+                doUpgradeLocked(version);
+            } else {
+                Slog.i(TAG, "Version upgrade breadcrumb found on disk! Continuing version upgrade");
+            }
+
+            if (mUpdateBreadcrumb.exists()) {
+                int previousVersion;
+                long token;
+                try (BufferedReader reader = new BufferedReader(
+                        new FileReader(mUpdateBreadcrumb))) {
+                    token = Long.parseLong(reader.readLine());
+                    previousVersion = Integer.parseInt(reader.readLine());
+                } catch (NumberFormatException | IOException e) {
+                    Slog.e(TAG, "Failed read version upgrade breadcrumb");
+                    throw new RuntimeException(e);
+                }
+                continueUpgradeLocked(previousVersion, token);
+            }
         }
 
-        if (version != CURRENT_VERSION || mNewUpdate) {
+        if (version != mCurrentVersion || mNewUpdate) {
             try (BufferedWriter writer = new BufferedWriter(new FileWriter(mVersionFile))) {
-                writer.write(Integer.toString(CURRENT_VERSION));
+                writer.write(Integer.toString(mCurrentVersion));
                 writer.write("\n");
                 writer.write(currentFingerprint);
                 writer.write("\n");
@@ -269,6 +348,14 @@
                 throw new RuntimeException(e);
             }
         }
+
+        if (mUpdateBreadcrumb.exists()) {
+            // Files should be up to date with current version. Clear the version update breadcrumb
+            if (!KEEP_VAK_FILES) {
+                removeVersionedBackupFiles();
+            }
+            mUpdateBreadcrumb.delete();
+        }
     }
 
     private String getBuildFingerprint() {
@@ -290,6 +377,119 @@
                     }
                 }
             }
+        } else {
+            // Turn all current usage stats files into versioned backup files
+            final long token = System.currentTimeMillis();
+            final FilenameFilter backupFileFilter = new FilenameFilter() {
+                @Override
+                public boolean accept(File dir, String name) {
+                    return !name.endsWith(BAK_SUFFIX) && !name.endsWith(VERSIONED_BAK_SUFFIX);
+                }
+            };
+
+            for (int i = 0; i < mIntervalDirs.length; i++) {
+                File[] files = mIntervalDirs[i].listFiles(backupFileFilter);
+                if (files != null) {
+                    for (int j = 0; j < files.length; j++) {
+                        final File backupFile = new File(
+                                files[j].toString() + "." + Long.toString(token) + "."
+                                        + Integer.toString(thisVersion) + VERSIONED_BAK_SUFFIX);
+                        if (DEBUG) {
+                            Slog.d(TAG, "Creating versioned (" + Integer.toString(thisVersion)
+                                    + ") backup of " + files[j].toString()
+                                    + " stat files for interval "
+                                    + i + " to " + backupFile.toString());
+                        }
+
+                        try {
+                            // Backup file should not already exist, but make sure it doesn't
+                            Files.deleteIfExists(backupFile.toPath());
+                            Files.move(files[j].toPath(), backupFile.toPath(),
+                                    StandardCopyOption.ATOMIC_MOVE);
+                        } catch (IOException e) {
+                            Slog.e(TAG, "Failed to back up file : " + files[j].toString());
+                            throw new RuntimeException(e);
+                        }
+                    }
+                }
+            }
+
+            // Leave a breadcrumb behind noting that all the usage stats have been copied to a
+            // versioned backup.
+            BufferedWriter writer = null;
+            try {
+                writer = new BufferedWriter(new FileWriter(mUpdateBreadcrumb));
+                writer.write(Long.toString(token));
+                writer.write("\n");
+                writer.write(Integer.toString(thisVersion));
+                writer.write("\n");
+                writer.flush();
+            } catch (IOException e) {
+                Slog.e(TAG, "Failed to write new version upgrade breadcrumb");
+                throw new RuntimeException(e);
+            } finally {
+                IoUtils.closeQuietly(writer);
+            }
+        }
+    }
+
+    private void continueUpgradeLocked(int version, long token) {
+        // Read all the backed ups for the specified version and rewrite them with the current
+        // version's file format.
+        final FilenameFilter versionedBackupFileFilter = new FilenameFilter() {
+            @Override
+            public boolean accept(File dir, String name) {
+                return name.endsWith("." + Long.toString(token) + "." + Integer.toString(version)
+                        + VERSIONED_BAK_SUFFIX);
+            }
+        };
+
+        for (int i = 0; i < mIntervalDirs.length; i++) {
+            File[] files = mIntervalDirs[i].listFiles(versionedBackupFileFilter);
+            if (files != null) {
+                for (int j = 0; j < files.length; j++) {
+                    if (DEBUG) {
+                        Slog.d(TAG,
+                                "Upgrading " + files[j].toString() + " to version ("
+                                        + Integer.toString(
+                                        mCurrentVersion) + ") for interval " + i);
+                    }
+                    try {
+                        IntervalStats stats = new IntervalStats();
+                        readLocked(new AtomicFile(files[j]), stats, version);
+                        writeLocked(new AtomicFile(new File(mIntervalDirs[i],
+                                Long.toString(stats.beginTime))), stats, mCurrentVersion);
+                    } catch (IOException e) {
+                        Slog.e(TAG,
+                                "Failed to upgrade versioned backup file : " + files[j].toString());
+                        throw new RuntimeException(e);
+                    }
+                }
+            }
+        }
+    }
+
+    private void removeVersionedBackupFiles() {
+        final FilenameFilter versionedBackupFileFilter = new FilenameFilter() {
+            @Override
+            public boolean accept(File dir, String name) {
+                return name.endsWith(VERSIONED_BAK_SUFFIX);
+            }
+        };
+
+        for (int i = 0; i < mIntervalDirs.length; i++) {
+            File[] files = mIntervalDirs[i].listFiles(versionedBackupFileFilter);
+            if (files != null) {
+                for (int j = 0; j < files.length; j++) {
+                    if (DEBUG) {
+                        Slog.d(TAG,
+                                "Removing " + files[j].toString() + " for interval " + i);
+                    }
+                    if (!files[j].delete()) {
+                        Slog.e(TAG, "Failed to delete file : " + files[j].toString());
+                    }
+                }
+            }
         }
     }
 
@@ -357,7 +557,7 @@
             try {
                 final AtomicFile f = mSortedStatFiles[intervalType].valueAt(fileCount - 1);
                 IntervalStats stats = new IntervalStats();
-                UsageStatsXml.read(f, stats);
+                readLocked(f, stats);
                 return stats;
             } catch (IOException e) {
                 Slog.e(TAG, "Failed to read usage stats file", e);
@@ -379,8 +579,8 @@
          * which means you should make a copy of the data before adding it to the
          * <code>accumulatedResult</code> list.
          *
-         * @param stats The {@link IntervalStats} object selected.
-         * @param mutable Whether or not the data inside the stats object is mutable.
+         * @param stats             The {@link IntervalStats} object selected.
+         * @param mutable           Whether or not the data inside the stats object is mutable.
          * @param accumulatedResult The list to which to add extracted data.
          */
         void combine(IntervalStats stats, boolean mutable, List<T> accumulatedResult);
@@ -443,7 +643,7 @@
                 }
 
                 try {
-                    UsageStatsXml.read(f, stats);
+                    readLocked(f, stats);
                     if (beginTime < stats.endTime) {
                         combiner.combine(stats, false, results);
                     }
@@ -523,14 +723,9 @@
         File[] files = dir.listFiles();
         if (files != null) {
             for (File f : files) {
-                String path = f.getPath();
-                if (path.endsWith(BAK_SUFFIX)) {
-                    f = new File(path.substring(0, path.length() - BAK_SUFFIX.length()));
-                }
-
                 long beginTime;
                 try {
-                    beginTime = UsageStatsXml.parseBeginTime(f);
+                    beginTime = parseBeginTime(f);
                 } catch (IOException e) {
                     beginTime = 0;
                 }
@@ -542,18 +737,13 @@
         }
     }
 
-    private static void pruneChooserCountsOlderThan(File dir, long expiryTime) {
+    private void pruneChooserCountsOlderThan(File dir, long expiryTime) {
         File[] files = dir.listFiles();
         if (files != null) {
             for (File f : files) {
-                String path = f.getPath();
-                if (path.endsWith(BAK_SUFFIX)) {
-                    f = new File(path.substring(0, path.length() - BAK_SUFFIX.length()));
-                }
-
                 long beginTime;
                 try {
-                    beginTime = UsageStatsXml.parseBeginTime(f);
+                    beginTime = parseBeginTime(f);
                 } catch (IOException e) {
                     beginTime = 0;
                 }
@@ -562,7 +752,7 @@
                     try {
                         final AtomicFile af = new AtomicFile(f);
                         final IntervalStats stats = new IntervalStats();
-                        UsageStatsXml.read(af, stats);
+                        readLocked(af, stats);
                         final int pkgCount = stats.packageStats.size();
                         for (int i = 0; i < pkgCount; i++) {
                             UsageStats pkgStats = stats.packageStats.valueAt(i);
@@ -570,7 +760,7 @@
                                 pkgStats.mChooserCounts.clear();
                             }
                         }
-                        UsageStatsXml.write(af, stats);
+                        writeLocked(af, stats);
                     } catch (IOException e) {
                         Slog.e(TAG, "Failed to delete chooser counts from usage stats file", e);
                     }
@@ -579,6 +769,222 @@
         }
     }
 
+
+    private static long parseBeginTime(AtomicFile file) throws IOException {
+        return parseBeginTime(file.getBaseFile());
+    }
+
+    private static long parseBeginTime(File file) throws IOException {
+        String name = file.getName();
+
+        // Parse out the digits from the the front of the file name
+        for (int i = 0; i < name.length(); i++) {
+            final char c = name.charAt(i);
+            if (c < '0' || c > '9') {
+                // found first char that is not a digit.
+                name = name.substring(0, i);
+                break;
+            }
+        }
+
+        try {
+            return Long.parseLong(name);
+        } catch (NumberFormatException e) {
+            throw new IOException(e);
+        }
+    }
+
+    private void writeLocked(AtomicFile file, IntervalStats stats) throws IOException {
+        writeLocked(file, stats, mCurrentVersion);
+    }
+
+    private static void writeLocked(AtomicFile file, IntervalStats stats, int version)
+            throws IOException {
+        FileOutputStream fos = file.startWrite();
+        try {
+            writeLocked(fos, stats, version);
+            file.finishWrite(fos);
+            fos = null;
+        } finally {
+            // When fos is null (successful write), this will no-op
+            file.failWrite(fos);
+        }
+    }
+
+    private void writeLocked(OutputStream out, IntervalStats stats) throws IOException {
+        writeLocked(out, stats, mCurrentVersion);
+    }
+
+    private static void writeLocked(OutputStream out, IntervalStats stats, int version)
+            throws IOException {
+        switch (version) {
+            case 1:
+            case 2:
+            case 3:
+                UsageStatsXml.write(out, stats);
+                break;
+            case 4:
+                UsageStatsProto.write(out, stats);
+                break;
+            default:
+                throw new RuntimeException(
+                        "Unhandled UsageStatsDatabase version: " + Integer.toString(version)
+                                + " on write.");
+        }
+    }
+
+    private void readLocked(AtomicFile file, IntervalStats statsOut) throws IOException {
+        readLocked(file, statsOut, mCurrentVersion);
+    }
+
+    private static void readLocked(AtomicFile file, IntervalStats statsOut, int version)
+            throws IOException {
+        try {
+            FileInputStream in = file.openRead();
+            try {
+                statsOut.beginTime = parseBeginTime(file);
+                readLocked(in, statsOut, version);
+                statsOut.lastTimeSaved = file.getLastModifiedTime();
+            } finally {
+                try {
+                    in.close();
+                } catch (IOException e) {
+                    // Empty
+                }
+            }
+        } catch (FileNotFoundException e) {
+            Slog.e(TAG, "UsageStatsDatabase", e);
+            throw e;
+        }
+        // STOPSHIP: b/111422946, b/115429334
+        // Everything below this comment is sanity check against the new database version.
+        // After the new version has soaked for some time the following should removed.
+        // The goal of this check is to make sure the the ProtoInputStream is properly reading from
+        // the UsageStats files.
+        final StringBuilder sb = new StringBuilder();
+        final int failureLogLimit = 10;
+        int failures = 0;
+
+        final int packagesSize = statsOut.packageStats.size();
+        for (int i = 0; i < packagesSize; i++) {
+            final UsageStats stat = statsOut.packageStats.valueAt(i);
+            if (stat == null) {
+                // ArrayMap may contain null values, skip them
+                continue;
+            }
+            if (stat.mPackageName.isEmpty()) {
+                if (failures++ < failureLogLimit) {
+                    sb.append("\nUnexpected empty usage stats package name loaded");
+                }
+            }
+            if (stat.mBeginTimeStamp > statsOut.endTime) {
+                if (failures++ < failureLogLimit) {
+                    sb.append("\nUnreasonable usage stats stat begin timestamp ");
+                    sb.append(stat.mBeginTimeStamp);
+                    sb.append(" loaded (beginTime : ");
+                    sb.append(statsOut.beginTime);
+                    sb.append(", endTime : ");
+                    sb.append(statsOut.endTime);
+                    sb.append(")");
+                }
+            }
+            if (stat.mEndTimeStamp > statsOut.endTime) {
+                if (failures++ < failureLogLimit) {
+                    sb.append("\nUnreasonable usage stats stat end timestamp ");
+                    sb.append(stat.mEndTimeStamp);
+                    sb.append(" loaded (beginTime : ");
+                    sb.append(statsOut.beginTime);
+                    sb.append(", endTime : ");
+                    sb.append(statsOut.endTime);
+                    sb.append(")");
+                }
+            }
+            if (stat.mLastTimeUsed > statsOut.endTime) {
+                if (failures++ < failureLogLimit) {
+                    sb.append("\nUnreasonable usage stats stat last used timestamp ");
+                    sb.append(stat.mLastTimeUsed);
+                    sb.append(" loaded (beginTime : ");
+                    sb.append(statsOut.beginTime);
+                    sb.append(", endTime : ");
+                    sb.append(statsOut.endTime);
+                    sb.append(")");
+                }
+            }
+        }
+
+        if (statsOut.events != null) {
+            final int eventSize = statsOut.events.size();
+            for (int i = 0; i < eventSize; i++) {
+                final UsageEvents.Event event = statsOut.events.get(i);
+                if (event.mPackage.isEmpty()) {
+                    if (failures++ < failureLogLimit) {
+                        sb.append("\nUnexpected empty empty package name loaded");
+                    }
+                }
+                if (event.mTimeStamp < statsOut.beginTime || event.mTimeStamp > statsOut.endTime) {
+                    if (failures++ < failureLogLimit) {
+                        sb.append("\nUnexpected event timestamp ");
+                        sb.append(event.mTimeStamp);
+                        sb.append(" loaded (beginTime : ");
+                        sb.append(statsOut.beginTime);
+                        sb.append(", endTime : ");
+                        sb.append(statsOut.endTime);
+                        sb.append(")");
+                    }
+                }
+                if (event.mEventType < 0 || event.mEventType > UsageEvents.Event.MAX_EVENT_TYPE) {
+                    if (failures++ < failureLogLimit) {
+                        sb.append("\nUnexpected event type ");
+                        sb.append(event.mEventType);
+                        sb.append(" loaded");
+                    }
+                }
+                if ((event.mFlags & ~UsageEvents.Event.VALID_FLAG_BITS) != 0) {
+                    if (failures++ < failureLogLimit) {
+                        sb.append("\nUnexpected event flag bit 0b");
+                        sb.append(Integer.toBinaryString(event.mFlags));
+                        sb.append(" loaded");
+                    }
+                }
+            }
+        }
+
+        if (failures != 0) {
+            if (failures > failureLogLimit) {
+                sb.append("\nFailure log limited (");
+                sb.append(failures);
+                sb.append(" total failures found!)");
+            }
+            sb.append("\nError found in:\n");
+            sb.append(file.getBaseFile().getAbsolutePath());
+            sb.append("\nPlease go to b/115429334 to help root cause this issue");
+            Slog.wtf(TAG,sb.toString());
+        }
+    }
+
+    private void readLocked(InputStream in, IntervalStats statsOut) throws IOException {
+        readLocked(in, statsOut, mCurrentVersion);
+    }
+
+    private static void readLocked(InputStream in, IntervalStats statsOut, int version)
+            throws IOException {
+        switch (version) {
+            case 1:
+            case 2:
+            case 3:
+                UsageStatsXml.read(in, statsOut);
+                break;
+            case 4:
+                UsageStatsProto.read(in, statsOut);
+                break;
+            default:
+                throw new RuntimeException(
+                        "Unhandled UsageStatsDatabase version: " + Integer.toString(version)
+                                + " on read.");
+        }
+
+    }
+
     /**
      * Update the stats in the database. They may not be written to disk immediately.
      */
@@ -596,7 +1002,7 @@
                 mSortedStatFiles[intervalType].put(stats.beginTime, f);
             }
 
-            UsageStatsXml.write(f, stats);
+            writeLocked(f, stats);
             stats.lastTimeSaved = f.getLastModifiedTime();
         }
     }
@@ -730,7 +1136,7 @@
             throws IOException {
         IntervalStats stats = new IntervalStats();
         try {
-            UsageStatsXml.read(statsFile, stats);
+            readLocked(statsFile, stats);
         } catch (IOException e) {
             Slog.e(TAG, "Failed to read usage stats file", e);
             out.writeInt(0);
@@ -756,12 +1162,12 @@
         if (stats.events != null) stats.events.clear();
     }
 
-    private static byte[] serializeIntervalStats(IntervalStats stats) {
+    private byte[] serializeIntervalStats(IntervalStats stats) {
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         DataOutputStream out = new DataOutputStream(baos);
         try {
             out.writeLong(stats.beginTime);
-            UsageStatsXml.write(out, stats);
+            writeLocked(out, stats);
         } catch (IOException ioe) {
             Slog.d(TAG, "Serializing IntervalStats Failed", ioe);
             baos.reset();
@@ -769,13 +1175,13 @@
         return baos.toByteArray();
     }
 
-    private static IntervalStats deserializeIntervalStats(byte[] data) {
+    private IntervalStats deserializeIntervalStats(byte[] data) {
         ByteArrayInputStream bais = new ByteArrayInputStream(data);
         DataInputStream in = new DataInputStream(bais);
         IntervalStats stats = new IntervalStats();
         try {
             stats.beginTime = in.readLong();
-            UsageStatsXml.read(in, stats);
+            readLocked(in, stats);
         } catch (IOException ioe) {
             Slog.d(TAG, "DeSerializing IntervalStats Failed", ioe);
             stats = null;
diff --git a/services/usage/java/com/android/server/usage/UsageStatsProto.java b/services/usage/java/com/android/server/usage/UsageStatsProto.java
new file mode 100644
index 0000000..30d303f
--- /dev/null
+++ b/services/usage/java/com/android/server/usage/UsageStatsProto.java
@@ -0,0 +1,552 @@
+/*
+ * 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.server.usage;
+
+import android.app.usage.ConfigurationStats;
+import android.app.usage.EventList;
+import android.app.usage.UsageEvents;
+import android.app.usage.UsageStats;
+import android.content.res.Configuration;
+import android.util.ArrayMap;
+
+import android.util.Slog;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.ProtocolException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * UsageStats reader/writer for Protocol Buffer format
+ */
+final class UsageStatsProto {
+    private static String TAG = "UsageStatsProto";
+
+    // Static-only utility class.
+    private UsageStatsProto() {}
+
+    private static List<String> readStringPool(ProtoInputStream proto) throws IOException {
+
+        final long token = proto.start(IntervalStatsProto.STRINGPOOL);
+        List<String> stringPool;
+        if (proto.isNextField(IntervalStatsProto.StringPool.SIZE)) {
+            stringPool = new ArrayList(proto.readInt(IntervalStatsProto.StringPool.SIZE));
+        } else {
+            stringPool = new ArrayList();
+        }
+        while (proto.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+            switch (proto.getFieldNumber()) {
+                case (int) IntervalStatsProto.StringPool.STRINGS:
+                    stringPool.add(proto.readString(IntervalStatsProto.StringPool.STRINGS));
+                    break;
+            }
+        }
+        proto.end(token);
+        return stringPool;
+    }
+
+    private static void loadUsageStats(ProtoInputStream proto, long fieldId,
+            IntervalStats statsOut, List<String> stringPool)
+            throws IOException {
+
+        final long token = proto.start(fieldId);
+        UsageStats stats;
+        if (proto.isNextField(IntervalStatsProto.UsageStats.PACKAGE_INDEX)) {
+            // Fast path reading the package name index. Most cases this should work since it is
+            // written first
+            stats = statsOut.getOrCreateUsageStats(
+                    stringPool.get(proto.readInt(IntervalStatsProto.UsageStats.PACKAGE_INDEX) - 1));
+        } else if (proto.isNextField(IntervalStatsProto.UsageStats.PACKAGE)) {
+            // No package index, try package name instead
+            stats = statsOut.getOrCreateUsageStats(
+                    proto.readString(IntervalStatsProto.UsageStats.PACKAGE));
+        } else {
+            // Temporarily store collected data to a UsageStats object. This is not efficient.
+            stats = new UsageStats();
+        }
+
+        while (proto.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+            switch (proto.getFieldNumber()) {
+                case (int) IntervalStatsProto.UsageStats.PACKAGE:
+                    // Fast track failed from some reason, add UsageStats object to statsOut now
+                    UsageStats tempPackage = statsOut.getOrCreateUsageStats(
+                            proto.readString(IntervalStatsProto.UsageStats.PACKAGE));
+                    tempPackage.mLastTimeUsed = stats.mLastTimeUsed;
+                    tempPackage.mTotalTimeInForeground = stats.mTotalTimeInForeground;
+                    tempPackage.mLastEvent = stats.mLastEvent;
+                    tempPackage.mAppLaunchCount = stats.mAppLaunchCount;
+                    stats = tempPackage;
+                    break;
+                case (int) IntervalStatsProto.UsageStats.PACKAGE_INDEX:
+                    // Fast track failed from some reason, add UsageStats object to statsOut now
+                    UsageStats tempPackageIndex = statsOut.getOrCreateUsageStats(stringPool.get(
+                            proto.readInt(IntervalStatsProto.UsageStats.PACKAGE_INDEX) - 1));
+                    tempPackageIndex.mLastTimeUsed = stats.mLastTimeUsed;
+                    tempPackageIndex.mTotalTimeInForeground = stats.mTotalTimeInForeground;
+                    tempPackageIndex.mLastEvent = stats.mLastEvent;
+                    tempPackageIndex.mAppLaunchCount = stats.mAppLaunchCount;
+                    stats = tempPackageIndex;
+                    break;
+                case (int) IntervalStatsProto.UsageStats.LAST_TIME_ACTIVE_MS:
+                    stats.mLastTimeUsed = statsOut.beginTime + proto.readLong(
+                            IntervalStatsProto.UsageStats.LAST_TIME_ACTIVE_MS);
+                    break;
+                case (int) IntervalStatsProto.UsageStats.TOTAL_TIME_ACTIVE_MS:
+                    stats.mTotalTimeInForeground = proto.readLong(
+                            IntervalStatsProto.UsageStats.TOTAL_TIME_ACTIVE_MS);
+                    break;
+                case (int) IntervalStatsProto.UsageStats.LAST_EVENT:
+                    stats.mLastEvent = proto.readInt(IntervalStatsProto.UsageStats.LAST_EVENT);
+                    break;
+                case (int) IntervalStatsProto.UsageStats.APP_LAUNCH_COUNT:
+                    stats.mAppLaunchCount = proto.readInt(
+                            IntervalStatsProto.UsageStats.APP_LAUNCH_COUNT);
+                    break;
+                case (int) IntervalStatsProto.UsageStats.CHOOSER_ACTIONS:
+                    final long chooserToken = proto.start(
+                            IntervalStatsProto.UsageStats.CHOOSER_ACTIONS);
+                    loadChooserCounts(proto, stats);
+                    proto.end(chooserToken);
+                    break;
+            }
+        }
+        if (stats.mLastTimeUsed == 0) {
+            // mLastTimeUsed was not assigned, assume default value of 0 plus beginTime;
+            stats.mLastTimeUsed = statsOut.beginTime;
+        }
+        proto.end(token);
+    }
+
+    private static void loadCountAndTime(ProtoInputStream proto, long fieldId,
+            IntervalStats.EventTracker tracker) throws IOException {
+        final long token = proto.start(fieldId);
+        while (true) {
+            switch (proto.nextField()) {
+                case (int) IntervalStatsProto.CountAndTime.COUNT:
+                    tracker.count = proto.readInt(IntervalStatsProto.CountAndTime.COUNT);
+                    break;
+                case (int) IntervalStatsProto.CountAndTime.TIME_MS:
+                    tracker.duration = proto.readLong(IntervalStatsProto.CountAndTime.TIME_MS);
+                    break;
+                case ProtoInputStream.NO_MORE_FIELDS:
+                    proto.end(token);
+                    return;
+            }
+        }
+    }
+
+    private static void loadChooserCounts(ProtoInputStream proto, UsageStats usageStats)
+            throws IOException {
+        if (usageStats.mChooserCounts == null) {
+            usageStats.mChooserCounts = new ArrayMap<>();
+        }
+        String action = null;
+        ArrayMap<String, Integer> counts;
+        if (proto.isNextField(IntervalStatsProto.UsageStats.ChooserAction.NAME)) {
+            // Fast path reading the action name. Most cases this should work since it is written
+            // first
+            action = proto.readString(IntervalStatsProto.UsageStats.ChooserAction.NAME);
+            counts = usageStats.mChooserCounts.get(action);
+            if (counts == null) {
+                counts = new ArrayMap<>();
+                usageStats.mChooserCounts.put(action, counts);
+            }
+        } else {
+            // Temporarily store collected data to an ArrayMap. This is not efficient.
+            counts = new ArrayMap<>();
+        }
+
+        while (true) {
+            switch (proto.nextField()) {
+                case (int) IntervalStatsProto.UsageStats.ChooserAction.NAME:
+                    // Fast path failed from some reason, add the ArrayMap object to usageStats now
+                    action = proto.readString(IntervalStatsProto.UsageStats.ChooserAction.NAME);
+                    usageStats.mChooserCounts.put(action, counts);
+                    break;
+                case (int) IntervalStatsProto.UsageStats.ChooserAction.COUNTS:
+                    final long token = proto.start(
+                            IntervalStatsProto.UsageStats.ChooserAction.COUNTS);
+                    loadCountsForAction(proto, counts);
+                    proto.end(token);
+                case ProtoInputStream.NO_MORE_FIELDS:
+                    if (action == null) {
+                        // default string
+                        usageStats.mChooserCounts.put("", counts);
+                    }
+                    return;
+            }
+        }
+    }
+
+    private static void loadCountsForAction(ProtoInputStream proto,
+            ArrayMap<String, Integer> counts) throws IOException {
+        String category = null;
+        int count = 0;
+        while (true) {
+            switch (proto.nextField()) {
+                case (int) IntervalStatsProto.UsageStats.ChooserAction.CategoryCount.NAME:
+                    category = proto.readString(
+                            IntervalStatsProto.UsageStats.ChooserAction.CategoryCount.NAME);
+                    break;
+                case (int) IntervalStatsProto.UsageStats.ChooserAction.CategoryCount.COUNT:
+                    count = proto.readInt(
+                            IntervalStatsProto.UsageStats.ChooserAction.CategoryCount.COUNT);
+                    break;
+                case ProtoInputStream.NO_MORE_FIELDS:
+                    if (category == null) {
+                        counts.put("", count);
+                    } else {
+                        counts.put(category, count);
+                    }
+                    return;
+            }
+        }
+    }
+
+    private static void loadConfigStats(ProtoInputStream proto, long fieldId,
+            IntervalStats statsOut) throws IOException {
+        final long token = proto.start(fieldId);
+        boolean configActive = false;
+        final Configuration config = new Configuration();
+        ConfigurationStats configStats;
+        if (proto.isNextField(IntervalStatsProto.Configuration.CONFIG)) {
+            // Fast path reading the configuration. Most cases this should work since it is
+            // written first
+            config.readFromProto(proto, IntervalStatsProto.Configuration.CONFIG);
+            configStats = statsOut.getOrCreateConfigurationStats(config);
+        } else {
+            // Temporarily store collected data to a ConfigurationStats object. This is not
+            // efficient.
+            configStats = new ConfigurationStats();
+        }
+        while (true) {
+            switch (proto.nextField()) {
+                case (int) IntervalStatsProto.Configuration.CONFIG:
+                    // Fast path failed from some reason, add ConfigStats object to statsOut now
+                    config.readFromProto(proto, IntervalStatsProto.Configuration.CONFIG);
+                    final ConfigurationStats temp = statsOut.getOrCreateConfigurationStats(config);
+                    temp.mLastTimeActive = configStats.mLastTimeActive;
+                    temp.mTotalTimeActive = configStats.mTotalTimeActive;
+                    temp.mActivationCount = configStats.mActivationCount;
+                    configStats = temp;
+                    break;
+                case (int) IntervalStatsProto.Configuration.LAST_TIME_ACTIVE_MS:
+                    configStats.mLastTimeActive = statsOut.beginTime + proto.readLong(
+                            IntervalStatsProto.Configuration.LAST_TIME_ACTIVE_MS);
+                    break;
+                case (int) IntervalStatsProto.Configuration.TOTAL_TIME_ACTIVE_MS:
+                    configStats.mTotalTimeActive = proto.readLong(
+                            IntervalStatsProto.Configuration.TOTAL_TIME_ACTIVE_MS);
+                    break;
+                case (int) IntervalStatsProto.Configuration.COUNT:
+                    configStats.mActivationCount = proto.readInt(
+                            IntervalStatsProto.Configuration.COUNT);
+                    break;
+                case (int) IntervalStatsProto.Configuration.ACTIVE:
+                    configActive = proto.readBoolean(IntervalStatsProto.Configuration.ACTIVE);
+                    break;
+                case ProtoInputStream.NO_MORE_FIELDS:
+                    if (configStats.mLastTimeActive == 0) {
+                        //mLastTimeActive was not assigned, assume default value of 0 plus beginTime
+                        configStats.mLastTimeActive = statsOut.beginTime;
+                    }
+                    if (configActive) {
+                        statsOut.activeConfiguration = configStats.mConfiguration;
+                    }
+                    proto.end(token);
+                    return;
+            }
+        }
+    }
+
+    private static void loadEvent(ProtoInputStream proto, long fieldId, IntervalStats statsOut,
+            List<String> stringPool) throws IOException {
+        final long token = proto.start(fieldId);
+        UsageEvents.Event event = statsOut.buildEvent(proto, stringPool);
+        proto.end(token);
+        if (event.mPackage == null) {
+            throw new ProtocolException("no package field present");
+        }
+
+        if (statsOut.events == null) {
+            statsOut.events = new EventList();
+        }
+        statsOut.events.insert(event);
+    }
+
+    private static void writeStringPool(ProtoOutputStream proto, final IntervalStats stats)
+            throws IOException {
+        final long token = proto.start(IntervalStatsProto.STRINGPOOL);
+        final int size = stats.mStringCache.size();
+        proto.write(IntervalStatsProto.StringPool.SIZE, size);
+        for (int i = 0; i < size; i++) {
+            proto.write(IntervalStatsProto.StringPool.STRINGS, stats.mStringCache.valueAt(i));
+        }
+        proto.end(token);
+    }
+
+    private static void writeUsageStats(ProtoOutputStream proto, long fieldId,
+            final IntervalStats stats, final UsageStats usageStats) throws IOException {
+        final long token = proto.start(fieldId);
+        // Write the package name first, so loadUsageStats can avoid creating an extra object
+        final int packageIndex = stats.mStringCache.indexOf(usageStats.mPackageName);
+        if (packageIndex >= 0) {
+            proto.write(IntervalStatsProto.UsageStats.PACKAGE_INDEX, packageIndex + 1);
+        } else {
+            // Package not in Stringpool for some reason, write full string instead
+            Slog.w(TAG, "UsageStats package name (" + usageStats.mPackageName
+                    + ") not found in IntervalStats string cache");
+            proto.write(IntervalStatsProto.UsageStats.PACKAGE, usageStats.mPackageName);
+        }
+        proto.write(IntervalStatsProto.UsageStats.LAST_TIME_ACTIVE_MS,
+                usageStats.mLastTimeUsed - stats.beginTime);
+        proto.write(IntervalStatsProto.UsageStats.TOTAL_TIME_ACTIVE_MS,
+                usageStats.mTotalTimeInForeground);
+        proto.write(IntervalStatsProto.UsageStats.LAST_EVENT, usageStats.mLastEvent);
+        proto.write(IntervalStatsProto.UsageStats.APP_LAUNCH_COUNT, usageStats.mAppLaunchCount);
+        writeChooserCounts(proto, usageStats);
+        proto.end(token);
+    }
+
+    private static void writeCountAndTime(ProtoOutputStream proto, long fieldId, int count,
+            long time) throws IOException {
+        final long token = proto.start(fieldId);
+        proto.write(IntervalStatsProto.CountAndTime.COUNT, count);
+        proto.write(IntervalStatsProto.CountAndTime.TIME_MS, time);
+        proto.end(token);
+    }
+
+
+    private static void writeChooserCounts(ProtoOutputStream proto, final UsageStats usageStats)
+            throws IOException {
+        if (usageStats == null || usageStats.mChooserCounts == null
+                || usageStats.mChooserCounts.keySet().isEmpty()) {
+            return;
+        }
+        final int chooserCountSize = usageStats.mChooserCounts.size();
+        for (int i = 0; i < chooserCountSize; i++) {
+            final String action = usageStats.mChooserCounts.keyAt(i);
+            final ArrayMap<String, Integer> counts = usageStats.mChooserCounts.valueAt(i);
+            if (action == null || counts == null || counts.isEmpty()) {
+                continue;
+            }
+            final long token = proto.start(IntervalStatsProto.UsageStats.CHOOSER_ACTIONS);
+            proto.write(IntervalStatsProto.UsageStats.ChooserAction.NAME, action);
+            writeCountsForAction(proto, counts);
+            proto.end(token);
+        }
+    }
+
+    private static void writeCountsForAction(ProtoOutputStream proto,
+            ArrayMap<String, Integer> counts) throws IOException {
+        final int countsSize = counts.size();
+        for (int i = 0; i < countsSize; i++) {
+            String key = counts.keyAt(i);
+            int count = counts.valueAt(i);
+            if (count > 0) {
+                final long token = proto.start(IntervalStatsProto.UsageStats.ChooserAction.COUNTS);
+                proto.write(IntervalStatsProto.UsageStats.ChooserAction.CategoryCount.NAME, key);
+                proto.write(IntervalStatsProto.UsageStats.ChooserAction.CategoryCount.COUNT, count);
+                proto.end(token);
+            }
+        }
+    }
+
+    private static void writeConfigStats(ProtoOutputStream proto, long fieldId,
+            final IntervalStats stats, final ConfigurationStats configStats, boolean isActive)
+            throws IOException {
+        final long token = proto.start(fieldId);
+        configStats.mConfiguration.writeToProto(proto, IntervalStatsProto.Configuration.CONFIG);
+        proto.write(IntervalStatsProto.Configuration.LAST_TIME_ACTIVE_MS,
+                configStats.mLastTimeActive - stats.beginTime);
+        proto.write(IntervalStatsProto.Configuration.TOTAL_TIME_ACTIVE_MS,
+                configStats.mTotalTimeActive);
+        proto.write(IntervalStatsProto.Configuration.COUNT, configStats.mActivationCount);
+        proto.write(IntervalStatsProto.Configuration.ACTIVE, isActive);
+        proto.end(token);
+
+    }
+
+    private static void writeEvent(ProtoOutputStream proto, long fieldId, final IntervalStats stats,
+            final UsageEvents.Event event) throws IOException {
+        final long token = proto.start(fieldId);
+        final int packageIndex = stats.mStringCache.indexOf(event.mPackage);
+        if (packageIndex >= 0) {
+            proto.write(IntervalStatsProto.Event.PACKAGE_INDEX, packageIndex + 1);
+        } else {
+            // Package not in Stringpool for some reason, write full string instead
+            Slog.w(TAG, "Usage event package name (" + event.mPackage
+                    + ") not found in IntervalStats string cache");
+            proto.write(IntervalStatsProto.Event.PACKAGE, event.mPackage);
+        }
+        if (event.mClass != null) {
+            final int classIndex = stats.mStringCache.indexOf(event.mClass);
+            if (classIndex >= 0) {
+                proto.write(IntervalStatsProto.Event.CLASS_INDEX, classIndex + 1);
+            } else {
+                // Class not in Stringpool for some reason, write full string instead
+                Slog.w(TAG, "Usage event class name (" + event.mClass
+                        + ") not found in IntervalStats string cache");
+                proto.write(IntervalStatsProto.Event.CLASS, event.mClass);
+            }
+        }
+        proto.write(IntervalStatsProto.Event.TIME_MS, event.mTimeStamp - stats.beginTime);
+        proto.write(IntervalStatsProto.Event.FLAGS, event.mFlags);
+        proto.write(IntervalStatsProto.Event.TYPE, event.mEventType);
+        switch (event.mEventType) {
+            case UsageEvents.Event.CONFIGURATION_CHANGE:
+                if (event.mConfiguration != null) {
+                    event.mConfiguration.writeToProto(proto, IntervalStatsProto.Event.CONFIG);
+                }
+                break;
+            case UsageEvents.Event.SHORTCUT_INVOCATION:
+                if (event.mShortcutId != null) {
+                    proto.write(IntervalStatsProto.Event.SHORTCUT_ID, event.mShortcutId);
+                }
+                break;
+            case UsageEvents.Event.STANDBY_BUCKET_CHANGED:
+                if (event.mBucketAndReason != 0) {
+                    proto.write(IntervalStatsProto.Event.STANDBY_BUCKET, event.mBucketAndReason);
+                }
+                break;
+            case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
+                if (event.mNotificationChannelId != null) {
+                    final int channelIndex = stats.mStringCache.indexOf(
+                            event.mNotificationChannelId);
+                    if (channelIndex >= 0) {
+                        proto.write(IntervalStatsProto.Event.NOTIFICATION_CHANNEL_INDEX,
+                                channelIndex + 1);
+                    } else {
+                        // Channel not in Stringpool for some reason, write full string instead
+                        Slog.w(TAG, "Usage event notification channel name ("
+                                + event.mNotificationChannelId
+                                + ") not found in IntervalStats string cache");
+                        proto.write(IntervalStatsProto.Event.NOTIFICATION_CHANNEL,
+                                event.mNotificationChannelId);
+                    }
+                }
+                break;
+        }
+        proto.end(token);
+    }
+
+    /**
+     * Reads from the {@link ProtoInputStream}.
+     *
+     * @param proto    The proto from which to read events.
+     * @param statsOut The stats object to populate with the data from the XML file.
+     */
+    public static void read(InputStream in, IntervalStats statsOut) throws IOException {
+        final ProtoInputStream proto = new ProtoInputStream(in);
+        List<String> stringPool = null;
+
+        statsOut.packageStats.clear();
+        statsOut.configurations.clear();
+        statsOut.activeConfiguration = null;
+
+        if (statsOut.events != null) {
+            statsOut.events.clear();
+        }
+
+        while (true) {
+            switch (proto.nextField()) {
+                case (int) IntervalStatsProto.END_TIME_MS:
+                    statsOut.endTime = statsOut.beginTime + proto.readLong(
+                            IntervalStatsProto.END_TIME_MS);
+                    break;
+                case (int) IntervalStatsProto.INTERACTIVE:
+                    loadCountAndTime(proto, IntervalStatsProto.INTERACTIVE,
+                            statsOut.interactiveTracker);
+                    break;
+                case (int) IntervalStatsProto.NON_INTERACTIVE:
+                    loadCountAndTime(proto, IntervalStatsProto.NON_INTERACTIVE,
+                            statsOut.nonInteractiveTracker);
+                    break;
+                case (int) IntervalStatsProto.KEYGUARD_SHOWN:
+                    loadCountAndTime(proto, IntervalStatsProto.KEYGUARD_SHOWN,
+                            statsOut.keyguardShownTracker);
+                    break;
+                case (int) IntervalStatsProto.KEYGUARD_HIDDEN:
+                    loadCountAndTime(proto, IntervalStatsProto.KEYGUARD_HIDDEN,
+                            statsOut.keyguardHiddenTracker);
+                    break;
+                case (int) IntervalStatsProto.STRINGPOOL:
+                    stringPool = readStringPool(proto);
+                    statsOut.mStringCache.addAll(stringPool);
+                    break;
+                case (int) IntervalStatsProto.PACKAGES:
+                    loadUsageStats(proto, IntervalStatsProto.PACKAGES, statsOut, stringPool);
+                    break;
+                case (int) IntervalStatsProto.CONFIGURATIONS:
+                    loadConfigStats(proto, IntervalStatsProto.CONFIGURATIONS, statsOut);
+                    break;
+                case (int) IntervalStatsProto.EVENT_LOG:
+                    loadEvent(proto, IntervalStatsProto.EVENT_LOG, statsOut, stringPool);
+                    break;
+                case ProtoInputStream.NO_MORE_FIELDS:
+                    if (statsOut.endTime == 0) {
+                        // endTime not assigned, assume default value of 0 plus beginTime
+                        statsOut.endTime = statsOut.beginTime;
+                    }
+                    return;
+            }
+        }
+    }
+
+    /**
+     * Writes the stats object to an ProtoBuf file.
+     *
+     * @param proto The serializer to which to write the packageStats data.
+     * @param stats The stats object to write to the XML file.
+     */
+    public static void write(OutputStream out, IntervalStats stats) throws IOException {
+        final ProtoOutputStream proto = new ProtoOutputStream(out);
+        proto.write(IntervalStatsProto.END_TIME_MS, stats.endTime - stats.beginTime);
+        // String pool should be written before the rest of the usage stats
+        writeStringPool(proto, stats);
+
+        writeCountAndTime(proto, IntervalStatsProto.INTERACTIVE, stats.interactiveTracker.count,
+                stats.interactiveTracker.duration);
+        writeCountAndTime(proto, IntervalStatsProto.NON_INTERACTIVE,
+                stats.nonInteractiveTracker.count, stats.nonInteractiveTracker.duration);
+        writeCountAndTime(proto, IntervalStatsProto.KEYGUARD_SHOWN,
+                stats.keyguardShownTracker.count, stats.keyguardShownTracker.duration);
+        writeCountAndTime(proto, IntervalStatsProto.KEYGUARD_HIDDEN,
+                stats.keyguardHiddenTracker.count, stats.keyguardHiddenTracker.duration);
+
+        final int statsCount = stats.packageStats.size();
+        for (int i = 0; i < statsCount; i++) {
+            writeUsageStats(proto, IntervalStatsProto.PACKAGES, stats,
+                    stats.packageStats.valueAt(i));
+        }
+        final int configCount = stats.configurations.size();
+        for (int i = 0; i < configCount; i++) {
+            boolean active = stats.activeConfiguration.equals(stats.configurations.keyAt(i));
+            writeConfigStats(proto, IntervalStatsProto.CONFIGURATIONS, stats,
+                    stats.configurations.valueAt(i), active);
+        }
+        final int eventCount = stats.events != null ? stats.events.size() : 0;
+        for (int i = 0; i < eventCount; i++) {
+            writeEvent(proto, IntervalStatsProto.EVENT_LOG, stats, stats.events.get(i));
+        }
+
+        proto.flush();
+    }
+}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXml.java b/services/usage/java/com/android/server/usage/UsageStatsXml.java
index e7db741..f8d1113 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXml.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXml.java
@@ -19,6 +19,9 @@
 import android.util.AtomicFile;
 import android.util.Slog;
 import android.util.Xml;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.XmlUtils;
 import org.xmlpull.v1.XmlPullParser;
@@ -33,61 +36,7 @@
     private static final String VERSION_ATTR = "version";
     static final String CHECKED_IN_SUFFIX = "-c";
 
-    public static long parseBeginTime(AtomicFile file) throws IOException {
-        return parseBeginTime(file.getBaseFile());
-    }
-
-    public static long parseBeginTime(File file) throws IOException {
-        String name = file.getName();
-
-        // Eat as many occurrences of -c as possible. This is due to a bug where -c
-        // would be appended more than once to a checked-in file, causing a crash
-        // on boot when indexing files since Long.parseLong() will puke on anything but
-        // a number.
-        while (name.endsWith(CHECKED_IN_SUFFIX)) {
-            name = name.substring(0, name.length() - CHECKED_IN_SUFFIX.length());
-        }
-
-        try {
-            return Long.parseLong(name);
-        } catch (NumberFormatException e) {
-            throw new IOException(e);
-        }
-    }
-
-    public static void read(AtomicFile file, IntervalStats statsOut) throws IOException {
-        try {
-            FileInputStream in = file.openRead();
-            try {
-                statsOut.beginTime = parseBeginTime(file);
-                read(in, statsOut);
-                statsOut.lastTimeSaved = file.getLastModifiedTime();
-            } finally {
-                try {
-                    in.close();
-                } catch (IOException e) {
-                    // Empty
-                }
-            }
-        } catch (FileNotFoundException e) {
-            Slog.e(TAG, "UsageStats Xml", e);
-            throw e;
-        }
-    }
-
-    public static void write(AtomicFile file, IntervalStats stats) throws IOException {
-        FileOutputStream fos = file.startWrite();
-        try {
-            write(fos, stats);
-            file.finishWrite(fos);
-            fos = null;
-        } finally {
-            // When fos is null (successful write), this will no-op
-            file.failWrite(fos);
-        }
-    }
-
-    static void read(InputStream in, IntervalStats statsOut) throws IOException {
+    public static void read(InputStream in, IntervalStats statsOut) throws IOException {
         XmlPullParser parser = Xml.newPullParser();
         try {
             parser.setInput(in, "utf-8");
@@ -113,7 +62,7 @@
         }
     }
 
-    static void write(OutputStream out, IntervalStats stats) throws IOException {
+    public static void write(OutputStream out, IntervalStats stats) throws IOException {
         FastXmlSerializer xml = new FastXmlSerializer();
         xml.setOutput(out, "utf-8");
         xml.startDocument("utf-8", true);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
index 6a1e97a..a68f9d3 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
@@ -196,11 +196,7 @@
                 event.mNotificationChannelId = (channelId != null) ? channelId.intern() : null;
                 break;
         }
-
-        if (statsOut.events == null) {
-            statsOut.events = new EventList();
-        }
-        statsOut.events.insert(event);
+        statsOut.addEvent(event);
     }
 
     private static void writeUsageStats(XmlSerializer xml, final IntervalStats stats,
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 9b194e9..1a8aba0 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -176,12 +176,8 @@
                     currentDailyStats.activeConfiguration, newFullConfig);
         }
 
-        // Add the event to the daily list.
-        if (currentDailyStats.events == null) {
-            currentDailyStats.events = new EventList();
-        }
         if (event.mEventType != UsageEvents.Event.SYSTEM_INTERACTION) {
-            currentDailyStats.events.insert(event);
+            currentDailyStats.addEvent(event);
         }
 
         boolean incrementAppLaunch = false;
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 995418e..57b652e 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -271,6 +271,14 @@
             KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL = "hide_carrier_network_settings_bool";
 
     /**
+     * Do only allow auto selection in Advanced Network Settings when in home network.
+     * Manual selection is allowed when in roaming network.
+     * @hide
+     */
+    public static final String
+            KEY_ONLY_AUTO_SELECT_IN_HOME_NETWORK_BOOL = "only_auto_select_in_home_network";
+
+    /**
      * Control whether users receive a simplified network settings UI and improved network
      * selection.
      */
@@ -2181,6 +2189,7 @@
         sDefaults.putBoolean(KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL, true);
         sDefaults.putBoolean(KEY_HAS_IN_CALL_NOISE_SUPPRESSION_BOOL, false);
         sDefaults.putBoolean(KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL, false);
+        sDefaults.putBoolean(KEY_ONLY_AUTO_SELECT_IN_HOME_NETWORK_BOOL, false);
         sDefaults.putBoolean(KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL, false);
         sDefaults.putBoolean(KEY_HIDE_SIM_LOCK_SETTINGS_BOOL, false);
 
diff --git a/telephony/java/android/telephony/NeighboringCellInfo.java b/telephony/java/android/telephony/NeighboringCellInfo.java
index 8e99518..5e4518f 100644
--- a/telephony/java/android/telephony/NeighboringCellInfo.java
+++ b/telephony/java/android/telephony/NeighboringCellInfo.java
@@ -32,7 +32,11 @@
 /**
  * Represents the neighboring cell information, including
  * Received Signal Strength and Cell ID location.
+ *
+ * @deprecated This class should not be used by anyone targeting SDK level 29 (Q) or higher.
+ *      Instead callers should use {@Link android.telephony.CellInfo}.
  */
+@Deprecated
 public class NeighboringCellInfo implements Parcelable
 {
     /**
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 38ee79f..d1091f4 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -2153,7 +2153,12 @@
 
     /**
      * Set preferred default data.
-     * Set on which slot default data will be on.
+     * Set on which slot most cellular data will be on.
+     * It's also usually what we set up internet connection on.
+     *
+     * PreferredData overwrites user setting of default data subscription. And it's used
+     * by ANAS or carrier apps to switch primary and CBRS subscription dynamically in multi-SIM
+     * devices.
      *
      * @param slotId which slot is preferred to for cellular data.
      * @hide
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 824533d..ea9ac39 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1590,6 +1590,7 @@
      *
      * @return List of NeighboringCellInfo or null if info unavailable.
      *
+     * @removed
      * @deprecated Use {@link #getAllCellInfo} which returns a superset of the information
      *             from NeighboringCellInfo, including LTE cell information.
      */
diff --git a/tests/net/java/android/net/netlink/InetDiagSocketTest.java b/tests/net/java/android/net/netlink/InetDiagSocketTest.java
index 39ecb7e5a..122edba 100644
--- a/tests/net/java/android/net/netlink/InetDiagSocketTest.java
+++ b/tests/net/java/android/net/netlink/InetDiagSocketTest.java
@@ -69,17 +69,12 @@
     private ConnectivityManager mCm;
     private Context mContext;
     private final static int SOCKET_TIMEOUT_MS = 100;
-    private boolean mInetDiagUdpEnabled;
 
     @Before
     public void setUp() throws Exception {
         Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
         mContext = instrumentation.getTargetContext();
         mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
-        int expectedUid = Process.myUid();
-        UdpConnection udp = new UdpConnection("127.0.0.1", "127.0.0.2");
-        int uid = mCm.getConnectionOwnerUid(udp.protocol, udp.local, udp.remote);
-        mInetDiagUdpEnabled = (uid == expectedUid);
     }
 
     private class Connection {
@@ -188,11 +183,6 @@
         tcp.close();
 
         /**
-         * TODO: STOPSHIP: Always test for UDP, do not allow opt-out.
-         */
-        if (!mInetDiagUdpEnabled) return;
-
-        /**
          * For UDP connections, either a complete match {protocol, local, remote} or a
          * partial match {protocol, local} should return a valid UID.
          */
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index fceaabd..1a05305 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -4536,4 +4536,78 @@
         mCellNetworkAgent.disconnect();
         mCm.unregisterNetworkCallback(networkCallback);
     }
+
+    @Test
+    public void testDataActivityTracking() throws RemoteException {
+        final TestNetworkCallback networkCallback = new TestNetworkCallback();
+        final NetworkRequest networkRequest = new NetworkRequest.Builder()
+                .addCapability(NET_CAPABILITY_INTERNET)
+                .build();
+        mCm.registerNetworkCallback(networkRequest, networkCallback);
+
+        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        final LinkProperties cellLp = new LinkProperties();
+        cellLp.setInterfaceName(MOBILE_IFNAME);
+        mCellNetworkAgent.sendLinkProperties(cellLp);
+        reset(mNetworkManagementService);
+        mCellNetworkAgent.connect(true);
+        networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        verify(mNetworkManagementService, times(1)).addIdleTimer(eq(MOBILE_IFNAME), anyInt(),
+                eq(ConnectivityManager.TYPE_MOBILE));
+
+        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        final LinkProperties wifiLp = new LinkProperties();
+        wifiLp.setInterfaceName(WIFI_IFNAME);
+        mWiFiNetworkAgent.sendLinkProperties(wifiLp);
+
+        // Network switch
+        reset(mNetworkManagementService);
+        mWiFiNetworkAgent.connect(true);
+        networkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+        networkCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+        networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+        verify(mNetworkManagementService, times(1)).addIdleTimer(eq(WIFI_IFNAME), anyInt(),
+                eq(ConnectivityManager.TYPE_WIFI));
+        verify(mNetworkManagementService, times(1)).removeIdleTimer(eq(MOBILE_IFNAME));
+
+        // Disconnect wifi and switch back to cell
+        reset(mNetworkManagementService);
+        mWiFiNetworkAgent.disconnect();
+        networkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+        assertNoCallbacks(networkCallback);
+        verify(mNetworkManagementService, times(1)).removeIdleTimer(eq(WIFI_IFNAME));
+        verify(mNetworkManagementService, times(1)).addIdleTimer(eq(MOBILE_IFNAME), anyInt(),
+                eq(ConnectivityManager.TYPE_MOBILE));
+
+        // reconnect wifi
+        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        wifiLp.setInterfaceName(WIFI_IFNAME);
+        mWiFiNetworkAgent.sendLinkProperties(wifiLp);
+        mWiFiNetworkAgent.connect(true);
+        networkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+        networkCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+        networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+
+        // Disconnect cell
+        reset(mNetworkManagementService);
+        mCellNetworkAgent.disconnect();
+        networkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
+        // LOST callback is triggered earlier than removing idle timer. Broadcast should also be
+        // sent as network being switched. Ensure rule removal for cell will not be triggered
+        // unexpectedly before network being removed.
+        waitForIdle();
+        verify(mNetworkManagementService, times(0)).removeIdleTimer(eq(MOBILE_IFNAME));
+        verify(mNetworkManagementService, times(1)).removeNetwork(
+                eq(mCellNetworkAgent.getNetwork().netId));
+
+        // Disconnect wifi
+        ConditionVariable cv = waitForConnectivityBroadcasts(1);
+        reset(mNetworkManagementService);
+        mWiFiNetworkAgent.disconnect();
+        waitFor(cv);
+        verify(mNetworkManagementService, times(1)).removeIdleTimer(eq(WIFI_IFNAME));
+
+        // Clean up
+        mCm.unregisterNetworkCallback(networkCallback);
+    }
 }
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index 40d5544..a6ed9f2 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -33,6 +33,7 @@
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
 import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
+import static android.provider.Settings.Global.TETHER_ENABLE_LEGACY_DHCP_SERVER;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
@@ -75,6 +76,8 @@
 import android.net.NetworkState;
 import android.net.NetworkUtils;
 import android.net.RouteInfo;
+import android.net.dhcp.DhcpServer;
+import android.net.dhcp.DhcpServingParams;
 import android.net.ip.IpServer;
 import android.net.ip.RouterAdvertisementDaemon;
 import android.net.util.InterfaceParams;
@@ -85,6 +88,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.INetworkManagementService;
+import android.os.Looper;
 import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.test.TestLooper;
@@ -146,6 +150,7 @@
     @Mock private UpstreamNetworkMonitor mUpstreamNetworkMonitor;
     @Mock private IPv6TetheringCoordinator mIPv6TetheringCoordinator;
     @Mock private RouterAdvertisementDaemon mRouterAdvertisementDaemon;
+    @Mock private DhcpServer mDhcpServer;
     @Mock private INetd mNetd;
 
     private final MockTetheringDependencies mTetheringDependencies =
@@ -240,6 +245,12 @@
                 public INetd getNetdService() {
                     return mNetd;
                 }
+
+                @Override
+                public DhcpServer makeDhcpServer(Looper looper, InterfaceParams iface,
+                        DhcpServingParams params, SharedLog log) {
+                    return mDhcpServer;
+                }
             };
         }
 
@@ -333,6 +344,7 @@
         mServiceContext = new MockContext(mContext);
         mContentResolver = new MockContentResolver(mServiceContext);
         mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+        Settings.Global.putInt(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, 0);
         mIntents = new Vector<>();
         mBroadcastReceiver = new BroadcastReceiver() {
             @Override
@@ -343,12 +355,16 @@
         mServiceContext.registerReceiver(mBroadcastReceiver,
                 new IntentFilter(ACTION_TETHER_STATE_CHANGED));
         mTetheringDependencies.reset();
-        mTethering = new Tethering(mServiceContext, mNMService, mStatsService, mPolicyManager,
-                                   mLooper.getLooper(), mSystemProperties,
-                                   mTetheringDependencies);
+        mTethering = makeTethering();
         verify(mNMService).registerTetheringStatsProvider(any(), anyString());
     }
 
+    private Tethering makeTethering() {
+        return new Tethering(mServiceContext, mNMService, mStatsService, mPolicyManager,
+                mLooper.getLooper(), mSystemProperties,
+                mTetheringDependencies);
+    }
+
     @After
     public void tearDown() {
         mServiceContext.unregisterReceiver(mBroadcastReceiver);
@@ -597,6 +613,18 @@
 
         sendIPv6TetherUpdates(upstreamState);
         verify(mRouterAdvertisementDaemon, never()).buildNewRa(any(), notNull());
+        verify(mDhcpServer, times(1)).start();
+    }
+
+    @Test
+    public void workingMobileUsbTethering_IPv4LegacyDhcp() {
+        Settings.Global.putInt(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, 1);
+        mTethering = makeTethering();
+        final NetworkState upstreamState = buildMobileIPv4UpstreamState();
+        runUsbTethering(upstreamState);
+        sendIPv6TetherUpdates(upstreamState);
+
+        verify(mDhcpServer, never()).start();
     }
 
     @Test
@@ -620,6 +648,7 @@
         verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
         verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
         verify(mRouterAdvertisementDaemon, times(1)).start();
+        verify(mDhcpServer, times(1)).start();
 
         sendIPv6TetherUpdates(upstreamState);
         verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull());
@@ -633,6 +662,7 @@
 
         verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
         verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+        verify(mDhcpServer, times(1)).start();
         verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
         verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME,
                 TEST_XLAT_MOBILE_IFNAME);
@@ -649,6 +679,7 @@
         runUsbTethering(upstreamState);
 
         verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+        verify(mDhcpServer, times(1)).start();
         verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
 
         // Then 464xlat comes up
@@ -671,6 +702,8 @@
         // Forwarding was not re-added for v6 (still times(1))
         verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
         verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+        // DHCP not restarted on downstream (still times(1))
+        verify(mDhcpServer, times(1)).start();
     }
 
     @Test
diff --git a/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java b/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
index bb31230..5217784 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
@@ -225,13 +225,4 @@
         final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog);
         assertFalse(cfg.enableLegacyDhcpServer);
     }
-
-    @Test
-    public void testNewDhcpServerDefault() {
-        Settings.Global.putString(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, null);
-
-        final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog);
-        // TODO: change to false when new server is promoted to default
-        assertTrue(cfg.enableLegacyDhcpServer);
-    }
 }
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 7a91347..59ba8e7 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1698,9 +1698,7 @@
      * @return the list of access points found in the most recent scan. An app must hold
      * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or
      * {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission
-     * in order to get valid results.  If there is a remote exception (e.g., either a communication
-     * problem with the system service or an exception within the framework) an empty list will be
-     * returned.
+     * in order to get valid results.
      */
     public List<ScanResult> getScanResults() {
         try {