Merge "Add APF black list for ether-type"
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/IncidentHelper_test.cpp b/cmds/incident_helper/tests/IncidentHelper_test.cpp
index 04109c9..04dd8de 100644
--- a/cmds/incident_helper/tests/IncidentHelper_test.cpp
+++ b/cmds/incident_helper/tests/IncidentHelper_test.cpp
@@ -97,7 +97,7 @@
     record2->set_last_change(2067286206l);
     record2->set_prevent_suspend_time(0l);
 
-    int fd = open(testFile.c_str(), O_RDONLY, 0444);
+    int fd = open(testFile.c_str(), O_RDONLY);
     ASSERT_TRUE(fd != -1);
 
     CaptureStdout();
@@ -149,7 +149,7 @@
     expected.mutable_summary()->mutable_ram()
         ->set_raw_text("3843972K total, 281424K free, 116764K buffers, 1777452K cached, 1136K shmem, 217916K slab");
 
-    int fd = open(testFile.c_str(), O_RDONLY, 0444);
+    int fd = open(testFile.c_str(), O_RDONLY);
     ASSERT_TRUE(fd != -1);
 
     CaptureStdout();
@@ -187,11 +187,11 @@
     expected.mutable_summary()->mutable_ram()
         ->set_raw_text("3843972K total, 281424K free, 116764K buffers, 1777452K cached, 1136K shmem, 217916K slab");
 
-    int fd = open(testFile.c_str(), O_RDONLY, 0444);
+    int fd = open(testFile.c_str(), O_RDONLY);
     ASSERT_TRUE(fd != -1);
 
     CaptureStdout();
     ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO));
     EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected));
     close(fd);
-}
\ No newline at end of file
+}
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/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/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/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/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/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index dee5a93..b3d2b66 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -15,14 +15,11 @@
 import android.graphics.BitmapFactory;
 import android.graphics.Color;
 import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
 import android.net.ConnectivityManager;
-import android.net.NetworkBadging;
 import android.os.BatteryManager;
 import android.os.UserManager;
 import android.print.PrintManager;
 import android.provider.Settings;
-import android.view.View;
 
 import com.android.internal.util.UserIcons;
 import com.android.settingslib.drawable.UserIconDrawable;
@@ -35,12 +32,12 @@
     private static String sServicesSystemSharedLibPackageName;
     private static String sSharedSystemSharedLibPackageName;
 
