Merge "Remove useless open modes"
diff --git a/api/current.txt b/api/current.txt
index 97facec..80c3ac2 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10887,6 +10887,7 @@
     field public static final int PROTECTION_FLAG_APPOP = 64; // 0x40
     field public static final int PROTECTION_FLAG_DEVELOPMENT = 32; // 0x20
     field public static final int PROTECTION_FLAG_INSTALLER = 256; // 0x100
+    field public static final int PROTECTION_FLAG_INSTANT = 4096; // 0x1000
     field public static final int PROTECTION_FLAG_PRE23 = 128; // 0x80
     field public static final int PROTECTION_FLAG_PREINSTALLED = 1024; // 0x400
     field public static final int PROTECTION_FLAG_PRIVILEGED = 16; // 0x10
diff --git a/api/system-current.txt b/api/system-current.txt
index 2212fd0..0398a02 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -10903,7 +10903,6 @@
     method public int getVersionCode();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.content.pm.InstantAppResolveInfo> CREATOR;
-    field public static final java.lang.String SHA_ALGORITHM = "SHA-256";
   }
 
   public static final class InstantAppResolveInfo.InstantAppDigest implements android.os.Parcelable {
@@ -11604,8 +11603,8 @@
     field public static final int PROTECTION_DANGEROUS = 1; // 0x1
     field public static final int PROTECTION_FLAG_APPOP = 64; // 0x40
     field public static final int PROTECTION_FLAG_DEVELOPMENT = 32; // 0x20
-    field public static final int PROTECTION_FLAG_EPHEMERAL = 4096; // 0x1000
     field public static final int PROTECTION_FLAG_INSTALLER = 256; // 0x100
+    field public static final int PROTECTION_FLAG_INSTANT = 4096; // 0x1000
     field public static final int PROTECTION_FLAG_PRE23 = 128; // 0x80
     field public static final int PROTECTION_FLAG_PREINSTALLED = 1024; // 0x400
     field public static final int PROTECTION_FLAG_PRIVILEGED = 16; // 0x10
diff --git a/api/test-current.txt b/api/test-current.txt
index 9ee2527..c5b6cb2 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -10928,8 +10928,8 @@
     field public static final int PROTECTION_DANGEROUS = 1; // 0x1
     field public static final int PROTECTION_FLAG_APPOP = 64; // 0x40
     field public static final int PROTECTION_FLAG_DEVELOPMENT = 32; // 0x20
-    field public static final int PROTECTION_FLAG_EPHEMERAL = 4096; // 0x1000
     field public static final int PROTECTION_FLAG_INSTALLER = 256; // 0x100
+    field public static final int PROTECTION_FLAG_INSTANT = 4096; // 0x1000
     field public static final int PROTECTION_FLAG_PRE23 = 128; // 0x80
     field public static final int PROTECTION_FLAG_PREINSTALLED = 1024; // 0x400
     field public static final int PROTECTION_FLAG_PRIVILEGED = 16; // 0x10
diff --git a/cmds/incident_helper/IncidentHelper.cpp b/cmds/incident_helper/IncidentHelper.cpp
index 664c48f..fba5e66 100644
--- a/cmds/incident_helper/IncidentHelper.cpp
+++ b/cmds/incident_helper/IncidentHelper.cpp
@@ -29,8 +29,37 @@
 
 using namespace android::base;
 using namespace android::os;
+using namespace google::protobuf;
 using namespace std;
 
+static bool
+SetTableField(::google::protobuf::Message* message, string field_name, string field_value) {
+    const Descriptor* descriptor = message->GetDescriptor();
+    const Reflection* reflection = message->GetReflection();
+
+    const FieldDescriptor* field = descriptor->FindFieldByName(field_name);
+    switch (field->type()) {
+        case FieldDescriptor::TYPE_STRING:
+            reflection->SetString(message, field, field_value);
+            return true;
+        case FieldDescriptor::TYPE_INT64:
+            reflection->SetInt64(message, field, atol(field_value.c_str()));
+            return true;
+        case FieldDescriptor::TYPE_UINT64:
+            reflection->SetUInt64(message, field, atol(field_value.c_str()));
+            return true;
+        case FieldDescriptor::TYPE_INT32:
+            reflection->SetInt32(message, field, atoi(field_value.c_str()));
+            return true;
+        case FieldDescriptor::TYPE_UINT32:
+            reflection->SetUInt32(message, field, atoi(field_value.c_str()));
+            return true;
+        default:
+            // Add new scalar types
+            return false;
+    }
+}
+
 // ================================================================================
 status_t ReverseParser::Parse(const int in, const int out) const
 {
@@ -51,31 +80,6 @@
 // ================================================================================
 static const string KERNEL_WAKEUP_LINE_DELIMITER = "\t";
 
-static void SetWakeupSourceField(WakeupSourceProto* source, string name, string value) {
-    if (name == "name") {
-        source->set_name(value.c_str());
-    } else if (name == "active_count") {
-        source->set_active_count(atoi(value.c_str()));
-    } else if (name == "event_count") {
-        source->set_event_count(atoi(value.c_str()));
-    } else if (name == "wakeup_count") {
-        source->set_wakeup_count(atoi(value.c_str()));
-    } else if (name == "expire_count") {
-        source->set_expire_count(atoi(value.c_str()));
-    } else if (name == "active_count") {
-        source->set_active_since(atol(value.c_str()));
-    } else if (name == "total_time") {
-        source->set_total_time(atol(value.c_str()));
-    } else if (name == "max_time") {
-        source->set_max_time(atol(value.c_str()));
-    } else if (name == "last_change") {
-        source->set_last_change(atol(value.c_str()));
-    } else if (name == "prevent_suspend_time") {
-        source->set_prevent_suspend_time(atol(value.c_str()));
-    }
-    // add new fields
-}
-
 status_t KernelWakesParser::Parse(const int in, const int out) const {
     Reader reader(in);
     string line;
@@ -90,12 +94,12 @@
         if (line.empty()) continue;
         // parse head line
         if (nline++ == 0) {
-            split(line, header, KERNEL_WAKEUP_LINE_DELIMITER);
+            header = parseHeader(line, KERNEL_WAKEUP_LINE_DELIMITER);
             continue;
         }
 
         // parse for each record, the line delimiter is \t only!
-        split(line, record, KERNEL_WAKEUP_LINE_DELIMITER);
+        record = parseRecord(line, KERNEL_WAKEUP_LINE_DELIMITER);
 
         if (record.size() != header.size()) {
             // TODO: log this to incident report!
@@ -105,7 +109,10 @@
 
         WakeupSourceProto* source = proto.add_wakeup_sources();
         for (int i=0; i<(int)record.size(); i++) {
-            SetWakeupSourceField(source, header[i], record[i]);
+            if (!SetTableField(source, header[i], record[i])) {
+                fprintf(stderr, "[%s]Line %d has bad value %s of %s\n",
+                        this->name.string(), nline, header[i].c_str(), record[i].c_str());
+            }
         }
     }
 
@@ -123,32 +130,6 @@
 }
 
 // ================================================================================
-// Remove K for numeric fields
-static void SetProcessField(ProcessProto* process, string name, string value) {
-    ssize_t len = value.size();
-    if (name == "PID") {
-        process->set_pid(atoi(value.c_str()));
-    } else if (name == "Vss") {
-        process->set_vss(atol(value.substr(0, len - 1).c_str()));
-    } else if (name == "Rss") {
-        process->set_rss(atol(value.substr(0, len - 1).c_str()));
-    } else if (name == "Pss") {
-        process->set_pss(atol(value.substr(0, len - 1).c_str()));
-    } else if (name == "Uss") {
-        process->set_uss(atol(value.substr(0, len - 1).c_str()));
-    } else if (name == "Swap") {
-        process->set_swap(atol(value.substr(0, len - 1).c_str()));
-    } else if (name == "PSwap") {
-        process->set_pswap(atol(value.substr(0, len - 1).c_str()));
-    } else if (name == "USwap") {
-        process->set_uswap(atol(value.substr(0, len - 1).c_str()));
-    } else if (name == "ZSwap") {
-        process->set_zswap(atol(value.substr(0, len - 1).c_str()));
-    } else if (name == "cmdline") {
-        process->set_cmdline(value);
-    }
-}
-
 status_t ProcrankParser::Parse(const int in, const int out) const {
     Reader reader(in);
     string line, content;
@@ -164,22 +145,22 @@
 
         // parse head line
         if (nline++ == 0) {
-            split(line, header);
+            header = parseHeader(line);
             continue;
         }
 
-        split(line, record);
+        record = parseRecord(line);
         if (record.size() != header.size()) {
             if (record[record.size() - 1] == "TOTAL") { // TOTAL record
                 ProcessProto* total = proto.mutable_summary()->mutable_total();
                 for (int i=1; i<=(int)record.size(); i++) {
-                    SetProcessField(total, header[header.size() - i], record[record.size() - i]);
+                    SetTableField(total, header[header.size() - i], record[record.size() - i]);
                 }
             } else if (record[0] == "ZRAM:") {
-                split(line, record, ":");
+                record = parseRecord(line, ":");
                 proto.mutable_summary()->mutable_zram()->set_raw_text(record[1]);
             } else if (record[0] == "RAM:") {
-                split(line, record, ":");
+                record = parseRecord(line, ":");
                 proto.mutable_summary()->mutable_ram()->set_raw_text(record[1]);
             } else {
                 fprintf(stderr, "[%s]Line %d has missing fields\n%s\n", this->name.string(), nline,
@@ -190,7 +171,10 @@
 
         ProcessProto* process = proto.add_processes();
         for (int i=0; i<(int)record.size(); i++) {
-            SetProcessField(process, header[i], record[i]);
+            if (!SetTableField(process, header[i], record[i])) {
+                fprintf(stderr, "[%s]Line %d has bad value %s of %s\n",
+                        this->name.string(), nline, header[i].c_str(), record[i].c_str());
+            }
         }
     }
 
diff --git a/cmds/incident_helper/ih_util.cpp b/cmds/incident_helper/ih_util.cpp
index bbb625f..b2fda23 100644
--- a/cmds/incident_helper/ih_util.cpp
+++ b/cmds/incident_helper/ih_util.cpp
@@ -23,16 +23,24 @@
 
 const ssize_t BUFFER_SIZE = 16 * 1024; // 4KB
 
-std::string trim(const std::string& s, const std::string& whitespace) {
-    const auto head = s.find_first_not_of(whitespace);
+
+static std::string trim(const std::string& s) {
+    const auto head = s.find_first_not_of(DEFAULT_WHITESPACE);
     if (head == std::string::npos) return "";
 
-    const auto tail = s.find_last_not_of(whitespace);
+    const auto tail = s.find_last_not_of(DEFAULT_WHITESPACE);
     return s.substr(head, tail - head + 1);
 }
 
+static std::string trimHeader(const std::string& s) {
+    std::string res = trim(s);
+    std::transform(res.begin(), res.end(), res.begin(), ::tolower);
+    return res;
+}
+
 // This is similiar to Split in android-base/file.h, but it won't add empty string
-void split(const std::string& line, std::vector<std::string>& words, const std::string& delimiters) {
+static void split(const std::string& line, std::vector<std::string>& words,
+        const trans_func& func, const std::string& delimiters) {
     words.clear();  // clear the buffer before split
 
     size_t base = 0;
@@ -40,7 +48,7 @@
     while (true) {
         found = line.find_first_of(delimiters, base);
         if (found != base) {
-            std::string word = trim(line.substr(base, found - base));
+            std::string word = (*func) (line.substr(base, found - base));
             if (!word.empty()) {
                 words.push_back(word);
             }
@@ -50,13 +58,18 @@
     }
 }
 
-bool assertHeaders(const char* expected[], const std::vector<std::string>& actual) {
-    for (size_t i = 0; i < actual.size(); i++) {
-        if (expected[i] == NULL || std::string(expected[i]) != actual[i]) {
-            return false;
-        }
-    }
-    return true;
+header_t parseHeader(const std::string& line, const std::string& delimiters) {
+    header_t header;
+    trans_func f = &trimHeader;
+    split(line, header, f, delimiters);
+    return header;
+}
+
+record_t parseRecord(const std::string& line, const std::string& delimiters) {
+    record_t record;
+    trans_func f = &trim;
+    split(line, record, f, delimiters);
+    return record;
 }
 
 Reader::Reader(const int fd) : Reader(fd, BUFFER_SIZE) {};
diff --git a/cmds/incident_helper/ih_util.h b/cmds/incident_helper/ih_util.h
index 9e0c18e..5598eed 100644
--- a/cmds/incident_helper/ih_util.h
+++ b/cmds/incident_helper/ih_util.h
@@ -23,16 +23,13 @@
 
 typedef std::vector<std::string> header_t;
 typedef std::vector<std::string> record_t;
+typedef std::string (*trans_func) (const std::string&);
 
 const char DEFAULT_NEWLINE = '\n';
 const std::string DEFAULT_WHITESPACE = " \t";
 
-std::string trim(const std::string& s, const std::string& whitespace = DEFAULT_WHITESPACE);
-
-void split(const std::string& line, std::vector<std::string>& words,
-    const std::string& delimiters = DEFAULT_WHITESPACE);
-
-bool assertHeaders(const char* expected[], const std::vector<std::string>& actual);
+header_t parseHeader(const std::string& line, const std::string& delimiters = DEFAULT_WHITESPACE);
+record_t parseRecord(const std::string& line, const std::string& delimiters = DEFAULT_WHITESPACE);
 
 /**
  * Reader class reads data from given fd in streaming fashion.
diff --git a/cmds/incident_helper/tests/ih_util_test.cpp b/cmds/incident_helper/tests/ih_util_test.cpp
index 5158e0a..3b9ed40 100644
--- a/cmds/incident_helper/tests/ih_util_test.cpp
+++ b/cmds/incident_helper/tests/ih_util_test.cpp
@@ -26,25 +26,38 @@
 using namespace std;
 using ::testing::StrEq;
 
-TEST(IhUtilTest, Trim) {
-    EXPECT_THAT(trim(" \t 100 00\toooh \t wqrw  "), StrEq("100 00\toooh \t wqrw"));
-    EXPECT_THAT(trim(" \t 100 00\toooh \t wqrw  ", " "), StrEq("\t 100 00\toooh \t wqrw"));
-}
-
-TEST(IhUtilTest, Split) {
-    vector<string> result, expected;
-    split(" \t \t\t ", result);
+TEST(IhUtilTest, ParseHeader) {
+    header_t result, expected;
+    result = parseHeader(" \t \t\t ");
     EXPECT_EQ(expected, result);
 
-    split(" \t 100 00\toooh \t wqrw", result);
+    result = parseHeader(" \t 100 00\tOpQ \t wqrw");
+    expected = { "100", "00", "opq", "wqrw" };
+    EXPECT_EQ(expected, result);
+
+    result = parseHeader(" \t 100 00\toooh \t wTF", "\t");
+    expected = { "100 00", "oooh", "wtf" };
+    EXPECT_EQ(expected, result);
+
+    result = parseHeader("123,456,78_9", ",");
+    expected = { "123", "456", "78_9" };
+    EXPECT_EQ(expected, result);
+}
+
+TEST(IhUtilTest, ParseRecord) {
+    record_t result, expected;
+    result = parseRecord(" \t \t\t ");
+    EXPECT_EQ(expected, result);
+
+    result = parseRecord(" \t 100 00\toooh \t wqrw");
     expected = { "100", "00", "oooh", "wqrw" };
     EXPECT_EQ(expected, result);
 
-    split(" \t 100 00\toooh \t wqrw", result, "\t");
+    result = parseRecord(" \t 100 00\toooh \t wqrw", "\t");
     expected = { "100 00", "oooh", "wqrw" };
     EXPECT_EQ(expected, result);
 
-    split("123,456,78_9", result, ",");
+    result = parseRecord("123,456,78_9", ",");
     expected = { "123", "456", "78_9" };
     EXPECT_EQ(expected, result);
 }
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index 9cc47ed..0f6f38e 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -330,11 +330,13 @@
 void CommandSection::init(const char* command, va_list args)
 {
     va_list copied_args;
-    va_copy(copied_args, args);
     int numOfArgs = 0;
-    while(va_arg(args, const char*) != NULL) {
+
+    va_copy(copied_args, args);
+    while(va_arg(copied_args, const char*) != NULL) {
         numOfArgs++;
     }
+    va_end(copied_args);
 
     // allocate extra 1 for command and 1 for NULL terminator
     mCommand = (const char**)malloc(sizeof(const char*) * (numOfArgs + 2));
@@ -342,13 +344,12 @@
     mCommand[0] = command;
     name = command;
     for (int i=0; i<numOfArgs; i++) {
-        const char* arg = va_arg(copied_args, const char*);
+        const char* arg = va_arg(args, const char*);
         mCommand[i+1] = arg;
         name += " ";
         name += arg;
     }
     mCommand[numOfArgs+1] = NULL;
-    va_end(copied_args);
 }
 
 CommandSection::CommandSection(int id, const int64_t timeoutMs, const char* command, ...)
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index adb3152..1c8a376 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1006,6 +1006,11 @@
             Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
             mFragments.restoreAllState(p, mLastNonConfigurationInstances != null
                     ? mLastNonConfigurationInstances.fragments : null);
+        } else {
+            AutofillManager afm = getAutofillManager();
+            if (afm != null) {
+                afm.dismissUi();
+            }
         }
         mFragments.dispatchCreate();
         getApplication().dispatchActivityCreated(this, savedInstanceState);
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index e5fe240..68fb7bc 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -30,8 +30,6 @@
 
 import com.android.internal.app.IVoiceInteractor;
 
-import java.io.PrintWriter;
-import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -90,11 +88,12 @@
             String processName, String abiOverride, int uid, Runnable crashHandler);
 
     /**
-     * Acquires a sleep token with the specified tag.
+     * Acquires a sleep token for the specified display with the specified tag.
      *
      * @param tag A string identifying the purpose of the token (eg. "Dream").
+     * @param displayId The display to apply the sleep token to.
      */
-    public abstract SleepToken acquireSleepToken(@NonNull String tag);
+    public abstract SleepToken acquireSleepToken(@NonNull String tag, int displayId);
 
     /**
      * Sleep tokens cause the activity manager to put the top activity to sleep.
diff --git a/core/java/android/content/pm/InstantAppResolveInfo.java b/core/java/android/content/pm/InstantAppResolveInfo.java
index dcaf66e..22e994f 100644
--- a/core/java/android/content/pm/InstantAppResolveInfo.java
+++ b/core/java/android/content/pm/InstantAppResolveInfo.java
@@ -37,7 +37,7 @@
 @SystemApi
 public final class InstantAppResolveInfo implements Parcelable {
     /** Algorithm that will be used to generate the domain digest */
-    public static final String SHA_ALGORITHM = "SHA-256";
+    private static final String SHA_ALGORITHM = "SHA-256";
 
     private final InstantAppDigest mDigest;
     private final String mPackageName;
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 07ce4e9..998e403 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3240,11 +3240,11 @@
         perm.info.protectionLevel = PermissionInfo.fixProtectionLevel(perm.info.protectionLevel);
 
         if ((perm.info.protectionLevel&PermissionInfo.PROTECTION_MASK_FLAGS) != 0) {
-            if ( (perm.info.protectionLevel&PermissionInfo.PROTECTION_FLAG_EPHEMERAL) == 0
+            if ( (perm.info.protectionLevel&PermissionInfo.PROTECTION_FLAG_INSTANT) == 0
                     && (perm.info.protectionLevel&PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) == 0
                     && (perm.info.protectionLevel&PermissionInfo.PROTECTION_MASK_BASE) !=
                     PermissionInfo.PROTECTION_SIGNATURE) {
-                outError[0] = "<permission>  protectionLevel specifies a non-ephemeral flag but is "
+                outError[0] = "<permission>  protectionLevel specifies a non-instnat flag but is "
                         + "not based on signature type";
                 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                 return false;
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 71a6321..b84c1b9 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -122,13 +122,10 @@
 
     /**
      * Additional flag for {@link #protectionLevel}, corresponding
-     * to the <code>ephemeral</code> value of
+     * to the <code>instant</code> value of
      * {@link android.R.attr#protectionLevel}.
-     * @hide
      */
-    @SystemApi
-    @TestApi
-    public static final int PROTECTION_FLAG_EPHEMERAL = 0x1000;
+    public static final int PROTECTION_FLAG_INSTANT = 0x1000;
 
     /**
      * Additional flag for {@link #protectionLevel}, corresponding
@@ -263,8 +260,8 @@
         if ((level&PermissionInfo.PROTECTION_FLAG_SETUP) != 0) {
             protLevel += "|setup";
         }
-        if ((level&PermissionInfo.PROTECTION_FLAG_EPHEMERAL) != 0) {
-            protLevel += "|ephemeral";
+        if ((level&PermissionInfo.PROTECTION_FLAG_INSTANT) != 0) {
+            protLevel += "|instant";
         }
         if ((level&PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) != 0) {
             protLevel += "|runtime";
diff --git a/core/java/android/net/ITetheringStatsProvider.aidl b/core/java/android/net/ITetheringStatsProvider.aidl
index 769086d..1aeabc1 100644
--- a/core/java/android/net/ITetheringStatsProvider.aidl
+++ b/core/java/android/net/ITetheringStatsProvider.aidl
@@ -19,7 +19,7 @@
 import android.net.NetworkStats;
 
 /**
- * Interface that allows NetworkManagementService to query for tethering statistics.
+ * Interface for NetworkManagementService to query tethering statistics and set data limits.
  *
  * TODO: this does not really need to be an interface since Tethering runs in the same process
  * as NetworkManagementService. Consider refactoring Tethering to use direct access to
@@ -29,5 +29,14 @@
  * @hide
  */
 interface ITetheringStatsProvider {
+    // Returns cumulative statistics for all tethering sessions since boot, on all upstreams.
     NetworkStats getTetherStats();
+
+    // Sets the interface quota for the specified upstream interface. This is defined as the number
+    // of bytes, starting from zero and counting from now, after which data should stop being
+    // forwarded to/from the specified upstream. A value of QUOTA_UNLIMITED means there is no limit.
+    void setInterfaceQuota(String iface, long quotaBytes);
+
+    // Indicates that no data usage limit is set.
+    const int QUOTA_UNLIMITED = -1;
 }
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index c068e6a..53b49f0 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -280,6 +280,76 @@
  * calling {@link FillResponse.Builder#setIgnoredIds(AutofillId...)} so the system does not trigger
  * a new {@link #onFillRequest(FillRequest, CancellationSignal, FillCallback)} when these views are
  * focused.
+ *
+ * <h3>Web security</h3>
+ *
+ * <p>When handling autofill requests that represent web pages (typically
+ * view structures whose root's {@link android.app.assist.AssistStructure.ViewNode#getClassName()}
+ * is a {@link android.webkit.WebView}), the service should take the following steps to verify if
+ * the structure can be autofilled with the data associated with the app requesting it:
+ *
+ * <ol>
+ *   <li>Use the {@link android.app.assist.AssistStructure.ViewNode#getWebDomain()} to get the
+ *       source of the document.
+ *   <li>Get the canonical domain using the
+ *       <a href="https://publicsuffix.org/>Public Suffix List</a> (see example below).
+ *   <li>Use <a href="https://developers.google.com/digital-asset-links/">Digital Asset Links</a>
+ *       to obtain the package name and certificate fingerprint of the package corresponding to
+ *       the canonical domain.
+ *   <li>Make sure the certificate fingerprint matches the value returned by Package Manager
+ *       (see "Package verification" section above).
+ * </ol>
+ *
+ * <p>Here's an example on how to get the canonical domain using
+ * <a href="https://github.com/google/guava">Guava</a>:
+ *
+ * <pre class="prettyprint">
+ * private static String getCanonicalDomain(String domain) {
+ *   InternetDomainName idn = InternetDomainName.from(domain);
+ *   while (!idn.isTopPrivateDomain() && idn != null) {
+ *     idn = idn.parent();
+ *   }
+ *   return idn == null ? null : idn.toString();
+ * }
+ * </pre>
+ *
+ * <p>If the association between the web domain and app package cannot be verified through the steps
+ * above, the service can still autofill the app, but it should warn the user about the potential
+ * data leakage first, and askfor the user to confirm. For example, the service could:
+ *
+ * <ol>
+ *   <li>Create a dataset that requires
+ *       {@link Dataset.Builder#setAuthentication(android.content.IntentSender) authentication} to
+ *       unlock.
+ *   <li>Include the web domain in the custom presentation for the
+ *       {@link Dataset.Builder#setValue(AutofillId, AutofillValue, android.widget.RemoteViews)
+ *       dataset value}.
+ *   <li>When the user select that dataset, show a disclaimer dialog explaining that the app is
+ *       requesting credentials for a web domain, but the service could not verify if the app owns
+ *       that domain. If the user agrees, then the service can unlock the dataset.
+ *   <li>Similarly, when adding a {@link SaveInfo} object for the request, the service should
+ *       include the above disclaimer in the {@link SaveInfo.Builder#setDescription(CharSequence)}.
+ * </ol>
+ *
+ * <p>This same procedure could also be used when the autofillable data is contained inside an
+ * {@code IFRAME}, in which case the WebView generates a new autofill context when a node inside
+ * the {@code IFRAME} is focused, which the root node containing the {@code IFRAME}'s {@code src}
+ * attribute on {@link android.app.assist.AssistStructure.ViewNode#getWebDomain()}. A typical and
+ * legitimate use case for this scenario is a financial app that allows the user
+ * to login on different bank accounts. For example, a financial app {@code my_financial_app} could
+ * use a WebView that loads contents from {@code banklogin.my_financial_app.com}, which contains an
+ * {@code IFRAME} node whose {@code src} attribute is {@code login.some_bank.com}. When fulfilling
+ * that request, the service could add an
+ * {@link Dataset.Builder#setAuthentication(android.content.IntentSender) authenticated dataset}
+ * whose presentation displays "Username for some_bank.com" and
+ * "Password for some_bank.com". Then when the user taps one of these options, the service
+ * shows the disclaimer dialog explaining that selecting that option would release the
+ * {@code login.some_bank.com} credentials to the {@code my_financial_app}; if the user agrees,
+ * then the service returns an unlocked dataset with the {@code some_bank.com} credentials.
+ *
+ * <p><b>Note:</b> The autofill service could also whitelist well-known browser apps and skip the
+ * verifications above, as long as the service can verify the authenticity of the browser app by
+ * checking its signing certificate.
  */
 public abstract class AutofillService extends Service {
     private static final String TAG = "AutofillService";
@@ -424,7 +494,7 @@
      * {@link SaveCallback#onSuccess()} or {@link SaveCallback#onFailure(CharSequence)})
      * to notify the result of the request.
      *
-     * <p><b>NOTE: </b>to retrieve the actual value of the field, the service should call
+     * <p><b>Note:</b> To retrieve the actual value of the field, the service should call
      * {@link android.app.assist.AssistStructure.ViewNode#getAutofillValue()}; if it calls
      * {@link android.app.assist.AssistStructure.ViewNode#getText()} or other methods, there is no
      * guarantee such method will return the most recent value of the field.
diff --git a/core/java/android/service/autofill/CustomDescription.java b/core/java/android/service/autofill/CustomDescription.java
index 3da0b5e..4f06bd7 100644
--- a/core/java/android/service/autofill/CustomDescription.java
+++ b/core/java/android/service/autofill/CustomDescription.java
@@ -22,11 +22,13 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Log;
-import android.util.SparseArray;
+import android.util.Pair;
 import android.widget.RemoteViews;
 
 import com.android.internal.util.Preconditions;
 
+import java.util.ArrayList;
+
 /**
  * Defines a custom description for the Save UI affordance.
  *
@@ -65,14 +67,17 @@
  *   new ImageTransformation.Builder(ccNumberId)
  *     .addOption(Pattern.compile(""^4815.*$"), R.drawable.ic_credit_card_logo1)
  *     .addOption(Pattern.compile(""^1623.*$"), R.drawable.ic_credit_card_logo2)
- *     .addOption(Pattern.compile(""^42.*$"), R.drawable.ic_credit_card_logo3);
+ *     .addOption(Pattern.compile(""^42.*$"), R.drawable.ic_credit_card_logo3)
+ *     .build();
  * // Masked credit card number (as .....LAST_4_DIGITS)
- * builder.addChild(R.id.templateCcNumber, new CharSequenceTransformation.Builder()
- *     .addField(ccNumberId, Pattern.compile(""^.*(\\d\\d\\d\\d)$"), "...$1")
+ * builder.addChild(R.id.templateCcNumber, new CharSequenceTransformation
+ *     .Builder(ccNumberId, Pattern.compile(""^.*(\\d\\d\\d\\d)$"), "...$1")
+ *     .build();
  * // Expiration date as MM / YYYY:
- * builder.addChild(R.id.templateExpDate, new CharSequenceTransformation.Builder()
- *     .addField(ccExpMonthId, Pattern.compile(""^(\\d\\d)$"), "Exp: $1")
- *     .addField(ccExpYearId, Pattern.compile(""^(\\d\\d)$"), "/$1");
+ * builder.addChild(R.id.templateExpDate, new CharSequenceTransformation
+ *     .Builder(ccExpMonthId, Pattern.compile(""^(\\d\\d)$"), "Exp: $1")
+ *     .addField(ccExpYearId, Pattern.compile(""^(\\d\\d)$"), "/$1")
+ *     .build();
  * </pre>
  *
  * <p>See {@link ImageTransformation}, {@link CharSequenceTransformation} for more info about these
@@ -83,7 +88,7 @@
     private static final String TAG = "CustomDescription";
 
     private final RemoteViews mPresentation;
-    private final SparseArray<InternalTransformation> mTransformations;
+    private final ArrayList<Pair<Integer, InternalTransformation>> mTransformations;
 
     private CustomDescription(Builder builder) {
         mPresentation = builder.mPresentation;
@@ -96,8 +101,9 @@
             final int size = mTransformations.size();
             if (sDebug) Log.d(TAG, "getPresentation(): applying " + size + " transformations");
             for (int i = 0; i < size; i++) {
-                final int id = mTransformations.keyAt(i);
-                final InternalTransformation transformation = mTransformations.valueAt(i);
+                final Pair<Integer, InternalTransformation> pair = mTransformations.get(i);
+                final int id = pair.first;
+                final InternalTransformation transformation = pair.second;
                 if (sDebug) Log.d(TAG, "#" + i + ": " + transformation);
 
                 try {
@@ -119,7 +125,7 @@
     public static class Builder {
         private final RemoteViews mPresentation;
 
-        private SparseArray<InternalTransformation> mTransformations;
+        private ArrayList<Pair<Integer, InternalTransformation>> mTransformations;
 
         /**
          * Default constructor.
@@ -134,6 +140,9 @@
          * Adds a transformation to replace the value of a child view with the fields in the
          * screen.
          *
+         * <p>When multiple transformations are added for the same child view, they will be applied
+         * in the same order as added.
+         *
          * @param id view id of the children view.
          * @param transformation an implementation provided by the Android System.
          * @return this builder.
@@ -144,9 +153,9 @@
             Preconditions.checkArgument((transformation instanceof InternalTransformation),
                     "not provided by Android System: " + transformation);
             if (mTransformations == null) {
-                mTransformations = new SparseArray<>();
+                mTransformations = new ArrayList<>();
             }
-            mTransformations.put(id, (InternalTransformation) transformation);
+            mTransformations.add(new Pair<>(id, (InternalTransformation) transformation));
             return this;
         }
 
@@ -189,8 +198,9 @@
             final int[] ids = new int[size];
             final InternalTransformation[] values = new InternalTransformation[size];
             for (int i = 0; i < size; i++) {
-                ids[i] = mTransformations.keyAt(i);
-                values[i] = mTransformations.valueAt(i);
+                final Pair<Integer, InternalTransformation> pair = mTransformations.get(i);
+                ids[i] = pair.first;
+                values[i] = pair.second;
             }
             dest.writeIntArray(ids);
             dest.writeParcelableArray(values, flags);
diff --git a/core/java/android/service/gatekeeper/GateKeeperResponse.java b/core/java/android/service/gatekeeper/GateKeeperResponse.java
index 287dc76..9b52934 100644
--- a/core/java/android/service/gatekeeper/GateKeeperResponse.java
+++ b/core/java/android/service/gatekeeper/GateKeeperResponse.java
@@ -106,6 +106,8 @@
             if (mPayload != null) {
                 dest.writeInt(mPayload.length);
                 dest.writeByteArray(mPayload);
+            } else {
+                dest.writeInt(0);
             }
         }
     }
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index f548d3b..1c6275f 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -195,7 +195,8 @@
         // Needed for throttling onComputeColors.
         private long mLastColorInvalidation;
         private final Runnable mNotifyColorsChanged = this::notifyColorsChanged;
-        private Supplier<Long> mClockFunction = SystemClock::elapsedRealtime;
+        private final Supplier<Long> mClockFunction;
+        private final Handler mHandler;
 
         DisplayManager mDisplayManager;
         Display mDisplay;
@@ -362,6 +363,26 @@
                 }
             }
         };
+
+        /**
+         * Default constructor
+         */
+        public Engine() {
+            this(SystemClock::elapsedRealtime, Handler.getMain());
+        }
+
+        /**
+         * Constructor used for test purposes.
+         *
+         * @param clockFunction Supplies current times in millis.
+         * @param handler Used for posting/deferring asynchronous calls.
+         * @hide
+         */
+        @VisibleForTesting
+        public Engine(Supplier<Long> clockFunction, Handler handler) {
+           mClockFunction = clockFunction;
+           mHandler = handler;
+        }
         
         /**
          * Provides access to the surface in which this wallpaper is drawn.
@@ -563,18 +584,17 @@
          */
         public void notifyColorsChanged() {
             final long now = mClockFunction.get();
-            final Handler mainHandler = Handler.getMain();
             if (now - mLastColorInvalidation < NOTIFY_COLORS_RATE_LIMIT_MS) {
                 Log.w(TAG, "This call has been deferred. You should only call "
                         + "notifyColorsChanged() once every "
                         + (NOTIFY_COLORS_RATE_LIMIT_MS / 1000f) + " seconds.");
-                if (!mainHandler.hasCallbacks(mNotifyColorsChanged)) {
-                    mainHandler.postDelayed(mNotifyColorsChanged, NOTIFY_COLORS_RATE_LIMIT_MS);
+                if (!mHandler.hasCallbacks(mNotifyColorsChanged)) {
+                    mHandler.postDelayed(mNotifyColorsChanged, NOTIFY_COLORS_RATE_LIMIT_MS);
                 }
                 return;
             }
             mLastColorInvalidation = now;
-            mainHandler.removeCallbacks(mNotifyColorsChanged);
+            mHandler.removeCallbacks(mNotifyColorsChanged);
 
             try {
                 final WallpaperColors newColors = onComputeColors();
@@ -662,14 +682,6 @@
             }
         }
 
-        /**
-         * @hide
-         */
-        @VisibleForTesting
-        public void setClockFunction(Supplier<Long> clockFunction) {
-            mClockFunction = clockFunction;
-        }
-
         void updateSurface(boolean forceRelayout, boolean forceReport, boolean redrawNeeded) {
             if (mDestroyed) {
                 Log.w(TAG, "Ignoring updateSurface: destroyed");
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 0cec496..c5c6b37 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -16,12 +16,18 @@
 
 package android.view;
 
+import static android.view.DisplayInfoProto.APP_HEIGHT;
+import static android.view.DisplayInfoProto.APP_WIDTH;
+import static android.view.DisplayInfoProto.LOGICAL_HEIGHT;
+import static android.view.DisplayInfoProto.LOGICAL_WIDTH;
+
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.ArraySet;
 import android.util.DisplayMetrics;
+import android.util.proto.ProtoOutputStream;
 
 import libcore.util.Objects;
 
@@ -654,6 +660,22 @@
         return sb.toString();
     }
 
+    /**
+     * Write to a protocol buffer output stream.
+     * Protocol buffer message definition at {@link android.view.DisplayInfoProto}
+     *
+     * @param protoOutputStream Stream to write the Rect object to.
+     * @param fieldId           Field Id of the DisplayInfoProto as defined in the parent message
+     */
+    public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) {
+        final long token = protoOutputStream.start(fieldId);
+        protoOutputStream.write(LOGICAL_WIDTH, logicalWidth);
+        protoOutputStream.write(LOGICAL_HEIGHT, logicalHeight);
+        protoOutputStream.write(APP_WIDTH, appWidth);
+        protoOutputStream.write(APP_HEIGHT, appHeight);
+        protoOutputStream.end(token);
+    }
+
     private static String flagsToString(int flags) {
         StringBuilder result = new StringBuilder();
         if ((flags & Display.FLAG_SECURE) != 0) {
diff --git a/core/java/android/view/IApplicationToken.aidl b/core/java/android/view/IApplicationToken.aidl
index b01c0ef..a063a70 100644
--- a/core/java/android/view/IApplicationToken.aidl
+++ b/core/java/android/view/IApplicationToken.aidl
@@ -20,5 +20,6 @@
 /** {@hide} */
 interface IApplicationToken
 {
+  String getName();
 }
 
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 1e50a85..fe50a64 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -34,6 +34,7 @@
 import android.os.Parcelable;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.proto.ProtoOutputStream;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -2545,6 +2546,15 @@
         }
 
         /**
+         * @hide
+         */
+        public void writeToProto(ProtoOutputStream proto, long fieldId) {
+            final long token = proto.start(fieldId);
+            proto.write(WindowLayoutParamsProto.TYPE, type);
+            proto.end(token);
+        }
+
+        /**
          * Scale the layout params' coordinates and size.
          * @hide
          */
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 49b7ed8..d2eb785 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -77,6 +77,7 @@
 import android.os.Looper;
 import android.os.RemoteException;
 import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
 import android.view.animation.Animation;
 
 import com.android.internal.policy.IKeyguardDismissCallback;
@@ -1646,6 +1647,14 @@
     public void dump(String prefix, PrintWriter writer, String[] args);
 
     /**
+     * Write the WindowManagerPolicy's state into the protocol buffer.
+     * The message is described in {@link com.android.server.wm.proto.WindowManagerPolicyProto}
+     *
+     * @param proto The protocol buffer output stream to write to.
+     */
+    void writeToProto(ProtoOutputStream proto, long fieldId);
+
+    /**
      * Returns whether a given window type can be magnified.
      *
      * @param windowType The window type.
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 29e5523..a0d9099 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -1339,6 +1339,25 @@
     }
 
     /**
+     * Dismiss the Autofill UI, even if associated with other sessions.
+     *
+     * <p>Typically called when a new activity starts so it can hide the existing Save UI (if any).
+     *
+     * @hide
+     */
+    public void dismissUi() {
+        if (sVerbose) Log.v(TAG, "dismissUi()");
+
+        if (mService == null) return;
+
+        try {
+            mService.dismissUi();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * View tracking information. Once all tracked views become invisible the session is finished.
      */
     private class TrackedViews {
diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl
index 627afa7..aa8e19a 100644
--- a/core/java/android/view/autofill/IAutoFillManager.aidl
+++ b/core/java/android/view/autofill/IAutoFillManager.aidl
@@ -49,4 +49,5 @@
     void disableOwnedAutofillServices(int userId);
     boolean isServiceSupported(int userId);
     boolean isServiceEnabled(int userId, String packageName);
+    void dismissUi();
 }
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 650c7d9..049f1ef 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -2725,6 +2725,18 @@
      * understood by the {@link android.service.autofill.AutofillService} implementations:
      *
      * <ol>
+     *   <li>Only the HTML nodes inside a {@code FORM} are generated.
+     *   <li>The source of the HTML is set using {@link ViewStructure#setWebDomain(String)} in the
+     *   node representing the WebView.
+     *   <li>If a web page has multiple {@code FORM}s, only the data for the current form is
+     *   represented&mdash;if the user taps a field from another form, then the current autofill
+     *   context is canceled (by calling {@link android.view.autofill.AutofillManager#cancel()} and
+     *   a new context is created for that {@code FORM}.
+     *   <li>Similarly, if the page has {@code IFRAME} nodes, they are not initially represented in
+     *   the view structure until the user taps a field from a {@code FORM} inside the
+     *   {@code IFRAME}, in which case it would be treated the same way as multiple forms described
+     *   above, except that the {@link ViewStructure#setWebDomain(String) web domain} of the
+     *   {@code FORM} contains the {@code src} attribute from the {@code IFRAME} node.
      *   <li>If the Android SDK provides a similar View, then should be set with the
      *   fully-qualified class name of such view.
      *   <li>The W3C autofill field ({@code autocomplete} tag attribute) maps to
diff --git a/core/proto/android/graphics/rect.proto b/core/proto/android/graphics/rect.proto
new file mode 100644
index 0000000..a65d331
--- /dev/null
+++ b/core/proto/android/graphics/rect.proto
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto3";
+
+package android.graphics;
+
+option java_multiple_files = true;
+
+message RectProto {
+  int32 left = 1;
+  int32 top = 2;
+  int32 right = 3;
+  int32 bottom = 4;
+}
+
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
new file mode 100644
index 0000000..7fb4802
--- /dev/null
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto3";
+
+import "frameworks/base/core/proto/android/graphics/rect.proto";
+import "frameworks/base/core/proto/android/view/displayinfo.proto";
+import "frameworks/base/core/proto/android/view/windowlayoutparams.proto";
+
+package com.android.server.wm.proto;
+
+option java_multiple_files = true;
+
+message WindowManagerServiceProto {
+  WindowManagerPolicyProto policy = 1;
+  /* window manager hierarchy structure */
+  repeated DisplayProto displays = 2;
+  /* window references in top down z order */
+  repeated IdentifierProto windows = 3;
+  IdentifierProto focused_window = 4;
+  string focused_app = 5;
+  IdentifierProto input_method_window = 6;
+  bool display_frozen = 7;
+  int32 rotation = 8;
+  int32 last_orientation = 9;
+  AppTransitionProto app_transition = 10;
+}
+
+/* represents PhoneWindowManager */
+message WindowManagerPolicyProto {
+  .android.graphics.RectProto stable_bounds = 1;
+}
+
+/* represents AppTransition */
+message AppTransitionProto {
+  enum AppState {
+    APP_STATE_IDLE = 0;
+    APP_STATE_READY = 1;
+    APP_STATE_RUNNING = 2;
+    APP_STATE_TIMEOUT = 3;
+  }
+  AppState app_transition_state = 1;
+  /* definitions for constants found in {@link com.android.server.wm.AppTransition} */
+  enum TransitionType {
+    TRANSIT_NONE = 0;
+    TRANSIT_UNSET = -1;
+    TRANSIT_ACTIVITY_OPEN = 6;
+    TRANSIT_ACTIVITY_CLOSE = 7;
+    TRANSIT_TASK_OPEN = 8;
+    TRANSIT_TASK_CLOSE = 9;
+    TRANSIT_TASK_TO_FRONT = 10;
+    TRANSIT_TASK_TO_BACK = 11;
+    TRANSIT_WALLPAPER_CLOSE = 12;
+    TRANSIT_WALLPAPER_OPEN = 13;
+    TRANSIT_WALLPAPER_INTRA_OPEN = 14;
+    TRANSIT_WALLPAPER_INTRA_CLOSE = 15;
+    TRANSIT_TASK_OPEN_BEHIND = 16;
+    TRANSIT_TASK_IN_PLACE = 17;
+    TRANSIT_ACTIVITY_RELAUNCH = 18;
+    TRANSIT_DOCK_TASK_FROM_RECENTS = 19;
+    TRANSIT_KEYGUARD_GOING_AWAY = 20;
+    TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER = 21;
+    TRANSIT_KEYGUARD_OCCLUDE = 22;
+    TRANSIT_KEYGUARD_UNOCCLUDE = 23;
+  }
+  TransitionType last_used_app_transition = 2;
+}
+
+/* represents DisplayContent */
+message DisplayProto {
+  int32 id = 1;
+  repeated StackProto stacks = 2;
+  DockedStackDividerControllerProto docked_stack_divider_controller = 3;
+  PinnedStackControllerProto pinned_stack_controller = 4;
+  /* non app windows */
+  repeated WindowTokenProto above_app_windows = 5;
+  repeated WindowTokenProto below_app_windows = 6;
+  repeated WindowTokenProto ime_windows = 7;
+  int32 dpi = 8;
+  .android.view.DisplayInfoProto display_info = 9;
+}
+
+
+/* represents DockedStackDividerController */
+message DockedStackDividerControllerProto {
+  bool minimized_dock = 1;
+}
+
+/* represents PinnedStackController */
+message PinnedStackControllerProto {
+  .android.graphics.RectProto default_bounds = 1;
+  .android.graphics.RectProto movement_bounds = 2;
+}
+
+/* represents TaskStack */
+message StackProto {
+  int32 id = 1;
+  repeated TaskProto tasks = 2;
+  bool fills_parent = 3;
+  .android.graphics.RectProto bounds = 4;
+  bool animation_background_surface_is_dimming = 5;
+}
+
+/* represents Task */
+message TaskProto {
+  int32 id = 1;
+  repeated AppWindowTokenProto app_window_tokens = 2;
+  bool fills_parent = 3;
+  .android.graphics.RectProto bounds = 4;
+  .android.graphics.RectProto temp_inset_bounds = 5;
+}
+
+/* represents AppWindowToken */
+message AppWindowTokenProto {
+  /* obtained from ActivityRecord */
+  string name = 1;
+  WindowTokenProto window_token = 2;
+}
+
+/* represents WindowToken */
+message WindowTokenProto {
+  int32 hash_code = 1;
+  repeated WindowStateProto windows = 2;
+}
+
+/* represents WindowState */
+message WindowStateProto {
+  IdentifierProto identifier = 1;
+  int32 display_id = 2;
+  int32 stack_id = 3;
+  .android.view.WindowLayoutParamsProto attributes = 4;
+  .android.graphics.RectProto given_content_insets = 5;
+  .android.graphics.RectProto frame = 6;
+  .android.graphics.RectProto containing_frame = 7;
+  .android.graphics.RectProto parent_frame = 8;
+  .android.graphics.RectProto content_frame = 9;
+  .android.graphics.RectProto content_insets = 10;
+  .android.graphics.RectProto surface_insets = 11;
+  WindowStateAnimatorProto animator = 12;
+  bool animating_exit = 13;
+  repeated WindowStateProto child_windows = 14;
+}
+
+message IdentifierProto {
+  int32 hash_code = 1;
+  int32 user_id = 2;
+  string title = 3;
+}
+
+/* represents WindowStateAnimator */
+message WindowStateAnimatorProto {
+  .android.graphics.RectProto last_clip_rect = 1;
+  WindowSurfaceControllerProto surface = 2;
+}
+
+/* represents WindowSurfaceController */
+message WindowSurfaceControllerProto {
+  bool shown = 1;
+  int32 layer = 2;
+}
\ No newline at end of file
diff --git a/core/proto/android/view/displayinfo.proto b/core/proto/android/view/displayinfo.proto
new file mode 100644
index 0000000..8583868
--- /dev/null
+++ b/core/proto/android/view/displayinfo.proto
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto3";
+
+package android.view;
+
+option java_multiple_files = true;
+
+/* represents DisplayInfo */
+message DisplayInfoProto {
+  int32 logical_width = 1;
+  int32 logical_height = 2;
+  int32 app_width = 3;
+  int32 app_height = 4;
+}
diff --git a/core/proto/android/view/windowlayoutparams.proto b/core/proto/android/view/windowlayoutparams.proto
new file mode 100644
index 0000000..5bb84dc
--- /dev/null
+++ b/core/proto/android/view/windowlayoutparams.proto
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto3";
+
+package android.view;
+
+option java_multiple_files = true;
+
+/* represents WindowManager.LayoutParams */
+message WindowLayoutParamsProto {
+  int32 type = 1;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 400c9e8..361aa08 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -775,7 +775,7 @@
         android:permissionGroup="android.permission-group.LOCATION"
         android:label="@string/permlab_accessFineLocation"
         android:description="@string/permdesc_accessFineLocation"
-        android:protectionLevel="dangerous|ephemeral" />
+        android:protectionLevel="dangerous|instant" />
 
     <!-- Allows an app to access approximate location.
          Alternatively, you might want {@link #ACCESS_FINE_LOCATION}.
@@ -785,7 +785,7 @@
         android:permissionGroup="android.permission-group.LOCATION"
         android:label="@string/permlab_accessCoarseLocation"
         android:description="@string/permdesc_accessCoarseLocation"
-        android:protectionLevel="dangerous|ephemeral" />
+        android:protectionLevel="dangerous|instant" />
 
     <!-- ====================================================================== -->
     <!-- Permissions for accessing the device telephony                         -->
@@ -820,13 +820,13 @@
         android:protectionLevel="dangerous" />
 
     <!-- Allows read access to the device's phone number(s). This is a subset of the capabilities
-         granted by {@link #READ_PHONE_STATE} but is exposed to ephemeral applications.
+         granted by {@link #READ_PHONE_STATE} but is exposed to instant applications.
          <p>Protection level: dangerous-->
     <permission android:name="android.permission.READ_PHONE_NUMBERS"
         android:permissionGroup="android.permission-group.PHONE"
         android:label="@string/permlab_readPhoneNumbers"
         android:description="@string/permdesc_readPhoneNumbers"
-        android:protectionLevel="dangerous|ephemeral" />
+        android:protectionLevel="dangerous|instant" />
 
     <!-- Allows an application to initiate a phone call without going through
         the Dialer user interface for the user to confirm the call.
@@ -1008,7 +1008,7 @@
         android:permissionGroup="android.permission-group.CAMERA"
         android:label="@string/permlab_camera"
         android:description="@string/permdesc_camera"
-        android:protectionLevel="dangerous|ephemeral" />
+        android:protectionLevel="dangerous|instant" />
 
 
     <!-- ====================================================================== -->
@@ -1251,7 +1251,7 @@
     <permission android:name="android.permission.INTERNET"
         android:description="@string/permdesc_createNetworkSockets"
         android:label="@string/permlab_createNetworkSockets"
-        android:protectionLevel="normal|ephemeral" />
+        android:protectionLevel="normal|instant" />
 
     <!-- Allows applications to access information about networks.
          <p>Protection level: normal
@@ -1259,7 +1259,7 @@
     <permission android:name="android.permission.ACCESS_NETWORK_STATE"
         android:description="@string/permdesc_accessNetworkState"
         android:label="@string/permlab_accessNetworkState"
-        android:protectionLevel="normal|ephemeral" />
+        android:protectionLevel="normal|instant" />
 
     <!-- Allows applications to access information about Wi-Fi networks.
          <p>Protection level: normal
@@ -1482,7 +1482,7 @@
     <permission android:name="android.permission.VIBRATE"
         android:label="@string/permlab_vibrate"
         android:description="@string/permdesc_vibrate"
-        android:protectionLevel="normal|ephemeral" />
+        android:protectionLevel="normal|instant" />
 
     <!-- Allows using PowerManager WakeLocks to keep processor from sleeping or screen
          from dimming.
@@ -1491,7 +1491,7 @@
     <permission android:name="android.permission.WAKE_LOCK"
         android:label="@string/permlab_wakeLock"
         android:description="@string/permdesc_wakeLock"
-        android:protectionLevel="normal|ephemeral" />
+        android:protectionLevel="normal|instant" />
 
     <!-- Allows using the device's IR transmitter, if available.
          <p>Protection level: normal
@@ -2390,7 +2390,8 @@
     <permission android:name="android.permission.UPDATE_DEVICE_STATS"
         android:protectionLevel="signature|privileged" />
 
-    <!-- @SystemApi @hide Allows an application to collect battery statistics -->
+    <!-- @SystemApi @hide Allows an application to collect application operation statistics.
+         Not for use by third party apps. -->
     <permission android:name="android.permission.GET_APP_OPS_STATS"
         android:protectionLevel="signature|privileged|development" />
 
@@ -3541,7 +3542,7 @@
 
     <!-- Allows an instant app to create foreground services. -->
     <permission android:name="android.permission.INSTANT_APP_FOREGROUND_SERVICE"
-        android:protectionLevel="signature|development|ephemeral|appop" />
+        android:protectionLevel="signature|development|instant|appop" />
 
     <application android:process="system"
                  android:persistent="true"
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 07a9a33..694a98b 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -242,9 +242,9 @@
         <!-- Additional flag from base permission type: this permission can be automatically
             granted to the setup wizard app -->
         <flag name="setup" value="0x800" />
-        <!-- Additional flag from base permission type: this permission can be granted to ephemeral
+        <!-- Additional flag from base permission type: this permission can be granted to instant
              apps -->
-        <flag name="ephemeral" value="0x1000" />
+        <flag name="instant" value="0x1000" />
         <!-- Additional flag from base permission type: this permission can only be granted to apps
              that target runtime permissions ({@link android.os.Build.VERSION_CODES#M} and above)
              -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 95aa264..f19a086 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2197,10 +2197,14 @@
     <string name="config_customAdbPublicKeyConfirmationSecondaryUserComponent"
             >com.android.systemui/com.android.systemui.usb.UsbDebuggingSecondaryUserActivity</string>
 
-    <!-- Name of the CustomDialog that is used for VPN -->
-    <string name="config_customVpnConfirmDialogComponent"
+    <!-- Name of the dialog that is used to request the user's consent to VPN connection -->
+    <string name="config_customVpnConfirmDialogComponent" translatable="false"
             >com.android.vpndialogs/com.android.vpndialogs.ConfirmDialog</string>
 
+    <!-- Name of the dialog that is used to inform the user that always-on VPN is disconnected -->
+    <string name="config_customVpnAlwaysOnDisconnectedDialogComponent" translatable="false"
+            >com.android.vpndialogs/com.android.vpndialogs.AlwaysOnDisconnectedDialog</string>
+
     <!-- Apps that are authorized to access shared accounts, overridden by product overlays -->
     <string name="config_appsAuthorizedForSharedAccounts" translatable="false">;com.android.settings;</string>
 
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 0ff24b7..4bec944 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3444,16 +3444,21 @@
     <!-- The text of the notification when VPN is active with a session name. -->
     <string name="vpn_text_long">Connected to <xliff:g id="session" example="office">%s</xliff:g>. Tap to manage the network.</string>
 
-    <!-- Notification title when connecting to lockdown VPN. -->
+    <!-- Notification title when connecting to always-on VPN, a VPN that's set to always stay
+         connected. -->
     <string name="vpn_lockdown_connecting">Always-on VPN connecting\u2026</string>
-    <!-- Notification title when connected to lockdown VPN. -->
+    <!-- Notification title when connected to always-on VPN, a VPN that's set to always stay
+         connected. -->
     <string name="vpn_lockdown_connected">Always-on VPN connected</string>
-    <!-- Notification title when not connected to lockdown VPN. -->
-    <string name="vpn_lockdown_disconnected">Always-on VPN disconnected</string>
-    <!-- Notification title when error connecting to lockdown VPN. -->
+    <!-- Notification title when not connected to always-on VPN, a VPN that's set to always stay
+         connected. -->
+    <string name="vpn_lockdown_disconnected">Disconnected from always-on VPN</string>
+    <!-- Notification title when error connecting to always-on VPN, a VPN that's set to always stay
+         connected. -->
     <string name="vpn_lockdown_error">Always-on VPN error</string>
-    <!-- Notification body that indicates user can touch to configure lockdown VPN connection. -->
-    <string name="vpn_lockdown_config">Tap to set up</string>
+    <!-- Notification body that indicates user can touch to configure always-on VPN, a VPN that's
+         set to always stay connected. -->
+    <string name="vpn_lockdown_config">Change network or VPN settings</string>
 
     <!-- Localized strings for WebView -->
     <!-- Label for button in a WebView that will open a chooser to choose a file to upload -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 326d31a..0fed465 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2002,6 +2002,7 @@
   <java-symbol type="string" name="config_customAdbPublicKeyConfirmationComponent" />
   <java-symbol type="string" name="config_customAdbPublicKeyConfirmationSecondaryUserComponent" />
   <java-symbol type="string" name="config_customVpnConfirmDialogComponent" />
+  <java-symbol type="string" name="config_customVpnAlwaysOnDisconnectedDialogComponent" />
   <java-symbol type="string" name="config_defaultNetworkScorerPackageName" />
   <java-symbol type="string" name="config_persistentDataPackageName" />
 
diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java
index deafb66..3dc928d 100644
--- a/graphics/java/android/graphics/Rect.java
+++ b/graphics/java/android/graphics/Rect.java
@@ -21,6 +21,7 @@
 import android.os.Parcelable;
 
 import android.text.TextUtils;
+import android.util.proto.ProtoOutputStream;
 import java.io.PrintWriter;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -194,7 +195,24 @@
         pw.print(top); pw.print("]["); pw.print(right);
         pw.print(','); pw.print(bottom); pw.print(']');
     }
-    
+
+    /**
+     * Write to a protocol buffer output stream.
+     * Protocol buffer message definition at {@link android.graphics.RectProto}
+     *
+     * @param protoOutputStream Stream to write the Rect object to.
+     * @param fieldId           Field Id of the Rect as defined in the parent message
+     * @hide
+     */
+    public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) {
+        final long token = protoOutputStream.start(fieldId);
+        protoOutputStream.write(RectProto.LEFT, left);
+        protoOutputStream.write(RectProto.TOP, top);
+        protoOutputStream.write(RectProto.RIGHT, right);
+        protoOutputStream.write(RectProto.BOTTOM, bottom);
+        protoOutputStream.end(token);
+    }
+
     /**
      * Returns true if the rectangle is empty (left >= right or top >= bottom)
      */
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index bc41810..8b03468 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -99,13 +99,22 @@
     }
 
     auto deviceInfo = DeviceInfo::get();
+    auto displayInfo = deviceInfo->displayInfo();
     int maxTextureSize = deviceInfo->maxTextureSize();
 
+    // Adjust cache size based on Pixel's desnsity.
+    constexpr float PIXEL_DENSITY = 2.6;
+    const float densityRatio = displayInfo.density / PIXEL_DENSITY;
+
     // TODO: Most devices are hardcoded with this configuration, does it need to be dynamic?
-    mSmallCacheWidth = std::min(1024, maxTextureSize);
-    mSmallCacheHeight = std::min(1024, maxTextureSize);
-    mLargeCacheWidth = std::min(2048, maxTextureSize);
-    mLargeCacheHeight = std::min(1024, maxTextureSize);
+    mSmallCacheWidth =
+            OffscreenBuffer::computeIdealDimension(std::min(1024, maxTextureSize) * densityRatio);
+    mSmallCacheHeight =
+            OffscreenBuffer::computeIdealDimension(std::min(1024, maxTextureSize) * densityRatio);
+    mLargeCacheWidth =
+            OffscreenBuffer::computeIdealDimension(std::min(2048, maxTextureSize) * densityRatio);
+    mLargeCacheHeight =
+            OffscreenBuffer::computeIdealDimension(std::min(1024, maxTextureSize) * densityRatio);
 
     if (sLogFontRendererCreate) {
         INIT_LOGD("  Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i",
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 4c84ad2..acc7539 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -176,7 +176,7 @@
         return sRenderPipelineType;
     }
     char prop[PROPERTY_VALUE_MAX];
-    property_get(PROPERTY_RENDERER, prop, "skiagl");
+    property_get(PROPERTY_RENDERER, prop, "opengl");
     if (!strcmp(prop, "skiagl") ) {
         ALOGD("Skia GL Pipeline");
         sRenderPipelineType = RenderPipelineType::SkiaGL;
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index bf18a7d..f85925d 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -2095,6 +2095,7 @@
                             errors |= ERROR_UNRECOGNIZED;
                     }
                     switch (profileLevel.profile) {
+                        case CodecProfileLevel.AVCProfileConstrainedHigh:
                         case CodecProfileLevel.AVCProfileHigh:
                             BR *= 1250; break;
                         case CodecProfileLevel.AVCProfileHigh10:
@@ -2107,6 +2108,7 @@
                             errors |= ERROR_UNSUPPORTED;
                             supported = false;
                             // fall through - treat as base profile
+                        case CodecProfileLevel.AVCProfileConstrainedBaseline:
                         case CodecProfileLevel.AVCProfileBaseline:
                         case CodecProfileLevel.AVCProfileMain:
                             BR *= 1000; break;
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index 48fb5bf..0f46096 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -703,37 +703,37 @@
     }
 
     /**
-     * Returns {@code true}, if {@code uri} is a channel URI.
+     * @return {@code true} if {@code uri} is a channel URI.
      */
-    public static boolean isChannelUri(Uri uri) {
+    public static boolean isChannelUri(@NonNull Uri uri) {
         return isChannelUriForTunerInput(uri) || isChannelUriForPassthroughInput(uri);
     }
 
     /**
-     * Returns {@code true}, if {@code uri} is a channel URI for a tuner input.
+     * @return {@code true} if {@code uri} is a channel URI for a tuner input.
      */
-    public static boolean isChannelUriForTunerInput(Uri uri) {
+    public static boolean isChannelUriForTunerInput(@NonNull Uri uri) {
         return isTvUri(uri) && isTwoSegmentUriStartingWith(uri, PATH_CHANNEL);
     }
 
     /**
-     * Returns {@code true}, if {@code uri} is a channel URI for a pass-through input.
+     * @return {@code true} if {@code uri} is a channel URI for a pass-through input.
      */
-    public static boolean isChannelUriForPassthroughInput(Uri uri) {
+    public static boolean isChannelUriForPassthroughInput(@NonNull Uri uri) {
         return isTvUri(uri) && isTwoSegmentUriStartingWith(uri, PATH_PASSTHROUGH);
     }
 
     /**
-     * Returns {@code true}, if {@code uri} is a program URI.
+     * @return {@code true} if {@code uri} is a program URI.
      */
-    public static boolean isProgramUri(Uri uri) {
+    public static boolean isProgramUri(@NonNull Uri uri) {
         return isTvUri(uri) && isTwoSegmentUriStartingWith(uri, PATH_PROGRAM);
     }
 
     /**
-     * Returns {@code true}, if {@code uri} is a recorded program URI.
+     * @return {@code true} if {@code uri} is a recorded program URI.
      */
-    public static boolean isRecordedProgramUri(Uri uri) {
+    public static boolean isRecordedProgramUri(@NonNull Uri uri) {
         return isTvUri(uri) && isTwoSegmentUriStartingWith(uri, PATH_RECORDED_PROGRAM);
     }
 
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
index ad46b60..d592c30 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
@@ -486,6 +486,12 @@
         loadingContent.layout(0, 0, loadingContent.getMeasuredWidth(),
                 loadingContent.getMeasuredHeight());
 
+        // To create a bitmap, height & width should be larger than 0
+        if (mPageContentHeight <= 0 || mPageContentWidth <= 0) {
+            Log.w(LOG_TAG, "Unable to create bitmap, height or width smaller than 0!");
+            return;
+        }
+
         Bitmap loadingBitmap = Bitmap.createBitmap(mPageContentWidth, mPageContentHeight,
                 Bitmap.Config.ARGB_8888);
         loadingContent.draw(new Canvas(loadingBitmap));
diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml
index db3274a..1f1b67e 100644
--- a/packages/SettingsLib/res/values/arrays.xml
+++ b/packages/SettingsLib/res/values/arrays.xml
@@ -121,8 +121,8 @@
         <item>Use System Selection (Default)</item>
         <item>SBC</item>
         <item>AAC</item>
-        <item>aptX</item>
-        <item>aptX HD</item>
+        <item><xliff:g id="aptx">Qualcomm(R) aptX(TM) audio</xliff:g></item>
+        <item><xliff:g id="aptx_hd">Qualcomm(R) aptX(TM) HD audio</xliff:g></item>
         <item>LDAC</item>
         <item>Enable Optional Codecs</item>
         <item>Disable Optional Codecs</item>
@@ -145,8 +145,8 @@
         <item>Use System Selection (Default)</item>
         <item>SBC</item>
         <item>AAC</item>
-        <item>aptX</item>
-        <item>aptX HD</item>
+        <item><xliff:g id="aptx">Qualcomm(R) aptX(TM) audio</xliff:g></item>
+        <item><xliff:g id="aptx_hd">Qualcomm(R) aptX(TM) HD audio</xliff:g></item>
         <item>LDAC</item>
         <item>Enable Optional Codecs</item>
         <item>Disable Optional Codecs</item>
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavBarButtonProvider.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavBarButtonProvider.java
index 5243228..1285ed8 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavBarButtonProvider.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavBarButtonProvider.java
@@ -27,7 +27,7 @@
 
     public static final String ACTION = "com.android.systemui.action.PLUGIN_NAV_BUTTON";
 
-    public static final int VERSION = 2;
+    public static final int VERSION = 3;
 
     /**
      * Returns a view in the nav bar.  If the id is set "back", "home", "recent_apps", "menu",
@@ -46,8 +46,6 @@
 
         void setVertical(boolean vertical);
 
-        void setCarMode(boolean carMode);
-
         void setDarkIntensity(float intensity);
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 669594b..bb6213b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1157,10 +1157,16 @@
         }
     }
 
+    private boolean shouldListenForFingerprintAssistant() {
+        return mAssistantVisible && mKeyguardOccluded
+                && !mUserFingerprintAuthenticated.get(getCurrentUser(), false)
+                && !mUserHasTrust.get(getCurrentUser(), false);
+    }
+
     private boolean shouldListenForFingerprint() {
         return (mKeyguardIsVisible || !mDeviceInteractive ||
                 (mBouncer && !mKeyguardGoingAway) || mGoingToSleep ||
-                (mAssistantVisible && mKeyguardOccluded))
+                shouldListenForFingerprintAssistant())
                 && !mSwitchingUser && !isFingerprintDisabled(getCurrentUser())
                 && !mKeyguardGoingAway;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/DockedStackExistsListener.java b/packages/SystemUI/src/com/android/systemui/DockedStackExistsListener.java
index 9fe730a..81e4db3 100644
--- a/packages/SystemUI/src/com/android/systemui/DockedStackExistsListener.java
+++ b/packages/SystemUI/src/com/android/systemui/DockedStackExistsListener.java
@@ -33,6 +33,7 @@
     private static final String TAG = "DockedStackExistsListener";
 
     private static ArrayList<WeakReference<Consumer<Boolean>>> sCallbacks = new ArrayList<>();
+    private static boolean mLastExists;
 
     static {
         try {
@@ -73,6 +74,7 @@
 
 
     private static void onDockedStackExistsChanged(boolean exists) {
+        mLastExists = exists;
         synchronized (sCallbacks) {
             sCallbacks.removeIf(wf -> {
                 Consumer<Boolean> l = wf.get();
@@ -83,6 +85,7 @@
     }
 
     public static void register(Consumer<Boolean> callback) {
+        callback.accept(mLastExists);
         synchronized (sCallbacks) {
             sCallbacks.add(new WeakReference<>(callback));
         }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 9b48320..56a3c87 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -31,7 +31,6 @@
 import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
-import android.graphics.RectF;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.util.Log;
@@ -47,10 +46,8 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.policy.PipSnapAlgorithm;
-import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.FlingAnimationUtils;
-import com.android.systemui.tuner.TunerService;
 
 import java.io.PrintWriter;
 
@@ -90,6 +87,8 @@
     // The current movement bounds
     private Rect mMovementBounds = new Rect();
 
+    // The reference inset bounds, used to determine the dismiss fraction
+    private Rect mInsetBounds = new Rect();
     // The reference bounds used to calculate the normal/expanded target bounds
     private Rect mNormalBounds = new Rect();
     private Rect mNormalMovementBounds = new Rect();
@@ -311,6 +310,7 @@
         mNormalMovementBounds = normalMovementBounds;
         mExpandedMovementBounds = expandedMovementBounds;
         mDisplayRotation = displayRotation;
+        mInsetBounds.set(insetBounds);
         updateMovementBounds(mMenuState);
 
         // If we have a deferred resize, apply it now
@@ -418,9 +418,12 @@
      * Updates the appearance of the menu and scrim on top of the PiP while dismissing.
      */
     private void updateDismissFraction() {
-        if (mMenuController != null) {
+        // Skip updating the dismiss fraction when the IME is showing. This is to work around an
+        // issue where starting the menu activity for the dismiss overlay will steal the window
+        // focus, which closes the IME.
+        if (mMenuController != null && !mIsImeShowing) {
             Rect bounds = mMotionHelper.getBounds();
-            final float target = mMovementBounds.bottom + bounds.height();
+            final float target = mInsetBounds.bottom;
             float fraction = 0f;
             if (bounds.bottom > target) {
                 final float distance = bounds.bottom - target;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 585b151..562210c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -69,6 +69,7 @@
 import com.android.systemui.recents.events.component.ActivityUnpinnedEvent;
 import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
 import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
+import com.android.systemui.recents.events.component.SetWaitingForTransitionStartEvent;
 import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
 import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
 import com.android.systemui.recents.events.ui.HideIncompatibleAppOverlayEvent;
@@ -517,6 +518,11 @@
     public void onEnterAnimationComplete() {
         super.onEnterAnimationComplete();
         EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
+
+        // Workaround for b/64694148: The animation started callback is not made (see
+        // RecentsImpl.getThumbnailTransitionActivityOptions) so reset the transition-waiting state
+        // once the enter animation has completed.
+        EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(false));
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
index 2bc2665..a83e659 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
@@ -174,16 +174,6 @@
         mCurrentView = currentView.findViewById(mId);
     }
 
-    public void setCarMode(boolean carMode) {
-        final int N = mViews.size();
-        for (int i = 0; i < N; i++) {
-            final View view = mViews.get(i);
-            if (view instanceof ButtonInterface) {
-                ((ButtonInterface) view).setCarMode(carMode);
-            }
-        }
-    }
-
     public void setVertical(boolean vertical) {
         mVertical = vertical;
         final int N = mViews.size();
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 57c8827..9a7039a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -683,8 +683,6 @@
 
             if (isCarMode != mInCarMode) {
                 mInCarMode = isCarMode;
-                getHomeButton().setCarMode(isCarMode);
-
                 if (ALTERNATE_CAR_MODE_UI) {
                     mUseCarModeUi = isCarMode;
                     uiCarModeChanged = true;
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 59e7005..65bfb49 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -429,7 +429,7 @@
     private int mStatusBarWindowState = WINDOW_STATE_SHOWING;
     protected StatusBarWindowManager mStatusBarWindowManager;
     protected UnlockMethodCache mUnlockMethodCache;
-    private DozeServiceHost mDozeServiceHost;
+    private DozeServiceHost mDozeServiceHost = new DozeServiceHost();
     private boolean mWakeUpComingFromTouch;
     private PointF mWakeUpTouchLocation;
 
@@ -733,7 +733,8 @@
     private KeyguardUserSwitcher mKeyguardUserSwitcher;
     private UserSwitcherController mUserSwitcherController;
     private NetworkController mNetworkController;
-    private KeyguardMonitorImpl mKeyguardMonitor;
+    private KeyguardMonitorImpl mKeyguardMonitor
+            = (KeyguardMonitorImpl) Dependency.get(KeyguardMonitor.class);
     private BatteryController mBatteryController;
     protected boolean mPanelExpanded;
     private IOverlayManager mOverlayManager;
@@ -741,7 +742,7 @@
     private boolean mIsKeyguard;
     private LogMaker mStatusBarStateLog;
     private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
-    private NotificationIconAreaController mNotificationIconAreaController;
+    protected NotificationIconAreaController mNotificationIconAreaController;
     private boolean mReinflateNotificationsOnUserSwitched;
     private HashMap<String, Entry> mPendingNotifications = new HashMap<>();
     private boolean mClearAllEnabled;
@@ -788,14 +789,12 @@
     public void start() {
         mNetworkController = Dependency.get(NetworkController.class);
         mUserSwitcherController = Dependency.get(UserSwitcherController.class);
-        mKeyguardMonitor = (KeyguardMonitorImpl) Dependency.get(KeyguardMonitor.class);
         mScreenLifecycle = Dependency.get(ScreenLifecycle.class);
         mScreenLifecycle.addObserver(mScreenObserver);
         mWakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class);
         mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
         mBatteryController = Dependency.get(BatteryController.class);
         mAssistManager = Dependency.get(AssistManager.class);
-        mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
         mSystemServicesProxy = SystemServicesProxy.getInstance(mContext);
         mOverlayManager = IOverlayManager.Stub.asInterface(
                 ServiceManager.getService(Context.OVERLAY_SERVICE));
@@ -981,7 +980,6 @@
         startKeyguard();
 
         KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateCallback);
-        mDozeServiceHost = new DozeServiceHost();
         putComponent(DozeHost.class, mDozeServiceHost);
 
         notifyUserAboutHiddenNotifications();
@@ -1328,7 +1326,7 @@
         // Clock and bottom icons
         mNotificationPanel.onOverlayChanged();
         // The status bar on the keyguard is a special layout.
-        mKeyguardStatusBar.onOverlayChanged();
+        if (mKeyguardStatusBar != null) mKeyguardStatusBar.onOverlayChanged();
         // Recreate Indication controller because internal references changed
         mKeyguardIndicationController =
                 SystemUIFactory.getInstance().createKeyguardIndicationController(mContext,
@@ -1373,7 +1371,7 @@
     }
 
     private void inflateSignalClusters() {
-        reinflateSignalCluster(mKeyguardStatusBar);
+        if (mKeyguardStatusBar != null) reinflateSignalCluster(mKeyguardStatusBar);
     }
 
     public static SignalClusterView reinflateSignalCluster(View view) {
@@ -4565,7 +4563,7 @@
             if (mKeyguardUserSwitcher != null) {
                 mKeyguardUserSwitcher.setKeyguard(true, fromShadeLocked);
             }
-            mStatusBarView.removePendingHideExpandedRunnables();
+            if (mStatusBarView != null) mStatusBarView.removePendingHideExpandedRunnables();
             if (mAmbientIndicationContainer != null) {
                 mAmbientIndicationContainer.setVisibility(View.VISIBLE);
             }
@@ -4603,7 +4601,7 @@
     /**
      * Switches theme from light to dark and vice-versa.
      */
-    private void updateTheme() {
+    protected void updateTheme() {
         final boolean inflated = mStackScroller != null;
 
         // The system wallpaper defines if QS should be light or dark.
@@ -5673,7 +5671,8 @@
 
     protected KeyguardManager mKeyguardManager;
     private LockPatternUtils mLockPatternUtils;
-    private DeviceProvisionedController mDeviceProvisionedController;
+    private DeviceProvisionedController mDeviceProvisionedController
+            = Dependency.get(DeviceProvisionedController.class);
     protected SystemServicesProxy mSystemServicesProxy;
 
     // UI-specific methods
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 65bfabd..0501771 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -304,11 +304,6 @@
     public void setVertical(boolean vertical) {
         //no op
     }
-
-    @Override
-    public void setCarMode(boolean carMode) {
-        // no op
-    }
 }
 
 
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenRadioLayout.java b/packages/SystemUI/src/com/android/systemui/volume/ZenRadioLayout.java
index 5dbcd8a..360907b 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenRadioLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenRadioLayout.java
@@ -46,9 +46,16 @@
             throw new IllegalStateException("Expected matching children");
         }
         boolean hasChanges = false;
+        View lastView = null;
         for (int i = 0; i < size; i++) {
             View radio = radioGroup.getChildAt(i);
             View content = radioContent.getChildAt(i);
+            if (lastView != null) {
+                radio.setAccessibilityTraversalAfter(lastView.getId());
+            }
+            View contentClick = findFirstClickable(content);
+            if (contentClick != null) contentClick.setAccessibilityTraversalAfter(radio.getId());
+            lastView = findLastClickable(content);
             if (radio.getLayoutParams().height != content.getMeasuredHeight()) {
                 hasChanges = true;
                 radio.getLayoutParams().height = content.getMeasuredHeight();
@@ -59,4 +66,28 @@
             super.onMeasure(widthMeasureSpec, heightMeasureSpec);
         }
     }
+
+    private View findFirstClickable(View content) {
+        if (content.isClickable()) return content;
+        if (content instanceof ViewGroup) {
+            ViewGroup group = (ViewGroup) content;
+            for (int i = 0; i < group.getChildCount(); i++) {
+                View v = findFirstClickable(group.getChildAt(i));
+                if (v != null) return v;
+            }
+        }
+        return null;
+    }
+
+    private View findLastClickable(View content) {
+        if (content.isClickable()) return content;
+        if (content instanceof ViewGroup) {
+            ViewGroup group = (ViewGroup) content;
+            for (int i = group.getChildCount() - 1; i >= 0; i--) {
+                View v = findLastClickable(group.getChildAt(i));
+                if (v != null) return v;
+            }
+        }
+        return null;
+    }
 }
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index adb3baf..67fae5b 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -42,6 +42,7 @@
     <uses-permission android:name="android.permission.TRUST_LISTENER" />
     <uses-permission android:name="android.permission.USE_FINGERPRINT" />
     <uses-permission android:name="android.permission.DEVICE_POWER" />
+    <uses-permission android:name="android.permission.READ_CONTACTS" />
 
     <application>
         <uses-library android:name="android.test.runner" />
diff --git a/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java b/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
index d0f0bfd..6417eb7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
@@ -17,6 +17,7 @@
 package com.android.systemui.colorextraction;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
 
 import android.app.WallpaperColors;
 import android.app.WallpaperManager;
@@ -25,7 +26,6 @@
 import android.support.test.runner.AndroidJUnit4;
 
 import com.android.internal.colorextraction.ColorExtractor;
-import com.android.internal.colorextraction.types.Tonal;
 import com.android.systemui.SysuiTestCase;
 
 import org.junit.Test;
@@ -48,18 +48,21 @@
 
     @Test
     public void getColors_usesGreyIfWallpaperNotVisible() {
-        SysuiColorExtractor extractor = new SysuiColorExtractor(getContext(),
-                new Tonal(getContext()), false);
+        ColorExtractor.GradientColors colors = new ColorExtractor.GradientColors();
+        colors.setMainColor(Color.RED);
+        colors.setSecondaryColor(Color.RED);
+
+        SysuiColorExtractor extractor = getTestableExtractor(colors);
         simulateEvent(extractor);
         extractor.setWallpaperVisible(false);
 
         ColorExtractor.GradientColors fallbackColors = extractor.getFallbackColors();
 
-        for (int which : sWhich) {
-            for (int type : sTypes) {
-                assertEquals("Not using fallback!", extractor.getColors(which, type),
-                        fallbackColors);
-            }
+        for (int type : sTypes) {
+            assertEquals("Not using fallback!",
+                    extractor.getColors(WallpaperManager.FLAG_SYSTEM, type), fallbackColors);
+            assertNotEquals("Wallpaper visibility event should not affect lock wallpaper.",
+                    extractor.getColors(WallpaperManager.FLAG_LOCK, type), fallbackColors);
         }
     }
 
@@ -69,13 +72,7 @@
         colors.setMainColor(Color.RED);
         colors.setSecondaryColor(Color.RED);
 
-        SysuiColorExtractor extractor = new SysuiColorExtractor(getContext(),
-                (inWallpaperColors, outGradientColorsNormal, outGradientColorsDark,
-                        outGradientColorsExtraDark) -> {
-                    outGradientColorsNormal.set(colors);
-                    outGradientColorsDark.set(colors);
-                    outGradientColorsExtraDark.set(colors);
-                }, false);
+        SysuiColorExtractor extractor = getTestableExtractor(colors);
         simulateEvent(extractor);
         extractor.setWallpaperVisible(true);
 
@@ -87,6 +84,16 @@
         }
     }
 
+    private SysuiColorExtractor getTestableExtractor(ColorExtractor.GradientColors colors) {
+        return new SysuiColorExtractor(getContext(),
+                (inWallpaperColors, outGradientColorsNormal, outGradientColorsDark,
+                        outGradientColorsExtraDark) -> {
+                    outGradientColorsNormal.set(colors);
+                    outGradientColorsDark.set(colors);
+                    outGradientColorsExtraDark.set(colors);
+                }, false);
+    }
+
     private void simulateEvent(SysuiColorExtractor extractor) {
         // Let's fake a color event
         extractor.onColorsChanged(new WallpaperColors(Color.valueOf(Color.GREEN), null, null, 0),
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 a706368..ac367d2 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
@@ -26,21 +26,19 @@
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.when;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.app.Notification;
 import android.app.trust.TrustManager;
+import android.content.Context;
 import android.hardware.fingerprint.FingerprintManager;
 import android.metrics.LogMaker;
+import android.os.Binder;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IPowerManager;
@@ -51,20 +49,25 @@
 import android.service.notification.StatusBarNotification;
 import android.support.test.filters.SmallTest;
 import android.support.test.metricshelper.MetricsAsserts;
-import android.support.test.runner.AndroidJUnit4;
 import android.testing.AndroidTestingRunner;
+import android.testing.LayoutInflaterBuilder;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.MessageHandler;
 import android.testing.TestableLooper.RunWithLooper;
 import android.util.DisplayMetrics;
+import android.view.View;
 import android.view.ViewGroup.LayoutParams;
+import android.widget.FrameLayout;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.logging.testing.FakeMetricsLogger;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.keyguard.KeyguardHostView.OnDismissAction;
+import com.android.keyguard.KeyguardStatusView;
+import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+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;
@@ -73,7 +76,11 @@
 import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.NotificationData.Entry;
 import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.policy.DateView;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 
 import org.junit.Before;
@@ -106,12 +113,20 @@
 
     @Before
     public void setup() throws Exception {
+        mContext.setTheme(R.style.Theme_SystemUI_Light);
+        mDependency.injectMockDependency(AssistManager.class);
+        mDependency.injectMockDependency(DeviceProvisionedController.class);
+        mDependency.injectTestDependency(KeyguardMonitor.class, mock(KeyguardMonitorImpl.class));
+        CommandQueue commandQueue = mock(CommandQueue.class);
+        when(commandQueue.asBinder()).thenReturn(new Binder());
+        mContext.putComponent(CommandQueue.class, commandQueue);
         mContext.addMockSystemService(TrustManager.class, mock(TrustManager.class));
         mContext.addMockSystemService(FingerprintManager.class, mock(FingerprintManager.class));
         mStatusBarKeyguardViewManager = mock(StatusBarKeyguardViewManager.class);
         mUnlockMethodCache = mock(UnlockMethodCache.class);
         mKeyguardIndicationController = mock(KeyguardIndicationController.class);
         mStackScroller = mock(NotificationStackScrollLayout.class);
+        when(mStackScroller.generateLayoutParams(any())).thenReturn(new LayoutParams(0, 0));
         mMetricsLogger = new FakeMetricsLogger();
         mHeadsUpManager = mock(HeadsUpManager.class);
         mNotificationData = mock(NotificationData.class);
@@ -133,6 +148,7 @@
                 mNotificationData, mPowerManager, mSystemServicesProxy, mNotificationPanelView,
                 mBarService);
         mStatusBar.mContext = mContext;
+        mStatusBar.mComponents = mContext.getComponents();
         doAnswer(invocation -> {
             OnDismissAction onDismissAction = (OnDismissAction) invocation.getArguments()[0];
             onDismissAction.onDismiss();
@@ -484,6 +500,16 @@
         mStatusBar.dump(null, new PrintWriter(new ByteArrayOutputStream()), null);
     }
 
+    @Test
+    @RunWithLooper(setAsMainLooper = true)
+    public void testUpdateKeyguardState_DoesNotCrash() {
+        mStatusBar.mStatusBarWindow = mock(StatusBarWindowView.class);
+        mStatusBar.mState = StatusBarState.KEYGUARD;
+        mStatusBar.mDozeScrimController = mock(DozeScrimController.class);
+        mStatusBar.mNotificationIconAreaController = mock(NotificationIconAreaController.class);
+        mStatusBar.updateKeyguardState(false, false);
+    }
+
     static class TestableStatusBar extends StatusBar {
         public TestableStatusBar(StatusBarKeyguardViewManager man,
                 UnlockMethodCache unlock, KeyguardIndicationController key,
@@ -502,6 +528,7 @@
             mNotificationPanel = panelView;
             mBarService = barService;
             mWakefulnessLifecycle = createAwakeWakefulnessLifecycle();
+            mScrimController = mock(ScrimController.class);
         }
 
         private WakefulnessLifecycle createAwakeWakefulnessLifecycle() {
@@ -511,6 +538,11 @@
             return wakefulnessLifecycle;
         }
 
+        @Override
+        protected void updateTheme() {
+            // Do nothing for now, until we have more mocking and StatusBar is smaller.
+        }
+
         public void setBarStateForTest(int state) {
             mState = state;
         }
diff --git a/packages/VpnDialogs/AndroidManifest.xml b/packages/VpnDialogs/AndroidManifest.xml
index a3d27ce..8172e71 100644
--- a/packages/VpnDialogs/AndroidManifest.xml
+++ b/packages/VpnDialogs/AndroidManifest.xml
@@ -23,9 +23,10 @@
     <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
 
     <application android:label="VpnDialogs"
-            android:allowBackup="false" >
+                 android:allowBackup="false">
+
         <activity android:name=".ConfirmDialog"
-                android:theme="@android:style/Theme.Material.Light.Dialog.Alert">
+                  android:theme="@android:style/Theme.Material.Light.Dialog.Alert">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
                 <category android:name="android.intent.category.DEFAULT"/>
@@ -33,12 +34,21 @@
         </activity>
 
         <activity android:name=".ManageDialog"
-                android:theme="@android:style/Theme.Material.Light.Dialog.Alert"
-                android:noHistory="true">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN"/>
-                <category android:name="android.intent.category.DEFAULT"/>
-            </intent-filter>
+                  android:theme="@android:style/Theme.Material.Light.Dialog.Alert"
+                  android:noHistory="true"
+                  android:excludeFromRecents="true"
+                  android:permission="android.permission.NETWORK_SETTINGS"
+                  android:exported="true">
         </activity>
+
+        <activity android:name=".AlwaysOnDisconnectedDialog"
+                  android:label="@string/always_on_disconnected_title"
+                  android:theme="@android:style/Theme.Material.Light.Dialog.Alert"
+                  android:noHistory="true"
+                  android:excludeFromRecents="true"
+                  android:permission="android.permission.NETWORK_SETTINGS"
+                  android:exported="true">
+        </activity>
+
     </application>
 </manifest>
diff --git a/packages/VpnDialogs/res/layout/always_on_disconnected.xml b/packages/VpnDialogs/res/layout/always_on_disconnected.xml
new file mode 100644
index 0000000..0f4a46d
--- /dev/null
+++ b/packages/VpnDialogs/res/layout/always_on_disconnected.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:padding="24dp">
+    <TextView android:id="@+id/message"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"/>
+</ScrollView>
diff --git a/packages/VpnDialogs/res/values/strings.xml b/packages/VpnDialogs/res/values/strings.xml
index 406bcc3..443a9bc 100644
--- a/packages/VpnDialogs/res/values/strings.xml
+++ b/packages/VpnDialogs/res/values/strings.xml
@@ -18,7 +18,6 @@
 
     <!-- Dialog title to identify the request from a VPN application. [CHAR LIMIT=60] -->
     <string name="prompt">Connection request</string>
-
     <!-- Dialog message to warn about the risk of using a VPN application. [CHAR LIMIT=NONE] -->
     <string name="warning"><xliff:g id="app">%s</xliff:g> wants to set up a VPN connection
         that allows it to monitor network traffic. Only accept if you trust the source.
@@ -31,11 +30,6 @@
 
     <!-- Dialog title for built-in VPN. [CHAR LIMIT=40]  -->
     <string name="legacy_title">VPN is connected</string>
-    <!-- Button label to configure the current VPN session. [CHAR LIMIT=20] -->
-    <string name="configure">Configure</string>
-    <!-- Button label to disconnect the current VPN session. [CHAR LIMIT=20] -->
-    <string name="disconnect">Disconnect</string>
-
     <!-- Label for the name of the current VPN session. [CHAR LIMIT=20] -->
     <string name="session">Session:</string>
     <!-- Label for the duration of the current VPN session. [CHAR LIMIT=20] -->
@@ -44,10 +38,55 @@
     <string name="data_transmitted">Sent:</string>
     <!-- Label for the network usage of data received over VPN. [CHAR LIMIT=20] -->
     <string name="data_received">Received:</string>
-
     <!-- Formatted string for the network usage over VPN. [CHAR LIMIT=40] -->
     <string name="data_value_format">
         <xliff:g id="number">%1$s</xliff:g> bytes /
         <xliff:g id="number">%2$s</xliff:g> packets
     </string>
+
+    <!-- This string is the title of a dialog. The dialog shows up for Android users when always-on
+     VPN, a VPN that's set to always stay connected, loses its connection. [CHAR LIMIT=60] -->
+    <string name="always_on_disconnected_title">Can\'t connect to always-on VPN</string>
+    <!-- This message is part of a dialog. The dialog shows up for users when always-on VPN, a VPN
+         that's set to always stay connected, loses its connection. Until the phone can reconnect to
+         the VPN, it'll automatically connect to a public network if possible. This text is followed
+         by a clickable link that leads to VPN settings. [CHAR LIMIT=NONE] -->
+    <string name="always_on_disconnected_message">
+        <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g> is set up to stay connected all
+        the time, but it can\'t connect right now. Your phone will use a public network until it can
+        reconnect to <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g>.
+    </string>
+    <!-- This message is part of a dialog. The dialog shows up for users when always-on VPN, a VPN
+         that's set to always stay connected, loses its connection while in the lockdown mode.
+         Until the phone can reconnect to the VPN, it won't be able to connect to the Internet. This
+         text is followed by a clickable link that leads to VPN settings. [CHAR LIMIT=NONE] -->
+    <string name="always_on_disconnected_message_lockdown">
+        <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g> is set up to stay connected all
+        the time, but it can\'t connect right now. You won\'t have a connection until the VPN can
+        reconnect.
+    </string>
+    <!-- This is a space separating the body text and the "Change VPN settings" link that follows
+         it. [CHAR LIMIT=5] -->
+    <string name="always_on_disconnected_message_separator">" "</string>
+    <!-- This is a clickable link appended at the end of the body text of a dialog. The dialog shows
+         up for users when always-on VPN, a VPN that's set to always stay connected, loses its
+         connection. This link takes the user to the VPN page in Settings. -->
+    <string name="always_on_disconnected_message_settings_link">Change VPN settings</string>
+
+    <!-- This is the label of a button in a dialog. The button takes the user to the VPN settings
+         screen. [CHAR LIMIT=20] -->
+    <string name="configure">Configure</string>
+    <!-- This is the label of a button in a dialog. The button lets the user disconnect from the
+         current VPN connection. [CHAR LIMIT=20] -->
+    <string name="disconnect">Disconnect</string>
+    <!-- This button is part of a dialog, and it opens the user's VPN app. The dialog shows up for
+         users when always-on VPN, a VPN that's set to always stay connected, loses its connection.
+         Until the phone can reconnect to the VPN, it may automatically connect to a public network.
+         If it doesn't, the user won't have a connection until the VPN reconnects. [CHAR LIMIT=20]
+         -->
+    <string name="open_app">Open app</string>
+    <!-- This is the label of a button in a dialog. The button lets the user dismiss the dialog
+         without any consequences. [CHAR LIMIT=20] -->
+    <string name="dismiss">Dismiss</string>
+
 </resources>
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java
new file mode 100644
index 0000000..846fcf8
--- /dev/null
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2017 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.vpndialogs;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.IConnectivityManager;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.SpannableStringBuilder;
+import android.text.method.LinkMovementMethod;
+import android.text.style.ClickableSpan;
+import android.util.Log;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.TextView;
+
+import com.android.internal.app.AlertActivity;
+import com.android.internal.net.VpnConfig;
+
+public class AlwaysOnDisconnectedDialog extends AlertActivity
+        implements DialogInterface.OnClickListener{
+
+    private static final String TAG = "VpnDisconnected";
+
+    private IConnectivityManager mService;
+    private int mUserId;
+    private String mVpnPackage;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mService = IConnectivityManager.Stub.asInterface(
+                ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
+        mUserId = UserHandle.myUserId();
+        mVpnPackage = getAlwaysOnVpnPackage();
+        if (mVpnPackage == null) {
+            finish();
+            return;
+        }
+
+        View view = View.inflate(this, R.layout.always_on_disconnected, null);
+        TextView messageView = view.findViewById(R.id.message);
+        messageView.setText(getMessage(getIntent().getBooleanExtra("lockdown", false)));
+        messageView.setMovementMethod(LinkMovementMethod.getInstance());
+
+        mAlertParams.mTitle = getString(R.string.always_on_disconnected_title);
+        mAlertParams.mPositiveButtonText = getString(R.string.open_app);
+        mAlertParams.mPositiveButtonListener = this;
+        mAlertParams.mNegativeButtonText = getString(R.string.dismiss);
+        mAlertParams.mNegativeButtonListener = this;
+        mAlertParams.mCancelable = false;
+        mAlertParams.mView = view;
+        setupAlert();
+
+        getWindow().setCloseOnTouchOutside(false);
+        getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
+    }
+
+    @Override
+    public void onClick(DialogInterface dialog, int which) {
+        switch (which) {
+            case BUTTON_POSITIVE:
+                PackageManager pm = getPackageManager();
+                final Intent intent = pm.getLaunchIntentForPackage(mVpnPackage);
+                if (intent != null) {
+                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+                    startActivity(intent);
+                }
+                finish();
+                break;
+            case BUTTON_NEGATIVE:
+                finish();
+                break;
+            default:
+                break;
+        }
+    }
+
+    private String getAlwaysOnVpnPackage() {
+        try {
+            return mService.getAlwaysOnVpnPackage(mUserId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Can't getAlwaysOnVpnPackage()", e);
+            return null;
+        }
+    }
+
+    private CharSequence getVpnLabel() {
+        try {
+            return VpnConfig.getVpnLabel(this, mVpnPackage);
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.w(TAG, "Can't getVpnLabel() for " + mVpnPackage, e);
+            return mVpnPackage;
+        }
+    }
+
+    private CharSequence getMessage(boolean isLockdown) {
+        final SpannableStringBuilder message = new SpannableStringBuilder();
+        final int baseMessageResId = isLockdown
+                ? R.string.always_on_disconnected_message_lockdown
+                : R.string.always_on_disconnected_message;
+        message.append(getString(baseMessageResId, getVpnLabel()));
+        message.append(getString(R.string.always_on_disconnected_message_separator));
+        message.append(getString(R.string.always_on_disconnected_message_settings_link),
+                new VpnSpan(), 0 /*flags*/);
+        return message;
+    }
+
+    private class VpnSpan extends ClickableSpan {
+        @Override
+        public void onClick(View unused) {
+            final Intent intent = new Intent(Settings.ACTION_VPN_SETTINGS);
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+            startActivity(intent);
+        }
+    }
+}
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
index 2fe6648..01dca7e 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
@@ -54,12 +54,6 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        if (getCallingPackage() != null) {
-            Log.e(TAG, getCallingPackage() + " cannot start this activity");
-            finish();
-            return;
-        }
-
         try {
 
             mService = IConnectivityManager.Stub.asInterface(
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index a59844d..1859d35 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -2394,6 +2394,7 @@
         public static final int MSG_SEND_RELEVANT_EVENTS_CHANGED_TO_CLIENTS = 12;
         public static final int MSG_SEND_ACCESSIBILITY_BUTTON_TO_INPUT_FILTER = 13;
         public static final int MSG_SHOW_ACCESSIBILITY_BUTTON_CHOOSER = 14;
+        public static final int MSG_INIT_SERVICE = 15;
 
         public MainHandler(Looper looper) {
             super(looper);
@@ -2492,6 +2493,11 @@
                 case MSG_SHOW_ACCESSIBILITY_BUTTON_CHOOSER: {
                     showAccessibilityButtonTargetSelection();
                 } break;
+
+                case MSG_INIT_SERVICE: {
+                    final Service service = (Service) msg.obj;
+                    service.initializeService();
+                } break;
             }
         }
 
@@ -2947,20 +2953,31 @@
                 if (userState.mBindingServices.contains(mComponentName) || mWasConnectedAndDied) {
                     userState.mBindingServices.remove(mComponentName);
                     mWasConnectedAndDied = false;
-                    try {
-                       mServiceInterface.init(this, mId, mOverlayWindowToken);
-                       onUserStateChangedLocked(userState);
-                    } catch (RemoteException re) {
-                        Slog.w(LOG_TAG, "Error while setting connection for service: "
-                                + service, re);
-                        binderDied();
-                    }
+                    onUserStateChangedLocked(userState);
+                    // Initialize the service on the main handler after we're done setting up for
+                    // the new configuration (for example, initializing the input filter).
+                    mMainHandler.obtainMessage(MainHandler.MSG_INIT_SERVICE, this).sendToTarget();
                 } else {
                     binderDied();
                 }
             }
         }
 
+        private void initializeService() {
+            final IAccessibilityServiceClient serviceInterface;
+            synchronized (mLock) {
+                serviceInterface = mServiceInterface;
+            }
+            if (serviceInterface == null) return;
+            try {
+                serviceInterface.init(this, mId, mOverlayWindowToken);
+            } catch (RemoteException re) {
+                Slog.w(LOG_TAG, "Error while setting connection for service: "
+                        + serviceInterface, re);
+                binderDied();
+            }
+        }
+
         private boolean isCalledForCurrentUserLocked() {
             // We treat calls from a profile as if made by its parent as profiles
             // share the accessibility state of the parent. The call below
@@ -3310,8 +3327,8 @@
                     }
                     if (mMotionEventInjector != null) {
                         List<GestureDescription.GestureStep> steps = gestureSteps.getList();
-                         mMotionEventInjector.injectEvents(steps, mServiceInterface, sequence);
-                         return;
+                        mMotionEventInjector.injectEvents(steps, mServiceInterface, sequence);
+                        return;
                     } else {
                         Slog.e(LOG_TAG, "MotionEventInjector installation timed out");
                     }
@@ -3450,18 +3467,15 @@
                     return region;
                 }
                 MagnificationController magnificationController = getMagnificationController();
-                boolean forceRegistration = mSecurityPolicy.canControlMagnification(this);
-                boolean initiallyRegistered = magnificationController.isRegisteredLocked();
-                if (!initiallyRegistered && forceRegistration) {
-                    magnificationController.register();
-                }
+                boolean registeredJustForThisCall =
+                        registerMagnificationIfNeeded(magnificationController);
                 final long identity = Binder.clearCallingIdentity();
                 try {
                     magnificationController.getMagnificationRegion(region);
                     return region;
                 } finally {
                     Binder.restoreCallingIdentity(identity);
-                    if (!initiallyRegistered && forceRegistration) {
+                    if (registeredJustForThisCall) {
                         magnificationController.unregister();
                     }
                 }
@@ -3475,11 +3489,17 @@
                     return 0.0f;
                 }
             }
+            MagnificationController magnificationController = getMagnificationController();
+            boolean registeredJustForThisCall =
+                    registerMagnificationIfNeeded(magnificationController);
             final long identity = Binder.clearCallingIdentity();
             try {
-                return getMagnificationController().getCenterX();
+                return magnificationController.getCenterX();
             } finally {
                 Binder.restoreCallingIdentity(identity);
+                if (registeredJustForThisCall) {
+                    magnificationController.unregister();
+                }
             }
         }
 
@@ -3490,14 +3510,30 @@
                     return 0.0f;
                 }
             }
+            MagnificationController magnificationController = getMagnificationController();
+            boolean registeredJustForThisCall =
+                    registerMagnificationIfNeeded(magnificationController);
             final long identity = Binder.clearCallingIdentity();
             try {
-                return getMagnificationController().getCenterY();
+                return magnificationController.getCenterY();
             } finally {
                 Binder.restoreCallingIdentity(identity);
+                if (registeredJustForThisCall) {
+                    magnificationController.unregister();
+                }
             }
         }
 
+        private boolean registerMagnificationIfNeeded(
+                MagnificationController magnificationController) {
+            if (!magnificationController.isRegisteredLocked()
+                    && mSecurityPolicy.canControlMagnification(this)) {
+                magnificationController.register();
+                return true;
+            }
+            return false;
+        }
+
         @Override
         public boolean resetMagnification(boolean animate) {
             synchronized (mLock) {
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 71f699c..e854079 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -655,6 +655,19 @@
         }
 
         @Override
+        public void dismissUi() {
+            final UserHandle user = getCallingUserHandle();
+
+            synchronized (mLock) {
+                final AutofillManagerServiceImpl service = peekServiceForUserLocked(
+                        user.getIdentifier());
+                if (service != null) {
+                    service.dismissUi();
+                }
+            }
+        }
+
+        @Override
         public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
 
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 751c054..841b1a5 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -41,7 +41,6 @@
 import android.os.Looper;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
-import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
 import android.service.autofill.AutofillService;
@@ -635,6 +634,12 @@
         }
     }
 
+    void dismissUi() {
+        if (sVerbose) Slog.v(TAG, "dismissUi()");
+
+        mUi.hideAll(null);
+    }
+
     private void sendStateToClients(boolean resetClient) {
         final RemoteCallbackList<IAutoFillManagerClient> clients;
         final int userClientCount;
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 13aca87..796b425 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -1567,6 +1567,17 @@
             } catch (NativeDaemonConnectorException e) {
                 throw e.rethrowAsParcelableException();
             }
+
+            synchronized (mTetheringStatsProviders) {
+                for (ITetheringStatsProvider provider : mTetheringStatsProviders.keySet()) {
+                    try {
+                        provider.setInterfaceQuota(iface, quotaBytes);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Problem setting tethering data limit on provider " +
+                                mTetheringStatsProviders.get(provider) + ": " + e);
+                    }
+                }
+            }
         }
     }
 
@@ -1593,6 +1604,17 @@
             } catch (NativeDaemonConnectorException e) {
                 throw e.rethrowAsParcelableException();
             }
+
+            synchronized (mTetheringStatsProviders) {
+                for (ITetheringStatsProvider provider : mTetheringStatsProviders.keySet()) {
+                    try {
+                        provider.setInterfaceQuota(iface, ITetheringStatsProvider.QUOTA_UNLIMITED);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Problem removing tethering data limit on provider " +
+                                mTetheringStatsProviders.get(provider) + ": " + e);
+                    }
+                }
+            }
         }
     }
 
@@ -1864,6 +1886,11 @@
             }
             return stats;
         }
+
+        @Override
+        public void setInterfaceQuota(String iface, long quotaBytes) {
+            // Do nothing. netd is already informed of quota changes in setInterfaceQuota.
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index c6f2fc0..7e90c92 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -5330,25 +5330,25 @@
         long identityToken = Binder.clearCallingIdentity();
         try {
             packages = mPackageManager.getPackagesForUid(callingUid);
-        } finally {
-            Binder.restoreCallingIdentity(identityToken);
-        }
-        if (packages == null) {
-            Log.d(TAG, "No packages for callingUid " + callingUid);
-            return false;
-        }
-        for (String name : packages) {
-            try {
-                PackageInfo packageInfo = mPackageManager.getPackageInfo(name, 0 /* flags */);
-                if (packageInfo != null
-                        && (packageInfo.applicationInfo.privateFlags
-                                & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
-                    return true;
-                }
-            } catch (PackageManager.NameNotFoundException e) {
-                Log.d(TAG, "Package not found " + e.getMessage());
+            if (packages == null) {
+                Log.d(TAG, "No packages for callingUid " + callingUid);
                 return false;
             }
+            for (String name : packages) {
+                try {
+                    PackageInfo packageInfo =
+                        mPackageManager.getPackageInfo(name, 0 /* flags */);
+                    if (packageInfo != null
+                        && (packageInfo.applicationInfo.privateFlags
+                            & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
+                        return true;
+                    }
+                } catch (PackageManager.NameNotFoundException e) {
+                    Log.d(TAG, "Package not found " + e.getMessage());
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identityToken);
         }
         return false;
     }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 90df649..81d6fd0 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1346,7 +1346,7 @@
      * Set while we are running a voice interaction.  This overrides
      * sleeping while it is active.
      */
-    private IVoiceInteractionSession mRunningVoice;
+    IVoiceInteractionSession mRunningVoice;
 
     /**
      * For some direct access we need to power manager.
@@ -1366,13 +1366,6 @@
     private int mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
 
     /**
-     * A list of tokens that cause the top activity to be put to sleep.
-     * They are used by components that may hide and block interaction with underlying
-     * activities.
-     */
-    final ArrayList<SleepToken> mSleepTokens = new ArrayList<SleepToken>();
-
-    /**
      * Set if we are shutting down the system, similar to sleeping.
      */
     boolean mShuttingDown = false;
@@ -12395,7 +12388,19 @@
     void onWakefulnessChanged(int wakefulness) {
         synchronized(this) {
             mWakefulness = wakefulness;
-            updateSleepIfNeededLocked();
+
+            // Also update state in a special way for running foreground services UI.
+            switch (mWakefulness) {
+                case PowerManagerInternal.WAKEFULNESS_ASLEEP:
+                case PowerManagerInternal.WAKEFULNESS_DREAMING:
+                case PowerManagerInternal.WAKEFULNESS_DOZING:
+                    mServices.updateScreenStateLocked(false /* screenOn */);
+                    break;
+                case PowerManagerInternal.WAKEFULNESS_AWAKE:
+                default:
+                    mServices.updateScreenStateLocked(true /* screenOn */);
+                    break;
+            }
         }
     }
 
@@ -12415,14 +12420,24 @@
     }
 
     void updateSleepIfNeededLocked() {
-        final boolean shouldSleep = shouldSleepLocked();
-        if (mSleeping && !shouldSleep) {
-            mSleeping = false;
-            startTimeTrackingFocusedActivityLocked();
-            mTopProcessState = ActivityManager.PROCESS_STATE_TOP;
-            mStackSupervisor.comeOutOfSleepIfNeededLocked();
-            sendNotifyVrManagerOfSleepState(false);
-            updateOomAdjLocked();
+        final boolean shouldSleep = !mStackSupervisor.hasAwakeDisplay();
+        final boolean wasSleeping = mSleeping;
+
+        if (!shouldSleep) {
+            // If wasSleeping is true, we need to wake up activity manager state from when
+            // we started sleeping. In either case, we need to apply the sleep tokens, which
+            // will wake up stacks or put them to sleep as appropriate.
+            if (wasSleeping) {
+                mSleeping = false;
+                startTimeTrackingFocusedActivityLocked();
+                mTopProcessState = ActivityManager.PROCESS_STATE_TOP;
+                mStackSupervisor.comeOutOfSleepIfNeededLocked();
+            }
+            mStackSupervisor.applySleepTokensLocked(true /* applyToStacks */);
+            if (wasSleeping) {
+                sendNotifyVrManagerOfSleepState(false);
+                updateOomAdjLocked();
+            }
         } else if (!mSleeping && shouldSleep) {
             mSleeping = true;
             if (mCurAppTimeTracker != null) {
@@ -12433,40 +12448,6 @@
             sendNotifyVrManagerOfSleepState(true);
             updateOomAdjLocked();
         }
-
-        // Also update state in a special way for running foreground services UI.
-        switch (mWakefulness) {
-            case PowerManagerInternal.WAKEFULNESS_ASLEEP:
-            case PowerManagerInternal.WAKEFULNESS_DREAMING:
-            case PowerManagerInternal.WAKEFULNESS_DOZING:
-                mServices.updateScreenStateLocked(false);
-                break;
-            case PowerManagerInternal.WAKEFULNESS_AWAKE:
-            default:
-                mServices.updateScreenStateLocked(true);
-                break;
-        }
-    }
-
-    private boolean shouldSleepLocked() {
-        // Resume applications while running a voice interactor.
-        if (mRunningVoice != null) {
-            return false;
-        }
-
-        // TODO: Transform the lock screen state into a sleep token instead.
-        switch (mWakefulness) {
-            case PowerManagerInternal.WAKEFULNESS_AWAKE:
-            case PowerManagerInternal.WAKEFULNESS_DREAMING:
-                // Pause applications whenever the lock screen is shown or any sleep
-                // tokens have been acquired.
-                return mKeyguardController.isKeyguardShowing() || !mSleepTokens.isEmpty();
-            case PowerManagerInternal.WAKEFULNESS_DOZING:
-            case PowerManagerInternal.WAKEFULNESS_ASLEEP:
-            default:
-                // If we're asleep then pause applications unconditionally.
-                return true;
-        }
     }
 
     /** Pokes the task persister. */
@@ -12507,6 +12488,7 @@
 
         synchronized(this) {
             mShuttingDown = true;
+            mStackSupervisor.prepareForShutdownLocked();
             updateEventDispatchingLocked();
             timedout = mStackSupervisor.shutdownLocked(timeout);
         }
@@ -14921,6 +14903,14 @@
                 this, in, out, err, args, callback, resultReceiver);
     }
 
+    SleepToken acquireSleepToken(String tag, int displayId) {
+        synchronized (this) {
+            final SleepToken token = mStackSupervisor.createSleepTokenLocked(tag, displayId);
+            updateSleepIfNeededLocked();
+            return token;
+        }
+    }
+
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return;
@@ -15817,7 +15807,7 @@
         if (dumpPackage == null) {
             pw.println("  mWakefulness="
                     + PowerManagerInternal.wakefulnessToString(mWakefulness));
-            pw.println("  mSleepTokens=" + mSleepTokens);
+            pw.println("  mSleepTokens=" + mStackSupervisor.mSleepTokens);
             pw.println("  mSleeping=" + mSleeping);
             pw.println("  mShuttingDown=" + mShuttingDown + " mTestPssMode=" + mTestPssMode);
             if (mRunningVoice != null) {
@@ -23778,15 +23768,9 @@
         }
 
         @Override
-        public SleepToken acquireSleepToken(String tag) {
+        public SleepToken acquireSleepToken(String tag, int displayId) {
             Preconditions.checkNotNull(tag);
-
-            synchronized (ActivityManagerService.this) {
-                SleepTokenImpl token = new SleepTokenImpl(tag);
-                mSleepTokens.add(token);
-                updateSleepIfNeededLocked();
-                return token;
-            }
+            return ActivityManagerService.this.acquireSleepToken(tag, displayId);
         }
 
         @Override
@@ -24223,30 +24207,6 @@
         }
     }
 
-    private final class SleepTokenImpl extends SleepToken {
-        private final String mTag;
-        private final long mAcquireTime;
-
-        public SleepTokenImpl(String tag) {
-            mTag = tag;
-            mAcquireTime = SystemClock.uptimeMillis();
-        }
-
-        @Override
-        public void release() {
-            synchronized (ActivityManagerService.this) {
-                if (mSleepTokens.remove(this)) {
-                    updateSleepIfNeededLocked();
-                }
-            }
-        }
-
-        @Override
-        public String toString() {
-            return "{\"" + mTag + "\", acquire at " + TimeUtils.formatUptime(mAcquireTime) + "}";
-        }
-    }
-
     /**
      * An implementation of IAppTask, that allows an app to manage its own tasks via
      * {@link android.app.ActivityManager.AppTask}.  We keep track of the callingUid to ensure that
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index df70a93..e0bcbe7 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -749,9 +749,11 @@
 
     static class Token extends IApplicationToken.Stub {
         private final WeakReference<ActivityRecord> weakActivity;
+        private final String name;
 
-        Token(ActivityRecord activity) {
+        Token(ActivityRecord activity, Intent intent) {
             weakActivity = new WeakReference<>(activity);
+            name = intent.getComponent().flattenToShortString();
         }
 
         private static ActivityRecord tokenToActivityRecordLocked(Token token) {
@@ -775,6 +777,11 @@
             sb.append('}');
             return sb.toString();
         }
+
+        @Override
+        public String getName() {
+            return name;
+        }
     }
 
     static ActivityRecord forTokenLocked(IBinder token) {
@@ -798,7 +805,7 @@
             ActivityStackSupervisor supervisor, ActivityOptions options,
             ActivityRecord sourceRecord) {
         service = _service;
-        appToken = new Token(this);
+        appToken = new Token(this, _intent);
         info = aInfo;
         launchedFromPid = _launchedFromPid;
         launchedFromUid = _launchedFromUid;
@@ -1341,7 +1348,9 @@
                 intent, getUriPermissionsLocked(), userId);
         final ReferrerIntent rintent = new ReferrerIntent(intent, referrer);
         boolean unsent = true;
-        final boolean isTopActivityWhileSleeping = service.isSleepingLocked() && isTopRunningActivity();
+        final ActivityStack stack = getStack();
+        final boolean isTopActivityWhileSleeping = isTopRunningActivity()
+                && (stack != null ? stack.shouldSleepActivities() : service.isSleepingLocked());
 
         // We want to immediately deliver the intent to the activity if:
         // - It is currently resumed or paused. i.e. it is currently visible to the user and we want
@@ -1731,7 +1740,7 @@
             // If the screen is going to turn on because the caller explicitly requested it and
             // the keyguard is not showing don't attempt to sleep. Otherwise the Activity will
             // pause and then resume again later, which will result in a double life-cycle event.
-            mStackSupervisor.checkReadyForSleepLocked();
+            stack.checkReadyForSleep();
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 5f8e418..e9f3608 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1171,10 +1171,25 @@
         }
     }
 
+    void checkReadyForSleep() {
+        if (shouldSleepActivities() && goToSleepIfPossible(false /* shuttingDown */)) {
+            mStackSupervisor.checkReadyForSleepLocked(true /* allowDelay */);
+        }
+    }
+
     /**
+     * Tries to put the activities in the stack to sleep.
+     *
+     * If the stack is not in a state where its activities can be put to sleep, this function will
+     * start any necessary actions to move the stack into such a state. It is expected that this
+     * function get called again when those actions complete.
+     *
+     * @param shuttingDown true when the called because the device is shutting down.
      * @return true if something must be done before going to sleep.
      */
-    boolean checkReadyForSleepLocked() {
+    boolean goToSleepIfPossible(boolean shuttingDown) {
+        boolean shouldSleep = true;
+
         if (mResumedActivity != null) {
             // Still have something resumed; can't sleep until it is paused.
             if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Sleep needs to pause " + mResumedActivity);
@@ -1184,26 +1199,47 @@
             // If we are in the middle of resuming the top activity in
             // {@link #resumeTopActivityUncheckedLocked}, mResumedActivity will be set but not
             // resumed yet. We must not proceed pausing the activity here. This method will be
-            // called again if necessary as part of
+            // called again if necessary as part of {@link #checkReadyForSleep} or
             // {@link ActivityStackSupervisor#checkReadyForSleepLocked}.
             if (mStackSupervisor.inResumeTopActivity) {
                 if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "In the middle of resuming top activity "
                         + mResumedActivity);
-                return true;
+            } else {
+                startPausingLocked(false, true, null, false);
             }
-
-            startPausingLocked(false, true, null, false);
-            return true;
-        }
-        if (mPausingActivity != null) {
+            shouldSleep = false ;
+        } else if (mPausingActivity != null) {
             // Still waiting for something to pause; can't sleep yet.
             if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Sleep still waiting to pause " + mPausingActivity);
-            return true;
+            shouldSleep = false;
         }
-        return false;
+
+        if (!shuttingDown) {
+            if (containsActivityFromStack(mStackSupervisor.mStoppingActivities)) {
+                // Still need to tell some activities to stop; can't sleep yet.
+                if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Sleep still need to stop "
+                        + mStackSupervisor.mStoppingActivities.size() + " activities");
+
+                mStackSupervisor.scheduleIdleLocked();
+                shouldSleep = false;
+            }
+
+            if (containsActivityFromStack(mStackSupervisor.mGoingToSleepActivities)) {
+                // Still need to tell some activities to sleep; can't sleep yet.
+                if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Sleep still need to sleep "
+                        + mStackSupervisor.mGoingToSleepActivities.size() + " activities");
+                shouldSleep = false;
+            }
+        }
+
+        if (shouldSleep) {
+            goToSleep();
+        }
+
+        return !shouldSleep;
     }
 
-    void goToSleep() {
+    private void goToSleep() {
         ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
 
         // Make sure any paused or stopped but visible activities are now sleeping.
@@ -1220,6 +1256,15 @@
         }
     }
 
+    private boolean containsActivityFromStack(List<ActivityRecord> rs) {
+        for (ActivityRecord r : rs) {
+            if (r.getStack() == this) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     /**
      * Schedule a pause timeout in case the app doesn't respond. We don't give it much time because
      * this directly impacts the responsiveness seen by the user.
@@ -1252,7 +1297,7 @@
         if (mPausingActivity != null) {
             Slog.wtf(TAG, "Going to pause when pause is already pending for " + mPausingActivity
                     + " state=" + mPausingActivity.state);
-            if (!mService.isSleepingLocked()) {
+            if (!shouldSleepActivities()) {
                 // Avoid recursion among check for sleep and complete pause during sleeping.
                 // Because activity will be paused immediately after resume, just let pause
                 // be completed by the order of activity paused from clients.
@@ -1412,7 +1457,7 @@
                     // We can't clobber it, because the stop confirmation will not be handled.
                     // We don't need to schedule another stop, we only need to let it happen.
                     prev.state = STOPPING;
-                } else if (!prev.visible || mService.isSleepingOrShuttingDownLocked()) {
+                } else if (!prev.visible || shouldSleepOrShutDownActivities()) {
                     // Clear out any deferred client hide we might currently have.
                     prev.setDeferHidingClient(false);
                     // If we were visible then resumeTopActivities will release resources before
@@ -1434,10 +1479,10 @@
 
         if (resumeNext) {
             final ActivityStack topStack = mStackSupervisor.getFocusedStack();
-            if (!mService.isSleepingOrShuttingDownLocked()) {
+            if (!topStack.shouldSleepOrShutDownActivities()) {
                 mStackSupervisor.resumeFocusedStackTopActivityLocked(topStack, prev, null);
             } else {
-                mStackSupervisor.checkReadyForSleepLocked();
+                checkReadyForSleep();
                 ActivityRecord top = topStack.topRunningActivityLocked();
                 if (top == null || (prev != null && top != prev)) {
                     // If there are no more activities available to run, do resume anyway to start
@@ -1503,7 +1548,7 @@
                 mStackSupervisor.scheduleIdleTimeoutLocked(r);
             }
         } else {
-            mStackSupervisor.checkReadyForSleepLocked();
+            checkReadyForSleep();
         }
     }
 
@@ -2212,7 +2257,7 @@
         // is skipped.
         final ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */);
         if (next == null || !next.canTurnScreenOn()) {
-            mStackSupervisor.checkReadyForSleepLocked();
+            checkReadyForSleep();
         }
 
         return result;
@@ -2297,7 +2342,7 @@
 
         // If we are sleeping, and there is no resumed activity, and the top
         // activity is paused, well that is the state we want.
-        if (mService.isSleepingOrShuttingDownLocked()
+        if (shouldSleepOrShutDownActivities()
                 && mLastPausedActivity == next
                 && mStackSupervisor.allPausedActivitiesComplete()) {
             // Make sure we have executed any pending transitions, since there
@@ -2389,7 +2434,7 @@
         // If the most recent activity was noHistory but was only stopped rather
         // than stopped+finished because the device went to sleep, we need to make
         // sure to finish it as we're making a new activity topmost.
-        if (mService.isSleepingLocked() && mLastNoHistoryActivity != null &&
+        if (shouldSleepActivities() && mLastNoHistoryActivity != null &&
                 !mLastNoHistoryActivity.finishing) {
             if (DEBUG_STATES) Slog.d(TAG_STATES,
                     "no-history finish of " + mLastNoHistoryActivity + " on new resume");
@@ -3401,7 +3446,7 @@
         if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
                 || (r.info.flags&ActivityInfo.FLAG_NO_HISTORY) != 0) {
             if (!r.finishing) {
-                if (!mService.isSleepingLocked()) {
+                if (!shouldSleepActivities()) {
                     if (DEBUG_STATES) Slog.d(TAG_STATES, "no-history finish of " + r);
                     if (requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null,
                             "stop-no-history", false)) {
@@ -3433,7 +3478,7 @@
                 EventLogTags.writeAmStopActivity(
                         r.userId, System.identityHashCode(r), r.shortComponentName);
                 r.app.thread.scheduleStopActivity(r.appToken, r.visible, r.configChangeFlags);
-                if (mService.isSleepingOrShuttingDownLocked()) {
+                if (shouldSleepOrShutDownActivities()) {
                     r.setSleeping(true);
                 }
                 Message msg = mHandler.obtainMessage(STOP_TIMEOUT_MSG, r);
@@ -5279,4 +5324,13 @@
         mNoAnimActivities.clear();
         ActivityOptions.abort(options);
     }
+
+    boolean shouldSleepActivities() {
+        final ActivityStackSupervisor.ActivityDisplay display = getDisplay();
+        return display != null ? display.isSleeping() : mService.isSleepingLocked();
+    }
+
+    boolean shouldSleepOrShutDownActivities() {
+        return shouldSleepActivities() || mService.isShuttingDownLocked();
+    }
 }
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 23a4cac..1568e8c 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -105,6 +105,7 @@
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityManager.StackId;
 import android.app.ActivityManager.StackInfo;
+import android.app.ActivityManagerInternal.SleepToken;
 import android.app.ActivityOptions;
 import android.app.AppOpsManager;
 import android.app.ProfilerInfo;
@@ -155,6 +156,7 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
+import android.util.TimeUtils;
 import android.view.Display;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -177,6 +179,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
 
@@ -375,9 +378,6 @@
      * is being brought in front of us. */
     boolean mUserLeaving = false;
 
-    /** Set when we have taken too long waiting to go to sleep. */
-    boolean mSleepTimeout = false;
-
     /**
      * We don't want to allow the device to go to sleep while in the process
      * of launching an activity.  This is primarily to allow alarm intent
@@ -393,6 +393,13 @@
      */
     PowerManager.WakeLock mGoingToSleep;
 
+    /**
+     * A list of tokens that cause the top activity to be put to sleep.
+     * They are used by components that may hide and block interaction with underlying
+     * activities.
+     */
+    final ArrayList<SleepToken> mSleepTokens = new ArrayList<SleepToken>();
+
     /** Stack id of the front stack when user switched, indexed by userId. */
     SparseIntArray mUserStackInFront = new SparseIntArray(2);
 
@@ -3126,6 +3133,16 @@
         return null;
     }
 
+    boolean hasAwakeDisplay() {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
+            if (!display.shouldSleep()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     void goingToSleepLocked() {
         scheduleSleepTimeout();
         if (!mGoingToSleep.isHeld()) {
@@ -3138,7 +3155,16 @@
                 mService.mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
             }
         }
-        checkReadyForSleepLocked();
+
+        applySleepTokensLocked(false /* applyToStacks */);
+
+        checkReadyForSleepLocked(true /* allowDelay */);
+    }
+
+    void prepareForShutdownLocked() {
+        for (int i = 0; i < mActivityDisplays.size(); i++) {
+            createSleepTokenLocked("shutdown", mActivityDisplays.keyAt(i));
+        }
     }
 
     boolean shutdownLocked(int timeout) {
@@ -3151,7 +3177,8 @@
             for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
                 final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
                 for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
-                    cantShutdown |= stacks.get(stackNdx).checkReadyForSleepLocked();
+                    cantShutdown |=
+                            stacks.get(stackNdx).goToSleepIfPossible(true /* shuttingDown */);
                 }
             }
             if (cantShutdown) {
@@ -3172,8 +3199,7 @@
         }
 
         // Force checkReadyForSleep to complete.
-        mSleepTimeout = true;
-        checkReadyForSleepLocked();
+        checkReadyForSleepLocked(false /* allowDelay */);
 
         return timedout;
     }
@@ -3183,54 +3209,75 @@
         if (mGoingToSleep.isHeld()) {
             mGoingToSleep.release();
         }
+    }
+
+    void applySleepTokensLocked(boolean applyToStacks) {
         for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+            // Set the sleeping state of the display.
+            final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
+            final boolean displayShouldSleep = display.shouldSleep();
+            if (displayShouldSleep == display.isSleeping()) {
+                continue;
+            }
+            display.setIsSleeping(displayShouldSleep);
+
+            if (!applyToStacks) {
+                continue;
+            }
+
+            // Set the sleeping state of the stacks on the display.
+            final ArrayList<ActivityStack> stacks = display.mStacks;
             for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
                 final ActivityStack stack = stacks.get(stackNdx);
-                stack.awakeFromSleepingLocked();
-                if (isFocusedStack(stack)) {
-                    resumeFocusedStackTopActivityLocked();
+                if (displayShouldSleep) {
+                    stack.goToSleepIfPossible(false /* shuttingDown */);
+                } else {
+                    stack.awakeFromSleepingLocked();
+                    if (isFocusedStack(stack)) {
+                        resumeFocusedStackTopActivityLocked();
+                    }
+                }
+            }
+
+            if (displayShouldSleep || mGoingToSleepActivities.isEmpty()) {
+                continue;
+            }
+            // The display is awake now, so clean up the going to sleep list.
+            for (Iterator<ActivityRecord> it = mGoingToSleepActivities.iterator(); it.hasNext(); ) {
+                final ActivityRecord r = it.next();
+                if (r.getDisplayId() == display.mDisplayId) {
+                    it.remove();
                 }
             }
         }
-        mGoingToSleepActivities.clear();
     }
 
     void activitySleptLocked(ActivityRecord r) {
         mGoingToSleepActivities.remove(r);
-        checkReadyForSleepLocked();
+        final ActivityStack s = r.getStack();
+        if (s != null) {
+            s.checkReadyForSleep();
+        } else {
+            checkReadyForSleepLocked(true);
+        }
     }
 
-    void checkReadyForSleepLocked() {
+    void checkReadyForSleepLocked(boolean allowDelay) {
         if (!mService.isSleepingOrShuttingDownLocked()) {
             // Do not care.
             return;
         }
 
-        if (!mSleepTimeout) {
+        if (allowDelay) {
             boolean dontSleep = false;
             for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-                final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+                final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
+                final ArrayList<ActivityStack> stacks = display.mStacks;
                 for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
-                    dontSleep |= stacks.get(stackNdx).checkReadyForSleepLocked();
+                    dontSleep |= stacks.get(stackNdx).goToSleepIfPossible(false /* shuttingDown */);
                 }
             }
 
-            if (mStoppingActivities.size() > 0) {
-                // Still need to tell some activities to stop; can't sleep yet.
-                if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Sleep still need to stop "
-                        + mStoppingActivities.size() + " activities");
-                scheduleIdleLocked();
-                dontSleep = true;
-            }
-
-            if (mGoingToSleepActivities.size() > 0) {
-                // Still need to tell some activities to sleep; can't sleep yet.
-                if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Sleep still need to sleep "
-                        + mGoingToSleepActivities.size() + " activities");
-                dontSleep = true;
-            }
-
             if (dontSleep) {
                 return;
             }
@@ -3239,13 +3286,6 @@
         // Send launch end powerhint before going sleep
         mService.mActivityStarter.sendPowerHintForLaunchEndIfNeeded();
 
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
-            for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
-                stacks.get(stackNdx).goToSleep();
-            }
-        }
-
         removeSleepTimeouts();
 
         if (mGoingToSleep.isHeld()) {
@@ -3516,21 +3556,27 @@
                     s.setVisibility(false);
                 }
             }
-            if ((!waitingVisible || mService.isSleepingOrShuttingDownLocked()) && remove) {
-                if (!processPausingActivities && s.state == PAUSING) {
-                    // Defer processing pausing activities in this iteration and reschedule
-                    // a delayed idle to reprocess it again
-                    removeTimeoutsForActivityLocked(idleActivity);
-                    scheduleIdleTimeoutLocked(idleActivity);
-                    continue;
-                }
+            if (remove) {
+                final ActivityStack stack = s.getStack();
+                final boolean shouldSleepOrShutDown = stack != null
+                        ? stack.shouldSleepOrShutDownActivities()
+                        : mService.isSleepingOrShuttingDownLocked();
+                if (!waitingVisible || shouldSleepOrShutDown) {
+                    if (!processPausingActivities && s.state == PAUSING) {
+                        // Defer processing pausing activities in this iteration and reschedule
+                        // a delayed idle to reprocess it again
+                        removeTimeoutsForActivityLocked(idleActivity);
+                        scheduleIdleTimeoutLocked(idleActivity);
+                        continue;
+                    }
 
-                if (DEBUG_STATES) Slog.v(TAG, "Ready to stop: " + s);
-                if (stops == null) {
-                    stops = new ArrayList<>();
+                    if (DEBUG_STATES) Slog.v(TAG, "Ready to stop: " + s);
+                    if (stops == null) {
+                        stops = new ArrayList<>();
+                    }
+                    stops.add(s);
+                    mStoppingActivities.remove(activityNdx);
                 }
-                stops.add(s);
-                mStoppingActivities.remove(activityNdx);
             }
         }
 
@@ -3583,7 +3629,6 @@
     public void dump(PrintWriter pw, String prefix) {
         pw.print(prefix); pw.print("mFocusedStack=" + mFocusedStack);
                 pw.print(" mLastFocusedStack="); pw.println(mLastFocusedStack);
-        pw.print(prefix); pw.println("mSleepTimeout=" + mSleepTimeout);
         pw.print(prefix);
         pw.println("mCurTaskIdForUser=" + mCurTaskIdForUser);
         pw.print(prefix); pw.println("mUserStackInFront=" + mUserStackInFront);
@@ -3680,6 +3725,8 @@
                 stackHeader.append("\n");
                 stackHeader.append("  mFullscreen=" + stack.mFullscreen);
                 stackHeader.append("\n");
+                stackHeader.append("  isSleeping=" + stack.shouldSleepActivities());
+                stackHeader.append("\n");
                 stackHeader.append("  mBounds=" + stack.mBounds);
 
                 final boolean printedStackHeader = stack.dumpActivitiesLocked(fd, pw, dumpAll,
@@ -3731,8 +3778,6 @@
                 "  Activities waiting for another to become visible:", null);
         printed |= dumpHistoryList(fd, pw, mGoingToSleepActivities, "  ", "Sleep", false, !dumpAll,
                 false, dumpPackage, true, "  Activities waiting to sleep:", null);
-        printed |= dumpHistoryList(fd, pw, mGoingToSleepActivities, "  ", "Sleep", false, !dumpAll,
-                false, dumpPackage, true, "  Activities waiting to sleep:", null);
 
         return printed;
     }
@@ -3843,7 +3888,6 @@
     }
 
     void removeSleepTimeouts() {
-        mSleepTimeout = false;
         mHandler.removeMessages(SLEEP_TIMEOUT_MSG);
     }
 
@@ -3945,6 +3989,9 @@
                         moveTasksToFullscreenStackLocked(stack.getStackId(), true /* onTop */);
                     }
                 }
+
+                releaseSleepTokens(activityDisplay);
+
                 mActivityDisplays.remove(displayId);
                 mWindowManager.onDisplayRemoved(displayId);
             }
@@ -3955,12 +4002,60 @@
         synchronized (mService) {
             ActivityDisplay activityDisplay = mActivityDisplays.get(displayId);
             if (activityDisplay != null) {
+                // The window policy is responsible for stopping activities on the default display
+                if (displayId != Display.DEFAULT_DISPLAY) {
+                    int displayState = activityDisplay.mDisplay.getState();
+                    if (displayState == Display.STATE_OFF && activityDisplay.mOffToken == null) {
+                        activityDisplay.mOffToken =
+                                mService.acquireSleepToken("Display-off", displayId);
+                    } else if (displayState == Display.STATE_ON
+                            && activityDisplay.mOffToken != null) {
+                        activityDisplay.mOffToken.release();
+                        activityDisplay.mOffToken = null;
+                    }
+                }
                 // TODO: Update the bounds.
             }
             mWindowManager.onDisplayChanged(displayId);
         }
     }
 
+    SleepToken createSleepTokenLocked(String tag, int displayId) {
+        ActivityDisplay display = mActivityDisplays.get(displayId);
+        if (display == null) {
+            throw new IllegalArgumentException("Invalid display: " + displayId);
+        }
+
+        final SleepTokenImpl token = new SleepTokenImpl(tag, displayId);
+        mSleepTokens.add(token);
+        display.mAllSleepTokens.add(token);
+        return token;
+    }
+
+    private void removeSleepTokenLocked(SleepTokenImpl token) {
+        mSleepTokens.remove(token);
+
+        ActivityDisplay display = mActivityDisplays.get(token.mDisplayId);
+        if (display != null) {
+            display.mAllSleepTokens.remove(token);
+            if (display.mAllSleepTokens.isEmpty()) {
+                mService.updateSleepIfNeededLocked();
+            }
+        }
+    }
+
+    private void releaseSleepTokens(ActivityDisplay display) {
+        if (display.mAllSleepTokens.isEmpty()) {
+            return;
+        }
+        for (SleepTokenImpl token : display.mAllSleepTokens) {
+            mSleepTokens.remove(token);
+        }
+        display.mAllSleepTokens.clear();
+
+        mService.updateSleepIfNeededLocked();
+    }
+
     private StackInfo getStackInfoLocked(ActivityStack stack) {
         final int displayId = stack.mDisplayId;
         final ActivityDisplay display = mActivityDisplays.get(displayId);
@@ -4266,9 +4361,9 @@
 
     void activityRelaunchedLocked(IBinder token) {
         mWindowManager.notifyAppRelaunchingFinished(token);
-        if (mService.isSleepingOrShuttingDownLocked()) {
-            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
-            if (r != null) {
+        final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+        if (r != null) {
+            if (r.getStack().shouldSleepOrShutDownActivities()) {
                 r.setSleeping(true, true);
             }
         }
@@ -4420,8 +4515,7 @@
                     synchronized (mService) {
                         if (mService.isSleepingOrShuttingDownLocked()) {
                             Slog.w(TAG, "Sleep timeout!  Sleeping now.");
-                            mSleepTimeout = true;
-                            checkReadyForSleepLocked();
+                            checkReadyForSleepLocked(false /* allowDelay */);
                         }
                     }
                 } break;
@@ -4546,6 +4640,13 @@
         /** Array of all UIDs that are present on the display. */
         private IntArray mDisplayAccessUIDs = new IntArray();
 
+        /** All tokens used to put activities on this stack to sleep (including mOffToken) */
+        final ArrayList<SleepTokenImpl> mAllSleepTokens = new ArrayList<>();
+        /** The token acquired by ActivityStackSupervisor to put stacks on the display to sleep */
+        SleepToken mOffToken;
+
+        private boolean mSleeping;
+
         @VisibleForTesting
         ActivityDisplay() {
             mActivityDisplays.put(mDisplayId, this);
@@ -4570,12 +4671,14 @@
             if (DEBUG_STACK) Slog.v(TAG_STACK, "attachStack: attaching " + stack
                     + " to displayId=" + mDisplayId + " position=" + position);
             mStacks.add(position, stack);
+            mService.updateSleepIfNeededLocked();
         }
 
         void detachStack(ActivityStack stack) {
             if (DEBUG_STACK) Slog.v(TAG_STACK, "detachStack: detaching " + stack
                     + " from displayId=" + mDisplayId);
             mStacks.remove(stack);
+            mService.updateSleepIfNeededLocked();
         }
 
         @Override
@@ -4623,6 +4726,19 @@
         boolean shouldDestroyContentOnRemove() {
             return mDisplay.getRemoveMode() == REMOVE_MODE_DESTROY_CONTENT;
         }
+
+        boolean shouldSleep() {
+            return (mStacks.isEmpty() || !mAllSleepTokens.isEmpty())
+                    && (mService.mRunningVoice == null);
+        }
+
+        boolean isSleeping() {
+            return mSleeping;
+        }
+
+        void setIsSleeping(boolean asleep) {
+            mSleeping = asleep;
+        }
     }
 
     ActivityStack findStackBehind(ActivityStack stack) {
@@ -4804,4 +4920,30 @@
             mResult.dump(pw, prefix);
         }
     }
+
+    private final class SleepTokenImpl extends SleepToken {
+        private final String mTag;
+        private final long mAcquireTime;
+        private final int mDisplayId;
+
+        public SleepTokenImpl(String tag, int displayId) {
+            mTag = tag;
+            mDisplayId = displayId;
+            mAcquireTime = SystemClock.uptimeMillis();
+        }
+
+        @Override
+        public void release() {
+            synchronized (mService) {
+                removeSleepTokenLocked(this);
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "{\"" + mTag + "\", display " + mDisplayId
+                    + ", acquire at " + TimeUtils.formatUptime(mAcquireTime) + "}";
+        }
+    }
+
 }
diff --git a/services/core/java/com/android/server/am/KeyguardController.java b/services/core/java/com/android/server/am/KeyguardController.java
index 58e71df..cea80c8 100644
--- a/services/core/java/com/android/server/am/KeyguardController.java
+++ b/services/core/java/com/android/server/am/KeyguardController.java
@@ -18,6 +18,7 @@
 
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
 import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
 import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
@@ -32,6 +33,7 @@
 import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_UNOCCLUDE;
 import static com.android.server.wm.AppTransition.TRANSIT_UNSET;
 
+import android.app.ActivityManagerInternal.SleepToken;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.Trace;
@@ -63,6 +65,7 @@
     private ActivityRecord mDismissingKeyguardActivity;
     private int mBeforeUnoccludeTransit;
     private int mVisibilityTransactionDepth;
+    private SleepToken mSleepToken;
 
     KeyguardController(ActivityManagerService service,
             ActivityStackSupervisor stackSupervisor) {
@@ -102,7 +105,7 @@
             mDismissalRequested = false;
         }
         mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-        mService.updateSleepIfNeededLocked();
+        updateKeyguardSleepToken();
     }
 
     /**
@@ -122,7 +125,7 @@
             mWindowManager.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY,
                     false /* alwaysKeepCurrent */, convertTransitFlags(flags),
                     false /* forceOverride */);
-            mService.updateSleepIfNeededLocked();
+            updateKeyguardSleepToken();
 
             // Some stack visibility might change (e.g. docked stack)
             mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
@@ -263,7 +266,7 @@
             try {
                 mWindowManager.prepareAppTransition(resolveOccludeTransit(),
                         false /* alwaysKeepCurrent */, 0 /* flags */, true /* forceOverride */);
-                mService.updateSleepIfNeededLocked();
+                updateKeyguardSleepToken();
                 mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
                 mWindowManager.executeAppTransition();
             } finally {
@@ -333,6 +336,15 @@
         }
     }
 
+    private void updateKeyguardSleepToken() {
+        if (mSleepToken == null && isKeyguardShowing()) {
+            mSleepToken = mService.acquireSleepToken("Keyguard", DEFAULT_DISPLAY);
+        } else if (mSleepToken != null && !isKeyguardShowing()) {
+            mSleepToken.release();
+            mSleepToken = null;
+        }
+    }
+
     void dump(PrintWriter pw, String prefix) {
         pw.println(prefix + "KeyguardController:");
         pw.println(prefix + "  mKeyguardShowing=" + mKeyguardShowing);
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index e82eabf..225d708 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -1416,7 +1416,11 @@
                 notificationManager.cancelAsUser(TAG, SystemMessage.NOTE_VPN_DISCONNECTED, user);
                 return;
             }
-            final Intent intent = new Intent(Settings.ACTION_VPN_SETTINGS);
+            final Intent intent = new Intent();
+            intent.setComponent(ComponentName.unflattenFromString(mContext.getString(
+                    R.string.config_customVpnAlwaysOnDisconnectedDialogComponent)));
+            intent.putExtra("lockdown", mLockdown);
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             final PendingIntent configIntent = mSystemServices.pendingIntentGetActivityAsUser(
                     intent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT, user);
             final Notification.Builder builder =
diff --git a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
index 1a5ff77..55e290a 100644
--- a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
+++ b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
@@ -46,7 +46,6 @@
 import java.util.HashSet;
 import java.util.Objects;
 import java.util.Set;
-import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -58,8 +57,6 @@
 public class OffloadController {
     private static final String TAG = OffloadController.class.getSimpleName();
 
-    private static final int STATS_FETCH_TIMEOUT_MS = 1000;
-
     private final Handler mHandler;
     private final OffloadHardwareInterface mHwInterface;
     private final ContentResolver mContentResolver;
@@ -76,9 +73,17 @@
     private Set<String> mLastLocalPrefixStrs;
 
     // Maps upstream interface names to offloaded traffic statistics.
+    // Always contains the latest value received from the hardware for each interface, regardless of
+    // whether offload is currently running on that interface.
     private HashMap<String, OffloadHardwareInterface.ForwardedStats>
             mForwardedStats = new HashMap<>();
 
+    // Maps upstream interface names to interface quotas.
+    // Always contains the latest value received from the framework for each interface, regardless
+    // of whether offload is currently running (or is even supported) on that interface. Only
+    // includes upstream interfaces that have a quota set.
+    private HashMap<String, Long> mInterfaceQuotas = new HashMap<>();
+
     public OffloadController(Handler h, OffloadHardwareInterface hwi,
             ContentResolver contentResolver, INetworkManagementService nms, SharedLog log) {
         mHandler = h;
@@ -177,36 +182,38 @@
         @Override
         public NetworkStats getTetherStats() {
             NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
-            CountDownLatch latch = new CountDownLatch(1);
 
-            mHandler.post(() -> {
-                try {
-                    NetworkStats.Entry entry = new NetworkStats.Entry();
-                    entry.set = SET_DEFAULT;
-                    entry.tag = TAG_NONE;
-                    entry.uid = UID_TETHERING;
+            // We can't just post to mHandler because we are mostly (but not always) called by
+            // NetworkStatsService#performPollLocked, which is (currently) on the same thread as us.
+            mHandler.runWithScissors(() -> {
+                NetworkStats.Entry entry = new NetworkStats.Entry();
+                entry.set = SET_DEFAULT;
+                entry.tag = TAG_NONE;
+                entry.uid = UID_TETHERING;
 
-                    updateStatsForCurrentUpstream();
+                updateStatsForCurrentUpstream();
 
-                    for (String iface : mForwardedStats.keySet()) {
-                        entry.iface = iface;
-                        entry.rxBytes = mForwardedStats.get(iface).rxBytes;
-                        entry.txBytes = mForwardedStats.get(iface).txBytes;
-                        stats.addValues(entry);
-                    }
-                } finally {
-                    latch.countDown();
+                for (String iface : mForwardedStats.keySet()) {
+                    entry.iface = iface;
+                    entry.rxBytes = mForwardedStats.get(iface).rxBytes;
+                    entry.txBytes = mForwardedStats.get(iface).txBytes;
+                    stats.addValues(entry);
                 }
-            });
-
-            try {
-                latch.await(STATS_FETCH_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-            } catch (InterruptedException e) {
-                mLog.e("Tethering stats fetch timed out after " + STATS_FETCH_TIMEOUT_MS + "ms");
-            }
+            }, 0);
 
             return stats;
         }
+
+        public void setInterfaceQuota(String iface, long quotaBytes) {
+            mHandler.post(() -> {
+                if (quotaBytes == ITetheringStatsProvider.QUOTA_UNLIMITED) {
+                    mInterfaceQuotas.remove(iface);
+                } else {
+                    mInterfaceQuotas.put(iface, quotaBytes);
+                }
+                maybeUpdateDataLimit(iface);
+            });
+        }
     }
 
     private void maybeUpdateStats(String iface) {
@@ -220,6 +227,22 @@
         mForwardedStats.get(iface).add(mHwInterface.getForwardedStats(iface));
     }
 
+    private boolean maybeUpdateDataLimit(String iface) {
+        // setDataLimit may only be called while offload is occuring on this upstream.
+        if (!started() ||
+                mUpstreamLinkProperties == null ||
+                !TextUtils.equals(iface, mUpstreamLinkProperties.getInterfaceName())) {
+            return true;
+        }
+
+        Long limit = mInterfaceQuotas.get(iface);
+        if (limit == null) {
+            limit = Long.MAX_VALUE;
+        }
+
+        return mHwInterface.setDataLimit(iface, limit);
+    }
+
     private void updateStatsForCurrentUpstream() {
         if (mUpstreamLinkProperties != null) {
             maybeUpdateStats(mUpstreamLinkProperties.getInterfaceName());
@@ -309,8 +332,21 @@
             }
         }
 
-        return mHwInterface.setUpstreamParameters(
+        boolean success = mHwInterface.setUpstreamParameters(
                 iface, v4addr, v4gateway, (v6gateways.isEmpty() ? null : v6gateways));
+
+        if (!success) {
+           return success;
+        }
+
+        // Data limits can only be set once offload is running on the upstream.
+        success = maybeUpdateDataLimit(iface);
+        if (!success) {
+            mLog.log("Setting data limit for " + iface + " failed, disabling offload.");
+            stop();
+        }
+
+        return success;
     }
 
     private boolean computeAndPushLocalPrefixes() {
diff --git a/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java b/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
index 25fab9e..86ff0a6 100644
--- a/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
+++ b/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
@@ -40,7 +40,7 @@
     // Change this value to control whether tether offload is enabled or
     // disabled by default in the absence of an explicit Settings value.
     // See accompanying unittest to distinguish 0 from non-0 values.
-    private static final int DEFAULT_TETHER_OFFLOAD_DISABLED = 1;
+    private static final int DEFAULT_TETHER_OFFLOAD_DISABLED = 0;
     private static final String NO_INTERFACE_NAME = "";
     private static final String NO_IPV4_ADDRESS = "";
     private static final String NO_IPV4_GATEWAY = "";
@@ -188,6 +188,27 @@
         return results.success;
     }
 
+    public boolean setDataLimit(String iface, long limit) {
+
+        final String logmsg = String.format("setDataLimit(%s, %d)", iface, limit);
+
+        final CbResults results = new CbResults();
+        try {
+            mOffloadControl.setDataLimit(
+                    iface, limit,
+                    (boolean success, String errMsg) -> {
+                        results.success = success;
+                        results.errMsg = errMsg;
+                    });
+        } catch (RemoteException e) {
+            record(logmsg, e);
+            return false;
+        }
+
+        record(logmsg, results);
+        return results.success;
+    }
+
     public boolean setUpstreamParameters(
             String iface, String v4addr, String v4gateway, ArrayList<String> v6gws) {
         iface = (iface != null) ? iface : NO_INTERFACE_NAME;
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 8087813..beaddb4 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -212,12 +212,25 @@
         }
     }
 
-    protected void onSettingRestored(String element, String value, int userId) {
+    protected void onSettingRestored(String element, String value, int backupSdkInt, int userId) {
         if (!mUseXml) {
             Slog.d(TAG, "Restored managed service setting: " + element);
             if (mConfig.secureSettingName.equals(element) ||
                     (mConfig.secondarySettingName != null
                             && mConfig.secondarySettingName.equals(element))) {
+                if (backupSdkInt < Build.VERSION_CODES.O) {
+                    // automatic system grants were added in O, so append the approved apps
+                    // rather than wiping out the setting
+                    String currentSetting =
+                            getApproved(userId, mConfig.secureSettingName.equals(element));
+                    if (!TextUtils.isEmpty(currentSetting)) {
+                        if (!TextUtils.isEmpty(value)) {
+                            value = value + ENABLED_SERVICES_SEPARATOR + currentSetting;
+                        } else {
+                            value = currentSetting;
+                        }
+                    }
+                }
                 Settings.Secure.putStringForUser(
                         mContext.getContentResolver(), element, value, userId);
                 loadAllowedComponentsFromSettings();
@@ -370,6 +383,13 @@
         }
     }
 
+    protected String getApproved(int userId, boolean primary) {
+        final ArrayMap<Boolean, ArraySet<String>> allowedByType =
+                mApproved.getOrDefault(userId, new ArrayMap<>());
+        ArraySet<String> approved = allowedByType.getOrDefault(primary, new ArraySet<>());
+        return String.join(ENABLED_SERVICES_SEPARATOR, approved);
+    }
+
     protected List<ComponentName> getAllowedComponents(int userId) {
         final List<ComponentName> allowedComponents = new ArrayList<>();
         final ArrayMap<Boolean, ArraySet<String>> allowedByType =
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 186243c..c84c3b6 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -816,8 +816,10 @@
                     String newValue = intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE);
                     int restoredFromSdkInt = intent.getIntExtra(
                             Intent.EXTRA_SETTING_RESTORED_FROM_SDK_INT, 0);
-                    mListeners.onSettingRestored(element, newValue, getSendingUserId());
-                    mConditionProviders.onSettingRestored(element, newValue, getSendingUserId());
+                    mListeners.onSettingRestored(
+                            element, newValue, restoredFromSdkInt, getSendingUserId());
+                    mConditionProviders.onSettingRestored(
+                            element, newValue, restoredFromSdkInt, getSendingUserId());
                 } catch (Exception e) {
                     Slog.wtf(TAG, "Cannot restore managed services from settings", e);
                 }
diff --git a/services/core/java/com/android/server/pm/BasePermission.java b/services/core/java/com/android/server/pm/BasePermission.java
index 2100038..30fda1e 100644
--- a/services/core/java/com/android/server/pm/BasePermission.java
+++ b/services/core/java/com/android/server/pm/BasePermission.java
@@ -96,7 +96,7 @@
     }
 
     public boolean isInstant() {
-        return (protectionLevel & PermissionInfo.PROTECTION_FLAG_EPHEMERAL) != 0;
+        return (protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0;
     }
 
     public boolean isRuntimeOnly() {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 84bb453..56f325f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -4246,10 +4246,17 @@
                 return null;
             }
             // If the caller is an app that targets pre 26 SDK drop protection flags.
-            final PermissionInfo permissionInfo = generatePermissionInfo(p, flags);
+            PermissionInfo permissionInfo = generatePermissionInfo(p, flags);
             if (permissionInfo != null) {
-                permissionInfo.protectionLevel = adjustPermissionProtectionFlagsLPr(
+                final int protectionLevel = adjustPermissionProtectionFlagsLPr(
                         permissionInfo.protectionLevel, packageName, callingUid);
+                if (permissionInfo.protectionLevel != protectionLevel) {
+                    // If we return different protection level, don't use the cached info
+                    if (p.perm != null && p.perm.info == permissionInfo) {
+                        permissionInfo = new PermissionInfo(permissionInfo);
+                    }
+                    permissionInfo.protectionLevel = protectionLevel;
+                }
             }
             return permissionInfo;
         }
@@ -18529,12 +18536,12 @@
                 BasePermission bp = mSettings.mPermissions.get(perm.info.name);
 
                 // Don't allow anyone but the system to define ephemeral permissions.
-                if ((perm.info.protectionLevel & PermissionInfo.PROTECTION_FLAG_EPHEMERAL) != 0
+                if ((perm.info.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0
                         && !systemApp) {
                     Slog.w(TAG, "Non-System package " + pkg.packageName
                             + " attempting to delcare ephemeral permission "
                             + perm.info.name + "; Removing ephemeral.");
-                    perm.info.protectionLevel &= ~PermissionInfo.PROTECTION_FLAG_EPHEMERAL;
+                    perm.info.protectionLevel &= ~PermissionInfo.PROTECTION_FLAG_INSTANT;
                 }
                 // Check whether the newly-scanned package wants to define an already-defined perm
                 if (bp != null) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index ae78d7c..6b23b72 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -115,6 +115,8 @@
 import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED;
 import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
 
+import static com.android.server.wm.proto.WindowManagerPolicyProto.STABLE_BOUNDS;
+
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityManager.StackId;
@@ -194,6 +196,7 @@
 import android.util.MutableBoolean;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
 import android.view.Display;
 import android.view.Gravity;
 import android.view.HapticFeedbackConstants;
@@ -7466,10 +7469,12 @@
         }
     }
 
+    // TODO (multidisplay): Support multiple displays in WindowManagerPolicy.
     private void updateDreamingSleepToken(boolean acquire) {
         if (acquire) {
             if (mDreamingSleepToken == null) {
-                mDreamingSleepToken = mActivityManagerInternal.acquireSleepToken("Dream");
+                mDreamingSleepToken = mActivityManagerInternal.acquireSleepToken(
+                        "Dream", Display.DEFAULT_DISPLAY);
             }
         } else {
             if (mDreamingSleepToken != null) {
@@ -7479,10 +7484,12 @@
         }
     }
 
+    // TODO (multidisplay): Support multiple displays in WindowManagerPolicy.
     private void updateScreenOffSleepToken(boolean acquire) {
         if (acquire) {
             if (mScreenOffSleepToken == null) {
-                mScreenOffSleepToken = mActivityManagerInternal.acquireSleepToken("ScreenOff");
+                mScreenOffSleepToken = mActivityManagerInternal.acquireSleepToken(
+                        "ScreenOff", Display.DEFAULT_DISPLAY);
             }
         } else {
             if (mScreenOffSleepToken != null) {
@@ -8218,6 +8225,14 @@
     }
 
     @Override
+    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        new Rect(mStableLeft, mStableTop, mStableRight, mStableBottom).writeToProto(proto,
+                STABLE_BOUNDS);
+        proto.end(token);
+    }
+
+    @Override
     public void dump(String prefix, PrintWriter pw, String[] args) {
         pw.print(prefix); pw.print("mSafeMode="); pw.print(mSafeMode);
                 pw.print(" mSystemReady="); pw.print(mSystemReady);
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 805250a..6484a13 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -429,6 +429,8 @@
         }
 
         public void getMagnificationRegionLocked(Region outMagnificationRegion) {
+            // Make sure we're working with the most current bounds
+            mMagnifedViewport.recomputeBoundsLocked();
             mMagnifedViewport.getMagnificationRegionLocked(outMagnificationRegion);
         }
 
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index cda3efd..c19ede0 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -47,6 +47,8 @@
 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE;
 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
+import static com.android.server.wm.proto.AppTransitionProto.APP_TRANSITION_STATE;
+import static com.android.server.wm.proto.AppTransitionProto.LAST_USED_APP_TRANSITION;
 
 import android.annotation.Nullable;
 import android.app.ActivityManager;
@@ -65,6 +67,7 @@
 import android.util.ArraySet;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
 import android.view.AppTransitionAnimationSpec;
 import android.view.IAppTransitionAnimationSpecsFuture;
 import android.view.WindowManager;
@@ -1975,6 +1978,13 @@
         }
     }
 
+    void writeToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        proto.write(APP_TRANSITION_STATE, mAppTransitionState);
+        proto.write(LAST_USED_APP_TRANSITION, mLastUsedAppTransition);
+        proto.end(token);
+    }
+
     @Override
     public void dump(PrintWriter pw, String prefix) {
         pw.print(prefix); pw.println(this);
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index ddbbde1..d4791a6 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -16,8 +16,8 @@
 
 package com.android.server.wm;
 
+import static android.view.Display.INVALID_DISPLAY;
 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
-
 import static com.android.server.wm.AppTransition.TRANSIT_UNSET;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
@@ -358,7 +358,7 @@
 
     // This must be called while inside a transaction.
     boolean stepAnimationLocked(long currentTime) {
-        if (mService.okToAnimate()) {
+        if (mAppToken.okToAnimate()) {
             // We will run animations as long as the display isn't frozen.
 
             if (animation == sDummyAnimation) {
@@ -423,8 +423,8 @@
 
         if (DEBUG_ANIM) Slog.v(TAG, "Animation done in " + mAppToken
                 + ": reportedVisible=" + mAppToken.reportedVisible
-                + " okToDisplay=" + mService.okToDisplay()
-                + " okToAnimate=" + mService.okToAnimate()
+                + " okToDisplay=" + mAppToken.okToDisplay()
+                + " okToAnimate=" + mAppToken.okToAnimate()
                 + " startingDisplayed=" + mAppToken.startingDisplayed);
 
         transformation.clear();
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index e9696d2..1ca98ac 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
 
 import static com.android.server.wm.AppTransition.TRANSIT_DOCK_TASK_FROM_RECENTS;
@@ -401,7 +402,7 @@
 
             // If we are preparing an app transition, then delay changing
             // the visibility of this token until we execute that transition.
-            if (mService.okToAnimate() && mService.mAppTransition.isTransitionSet()) {
+            if (wtoken.okToAnimate() && mService.mAppTransition.isTransitionSet()) {
                 // A dummy animation is a placeholder animation which informs others that an
                 // animation is going on (in this case an application transition). If the animation
                 // was transferred from another application/animator, no dummy animator should be
@@ -478,7 +479,7 @@
 
             // If the display is frozen, we won't do anything until the actual window is
             // displayed so there is no reason to put in the starting window.
-            if (!mService.okToDisplay()) {
+            if (!mContainer.okToDisplay()) {
                 return false;
             }
 
@@ -699,16 +700,17 @@
 
     public void startFreezingScreen(int configChanges) {
         synchronized(mWindowMap) {
-            if (configChanges == 0 && mService.okToDisplay()) {
-                if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Skipping set freeze of " + mToken);
-                return;
-            }
-
             if (mContainer == null) {
                 Slog.w(TAG_WM,
                         "Attempted to freeze screen with non-existing app token: " + mContainer);
                 return;
             }
+
+            if (configChanges == 0 && mContainer.okToDisplay()) {
+                if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Skipping set freeze of " + mToken);
+                return;
+            }
+
             mContainer.startFreezingScreen();
         }
     }
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 2e4de8c..505b4c4 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -49,6 +49,8 @@
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
 import static com.android.server.wm.WindowManagerService.logWithStack;
+import static com.android.server.wm.proto.AppWindowTokenProto.NAME;
+import static com.android.server.wm.proto.AppWindowTokenProto.WINDOW_TOKEN;
 
 import android.annotation.NonNull;
 import android.app.Activity;
@@ -57,8 +59,10 @@
 import android.os.Binder;
 import android.os.Debug;
 import android.os.IBinder;
+import android.os.RemoteException;
 import android.os.SystemClock;
 import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
 import android.view.IApplicationToken;
 import android.view.SurfaceControl;
 import android.view.WindowManager;
@@ -1746,6 +1750,26 @@
     }
 
     @Override
+    void writeToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        writeNameToProto(proto, NAME);
+        super.writeToProto(proto, WINDOW_TOKEN);
+        proto.end(token);
+    }
+
+    void writeNameToProto(ProtoOutputStream proto, long fieldId) {
+        if (appToken == null) {
+            return;
+        }
+        try {
+            proto.write(fieldId, appToken.getName());
+        } catch (RemoteException e) {
+            // This shouldn't happen, but in this case fall back to outputting nothing
+            Slog.e(TAG, e.toString());
+        }
+    }
+
+    @Override
     public String toString() {
         if (stringName == null) {
             StringBuilder sb = new StringBuilder();
diff --git a/services/core/java/com/android/server/wm/DimLayerController.java b/services/core/java/com/android/server/wm/DimLayerController.java
index 7414928..6f9e45a 100644
--- a/services/core/java/com/android/server/wm/DimLayerController.java
+++ b/services/core/java/com/android/server/wm/DimLayerController.java
@@ -290,7 +290,7 @@
             state.dimLayer.setLayer(dimLayer);
         }
         if (state.dimLayer.isAnimating()) {
-            if (!mDisplayContent.mService.okToAnimate()) {
+            if (!mDisplayContent.okToAnimate()) {
                 // Jump to the end of the animation.
                 state.dimLayer.show();
             } else {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 465ca86..2df773e 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -97,6 +97,15 @@
 import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
 import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW;
 import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE;
+import static com.android.server.wm.proto.DisplayProto.ABOVE_APP_WINDOWS;
+import static com.android.server.wm.proto.DisplayProto.BELOW_APP_WINDOWS;
+import static com.android.server.wm.proto.DisplayProto.DISPLAY_INFO;
+import static com.android.server.wm.proto.DisplayProto.DOCKED_STACK_DIVIDER_CONTROLLER;
+import static com.android.server.wm.proto.DisplayProto.DPI;
+import static com.android.server.wm.proto.DisplayProto.ID;
+import static com.android.server.wm.proto.DisplayProto.IME_WINDOWS;
+import static com.android.server.wm.proto.DisplayProto.PINNED_STACK_CONTROLLER;
+import static com.android.server.wm.proto.DisplayProto.STACKS;
 
 import android.annotation.NonNull;
 import android.app.ActivityManager.StackId;
@@ -118,6 +127,7 @@
 import android.util.DisplayMetrics;
 import android.util.MutableBoolean;
 import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
 import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.InputDevice;
@@ -2099,6 +2109,32 @@
         }
     }
 
+    void writeToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        proto.write(ID, mDisplayId);
+        for (int stackNdx = mTaskStackContainers.size() - 1; stackNdx >= 0; --stackNdx) {
+            final TaskStack stack = mTaskStackContainers.get(stackNdx);
+            stack.writeToProto(proto, STACKS);
+        }
+        mDividerControllerLocked.writeToProto(proto, DOCKED_STACK_DIVIDER_CONTROLLER);
+        mPinnedStackControllerLocked.writeToProto(proto, PINNED_STACK_CONTROLLER);
+        for (int i = mAboveAppWindowsContainers.size() - 1; i >= 0; --i) {
+            final WindowToken windowToken = mAboveAppWindowsContainers.get(i);
+            windowToken.writeToProto(proto, ABOVE_APP_WINDOWS);
+        }
+        for (int i = mBelowAppWindowsContainers.size() - 1; i >= 0; --i) {
+            final WindowToken windowToken = mBelowAppWindowsContainers.get(i);
+            windowToken.writeToProto(proto, BELOW_APP_WINDOWS);
+        }
+        for (int i = mImeWindowsContainers.size() - 1; i >= 0; --i) {
+            final WindowToken windowToken = mImeWindowsContainers.get(i);
+            windowToken.writeToProto(proto, IME_WINDOWS);
+        }
+        proto.write(DPI, mBaseDisplayDensity);
+        mDisplayInfo.writeToProto(proto, DISPLAY_INFO);
+        proto.end(token);
+    }
+
     public void dump(String prefix, PrintWriter pw) {
         pw.print(prefix); pw.print("Display: mDisplayId="); pw.println(mDisplayId);
         final String subPrefix = "  " + prefix;
@@ -3204,6 +3240,19 @@
         mService.requestTraversal();
     }
 
+    boolean okToDisplay() {
+        if (mDisplayId == DEFAULT_DISPLAY) {
+            return !mService.mDisplayFrozen
+                    && mService.mDisplayEnabled && mService.mPolicy.isScreenOn();
+        }
+        return mDisplayInfo.state == Display.STATE_ON;
+    }
+
+    boolean okToAnimate() {
+        return okToDisplay() &&
+                (mDisplayId != DEFAULT_DISPLAY || mService.mPolicy.okToAnimate());
+    }
+
     static final class TaskForResizePointSearchResult {
         boolean searchDone;
         Task taskForResize;
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 6b51455..2b45d67 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -34,6 +34,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.H.NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED;
 import static com.android.server.wm.WindowManagerService.LAYER_OFFSET_DIM;
+import static com.android.server.wm.proto.DockedStackDividerControllerProto.MINIMIZED_DOCK;
 
 import android.content.Context;
 import android.content.res.Configuration;
@@ -42,6 +43,7 @@
 import android.os.RemoteException;
 import android.util.ArraySet;
 import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
 import android.view.DisplayInfo;
 import android.view.IDockedStackListener;
 import android.view.animation.AnimationUtils;
@@ -919,4 +921,10 @@
             mDimLayer.printTo(prefix + "    ", pw);
         }
     }
+
+    void writeToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        proto.write(MINIMIZED_DOCK, mMinimizedDock);
+        proto.end(token);
+    }
 }
diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index 6d33ce2..1e7140a 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -21,6 +21,8 @@
 
 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.proto.PinnedStackControllerProto.DEFAULT_BOUNDS;
+import static com.android.server.wm.proto.PinnedStackControllerProto.MOVEMENT_BOUNDS;
 
 import android.app.RemoteAction;
 import android.content.pm.ParceledListSlice;
@@ -35,6 +37,7 @@
 import android.util.Size;
 import android.util.Slog;
 import android.util.TypedValue;
+import android.util.proto.ProtoOutputStream;
 import android.view.DisplayInfo;
 import android.view.Gravity;
 import android.view.IPinnedStackController;
@@ -505,4 +508,12 @@
             pw.println(prefix + "  ]");
         }
     }
+
+    void writeToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        getDefaultBounds().writeToProto(proto, DEFAULT_BOUNDS);
+        mService.getStackBounds(PINNED_STACK_ID, mTmpRect);
+        getMovementBounds(mTmpRect).writeToProto(proto, MOVEMENT_BOUNDS);
+        proto.end(token);
+    }
 }
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index e83b792..05ef1a5 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -35,11 +35,11 @@
 import android.util.EventLog;
 import android.util.Slog;
 import android.util.SparseIntArray;
+import android.util.proto.ProtoOutputStream;
 import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.WindowManager;
 
-import com.android.internal.os.SomeArgs;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.EventLogTags;
 
@@ -89,6 +89,8 @@
 import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION;
 import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_ACTION_PENDING;
 import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE;
+import static com.android.server.wm.proto.WindowManagerServiceProto.DISPLAYS;
+import static com.android.server.wm.proto.WindowManagerServiceProto.WINDOWS;
 
 /** Root {@link WindowContainer} for the device. */
 class RootWindowContainer extends WindowContainer<DisplayContent> {
@@ -1055,6 +1057,19 @@
         }
     }
 
+    void writeToProto(ProtoOutputStream proto) {
+        if (mService.mDisplayReady) {
+            final int count = mChildren.size();
+            for (int i = 0; i < count; ++i) {
+                final DisplayContent displayContent = mChildren.get(i);
+                displayContent.writeToProto(proto, DISPLAYS);
+            }
+        }
+        forAllWindows((w) -> {
+            w.writeIdentifierToProto(proto, WINDOWS);
+        }, true);
+    }
+
     @Override
     String getName() {
         return "ROOT";
diff --git a/services/core/java/com/android/server/wm/SurfaceControlWithBackground.java b/services/core/java/com/android/server/wm/SurfaceControlWithBackground.java
index ac06c6d..3603f2f 100644
--- a/services/core/java/com/android/server/wm/SurfaceControlWithBackground.java
+++ b/services/core/java/com/android/server/wm/SurfaceControlWithBackground.java
@@ -29,12 +29,16 @@
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+import static android.view.WindowManagerPolicy.NAV_BAR_BOTTOM;
+import static android.view.WindowManagerPolicy.NAV_BAR_LEFT;
+import static android.view.WindowManagerPolicy.NAV_BAR_RIGHT;
 
 /**
- * SurfaceControl extension that has background sized to match its container.
+ * SurfaceControl extension that has black background behind navigation bar area for fullscreen
+ * letterboxed apps.
  */
 class SurfaceControlWithBackground extends SurfaceControl {
-    // SurfaceControl that holds the background behind opaque letterboxed app windows.
+    // SurfaceControl that holds the background.
     private SurfaceControl mBackgroundControl;
 
     // Flag that defines whether the background should be shown.
@@ -51,8 +55,10 @@
     private float mLastDsDx = 1, mLastDsDy = 1;
     private float mLastX, mLastY;
 
-    // Will skip alpha animation for background of starting window.
-    private boolean mIsStartingWindow;
+    // SurfaceFlinger doesn't support crop rectangles where width or height is non-positive.
+    // If we just set an empty crop it will behave as if there is no crop at all.
+    // To fix this we explicitly hide the surface and won't let it to be shown.
+    private boolean mHiddenForCrop = false;
 
     public SurfaceControlWithBackground(SurfaceControlWithBackground other) {
         super(other);
@@ -76,7 +82,6 @@
         mLastWidth = w;
         mLastHeight = h;
         mWindowSurfaceController.getContainerRect(mTmpContainerRect);
-        mIsStartingWindow = windowType == TYPE_APPLICATION_STARTING;
         mBackgroundControl = new SurfaceControl(s, "Background for - " + name,
                 mTmpContainerRect.width(), mTmpContainerRect.height(), PixelFormat.OPAQUE,
                 flags | SurfaceControl.FX_SURFACE_DIM);
@@ -89,10 +94,7 @@
         if (mBackgroundControl == null) {
             return;
         }
-        // We won't animate alpha for starting window because it will be visible as a flash for user
-        // when fading out to reveal real app window.
-        final float backgroundAlpha = mIsStartingWindow && alpha < 1.f ? 0 : alpha;
-        mBackgroundControl.setAlpha(backgroundAlpha);
+        mBackgroundControl.setAlpha(alpha);
     }
 
     @Override
@@ -146,15 +148,10 @@
         if (mBackgroundControl == null) {
             return;
         }
-        if (crop.width() < mLastWidth || crop.height() < mLastHeight) {
-            // We're animating and cropping window, compute the appropriate crop for background.
-            calculateBgCrop(crop);
-            mBackgroundControl.setWindowCrop(mTmpContainerRect);
-        } else {
-            // When not animating just set crop to container rect.
-            mWindowSurfaceController.getContainerRect(mTmpContainerRect);
-            mBackgroundControl.setWindowCrop(mTmpContainerRect);
-        }
+        calculateBgCrop(crop);
+        mBackgroundControl.setWindowCrop(mTmpContainerRect);
+        mHiddenForCrop = mTmpContainerRect.isEmpty();
+        updateBackgroundVisibility();
     }
 
     @Override
@@ -164,34 +161,24 @@
         if (mBackgroundControl == null) {
             return;
         }
-        if (crop.width() < mLastWidth || crop.height() < mLastHeight) {
-            // We're animating and cropping window, compute the appropriate crop for background.
-            calculateBgCrop(crop);
-            mBackgroundControl.setFinalCrop(mTmpContainerRect);
-        } else {
-            // When not animating just set crop to container rect.
-            mWindowSurfaceController.getContainerRect(mTmpContainerRect);
-            mBackgroundControl.setFinalCrop(mTmpContainerRect);
-        }
+        mWindowSurfaceController.getContainerRect(mTmpContainerRect);
+        mBackgroundControl.setFinalCrop(mTmpContainerRect);
     }
 
-    /** Compute background crop based on current animation progress for main surface control. */
+    /**
+     * Compute background crop based on current animation progress for main surface control and
+     * update {@link #mTmpContainerRect} with new values.
+     */
     private void calculateBgCrop(Rect crop) {
         // Track overall progress of animation by computing cropped portion of status bar.
         final Rect contentInsets = mWindowSurfaceController.mAnimator.mWin.mContentInsets;
         float d = contentInsets.top == 0 ? 0 : (float) crop.top / contentInsets.top;
         if (d > 1.f) {
             // We're running expand animation from launcher, won't compute custom bg crop here.
-            mTmpContainerRect.set(crop);
+            mTmpContainerRect.setEmpty();
             return;
         }
 
-        // Compute additional offset for the background when app window is positioned not at (0,0).
-        // E.g. landscape with navigation bar on the left.
-        final Rect winFrame = mWindowSurfaceController.mAnimator.mWin.mFrame;
-        final int offsetX = (int) (winFrame.left * mLastDsDx * d + 0.5);
-        final int offsetY = (int) (winFrame.top * mLastDsDy * d + 0.5);
-
         // Compute new scaled width and height for background that will depend on current animation
         // progress. Those consist of current crop rect for the main surface + scaled areas outside
         // of letterboxed area.
@@ -201,17 +188,39 @@
         // computing correct frames for letterboxed windows in WindowState.
         d = d < 0.025f ? 0 : d;
         mWindowSurfaceController.getContainerRect(mTmpContainerRect);
-        final int backgroundWidth =
-                (int) (crop.width() + (mTmpContainerRect.width() - mLastWidth) * (1 - d) + 0.5);
-        final int backgroundHeight =
-                (int) (crop.height() + (mTmpContainerRect.height() - mLastHeight) * (1 - d) + 0.5);
+        int backgroundWidth = 0, backgroundHeight = 0;
+        // Compute additional offset for the background when app window is positioned not at (0,0).
+        // E.g. landscape with navigation bar on the left.
+        final Rect winFrame = mWindowSurfaceController.mAnimator.mWin.mFrame;
+        int offsetX = (int)((winFrame.left - mTmpContainerRect.left) * mLastDsDx),
+                offsetY = (int) ((winFrame.top - mTmpContainerRect.top) * mLastDsDy);
 
-        mTmpContainerRect.set(crop);
-        // Make sure that part of background to left/top is visible and scaled.
-        mTmpContainerRect.offset(offsetX, offsetY);
-        // Set correct width/height, so that area to right/bottom is cropped properly.
-        mTmpContainerRect.right = mTmpContainerRect.left + backgroundWidth;
-        mTmpContainerRect.bottom = mTmpContainerRect.top + backgroundHeight;
+        // Position and size background.
+        final int bgPosition = mWindowSurfaceController.mAnimator.mService.getNavBarPosition();
+
+        switch (bgPosition) {
+            case NAV_BAR_LEFT:
+                backgroundWidth = (int) ((mTmpContainerRect.width() - mLastWidth) * (1 - d) + 0.5);
+                backgroundHeight = crop.height();
+                offsetX += crop.left - backgroundWidth;
+                offsetY += crop.top;
+                break;
+            case NAV_BAR_RIGHT:
+                backgroundWidth = (int) ((mTmpContainerRect.width() - mLastWidth) * (1 - d) + 0.5);
+                backgroundHeight = crop.height();
+                offsetX += crop.right;
+                offsetY += crop.top;
+                break;
+            case NAV_BAR_BOTTOM:
+                backgroundWidth = crop.width();
+                backgroundHeight = (int) ((mTmpContainerRect.height() - mLastHeight) * (1 - d)
+                        + 0.5);
+                offsetX += crop.left;
+                offsetY += crop.bottom;
+                break;
+        }
+        mTmpContainerRect.set(offsetX, offsetY, offsetX + backgroundWidth,
+                offsetY + backgroundHeight);
     }
 
     @Override
@@ -317,7 +326,7 @@
             return;
         }
         final AppWindowToken appWindowToken = mWindowSurfaceController.mAnimator.mWin.mAppToken;
-        if (appWindowToken != null && appWindowToken.fillsParent() && mVisible) {
+        if (!mHiddenForCrop && mVisible && appWindowToken != null && appWindowToken.fillsParent()) {
             mBackgroundControl.show();
         } else {
             mBackgroundControl.hide();
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index e5055e9..6349b05 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -29,6 +29,11 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
 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.proto.TaskProto.APP_WINDOW_TOKENS;
+import static com.android.server.wm.proto.TaskProto.BOUNDS;
+import static com.android.server.wm.proto.TaskProto.FILLS_PARENT;
+import static com.android.server.wm.proto.TaskProto.ID;
+import static com.android.server.wm.proto.TaskProto.TEMP_INSET_BOUNDS;
 
 import android.app.ActivityManager.StackId;
 import android.app.ActivityManager.TaskDescription;
@@ -37,11 +42,11 @@
 import android.graphics.Rect;
 import android.util.EventLog;
 import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
 import android.view.DisplayInfo;
 import android.view.Surface;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.wm.DimLayer.DimLayerUser;
 
 import java.io.PrintWriter;
 import java.util.function.Consumer;
@@ -735,6 +740,19 @@
         return "Task=" + mTaskId;
     }
 
+    void writeToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        proto.write(ID, mTaskId);
+        for (int i = mChildren.size() - 1; i >= 0; i--) {
+            final AppWindowToken appWindowToken = mChildren.get(i);
+            appWindowToken.writeToProto(proto, APP_WINDOW_TOKENS);
+        }
+        proto.write(FILLS_PARENT, mFillsParent);
+        mBounds.writeToProto(proto, BOUNDS);
+        mTempInsetBounds.writeToProto(proto, TEMP_INSET_BOUNDS);
+        proto.end(token);
+    }
+
     public void dump(String prefix, PrintWriter pw) {
         final String doublePrefix = prefix + "  ";
 
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 940ad33..60e6c7c 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -204,7 +204,7 @@
         }
         final GraphicBuffer buffer = top.mDisplayContent.screenshotApplicationsToBuffer(top.token,
                 -1, -1, false, 1.0f, false, true);
-        if (buffer == null) {
+        if (buffer == null || buffer.getWidth() <= 1 || buffer.getHeight() <= 1) {
             return null;
         }
         return new TaskSnapshot(buffer, top.getConfiguration().orientation,
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 39878cc..474edaa 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -36,6 +36,11 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.LAYER_OFFSET_DIM;
+import static com.android.server.wm.proto.StackProto.ANIMATION_BACKGROUND_SURFACE_IS_DIMMING;
+import static com.android.server.wm.proto.StackProto.BOUNDS;
+import static com.android.server.wm.proto.StackProto.FILLS_PARENT;
+import static com.android.server.wm.proto.StackProto.ID;
+import static com.android.server.wm.proto.StackProto.TASKS;
 
 import android.app.ActivityManager.StackId;
 import android.content.res.Configuration;
@@ -45,6 +50,7 @@
 import android.util.EventLog;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
 import android.view.DisplayInfo;
 import android.view.Surface;
 
@@ -52,7 +58,6 @@
 import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
 import com.android.internal.policy.DockedDividerUtils;
 import com.android.server.EventLogTags;
-import com.android.server.UiThread;
 
 import java.io.PrintWriter;
 
@@ -1225,6 +1230,18 @@
         return mMinimizeAmount != 0f;
     }
 
+    void writeToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        proto.write(ID, mStackId);
+        for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; taskNdx--) {
+            mChildren.get(taskNdx).writeToProto(proto, TASKS);
+        }
+        proto.write(FILLS_PARENT, mFillsParent);
+        mBounds.writeToProto(proto, BOUNDS);
+        proto.write(ANIMATION_BACKGROUND_SURFACE_IS_DIMMING, mAnimationBackgroundSurface.isDimming());
+        proto.end(token);
+    }
+
     public void dump(String prefix, PrintWriter pw) {
         pw.println(prefix + "mStackId=" + mStackId);
         pw.println(prefix + "mDeferRemoval=" + mDeferRemoval);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 5c664c2..86d8dd2 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -102,6 +102,14 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_KEEP_SCREEN_ON;
 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.proto.WindowManagerServiceProto.APP_TRANSITION;
+import static com.android.server.wm.proto.WindowManagerServiceProto.DISPLAY_FROZEN;
+import static com.android.server.wm.proto.WindowManagerServiceProto.FOCUSED_APP;
+import static com.android.server.wm.proto.WindowManagerServiceProto.FOCUSED_WINDOW;
+import static com.android.server.wm.proto.WindowManagerServiceProto.INPUT_METHOD_WINDOW;
+import static com.android.server.wm.proto.WindowManagerServiceProto.LAST_ORIENTATION;
+import static com.android.server.wm.proto.WindowManagerServiceProto.POLICY;
+import static com.android.server.wm.proto.WindowManagerServiceProto.ROTATION;
 
 import android.Manifest;
 import android.Manifest.permission;
@@ -175,6 +183,7 @@
 import android.util.SparseIntArray;
 import android.util.TimeUtils;
 import android.util.TypedValue;
+import android.util.proto.ProtoOutputStream;
 import android.view.AppTransitionAnimationSpec;
 import android.view.Display;
 import android.view.DisplayInfo;
@@ -2347,7 +2356,7 @@
         // artifacts when we unfreeze the display if some different animation
         // is running.
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "WM#applyAnimationLocked");
-        if (okToAnimate()) {
+        if (atoken.okToAnimate()) {
             final DisplayContent displayContent = atoken.getTask().getDisplayContent();
             final DisplayInfo displayInfo = displayContent.getDisplayInfo();
             final int width = displayInfo.appWidth;
@@ -2425,14 +2434,6 @@
         return false;
     }
 
-    boolean okToDisplay() {
-        return !mDisplayFrozen && mDisplayEnabled && mPolicy.isScreenOn();
-    }
-
-    boolean okToAnimate() {
-        return okToDisplay() && mPolicy.okToAnimate();
-    }
-
     @Override
     public void addWindowToken(IBinder binder, int type, int displayId) {
         if (!checkCallingPermission(MANAGE_APP_TOKENS, "addWindowToken()")) {
@@ -2699,7 +2700,9 @@
         synchronized(mWindowMap) {
             boolean prepared = mAppTransition.prepareAppTransitionLocked(transit, alwaysKeepCurrent,
                     flags, forceOverride);
-            if (prepared && okToAnimate()) {
+            // TODO (multidisplay): associate app transitions with displays
+            final DisplayContent dc = mRoot.getDisplayContent(DEFAULT_DISPLAY);
+            if (prepared && dc != null && dc.okToAnimate()) {
                 mSkipAppTransitionAnimation = false;
             }
         }
@@ -5817,7 +5820,10 @@
         // If the screen is currently frozen or off, then keep
         // it frozen/off until this window draws at its new
         // orientation.
-        if (!okToDisplay() && mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) {
+        // TODO (multidisplay): Support screen freezing on secondary displays.
+        final DisplayContent dc = mRoot.getDisplayContent(DEFAULT_DISPLAY);
+        if ((dc == null || !dc.okToDisplay())
+                && mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) {
             if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Changing surface while display frozen: " + w);
             w.setOrientationChanging(true);
             w.mLastFreezeDuration = 0;
@@ -6033,7 +6039,7 @@
             return;
         }
 
-        if (!displayContent.isReady() || !mPolicy.isScreenOn() || !okToAnimate()) {
+        if (!displayContent.isReady() || !mPolicy.isScreenOn() || !displayContent.okToAnimate()) {
             // No need to freeze the screen before the display is ready,  if the screen is off,
             // or we can't currently animate.
             return;
@@ -6524,6 +6530,25 @@
         }
     }
 
+    private void writeToProtoLocked(ProtoOutputStream proto) {
+        mPolicy.writeToProto(proto, POLICY);
+        mRoot.writeToProto(proto);
+        if (mCurrentFocus != null) {
+            mCurrentFocus.writeIdentifierToProto(proto, FOCUSED_WINDOW);
+        }
+        if (mFocusedApp != null) {
+            mFocusedApp.writeNameToProto(proto, FOCUSED_APP);
+        }
+        if (mInputMethodWindow != null) {
+            mInputMethodWindow.writeIdentifierToProto(proto, INPUT_METHOD_WINDOW);
+        }
+        proto.write(DISPLAY_FROZEN, mDisplayFrozen);
+        final DisplayContent defaultDisplayContent = getDefaultDisplayContentLocked();
+        proto.write(ROTATION, defaultDisplayContent.getRotation());
+        proto.write(LAST_ORIENTATION, defaultDisplayContent.getLastOrientation());
+        mAppTransition.writeToProto(proto, APP_TRANSITION);
+    }
+
     private void dumpWindowsLocked(PrintWriter pw, boolean dumpAll,
             ArrayList<WindowState> windows) {
         pw.println("WINDOW MANAGER WINDOWS (dumpsys window windows)");
@@ -6798,6 +6823,7 @@
         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
 
         boolean dumpAll = false;
+        boolean useProto = false;
 
         int opti = 0;
         while (opti < args.length) {
@@ -6808,6 +6834,8 @@
             opti++;
             if ("-a".equals(opt)) {
                 dumpAll = true;
+            } else if ("--proto".equals(opt)) {
+                useProto = true;
             } else if ("-h".equals(opt)) {
                 pw.println("Window manager dump options:");
                 pw.println("  [-a] [-h] [cmd] ...");
@@ -6827,12 +6855,21 @@
                 pw.println("    \"visible\" for the visible windows.");
                 pw.println("    \"visible-apps\" for the visible app windows.");
                 pw.println("  -a: include all available server state.");
+                pw.println("  --proto: output dump in protocol buffer format.");
                 return;
             } else {
                 pw.println("Unknown argument: " + opt + "; use -h for help");
             }
         }
 
+        if (useProto) {
+            final ProtoOutputStream proto = new ProtoOutputStream(fd);
+            synchronized (mWindowMap) {
+                writeToProtoLocked(proto);
+            }
+            proto.flush();
+            return;
+        }
         // Is the caller requesting to dump a particular piece of data?
         if (opti < args.length) {
             String cmd = args[opti];
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 12abe39..98bd25e 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -102,6 +102,23 @@
 import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
 import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
 import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW;
+import static com.android.server.wm.proto.IdentifierProto.HASH_CODE;
+import static com.android.server.wm.proto.IdentifierProto.TITLE;
+import static com.android.server.wm.proto.IdentifierProto.USER_ID;
+import static com.android.server.wm.proto.WindowStateProto.ANIMATING_EXIT;
+import static com.android.server.wm.proto.WindowStateProto.ANIMATOR;
+import static com.android.server.wm.proto.WindowStateProto.ATTRIBUTES;
+import static com.android.server.wm.proto.WindowStateProto.CHILD_WINDOWS;
+import static com.android.server.wm.proto.WindowStateProto.CONTAINING_FRAME;
+import static com.android.server.wm.proto.WindowStateProto.CONTENT_FRAME;
+import static com.android.server.wm.proto.WindowStateProto.CONTENT_INSETS;
+import static com.android.server.wm.proto.WindowStateProto.DISPLAY_ID;
+import static com.android.server.wm.proto.WindowStateProto.FRAME;
+import static com.android.server.wm.proto.WindowStateProto.GIVEN_CONTENT_INSETS;
+import static com.android.server.wm.proto.WindowStateProto.IDENTIFIER;
+import static com.android.server.wm.proto.WindowStateProto.PARENT_FRAME;
+import static com.android.server.wm.proto.WindowStateProto.STACK_ID;
+import static com.android.server.wm.proto.WindowStateProto.SURFACE_INSETS;
 
 import android.app.AppOpsManager;
 import android.content.Context;
@@ -125,6 +142,7 @@
 import android.util.DisplayMetrics;
 import android.util.Slog;
 import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
 import android.view.DisplayInfo;
 import android.view.Gravity;
 import android.view.IApplicationToken;
@@ -1711,7 +1729,7 @@
         final boolean adjustedForMinimizedDockOrIme = task != null
                 && (task.mStack.isAdjustedForMinimizedDockedStack()
                 || task.mStack.isAdjustedForIme());
-        if (mService.okToAnimate()
+        if (mToken.okToAnimate()
                 && (mAttrs.privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0
                 && !isDragResizing() && !adjustedForMinimizedDockOrIme
                 && (task == null || getTask().mStack.hasMovementAnimations())
@@ -1880,7 +1898,7 @@
         // First, see if we need to run an animation. If we do, we have to hold off on removing the
         // window until the animation is done. If the display is frozen, just remove immediately,
         // since the animation wouldn't be seen.
-        if (mHasSurface && mService.okToAnimate()) {
+        if (mHasSurface && mToken.okToAnimate()) {
             if (mWillReplaceWindow) {
                 // This window is going to be replaced. We need to keep it around until the new one
                 // gets added, then we will get rid of this one.
@@ -2315,7 +2333,7 @@
             mLayoutNeeded = true;
         }
 
-        if (isDrawnLw() && mService.okToAnimate()) {
+        if (isDrawnLw() && mToken.okToAnimate()) {
             mWinAnimator.applyEnterAnimationLocked();
         }
     }
@@ -2471,7 +2489,7 @@
         if (doAnimation) {
             if (DEBUG_VISIBILITY) Slog.v(TAG, "doAnimation: mPolicyVisibility="
                     + mPolicyVisibility + " mAnimation=" + mWinAnimator.mAnimation);
-            if (!mService.okToAnimate()) {
+            if (!mToken.okToAnimate()) {
                 doAnimation = false;
             } else if (mPolicyVisibility && mWinAnimator.mAnimation == null) {
                 // Check for the case where we are currently visible and
@@ -2501,7 +2519,7 @@
 
     boolean hideLw(boolean doAnimation, boolean requestAnim) {
         if (doAnimation) {
-            if (!mService.okToAnimate()) {
+            if (!mToken.okToAnimate()) {
                 doAnimation = false;
             }
         }
@@ -3390,6 +3408,38 @@
                 || (isChildWindow() && getParentWindow().isDockedResizing());
     }
 
+    void writeToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        writeIdentifierToProto(proto, IDENTIFIER);
+        proto.write(DISPLAY_ID, getDisplayId());
+        proto.write(STACK_ID, getStackId());
+        mAttrs.writeToProto(proto, ATTRIBUTES);
+        mGivenContentInsets.writeToProto(proto, GIVEN_CONTENT_INSETS);
+        mFrame.writeToProto(proto, FRAME);
+        mContainingFrame.writeToProto(proto, CONTAINING_FRAME);
+        mParentFrame.writeToProto(proto, PARENT_FRAME);
+        mContentFrame.writeToProto(proto, CONTENT_FRAME);
+        mContentInsets.writeToProto(proto, CONTENT_INSETS);
+        mAttrs.surfaceInsets.writeToProto(proto, SURFACE_INSETS);
+        mWinAnimator.writeToProto(proto, ANIMATOR);
+        proto.write(ANIMATING_EXIT, mAnimatingExit);
+        for (int i = 0; i < mChildren.size(); i++) {
+            mChildren.get(i).writeToProto(proto, CHILD_WINDOWS);
+        }
+        proto.end(token);
+    }
+
+    void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        proto.write(HASH_CODE, System.identityHashCode(this));
+        proto.write(USER_ID, UserHandle.getUserId(mOwnerUid));
+        final CharSequence title = getWindowTag();
+        if (title != null) {
+            proto.write(TITLE, title.toString());
+        }
+        proto.end(token);
+    }
+
     void dump(PrintWriter pw, String prefix, boolean dumpAll) {
         final TaskStack stack = getStack();
         pw.print(prefix); pw.print("mDisplayId="); pw.print(getDisplayId());
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 86265c29..9d3db7c 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -46,6 +46,8 @@
 import static com.android.server.wm.WindowManagerService.logWithStack;
 import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
 import static com.android.server.wm.WindowSurfacePlacer.SET_TURN_ON_SCREEN;
+import static com.android.server.wm.proto.WindowStateAnimatorProto.LAST_CLIP_RECT;
+import static com.android.server.wm.proto.WindowStateAnimatorProto.SURFACE;
 
 import android.content.Context;
 import android.graphics.Matrix;
@@ -57,6 +59,7 @@
 import android.os.Debug;
 import android.os.Trace;
 import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
 import android.view.DisplayInfo;
 import android.view.MagnificationSpec;
 import android.view.Surface.OutOfResourcesException;
@@ -369,7 +372,7 @@
         // we just started or just stopped animating by comparing mWasAnimating with isAnimationSet().
         mWasAnimating = mAnimating;
         final DisplayContent displayContent = mWin.getDisplayContent();
-        if (displayContent != null && mService.okToAnimate()) {
+        if (mWin.mToken.okToAnimate()) {
             // We will run animations as long as the display isn't frozen.
 
             if (mWin.isDrawnLw() && mAnimation != null) {
@@ -1812,7 +1815,7 @@
         // artifacts when we unfreeze the display if some different animation
         // is running.
         Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WSA#applyAnimationLocked");
-        if (mService.okToAnimate()) {
+        if (mWin.mToken.okToAnimate()) {
             int anim = mPolicy.selectAnimationLw(mWin, transit);
             int attr = -1;
             Animation a = null;
@@ -1885,6 +1888,15 @@
         mAnimation = newAnimation;
     }
 
+    void writeToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        mLastClipRect.writeToProto(proto, LAST_CLIP_RECT);
+        if (mSurfaceController != null) {
+            mSurfaceController.writeToProto(proto, SURFACE);
+        }
+        proto.end(token);
+    }
+
     public void dump(PrintWriter pw, String prefix, boolean dumpAll) {
         if (mAnimating || mLocalAnimating || mAnimationIsEntrance
                 || mAnimation != null) {
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 110d5cb..2e1e3f7 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -17,6 +17,8 @@
 package com.android.server.wm;
 
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static android.view.Surface.SCALING_MODE_SCALE_TO_WINDOW;
+
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_SURFACE_ALLOC;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
@@ -24,7 +26,8 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static android.view.Surface.SCALING_MODE_SCALE_TO_WINDOW;
+import static com.android.server.wm.proto.WindowSurfaceControllerProto.LAYER;
+import static com.android.server.wm.proto.WindowSurfaceControllerProto.SHOWN;
 
 import android.graphics.Point;
 import android.graphics.PointF;
@@ -33,6 +36,7 @@
 import android.os.IBinder;
 import android.os.Debug;
 import android.os.Trace;
+import android.util.proto.ProtoOutputStream;
 import android.view.Surface;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
@@ -534,6 +538,12 @@
         return mSurfaceH;
     }
 
+    void writeToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        proto.write(SHOWN, mSurfaceShown);
+        proto.write(LAYER, mSurfaceLayer);
+        proto.end(token);
+    }
 
     public void dump(PrintWriter pw, String prefix, boolean dumpAll) {
         if (dumpAll) {
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index e3033c9..422615b 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -16,9 +16,9 @@
 
 package com.android.server.wm;
 
+import android.util.proto.ProtoOutputStream;
 import java.util.Comparator;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS;
@@ -26,6 +26,8 @@
 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.WindowManagerService.UPDATE_FOCUS_NORMAL;
+import static com.android.server.wm.proto.WindowTokenProto.HASH_CODE;
+import static com.android.server.wm.proto.WindowTokenProto.WINDOWS;
 
 import android.os.Debug;
 import android.os.IBinder;
@@ -261,6 +263,16 @@
         super.onDisplayChanged(dc);
     }
 
+    void writeToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        proto.write(HASH_CODE, System.identityHashCode(this));
+        for (int i = 0; i < mChildren.size(); i++) {
+            final WindowState w = mChildren.get(i);
+            w.writeToProto(proto, WINDOWS);
+        }
+        proto.end(token);
+    }
+
     void dump(PrintWriter pw, String prefix) {
         pw.print(prefix); pw.print("windows="); pw.println(mChildren);
         pw.print(prefix); pw.print("windowType="); pw.print(windowType);
@@ -288,4 +300,12 @@
     String getName() {
         return toString();
     }
+
+    boolean okToDisplay() {
+        return mDisplayContent != null && mDisplayContent.okToDisplay();
+    }
+
+    boolean okToAnimate() {
+        return mDisplayContent != null && mDisplayContent.okToAnimate();
+    }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index ab919a2..eb9683e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -99,6 +99,7 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.ParceledListSlice;
+import android.content.pm.PermissionInfo;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.content.pm.StringParceledListSlice;
@@ -9572,6 +9573,9 @@
                         < android.os.Build.VERSION_CODES.M) {
                     return false;
                 }
+                if (!isRuntimePermission(permission)) {
+                    return false;
+                }
                 final PackageManager packageManager = mInjector.getPackageManager();
                 switch (grantState) {
                     case DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED: {
@@ -9598,6 +9602,8 @@
                 return true;
             } catch (SecurityException se) {
                 return false;
+            } catch (NameNotFoundException e) {
+                return false;
             } finally {
                 mInjector.binderRestoreCallingIdentity(ident);
             }
@@ -9647,6 +9653,13 @@
         }
     }
 
+    public boolean isRuntimePermission(String permissionName) throws NameNotFoundException {
+        final PackageManager packageManager = mInjector.getPackageManager();
+        PermissionInfo permissionInfo = packageManager.getPermissionInfo(permissionName, 0);
+        return (permissionInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
+                == PermissionInfo.PROTECTION_DANGEROUS;
+    }
+
     @Override
     public boolean isProvisioningAllowed(String action, String packageName) {
         Preconditions.checkNotNull(packageName);
diff --git a/services/tests/notification/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/notification/src/com/android/server/notification/ManagedServicesTest.java
index bd65f57..8242149 100644
--- a/services/tests/notification/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/notification/src/com/android/server/notification/ManagedServicesTest.java
@@ -38,6 +38,7 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.content.pm.UserInfo;
+import android.os.Build;
 import android.os.IBinder;
 import android.os.IInterface;
 import android.os.UserHandle;
@@ -146,19 +147,86 @@
                 service.onSettingRestored(
                         service.getConfig().secureSettingName,
                         mExpectedPrimary.get(approvalLevel).get(userId),
-                        userId);
+                        Build.VERSION_CODES.O, userId);
             }
             verifyExpectedApprovedEntries(service, true);
 
             for (int userId : mExpectedSecondary.get(approvalLevel).keySet()) {
                 service.onSettingRestored(service.getConfig().secondarySettingName,
-                        mExpectedSecondary.get(approvalLevel).get(userId), userId);
+                        mExpectedSecondary.get(approvalLevel).get(userId), Build.VERSION_CODES.O,
+                        userId);
             }
             verifyExpectedApprovedEntries(service);
         }
     }
 
     @Test
+    public void testBackupAndRestore_migration_preO() throws Exception {
+        ArrayMap backupPrimaryPackages = new ArrayMap<>();
+        backupPrimaryPackages.put(0, "backup.0:backup:0a");
+        backupPrimaryPackages.put(10, "10.backup");
+        backupPrimaryPackages.put(11, "eleven");
+        backupPrimaryPackages.put(12, "");
+        ArrayMap backupPrimaryComponentNames = new ArrayMap<>();
+        backupPrimaryComponentNames.put(0, "backup.first/whatever:a/b");
+        backupPrimaryComponentNames.put(10, "again/M1");
+        backupPrimaryComponentNames.put(11, "orange/youglad:itisnot/banana");
+        backupPrimaryComponentNames.put(12, "");
+        ArrayMap<Integer, ArrayMap<Integer, String>> backupPrimary = new ArrayMap<>();
+        backupPrimary.put(APPROVAL_BY_PACKAGE, backupPrimaryPackages);
+        backupPrimary.put(APPROVAL_BY_COMPONENT, backupPrimaryComponentNames);
+
+        ArrayMap backupSecondaryComponentNames = new ArrayMap<>();
+        backupSecondaryComponentNames.put(0, "secondary.1/component.Name");
+        backupSecondaryComponentNames.put(10,
+                "this.is.another.package.backup/with.Component:component.backup/2");
+        ArrayMap backupSecondaryPackages = new ArrayMap<>();
+        backupSecondaryPackages.put(0, "");
+        backupSecondaryPackages.put(10,
+                "this.is.another.package.backup:package.backup");
+        ArrayMap<Integer, ArrayMap<Integer, String>> backupSecondary = new ArrayMap<>();
+        backupSecondary.put(APPROVAL_BY_PACKAGE, backupSecondaryPackages);
+        backupSecondary.put(APPROVAL_BY_COMPONENT, backupSecondaryComponentNames);
+
+        for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
+            ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
+                    mIpm, approvalLevel);
+
+            // not an expected flow but a way to get data into the settings
+            for (int userId : mExpectedPrimary.get(approvalLevel).keySet()) {
+                service.onSettingRestored(
+                        service.getConfig().secureSettingName,
+                        mExpectedPrimary.get(approvalLevel).get(userId),
+                        Build.VERSION_CODES.O, userId);
+            }
+
+            for (int userId : mExpectedSecondary.get(approvalLevel).keySet()) {
+                service.onSettingRestored(service.getConfig().secondarySettingName,
+                        mExpectedSecondary.get(approvalLevel).get(userId), Build.VERSION_CODES.O,
+                        userId);
+            }
+
+            // actual test
+            for (int userId : backupPrimary.get(approvalLevel).keySet()) {
+                service.onSettingRestored(
+                        service.getConfig().secureSettingName,
+                        backupPrimary.get(approvalLevel).get(userId),
+                        Build.VERSION_CODES.N_MR1, userId);
+            }
+            verifyExpectedApprovedEntries(service, true);
+
+            for (int userId : backupSecondary.get(approvalLevel).keySet()) {
+                service.onSettingRestored(service.getConfig().secondarySettingName,
+                        backupSecondary.get(approvalLevel).get(userId),
+                        Build.VERSION_CODES.N_MR1, userId);
+            }
+            verifyExpectedApprovedEntries(service);
+            verifyExpectedApprovedEntries(service, backupPrimary.get(approvalLevel));
+            verifyExpectedApprovedEntries(service, backupSecondary.get(approvalLevel));
+        }
+    }
+
+    @Test
     public void testReadXml_migrationFromSettings() throws Exception {
         for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
             ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
@@ -619,25 +687,22 @@
         ArrayMap<Integer, String> verifyMap = primary
                 ? mExpectedPrimary.get(service.mApprovalLevel)
                 : mExpectedSecondary.get(service.mApprovalLevel);
+        verifyExpectedApprovedEntries(service, verifyMap);
+    }
+
+    private void verifyExpectedApprovedEntries(ManagedServices service,
+            ArrayMap<Integer, String> verifyMap) {
         for (int userId : verifyMap.keySet()) {
             for (String verifyValue : verifyMap.get(userId).split(":")) {
                 if (!TextUtils.isEmpty(verifyValue)) {
                     assertTrue("service type " + service.mApprovalLevel + ":"
-                            + verifyValue + " is not allowed for user " + userId,
+                                    + verifyValue + " is not allowed for user " + userId,
                             service.isPackageOrComponentAllowed(verifyValue, userId));
                 }
             }
         }
     }
 
-    private boolean isPackage(String packageOrComponent) {
-        final ComponentName component = ComponentName.unflattenFromString(packageOrComponent);
-        if (component != null) {
-            return false;
-        }
-        return true;
-    }
-
     private void writeExpectedValuesToSettings(int approvalLevel) {
         for (int userId : mExpectedPrimary.get(approvalLevel).keySet()) {
             Settings.Secure.putStringForUser(getContext().getContentResolver(), SETTING,
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
index 48464e5..ba22159 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
@@ -78,7 +78,7 @@
         service.mStackSupervisor.inResumeTopActivity = true;
         testStack.mResumedActivity = activityRecord;
 
-        final boolean waiting = testStack.checkReadyForSleepLocked();
+        final boolean waiting = testStack.goToSleepIfPossible(false);
 
         // Ensure we report not being ready for sleep.
         assertTrue(waiting);
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index 04b5bde..4ad92c7 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -218,6 +218,12 @@
 
             return createTestStack(stackId, createOnTop);
         }
+
+        // Always keep things awake
+        @Override
+        boolean hasAwakeDisplay() {
+            return true;
+        }
     }
 
     private static WindowManagerService prepareMockWindowManager() {
diff --git a/services/tests/servicestests/src/com/android/server/wallpaper/WallpaperServiceTests.java b/services/tests/servicestests/src/com/android/server/wallpaper/WallpaperServiceTests.java
index 0d29e89..9c010a0 100644
--- a/services/tests/servicestests/src/com/android/server/wallpaper/WallpaperServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/wallpaper/WallpaperServiceTests.java
@@ -17,10 +17,14 @@
 package com.android.server.wallpaper;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
 import android.app.WallpaperColors;
+import android.os.Handler;
+import android.os.Message;
 import android.os.SystemClock;
 import android.service.wallpaper.WallpaperService;
+import android.support.test.annotation.UiThreadTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
@@ -28,18 +32,31 @@
 import org.junit.runner.RunWith;
 
 import java.util.concurrent.CountDownLatch;
+import java.util.function.Supplier;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class WallpaperServiceTests {
 
+    @UiThreadTest
     @Test
     public void testNotifyColorsChanged_rateLimit() throws Exception {
+        long[] clockOffset = {0};
+        boolean[] postDelayed = {false};
+        Supplier<Long> clockFunction = () -> SystemClock.elapsedRealtime() + clockOffset[0];
+        Handler handler = new Handler() {
+            @Override
+            public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
+                postDelayed[0] = true;
+                return super.sendMessageAtTime(msg, uptimeMillis);
+            }
+        };
+
         CountDownLatch eventCountdown = new CountDownLatch(2);
         WallpaperService service = new WallpaperService() {
             @Override
             public Engine onCreateEngine() {
-                return new WallpaperService.Engine() {
+                return new WallpaperService.Engine(clockFunction, handler) {
                     @Override
                     public WallpaperColors onComputeColors() {
                         eventCountdown.countDown();
@@ -59,8 +76,11 @@
         engine.notifyColorsChanged();
         assertEquals("OnComputeColors should have been throttled.",
                 1, eventCountdown.getCount());
-        // Called after being deferred.
-        engine.setClockFunction(() ->  SystemClock.elapsedRealtime() + 1500);
+        // Should have been posted to the handler.
+        assertTrue("Event should have been delayed", postDelayed[0]);
+
+        // Called again after being deferred.
+        clockOffset[0] = 1500;
         engine.notifyColorsChanged();
         assertEquals("OnComputeColors should have been deferred.",
                 0, eventCountdown.getCount());
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 95adc9c..9f57f49 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -29,6 +29,7 @@
 import static org.mockito.Mockito.doAnswer;
 
 import android.os.PowerSaveState;
+import android.util.proto.ProtoOutputStream;
 import org.mockito.invocation.InvocationOnMock;
 
 import android.annotation.Nullable;
@@ -596,6 +597,11 @@
     }
 
     @Override
+    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+
+    }
+
+    @Override
     public void dump(String prefix, PrintWriter writer, String[] args) {
 
     }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
index 7ff1110..40c79bb 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
@@ -92,7 +92,9 @@
         boolean mOnTop = false;
 
         TestAppWindowToken(DisplayContent dc) {
-            super(dc.mService, new IApplicationToken.Stub() {}, false, dc, true /* fillsParent */,
+            super(dc.mService, new IApplicationToken.Stub() {
+                public String getName() {return null;}
+                }, false, dc, true /* fillsParent */,
                     null /* overrideConfig */, null /* bounds */);
         }
 
@@ -287,6 +289,10 @@
         public IBinder asBinder() {
             return mBinder;
         }
+        @Override
+        public String getName() {
+            return null;
+        }
     }
 
     /** Used to track resize reports. */
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 5fb83ab..e448fb2 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -795,34 +795,57 @@
         return rtString;
     }
 
+    /**
+     * Convert RIL Service State to String
+     *
+     * @param serviceState
+     * @return String representation of the ServiceState
+     *
+     * @hide
+     */
+    public static String rilServiceStateToString(int serviceState) {
+        switch(serviceState) {
+            case STATE_IN_SERVICE:
+                return "IN_SERVICE";
+            case STATE_OUT_OF_SERVICE:
+                return "OUT_OF_SERVICE";
+            case STATE_EMERGENCY_ONLY:
+                return "EMERGENCY_ONLY";
+            case STATE_POWER_OFF:
+                return "POWER_OFF";
+            default:
+                return "UNKNOWN";
+        }
+    }
+
     @Override
     public String toString() {
-        String radioTechnology = rilRadioTechnologyToString(mRilVoiceRadioTechnology);
-        String dataRadioTechnology = rilRadioTechnologyToString(mRilDataRadioTechnology);
-
-        return (mVoiceRegState + " " + mDataRegState
-                + " "
-                + "voice " + getRoamingLogString(mVoiceRoamingType)
-                + " "
-                + "data " + getRoamingLogString(mDataRoamingType)
-                + " " + mVoiceOperatorAlphaLong
-                + " " + mVoiceOperatorAlphaShort
-                + " " + mVoiceOperatorNumeric
-                + " " + mDataOperatorAlphaLong
-                + " " + mDataOperatorAlphaShort
-                + " " + mDataOperatorNumeric
-                + " " + (mIsManualNetworkSelection ? "(manual)" : "")
-                + " " + radioTechnology
-                + " " + dataRadioTechnology
-                + " " + (mCssIndicator ? "CSS supported" : "CSS not supported")
-                + " " + mNetworkId
-                + " " + mSystemId
-                + " RoamInd=" + mCdmaRoamingIndicator
-                + " DefRoamInd=" + mCdmaDefaultRoamingIndicator
-                + " EmergOnly=" + mIsEmergencyOnly
-                + " IsDataRoamingFromRegistration=" + mIsDataRoamingFromRegistration
-                + " IsUsingCarrierAggregation=" + mIsUsingCarrierAggregation
-                + " mLteEarfcnRsrpBoost=" + mLteEarfcnRsrpBoost);
+        return new StringBuilder().append("{mVoiceRegState=").append(mVoiceRegState)
+            .append("(" + rilServiceStateToString(mVoiceRegState) + ")")
+            .append(", mDataRegState=").append(mDataRegState)
+            .append("(" + rilServiceStateToString(mDataRegState) + ")")
+            .append(", mVoiceRoamingType=").append(getRoamingLogString(mVoiceRoamingType))
+            .append(", mDataRoamingType=").append(getRoamingLogString(mDataRoamingType))
+            .append(", mVoiceOperatorAlphaLong=").append(mVoiceOperatorAlphaLong)
+            .append(", mVoiceOperatorAlphaShort=").append(mVoiceOperatorAlphaShort)
+            .append(", mDataOperatorAlphaLong=").append(mDataOperatorAlphaLong)
+            .append(", mDataOperatorAlphaShort=").append(mDataOperatorAlphaShort)
+            .append(", isManualNetworkSelection=").append(mIsManualNetworkSelection)
+            .append(mIsManualNetworkSelection ? "(manual)" : "(automatic)")
+            .append(", mRilVoiceRadioTechnology=").append(mRilVoiceRadioTechnology)
+            .append("(" + rilRadioTechnologyToString(mRilVoiceRadioTechnology) + ")")
+            .append(", mRilDataRadioTechnology=").append(mRilDataRadioTechnology)
+            .append("(" + rilRadioTechnologyToString(mRilDataRadioTechnology) + ")")
+            .append(", mCssIndicator=").append(mCssIndicator ? "supported" : "unsupported")
+            .append(", mNetworkId=").append(mNetworkId)
+            .append(", mSystemId=").append(mSystemId)
+            .append(", mCdmaRoamingIndicator=").append(mCdmaRoamingIndicator)
+            .append(", mCdmaDefaultRoamingIndicator=").append(mCdmaDefaultRoamingIndicator)
+            .append(", mIsEmergencyOnly=").append(mIsEmergencyOnly)
+            .append(", mIsDataRoamingFromRegistration=").append(mIsDataRoamingFromRegistration)
+            .append(", mIsUsingCarrierAggregation=").append(mIsUsingCarrierAggregation)
+            .append(", mLteEarfcnRsrpBoost=").append(mLteEarfcnRsrpBoost)
+            .append("}").toString();
     }
 
     private void setNullState(int state) {
diff --git a/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java b/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java
index dcb9723..c4965a0 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java
@@ -25,6 +25,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyLong;
 import static org.mockito.Matchers.anyObject;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
@@ -45,6 +46,7 @@
 import android.net.NetworkStats;
 import android.net.RouteInfo;
 import android.net.util.SharedLog;
+import android.os.ConditionVariable;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.INetworkManagementService;
@@ -110,6 +112,12 @@
         Settings.Global.putInt(mContentResolver, TETHER_OFFLOAD_DISABLED, 0);
     }
 
+    private void waitForIdle() {
+        ConditionVariable cv = new ConditionVariable();
+        new Handler(Looper.getMainLooper()).post(() -> { cv.open(); });
+        cv.block();
+    }
+
     private OffloadController makeOffloadController() throws Exception {
         OffloadController offload = new OffloadController(new Handler(Looper.getMainLooper()),
                 mHardware, mContentResolver, mNMService, new SharedLog("test"));
@@ -417,4 +425,68 @@
         entry = stats.getValues(ethernetPosition, entry);
         assertNetworkStats(ethernetIface, ethernetStats, entry);
     }
+
+    @Test
+    public void testSetInterfaceQuota() throws Exception {
+        setupFunctioningHardwareInterface();
+        enableOffload();
+
+        final OffloadController offload = makeOffloadController();
+        offload.start();
+
+        final String ethernetIface = "eth1";
+        final String mobileIface = "rmnet_data0";
+        final long ethernetLimit = 12345;
+        final long mobileLimit = 12345678;
+
+        final LinkProperties lp = new LinkProperties();
+        lp.setInterfaceName(ethernetIface);
+        offload.setUpstreamLinkProperties(lp);
+
+        ITetheringStatsProvider provider = mTetherStatsProviderCaptor.getValue();
+        final InOrder inOrder = inOrder(mHardware);
+        when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(true);
+        when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(true);
+
+        // Applying an interface quota to the current upstream immediately sends it to the hardware.
+        provider.setInterfaceQuota(ethernetIface, ethernetLimit);
+        waitForIdle();
+        inOrder.verify(mHardware).setDataLimit(ethernetIface, ethernetLimit);
+        inOrder.verifyNoMoreInteractions();
+
+        // Applying an interface quota to another upstream does not take any immediate action.
+        provider.setInterfaceQuota(mobileIface, mobileLimit);
+        waitForIdle();
+        inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong());
+
+        // Switching to that upstream causes the quota to be applied if the parameters were applied
+        // correctly.
+        lp.setInterfaceName(mobileIface);
+        offload.setUpstreamLinkProperties(lp);
+        waitForIdle();
+        inOrder.verify(mHardware).setDataLimit(mobileIface, mobileLimit);
+
+        // Setting a limit of ITetheringStatsProvider.QUOTA_UNLIMITED causes the limit to be set
+        // to Long.MAX_VALUE.
+        provider.setInterfaceQuota(mobileIface, ITetheringStatsProvider.QUOTA_UNLIMITED);
+        waitForIdle();
+        inOrder.verify(mHardware).setDataLimit(mobileIface, Long.MAX_VALUE);
+
+        // If setting upstream parameters fails, then the data limit is not set.
+        when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(false);
+        lp.setInterfaceName(ethernetIface);
+        offload.setUpstreamLinkProperties(lp);
+        provider.setInterfaceQuota(mobileIface, mobileLimit);
+        waitForIdle();
+        inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong());
+
+        // If setting the data limit fails while changing upstreams, offload is stopped.
+        when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(true);
+        when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(false);
+        lp.setInterfaceName(mobileIface);
+        offload.setUpstreamLinkProperties(lp);
+        provider.setInterfaceQuota(mobileIface, mobileLimit);
+        waitForIdle();
+        inOrder.verify(mHardware).stopOffloadControl();
+    }
 }
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 9005c5b..c25c4f6 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -574,6 +574,11 @@
         if (file_op.xml_to_flatten) {
           std::vector<std::unique_ptr<xml::XmlResource>> versioned_docs =
               LinkAndVersionXmlFile(table, &file_op);
+          if (versioned_docs.empty()) {
+            error = true;
+            continue;
+          }
+
           for (std::unique_ptr<xml::XmlResource>& doc : versioned_docs) {
             std::string dst_path = file_op.dst_path;
             if (doc->file.config != file_op.config) {