Merge "Guard against null server" into pi-dev
diff --git a/Android.mk b/Android.mk
index e2f88e8..b630c3f 100644
--- a/Android.mk
+++ b/Android.mk
@@ -869,11 +869,27 @@
 $(eval $(call copy-one-file,frameworks/base/config/hiddenapi-blacklist.txt,\
                             $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)))
 
+# Automatically add all methods which match the following signatures.
+# These need to be greylisted in order to allow applications to write their
+# own serializers.
+$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST): REGEX_SERIALIZATION := \
+    "readObject\(Ljava/io/ObjectInputStream;\)V" \
+    "readObjectNoData\(\)V" \
+    "readResolve\(\)Ljava/lang/Object;" \
+    "serialVersionUID:J" \
+    "serialPersistentFields:\[Ljava/io/ObjectStreamField;" \
+    "writeObject\(Ljava/io/ObjectOutputStream;\)V" \
+    "writeReplace\(\)Ljava/lang/Object;"
+$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST): PRIVATE_API := $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE)
 # Temporarily merge light greylist from two files. Vendor list will become dark
 # grey once we remove the UI toast.
 $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST): frameworks/base/config/hiddenapi-light-greylist.txt \
-                                               frameworks/base/config/hiddenapi-vendor-list.txt
-	sort $^ > $@
+                                               frameworks/base/config/hiddenapi-vendor-list.txt \
+                                               $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE)
+	sort frameworks/base/config/hiddenapi-light-greylist.txt \
+	     frameworks/base/config/hiddenapi-vendor-list.txt \
+	     <(grep -E "\->("$(subst $(space),"|",$(REGEX_SERIALIZATION))")$$" $(PRIVATE_API)) \
+	> $@
 
 # Generate dark greylist as private API minus (blacklist plus light greylist).
 
diff --git a/api/current.txt b/api/current.txt
index d37d3d1..64b2d58 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -7041,7 +7041,8 @@
     method public int getBackoffPolicy();
     method public android.content.ClipData getClipData();
     method public int getClipGrantFlags();
-    method public long getEstimatedNetworkBytes();
+    method public long getEstimatedNetworkDownloadBytes();
+    method public long getEstimatedNetworkUploadBytes();
     method public android.os.PersistableBundle getExtras();
     method public long getFlexMillis();
     method public int getId();
@@ -7058,8 +7059,10 @@
     method public long getTriggerContentMaxDelay();
     method public long getTriggerContentUpdateDelay();
     method public android.app.job.JobInfo.TriggerContentUri[] getTriggerContentUris();
+    method public boolean isImportantWhileForeground();
     method public boolean isPeriodic();
     method public boolean isPersisted();
+    method public boolean isPrefetch();
     method public boolean isRequireBatteryNotLow();
     method public boolean isRequireCharging();
     method public boolean isRequireDeviceIdle();
@@ -7085,15 +7088,15 @@
     method public android.app.job.JobInfo build();
     method public android.app.job.JobInfo.Builder setBackoffCriteria(long, int);
     method public android.app.job.JobInfo.Builder setClipData(android.content.ClipData, int);
-    method public android.app.job.JobInfo.Builder setEstimatedNetworkBytes(long);
+    method public android.app.job.JobInfo.Builder setEstimatedNetworkBytes(long, long);
     method public android.app.job.JobInfo.Builder setExtras(android.os.PersistableBundle);
     method public android.app.job.JobInfo.Builder setImportantWhileForeground(boolean);
-    method public android.app.job.JobInfo.Builder setIsPrefetch(boolean);
     method public android.app.job.JobInfo.Builder setMinimumLatency(long);
     method public android.app.job.JobInfo.Builder setOverrideDeadline(long);
     method public android.app.job.JobInfo.Builder setPeriodic(long);
     method public android.app.job.JobInfo.Builder setPeriodic(long, long);
     method public android.app.job.JobInfo.Builder setPersisted(boolean);
+    method public android.app.job.JobInfo.Builder setPrefetch(boolean);
     method public android.app.job.JobInfo.Builder setRequiredNetwork(android.net.NetworkRequest);
     method public android.app.job.JobInfo.Builder setRequiredNetworkType(int);
     method public android.app.job.JobInfo.Builder setRequiresBatteryNotLow(boolean);
@@ -7163,10 +7166,11 @@
 
   public final class JobWorkItem implements android.os.Parcelable {
     ctor public JobWorkItem(android.content.Intent);
-    ctor public JobWorkItem(android.content.Intent, long);
+    ctor public JobWorkItem(android.content.Intent, long, long);
     method public int describeContents();
     method public int getDeliveryCount();
-    method public long getEstimatedNetworkBytes();
+    method public long getEstimatedNetworkDownloadBytes();
+    method public long getEstimatedNetworkUploadBytes();
     method public android.content.Intent getIntent();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.job.JobWorkItem> CREATOR;
@@ -24480,29 +24484,6 @@
     field public static final int RATING_THUMB_UP_DOWN = 2; // 0x2
   }
 
-  public final class Rating2 {
-    method public static android.media.Rating2 fromBundle(android.content.Context, android.os.Bundle);
-    method public float getPercentRating();
-    method public int getRatingStyle();
-    method public float getStarRating();
-    method public boolean hasHeart();
-    method public boolean isRated();
-    method public boolean isThumbUp();
-    method public static android.media.Rating2 newHeartRating(android.content.Context, boolean);
-    method public static android.media.Rating2 newPercentageRating(android.content.Context, float);
-    method public static android.media.Rating2 newStarRating(android.content.Context, int, float);
-    method public static android.media.Rating2 newThumbRating(android.content.Context, boolean);
-    method public static android.media.Rating2 newUnratedRating(android.content.Context, int);
-    method public android.os.Bundle toBundle();
-    field public static final int RATING_3_STARS = 3; // 0x3
-    field public static final int RATING_4_STARS = 4; // 0x4
-    field public static final int RATING_5_STARS = 5; // 0x5
-    field public static final int RATING_HEART = 1; // 0x1
-    field public static final int RATING_NONE = 0; // 0x0
-    field public static final int RATING_PERCENTAGE = 6; // 0x6
-    field public static final int RATING_THUMB_UP_DOWN = 2; // 0x2
-  }
-
   public deprecated class RemoteControlClient {
     ctor public RemoteControlClient(android.app.PendingIntent);
     ctor public RemoteControlClient(android.app.PendingIntent, android.os.Looper);
@@ -27278,6 +27259,7 @@
     method public static long getMobileTxBytes();
     method public static long getMobileTxPackets();
     method public static int getThreadStatsTag();
+    method public static int getThreadStatsUid();
     method public static long getTotalRxBytes();
     method public static long getTotalRxPackets();
     method public static long getTotalTxBytes();
@@ -27297,7 +27279,7 @@
     method public static void incrementOperationCount(int);
     method public static void incrementOperationCount(int, int);
     method public static void setThreadStatsTag(int);
-    method public static void setThreadStatsUidSelf();
+    method public static void setThreadStatsUid(int);
     method public static void tagDatagramSocket(java.net.DatagramSocket) throws java.net.SocketException;
     method public static void tagFileDescriptor(java.io.FileDescriptor) throws java.io.IOException;
     method public static void tagSocket(java.net.Socket) throws java.net.SocketException;
@@ -32058,7 +32040,6 @@
     ctor public Build.VERSION();
     field public static final java.lang.String BASE_OS;
     field public static final java.lang.String CODENAME;
-    field public static final int FIRST_SDK_INT;
     field public static final java.lang.String INCREMENTAL;
     field public static final int MIN_SUPPORTED_TARGET_SDK_INT;
     field public static final int PREVIEW_SDK_INT;
@@ -33094,8 +33075,6 @@
   }
 
   public final class SystemClock {
-    method public static java.time.Clock currentNetworkTimeClock();
-    method public static long currentNetworkTimeMillis();
     method public static long currentThreadTimeMillis();
     method public static long elapsedRealtime();
     method public static long elapsedRealtimeNanos();
@@ -41676,6 +41655,8 @@
 
   public abstract class CellIdentity implements android.os.Parcelable {
     method public int describeContents();
+    method public java.lang.CharSequence getOperatorAlphaLong();
+    method public java.lang.CharSequence getOperatorAlphaShort();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.telephony.CellIdentity> CREATOR;
   }
@@ -41685,8 +41666,6 @@
     method public int getLatitude();
     method public int getLongitude();
     method public int getNetworkId();
-    method public java.lang.CharSequence getOperatorAlphaLong();
-    method public java.lang.CharSequence getOperatorAlphaShort();
     method public int getSystemId();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.telephony.CellIdentityCdma> CREATOR;
@@ -41702,8 +41681,6 @@
     method public deprecated int getMnc();
     method public java.lang.String getMncString();
     method public java.lang.String getMobileNetworkOperator();
-    method public java.lang.CharSequence getOperatorAlphaLong();
-    method public java.lang.CharSequence getOperatorAlphaShort();
     method public deprecated int getPsc();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.telephony.CellIdentityGsm> CREATOR;
@@ -41718,8 +41695,6 @@
     method public deprecated int getMnc();
     method public java.lang.String getMncString();
     method public java.lang.String getMobileNetworkOperator();
-    method public java.lang.CharSequence getOperatorAlphaLong();
-    method public java.lang.CharSequence getOperatorAlphaShort();
     method public int getPci();
     method public int getTac();
     method public void writeToParcel(android.os.Parcel, int);
@@ -41744,8 +41719,6 @@
     method public deprecated int getMnc();
     method public java.lang.String getMncString();
     method public java.lang.String getMobileNetworkOperator();
-    method public java.lang.CharSequence getOperatorAlphaLong();
-    method public java.lang.CharSequence getOperatorAlphaShort();
     method public int getPsc();
     method public int getUarfcn();
     method public void writeToParcel(android.os.Parcel, int);
@@ -45071,18 +45044,6 @@
     field public static final deprecated boolean RELEASE = true;
   }
 
-  public class DataUnit extends java.lang.Enum {
-    method public long toBytes(long);
-    method public static android.util.DataUnit valueOf(java.lang.String);
-    method public static final android.util.DataUnit[] values();
-    enum_constant public static final android.util.DataUnit GIBIBYTES;
-    enum_constant public static final android.util.DataUnit GIGABYTES;
-    enum_constant public static final android.util.DataUnit KIBIBYTES;
-    enum_constant public static final android.util.DataUnit KILOBYTES;
-    enum_constant public static final android.util.DataUnit MEBIBYTES;
-    enum_constant public static final android.util.DataUnit MEGABYTES;
-  }
-
   public class DebugUtils {
     method public static boolean isObjectSelected(java.lang.Object);
   }
diff --git a/api/removed.txt b/api/removed.txt
index 1228fd1..4279c31 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -54,6 +54,24 @@
 
 }
 
+package android.app.job {
+
+  public class JobInfo implements android.os.Parcelable {
+    method public deprecated long getEstimatedNetworkBytes();
+  }
+
+  public static final class JobInfo.Builder {
+    method public deprecated android.app.job.JobInfo.Builder setEstimatedNetworkBytes(long);
+    method public deprecated android.app.job.JobInfo.Builder setIsPrefetch(boolean);
+  }
+
+  public final class JobWorkItem implements android.os.Parcelable {
+    ctor public deprecated JobWorkItem(android.content.Intent, long);
+    method public deprecated long getEstimatedNetworkBytes();
+  }
+
+}
+
 package android.app.usage {
 
   public final class StorageStats implements android.os.Parcelable {
@@ -261,6 +279,10 @@
     method public static deprecated org.apache.http.conn.ssl.SSLSocketFactory getHttpSocketFactory(int, android.net.SSLSessionCache);
   }
 
+  public class TrafficStats {
+    method public static deprecated void setThreadStatsUidSelf();
+  }
+
 }
 
 package android.os {
diff --git a/api/test-current.txt b/api/test-current.txt
index 85ed4b9..d323725 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -482,6 +482,7 @@
 package android.os {
 
   public static class Build.VERSION {
+    field public static final int FIRST_SDK_INT;
     field public static final int RESOURCES_SDK_INT;
   }
 
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 556709b..1aef0c4 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -206,7 +206,9 @@
     tests/e2e/GaugeMetric_e2e_push_test.cpp \
     tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp \
     tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp \
-    tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp
+    tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp \
+    tests/e2e/Anomaly_count_e2e_test.cpp \
+    tests/e2e/Anomaly_duration_sum_e2e_test.cpp
 
 LOCAL_STATIC_LIBRARIES := \
     $(statsd_common_static_libraries) \
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 1be4dc5..a07a355 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -69,6 +69,11 @@
     void dumpStates(FILE* out, bool verbose);
 
 private:
+    // For testing only.
+    inline sp<AlarmMonitor> getAnomalyAlarmMonitor() const {
+        return mAnomalyAlarmMonitor;
+    }
+
     mutable mutex mMetricsMutex;
 
     std::unordered_map<ConfigKey, sp<MetricsManager>> mMetricsManagers;
@@ -133,13 +138,15 @@
     FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_SimpleCondition);
     FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_PartialLink_SimpleCondition);
 
-
-
     FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_PartialLink_AND_CombinationCondition);
     FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_AND_CombinationCondition);
     FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_AND_CombinationCondition);
 
-
+    FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_single_bucket);
+    FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_multiple_buckets);
+    FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket);
+    FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets);
+    FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
index 49de1ac..f0960e3 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
@@ -66,6 +66,9 @@
 
 void AnomalyTracker::advanceMostRecentBucketTo(const int64_t& bucketNum) {
     VLOG("advanceMostRecentBucketTo() called.");
+    if (mNumOfPastBuckets <= 0) {
+        return;
+    }
     if (bucketNum <= mMostRecentBucketNum) {
         ALOGW("Cannot advance buckets backwards (bucketNum=%lld but mMostRecentBucketNum=%lld)",
               (long long)bucketNum, (long long)mMostRecentBucketNum);
@@ -170,7 +173,8 @@
 
 int64_t AnomalyTracker::getPastBucketValue(const MetricDimensionKey& key,
                                            const int64_t& bucketNum) const {
-    if (bucketNum < 0 || bucketNum <= mMostRecentBucketNum - mNumOfPastBuckets
+    if (bucketNum < 0 || mMostRecentBucketNum < 0
+            || bucketNum <= mMostRecentBucketNum - mNumOfPastBuckets
             || bucketNum > mMostRecentBucketNum) {
         return 0;
     }
@@ -241,14 +245,10 @@
 }
 
 bool AnomalyTracker::isInRefractoryPeriod(const uint64_t& timestampNs,
-                                          const MetricDimensionKey& key) {
+                                          const MetricDimensionKey& key) const {
     const auto& it = mRefractoryPeriodEndsSec.find(key);
     if (it != mRefractoryPeriodEndsSec.end()) {
-        if (timestampNs < it->second * NS_PER_SEC) {
-            return true;
-        } else {
-            mRefractoryPeriodEndsSec.erase(key);
-        }
+        return timestampNs < it->second * NS_PER_SEC;
     }
     return false;
 }
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.h b/cmds/statsd/src/anomaly/AnomalyTracker.h
index d3da7dc..ae0af64 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.h
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.h
@@ -113,6 +113,13 @@
     }
 
 protected:
+    // For testing only.
+    // Returns the alarm timestamp in seconds for the query dimension if it exists. Otherwise
+    // returns 0.
+    virtual uint32_t getAlarmTimestampSec(const MetricDimensionKey& dimensionKey) const {
+        return 0;   // The base AnomalyTracker class doesn't have alarms.
+    }
+
     // statsd_config.proto Alert message that defines this tracker.
     const Alert mAlert;
 
@@ -159,8 +166,7 @@
     void subtractValueFromSum(const MetricDimensionKey& key, const int64_t& bucketValue);
 
     // Returns true if in the refractory period, else false.
-    // If there is a stored refractory period but it ended prior to timestampNs, it is removed.
-    bool isInRefractoryPeriod(const uint64_t& timestampNs, const MetricDimensionKey& key);
+    bool isInRefractoryPeriod(const uint64_t& timestampNs, const MetricDimensionKey& key) const;
 
     // Calculates the corresponding bucket index within the circular array.
     // Requires bucketNum >= 0.
@@ -176,6 +182,9 @@
     FRIEND_TEST(AnomalyTrackerTest, TestSparseBuckets);
     FRIEND_TEST(GaugeMetricProducerTest, TestAnomalyDetection);
     FRIEND_TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced);
+    FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket);
+    FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets);
+    FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
index 79067eb..cdc4251 100644
--- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
@@ -38,11 +38,10 @@
 void DurationAnomalyTracker::startAlarm(const MetricDimensionKey& dimensionKey,
                                         const uint64_t& timestampNs) {
     // Alarms are stored in secs. Must round up, since if it fires early, it is ignored completely.
-    uint32_t timestampSec = static_cast<uint32_t>((timestampNs -1)/ NS_PER_SEC) + 1; // round up
+    uint32_t timestampSec = static_cast<uint32_t>((timestampNs -1) / NS_PER_SEC) + 1; // round up
     if (isInRefractoryPeriod(timestampNs, dimensionKey)) {
-        // TODO: Bug! By the refractory's end, the data might be erased and the alarm inapplicable.
-        VLOG("Setting a delayed anomaly alarm lest it fall in the refractory period");
-        timestampSec = getRefractoryPeriodEndsSec(dimensionKey) + 1;
+        VLOG("Not setting anomaly alarm since it would fall in the refractory period.");
+        return;
     }
 
     auto itr = mAlarms.find(dimensionKey);
diff --git a/cmds/statsd/src/anomaly/DurationAnomalyTracker.h b/cmds/statsd/src/anomaly/DurationAnomalyTracker.h
index 92bb2bc..53155d9 100644
--- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.h
+++ b/cmds/statsd/src/anomaly/DurationAnomalyTracker.h
@@ -52,6 +52,13 @@
             unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& firedAlarms) override;
 
 protected:
+    // Returns the alarm timestamp in seconds for the query dimension if it exists. Otherwise
+    // returns 0.
+    uint32_t getAlarmTimestampSec(const MetricDimensionKey& dimensionKey) const override {
+        auto it = mAlarms.find(dimensionKey);
+        return it == mAlarms.end() ? 0 : it->second->timestampSec;
+    }
+
     // The alarms owned by this tracker. The alarm monitor also shares the alarm pointers when they
     // are still active.
     std::unordered_map<MetricDimensionKey, sp<const InternalAlarm>> mAlarms;
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index ea45f43..4983f96 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -188,7 +188,7 @@
 
     // Convenience to compute the current bucket's end time, which is always aligned with the
     // start time of the metric.
-    uint64_t getCurrentBucketEndTimeNs() {
+    uint64_t getCurrentBucketEndTimeNs() const {
         return mStartTimeNs + (mCurrentBucketNum + 1) * mBucketSizeNs;
     }
 
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 46a9b34..05ce84d 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -178,6 +178,12 @@
     FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_AND_CombinationCondition);
     FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_PartialLink_AND_CombinationCondition);
 
+    FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_single_bucket);
+    FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_multiple_buckets);
+    FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket);
+    FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets);
+    FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period);
+
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
index 991a76a..ddfb8cc 100644
--- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
@@ -109,7 +109,7 @@
 
     // Predict the anomaly timestamp given the current status.
     virtual int64_t predictAnomalyTimestampNs(const DurationAnomalyTracker& anomalyTracker,
-                                              const uint64_t currentTimestamp) const = 0;
+                                              const int64_t currentTimestamp) const = 0;
     // Dump internal states for debugging
     virtual void dumpStates(FILE* out, bool verbose) const = 0;
 
@@ -118,12 +118,19 @@
     }
 
 protected:
+    uint64_t getCurrentBucketEndTimeNs() const {
+        return mStartTimeNs + (mCurrentBucketNum + 1) * mBucketSizeNs;
+    }
+
     // Starts the anomaly alarm.
     void startAnomalyAlarm(const uint64_t eventTime) {
         for (auto& anomalyTracker : mAnomalyTrackers) {
             if (anomalyTracker != nullptr) {
-                anomalyTracker->startAlarm(mEventKey,
-                                           predictAnomalyTimestampNs(*anomalyTracker, eventTime));
+                const uint64_t alarmTimestampNs =
+                    predictAnomalyTimestampNs(*anomalyTracker, eventTime);
+                if (alarmTimestampNs > 0) {
+                    anomalyTracker->startAlarm(mEventKey, alarmTimestampNs);
+                }
             }
         }
     }
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
index c9547cf..df9e6ae 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
@@ -313,7 +313,7 @@
 }
 
 int64_t MaxDurationTracker::predictAnomalyTimestampNs(const DurationAnomalyTracker& anomalyTracker,
-                                                      const uint64_t currentTimestamp) const {
+                                                      const int64_t currentTimestamp) const {
     // The allowed time we can continue in the current state is the
     // (anomaly threshold) - max(elapsed time of the started mInfos).
     int64_t maxElapsed = 0;
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
index 0452d37..32d42fa 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
@@ -57,7 +57,7 @@
     void onConditionChanged(bool condition, const uint64_t timestamp) override;
 
     int64_t predictAnomalyTimestampNs(const DurationAnomalyTracker& anomalyTracker,
-                                      const uint64_t currentTimestamp) const override;
+                                      const int64_t currentTimestamp) const override;
     void dumpStates(FILE* out, bool verbose) const override;
 
 private:
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
index b418a85..da79217 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
@@ -166,13 +166,13 @@
         current_info.mDuration = mDuration;
         (*output)[mEventKey].push_back(current_info);
         mDurationFullBucket += mDuration;
-        if (eventTimeNs > fullBucketEnd) {
-            // End of full bucket, can send to anomaly tracker now.
-            addPastBucketToAnomalyTrackers(mDurationFullBucket, mCurrentBucketNum);
-            mDurationFullBucket = 0;
-        }
         VLOG("  duration: %lld", (long long)current_info.mDuration);
     }
+    if (eventTimeNs > fullBucketEnd) {
+        // End of full bucket, can send to anomaly tracker now.
+        addPastBucketToAnomalyTrackers(mDurationFullBucket, mCurrentBucketNum);
+        mDurationFullBucket = 0;
+    }
 
     if (mStarted.size() > 0) {
         for (int i = 1; i < numBucketsForward; i++) {
@@ -186,6 +186,10 @@
             addPastBucketToAnomalyTrackers(info.mDuration, mCurrentBucketNum + i);
             VLOG("  add filling bucket with duration %lld", (long long)info.mDuration);
         }
+    } else {
+        if (numBucketsForward >= 2) {
+            addPastBucketToAnomalyTrackers(0, mCurrentBucketNum + numBucketsForward - 1);
+        }
     }
 
     mDuration = 0;
@@ -320,57 +324,84 @@
 }
 
 int64_t OringDurationTracker::predictAnomalyTimestampNs(
-        const DurationAnomalyTracker& anomalyTracker, const uint64_t eventTimestampNs) const {
+        const DurationAnomalyTracker& anomalyTracker, const int64_t eventTimestampNs) const {
     // TODO: Unit-test this and see if it can be done more efficiently (e.g. use int32).
-    // All variables below represent durations (not timestamps).
 
+    // The anomaly threshold.
     const int64_t thresholdNs = anomalyTracker.getAnomalyThreshold();
 
-    // The time until the current bucket ends. This is how much more 'space' it can hold.
-    const int64_t currRemainingBucketSizeNs =
-            mBucketSizeNs - (eventTimestampNs - mCurrentBucketStartTimeNs);
-    if (currRemainingBucketSizeNs < 0) {
-        ALOGE("OringDurationTracker currRemainingBucketSizeNs < 0");
-        // This should never happen. Return the safest thing possible given that data is corrupt.
-        return eventTimestampNs + thresholdNs;
-    }
+    // The timestamp of the current bucket end.
+    const int64_t currentBucketEndNs = getCurrentBucketEndTimeNs();
+
+    // The past duration ns for the current bucket.
+    int64_t currentBucketPastNs = mDuration + mDurationFullBucket;
 
     // As we move into the future, old buckets get overwritten (so their old data is erased).
-
     // Sum of past durations. Will change as we overwrite old buckets.
-    int64_t pastNs = mDuration + mDurationFullBucket;
-    pastNs += anomalyTracker.getSumOverPastBuckets(mEventKey);
+    int64_t pastNs = currentBucketPastNs + anomalyTracker.getSumOverPastBuckets(mEventKey);
 
-    // How much of the threshold is still unaccounted after considering pastNs.
-    int64_t leftNs = thresholdNs - pastNs;
+    // The refractory period end timestamp for dimension mEventKey.
+    const int64_t refractoryPeriodEndNs =
+            anomalyTracker.getRefractoryPeriodEndsSec(mEventKey) * NS_PER_SEC;
 
-    // First deal with the remainder of the current bucket.
-    if (leftNs <= currRemainingBucketSizeNs) {  // Predict the anomaly will occur in this bucket.
-        return eventTimestampNs + leftNs;
+    // The anomaly should happen when accumulated wakelock duration is above the threshold and
+    // not within the refractory period.
+    int64_t anomalyTimestampNs =
+        std::max(eventTimestampNs + thresholdNs - pastNs, refractoryPeriodEndNs);
+    // If the predicted the anomaly timestamp is within the current bucket, return it directly.
+    if (anomalyTimestampNs <= currentBucketEndNs) {
+        return std::max(eventTimestampNs, anomalyTimestampNs);
     }
-    // The remainder of this bucket contributes, but we must then move to the next bucket.
-    pastNs += currRemainingBucketSizeNs;
 
-    // Now deal with the past buckets, starting with the oldest.
-    for (int futBucketIdx = 0; futBucketIdx < anomalyTracker.getNumOfPastBuckets();
-         futBucketIdx++) {
-        // We now overwrite the oldest bucket with the previous 'current', and start a new
-        // 'current'.
+    // Remove the old bucket.
+    if (anomalyTracker.getNumOfPastBuckets() > 0) {
         pastNs -= anomalyTracker.getPastBucketValue(
-                mEventKey, mCurrentBucketNum - anomalyTracker.getNumOfPastBuckets() + futBucketIdx);
-        leftNs = thresholdNs - pastNs;
-        if (leftNs <= mBucketSizeNs) {  // Predict anomaly will occur in this bucket.
-            return eventTimestampNs + currRemainingBucketSizeNs + (futBucketIdx * mBucketSizeNs) +
-                   leftNs;
-        } else {  // This bucket would be entirely filled, and we'll need to move to the next
-                  // bucket.
-            pastNs += mBucketSizeNs;
+                            mEventKey,
+                            mCurrentBucketNum - anomalyTracker.getNumOfPastBuckets());
+        // Add the remaining of the current bucket to the accumulated wakelock duration.
+        pastNs += (currentBucketEndNs - eventTimestampNs);
+    } else {
+        // The anomaly depends on only one bucket.
+        pastNs = 0;
+    }
+
+    // The anomaly will not happen in the current bucket. We need to iterate over the future buckets
+    // to predict the accumulated wakelock duration and determine the anomaly timestamp accordingly.
+    for (int futureBucketIdx = 1; futureBucketIdx <= anomalyTracker.getNumOfPastBuckets() + 1;
+            futureBucketIdx++) {
+        // The alarm candidate timestamp should meet two requirements:
+        // 1. the accumulated wakelock duration is above the threshold.
+        // 2. it is not within the refractory period.
+        // 3. the alarm timestamp falls in this bucket. Otherwise we need to flush the past buckets,
+        //    find the new alarm candidate timestamp and check these requirements again.
+        const int64_t bucketEndNs = currentBucketEndNs + futureBucketIdx * mBucketSizeNs;
+        int64_t anomalyTimestampNs =
+            std::max(bucketEndNs - mBucketSizeNs + thresholdNs - pastNs, refractoryPeriodEndNs);
+        if (anomalyTimestampNs <= bucketEndNs) {
+            return anomalyTimestampNs;
+        }
+        if (anomalyTracker.getNumOfPastBuckets() <= 0) {
+            continue;
+        }
+
+        // No valid alarm timestamp is found in this bucket. The clock moves to the end of the
+        // bucket. Update the pastNs.
+        pastNs += mBucketSizeNs;
+        // 1. If the oldest past bucket is still in the past bucket window, we could fetch the past
+        // bucket and erase it from pastNs.
+        // 2. If the oldest past bucket is the current bucket, we should compute the
+        //   wakelock duration in the current bucket and erase it from pastNs.
+        // 3. Otherwise all othe past buckets are ancient.
+        if (futureBucketIdx < anomalyTracker.getNumOfPastBuckets()) {
+            pastNs -= anomalyTracker.getPastBucketValue(
+                    mEventKey,
+                    mCurrentBucketNum - anomalyTracker.getNumOfPastBuckets() + futureBucketIdx);
+        } else if (futureBucketIdx == anomalyTracker.getNumOfPastBuckets()) {
+            pastNs -= (currentBucketPastNs + (currentBucketEndNs - eventTimestampNs));
         }
     }
 
-    // If we have reached this point, we even have to overwrite the the original current bucket.
-    // Thus, none of the past data will still be extant - pastNs is now 0.
-    return eventTimestampNs + thresholdNs;
+    return std::max(eventTimestampNs + thresholdNs, refractoryPeriodEndNs);
 }
 
 void OringDurationTracker::dumpStates(FILE* out, bool verbose) const {
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
index 610e3ea..ca8abfe 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
@@ -56,7 +56,7 @@
             std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) override;
 
     int64_t predictAnomalyTimestampNs(const DurationAnomalyTracker& anomalyTracker,
-                                      const uint64_t currentTimestamp) const override;
+                                      const int64_t currentTimestamp) const override;
     void dumpStates(FILE* out, bool verbose) const override;
 
 private:
diff --git a/cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp b/cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp
new file mode 100644
index 0000000..93ecde5
--- /dev/null
+++ b/cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp
@@ -0,0 +1,241 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <gtest/gtest.h>
+
+#include "src/StatsLogProcessor.h"
+#include "src/stats_log_util.h"
+#include "tests/statsd_test_util.h"
+
+#include <vector>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+#ifdef __ANDROID__
+
+namespace {
+
+StatsdConfig CreateStatsdConfig(int num_buckets, int threshold) {
+    StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+    auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
+
+    *config.add_atom_matcher() = wakelockAcquireMatcher;
+
+    auto countMetric = config.add_count_metric();
+    countMetric->set_id(123456);
+    countMetric->set_what(wakelockAcquireMatcher.id());
+    *countMetric->mutable_dimensions_in_what() = CreateAttributionUidDimensions(
+            android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+    countMetric->set_bucket(FIVE_MINUTES);
+
+    auto alert = config.add_alert();
+    alert->set_id(StringToId("alert"));
+    alert->set_metric_id(123456);
+    alert->set_num_buckets(num_buckets);
+    alert->set_refractory_period_secs(10);
+    alert->set_trigger_if_sum_gt(threshold);
+    return config;
+}
+
+}  // namespace
+
+TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_single_bucket) {
+    const int num_buckets = 1;
+    const int threshold = 3;
+    auto config = CreateStatsdConfig(num_buckets, threshold);
+    const uint64_t alert_id = config.alert(0).id();
+    const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs();
+
+    int64_t bucketStartTimeNs = 10000000000;
+    int64_t bucketSizeNs =
+        TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
+
+    ConfigKey cfgKey;
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+    EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
+
+    sp<AnomalyTracker> anomalyTracker =
+        processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
+
+    std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
+    std::vector<AttributionNodeInternal> attributions2 = {
+        CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1")};
+    std::vector<AttributionNodeInternal> attributions3 = {
+        CreateAttribution(111, "App1"), CreateAttribution(333, "App3")};
+    std::vector<AttributionNodeInternal> attributions4 = {
+        CreateAttribution(222, "GMSCoreModule1"), CreateAttribution(333, "App3")};
+    std::vector<AttributionNodeInternal> attributions5 = {
+        CreateAttribution(222, "GMSCoreModule1") };
+
+    FieldValue fieldValue1(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
+                           Value((int32_t)111));
+    HashableDimensionKey whatKey1({fieldValue1});
+    MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY);
+
+    FieldValue fieldValue2(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
+                           Value((int32_t)222));
+    HashableDimensionKey whatKey2({fieldValue2});
+    MetricDimensionKey dimensionKey2(whatKey2, DEFAULT_DIMENSION_KEY);
+
+    auto event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 2);
+    processor->OnLogEvent(event.get());
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    event = CreateAcquireWakelockEvent(attributions4, "wl2", bucketStartTimeNs + 2);
+    processor->OnLogEvent(event.get());
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
+
+    event = CreateAcquireWakelockEvent(attributions2, "wl1", bucketStartTimeNs + 3);
+    processor->OnLogEvent(event.get());
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    event = CreateAcquireWakelockEvent(attributions5, "wl2", bucketStartTimeNs + 3);
+    processor->OnLogEvent(event.get());
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
+
+    event = CreateAcquireWakelockEvent(attributions3, "wl1", bucketStartTimeNs + 4);
+    processor->OnLogEvent(event.get());
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    event = CreateAcquireWakelockEvent(attributions5, "wl2", bucketStartTimeNs + 4);
+    processor->OnLogEvent(event.get());
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
+
+    // Fired alarm and refractory period end timestamp updated.
+    event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 5);
+    processor->OnLogEvent(event.get());
+    EXPECT_EQ(refractory_period_sec + bucketStartTimeNs / NS_PER_SEC + 1,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 100);
+    processor->OnLogEvent(event.get());
+    EXPECT_EQ(refractory_period_sec + bucketStartTimeNs / NS_PER_SEC + 1,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + bucketSizeNs - 1);
+    processor->OnLogEvent(event.get());
+    EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs - 1) / NS_PER_SEC + 1,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 1);
+    processor->OnLogEvent(event.get());
+    EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs - 1) / NS_PER_SEC + 1,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    event = CreateAcquireWakelockEvent(attributions4, "wl2", bucketStartTimeNs + bucketSizeNs + 1);
+    processor->OnLogEvent(event.get());
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
+
+    event = CreateAcquireWakelockEvent(attributions5, "wl2", bucketStartTimeNs + bucketSizeNs + 2);
+    processor->OnLogEvent(event.get());
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
+
+    event = CreateAcquireWakelockEvent(attributions5, "wl2", bucketStartTimeNs + bucketSizeNs + 3);
+    processor->OnLogEvent(event.get());
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
+
+    event = CreateAcquireWakelockEvent(attributions5, "wl2", bucketStartTimeNs + bucketSizeNs + 4);
+    processor->OnLogEvent(event.get());
+    EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 4) / NS_PER_SEC + 1,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
+}
+
+TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_multiple_buckets) {
+    const int num_buckets = 3;
+    const int threshold = 3;
+    auto config = CreateStatsdConfig(num_buckets, threshold);
+    const uint64_t alert_id = config.alert(0).id();
+    const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs();
+
+    int64_t bucketStartTimeNs = 10000000000;
+    int64_t bucketSizeNs =
+        TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
+
+    ConfigKey cfgKey;
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+    EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
+
+    sp<AnomalyTracker> anomalyTracker =
+        processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
+
+    std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
+    std::vector<AttributionNodeInternal> attributions2 = {
+        CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1")};
+    std::vector<AttributionNodeInternal> attributions3 = {
+        CreateAttribution(111, "App1"), CreateAttribution(333, "App3")};
+    std::vector<AttributionNodeInternal> attributions4 = {
+        CreateAttribution(222, "GMSCoreModule1"), CreateAttribution(333, "App3")};
+    std::vector<AttributionNodeInternal> attributions5 = {
+        CreateAttribution(222, "GMSCoreModule1") };
+
+    FieldValue fieldValue1(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
+                           Value((int32_t)111));
+    HashableDimensionKey whatKey1({fieldValue1});
+    MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY);
+
+    FieldValue fieldValue2(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
+                           Value((int32_t)222));
+    HashableDimensionKey whatKey2({fieldValue2});
+    MetricDimensionKey dimensionKey2(whatKey2, DEFAULT_DIMENSION_KEY);
+
+    auto event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 2);
+    processor->OnLogEvent(event.get());
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    event = CreateAcquireWakelockEvent(attributions2, "wl1", bucketStartTimeNs + 3);
+    processor->OnLogEvent(event.get());
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    // Fired alarm and refractory period end timestamp updated.
+    event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 4);
+    processor->OnLogEvent(event.get());
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 1);
+    processor->OnLogEvent(event.get());
+    EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 1) / NS_PER_SEC + 1,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    event = CreateAcquireWakelockEvent(attributions2, "wl1", bucketStartTimeNs + bucketSizeNs + 2);
+    processor->OnLogEvent(event.get());
+    EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 1) / NS_PER_SEC + 1,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    event = CreateAcquireWakelockEvent(
+        attributions2, "wl1", bucketStartTimeNs + 3 * bucketSizeNs + 1);
+    processor->OnLogEvent(event.get());
+    EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 1) / NS_PER_SEC + 1,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    event = CreateAcquireWakelockEvent(
+        attributions2, "wl1", bucketStartTimeNs + 3 * bucketSizeNs + 2);
+    processor->OnLogEvent(event.get());
+    EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + 3 * bucketSizeNs + 2) / NS_PER_SEC + 1,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp b/cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp
new file mode 100644
index 0000000..e924b03
--- /dev/null
+++ b/cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp
@@ -0,0 +1,486 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <gtest/gtest.h>
+
+#include "src/anomaly/DurationAnomalyTracker.h"
+#include "src/StatsLogProcessor.h"
+#include "src/stats_log_util.h"
+#include "tests/statsd_test_util.h"
+
+#include <vector>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+#ifdef __ANDROID__
+
+namespace {
+
+StatsdConfig CreateStatsdConfig(int num_buckets,
+                                uint64_t threshold_ns,
+                                DurationMetric::AggregationType aggregationType,
+                                bool nesting) {
+    StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+    *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
+    *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
+
+    auto screenIsOffPredicate = CreateScreenIsOffPredicate();
+    *config.add_predicate() = screenIsOffPredicate;
+
+    auto holdingWakelockPredicate = CreateHoldingWakelockPredicate();
+    FieldMatcher dimensions = CreateAttributionUidDimensions(
+            android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+    dimensions.add_child()->set_field(3);  // The wakelock tag is set in field 3 of the wakelock.
+    *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = dimensions;
+    holdingWakelockPredicate.mutable_simple_predicate()->set_count_nesting(nesting);
+    *config.add_predicate() = holdingWakelockPredicate;
+
+    auto durationMetric = config.add_duration_metric();
+    durationMetric->set_id(StringToId("WakelockDuration"));
+    durationMetric->set_what(holdingWakelockPredicate.id());
+    durationMetric->set_condition(screenIsOffPredicate.id());
+    durationMetric->set_aggregation_type(aggregationType);
+    *durationMetric->mutable_dimensions_in_what() =
+        CreateAttributionUidDimensions(android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+    durationMetric->set_bucket(FIVE_MINUTES);
+
+    auto alert = config.add_alert();
+    alert->set_id(StringToId("alert"));
+    alert->set_metric_id(StringToId("WakelockDuration"));
+    alert->set_num_buckets(num_buckets);
+    alert->set_refractory_period_secs(2);
+    alert->set_trigger_if_sum_gt(threshold_ns);
+    return config;
+}
+
+}  // namespace
+
+std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1"),
+                                                      CreateAttribution(222, "GMSCoreModule1")};
+
+std::vector<AttributionNodeInternal> attributions2 = {CreateAttribution(111, "App2"),
+                                                      CreateAttribution(222, "GMSCoreModule1")};
+
+std::vector<AttributionNodeInternal> attributions3 = {CreateAttribution(222, "GMSCoreModule1")};
+
+MetricDimensionKey dimensionKey(
+    HashableDimensionKey({FieldValue(Field(android::util::WAKELOCK_STATE_CHANGED,
+                                           (int32_t)0x02010101), Value((int32_t)111))}),
+    DEFAULT_DIMENSION_KEY);
+
+MetricDimensionKey dimensionKey2(
+    HashableDimensionKey({FieldValue(Field(android::util::WAKELOCK_STATE_CHANGED,
+                                           (int32_t)0x02010101), Value((int32_t)222))}),
+    DEFAULT_DIMENSION_KEY);
+
+TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket) {
+    const int num_buckets = 1;
+    const uint64_t threshold_ns = NS_PER_SEC;
+    auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, true);
+    const uint64_t alert_id = config.alert(0).id();
+    const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs();
+
+    int64_t bucketStartTimeNs = 10 * NS_PER_SEC;
+    int64_t bucketSizeNs =
+        TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000;
+
+    ConfigKey cfgKey;
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+    EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
+
+    sp<AnomalyTracker> anomalyTracker =
+        processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
+
+    auto screen_on_event = CreateScreenStateChangedEvent(
+            android::view::DisplayStateEnum::DISPLAY_STATE_ON, bucketStartTimeNs + 1);
+    auto screen_off_event = CreateScreenStateChangedEvent(
+            android::view::DisplayStateEnum::DISPLAY_STATE_OFF, bucketStartTimeNs + 10);
+    processor->OnLogEvent(screen_on_event.get());
+    processor->OnLogEvent(screen_off_event.get());
+
+    // Acquire wakelock wl1.
+    auto acquire_event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 11);
+    processor->OnLogEvent(acquire_event.get());
+    EXPECT_EQ((bucketStartTimeNs + 11 + threshold_ns) / NS_PER_SEC + 1,
+              anomalyTracker->getAlarmTimestampSec(dimensionKey));
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+    // Release wakelock wl1. No anomaly detected. Alarm cancelled at the "release" event.
+    auto release_event = CreateReleaseWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 101);
+    processor->OnLogEvent(release_event.get());
+    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+    // Acquire wakelock wl1 within bucket #0.
+    acquire_event = CreateAcquireWakelockEvent(attributions2, "wl1", bucketStartTimeNs + 110);
+    processor->OnLogEvent(acquire_event.get());
+    EXPECT_EQ((bucketStartTimeNs + 110 + threshold_ns - 90) / NS_PER_SEC + 1,
+              anomalyTracker->getAlarmTimestampSec(dimensionKey));
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+    // Release wakelock wl1. One anomaly detected.
+    release_event = CreateReleaseWakelockEvent(
+            attributions2, "wl1", bucketStartTimeNs + NS_PER_SEC + 109);
+    processor->OnLogEvent(release_event.get());
+    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+    EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + NS_PER_SEC + 109) / NS_PER_SEC + 1,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+    // Acquire wakelock wl1.
+    acquire_event = CreateAcquireWakelockEvent(
+        attributions1, "wl1", bucketStartTimeNs + NS_PER_SEC + 112);
+    processor->OnLogEvent(acquire_event.get());
+    // Wakelock has been hold longer than the threshold in bucket #0. The alarm is set at the
+    // end of the refractory period.
+    const int64_t alarmFiredTimestampSec0 = anomalyTracker->getAlarmTimestampSec(dimensionKey);
+    EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + NS_PER_SEC + 109) / NS_PER_SEC + 1,
+              (uint32_t)alarmFiredTimestampSec0);
+
+    // Anomaly alarm fired.
+    auto alarmSet = processor->getAnomalyAlarmMonitor()->popSoonerThan(
+            static_cast<uint32_t>(alarmFiredTimestampSec0));
+    EXPECT_EQ(1u, alarmSet.size());
+    processor->onAnomalyAlarmFired(alarmFiredTimestampSec0 * NS_PER_SEC, alarmSet);
+    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+    EXPECT_EQ(refractory_period_sec + alarmFiredTimestampSec0,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+    // Release wakelock wl1.
+    release_event = CreateReleaseWakelockEvent(
+            attributions1, "wl1", alarmFiredTimestampSec0 * NS_PER_SEC + NS_PER_SEC + 1);
+    processor->OnLogEvent(release_event.get());
+    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+    // Within refractory period. No more anomaly detected.
+    EXPECT_EQ(refractory_period_sec + alarmFiredTimestampSec0,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+    // Acquire wakelock wl1.
+    acquire_event = CreateAcquireWakelockEvent(
+        attributions2, "wl1", bucketStartTimeNs + bucketSizeNs -  5 * NS_PER_SEC - 11);
+    processor->OnLogEvent(acquire_event.get());
+    const int64_t alarmFiredTimestampSec1 = anomalyTracker->getAlarmTimestampSec(dimensionKey);
+    EXPECT_EQ((bucketStartTimeNs + bucketSizeNs - 5 * NS_PER_SEC) / NS_PER_SEC,
+              (uint64_t)alarmFiredTimestampSec1);
+
+    // Release wakelock wl1.
+    release_event = CreateReleaseWakelockEvent(
+        attributions2, "wl1", bucketStartTimeNs + bucketSizeNs - 4 * NS_PER_SEC - 10);
+    processor->OnLogEvent(release_event.get());
+    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+    EXPECT_EQ(refractory_period_sec +
+                    (bucketStartTimeNs + bucketSizeNs - 4 * NS_PER_SEC - 10) / NS_PER_SEC + 1,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+    alarmSet = processor->getAnomalyAlarmMonitor()->popSoonerThan(
+                static_cast<uint32_t>(alarmFiredTimestampSec1));
+    EXPECT_EQ(0u, alarmSet.size());
+
+    // Acquire wakelock wl1 near the end of bucket #0.
+    acquire_event = CreateAcquireWakelockEvent(
+            attributions1, "wl1", bucketStartTimeNs + bucketSizeNs - 2);
+    processor->OnLogEvent(acquire_event.get());
+    EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC,
+               anomalyTracker->getAlarmTimestampSec(dimensionKey));
+
+    // Release the event at early bucket #1.
+    release_event = CreateReleaseWakelockEvent(
+            attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + NS_PER_SEC - 1);
+    processor->OnLogEvent(release_event.get());
+    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+    // Anomaly detected when stopping the alarm. The refractory period does not change.
+    EXPECT_EQ(refractory_period_sec +
+                    (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+    // Condition changes to false.
+    screen_on_event = CreateScreenStateChangedEvent(
+        android::view::DisplayStateEnum::DISPLAY_STATE_ON,
+        bucketStartTimeNs + 2 * bucketSizeNs + 20);
+    processor->OnLogEvent(screen_on_event.get());
+    EXPECT_EQ(refractory_period_sec +
+                    (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+
+    acquire_event = CreateAcquireWakelockEvent(
+        attributions2, "wl1", bucketStartTimeNs + 2 * bucketSizeNs + 30);
+    processor->OnLogEvent(acquire_event.get());
+    // The condition is false. Do not start the alarm.
+    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+    EXPECT_EQ(refractory_period_sec +
+                    (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+    // Condition turns true.
+    screen_off_event = CreateScreenStateChangedEvent(
+        android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
+        bucketStartTimeNs + 2 * bucketSizeNs + NS_PER_SEC);
+    processor->OnLogEvent(screen_off_event.get());
+    EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs + NS_PER_SEC + threshold_ns) / NS_PER_SEC,
+              anomalyTracker->getAlarmTimestampSec(dimensionKey));
+
+    // Condition turns to false.
+    screen_on_event = CreateScreenStateChangedEvent(
+        android::view::DisplayStateEnum::DISPLAY_STATE_ON,
+        bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 1);
+    processor->OnLogEvent(screen_on_event.get());
+    // Condition turns to false. Cancelled the alarm.
+    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+    //  Detected one anomaly.
+    EXPECT_EQ(refractory_period_sec +
+                    (bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 1) / NS_PER_SEC + 1,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+    // Condition turns to true again.
+    screen_off_event = CreateScreenStateChangedEvent(
+        android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
+        bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 2);
+    processor->OnLogEvent(screen_off_event.get());
+    EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 2 + 2 + 1,
+              anomalyTracker->getAlarmTimestampSec(dimensionKey));
+
+    release_event = CreateReleaseWakelockEvent(
+        attributions2, "wl1", bucketStartTimeNs + 2 * bucketSizeNs + 5 * NS_PER_SEC);
+    processor->OnLogEvent(release_event.get());
+    EXPECT_EQ(refractory_period_sec +
+                    (bucketStartTimeNs + 2 * bucketSizeNs + 5 * NS_PER_SEC) / NS_PER_SEC,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+}
+
+TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets) {
+    const int num_buckets = 3;
+    const uint64_t threshold_ns = NS_PER_SEC;
+    auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, true);
+    const uint64_t alert_id = config.alert(0).id();
+    const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs();
+
+    int64_t bucketStartTimeNs = 10 * NS_PER_SEC;
+    int64_t bucketSizeNs =
+        TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000;
+
+    ConfigKey cfgKey;
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+    EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
+
+    sp<AnomalyTracker> anomalyTracker =
+        processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
+
+    auto screen_off_event = CreateScreenStateChangedEvent(
+            android::view::DisplayStateEnum::DISPLAY_STATE_OFF, bucketStartTimeNs + 1);
+    processor->OnLogEvent(screen_off_event.get());
+
+    // Acquire wakelock "wc1" in bucket #0.
+    auto acquire_event = CreateAcquireWakelockEvent(
+        attributions1, "wl1", bucketStartTimeNs + bucketSizeNs -  NS_PER_SEC / 2 - 1);
+    processor->OnLogEvent(acquire_event.get());
+    EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 1,
+              anomalyTracker->getAlarmTimestampSec(dimensionKey));
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+    // Release wakelock "wc1" in bucket #0.
+    auto release_event = CreateReleaseWakelockEvent(
+        attributions1, "wl1", bucketStartTimeNs + bucketSizeNs - 1);
+    processor->OnLogEvent(release_event.get());
+    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+    // Acquire wakelock "wc1" in bucket #1.
+    acquire_event = CreateAcquireWakelockEvent(
+        attributions2, "wl1", bucketStartTimeNs + bucketSizeNs + 1);
+    processor->OnLogEvent(acquire_event.get());
+    EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 1,
+              anomalyTracker->getAlarmTimestampSec(dimensionKey));
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+    release_event = CreateReleaseWakelockEvent(
+        attributions2, "wl1", bucketStartTimeNs + bucketSizeNs + 100);
+    processor->OnLogEvent(release_event.get());
+    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+    // Acquire wakelock "wc2" in bucket #2.
+    acquire_event = CreateAcquireWakelockEvent(
+        attributions3, "wl2", bucketStartTimeNs + 2 * bucketSizeNs + 1);
+    processor->OnLogEvent(acquire_event.get());
+    EXPECT_EQ((bucketStartTimeNs + 2 *  bucketSizeNs) / NS_PER_SEC + 2,
+              anomalyTracker->getAlarmTimestampSec(dimensionKey2));
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
+
+    // Release wakelock "wc2" in bucket #2.
+    release_event = CreateReleaseWakelockEvent(
+        attributions3, "wl2", bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC);
+    processor->OnLogEvent(release_event.get());
+    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey2));
+    EXPECT_EQ(refractory_period_sec +
+                   (bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC) / NS_PER_SEC,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
+
+    // Acquire wakelock "wc1" in bucket #2.
+    acquire_event = CreateAcquireWakelockEvent(
+        attributions2, "wl1", bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC);
+    processor->OnLogEvent(acquire_event.get());
+    EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 2 + 1,
+              anomalyTracker->getAlarmTimestampSec(dimensionKey));
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+    // Release wakelock "wc1" in bucket #2.
+    release_event = CreateReleaseWakelockEvent(
+        attributions2, "wl1", bucketStartTimeNs + 2 * bucketSizeNs + 2.5 * NS_PER_SEC);
+    processor->OnLogEvent(release_event.get());
+    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+    EXPECT_EQ(refractory_period_sec +
+                   (int64_t)(bucketStartTimeNs + 2 * bucketSizeNs + 2.5 * NS_PER_SEC) / NS_PER_SEC + 1,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+    acquire_event = CreateAcquireWakelockEvent(
+        attributions3, "wl2", bucketStartTimeNs + 6 * bucketSizeNs - NS_PER_SEC + 4);
+    processor->OnLogEvent(acquire_event.get());
+    acquire_event = CreateAcquireWakelockEvent(
+        attributions1, "wl1", bucketStartTimeNs + 6 * bucketSizeNs - NS_PER_SEC + 5);
+    processor->OnLogEvent(acquire_event.get());
+    EXPECT_EQ((bucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + 1,
+              anomalyTracker->getAlarmTimestampSec(dimensionKey));
+    EXPECT_EQ((bucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + 1,
+              anomalyTracker->getAlarmTimestampSec(dimensionKey2));
+
+    release_event = CreateReleaseWakelockEvent(
+        attributions3, "wl2", bucketStartTimeNs + 6 * bucketSizeNs + 2);
+    processor->OnLogEvent(release_event.get());
+    release_event = CreateReleaseWakelockEvent(
+        attributions1, "wl1", bucketStartTimeNs + 6 * bucketSizeNs + 6);
+    processor->OnLogEvent(release_event.get());
+    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey2));
+    // The buckets are not messed up across dimensions. Only one dimension has anomaly triggered.
+    EXPECT_EQ(refractory_period_sec +
+                   (int64_t)(bucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + 1,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+}
+
+TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period) {
+    const int num_buckets = 2;
+    const uint64_t threshold_ns = 3 * NS_PER_SEC;
+    auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, false);
+    int64_t bucketStartTimeNs = 10 * NS_PER_SEC;
+    int64_t bucketSizeNs =
+        TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000;
+
+    const uint64_t alert_id = config.alert(0).id();
+    const uint32_t refractory_period_sec = 3 * bucketSizeNs / NS_PER_SEC;
+    config.mutable_alert(0)->set_refractory_period_secs(refractory_period_sec);
+
+    ConfigKey cfgKey;
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+    EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
+
+    sp<AnomalyTracker> anomalyTracker =
+        processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
+
+    auto screen_off_event = CreateScreenStateChangedEvent(
+            android::view::DisplayStateEnum::DISPLAY_STATE_OFF, bucketStartTimeNs + 1);
+    processor->OnLogEvent(screen_off_event.get());
+
+    // Acquire wakelock "wc1" in bucket #0.
+    auto acquire_event = CreateAcquireWakelockEvent(
+        attributions1, "wl1", bucketStartTimeNs + bucketSizeNs - 100);
+    processor->OnLogEvent(acquire_event.get());
+    EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 3,
+              anomalyTracker->getAlarmTimestampSec(dimensionKey));
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+    // Acquire the wakelock "wc1" again.
+    acquire_event = CreateAcquireWakelockEvent(
+        attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 2 * NS_PER_SEC + 1);
+    processor->OnLogEvent(acquire_event.get());
+    // The alarm does not change.
+    EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 3,
+              anomalyTracker->getAlarmTimestampSec(dimensionKey));
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+    // Anomaly alarm fired late.
+    const int64_t firedAlarmTimestampNs = bucketStartTimeNs + 2 * bucketSizeNs - NS_PER_SEC;
+    auto alarmSet = processor->getAnomalyAlarmMonitor()->popSoonerThan(
+            static_cast<uint32_t>(firedAlarmTimestampNs / NS_PER_SEC));
+    EXPECT_EQ(1u, alarmSet.size());
+    processor->onAnomalyAlarmFired(firedAlarmTimestampNs, alarmSet);
+    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+    EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+    acquire_event = CreateAcquireWakelockEvent(
+        attributions1, "wl1", bucketStartTimeNs + 2 * bucketSizeNs - 100);
+    processor->OnLogEvent(acquire_event.get());
+    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+    EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+    auto release_event = CreateReleaseWakelockEvent(
+        attributions1, "wl1", bucketStartTimeNs + 2 * bucketSizeNs + 1);
+    processor->OnLogEvent(release_event.get());
+    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+    // Within the refractory period. No anomaly.
+    EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+    // A new wakelock, but still within refractory period.
+    acquire_event = CreateAcquireWakelockEvent(
+        attributions1, "wl1", bucketStartTimeNs + 2 * bucketSizeNs + 10 * NS_PER_SEC);
+    processor->OnLogEvent(acquire_event.get());
+    EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
+              anomalyTracker->getAlarmTimestampSec(dimensionKey));
+
+    release_event = CreateReleaseWakelockEvent(
+        attributions1, "wl1", bucketStartTimeNs + 3 * bucketSizeNs - NS_PER_SEC);
+    // Still in the refractory period. No anomaly.
+    processor->OnLogEvent(release_event.get());
+    EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+    acquire_event = CreateAcquireWakelockEvent(
+        attributions1, "wl1", bucketStartTimeNs + 5 * bucketSizeNs - 3 * NS_PER_SEC - 5);
+    processor->OnLogEvent(acquire_event.get());
+    EXPECT_EQ((bucketStartTimeNs + 5 * bucketSizeNs) / NS_PER_SEC,
+              anomalyTracker->getAlarmTimestampSec(dimensionKey));
+
+    release_event = CreateReleaseWakelockEvent(
+        attributions1, "wl1", bucketStartTimeNs + 5 * bucketSizeNs - 3 * NS_PER_SEC - 4);
+    processor->OnLogEvent(release_event.get());
+    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+
+    acquire_event = CreateAcquireWakelockEvent(
+        attributions1, "wl1", bucketStartTimeNs + 5 * bucketSizeNs - 3 * NS_PER_SEC - 3);
+    processor->OnLogEvent(acquire_event.get());
+    EXPECT_EQ((bucketStartTimeNs + 5 * bucketSizeNs) / NS_PER_SEC,
+              anomalyTracker->getAlarmTimestampSec(dimensionKey));
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp
index 2287c2b..c2334d8 100644
--- a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp
+++ b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp
@@ -458,7 +458,7 @@
 }  // namespace
 
 TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_OR_CombinationCondition) {
-    for (auto aggregationType : { DurationMetric::MAX_SPARSE}) { // DurationMetric::SUM,
+    for (auto aggregationType : { DurationMetric::MAX_SPARSE, DurationMetric::SUM}) {
         ConfigKey cfgKey;
         auto config = CreateDurationMetricConfig_NoLink_CombinationCondition(aggregationType);
         int64_t bucketStartTimeNs = 10000000000;
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index 5ef84e6..a75d6c8 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -40,7 +40,7 @@
 const ConfigKey kConfigKey(0, 12345);
 const int tagId = 1;
 const int64_t metricId = 123;
-const int64_t bucketStartTimeNs = 10000000000;
+const int64_t bucketStartTimeNs = 10 * NS_PER_SEC;
 const int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
 const int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
 const int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
index 9b27f3c..13cdb0b 100644
--- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
@@ -44,7 +44,7 @@
 const HashableDimensionKey kConditionKey1 = getMockedDimensionKey(TagId, 1, "maps");
 const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
 const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
-const uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+const uint64_t bucketSizeNs = 30 * NS_PER_SEC;
 
 TEST(OringDurationTrackerTest, TestDurationOverlap) {
     const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
@@ -370,6 +370,103 @@
               tracker.predictAnomalyTimestampNs(*anomalyTracker, event3StartTimeNs));
 }
 
+TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp2) {
+    vector<Matcher> dimensionInCondition;
+    Alert alert;
+    alert.set_id(101);
+    alert.set_metric_id(1);
+    alert.set_trigger_if_sum_gt(5 * NS_PER_SEC);
+    alert.set_num_buckets(1);
+    alert.set_refractory_period_secs(20);
+
+    uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
+    uint64_t bucketNum = 0;
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    sp<AlarmMonitor> alarmMonitor;
+    sp<DurationAnomalyTracker> anomalyTracker =
+        new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
+    OringDurationTracker tracker(kConfigKey, metricId, DEFAULT_METRIC_DIMENSION_KEY, wizard, 1,
+                                 dimensionInCondition,
+                                 true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
+                                 bucketSizeNs, true, false, {anomalyTracker});
+
+    uint64_t eventStartTimeNs = bucketStartTimeNs + 9 * NS_PER_SEC;
+    tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, ConditionKey());
+    // Anomaly happens in the bucket #1.
+    EXPECT_EQ((long long)(bucketStartTimeNs + 14 * NS_PER_SEC),
+              tracker.predictAnomalyTimestampNs(*anomalyTracker, eventStartTimeNs));
+
+    tracker.noteStop(DEFAULT_DIMENSION_KEY, bucketStartTimeNs + 14 * NS_PER_SEC, false);
+
+    EXPECT_EQ((long long)(bucketStartTimeNs + 34 * NS_PER_SEC) / NS_PER_SEC,
+              anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY));
+
+    uint64_t event2StartTimeNs = bucketStartTimeNs + 22 * NS_PER_SEC;
+    EXPECT_EQ((long long)(bucketStartTimeNs + 34 * NS_PER_SEC) / NS_PER_SEC,
+              anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY));
+    EXPECT_EQ((long long)(bucketStartTimeNs + 35 * NS_PER_SEC),
+              tracker.predictAnomalyTimestampNs(*anomalyTracker, event2StartTimeNs));
+}
+
+TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp3) {
+    // Test the cases where the refractory period is smaller than the bucket size, longer than
+    // the bucket size, and longer than 2x of the anomaly detection window.
+    for (int j = 0; j < 3; j++) {
+        uint64_t thresholdNs = j * bucketSizeNs + 5 * NS_PER_SEC;
+        for (int i = 0; i <= 7; ++i) {
+            vector<Matcher> dimensionInCondition;
+            Alert alert;
+            alert.set_id(101);
+            alert.set_metric_id(1);
+            alert.set_trigger_if_sum_gt(thresholdNs);
+            alert.set_num_buckets(3);
+            alert.set_refractory_period_secs(
+                bucketSizeNs / NS_PER_SEC / 2 + i * bucketSizeNs / NS_PER_SEC);
+
+            uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
+            uint64_t bucketNum = 101;
+
+            sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+            sp<AlarmMonitor> alarmMonitor;
+            sp<DurationAnomalyTracker> anomalyTracker =
+                new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
+            OringDurationTracker tracker(kConfigKey, metricId, DEFAULT_METRIC_DIMENSION_KEY,
+                                         wizard, 1, dimensionInCondition,
+                                         true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
+                                         bucketSizeNs, true, false, {anomalyTracker});
+
+            uint64_t eventStartTimeNs = bucketStartTimeNs + 9 * NS_PER_SEC;
+            tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, ConditionKey());
+            EXPECT_EQ((long long)(eventStartTimeNs + thresholdNs),
+                      tracker.predictAnomalyTimestampNs(*anomalyTracker, eventStartTimeNs));
+            uint64_t eventStopTimeNs = eventStartTimeNs + thresholdNs + NS_PER_SEC;
+            tracker.noteStop(DEFAULT_DIMENSION_KEY, eventStopTimeNs, false);
+
+            uint64_t refractoryPeriodEndSec =
+                anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY);
+            EXPECT_EQ((long long)(eventStopTimeNs) / NS_PER_SEC + alert.refractory_period_secs(),
+                       refractoryPeriodEndSec);
+
+            // Acquire and release a wakelock in the next bucket.
+            uint64_t event2StartTimeNs = eventStopTimeNs + bucketSizeNs;
+            tracker.noteStart(DEFAULT_DIMENSION_KEY, true, event2StartTimeNs, ConditionKey());
+            uint64_t event2StopTimeNs = event2StartTimeNs + 4 * NS_PER_SEC;
+            tracker.noteStop(DEFAULT_DIMENSION_KEY, event2StopTimeNs, false);
+
+            // Test the alarm prediction works well when seeing another wakelock start event.
+            for (int k = 0; k <= 2; ++k) {
+                uint64_t event3StartTimeNs = event2StopTimeNs + NS_PER_SEC + k * bucketSizeNs;
+                uint64_t alarmTimestampNs =
+                    tracker.predictAnomalyTimestampNs(*anomalyTracker, event3StartTimeNs);
+                EXPECT_GT(alarmTimestampNs, 0u);
+                EXPECT_GE(alarmTimestampNs, event3StartTimeNs);
+                EXPECT_GE(alarmTimestampNs, refractoryPeriodEndSec * NS_PER_SEC);
+            }
+        }
+    }
+}
+
 TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm) {
     const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
 
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index 0f785df..ce44a35 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -447,7 +447,9 @@
 sp<StatsLogProcessor> CreateStatsLogProcessor(const long timeBaseSec, const StatsdConfig& config,
                                               const ConfigKey& key) {
     sp<UidMap> uidMap = new UidMap();
-    sp<AlarmMonitor> anomalyAlarmMonitor;
+    sp<AlarmMonitor> anomalyAlarmMonitor =
+        new AlarmMonitor(1,  [](const sp<IStatsCompanionService>&, int64_t){},
+                [](const sp<IStatsCompanionService>&){});
     sp<AlarmMonitor> periodicAlarmMonitor;
     sp<StatsLogProcessor> processor = new StatsLogProcessor(
         uidMap, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseSec, [](const ConfigKey&){});
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 8a9c738..8e2e43c 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -330,8 +330,6 @@
 Landroid/app/TaskStackListener;-><init>()V
 Landroid/app/TimePickerDialog;->mTimePicker:Landroid/widget/TimePicker;
 Landroid/app/trust/ITrustManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
-Landroid/app/usage/StorageStatsManager;->getFreeBytes(Ljava/lang/String;)J
-Landroid/app/usage/StorageStatsManager;->getTotalBytes(Ljava/lang/String;)J
 Landroid/app/usage/UsageStatsManager;->mService:Landroid/app/usage/IUsageStatsManager;
 Landroid/app/usage/UsageStats;->mLastEvent:I
 Landroid/app/usage/UsageStats;->mTotalTimeInForeground:J
@@ -824,56 +822,24 @@
 Landroid/hardware/usb/UsbRequest;->mLength:I
 Landroid/hardware/usb/UsbRequest;->mNativeContext:J
 Landroid/icu/impl/CurrencyData;-><init>()V
-Landroid/icu/impl/number/DecimalFormatProperties;->readObject(Ljava/io/ObjectInputStream;)V
-Landroid/icu/impl/number/DecimalFormatProperties;->writeObject(Ljava/io/ObjectOutputStream;)V
-Landroid/icu/impl/TimeZoneGenericNames;->readObject(Ljava/io/ObjectInputStream;)V
 Landroid/icu/text/ArabicShaping;->isAlefMaksouraChar(C)Z
 Landroid/icu/text/ArabicShaping;->isSeenTailFamilyChar(C)I
 Landroid/icu/text/ArabicShaping;->isTailChar(C)Z
 Landroid/icu/text/ArabicShaping;->isYehHamzaChar(C)Z
-Landroid/icu/text/DateFormat;->readObject(Ljava/io/ObjectInputStream;)V
 Landroid/icu/text/DateFormatSymbols;->getLocale(Landroid/icu/util/ULocale$Type;)Landroid/icu/util/ULocale;
-Landroid/icu/text/DateFormatSymbols;->readObject(Ljava/io/ObjectInputStream;)V
 Landroid/icu/text/DateIntervalFormat;-><init>()V
-Landroid/icu/text/DateIntervalFormat;->readObject(Ljava/io/ObjectInputStream;)V
 Landroid/icu/text/DateTimePatternGenerator$DistanceInfo;-><init>()V
-Landroid/icu/text/DecimalFormat_ICU58_Android;->readObject(Ljava/io/ObjectInputStream;)V
-Landroid/icu/text/DecimalFormat_ICU58_Android;->writeObject(Ljava/io/ObjectOutputStream;)V
-Landroid/icu/text/DecimalFormat;->readObject(Ljava/io/ObjectInputStream;)V
 Landroid/icu/text/DecimalFormatSymbols;->getLocale(Landroid/icu/util/ULocale$Type;)Landroid/icu/util/ULocale;
-Landroid/icu/text/DecimalFormatSymbols;->readObject(Ljava/io/ObjectInputStream;)V
-Landroid/icu/text/DecimalFormat;->writeObject(Ljava/io/ObjectOutputStream;)V
-Landroid/icu/text/MessageFormat;->readObject(Ljava/io/ObjectInputStream;)V
-Landroid/icu/text/MessageFormat;->writeObject(Ljava/io/ObjectOutputStream;)V
-Landroid/icu/text/NumberFormat;->readObject(Ljava/io/ObjectInputStream;)V
-Landroid/icu/text/NumberFormat;->writeObject(Ljava/io/ObjectOutputStream;)V
-Landroid/icu/text/PluralFormat;->readObject(Ljava/io/ObjectInputStream;)V
-Landroid/icu/text/PluralRules$FixedDecimal;->readObject(Ljava/io/ObjectInputStream;)V
-Landroid/icu/text/PluralRules$FixedDecimal;->writeObject(Ljava/io/ObjectOutputStream;)V
-Landroid/icu/text/PluralRules;->readObject(Ljava/io/ObjectInputStream;)V
-Landroid/icu/text/PluralRules;->writeObject(Ljava/io/ObjectOutputStream;)V
 Landroid/icu/text/RuleBasedCollator;->getLocale(Landroid/icu/util/ULocale$Type;)Landroid/icu/util/ULocale;
-Landroid/icu/text/RuleBasedNumberFormat;->readObject(Ljava/io/ObjectInputStream;)V
-Landroid/icu/text/RuleBasedNumberFormat;->writeObject(Ljava/io/ObjectOutputStream;)V
-Landroid/icu/text/SelectFormat;->readObject(Ljava/io/ObjectInputStream;)V
-Landroid/icu/text/SimpleDateFormat;->readObject(Ljava/io/ObjectInputStream;)V
-Landroid/icu/text/SimpleDateFormat;->writeObject(Ljava/io/ObjectOutputStream;)V
 Landroid/icu/text/SpoofChecker$ScriptSet;->and(I)V
 Landroid/icu/text/SpoofChecker$ScriptSet;-><init>()V
 Landroid/icu/text/SpoofChecker$ScriptSet;->isFull()Z
 Landroid/icu/text/SpoofChecker$ScriptSet;->setAll()V
-Landroid/icu/text/TimeZoneFormat;->readObject(Ljava/io/ObjectInputStream;)V
-Landroid/icu/text/TimeZoneFormat;->writeObject(Ljava/io/ObjectOutputStream;)V
 Landroid/icu/text/TimeZoneNames$DefaultTimeZoneNames$FactoryImpl;-><init>()V
 Landroid/icu/text/Transliterator;->createFromRules(Ljava/lang/String;Ljava/lang/String;I)Landroid/icu/text/Transliterator;
 Landroid/icu/text/Transliterator;->transliterate(Ljava/lang/String;)Ljava/lang/String;
 Landroid/icu/text/UFormat;->getLocale(Landroid/icu/util/ULocale$Type;)Landroid/icu/util/ULocale;
 Landroid/icu/util/Calendar;->getLocale(Landroid/icu/util/ULocale$Type;)Landroid/icu/util/ULocale;
-Landroid/icu/util/Calendar;->readObject(Ljava/io/ObjectInputStream;)V
-Landroid/icu/util/Calendar;->writeObject(Ljava/io/ObjectOutputStream;)V
-Landroid/icu/util/ChineseCalendar;->readObject(Ljava/io/ObjectInputStream;)V
-Landroid/icu/util/IslamicCalendar;->readObject(Ljava/io/ObjectInputStream;)V
-Landroid/icu/util/SimpleTimeZone;->readObject(Ljava/io/ObjectInputStream;)V
 Landroid/inputmethodservice/InputMethodService;->mExtractEditText:Landroid/inputmethodservice/ExtractEditText;
 Landroid/location/CountryDetector;->detectCountry()Landroid/location/Country;
 Landroid/location/Country;->getCountryIso()Ljava/lang/String;
@@ -1357,7 +1323,6 @@
 Landroid/os/storage/VolumeInfo;->getPath()Ljava/io/File;
 Landroid/os/storage/VolumeInfo;->getState()I
 Landroid/os/storage/VolumeInfo;->getType()I
-Landroid/os/storage/VolumeInfo;->isMountedReadable()Z
 Landroid/os/storage/VolumeInfo;->isPrimary()Z
 Landroid/os/storage/VolumeInfo;->isVisible()Z
 Landroid/os/StrictMode;->disableDeathOnFileUriExposure()V
@@ -1992,7 +1957,6 @@
 Landroid/util/Pools$SynchronizedPool;-><init>(I)V
 Landroid/util/Rational;->mDenominator:I
 Landroid/util/Rational;->mNumerator:I
-Landroid/util/Rational;->readObject(Ljava/io/ObjectInputStream;)V
 Landroid/util/Singleton;->mInstance:Ljava/lang/Object;
 Landroid/util/Slog;->d(Ljava/lang/String;Ljava/lang/String;)I
 Landroid/util/SparseIntArray;->mKeys:[I
@@ -2841,8 +2805,6 @@
 Lcom/android/okhttp/OkHttpClient;->dns:Lcom/android/okhttp/Dns;
 Lcom/android/okhttp/OkHttpClient;->setProtocols(Ljava/util/List;)Lcom/android/okhttp/OkHttpClient;
 Lcom/android/okhttp/OkHttpClient;->setRetryOnConnectionFailure(Z)V
-Lcom/android/okhttp/okio/ByteString;->readObject(Ljava/io/ObjectInputStream;)V
-Lcom/android/okhttp/okio/ByteString;->writeObject(Ljava/io/ObjectOutputStream;)V
 Lcom/android/okhttp/Request;->headers:Lcom/android/okhttp/Headers;
 Lcom/android/okhttp/Request;->method:Ljava/lang/String;
 Lcom/android/okhttp/Request;->url:Lcom/android/okhttp/HttpUrl;
@@ -2945,20 +2907,14 @@
 Ldalvik/system/VMRuntime;->vmLibrary()Ljava/lang/String;
 Ldalvik/system/VMStack;->getCallingClassLoader()Ljava/lang/ClassLoader;
 Ldalvik/system/VMStack;->getStackClass2()Ljava/lang/Class;
-Ljava/awt/font/NumericShaper;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/beans/PropertyChangeSupport;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/beans/PropertyChangeSupport;->writeObject(Ljava/io/ObjectOutputStream;)V
 Ljava/io/FileDescriptor;->descriptor:I
 Ljava/io/FileDescriptor;->getInt$()I
 Ljava/io/FileDescriptor;->setInt$(I)V
 Ljava/io/FileInputStream;->fd:Ljava/io/FileDescriptor;
 Ljava/io/FileOutputStream;->fd:Ljava/io/FileDescriptor;
-Ljava/io/File;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/io/File;->writeObject(Ljava/io/ObjectOutputStream;)V
 Ljava/io/ObjectStreamClass;->getConstructorId(Ljava/lang/Class;)J
 Ljava/io/ObjectStreamClass;->newInstance(Ljava/lang/Class;J)Ljava/lang/Object;
 Ljava/io/ObjectStreamClass;->newInstance()Ljava/lang/Object;
-Ljava/io/UncheckedIOException;->readObject(Ljava/io/ObjectInputStream;)V
 Ljava/lang/AbstractStringBuilder;->value:[C
 Ljava/lang/Boolean;->value:Z
 Ljava/lang/Byte;->value:B
@@ -2979,11 +2935,8 @@
 Ljava/lang/Daemons;->start()V
 Ljava/lang/Daemons;->stop()V
 Ljava/lang/Double;->value:D
-Ljava/lang/Enum;->readObject(Ljava/io/ObjectInputStream;)V
 Ljava/lang/Float;->value:F
 Ljava/lang/Integer;->value:I
-Ljava/lang/invoke/MethodType;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/lang/invoke/MethodType;->writeObject(Ljava/io/ObjectOutputStream;)V
 Ljava/lang/Long;->value:J
 Ljava/lang/ref/FinalizerReference;->add(Ljava/lang/Object;)V
 Ljava/lang/ref/FinalizerReference;->head:Ljava/lang/ref/FinalizerReference;
@@ -2999,10 +2952,6 @@
 Ljava/lang/Runtime;->load(Ljava/lang/String;Ljava/lang/ClassLoader;)V
 Ljava/lang/Runtime;->nativeLoad(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/String;
 Ljava/lang/Short;->value:S
-Ljava/lang/StringBuffer;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/lang/StringBuffer;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/lang/StringBuilder;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/lang/StringBuilder;->writeObject(Ljava/io/ObjectOutputStream;)V
 Ljava/lang/String;-><init>(II[C)V
 Ljava/lang/System;-><init>()V
 Ljava/lang/Thread;->daemon:Z
@@ -3026,16 +2975,9 @@
 Ljava/lang/Throwable;->cause:Ljava/lang/Throwable;
 Ljava/lang/Throwable;->detailMessage:Ljava/lang/String;
 Ljava/lang/Throwable;->nativeFillInStackTrace()Ljava/lang/Object;
-Ljava/lang/Throwable;->readObject(Ljava/io/ObjectInputStream;)V
 Ljava/lang/Throwable;->stackTrace:[Ljava/lang/StackTraceElement;
 Ljava/lang/Throwable;->suppressedExceptions:Ljava/util/List;
-Ljava/lang/Throwable;->writeObject(Ljava/io/ObjectOutputStream;)V
 Ljava/lang/Void;-><init>()V
-Ljava/math/BigDecimal;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/math/BigDecimal;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/math/BigInteger;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/math/BigInteger;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/math/MathContext;->readObject(Ljava/io/ObjectInputStream;)V
 Ljava/net/Authenticator;->theAuthenticator:Ljava/net/Authenticator;
 Ljava/net/DatagramSocket;->impl:Ljava/net/DatagramSocketImpl;
 Ljava/net/HttpCookie;->httpOnly:Z
@@ -3047,8 +2989,6 @@
 Ljava/net/Inet6Address$Inet6AddressHolder;->scope_id_set:Z
 Ljava/net/Inet6Address$Inet6AddressHolder;->scope_ifname:Ljava/net/NetworkInterface;
 Ljava/net/Inet6Address;-><init>()V
-Ljava/net/Inet6Address;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/net/Inet6Address;->writeObject(Ljava/io/ObjectOutputStream;)V
 Ljava/net/InetAddress;->clearDnsCache()V
 Ljava/net/InetAddress;->holder:Ljava/net/InetAddress$InetAddressHolder;
 Ljava/net/InetAddress$InetAddressHolder;->address:I
@@ -3057,19 +2997,11 @@
 Ljava/net/InetAddress$InetAddressHolder;->originalHostName:Ljava/lang/String;
 Ljava/net/InetAddress;->isNumeric(Ljava/lang/String;)Z
 Ljava/net/InetAddress;->parseNumericAddress(Ljava/lang/String;)Ljava/net/InetAddress;
-Ljava/net/InetAddress;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/net/InetAddress;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/net/InetSocketAddress;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/net/InetSocketAddress;->writeObject(Ljava/io/ObjectOutputStream;)V
 Ljava/net/Socket;->getFileDescriptor$()Ljava/io/FileDescriptor;
 Ljava/net/Socket;->impl:Ljava/net/SocketImpl;
 Ljava/net/URI;->host:Ljava/lang/String;
-Ljava/net/URI;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/net/URI;->writeObject(Ljava/io/ObjectOutputStream;)V
 Ljava/net/URL;->handler:Ljava/net/URLStreamHandler;
 Ljava/net/URL;->handlers:Ljava/util/Hashtable;
-Ljava/net/URL;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/net/URL;->writeObject(Ljava/io/ObjectOutputStream;)V
 Ljava/nio/Buffer;->address:J
 Ljava/nio/Buffer;->capacity:I
 Ljava/nio/Buffer;->_elementSizeShift:I
@@ -3080,168 +3012,37 @@
 Ljava/nio/ByteBuffer;->offset:I
 Ljava/nio/charset/CharsetEncoder;->canEncode(Ljava/nio/CharBuffer;)Z
 Ljava/nio/DirectByteBuffer;-><init>(JI)V
-Ljava/nio/file/DirectoryIteratorException;->readObject(Ljava/io/ObjectInputStream;)V
 Ljava/nio/NIOAccess;->getBaseArray(Ljava/nio/Buffer;)Ljava/lang/Object;
 Ljava/nio/NIOAccess;->getBaseArrayOffset(Ljava/nio/Buffer;)I
 Ljava/nio/NIOAccess;->getBasePointer(Ljava/nio/Buffer;)J
-Ljava/security/cert/CertificateRevokedException;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/security/cert/CertificateRevokedException;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/security/cert/CertPathValidatorException;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/security/CodeSigner;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/security/GuardedObject;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/security/Provider;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/security/SignedObject;->readObject(Ljava/io/ObjectInputStream;)V
 Ljava/security/spec/ECParameterSpec;->getCurveName()Ljava/lang/String;
 Ljava/security/spec/ECParameterSpec;->setCurveName(Ljava/lang/String;)V
-Ljava/security/Timestamp;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/text/ChoiceFormat;->readObject(Ljava/io/ObjectInputStream;)V
 Ljava/text/DateFormat;->is24Hour:Ljava/lang/Boolean;
-Ljava/text/DateFormatSymbols;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/text/DateFormatSymbols;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/text/DecimalFormat;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/text/DecimalFormatSymbols;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/text/DecimalFormatSymbols;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/text/DecimalFormat;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/text/MessageFormat;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/text/NumberFormat;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/text/NumberFormat;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/text/SimpleDateFormat;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/chrono/AbstractChronology;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/chrono/HijrahChronology;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/chrono/HijrahDate;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/chrono/IsoChronology;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/chrono/JapaneseChronology;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/chrono/JapaneseDate;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/chrono/JapaneseEra;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/chrono/MinguoChronology;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/chrono/MinguoDate;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/chrono/ThaiBuddhistChronology;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/chrono/ThaiBuddhistDate;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/Duration;->readObject(Ljava/io/ObjectInputStream;)V
 Ljava/time/Duration;->toSeconds()Ljava/math/BigDecimal;
-Ljava/time/Instant;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/LocalDate;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/LocalDateTime;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/LocalTime;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/MonthDay;->readObject(Ljava/io/ObjectInputStream;)V
 Ljava/time/OffsetDateTime;-><init>(Ljava/time/LocalDateTime;Ljava/time/ZoneOffset;)V
-Ljava/time/OffsetDateTime;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/OffsetTime;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/Period;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/temporal/ValueRange;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/temporal/WeekFields;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/YearMonth;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/Year;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/ZonedDateTime;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/ZoneId;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/ZoneOffset;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/zone/ZoneOffsetTransition;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/zone/ZoneOffsetTransitionRule;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/zone/ZoneRules;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/ArrayDeque;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/ArrayDeque;->writeObject(Ljava/io/ObjectOutputStream;)V
 Ljava/util/ArrayList;->elementData:[Ljava/lang/Object;
-Ljava/util/ArrayList;->readObject(Ljava/io/ObjectInputStream;)V
 Ljava/util/ArrayList;->size:I
 Ljava/util/ArrayList$SubList;->parent:Ljava/util/AbstractList;
 Ljava/util/ArrayList$SubList;->parentOffset:I
 Ljava/util/ArrayList$SubList;->size:I
-Ljava/util/ArrayList;->writeObject(Ljava/io/ObjectOutputStream;)V
 Ljava/util/Arrays$ArrayList;->a:[Ljava/lang/Object;
-Ljava/util/BitSet;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/BitSet;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/Calendar;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/Calendar;->writeObject(Ljava/io/ObjectOutputStream;)V
 Ljava/util/Calendar;->zone:Ljava/util/TimeZone;
 Ljava/util/Collections$EmptyList;-><init>()V
-Ljava/util/Collections$SetFromMap;->readObject(Ljava/io/ObjectInputStream;)V
 Ljava/util/Collections$SynchronizedCollection;->c:Ljava/util/Collection;
-Ljava/util/Collections$SynchronizedCollection;->writeObject(Ljava/io/ObjectOutputStream;)V
 Ljava/util/Collections$SynchronizedMap;->m:Ljava/util/Map;
-Ljava/util/Collections$SynchronizedMap;->writeObject(Ljava/io/ObjectOutputStream;)V
 Ljava/util/Collections$UnmodifiableCollection;->c:Ljava/util/Collection;
 Ljava/util/Collections$UnmodifiableMap;->m:Ljava/util/Map;
-Ljava/util/concurrent/atomic/AtomicReferenceArray;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/concurrent/atomic/DoubleAccumulator;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/concurrent/atomic/DoubleAdder;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/concurrent/atomic/LongAccumulator;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/concurrent/atomic/LongAdder;->readObject(Ljava/io/ObjectInputStream;)V
 Ljava/util/concurrent/ConcurrentHashMap$BaseIterator;->hasMoreElements()Z
-Ljava/util/concurrent/ConcurrentHashMap;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/concurrent/ConcurrentHashMap;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/concurrent/ConcurrentLinkedDeque;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/concurrent/ConcurrentLinkedDeque;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/concurrent/ConcurrentLinkedQueue;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/concurrent/ConcurrentLinkedQueue;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/concurrent/ConcurrentSkipListMap;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/concurrent/ConcurrentSkipListMap;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/concurrent/CopyOnWriteArrayList;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/concurrent/CopyOnWriteArrayList;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/concurrent/ForkJoinTask;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/concurrent/ForkJoinTask;->writeObject(Ljava/io/ObjectOutputStream;)V
 Ljava/util/concurrent/FutureTask;->callable:Ljava/util/concurrent/Callable;
-Ljava/util/concurrent/LinkedBlockingDeque;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/concurrent/LinkedBlockingDeque;->writeObject(Ljava/io/ObjectOutputStream;)V
 Ljava/util/concurrent/LinkedBlockingQueue;->capacity:I
-Ljava/util/concurrent/LinkedBlockingQueue;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/concurrent/LinkedBlockingQueue;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/concurrent/LinkedTransferQueue;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/concurrent/LinkedTransferQueue;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/concurrent/locks/ReentrantLock$Sync;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/concurrent/locks/ReentrantReadWriteLock$Sync;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/concurrent/locks/StampedLock;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/concurrent/PriorityBlockingQueue;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/concurrent/PriorityBlockingQueue;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/concurrent/SynchronousQueue;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/concurrent/SynchronousQueue;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/concurrent/ThreadLocalRandom;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/Date;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/Date;->writeObject(Ljava/io/ObjectOutputStream;)V
 Ljava/util/EnumMap;->keyType:Ljava/lang/Class;
-Ljava/util/EnumMap;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/EnumMap;->writeObject(Ljava/io/ObjectOutputStream;)V
 Ljava/util/EnumSet;->elementType:Ljava/lang/Class;
-Ljava/util/EnumSet;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/GregorianCalendar;->readObject(Ljava/io/ObjectInputStream;)V
 Ljava/util/HashMap$HashIterator;->hasNext()Z
-Ljava/util/HashMap;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/HashMap;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/HashSet;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/HashSet;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/Hashtable;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/Hashtable;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/IdentityHashMap;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/IdentityHashMap;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/InvalidPropertiesFormatException;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/InvalidPropertiesFormatException;->writeObject(Ljava/io/ObjectOutputStream;)V
 Ljava/util/LinkedHashMap;->eldest()Ljava/util/Map$Entry;
 Ljava/util/LinkedHashMap$LinkedHashIterator;->hasNext()Z
-Ljava/util/LinkedList;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/LinkedList;->writeObject(Ljava/io/ObjectOutputStream;)V
 Ljava/util/Locale;->createConstant(Ljava/lang/String;Ljava/lang/String;)Ljava/util/Locale;
-Ljava/util/Locale;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/Locale;->readResolve()Ljava/lang/Object;
-Ljava/util/Locale;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/logging/LogRecord;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/logging/LogRecord;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/prefs/NodeChangeEvent;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/prefs/NodeChangeEvent;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/prefs/PreferenceChangeEvent;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/prefs/PreferenceChangeEvent;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/PriorityQueue;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/PriorityQueue;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/Random;->readObject(Ljava/io/ObjectInputStream;)V
 Ljava/util/Random;->seedUniquifier()J
-Ljava/util/Random;->writeObject(Ljava/io/ObjectOutputStream;)V
 Ljava/util/regex/Matcher;->appendPos:I
-Ljava/util/regex/Pattern;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/SimpleTimeZone;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/SimpleTimeZone;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/TreeMap;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/TreeMap;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/TreeSet;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/TreeSet;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/Vector;->writeObject(Ljava/io/ObjectOutputStream;)V
 Ljava/util/zip/Deflater;->buf:[B
 Ljava/util/zip/Deflater;->finished:Z
 Ljava/util/zip/Deflater;->finish:Z
@@ -3257,23 +3058,11 @@
 Ljava/util/zip/Inflater;->off:I
 Ljava/util/zip/ZipEntry;-><init>(Ljava/lang/String;Ljava/lang/String;JJJII[BJ)V
 Ljava/util/zip/ZipFile;->jzfile:J
-Ljavax/crypto/SealedObject;->readObject(Ljava/io/ObjectInputStream;)V
 Ljavax/net/ssl/SSLServerSocketFactory;->defaultServerSocketFactory:Ljavax/net/ssl/SSLServerSocketFactory;
 Ljavax/net/ssl/SSLSocketFactory;->defaultSocketFactory:Ljavax/net/ssl/SSLSocketFactory;
-Ljavax/security/auth/Subject;->readObject(Ljava/io/ObjectInputStream;)V
-Ljavax/security/auth/Subject$SecureSet;->readObject(Ljava/io/ObjectInputStream;)V
-Ljavax/security/auth/Subject$SecureSet;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljavax/security/auth/Subject;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljavax/security/auth/x500/X500Principal;->readObject(Ljava/io/ObjectInputStream;)V
-Ljavax/security/auth/x500/X500Principal;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljavax/xml/datatype/DatatypeConfigurationException;->readObject(Ljava/io/ObjectInputStream;)V
-Ljavax/xml/namespace/QName;->readObject(Ljava/io/ObjectInputStream;)V
 Llibcore/util/ZoneInfo;->mTransitions:[J
-Llibcore/util/ZoneInfo;->readObject(Ljava/io/ObjectInputStream;)V
 Lorg/apache/http/conn/ssl/SSLSocketFactory;-><init>(Ljavax/net/ssl/SSLSocketFactory;)V
 Lorg/apache/http/conn/ssl/SSLSocketFactory;-><init>()V
 Lorg/json/JSONArray;->values:Ljava/util/List;
 Lorg/json/JSONObject;->writeTo(Lorg/json/JSONStringer;)V
 Lsun/misc/Unsafe;->theUnsafe:Lsun/misc/Unsafe;
-Lsun/security/util/ObjectIdentifier;->readObject(Ljava/io/ObjectInputStream;)V
-Lsun/security/util/ObjectIdentifier;->writeObject(Ljava/io/ObjectOutputStream;)V
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index f01eee4..1df724e 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -30,12 +30,15 @@
 import android.app.assist.AssistContent;
 import android.app.assist.AssistStructure;
 import android.app.backup.BackupAgent;
+import android.app.servertransaction.ActivityLifecycleItem;
 import android.app.servertransaction.ActivityLifecycleItem.LifecycleState;
+import android.app.servertransaction.ActivityRelaunchItem;
 import android.app.servertransaction.ActivityResultItem;
 import android.app.servertransaction.ClientTransaction;
 import android.app.servertransaction.PendingTransactionActions;
 import android.app.servertransaction.PendingTransactionActions.StopInfo;
 import android.app.servertransaction.TransactionExecutor;
+import android.app.servertransaction.TransactionExecutorHelper;
 import android.content.BroadcastReceiver;
 import android.content.ComponentCallbacks2;
 import android.content.ComponentName;
@@ -520,6 +523,10 @@
             return activityInfo.persistableMode == ActivityInfo.PERSIST_ACROSS_REBOOTS;
         }
 
+        public boolean isVisibleFromServer() {
+            return activity != null && activity.mVisibleFromServer;
+        }
+
         public String toString() {
             ComponentName componentName = intent != null ? intent.getComponent() : null;
             return "ActivityRecord{"
@@ -1797,6 +1804,7 @@
                         // message is handled.
                         transaction.recycle();
                     }
+                    // TODO(lifecycler): Recycle locally scheduled transactions.
                     break;
             }
             Object obj = msg.obj;
@@ -2755,6 +2763,11 @@
         }
     }
 
+    @Override
+    TransactionExecutor getTransactionExecutor() {
+        return mTransactionExecutor;
+    }
+
     void sendMessage(int what, Object obj) {
         sendMessage(what, obj, 0, 0, false);
     }
@@ -4723,15 +4736,22 @@
             return;
         }
 
-        // TODO(b/73747058): Investigate converting this to use transaction to relaunch.
-        handleRelaunchActivityInner(r, 0 /* configChanges */, null /* pendingResults */,
-                null /* pendingIntents */, null /* pendingActions */, prevState != ON_RESUME,
-                r.overrideConfig, "handleRelaunchActivityLocally");
 
-        // Restore back to the previous state before relaunch if needed.
-        if (prevState != r.getLifecycleState()) {
-            mTransactionExecutor.cycleToPath(r, prevState);
-        }
+        // Initialize a relaunch request.
+        final MergedConfiguration mergedConfiguration = new MergedConfiguration(
+                r.createdConfig != null ? r.createdConfig : mConfiguration,
+                r.overrideConfig);
+        final ActivityRelaunchItem activityRelaunchItem = ActivityRelaunchItem.obtain(
+                null /* pendingResults */, null /* pendingIntents */, 0 /* configChanges */,
+                mergedConfiguration, r.mPreserveWindow);
+        // Make sure to match the existing lifecycle state in the end of the transaction.
+        final ActivityLifecycleItem lifecycleRequest =
+                TransactionExecutorHelper.getLifecycleRequestForCurrentState(r);
+        // Schedule the transaction.
+        final ClientTransaction transaction = ClientTransaction.obtain(this.mAppThread, r.token);
+        transaction.addCallback(activityRelaunchItem);
+        transaction.setLifecycleStateRequest(lifecycleRequest);
+        executeTransaction(transaction);
     }
 
     private void handleRelaunchActivityInner(ActivityClientRecord r, int configChanges,
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index 961bca2..925080e 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -17,6 +17,7 @@
 
 import android.app.servertransaction.ClientTransaction;
 import android.app.servertransaction.PendingTransactionActions;
+import android.app.servertransaction.TransactionExecutor;
 import android.content.pm.ApplicationInfo;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
@@ -43,6 +44,22 @@
         sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
     }
 
+    /**
+     * Execute transaction immediately without scheduling it. This is used for local requests, so
+     * it will also recycle the transaction.
+     */
+    void executeTransaction(ClientTransaction transaction) {
+        transaction.preExecute(this);
+        getTransactionExecutor().execute(transaction);
+        transaction.recycle();
+    }
+
+    /**
+     * Get the {@link TransactionExecutor} that will be performing lifecycle transitions and
+     * callbacks for activities.
+     */
+    abstract TransactionExecutor getTransactionExecutor();
+
     abstract void sendMessage(int what, Object obj);
 
 
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 4cb7f89..3015398 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -8610,6 +8610,7 @@
      * @hide
      */
     @SystemApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
     @UserProvisioningState
     public int getUserProvisioningState() {
         throwIfParentInstance("getUserProvisioningState");
@@ -8754,6 +8755,7 @@
      * @hide
      */
     @SystemApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
     public boolean isDeviceProvisioned() {
         try {
             return mService.isDeviceProvisioned();
@@ -9418,7 +9420,21 @@
      * <p>This method may returns {@code -1} if {@code apnSetting} conflicts with an existing
      * override APN. Update the existing conflicted APN with
      * {@link #updateOverrideApn(ComponentName, int, ApnSetting)} instead of adding a new entry.
-     * <p>See {@link ApnSetting} for the definition of conflict.
+     * <p>Two override APNs are considered to conflict when all the following APIs return
+     * the same values on both override APNs:
+     * <ul>
+     *   <li>{@link ApnSetting#getOperatorNumeric()}</li>
+     *   <li>{@link ApnSetting#getApnName()}</li>
+     *   <li>{@link ApnSetting#getProxyAddress()}</li>
+     *   <li>{@link ApnSetting#getProxyPort()}</li>
+     *   <li>{@link ApnSetting#getMmsProxyAddress()}</li>
+     *   <li>{@link ApnSetting#getMmsProxyPort()}</li>
+     *   <li>{@link ApnSetting#getMmsc()}</li>
+     *   <li>{@link ApnSetting#isEnabled()}</li>
+     *   <li>{@link ApnSetting#getMvnoType()}</li>
+     *   <li>{@link ApnSetting#getProtocol()}</li>
+     *   <li>{@link ApnSetting#getRoamingProtocol()}</li>
+     * </ul>
      *
      * @param admin which {@link DeviceAdminReceiver} this request is associated with
      * @param apnSetting the override APN to insert
@@ -9447,7 +9463,7 @@
      * {@code apnId}.
      * <p>This method may also returns {@code false} if {@code apnSetting} conflicts with an
      * existing override APN. Update the existing conflicted APN instead.
-     * <p>See {@link ApnSetting} for the definition of conflict.
+     * <p>See {@link #addOverrideApn} for the definition of conflict.
      *
      * @param admin which {@link DeviceAdminReceiver} this request is associated with
      * @param apnId the {@code id} of the override APN to update
diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java
index ee13880..02afcc7 100644
--- a/core/java/android/app/job/JobInfo.java
+++ b/core/java/android/app/job/JobInfo.java
@@ -65,7 +65,6 @@
             NETWORK_TYPE_UNMETERED,
             NETWORK_TYPE_NOT_ROAMING,
             NETWORK_TYPE_CELLULAR,
-            NETWORK_TYPE_METERED,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface NetworkType {}
@@ -253,7 +252,7 @@
     /**
      * @hide
      */
-    public static final int FLAG_IS_PREFETCH = 1 << 2;
+    public static final int FLAG_PREFETCH = 1 << 2;
 
     /**
      * This job needs to be exempted from the app standby throttling. Only the system (UID 1000)
@@ -296,7 +295,8 @@
     private final boolean hasEarlyConstraint;
     private final boolean hasLateConstraint;
     private final NetworkRequest networkRequest;
-    private final long networkBytes;
+    private final long networkDownloadBytes;
+    private final long networkUploadBytes;
     private final long minLatencyMillis;
     private final long maxExecutionDelayMillis;
     private final boolean isPeriodic;
@@ -317,30 +317,28 @@
     }
 
     /**
-     * Bundle of extras which are returned to your application at execution time.
+     * @see JobInfo.Builder#setExtras(PersistableBundle)
      */
     public @NonNull PersistableBundle getExtras() {
         return extras;
     }
 
     /**
-     * Bundle of transient extras which are returned to your application at execution time,
-     * but not persisted by the system.
+     * @see JobInfo.Builder#setTransientExtras(Bundle)
      */
     public @NonNull Bundle getTransientExtras() {
         return transientExtras;
     }
 
     /**
-     * ClipData of information that is returned to your application at execution time,
-     * but not persisted by the system.
+     * @see JobInfo.Builder#setClipData(ClipData, int)
      */
     public @Nullable ClipData getClipData() {
         return clipData;
     }
 
     /**
-     * Permission grants that go along with {@link #getClipData}.
+     * @see JobInfo.Builder#setClipData(ClipData, int)
      */
     public int getClipGrantFlags() {
         return clipGrantFlags;
@@ -369,32 +367,28 @@
     }
 
     /**
-     * Whether this job requires that the device be charging (or be a non-battery-powered
-     * device connected to permanent power, such as Android TV devices).
+     * @see JobInfo.Builder#setRequiresCharging(boolean)
      */
     public boolean isRequireCharging() {
         return (constraintFlags & CONSTRAINT_FLAG_CHARGING) != 0;
     }
 
     /**
-     * Whether this job needs the device's battery level to not be at below the critical threshold.
+     * @see JobInfo.Builder#setRequiresBatteryNotLow(boolean)
      */
     public boolean isRequireBatteryNotLow() {
         return (constraintFlags & CONSTRAINT_FLAG_BATTERY_NOT_LOW) != 0;
     }
 
     /**
-     * Whether this job requires that the user <em>not</em> be interacting with the device.
-     *
-     * <p class="note">This is <em>not</em> the same as "doze" or "device idle";
-     * it is purely about the user's direct interactions.</p>
+     * @see JobInfo.Builder#setRequiresDeviceIdle(boolean)
      */
     public boolean isRequireDeviceIdle() {
         return (constraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0;
     }
 
     /**
-     * Whether this job needs the device's storage to not be low.
+     * @see JobInfo.Builder#setRequiresStorageNotLow(boolean)
      */
     public boolean isRequireStorageNotLow() {
         return (constraintFlags & CONSTRAINT_FLAG_STORAGE_NOT_LOW) != 0;
@@ -410,6 +404,7 @@
     /**
      * Which content: URIs must change for the job to be scheduled.  Returns null
      * if there are none required.
+     * @see JobInfo.Builder#addTriggerContentUri(TriggerContentUri)
      */
     public @Nullable TriggerContentUri[] getTriggerContentUris() {
         return triggerContentUris;
@@ -418,6 +413,7 @@
     /**
      * When triggering on content URI changes, this is the delay from when a change
      * is detected until the job is scheduled.
+     * @see JobInfo.Builder#setTriggerContentUpdateDelay(long)
      */
     public long getTriggerContentUpdateDelay() {
         return triggerContentUpdateDelay;
@@ -426,6 +422,7 @@
     /**
      * When triggering on content URI changes, this is the maximum delay we will
      * use before scheduling the job.
+     * @see JobInfo.Builder#setTriggerContentMaxDelay(long)
      */
     public long getTriggerContentMaxDelay() {
         return triggerContentMaxDelay;
@@ -466,28 +463,59 @@
     }
 
     /**
-     * Return the estimated size of network traffic that will be performed by
+     * @deprecated replaced by {@link #getEstimatedNetworkDownloadBytes()} and
+     *             {@link #getEstimatedNetworkUploadBytes()}.
+     * @removed
+     */
+    @Deprecated
+    public @BytesLong long getEstimatedNetworkBytes() {
+        if (networkDownloadBytes == NETWORK_BYTES_UNKNOWN
+                && networkUploadBytes == NETWORK_BYTES_UNKNOWN) {
+            return NETWORK_BYTES_UNKNOWN;
+        } else if (networkDownloadBytes == NETWORK_BYTES_UNKNOWN) {
+            return networkUploadBytes;
+        } else if (networkUploadBytes == NETWORK_BYTES_UNKNOWN) {
+            return networkDownloadBytes;
+        } else {
+            return networkDownloadBytes + networkUploadBytes;
+        }
+    }
+
+    /**
+     * Return the estimated size of download traffic that will be performed by
      * this job, in bytes.
      *
-     * @return Estimated size of network traffic, or
+     * @return Estimated size of download traffic, or
      *         {@link #NETWORK_BYTES_UNKNOWN} when unknown.
-     * @see Builder#setEstimatedNetworkBytes(long)
+     * @see Builder#setEstimatedNetworkBytes(long, long)
      */
-    public @BytesLong long getEstimatedNetworkBytes() {
-        return networkBytes;
+    public @BytesLong long getEstimatedNetworkDownloadBytes() {
+        return networkDownloadBytes;
+    }
+
+    /**
+     * Return the estimated size of upload traffic that will be performed by
+     * this job, in bytes.
+     *
+     * @return Estimated size of upload traffic, or
+     *         {@link #NETWORK_BYTES_UNKNOWN} when unknown.
+     * @see Builder#setEstimatedNetworkBytes(long, long)
+     */
+    public @BytesLong long getEstimatedNetworkUploadBytes() {
+        return networkUploadBytes;
     }
 
     /**
      * Set for a job that does not recur periodically, to specify a delay after which the job
      * will be eligible for execution. This value is not set if the job recurs periodically.
+     * @see JobInfo.Builder#setMinimumLatency(long)
      */
     public long getMinLatencyMillis() {
         return minLatencyMillis;
     }
 
     /**
-     * See {@link Builder#setOverrideDeadline(long)}. This value is not set if the job recurs
-     * periodically.
+     * @see JobInfo.Builder#setOverrideDeadline(long)
      */
     public long getMaxExecutionDelayMillis() {
         return maxExecutionDelayMillis;
@@ -495,13 +523,15 @@
 
     /**
      * Track whether this job will repeat with a given period.
+     * @see JobInfo.Builder#setPeriodic(long)
+     * @see JobInfo.Builder#setPeriodic(long, long)
      */
     public boolean isPeriodic() {
         return isPeriodic;
     }
 
     /**
-     * @return Whether or not this job should be persisted across device reboots.
+     * @see JobInfo.Builder#setPersisted(boolean)
      */
     public boolean isPersisted() {
         return isPersisted;
@@ -510,6 +540,8 @@
     /**
      * Set to the interval between occurrences of this job. This value is <b>not</b> set if the
      * job does not recur periodically.
+     * @see JobInfo.Builder#setPeriodic(long)
+     * @see JobInfo.Builder#setPeriodic(long, long)
      */
     public long getIntervalMillis() {
         return intervalMillis;
@@ -518,6 +550,8 @@
     /**
      * Flex time for this job. Only valid if this is a periodic job.  The job can
      * execute at any time in a window of flex length at the end of the period.
+     * @see JobInfo.Builder#setPeriodic(long)
+     * @see JobInfo.Builder#setPeriodic(long, long)
      */
     public long getFlexMillis() {
         return flexMillis;
@@ -527,6 +561,7 @@
      * The amount of time the JobScheduler will wait before rescheduling a failed job. This value
      * will be increased depending on the backoff policy specified at job creation time. Defaults
      * to 30 seconds, minimum is currently 10 seconds.
+     * @see JobInfo.Builder#setBackoffCriteria(long, int)
      */
     public long getInitialBackoffMillis() {
         return initialBackoffMillis;
@@ -534,12 +569,27 @@
 
     /**
      * Return the backoff policy of this job.
+     * @see JobInfo.Builder#setBackoffCriteria(long, int)
      */
     public @BackoffPolicy int getBackoffPolicy() {
         return backoffPolicy;
     }
 
     /**
+     * @see JobInfo.Builder#setImportantWhileForeground(boolean)
+     */
+    public boolean isImportantWhileForeground() {
+        return (flags & FLAG_IMPORTANT_WHILE_FOREGROUND) != 0;
+    }
+
+    /**
+     * @see JobInfo.Builder#setPrefetch(boolean)
+     */
+    public boolean isPrefetch() {
+        return (flags & FLAG_PREFETCH) != 0;
+    }
+
+    /**
      * User can specify an early constraint of 0L, which is valid, so we keep track of whether the
      * function was called at all.
      * @hide
@@ -610,7 +660,10 @@
         if (!Objects.equals(networkRequest, j.networkRequest)) {
             return false;
         }
-        if (networkBytes != j.networkBytes) {
+        if (networkDownloadBytes != j.networkDownloadBytes) {
+            return false;
+        }
+        if (networkUploadBytes != j.networkUploadBytes) {
             return false;
         }
         if (minLatencyMillis != j.minLatencyMillis) {
@@ -673,7 +726,8 @@
         if (networkRequest != null) {
             hashCode = 31 * hashCode + networkRequest.hashCode();
         }
-        hashCode = 31 * hashCode + Long.hashCode(networkBytes);
+        hashCode = 31 * hashCode + Long.hashCode(networkDownloadBytes);
+        hashCode = 31 * hashCode + Long.hashCode(networkUploadBytes);
         hashCode = 31 * hashCode + Long.hashCode(minLatencyMillis);
         hashCode = 31 * hashCode + Long.hashCode(maxExecutionDelayMillis);
         hashCode = 31 * hashCode + Boolean.hashCode(isPeriodic);
@@ -708,7 +762,8 @@
         } else {
             networkRequest = null;
         }
-        networkBytes = in.readLong();
+        networkDownloadBytes = in.readLong();
+        networkUploadBytes = in.readLong();
         minLatencyMillis = in.readLong();
         maxExecutionDelayMillis = in.readLong();
         isPeriodic = in.readInt() == 1;
@@ -737,7 +792,8 @@
         triggerContentUpdateDelay = b.mTriggerContentUpdateDelay;
         triggerContentMaxDelay = b.mTriggerContentMaxDelay;
         networkRequest = b.mNetworkRequest;
-        networkBytes = b.mNetworkBytes;
+        networkDownloadBytes = b.mNetworkDownloadBytes;
+        networkUploadBytes = b.mNetworkUploadBytes;
         minLatencyMillis = b.mMinLatencyMillis;
         maxExecutionDelayMillis = b.mMaxExecutionDelayMillis;
         isPeriodic = b.mIsPeriodic;
@@ -780,7 +836,8 @@
         } else {
             out.writeInt(0);
         }
-        out.writeLong(networkBytes);
+        out.writeLong(networkDownloadBytes);
+        out.writeLong(networkUploadBytes);
         out.writeLong(minLatencyMillis);
         out.writeLong(maxExecutionDelayMillis);
         out.writeInt(isPeriodic ? 1 : 0);
@@ -914,7 +971,8 @@
         // Requirements.
         private int mConstraintFlags;
         private NetworkRequest mNetworkRequest;
-        private long mNetworkBytes = NETWORK_BYTES_UNKNOWN;
+        private long mNetworkDownloadBytes = NETWORK_BYTES_UNKNOWN;
+        private long mNetworkUploadBytes = NETWORK_BYTES_UNKNOWN;
         private ArrayList<TriggerContentUri> mTriggerContentUris;
         private long mTriggerContentUpdateDelay = -1;
         private long mTriggerContentMaxDelay = -1;
@@ -965,6 +1023,7 @@
         /**
          * Set optional extras. This is persisted, so we only allow primitive types.
          * @param extras Bundle containing extras you want the scheduler to hold on to for you.
+         * @see JobInfo#getExtras()
          */
         public Builder setExtras(@NonNull PersistableBundle extras) {
             mExtras = extras;
@@ -979,6 +1038,7 @@
          * {@link android.app.job.JobInfo.Builder#build()} is called.</p>
          *
          * @param extras Bundle containing extras you want the scheduler to hold on to for you.
+         * @see JobInfo#getTransientExtras()
          */
         public Builder setTransientExtras(@NonNull Bundle extras) {
             mTransientExtras = extras;
@@ -1006,6 +1066,8 @@
          * a combination of {@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION},
          * {@link android.content.Intent#FLAG_GRANT_WRITE_URI_PERMISSION}, and
          * {@link android.content.Intent#FLAG_GRANT_PREFIX_URI_PERMISSION}.
+         * @see JobInfo#getClipData()
+         * @see JobInfo#getClipGrantFlags()
          */
         public Builder setClipData(@Nullable ClipData clip, int grantFlags) {
             mClipData = clip;
@@ -1096,6 +1158,16 @@
         }
 
         /**
+         * @deprecated replaced by
+         *             {@link #setEstimatedNetworkBytes(long, long)}.
+         * @removed
+         */
+        @Deprecated
+        public Builder setEstimatedNetworkBytes(@BytesLong long networkBytes) {
+            return setEstimatedNetworkBytes(networkBytes, NETWORK_BYTES_UNKNOWN);
+        }
+
+        /**
          * Set the estimated size of network traffic that will be performed by
          * this job, in bytes.
          * <p>
@@ -1112,23 +1184,30 @@
          * <li>A job that synchronizes email could end up using an extreme range
          * of data, from under 1KB when nothing has changed, to dozens of MB
          * when there are new emails with attachments. Jobs that cannot provide
-         * reasonable estimates should leave this estimated value undefined.
+         * reasonable estimates should use the sentinel value
+         * {@link JobInfo#NETWORK_BYTES_UNKNOWN}.
          * </ul>
          * Note that the system may choose to delay jobs with large network
          * usage estimates when the device has a poor network connection, in
          * order to save battery.
+         * <p>
+         * The values provided here only reflect the traffic that will be
+         * performed by the base job; if you're using {@link JobWorkItem} then
+         * you also need to define the network traffic used by each work item
+         * when constructing them.
          *
-         * @param networkBytes The estimated size of network traffic that will
-         *            be performed by this job, in bytes. This value only
-         *            reflects the traffic that will be performed by the base
-         *            job; if you're using {@link JobWorkItem} then you also
-         *            need to define the network traffic used by each work item
-         *            when constructing them.
-         * @see JobInfo#getEstimatedNetworkBytes()
-         * @see JobWorkItem#JobWorkItem(android.content.Intent, long)
+         * @param downloadBytes The estimated size of network traffic that will
+         *            be downloaded by this job, in bytes.
+         * @param uploadBytes The estimated size of network traffic that will be
+         *            uploaded by this job, in bytes.
+         * @see JobInfo#getEstimatedNetworkDownloadBytes()
+         * @see JobInfo#getEstimatedNetworkUploadBytes()
+         * @see JobWorkItem#JobWorkItem(android.content.Intent, long, long)
          */
-        public Builder setEstimatedNetworkBytes(@BytesLong long networkBytes) {
-            mNetworkBytes = networkBytes;
+        public Builder setEstimatedNetworkBytes(@BytesLong long downloadBytes,
+                @BytesLong long uploadBytes) {
+            mNetworkDownloadBytes = downloadBytes;
+            mNetworkUploadBytes = uploadBytes;
             return this;
         }
 
@@ -1146,6 +1225,7 @@
          *
          * @param requiresCharging Pass {@code true} to require that the device be
          *     charging in order to run the job.
+         * @see JobInfo#isRequireCharging()
          */
         public Builder setRequiresCharging(boolean requiresCharging) {
             mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_CHARGING)
@@ -1159,6 +1239,7 @@
          * is not low, which is generally the point where the user is given a "low battery"
          * warning.
          * @param batteryNotLow Whether or not the device's battery level must not be low.
+         * @see JobInfo#isRequireBatteryNotLow()
          */
         public Builder setRequiresBatteryNotLow(boolean batteryNotLow) {
             mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_BATTERY_NOT_LOW)
@@ -1183,6 +1264,7 @@
          *
          * @param requiresDeviceIdle Pass {@code true} to prevent the job from running
          *     while the device is being used interactively.
+         * @see JobInfo#isRequireDeviceIdle()
          */
         public Builder setRequiresDeviceIdle(boolean requiresDeviceIdle) {
             mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_DEVICE_IDLE)
@@ -1196,6 +1278,7 @@
          * in a low storage state, which is generally the point where the user is given a
          * "low storage" warning.
          * @param storageNotLow Whether or not the device's available storage must not be low.
+         * @see JobInfo#isRequireStorageNotLow()
          */
         public Builder setRequiresStorageNotLow(boolean storageNotLow) {
             mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_STORAGE_NOT_LOW)
@@ -1228,6 +1311,7 @@
          *      job}
          *
          * @param uri The content: URI to monitor.
+         * @see JobInfo#getTriggerContentUris()
          */
         public Builder addTriggerContentUri(@NonNull TriggerContentUri uri) {
             if (mTriggerContentUris == null) {
@@ -1242,6 +1326,7 @@
          * the job is scheduled.  If there are more changes during that time, the delay
          * will be reset to start at the time of the most recent change.
          * @param durationMs Delay after most recent content change, in milliseconds.
+         * @see JobInfo#getTriggerContentUpdateDelay()
          */
         public Builder setTriggerContentUpdateDelay(long durationMs) {
             mTriggerContentUpdateDelay = durationMs;
@@ -1252,6 +1337,7 @@
          * Set the maximum total delay (in milliseconds) that is allowed from the first
          * time a content change is detected until the job is scheduled.
          * @param durationMs Delay after initial content change, in milliseconds.
+         * @see JobInfo#getTriggerContentMaxDelay()
          */
         public Builder setTriggerContentMaxDelay(long durationMs) {
             mTriggerContentMaxDelay = durationMs;
@@ -1265,6 +1351,8 @@
          * Setting this function on the builder with {@link #setMinimumLatency(long)} or
          * {@link #setOverrideDeadline(long)} will result in an error.
          * @param intervalMillis Millisecond interval for which this job will repeat.
+         * @see JobInfo#getIntervalMillis()
+         * @see JobInfo#getFlexMillis()
          */
         public Builder setPeriodic(long intervalMillis) {
             return setPeriodic(intervalMillis, intervalMillis);
@@ -1278,6 +1366,8 @@
          * @param flexMillis Millisecond flex for this job. Flex is clamped to be at least
          *                   {@link #getMinFlexMillis()} or 5 percent of the period, whichever is
          *                   higher.
+         * @see JobInfo#getIntervalMillis()
+         * @see JobInfo#getFlexMillis()
          */
         public Builder setPeriodic(long intervalMillis, long flexMillis) {
             final long minPeriod = getMinPeriodMillis();
@@ -1309,6 +1399,7 @@
          * {@link android.app.job.JobInfo.Builder#build()} is called.
          * @param minLatencyMillis Milliseconds before which this job will not be considered for
          *                         execution.
+         * @see JobInfo#getMinLatencyMillis()
          */
         public Builder setMinimumLatency(long minLatencyMillis) {
             mMinLatencyMillis = minLatencyMillis;
@@ -1322,6 +1413,7 @@
          * this property on a periodic job, doing so will throw an
          * {@link java.lang.IllegalArgumentException} when
          * {@link android.app.job.JobInfo.Builder#build()} is called.
+         * @see JobInfo#getMaxExecutionDelayMillis()
          */
         public Builder setOverrideDeadline(long maxExecutionDelayMillis) {
             mMaxExecutionDelayMillis = maxExecutionDelayMillis;
@@ -1341,6 +1433,8 @@
          * mode.
          * @param initialBackoffMillis Millisecond time interval to wait initially when job has
          *                             failed.
+         * @see JobInfo#getInitialBackoffMillis()
+         * @see JobInfo#getBackoffPolicy()
          */
         public Builder setBackoffCriteria(long initialBackoffMillis,
                 @BackoffPolicy int backoffPolicy) {
@@ -1371,6 +1465,7 @@
          *
          * @param importantWhileForeground whether to relax doze restrictions for this job when the
          *                                 app is in the foreground. False by default.
+         * @see JobInfo#isImportantWhileForeground()
          */
         public Builder setImportantWhileForeground(boolean importantWhileForeground) {
             if (importantWhileForeground) {
@@ -1382,6 +1477,15 @@
         }
 
         /**
+         * @removed
+         * @deprecated replaced with {@link #setPrefetch(boolean)}
+         */
+        @Deprecated
+        public Builder setIsPrefetch(boolean isPrefetch) {
+            return setPrefetch(isPrefetch);
+        }
+
+        /**
          * Setting this to true indicates that this job is designed to prefetch
          * content that will make a material improvement to the experience of
          * the specific user of this device. For example, fetching top headlines
@@ -1393,12 +1497,13 @@
          * network when there is a surplus of metered data available. The system
          * may also use this signal in combination with end user usage patterns
          * to ensure data is prefetched before the user launches your app.
+         * @see JobInfo#isPrefetch()
          */
-        public Builder setIsPrefetch(boolean isPrefetch) {
-            if (isPrefetch) {
-                mFlags |= FLAG_IS_PREFETCH;
+        public Builder setPrefetch(boolean prefetch) {
+            if (prefetch) {
+                mFlags |= FLAG_PREFETCH;
             } else {
-                mFlags &= (~FLAG_IS_PREFETCH);
+                mFlags &= (~FLAG_PREFETCH);
             }
             return this;
         }
@@ -1408,6 +1513,7 @@
          *
          * @param isPersisted True to indicate that the job will be written to
          *            disk and loaded at boot.
+         * @see JobInfo#isPersisted()
          */
         @RequiresPermission(android.Manifest.permission.RECEIVE_BOOT_COMPLETED)
         public Builder setPersisted(boolean isPersisted) {
@@ -1427,7 +1533,7 @@
                         "constraints, this is not allowed.");
             }
             // Check that network estimates require network type
-            if (mNetworkBytes > 0 && mNetworkRequest == null) {
+            if ((mNetworkDownloadBytes > 0 || mNetworkUploadBytes > 0) && mNetworkRequest == null) {
                 throw new IllegalArgumentException(
                         "Can't provide estimated network usage without requiring a network");
             }
diff --git a/core/java/android/app/job/JobWorkItem.java b/core/java/android/app/job/JobWorkItem.java
index 1c46e8e..995f522 100644
--- a/core/java/android/app/job/JobWorkItem.java
+++ b/core/java/android/app/job/JobWorkItem.java
@@ -16,6 +16,8 @@
 
 package android.app.job;
 
+import static android.app.job.JobInfo.NETWORK_BYTES_UNKNOWN;
+
 import android.annotation.BytesLong;
 import android.content.Intent;
 import android.os.Parcel;
@@ -28,7 +30,8 @@
  */
 final public class JobWorkItem implements Parcelable {
     final Intent mIntent;
-    final long mNetworkBytes;
+    final long mNetworkDownloadBytes;
+    final long mNetworkUploadBytes;
     int mDeliveryCount;
     int mWorkId;
     Object mGrants;
@@ -41,22 +44,36 @@
      */
     public JobWorkItem(Intent intent) {
         mIntent = intent;
-        mNetworkBytes = JobInfo.NETWORK_BYTES_UNKNOWN;
+        mNetworkDownloadBytes = NETWORK_BYTES_UNKNOWN;
+        mNetworkUploadBytes = NETWORK_BYTES_UNKNOWN;
+    }
+
+    /**
+     * @deprecated replaced by {@link #JobWorkItem(Intent, long, long)}
+     * @removed
+     */
+    @Deprecated
+    public JobWorkItem(Intent intent, @BytesLong long networkBytes) {
+        this(intent, networkBytes, NETWORK_BYTES_UNKNOWN);
     }
 
     /**
      * Create a new piece of work, which can be submitted to
      * {@link JobScheduler#enqueue JobScheduler.enqueue}.
+     * <p>
+     * See {@link JobInfo.Builder#setEstimatedNetworkBytes(long, long)} for
+     * details about how to estimate network traffic.
      *
      * @param intent The general Intent describing this work.
-     * @param networkBytes The estimated size of network traffic that will be
-     *            performed by this job work item, in bytes. See
-     *            {@link JobInfo.Builder#setEstimatedNetworkBytes(long)} for
-     *            details about how to estimate.
+     * @param downloadBytes The estimated size of network traffic that will be
+     *            downloaded by this job work item, in bytes.
+     * @param uploadBytes The estimated size of network traffic that will be
+     *            uploaded by this job work item, in bytes.
      */
-    public JobWorkItem(Intent intent, @BytesLong long networkBytes) {
+    public JobWorkItem(Intent intent, @BytesLong long downloadBytes, @BytesLong long uploadBytes) {
         mIntent = intent;
-        mNetworkBytes = networkBytes;
+        mNetworkDownloadBytes = downloadBytes;
+        mNetworkUploadBytes = uploadBytes;
     }
 
     /**
@@ -67,14 +84,44 @@
     }
 
     /**
-     * Return the estimated size of network traffic that will be performed by
+     * @deprecated replaced by {@link #getEstimatedNetworkDownloadBytes()} and
+     *             {@link #getEstimatedNetworkUploadBytes()}.
+     * @removed
+     */
+    @Deprecated
+    public @BytesLong long getEstimatedNetworkBytes() {
+        if (mNetworkDownloadBytes == NETWORK_BYTES_UNKNOWN
+                && mNetworkUploadBytes == NETWORK_BYTES_UNKNOWN) {
+            return NETWORK_BYTES_UNKNOWN;
+        } else if (mNetworkDownloadBytes == NETWORK_BYTES_UNKNOWN) {
+            return mNetworkUploadBytes;
+        } else if (mNetworkUploadBytes == NETWORK_BYTES_UNKNOWN) {
+            return mNetworkDownloadBytes;
+        } else {
+            return mNetworkDownloadBytes + mNetworkUploadBytes;
+        }
+    }
+
+    /**
+     * Return the estimated size of download traffic that will be performed by
+     * this job, in bytes.
+     *
+     * @return Estimated size of download traffic, or
+     *         {@link JobInfo#NETWORK_BYTES_UNKNOWN} when unknown.
+     */
+    public @BytesLong long getEstimatedNetworkDownloadBytes() {
+        return mNetworkDownloadBytes;
+    }
+
+    /**
+     * Return the estimated size of upload traffic that will be performed by
      * this job work item, in bytes.
      *
-     * @return estimated size, or {@link JobInfo#NETWORK_BYTES_UNKNOWN} when
-     *         unknown.
+     * @return Estimated size of upload traffic, or
+     *         {@link JobInfo#NETWORK_BYTES_UNKNOWN} when unknown.
      */
-    public @BytesLong long getEstimatedNetworkBytes() {
-        return mNetworkBytes;
+    public @BytesLong long getEstimatedNetworkUploadBytes() {
+        return mNetworkUploadBytes;
     }
 
     /**
@@ -128,9 +175,13 @@
         sb.append(mWorkId);
         sb.append(" intent=");
         sb.append(mIntent);
-        if (mNetworkBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
-            sb.append(" networkBytes=");
-            sb.append(mNetworkBytes);
+        if (mNetworkDownloadBytes != NETWORK_BYTES_UNKNOWN) {
+            sb.append(" downloadBytes=");
+            sb.append(mNetworkDownloadBytes);
+        }
+        if (mNetworkUploadBytes != NETWORK_BYTES_UNKNOWN) {
+            sb.append(" uploadBytes=");
+            sb.append(mNetworkUploadBytes);
         }
         if (mDeliveryCount != 0) {
             sb.append(" dcount=");
@@ -151,7 +202,8 @@
         } else {
             out.writeInt(0);
         }
-        out.writeLong(mNetworkBytes);
+        out.writeLong(mNetworkDownloadBytes);
+        out.writeLong(mNetworkUploadBytes);
         out.writeInt(mDeliveryCount);
         out.writeInt(mWorkId);
     }
@@ -173,7 +225,8 @@
         } else {
             mIntent = null;
         }
-        mNetworkBytes = in.readLong();
+        mNetworkDownloadBytes = in.readLong();
+        mNetworkUploadBytes = in.readLong();
         mDeliveryCount = in.readInt();
         mWorkId = in.readInt();
     }
diff --git a/core/java/android/app/servertransaction/TransactionExecutorHelper.java b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
index 7e66fd7..01b13a2 100644
--- a/core/java/android/app/servertransaction/TransactionExecutorHelper.java
+++ b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
@@ -26,7 +26,7 @@
 import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE;
 import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED;
 
-import android.app.ActivityThread;
+import android.app.ActivityThread.ActivityClientRecord;
 import android.util.IntArray;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -124,7 +124,7 @@
      *         {@link ActivityLifecycleItem#UNDEFINED} if there is not path.
      */
     @VisibleForTesting
-    public int getClosestPreExecutionState(ActivityThread.ActivityClientRecord r,
+    public int getClosestPreExecutionState(ActivityClientRecord r,
             int postExecutionState) {
         switch (postExecutionState) {
             case UNDEFINED:
@@ -147,7 +147,7 @@
      *         were provided or there is not path.
      */
     @VisibleForTesting
-    public int getClosestOfStates(ActivityThread.ActivityClientRecord r, int[] finalStates) {
+    public int getClosestOfStates(ActivityClientRecord r, int[] finalStates) {
         if (finalStates == null || finalStates.length == 0) {
             return UNDEFINED;
         }
@@ -168,6 +168,27 @@
         return closestState;
     }
 
+    /** Get the lifecycle state request to match the current state in the end of a transaction. */
+    public static ActivityLifecycleItem getLifecycleRequestForCurrentState(ActivityClientRecord r) {
+        final int prevState = r.getLifecycleState();
+        final ActivityLifecycleItem lifecycleItem;
+        switch (prevState) {
+            // TODO(lifecycler): Extend to support all possible states.
+            case ON_PAUSE:
+                lifecycleItem = PauseActivityItem.obtain();
+                break;
+            case ON_STOP:
+                lifecycleItem = StopActivityItem.obtain(r.isVisibleFromServer(),
+                        0 /* configChanges */);
+                break;
+            default:
+                lifecycleItem = ResumeActivityItem.obtain(false /* isForward */);
+                break;
+        }
+
+        return lifecycleItem;
+    }
+
     /**
      * Check if there is a destruction involved in the path. We want to avoid a lifecycle sequence
      * that involves destruction and recreation if there is another path.
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 387a836..e85058d 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1101,6 +1101,58 @@
     /** @hide */
     public String[] splitClassLoaderNames;
 
+    /**
+     * Represents the default policy. The actual policy used will depend on other properties of
+     * the application, e.g. the target SDK version.
+     * @hide
+     */
+    public static final int HIDDEN_API_ENFORCEMENT_DEFAULT = -1;
+    /**
+     * No API enforcement; the app can access the entire internal private API. Only for use by
+     * system apps.
+     * @hide
+     */
+    public static final int HIDDEN_API_ENFORCEMENT_NONE = 0;
+    /**
+     * Light grey list enforcement, the strictest option. Enforces the light grey, dark grey and
+     * black lists.
+     * @hide
+     * */
+    public static final int HIDDEN_API_ENFORCEMENT_ALL_LISTS = 1;
+    /**
+     * Dark grey list enforcement. Enforces the dark grey and black lists
+     * @hide
+     */
+    public static final int HIDDEN_API_ENFORCEMENT_DARK_GREY_AND_BLACK = 2;
+    /**
+     * Blacklist enforcement only.
+     * @hide
+     */
+    public static final int HIDDEN_API_ENFORCEMENT_BLACK = 3;
+
+    private static final int HIDDEN_API_ENFORCEMENT_MAX = HIDDEN_API_ENFORCEMENT_BLACK;
+
+    /**
+     * Values in this IntDef MUST be kept in sync with enum hiddenapi::EnforcementPolicy in
+     * art/runtime/hidden_api.h
+     * @hide
+     */
+    @IntDef(prefix = { "HIDDEN_API_ENFORCEMENT_" }, value = {
+            HIDDEN_API_ENFORCEMENT_DEFAULT,
+            HIDDEN_API_ENFORCEMENT_NONE,
+            HIDDEN_API_ENFORCEMENT_ALL_LISTS,
+            HIDDEN_API_ENFORCEMENT_DARK_GREY_AND_BLACK,
+            HIDDEN_API_ENFORCEMENT_BLACK,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface HiddenApiEnforcementPolicy {}
+
+    private boolean isValidHiddenApiEnforcementPolicy(int policy) {
+        return policy >= HIDDEN_API_ENFORCEMENT_DEFAULT && policy <= HIDDEN_API_ENFORCEMENT_MAX;
+    }
+
+    private int mHiddenApiPolicy = HIDDEN_API_ENFORCEMENT_DEFAULT;
+
     public void dump(Printer pw, String prefix) {
         dump(pw, prefix, DUMP_FLAG_ALL);
     }
@@ -1188,7 +1240,7 @@
             if (category != CATEGORY_UNDEFINED) {
                 pw.println(prefix + "category=" + category);
             }
-            pw.println(prefix + "isAllowedToUseHiddenApi=" + isAllowedToUseHiddenApi());
+            pw.println(prefix + "HiddenApiEnforcementPolicy=" + getHiddenApiEnforcementPolicy());
         }
         super.dumpBack(pw, prefix);
     }
@@ -1386,6 +1438,7 @@
         appComponentFactory = orig.appComponentFactory;
         compileSdkVersion = orig.compileSdkVersion;
         compileSdkVersionCodename = orig.compileSdkVersionCodename;
+        mHiddenApiPolicy = orig.mHiddenApiPolicy;
     }
 
     public String toString() {
@@ -1459,6 +1512,7 @@
         dest.writeInt(compileSdkVersion);
         dest.writeString(compileSdkVersionCodename);
         dest.writeString(appComponentFactory);
+        dest.writeInt(mHiddenApiPolicy);
     }
 
     public static final Parcelable.Creator<ApplicationInfo> CREATOR
@@ -1529,6 +1583,7 @@
         compileSdkVersion = source.readInt();
         compileSdkVersionCodename = source.readString();
         appComponentFactory = source.readString();
+        mHiddenApiPolicy = source.readInt();
     }
 
     /**
@@ -1599,13 +1654,31 @@
         }
     }
 
+    private boolean isPackageWhitelistedForHiddenApis() {
+        return SystemConfig.getInstance().getHiddenApiWhitelistedApps().contains(packageName);
+    }
+
     /**
      * @hide
      */
-    public boolean isAllowedToUseHiddenApi() {
-        boolean whitelisted =
-                SystemConfig.getInstance().getHiddenApiWhitelistedApps().contains(packageName);
-        return whitelisted && (isSystemApp() || isUpdatedSystemApp());
+    public @HiddenApiEnforcementPolicy int getHiddenApiEnforcementPolicy() {
+        if (mHiddenApiPolicy != HIDDEN_API_ENFORCEMENT_DEFAULT) {
+            return mHiddenApiPolicy;
+        }
+        if (isPackageWhitelistedForHiddenApis() && (isSystemApp() || isUpdatedSystemApp())) {
+            return HIDDEN_API_ENFORCEMENT_NONE;
+        }
+        return HIDDEN_API_ENFORCEMENT_BLACK;
+    }
+
+    /**
+     * @hide
+     */
+    public void setHiddenApiEnforcementPolicy(@HiddenApiEnforcementPolicy int policy) {
+        if (!isValidHiddenApiEnforcementPolicy(policy)) {
+            throw new IllegalArgumentException("Invalid API enforcement policy: " + policy);
+        }
+        mHiddenApiPolicy = policy;
     }
 
     /**
diff --git a/core/java/android/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
index 56cba79..c6c676f 100644
--- a/core/java/android/database/sqlite/SQLiteQueryBuilder.java
+++ b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
@@ -31,7 +31,7 @@
 import java.util.regex.Pattern;
 
 /**
- * This is a convience class that helps build SQL queries to be sent to
+ * This is a convenience class that helps build SQL queries to be sent to
  * {@link SQLiteDatabase} objects.
  */
 public class SQLiteQueryBuilder
diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl
index 91bbdc7..6d9c913 100644
--- a/core/java/android/hardware/usb/IUsbManager.aidl
+++ b/core/java/android/hardware/usb/IUsbManager.aidl
@@ -105,6 +105,12 @@
     /* Gets the current screen unlocked functions. */
     long getScreenUnlockedFunctions();
 
+    /* Get the functionfs control handle for the given function. Usb
+     * descriptors will already be written, and the handle will be
+     * ready to use.
+     */
+    ParcelFileDescriptor getControlFd(long function);
+
     /* Allow USB debugging from the attached host. If alwaysAllow is true, add the
      * the public key to list of host keys that the user has approved.
      */
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 572c585..46142e3 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -192,14 +192,6 @@
     public static final String USB_DATA_UNLOCKED = "unlocked";
 
     /**
-     * Boolean extra indicating whether the intent represents a change in the usb
-     * configuration (as opposed to a state update).
-     *
-     * {@hide}
-     */
-    public static final String USB_CONFIG_CHANGED = "config_changed";
-
-    /**
      * A placeholder indicating that no USB function is being specified.
      * Used for compatibility with old init scripts to indicate no functions vs. charging function.
      *
@@ -471,6 +463,25 @@
     }
 
     /**
+     * Gets the functionfs control file descriptor for the given function, with
+     * the usb descriptors and strings already written. The file descriptor is used
+     * by the function implementation to handle events and control requests.
+     *
+     * @param function to get control fd for. Currently {@link #FUNCTION_MTP} and
+     * {@link #FUNCTION_PTP} are supported.
+     * @return A ParcelFileDescriptor holding the valid fd, or null if the fd was not found.
+     *
+     * {@hide}
+     */
+    public ParcelFileDescriptor getControlFd(long function) {
+        try {
+            return mService.getControlFd(function);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Returns true if the caller has permission to access the device.
      * Permission might have been granted temporarily via
      * {@link #requestPermission(UsbDevice, PendingIntent)} or
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index 7922276..40d53b7 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -16,7 +16,6 @@
 
 package android.net;
 
-import android.annotation.RequiresPermission;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
@@ -259,30 +258,47 @@
     /**
      * Set specific UID to use when accounting {@link Socket} traffic
      * originating from the current thread. Designed for use when performing an
-     * operation on behalf of another application.
+     * operation on behalf of another application, or when another application
+     * is performing operations on your behalf.
+     * <p>
+     * Any app can <em>accept</em> blame for traffic performed on a socket
+     * originally created by another app by calling this method with the
+     * {@link android.system.Os#getuid()} value. However, only apps holding the
+     * {@code android.Manifest.permission#UPDATE_DEVICE_STATS} permission may
+     * <em>assign</em> blame to another UIDs.
      * <p>
      * Changes only take effect during subsequent calls to
      * {@link #tagSocket(Socket)}.
-     * <p>
-     * To take effect, caller must hold
-     * {@link android.Manifest.permission#UPDATE_DEVICE_STATS} permission.
-     *
-     * @hide
      */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+    @SuppressLint("Doclava125")
     public static void setThreadStatsUid(int uid) {
         NetworkManagementSocketTagger.setThreadSocketStatsUid(uid);
     }
 
     /**
+     * Get the active UID used when accounting {@link Socket} traffic originating
+     * from the current thread. Only one active tag per thread is supported.
+     * {@link #tagSocket(Socket)}.
+     *
+     * @see #setThreadStatsUid(int)
+     */
+    public static int getThreadStatsUid() {
+        return NetworkManagementSocketTagger.getThreadSocketStatsUid();
+    }
+
+    /**
      * Set specific UID to use when accounting {@link Socket} traffic
      * originating from the current thread as the calling UID. Designed for use
      * when another application is performing operations on your behalf.
      * <p>
      * Changes only take effect during subsequent calls to
      * {@link #tagSocket(Socket)}.
+     *
+     * @removed
+     * @deprecated use {@link #setThreadStatsUid(int)} instead.
      */
+    @Deprecated
     public static void setThreadStatsUidSelf() {
         setThreadStatsUid(android.os.Process.myUid());
     }
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index df6ce8e..e22c65f 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -242,7 +242,9 @@
          * Possible values are defined in {@link Build.VERSION_CODES}.
          *
          * @see #SDK_INT
+         * @hide
          */
+        @TestApi
         public static final int FIRST_SDK_INT = SystemProperties
                 .getInt("ro.product.first_api_level", 0);
 
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 1160415..88d6e84 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -411,6 +411,9 @@
                 checkpoint = 0;
             }
         }
+        if (listener != null) {
+            listener.onProgress(progress);
+        }
         return progress;
     }
 
@@ -440,6 +443,9 @@
                 checkpoint = 0;
             }
         }
+        if (listener != null) {
+            listener.onProgress(progress);
+        }
         return progress;
     }
 
@@ -479,6 +485,9 @@
                 checkpoint = 0;
             }
         }
+        if (listener != null) {
+            listener.onProgress(progress);
+        }
         return progress;
     }
 
diff --git a/core/java/android/os/SystemClock.java b/core/java/android/os/SystemClock.java
index 0f70427..b254166 100644
--- a/core/java/android/os/SystemClock.java
+++ b/core/java/android/os/SystemClock.java
@@ -269,6 +269,7 @@
      * time or throw.
      *
      * @throws DateTimeException when no accurate network time can be provided.
+     * @hide
      */
     public static long currentNetworkTimeMillis() {
         final IAlarmManager mgr = IAlarmManager.Stub
@@ -302,6 +303,7 @@
      * time or throw.
      *
      * @throws DateTimeException when no accurate network time can be provided.
+     * @hide
      */
     public static @NonNull Clock currentNetworkTimeClock() {
         return new SimpleClock(ZoneOffset.UTC) {
diff --git a/core/java/android/provider/BlockedNumberContract.java b/core/java/android/provider/BlockedNumberContract.java
index 8aef012..67c6fb9 100644
--- a/core/java/android/provider/BlockedNumberContract.java
+++ b/core/java/android/provider/BlockedNumberContract.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.net.Uri;
 import android.os.Bundle;
+import android.telecom.Log;
 
 /**
  * <p>
@@ -261,9 +262,16 @@
      */
     @WorkerThread
     public static boolean isBlocked(Context context, String phoneNumber) {
-        final Bundle res = context.getContentResolver().call(
-                AUTHORITY_URI, METHOD_IS_BLOCKED, phoneNumber, null);
-        return res != null && res.getBoolean(RES_NUMBER_IS_BLOCKED, false);
+        try {
+            final Bundle res = context.getContentResolver().call(
+                    AUTHORITY_URI, METHOD_IS_BLOCKED, phoneNumber, null);
+            return res != null && res.getBoolean(RES_NUMBER_IS_BLOCKED, false);
+        } catch (NullPointerException | IllegalArgumentException ex) {
+            // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
+            // either of these happen.
+            Log.w(null, "isBlocked: provider not ready.");
+            return false;
+        }
     }
 
     /**
@@ -297,9 +305,16 @@
      * @return {@code true} if the current user can block numbers.
      */
     public static boolean canCurrentUserBlockNumbers(Context context) {
-        final Bundle res = context.getContentResolver().call(
-                AUTHORITY_URI, METHOD_CAN_CURRENT_USER_BLOCK_NUMBERS, null, null);
-        return res != null && res.getBoolean(RES_CAN_BLOCK_NUMBERS, false);
+        try {
+            final Bundle res = context.getContentResolver().call(
+                    AUTHORITY_URI, METHOD_CAN_CURRENT_USER_BLOCK_NUMBERS, null, null);
+            return res != null && res.getBoolean(RES_CAN_BLOCK_NUMBERS, false);
+        } catch (NullPointerException | IllegalArgumentException ex) {
+            // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
+            // either of these happen.
+            Log.w(null, "canCurrentUserBlockNumbers: provider not ready.");
+            return false;
+        }
     }
 
     /**
@@ -368,8 +383,14 @@
          * the provider unless {@link #endBlockSuppression(Context)} is called.
          */
         public static void notifyEmergencyContact(Context context) {
-            context.getContentResolver().call(
-                    AUTHORITY_URI, METHOD_NOTIFY_EMERGENCY_CONTACT, null, null);
+            try {
+                context.getContentResolver().call(
+                        AUTHORITY_URI, METHOD_NOTIFY_EMERGENCY_CONTACT, null, null);
+            } catch (NullPointerException | IllegalArgumentException ex) {
+                // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
+                // either of these happen.
+                Log.w(null, "notifyEmergencyContact: provider not ready.");
+            }
         }
 
         /**
@@ -394,9 +415,16 @@
          */
         public static boolean shouldSystemBlockNumber(Context context, String phoneNumber,
                 Bundle extras) {
-            final Bundle res = context.getContentResolver().call(
-                    AUTHORITY_URI, METHOD_SHOULD_SYSTEM_BLOCK_NUMBER, phoneNumber, extras);
-            return res != null && res.getBoolean(RES_NUMBER_IS_BLOCKED, false);
+            try {
+                final Bundle res = context.getContentResolver().call(
+                        AUTHORITY_URI, METHOD_SHOULD_SYSTEM_BLOCK_NUMBER, phoneNumber, extras);
+                return res != null && res.getBoolean(RES_NUMBER_IS_BLOCKED, false);
+            } catch (NullPointerException | IllegalArgumentException ex) {
+                // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
+                // either of these happen.
+                Log.w(null, "shouldSystemBlockNumber: provider not ready.");
+                return false;
+            }
         }
 
         /**
@@ -416,9 +444,16 @@
          * @return {@code true} if should show emergency call notification. {@code false} otherwise.
          */
         public static boolean shouldShowEmergencyCallNotification(Context context) {
-            final Bundle res = context.getContentResolver().call(
-                    AUTHORITY_URI, METHOD_SHOULD_SHOW_EMERGENCY_CALL_NOTIFICATION, null, null);
-            return res != null && res.getBoolean(RES_SHOW_EMERGENCY_CALL_NOTIFICATION, false);
+            try {
+                final Bundle res = context.getContentResolver().call(
+                        AUTHORITY_URI, METHOD_SHOULD_SHOW_EMERGENCY_CALL_NOTIFICATION, null, null);
+                return res != null && res.getBoolean(RES_SHOW_EMERGENCY_CALL_NOTIFICATION, false);
+            } catch (NullPointerException | IllegalArgumentException ex) {
+                // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
+                // either of these happen.
+                Log.w(null, "shouldShowEmergencyCallNotification: provider not ready.");
+                return false;
+            }
         }
 
         /**
@@ -436,9 +471,16 @@
         public static boolean getEnhancedBlockSetting(Context context, String key) {
             Bundle extras = new Bundle();
             extras.putString(EXTRA_ENHANCED_SETTING_KEY, key);
-            final Bundle res = context.getContentResolver().call(
-                    AUTHORITY_URI, METHOD_GET_ENHANCED_BLOCK_SETTING, null, extras);
-            return res != null && res.getBoolean(RES_ENHANCED_SETTING_IS_ENABLED, false);
+            try {
+                final Bundle res = context.getContentResolver().call(
+                        AUTHORITY_URI, METHOD_GET_ENHANCED_BLOCK_SETTING, null, extras);
+                return res != null && res.getBoolean(RES_ENHANCED_SETTING_IS_ENABLED, false);
+            } catch (NullPointerException | IllegalArgumentException ex) {
+                // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
+                // either of these happen.
+                Log.w(null, "getEnhancedBlockSetting: provider not ready.");
+                return false;
+            }
         }
 
         /**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index b58e5b3..9ecae8c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1348,10 +1348,18 @@
             = "android.settings.NOTIFICATION_SETTINGS";
 
     /**
+     * Activity Action: Show app listing settings, filtered by those that send notifications.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_ALL_APPS_NOTIFICATION_SETTINGS =
+            "android.settings.ALL_APPS_NOTIFICATION_SETTINGS";
+
+    /**
      * Activity Action: Show notification settings for a single app.
      * <p>
-     *     Input: {@link #EXTRA_APP_PACKAGE}, the package containing the channel to display.
-     *     Input: Optionally, {@link #EXTRA_CHANNEL_ID}, to highlight that channel.
+     *     Input: {@link #EXTRA_APP_PACKAGE}, the package to display.
      * <p>
      * Output: Nothing.
      */
diff --git a/core/java/android/util/DataUnit.java b/core/java/android/util/DataUnit.java
index ea4266e..cf045b8 100644
--- a/core/java/android/util/DataUnit.java
+++ b/core/java/android/util/DataUnit.java
@@ -29,6 +29,8 @@
  * "kibibyte" as an IEC unit of 1024 bytes.
  * <p>
  * This design is mirrored after {@link TimeUnit} and {@link ChronoUnit}.
+ *
+ * @hide
  */
 public enum DataUnit {
     KILOBYTES { @Override public long toBytes(long v) { return v * 1_000; } },
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 5bee87c..01fd090 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -274,6 +274,16 @@
     public static final int STATE_DISABLED_BY_SERVICE = 4;
 
     /**
+     * Same as {@link #STATE_UNKNOWN}, but used on
+     * {@link AutofillManagerClient#setSessionFinished(int)} when the session was finished because
+     * the URL bar changed on client mode
+     *
+     * @hide
+     */
+    public static final int STATE_UNKNOWN_COMPAT_MODE = 5;
+
+
+    /**
      * Timeout in ms for calls to the field classification service.
      * @hide
      */
@@ -1947,15 +1957,24 @@
      * Marks the state of the session as finished.
      *
      * @param newState {@link #STATE_FINISHED} (because the autofill service returned a {@code null}
-     *  FillResponse), {@link #STATE_UNKNOWN} (because the session was removed), or
-     *  {@link #STATE_DISABLED_BY_SERVICE} (because the autofill service disabled further autofill
-     *  requests for the activity).
+     *  FillResponse), {@link #STATE_UNKNOWN} (because the session was removed),
+     *  {@link #STATE_UNKNOWN_COMPAT_MODE} (beucase the session was finished when the URL bar
+     *  changed on compat mode), or {@link #STATE_DISABLED_BY_SERVICE} (because the autofill service
+     *  disabled further autofill requests for the activity).
      */
     private void setSessionFinished(int newState) {
         synchronized (mLock) {
-            if (sVerbose) Log.v(TAG, "setSessionFinished(): from " + mState + " to " + newState);
-            resetSessionLocked(/* resetEnteredIds= */ false);
-            mState = newState;
+            if (sVerbose) {
+                Log.v(TAG, "setSessionFinished(): from " + getStateAsStringLocked() + " to "
+                        + getStateAsString(newState));
+            }
+            if (newState == STATE_UNKNOWN_COMPAT_MODE) {
+                resetSessionLocked(/* resetEnteredIds= */ true);
+                mState = STATE_UNKNOWN;
+            } else {
+                resetSessionLocked(/* resetEnteredIds= */ false);
+                mState = newState;
+            }
         }
     }
 
@@ -2107,19 +2126,26 @@
 
     @GuardedBy("mLock")
     private String getStateAsStringLocked() {
-        switch (mState) {
+        return getStateAsString(mState);
+    }
+
+    @NonNull
+    private static String getStateAsString(int state) {
+        switch (state) {
             case STATE_UNKNOWN:
-                return "STATE_UNKNOWN";
+                return "UNKNOWN";
             case STATE_ACTIVE:
-                return "STATE_ACTIVE";
+                return "ACTIVE";
             case STATE_FINISHED:
-                return "STATE_FINISHED";
+                return "FINISHED";
             case STATE_SHOWING_SAVE_UI:
-                return "STATE_SHOWING_SAVE_UI";
+                return "SHOWING_SAVE_UI";
             case STATE_DISABLED_BY_SERVICE:
-                return "STATE_DISABLED_BY_SERVICE";
+                return "DISABLED_BY_SERVICE";
+            case STATE_UNKNOWN_COMPAT_MODE:
+                return "UNKNOWN_COMPAT_MODE";
             default:
-                return "INVALID:" + mState;
+                return "INVALID:" + state;
         }
     }
 
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 3775835..57d64b9 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -4824,13 +4824,23 @@
             return true;
         }
 
+        private boolean tooLargeTextForMagnifier() {
+            final float magnifierContentHeight = Math.round(
+                    mMagnifierAnimator.mMagnifier.getHeight()
+                            / mMagnifierAnimator.mMagnifier.getZoom());
+            final Paint.FontMetrics fontMetrics = mTextView.getPaint().getFontMetrics();
+            final float glyphHeight = fontMetrics.descent - fontMetrics.ascent;
+            return glyphHeight > magnifierContentHeight;
+        }
+
         protected final void updateMagnifier(@NonNull final MotionEvent event) {
             if (mMagnifierAnimator == null) {
                 return;
             }
 
             final PointF showPosInView = new PointF();
-            final boolean shouldShow = obtainMagnifierShowCoordinates(event, showPosInView);
+            final boolean shouldShow = !tooLargeTextForMagnifier()
+                    && obtainMagnifierShowCoordinates(event, showPosInView);
             if (shouldShow) {
                 // Make the cursor visible and stop blinking.
                 mRenderCursorRegardlessTiming = true;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 7cf3e10..7b1acb1 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -644,8 +644,12 @@
      */
     private Layout mSavedMarqueeModeLayout;
 
+    // Do not update following mText/mSpannable/mPrecomputed except for setTextInternal()
     @ViewDebug.ExportedProperty(category = "text")
-    private CharSequence mText;
+    private @Nullable CharSequence mText;
+    private @Nullable Spannable mSpannable;
+    private @Nullable PrecomputedText mPrecomputed;
+
     private CharSequence mTransformed;
     private BufferType mBufferType = BufferType.NORMAL;
 
@@ -874,7 +878,7 @@
             setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
         }
 
-        mText = "";
+        setTextInternal("");
 
         final Resources res = getResources();
         final CompatibilityInfo compat = res.getCompatibilityInfo();
@@ -1615,6 +1619,13 @@
         }
     }
 
+    // Update mText and mPrecomputed
+    private void setTextInternal(@Nullable CharSequence text) {
+        mText = text;
+        mSpannable = (text instanceof Spannable) ? (Spannable) text : null;
+        mPrecomputed = (text instanceof PrecomputedText) ? (PrecomputedText) text : null;
+    }
+
     /**
      * Specify whether this widget should automatically scale the text to try to perfectly fit
      * within the layout bounds by using the default auto-size configuration.
@@ -1973,9 +1984,9 @@
                         }
                     }
                 }
-            } else if (mText instanceof Spannable) {
+            } else if (mSpannable != null) {
                 // Reset the selection.
-                Selection.setSelection((Spannable) mText, getSelectionEnd());
+                Selection.setSelection(mSpannable, getSelectionEnd());
             }
         }
     }
@@ -2359,7 +2370,7 @@
         if (mMovement != movement) {
             mMovement = movement;
 
-            if (movement != null && !(mText instanceof Spannable)) {
+            if (movement != null && mSpannable == null) {
                 setText(mText);
             }
 
@@ -2409,8 +2420,8 @@
             return;
         }
         if (mTransformation != null) {
-            if (mText instanceof Spannable) {
-                ((Spannable) mText).removeSpan(mTransformation);
+            if (mSpannable != null) {
+                mSpannable.removeSpan(mTransformation);
             }
         }
 
@@ -5254,7 +5265,7 @@
         ((Editable) mText).append(text, start, end);
 
         if (mAutoLinkMask != 0) {
-            boolean linksWereAdded = Linkify.addLinks((Spannable) mText, mAutoLinkMask);
+            boolean linksWereAdded = Linkify.addLinks(mSpannable, mAutoLinkMask);
             // Do not change the movement method for text that support text selection as it
             // would prevent an arbitrary cursor displacement.
             if (linksWereAdded && mLinksClickable && !textCanBeSelected()) {
@@ -5413,7 +5424,7 @@
         }
 
         if (ss.selStart >= 0 && ss.selEnd >= 0) {
-            if (mText instanceof Spannable) {
+            if (mSpannable != null) {
                 int len = mText.length();
 
                 if (ss.selStart > len || ss.selEnd > len) {
@@ -5426,7 +5437,7 @@
                     Log.e(LOG_TAG, "Saved cursor position " + ss.selStart + "/" + ss.selEnd
                             + " out of range for " + restored + "text " + mText);
                 } else {
-                    Selection.setSelection((Spannable) mText, ss.selStart, ss.selEnd);
+                    Selection.setSelection(mSpannable, ss.selStart, ss.selEnd);
 
                     if (ss.frozenWithFocus) {
                         createEditorIfNeeded();
@@ -5688,7 +5699,7 @@
                  * movement method, because setMovementMethod() may call
                  * setText() again to try to upgrade the buffer type.
                  */
-                mText = text;
+                setTextInternal(text);
 
                 // Do not change the movement method for text that support text selection as it
                 // would prevent an arbitrary cursor displacement.
@@ -5699,7 +5710,7 @@
         }
 
         mBufferType = type;
-        mText = text;
+        setTextInternal(text);
 
         if (mTransformation == null) {
             mTransformed = text;
@@ -5825,8 +5836,8 @@
         setText(text, type);
 
         if (start >= 0 || end >= 0) {
-            if (mText instanceof Spannable) {
-                Selection.setSelection((Spannable) mText,
+            if (mSpannable != null) {
+                Selection.setSelection(mSpannable,
                                        Math.max(0, Math.min(start, len)),
                                        Math.max(0, Math.min(end, len)));
             }
@@ -6020,7 +6031,7 @@
         }
 
         if (!isSuggestionsEnabled()) {
-            mText = removeSuggestionSpans(mText);
+            setTextInternal(removeSuggestionSpans(mText));
         }
 
         InputMethodManager imm = InputMethodManager.peekInstance();
@@ -6948,8 +6959,7 @@
     public boolean hasOverlappingRendering() {
         // horizontal fading edge causes SaveLayerAlpha, which doesn't support alpha modulation
         return ((getBackground() != null && getBackground().getCurrent() != null)
-                || mText instanceof Spannable || hasSelection()
-                || isHorizontalFadingEdgeEnabled());
+                || mSpannable != null || hasSelection() || isHorizontalFadingEdgeEnabled());
     }
 
     /**
@@ -7399,11 +7409,11 @@
 
     @Override
     public PointerIcon onResolvePointerIcon(MotionEvent event, int pointerIndex) {
-        if (mText instanceof Spannable && mLinksClickable) {
+        if (mSpannable != null && mLinksClickable) {
             final float x = event.getX(pointerIndex);
             final float y = event.getY(pointerIndex);
             final int offset = getOffsetForPosition(x, y);
-            final ClickableSpan[] clickables = ((Spannable) mText).getSpans(offset, offset,
+            final ClickableSpan[] clickables = mSpannable.getSpans(offset, offset,
                     ClickableSpan.class);
             if (clickables.length > 0) {
                 return PointerIcon.getSystemIcon(mContext, PointerIcon.TYPE_HAND);
@@ -7496,10 +7506,10 @@
 
         } else if (which == KEY_DOWN_HANDLED_BY_MOVEMENT_METHOD) {
             // mMovement is not null from doKeyDown
-            mMovement.onKeyUp(this, (Spannable) mText, keyCode, up);
+            mMovement.onKeyUp(this, mSpannable, keyCode, up);
             while (--repeatCount > 0) {
-                mMovement.onKeyDown(this, (Spannable) mText, keyCode, down);
-                mMovement.onKeyUp(this, (Spannable) mText, keyCode, up);
+                mMovement.onKeyDown(this, mSpannable, keyCode, down);
+                mMovement.onKeyUp(this, mSpannable, keyCode, up);
             }
         }
 
@@ -7694,8 +7704,7 @@
             boolean doDown = true;
             if (otherEvent != null) {
                 try {
-                    boolean handled = mMovement.onKeyOther(this, (Spannable) mText,
-                            otherEvent);
+                    boolean handled = mMovement.onKeyOther(this, mSpannable, otherEvent);
                     doDown = false;
                     if (handled) {
                         return KEY_EVENT_HANDLED;
@@ -7706,7 +7715,7 @@
                 }
             }
             if (doDown) {
-                if (mMovement.onKeyDown(this, (Spannable) mText, keyCode, event)) {
+                if (mMovement.onKeyDown(this, mSpannable, keyCode, event)) {
                     if (event.getRepeatCount() == 0 && !KeyEvent.isModifierKey(keyCode)) {
                         mPreventDefaultMovement = true;
                     }
@@ -7848,7 +7857,7 @@
         }
 
         if (mMovement != null && mLayout != null) {
-            if (mMovement.onKeyUp(this, (Spannable) mText, keyCode, event)) {
+            if (mMovement.onKeyUp(this, mSpannable, keyCode, event)) {
                 return true;
             }
         }
@@ -8314,13 +8323,23 @@
     }
 
     /**
+     * Returns true if DynamicLayout is required
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public boolean useDynamicLayout() {
+        return isTextSelectable() || (mSpannable != null && mPrecomputed == null);
+    }
+
+    /**
      * @hide
      */
     protected Layout makeSingleLayout(int wantWidth, BoringLayout.Metrics boring, int ellipsisWidth,
             Layout.Alignment alignment, boolean shouldEllipsize, TruncateAt effectiveEllipsize,
             boolean useSaved) {
         Layout result = null;
-        if (mText instanceof Spannable) {
+        if (useDynamicLayout()) {
             final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(mText, mTextPaint,
                     wantWidth)
                     .setDisplayText(mTransformed)
@@ -9262,7 +9281,7 @@
         }
 
         if (newStart != start) {
-            Selection.setSelection((Spannable) mText, newStart);
+            Selection.setSelection(mSpannable, newStart);
             return true;
         }
 
@@ -9999,9 +10018,8 @@
         if (mEditor != null) mEditor.onFocusChanged(focused, direction);
 
         if (focused) {
-            if (mText instanceof Spannable) {
-                Spannable sp = (Spannable) mText;
-                MetaKeyKeyListener.resetMetaState(sp);
+            if (mSpannable != null) {
+                MetaKeyKeyListener.resetMetaState(mSpannable);
             }
         }
 
@@ -10039,7 +10057,7 @@
      */
     public void clearComposingText() {
         if (mText instanceof Spannable) {
-            BaseInputConnection.removeComposingSpans((Spannable) mText);
+            BaseInputConnection.removeComposingSpans(mSpannable);
         }
     }
 
@@ -10095,7 +10113,7 @@
             boolean handled = false;
 
             if (mMovement != null) {
-                handled |= mMovement.onTouchEvent(this, (Spannable) mText, event);
+                handled |= mMovement.onTouchEvent(this, mSpannable, event);
             }
 
             final boolean textIsSelectable = isTextSelectable();
@@ -10103,7 +10121,7 @@
                 // The LinkMovementMethod which should handle taps on links has not been installed
                 // on non editable text that support text selection.
                 // We reproduce its behavior here to open links for these.
-                ClickableSpan[] links = ((Spannable) mText).getSpans(getSelectionStart(),
+                ClickableSpan[] links = mSpannable.getSpans(getSelectionStart(),
                     getSelectionEnd(), ClickableSpan.class);
 
                 if (links.length > 0) {
@@ -10138,7 +10156,7 @@
     public boolean onGenericMotionEvent(MotionEvent event) {
         if (mMovement != null && mText instanceof Spannable && mLayout != null) {
             try {
-                if (mMovement.onGenericMotionEvent(this, (Spannable) mText, event)) {
+                if (mMovement.onGenericMotionEvent(this, mSpannable, event)) {
                     return true;
                 }
             } catch (AbstractMethodError ex) {
@@ -10199,8 +10217,8 @@
 
     @Override
     public boolean onTrackballEvent(MotionEvent event) {
-        if (mMovement != null && mText instanceof Spannable && mLayout != null) {
-            if (mMovement.onTrackballEvent(this, (Spannable) mText, event)) {
+        if (mMovement != null && mSpannable != null && mLayout != null) {
+            if (mMovement.onTrackballEvent(this, mSpannable, event)) {
                 return true;
             }
         }
@@ -11121,7 +11139,7 @@
                 if (mText != null) {
                     int updatedTextLength = mText.length();
                     if (updatedTextLength > 0) {
-                        Selection.setSelection((Spannable) mText, updatedTextLength);
+                        Selection.setSelection(mSpannable, updatedTextLength);
                     }
                 }
             } return true;
@@ -11697,7 +11715,7 @@
             hideFloatingToolbar(FLOATING_TOOLBAR_SELECT_ALL_REFRESH_DELAY);
         }
         final int length = mText.length();
-        Selection.setSelection((Spannable) mText, 0, length);
+        Selection.setSelection(mSpannable, 0, length);
         return length > 0;
     }
 
@@ -11725,7 +11743,7 @@
                 }
                 if (paste != null) {
                     if (!didFirst) {
-                        Selection.setSelection((Spannable) mText, max);
+                        Selection.setSelection(mSpannable, max);
                         ((Editable) mText).replace(min, max, paste);
                         didFirst = true;
                     } else {
@@ -11747,7 +11765,7 @@
             selectedText = TextUtils.trimToParcelableSize(selectedText);
             sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, selectedText);
             getContext().startActivity(Intent.createChooser(sharingIntent, null));
-            Selection.setSelection((Spannable) mText, getSelectionEnd());
+            Selection.setSelection(mSpannable, getSelectionEnd());
         }
     }
 
@@ -11822,7 +11840,7 @@
             case DragEvent.ACTION_DRAG_LOCATION:
                 if (mText instanceof Spannable) {
                     final int offset = getOffsetForPosition(event.getX(), event.getY());
-                    Selection.setSelection((Spannable) mText, offset);
+                    Selection.setSelection(mSpannable, offset);
                 }
                 return true;
 
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 28a7c12..cbd3ad5 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -53,10 +53,21 @@
     public static final int DISABLE_VERIFIER = 1 << 9;
     /** Only use oat files located in /system. Otherwise use dex/jar/apk . */
     public static final int ONLY_USE_SYSTEM_OAT_FILES = 1 << 10;
-    /** Do enfore hidden API access restrictions. */
-    public static final int ENABLE_HIDDEN_API_CHECKS = 1 << 11;
     /** Force generation of native debugging information for backtraces. */
-    public static final int DEBUG_GENERATE_MINI_DEBUG_INFO = 1 << 12;
+    public static final int DEBUG_GENERATE_MINI_DEBUG_INFO = 1 << 11;
+    /**
+     * Hidden API access restrictions. This is a mask for bits representing the API enforcement
+     * policy, defined by {@code @ApplicationInfo.HiddenApiEnforcementPolicy}.
+     */
+    public static final int API_ENFORCEMENT_POLICY_MASK = (1 << 12) | (1 << 13);
+    /**
+     * Bit shift for use with {@link #API_ENFORCEMENT_POLICY_MASK}.
+     *
+     * (flags & API_ENFORCEMENT_POLICY_MASK) >> API_ENFORCEMENT_POLICY_SHIFT gives
+     * @ApplicationInfo.ApiEnforcementPolicy values.
+     */
+    public static final int API_ENFORCEMENT_POLICY_SHIFT =
+            Integer.numberOfTrailingZeros(API_ENFORCEMENT_POLICY_MASK);
 
     /** No external storage should be mounted. */
     public static final int MOUNT_EXTERNAL_NONE = IVold.REMOUNT_MODE_NONE;
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 66035f4..9467ecc 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -652,7 +652,7 @@
         String args[] = {
             "--setuid=1000",
             "--setgid=1000",
-            "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,1032,1065,3001,3002,3003,3006,3007,3009,3010",
+            "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,1024,1032,1065,3001,3002,3003,3006,3007,3009,3010",
             "--capabilities=" + capabilities + "," + capabilities,
             "--nice-name=system_server",
             "--runtime-args",
diff --git a/core/java/com/android/server/NetworkManagementSocketTagger.java b/core/java/com/android/server/NetworkManagementSocketTagger.java
index 03f2bc1..2959667 100644
--- a/core/java/com/android/server/NetworkManagementSocketTagger.java
+++ b/core/java/com/android/server/NetworkManagementSocketTagger.java
@@ -67,6 +67,10 @@
         return old;
     }
 
+    public static int getThreadSocketStatsUid() {
+        return threadSocketTags.get().statsUid;
+    }
+
     @Override
     public void tag(FileDescriptor fd) throws SocketException {
         final SocketTags options = threadSocketTags.get();
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index 122e5c4..8d4b56c 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -514,7 +514,7 @@
         optional int32 uid = 3;
         // Job IDs can technically be negative.
         optional int32 job_id = 4;
-        optional string tag = 5 [ (.android.privacy).dest = DEST_EXPLICIT ];
+        optional string tag = 5;
         // Only valid for STOP_JOB or STOP_PERIODIC_JOB Events.
         optional .android.app.job.StopReasonEnum stop_reason = 6;
     }
@@ -538,7 +538,7 @@
 
     // The UID that scheduled the job.
     optional int32 calling_uid = 1;
-    optional string tag = 2 [ (.android.privacy).dest = DEST_EXPLICIT ];
+    optional string tag = 2;
 
     // The UID for which the job is being run.
     optional int32 source_uid = 3;
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 55c17b9..b5e223c 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1340,6 +1340,30 @@
     <integer-array name="config_autoBrightnessKeyboardBacklightValues">
     </integer-array>
 
+    <!-- Array of light sensor lux values to define the minimum brightness curve, which guarantees
+         that any curve that dips below it is rejected by the system.
+         This prevents auto-brightness from setting the screen so dark as to prevent the user from
+         disabling auto-brightness or reseting the brightness curve via ADB.
+
+         The control points must be strictly increasing. Each control point corresponds to an entry
+         in the minimum brightness nits array. -->
+    <integer-array name="config_autoBrightnessMinimumBrightnessCurveLux">
+        <item>2000</item>
+        <item>4000</item>
+    </integer-array>
+
+    <!-- Array of desired screen brightness in nits corresponding to the lux values
+         in the config_autoBrightnessMinimumBrightnessCurveLux array.
+
+         This array should have size one greater than the size of the
+         config_autoBrightnessMinimumBrightnessCurveLux array. The values must be non-negative and
+         non-decreasing. -->
+    <array name="config_autoBrightnessMinimumBrightnessCurveNits">
+        <item>1.0</item>
+        <item>50.0</item>
+        <item>90.0</item>
+    </array>
+
     <!-- Array of hysteresis constraint values for brightening, represented as tenths of a
          percent. The length of this array is assumed to be one greater than
          config_dynamicHysteresisLuxLevels. The brightening threshold is calculated as
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 75f8013..5eeb418 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1794,6 +1794,8 @@
   <java-symbol type="array" name="config_autoBrightnessKeyboardBacklightValues" />
   <java-symbol type="array" name="config_autoBrightnessLcdBacklightValues" />
   <java-symbol type="array" name="config_autoBrightnessLevels" />
+  <java-symbol type="array" name="config_autoBrightnessMinimumBrightnessCurveLux" />
+  <java-symbol type="array" name="config_autoBrightnessMinimumBrightnessCurveNits" />
   <java-symbol type="array" name="config_dynamicHysteresisBrightLevels" />
   <java-symbol type="array" name="config_dynamicHysteresisDarkLevels" />
   <java-symbol type="array" name="config_dynamicHysteresisLuxLevels" />
diff --git a/core/tests/coretests/src/android/widget/TextViewTest.java b/core/tests/coretests/src/android/widget/TextViewTest.java
index 2b5b27b..4f1efbf 100644
--- a/core/tests/coretests/src/android/widget/TextViewTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewTest.java
@@ -17,6 +17,7 @@
 package android.widget;
 
 import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertTrue;
 
@@ -32,9 +33,11 @@
 import android.support.test.runner.AndroidJUnit4;
 import android.text.GetChars;
 import android.text.Layout;
+import android.text.PrecomputedText;
 import android.text.Selection;
 import android.text.Spannable;
 import android.view.View;
+import android.widget.TextView.BufferType;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -241,6 +244,82 @@
         mTextView.onTextContextMenuItem(TextView.ID_CUT);
     }
 
+    @Test
+    public void testUseDynamicLayout() {
+        mTextView = new TextView(mActivity);
+        mTextView.setTextIsSelectable(true);
+        String text = "HelloWorld";
+        PrecomputedText precomputed =
+                PrecomputedText.create(text, mTextView.getTextMetricsParams());
+
+        mTextView.setTextIsSelectable(false);
+        mTextView.setText(text);
+        assertFalse(mTextView.useDynamicLayout());
+
+        mTextView.setTextIsSelectable(true);
+        mTextView.setText(text);
+        assertTrue(mTextView.useDynamicLayout());
+
+        mTextView.setTextIsSelectable(false);
+        mTextView.setText(precomputed);
+        assertFalse(mTextView.useDynamicLayout());
+
+        mTextView.setTextIsSelectable(true);
+        mTextView.setText(precomputed);
+        assertTrue(mTextView.useDynamicLayout());
+    }
+
+    @Test
+    public void testUseDynamicLayout_SPANNABLE() {
+        mTextView = new TextView(mActivity);
+        mTextView.setTextIsSelectable(true);
+        String text = "HelloWorld";
+        PrecomputedText precomputed =
+                PrecomputedText.create(text, mTextView.getTextMetricsParams());
+
+        mTextView.setTextIsSelectable(false);
+        mTextView.setText(text, BufferType.SPANNABLE);
+        android.util.Log.e("TextViewTest", "Text:" + mTextView.getText().getClass().getName());
+        assertTrue(mTextView.useDynamicLayout());
+
+        mTextView.setTextIsSelectable(true);
+        mTextView.setText(text, BufferType.SPANNABLE);
+        assertTrue(mTextView.useDynamicLayout());
+
+        mTextView.setTextIsSelectable(false);
+        mTextView.setText(precomputed, BufferType.SPANNABLE);
+        assertFalse(mTextView.useDynamicLayout());
+
+        mTextView.setTextIsSelectable(true);
+        mTextView.setText(precomputed, BufferType.SPANNABLE);
+        assertTrue(mTextView.useDynamicLayout());
+    }
+
+    @Test
+    public void testUseDynamicLayout_EDITABLE() {
+        mTextView = new TextView(mActivity);
+        mTextView.setTextIsSelectable(true);
+        String text = "HelloWorld";
+        PrecomputedText precomputed =
+                PrecomputedText.create(text, mTextView.getTextMetricsParams());
+
+        mTextView.setTextIsSelectable(false);
+        mTextView.setText(text, BufferType.EDITABLE);
+        assertTrue(mTextView.useDynamicLayout());
+
+        mTextView.setTextIsSelectable(true);
+        mTextView.setText(text, BufferType.EDITABLE);
+        assertTrue(mTextView.useDynamicLayout());
+
+        mTextView.setTextIsSelectable(false);
+        mTextView.setText(precomputed, BufferType.EDITABLE);
+        assertTrue(mTextView.useDynamicLayout());
+
+        mTextView.setTextIsSelectable(true);
+        mTextView.setText(precomputed, BufferType.EDITABLE);
+        assertTrue(mTextView.useDynamicLayout());
+    }
+
     private String createLongText() {
         int size = 600 * 1000;
         final StringBuilder builder = new StringBuilder(size);
diff --git a/data/etc/hiddenapi-package-whitelist.xml b/data/etc/hiddenapi-package-whitelist.xml
index bacddf14..a4a1b94 100644
--- a/data/etc/hiddenapi-package-whitelist.xml
+++ b/data/etc/hiddenapi-package-whitelist.xml
@@ -64,6 +64,7 @@
   <hidden-api-whitelisted-app package="com.android.gallery" />
   <hidden-api-whitelisted-app package="com.android.hotspot2" />
   <hidden-api-whitelisted-app package="com.android.keychain" />
+  <hidden-api-whitelisted-app package="com.android.launcher3" />
   <hidden-api-whitelisted-app package="com.android.location.fused" />
   <hidden-api-whitelisted-app package="com.android.managedprovisioning" />
   <hidden-api-whitelisted-app package="com.android.mms.service" />
diff --git a/data/keyboards/Android.mk b/data/keyboards/Android.mk
index 2daef0c..7949c77 100644
--- a/data/keyboards/Android.mk
+++ b/data/keyboards/Android.mk
@@ -28,7 +28,7 @@
 validatekeymaps := $(HOST_OUT_EXECUTABLES)/validatekeymaps$(HOST_EXECUTABLE_SUFFIX)
 $(LOCAL_BUILT_MODULE): PRIVATE_VALIDATEKEYMAPS := $(validatekeymaps)
 $(LOCAL_BUILT_MODULE) : $(framework_keylayouts) $(framework_keycharmaps) $(framework_keyconfigs) | $(validatekeymaps)
-	$(hide) -q $(PRIVATE_VALIDATEKEYMAPS) $^
+	$(hide) $(PRIVATE_VALIDATEKEYMAPS) -q $^
 	$(hide) mkdir -p $(dir $@) && touch $@
 
 # Run validatekeymaps uncondionally for platform build.
diff --git a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
index 457e4aa..898939e 100644
--- a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
@@ -52,8 +52,20 @@
 /**
  * {@link Drawable} for drawing animated images (like GIF).
  *
+ * <p>The framework handles decoding subsequent frames in another thread and
+ * updating when necessary. The drawable will only animate while it is being
+ * displayed.</p>
+ *
  * <p>Created by {@link ImageDecoder#decodeDrawable}. A user needs to call
  * {@link #start} to start the animation.</p>
+ *
+ * <p>It can also be defined in XML using the <code>&lt;animated-image></code>
+ * element.</p>
+ *
+ * @attr ref android.R.styleable#AnimatedImageDrawable_src
+ * @attr ref android.R.styleable#AnimatedImageDrawable_autoStart
+ * @attr ref android.R.styleable#AnimatedImageDrawable_repeatCount
+ * @attr ref android.R.styleable#AnimatedImageDrawable_autoMirrored
  */
 public class AnimatedImageDrawable extends Drawable implements Animatable2 {
     private int mIntrinsicWidth;
@@ -456,8 +468,8 @@
      *  <p>Does nothing if the animation is already running. If the animation is stopped,
      *  this will reset it.</p>
      *
-     *  <p>If the animation starts, this will call
-     *  {@link Animatable2.AnimationCallback#onAnimationStart}.</p>
+     *  <p>When the drawable is drawn, starting the animation,
+     *  {@link Animatable2.AnimationCallback#onAnimationStart} will be called.</p>
      */
     @Override
     public void start() {
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 9175416..bc0e43b 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -3226,9 +3226,18 @@
 
         if (stripOffsetsAttribute != null && stripByteCountsAttribute != null) {
             long[] stripOffsets =
-                    (long[]) stripOffsetsAttribute.getValue(mExifByteOrder);
+                    convertToLongArray(stripOffsetsAttribute.getValue(mExifByteOrder));
             long[] stripByteCounts =
-                    (long[]) stripByteCountsAttribute.getValue(mExifByteOrder);
+                    convertToLongArray(stripByteCountsAttribute.getValue(mExifByteOrder));
+
+            if (stripOffsets == null) {
+                Log.w(TAG, "stripOffsets should not be null.");
+                return;
+            }
+            if (stripByteCounts == null) {
+                Log.w(TAG, "stripByteCounts should not be null.");
+                return;
+            }
 
             // Set thumbnail byte array data for non-consecutive strip bytes
             byte[] totalStripBytes =
@@ -4025,4 +4034,22 @@
         }
         return false;
     }
+
+    /**
+     * Convert given int[] to long[]. If long[] is given, just return it.
+     * Return null for other types of input.
+     */
+    private static long[] convertToLongArray(Object inputObj) {
+        if (inputObj instanceof int[]) {
+            int[] input = (int[]) inputObj;
+            long[] result = new long[input.length];
+            for (int i = 0; i < input.length; i++) {
+                result[i] = input[i];
+            }
+            return result;
+        } else if (inputObj instanceof long[]) {
+            return (long[]) inputObj;
+        }
+        return null;
+    }
 }
diff --git a/media/java/android/media/MediaController2.java b/media/java/android/media/MediaController2.java
index 17002aa..591f33f 100644
--- a/media/java/android/media/MediaController2.java
+++ b/media/java/android/media/MediaController2.java
@@ -25,9 +25,7 @@
 import android.content.Context;
 import android.media.MediaPlaylistAgent.RepeatMode;
 import android.media.MediaPlaylistAgent.ShuffleMode;
-import android.media.MediaSession2.Command;
 import android.media.MediaSession2.CommandButton;
-import android.media.MediaSession2.CommandGroup;
 import android.media.MediaSession2.ControllerInfo;
 import android.media.MediaSession2.ErrorCode;
 import android.media.session.MediaSessionManager;
@@ -56,7 +54,7 @@
  * When controlling {@link MediaSessionService2}, the {@link MediaController2} would be
  * available only if the session service allows this controller by
  * {@link MediaSession2.SessionCallback#onConnect(MediaSession2, ControllerInfo)} for the service.
- * Wait {@link ControllerCallback#onConnected(MediaController2, CommandGroup)} or
+ * Wait {@link ControllerCallback#onConnected(MediaController2, SessionCommandGroup2)} or
  * {@link ControllerCallback#onDisconnected(MediaController2)} for the result.
  * <p>
  * A controller can be created through token from {@link MediaSessionManager} if you hold the
@@ -83,7 +81,7 @@
          * @param allowedCommands commands that's allowed by the session.
          */
         public void onConnected(@NonNull MediaController2 controller,
-                @NonNull CommandGroup allowedCommands) { }
+                @NonNull SessionCommandGroup2 allowedCommands) { }
 
         /**
          * Called when the session refuses the controller or the controller is disconnected from
@@ -102,7 +100,8 @@
          * Called when the session set the custom layout through the
          * {@link MediaSession2#setCustomLayout(ControllerInfo, List)}.
          * <p>
-         * Can be called before {@link #onConnected(MediaController2, CommandGroup)} is called.
+         * Can be called before {@link #onConnected(MediaController2, SessionCommandGroup2)} is
+         * called.
          *
          * @param controller the controller for this event
          * @param layout
@@ -126,7 +125,7 @@
          * @param commands newly allowed commands
          */
         public void onAllowedCommandsChanged(@NonNull MediaController2 controller,
-                @NonNull CommandGroup commands) { }
+                @NonNull SessionCommandGroup2 commands) { }
 
         /**
          * Called when the session sent a custom command.
@@ -137,7 +136,7 @@
          * @param receiver
          */
         public void onCustomCommand(@NonNull MediaController2 controller,
-                @NonNull Command command, @Nullable Bundle args,
+                @NonNull SessionCommand2 command, @Nullable Bundle args,
                 @Nullable ResultReceiver receiver) { }
 
         /**
@@ -149,16 +148,6 @@
         public void onPlayerStateChanged(@NonNull MediaController2 controller, int state) { }
 
         /**
-         * Called when the player's position is changed
-         *
-         * @param controller the controller for this event
-         * @param eventTimeMs timestamp when the position information is sent from the session
-         * @param positionMs position in millis
-         */
-        public void onPositionChanged(@NonNull MediaController2 controller,
-                long eventTimeMs, long positionMs) { }
-
-        /**
          * Called when playback speed is changed.
          *
          * @param controller the controller for this event
@@ -180,6 +169,14 @@
                 @NonNull MediaItem2 item, @MediaPlayerBase.BuffState int state) { }
 
         /**
+         * Called to indicate that seeking is completed.
+         *
+         * @param controller the controller for this event.
+         * @param position the previous seeking request.
+         */
+        public void onSeekCompleted(@NonNull MediaController2 controller, long position) { }
+
+        /**
          * Called when a error from
          *
          * @param controller the controller for this event
@@ -197,7 +194,6 @@
          *
          * @param controller the controller for this event
          * @param item new item
-         * @see #onPositionChanged(MediaController2, long, long)
          * @see #onBufferingStateChanged(MediaController2, MediaItem2, int)
          */
         // TODO(jaewan): Use this (b/74316764)
@@ -423,16 +419,14 @@
     }
 
     /**
-     * Start fast forwarding. If playback is already fast forwarding this
-     * may increase the rate.
+     * Fast forwards playback. If playback is already fast forwarding this may increase the rate.
      */
     public void fastForward() {
         mProvider.fastForward_impl();
     }
 
     /**
-     * Start rewinding. If playback is already rewinding this may increase
-     * the rate.
+     * Rewinds playback. If playback is already rewinding this may increase the rate.
      */
     public void rewind() {
         mProvider.rewind_impl();
@@ -689,7 +683,7 @@
      * @param args optional argument
      * @param cb optional result receiver
      */
-    public void sendCustomCommand(@NonNull Command command, @Nullable Bundle args,
+    public void sendCustomCommand(@NonNull SessionCommand2 command, @Nullable Bundle args,
             @Nullable ResultReceiver cb) {
         mProvider.sendCustomCommand_impl(command, args, cb);
     }
diff --git a/media/java/android/media/MediaItem2.java b/media/java/android/media/MediaItem2.java
index 1967a1c..423a1fd 100644
--- a/media/java/android/media/MediaItem2.java
+++ b/media/java/android/media/MediaItem2.java
@@ -19,7 +19,6 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.Context;
 import android.media.update.ApiLoader;
 import android.media.update.MediaItem2Provider;
 import android.os.Bundle;
@@ -81,8 +80,8 @@
         return mProvider.toBundle_impl();
     }
 
-    public static MediaItem2 fromBundle(Context context, Bundle bundle) {
-        return ApiLoader.getProvider().fromBundle_MediaItem2(context, bundle);
+    public static MediaItem2 fromBundle(Bundle bundle) {
+        return ApiLoader.getProvider().fromBundle_MediaItem2(bundle);
     }
 
     public String toString() {
@@ -161,11 +160,10 @@
         /**
          * Constructor for {@link Builder}
          *
-         * @param context
          * @param flags
          */
-        public Builder(@NonNull Context context, @Flags int flags) {
-            mProvider = ApiLoader.getProvider().createMediaItem2Builder(context, this, flags);
+        public Builder(@Flags int flags) {
+            mProvider = ApiLoader.getProvider().createMediaItem2Builder(this, flags);
         }
 
         /**
diff --git a/media/java/android/media/MediaLibraryService2.java b/media/java/android/media/MediaLibraryService2.java
index 034d17e..f29d386 100644
--- a/media/java/android/media/MediaLibraryService2.java
+++ b/media/java/android/media/MediaLibraryService2.java
@@ -20,7 +20,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.PendingIntent;
-import android.content.Context;
 import android.media.MediaLibraryService2.MediaLibrarySession.Builder;
 import android.media.MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback;
 import android.media.MediaSession2.ControllerInfo;
@@ -74,8 +73,8 @@
          * Callback for the {@link MediaLibrarySession}.
          */
         public static class MediaLibrarySessionCallback extends MediaSession2.SessionCallback {
-            public MediaLibrarySessionCallback(@NonNull Context context) {
-                super(context);
+            public MediaLibrarySessionCallback() {
+                super();
             }
 
             /**
@@ -401,10 +400,9 @@
          * @param rootId The root id for browsing.
          * @param extras Any extras about the library service.
          */
-        public LibraryRoot(@NonNull Context context,
-                @NonNull String rootId, @Nullable Bundle extras) {
+        public LibraryRoot(@NonNull String rootId, @Nullable Bundle extras) {
             mProvider = ApiLoader.getProvider().createMediaLibraryService2LibraryRoot(
-                    context, this, rootId, extras);
+                    this, rootId, extras);
         }
 
         /**
diff --git a/media/java/android/media/MediaMetadata2.java b/media/java/android/media/MediaMetadata2.java
index 59dd8cb..7b03ae0 100644
--- a/media/java/android/media/MediaMetadata2.java
+++ b/media/java/android/media/MediaMetadata2.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.StringDef;
-import android.content.Context;
 import android.graphics.Bitmap;
 import android.media.update.ApiLoader;
 import android.media.update.MediaMetadata2Provider;
@@ -657,13 +656,11 @@
      * Creates the {@link MediaMetadata2} from the bundle that previously returned by
      * {@link #toBundle()}.
      *
-     * @param context context
      * @param bundle bundle for the metadata
      * @return a new MediaMetadata2
      */
-    public static @NonNull MediaMetadata2 fromBundle(@NonNull Context context,
-            @Nullable Bundle bundle) {
-        return ApiLoader.getProvider().fromBundle_MediaMetadata2(context, bundle);
+    public static @NonNull MediaMetadata2 fromBundle(@Nullable Bundle bundle) {
+        return ApiLoader.getProvider().fromBundle_MediaMetadata2(bundle);
     }
 
     /**
@@ -677,8 +674,8 @@
          * Create an empty Builder. Any field that should be included in the
          * {@link MediaMetadata2} must be added.
          */
-        public Builder(@NonNull Context context) {
-            mProvider = ApiLoader.getProvider().createMediaMetadata2Builder(context, this);
+        public Builder() {
+            mProvider = ApiLoader.getProvider().createMediaMetadata2Builder(this);
         }
 
         /**
@@ -688,8 +685,8 @@
          *
          * @param source
          */
-        public Builder(@NonNull Context context, @NonNull MediaMetadata2 source) {
-            mProvider = ApiLoader.getProvider().createMediaMetadata2Builder(context, this, source);
+        public Builder(@NonNull MediaMetadata2 source) {
+            mProvider = ApiLoader.getProvider().createMediaMetadata2Builder(this, source);
         }
 
         /**
diff --git a/media/java/android/media/MediaPlayerBase.java b/media/java/android/media/MediaPlayerBase.java
index 5c08f19..a426552 100644
--- a/media/java/android/media/MediaPlayerBase.java
+++ b/media/java/android/media/MediaPlayerBase.java
@@ -130,33 +130,6 @@
      */
     public abstract void seekTo(long pos);
 
-    /**
-     * Fast forwards playback. If playback is already fast forwarding this may increase the rate.
-     * <p>
-     * Default implementation sets the playback speed to the 2.0f
-     * @see #setPlaybackSpeed(float)
-     * @hide
-     */
-    // TODO(jaewan): Unhide (b/74724709)
-    public void fastForward() {
-        setPlaybackSpeed(2.0f);
-    }
-
-    /**
-     * Rewinds playback. If playback is already rewinding this may increase the rate.
-     * <p>
-     * Default implementation sets the playback speed to the -1.0f if
-     * {@link #isReversePlaybackSupported()} returns {@code true}.
-     * @see #setPlaybackSpeed(float)
-     * @hide
-     */
-    // TODO(jaewan): Unhide (b/74724709)
-    public void rewind() {
-        if (isReversePlaybackSupported()) {
-            setPlaybackSpeed(-1.0f);
-        }
-    }
-
     public static final long UNKNOWN_TIME = -1;
 
     /**
@@ -340,10 +313,19 @@
 
         /**
          * Called to indicate that the playback speed has changed.
-         * @param mpb the player that is buffering
+         * @param mpb the player that has changed the playback speed.
          * @param speed the new playback speed.
          */
         public void onPlaybackSpeedChanged(@NonNull MediaPlayerBase mpb, float speed) { }
+
+        /**
+         * Called to indicate that {@link #seekTo(long)} is completed.
+         *
+         * @param mpb the player that has completed seeking.
+         * @param position the previous seeking request.
+         * @see #seekTo(long)
+         */
+        public void onSeekCompleted(@NonNull MediaPlayerBase mpb, long position) { }
     }
 
 }
diff --git a/media/java/android/media/MediaPlaylistAgent.java b/media/java/android/media/MediaPlaylistAgent.java
index f339229..88f37e7 100644
--- a/media/java/android/media/MediaPlaylistAgent.java
+++ b/media/java/android/media/MediaPlaylistAgent.java
@@ -20,7 +20,6 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.Context;
 import android.media.update.ApiLoader;
 import android.media.update.MediaPlaylistAgentProvider;
 
@@ -148,8 +147,8 @@
                 @RepeatMode int repeatMode) { }
     }
 
-    public MediaPlaylistAgent(@NonNull Context context) {
-        mProvider = ApiLoader.getProvider().createMediaPlaylistAgent(context, this);
+    public MediaPlaylistAgent() {
+        mProvider = ApiLoader.getProvider().createMediaPlaylistAgent(this);
     }
 
     /**
diff --git a/media/java/android/media/MediaSession2.java b/media/java/android/media/MediaSession2.java
index 2033c59..2b3c2b4 100644
--- a/media/java/android/media/MediaSession2.java
+++ b/media/java/android/media/MediaSession2.java
@@ -33,8 +33,6 @@
 import android.media.update.MediaSession2Provider;
 import android.media.update.MediaSession2Provider.BuilderBaseProvider;
 import android.media.update.MediaSession2Provider.CommandButtonProvider;
-import android.media.update.MediaSession2Provider.CommandGroupProvider;
-import android.media.update.MediaSession2Provider.CommandProvider;
 import android.media.update.MediaSession2Provider.ControllerInfoProvider;
 import android.media.update.ProviderCreator;
 import android.net.Uri;
@@ -45,7 +43,6 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.List;
-import java.util.Set;
 import java.util.concurrent.Executor;
 
 /**
@@ -85,255 +82,6 @@
     private final MediaSession2Provider mProvider;
 
     /**
-     * Command code for the custom command which can be defined by string action in the
-     * {@link Command}.
-     */
-    public static final int COMMAND_CODE_CUSTOM = 0;
-
-    /**
-     * Command code for {@link MediaController2#play()}.
-     * <p>
-     * Command would be sent directly to the player if the session doesn't reject the request
-     * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
-     */
-    public static final int COMMAND_CODE_PLAYBACK_PLAY = 1;
-
-    /**
-     * Command code for {@link MediaController2#pause()}.
-     * <p>
-     * Command would be sent directly to the player if the session doesn't reject the request
-     * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
-     */
-    public static final int COMMAND_CODE_PLAYBACK_PAUSE = 2;
-
-    /**
-     * Command code for {@link MediaController2#stop()}.
-     * <p>
-     * Command would be sent directly to the player if the session doesn't reject the request
-     * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
-     */
-    public static final int COMMAND_CODE_PLAYBACK_STOP = 3;
-
-    /**
-     * Command code for {@link MediaController2#skipToNextItem()}.
-     * <p>
-     * Command would be sent directly to the playlist agent if the session doesn't reject the
-     * request through the {@link SessionCallback#onCommandRequest(
-     * MediaSession2, ControllerInfo, Command)}.
-     */
-    public static final int COMMAND_CODE_PLAYLIST_SKIP_NEXT_ITEM = 4;
-
-    /**
-     * Command code for {@link MediaController2#skipToPreviousItem()}.
-     * <p>
-     * Command would be sent directly to the playlist agent if the session doesn't reject the
-     * request through the {@link SessionCallback#onCommandRequest(
-     * MediaSession2, ControllerInfo, Command)}.
-     */
-    public static final int COMMAND_CODE_PLAYLIST_SKIP_PREV_ITEM = 5;
-
-    /**
-     * Command code for {@link MediaController2#prepare()}.
-     * <p>
-     * Command would be sent directly to the player if the session doesn't reject the request
-     * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
-     */
-    public static final int COMMAND_CODE_PLAYBACK_PREPARE = 6;
-
-    /**
-     * Command code for {@link MediaController2#fastForward()}.
-     * <p>
-     * This is transport control command. Command would be sent directly to the player if the
-     * session doesn't reject the request through the
-     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
-     */
-    public static final int COMMAND_CODE_PLAYBACK_FAST_FORWARD = 7;
-
-    /**
-     * Command code for {@link MediaController2#rewind()}.
-     * <p>
-     * Command would be sent directly to the player if the session doesn't reject the request
-     * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
-     */
-    public static final int COMMAND_CODE_PLAYBACK_REWIND = 8;
-
-    /**
-     * Command code for {@link MediaController2#seekTo(long)}.
-     * <p>
-     * Command would be sent directly to the player if the session doesn't reject the request
-     * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
-     */
-    public static final int COMMAND_CODE_PLAYBACK_SEEK_TO = 9;
-
-    /**
-     * Command code for both {@link MediaController2#setVolumeTo(int, int)}.
-     * <p>
-     * Command would set the device volume or send to the volume provider directly if the session
-     * doesn't reject the request through the
-     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
-     */
-    public static final int COMMAND_CODE_PLAYBACK_SET_VOLUME = 10;
-
-    /**
-     * Command code for both {@link MediaController2#adjustVolume(int, int)}.
-     * <p>
-     * Command would adjust the device volume or send to the volume provider directly if the session
-     * doesn't reject the request through the
-     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
-     */
-    public static final int COMMAND_CODE_PLAYBACK_ADJUST_VOLUME = 11;
-
-    /**
-     * Command code for {@link MediaController2#skipToPlaylistItem(MediaItem2)}.
-     * <p>
-     * Command would be sent directly to the playlist agent if the session doesn't reject the
-     * request through the
-     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
-     */
-    public static final int COMMAND_CODE_PLAYLIST_SKIP_TO_PLAYLIST_ITEM = 12;
-
-    /**
-     * Command code for {@link MediaController2#setShuffleMode(int)}.
-     * <p>
-     * Command would be sent directly to the playlist agent if the session doesn't reject the
-     * request through the
-     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
-     */
-    public static final int COMMAND_CODE_PLAYLIST_SET_SHUFFLE_MODE = 13;
-
-    /**
-     * Command code for {@link MediaController2#setRepeatMode(int)}.
-     * <p>
-     * Command would be sent directly to the playlist agent if the session doesn't reject the
-     * request through the
-     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
-     */
-    public static final int COMMAND_CODE_PLAYLIST_SET_REPEAT_MODE = 14;
-
-    /**
-     * Command code for {@link MediaController2#addPlaylistItem(int, MediaItem2)}.
-     * <p>
-     * Command would be sent directly to the playlist agent if the session doesn't reject the
-     * request through the
-     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
-     */
-    public static final int COMMAND_CODE_PLAYLIST_ADD_ITEM = 15;
-
-    /**
-     * Command code for {@link MediaController2#addPlaylistItem(int, MediaItem2)}.
-     * <p>
-     * Command would be sent directly to the playlist agent if the session doesn't reject the
-     * request through the
-     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
-     */
-    public static final int COMMAND_CODE_PLAYLIST_REMOVE_ITEM = 16;
-
-    /**
-     * Command code for {@link MediaController2#replacePlaylistItem(int, MediaItem2)}.
-     * <p>
-     * Command would be sent directly to the playlist agent if the session doesn't reject the
-     * request through the
-     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
-     */
-    public static final int COMMAND_CODE_PLAYLIST_REPLACE_ITEM = 17;
-
-    /**
-     * Command code for {@link MediaController2#getPlaylist()}. This will expose metadata
-     * information to the controller.
-     * <p>
-     * Command would be sent directly to the playlist agent if the session doesn't reject the
-     * request through the
-     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
-     */
-    public static final int COMMAND_CODE_PLAYLIST_GET_LIST = 18;
-
-    /**
-     * Command code for {@link MediaController2#setPlaylist(List, MediaMetadata2)}.
-     * <p>
-     * Command would be sent directly to the playlist agent if the session doesn't reject the
-     * request through the
-     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
-     */
-    public static final int COMMAND_CODE_PLAYLIST_SET_LIST = 19;
-
-    /**
-     * Command code for {@link MediaController2#getPlaylistMetadata()}. This will expose
-     * metadata information to the controller.
-     * <p>
-     * Command would be sent directly to the playlist agent if the session doesn't reject the
-     * request through the
-     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
-     */
-    public static final int COMMAND_CODE_PLAYLIST_GET_LIST_METADATA = 20;
-
-    /**
-     * Command code for {@link MediaController2#updatePlaylistMetadata(MediaMetadata2)}.
-     * <p>
-     * Command would be sent directly to the playlist agent if the session doesn't reject the
-     * request through the
-     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
-     */
-    public static final int COMMAND_CODE_PLAYLIST_SET_LIST_METADATA = 21;
-
-    /**
-     * Command code for {@link MediaController2#playFromMediaId(String, Bundle)}.
-     */
-    public static final int COMMAND_CODE_SESSION_PLAY_FROM_MEDIA_ID = 22;
-
-    /**
-     * Command code for {@link MediaController2#playFromUri(Uri, Bundle)}.
-     */
-    public static final int COMMAND_CODE_SESSION_PLAY_FROM_URI = 23;
-
-    /**
-     * Command code for {@link MediaController2#playFromSearch(String, Bundle)}.
-     */
-    public static final int COMMAND_CODE_SESSION_PLAY_FROM_SEARCH = 24;
-
-    /**
-     * Command code for {@link MediaController2#prepareFromMediaId(String, Bundle)}.
-     */
-    public static final int COMMAND_CODE_SESSION_PREPARE_FROM_MEDIA_ID = 25;
-
-    /**
-     * Command code for {@link MediaController2#prepareFromUri(Uri, Bundle)}.
-     */
-    public static final int COMMAND_CODE_SESSION_PREPARE_FROM_URI = 26;
-
-    /**
-     * Command code for {@link MediaController2#prepareFromSearch(String, Bundle)}.
-     */
-    public static final int COMMAND_CODE_SESSION_PREPARE_FROM_SEARCH = 27;
-
-    /**
-     * Command code for {@link MediaController2#setRating(String, Rating2)}.
-     * @hide
-     */
-    public static final int COMMAND_CODE_SESSION_SET_RATING = 28;
-
-    /**
-     * Command code for {@link android.media.MediaLibraryService2.MediaLibrarySession} specific
-     * functions. With or without this, a {@link MediaSession2} that isn't
-     * {@link android.media.MediaLibraryService2.MediaLibrarySession} would automatically reject
-     * the calls.
-     *
-     * @see android.media.MediaLibraryService2.MediaLibrarySession
-     * @see MediaBrowser2
-     * @hide
-     */
-    // TODO(jaewan): Remove
-    public static final int COMMAND_CODE_BROWSER = 29;
-
-    // TODO(jaewan): Add javadoc
-    public static final int COMMAND_CODE_LIBRARY_GET_CHILDREN = 29;
-    public static final int COMMAND_CODE_LIBRARY_GET_ITEM = 30;
-    public static final int COMMAND_CODE_LIBRARY_GET_LIBRARY_ROOT = 31;
-    public static final int COMMAND_CODE_LIBRARY_GET_SEARCH_RESULT = 32;
-    public static final int COMMAND_CODE_LIBRARY_SEARCH = 33;
-    public static final int COMMAND_CODE_LIBRARY_SUBSCRIBE = 34;
-    public static final int COMMAND_CODE_LIBRARY_UNSUBSCRIBE = 35;
-
-    /**
      * @hide
      */
     @IntDef({ERROR_CODE_UNKNOWN_ERROR, ERROR_CODE_APP_ERROR, ERROR_CODE_NOT_SUPPORTED,
@@ -436,157 +184,6 @@
     }
 
     /**
-     * Define a command that a {@link MediaController2} can send to a {@link MediaSession2}.
-     * <p>
-     * If {@link #getCommandCode()} isn't {@link #COMMAND_CODE_CUSTOM}), it's predefined command.
-     * If {@link #getCommandCode()} is {@link #COMMAND_CODE_CUSTOM}), it's custom command and
-     * {@link #getCustomCommand()} shouldn't be {@code null}.
-     */
-    public static final class Command {
-        private final CommandProvider mProvider;
-
-        public Command(@NonNull Context context, int commandCode) {
-            mProvider = ApiLoader.getProvider().createMediaSession2Command(
-                    this, commandCode, null, null);
-        }
-
-        public Command(@NonNull Context context, @NonNull String action, @Nullable Bundle extras) {
-            if (action == null) {
-                throw new IllegalArgumentException("action shouldn't be null");
-            }
-            mProvider = ApiLoader.getProvider().createMediaSession2Command(
-                    this, COMMAND_CODE_CUSTOM, action, extras);
-        }
-
-        /**
-         * @hide
-         */
-        public CommandProvider getProvider() {
-            return mProvider;
-        }
-
-        public int getCommandCode() {
-            return mProvider.getCommandCode_impl();
-        }
-
-        public @Nullable String getCustomCommand() {
-            return mProvider.getCustomCommand_impl();
-        }
-
-        public @Nullable Bundle getExtras() {
-            return mProvider.getExtras_impl();
-        }
-
-        /**
-         * @return a new Bundle instance from the Command
-         * @hide
-         */
-        public Bundle toBundle() {
-            return mProvider.toBundle_impl();
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (!(obj instanceof Command)) {
-                return false;
-            }
-            return mProvider.equals_impl(((Command) obj).mProvider);
-        }
-
-        @Override
-        public int hashCode() {
-            return mProvider.hashCode_impl();
-        }
-
-        /**
-         * @return a new Command instance from the Bundle
-         * @hide
-         */
-        public static Command fromBundle(@NonNull Context context, @NonNull Bundle command) {
-            return ApiLoader.getProvider().fromBundle_MediaSession2Command(context, command);
-        }
-    }
-
-    /**
-     * Represent set of {@link Command}.
-     */
-    public static final class CommandGroup {
-        private final CommandGroupProvider mProvider;
-
-        public CommandGroup(@NonNull Context context) {
-            mProvider = ApiLoader.getProvider().createMediaSession2CommandGroup(
-                    context, this, null);
-        }
-
-        public CommandGroup(@NonNull Context context, @Nullable CommandGroup others) {
-            mProvider = ApiLoader.getProvider().createMediaSession2CommandGroup(
-                    context, this, others);
-        }
-
-        /**
-         * @hide
-         */
-        public CommandGroup(@NonNull CommandGroupProvider provider) {
-            mProvider = provider;
-        }
-
-        public void addCommand(@NonNull Command command) {
-            mProvider.addCommand_impl(command);
-        }
-
-        public void addCommand(int commandCode) {
-            // TODO(jaewna): Implement
-        }
-
-        public void addAllPredefinedCommands() {
-            mProvider.addAllPredefinedCommands_impl();
-        }
-
-        public void removeCommand(@NonNull Command command) {
-            mProvider.removeCommand_impl(command);
-        }
-
-        public void removeCommand(int commandCode) {
-            // TODO(jaewan): Implement.
-        }
-
-        public boolean hasCommand(@NonNull Command command) {
-            return mProvider.hasCommand_impl(command);
-        }
-
-        public boolean hasCommand(int code) {
-            return mProvider.hasCommand_impl(code);
-        }
-
-        public @NonNull Set<Command> getCommands() {
-            return mProvider.getCommands_impl();
-        }
-
-        /**
-         * @hide
-         */
-        public @NonNull CommandGroupProvider getProvider() {
-            return mProvider;
-        }
-
-        /**
-         * @return new bundle from the CommandGroup
-         * @hide
-         */
-        public @NonNull Bundle toBundle() {
-            return mProvider.toBundle_impl();
-        }
-
-        /**
-         * @return new instance of CommandGroup from the bundle
-         * @hide
-         */
-        public static @Nullable CommandGroup fromBundle(Context context, Bundle commands) {
-            return ApiLoader.getProvider().fromBundle_MediaSession2CommandGroup(context, commands);
-        }
-    }
-
-    /**
      * Callback to be called for all incoming commands from {@link MediaController2}s.
      * <p>
      * If it's not set, the session will accept all controllers and all incoming commands by
@@ -594,15 +191,6 @@
      */
     // TODO(jaewan): Move this to updatable for default implementation (b/74091963)
     public static abstract class SessionCallback {
-        private final Context mContext;
-
-        public SessionCallback(@NonNull Context context) {
-            if (context == null) {
-                throw new IllegalArgumentException("context shouldn't be null");
-            }
-            mContext = context;
-        }
-
         /**
          * Called when a controller is created for this session. Return allowed commands for
          * controller. By default it allows all connection requests and commands.
@@ -615,9 +203,9 @@
          * @param controller controller information.
          * @return allowed commands. Can be {@code null} to reject connection.
          */
-        public @Nullable CommandGroup onConnect(@NonNull MediaSession2 session,
+        public @Nullable SessionCommandGroup2 onConnect(@NonNull MediaSession2 session,
                 @NonNull ControllerInfo controller) {
-            CommandGroup commands = new CommandGroup(mContext);
+            SessionCommandGroup2 commands = new SessionCommandGroup2();
             commands.addAllPredefinedCommands();
             return commands;
         }
@@ -639,23 +227,23 @@
          * @param controller controller information.
          * @param command a command. This method will be called for every single command.
          * @return {@code true} if you want to accept incoming command. {@code false} otherwise.
-         * @see #COMMAND_CODE_PLAYBACK_PLAY
-         * @see #COMMAND_CODE_PLAYBACK_PAUSE
-         * @see #COMMAND_CODE_PLAYBACK_STOP
-         * @see #COMMAND_CODE_PLAYLIST_SKIP_NEXT_ITEM
-         * @see #COMMAND_CODE_PLAYLIST_SKIP_PREV_ITEM
-         * @see #COMMAND_CODE_PLAYBACK_PREPARE
-         * @see #COMMAND_CODE_PLAYBACK_FAST_FORWARD
-         * @see #COMMAND_CODE_PLAYBACK_REWIND
-         * @see #COMMAND_CODE_PLAYBACK_SEEK_TO
-         * @see #COMMAND_CODE_PLAYLIST_SKIP_TO_PLAYLIST_ITEM
-         * @see #COMMAND_CODE_PLAYLIST_ADD_ITEM
-         * @see #COMMAND_CODE_PLAYLIST_REMOVE_ITEM
-         * @see #COMMAND_CODE_PLAYLIST_GET_LIST
-         * @see #COMMAND_CODE_PLAYBACK_SET_VOLUME
+         * @see SessionCommand2#COMMAND_CODE_PLAYBACK_PLAY
+         * @see SessionCommand2#COMMAND_CODE_PLAYBACK_PAUSE
+         * @see SessionCommand2#COMMAND_CODE_PLAYBACK_STOP
+         * @see SessionCommand2#COMMAND_CODE_PLAYLIST_SKIP_NEXT_ITEM
+         * @see SessionCommand2#COMMAND_CODE_PLAYLIST_SKIP_PREV_ITEM
+         * @see SessionCommand2#COMMAND_CODE_PLAYBACK_PREPARE
+         * @see SessionCommand2#COMMAND_CODE_SESSION_FAST_FORWARD
+         * @see SessionCommand2#COMMAND_CODE_SESSION_REWIND
+         * @see SessionCommand2#COMMAND_CODE_PLAYBACK_SEEK_TO
+         * @see SessionCommand2#COMMAND_CODE_PLAYLIST_SKIP_TO_PLAYLIST_ITEM
+         * @see SessionCommand2#COMMAND_CODE_PLAYLIST_ADD_ITEM
+         * @see SessionCommand2#COMMAND_CODE_PLAYLIST_REMOVE_ITEM
+         * @see SessionCommand2#COMMAND_CODE_PLAYLIST_GET_LIST
+         * @see SessionCommand2#COMMAND_CODE_SET_VOLUME
          */
         public boolean onCommandRequest(@NonNull MediaSession2 session,
-                @NonNull ControllerInfo controller, @NonNull Command command) {
+                @NonNull ControllerInfo controller, @NonNull SessionCommand2 command) {
             return true;
         }
 
@@ -678,7 +266,7 @@
 
         /**
          * Called when a controller sent a custom command through
-         * {@link MediaController2#sendCustomCommand(Command, Bundle, ResultReceiver)}.
+         * {@link MediaController2#sendCustomCommand(SessionCommand2, Bundle, ResultReceiver)}.
          *
          * @param session the session for this event
          * @param controller controller information
@@ -687,7 +275,7 @@
          * @param cb optional result receiver
          */
         public void onCustomCommand(@NonNull MediaSession2 session,
-                @NonNull ControllerInfo controller, @NonNull Command customCommand,
+                @NonNull ControllerInfo controller, @NonNull SessionCommand2 customCommand,
                 @Nullable Bundle args, @Nullable ResultReceiver cb) { }
 
         /**
@@ -698,7 +286,7 @@
          * @param controller controller information
          * @param mediaId media id
          * @param extras optional extra bundle
-         * @see #COMMAND_CODE_SESSION_PLAY_FROM_MEDIA_ID
+         * @see SessionCommand2#COMMAND_CODE_SESSION_PLAY_FROM_MEDIA_ID
          */
         public void onPlayFromMediaId(@NonNull MediaSession2 session,
                 @NonNull ControllerInfo controller, @NonNull String mediaId,
@@ -715,7 +303,7 @@
          * @param controller controller information
          * @param query query string. Can be empty to indicate any suggested media
          * @param extras optional extra bundle
-         * @see #COMMAND_CODE_SESSION_PLAY_FROM_SEARCH
+         * @see SessionCommand2#COMMAND_CODE_SESSION_PLAY_FROM_SEARCH
          */
         public void onPlayFromSearch(@NonNull MediaSession2 session,
                 @NonNull ControllerInfo controller, @NonNull String query,
@@ -729,7 +317,7 @@
          * @param controller controller information
          * @param uri uri
          * @param extras optional extra bundle
-         * @see #COMMAND_CODE_SESSION_PLAY_FROM_URI
+         * @see SessionCommand2#COMMAND_CODE_SESSION_PLAY_FROM_URI
          */
         public void onPlayFromUri(@NonNull MediaSession2 session,
                 @NonNull ControllerInfo controller, @NonNull Uri uri,
@@ -753,7 +341,7 @@
          * @param controller controller information
          * @param mediaId media id to prepare
          * @param extras optional extra bundle
-         * @see #COMMAND_CODE_SESSION_PREPARE_FROM_MEDIA_ID
+         * @see SessionCommand2#COMMAND_CODE_SESSION_PREPARE_FROM_MEDIA_ID
          */
         public void onPrepareFromMediaId(@NonNull MediaSession2 session,
                 @NonNull ControllerInfo controller, @NonNull String mediaId,
@@ -777,7 +365,7 @@
          * @param controller controller information
          * @param query query string. Can be empty to indicate any suggested media
          * @param extras optional extra bundle
-         * @see #COMMAND_CODE_SESSION_PREPARE_FROM_SEARCH
+         * @see SessionCommand2#COMMAND_CODE_SESSION_PREPARE_FROM_SEARCH
          */
         public void onPrepareFromSearch(@NonNull MediaSession2 session,
                 @NonNull ControllerInfo controller, @NonNull String query,
@@ -801,12 +389,26 @@
          * @param controller controller information
          * @param uri uri
          * @param extras optional extra bundle
-         * @see #COMMAND_CODE_SESSION_PREPARE_FROM_URI
+         * @see SessionCommand2#COMMAND_CODE_SESSION_PREPARE_FROM_URI
          */
         public void onPrepareFromUri(@NonNull MediaSession2 session,
                 @NonNull ControllerInfo controller, @NonNull Uri uri, @Nullable Bundle extras) { }
 
         /**
+         * Called when a controller called {@link MediaController2#fastForward()}
+         *
+         * @param session the session for this event
+         */
+        public void onFastForward(@NonNull MediaSession2 session) { }
+
+        /**
+         * Called when a controller called {@link MediaController2#rewind()}
+         *
+         * @param session the session for this event
+         */
+        public void onRewind(@NonNull MediaSession2 session) { }
+
+        /**
          * Called when the player's current playing item is changed
          * <p>
          * When it's called, you should invalidate previous playback information and wait for later
@@ -861,6 +463,17 @@
                 @NonNull MediaPlayerBase player, float speed) { }
 
         /**
+         * Called to indicate that {@link #seekTo(long)} is completed.
+         *
+         * @param session the session for this event.
+         * @param mpb the player that has completed seeking.
+         * @param position the previous seeking request.
+         * @see #seekTo(long)
+         */
+        public void onSeekCompleted(@NonNull MediaSession2 session, @NonNull MediaPlayerBase mpb,
+                long position) { }
+
+        /**
          * Called when a playlist is changed from the {@link MediaPlaylistAgent}.
          * <p>
          * This is called when the underlying agent has called
@@ -1146,7 +759,7 @@
     }
 
     /**
-     * Button for a {@link Command} that will be shown by the controller.
+     * Button for a {@link SessionCommand2} that will be shown by the controller.
      * <p>
      * It's up to the controller's decision to respect or ignore this customization request.
      */
@@ -1166,7 +779,8 @@
          *
          * @return command or {@code null}
          */
-        public @Nullable Command getCommand() {
+        public @Nullable
+        SessionCommand2 getCommand() {
             return mProvider.getCommand_impl();
         }
 
@@ -1221,12 +835,11 @@
         public static final class Builder {
             private final CommandButtonProvider.BuilderProvider mProvider;
 
-            public Builder(@NonNull Context context) {
-                mProvider = ApiLoader.getProvider().createMediaSession2CommandButtonBuilder(
-                        context, this);
+            public Builder() {
+                mProvider = ApiLoader.getProvider().createMediaSession2CommandButtonBuilder(this);
             }
 
-            public @NonNull Builder setCommand(@Nullable Command command) {
+            public @NonNull Builder setCommand(@Nullable SessionCommand2 command) {
                 return mProvider.setCommand_impl(command);
             }
 
@@ -1363,7 +976,8 @@
      *      expanded row:   layout[5] layout[6] layout[7] layout[8] layout[9]
      *      main row:       layout[3] layout[1] layout[0] layout[2] layout[4]
      * <p>
-     * This API can be called in the {@link SessionCallback#onConnect(MediaSession2, ControllerInfo)}.
+     * This API can be called in the {@link SessionCallback#onConnect(
+     * MediaSession2, ControllerInfo)}.
      *
      * @param controller controller to specify layout.
      * @param layout ordered list of layout.
@@ -1380,7 +994,7 @@
      * @param commands new allowed commands
      */
     public void setAllowedCommands(@NonNull ControllerInfo controller,
-            @NonNull CommandGroup commands) {
+            @NonNull SessionCommandGroup2 commands) {
         mProvider.setAllowedCommands_impl(controller, commands);
     }
 
@@ -1390,7 +1004,7 @@
      * @param command a command
      * @param args optional argument
      */
-    public void sendCustomCommand(@NonNull Command command, @Nullable Bundle args) {
+    public void sendCustomCommand(@NonNull SessionCommand2 command, @Nullable Bundle args) {
         mProvider.sendCustomCommand_impl(command, args);
     }
 
@@ -1401,8 +1015,9 @@
      * @param args optional argument
      * @param receiver result receiver for the session
      */
-    public void sendCustomCommand(@NonNull ControllerInfo controller, @NonNull Command command,
-            @Nullable Bundle args, @Nullable ResultReceiver receiver) {
+    public void sendCustomCommand(@NonNull ControllerInfo controller,
+            @NonNull SessionCommand2 command, @Nullable Bundle args,
+            @Nullable ResultReceiver receiver) {
         // Equivalent to the MediaController.sendCustomCommand(Action action, ResultReceiver r);
         mProvider.sendCustomCommand_impl(controller, command, args, receiver);
     }
@@ -1448,20 +1063,6 @@
     }
 
     /**
-     * Fast forwards playback. If playback is already fast forwarding this may increase the rate.
-     */
-    public void fastForward() {
-        mProvider.fastForward_impl();
-    }
-
-    /**
-     * Rewinds playback. If playback is already rewinding this may increase the rate.
-     */
-    public void rewind() {
-        mProvider.rewind_impl();
-    }
-
-    /**
      * Move to a new location in the media stream.
      *
      * @param pos Position to move to, in milliseconds.
@@ -1562,7 +1163,8 @@
      *      <li>{@link MediaItem2} specified by {@link #setPlaylist(List, MediaMetadata2)} doesn't
      *          have {@link DataSourceDesc}</li>
      *      <li>{@link MediaController2#addPlaylistItem(int, MediaItem2)} is called and accepted
-     *          by {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
+     *          by {@link SessionCallback#onCommandRequest(
+     *          MediaSession2, ControllerInfo, SessionCommand2)}.
      *          In that case, an item would be added automatically without the data source.</li>
      * </ul>
      * <p>
@@ -1574,9 +1176,9 @@
      * @param helper a data source missing helper.
      * @throws IllegalStateException when the helper is set when the playlist agent is set
      * @see #setPlaylist(List, MediaMetadata2)
-     * @see SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)
-     * @see #COMMAND_CODE_PLAYLIST_ADD_ITEM
-     * @see #COMMAND_CODE_PLAYLIST_REPLACE_ITEM
+     * @see SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)
+     * @see SessionCommand2#COMMAND_CODE_PLAYLIST_ADD_ITEM
+     * @see SessionCommand2#COMMAND_CODE_PLAYLIST_REPLACE_ITEM
      */
     public void setOnDataSourceMissingHelper(@NonNull OnDataSourceMissingHelper helper) {
         mProvider.setOnDataSourceMissingHelper_impl(helper);
diff --git a/media/java/android/media/MediaSessionService2.java b/media/java/android/media/MediaSessionService2.java
index 85ac9b2..6c3a4bf 100644
--- a/media/java/android/media/MediaSessionService2.java
+++ b/media/java/android/media/MediaSessionService2.java
@@ -21,7 +21,6 @@
 import android.annotation.Nullable;
 import android.app.Notification;
 import android.app.Service;
-import android.content.Context;
 import android.content.Intent;
 import android.media.MediaSession2.ControllerInfo;
 import android.media.update.ApiLoader;
@@ -213,16 +212,14 @@
         /**
          * Default constructor
          *
-         * @param context context
          * @param notificationId notification id to be used for
          *      {@link android.app.NotificationManager#notify(int, Notification)}.
          * @param notification a notification to make session service foreground service. Media
          *      style notification is recommended here.
          */
-        public MediaNotification(@NonNull Context context,
-                int notificationId, @NonNull Notification notification) {
+        public MediaNotification(int notificationId, @NonNull Notification notification) {
             mProvider = ApiLoader.getProvider().createMediaSessionService2MediaNotification(
-                    context, this, notificationId, notification);
+                    this, notificationId, notification);
         }
 
         public int getNotificationId() {
diff --git a/media/java/android/media/Rating2.java b/media/java/android/media/Rating2.java
index 5f7a334..9213190 100644
--- a/media/java/android/media/Rating2.java
+++ b/media/java/android/media/Rating2.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.IntDef;
-import android.content.Context;
 import android.media.update.ApiLoader;
 import android.media.update.Rating2Provider;
 import android.os.Bundle;
@@ -29,6 +28,7 @@
 import java.lang.annotation.RetentionPolicy;
 
 /**
+ * @hide
  * A class to encapsulate rating information used as content metadata.
  * A rating is defined by its rating style (see {@link #RATING_HEART},
  * {@link #RATING_THUMB_UP_DOWN}, {@link #RATING_3_STARS}, {@link #RATING_4_STARS},
@@ -126,12 +126,11 @@
     /**
      * Create an instance from bundle object, previoulsy created by {@link #toBundle()}
      *
-     * @param context context
      * @param bundle bundle
      * @return new Rating2 instance or {@code null} for error
      */
-    public static Rating2 fromBundle(@NonNull Context context, @Nullable Bundle bundle) {
-        return ApiLoader.getProvider().fromBundle_Rating2(context, bundle);
+    public static Rating2 fromBundle(@Nullable Bundle bundle) {
+        return ApiLoader.getProvider().fromBundle_Rating2(bundle);
     }
 
     /**
@@ -146,39 +145,35 @@
      * Return a Rating2 instance with no rating.
      * Create and return a new Rating2 instance with no rating known for the given
      * rating style.
-     * @param context context
      * @param ratingStyle one of {@link #RATING_HEART}, {@link #RATING_THUMB_UP_DOWN},
      *    {@link #RATING_3_STARS}, {@link #RATING_4_STARS}, {@link #RATING_5_STARS},
      *    or {@link #RATING_PERCENTAGE}.
      * @return null if an invalid rating style is passed, a new Rating2 instance otherwise.
      */
-    public static @Nullable Rating2 newUnratedRating(@NonNull Context context,
-            @Style int ratingStyle) {
-        return ApiLoader.getProvider().newUnratedRating_Rating2(context, ratingStyle);
+    public static @Nullable Rating2 newUnratedRating(@Style int ratingStyle) {
+        return ApiLoader.getProvider().newUnratedRating_Rating2(ratingStyle);
     }
 
     /**
      * Return a Rating2 instance with a heart-based rating.
      * Create and return a new Rating2 instance with a rating style of {@link #RATING_HEART},
      * and a heart-based rating.
-     * @param context context
      * @param hasHeart true for a "heart selected" rating, false for "heart unselected".
      * @return a new Rating2 instance.
      */
-    public static @Nullable Rating2 newHeartRating(@NonNull Context context, boolean hasHeart) {
-        return ApiLoader.getProvider().newHeartRating_Rating2(context, hasHeart);
+    public static @Nullable Rating2 newHeartRating(boolean hasHeart) {
+        return ApiLoader.getProvider().newHeartRating_Rating2(hasHeart);
     }
 
     /**
      * Return a Rating2 instance with a thumb-based rating.
      * Create and return a new Rating2 instance with a {@link #RATING_THUMB_UP_DOWN}
      * rating style, and a "thumb up" or "thumb down" rating.
-     * @param context context
      * @param thumbIsUp true for a "thumb up" rating, false for "thumb down".
      * @return a new Rating2 instance.
      */
-    public static @Nullable Rating2 newThumbRating(@NonNull Context context, boolean thumbIsUp) {
-        return ApiLoader.getProvider().newThumbRating_Rating2(context, thumbIsUp);
+    public static @Nullable Rating2 newThumbRating(boolean thumbIsUp) {
+        return ApiLoader.getProvider().newThumbRating_Rating2(thumbIsUp);
     }
 
     /**
@@ -186,7 +181,6 @@
      * Create and return a new Rating2 instance with one of the star-base rating styles
      * and the given integer or fractional number of stars. Non integer values can for instance
      * be used to represent an average rating value, which might not be an integer number of stars.
-     * @param context context
      * @param starRatingStyle one of {@link #RATING_3_STARS}, {@link #RATING_4_STARS},
      *     {@link #RATING_5_STARS}.
      * @param starRating a number ranging from 0.0f to 3.0f, 4.0f or 5.0f according to
@@ -194,26 +188,25 @@
      * @return null if the rating style is invalid, or the rating is out of range,
      *     a new Rating2 instance otherwise.
      */
-    public static @Nullable Rating2 newStarRating(@NonNull Context context,
+    public static @Nullable Rating2 newStarRating(
             @StarStyle int starRatingStyle, float starRating) {
-        return ApiLoader.getProvider().newStarRating_Rating2(context, starRatingStyle, starRating);
+        return ApiLoader.getProvider().newStarRating_Rating2(starRatingStyle, starRating);
     }
 
     /**
      * Return a Rating2 instance with a percentage-based rating.
      * Create and return a new Rating2 instance with a {@link #RATING_PERCENTAGE}
      * rating style, and a rating of the given percentage.
-     * @param context context
      * @param percent the value of the rating
      * @return null if the rating is out of range, a new Rating2 instance otherwise.
      */
-    public static @Nullable Rating2 newPercentageRating(@NonNull Context context, float percent) {
-        return ApiLoader.getProvider().newPercentageRating_Rating2(context, percent);
+    public static @Nullable Rating2 newPercentageRating(float percent) {
+        return ApiLoader.getProvider().newPercentageRating_Rating2(percent);
     }
 
     /**
      * Return whether there is a rating value available.
-     * @return true if the instance was not created with {@link #newUnratedRating(Context, int)}.
+     * @return true if the instance was not created with {@link #newUnratedRating(int)}.
      */
     public boolean isRated() {
         return mProvider.isRated_impl();
diff --git a/media/java/android/media/SessionCommand2.java b/media/java/android/media/SessionCommand2.java
new file mode 100644
index 0000000..fe86a3a
--- /dev/null
+++ b/media/java/android/media/SessionCommand2.java
@@ -0,0 +1,336 @@
+/*
+ * Copyright 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 android.media;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.media.update.ApiLoader;
+import android.media.update.MediaSession2Provider;
+import android.media.MediaSession2.ControllerInfo;
+import android.media.MediaSession2.SessionCallback;
+import android.net.Uri;
+import android.os.Bundle;
+
+import java.util.List;
+
+/**
+ * @hide
+ * Define a command that a {@link MediaController2} can send to a {@link MediaSession2}.
+ * <p>
+ * If {@link #getCommandCode()} isn't {@link #COMMAND_CODE_CUSTOM}), it's predefined command.
+ * If {@link #getCommandCode()} is {@link #COMMAND_CODE_CUSTOM}), it's custom command and
+ * {@link #getCustomCommand()} shouldn't be {@code null}.
+ */
+public final class SessionCommand2 {
+    /**
+     * Command code for the custom command which can be defined by string action in the
+     * {@link SessionCommand2}.
+     */
+    public static final int COMMAND_CODE_CUSTOM = 0;
+
+    /**
+     * Command code for {@link MediaController2#play()}.
+     * <p>
+     * Command would be sent directly to the player if the session doesn't reject the request
+     * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo,
+     * SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYBACK_PLAY = 1;
+
+    /**
+     * Command code for {@link MediaController2#pause()}.
+     * <p>
+     * Command would be sent directly to the player if the session doesn't reject the request
+     * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo,
+     * SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYBACK_PAUSE = 2;
+
+    /**
+     * Command code for {@link MediaController2#stop()}.
+     * <p>
+     * Command would be sent directly to the player if the session doesn't reject the request
+     * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo,
+     * SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYBACK_STOP = 3;
+
+    /**
+     * Command code for {@link MediaController2#skipToNextItem()}.
+     * <p>
+     * Command would be sent directly to the playlist agent if the session doesn't reject the
+     * request through the {@link SessionCallback#onCommandRequest(
+     * MediaSession2, ControllerInfo, SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYLIST_SKIP_NEXT_ITEM = 4;
+
+    /**
+     * Command code for {@link MediaController2#skipToPreviousItem()}.
+     * <p>
+     * Command would be sent directly to the playlist agent if the session doesn't reject the
+     * request through the {@link SessionCallback#onCommandRequest(
+     * MediaSession2, ControllerInfo, SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYLIST_SKIP_PREV_ITEM = 5;
+
+    /**
+     * Command code for {@link MediaController2#prepare()}.
+     * <p>
+     * Command would be sent directly to the player if the session doesn't reject the request
+     * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo,
+     * SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYBACK_PREPARE = 6;
+
+    /**
+     * Command code for {@link MediaController2#fastForward()}.
+     */
+    public static final int COMMAND_CODE_SESSION_FAST_FORWARD = 7;
+
+    /**
+     * Command code for {@link MediaController2#rewind()}.
+     */
+    public static final int COMMAND_CODE_SESSION_REWIND = 8;
+
+    /**
+     * Command code for {@link MediaController2#seekTo(long)}.
+     * <p>
+     * Command would be sent directly to the player if the session doesn't reject the request
+     * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo,
+     * SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYBACK_SEEK_TO = 9;
+
+    /**
+     * Command code for both {@link MediaController2#setVolumeTo(int, int)}.
+     * <p>
+     * Command would set the device volume or send to the volume provider directly if the session
+     * doesn't reject the request through the
+     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_SET_VOLUME = 10;
+
+    /**
+     * Command code for both {@link MediaController2#adjustVolume(int, int)}.
+     * <p>
+     * Command would adjust the device volume or send to the volume provider directly if the session
+     * doesn't reject the request through the
+     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_ADJUST_VOLUME = 11;
+
+    /**
+     * Command code for {@link MediaController2#skipToPlaylistItem(MediaItem2)}.
+     * <p>
+     * Command would be sent directly to the playlist agent if the session doesn't reject the
+     * request through the
+     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYLIST_SKIP_TO_PLAYLIST_ITEM = 12;
+
+    /**
+     * Command code for {@link MediaController2#setShuffleMode(int)}.
+     * <p>
+     * Command would be sent directly to the playlist agent if the session doesn't reject the
+     * request through the
+     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYLIST_SET_SHUFFLE_MODE = 13;
+
+    /**
+     * Command code for {@link MediaController2#setRepeatMode(int)}.
+     * <p>
+     * Command would be sent directly to the playlist agent if the session doesn't reject the
+     * request through the
+     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYLIST_SET_REPEAT_MODE = 14;
+
+    /**
+     * Command code for {@link MediaController2#addPlaylistItem(int, MediaItem2)}.
+     * <p>
+     * Command would be sent directly to the playlist agent if the session doesn't reject the
+     * request through the
+     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYLIST_ADD_ITEM = 15;
+
+    /**
+     * Command code for {@link MediaController2#addPlaylistItem(int, MediaItem2)}.
+     * <p>
+     * Command would be sent directly to the playlist agent if the session doesn't reject the
+     * request through the
+     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYLIST_REMOVE_ITEM = 16;
+
+    /**
+     * Command code for {@link MediaController2#replacePlaylistItem(int, MediaItem2)}.
+     * <p>
+     * Command would be sent directly to the playlist agent if the session doesn't reject the
+     * request through the
+     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYLIST_REPLACE_ITEM = 17;
+
+    /**
+     * Command code for {@link MediaController2#getPlaylist()}. This will expose metadata
+     * information to the controller.
+     * <p>
+     * Command would be sent directly to the playlist agent if the session doesn't reject the
+     * request through the
+     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYLIST_GET_LIST = 18;
+
+    /**
+     * Command code for {@link MediaController2#setPlaylist(List, MediaMetadata2)}.
+     * <p>
+     * Command would be sent directly to the playlist agent if the session doesn't reject the
+     * request through the
+     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYLIST_SET_LIST = 19;
+
+    /**
+     * Command code for {@link MediaController2#getPlaylistMetadata()}. This will expose
+     * metadata information to the controller.
+     * <p>
+     * Command would be sent directly to the playlist agent if the session doesn't reject the
+     * request through the
+     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYLIST_GET_LIST_METADATA = 20;
+
+    /**
+     * Command code for {@link MediaController2#updatePlaylistMetadata(MediaMetadata2)}.
+     * <p>
+     * Command would be sent directly to the playlist agent if the session doesn't reject the
+     * request through the
+     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYLIST_SET_LIST_METADATA = 21;
+
+    /**
+     * Command code for {@link MediaController2#playFromMediaId(String, Bundle)}.
+     */
+    public static final int COMMAND_CODE_SESSION_PLAY_FROM_MEDIA_ID = 22;
+
+    /**
+     * Command code for {@link MediaController2#playFromUri(Uri, Bundle)}.
+     */
+    public static final int COMMAND_CODE_SESSION_PLAY_FROM_URI = 23;
+
+    /**
+     * Command code for {@link MediaController2#playFromSearch(String, Bundle)}.
+     */
+    public static final int COMMAND_CODE_SESSION_PLAY_FROM_SEARCH = 24;
+
+    /**
+     * Command code for {@link MediaController2#prepareFromMediaId(String, Bundle)}.
+     */
+    public static final int COMMAND_CODE_SESSION_PREPARE_FROM_MEDIA_ID = 25;
+
+    /**
+     * Command code for {@link MediaController2#prepareFromUri(Uri, Bundle)}.
+     */
+    public static final int COMMAND_CODE_SESSION_PREPARE_FROM_URI = 26;
+
+    /**
+     * Command code for {@link MediaController2#prepareFromSearch(String, Bundle)}.
+     */
+    public static final int COMMAND_CODE_SESSION_PREPARE_FROM_SEARCH = 27;
+
+    /**
+     * Command code for {@link MediaController2#setRating(String, Rating2)}.
+     */
+    public static final int COMMAND_CODE_SESSION_SET_RATING = 28;
+
+    // TODO(jaewan): Add javadoc
+    public static final int COMMAND_CODE_LIBRARY_GET_CHILDREN = 29;
+    public static final int COMMAND_CODE_LIBRARY_GET_ITEM = 30;
+    public static final int COMMAND_CODE_LIBRARY_GET_LIBRARY_ROOT = 31;
+    public static final int COMMAND_CODE_LIBRARY_GET_SEARCH_RESULT = 32;
+    public static final int COMMAND_CODE_LIBRARY_SEARCH = 33;
+    public static final int COMMAND_CODE_LIBRARY_SUBSCRIBE = 34;
+    public static final int COMMAND_CODE_LIBRARY_UNSUBSCRIBE = 35;
+
+    // TODO(jaewan): Rename and move provider
+    private final MediaSession2Provider.CommandProvider mProvider;
+
+    public SessionCommand2(int commandCode) {
+        mProvider = ApiLoader.getProvider().createMediaSession2Command(
+                this, commandCode, null, null);
+    }
+
+    public SessionCommand2(@NonNull String action, @Nullable Bundle extras) {
+        if (action == null) {
+            throw new IllegalArgumentException("action shouldn't be null");
+        }
+        mProvider = ApiLoader.getProvider().createMediaSession2Command(
+                this, COMMAND_CODE_CUSTOM, action, extras);
+    }
+
+    /**
+     * @hide
+     */
+    public MediaSession2Provider.CommandProvider getProvider() {
+        return mProvider;
+    }
+
+    public int getCommandCode() {
+        return mProvider.getCommandCode_impl();
+    }
+
+    public @Nullable String getCustomCommand() {
+        return mProvider.getCustomCommand_impl();
+    }
+
+    public @Nullable Bundle getExtras() {
+        return mProvider.getExtras_impl();
+    }
+
+    /**
+     * @return a new Bundle instance from the Command
+     * @hide
+     */
+    public Bundle toBundle() {
+        return mProvider.toBundle_impl();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof SessionCommand2)) {
+            return false;
+        }
+        return mProvider.equals_impl(((SessionCommand2) obj).mProvider);
+    }
+
+    @Override
+    public int hashCode() {
+        return mProvider.hashCode_impl();
+    }
+
+    /**
+     * @return a new Command instance from the Bundle
+     * @hide
+     */
+    public static SessionCommand2 fromBundle(@NonNull Bundle command) {
+        return ApiLoader.getProvider().fromBundle_MediaSession2Command(command);
+    }
+}
diff --git a/media/java/android/media/SessionCommandGroup2.java b/media/java/android/media/SessionCommandGroup2.java
new file mode 100644
index 0000000..399765e
--- /dev/null
+++ b/media/java/android/media/SessionCommandGroup2.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 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 android.media;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.media.update.ApiLoader;
+import android.media.update.MediaSession2Provider;
+import android.os.Bundle;
+
+import java.util.Set;
+
+/**
+ * @hide
+ * Represent set of {@link SessionCommand2}.
+ */
+public final class SessionCommandGroup2 {
+    // TODO(jaewan): Rename and move provider
+    private final MediaSession2Provider.CommandGroupProvider mProvider;
+
+    public SessionCommandGroup2() {
+        mProvider = ApiLoader.getProvider().createMediaSession2CommandGroup(this, null);
+    }
+
+    public SessionCommandGroup2(@Nullable SessionCommandGroup2 others) {
+        mProvider = ApiLoader.getProvider().createMediaSession2CommandGroup(this, others);
+    }
+
+    /**
+     * @hide
+     */
+    public SessionCommandGroup2(@NonNull MediaSession2Provider.CommandGroupProvider provider) {
+        mProvider = provider;
+    }
+
+    public void addCommand(@NonNull SessionCommand2 command) {
+        mProvider.addCommand_impl(command);
+    }
+
+    public void addCommand(int commandCode) {
+        // TODO(jaewna): Implement
+    }
+
+    public void addAllPredefinedCommands() {
+        mProvider.addAllPredefinedCommands_impl();
+    }
+
+    public void removeCommand(@NonNull SessionCommand2 command) {
+        mProvider.removeCommand_impl(command);
+    }
+
+    public void removeCommand(int commandCode) {
+        // TODO(jaewan): Implement.
+    }
+
+    public boolean hasCommand(@NonNull SessionCommand2 command) {
+        return mProvider.hasCommand_impl(command);
+    }
+
+    public boolean hasCommand(int code) {
+        return mProvider.hasCommand_impl(code);
+    }
+
+    public @NonNull
+    Set<SessionCommand2> getCommands() {
+        return mProvider.getCommands_impl();
+    }
+
+    /**
+     * @hide
+     */
+    public @NonNull MediaSession2Provider.CommandGroupProvider getProvider() {
+        return mProvider;
+    }
+
+    /**
+     * @return new bundle from the CommandGroup
+     * @hide
+     */
+    public @NonNull Bundle toBundle() {
+        return mProvider.toBundle_impl();
+    }
+
+    /**
+     * @return new instance of CommandGroup from the bundle
+     * @hide
+     */
+    public static @Nullable SessionCommandGroup2 fromBundle(Bundle commands) {
+        return ApiLoader.getProvider().fromBundle_MediaSession2CommandGroup(commands);
+    }
+}
diff --git a/media/java/android/media/SessionToken2.java b/media/java/android/media/SessionToken2.java
index f088be3..bf2d445 100644
--- a/media/java/android/media/SessionToken2.java
+++ b/media/java/android/media/SessionToken2.java
@@ -150,8 +150,8 @@
      * @param bundle
      * @return
      */
-    public static SessionToken2 fromBundle(@NonNull Context context, @NonNull Bundle bundle) {
-        return ApiLoader.getProvider().fromBundle_SessionToken2(context, bundle);
+    public static SessionToken2 fromBundle(@NonNull Bundle bundle) {
+        return ApiLoader.getProvider().fromBundle_SessionToken2(bundle);
     }
 
     /**
diff --git a/media/java/android/media/VolumeProvider2.java b/media/java/android/media/VolumeProvider2.java
index 2d96d096..1a4608f 100644
--- a/media/java/android/media/VolumeProvider2.java
+++ b/media/java/android/media/VolumeProvider2.java
@@ -18,7 +18,6 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
-import android.content.Context;
 import android.media.update.ApiLoader;
 import android.media.update.VolumeProvider2Provider;
 
@@ -75,10 +74,9 @@
      * @param maxVolume The maximum allowed volume.
      * @param currentVolume The current volume on the output.
      */
-    public VolumeProvider2(@NonNull Context context, @ControlType int controlType,
-            int maxVolume, int currentVolume) {
+    public VolumeProvider2(@ControlType int controlType, int maxVolume, int currentVolume) {
         mProvider = ApiLoader.getProvider().createVolumeProvider2(
-                context, this, controlType, maxVolume, currentVolume);
+                this, controlType, maxVolume, currentVolume);
     }
 
     /**
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index b7f4998..3b12fca 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -407,7 +407,7 @@
             List<Bundle> bundles = mService.getSessionTokens(
                     /* activeSessionOnly */ true, /* sessionServiceOnly */ false,
                     mContext.getPackageName());
-            return toTokenList(mContext, bundles);
+            return toTokenList(bundles);
         } catch (RemoteException e) {
             Log.wtf(TAG, "Cannot communicate with the service.", e);
             return Collections.emptyList();
@@ -430,7 +430,7 @@
             List<Bundle> bundles = mService.getSessionTokens(
                     /* activeSessionOnly */ false, /* sessionServiceOnly */ true,
                     mContext.getPackageName());
-            return toTokenList(mContext, bundles);
+            return toTokenList(bundles);
         } catch (RemoteException e) {
             Log.wtf(TAG, "Cannot communicate with the service.", e);
             return Collections.emptyList();
@@ -455,7 +455,7 @@
             List<Bundle> bundles = mService.getSessionTokens(
                     /* activeSessionOnly */ false, /* sessionServiceOnly */ false,
                     mContext.getPackageName());
-            return toTokenList(mContext, bundles);
+            return toTokenList(bundles);
         } catch (RemoteException e) {
             Log.wtf(TAG, "Cannot communicate with the service.", e);
             return Collections.emptyList();
@@ -540,11 +540,11 @@
         }
     }
 
-    private static List<SessionToken2> toTokenList(Context context, List<Bundle> bundles) {
+    private static List<SessionToken2> toTokenList(List<Bundle> bundles) {
         List<SessionToken2> tokens = new ArrayList<>();
         if (bundles != null) {
             for (int i = 0; i < bundles.size(); i++) {
-                SessionToken2 token = SessionToken2.fromBundle(context, bundles.get(i));
+                SessionToken2 token = SessionToken2.fromBundle(bundles.get(i));
                 if (token != null) {
                     tokens.add(token);
                 }
@@ -829,7 +829,7 @@
                         final Context context = mContext;
                         final OnSessionTokensChangedListener listener = mListener;
                         if (context != null && listener != null) {
-                            listener.onSessionTokensChanged(toTokenList(context, bundles));
+                            listener.onSessionTokensChanged(toTokenList(bundles));
                         }
                     });
                 }
diff --git a/media/java/android/media/update/MediaController2Provider.java b/media/java/android/media/update/MediaController2Provider.java
index 213897d..7234f7b 100644
--- a/media/java/android/media/update/MediaController2Provider.java
+++ b/media/java/android/media/update/MediaController2Provider.java
@@ -21,7 +21,7 @@
 import android.media.MediaController2.PlaybackInfo;
 import android.media.MediaItem2;
 import android.media.MediaMetadata2;
-import android.media.MediaSession2.Command;
+import android.media.SessionCommand2;
 import android.media.Rating2;
 import android.media.SessionToken2;
 import android.net.Uri;
@@ -52,9 +52,11 @@
     void playFromSearch_impl(String query, Bundle extras);
     void playFromUri_impl(Uri uri, Bundle extras);
     void playFromMediaId_impl(String mediaId, Bundle extras);
+    void fastForward_impl();
+    void rewind_impl();
 
     void setRating_impl(String mediaId, Rating2 rating);
-    void sendCustomCommand_impl(Command command, Bundle args, ResultReceiver cb);
+    void sendCustomCommand_impl(SessionCommand2 command, Bundle args, ResultReceiver cb);
     List<MediaItem2> getPlaylist_impl();
     void setPlaylist_impl(List<MediaItem2> list, MediaMetadata2 metadata);
     MediaMetadata2 getPlaylistMetadata_impl();
diff --git a/media/java/android/media/update/MediaSession2Provider.java b/media/java/android/media/update/MediaSession2Provider.java
index 5a1db3e..4751348 100644
--- a/media/java/android/media/update/MediaSession2Provider.java
+++ b/media/java/android/media/update/MediaSession2Provider.java
@@ -23,10 +23,10 @@
 import android.media.MediaPlayerBase;
 import android.media.MediaPlaylistAgent;
 import android.media.MediaSession2;
-import android.media.MediaSession2.Command;
+import android.media.SessionCommand2;
 import android.media.MediaSession2.CommandButton;
 import android.media.MediaSession2.CommandButton.Builder;
-import android.media.MediaSession2.CommandGroup;
+import android.media.SessionCommandGroup2;
 import android.media.MediaSession2.ControllerInfo;
 import android.media.MediaSession2.OnDataSourceMissingHelper;
 import android.media.MediaSession2.SessionCallback;
@@ -55,10 +55,10 @@
     List<ControllerInfo> getConnectedControllers_impl();
     void setCustomLayout_impl(ControllerInfo controller, List<CommandButton> layout);
     void setAudioFocusRequest_impl(AudioFocusRequest afr);
-    void setAllowedCommands_impl(ControllerInfo controller, CommandGroup commands);
-    void sendCustomCommand_impl(ControllerInfo controller, Command command, Bundle args,
+    void setAllowedCommands_impl(ControllerInfo controller, SessionCommandGroup2 commands);
+    void sendCustomCommand_impl(ControllerInfo controller, SessionCommand2 command, Bundle args,
             ResultReceiver receiver);
-    void sendCustomCommand_impl(Command command, Bundle args);
+    void sendCustomCommand_impl(SessionCommand2 command, Bundle args);
     void addPlaylistItem_impl(int index, MediaItem2 item);
     void removePlaylistItem_impl(MediaItem2 item);
     void replacePlaylistItem_impl(int index, MediaItem2 item);
@@ -72,6 +72,7 @@
     void setOnDataSourceMissingHelper_impl(OnDataSourceMissingHelper helper);
     void clearOnDataSourceMissingHelper_impl();
 
+    // TODO(jaewan): Rename and move provider
     interface CommandProvider {
         int getCommandCode_impl();
         String getCustomCommand_impl();
@@ -82,25 +83,26 @@
         int hashCode_impl();
     }
 
+    // TODO(jaewan): Rename and move provider
     interface CommandGroupProvider {
-        void addCommand_impl(Command command);
+        void addCommand_impl(SessionCommand2 command);
         void addAllPredefinedCommands_impl();
-        void removeCommand_impl(Command command);
-        boolean hasCommand_impl(Command command);
+        void removeCommand_impl(SessionCommand2 command);
+        boolean hasCommand_impl(SessionCommand2 command);
         boolean hasCommand_impl(int code);
-        Set<Command> getCommands_impl();
+        Set<SessionCommand2> getCommands_impl();
         Bundle toBundle_impl();
     }
 
     interface CommandButtonProvider {
-        Command getCommand_impl();
+        SessionCommand2 getCommand_impl();
         int getIconResId_impl();
         String getDisplayName_impl();
         Bundle getExtras_impl();
         boolean isEnabled_impl();
 
         interface BuilderProvider {
-            Builder setCommand_impl(Command command);
+            Builder setCommand_impl(SessionCommand2 command);
             Builder setIconResId_impl(int resId);
             Builder setDisplayName_impl(String displayName);
             Builder setEnabled_impl(boolean enabled);
diff --git a/media/java/android/media/update/StaticProvider.java b/media/java/android/media/update/StaticProvider.java
index 1c0e255..8687b80 100644
--- a/media/java/android/media/update/StaticProvider.java
+++ b/media/java/android/media/update/StaticProvider.java
@@ -35,6 +35,8 @@
 import android.media.MediaSessionService2;
 import android.media.MediaSessionService2.MediaNotification;
 import android.media.Rating2;
+import android.media.SessionCommand2;
+import android.media.SessionCommandGroup2;
 import android.media.SessionToken2;
 import android.media.VolumeProvider2;
 import android.media.update.MediaLibraryService2Provider.LibraryRootProvider;
@@ -67,16 +69,16 @@
             ViewGroupProvider superProvider, ViewGroupProvider privateProvider,
             @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes);
 
-    CommandProvider createMediaSession2Command(MediaSession2.Command instance,
+    CommandProvider createMediaSession2Command(SessionCommand2 instance,
             int commandCode, String action, Bundle extra);
-    MediaSession2.Command fromBundle_MediaSession2Command(Context context, Bundle bundle);
-    CommandGroupProvider createMediaSession2CommandGroup(Context context,
-            MediaSession2.CommandGroup instance, MediaSession2.CommandGroup others);
-    MediaSession2.CommandGroup fromBundle_MediaSession2CommandGroup(Context context, Bundle bundle);
+    SessionCommand2 fromBundle_MediaSession2Command(Bundle bundle);
+    CommandGroupProvider createMediaSession2CommandGroup(SessionCommandGroup2 instance,
+            SessionCommandGroup2 others);
+    SessionCommandGroup2 fromBundle_MediaSession2CommandGroup(Bundle bundle);
     ControllerInfoProvider createMediaSession2ControllerInfo(Context context,
             MediaSession2.ControllerInfo instance, int uid, int pid,
             String packageName, IInterface callback);
-    CommandButtonProvider.BuilderProvider createMediaSession2CommandButtonBuilder(Context context,
+    CommandButtonProvider.BuilderProvider createMediaSession2CommandButtonBuilder(
             MediaSession2.CommandButton.Builder instance);
     BuilderBaseProvider<MediaSession2, SessionCallback> createMediaSession2Builder(
             Context context, MediaSession2.Builder instance);
@@ -88,7 +90,7 @@
             SessionToken2 token, Executor executor, BrowserCallback callback);
 
     MediaSessionService2Provider createMediaSessionService2(MediaSessionService2 instance);
-    MediaNotificationProvider createMediaSessionService2MediaNotification(Context context,
+    MediaNotificationProvider createMediaSessionService2MediaNotification(
             MediaNotification mediaNotification, int notificationId, Notification notification);
 
     MediaSessionService2Provider createMediaLibraryService2(MediaLibraryService2 instance);
@@ -96,33 +98,32 @@
         createMediaLibraryService2Builder(
             MediaLibraryService2 service, MediaLibrarySession.Builder instance,
             Executor callbackExecutor, MediaLibrarySessionCallback callback);
-    LibraryRootProvider createMediaLibraryService2LibraryRoot(Context context, LibraryRoot instance,
-            String rootId, Bundle extras);
+    LibraryRootProvider createMediaLibraryService2LibraryRoot(LibraryRoot instance, String rootId,
+            Bundle extras);
 
     SessionToken2Provider createSessionToken2(Context context, SessionToken2 instance,
             String packageName, String serviceName, int uid);
-    SessionToken2 fromBundle_SessionToken2(Context context, Bundle bundle);
+    SessionToken2 fromBundle_SessionToken2(Bundle bundle);
 
-    MediaItem2Provider.BuilderProvider createMediaItem2Builder(
-            Context context, MediaItem2.Builder instance, int flags);
-    MediaItem2 fromBundle_MediaItem2(Context context, Bundle bundle);
+    MediaItem2Provider.BuilderProvider createMediaItem2Builder(MediaItem2.Builder instance,
+            int flags);
+    MediaItem2 fromBundle_MediaItem2(Bundle bundle);
 
-    VolumeProvider2Provider createVolumeProvider2(Context context, VolumeProvider2 instance,
-            int controlType, int maxVolume, int currentVolume);
+    VolumeProvider2Provider createVolumeProvider2(VolumeProvider2 instance, int controlType,
+            int maxVolume, int currentVolume);
 
-    MediaMetadata2 fromBundle_MediaMetadata2(Context context, Bundle bundle);
+    MediaMetadata2 fromBundle_MediaMetadata2(Bundle bundle);
     MediaMetadata2Provider.BuilderProvider createMediaMetadata2Builder(
-            Context context, MediaMetadata2.Builder instance);
+            MediaMetadata2.Builder instance);
     MediaMetadata2Provider.BuilderProvider createMediaMetadata2Builder(
-            Context context, MediaMetadata2.Builder instance, MediaMetadata2 source);
+            MediaMetadata2.Builder instance, MediaMetadata2 source);
 
-    Rating2 newUnratedRating_Rating2(Context context, int ratingStyle);
-    Rating2 fromBundle_Rating2(Context context, Bundle bundle);
-    Rating2 newHeartRating_Rating2(Context context, boolean hasHeart);
-    Rating2 newThumbRating_Rating2(Context context, boolean thumbIsUp);
-    Rating2 newStarRating_Rating2(Context context, int starRatingStyle, float starRating);
-    Rating2 newPercentageRating_Rating2(Context context, float percent);
+    Rating2 newUnratedRating_Rating2(int ratingStyle);
+    Rating2 fromBundle_Rating2(Bundle bundle);
+    Rating2 newHeartRating_Rating2(boolean hasHeart);
+    Rating2 newThumbRating_Rating2(boolean thumbIsUp);
+    Rating2 newStarRating_Rating2(int starRatingStyle, float starRating);
+    Rating2 newPercentageRating_Rating2(float percent);
 
-    MediaPlaylistAgentProvider createMediaPlaylistAgent(Context context,
-            MediaPlaylistAgent instance);
+    MediaPlaylistAgentProvider createMediaPlaylistAgent(MediaPlaylistAgent instance);
 }
diff --git a/media/java/android/media/update/TransportControlProvider.java b/media/java/android/media/update/TransportControlProvider.java
index 03944d2..d89a88a 100644
--- a/media/java/android/media/update/TransportControlProvider.java
+++ b/media/java/android/media/update/TransportControlProvider.java
@@ -29,8 +29,6 @@
     void skipToNextItem_impl();
 
     void prepare_impl();
-    void fastForward_impl();
-    void rewind_impl();
     void seekTo_impl(long pos);
     void skipToPlaylistItem_impl(MediaItem2 item);
 
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index f15de2c..7976f67 100755
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -243,11 +243,11 @@
         }
     };
 
-    public MtpDatabase(Context context, Context userContext, String volumeName,
+    public MtpDatabase(Context context, String volumeName,
             String[] subDirectories) {
         native_setup();
         mContext = context;
-        mMediaProvider = userContext.getContentResolver()
+        mMediaProvider = context.getContentResolver()
                 .acquireContentProviderClient(MediaStore.AUTHORITY);
         mVolumeName = volumeName;
         mObjectsUri = Files.getMtpObjectsUri(volumeName);
diff --git a/media/java/android/mtp/MtpServer.java b/media/java/android/mtp/MtpServer.java
index f2b1106..8af5ff7 100644
--- a/media/java/android/mtp/MtpServer.java
+++ b/media/java/android/mtp/MtpServer.java
@@ -18,6 +18,8 @@
 
 import com.android.internal.util.Preconditions;
 
+import java.io.FileDescriptor;
+
 /**
  * Java wrapper for MTP/PTP support as USB responder.
  * {@hide}
@@ -34,6 +36,7 @@
 
     public MtpServer(
             MtpDatabase database,
+            FileDescriptor controlFd,
             boolean usePtp,
             Runnable onTerminate,
             String deviceInfoManufacturer,
@@ -44,6 +47,7 @@
         mOnTerminate = Preconditions.checkNotNull(onTerminate);
         native_setup(
                 database,
+                controlFd,
                 usePtp,
                 deviceInfoManufacturer,
                 deviceInfoModel,
@@ -92,6 +96,7 @@
     public static native final void native_configure(boolean usePtp);
     private native final void native_setup(
             MtpDatabase database,
+            FileDescriptor controlFd,
             boolean usePtp,
             String deviceInfoManufacturer,
             String deviceInfoModel,
diff --git a/media/jni/android_mtp_MtpServer.cpp b/media/jni/android_mtp_MtpServer.cpp
index c76cebe..8730e06 100644
--- a/media/jni/android_mtp_MtpServer.cpp
+++ b/media/jni/android_mtp_MtpServer.cpp
@@ -55,22 +55,17 @@
     return (MtpServer*)env->GetLongField(thiz, field_MtpServer_nativeContext);
 }
 
-static void android_mtp_configure(JNIEnv *, jobject, jboolean usePtp) {
-    MtpServer::configure(usePtp);
-}
-
 static void
-android_mtp_MtpServer_setup(JNIEnv *env, jobject thiz, jobject javaDatabase, jboolean usePtp,
-        jstring deviceInfoManufacturer,
-        jstring deviceInfoModel,
-        jstring deviceInfoDeviceVersion,
-        jstring deviceInfoSerialNumber)
+android_mtp_MtpServer_setup(JNIEnv *env, jobject thiz, jobject javaDatabase, jobject jControlFd,
+        jboolean usePtp, jstring deviceInfoManufacturer, jstring deviceInfoModel,
+        jstring deviceInfoDeviceVersion, jstring deviceInfoSerialNumber)
 {
     const char *deviceInfoManufacturerStr = env->GetStringUTFChars(deviceInfoManufacturer, NULL);
     const char *deviceInfoModelStr = env->GetStringUTFChars(deviceInfoModel, NULL);
     const char *deviceInfoDeviceVersionStr = env->GetStringUTFChars(deviceInfoDeviceVersion, NULL);
     const char *deviceInfoSerialNumberStr = env->GetStringUTFChars(deviceInfoSerialNumber, NULL);
-    MtpServer* server = new MtpServer(getMtpDatabase(env, javaDatabase),
+    int controlFd = dup(jniGetFDFromFileDescriptor(env, jControlFd));
+    MtpServer* server = new MtpServer(getMtpDatabase(env, javaDatabase), controlFd,
             usePtp,
             MtpString((deviceInfoManufacturerStr != NULL) ? deviceInfoManufacturerStr : ""),
             MtpString((deviceInfoModelStr != NULL) ? deviceInfoModelStr : ""),
@@ -201,8 +196,7 @@
 // ----------------------------------------------------------------------------
 
 static const JNINativeMethod gMethods[] = {
-    {"native_configure",              "(Z)V",  (void *)android_mtp_configure},
-    {"native_setup",                "(Landroid/mtp/MtpDatabase;ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
+    {"native_setup",                "(Landroid/mtp/MtpDatabase;Ljava/io/FileDescriptor;ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
                                             (void *)android_mtp_MtpServer_setup},
     {"native_run",                  "()V",  (void *)android_mtp_MtpServer_run},
     {"native_cleanup",              "()V",  (void *)android_mtp_MtpServer_cleanup},
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index 4db0034..7828c4c 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -42,6 +42,7 @@
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
+import android.webkit.CookieManager;
 import android.webkit.SslErrorHandler;
 import android.webkit.WebChromeClient;
 import android.webkit.WebSettings;
@@ -147,6 +148,7 @@
 
         final WebView webview = getWebview();
         webview.clearCache(true);
+        CookieManager.getInstance().setAcceptThirdPartyCookies(webview, true);
         WebSettings webSettings = webview.getSettings();
         webSettings.setJavaScriptEnabled(true);
         webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE);
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 24a6804..584fc34 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -97,6 +97,12 @@
     <!-- Summary for Connected wifi network without internet -->
     <string name="wifi_connected_no_internet">Connected, no internet</string>
 
+    <!-- Wi-Fi status indicating that the current network is connected, but has no internet access. -->
+    <string name="wifi_status_no_internet">No internet</string>
+
+    <!-- Wi-Fi status indicating that the current network is connected requires sign in to access the internet. -->
+    <string name="wifi_status_sign_in_required">Sign in required</string>
+
     <!-- Summary for networks failing to connect due to association rejection status 17, AP full -->
     <string name="wifi_ap_unable_to_handle_new_sta">Access point temporarily full</string>
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index c5c1169..78045f9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -1154,7 +1154,7 @@
 
     @Nullable
     @Speed
-    private int roundToClosestSpeedEnum(int speed) {
+    private static int roundToClosestSpeedEnum(int speed) {
         if (speed < Speed.SLOW) {
             return Speed.NONE;
         } else if (speed < (Speed.SLOW + Speed.MODERATE) / 2) {
@@ -1170,21 +1170,31 @@
 
     @Nullable
     String getSpeedLabel(@Speed int speed) {
+        return getSpeedLabel(mContext, speed);
+    }
+
+    private static String getSpeedLabel(Context context, int speed) {
         switch (speed) {
             case Speed.VERY_FAST:
-                return mContext.getString(R.string.speed_label_very_fast);
+                return context.getString(R.string.speed_label_very_fast);
             case Speed.FAST:
-                return mContext.getString(R.string.speed_label_fast);
+                return context.getString(R.string.speed_label_fast);
             case Speed.MODERATE:
-                return mContext.getString(R.string.speed_label_okay);
+                return context.getString(R.string.speed_label_okay);
             case Speed.SLOW:
-                return mContext.getString(R.string.speed_label_slow);
+                return context.getString(R.string.speed_label_slow);
             case Speed.NONE:
             default:
                 return null;
         }
     }
 
+    /** Return the speed label for a {@link ScoredNetwork} at the specified {@code rssi} level. */
+    @Nullable
+    public static String getSpeedLabel(Context context, ScoredNetwork scoredNetwork, int rssi) {
+        return getSpeedLabel(context, roundToClosestSpeedEnum(scoredNetwork.calculateBadge(rssi)));
+    }
+
     /** Return true if the current RSSI is reachable, and false otherwise. */
     public boolean isReachable() {
         return mRssi != UNREACHABLE_RSSI;
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index 6e12e20..4cd23f9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -10,28 +10,90 @@
 
 package com.android.settingslib.wifi;
 
+import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+
+import android.content.Context;
 import android.content.Intent;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
+import android.net.NetworkKey;
+import android.net.NetworkRequest;
+import android.net.NetworkScoreManager;
+import android.net.ScoredNetwork;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
+import android.net.wifi.WifiNetworkScoreCache;
 import android.net.wifi.WifiSsid;
+import android.os.Handler;
+import android.os.Looper;
+
+import com.android.settingslib.R;
 
 import java.util.List;
 
-public class WifiStatusTracker {
-
+public class WifiStatusTracker extends ConnectivityManager.NetworkCallback {
+    private final Context mContext;
+    private final WifiNetworkScoreCache mWifiNetworkScoreCache;
     private final WifiManager mWifiManager;
+    private final NetworkScoreManager mNetworkScoreManager;
+    private final ConnectivityManager mConnectivityManager;
+    private final WifiNetworkScoreCache.CacheListener mCacheListener =
+            new WifiNetworkScoreCache.CacheListener(new Handler(Looper.getMainLooper())) {
+                @Override
+                public void networkCacheUpdated(List<ScoredNetwork> updatedNetworks) {
+                    updateStatusLabel();
+                    mCallback.run();
+                }
+            };
+    private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder()
+            .clearCapabilities().addTransportType(NetworkCapabilities.TRANSPORT_WIFI).build();
+    private final ConnectivityManager.NetworkCallback mNetworkCallback = new ConnectivityManager
+            .NetworkCallback() {
+        @Override
+        public void onCapabilitiesChanged(
+                Network network, NetworkCapabilities networkCapabilities) {
+            updateStatusLabel();
+            mCallback.run();
+        }
+    };
+    private final Runnable mCallback;
+
+    private WifiInfo mWifiInfo;
     public boolean enabled;
     public int state;
     public boolean connected;
-    public boolean connecting;
     public String ssid;
     public int rssi;
     public int level;
+    public String statusLabel;
 
-    public WifiStatusTracker(WifiManager wifiManager) {
+    public WifiStatusTracker(Context context, WifiManager wifiManager,
+            NetworkScoreManager networkScoreManager, ConnectivityManager connectivityManager,
+            Runnable callback) {
+        mContext = context;
         mWifiManager = wifiManager;
+        mWifiNetworkScoreCache = new WifiNetworkScoreCache(context);
+        mNetworkScoreManager = networkScoreManager;
+        mConnectivityManager = connectivityManager;
+        mCallback = callback;
+    }
+
+    public void setListening(boolean listening) {
+        if (listening) {
+            mNetworkScoreManager.registerNetworkScoreCache(NetworkKey.TYPE_WIFI,
+                    mWifiNetworkScoreCache, NetworkScoreManager.CACHE_FILTER_CURRENT_NETWORK);
+            mWifiNetworkScoreCache.registerListener(mCacheListener);
+            mConnectivityManager.registerNetworkCallback(mNetworkRequest, mNetworkCallback);
+        } else {
+            mNetworkScoreManager.unregisterNetworkScoreCache(NetworkKey.TYPE_WIFI,
+                    mWifiNetworkScoreCache);
+            mWifiNetworkScoreCache.unregisterListener();
+            mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
+        }
     }
 
     public void handleBroadcast(Intent intent) {
@@ -40,34 +102,59 @@
             state = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
                     WifiManager.WIFI_STATE_UNKNOWN);
             enabled = state == WifiManager.WIFI_STATE_ENABLED;
-
-
-            enabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
-                    WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED;
         } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
-            final NetworkInfo networkInfo = (NetworkInfo)
+            final NetworkInfo networkInfo =
                     intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
-            connecting = networkInfo != null && !networkInfo.isConnected()
-                    && networkInfo.isConnectedOrConnecting();
             connected = networkInfo != null && networkInfo.isConnected();
-            // If Connected grab the signal strength and ssid.
+            mWifiInfo = null;
+            ssid = null;
             if (connected) {
-                WifiInfo info = mWifiManager.getConnectionInfo();
-                if (info != null) {
-                    ssid = getValidSsid(info);
-                } else {
-                    ssid = null;
+                mWifiInfo = mWifiManager.getConnectionInfo();
+                if (mWifiInfo != null) {
+                    ssid = getValidSsid(mWifiInfo);
+                    updateRssi(mWifiInfo.getRssi());
+                    maybeRequestNetworkScore();
                 }
-            } else if (!connected) {
-                ssid = null;
             }
+            updateStatusLabel();
         } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
             // Default to -200 as its below WifiManager.MIN_RSSI.
-            rssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200);
-            level = WifiManager.calculateSignalLevel(rssi, 5);
+            updateRssi(intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200));
+            updateStatusLabel();
         }
     }
 
+    private void updateRssi(int newRssi) {
+        rssi = newRssi;
+        level = WifiManager.calculateSignalLevel(rssi, WifiManager.RSSI_LEVELS);
+    }
+
+    private void maybeRequestNetworkScore() {
+        NetworkKey networkKey = NetworkKey.createFromWifiInfo(mWifiInfo);
+        if (mWifiNetworkScoreCache.getScoredNetwork(networkKey) == null) {
+            mNetworkScoreManager.requestScores(new NetworkKey[]{ networkKey });
+        }
+    }
+
+    private void updateStatusLabel() {
+        final NetworkCapabilities networkCapabilities
+                = mConnectivityManager.getNetworkCapabilities(mWifiManager.getCurrentNetwork());
+        if (networkCapabilities != null) {
+            if (networkCapabilities.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL)) {
+                statusLabel = mContext.getString(R.string.wifi_status_sign_in_required);
+                return;
+            } else if (!networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) {
+                statusLabel = mContext.getString(R.string.wifi_status_no_internet);
+                return;
+            }
+        }
+
+        ScoredNetwork scoredNetwork =
+                mWifiNetworkScoreCache.getScoredNetwork(NetworkKey.createFromWifiInfo(mWifiInfo));
+        statusLabel = scoredNetwork == null
+                ? null : AccessPoint.getSpeedLabel(mContext, scoredNetwork, rssi);
+    }
+
     private String getValidSsid(WifiInfo info) {
         String ssid = info.getSSID();
         if (ssid != null && !WifiSsid.NONE.equals(ssid)) {
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index ea1ad2d1..ef18725 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -22,6 +22,7 @@
     android:layout_width="match_parent"
     android:layout_height="@dimen/qs_footer_height"
     android:elevation="4dp"
+    android:background="@android:color/transparent"
     android:baselineAligned="false"
     android:clickable="false"
     android:clipChildren="false"
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index 1c9ce18..72ff653 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -56,9 +56,9 @@
         android:layout_height="wrap_content"
         android:layout_marginBottom="@dimen/qs_footer_height"
         android:elevation="4dp"
+        android:background="@android:color/transparent"
     />
 
-
     <include layout="@layout/quick_status_bar_expanded_header" />
 
     <include layout="@layout/qs_footer_impl" />
diff --git a/packages/SystemUI/res/layout/qs_tile_label.xml b/packages/SystemUI/res/layout/qs_tile_label.xml
index b138df0..74c22b0 100644
--- a/packages/SystemUI/res/layout/qs_tile_label.xml
+++ b/packages/SystemUI/res/layout/qs_tile_label.xml
@@ -44,7 +44,7 @@
             android:maxLines="2"
             android:padding="0dp"
             android:gravity="center"
-            android:ellipsize="end"
+            android:ellipsize="marquee"
             android:textAppearance="@style/TextAppearance.QS.TileLabel"
             android:textColor="?android:attr/textColorPrimary"/>
 
@@ -75,6 +75,7 @@
         android:layout_alignEnd="@id/label_group"
         android:layout_below="@id/label_group"
         android:clickable="false"
+        android:ellipsize="marquee"
         android:maxLines="1"
         android:padding="0dp"
         android:visibility="gone"
diff --git a/packages/SystemUI/res/layout/status_bar_notification_dismiss_all.xml b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
similarity index 63%
rename from packages/SystemUI/res/layout/status_bar_notification_dismiss_all.xml
rename to packages/SystemUI/res/layout/status_bar_notification_footer.xml
index 8dc4cb4..aa0d4a0 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_dismiss_all.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
@@ -15,13 +15,27 @@
   -->
 
 <!-- Extends Framelayout -->
-<com.android.systemui.statusbar.DismissView
+<com.android.systemui.statusbar.FooterView
         xmlns:android="http://schemas.android.com/apk/res/android"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:paddingEnd="8dp"
         android:visibility="gone">
-    <com.android.systemui.statusbar.DismissViewButton
+    <FrameLayout
+        android:id="@+id/content"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" >
+        <com.android.systemui.statusbar.FooterViewButton
+            style="@android:style/Widget.Material.Button.Borderless"
+            android:id="@+id/manage_text"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="start"
+            android:focusable="true"
+            android:text="@string/manage_notifications_text"
+            android:textColor="?attr/wallpaperTextColor"
+            android:textAllCaps="false"/>
+        <com.android.systemui.statusbar.FooterViewButton
             style="@android:style/Widget.Material.Button.Borderless"
             android:id="@+id/dismiss_text"
             android:layout_width="wrap_content"
@@ -32,4 +46,5 @@
             android:text="@string/clear_all_notifications_text"
             android:textColor="?attr/wallpaperTextColor"
             android:textAllCaps="true"/>
-</com.android.systemui.statusbar.DismissView>
+    </FrameLayout>
+</com.android.systemui.statusbar.FooterView>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index e37ca1c..c59492f 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -24,6 +24,8 @@
 
     <dimen name="brightness_mirror_height">96dp</dimen>
 
+    <!-- Width for the spacer, used between QS tiles. -->
+    <dimen name="qs_quick_tile_space_width">38dp</dimen>
     <dimen name="qs_tile_margin_top">2dp</dimen>
     <dimen name="qs_header_tooltip_height">24dp</dimen>
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index ff3f696..91c8724 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -317,6 +317,8 @@
     <dimen name="qs_tile_margin_vertical">24dp</dimen>
     <dimen name="qs_tile_margin_top">18dp</dimen>
     <dimen name="qs_quick_tile_size">48dp</dimen>
+    <!-- Width for the spacer, used between QS tiles. -->
+    <dimen name="qs_quick_tile_space_width">0dp</dimen>
     <dimen name="qs_quick_tile_padding">12dp</dimen>
     <dimen name="qs_header_gear_translation">16dp</dimen>
     <dimen name="qs_header_tile_margin_horizontal">0dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index d3a55e8..909c18b 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -191,16 +191,14 @@
     <string name="screenshot_saving_ticker">Saving screenshot\u2026</string>
     <!-- Notification title displayed when a screenshot is being saved to the Gallery. [CHAR LIMIT=50] -->
     <string name="screenshot_saving_title">Saving screenshot\u2026</string>
-    <!-- Notification text displayed when a screenshot is being saved to the Gallery. [CHAR LIMIT=100] -->
-    <string name="screenshot_saving_text">Screenshot is being saved</string>
     <!-- Notification title displayed when a screenshot is saved to the Gallery. [CHAR LIMIT=50] -->
     <string name="screenshot_saved_title">Screenshot saved</string>
     <!-- Notification text displayed when a screenshot is saved to the Gallery. [CHAR LIMIT=100] -->
     <string name="screenshot_saved_text">Tap to view your screenshot</string>
     <!-- Notification title displayed when we fail to take a screenshot. [CHAR LIMIT=50] -->
-    <string name="screenshot_failed_title">Couldn\'t capture screenshot</string>
+    <string name="screenshot_failed_title">Couldn\'t save screenshot</string>
     <!-- Notification text displayed when we fail to save a screenshot for unknown reasons. [CHAR LIMIT=100] -->
-    <string name="screenshot_failed_to_save_unknown_text">Problem encountered while saving screenshot</string>
+    <string name="screenshot_failed_to_save_unknown_text">Try taking screenshot again</string>
     <!-- Notification text displayed when we fail to save a screenshot. [CHAR LIMIT=100] -->
     <string name="screenshot_failed_to_save_text">Can\'t save screenshot due to limited storage space</string>
     <!-- Notification text displayed when we fail to take a screenshot. [CHAR LIMIT=100] -->
@@ -1055,6 +1053,9 @@
     <!-- The text to clear all notifications. [CHAR LIMIT=60] -->
     <string name="clear_all_notifications_text">Clear all</string>
 
+    <!-- The text for the manage notifications link. [CHAR LIMIT=40] -->
+    <string name="manage_notifications_text">Manage notifications</string>
+
     <!-- The text to show in the notifications shade when dnd is suppressing notifications. [CHAR LIMIT=100] -->
     <string name="dnd_suppressing_shade_text">Do Not disturb is hiding notifications</string>
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java
index 62b5004..5b4d652 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java
@@ -137,6 +137,10 @@
         mCancelAction = cancelAction;
     }
 
+    public boolean hasDismissActions() {
+        return mDismissAction != null || mCancelAction != null;
+    }
+
     public void cancelDismissAction() {
         setOnDismissAction(null, null);
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index d24675c..1bab36b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -68,7 +68,6 @@
 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
 import android.telephony.TelephonyManager;
 import android.util.Log;
-import android.util.Slog;
 import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
 
@@ -88,6 +87,7 @@
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map.Entry;
 
@@ -400,16 +400,9 @@
         // Hack level over 9000: Because the subscription id is not yet valid when we see the
         // first update in handleSimStateChange, we need to force refresh all all SIM states
         // so the subscription id for them is consistent.
-        ArrayList<SubscriptionInfo> changedSubscriptions = new ArrayList<>();
-        for (int i = 0; i < subscriptionInfos.size(); i++) {
-            SubscriptionInfo info = subscriptionInfos.get(i);
-            boolean changed = refreshSimState(info.getSubscriptionId(), info.getSimSlotIndex());
-            if (changed) {
-                changedSubscriptions.add(info);
-            }
-        }
-        for (int i = 0; i < changedSubscriptions.size(); i++) {
-            SimData data = mSimDatas.get(changedSubscriptions.get(i).getSubscriptionId());
+        List<Integer> changedSubscriptionIds = refreshSimState(subscriptionInfos);
+        for (int i = 0; i < changedSubscriptionIds.size(); i++) {
+            SimData data = mSimDatas.get(changedSubscriptionIds.get(i));
             for (int j = 0; j < mCallbacks.size(); j++) {
                 KeyguardUpdateMonitorCallback cb = mCallbacks.get(j).get();
                 if (cb != null) {
@@ -1846,34 +1839,56 @@
     };
 
     /**
-     * @return true if and only if the state has changed for the specified {@code slotId}
+     * @return A list of changed subscriptions, maybe empty but never null
      */
-    private boolean refreshSimState(int subId, int slotId) {
+    private List<Integer> refreshSimState(final List<SubscriptionInfo> activeSubscriptionInfos) {
 
         // This is awful. It exists because there are two APIs for getting the SIM status
         // that don't return the complete set of values and have different types. In Keyguard we
         // need IccCardConstants, but TelephonyManager would only give us
         // TelephonyManager.SIM_STATE*, so we retrieve it manually.
         final TelephonyManager tele = TelephonyManager.from(mContext);
-        int simState =  tele.getSimState(slotId);
-        State state;
-        try {
-            state = State.intToState(simState);
-        } catch(IllegalArgumentException ex) {
-            Log.w(TAG, "Unknown sim state: " + simState);
-            state = State.UNKNOWN;
+        ArrayList<Integer> changedSubscriptionIds = new ArrayList<>();
+        HashSet<Integer> activeSubIds = new HashSet<>();
+
+        for (SubscriptionInfo info : activeSubscriptionInfos) {
+            int subId = info.getSubscriptionId();
+            int slotId = info.getSimSlotIndex();
+            int simState =  tele.getSimState(slotId);
+            State state;
+            try {
+                state = State.intToState(simState);
+            } catch(IllegalArgumentException ex) {
+                Log.w(TAG, "Unknown sim state: " + simState);
+                state = State.UNKNOWN;
+            }
+
+            SimData data = mSimDatas.get(subId);
+            final boolean changed;
+            if (data == null) {
+                data = new SimData(state, slotId, subId);
+                mSimDatas.put(subId, data);
+                changed = true;               // no data yet; force update
+            } else {
+                changed = data.simState != state;
+                data.simState = state;
+            }
+            if (changed) {
+                changedSubscriptionIds.add(subId);
+            }
+
+            activeSubIds.add(subId);
         }
-        SimData data = mSimDatas.get(subId);
-        final boolean changed;
-        if (data == null) {
-            data = new SimData(state, slotId, subId);
-            mSimDatas.put(subId, data);
-            changed = true; // no data yet; force update
-        } else {
-            changed = data.simState != state;
-            data.simState = state;
+
+        for (SimData data : mSimDatas.values()) {
+            if (!activeSubIds.contains(data.subId) && data.simState != State.ABSENT) {
+                // for the inactive subscriptions, reset state to ABSENT
+                data.simState = State.ABSENT;
+                changedSubscriptionIds.add(data.subId);
+            }
         }
-        return changed;
+
+        return changedSubscriptionIds;
     }
 
     public static boolean isSimPinSecure(IccCardConstants.State state) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 2270b60..c9c04d9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -17,6 +17,7 @@
 package com.android.systemui.qs;
 
 import android.content.Context;
+import android.content.res.Configuration;
 import android.util.AttributeSet;
 import android.view.Gravity;
 import android.view.View;
@@ -179,16 +180,56 @@
 
         protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
         private boolean mListening;
+        /** Size of the QS tile (width & height). */
+        private int mTileDimensionSize;
 
         public HeaderTileLayout(Context context) {
             super(context);
             setClipChildren(false);
             setClipToPadding(false);
-            setGravity(Gravity.CENTER_VERTICAL);
+
+            mTileDimensionSize = mContext.getResources().getDimensionPixelSize(
+                    R.dimen.qs_quick_tile_size);
+
+            setGravity(Gravity.CENTER);
             setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
         }
 
         @Override
+        protected void onConfigurationChanged(Configuration newConfig) {
+            super.onConfigurationChanged(newConfig);
+
+            setGravity(Gravity.CENTER);
+            LayoutParams staticSpaceLayoutParams = generateSpaceLayoutParams(
+                    mContext.getResources().getDimensionPixelSize(
+                            R.dimen.qs_quick_tile_space_width));
+
+            // Update space params since they fill any open space in portrait orientation and have
+            // a static width in landscape orientation.
+            final int childViewCount = getChildCount();
+            for (int i = 0; i < childViewCount; i++) {
+                View childView = getChildAt(i);
+                if (childView instanceof Space) {
+                    childView.setLayoutParams(staticSpaceLayoutParams);
+                }
+            }
+        }
+
+        /**
+         * Returns {@link LayoutParams} based on the given {@code spaceWidth}. If the width is 0,
+         * then we're going to have the space expand to take up as much space as possible. If the
+         * width is non-zero, we want the inter-tile spacers to be fixed.
+         */
+        private LayoutParams generateSpaceLayoutParams(int spaceWidth) {
+            LayoutParams lp = new LayoutParams(spaceWidth, mTileDimensionSize);
+            if (spaceWidth == 0) {
+                lp.weight = 1;
+            }
+            lp.gravity = Gravity.CENTER;
+            return lp;
+        }
+
+        @Override
         public void setListening(boolean listening) {
             if (mListening == listening) return;
             mListening = listening;
@@ -200,25 +241,22 @@
         @Override
         public void addTile(TileRecord tile) {
             if (getChildCount() != 0) {
-                // Add a spacer.
-                addView(new Space(mContext), getChildCount(), generateSpaceParams());
+                // Add a spacer between tiles. We want static-width spaces if we're in landscape to
+                // keep the tiles close. For portrait, we stick with spaces that fill up any
+                // available space.
+                LayoutParams spaceLayoutParams = generateSpaceLayoutParams(
+                        mContext.getResources().getDimensionPixelSize(
+                                R.dimen.qs_quick_tile_space_width));
+                addView(new Space(mContext), getChildCount(), spaceLayoutParams);
             }
-            addView(tile.tileView, getChildCount(), generateLayoutParams());
+
+            addView(tile.tileView, getChildCount(), generateTileLayoutParams());
             mRecords.add(tile);
             tile.tile.setListening(this, mListening);
         }
 
-        private LayoutParams generateSpaceParams() {
-            int size = mContext.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size);
-            LayoutParams lp = new LayoutParams(0, size);
-            lp.weight = 1;
-            lp.gravity = Gravity.CENTER;
-            return lp;
-        }
-
-        private LayoutParams generateLayoutParams() {
-            int size = mContext.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size);
-            LayoutParams lp = new LayoutParams(size, size);
+        private LayoutParams generateTileLayoutParams() {
+            LayoutParams lp = new LayoutParams(mTileDimensionSize, mTileDimensionSize);
             lp.gravity = Gravity.CENTER;
             return lp;
         }
@@ -237,8 +275,8 @@
         }
 
         private int getChildIndex(QSTileView tileView) {
-            final int N = getChildCount();
-            for (int i = 0; i < N; i++) {
+            final int childViewCount = getChildCount();
+            for (int i = 0; i < childViewCount; i++) {
                 if (getChildAt(i) == tileView) {
                     return i;
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index c9c678c..a9defc8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -15,7 +15,6 @@
 
 import static com.android.systemui.qs.tileimpl.QSIconViewImpl.QS_ANIM_LENGTH;
 
-import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.content.Context;
 import android.content.res.ColorStateList;
@@ -122,14 +121,14 @@
     private void setRipple(RippleDrawable tileBackground) {
         mRipple = tileBackground;
         if (getWidth() != 0) {
-            updateRippleSize(getWidth(), getHeight());
+            updateRippleSize();
         }
     }
 
-    private void updateRippleSize(int width, int height) {
+    private void updateRippleSize() {
         // center the touch feedback on the center of the icon, and dial it down a bit
-        final int cx = width / 2;
-        final int cy = mIconFrame.getMeasuredHeight() / 2;
+        final int cx = mIconFrame.getMeasuredWidth() / 2 + mIconFrame.getLeft();
+        final int cy = mIconFrame.getMeasuredHeight() / 2 + mIconFrame.getTop();
         final int rad = (int) (mIcon.getHeight() * .85f);
         mRipple.setHotspotBounds(cx - rad, cy - rad, cx + rad, cy + rad);
     }
@@ -151,11 +150,8 @@
     @Override
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
         super.onLayout(changed, l, t, r, b);
-        final int w = getMeasuredWidth();
-        final int h = getMeasuredHeight();
-
         if (mRipple != null) {
-            updateRippleSize(w, h);
+            updateRippleSize();
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
index 4774785..3cb4c71 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
@@ -32,11 +32,12 @@
 import com.android.systemui.plugins.qs.QSIconView;
 import com.android.systemui.plugins.qs.QSTile;
 
+
 import java.util.Objects;
 
 /** View that represents a standard quick settings tile. **/
 public class QSTileView extends QSTileBaseView {
-
+    private static final int DEFAULT_MAX_LINES = 2;
     private static final boolean DUAL_TARGET_ALLOWED = false;
     private View mDivider;
     protected TextView mLabel;
@@ -61,7 +62,7 @@
         setId(View.generateViewId());
         createLabel();
         setOrientation(VERTICAL);
-        setGravity(Gravity.CENTER);
+        setGravity(Gravity.CENTER_HORIZONTAL | Gravity.TOP);
     }
 
     TextView getLabel() {
@@ -72,6 +73,7 @@
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
         FontSizeUtils.updateFontSize(mLabel, R.dimen.qs_tile_text_size);
+        FontSizeUtils.updateFontSize(mSecondLine, R.dimen.qs_tile_text_size);
     }
 
     @Override
@@ -85,17 +87,33 @@
         mLabelContainer.setClipChildren(false);
         mLabelContainer.setClipToPadding(false);
         mLabel = mLabelContainer.findViewById(R.id.tile_label);
+        mLabel.setSelected(true); // Allow marquee to work.
         mPadLock = mLabelContainer.findViewById(R.id.restricted_padlock);
         mDivider = mLabelContainer.findViewById(R.id.underline);
         mExpandIndicator = mLabelContainer.findViewById(R.id.expand_indicator);
         mExpandSpace = mLabelContainer.findViewById(R.id.expand_space);
         mSecondLine = mLabelContainer.findViewById(R.id.app_label);
         mSecondLine.setAlpha(.6f);
-
+        mSecondLine.setSelected(true); // Allow marquee to work.
         addView(mLabelContainer);
     }
 
     @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        if (mLabel.getMaxLines() != DEFAULT_MAX_LINES) {
+            mLabel.setMaxLines(DEFAULT_MAX_LINES);
+        }
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+        // Remeasure view if the secondary label text will be cut off.
+        if (!TextUtils.isEmpty(mSecondLine.getText())
+                && mSecondLine.getLineHeight() > mSecondLine.getHeight()) {
+            mLabel.setSingleLine();
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        }
+    }
+
+    @Override
     protected void handleStateChanged(QSTile.State state) {
         super.handleStateChanged(state);
         if (!Objects.equals(mLabel.getText(), state.label) || mState != state.state) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 28fdc11..8a1e4da 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -150,8 +150,8 @@
             cb = mSignalCallback.mInfo;
         }
         boolean transientEnabling = arg == ARG_SHOW_TRANSIENT_ENABLING;
-        boolean wifiConnected = cb.enabled && (cb.wifiSignalIconId > 0) && (cb.enabledDesc != null);
-        boolean wifiNotConnected = (cb.wifiSignalIconId > 0) && (cb.enabledDesc == null);
+        boolean wifiConnected = cb.enabled && (cb.wifiSignalIconId > 0) && (cb.ssid != null);
+        boolean wifiNotConnected = (cb.wifiSignalIconId > 0) && (cb.ssid == null);
         boolean enabledChanging = state.value != cb.enabled;
         if (enabledChanging) {
             mDetailAdapter.setItemsVisible(cb.enabled);
@@ -163,7 +163,7 @@
         }
         state.slash.isSlashed = false;
         boolean isTransient = transientEnabling || cb.isTransient;
-        state.secondaryLabel = getSecondaryLabel(isTransient);
+        state.secondaryLabel = getSecondaryLabel(isTransient, cb.statusLabel);
         state.state = Tile.STATE_ACTIVE;
         state.dualTarget = true;
         state.value = transientEnabling || cb.enabled;
@@ -181,7 +181,7 @@
             state.label = r.getString(R.string.quick_settings_wifi_label);
         } else if (wifiConnected) {
             state.icon = ResourceIcon.get(cb.wifiSignalIconId);
-            state.label = removeDoubleQuotes(cb.enabledDesc);
+            state.label = removeDoubleQuotes(cb.ssid);
         } else if (wifiNotConnected) {
             state.icon = ResourceIcon.get(R.drawable.ic_qs_wifi_disconnected);
             state.label = r.getString(R.string.quick_settings_wifi_label);
@@ -194,7 +194,7 @@
         if (state.value) {
             if (wifiConnected) {
                 minimalContentDescription.append(cb.wifiSignalContentDescription).append(",");
-                minimalContentDescription.append(removeDoubleQuotes(cb.enabledDesc));
+                minimalContentDescription.append(removeDoubleQuotes(cb.ssid));
             }
         }
         state.contentDescription = minimalContentDescription.toString();
@@ -203,10 +203,10 @@
         state.expandedAccessibilityClassName = Switch.class.getName();
     }
 
-    private CharSequence getSecondaryLabel(boolean isTransient) {
+    private CharSequence getSecondaryLabel(boolean isTransient, String statusLabel) {
         return isTransient
                 ? mContext.getString(R.string.quick_settings_wifi_secondary_label_transient)
-                : null;
+                : statusLabel;
     }
 
     @Override
@@ -246,11 +246,12 @@
         boolean enabled;
         boolean connected;
         int wifiSignalIconId;
-        String enabledDesc;
+        String ssid;
         boolean activityIn;
         boolean activityOut;
         String wifiSignalContentDescription;
         boolean isTransient;
+        public String statusLabel;
 
         @Override
         public String toString() {
@@ -258,7 +259,7 @@
                     .append("enabled=").append(enabled)
                     .append(",connected=").append(connected)
                     .append(",wifiSignalIconId=").append(wifiSignalIconId)
-                    .append(",enabledDesc=").append(enabledDesc)
+                    .append(",ssid=").append(ssid)
                     .append(",activityIn=").append(activityIn)
                     .append(",activityOut=").append(activityOut)
                     .append(",wifiSignalContentDescription=").append(wifiSignalContentDescription)
@@ -272,16 +273,18 @@
 
         @Override
         public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon,
-                boolean activityIn, boolean activityOut, String description, boolean isTransient) {
+                boolean activityIn, boolean activityOut, String description, boolean isTransient,
+                String statusLabel) {
             if (DEBUG) Log.d(TAG, "onWifiSignalChanged enabled=" + enabled);
             mInfo.enabled = enabled;
             mInfo.connected = qsIcon.visible;
             mInfo.wifiSignalIconId = qsIcon.icon;
-            mInfo.enabledDesc = description;
+            mInfo.ssid = description;
             mInfo.activityIn = activityIn;
             mInfo.activityOut = activityOut;
             mInfo.wifiSignalContentDescription = qsIcon.contentDescription;
             mInfo.isTransient = isTransient;
+            mInfo.statusLabel = statusLabel;
             if (isShowingDetail()) {
                 mDetailAdapter.updateItems();
             }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 068fd3f..227f2d2 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -189,7 +189,6 @@
         mPublicNotificationBuilder =
                 new Notification.Builder(context, NotificationChannels.SCREENSHOTS_HEADSUP)
                         .setContentTitle(r.getString(R.string.screenshot_saving_title))
-                        .setContentText(r.getString(R.string.screenshot_saving_text))
                         .setSmallIcon(R.drawable.stat_notify_image)
                         .setCategory(Notification.CATEGORY_PROGRESS)
                         .setWhen(now)
@@ -203,7 +202,6 @@
             .setTicker(r.getString(R.string.screenshot_saving_ticker)
                     + (mTickerAddSpace ? " " : ""))
             .setContentTitle(r.getString(R.string.screenshot_saving_title))
-            .setContentText(r.getString(R.string.screenshot_saving_text))
             .setSmallIcon(R.drawable.stat_notify_image)
             .setWhen(now)
             .setShowWhen(true)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java b/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
index 3698c3a0..4388b41 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
@@ -48,6 +48,11 @@
         return findViewById(R.id.no_notifications);
     }
 
+    @Override
+    protected View findSecondaryView() {
+        return null;
+    }
+
     public void setTextColor(@ColorInt int color) {
         mEmptyText.setTextColor(color);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 24ebc83..ebc7b94 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -1061,6 +1061,7 @@
     }
 
     public void setDismissed(boolean fromAccessibility) {
+        setLongPressListener(null);
         mDismissed = true;
         mGroupParentWhenDismissed = mNotificationParent;
         mRefocusOnDismiss = fromAccessibility;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DismissView.java b/packages/SystemUI/src/com/android/systemui/statusbar/FooterView.java
similarity index 68%
rename from packages/SystemUI/src/com/android/systemui/statusbar/DismissView.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/FooterView.java
index d7c6443..0f4b621 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DismissView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FooterView.java
@@ -26,11 +26,12 @@
 import com.android.systemui.statusbar.stack.ExpandableViewState;
 import com.android.systemui.statusbar.stack.StackScrollState;
 
-public class DismissView extends StackScrollerDecorView {
+public class FooterView extends StackScrollerDecorView {
     private final int mClearAllTopPadding;
-    private DismissViewButton mDismissButton;
+    private FooterViewButton mDismissButton;
+    private FooterViewButton mManageButton;
 
-    public DismissView(Context context, AttributeSet attrs) {
+    public FooterView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mClearAllTopPadding = context.getResources().getDimensionPixelSize(
                 R.dimen.clear_all_padding_top);
@@ -38,21 +39,31 @@
 
     @Override
     protected View findContentView() {
+        return findViewById(R.id.content);
+    }
+
+    protected View findSecondaryView() {
         return findViewById(R.id.dismiss_text);
     }
 
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mDismissButton = (DismissViewButton) findContentView();
+        mDismissButton = (FooterViewButton) findSecondaryView();
+        mManageButton = findViewById(R.id.manage_text);
     }
 
     public void setTextColor(@ColorInt int color) {
+        mManageButton.setTextColor(color);
         mDismissButton.setTextColor(color);
     }
 
-    public void setOnButtonClickListener(OnClickListener listener) {
-        mContent.setOnClickListener(listener);
+    public void setManageButtonClickListener(OnClickListener listener) {
+        mManageButton.setOnClickListener(listener);
+    }
+
+    public void setDismissButtonClickListener(OnClickListener listener) {
+        mDismissButton.setOnClickListener(listener);
     }
 
     public boolean isOnEmptySpace(float touchX, float touchY) {
@@ -68,25 +79,26 @@
         mDismissButton.setText(R.string.clear_all_notifications_text);
         mDismissButton.setContentDescription(
                 mContext.getString(R.string.accessibility_clear_all));
+        mManageButton.setText(R.string.manage_notifications_text);
     }
 
     public boolean isButtonVisible() {
-        return mDismissButton.getAlpha() != 0.0f;
+        return mManageButton.getAlpha() != 0.0f;
     }
 
     @Override
     public ExpandableViewState createNewViewState(StackScrollState stackScrollState) {
-        return new DismissViewState();
+        return new FooterViewState();
     }
 
-    public class DismissViewState extends ExpandableViewState {
+    public class FooterViewState extends ExpandableViewState {
         @Override
         public void applyToView(View view) {
             super.applyToView(view);
-            if (view instanceof DismissView) {
-                DismissView dismissView = (DismissView) view;
+            if (view instanceof FooterView) {
+                FooterView footerView = (FooterView) view;
                 boolean visible = this.clipTopAmount < mClearAllTopPadding;
-                dismissView.performVisibilityAnimation(visible && !dismissView.willBeGone());
+                footerView.performVisibilityAnimation(visible && !footerView.willBeGone());
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DismissViewButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/FooterViewButton.java
similarity index 84%
rename from packages/SystemUI/src/com/android/systemui/statusbar/DismissViewButton.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/FooterViewButton.java
index b608d67..16ca0f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DismissViewButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FooterViewButton.java
@@ -23,21 +23,21 @@
 
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 
-public class DismissViewButton extends AlphaOptimizedButton {
+public class FooterViewButton extends AlphaOptimizedButton {
 
-    public DismissViewButton(Context context) {
+    public FooterViewButton(Context context) {
         this(context, null);
     }
 
-    public DismissViewButton(Context context, AttributeSet attrs) {
+    public FooterViewButton(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
-    public DismissViewButton(Context context, AttributeSet attrs, int defStyleAttr) {
+    public FooterViewButton(Context context, AttributeSet attrs, int defStyleAttr) {
         this(context, attrs, defStyleAttr, 0);
     }
 
-    public DismissViewButton(Context context, AttributeSet attrs, int defStyleAttr,
+    public FooterViewButton(Context context, AttributeSet attrs, int defStyleAttr,
             int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
index 3cf7741..e7b768f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
@@ -55,7 +55,6 @@
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Objects;
 
 // Intimately tied to the design of res/layout/signal_cluster_view.xml
 public class SignalClusterView extends LinearLayout implements NetworkControllerImpl.SignalCallback,
@@ -277,7 +276,8 @@
 
     @Override
     public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon,
-            boolean activityIn, boolean activityOut, String description, boolean isTransient) {
+            boolean activityIn, boolean activityOut, String description, boolean isTransient,
+            String secondaryLabel) {
         mWifiVisible = statusIcon.visible && !mBlockWifi;
         mWifiStrengthId = statusIcon.icon;
         mWifiDescription = statusIcon.contentDescription;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java
index badc40d..14a6c42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java
@@ -31,8 +31,11 @@
 public abstract class StackScrollerDecorView extends ExpandableView {
 
     protected View mContent;
+    protected View mSecondaryView;
     private boolean mIsVisible;
+    private boolean mIsSecondaryVisible;
     private boolean mAnimating;
+    private int mDuration = 260;
 
     public StackScrollerDecorView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -42,6 +45,7 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
         mContent = findContentView();
+        mSecondaryView = findSecondaryView();
         setInvisible();
     }
 
@@ -57,17 +61,37 @@
     }
 
     public void performVisibilityAnimation(boolean nowVisible) {
-        animateText(nowVisible, null /* onFinishedRunnable */);
+        animateText(mContent, nowVisible, null /* onFinishedRunnable */);
+        mIsVisible = nowVisible;
     }
 
     public void performVisibilityAnimation(boolean nowVisible, Runnable onFinishedRunnable) {
-        animateText(nowVisible, onFinishedRunnable);
+        animateText(mContent, nowVisible, onFinishedRunnable);
+        mIsVisible = nowVisible;
+    }
+
+    public void performSecondaryVisibilityAnimation(boolean nowVisible) {
+        performSecondaryVisibilityAnimation(nowVisible, null /* onFinishedRunnable */);
+    }
+
+    public void performSecondaryVisibilityAnimation(boolean nowVisible,
+            Runnable onFinishedRunnable) {
+        animateText(mSecondaryView, nowVisible, onFinishedRunnable);
+        mIsSecondaryVisible = nowVisible;
+    }
+
+    public boolean isSecondaryVisible() {
+        return mSecondaryView != null && (mIsSecondaryVisible || mAnimating);
     }
 
     public boolean isVisible() {
         return mIsVisible || mAnimating;
     }
 
+    void setDuration(int duration) {
+        mDuration = duration;
+    }
+
     /**
      * Animate the text to a new visibility.
      *
@@ -75,7 +99,10 @@
      * @param onFinishedRunnable A runnable which should be run when the animation is
      *        finished.
      */
-    private void animateText(boolean nowVisible, final Runnable onFinishedRunnable) {
+    private void animateText(View view, boolean nowVisible, final Runnable onFinishedRunnable) {
+        if (view == null) {
+            return;
+        }
         if (nowVisible != mIsVisible) {
             // Animate text
             float endValue = nowVisible ? 1.0f : 0.0f;
@@ -86,10 +113,10 @@
                 interpolator = Interpolators.ALPHA_OUT;
             }
             mAnimating = true;
-            mContent.animate()
+            view.animate()
                     .alpha(endValue)
                     .setInterpolator(interpolator)
-                    .setDuration(260)
+                    .setDuration(mDuration)
                     .withEndAction(new Runnable() {
                         @Override
                         public void run() {
@@ -99,7 +126,6 @@
                             }
                         }
                     });
-            mIsVisible = nowVisible;
         } else {
             if (onFinishedRunnable != null) {
                 onFinishedRunnable.run();
@@ -109,7 +135,11 @@
 
     public void setInvisible() {
         mContent.setAlpha(0.0f);
+        if (mSecondaryView != null) {
+            mSecondaryView.setAlpha(0.0f);
+        }
         mIsVisible = false;
+        mIsSecondaryVisible = false;
     }
 
     @Override
@@ -134,7 +164,15 @@
 
     public void cancelAnimation() {
         mContent.animate().cancel();
+        if (mSecondaryView != null) {
+            mSecondaryView.animate().cancel();
+        }
     }
 
     protected abstract View findContentView();
+
+    /**
+     * Returns a view that might not always appear while the main content view is still visible.
+     */
+    protected abstract View findSecondaryView();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index d609ae7..fcd4e8f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -330,6 +330,10 @@
         }
     }
 
+    public boolean willDismissWithAction() {
+        return mKeyguardView != null && mKeyguardView.hasDismissActions();
+    }
+
     protected void ensureView() {
         // Removal of the view might be deferred to reduce unlock latency,
         // in this case we need to force the removal, otherwise we'll
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index a39800d..58f8baa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -171,7 +171,7 @@
     private final OverviewProxyListener mOverviewProxyListener = new OverviewProxyListener() {
         @Override
         public void onConnectionChanged(boolean isConnected) {
-            mNavigationBarView.onOverviewProxyConnectionChanged(isConnected);
+            mNavigationBarView.updateStates();
             updateScreenPinningGestures();
             WindowManagerWrapper.getInstance()
                     .setNavBarVirtualKeyHapticFeedbackEnabled(!isConnected);
@@ -188,6 +188,7 @@
         @Override
         public void onInteractionFlagsChanged(@InteractionType int flags) {
             mNavigationBarView.updateStates();
+            updateScreenPinningGestures();
         }
     };
 
@@ -925,7 +926,9 @@
     private boolean onLongPressRecents() {
         if (mRecents == null || !ActivityManager.supportsMultiWindow(getContext())
                 || !mDivider.getView().getSnapAlgorithm().isSplitScreenFeasible()
-                || Recents.getConfiguration().isLowRamDevice) {
+                || Recents.getConfiguration().isLowRamDevice
+                // If we are connected to the overview service, then disable the recents button
+                || mOverviewProxyService.getProxy() != null) {
             return false;
         }
 
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 8fb0620..b4cb088 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -681,6 +681,7 @@
         updateSlippery();
         reloadNavIcons();
         updateNavButtonIcons();
+        setUpSwipeUpOnboarding(isQuickStepSwipeUpEnabled());
     }
 
     private void updateSlippery() {
@@ -816,11 +817,6 @@
         }
     }
 
-    public void onOverviewProxyConnectionChanged(boolean isConnected) {
-        updateStates();
-        setUpSwipeUpOnboarding(isQuickStepSwipeUpEnabled());
-    }
-
     @Override
     protected void onDraw(Canvas canvas) {
         mGestureHelper.onDraw(canvas);
@@ -842,10 +838,17 @@
             buttonBounds.setEmpty();
             return;
         }
+        // Temporarily reset the translation back to origin to get the position in window
+        final float posX = view.getTranslationX();
+        final float posY = view.getTranslationY();
+        view.setTranslationX(0);
+        view.setTranslationY(0);
         view.getLocationInWindow(mTmpPosition);
         buttonBounds.set(mTmpPosition[0], mTmpPosition[1],
                 mTmpPosition[0] + view.getMeasuredWidth(),
                 mTmpPosition[1] + view.getMeasuredHeight());
+        view.setTranslationX(posX);
+        view.setTranslationY(posY);
     }
 
     private void updateRotatedViews() {
@@ -1038,7 +1041,7 @@
         onPluginDisconnected(null); // Create default gesture helper
         Dependency.get(PluginManager.class).addPluginListener(this,
                 NavGesture.class, false /* Only one */);
-        setUpSwipeUpOnboarding(mOverviewProxyService.getProxy() != null);
+        setUpSwipeUpOnboarding(isQuickStepSwipeUpEnabled());
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 64e205d..cccda90 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -2165,18 +2165,18 @@
 
     @Override
     protected boolean fullyExpandedClearAllVisible() {
-        return mNotificationStackScroller.isDismissViewNotGone()
+        return mNotificationStackScroller.isFooterViewNotGone()
                 && mNotificationStackScroller.isScrolledToBottom() && !mQsExpandImmediate;
     }
 
     @Override
     protected boolean isClearAllVisible() {
-        return mNotificationStackScroller.isDismissViewVisible();
+        return mNotificationStackScroller.isFooterViewVisible();
     }
 
     @Override
     protected int getClearAllHeight() {
-        return mNotificationStackScroller.getDismissViewHeight();
+        return mNotificationStackScroller.getFooterViewHeight();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index cfc0cc6..7a6e98d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -148,7 +148,7 @@
     private float mNotificationDensity;
 
     // Scrim blanking callbacks
-    private Choreographer.FrameCallback mPendingFrameCallback;
+    private Runnable mPendingFrameCallback;
     private Runnable mBlankingTransitionRunnable;
 
     private final WakeLock mWakeLock;
@@ -240,7 +240,7 @@
 
         // Cancel blanking transitions that were pending before we requested a new state
         if (mPendingFrameCallback != null) {
-            Choreographer.getInstance().removeFrameCallback(mPendingFrameCallback);
+            mScrimBehind.removeCallbacks(mPendingFrameCallback);
             mPendingFrameCallback = null;
         }
         if (getHandler().hasCallbacks(mBlankingTransitionRunnable)) {
@@ -278,7 +278,7 @@
             // with too many things at this case, in order to not skip the initial frames.
             mScrimInFront.postOnAnimationDelayed(this::scheduleUpdate, 16);
             mAnimationDelay = StatusBar.FADE_KEYGUARD_START_DELAY;
-        } else if (!mDozeParameters.getAlwaysOn() && oldState == ScrimState.AOD
+        } else if ((!mDozeParameters.getAlwaysOn() && oldState == ScrimState.AOD)
                 || (mState == ScrimState.AOD && !mDozeParameters.getDisplayNeedsBlanking())) {
             // Scheduling a frame isn't enough when:
             //  • Leaving doze and we need to modify scrim color immediately
@@ -727,7 +727,7 @@
 
         // Notify callback that the screen is completely black and we're
         // ready to change the display power mode
-        mPendingFrameCallback = frameTimeNanos -> {
+        mPendingFrameCallback = () -> {
             if (mCallback != null) {
                 mCallback.onDisplayBlanked();
                 mScreenBlankingCallbackCalled = true;
@@ -743,7 +743,7 @@
 
             // Setting power states can happen after we push out the frame. Make sure we
             // stay fully opaque until the power state request reaches the lower levels.
-            final int delay = mScreenOn ? 16 : 500;
+            final int delay = mScreenOn ? 32 : 500;
             if (DEBUG) {
                 Log.d(TAG, "Fading out scrims with delay: " + delay);
             }
@@ -752,9 +752,15 @@
         doOnTheNextFrame(mPendingFrameCallback);
     }
 
+    /**
+     * Executes a callback after the frame has hit the display.
+     * @param callback What to run.
+     */
     @VisibleForTesting
-    protected void doOnTheNextFrame(Choreographer.FrameCallback callback) {
-        Choreographer.getInstance().postFrameCallback(callback);
+    protected void doOnTheNextFrame(Runnable callback) {
+        // Just calling View#postOnAnimation isn't enough because the frame might not have reached
+        // the display yet. A timeout is the safest solution.
+        mScrimBehind.postOnAnimationDelayed(callback, 32 /* delayMillis */);
     }
 
     @VisibleForTesting
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 8cd6295..ddb67df 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -130,7 +130,6 @@
 import com.android.internal.colorextraction.ColorExtractor;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.internal.widget.LockPatternUtils;
@@ -185,12 +184,11 @@
 import com.android.systemui.statusbar.BackDropView;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.CrossFadeHelper;
-import com.android.systemui.statusbar.DismissView;
 import com.android.systemui.statusbar.DragDownHelper;
 import com.android.systemui.statusbar.EmptyShadeView;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.FooterView;
 import com.android.systemui.statusbar.GestureRecorder;
-import com.android.systemui.statusbar.HeadsUpStatusBarView;
 import com.android.systemui.statusbar.KeyboardShortcuts;
 import com.android.systemui.statusbar.KeyguardIndicationController;
 import com.android.systemui.statusbar.NotificationData;
@@ -237,7 +235,6 @@
 import com.android.systemui.statusbar.policy.UserSwitcherController;
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
-import com.android.systemui.util.NotificationChannels;
 import com.android.systemui.volume.VolumeComponent;
 
 import java.io.FileDescriptor;
@@ -577,7 +574,7 @@
     private final LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
     protected NotificationIconAreaController mNotificationIconAreaController;
     private boolean mReinflateNotificationsOnUserSwitched;
-    private boolean mClearAllEnabled;
+    protected boolean mClearAllEnabled;
     @Nullable private View mAmbientIndicationContainer;
     private SysuiColorExtractor mColorExtractor;
     private ScreenLifecycle mScreenLifecycle;
@@ -868,7 +865,7 @@
         mVisualStabilityManager.setVisibilityLocationProvider(mStackScroller);
 
         inflateEmptyShadeView();
-        inflateDismissView();
+        inflateFooterView();
 
         mBackdrop = mStatusBarWindow.findViewById(R.id.backdrop);
         mBackdropFront = mBackdrop.findViewById(R.id.backdrop_front);
@@ -1133,8 +1130,8 @@
 
     protected void reevaluateStyles() {
         inflateSignalClusters();
-        inflateDismissView();
-        updateClearAll();
+        inflateFooterView();
+        updateFooter();
         inflateEmptyShadeView();
         updateEmptyShadeView();
     }
@@ -1186,18 +1183,21 @@
         mStackScroller.setEmptyShadeView(mEmptyShadeView);
     }
 
-    private void inflateDismissView() {
-        if (!mClearAllEnabled || mStackScroller == null) {
+    private void inflateFooterView() {
+        if (mStackScroller == null) {
             return;
         }
 
-        mDismissView = (DismissView) LayoutInflater.from(mContext).inflate(
-                R.layout.status_bar_notification_dismiss_all, mStackScroller, false);
-        mDismissView.setOnButtonClickListener(v -> {
+        mFooterView = (FooterView) LayoutInflater.from(mContext).inflate(
+                R.layout.status_bar_notification_footer, mStackScroller, false);
+        mFooterView.setDismissButtonClickListener(v -> {
             mMetricsLogger.action(MetricsEvent.ACTION_DISMISS_ALL_NOTES);
             clearAllNotifications();
         });
-        mStackScroller.setDismissView(mDismissView);
+        mFooterView.setManageButtonClickListener(v -> {
+            manageNotifications();
+        });
+        mStackScroller.setFooterView(mFooterView);
     }
 
     protected void createUserSwitcher() {
@@ -1211,6 +1211,12 @@
                 R.layout.super_status_bar, null);
     }
 
+    public void manageNotifications() {
+        Intent intent = new Intent(Settings.ACTION_ALL_APPS_NOTIFICATION_SETTINGS);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        startActivity(intent, true, true);
+    }
+
     public void clearAllNotifications() {
 
         // animate-swipe all dismissable notifications, then animate the shade closed
@@ -1393,7 +1399,7 @@
         mViewHierarchyManager.updateNotificationViews();
 
         updateSpeedBumpIndex();
-        updateClearAll();
+        updateFooter();
         updateEmptyShadeView();
 
         updateQsExpansionEnabled();
@@ -1457,13 +1463,14 @@
         mQSPanel.clickTile(tile);
     }
 
-    private void updateClearAll() {
-        if (!mClearAllEnabled) {
-            return;
-        }
-        boolean showDismissView = mState != StatusBarState.KEYGUARD
+    @VisibleForTesting
+    protected void updateFooter() {
+        boolean showFooterView = mState != StatusBarState.KEYGUARD
+                && mEntryManager.getNotificationData().getActiveNotifications().size() != 0;
+        boolean showDismissView = mClearAllEnabled && mState != StatusBarState.KEYGUARD
                 && hasActiveClearableNotifications();
-        mStackScroller.updateDismissView(showDismissView);
+
+        mStackScroller.updateFooterView(showFooterView, showDismissView);
     }
 
     /**
@@ -4654,9 +4661,12 @@
 
         if (mBouncerShowing) {
             // Bouncer needs the front scrim when it's on top of an activity,
-            // tapping on a notification or editing QS.
-            mScrimController.transitionTo(mIsOccluded || mNotificationPanel.needsScrimming() ?
-                    ScrimState.BOUNCER_SCRIMMED : ScrimState.BOUNCER);
+            // tapping on a notification, editing QS or being dismissed by
+            // FLAG_DISMISS_KEYGUARD_ACTIVITY.
+            ScrimState state = mIsOccluded || mNotificationPanel.needsScrimming()
+                    || mStatusBarKeyguardViewManager.willDismissWithAction() ?
+                    ScrimState.BOUNCER_SCRIMMED : ScrimState.BOUNCER;
+            mScrimController.transitionTo(state);
         } else if (mLaunchCameraOnScreenTurningOn || isInLaunchTransition()) {
             mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback);
         } else if (mBrightnessMirrorVisible) {
@@ -4935,7 +4945,7 @@
     protected RecentsComponent mRecents;
 
     protected NotificationShelf mNotificationShelf;
-    protected DismissView mDismissView;
+    protected FooterView mFooterView;
     protected EmptyShadeView mEmptyShadeView;
 
     protected AssistManager mAssistManager;
@@ -5408,8 +5418,8 @@
         // incremented in the following "changeViewPosition" calls so that its value is correct for
         // subsequent calls.
         int offsetFromEnd = 1;
-        if (mDismissView != null) {
-            mStackScroller.changeViewPosition(mDismissView,
+        if (mFooterView != null) {
+            mStackScroller.changeViewPosition(mFooterView,
                     mStackScroller.getChildCount() - offsetFromEnd++);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 56a7b1b..1a64b00 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -140,11 +140,14 @@
     }
 
     private void onPanelExpansionChanged(float expansion, boolean tracking) {
-        // We don't want to translate the bounce when the keyguard is occluded, because we're in
-        // a FLAG_SHOW_WHEN_LOCKED activity and need to conserve the original animation.
-        // We also don't want to show the bouncer when the user quickly taps on the display.
+        // We don't want to translate the bounce when:
+        // • Keyguard is occluded, because we're in a FLAG_SHOW_WHEN_LOCKED activity and need to
+        //   conserve the original animation.
+        // • The user quickly taps on the display and we show "swipe up to unlock."
+        // • Keyguard will be dismissed by an action. a.k.a: FLAG_DISMISS_KEYGUARD_ACTIVITY
         final boolean noLongerTracking = mLastTracking != tracking && !tracking;
-        if (mOccluded || mNotificationPanelView.isUnlockHintRunning()) {
+        if (mOccluded || mNotificationPanelView.isUnlockHintRunning()
+                || mBouncer.willDismissWithAction()) {
             mBouncer.setExpansion(0);
         } else if (mShowing && mStatusBar.isKeyguardCurrentlySecure() && !mDozing) {
             mBouncer.setExpansion(expansion);
@@ -696,6 +699,10 @@
         }
     }
 
+    public boolean willDismissWithAction() {
+        return mBouncer.willDismissWithAction();
+    }
+
     private static class DismissWithActionRequest {
         final OnDismissAction dismissAction;
         final Runnable cancelAction;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
index 5159e8d..b76d536 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
@@ -109,13 +109,13 @@
     @Override
     public void setWifiIndicators(final boolean enabled, final IconState statusIcon,
             final IconState qsIcon, final boolean activityIn, final boolean activityOut,
-            final String description, boolean isTransient) {
+            final String description, boolean isTransient, String secondaryLabel) {
         post(new Runnable() {
             @Override
             public void run() {
                 for (SignalCallback callback : mSignalCallbacks) {
                     callback.setWifiIndicators(enabled, statusIcon, qsIcon, activityIn, activityOut,
-                            description, isTransient);
+                            description, isTransient, secondaryLabel);
                 }
             }
         });
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index 76e3ad7..51fef7d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -47,7 +47,8 @@
 
     public interface SignalCallback {
         default void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon,
-                boolean activityIn, boolean activityOut, String description, boolean isTransient) {}
+                boolean activityIn, boolean activityOut, String description, boolean isTransient,
+                String statusLabel) {}
 
         default void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
                 int qsType, boolean activityIn, boolean activityOut, String typeContentDescription,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index 0f65421..e5e576d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -17,12 +17,15 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.net.ConnectivityManager;
 import android.net.NetworkCapabilities;
+import android.net.NetworkScoreManager;
 import android.net.wifi.WifiManager;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 import android.os.Messenger;
+import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -37,7 +40,6 @@
 
 public class WifiSignalController extends
         SignalController<WifiSignalController.WifiState, SignalController.IconGroup> {
-    private final WifiManager mWifiManager;
     private final AsyncChannel mWifiChannel;
     private final boolean mHasMobileData;
     private final WifiStatusTracker mWifiTracker;
@@ -47,12 +49,17 @@
             WifiManager wifiManager) {
         super("WifiSignalController", context, NetworkCapabilities.TRANSPORT_WIFI,
                 callbackHandler, networkController);
-        mWifiManager = wifiManager;
-        mWifiTracker = new WifiStatusTracker(mWifiManager);
+        NetworkScoreManager networkScoreManager =
+                context.getSystemService(NetworkScoreManager.class);
+        ConnectivityManager connectivityManager =
+                context.getSystemService(ConnectivityManager.class);
+        mWifiTracker = new WifiStatusTracker(mContext, wifiManager, networkScoreManager,
+                connectivityManager, this::handleStatusUpdated);
+        mWifiTracker.setListening(true);
         mHasMobileData = hasMobileData;
         Handler handler = new WifiHandler(Looper.getMainLooper());
         mWifiChannel = new AsyncChannel();
-        Messenger wifiMessenger = mWifiManager.getWifiServiceMessenger();
+        Messenger wifiMessenger = wifiManager.getWifiServiceMessenger();
         if (wifiMessenger != null) {
             mWifiChannel.connect(context, handler, wifiMessenger);
         }
@@ -68,7 +75,6 @@
                 WifiIcons.QS_WIFI_NO_NETWORK,
                 AccessibilityContentDescriptions.WIFI_NO_CONNECTION
                 );
-
     }
 
     @Override
@@ -87,13 +93,12 @@
         if (mCurrentState.inetCondition == 0) {
             contentDescription += ("," + mContext.getString(R.string.data_connection_no_internet));
         }
-
         IconState statusIcon = new IconState(wifiVisible, getCurrentIconId(), contentDescription);
         IconState qsIcon = new IconState(mCurrentState.connected, getQsCurrentIconId(),
                 contentDescription);
         callback.setWifiIndicators(mCurrentState.enabled, statusIcon, qsIcon,
                 ssidPresent && mCurrentState.activityIn, ssidPresent && mCurrentState.activityOut,
-                wifiDesc, mCurrentState.isTransient);
+                wifiDesc, mCurrentState.isTransient, mCurrentState.statusLabel);
     }
 
     /**
@@ -106,6 +111,12 @@
         mCurrentState.ssid = mWifiTracker.ssid;
         mCurrentState.rssi = mWifiTracker.rssi;
         mCurrentState.level = mWifiTracker.level;
+        mCurrentState.statusLabel = mWifiTracker.statusLabel;
+        notifyListenersIfNecessary();
+    }
+
+    private void handleStatusUpdated() {
+        mCurrentState.statusLabel = mWifiTracker.statusLabel;
         notifyListenersIfNecessary();
     }
 
@@ -150,6 +161,7 @@
     static class WifiState extends SignalController.State {
         String ssid;
         boolean isTransient;
+        String statusLabel;
 
         @Override
         public void copyFrom(State s) {
@@ -157,20 +169,26 @@
             WifiState state = (WifiState) s;
             ssid = state.ssid;
             isTransient = state.isTransient;
+            statusLabel = state.statusLabel;
         }
 
         @Override
         protected void toString(StringBuilder builder) {
             super.toString(builder);
-            builder.append(',').append("ssid=").append(ssid);
-            builder.append(',').append("isTransient=").append(isTransient);
+            builder.append(",ssid=").append(ssid)
+                .append(",isTransient=").append(isTransient)
+                .append(",statusLabel=").append(statusLabel);
         }
 
         @Override
         public boolean equals(Object o) {
-            return super.equals(o)
-                    && Objects.equals(((WifiState) o).ssid, ssid)
-                    && (((WifiState) o).isTransient == isTransient);
+            if (!super.equals(o)) {
+                return false;
+            }
+            WifiState other = (WifiState) o;
+            return Objects.equals(other.ssid, ssid)
+                    && other.isTransient == isTransient
+                    && TextUtils.equals(other.statusLabel, statusLabel);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index dc94203..8f6fa49 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -16,7 +16,8 @@
 
 package com.android.systemui.statusbar.stack;
 
-import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
+import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator
+        .ExpandAnimationParameters;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -43,6 +44,7 @@
 import android.service.notification.StatusBarNotification;
 import android.support.annotation.NonNull;
 import android.support.annotation.VisibleForTesting;
+import android.support.v4.graphics.ColorUtils;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
 import android.util.Log;
@@ -77,10 +79,10 @@
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
 import com.android.systemui.statusbar.ActivatableNotificationView;
-import com.android.systemui.statusbar.DismissView;
 import com.android.systemui.statusbar.EmptyShadeView;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.ExpandableView;
+import com.android.systemui.statusbar.FooterView;
 import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.NotificationGuts;
 import com.android.systemui.statusbar.NotificationListContainer;
@@ -99,8 +101,6 @@
 import com.android.systemui.statusbar.policy.HeadsUpUtil;
 import com.android.systemui.statusbar.policy.ScrollAdapter;
 
-import android.support.v4.graphics.ColorUtils;
-
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -109,7 +109,6 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.function.BiConsumer;
-import java.util.function.Consumer;
 
 /**
  * A layout which handles a dynamic amount of notifications and presents them in a scrollable stack.
@@ -226,7 +225,7 @@
     private boolean mExpandingNotification;
     private boolean mExpandedInThisMotion;
     protected boolean mScrollingEnabled;
-    protected DismissView mDismissView;
+    protected FooterView mFooterView;
     protected EmptyShadeView mEmptyShadeView;
     private boolean mDismissAllInProgress;
     private boolean mFadeNotificationsOnDismiss;
@@ -3838,13 +3837,13 @@
         Context context = new ContextThemeWrapper(mContext,
                 lightTheme ? R.style.Theme_SystemUI_Light : R.style.Theme_SystemUI);
         final int textColor = Utils.getColorAttr(context, R.attr.wallpaperTextColor);
-        mDismissView.setTextColor(textColor);
+        mFooterView.setTextColor(textColor);
         mEmptyShadeView.setTextColor(textColor);
     }
 
     public void goToFullShade(long delay) {
-        if (mDismissView != null) {
-            mDismissView.setInvisible();
+        if (mFooterView != null) {
+            mFooterView.setInvisible();
         }
         mEmptyShadeView.setInvisible();
         mGoToFullShadeNeedsAnimation = true;
@@ -3967,14 +3966,14 @@
         return -1;
     }
 
-    public void setDismissView(@NonNull DismissView dismissView) {
+    public void setFooterView(@NonNull FooterView footerView) {
         int index = -1;
-        if (mDismissView != null) {
-            index = indexOfChild(mDismissView);
-            removeView(mDismissView);
+        if (mFooterView != null) {
+            index = indexOfChild(mFooterView);
+            removeView(mFooterView);
         }
-        mDismissView = dismissView;
-        addView(mDismissView, index);
+        mFooterView = footerView;
+        addView(mFooterView, index);
     }
 
     public void setEmptyShadeView(EmptyShadeView emptyShadeView) {
@@ -3992,77 +3991,64 @@
         int newVisibility = visible ? VISIBLE : GONE;
         if (oldVisibility != newVisibility) {
             if (newVisibility != GONE) {
-                if (mEmptyShadeView.willBeGone()) {
-                    mEmptyShadeView.cancelAnimation();
-                } else {
-                    mEmptyShadeView.setInvisible();
-                }
                 if (mStatusBar.areNotificationsHidden()) {
                     mEmptyShadeView.setText(R.string.dnd_suppressing_shade_text);
                 } else {
                     mEmptyShadeView.setText(R.string.empty_shade_text);
                 }
-                mEmptyShadeView.setVisibility(newVisibility);
-                mEmptyShadeView.setWillBeGone(false);
-                updateContentHeight();
-                notifyHeightChangeListener(mEmptyShadeView);
+                showFooterView(mEmptyShadeView);
             } else {
-                Runnable onFinishedRunnable = new Runnable() {
-                    @Override
-                    public void run() {
-                        mEmptyShadeView.setVisibility(GONE);
-                        mEmptyShadeView.setWillBeGone(false);
-                        updateContentHeight();
-                        notifyHeightChangeListener(mEmptyShadeView);
-                    }
-                };
-                if (mAnimationsEnabled && mIsExpanded) {
-                    mEmptyShadeView.setWillBeGone(true);
-                    mEmptyShadeView.performVisibilityAnimation(false, onFinishedRunnable);
-                } else {
-                    mEmptyShadeView.setInvisible();
-                    onFinishedRunnable.run();
-                }
+                hideFooterView(mEmptyShadeView, true);
             }
         }
     }
 
-    public void updateDismissView(boolean visible) {
-        if (mDismissView == null) {
+    public void updateFooterView(boolean visible, boolean showDismissView) {
+        if (mFooterView == null) {
             return;
         }
-
-        int oldVisibility = mDismissView.willBeGone() ? GONE : mDismissView.getVisibility();
+        int oldVisibility = mFooterView.willBeGone() ? GONE : mFooterView.getVisibility();
         int newVisibility = visible ? VISIBLE : GONE;
         if (oldVisibility != newVisibility) {
             if (newVisibility != GONE) {
-                if (mDismissView.willBeGone()) {
-                    mDismissView.cancelAnimation();
-                } else {
-                    mDismissView.setInvisible();
-                }
-                mDismissView.setVisibility(newVisibility);
-                mDismissView.setWillBeGone(false);
-                updateContentHeight();
-                notifyHeightChangeListener(mDismissView);
+                showFooterView(mFooterView);
             } else {
-                Runnable dimissHideFinishRunnable = new Runnable() {
-                    @Override
-                    public void run() {
-                        mDismissView.setVisibility(GONE);
-                        mDismissView.setWillBeGone(false);
-                        updateContentHeight();
-                        notifyHeightChangeListener(mDismissView);
-                    }
-                };
-                if (mDismissView.isButtonVisible() && mIsExpanded && mAnimationsEnabled) {
-                    mDismissView.setWillBeGone(true);
-                    mDismissView.performVisibilityAnimation(false, dimissHideFinishRunnable);
-                } else {
-                    dimissHideFinishRunnable.run();
-                }
+                hideFooterView(mFooterView, mFooterView.isButtonVisible());
             }
         }
+        if (mFooterView.isSecondaryVisible() != showDismissView) {
+            mFooterView.performSecondaryVisibilityAnimation(showDismissView);
+        }
+    }
+
+    private void showFooterView(StackScrollerDecorView footerView) {
+        if (footerView.willBeGone()) {
+            footerView.cancelAnimation();
+        } else {
+            footerView.setInvisible();
+        }
+        footerView.setVisibility(VISIBLE);
+        footerView.setWillBeGone(false);
+        updateContentHeight();
+        notifyHeightChangeListener(footerView);
+    }
+
+    private void hideFooterView(StackScrollerDecorView footerView, boolean isButtonVisible) {
+        Runnable onHideFinishRunnable = new Runnable() {
+            @Override
+            public void run() {
+                footerView.setVisibility(GONE);
+                footerView.setWillBeGone(false);
+                updateContentHeight();
+                notifyHeightChangeListener(footerView);
+            }
+        };
+        if (isButtonVisible && mIsExpanded && mAnimationsEnabled) {
+            footerView.setWillBeGone(true);
+            footerView.performVisibilityAnimation(false, onHideFinishRunnable);
+        } else {
+            onHideFinishRunnable.run();
+        }
     }
 
     public void setDismissAllInProgress(boolean dismissAllInProgress) {
@@ -4088,18 +4074,18 @@
         }
     }
 
-    public boolean isDismissViewNotGone() {
-        return mDismissView != null
-                && mDismissView.getVisibility() != View.GONE
-                && !mDismissView.willBeGone();
+    public boolean isFooterViewNotGone() {
+        return mFooterView != null
+                && mFooterView.getVisibility() != View.GONE
+                && !mFooterView.willBeGone();
     }
 
-    public boolean isDismissViewVisible() {
-        return mDismissView != null && mDismissView.isVisible();
+    public boolean isFooterViewVisible() {
+        return mFooterView != null && mFooterView.isVisible();
     }
 
-    public int getDismissViewHeight() {
-        return mDismissView == null ? 0 : mDismissView.getHeight() + mPaddingBetweenElements;
+    public int getFooterViewHeight() {
+        return mFooterView == null ? 0 : mFooterView.getHeight() + mPaddingBetweenElements;
     }
 
     public int getEmptyShadeViewHeight() {
@@ -4155,8 +4141,8 @@
                 }
                 boolean belowChild = touchY > childTop + child.getActualHeight()
                         - child.getClipBottomAmount();
-                if (child == mDismissView) {
-                    if(!belowChild && !mDismissView.isOnEmptySpace(touchX - mDismissView.getX(),
+                if (child == mFooterView) {
+                    if(!belowChild && !mFooterView.isOnEmptySpace(touchX - mFooterView.getX(),
                                     touchY - childTop)) {
                         // We clicked on the dismiss button
                         return false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index 7c8e0fc..a8d2d98 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -22,10 +22,10 @@
 import android.view.View;
 import android.view.ViewGroup;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.DismissView;
 import com.android.systemui.statusbar.EmptyShadeView;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.ExpandableView;
+import com.android.systemui.statusbar.FooterView;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.notification.NotificationUtils;
 
@@ -390,7 +390,7 @@
         int paddingAfterChild = getPaddingAfterChild(algorithmState, child);
         int childHeight = getMaxAllowedChildHeight(child);
         childViewState.yTranslation = currentYPosition;
-        boolean isDismissView = child instanceof DismissView;
+        boolean isFooterView = child instanceof FooterView;
         boolean isEmptyShadeView = child instanceof EmptyShadeView;
 
         childViewState.location = ExpandableViewState.LOCATION_MAIN_AREA;
@@ -404,7 +404,7 @@
             float end = childViewState.yTranslation + childViewState.height + inset;
             childViewState.headsUpIsVisible = end < ambientState.getMaxHeadsUpTranslation();
         }
-        if (isDismissView) {
+        if (isFooterView) {
             childViewState.yTranslation = Math.min(childViewState.yTranslation,
                     ambientState.getInnerHeight() - childHeight);
         } else if (isEmptyShadeView) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 1c8a26c..d9cad0e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -18,7 +18,6 @@
 
 import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK;
 import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_GENERIC;
-import static android.media.AudioManager.RINGER_MODE_MAX;
 import static android.media.AudioManager.RINGER_MODE_NORMAL;
 import static android.media.AudioManager.RINGER_MODE_SILENT;
 import static android.media.AudioManager.RINGER_MODE_VIBRATE;
@@ -570,7 +569,11 @@
                 return;
             }
 
-            enableRingerViewsH(mState.zenMode == Global.ZEN_MODE_OFF || !mState.disallowRinger);
+            boolean isZenMuted = mState.zenMode == Global.ZEN_MODE_ALARMS
+                    || mState.zenMode == Global.ZEN_MODE_NO_INTERRUPTIONS
+                    || (mState.zenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+                        && mState.disallowRinger);
+            enableRingerViewsH(!isZenMuted);
             switch (mState.ringerModeInternal) {
                 case AudioManager.RINGER_MODE_VIBRATE:
                     mRingerIcon.setImageResource(R.drawable.ic_volume_ringer_vibrate);
@@ -586,7 +589,7 @@
                 case AudioManager.RINGER_MODE_NORMAL:
                 default:
                     boolean muted = (mAutomute && ss.level == 0) || ss.muted;
-                    if (muted) {
+                    if (!isZenMuted && muted) {
                         mRingerIcon.setImageResource(R.drawable.ic_volume_ringer_mute);
                         mRingerIcon.setContentDescription(mContext.getString(
                                 R.string.volume_stream_content_description_unmute,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewTest.java
new file mode 100644
index 0000000..3f85c9d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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.keyguard;
+
+import static org.mockito.Mockito.mock;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class KeyguardHostViewTest extends SysuiTestCase {
+
+    private KeyguardHostView mKeyguardHostView;
+
+    @Before
+    public void setup() {
+        mKeyguardHostView = new KeyguardHostView(getContext());
+    }
+
+    @Test
+    public void testHasDismissActions() {
+        Assert.assertFalse("Action not set yet", mKeyguardHostView.hasDismissActions());
+        mKeyguardHostView.setOnDismissAction(mock(KeyguardHostView.OnDismissAction.class),
+                null /* cancelAction */);
+        Assert.assertTrue("Action should exist", mKeyguardHostView.hasDismissActions());
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
index 231a1866..af29ab4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
@@ -18,7 +18,9 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -32,6 +34,7 @@
 import android.view.View;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
 import com.android.systemui.statusbar.stack.NotificationChildrenContainer;
 
@@ -159,6 +162,22 @@
     }
 
     @Test
+    public void testSetDismissed_longPressListenerRemoved() {
+        ExpandableNotificationRow.LongPressListener listener =
+                mock(ExpandableNotificationRow.LongPressListener.class);
+        mGroup.setLongPressListener(listener);
+        mGroup.doLongClickCallback(0,0);
+        verify(listener, times(1)).onLongPress(eq(mGroup), eq(0), eq(0),
+                any(NotificationMenuRowPlugin.MenuItem.class));
+        reset(listener);
+
+        mGroup.setDismissed(true);
+        mGroup.doLongClickCallback(0,0);
+        verify(listener, times(0)).onLongPress(eq(mGroup), eq(0), eq(0),
+                any(NotificationMenuRowPlugin.MenuItem.class));
+    }
+
+    @Test
     public void testShowAppOps_noHeader() {
         // public notification is custom layout - no header
         mGroup.setSensitive(true, true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/FooterViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/FooterViewTest.java
new file mode 100644
index 0000000..96b0255
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/FooterViewTest.java
@@ -0,0 +1,123 @@
+/*
+ * 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.statusbar;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.graphics.drawable.Icon;
+import android.os.UserHandle;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import com.android.internal.statusbar.StatusBarIcon;
+import com.android.internal.util.NotificationColorUtil;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class FooterViewTest extends SysuiTestCase {
+
+    FooterView mView;
+
+    @Before
+    public void setUp() {
+        mView = (FooterView) LayoutInflater.from(mContext).inflate(
+                R.layout.status_bar_notification_footer, null, false);
+        mView.setDuration(0);
+    }
+
+    @Test
+    public void testViewsNotNull() {
+        assertNotNull(mView.findContentView());
+        assertNotNull(mView.findSecondaryView());
+    }
+
+    @Test
+    public void setDismissOnClick() {
+        mView.setDismissButtonClickListener(mock(View.OnClickListener.class));
+        assertTrue(mView.findSecondaryView().hasOnClickListeners());
+    }
+
+    @Test
+    public void setManageOnClick() {
+        mView.setManageButtonClickListener(mock(View.OnClickListener.class));
+        assertTrue(mView.findViewById(R.id.manage_text).hasOnClickListeners());
+    }
+
+    @Test
+    public void testPerformVisibilityAnimation() {
+        mView.setInvisible();
+        assertFalse(mView.isVisible());
+
+        Runnable test = new Runnable() {
+            @Override
+            public void run() {
+                assertEquals(1.0f, mView.findContentView().getAlpha());
+                assertEquals(0.0f, mView.findSecondaryView().getAlpha());
+                assertTrue(mView.isVisible());
+            }
+        };
+        mView.performVisibilityAnimation(true, test);
+    }
+
+    @Test
+    public void testPerformSecondaryVisibilityAnimation() {
+        mView.setInvisible();
+        assertFalse(mView.isSecondaryVisible());
+
+        Runnable test = new Runnable() {
+            @Override
+            public void run() {
+                assertEquals(0.0f, mView.findContentView().getAlpha());
+                assertEquals(1.0f, mView.findSecondaryView().getAlpha());
+                assertTrue(mView.isSecondaryVisible());
+            }
+        };
+        mView.performSecondaryVisibilityAnimation(true, test);
+    }
+}
+
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
index a37947d..67453d5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
@@ -22,6 +22,7 @@
 import static org.mockito.ArgumentMatchers.anyFloat;
 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.reset;
 import static org.mockito.Mockito.spy;
@@ -307,4 +308,12 @@
                     mBouncer.isSecure(), mode != KeyguardSecurityModel.SecurityMode.None);
         }
     }
+
+    @Test
+    public void testWillDismissWithAction() {
+        mBouncer.ensureView();
+        Assert.assertFalse("Action not set yet", mBouncer.willDismissWithAction());
+        when(mKeyguardHostView.hasDismissActions()).thenReturn(true);
+        Assert.assertTrue("Action should exist", mBouncer.willDismissWithAction());
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 45845fc..7743c6b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -39,10 +39,8 @@
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
-import android.view.Choreographer;
 import android.view.View;
 
-import com.android.internal.util.Preconditions;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.ScrimView;
@@ -557,8 +555,8 @@
          * @param callback What to execute.
          */
         @Override
-        protected void doOnTheNextFrame(Choreographer.FrameCallback callback) {
-            callback.doFrame(0);
+        protected void doOnTheNextFrame(Runnable callback) {
+            callback.run();
         }
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index f13fa4e..f81ffd8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -18,7 +18,9 @@
 
 import static android.app.NotificationManager.IMPORTANCE_HIGH;
 
+import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.TestCase.fail;
 
@@ -55,6 +57,8 @@
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 import android.util.SparseArray;
+import android.view.Gravity;
+import android.view.View;
 import android.view.ViewGroup.LayoutParams;
 
 import com.android.internal.logging.MetricsLogger;
@@ -65,13 +69,15 @@
 import com.android.systemui.ForegroundServiceController;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.UiOffloadThread;
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.statusbar.ActivatableNotificationView;
 import com.android.systemui.statusbar.AppOpsListener;
 import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.FooterView;
+import com.android.systemui.statusbar.FooterViewButton;
 import com.android.systemui.statusbar.KeyguardIndicationController;
 import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.NotificationData.Entry;
@@ -88,7 +94,6 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
 import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
@@ -97,12 +102,14 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
 import java.io.ByteArrayOutputStream;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.function.Predicate;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -558,6 +565,68 @@
         verify(mScrimController).setKeyguardOccluded(eq(false));
     }
 
+    @Test
+    public void testInflateFooterView() {
+        mStatusBar.reevaluateStyles();
+        ArgumentCaptor<FooterView> captor = ArgumentCaptor.forClass(FooterView.class);
+        verify(mStackScroller).setFooterView(captor.capture());
+
+        assertNotNull(captor.getValue().findViewById(R.id.manage_text).hasOnClickListeners());
+        assertNotNull(captor.getValue().findViewById(R.id.dismiss_text).hasOnClickListeners());
+    }
+
+    @Test
+    public void testUpdateFooter_noNotifications() {
+        mStatusBar.setBarStateForTest(StatusBarState.SHADE);
+        assertEquals(0, mEntryManager.getNotificationData().getActiveNotifications().size());
+
+        mStatusBar.updateFooter();
+        verify(mStackScroller).updateFooterView(false, false);
+    }
+
+    @Test
+    public void testUpdateFooter_oneClearableNotification() {
+        mStatusBar.setBarStateForTest(StatusBarState.SHADE);
+        ArrayList<Entry> entries = new ArrayList<>();
+        entries.add(mock(Entry.class));
+        when(mNotificationData.getActiveNotifications()).thenReturn(entries);
+
+        ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
+        when(row.canViewBeDismissed()).thenReturn(true);
+        when(mStackScroller.getChildCount()).thenReturn(1);
+        when(mStackScroller.getChildAt(anyInt())).thenReturn(row);
+
+        mStatusBar.updateFooter();
+        verify(mStackScroller).updateFooterView(true, true);
+    }
+
+    @Test
+    public void testUpdateFooter_oneNonClearableNotification() {
+        mStatusBar.setBarStateForTest(StatusBarState.SHADE);
+        ArrayList<Entry> entries = new ArrayList<>();
+        entries.add(mock(Entry.class));
+        when(mNotificationData.getActiveNotifications()).thenReturn(entries);
+
+        mStatusBar.updateFooter();
+        verify(mStackScroller).updateFooterView(true, false);
+    }
+
+    @Test
+    public void testUpdateFooter_atEnd() {
+        // add footer
+        mStatusBar.reevaluateStyles();
+
+        // add notification
+        ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
+        when(row.isClearable()).thenReturn(true);
+        mStackScroller.addContainerView(row);
+
+        mStatusBar.onUpdateRowStates();
+
+        // move footer to end
+        verify(mStackScroller).changeViewPosition(any(FooterView.class), eq(-1 /* end */));
+    }
+
     static class TestableStatusBar extends StatusBar {
         public TestableStatusBar(StatusBarKeyguardViewManager man,
                 UnlockMethodCache unlock, KeyguardIndicationController key,
@@ -587,6 +656,7 @@
             mScrimController = scrimController;
             mFingerprintUnlockController = fingerprintUnlockController;
             mActivityLaunchAnimator = launchAnimator;
+            mClearAllEnabled = true;
         }
 
         private WakefulnessLifecycle createAwakeWakefulnessLifecycle() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
index 2afb48c..ed0f9ab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
@@ -18,7 +18,6 @@
 import android.os.HandlerThread;
 import android.support.test.runner.AndroidJUnit4;
 import android.telephony.SubscriptionInfo;
-import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
@@ -81,7 +80,8 @@
         boolean in = true;
         boolean out = true;
         String description = "Test";
-        mHandler.setWifiIndicators(enabled, status, qs, in, out, description, true);
+        String secondaryLabel = "Secondary label";
+        mHandler.setWifiIndicators(enabled, status, qs, in, out, description, true, secondaryLabel);
         waitForCallbacks();
 
         ArgumentCaptor<Boolean> enableArg = ArgumentCaptor.forClass(Boolean.class);
@@ -91,9 +91,10 @@
         ArgumentCaptor<Boolean> outArg = ArgumentCaptor.forClass(Boolean.class);
         ArgumentCaptor<String> descArg = ArgumentCaptor.forClass(String.class);
         ArgumentCaptor<Boolean> isTransient = ArgumentCaptor.forClass(Boolean.class);
+        ArgumentCaptor<String> secondary = ArgumentCaptor.forClass(String.class);
         Mockito.verify(mSignalCallback).setWifiIndicators(enableArg.capture(),
                 statusArg.capture(), qsArg.capture(), inArg.capture(), outArg.capture(),
-                descArg.capture(), isTransient.capture());
+                descArg.capture(), isTransient.capture(), secondary.capture());
         assertEquals(enabled, (boolean) enableArg.getValue());
         assertEquals(status, statusArg.getValue());
         assertEquals(qs, qsArg.getValue());
@@ -101,6 +102,7 @@
         assertEquals(out, (boolean) outArg.getValue());
         assertEquals(description, descArg.getValue());
         assertTrue(isTransient.getValue());
+        assertEquals(secondaryLabel, secondary.getValue());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
index fc3de84..d30e777 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
@@ -5,8 +5,6 @@
 import android.net.NetworkInfo;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
-import android.net.wifi.WifiSsid;
-import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
@@ -20,6 +18,7 @@
 
 import static junit.framework.Assert.assertEquals;
 
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Mockito.when;
@@ -39,6 +38,7 @@
         verifyLastWifiIcon(false, WifiIcons.WIFI_NO_NETWORK);
 
         setWifiState(true, testSsid);
+        setWifiLevel(0);
         verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[0][0]);
 
         for (int testLevel = 0; testLevel < WifiIcons.WIFI_LEVEL_COUNT; testLevel++) {
@@ -160,7 +160,8 @@
         ArgumentCaptor<Boolean> outArg = ArgumentCaptor.forClass(Boolean.class);
 
         Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setWifiIndicators(
-                anyBoolean(), any(), any(), inArg.capture(), outArg.capture(), any(), anyBoolean());
+                anyBoolean(), any(), any(), inArg.capture(), outArg.capture(), any(), anyBoolean(),
+                any());
         assertEquals("WiFi data in, in quick settings", in, (boolean) inArg.getValue());
         assertEquals("WiFi data out, in quick settings", out, (boolean) outArg.getValue());
     }
@@ -173,7 +174,7 @@
 
         Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setWifiIndicators(
                 enabledArg.capture(), any(), iconArg.capture(), anyBoolean(),
-                anyBoolean(), descArg.capture(), anyBoolean());
+                anyBoolean(), descArg.capture(), anyBoolean(), any());
         IconState iconState = iconArg.getValue();
         assertEquals("WiFi enabled, in quick settings", enabled, (boolean) enabledArg.getValue());
         assertEquals("WiFi connected, in quick settings", connected, iconState.visible);
@@ -186,7 +187,7 @@
 
         Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setWifiIndicators(
                 anyBoolean(), iconArg.capture(), any(), anyBoolean(), anyBoolean(),
-                any(), anyBoolean());
+                any(), anyBoolean(), any());
         IconState iconState = iconArg.getValue();
         assertEquals("WiFi visible, in status bar", visible, iconState.visible);
         assertEquals("WiFi signal, in status bar", icon, iconState.icon);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java
index 6fa91ff..42069e2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java
@@ -23,10 +23,12 @@
 import android.support.test.annotation.UiThreadTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
 
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.EmptyShadeView;
+import com.android.systemui.statusbar.FooterView;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.phone.StatusBar;
@@ -94,4 +96,30 @@
 
         verify(view).setText(R.string.empty_shade_text);
     }
+
+    @Test
+    public void manageNotifications_visible() {
+        FooterView view = mock(FooterView.class);
+        mStackScroller.setFooterView(view);
+        when(view.willBeGone()).thenReturn(true);
+        when(view.isSecondaryVisible()).thenReturn(true);
+
+        mStackScroller.updateFooterView(true, false);
+
+        verify(view).setVisibility(View.VISIBLE);
+        verify(view).performSecondaryVisibilityAnimation(false);
+    }
+
+    @Test
+    public void clearAll_visible() {
+        FooterView view = mock(FooterView.class);
+        mStackScroller.setFooterView(view);
+        when(view.willBeGone()).thenReturn(true);
+        when(view.isSecondaryVisible()).thenReturn(false);
+
+        mStackScroller.updateFooterView(true, true);
+
+        verify(view).setVisibility(View.VISIBLE);
+        verify(view).performSecondaryVisibilityAnimation(true);
+    }
 }
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 9417f04..c6878d7 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -5547,6 +5547,21 @@
     // OS: P
     ACTION_ANOMALY_TRIGGERED = 1367;
 
+    // ACTION: Settings > Condition > Device muted
+    // CATEGORY: SETTINGS
+    // OS: P
+    SETTINGS_CONDITION_DEVICE_MUTED = 1368;
+
+    // ACTION: Settings > Condition > Device vibrate
+    // CATEGORY: SETTINGS
+    // OS: P
+    SETTINGS_CONDITION_DEVICE_VIBRATE = 1369;
+
+    // OPEN: Settings > Connected devices > previously connected devices
+    // CATEGORY: SETTINGS
+    // OS: P
+    PREVIOUSLY_CONNECTED_DEVICES = 1370;
+
     // ---- End P Constants, all P constants go above this line ----
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
index 39d0070..9c74188 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -382,6 +382,9 @@
 
   // WifiWake statistics
   optional WifiWakeStats wifi_wake_stats = 94;
+
+  // Histogram counting instances of scans with N many 802.11mc (RTT) supporting APs
+  repeated NumConnectableNetworksBucket observed_80211mc_supporting_aps_in_scan_histogram = 95;
 }
 
 // Information that gets logged for every WiFi connection.
diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java
index 7bb532e..58f78ec 100644
--- a/services/autofill/java/com/android/server/autofill/Helper.java
+++ b/services/autofill/java/com/android/server/autofill/Helper.java
@@ -180,9 +180,11 @@
      *
      * @param structure Assist structure
      * @param urlBarIds list of ids; only the first id found will be sanitized.
+     *
+     * @return the node containing the URL bar
      */
     @Nullable
-    public static void sanitizeUrlBar(@NonNull AssistStructure structure,
+    public static ViewNode sanitizeUrlBar(@NonNull AssistStructure structure,
             @NonNull String[] urlBarIds) {
         final ViewNode urlBarNode = findViewNode(structure, (node) -> {
             return ArrayUtils.contains(urlBarIds, node.getIdEntry());
@@ -191,7 +193,7 @@
             final String domain = urlBarNode.getText().toString();
             if (domain.isEmpty()) {
                 if (sDebug) Slog.d(TAG, "sanitizeUrlBar(): empty on " + urlBarNode.getIdEntry());
-                return;
+                return null;
             }
             urlBarNode.setWebDomain(domain);
             if (sDebug) {
@@ -199,6 +201,7 @@
                         + urlBarNode.getWebDomain());
             }
         }
+        return urlBarNode;
     }
 
     private interface ViewNodeFilter {
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 1e1de35..bcb0bc4 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -146,6 +146,13 @@
     /** Whether the app being autofilled is running in compat mode. */
     private final boolean mCompatMode;
 
+    /** Node representing the URL bar on compat mode. */
+    @GuardedBy("mLock")
+    private ViewNode mUrlBar;
+
+    @GuardedBy("mLock")
+    private boolean mSaveOnAllViewsInvisible;
+
     @GuardedBy("mLock")
     private final ArrayMap<AutofillId, ViewState> mViewStates = new ArrayMap<>();
 
@@ -280,7 +287,17 @@
                         Slog.d(TAG, "url_bars in compat mode: " + Arrays.toString(urlBarIds));
                     }
                     if (urlBarIds != null) {
-                        Helper.sanitizeUrlBar(structure, urlBarIds);
+                        mUrlBar = Helper.sanitizeUrlBar(structure, urlBarIds);
+                        if (mUrlBar != null) {
+                            final AutofillId urlBarId = mUrlBar.getAutofillId();
+                            if (sDebug) {
+                                Slog.d(TAG, "Setting urlBar as id=" + urlBarId + " and domain "
+                                        + mUrlBar.getWebDomain());
+                            }
+                            final ViewState viewState = new ViewState(Session.this, urlBarId,
+                                    Session.this, ViewState.STATE_URL_BAR);
+                            mViewStates.put(urlBarId, viewState);
+                        }
                     }
                 }
                 structure.sanitizeForParceling(true);
@@ -1878,14 +1895,15 @@
             return;
         }
         if (sVerbose) {
-            Slog.v(TAG, "updateLocked(): id=" + id + ", action=" + action + ", flags=" + flags);
+            Slog.v(TAG, "updateLocked(): id=" + id + ", action=" + actionAsString(action)
+                    + ", flags=" + flags);
         }
         ViewState viewState = mViewStates.get(id);
 
         if (viewState == null) {
             if (action == ACTION_START_SESSION || action == ACTION_VALUE_CHANGED
                     || action == ACTION_VIEW_ENTERED) {
-                if (sVerbose) Slog.v(TAG, "Creating viewState for " + id + " on " + action);
+                if (sVerbose) Slog.v(TAG, "Creating viewState for " + id);
                 boolean isIgnored = isIgnoredLocked(id);
                 viewState = new ViewState(this, id, this,
                         isIgnored ? ViewState.STATE_IGNORED : ViewState.STATE_INITIAL);
@@ -1895,11 +1913,11 @@
                 // detectable, and batch-send them when the session is finished (but that will
                 // require tracking detectable fields on AutofillManager)
                 if (isIgnored) {
-                    if (sDebug) Slog.d(TAG, "updateLocked(): ignoring view " + id);
+                    if (sDebug) Slog.d(TAG, "updateLocked(): ignoring view " + viewState);
                     return;
                 }
             } else {
-                if (sVerbose) Slog.v(TAG, "Ignored action " + action + " for " + id);
+                if (sVerbose) Slog.v(TAG, "Ignoring specific action when viewState=null");
                 return;
             }
         }
@@ -1913,6 +1931,40 @@
                 requestNewFillResponseLocked(flags);
                 break;
             case ACTION_VALUE_CHANGED:
+                if (mCompatMode && (viewState.getState() & ViewState.STATE_URL_BAR) != 0) {
+                    // Must cancel the session if the value of the URL bar changed
+                    final String currentUrl = mUrlBar == null ? null
+                            : mUrlBar.getText().toString().trim();
+                    if (currentUrl == null) {
+                        // Sanity check - shouldn't happen.
+                        wtf(null, "URL bar value changed, but current value is null");
+                        return;
+                    }
+                    if (value == null || ! value.isText()) {
+                        // Sanity check - shouldn't happen.
+                        wtf(null, "URL bar value changed to null or non-text: %s", value);
+                        return;
+                    }
+                    final String newUrl = value.getTextValue().toString();
+                    if (newUrl.equals(currentUrl)) {
+                        if (sDebug) Slog.d(TAG, "Ignoring change on URL bar as it's the same");
+                        return;
+                    }
+                    if (mSaveOnAllViewsInvisible) {
+                        // We cannot cancel the session because it could hinder Save when all views
+                        // are finished, as the URL bar changed callback is usually called before
+                        // the virtual views become invisible.
+                        if (sDebug) {
+                            Slog.d(TAG, "Ignoring change on URL because session will finish when "
+                                    + "views are gone");
+                        }
+                        return;
+                    }
+                    if (sDebug) Slog.d(TAG, "Finishing session because URL bar changed");
+                    forceRemoveSelfLocked(AutofillManager.STATE_UNKNOWN_COMPAT_MODE);
+                    return;
+                }
+
                 if (value != null && !value.equals(viewState.getCurrentValue())) {
                     if (value.isEmpty()
                             && viewState.getCurrentValue() != null
@@ -1953,6 +2005,12 @@
                 if (sVerbose && virtualBounds != null) {
                     Slog.v(TAG, "entered on virtual child " + id + ": " + virtualBounds);
                 }
+
+                if (mCompatMode && (viewState.getState() & ViewState.STATE_URL_BAR) != 0) {
+                    if (sDebug) Slog.d(TAG, "Ignoring VIEW_ENTERED on URL BAR (id=" + id + ")");
+                    return;
+                }
+
                 requestNewFillResponseOnViewEnteredIfNecessaryLocked(id, viewState, flags);
 
                 // Remove the UI if the ViewState has changed.
@@ -2068,7 +2126,7 @@
         if (response == null) return;
 
         ArraySet<AutofillId> trackedViews = null;
-        boolean saveOnAllViewsInvisible = false;
+        mSaveOnAllViewsInvisible = false;
         boolean saveOnFinish = true;
         final SaveInfo saveInfo = response.getSaveInfo();
         final AutofillId saveTriggerId;
@@ -2081,10 +2139,10 @@
             if (mCompatMode) {
                 flags |= SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE;
             }
-            saveOnAllViewsInvisible = (flags & SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE) != 0;
+            mSaveOnAllViewsInvisible = (flags & SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE) != 0;
 
             // We only need to track views if we want to save once they become invisible.
-            if (saveOnAllViewsInvisible) {
+            if (mSaveOnAllViewsInvisible) {
                 if (trackedViews == null) {
                     trackedViews = new ArraySet<>();
                 }
@@ -2129,7 +2187,7 @@
                 Slog.v(TAG, "updateTrackedIdsLocked(): " + trackedViews + " => " + fillableIds
                         + " triggerId: " + saveTriggerId + " saveOnFinish:" + saveOnFinish);
             }
-            mClient.setTrackedViews(id, toArray(trackedViews), saveOnAllViewsInvisible,
+            mClient.setTrackedViews(id, toArray(trackedViews), mSaveOnAllViewsInvisible,
                     saveOnFinish, toArray(fillableIds), saveTriggerId);
         } catch (RemoteException e) {
             Slog.w(TAG, "Cannot set tracked ids", e);
@@ -2421,6 +2479,16 @@
         pw.print(prefix); pw.print("mClientState: "); pw.println(
                 Helper.bundleToString(mClientState));
         pw.print(prefix); pw.print("mCompatMode: "); pw.println(mCompatMode);
+        pw.print(prefix); pw.print("mUrlBar: ");
+        if (mUrlBar == null) {
+            pw.println("N/A");
+        } else {
+            pw.print("id="); pw.print(mUrlBar.getAutofillId());
+            pw.print(" domain="); pw.print(mUrlBar.getWebDomain());
+            pw.print(" text="); Helper.printlnRedactedText(pw, mUrlBar.getText());
+        }
+        pw.print(prefix); pw.print("mSaveOnAllViewsInvisible: "); pw.println(
+                mSaveOnAllViewsInvisible);
         pw.print(prefix); pw.print("mSelectedDatasetIds: "); pw.println(mSelectedDatasetIds);
         mRemoteFillService.dump(prefix, pw);
     }
@@ -2513,6 +2581,11 @@
      */
     @GuardedBy("mLock")
     void forceRemoveSelfLocked() {
+        forceRemoveSelfLocked(AutofillManager.STATE_UNKNOWN);
+    }
+
+    @GuardedBy("mLock")
+    void forceRemoveSelfLocked(int clientState) {
         if (sVerbose) Slog.v(TAG, "forceRemoveSelfLocked(): " + mPendingSaveUi);
 
         final boolean isPendingSaveUi = isSaveUiPendingLocked();
@@ -2521,7 +2594,7 @@
         mUi.destroyAll(mPendingSaveUi, this, false);
         if (!isPendingSaveUi) {
             try {
-                mClient.setSessionFinished(AutofillManager.STATE_UNKNOWN);
+                mClient.setSessionFinished(clientState);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Error notifying client to finish session", e);
             }
@@ -2624,4 +2697,19 @@
             Slog.wtf(TAG, message);
         }
     }
+
+    private static String actionAsString(int action) {
+        switch (action) {
+            case ACTION_START_SESSION:
+                return "START_SESSION";
+            case ACTION_VIEW_ENTERED:
+                return "VIEW_ENTERED";
+            case ACTION_VIEW_EXITED:
+                return "VIEW_EXITED";
+            case ACTION_VALUE_CHANGED:
+                return "VALUE_CHANGED";
+            default:
+                return "UNKNOWN_" + action;
+        }
+    }
 }
diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java
index 03c5850..97ab97b 100644
--- a/services/autofill/java/com/android/server/autofill/ViewState.java
+++ b/services/autofill/java/com/android/server/autofill/ViewState.java
@@ -67,6 +67,8 @@
     public static final int STATE_IGNORED = 0x080;
     /** User manually request autofill in this view, after it was already autofilled. */
     public static final int STATE_RESTARTED_SESSION = 0x100;
+    /** View is the URL bar of a package on compat mode. */
+    public  static final int STATE_URL_BAR = 0x200;
 
     public final AutofillId id;
 
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 3d7b21d..379658f 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -2668,6 +2668,9 @@
         final int userId = UserHandle.getUserId(Binder.getCallingUid());
         final UserEnvironment userEnv = new UserEnvironment(userId);
 
+        // Ignore requests to create directories while storage is locked
+        if (!isUserKeyUnlocked(userId)) return;
+
         // Validate that reported package name belongs to caller
         final AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(
                 Context.APP_OPS_SERVICE);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4f0acf7..5b5b80b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -281,6 +281,7 @@
 import android.content.IntentFilter;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.ApplicationInfo.HiddenApiEnforcementPolicy;
 import android.content.pm.ConfigurationInfo;
 import android.content.pm.IPackageDataObserver;
 import android.content.pm.IPackageManager;
@@ -1261,11 +1262,17 @@
         }
 
         public static GrantUri resolve(int defaultSourceUserHandle, Uri uri) {
-            return new GrantUri(ContentProvider.getUserIdFromUri(uri, defaultSourceUserHandle),
-                    ContentProvider.getUriWithoutUserId(uri), false);
+            if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
+                return new GrantUri(ContentProvider.getUserIdFromUri(uri, defaultSourceUserHandle),
+                        ContentProvider.getUriWithoutUserId(uri), false);
+            } else {
+                return new GrantUri(defaultSourceUserHandle, uri, false);
+            }
         }
     }
 
+    boolean mSystemProvidersInstalled;
+
     CoreSettingsObserver mCoreSettingsObserver;
 
     FontScaleSettingObserver mFontScaleSettingObserver;
@@ -4183,12 +4190,14 @@
                 runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES;
             }
 
-            if (!app.info.isAllowedToUseHiddenApi() &&
-                    !disableHiddenApiChecks &&
-                    !mHiddenApiBlacklist.isDisabled()) {
-                // This app is not allowed to use undocumented and private APIs, or blacklisting is
-                // enabled. Set up its runtime with the appropriate flag.
-                runtimeFlags |= Zygote.ENABLE_HIDDEN_API_CHECKS;
+            if (!disableHiddenApiChecks && !mHiddenApiBlacklist.isDisabled()) {
+                @HiddenApiEnforcementPolicy int policy =
+                        app.info.getHiddenApiEnforcementPolicy();
+                int policyBits = (policy << Zygote.API_ENFORCEMENT_POLICY_SHIFT);
+                if ((policyBits & Zygote.API_ENFORCEMENT_POLICY_MASK) != policyBits) {
+                    throw new IllegalStateException("Invalid API policy: " + policy);
+                }
+                runtimeFlags |= policyBits;
             }
 
             String invokeWith = null;
@@ -12110,6 +12119,14 @@
                             "Attempt to launch content provider before system ready");
                 }
 
+                // If system providers are not installed yet we aggressively crash to avoid
+                // creating multiple instance of these providers and then bad things happen!
+                if (!mSystemProvidersInstalled && cpi.applicationInfo.isSystemApp()
+                        && "system".equals(cpi.processName)) {
+                    throw new IllegalStateException("Cannot access system provider: '"
+                            + cpi.authority + "' before system providers are installed!");
+                }
+
                 // Make sure that the user who owns this provider is running.  If not,
                 // we don't want to allow it to run.
                 if (!mUserController.isUserRunning(userId, 0)) {
@@ -12663,6 +12680,10 @@
             mSystemThread.installSystemProviders(providers);
         }
 
+        synchronized (this) {
+            mSystemProvidersInstalled = true;
+        }
+
         mConstants.start(mContext.getContentResolver());
         mCoreSettingsObserver = new CoreSettingsObserver(this);
         mFontScaleSettingObserver = new FontScaleSettingObserver();
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index 4313d17..711d40b 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -60,8 +60,14 @@
         int[] backlightRange = resources.getIntArray(
                 com.android.internal.R.array.config_screenBrightnessBacklight);
 
+        float[] minimumBrightnessCurveLux = getLuxLevels(resources.getIntArray(
+                com.android.internal.R.array.config_autoBrightnessMinimumBrightnessCurveLux));
+        float[] minimumBrightnessCurveNits = getFloatArray(resources.obtainTypedArray(
+                com.android.internal.R.array.config_autoBrightnessMinimumBrightnessCurveNits));
+
         if (isValidMapping(nitsRange, backlightRange)
-                && isValidMapping(luxLevels, brightnessLevelsNits)) {
+                && isValidMapping(luxLevels, brightnessLevelsNits)
+                && isValidMapping(minimumBrightnessCurveLux, minimumBrightnessCurveNits)) {
             int minimumBacklight = resources.getInteger(
                     com.android.internal.R.integer.config_screenBrightnessSettingMinimum);
             int maximumBacklight = resources.getInteger(
@@ -73,7 +79,8 @@
             }
             BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder();
             builder.setCurve(luxLevels, brightnessLevelsNits);
-            return new PhysicalMappingStrategy(builder.build(), nitsRange, backlightRange);
+            return new PhysicalMappingStrategy(builder.build(), nitsRange, backlightRange,
+                    minimumBrightnessCurveLux, minimumBrightnessCurveNits);
         } else if (isValidMapping(luxLevels, brightnessLevelsBacklight)) {
             return new SimpleMappingStrategy(luxLevels, brightnessLevelsBacklight);
         } else {
@@ -448,8 +455,11 @@
         private float mUserLux;
         private float mUserBrightness;
 
+        private final Spline mMinimumBrightnessCurve;
+
         public PhysicalMappingStrategy(BrightnessConfiguration config,
-                float[] nits, int[] backlight) {
+                float[] nits, int[] backlight, float[] minimumBrightnessCurveLux,
+                float[] minimumBrightnessCurveNits) {
             Preconditions.checkArgument(nits.length != 0 && backlight.length != 0,
                     "Nits and backlight arrays must not be empty!");
             Preconditions.checkArgument(nits.length == backlight.length,
@@ -469,6 +479,9 @@
                 normalizedBacklight[i] = normalizeAbsoluteBrightness(backlight[i]);
             }
 
+            mMinimumBrightnessCurve = Spline.createSpline(
+                minimumBrightnessCurveLux, minimumBrightnessCurveNits);
+
             mNitsToBacklightSpline = createSpline(nits, normalizedBacklight);
             mBacklightToNitsSpline = createSpline(normalizedBacklight, nits);
 
@@ -484,7 +497,7 @@
             if (config.equals(mConfig)) {
                 return false;
             }
-
+            validateBrightnessConfiguration(config);
             Pair<float[], float[]> curve = config.getCurve();
             mBrightnessSpline = createSpline(curve.first /*lux*/, curve.second /*nits*/);
             mConfig = config;
@@ -549,5 +562,24 @@
             pw.println("  mUserLux=" + mUserLux);
             pw.println("  mUserBrightness=" + mUserBrightness);
         }
+
+        private void validateBrightnessConfiguration(BrightnessConfiguration config) {
+            Pair<float[], float[]> curve = config.getCurve();
+            Spline brightnessSpline = Spline.createSpline(curve.first, curve.second);
+            if (isBrightnessSplineTooDark(brightnessSpline)) {
+                throw new IllegalArgumentException("brightness curve is too dark");
+            }
+        }
+
+        private boolean isBrightnessSplineTooDark(Spline brightnessSpline) {
+            float[] lux = mDefaultConfig.getCurve().first;
+            for (int i = 0; i < lux.length; i++) {
+                if (brightnessSpline.interpolate(lux[i]) <
+                        mMinimumBrightnessCurve.interpolate(lux[i])) {
+                    return true;
+                }
+            }
+            return false;
+        }
     }
 }
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index ff8b88b..1784ef1 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -500,7 +500,7 @@
     }
 
     public void onSwitchUser(@UserIdInt int newUserId) {
-        handleSettingsChange();
+        handleSettingsChange(true /* userSwitch */);
         mBrightnessTracker.onSwitchUser(newUserId);
     }
 
@@ -1420,8 +1420,12 @@
         mHandler.post(mOnStateChangedRunnable);
     }
 
-    private void handleSettingsChange() {
+    private void handleSettingsChange(boolean userSwitch) {
         mPendingScreenBrightnessSetting = getScreenBrightnessSetting();
+        if (userSwitch) {
+            // Don't treat user switches as user initiated change.
+            mCurrentScreenBrightnessSetting = mPendingScreenBrightnessSetting;
+        }
         mPendingAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting();
         // We don't bother with a pending variable for VR screen brightness since we just
         // immediately adapt to it.
@@ -1735,7 +1739,7 @@
 
         @Override
         public void onChange(boolean selfChange, Uri uri) {
-            handleSettingsChange();
+            handleSettingsChange(false /* userSwitch */);
         }
     }
 
diff --git a/services/core/java/com/android/server/job/controllers/ConnectivityController.java b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
index abe55bb..8365fd2 100644
--- a/services/core/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
@@ -160,7 +160,7 @@
     private static boolean isRelaxedSatisfied(JobStatus jobStatus, Network network,
             NetworkCapabilities capabilities, Constants constants) {
         // Only consider doing this for prefetching jobs
-        if ((jobStatus.getJob().getFlags() & JobInfo.FLAG_IS_PREFETCH) == 0) {
+        if (!jobStatus.getJob().isPrefetch()) {
             return false;
         }
 
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 7c56f4d..f617964 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -21,6 +21,7 @@
 import static android.content.Context.KEYGUARD_SERVICE;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
 import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_ENABLED_KEY;
 import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_HANDLE_KEY;
@@ -524,6 +525,10 @@
 
     public void onCleanupUser(int userId) {
         hideEncryptionNotification(new UserHandle(userId));
+        // User is stopped with its CE key evicted. Require strong auth next time to be able to
+        // unlock the user's storage. Use STRONG_AUTH_REQUIRED_AFTER_BOOT since stopping and
+        // restarting a user later is equivalent to rebooting the device.
+        requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_BOOT, userId);
     }
 
     public void onStartUser(final int userId) {
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index f0bcb81..6d2bec8 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -217,12 +217,17 @@
                     ERROR_INVALID_CERTIFICATE, "Failed to validate certificate.");
         }
 
+        boolean wasInitialized = mDatabase.getRecoveryServiceCertPath(userId, uid) != null;
+
         // Save the chosen and validated certificate into database
         try {
             Log.d(TAG, "Saving the randomly chosen endpoint certificate to database");
             if (mDatabase.setRecoveryServiceCertPath(userId, uid, certPath) > 0) {
                 mDatabase.setRecoveryServiceCertSerial(userId, uid, newSerial);
-                mDatabase.setShouldCreateSnapshot(userId, uid, true);
+                if (wasInitialized) {
+                    Log.i(TAG, "This is a certificate change. Snapshot pending.");
+                    mDatabase.setShouldCreateSnapshot(userId, uid, true);
+                }
                 mDatabase.setCounterId(userId, uid, new SecureRandom().nextLong());
             }
         } catch (CertificateEncodingException e) {
@@ -382,10 +387,26 @@
         Preconditions.checkNotNull(secretTypes, "secretTypes is null");
         int userId = UserHandle.getCallingUserId();
         int uid = Binder.getCallingUid();
-        long updatedRows = mDatabase.setRecoverySecretTypes(userId, uid, secretTypes);
-        if (updatedRows > 0) {
-            mDatabase.setShouldCreateSnapshot(userId, uid, true);
+
+        int[] currentSecretTypes = mDatabase.getRecoverySecretTypes(userId, uid);
+        if (Arrays.equals(secretTypes, currentSecretTypes)) {
+            Log.v(TAG, "Not updating secret types - same as old value.");
+            return;
         }
+
+        long updatedRows = mDatabase.setRecoverySecretTypes(userId, uid, secretTypes);
+        if (updatedRows < 1) {
+            throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR,
+                    "Database error trying to set secret types.");
+        }
+
+        if (currentSecretTypes.length == 0) {
+            Log.i(TAG, "Initialized secret types.");
+            return;
+        }
+
+        Log.i(TAG, "Updated secret types. Snapshot pending.");
+        mDatabase.setShouldCreateSnapshot(userId, uid, true);
     }
 
     /**
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 7348b84..01f0d74 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -1532,7 +1532,7 @@
         @Override
         public boolean createSession2(Bundle sessionToken) {
             final int uid = Binder.getCallingUid();
-            final SessionToken2 token = SessionToken2.fromBundle(getContext(), sessionToken);
+            final SessionToken2 token = SessionToken2.fromBundle(sessionToken);
             if (token == null || token.getUid() != uid) {
                 Log.w(TAG, "onSessionCreated failed, expected caller uid=" + token.getUid()
                         + " but from uid=" + uid);
@@ -1571,7 +1571,7 @@
         @Override
         public void destroySession2(Bundle sessionToken) {
             final int uid = Binder.getCallingUid();
-            final SessionToken2 token = SessionToken2.fromBundle(getContext(), sessionToken);
+            final SessionToken2 token = SessionToken2.fromBundle(sessionToken);
             if (token == null || token.getUid() != uid) {
                 Log.w(TAG, "onSessionDestroyed failed, expected caller uid=" + token.getUid()
                         + " but from uid=" + uid);
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index efca159..8febecf 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1141,7 +1141,7 @@
         // TODO: support shared UIDs
         if (maxBytes > 0 && maxBytes > totalBytes / 2) {
             final String[] packageNames = mContext.getPackageManager().getPackagesForUid(maxUid);
-            if (packageNames.length == 1) {
+            if (packageNames != null && packageNames.length == 1) {
                 try {
                     return mContext.getPackageManager().getApplicationInfo(packageNames[0],
                             MATCH_ANY_USER | MATCH_DISABLED_COMPONENTS | MATCH_DIRECT_BOOT_AWARE
diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java b/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java
index 2714d5e..d793842 100644
--- a/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java
+++ b/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java
@@ -107,6 +107,10 @@
      * Reload watchlist by reading config file.
      */
     public void reloadConfig() {
+        if (!mXmlFile.exists()) {
+            // No config file
+            return;
+        }
         try (FileInputStream stream = new FileInputStream(mXmlFile)){
             final List<byte[]> crc32DomainList = new ArrayList<>();
             final List<byte[]> sha256DomainList = new ArrayList<>();
diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistSettings.java b/services/core/java/com/android/server/net/watchlist/WatchlistSettings.java
index f5ba889..e20a510 100644
--- a/services/core/java/com/android/server/net/watchlist/WatchlistSettings.java
+++ b/services/core/java/com/android/server/net/watchlist/WatchlistSettings.java
@@ -87,6 +87,10 @@
     }
 
     public void reloadSettings() {
+        if (!mXmlFile.exists()) {
+            // No settings config
+            return;
+        }
         try (FileInputStream stream = mXmlFile.openRead()){
             XmlPullParser parser = Xml.newPullParser();
             parser.setInput(stream, StandardCharsets.UTF_8.name());
@@ -97,7 +101,7 @@
                     mPrivacySecretKey = parseSecretKey(parser);
                 }
             }
-            Log.i(TAG, "Reload watchlist settings done");
+            Slog.i(TAG, "Reload watchlist settings done");
         } catch (IllegalStateException | NullPointerException | NumberFormatException |
                 XmlPullParserException | IOException | IndexOutOfBoundsException e) {
             Slog.e(TAG, "Failed parsing xml", e);
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 5c82343..586abc1 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -933,11 +933,7 @@
                 }
                 break;
             case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
-                if (ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(mConfig)
-                        && ringerModeInternal != AudioManager.RINGER_MODE_SILENT) {
-                    setPreviousRingerModeSetting(ringerModeInternal);
-                    newRingerModeInternal = AudioManager.RINGER_MODE_SILENT;
-                }
+                // do not apply zen to ringer, streams zen muted in AudioService
                 break;
             case Global.ZEN_MODE_OFF:
                 if (ringerModeInternal == AudioManager.RINGER_MODE_SILENT) {
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 9e7ad47..61c6be7 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -47,6 +47,8 @@
 
 import dalvik.system.DexFile;
 
+import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_NONE;
+
 import static com.android.server.pm.Installer.DEXOPT_BOOTCOMPLETE;
 import static com.android.server.pm.Installer.DEXOPT_DEBUGGABLE;
 import static com.android.server.pm.Installer.DEXOPT_PROFILE_GUIDED;
@@ -532,7 +534,10 @@
         int profileFlag = isProfileGuidedFilter ? DEXOPT_PROFILE_GUIDED : 0;
         // Some apps are executed with restrictions on hidden API usage. If this app is one
         // of them, pass a flag to dexopt to enable the same restrictions during compilation.
-        int hiddenApiFlag = info.isAllowedToUseHiddenApi() ? 0 : DEXOPT_ENABLE_HIDDEN_API_CHECKS;
+        // TODO we should pass the actual flag value to dexopt, rather than assuming blacklist
+        int hiddenApiFlag = info.getHiddenApiEnforcementPolicy() == HIDDEN_API_ENFORCEMENT_NONE
+                ? 0
+                : DEXOPT_ENABLE_HIDDEN_API_CHECKS;
         // Avoid generating CompactDex for modes that are latency critical.
         final int compilationReason = options.getCompilationReason();
         boolean generateCompactDex = true;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 950d8df..5a63f29 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -10355,7 +10355,7 @@
 
         if (Build.IS_DEBUGGABLE &&
                 pkg.isPrivileged() &&
-                !SystemProperties.getBoolean(PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB, true)) {
+                SystemProperties.getBoolean(PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB, false)) {
             PackageManagerServiceUtils.logPackageHasUncompressedCode(pkg);
         }
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 5060c4d..853081a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -331,18 +331,18 @@
                 ZipEntry entry = it.next();
                 if (entry.getName().endsWith(".dex")) {
                     if (entry.getMethod() != ZipEntry.STORED) {
-                        Slog.wtf(TAG, "APK " + fileName + " has compressed dex code " +
+                        Slog.w(TAG, "APK " + fileName + " has compressed dex code " +
                                 entry.getName());
                     } else if ((entry.getDataOffset() & 0x3) != 0) {
-                        Slog.wtf(TAG, "APK " + fileName + " has unaligned dex code " +
+                        Slog.w(TAG, "APK " + fileName + " has unaligned dex code " +
                                 entry.getName());
                     }
                 } else if (entry.getName().endsWith(".so")) {
                     if (entry.getMethod() != ZipEntry.STORED) {
-                        Slog.wtf(TAG, "APK " + fileName + " has compressed native code " +
+                        Slog.w(TAG, "APK " + fileName + " has compressed native code " +
                                 entry.getName());
                     } else if ((entry.getDataOffset() & (0x1000 - 1)) != 0) {
-                        Slog.wtf(TAG, "APK " + fileName + " has unaligned native code " +
+                        Slog.w(TAG, "APK " + fileName + " has unaligned native code " +
                                 entry.getName());
                     }
                 }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 3e47ea6..a7eac55 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -385,6 +385,11 @@
      */
     private int mSurfaceSize;
 
+    /**
+     * Sequence number for the current layout pass.
+     */
+    int mLayoutSeq = 0;
+
     /** Temporary float array to retrieve 3x3 matrix values. */
     private final float[] mTmpFloats = new float[9];
 
@@ -554,7 +559,7 @@
                 w.prelayout();
                 final boolean firstLayout = !w.isLaidOut();
                 mService.mPolicy.layoutWindowLw(w, null, mDisplayFrames);
-                w.mLayoutSeq = mService.mLayoutSeq;
+                w.mLayoutSeq = mLayoutSeq;
 
                 // If this is the first layout, we need to initialize the last inset values as
                 // otherwise we'd immediately cause an unnecessary resize.
@@ -593,7 +598,7 @@
                 w.mLayoutNeeded = false;
                 w.prelayout();
                 mService.mPolicy.layoutWindowLw(w, w.getParentWindow(), mDisplayFrames);
-                w.mLayoutSeq = mService.mLayoutSeq;
+                w.mLayoutSeq = mLayoutSeq;
                 if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + w.mFrame
                         + " mContainingFrame=" + w.mContainingFrame
                         + " mDisplayFrame=" + w.mDisplayFrame);
@@ -2219,6 +2224,9 @@
             pw.println(" mTouchExcludeRegion=" + mTouchExcludeRegion);
 
         pw.println();
+        pw.print(prefix); pw.print("mLayoutSeq="); pw.println(mLayoutSeq);
+
+        pw.println();
         pw.println(prefix + "Application tokens in top down Z order:");
         for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
             final TaskStack stack = mTaskStackContainers.getChildAt(stackNdx);
@@ -2927,9 +2935,9 @@
             mService.mScreenRect.set(0, 0, dw, dh);
         }
 
-        int seq = mService.mLayoutSeq + 1;
+        int seq = mLayoutSeq + 1;
         if (seq < 0) seq = 0;
-        mService.mLayoutSeq = seq;
+        mLayoutSeq = seq;
 
         // Used to indicate that we have processed the dream window and all additional windows are
         // behind it.
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 19d6691..87a7a74 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -51,6 +51,7 @@
 import com.google.android.collect.Sets;
 
 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
+import com.android.server.wm.utils.InsetUtils;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -400,9 +401,11 @@
             if (mainWindow == null) {
                 return null;
             }
+            final Rect insets = new Rect(mainWindow.mContentInsets);
+            InsetUtils.addInsets(insets, mainWindow.mAppToken.getLetterboxInsets());
             mTarget = new RemoteAnimationTarget(mTask.mTaskId, MODE_CLOSING, mCapturedLeash,
                     !mTask.fillsParent(), mainWindow.mWinAnimator.mLastClipRect,
-                    mainWindow.mContentInsets, mTask.getPrefixOrderIndex(), position, bounds,
+                    insets, mTask.getPrefixOrderIndex(), position, bounds,
                     mTask.getWindowConfiguration(), mIsRecentTaskInvisible);
             return mTarget;
         }
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index c590067..3e7feda 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -16,14 +16,15 @@
 
 package com.android.server.wm;
 
+import static com.android.server.wm.AnimationAdapterProto.REMOTE;
+import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.AnimationAdapterProto.REMOTE;
-import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
 
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.os.Binder;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -37,6 +38,7 @@
 
 import com.android.internal.util.FastPrintWriter;
 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
+import com.android.server.wm.utils.InsetUtils;
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
@@ -194,12 +196,17 @@
 
         @Override
         public void onAnimationFinished() throws RemoteException {
-            if (mOuter != null) {
-                mOuter.onAnimationFinished();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                if (mOuter != null) {
+                    mOuter.onAnimationFinished();
 
-                // In case the client holds on to the finish callback, make sure we don't leak
-                // RemoteAnimationController which in turn would leak the runner on the client.
-                mOuter = null;
+                    // In case the client holds on to the finish callback, make sure we don't leak
+                    // RemoteAnimationController which in turn would leak the runner on the client.
+                    mOuter = null;
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
             }
         }
 
@@ -235,9 +242,11 @@
                     || mCapturedLeash == null) {
                 return null;
             }
+            final Rect insets = new Rect(mainWindow.mContentInsets);
+            InsetUtils.addInsets(insets, mAppWindowToken.getLetterboxInsets());
             mTarget = new RemoteAnimationTarget(task.mTaskId, getMode(),
                     mCapturedLeash, !mAppWindowToken.fillsParent(),
-                    mainWindow.mWinAnimator.mLastClipRect, mainWindow.mContentInsets,
+                    mainWindow.mWinAnimator.mLastClipRect, insets,
                     mAppWindowToken.getPrefixOrderIndex(), mPosition, mStackBounds,
                     task.getWindowConfiguration(), false /*isNotInRecents*/);
             return mTarget;
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 970a8d7..5f9d679 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -27,7 +27,6 @@
 import android.app.ActivityManager.TaskSnapshot;
 import android.content.pm.PackageManager;
 import android.graphics.Bitmap;
-import android.graphics.Color;
 import android.graphics.GraphicBuffer;
 import android.graphics.Rect;
 import android.os.Environment;
@@ -45,6 +44,7 @@
 import com.android.server.policy.WindowManagerPolicy.ScreenOffListener;
 import com.android.server.policy.WindowManagerPolicy.StartingSurface;
 import com.android.server.wm.TaskSnapshotSurface.SystemBarBackgroundPainter;
+import com.android.server.wm.utils.InsetUtils;
 
 import com.google.android.collect.Sets;
 
@@ -273,7 +273,7 @@
             return null;
         }
         return new TaskSnapshot(buffer, top.getConfiguration().orientation,
-                getInsetsFromTaskBounds(mainWindow, task),
+                getInsets(mainWindow),
                 isLowRamDevice /* reduced */, scaleFraction /* scale */,
                 true /* isRealSnapshot */);
     }
@@ -282,11 +282,11 @@
         return mIsRunningOnWear || mIsRunningOnTv || mIsRunningOnIoT;
     }
 
-    private Rect getInsetsFromTaskBounds(WindowState state, Task task) {
+    private Rect getInsets(WindowState state) {
         // XXX(b/72757033): These are insets relative to the window frame, but we're really
         // interested in the insets relative to the task bounds.
-        Rect insets = minRect(state.mContentInsets, state.mStableInsets);
-        insets = maxRect(insets, state.mAppToken.getLetterboxInsets());
+        final Rect insets = minRect(state.mContentInsets, state.mStableInsets);
+        InsetUtils.addInsets(insets, state.mAppToken.getLetterboxInsets());
         return insets;
     }
 
@@ -297,13 +297,6 @@
                 Math.min(rect1.bottom, rect2.bottom));
     }
 
-    private Rect maxRect(Rect rect1, Rect rect2) {
-        return new Rect(Math.max(rect1.left, rect2.left),
-                Math.max(rect1.top, rect2.top),
-                Math.max(rect1.right, rect2.right),
-                Math.max(rect1.bottom, rect2.bottom));
-    }
-
     /**
      * Retrieves all closing tasks based on the list of closing apps during an app transition.
      */
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 56b314f..449aa2c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -596,8 +596,6 @@
     boolean mClientFreezingScreen = false;
     int mAppsFreezingScreen = 0;
 
-    int mLayoutSeq = 0;
-
     // Last systemUiVisibility we received from status bar.
     int mLastStatusBarVisibility = 0;
     // Last systemUiVisibility we dispatched to windows.
@@ -6344,8 +6342,7 @@
         if (mInputMethodTarget != null) {
             pw.print("  mInputMethodTarget="); pw.println(mInputMethodTarget);
         }
-        pw.print("  mInTouchMode="); pw.print(mInTouchMode);
-                pw.print(" mLayoutSeq="); pw.println(mLayoutSeq);
+        pw.print("  mInTouchMode="); pw.println(mInTouchMode);
         pw.print("  mLastDisplayFreezeDuration=");
                 TimeUtils.formatDuration(mLastDisplayFreezeDuration, pw);
                 if ( mLastFinishedFreezeSource != null) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 993556f..30bbfe0 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1236,7 +1236,7 @@
      */
     void updateResizingWindowIfNeeded() {
         final WindowStateAnimator winAnimator = mWinAnimator;
-        if (!mHasSurface || mService.mLayoutSeq != mLayoutSeq || isGoneForLayoutLw()) {
+        if (!mHasSurface || getDisplayContent().mLayoutSeq != mLayoutSeq || isGoneForLayoutLw()) {
             return;
         }
 
@@ -1360,6 +1360,15 @@
         return mToken.getDisplayContent();
     }
 
+    @Override
+    void onDisplayChanged(DisplayContent dc) {
+        super.onDisplayChanged(dc);
+        // Window was not laid out for this display yet, so make sure mLayoutSeq does not match.
+        if (dc != null) {
+            mLayoutSeq = dc.mLayoutSeq - 1;
+        }
+    }
+
     DisplayInfo getDisplayInfo() {
         final DisplayContent displayContent = getDisplayContent();
         return displayContent != null ? displayContent.getDisplayInfo() : null;
diff --git a/services/core/java/com/android/server/wm/utils/InsetUtils.java b/services/core/java/com/android/server/wm/utils/InsetUtils.java
new file mode 100644
index 0000000..b4a998a
--- /dev/null
+++ b/services/core/java/com/android/server/wm/utils/InsetUtils.java
@@ -0,0 +1,38 @@
+/*
+ * 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.wm.utils;
+
+import android.graphics.Rect;
+
+/**
+ * Utility methods to handle insets represented as rects.
+ */
+public class InsetUtils {
+
+    private InsetUtils() {
+    }
+
+    /**
+     * Adds {@code insetsToAdd} to {@code inOutInsets}.
+     */
+    public static void addInsets(Rect inOutInsets, Rect insetsToAdd) {
+        inOutInsets.left += insetsToAdd.left;
+        inOutInsets.top += insetsToAdd.top;
+        inOutInsets.right += insetsToAdd.right;
+        inOutInsets.bottom += insetsToAdd.bottom;
+    }
+}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 427b9d2..89efe12 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -86,6 +86,7 @@
         "libhardware_legacy",
         "libhidlbase",
         "libkeystore_binder",
+        "libmtp",
         "libnativehelper",
         "libutils",
         "libui",
diff --git a/services/core/jni/com_android_server_UsbDeviceManager.cpp b/services/core/jni/com_android_server_UsbDeviceManager.cpp
index f37f870..ff1ec04 100644
--- a/services/core/jni/com_android_server_UsbDeviceManager.cpp
+++ b/services/core/jni/com_android_server_UsbDeviceManager.cpp
@@ -21,6 +21,7 @@
 #include <nativehelper/JNIHelp.h>
 #include "android_runtime/AndroidRuntime.h"
 #include "android_runtime/Log.h"
+#include "MtpDescriptors.h"
 
 #include <stdio.h>
 #include <asm/byteorder.h>
@@ -118,6 +119,38 @@
     return result;
 }
 
+static jobject android_server_UsbDeviceManager_openControl(JNIEnv *env, jobject /* thiz */, jstring jFunction) {
+    const char *function = env->GetStringUTFChars(jFunction, NULL);
+    bool ptp = false;
+    int fd = -1;
+    if (!strcmp(function, "ptp")) {
+        ptp = true;
+    }
+    if (!strcmp(function, "mtp") || ptp) {
+        fd = TEMP_FAILURE_RETRY(open(ptp ? FFS_PTP_EP0 : FFS_MTP_EP0, O_RDWR));
+        if (fd < 0) {
+            ALOGE("could not open control for %s %s", function, strerror(errno));
+            goto error;
+        }
+        if (!writeDescriptors(fd, ptp)) {
+            goto error;
+        }
+    }
+
+    if (function != NULL) {
+        env->ReleaseStringUTFChars(jFunction, function);
+    }
+    return jniCreateFileDescriptor(env, fd);
+error:
+    if (fd != -1) {
+        close(fd);
+    }
+    if (function != NULL) {
+        env->ReleaseStringUTFChars(jFunction, function);
+    }
+    return NULL;
+}
+
 static const JNINativeMethod method_table[] = {
     { "nativeGetAccessoryStrings",  "()[Ljava/lang/String;",
                                     (void*)android_server_UsbDeviceManager_getAccessoryStrings },
@@ -127,6 +160,8 @@
                                     (void*)android_server_UsbDeviceManager_isStartRequested },
     { "nativeGetAudioMode",         "()I",
                                     (void*)android_server_UsbDeviceManager_getAudioMode },
+    { "nativeOpenControl",          "(Ljava/lang/String;)Ljava/io/FileDescriptor;",
+                                    (void*)android_server_UsbDeviceManager_openControl },
 };
 
 int register_android_server_UsbDeviceManager(JNIEnv *env)
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 2e07703..1e216a3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -566,9 +566,7 @@
     }
 
     public static class DevicePolicyData {
-        @NonNull PasswordMetrics mActivePasswordMetrics = new PasswordMetrics();
         int mFailedPasswordAttempts = 0;
-        boolean mPasswordStateHasBeenSetSinceBoot = false;
         boolean mPasswordValidAtLastCheckpoint = true;
 
         int mUserHandle;
@@ -628,6 +626,8 @@
     }
 
     final SparseArray<DevicePolicyData> mUserData = new SparseArray<>();
+    @GuardedBy("DevicePolicyManagerService.this")
+    final SparseArray<PasswordMetrics> mUserPasswordMetrics = new SparseArray<>();
 
     final Handler mHandler;
     final Handler mBackgroundHandler;
@@ -2158,6 +2158,16 @@
     }
 
     /**
+     * Provides PasswordMetrics object corresponding to the given user.
+     * @param userHandle the user for whom to provide metrics.
+     * @return the user password metrics, or {@code null} if none have been associated with
+     * the user yet (for example, if the device has booted but not been unlocked).
+     */
+    PasswordMetrics getUserPasswordMetricsLocked(int userHandle) {
+        return mUserPasswordMetrics.get(userHandle);
+    }
+
+    /**
      * Creates and loads the policy data from xml for data that is shared between
      * various profiles of a user. In contrast to {@link #getUserData(int)}
      * it allows access to data of users other than the calling user.
@@ -2191,6 +2201,10 @@
             if (policy != null) {
                 mUserData.remove(userHandle);
             }
+            if (mUserPasswordMetrics.get(userHandle) != null) {
+                mUserPasswordMetrics.remove(userHandle);
+            }
+
             File policyFile = new File(mInjector.environmentGetUserSystemDirectory(userHandle),
                     DEVICE_POLICIES_XML);
             policyFile.delete();
@@ -3907,9 +3921,15 @@
     private void updatePasswordValidityCheckpointLocked(int userHandle, boolean parent) {
         final int credentialOwner = getCredentialOwner(userHandle, parent);
         DevicePolicyData policy = getUserData(credentialOwner);
+        PasswordMetrics metrics = getUserPasswordMetricsLocked(credentialOwner);
+        if (metrics == null) {
+            Slog.wtf(LOG_TAG, "Should have had a valid password metrics for updating checkpoint " +
+                    "validity.");
+            metrics = new PasswordMetrics();
+        }
         policy.mPasswordValidAtLastCheckpoint =
                 isPasswordSufficientForUserWithoutCheckpointLocked(
-                        policy.mActivePasswordMetrics, userHandle, parent);
+                        metrics, userHandle, parent);
 
         saveSettingsLocked(credentialOwner);
     }
@@ -4532,8 +4552,11 @@
             // This API can only be called by an active device admin,
             // so try to retrieve it to check that the caller is one.
             getActiveAdminForCallerLocked(null, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
-            DevicePolicyData policy = getUserDataUnchecked(getCredentialOwner(userHandle, parent));
-            return isActivePasswordSufficientForUserLocked(policy, userHandle, parent);
+            int credentialOwner = getCredentialOwner(userHandle, parent);
+            DevicePolicyData policy = getUserDataUnchecked(credentialOwner);
+            PasswordMetrics metrics = getUserPasswordMetricsLocked(credentialOwner);
+            return isActivePasswordSufficientForUserLocked(
+                    policy.mPasswordValidAtLastCheckpoint, metrics, userHandle, parent);
         }
     }
 
@@ -4559,25 +4582,31 @@
         synchronized (this) {
             final int targetUser = getProfileParentId(userHandle);
             enforceUserUnlocked(targetUser, false);
-            DevicePolicyData policy = getUserDataUnchecked(getCredentialOwner(userHandle, false));
-            return isActivePasswordSufficientForUserLocked(policy, targetUser, false);
+            int credentialOwner = getCredentialOwner(userHandle, false);
+            DevicePolicyData policy = getUserDataUnchecked(credentialOwner);
+            PasswordMetrics metrics = getUserPasswordMetricsLocked(credentialOwner);
+            return isActivePasswordSufficientForUserLocked(
+                    policy.mPasswordValidAtLastCheckpoint, metrics, targetUser, false);
         }
     }
 
     private boolean isActivePasswordSufficientForUserLocked(
-            DevicePolicyData policy, int userHandle, boolean parent) {
-        if (!mInjector.storageManagerIsFileBasedEncryptionEnabled()
-                && !policy.mPasswordStateHasBeenSetSinceBoot) {
+            boolean passwordValidAtLastCheckpoint, PasswordMetrics metrics, int userHandle,
+            boolean parent) {
+        if (!mInjector.storageManagerIsFileBasedEncryptionEnabled() && (metrics == null)) {
             // Before user enters their password for the first time after a reboot, return the
             // value of this flag, which tells us whether the password was valid the last time
             // settings were saved.  If DPC changes password requirements on boot so that the
             // current password no longer meets the requirements, this value will be stale until
             // the next time the password is entered.
-            return policy.mPasswordValidAtLastCheckpoint;
+            return passwordValidAtLastCheckpoint;
         }
 
-        return isPasswordSufficientForUserWithoutCheckpointLocked(
-                policy.mActivePasswordMetrics, userHandle, parent);
+        if (metrics == null) {
+            Slog.wtf(LOG_TAG, "FBE device, should have been unlocked and had valid metrics.");
+            metrics = new PasswordMetrics();
+        }
+        return isPasswordSufficientForUserWithoutCheckpointLocked(metrics, userHandle, parent);
     }
 
     /**
@@ -6123,10 +6152,8 @@
         }
 
         validateQualityConstant(metrics.quality);
-        DevicePolicyData policy = getUserData(userHandle);
         synchronized (this) {
-            policy.mActivePasswordMetrics = metrics;
-            policy.mPasswordStateHasBeenSetSinceBoot = true;
+            mUserPasswordMetrics.put(userHandle, metrics);
         }
     }
 
@@ -7640,6 +7667,7 @@
         if (!mHasFeature) {
             return DevicePolicyManager.STATE_USER_UNMANAGED;
         }
+        enforceManageUsers();
         int userHandle = mInjector.userHandleGetCallingUserId();
         return getUserProvisioningState(userHandle);
     }
@@ -7833,8 +7861,7 @@
             ApplicationInfo appInfo = userContext.getApplicationInfo();
             CharSequence result = null;
             if (appInfo != null) {
-                PackageManager pm = userContext.getPackageManager();
-                result = pm.getApplicationLabel(appInfo);
+                result = appInfo.loadUnsafeLabel(userContext.getPackageManager());
             }
             return result != null ? result.toString() : null;
         } finally {
@@ -11816,6 +11843,7 @@
 
     @Override
     public boolean isDeviceProvisioned() {
+        enforceManageUsers();
         synchronized (this) {
             return getUserDataUnchecked(UserHandle.USER_SYSTEM).mUserSetupComplete;
         }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 0268519..8366114 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -723,6 +723,11 @@
                 mSystemUpdatePolicy.saveToXml(out);
                 out.endTag(null, TAG_SYSTEM_UPDATE_POLICY);
             }
+
+            if (mSystemUpdateInfo != null) {
+                mSystemUpdateInfo.writeToXml(out, TAG_PENDING_OTA_INFO);
+            }
+
             if (mSystemUpdateFreezeStart != null || mSystemUpdateFreezeEnd != null) {
                 out.startTag(null, TAG_FREEZE_PERIOD_RECORD);
                 if (mSystemUpdateFreezeStart != null) {
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 43490d3..a64efb7 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -2387,12 +2387,12 @@
     }
 
     public void testGetUserProvisioningState_defaultResult() {
+        mContext.callerPermissions.add(permission.MANAGE_USERS);
         assertEquals(DevicePolicyManager.STATE_USER_UNMANAGED, dpm.getUserProvisioningState());
     }
 
     public void testSetUserProvisioningState_permission() throws Exception {
         setupProfileOwner();
-        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
 
         exerciseUserProvisioningTransitions(DpmMockContext.CALLER_USER_HANDLE,
                 DevicePolicyManager.STATE_USER_SETUP_FINALIZED);
@@ -2407,6 +2407,7 @@
 
     public void testSetUserProvisioningState_noManagement() {
         mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+        mContext.callerPermissions.add(permission.MANAGE_USERS);
         assertExpectException(IllegalStateException.class,
                 /* messageRegex= */ "change provisioning state unless a .* owner is set",
                 () -> dpm.setUserProvisioningState(DevicePolicyManager.STATE_USER_SETUP_FINALIZED,
@@ -2417,7 +2418,6 @@
     public void testSetUserProvisioningState_deviceOwnerFromSetupWizard() throws Exception {
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
         setupDeviceOwner();
-        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
 
         exerciseUserProvisioningTransitions(UserHandle.USER_SYSTEM,
                 DevicePolicyManager.STATE_USER_SETUP_COMPLETE,
@@ -2428,7 +2428,6 @@
             throws Exception {
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
         setupDeviceOwner();
-        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
 
         exerciseUserProvisioningTransitions(UserHandle.USER_SYSTEM,
                 DevicePolicyManager.STATE_USER_SETUP_INCOMPLETE,
@@ -2438,7 +2437,6 @@
     public void testSetUserProvisioningState_deviceOwnerWithoutSetupWizard() throws Exception {
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
         setupDeviceOwner();
-        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
 
         exerciseUserProvisioningTransitions(UserHandle.USER_SYSTEM,
                 DevicePolicyManager.STATE_USER_SETUP_FINALIZED);
@@ -2447,7 +2445,6 @@
     public void testSetUserProvisioningState_managedProfileFromSetupWizard_primaryUser()
             throws Exception {
         setupProfileOwner();
-        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
 
         exerciseUserProvisioningTransitions(DpmMockContext.CALLER_USER_HANDLE,
                 DevicePolicyManager.STATE_USER_PROFILE_COMPLETE,
@@ -2457,7 +2454,6 @@
     public void testSetUserProvisioningState_managedProfileFromSetupWizard_managedProfile()
             throws Exception {
         setupProfileOwner();
-        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
 
         exerciseUserProvisioningTransitions(DpmMockContext.CALLER_USER_HANDLE,
                 DevicePolicyManager.STATE_USER_SETUP_COMPLETE,
@@ -2466,7 +2462,6 @@
 
     public void testSetUserProvisioningState_managedProfileWithoutSetupWizard() throws Exception {
         setupProfileOwner();
-        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
 
         exerciseUserProvisioningTransitions(DpmMockContext.CALLER_USER_HANDLE,
                 DevicePolicyManager.STATE_USER_SETUP_FINALIZED);
@@ -2474,7 +2469,6 @@
 
     public void testSetUserProvisioningState_illegalTransitionOutOfFinalized1() throws Exception {
         setupProfileOwner();
-        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
 
         assertExpectException(IllegalStateException.class,
                 /* messageRegex= */ "Cannot move to user provisioning state",
@@ -2486,7 +2480,6 @@
     public void testSetUserProvisioningState_illegalTransitionToAnotherInProgressState()
             throws Exception {
         setupProfileOwner();
-        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
 
         assertExpectException(IllegalStateException.class,
                 /* messageRegex= */ "Cannot move to user provisioning state",
@@ -2496,6 +2489,9 @@
     }
 
     private void exerciseUserProvisioningTransitions(int userId, int... states) {
+        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+        mContext.callerPermissions.add(permission.MANAGE_USERS);
+
         assertEquals(DevicePolicyManager.STATE_USER_UNMANAGED, dpm.getUserProvisioningState());
         for (int state : states) {
             dpm.setUserProvisioningState(state, userId);
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
index fb25cf3..d922df3 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
@@ -85,6 +85,9 @@
         255
     };
 
+    private static final int[] MINIMUM_BRIGHTNESS_CURVE_LUX = { 2000, 4000 };
+    private static final float[] MINIMUM_BRIGHTNESS_CURVE_NITS = { 1.0f, 50.0f, 90.0f };
+
     private static final float[] DISPLAY_RANGE_NITS = { 2.685f, 478.5f };
     private static final int[] BACKLIGHT_RANGE = { 1, 255 };
 
@@ -381,6 +384,19 @@
                 com.android.internal.R.array.config_autoBrightnessDisplayValuesNits))
                 .thenReturn(mockBrightnessLevelNits);
 
+        int[] mockMinimumBrightnessCurveLux = new int[MINIMUM_BRIGHTNESS_CURVE_LUX.length];
+        for (int i = 0; i < mockMinimumBrightnessCurveLux.length; i++) {
+            mockMinimumBrightnessCurveLux[i] = (int) MINIMUM_BRIGHTNESS_CURVE_LUX[i];
+        }
+        when(mockResources.getIntArray(
+                com.android.internal.R.array.config_autoBrightnessMinimumBrightnessCurveLux))
+                .thenReturn(mockMinimumBrightnessCurveLux);
+        TypedArray mockMinimumBrightnessCurveNits = createFloatTypedArray(
+                MINIMUM_BRIGHTNESS_CURVE_NITS);
+        when(mockResources.obtainTypedArray(
+                com.android.internal.R.array.config_autoBrightnessMinimumBrightnessCurveNits))
+                .thenReturn(mockMinimumBrightnessCurveNits);
+
         TypedArray mockNitsRange = createFloatTypedArray(nitsRange);
         when(mockResources.obtainTypedArray(
                 com.android.internal.R.array.config_screenBrightnessNits))
@@ -419,4 +435,78 @@
         return mockArray;
     }
 
+    private float[] getNearMinimumNits(float epsilon) {
+        float[] lux = new float[MINIMUM_BRIGHTNESS_CURVE_LUX.length + 1];
+        for (int i = 0; i < MINIMUM_BRIGHTNESS_CURVE_LUX.length; i++) {
+            lux[i+1] = MINIMUM_BRIGHTNESS_CURVE_LUX[i];
+        }
+        Spline minimumBrightnessCurve = Spline.createSpline(lux, MINIMUM_BRIGHTNESS_CURVE_NITS);
+        float[] nits = new float[LUX_LEVELS.length];
+        for (int i = 0; i < nits.length; i++) {
+            nits[i] = minimumBrightnessCurve.interpolate(LUX_LEVELS[i]) + epsilon;
+        }
+        return nits;
+    }
+
+    @Test
+    public void testCreateWithTooDarkBrightnessConfigurationThrowsException() {
+        float[] nits = getNearMinimumNits(-0.1f);
+        Resources res = createResources(LUX_LEVELS, nits, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
+        Exception thrown = null;
+        try {
+            BrightnessMappingStrategy.create(res);
+        } catch (IllegalArgumentException e) {
+            thrown = e;
+        }
+        assertNotNull("Failed to throw IllegalArgumentException", thrown);
+    }
+
+    @Test
+    public void testCreationWithBrightEnoughBrightnessConfigurationDoesNotThrowException() {
+        float[] nits = getNearMinimumNits(0);
+        Resources res = createResources(LUX_LEVELS, nits, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
+        assertNotNull("Failed to create BrightnessMappingStrategy",
+                BrightnessMappingStrategy.create(res));
+    }
+
+    @Test
+    public void testSettingTooDarkBrightnessConfigurationThrowsException() {
+        Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS,
+                BACKLIGHT_RANGE);
+        BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res);
+        assertNotNull("Failed to create BrightnessMappingStrategy", strategy);
+        float[] lux = new float[LUX_LEVELS.length];
+        for (int i = 0; i < lux.length; i++) {
+            lux[i] = LUX_LEVELS[i];
+        }
+        float[] nits = getNearMinimumNits(-0.1f);
+        BrightnessConfiguration config = new BrightnessConfiguration.Builder()
+                .setCurve(lux, nits)
+                .build();
+        Exception thrown = null;
+        try {
+            strategy.setBrightnessConfiguration(config);
+        } catch (IllegalArgumentException e) {
+            thrown = e;
+        }
+        assertNotNull("Failed to throw IllegalArgumentException", thrown);
+    }
+
+    @Test
+    public void testSettingBrightEnouhgBrightnessConfigurationDoesNotThrowException() {
+        Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS,
+                BACKLIGHT_RANGE);
+        BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res);
+        assertNotNull("Failed to create BrightnessMappingStrategy", strategy);
+        float[] lux = new float[LUX_LEVELS.length];
+        for (int i = 0; i < lux.length; i++) {
+            lux[i] = LUX_LEVELS[i];
+        }
+        float[] nits = getNearMinimumNits(0);
+        BrightnessConfiguration config = new BrightnessConfiguration.Builder()
+                .setCurve(lux, nits)
+                .build();
+        assertTrue("failed to set brightness configuration",
+            strategy.setBrightnessConfiguration(config));
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index b7ce59d..8db2537 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -298,7 +298,7 @@
         mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
                 TestData.getCertXmlWithSerial(certSerial));
 
-        assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
+        assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
         assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid)).isEqualTo(
                 TestData.CERT_PATH_1);
         assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid)).isEqualTo(
@@ -348,6 +348,7 @@
 
         assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid))
                 .isEqualTo(certSerial + 1);
+        assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
     }
 
     @Test
@@ -363,6 +364,7 @@
 
         assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid))
                 .isEqualTo(certSerial);
+        assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
     }
 
     @Test
@@ -373,7 +375,6 @@
 
         mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
                 TestData.getCertXmlWithSerial(certSerial));
-        mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
         mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
                 TestData.getCertXmlWithSerial(certSerial));
 
@@ -404,7 +405,7 @@
         mRecoverableKeyStoreManager.initRecoveryServiceWithSigFile(
                 ROOT_CERTIFICATE_ALIAS, TestData.getCertXml(), TestData.getSigXml());
 
-        assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
+        assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
         assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid)).isEqualTo(
                 TestData.CERT_PATH_1);
         assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isNull();
@@ -879,7 +880,7 @@
     }
 
     @Test
-    public void setRecoverySecretTypes() throws Exception {
+    public void setRecoverySecretTypes_updatesSecretTypes() throws Exception {
         int[] types1 = new int[]{11, 2000};
         int[] types2 = new int[]{1, 2, 3};
         int[] types3 = new int[]{};
@@ -898,6 +899,41 @@
     }
 
     @Test
+    public void setRecoverySecretTypes_doesNotSetSnapshotPendingIfIniting() throws Exception {
+        int uid = Binder.getCallingUid();
+        int userId = UserHandle.getCallingUserId();
+        int[] secretTypes = new int[] { 101 };
+
+        mRecoverableKeyStoreManager.setRecoverySecretTypes(secretTypes);
+
+        assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
+    }
+
+    @Test
+    public void setRecoverySecretTypes_doesNotSetSnapshotPendingIfSettingSameValue()
+            throws Exception {
+        int uid = Binder.getCallingUid();
+        int userId = UserHandle.getCallingUserId();
+        int[] secretTypes = new int[] { 101 };
+
+        mRecoverableKeyStoreManager.setRecoverySecretTypes(secretTypes);
+        mRecoverableKeyStoreManager.setRecoverySecretTypes(secretTypes);
+
+        assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
+    }
+
+    @Test
+    public void setRecoverySecretTypes_setsSnapshotPendingIfUpdatingValue() throws Exception {
+        int uid = Binder.getCallingUid();
+        int userId = UserHandle.getCallingUserId();
+
+        mRecoverableKeyStoreManager.setRecoverySecretTypes(new int[] { 101 });
+        mRecoverableKeyStoreManager.setRecoverySecretTypes(new int[] { 102 });
+
+        assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
+    }
+
+    @Test
     public void setRecoverySecretTypes_throwsIfNullTypes() throws Exception {
         try {
             mRecoverableKeyStoreManager.setRecoverySecretTypes(/*types=*/ null);
@@ -911,12 +947,12 @@
     public void setRecoverySecretTypes_updatesShouldCreateSnapshot() throws Exception {
         int uid = Binder.getCallingUid();
         int userId = UserHandle.getCallingUserId();
-        int[] types = new int[]{1, 2, 3};
+        mRecoverableKeyStoreManager.setRecoverySecretTypes(new int[] { 1 });
 
         mRecoverableKeyStoreManager.generateAndStoreKey(TEST_ALIAS);
         // Pretend that key was synced
         mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
-        mRecoverableKeyStoreManager.setRecoverySecretTypes(types);
+        mRecoverableKeyStoreManager.setRecoverySecretTypes(new int[] { 2 });
 
         assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
     }
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorageTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorageTest.java
index ead817a..c772956 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorageTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorageTest.java
@@ -7,23 +7,19 @@
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
+import com.android.server.locksettings.recoverablekeystore.TestData;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.security.cert.CertificateException;
 import java.util.ArrayList;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class RecoverySnapshotStorageTest {
-    private static final KeyChainSnapshot MINIMAL_KEYCHAIN_SNAPSHOT = new KeyChainSnapshot.Builder()
-            .setCounterId(1)
-            .setSnapshotVersion(1)
-            .setServerParams(new byte[0])
-            .setMaxAttempts(10)
-            .setEncryptedRecoveryKeyBlob(new byte[0])
-            .setKeyChainProtectionParams(new ArrayList<>())
-            .setWrappedApplicationKeys(new ArrayList<>())
-            .build();
+    private static final KeyChainSnapshot MINIMAL_KEYCHAIN_SNAPSHOT =
+            createMinimalKeyChainSnapshot();
 
     private final RecoverySnapshotStorage mRecoverySnapshotStorage = new RecoverySnapshotStorage();
 
@@ -50,4 +46,21 @@
 
         assertNull(mRecoverySnapshotStorage.get(1000));
     }
+
+    private static KeyChainSnapshot createMinimalKeyChainSnapshot() {
+        try {
+            return new KeyChainSnapshot.Builder()
+                    .setCounterId(1)
+                    .setSnapshotVersion(1)
+                    .setServerParams(new byte[0])
+                    .setMaxAttempts(10)
+                    .setEncryptedRecoveryKeyBlob(new byte[0])
+                    .setKeyChainProtectionParams(new ArrayList<>())
+                    .setWrappedApplicationKeys(new ArrayList<>())
+                    .setTrustedHardwareCertPath(TestData.CERT_PATH_1)
+                    .build();
+        } catch (CertificateException e) {
+            throw new RuntimeException(e);
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
index b645700..7f1bcac 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
@@ -30,8 +30,10 @@
 import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
+import static org.hamcrest.Matchers.is;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -436,6 +438,20 @@
     }
 
     @Test
+    public void testLayoutSeq_assignedDuringLayout() throws Exception {
+        synchronized (sWm.getWindowManagerLock()) {
+
+            final DisplayContent dc = createNewDisplay();
+            final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
+
+            dc.setLayoutNeeded();
+            dc.performLayout(true /* initial */, false /* updateImeWindows */);
+
+            assertThat(win.mLayoutSeq, is(dc.mLayoutSeq));
+        }
+    }
+
+    @Test
     @SuppressLint("InlinedApi")
     public void testOrientationDefinedByKeyguard() {
         final DisplayContent dc = createNewDisplay();
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
index 56b7d9f..1248eae 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
@@ -40,9 +40,12 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
@@ -312,6 +315,17 @@
         assertTrue(child2.isSelfOrAncestorWindowAnimatingExit());
     }
 
+    @Test
+    public void testLayoutSeqResetOnReparent() throws Exception {
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+        app.mLayoutSeq = 1;
+        mDisplayContent.mLayoutSeq = 1;
+
+        app.onDisplayChanged(mDisplayContent);
+
+        assertThat(app.mLayoutSeq, not(is(mDisplayContent.mLayoutSeq)));
+    }
+
     private void testPrepareWindowToDisplayDuringRelayout(boolean wasVisible) {
         reset(mPowerManagerWrapper);
         final WindowState root = createWindow(null, TYPE_APPLICATION, "root");
diff --git a/services/tests/servicestests/src/com/android/server/wm/utils/InsetUtilsTest.java b/services/tests/servicestests/src/com/android/server/wm/utils/InsetUtilsTest.java
new file mode 100644
index 0000000..d0f0fe3
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/utils/InsetUtilsTest.java
@@ -0,0 +1,43 @@
+/*
+ * 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.wm.utils;
+
+import static junit.framework.Assert.assertEquals;
+
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Pair;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class InsetUtilsTest {
+
+    @Test
+    public void testAdd() throws Exception {
+        final Rect rect1 = new Rect(10, 20, 30, 40);
+        final Rect rect2 = new Rect(50, 60, 70, 80);
+        InsetUtils.addInsets(rect1, rect2);
+        assertEquals(new Rect(60, 80, 100, 120), rect1);
+    }
+}
+
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 381e04c..3acc277 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -329,12 +329,12 @@
         mZenModeHelperSpy.mConfig.allowEvents = false;
         mZenModeHelperSpy.mConfig.allowRepeatCallers= false;
 
-        // 2. apply priority only zen - verify ringer is set to silent
+        // 2. apply priority only zen - verify ringer is unchanged
         mZenModeHelperSpy.applyZenToRingerMode();
-        verify(mAudioManager, atLeastOnce()).setRingerModeInternal(AudioManager.RINGER_MODE_SILENT,
+        verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_SILENT,
                 mZenModeHelperSpy.TAG);
 
-        // 3. apply zen off - verify zen is set to prevoius ringer (normal)
+        // 3. apply zen off - verify zen is set to previous ringer (normal)
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
         mZenModeHelperSpy.mZenMode = Global.ZEN_MODE_OFF;
         mZenModeHelperSpy.applyZenToRingerMode();
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index ef0780a..9f8042c 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -83,6 +83,7 @@
 import com.android.server.LocalServices;
 
 import java.io.File;
+import java.io.FileDescriptor;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.util.HashMap;
@@ -181,6 +182,7 @@
     private final UEventObserver mUEventObserver;
 
     private static Set<Integer> sBlackListedInterfaces;
+    private HashMap<Long, FileDescriptor> mControlFds;
 
     static {
         sBlackListedInterfaces = new HashSet<>();
@@ -270,6 +272,18 @@
             Slog.i(TAG, "USB GADGET HAL not present in the device", e);
         }
 
+        mControlFds = new HashMap<>();
+        FileDescriptor mtpFd = nativeOpenControl(UsbManager.USB_FUNCTION_MTP);
+        if (mtpFd == null) {
+            Slog.e(TAG, "Failed to open control for mtp");
+        }
+        mControlFds.put(UsbManager.FUNCTION_MTP, mtpFd);
+        FileDescriptor ptpFd = nativeOpenControl(UsbManager.USB_FUNCTION_PTP);
+        if (mtpFd == null) {
+            Slog.e(TAG, "Failed to open control for mtp");
+        }
+        mControlFds.put(UsbManager.FUNCTION_PTP, ptpFd);
+
         boolean secureAdbEnabled = SystemProperties.getBoolean("ro.adb.secure", false);
         boolean dataEncrypted = "1".equals(SystemProperties.get("vold.decrypt"));
         if (secureAdbEnabled && !dataEncrypted) {
@@ -704,8 +718,7 @@
             return false;
         }
 
-        protected void updateUsbStateBroadcastIfNeeded(long functions,
-                boolean configChanged) {
+        protected void updateUsbStateBroadcastIfNeeded(long functions) {
             // send a sticky broadcast containing current USB state
             Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
             intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
@@ -716,7 +729,6 @@
             intent.putExtra(UsbManager.USB_CONFIGURED, mConfigured);
             intent.putExtra(UsbManager.USB_DATA_UNLOCKED,
                     isUsbTransferAllowed() && isUsbDataTransferActive(mCurrentFunctions));
-            intent.putExtra(UsbManager.USB_CONFIG_CHANGED, configChanged);
 
             long remainingFunctions = functions;
             while (remainingFunctions != 0) {
@@ -726,7 +738,7 @@
             }
 
             // send broadcast intent only if the USB state has changed
-            if (!isUsbStateChanged(intent) && !configChanged) {
+            if (!isUsbStateChanged(intent)) {
                 if (DEBUG) {
                     Slog.d(TAG, "skip broadcasting " + intent + " extras: " + intent.getExtras());
                 }
@@ -798,8 +810,7 @@
                     updateUsbNotification(false);
                     updateAdbNotification(false);
                     if (mBootCompleted) {
-                        updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions),
-                                false);
+                        updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions));
                     }
                     if ((mCurrentFunctions & UsbManager.FUNCTION_ACCESSORY) != 0) {
                         updateCurrentAccessory();
@@ -812,7 +823,7 @@
                                     && mScreenUnlockedFunctions != UsbManager.FUNCTION_NONE) {
                                 setScreenUnlockedFunctions();
                             } else {
-                                setEnabledFunctions(UsbManager.FUNCTION_NONE, !mAdbEnabled);
+                                setEnabledFunctions(UsbManager.FUNCTION_NONE, false);
                             }
                         }
                         updateUsbFunctions();
@@ -847,8 +858,7 @@
                     updateUsbNotification(false);
                     if (mBootCompleted) {
                         if (mHostConnected || prevHostConnected) {
-                            updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions),
-                                    false);
+                            updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions));
                         }
                     } else {
                         mPendingBootBroadcast = true;
@@ -994,7 +1004,7 @@
         protected void finishBoot() {
             if (mBootCompleted && mCurrentUsbFunctionsReceived && mSystemReady) {
                 if (mPendingBootBroadcast) {
-                    updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions), false);
+                    updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions));
                     mPendingBootBroadcast = false;
                 }
                 if (!mScreenLocked
@@ -1597,7 +1607,7 @@
                     /**
                      * Start up dependent services.
                      */
-                    updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions), true);
+                    updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions));
                 }
 
                 if (!waitForState(oemFunctions)) {
@@ -1943,7 +1953,7 @@
 
                 if (mBootCompleted && isUsbDataTransferActive(functions)) {
                     // Start up dependent services.
-                    updateUsbStateBroadcastIfNeeded(functions, true);
+                    updateUsbStateBroadcastIfNeeded(functions);
                 }
             }
         }
@@ -1979,6 +1989,22 @@
         return mHandler.getEnabledFunctions();
     }
 
+    /**
+     * Returns a dup of the control file descriptor for the given function.
+     */
+    public ParcelFileDescriptor getControlFd(long usbFunction) {
+        FileDescriptor fd = mControlFds.get(usbFunction);
+        if (fd == null) {
+            return null;
+        }
+        try {
+            return ParcelFileDescriptor.dup(fd);
+        } catch (IOException e) {
+            Slog.e(TAG, "Could not dup fd for " + usbFunction);
+            return null;
+        }
+    }
+
     public long getScreenUnlockedFunctions() {
         return mHandler.getScreenUnlockedFunctions();
     }
@@ -2063,6 +2089,8 @@
 
     private native ParcelFileDescriptor nativeOpenAccessory();
 
+    private native FileDescriptor nativeOpenControl(String usbFunction);
+
     private native boolean nativeIsStartRequested();
 
     private native int nativeGetAudioMode();
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index c1a7591..e92bd74 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -304,6 +304,13 @@
         return null;
     }
 
+    /* Returns a dup of the control file descriptor for the given function. */
+    @Override
+    public ParcelFileDescriptor getControlFd(long function) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_MTP, null);
+        return mDeviceManager.getControlFd(function);
+    }
+
     @Override
     public void setDevicePackage(UsbDevice device, String packageName, int userId) {
         device = Preconditions.checkNotNull(device);
diff --git a/telephony/java/android/telephony/CellIdentity.java b/telephony/java/android/telephony/CellIdentity.java
index 08f8bb6..890a6ea 100644
--- a/telephony/java/android/telephony/CellIdentity.java
+++ b/telephony/java/android/telephony/CellIdentity.java
@@ -18,11 +18,14 @@
 
 import android.annotation.CallSuper;
 import android.annotation.IntDef;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.text.TextUtils;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
 
 /**
  * CellIdentity represents the identity of a unique cell. This is the base class for
@@ -84,8 +87,16 @@
     /** @hide */
     protected final String mMncStr;
 
+    // long alpha Operator Name String or Enhanced Operator Name String
     /** @hide */
-    protected CellIdentity(String tag, int type, String mcc, String mnc) {
+    protected final String mAlphaLong;
+    // short alpha Operator Name String or Enhanced Operator Name String
+    /** @hide */
+    protected final String mAlphaShort;
+
+    /** @hide */
+    protected CellIdentity(String tag, int type, String mcc, String mnc, String alphal,
+                           String alphas) {
         mTag = tag;
         mType = type;
 
@@ -113,6 +124,8 @@
             mMncStr = null;
             log("invalid MNC format: " + mnc);
         }
+        mAlphaLong = alphal;
+        mAlphaShort = alphas;
     }
 
     /** Implement the Parcelable interface */
@@ -138,6 +151,40 @@
     }
 
     /**
+     * @return The long alpha tag associated with the current scan result (may be the operator
+     * name string or extended operator name string). May be null if unknown.
+     */
+    @Nullable
+    public CharSequence getOperatorAlphaLong() {
+        return mAlphaLong;
+    }
+
+    /**
+     * @return The short alpha tag associated with the current scan result (may be the operator
+     * name string or extended operator name string).  May be null if unknown.
+     */
+    @Nullable
+    public CharSequence getOperatorAlphaShort() {
+        return mAlphaShort;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof CellIdentity)) {
+            return false;
+        }
+
+        CellIdentity o = (CellIdentity) other;
+        return TextUtils.equals(mAlphaLong, o.mAlphaLong)
+                && TextUtils.equals(mAlphaShort, o.mAlphaShort);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mAlphaLong, mAlphaShort, mMccStr, mMncStr, mType);
+    }
+
+    /**
      * Used by child classes for parceling.
      *
      * @hide
@@ -147,6 +194,8 @@
         dest.writeInt(type);
         dest.writeString(mMccStr);
         dest.writeString(mMncStr);
+        dest.writeString(mAlphaLong);
+        dest.writeString(mAlphaShort);
     }
 
     /**
@@ -154,7 +203,8 @@
      * @hide
      */
     protected CellIdentity(String tag, int type, Parcel source) {
-        this(tag, type, source.readString(), source.readString());
+        this(tag, type, source.readString(), source.readString(),
+                source.readString(), source.readString());
     }
 
     /** Implement the Parcelable interface */
diff --git a/telephony/java/android/telephony/CellIdentityCdma.java b/telephony/java/android/telephony/CellIdentityCdma.java
index 713ac00..58a2c45 100644
--- a/telephony/java/android/telephony/CellIdentityCdma.java
+++ b/telephony/java/android/telephony/CellIdentityCdma.java
@@ -49,23 +49,17 @@
      * to +90 degrees).
      */
     private final int mLatitude;
-    // long alpha Operator Name String or Enhanced Operator Name String
-    private final String mAlphaLong;
-    // short alpha Operator Name String or Enhanced Operator Name String
-    private final String mAlphaShort;
 
     /**
      * @hide
      */
     public CellIdentityCdma() {
-        super(TAG, TYPE_CDMA, null, null);
+        super(TAG, TYPE_CDMA, null, null, null, null);
         mNetworkId = Integer.MAX_VALUE;
         mSystemId = Integer.MAX_VALUE;
         mBasestationId = Integer.MAX_VALUE;
         mLongitude = Integer.MAX_VALUE;
         mLatitude = Integer.MAX_VALUE;
-        mAlphaLong = null;
-        mAlphaShort = null;
     }
 
     /**
@@ -100,7 +94,7 @@
      */
     public CellIdentityCdma(int nid, int sid, int bid, int lon, int lat, String alphal,
                              String alphas) {
-        super(TAG, TYPE_CDMA, null, null);
+        super(TAG, TYPE_CDMA, null, null, alphal, alphas);
         mNetworkId = nid;
         mSystemId = sid;
         mBasestationId = bid;
@@ -110,8 +104,6 @@
         } else {
             mLongitude = mLatitude = Integer.MAX_VALUE;
         }
-        mAlphaLong = alphal;
-        mAlphaShort = alphas;
     }
 
     private CellIdentityCdma(CellIdentityCdma cid) {
@@ -178,28 +170,10 @@
         return mLatitude;
     }
 
-    /**
-     * @return The long alpha tag associated with the current scan result (may be the operator
-     * name string or extended operator name string). May be null if unknown.
-     */
-    @Nullable
-    public CharSequence getOperatorAlphaLong() {
-        return mAlphaLong;
-    }
-
-    /**
-     * @return The short alpha tag associated with the current scan result (may be the operator
-     * name string or extended operator name string).  May be null if unknown.
-     */
-    @Nullable
-    public CharSequence getOperatorAlphaShort() {
-        return mAlphaShort;
-    }
-
     @Override
     public int hashCode() {
         return Objects.hash(mNetworkId, mSystemId, mBasestationId, mLatitude, mLongitude,
-                mAlphaLong, mAlphaShort);
+                super.hashCode());
     }
 
     @Override
@@ -219,8 +193,7 @@
                 && mBasestationId == o.mBasestationId
                 && mLatitude == o.mLatitude
                 && mLongitude == o.mLongitude
-                && TextUtils.equals(mAlphaLong, o.mAlphaLong)
-                && TextUtils.equals(mAlphaShort, o.mAlphaShort);
+                && super.equals(other);
     }
 
     @Override
@@ -246,8 +219,6 @@
         dest.writeInt(mBasestationId);
         dest.writeInt(mLongitude);
         dest.writeInt(mLatitude);
-        dest.writeString(mAlphaLong);
-        dest.writeString(mAlphaShort);
     }
 
     /** Construct from Parcel, type has already been processed */
@@ -258,8 +229,6 @@
         mBasestationId = in.readInt();
         mLongitude = in.readInt();
         mLatitude = in.readInt();
-        mAlphaLong = in.readString();
-        mAlphaShort = in.readString();
 
         if (DBG) log(toString());
     }
diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java
index aae7929..c697880 100644
--- a/telephony/java/android/telephony/CellIdentityGsm.java
+++ b/telephony/java/android/telephony/CellIdentityGsm.java
@@ -37,22 +37,16 @@
     private final int mArfcn;
     // 6-bit Base Station Identity Code
     private final int mBsic;
-    // long alpha Operator Name String or Enhanced Operator Name String
-    private final String mAlphaLong;
-    // short alpha Operator Name String or Enhanced Operator Name String
-    private final String mAlphaShort;
 
     /**
      * @hide
      */
     public CellIdentityGsm() {
-        super(TAG, TYPE_GSM, null, null);
+        super(TAG, TYPE_GSM, null, null, null, null);
         mLac = Integer.MAX_VALUE;
         mCid = Integer.MAX_VALUE;
         mArfcn = Integer.MAX_VALUE;
         mBsic = Integer.MAX_VALUE;
-        mAlphaLong = null;
-        mAlphaShort = null;
     }
     /**
      * public constructor
@@ -98,16 +92,13 @@
      */
     public CellIdentityGsm(int lac, int cid, int arfcn, int bsic, String mccStr,
                             String mncStr, String alphal, String alphas) {
-        super(TAG, TYPE_GSM, mccStr, mncStr);
+        super(TAG, TYPE_GSM, mccStr, mncStr, alphal, alphas);
         mLac = lac;
         mCid = cid;
         mArfcn = arfcn;
         // In RIL BSIC is a UINT8, so 0xFF is the 'INVALID' designator
         // for inbound parcels
         mBsic = (bsic == 0xFF) ? Integer.MAX_VALUE : bsic;
-
-        mAlphaLong = alphal;
-        mAlphaShort = alphas;
     }
 
     private CellIdentityGsm(CellIdentityGsm cid) {
@@ -188,24 +179,6 @@
         return mMncStr;
     }
 
-    /**
-     * @return The long alpha tag associated with the current scan result (may be the operator
-     * name string or extended operator name string). May be null if unknown.
-     */
-    @Nullable
-    public CharSequence getOperatorAlphaLong() {
-        return mAlphaLong;
-    }
-
-    /**
-     * @return The short alpha tag associated with the current scan result (may be the operator
-     * name string or extended operator name string).  May be null if unknown.
-     */
-    @Nullable
-    public CharSequence getOperatorAlphaShort() {
-        return mAlphaShort;
-    }
-
     /** @hide */
     @Override
     public int getChannelNumber() {
@@ -223,7 +196,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mMccStr, mMncStr, mLac, mCid, mAlphaLong, mAlphaShort);
+        return Objects.hash(mLac, mCid, super.hashCode());
     }
 
     @Override
@@ -243,8 +216,7 @@
                 && mBsic == o.mBsic
                 && TextUtils.equals(mMccStr, o.mMccStr)
                 && TextUtils.equals(mMncStr, o.mMncStr)
-                && TextUtils.equals(mAlphaLong, o.mAlphaLong)
-                && TextUtils.equals(mAlphaShort, o.mAlphaShort);
+                && super.equals(other);
     }
 
     @Override
@@ -270,8 +242,6 @@
         dest.writeInt(mCid);
         dest.writeInt(mArfcn);
         dest.writeInt(mBsic);
-        dest.writeString(mAlphaLong);
-        dest.writeString(mAlphaShort);
     }
 
     /** Construct from Parcel, type has already been processed */
@@ -281,8 +251,6 @@
         mCid = in.readInt();
         mArfcn = in.readInt();
         mBsic = in.readInt();
-        mAlphaLong = in.readString();
-        mAlphaShort = in.readString();
 
         if (DBG) log(toString());
     }
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index 9b3ef56..177fced 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -37,10 +37,6 @@
     private final int mTac;
     // 18-bit Absolute RF Channel Number
     private final int mEarfcn;
-    // long alpha Operator Name String or Enhanced Operator Name String
-    private final String mAlphaLong;
-    // short alpha Operator Name String or Enhanced Operator Name String
-    private final String mAlphaShort;
     // cell bandwidth, in kHz
     private final int mBandwidth;
 
@@ -48,14 +44,12 @@
      * @hide
      */
     public CellIdentityLte() {
-        super(TAG, TYPE_LTE, null, null);
+        super(TAG, TYPE_LTE, null, null, null, null);
         mCi = Integer.MAX_VALUE;
         mPci = Integer.MAX_VALUE;
         mTac = Integer.MAX_VALUE;
         mEarfcn = Integer.MAX_VALUE;
         mBandwidth = Integer.MAX_VALUE;
-        mAlphaLong = null;
-        mAlphaShort = null;
     }
 
     /**
@@ -105,14 +99,12 @@
      */
     public CellIdentityLte(int ci, int pci, int tac, int earfcn, int bandwidth, String mccStr,
             String mncStr, String alphal, String alphas) {
-        super(TAG, TYPE_LTE, mccStr, mncStr);
+        super(TAG, TYPE_LTE, mccStr, mncStr, alphal, alphas);
         mCi = ci;
         mPci = pci;
         mTac = tac;
         mEarfcn = earfcn;
         mBandwidth = bandwidth;
-        mAlphaLong = alphal;
-        mAlphaShort = alphas;
     }
 
     private CellIdentityLte(CellIdentityLte cid) {
@@ -198,24 +190,6 @@
         return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
     }
 
-    /**
-     * @return The long alpha tag associated with the current scan result (may be the operator
-     * name string or extended operator name string). May be null if unknown.
-     */
-    @Nullable
-    public CharSequence getOperatorAlphaLong() {
-        return mAlphaLong;
-    }
-
-    /**
-     * @return The short alpha tag associated with the current scan result (may be the operator
-     * name string or extended operator name string).  May be null if unknown.
-     */
-    @Nullable
-    public CharSequence getOperatorAlphaShort() {
-        return mAlphaShort;
-    }
-
     /** @hide */
     @Override
     public int getChannelNumber() {
@@ -224,7 +198,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mMccStr, mMncStr, mCi, mPci, mTac, mAlphaLong, mAlphaShort);
+        return Objects.hash(mCi, mPci, mTac, super.hashCode());
     }
 
     @Override
@@ -245,8 +219,7 @@
                 && mBandwidth == o.mBandwidth
                 && TextUtils.equals(mMccStr, o.mMccStr)
                 && TextUtils.equals(mMncStr, o.mMncStr)
-                && TextUtils.equals(mAlphaLong, o.mAlphaLong)
-                && TextUtils.equals(mAlphaShort, o.mAlphaShort);
+                && super.equals(other);
     }
 
     @Override
@@ -274,8 +247,6 @@
         dest.writeInt(mTac);
         dest.writeInt(mEarfcn);
         dest.writeInt(mBandwidth);
-        dest.writeString(mAlphaLong);
-        dest.writeString(mAlphaShort);
     }
 
     /** Construct from Parcel, type has already been processed */
@@ -286,8 +257,6 @@
         mTac = in.readInt();
         mEarfcn = in.readInt();
         mBandwidth = in.readInt();
-        mAlphaLong = in.readString();
-        mAlphaShort = in.readString();
 
         if (DBG) log(toString());
     }
diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java
index 7475c74..18ab6d4 100644
--- a/telephony/java/android/telephony/CellIdentityTdscdma.java
+++ b/telephony/java/android/telephony/CellIdentityTdscdma.java
@@ -35,21 +35,15 @@
     private final int mCid;
     // 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown.
     private final int mCpid;
-    // long alpha Operator Name String or Enhanced Operator Name String
-    private final String mAlphaLong;
-    // short alpha Operator Name String or Enhanced Operator Name String
-    private final String mAlphaShort;
 
     /**
      * @hide
      */
     public CellIdentityTdscdma() {
-        super(TAG, TYPE_TDSCDMA, null, null);
+        super(TAG, TYPE_TDSCDMA, null, null, null, null);
         mLac = Integer.MAX_VALUE;
         mCid = Integer.MAX_VALUE;
         mCpid = Integer.MAX_VALUE;
-        mAlphaLong = null;
-        mAlphaShort = null;
     }
 
     /**
@@ -76,12 +70,10 @@
      * @hide
      */
     public CellIdentityTdscdma(String mcc, String mnc, int lac, int cid, int cpid) {
-        super(TAG, TYPE_TDSCDMA, mcc, mnc);
+        super(TAG, TYPE_TDSCDMA, mcc, mnc, null, null);
         mLac = lac;
         mCid = cid;
         mCpid = cpid;
-        mAlphaLong = null;
-        mAlphaShort = null;
     }
 
     /**
@@ -97,12 +89,10 @@
      */
     public CellIdentityTdscdma(String mcc, String mnc, int lac, int cid, int cpid,
             String alphal, String alphas) {
-        super(TAG, TYPE_TDSCDMA, mcc, mnc);
+        super(TAG, TYPE_TDSCDMA, mcc, mnc, alphal, alphas);
         mLac = lac;
         mCid = cid;
         mCpid = cpid;
-        mAlphaLong = alphal;
-        mAlphaShort = alphas;
     }
 
     private CellIdentityTdscdma(CellIdentityTdscdma cid) {
@@ -151,31 +141,9 @@
         return mCpid;
     }
 
-    /**
-     * @return The long alpha tag associated with the current scan result (may be the operator
-     * name string or extended operator name string). May be null if unknown.
-     *
-     * @hide
-     */
-    @Nullable
-    public CharSequence getOperatorAlphaLong() {
-        return mAlphaLong;
-    }
-
-    /**
-     * @return The short alpha tag associated with the current scan result (may be the operator
-     * name string or extended operator name string).  May be null if unknown.
-     *
-     * @hide
-     */
-    @Nullable
-    public CharSequence getOperatorAlphaShort() {
-        return mAlphaShort;
-    }
-
     @Override
     public int hashCode() {
-        return Objects.hash(mMccStr, mMncStr, mLac, mCid, mCpid, mAlphaLong, mAlphaShort);
+        return Objects.hash(mLac, mCid, mCpid, super.hashCode());
     }
 
     @Override
@@ -194,8 +162,7 @@
                 && mLac == o.mLac
                 && mCid == o.mCid
                 && mCpid == o.mCpid
-                && mAlphaLong == o.mAlphaLong
-                && mAlphaShort == o.mAlphaShort;
+                && super.equals(other);
     }
 
     @Override
@@ -219,8 +186,6 @@
         dest.writeInt(mLac);
         dest.writeInt(mCid);
         dest.writeInt(mCpid);
-        dest.writeString(mAlphaLong);
-        dest.writeString(mAlphaShort);
     }
 
     /** Construct from Parcel, type has already been processed */
@@ -229,8 +194,6 @@
         mLac = in.readInt();
         mCid = in.readInt();
         mCpid = in.readInt();
-        mAlphaLong = in.readString();
-        mAlphaShort = in.readString();
 
         if (DBG) log(toString());
     }
diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java
index 52fa54f..984483e 100644
--- a/telephony/java/android/telephony/CellIdentityWcdma.java
+++ b/telephony/java/android/telephony/CellIdentityWcdma.java
@@ -37,22 +37,16 @@
     private final int mPsc;
     // 16-bit UMTS Absolute RF Channel Number
     private final int mUarfcn;
-    // long alpha Operator Name String or Enhanced Operator Name String
-    private final String mAlphaLong;
-    // short alpha Operator Name String or Enhanced Operator Name String
-    private final String mAlphaShort;
 
     /**
      * @hide
      */
     public CellIdentityWcdma() {
-        super(TAG, TYPE_TDSCDMA, null, null);
+        super(TAG, TYPE_TDSCDMA, null, null, null, null);
         mLac = Integer.MAX_VALUE;
         mCid = Integer.MAX_VALUE;
         mPsc = Integer.MAX_VALUE;
         mUarfcn = Integer.MAX_VALUE;
-        mAlphaLong = null;
-        mAlphaShort = null;
     }
     /**
      * public constructor
@@ -99,13 +93,11 @@
      */
     public CellIdentityWcdma (int lac, int cid, int psc, int uarfcn,
                               String mccStr, String mncStr, String alphal, String alphas) {
-        super(TAG, TYPE_WCDMA, mccStr, mncStr);
+        super(TAG, TYPE_WCDMA, mccStr, mncStr, alphal, alphas);
         mLac = lac;
         mCid = cid;
         mPsc = psc;
         mUarfcn = uarfcn;
-        mAlphaLong = alphal;
-        mAlphaShort = alphas;
     }
 
     private CellIdentityWcdma(CellIdentityWcdma cid) {
@@ -179,27 +171,9 @@
         return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
     }
 
-    /**
-     * @return The long alpha tag associated with the current scan result (may be the operator
-     * name string or extended operator name string). May be null if unknown.
-     */
-    @Nullable
-    public CharSequence getOperatorAlphaLong() {
-        return mAlphaLong;
-    }
-
-    /**
-     * @return The short alpha tag associated with the current scan result (may be the operator
-     * name string or extended operator name string).  May be null if unknown.
-     */
-    @Nullable
-    public CharSequence getOperatorAlphaShort() {
-        return mAlphaShort;
-    }
-
     @Override
     public int hashCode() {
-        return Objects.hash(mMccStr, mMncStr, mLac, mCid, mPsc, mAlphaLong, mAlphaShort);
+        return Objects.hash(mLac, mCid, mPsc, super.hashCode());
     }
 
     /**
@@ -232,8 +206,7 @@
                 && mUarfcn == o.mUarfcn
                 && TextUtils.equals(mMccStr, o.mMccStr)
                 && TextUtils.equals(mMncStr, o.mMncStr)
-                && TextUtils.equals(mAlphaLong, o.mAlphaLong)
-                && TextUtils.equals(mAlphaShort, o.mAlphaShort);
+                && super.equals(other);
     }
 
     @Override
@@ -259,8 +232,6 @@
         dest.writeInt(mCid);
         dest.writeInt(mPsc);
         dest.writeInt(mUarfcn);
-        dest.writeString(mAlphaLong);
-        dest.writeString(mAlphaShort);
     }
 
     /** Construct from Parcel, type has already been processed */
@@ -270,8 +241,6 @@
         mCid = in.readInt();
         mPsc = in.readInt();
         mUarfcn = in.readInt();
-        mAlphaLong = in.readString();
-        mAlphaShort = in.readString();
         if (DBG) log(toString());
     }