-    static final int[] WIFI_PIE_FOR_BADGING = {
-          com.android.internal.R.drawable.ic_signal_wifi_badged_0_bars,
-          com.android.internal.R.drawable.ic_signal_wifi_badged_1_bar,
-          com.android.internal.R.drawable.ic_signal_wifi_badged_2_bars,
-          com.android.internal.R.drawable.ic_signal_wifi_badged_3_bars,
-          com.android.internal.R.drawable.ic_signal_wifi_badged_4_bars
+    static final int[] WIFI_PIE = {
+        com.android.internal.R.drawable.ic_wifi_signal_0,
+        com.android.internal.R.drawable.ic_wifi_signal_1,
+        com.android.internal.R.drawable.ic_wifi_signal_2,
+        com.android.internal.R.drawable.ic_wifi_signal_3,
+        com.android.internal.R.drawable.ic_wifi_signal_4
     };
 
     /**
@@ -272,42 +269,17 @@
     }
 
     /**
-     * Returns a badged Wifi icon drawable.
+     * Returns the Wifi icon resource for a given RSSI level.
      *
-     * <p>The first layer contains the Wifi pie and the second layer contains the badge. Callers
-     * should set the drawable to the appropriate size and tint color.
-     *
-     * @param context The caller's context (must have access to internal resources)
      * @param level The number of bars to show (0-4)
-     * @param badge The badge enum {@see android.net.ScoredNetwork}
      *
-     * @throws IllegalArgumentException if an invalid badge enum is given
-     *
-     * @deprecated TODO(sghuman): Finalize the form of this method and then move it to a new
-     *         location.
+     * @throws IllegalArgumentException if an invalid RSSI level is given.
      */
-    public static LayerDrawable getBadgedWifiIcon(Context context, int level, int badge) {
-        return new LayerDrawable(
-                new Drawable[] {
-                        context.getDrawable(WIFI_PIE_FOR_BADGING[level]),
-                        context.getDrawable(getWifiBadgeResource(badge))
-                });
-    }
-
-    private static int getWifiBadgeResource(int badge) {
-        switch (badge) {
-            case NetworkBadging.BADGING_NONE:
-                return View.NO_ID;
-            case NetworkBadging.BADGING_SD:
-                return com.android.internal.R.drawable.ic_signal_wifi_badged_sd;
-            case NetworkBadging.BADGING_HD:
-                return com.android.internal.R.drawable.ic_signal_wifi_badged_hd;
-            case NetworkBadging.BADGING_4K:
-                return com.android.internal.R.drawable.ic_signal_wifi_badged_4k;
-            default:
-                throw new IllegalArgumentException(
-                    "No badge resource found for badge value: " + badge);
+    public static int getWifiIconResource(int level) {
+        if (level < 0 || level >= WIFI_PIE.length) {
+            throw new IllegalArgumentException("No Wifi icon found for level: " + level);
         }
+        return WIFI_PIE[level];
     }
 
     public static int getDefaultStorageManagerDaysToRetain(Resources resources) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
index 2c4f9c4..f3be8d0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
@@ -21,7 +21,6 @@
 import android.content.res.TypedArray;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.StateListDrawable;
-import android.net.NetworkBadging;
 import android.net.wifi.WifiConfiguration;
 import android.os.Looper;
 import android.os.UserHandle;
@@ -37,6 +36,7 @@
 import com.android.settingslib.R;
 import com.android.settingslib.TronUtils;
 import com.android.settingslib.Utils;
+import com.android.settingslib.wifi.AccessPoint.Speed;
 
 public class AccessPointPreference extends Preference {
 
@@ -71,7 +71,7 @@
     private int mLevel;
     private CharSequence mContentDescription;
     private int mDefaultIconResId;
-    private int mWifiSpeed = NetworkBadging.BADGING_NONE;
+    private int mWifiSpeed = Speed.NONE;
 
     public static String generatePreferenceKey(AccessPoint accessPoint) {
         StringBuilder builder = new StringBuilder();
@@ -179,9 +179,7 @@
         }
         TronUtils.logWifiSettingsSpeed(context, mWifiSpeed);
 
-        // TODO(b/62355275): Revert this to N code after deleting NetworkBadging API
-        Drawable drawable = NetworkBadging.getWifiIcon(
-                level, NetworkBadging.BADGING_NONE, getContext().getTheme());
+        Drawable drawable = context.getDrawable(Utils.getWifiIconResource(level));
         if (!mForSavedNetworks && drawable != null) {
             drawable.setTint(Utils.getColorAttr(context, android.R.attr.colorControlNormal));
             setIcon(drawable);
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/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/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/TextServicesManagerService.java b/services/core/java/com/android/server/TextServicesManagerService.java
index a357909..951a073 100644
--- a/services/core/java/com/android/server/TextServicesManagerService.java
+++ b/services/core/java/com/android/server/TextServicesManagerService.java
@@ -75,12 +75,9 @@
     private final Context mContext;
     private final TextServicesMonitor mMonitor;
     private final SparseArray<TextServicesData> mUserData = new SparseArray<>();
-    private final TextServicesSettings mSettings;
     @NonNull
     private final UserManager mUserManager;
     private final Object mLock = new Object();
-    @GuardedBy("mLock")
-    private int mSpellCheckerMapUpdateCount = 0;
 
     private static class TextServicesData {
         @UserIdInt
@@ -88,12 +85,180 @@
         private final HashMap<String, SpellCheckerInfo> mSpellCheckerMap;
         private final ArrayList<SpellCheckerInfo> mSpellCheckerList;
         private final HashMap<String, SpellCheckerBindGroup> mSpellCheckerBindGroups;
+        private final Context mContext;
+        private final ContentResolver mResolver;
+        public int mUpdateCount = 0;
 
-        public TextServicesData(@UserIdInt int userId) {
+        public TextServicesData(@UserIdInt int userId, @NonNull Context context) {
             mUserId = userId;
             mSpellCheckerMap = new HashMap<>();
             mSpellCheckerList = new ArrayList<>();
             mSpellCheckerBindGroups = new HashMap<>();
+            mContext = context;
+            mResolver = context.getContentResolver();
+        }
+
+        private void putString(final String key, final String str) {
+            Settings.Secure.putStringForUser(mResolver, key, str, mUserId);
+        }
+
+        @Nullable
+        private String getString(@NonNull final String key, @Nullable final String defaultValue) {
+            final String result;
+            result = Settings.Secure.getStringForUser(mResolver, key, mUserId);
+            return result != null ? result : defaultValue;
+        }
+
+        private void putInt(final String key, final int value) {
+            Settings.Secure.putIntForUser(mResolver, key, value, mUserId);
+        }
+
+        private int getInt(final String key, final int defaultValue) {
+            return Settings.Secure.getIntForUser(mResolver, key, defaultValue, mUserId);
+        }
+
+        private boolean getBoolean(final String key, final boolean defaultValue) {
+            return getInt(key, defaultValue ? 1 : 0) == 1;
+        }
+
+        private void putSelectedSpellChecker(@Nullable String sciId) {
+            if (TextUtils.isEmpty(sciId)) {
+                // OK to coalesce to null, since getSelectedSpellChecker() can take care of the
+                // empty data scenario.
+                putString(Settings.Secure.SELECTED_SPELL_CHECKER, null);
+            } else {
+                putString(Settings.Secure.SELECTED_SPELL_CHECKER, sciId);
+            }
+        }
+
+        private void putSelectedSpellCheckerSubtype(int hashCode) {
+            putInt(Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE, hashCode);
+        }
+
+        @NonNull
+        private String getSelectedSpellChecker() {
+            return getString(Settings.Secure.SELECTED_SPELL_CHECKER, "");
+        }
+
+        public int getSelectedSpellCheckerSubtype(final int defaultValue) {
+            return getInt(Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE, defaultValue);
+        }
+
+        public boolean isSpellCheckerEnabled() {
+            return getBoolean(Settings.Secure.SPELL_CHECKER_ENABLED, true);
+        }
+
+        @Nullable
+        public SpellCheckerInfo getCurrentSpellChecker() {
+            final String curSpellCheckerId = getSelectedSpellChecker();
+            if (TextUtils.isEmpty(curSpellCheckerId)) {
+                return null;
+            }
+            return mSpellCheckerMap.get(curSpellCheckerId);
+        }
+
+        public void setCurrentSpellChecker(SpellCheckerInfo sci) {
+            putSelectedSpellChecker(sci.getId());
+            putSelectedSpellCheckerSubtype(SpellCheckerSubtype.SUBTYPE_ID_NONE);
+        }
+
+        private void initializeTextServicesData() {
+            if (DBG) {
+                Slog.d(TAG, "initializeTextServicesData for user: " + mUserId);
+            }
+            mSpellCheckerList.clear();
+            mSpellCheckerMap.clear();
+            mUpdateCount++;
+            final PackageManager pm = mContext.getPackageManager();
+            // Note: We do not specify PackageManager.MATCH_ENCRYPTION_* flags here because the
+            // default behavior of PackageManager is exactly what we want.  It by default picks up
+            // appropriate services depending on the unlock state for the specified user.
+            final List<ResolveInfo> services = pm.queryIntentServicesAsUser(
+                    new Intent(SpellCheckerService.SERVICE_INTERFACE), PackageManager.GET_META_DATA,
+                    mUserId);
+            final int N = services.size();
+            for (int i = 0; i < N; ++i) {
+                final ResolveInfo ri = services.get(i);
+                final ServiceInfo si = ri.serviceInfo;
+                final ComponentName compName = new ComponentName(si.packageName, si.name);
+                if (!android.Manifest.permission.BIND_TEXT_SERVICE.equals(si.permission)) {
+                    Slog.w(TAG, "Skipping text service " + compName
+                            + ": it does not require the permission "
+                            + android.Manifest.permission.BIND_TEXT_SERVICE);
+                    continue;
+                }
+                if (DBG) Slog.d(TAG, "Add: " + compName + " for user: " + mUserId);
+                try {
+                    final SpellCheckerInfo sci = new SpellCheckerInfo(mContext, ri);
+                    if (sci.getSubtypeCount() <= 0) {
+                        Slog.w(TAG, "Skipping text service " + compName
+                                + ": it does not contain subtypes.");
+                        continue;
+                    }
+                    mSpellCheckerList.add(sci);
+                    mSpellCheckerMap.put(sci.getId(), sci);
+                } catch (XmlPullParserException e) {
+                    Slog.w(TAG, "Unable to load the spell checker " + compName, e);
+                } catch (IOException e) {
+                    Slog.w(TAG, "Unable to load the spell checker " + compName, e);
+                }
+            }
+            if (DBG) {
+                Slog.d(TAG, "initializeSpellCheckerMap: " + mSpellCheckerList.size() + ","
+                        + mSpellCheckerMap.size());
+            }
+        }
+
+        private void dump(PrintWriter pw) {
+            int spellCheckerIndex = 0;
+            pw.println("  User #" + mUserId);
+            pw.println("  Spell Checkers:");
+            pw.println("  Spell Checkers: " +  "mUpdateCount=" + mUpdateCount);
+            for (final SpellCheckerInfo info : mSpellCheckerMap.values()) {
+                pw.println("  Spell Checker #" + spellCheckerIndex);
+                info.dump(pw, "    ");
+                ++spellCheckerIndex;
+            }
+
+            pw.println("");
+            pw.println("  Spell Checker Bind Groups:");
+            HashMap<String, SpellCheckerBindGroup> spellCheckerBindGroups = mSpellCheckerBindGroups;
+            for (final Map.Entry<String, SpellCheckerBindGroup> ent
+                    : spellCheckerBindGroups.entrySet()) {
+                final SpellCheckerBindGroup grp = ent.getValue();
+                pw.println("    " + ent.getKey() + " " + grp + ":");
+                pw.println("      " + "mInternalConnection=" + grp.mInternalConnection);
+                pw.println("      " + "mSpellChecker=" + grp.mSpellChecker);
+                pw.println("      " + "mUnbindCalled=" + grp.mUnbindCalled);
+                pw.println("      " + "mConnected=" + grp.mConnected);
+                final int numPendingSessionRequests = grp.mPendingSessionRequests.size();
+                for (int j = 0; j < numPendingSessionRequests; j++) {
+                    final SessionRequest req = grp.mPendingSessionRequests.get(j);
+                    pw.println("      " + "Pending Request #" + j + ":");
+                    pw.println("        " + "mTsListener=" + req.mTsListener);
+                    pw.println("        " + "mScListener=" + req.mScListener);
+                    pw.println(
+                            "        " + "mScLocale=" + req.mLocale + " mUid=" + req.mUserId);
+                }
+                final int numOnGoingSessionRequests = grp.mOnGoingSessionRequests.size();
+                for (int j = 0; j < numOnGoingSessionRequests; j++) {
+                    final SessionRequest req = grp.mOnGoingSessionRequests.get(j);
+                    pw.println("      " + "On going Request #" + j + ":");
+                    ++j;
+                    pw.println("        " + "mTsListener=" + req.mTsListener);
+                    pw.println("        " + "mScListener=" + req.mScListener);
+                    pw.println(
+                            "        " + "mScLocale=" + req.mLocale + " mUid=" + req.mUserId);
+                }
+                final int N = grp.mListeners.getRegisteredCallbackCount();
+                for (int j = 0; j < N; j++) {
+                    final ISpellCheckerSessionListener mScListener =
+                            grp.mListeners.getRegisteredCallbackItem(j);
+                    pw.println("      " + "Listener #" + j + ":");
+                    pw.println("        " + "mScListener=" + mScListener);
+                    pw.println("        " + "mGroup=" + grp);
+                }
+            }
         }
     }
 
@@ -153,18 +318,17 @@
 
         mMonitor = new TextServicesMonitor();
         mMonitor.register(context, null, UserHandle.ALL, true);
-        mSettings = new TextServicesSettings(context.getContentResolver());
     }
 
     private void initializeInternalStateLocked(@UserIdInt int userId) {
         TextServicesData tsd = mUserData.get(userId);
         if (tsd == null) {
-            tsd = new TextServicesData(userId);
+            tsd = new TextServicesData(userId, mContext);
             mUserData.put(userId, tsd);
         }
 
-        updateTextServiceDataLocked(tsd);
-        SpellCheckerInfo sci = getCurrentSpellCheckerInternalLocked(tsd);
+        tsd.initializeTextServicesData();
+        SpellCheckerInfo sci = tsd.getCurrentSpellChecker();
         if (sci == null) {
             sci = findAvailSpellCheckerLocked(null, tsd);
             if (sci != null) {
@@ -176,14 +340,6 @@
         }
     }
 
-    private void updateTextServiceDataLocked(TextServicesData tsd) {
-        if (DBG) {
-            Slog.d(TAG, "updateTextServiceDataLocked for user: " + tsd.mUserId);
-        }
-        buildSpellCheckerMapLocked(mContext, tsd.mSpellCheckerList, tsd.mSpellCheckerMap,
-                tsd.mUserId);
-    }
-
     private final class TextServicesMonitor extends PackageMonitor {
         @Override
         public void onSomePackagesChanged() {
@@ -197,8 +353,8 @@
                 if (tsd == null) return;
 
                 // TODO: Update for each locale
-                SpellCheckerInfo sci = getCurrentSpellCheckerInternalLocked(tsd);
-                updateTextServiceDataLocked(tsd);
+                SpellCheckerInfo sci = tsd.getCurrentSpellChecker();
+                tsd.initializeTextServicesData();
                 // If no spell checker is enabled, just return. The user should explicitly
                 // enable the spell checker.
                 if (sci == null) return;
@@ -219,51 +375,6 @@
         }
     }
 
-    private void buildSpellCheckerMapLocked(Context context,
-            ArrayList<SpellCheckerInfo> list, HashMap<String, SpellCheckerInfo> map,
-            @UserIdInt int userId) {
-        list.clear();
-        map.clear();
-        mSpellCheckerMapUpdateCount++;
-        final PackageManager pm = context.getPackageManager();
-        // Note: We do not specify PackageManager.MATCH_ENCRYPTION_* flags here because the default
-        // behavior of PackageManager is exactly what we want.  It by default picks up appropriate
-        // services depending on the unlock state for the specified user.
-        final List<ResolveInfo> services = pm.queryIntentServicesAsUser(
-                new Intent(SpellCheckerService.SERVICE_INTERFACE), PackageManager.GET_META_DATA,
-                userId);
-        final int N = services.size();
-        for (int i = 0; i < N; ++i) {
-            final ResolveInfo ri = services.get(i);
-            final ServiceInfo si = ri.serviceInfo;
-            final ComponentName compName = new ComponentName(si.packageName, si.name);
-            if (!android.Manifest.permission.BIND_TEXT_SERVICE.equals(si.permission)) {
-                Slog.w(TAG, "Skipping text service " + compName
-                        + ": it does not require the permission "
-                        + android.Manifest.permission.BIND_TEXT_SERVICE);
-                continue;
-            }
-            if (DBG) Slog.d(TAG, "Add: " + compName + " for user: " + userId);
-            try {
-                final SpellCheckerInfo sci = new SpellCheckerInfo(context, ri);
-                if (sci.getSubtypeCount() <= 0) {
-                    Slog.w(TAG, "Skipping text service " + compName
-                            + ": it does not contain subtypes.");
-                    continue;
-                }
-                list.add(sci);
-                map.put(sci.getId(), sci);
-            } catch (XmlPullParserException e) {
-                Slog.w(TAG, "Unable to load the spell checker " + compName, e);
-            } catch (IOException e) {
-                Slog.w(TAG, "Unable to load the spell checker " + compName, e);
-            }
-        }
-        if (DBG) {
-            Slog.d(TAG, "buildSpellCheckerMapLocked: " + list.size() + "," + map.size());
-        }
-    }
-
     private boolean bindCurrentSpellCheckerService(
             Intent service, ServiceConnection conn, int flags, @UserIdInt int userId) {
         if (service == null || conn == null) {
@@ -348,21 +459,10 @@
             TextServicesData tsd = mUserData.get(userId);
             if (tsd == null) return null;
 
-            return getCurrentSpellCheckerInternalLocked(tsd);
+            return tsd.getCurrentSpellChecker();
         }
     }
 
-    private SpellCheckerInfo getCurrentSpellCheckerInternalLocked(TextServicesData tsd) {
-        final String curSpellCheckerId = mSettings.getSelectedSpellChecker(tsd.mUserId);
-        if (DBG) {
-            Slog.w(TAG, "getCurrentSpellChecker: " + curSpellCheckerId);
-        }
-        if (TextUtils.isEmpty(curSpellCheckerId)) {
-            return null;
-        }
-        return tsd.mSpellCheckerMap.get(curSpellCheckerId);
-    }
-
     // TODO: Respect allowImplicitlySelectedSubtype
     // TODO: Save SpellCheckerSubtype by supported languages by looking at "locale".
     @Override
@@ -378,12 +478,11 @@
             if (tsd == null) return null;
 
             subtypeHashCode =
-                    mSettings.getSelectedSpellCheckerSubtype(SpellCheckerSubtype.SUBTYPE_ID_NONE,
-                            userId);
+                    tsd.getSelectedSpellCheckerSubtype(SpellCheckerSubtype.SUBTYPE_ID_NONE);
             if (DBG) {
                 Slog.w(TAG, "getCurrentSpellCheckerSubtype: " + subtypeHashCode);
             }
-            sci = getCurrentSpellCheckerInternalLocked(tsd);
+            sci = tsd.getCurrentSpellChecker();
             systemLocale = mContext.getResources().getConfiguration().locale;
         }
         if (sci == null || sci.getSubtypeCount() == 0) {
@@ -496,7 +595,7 @@
             TextServicesData tsd = mUserData.get(userId);
             if (tsd == null) return false;
 
-            return isSpellCheckerEnabledLocked(userId);
+            return tsd.isSpellCheckerEnabled();
         }
     }
 
@@ -578,42 +677,7 @@
         }
         final long ident = Binder.clearCallingIdentity();
         try {
-            mSettings.putSelectedSpellChecker(sciId, tsd.mUserId);
-            setCurrentSpellCheckerSubtypeLocked(0, tsd);
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    private void setCurrentSpellCheckerSubtypeLocked(int hashCode, TextServicesData tsd) {
-        if (DBG) {
-            Slog.w(TAG, "setCurrentSpellCheckerSubtype: " + hashCode);
-        }
-
-        final SpellCheckerInfo sci = getCurrentSpellCheckerInternalLocked(tsd);
-        int tempHashCode = 0;
-        for (int i = 0; sci != null && i < sci.getSubtypeCount(); ++i) {
-            if(sci.getSubtypeAt(i).hashCode() == hashCode) {
-                tempHashCode = hashCode;
-                break;
-            }
-        }
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            mSettings.putSelectedSpellCheckerSubtype(tempHashCode, tsd.mUserId);
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    private boolean isSpellCheckerEnabledLocked(@UserIdInt int userId) {
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            final boolean retval = mSettings.isSpellCheckerEnabled(userId);
-            if (DBG) {
-                Slog.w(TAG, "getSpellCheckerEnabled: " + retval);
-            }
-            return retval;
+            tsd.setCurrentSpellChecker(sci);
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
@@ -626,14 +690,11 @@
         if (args.length == 0) {  // Dump all users' data
             synchronized (mLock) {
                 pw.println("Current Text Services Manager state:");
-                pw.println("  Text Services: mSpellCheckerMapUpdateCount="
-                        + mSpellCheckerMapUpdateCount);
                 pw.println("  Users:");
                 final int numOfUsers = mUserData.size();
                 for (int i = 0; i < numOfUsers; i++) {
-                    int userId = mUserData.keyAt(i);
-                    pw.println("  User #" + userId);
-                    dumpUserDataLocked(pw, userId);
+                    TextServicesData tsd = mUserData.valueAt(i);
+                    tsd.dump(pw);
                 }
             }
         } else {  // Dump a given user's data
@@ -654,68 +715,13 @@
                 }
                 synchronized (mLock) {
                     pw.println("Current Text Services Manager state:");
-                    pw.println("  Text Services: mSpellCheckerMapUpdateCount="
-                            + mSpellCheckerMapUpdateCount);
                     pw.println("  User " + userId + ":");
-                    dumpUserDataLocked(pw, userId);
+                    tsd.dump(pw);
                 }
             }
         }
     }
 
-    private void dumpUserDataLocked(PrintWriter pw, @UserIdInt int userId) {
-        TextServicesData tsd = mUserData.get(userId);
-        HashMap<String, SpellCheckerInfo> spellCheckerMap = tsd.mSpellCheckerMap;
-        int spellCheckerIndex = 0;
-        pw.println("  Spell Checkers:");
-        for (final SpellCheckerInfo info : spellCheckerMap.values()) {
-            pw.println("  Spell Checker #" + spellCheckerIndex);
-            info.dump(pw, "    ");
-            ++spellCheckerIndex;
-        }
-
-        pw.println("");
-        pw.println("  Spell Checker Bind Groups:");
-        HashMap<String, SpellCheckerBindGroup> spellCheckerBindGroups =
-                tsd.mSpellCheckerBindGroups;
-        for (final Map.Entry<String, SpellCheckerBindGroup> ent
-                : spellCheckerBindGroups.entrySet()) {
-            final SpellCheckerBindGroup grp = ent.getValue();
-            pw.println("    " + ent.getKey() + " " + grp + ":");
-            pw.println("      " + "mInternalConnection=" + grp.mInternalConnection);
-            pw.println("      " + "mSpellChecker=" + grp.mSpellChecker);
-            pw.println("      " + "mUnbindCalled=" + grp.mUnbindCalled);
-            pw.println("      " + "mConnected=" + grp.mConnected);
-            final int numPendingSessionRequests = grp.mPendingSessionRequests.size();
-            for (int j = 0; j < numPendingSessionRequests; j++) {
-                final SessionRequest req = grp.mPendingSessionRequests.get(j);
-                pw.println("      " + "Pending Request #" + j + ":");
-                pw.println("        " + "mTsListener=" + req.mTsListener);
-                pw.println("        " + "mScListener=" + req.mScListener);
-                pw.println(
-                        "        " + "mScLocale=" + req.mLocale + " mUid=" + req.mUserId);
-            }
-            final int numOnGoingSessionRequests = grp.mOnGoingSessionRequests.size();
-            for (int j = 0; j < numOnGoingSessionRequests; j++) {
-                final SessionRequest req = grp.mOnGoingSessionRequests.get(j);
-                pw.println("      " + "On going Request #" + j + ":");
-                ++j;
-                pw.println("        " + "mTsListener=" + req.mTsListener);
-                pw.println("        " + "mScListener=" + req.mScListener);
-                pw.println(
-                        "        " + "mScLocale=" + req.mLocale + " mUid=" + req.mUserId);
-            }
-            final int N = grp.mListeners.getRegisteredCallbackCount();
-            for (int j = 0; j < N; j++) {
-                final ISpellCheckerSessionListener mScListener =
-                        grp.mListeners.getRegisteredCallbackItem(j);
-                pw.println("      " + "Listener #" + j + ":");
-                pw.println("        " + "mScListener=" + mScListener);
-                pw.println("        " + "mGroup=" + grp);
-            }
-        }
-    }
-
     private static final class SessionRequest {
         @UserIdInt
         public final int mUserId;
@@ -971,64 +977,4 @@
             mBindGroup.onSessionCreated(newSession, mRequest);
         }
     }
-
-    private static final class TextServicesSettings {
-        private final ContentResolver mResolver;
-
-        public TextServicesSettings(ContentResolver resolver) {
-            mResolver = resolver;
-        }
-
-        private void putString(final String key, final String str, @UserIdInt int userId) {
-            Settings.Secure.putStringForUser(mResolver, key, str, userId);
-        }
-
-        @Nullable
-        private String getString(@NonNull final String key, @Nullable final String defaultValue,
-                @UserIdInt int userId) {
-            final String result;
-            result = Settings.Secure.getStringForUser(mResolver, key, userId);
-            return result != null ? result : defaultValue;
-        }
-
-        private void putInt(final String key, final int value, @UserIdInt int userId) {
-            Settings.Secure.putIntForUser(mResolver, key, value, userId);
-        }
-
-        private int getInt(final String key, final int defaultValue, @UserIdInt int userId) {
-            return Settings.Secure.getIntForUser(mResolver, key, defaultValue, userId);
-        }
-
-        private boolean getBoolean(final String key, final boolean defaultValue,
-                @UserIdInt int userId) {
-            return getInt(key, defaultValue ? 1 : 0, userId) == 1;
-        }
-
-        public void putSelectedSpellChecker(@Nullable String sciId, @UserIdInt int userId) {
-            if (TextUtils.isEmpty(sciId)) {
-                // OK to coalesce to null, since getSelectedSpellChecker() can take care of the
-                // empty data scenario.
-                putString(Settings.Secure.SELECTED_SPELL_CHECKER, null, userId);
-            } else {
-                putString(Settings.Secure.SELECTED_SPELL_CHECKER, sciId, userId);
-            }
-        }
-
-        public void putSelectedSpellCheckerSubtype(int hashCode, @UserIdInt int userId) {
-            putInt(Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE, hashCode, userId);
-        }
-
-        @NonNull
-        public String getSelectedSpellChecker(@UserIdInt int userId) {
-            return getString(Settings.Secure.SELECTED_SPELL_CHECKER, "", userId);
-        }
-
-        public int getSelectedSpellCheckerSubtype(final int defaultValue, @UserIdInt int userId) {
-            return getInt(Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE, defaultValue, userId);
-        }
-
-        public boolean isSpellCheckerEnabled(@UserIdInt int userId) {
-            return getBoolean(Settings.Secure.SPELL_CHECKER_ENABLED, true, userId);
-        }
-    }
 }
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..1568e8c5 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/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/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/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/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/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) {